diff options
Diffstat (limited to 'components/canvas')
-rw-r--r-- | components/canvas/Cargo.toml | 13 | ||||
-rw-r--r-- | components/canvas/canvas_data.rs | 9 | ||||
-rw-r--r-- | components/canvas/lib.rs | 7 | ||||
-rw-r--r-- | components/canvas/webgl_limits.rs | 259 | ||||
-rw-r--r-- | components/canvas/webgl_mode/inprocess.rs | 147 | ||||
-rw-r--r-- | components/canvas/webgl_mode/mod.rs | 7 | ||||
-rw-r--r-- | components/canvas/webgl_thread.rs | 3250 | ||||
-rw-r--r-- | components/canvas/webxr.rs | 337 |
8 files changed, 8 insertions, 4021 deletions
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 7e7b00efe11..6084fc6e434 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -11,24 +11,15 @@ rust-version.workspace = true name = "canvas" path = "lib.rs" -[features] -webgl_backtrace = ["canvas_traits/webgl_backtrace"] -webxr = ["dep:webxr", "dep:webxr-api"] - [dependencies] app_units = { workspace = true } -bitflags = { workspace = true } -byteorder = { workspace = true } canvas_traits = { workspace = true } compositing_traits = { workspace = true } crossbeam-channel = { workspace = true } cssparser = { workspace = true } euclid = { workspace = true } -fnv = { workspace = true } font-kit = "0.14" fonts = { path = "../fonts" } -glow = { workspace = true } -half = "2" ipc-channel = { workspace = true } log = { workspace = true } lyon_geom = "1.0.4" @@ -40,9 +31,5 @@ raqote = "0.8.5" servo_arc = { workspace = true } snapshot = { workspace = true } stylo = { workspace = true } -surfman = { workspace = true } unicode-script = { workspace = true } -webrender = { workspace = true } webrender_api = { workspace = true } -webxr = { path = "../webxr", features = ["ipc"], optional = true } -webxr-api = { workspace = true, features = ["ipc"], optional = true } diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 99d6273813e..2667b7f6b44 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -28,6 +28,10 @@ use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey use crate::raqote_backend::Repetition; +// Asserts on WR texture cache update for zero sized image with raw data. +// https://github.com/servo/webrender/blob/main/webrender/src/texture_cache.rs#L1475 +const MIN_WR_IMAGE_SIZE: Size2D<u64> = Size2D::new(1, 1); + fn to_path(path: &[PathSegment], mut builder: Box<dyn GenericPathBuilder>) -> Path { let mut build_ref = PathBuilderRef { builder: &mut builder, @@ -595,6 +599,7 @@ impl<'a> CanvasData<'a> { compositor_api: CrossProcessCompositorApi, font_context: Arc<FontContext>, ) -> CanvasData<'a> { + let size = size.max(MIN_WR_IMAGE_SIZE); let backend = create_backend(); let draw_target = backend.create_drawtarget(size); let image_key = compositor_api.generate_image_key().unwrap(); @@ -1402,7 +1407,9 @@ impl<'a> CanvasData<'a> { } pub fn recreate(&mut self, size: Option<Size2D<u64>>) { - let size = size.unwrap_or_else(|| self.drawtarget.get_size().to_u64()); + let size = size + .unwrap_or_else(|| self.drawtarget.get_size().to_u64()) + .max(MIN_WR_IMAGE_SIZE); self.drawtarget = self .backend .create_drawtarget(Size2D::new(size.width, size.height)); diff --git a/components/canvas/lib.rs b/components/canvas/lib.rs index d2c62c1d8b6..86c291fdc87 100644 --- a/components/canvas/lib.rs +++ b/components/canvas/lib.rs @@ -6,12 +6,5 @@ mod raqote_backend; -pub use webgl_mode::WebGLComm; - pub mod canvas_data; pub mod canvas_paint_thread; -mod webgl_limits; -mod webgl_mode; -pub mod webgl_thread; -#[cfg(feature = "webxr")] -mod webxr; diff --git a/components/canvas/webgl_limits.rs b/components/canvas/webgl_limits.rs deleted file mode 100644 index f683b6efff6..00000000000 --- a/components/canvas/webgl_limits.rs +++ /dev/null @@ -1,259 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use canvas_traits::webgl::{GLLimits, WebGLVersion}; -use glow::{self as gl, Context as Gl, HasContext}; -type GLenum = u32; - -pub trait GLLimitsDetect { - fn detect(gl: &Gl, webgl_version: WebGLVersion) -> Self; -} - -impl GLLimitsDetect for GLLimits { - fn detect(gl: &Gl, webgl_version: WebGLVersion) -> GLLimits { - let max_vertex_attribs = gl.get_integer(gl::MAX_VERTEX_ATTRIBS); - let max_tex_size = gl.get_integer(gl::MAX_TEXTURE_SIZE); - let max_cube_map_tex_size = gl.get_integer(gl::MAX_CUBE_MAP_TEXTURE_SIZE); - let max_combined_texture_image_units = gl.get_integer(gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS); - let max_renderbuffer_size = gl.get_integer(gl::MAX_RENDERBUFFER_SIZE); - let max_texture_image_units = gl.get_integer(gl::MAX_TEXTURE_IMAGE_UNITS); - let max_vertex_texture_image_units = gl.get_integer(gl::MAX_VERTEX_TEXTURE_IMAGE_UNITS); - - // TODO: better value for this? - let max_client_wait_timeout_webgl = std::time::Duration::new(1, 0); - - // Based on: - // https://searchfox.org/mozilla-central/rev/5a744713370ec47969595e369fd5125f123e6d24/dom/canvas/WebGLContextValidate.cpp#523-558 - let ( - max_fragment_uniform_vectors, - max_varying_vectors, - max_vertex_uniform_vectors, - max_vertex_output_vectors, - max_fragment_input_vectors, - ); - if gl.version().is_embedded { - max_fragment_uniform_vectors = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_VECTORS); - max_varying_vectors = gl.get_integer(gl::MAX_VARYING_VECTORS); - max_vertex_uniform_vectors = gl.get_integer(gl::MAX_VERTEX_UNIFORM_VECTORS); - max_vertex_output_vectors = gl - .try_get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS) - .map(|c| c / 4) - .unwrap_or(max_varying_vectors); - max_fragment_input_vectors = gl - .try_get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS) - .map(|c| c / 4) - .unwrap_or(max_vertex_output_vectors); - } else { - max_fragment_uniform_vectors = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_COMPONENTS) / 4; - max_vertex_uniform_vectors = gl.get_integer(gl::MAX_VERTEX_UNIFORM_COMPONENTS) / 4; - - max_fragment_input_vectors = gl - .try_get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS) - .or_else(|| gl.try_get_integer(gl::MAX_VARYING_COMPONENTS)) - .map(|c| c / 4) - .unwrap_or_else(|| gl.get_integer(gl::MAX_VARYING_VECTORS)); - max_vertex_output_vectors = gl - .try_get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS) - .map(|c| c / 4) - .unwrap_or(max_fragment_input_vectors); - max_varying_vectors = max_vertex_output_vectors - .min(max_fragment_input_vectors) - .max(4); - }; - - let ( - max_uniform_block_size, - max_uniform_buffer_bindings, - min_program_texel_offset, - max_program_texel_offset, - max_transform_feedback_separate_attribs, - max_draw_buffers, - max_color_attachments, - max_combined_uniform_blocks, - max_combined_vertex_uniform_components, - max_combined_fragment_uniform_components, - max_vertex_uniform_blocks, - max_vertex_uniform_components, - max_fragment_uniform_blocks, - max_fragment_uniform_components, - max_3d_texture_size, - max_array_texture_layers, - uniform_buffer_offset_alignment, - max_element_index, - max_elements_indices, - max_elements_vertices, - max_fragment_input_components, - max_samples, - max_server_wait_timeout, - max_texture_lod_bias, - max_varying_components, - max_vertex_output_components, - ); - if webgl_version == WebGLVersion::WebGL2 { - max_uniform_block_size = gl.get_integer64(gl::MAX_UNIFORM_BLOCK_SIZE); - max_uniform_buffer_bindings = gl.get_integer(gl::MAX_UNIFORM_BUFFER_BINDINGS); - min_program_texel_offset = gl.get_signed_integer(gl::MIN_PROGRAM_TEXEL_OFFSET); - max_program_texel_offset = gl.get_integer(gl::MAX_PROGRAM_TEXEL_OFFSET); - max_transform_feedback_separate_attribs = - gl.get_integer(gl::MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS); - max_color_attachments = gl.get_integer(gl::MAX_COLOR_ATTACHMENTS); - max_draw_buffers = gl - .get_integer(gl::MAX_DRAW_BUFFERS) - .min(max_color_attachments); - max_combined_uniform_blocks = gl.get_integer(gl::MAX_COMBINED_UNIFORM_BLOCKS); - max_combined_vertex_uniform_components = - gl.get_integer64(gl::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS); - max_combined_fragment_uniform_components = - gl.get_integer64(gl::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS); - max_vertex_uniform_blocks = gl.get_integer(gl::MAX_VERTEX_UNIFORM_BLOCKS); - max_vertex_uniform_components = gl.get_integer(gl::MAX_VERTEX_UNIFORM_COMPONENTS); - max_fragment_uniform_blocks = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_BLOCKS); - max_fragment_uniform_components = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_COMPONENTS); - uniform_buffer_offset_alignment = gl.get_integer(gl::UNIFORM_BUFFER_OFFSET_ALIGNMENT); - max_3d_texture_size = gl.get_integer(gl::MAX_3D_TEXTURE_SIZE); - max_array_texture_layers = gl.get_integer(gl::MAX_ARRAY_TEXTURE_LAYERS); - max_element_index = gl - .try_get_integer64(gl::MAX_ELEMENT_INDEX) - .unwrap_or(u32::MAX as u64); // requires GL 4.3 - max_elements_indices = gl.get_integer(gl::MAX_ELEMENTS_INDICES); - max_elements_vertices = gl.get_integer(gl::MAX_ELEMENTS_VERTICES); - max_fragment_input_components = gl.get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS); - max_samples = gl.get_integer(gl::MAX_SAMPLES); - max_server_wait_timeout = - std::time::Duration::from_nanos(gl.get_integer64(gl::MAX_SERVER_WAIT_TIMEOUT)); - max_texture_lod_bias = gl.get_float(gl::MAX_TEXTURE_LOD_BIAS); - max_varying_components = gl.try_get_integer(gl::MAX_VARYING_COMPONENTS).unwrap_or( - // macOS Core Profile is buggy. The spec says this value is 4 * MAX_VARYING_VECTORS. - max_varying_vectors * 4, - ); - max_vertex_output_components = gl.get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS); - } else { - max_uniform_block_size = 0; - max_uniform_buffer_bindings = 0; - min_program_texel_offset = 0; - max_program_texel_offset = 0; - max_transform_feedback_separate_attribs = 0; - max_color_attachments = 1; - max_draw_buffers = 1; - max_combined_uniform_blocks = 0; - max_combined_vertex_uniform_components = 0; - max_combined_fragment_uniform_components = 0; - max_vertex_uniform_blocks = 0; - max_vertex_uniform_components = 0; - max_fragment_uniform_blocks = 0; - max_fragment_uniform_components = 0; - uniform_buffer_offset_alignment = 0; - max_3d_texture_size = 0; - max_array_texture_layers = 0; - max_element_index = 0; - max_elements_indices = 0; - max_elements_vertices = 0; - max_fragment_input_components = 0; - max_samples = 0; - max_server_wait_timeout = std::time::Duration::default(); - max_texture_lod_bias = 0.0; - max_varying_components = 0; - max_vertex_output_components = 0; - } - - GLLimits { - max_vertex_attribs, - max_tex_size, - max_cube_map_tex_size, - max_combined_texture_image_units, - max_fragment_uniform_vectors, - max_renderbuffer_size, - max_texture_image_units, - max_varying_vectors, - max_vertex_texture_image_units, - max_vertex_uniform_vectors, - max_client_wait_timeout_webgl, - max_transform_feedback_separate_attribs, - max_vertex_output_vectors, - max_fragment_input_vectors, - max_uniform_buffer_bindings, - min_program_texel_offset, - max_program_texel_offset, - max_color_attachments, - max_draw_buffers, - max_uniform_block_size, - max_combined_uniform_blocks, - max_combined_vertex_uniform_components, - max_combined_fragment_uniform_components, - max_vertex_uniform_blocks, - max_vertex_uniform_components, - max_fragment_uniform_blocks, - max_fragment_uniform_components, - max_3d_texture_size, - max_array_texture_layers, - uniform_buffer_offset_alignment, - max_element_index, - max_elements_indices, - max_elements_vertices, - max_fragment_input_components, - max_samples, - max_server_wait_timeout, - max_texture_lod_bias, - max_varying_components, - max_vertex_output_components, - } - } -} - -trait GLExt { - fn try_get_integer(self, parameter: GLenum) -> Option<u32>; - fn try_get_integer64(self, parameter: GLenum) -> Option<u64>; - fn try_get_signed_integer(self, parameter: GLenum) -> Option<i32>; - fn try_get_float(self, parameter: GLenum) -> Option<f32>; - fn get_integer(self, parameter: GLenum) -> u32; - fn get_integer64(self, parameter: GLenum) -> u64; - fn get_signed_integer(self, parameter: GLenum) -> i32; - fn get_float(self, parameter: GLenum) -> f32; -} - -macro_rules! create_fun { - ($tryer:ident, $getter:ident, $gltype:ty, $glcall:ident, $rstype:ty) => { - #[allow(unsafe_code)] - fn $tryer(self, parameter: GLenum) -> Option<$rstype> { - let mut value = [<$gltype>::default()]; - unsafe { - self.$glcall(parameter, &mut value); - } - if unsafe { self.get_error() } != gl::NO_ERROR { - None - } else { - Some(value[0] as $rstype) - } - } - - fn $getter(self, parameter: GLenum) -> $rstype { - self.$tryer(parameter).unwrap() - } - }; -} - -impl GLExt for &Gl { - create_fun!( - try_get_integer, - get_integer, - i32, - get_parameter_i32_slice, - u32 - ); - create_fun!( - try_get_integer64, - get_integer64, - i64, - get_parameter_i64_slice, - u64 - ); - create_fun!( - try_get_signed_integer, - get_signed_integer, - i32, - get_parameter_i32_slice, - i32 - ); - create_fun!(try_get_float, get_float, f32, get_parameter_f32_slice, f32); -} diff --git a/components/canvas/webgl_mode/inprocess.rs b/components/canvas/webgl_mode/inprocess.rs deleted file mode 100644 index 566da2c58c8..00000000000 --- a/components/canvas/webgl_mode/inprocess.rs +++ /dev/null @@ -1,147 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::default::Default; -use std::rc::Rc; -use std::sync::{Arc, Mutex}; - -use canvas_traits::webgl::{GlType, WebGLContextId, WebGLMsg, WebGLThreads, webgl_channel}; -use compositing_traits::rendering_context::RenderingContext; -use compositing_traits::{ - WebrenderExternalImageApi, WebrenderExternalImageRegistry, WebrenderImageSource, -}; -use euclid::default::Size2D; -use fnv::FnvHashMap; -use log::debug; -use surfman::chains::{SwapChainAPI, SwapChains, SwapChainsAPI}; -use surfman::{Device, SurfaceTexture}; -use webrender::RenderApiSender; -use webrender_api::DocumentId; -#[cfg(feature = "webxr")] -use webxr::SurfmanGL as WebXRSurfman; -#[cfg(feature = "webxr")] -use webxr_api::LayerGrandManager as WebXRLayerGrandManager; - -use crate::webgl_thread::{WebGLThread, WebGLThreadInit}; - -pub struct WebGLComm { - pub webgl_threads: WebGLThreads, - pub image_handler: Box<dyn WebrenderExternalImageApi>, - #[cfg(feature = "webxr")] - pub webxr_layer_grand_manager: WebXRLayerGrandManager<WebXRSurfman>, -} - -impl WebGLComm { - /// Creates a new `WebGLComm` object. - pub fn new( - rendering_context: Rc<dyn RenderingContext>, - webrender_api_sender: RenderApiSender, - webrender_doc: DocumentId, - external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, - api_type: GlType, - ) -> WebGLComm { - debug!("WebGLThreads::new()"); - let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap(); - let webrender_swap_chains = SwapChains::new(); - #[cfg(feature = "webxr")] - let webxr_init = crate::webxr::WebXRBridgeInit::new(sender.clone()); - #[cfg(feature = "webxr")] - let webxr_layer_grand_manager = webxr_init.layer_grand_manager(); - let connection = rendering_context - .connection() - .expect("Failed to get connection"); - let adapter = connection - .create_adapter() - .expect("Failed to create adapter"); - - // This implementation creates a single `WebGLThread` for all the pipelines. - let init = WebGLThreadInit { - webrender_api_sender, - webrender_doc, - external_images, - sender: sender.clone(), - receiver, - webrender_swap_chains: webrender_swap_chains.clone(), - connection, - adapter, - api_type, - #[cfg(feature = "webxr")] - webxr_init, - }; - - let external = WebGLExternalImages::new(rendering_context, webrender_swap_chains); - - WebGLThread::run_on_own_thread(init); - - WebGLComm { - webgl_threads: WebGLThreads(sender), - image_handler: Box::new(external), - #[cfg(feature = "webxr")] - webxr_layer_grand_manager, - } - } -} - -/// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads. -struct WebGLExternalImages { - rendering_context: Rc<dyn RenderingContext>, - swap_chains: SwapChains<WebGLContextId, Device>, - locked_front_buffers: FnvHashMap<WebGLContextId, SurfaceTexture>, -} - -impl WebGLExternalImages { - fn new( - rendering_context: Rc<dyn RenderingContext>, - swap_chains: SwapChains<WebGLContextId, Device>, - ) -> Self { - Self { - rendering_context, - swap_chains, - locked_front_buffers: FnvHashMap::default(), - } - } - - fn lock_swap_chain(&mut self, id: WebGLContextId) -> Option<(u32, Size2D<i32>)> { - debug!("... locking chain {:?}", id); - let front_buffer = self.swap_chains.get(id)?.take_surface()?; - - if let Some((surface_texture, gl_texture, size)) = - self.rendering_context.create_texture(front_buffer) - { - self.locked_front_buffers.insert(id, surface_texture); - - Some((gl_texture, size)) - } else { - None - } - } - - fn unlock_swap_chain(&mut self, id: WebGLContextId) -> Option<()> { - debug!("... unlocked chain {:?}", id); - let locked_front_buffer = self.locked_front_buffers.remove(&id)?; - if let Some(locked_front_buffer) = - self.rendering_context.destroy_texture(locked_front_buffer) - { - self.swap_chains - .get(id)? - .recycle_surface(locked_front_buffer); - Some(()) - } else { - None - } - } -} - -impl WebrenderExternalImageApi for WebGLExternalImages { - fn lock(&mut self, id: u64) -> (WebrenderImageSource, Size2D<i32>) { - let id = WebGLContextId(id); - let (texture_id, size) = self.lock_swap_chain(id).unwrap_or_default(); - (WebrenderImageSource::TextureHandle(texture_id), size) - } - - fn unlock(&mut self, id: u64) { - let id = WebGLContextId(id); - self.unlock_swap_chain(id); - } -} diff --git a/components/canvas/webgl_mode/mod.rs b/components/canvas/webgl_mode/mod.rs deleted file mode 100644 index 8bc74f6e244..00000000000 --- a/components/canvas/webgl_mode/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -mod inprocess; - -pub use self::inprocess::WebGLComm; diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs deleted file mode 100644 index b1ac2b2d3c4..00000000000 --- a/components/canvas/webgl_thread.rs +++ /dev/null @@ -1,3250 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -#![allow(unsafe_code)] -use std::borrow::Cow; -use std::num::NonZeroU32; -use std::rc::Rc; -use std::sync::{Arc, Mutex}; -use std::{slice, thread}; - -use bitflags::bitflags; -use byteorder::{ByteOrder, NativeEndian, WriteBytesExt}; -use canvas_traits::webgl; -#[cfg(feature = "webxr")] -use canvas_traits::webgl::WebXRCommand; -use canvas_traits::webgl::{ - ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, AlphaTreatment, - GLContextAttributes, GLLimits, GlType, InternalFormatIntVec, ProgramLinkInfo, TexDataType, - TexFormat, WebGLBufferId, WebGLChan, WebGLCommand, WebGLCommandBacktrace, WebGLContextId, - WebGLCreateContextResult, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLMsg, - WebGLMsgSender, WebGLProgramId, WebGLQueryId, WebGLReceiver, WebGLRenderbufferId, - WebGLSLVersion, WebGLSamplerId, WebGLSender, WebGLShaderId, WebGLSyncId, WebGLTextureId, - WebGLVersion, WebGLVertexArrayId, YAxisTreatment, -}; -use compositing_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType}; -use euclid::default::Size2D; -use fnv::FnvHashMap; -use glow::{ - self as gl, ActiveTransformFeedback, Context as Gl, HasContext, NativeTransformFeedback, - NativeUniformLocation, NativeVertexArray, PixelUnpackData, ShaderPrecisionFormat, - bytes_per_type, components_per_format, -}; -use half::f16; -use ipc_channel::ipc::IpcSharedMemory; -use log::{debug, error, trace, warn}; -use pixels::{self, PixelFormat, unmultiply_inplace}; -use surfman::chains::{PreserveBuffer, SwapChains, SwapChainsAPI}; -use surfman::{ - self, Adapter, Connection, Context, ContextAttributeFlags, ContextAttributes, Device, - GLVersion, SurfaceAccess, SurfaceInfo, SurfaceType, -}; -use webrender::{RenderApi, RenderApiSender, Transaction}; -use webrender_api::units::DeviceIntSize; -use webrender_api::{ - DirtyRect, DocumentId, ExternalImageData, ExternalImageId, ExternalImageType, ImageBufferKind, - ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey, -}; - -use crate::webgl_limits::GLLimitsDetect; -#[cfg(feature = "webxr")] -use crate::webxr::{WebXRBridge, WebXRBridgeContexts, WebXRBridgeInit}; - -type GLint = i32; - -fn native_uniform_location(location: i32) -> Option<NativeUniformLocation> { - location.try_into().ok().map(NativeUniformLocation) -} - -pub(crate) struct GLContextData { - pub(crate) ctx: Context, - pub(crate) gl: Rc<glow::Context>, - state: GLState, - attributes: GLContextAttributes, -} - -#[derive(Debug)] -pub struct GLState { - _webgl_version: WebGLVersion, - _gl_version: GLVersion, - requested_flags: ContextAttributeFlags, - // This is the WebGL view of the color mask - // The GL view may be different: if the GL context supports alpha - // but the WebGL context doesn't, then color_write_mask.3 might be true - // but the GL color write mask is false. - color_write_mask: [bool; 4], - clear_color: (f32, f32, f32, f32), - scissor_test_enabled: bool, - // The WebGL view of the stencil write mask (see comment re `color_write_mask`) - stencil_write_mask: (u32, u32), - stencil_test_enabled: bool, - stencil_clear_value: i32, - // The WebGL view of the depth write mask (see comment re `color_write_mask`) - depth_write_mask: bool, - depth_test_enabled: bool, - depth_clear_value: f64, - // True when the default framebuffer is bound to DRAW_FRAMEBUFFER - drawing_to_default_framebuffer: bool, - default_vao: Option<NativeVertexArray>, -} - -impl GLState { - // Are we faking having no alpha / depth / stencil? - fn fake_no_alpha(&self) -> bool { - self.drawing_to_default_framebuffer & - !self.requested_flags.contains(ContextAttributeFlags::ALPHA) - } - - fn fake_no_depth(&self) -> bool { - self.drawing_to_default_framebuffer & - !self.requested_flags.contains(ContextAttributeFlags::DEPTH) - } - - fn fake_no_stencil(&self) -> bool { - self.drawing_to_default_framebuffer & - !self - .requested_flags - .contains(ContextAttributeFlags::STENCIL) - } - - // We maintain invariants between the GLState object and the GL state. - fn restore_invariant(&self, gl: &Gl) { - self.restore_clear_color_invariant(gl); - self.restore_scissor_invariant(gl); - self.restore_alpha_invariant(gl); - self.restore_depth_invariant(gl); - self.restore_stencil_invariant(gl); - } - - fn restore_clear_color_invariant(&self, gl: &Gl) { - let (r, g, b, a) = self.clear_color; - unsafe { gl.clear_color(r, g, b, a) }; - } - - fn restore_scissor_invariant(&self, gl: &Gl) { - if self.scissor_test_enabled { - unsafe { gl.enable(gl::SCISSOR_TEST) }; - } else { - unsafe { gl.disable(gl::SCISSOR_TEST) }; - } - } - - fn restore_alpha_invariant(&self, gl: &Gl) { - let [r, g, b, a] = self.color_write_mask; - if self.fake_no_alpha() { - unsafe { gl.color_mask(r, g, b, false) }; - } else { - unsafe { gl.color_mask(r, g, b, a) }; - } - } - - fn restore_depth_invariant(&self, gl: &Gl) { - unsafe { - if self.fake_no_depth() { - gl.depth_mask(false); - gl.disable(gl::DEPTH_TEST); - } else { - gl.depth_mask(self.depth_write_mask); - if self.depth_test_enabled { - gl.enable(gl::DEPTH_TEST); - } else { - gl.disable(gl::DEPTH_TEST); - } - } - } - } - - fn restore_stencil_invariant(&self, gl: &Gl) { - unsafe { - if self.fake_no_stencil() { - gl.stencil_mask(0); - gl.disable(gl::STENCIL_TEST); - } else { - let (f, b) = self.stencil_write_mask; - gl.stencil_mask_separate(gl::FRONT, f); - gl.stencil_mask_separate(gl::BACK, b); - if self.stencil_test_enabled { - gl.enable(gl::STENCIL_TEST); - } else { - gl.disable(gl::STENCIL_TEST); - } - } - } - } -} - -impl Default for GLState { - fn default() -> GLState { - GLState { - _gl_version: GLVersion { major: 1, minor: 0 }, - _webgl_version: WebGLVersion::WebGL1, - requested_flags: ContextAttributeFlags::empty(), - color_write_mask: [true, true, true, true], - clear_color: (0., 0., 0., 0.), - scissor_test_enabled: false, - // Should these be 0xFFFF_FFFF? - stencil_write_mask: (0, 0), - stencil_test_enabled: false, - stencil_clear_value: 0, - depth_write_mask: true, - depth_test_enabled: false, - depth_clear_value: 1., - default_vao: None, - drawing_to_default_framebuffer: true, - } - } -} - -/// A WebGLThread manages the life cycle and message multiplexing of -/// a set of WebGLContexts living in the same thread. -pub(crate) struct WebGLThread { - /// The GPU device. - device: Device, - /// Channel used to generate/update or delete `ImageKey`s. - webrender_api: RenderApi, - webrender_doc: DocumentId, - /// Map of live WebGLContexts. - contexts: FnvHashMap<WebGLContextId, GLContextData>, - /// Cached information for WebGLContexts. - cached_context_info: FnvHashMap<WebGLContextId, WebGLContextInfo>, - /// Current bound context. - bound_context_id: Option<WebGLContextId>, - /// List of registered webrender external images. - /// We use it to get an unique ID for new WebGLContexts. - external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, - /// The receiver that will be used for processing WebGL messages. - receiver: crossbeam_channel::Receiver<WebGLMsg>, - /// The receiver that should be used to send WebGL messages for processing. - sender: WebGLSender<WebGLMsg>, - /// The swap chains used by webrender - webrender_swap_chains: SwapChains<WebGLContextId, Device>, - /// Whether this context is a GL or GLES context. - api_type: GlType, - #[cfg(feature = "webxr")] - /// The bridge to WebXR - pub webxr_bridge: WebXRBridge, -} - -/// The data required to initialize an instance of the WebGLThread type. -pub(crate) struct WebGLThreadInit { - pub webrender_api_sender: RenderApiSender, - pub webrender_doc: DocumentId, - pub external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, - pub sender: WebGLSender<WebGLMsg>, - pub receiver: WebGLReceiver<WebGLMsg>, - pub webrender_swap_chains: SwapChains<WebGLContextId, Device>, - pub connection: Connection, - pub adapter: Adapter, - pub api_type: GlType, - #[cfg(feature = "webxr")] - pub webxr_init: WebXRBridgeInit, -} - -// A size at which it should be safe to create GL contexts -const SAFE_VIEWPORT_DIMS: [u32; 2] = [1024, 1024]; - -impl WebGLThread { - /// Create a new instance of WebGLThread. - pub(crate) fn new( - WebGLThreadInit { - webrender_api_sender, - webrender_doc, - external_images, - sender, - receiver, - webrender_swap_chains, - connection, - adapter, - api_type, - #[cfg(feature = "webxr")] - webxr_init, - }: WebGLThreadInit, - ) -> Self { - WebGLThread { - device: connection - .create_device(&adapter) - .expect("Couldn't open WebGL device!"), - webrender_api: webrender_api_sender.create_api(), - webrender_doc, - contexts: Default::default(), - cached_context_info: Default::default(), - bound_context_id: None, - external_images, - sender, - receiver: receiver.into_inner(), - webrender_swap_chains, - api_type, - #[cfg(feature = "webxr")] - webxr_bridge: WebXRBridge::new(webxr_init), - } - } - - /// Perform all initialization required to run an instance of WebGLThread - /// in parallel on its own dedicated thread. - pub(crate) fn run_on_own_thread(init: WebGLThreadInit) { - thread::Builder::new() - .name("WebGL".to_owned()) - .spawn(move || { - let mut data = WebGLThread::new(init); - data.process(); - }) - .expect("Thread spawning failed"); - } - - fn process(&mut self) { - let webgl_chan = WebGLChan(self.sender.clone()); - while let Ok(msg) = self.receiver.recv() { - let exit = self.handle_msg(msg, &webgl_chan); - if exit { - break; - } - } - } - - /// Handles a generic WebGLMsg message - fn handle_msg(&mut self, msg: WebGLMsg, webgl_chan: &WebGLChan) -> bool { - trace!("processing {:?}", msg); - match msg { - WebGLMsg::CreateContext(version, size, attributes, result_sender) => { - let result = self.create_webgl_context(version, size, attributes); - - result_sender - .send(result.map(|(id, limits)| { - let image_key = self - .cached_context_info - .get_mut(&id) - .expect("Where's the cached context info?") - .image_key; - - let data = Self::make_current_if_needed( - &self.device, - id, - &self.contexts, - &mut self.bound_context_id, - ) - .expect("WebGLContext not found"); - let glsl_version = Self::get_glsl_version(&data.gl); - let api_type = if data.gl.version().is_embedded { - GlType::Gles - } else { - GlType::Gl - }; - - // FIXME(nox): Should probably be done by surfman. - if api_type != GlType::Gles { - // Points sprites are enabled by default in OpenGL 3.2 core - // and in GLES. Rather than doing version detection, it does - // not hurt to enable them anyways. - - unsafe { - // XXX: Do we even need to this? - const GL_POINT_SPRITE: u32 = 0x8861; - data.gl.enable(GL_POINT_SPRITE); - let err = data.gl.get_error(); - if err != 0 { - warn!("Error enabling GL point sprites: {}", err); - } - - data.gl.enable(gl::PROGRAM_POINT_SIZE); - let err = data.gl.get_error(); - if err != 0 { - warn!("Error enabling GL program point size: {}", err); - } - } - } - - WebGLCreateContextResult { - sender: WebGLMsgSender::new(id, webgl_chan.clone()), - limits, - glsl_version, - api_type, - image_key, - } - })) - .unwrap(); - }, - WebGLMsg::ResizeContext(ctx_id, size, sender) => { - let _ = sender.send(self.resize_webgl_context(ctx_id, size)); - }, - WebGLMsg::RemoveContext(ctx_id) => { - self.remove_webgl_context(ctx_id); - }, - WebGLMsg::WebGLCommand(ctx_id, command, backtrace) => { - self.handle_webgl_command(ctx_id, command, backtrace); - }, - WebGLMsg::WebXRCommand(_command) => { - #[cfg(feature = "webxr")] - self.handle_webxr_command(_command); - }, - WebGLMsg::SwapBuffers(swap_ids, sender, sent_time) => { - self.handle_swap_buffers(swap_ids, sender, sent_time); - }, - WebGLMsg::Exit(sender) => { - // Call remove_context functions in order to correctly delete WebRender image keys. - let context_ids: Vec<WebGLContextId> = self.contexts.keys().copied().collect(); - for id in context_ids { - self.remove_webgl_context(id); - } - - // Block on shutting-down WebRender. - self.webrender_api.shut_down(true); - if let Err(e) = sender.send(()) { - warn!("Failed to send response to WebGLMsg::Exit ({e})"); - } - return true; - }, - } - - false - } - - #[cfg(feature = "webxr")] - /// Handles a WebXR message - fn handle_webxr_command(&mut self, command: WebXRCommand) { - trace!("processing {:?}", command); - let mut contexts = WebXRBridgeContexts { - contexts: &mut self.contexts, - bound_context_id: &mut self.bound_context_id, - }; - match command { - WebXRCommand::CreateLayerManager(sender) => { - let result = self - .webxr_bridge - .create_layer_manager(&mut self.device, &mut contexts); - let _ = sender.send(result); - }, - WebXRCommand::DestroyLayerManager(manager_id) => { - self.webxr_bridge.destroy_layer_manager(manager_id); - }, - WebXRCommand::CreateLayer(manager_id, context_id, layer_init, sender) => { - let result = self.webxr_bridge.create_layer( - manager_id, - &mut self.device, - &mut contexts, - context_id, - layer_init, - ); - let _ = sender.send(result); - }, - WebXRCommand::DestroyLayer(manager_id, context_id, layer_id) => { - self.webxr_bridge.destroy_layer( - manager_id, - &mut self.device, - &mut contexts, - context_id, - layer_id, - ); - }, - WebXRCommand::BeginFrame(manager_id, layers, sender) => { - let result = self.webxr_bridge.begin_frame( - manager_id, - &mut self.device, - &mut contexts, - &layers[..], - ); - let _ = sender.send(result); - }, - WebXRCommand::EndFrame(manager_id, layers, sender) => { - let result = self.webxr_bridge.end_frame( - manager_id, - &mut self.device, - &mut contexts, - &layers[..], - ); - let _ = sender.send(result); - }, - } - } - - /// Handles a WebGLCommand for a specific WebGLContext - fn handle_webgl_command( - &mut self, - context_id: WebGLContextId, - command: WebGLCommand, - backtrace: WebGLCommandBacktrace, - ) { - if self.cached_context_info.get_mut(&context_id).is_none() { - return; - } - let data = Self::make_current_if_needed_mut( - &self.device, - context_id, - &mut self.contexts, - &mut self.bound_context_id, - ); - if let Some(data) = data { - WebGLImpl::apply( - &self.device, - &data.ctx, - &data.gl, - &mut data.state, - &data.attributes, - command, - backtrace, - ); - } - } - - /// Creates a new WebGLContext - fn create_webgl_context( - &mut self, - webgl_version: WebGLVersion, - requested_size: Size2D<u32>, - attributes: GLContextAttributes, - ) -> Result<(WebGLContextId, webgl::GLLimits), String> { - debug!( - "WebGLThread::create_webgl_context({:?}, {:?}, {:?})", - webgl_version, requested_size, attributes - ); - - // Creating a new GLContext may make the current bound context_id dirty. - // Clear it to ensure that make_current() is called in subsequent commands. - self.bound_context_id = None; - - let requested_flags = - attributes.to_surfman_context_attribute_flags(webgl_version, self.api_type); - // Some GL implementations seem to only allow famebuffers - // to have alpha, depth and stencil if their creating context does. - // WebGL requires all contexts to be able to create framebuffers with - // alpha, depth and stencil. So we always create a context with them, - // and fake not having them if requested. - let flags = requested_flags | - ContextAttributeFlags::ALPHA | - ContextAttributeFlags::DEPTH | - ContextAttributeFlags::STENCIL; - let context_attributes = &ContextAttributes { - version: webgl_version.to_surfman_version(self.api_type), - flags, - }; - - let context_descriptor = self - .device - .create_context_descriptor(context_attributes) - .map_err(|err| format!("Failed to create context descriptor: {:?}", err))?; - - let safe_size = Size2D::new( - requested_size.width.min(SAFE_VIEWPORT_DIMS[0]).max(1), - requested_size.height.min(SAFE_VIEWPORT_DIMS[1]).max(1), - ); - let surface_type = SurfaceType::Generic { - size: safe_size.to_i32(), - }; - let surface_access = self.surface_access(); - - let mut ctx = self - .device - .create_context(&context_descriptor, None) - .map_err(|err| format!("Failed to create the GL context: {:?}", err))?; - let surface = self - .device - .create_surface(&ctx, surface_access, surface_type) - .map_err(|err| format!("Failed to create the initial surface: {:?}", err))?; - self.device - .bind_surface_to_context(&mut ctx, surface) - .map_err(|err| format!("Failed to bind initial surface: {:?}", err))?; - // https://github.com/pcwalton/surfman/issues/7 - self.device - .make_context_current(&ctx) - .map_err(|err| format!("Failed to make new context current: {:?}", err))?; - - let id = WebGLContextId( - self.external_images - .lock() - .expect("Lock poisoned?") - .next_id(WebrenderImageHandlerType::WebGL) - .0, - ); - - self.webrender_swap_chains - .create_attached_swap_chain(id, &mut self.device, &mut ctx, surface_access) - .map_err(|err| format!("Failed to create swap chain: {:?}", err))?; - - let swap_chain = self - .webrender_swap_chains - .get(id) - .expect("Failed to get the swap chain"); - - debug!( - "Created webgl context {:?}/{:?}", - id, - self.device.context_id(&ctx), - ); - - let gl = unsafe { - Rc::new(match self.api_type { - GlType::Gl => glow::Context::from_loader_function(|symbol_name| { - self.device.get_proc_address(&ctx, symbol_name) - }), - GlType::Gles => glow::Context::from_loader_function(|symbol_name| { - self.device.get_proc_address(&ctx, symbol_name) - }), - }) - }; - - let limits = GLLimits::detect(&gl, webgl_version); - - let size = clamp_viewport(&gl, requested_size); - if safe_size != size { - debug!("Resizing swap chain from {:?} to {:?}", safe_size, size); - swap_chain - .resize(&mut self.device, &mut ctx, size.to_i32()) - .map_err(|err| format!("Failed to resize swap chain: {:?}", err))?; - } - - let descriptor = self.device.context_descriptor(&ctx); - let descriptor_attributes = self.device.context_descriptor_attributes(&descriptor); - let gl_version = descriptor_attributes.version; - let has_alpha = requested_flags.contains(ContextAttributeFlags::ALPHA); - let image_buffer_kind = current_wr_image_buffer_kind(&self.device); - - self.device.make_context_current(&ctx).unwrap(); - let framebuffer = self - .device - .context_surface_info(&ctx) - .map_err(|err| format!("Failed to get context surface info: {:?}", err))? - .ok_or_else(|| "Failed to get context surface info".to_string())? - .framebuffer_object; - - unsafe { - gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer); - gl.viewport(0, 0, size.width as i32, size.height as i32); - gl.scissor(0, 0, size.width as i32, size.height as i32); - gl.clear_color(0., 0., 0., !has_alpha as u32 as f32); - gl.clear_depth(1.); - gl.clear_stencil(0); - gl.clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); - gl.clear_color(0., 0., 0., 0.); - debug_assert_eq!(gl.get_error(), gl::NO_ERROR); - } - - let default_vao = if let Some(vao) = WebGLImpl::create_vertex_array(&gl) { - WebGLImpl::bind_vertex_array(&gl, Some(vao.glow())); - Some(vao.glow()) - } else { - None - }; - - let state = GLState { - _gl_version: gl_version, - _webgl_version: webgl_version, - requested_flags, - default_vao, - ..Default::default() - }; - debug!("Created state {:?}", state); - - state.restore_invariant(&gl); - debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR); - - self.contexts.insert( - id, - GLContextData { - ctx, - gl, - state, - attributes, - }, - ); - - let image_key = Self::create_wr_external_image( - &mut self.webrender_api, - self.webrender_doc, - size.to_i32(), - has_alpha, - id, - image_buffer_kind, - ); - - self.cached_context_info - .insert(id, WebGLContextInfo { image_key }); - - Ok((id, limits)) - } - - /// Resizes a WebGLContext - fn resize_webgl_context( - &mut self, - context_id: WebGLContextId, - requested_size: Size2D<u32>, - ) -> Result<(), String> { - let data = Self::make_current_if_needed_mut( - &self.device, - context_id, - &mut self.contexts, - &mut self.bound_context_id, - ) - .expect("Missing WebGL context!"); - - let size = clamp_viewport(&data.gl, requested_size); - - // Check to see if any of the current framebuffer bindings are the surface we're about to - // throw out. If so, we'll have to reset them after destroying the surface. - let framebuffer_rebinding_info = - FramebufferRebindingInfo::detect(&self.device, &data.ctx, &data.gl); - - // Resize the swap chains - if let Some(swap_chain) = self.webrender_swap_chains.get(context_id) { - let alpha = data - .state - .requested_flags - .contains(ContextAttributeFlags::ALPHA); - let clear_color = [0.0, 0.0, 0.0, !alpha as i32 as f32]; - swap_chain - .resize(&mut self.device, &mut data.ctx, size.to_i32()) - .map_err(|err| format!("Failed to resize swap chain: {:?}", err))?; - swap_chain - .clear_surface(&mut self.device, &mut data.ctx, &data.gl, clear_color) - .map_err(|err| format!("Failed to clear resized swap chain: {:?}", err))?; - } else { - error!("Failed to find swap chain"); - } - - // Reset framebuffer bindings as appropriate. - framebuffer_rebinding_info.apply(&self.device, &data.ctx, &data.gl); - debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR); - - let has_alpha = data - .state - .requested_flags - .contains(ContextAttributeFlags::ALPHA); - self.update_wr_image_for_context(context_id, size.to_i32(), has_alpha); - - Ok(()) - } - - /// Removes a WebGLContext and releases attached resources. - fn remove_webgl_context(&mut self, context_id: WebGLContextId) { - // Release webrender image keys. - if let Some(info) = self.cached_context_info.remove(&context_id) { - let mut txn = Transaction::new(); - txn.delete_image(info.image_key); - self.webrender_api.send_transaction(self.webrender_doc, txn) - } - - // We need to make the context current so its resources can be disposed of. - Self::make_current_if_needed( - &self.device, - context_id, - &self.contexts, - &mut self.bound_context_id, - ); - - #[cfg(feature = "webxr")] - { - // Destroy WebXR layers associated with this context - let webxr_context_id = webxr_api::ContextId::from(context_id); - let mut webxr_contexts = WebXRBridgeContexts { - contexts: &mut self.contexts, - bound_context_id: &mut self.bound_context_id, - }; - self.webxr_bridge.destroy_all_layers( - &mut self.device, - &mut webxr_contexts, - webxr_context_id, - ); - } - - // Release GL context. - let mut data = match self.contexts.remove(&context_id) { - Some(data) => data, - None => return, - }; - - // Destroy the swap chains - self.webrender_swap_chains - .destroy(context_id, &mut self.device, &mut data.ctx) - .unwrap(); - - // Destroy the context - self.device.destroy_context(&mut data.ctx).unwrap(); - - // Removing a GLContext may make the current bound context_id dirty. - self.bound_context_id = None; - } - - fn handle_swap_buffers( - &mut self, - context_ids: Vec<WebGLContextId>, - completed_sender: WebGLSender<u64>, - _sent_time: u64, - ) { - debug!("handle_swap_buffers()"); - for context_id in context_ids { - let data = Self::make_current_if_needed_mut( - &self.device, - context_id, - &mut self.contexts, - &mut self.bound_context_id, - ) - .expect("Where's the GL data?"); - - // Ensure there are no pending GL errors from other parts of the pipeline. - debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR); - - // Check to see if any of the current framebuffer bindings are the surface we're about - // to swap out. If so, we'll have to reset them after destroying the surface. - let framebuffer_rebinding_info = - FramebufferRebindingInfo::detect(&self.device, &data.ctx, &data.gl); - debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR); - - debug!("Getting swap chain for {:?}", context_id); - let swap_chain = self - .webrender_swap_chains - .get(context_id) - .expect("Where's the swap chain?"); - - debug!("Swapping {:?}", context_id); - swap_chain - .swap_buffers( - &mut self.device, - &mut data.ctx, - if data.attributes.preserve_drawing_buffer { - PreserveBuffer::Yes(&data.gl) - } else { - PreserveBuffer::No - }, - ) - .unwrap(); - debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR); - - if !data.attributes.preserve_drawing_buffer { - debug!("Clearing {:?}", context_id); - let alpha = data - .state - .requested_flags - .contains(ContextAttributeFlags::ALPHA); - let clear_color = [0.0, 0.0, 0.0, !alpha as i32 as f32]; - swap_chain - .clear_surface(&mut self.device, &mut data.ctx, &data.gl, clear_color) - .unwrap(); - debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR); - } - - // Rebind framebuffers as appropriate. - debug!("Rebinding {:?}", context_id); - framebuffer_rebinding_info.apply(&self.device, &data.ctx, &data.gl); - debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR); - - let SurfaceInfo { - size, - framebuffer_object, - id, - .. - } = self - .device - .context_surface_info(&data.ctx) - .unwrap() - .unwrap(); - debug!( - "... rebound framebuffer {:?}, new back buffer surface is {:?}", - framebuffer_object, id - ); - - let has_alpha = data - .state - .requested_flags - .contains(ContextAttributeFlags::ALPHA); - self.update_wr_image_for_context(context_id, size, has_alpha); - } - - #[allow(unused)] - let mut end_swap = 0; - completed_sender.send(end_swap).unwrap(); - } - - /// Which access mode to use - fn surface_access(&self) -> SurfaceAccess { - SurfaceAccess::GPUOnly - } - - /// Gets a reference to a Context for a given WebGLContextId and makes it current if required. - pub(crate) fn make_current_if_needed<'a>( - device: &Device, - context_id: WebGLContextId, - contexts: &'a FnvHashMap<WebGLContextId, GLContextData>, - bound_id: &mut Option<WebGLContextId>, - ) -> Option<&'a GLContextData> { - let data = contexts.get(&context_id); - - if let Some(data) = data { - if Some(context_id) != *bound_id { - device.make_context_current(&data.ctx).unwrap(); - *bound_id = Some(context_id); - } - } - - data - } - - /// Gets a mutable reference to a GLContextWrapper for a WebGLContextId and makes it current if required. - pub(crate) fn make_current_if_needed_mut<'a>( - device: &Device, - context_id: WebGLContextId, - contexts: &'a mut FnvHashMap<WebGLContextId, GLContextData>, - bound_id: &mut Option<WebGLContextId>, - ) -> Option<&'a mut GLContextData> { - let data = contexts.get_mut(&context_id); - - if let Some(ref data) = data { - if Some(context_id) != *bound_id { - device.make_context_current(&data.ctx).unwrap(); - *bound_id = Some(context_id); - } - } - - data - } - - /// Creates a `webrender_api::ImageKey` that uses shared textures. - fn create_wr_external_image( - webrender_api: &mut RenderApi, - webrender_doc: DocumentId, - size: Size2D<i32>, - alpha: bool, - context_id: WebGLContextId, - image_buffer_kind: ImageBufferKind, - ) -> ImageKey { - let descriptor = Self::image_descriptor(size, alpha); - let data = Self::external_image_data(context_id, image_buffer_kind); - - let image_key = webrender_api.generate_image_key(); - let mut txn = Transaction::new(); - txn.add_image(image_key, descriptor, data, None); - webrender_api.send_transaction(webrender_doc, txn); - - image_key - } - - /// Tell WebRender to invalidate any cached tiles for a given `WebGLContextId` - /// when the underlying surface has changed e.g due to resize or buffer swap - fn update_wr_image_for_context( - &mut self, - context_id: WebGLContextId, - size: Size2D<i32>, - has_alpha: bool, - ) { - let info = self.cached_context_info.get(&context_id).unwrap(); - let image_buffer_kind = current_wr_image_buffer_kind(&self.device); - - let descriptor = Self::image_descriptor(size, has_alpha); - let image_data = Self::external_image_data(context_id, image_buffer_kind); - - let mut txn = Transaction::new(); - txn.update_image(info.image_key, descriptor, image_data, &DirtyRect::All); - self.webrender_api.send_transaction(self.webrender_doc, txn); - } - - /// Helper function to create a `ImageDescriptor`. - fn image_descriptor(size: Size2D<i32>, alpha: bool) -> ImageDescriptor { - let mut flags = ImageDescriptorFlags::empty(); - flags.set(ImageDescriptorFlags::IS_OPAQUE, !alpha); - ImageDescriptor { - size: DeviceIntSize::new(size.width, size.height), - stride: None, - format: ImageFormat::BGRA8, - offset: 0, - flags, - } - } - - /// Helper function to create a `ImageData::External` instance. - fn external_image_data( - context_id: WebGLContextId, - image_buffer_kind: ImageBufferKind, - ) -> ImageData { - let data = ExternalImageData { - id: ExternalImageId(context_id.0), - channel_index: 0, - image_type: ExternalImageType::TextureHandle(image_buffer_kind), - normalized_uvs: false, - }; - ImageData::External(data) - } - - /// Gets the GLSL Version supported by a GLContext. - fn get_glsl_version(gl: &Gl) -> WebGLSLVersion { - let version = unsafe { gl.get_parameter_string(gl::SHADING_LANGUAGE_VERSION) }; - // Fomat used by SHADING_LANGUAGE_VERSION query : major.minor[.release] [vendor info] - let mut values = version.split(&['.', ' '][..]); - let major = values - .next() - .and_then(|v| v.parse::<u32>().ok()) - .unwrap_or(1); - let minor = values - .next() - .and_then(|v| v.parse::<u32>().ok()) - .unwrap_or(20); - - WebGLSLVersion { major, minor } - } -} - -/// Helper struct to store cached WebGLContext information. -struct WebGLContextInfo { - /// Currently used WebRender image key. - image_key: ImageKey, -} - -// TODO(pcwalton): Add `GL_TEXTURE_EXTERNAL_OES`? -fn current_wr_image_buffer_kind(device: &Device) -> ImageBufferKind { - match device.surface_gl_texture_target() { - gl::TEXTURE_RECTANGLE => ImageBufferKind::TextureRect, - _ => ImageBufferKind::Texture2D, - } -} - -/// WebGL Commands Implementation -pub struct WebGLImpl; - -impl WebGLImpl { - pub fn apply( - device: &Device, - ctx: &Context, - gl: &Gl, - state: &mut GLState, - attributes: &GLContextAttributes, - command: WebGLCommand, - _backtrace: WebGLCommandBacktrace, - ) { - debug!("WebGLImpl::apply({:?})", command); - - // Ensure there are no pending GL errors from other parts of the pipeline. - debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR); - - match command { - WebGLCommand::GetContextAttributes(ref sender) => sender.send(*attributes).unwrap(), - WebGLCommand::ActiveTexture(target) => unsafe { gl.active_texture(target) }, - WebGLCommand::AttachShader(program_id, shader_id) => unsafe { - gl.attach_shader(program_id.glow(), shader_id.glow()) - }, - WebGLCommand::DetachShader(program_id, shader_id) => unsafe { - gl.detach_shader(program_id.glow(), shader_id.glow()) - }, - WebGLCommand::BindAttribLocation(program_id, index, ref name) => unsafe { - gl.bind_attrib_location(program_id.glow(), index, &to_name_in_compiled_shader(name)) - }, - WebGLCommand::BlendColor(r, g, b, a) => unsafe { gl.blend_color(r, g, b, a) }, - WebGLCommand::BlendEquation(mode) => unsafe { gl.blend_equation(mode) }, - WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha) => unsafe { - gl.blend_equation_separate(mode_rgb, mode_alpha) - }, - WebGLCommand::BlendFunc(src, dest) => unsafe { gl.blend_func(src, dest) }, - WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha) => unsafe { - gl.blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha) - }, - WebGLCommand::BufferData(buffer_type, ref receiver, usage) => unsafe { - gl.buffer_data_u8_slice(buffer_type, &receiver.recv().unwrap(), usage) - }, - WebGLCommand::BufferSubData(buffer_type, offset, ref receiver) => unsafe { - gl.buffer_sub_data_u8_slice(buffer_type, offset as i32, &receiver.recv().unwrap()) - }, - WebGLCommand::CopyBufferSubData(src, dst, src_offset, dst_offset, size) => { - unsafe { - gl.copy_buffer_sub_data( - src, - dst, - src_offset as i32, - dst_offset as i32, - size as i32, - ) - }; - }, - WebGLCommand::GetBufferSubData(buffer_type, offset, length, ref sender) => unsafe { - let ptr = gl.map_buffer_range( - buffer_type, - offset as i32, - length as i32, - gl::MAP_READ_BIT, - ); - let data: &[u8] = slice::from_raw_parts(ptr as _, length); - sender.send(data).unwrap(); - gl.unmap_buffer(buffer_type); - }, - WebGLCommand::Clear(mask) => { - unsafe { gl.clear(mask) }; - }, - WebGLCommand::ClearColor(r, g, b, a) => { - state.clear_color = (r, g, b, a); - unsafe { gl.clear_color(r, g, b, a) }; - }, - WebGLCommand::ClearDepth(depth) => { - let value = depth.clamp(0., 1.) as f64; - state.depth_clear_value = value; - unsafe { gl.clear_depth(value) } - }, - WebGLCommand::ClearStencil(stencil) => { - state.stencil_clear_value = stencil; - unsafe { gl.clear_stencil(stencil) }; - }, - WebGLCommand::ColorMask(r, g, b, a) => { - state.color_write_mask = [r, g, b, a]; - state.restore_alpha_invariant(gl); - }, - WebGLCommand::CopyTexImage2D( - target, - level, - internal_format, - x, - y, - width, - height, - border, - ) => unsafe { - gl.copy_tex_image_2d(target, level, internal_format, x, y, width, height, border) - }, - WebGLCommand::CopyTexSubImage2D( - target, - level, - xoffset, - yoffset, - x, - y, - width, - height, - ) => unsafe { - gl.copy_tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height) - }, - WebGLCommand::CullFace(mode) => unsafe { gl.cull_face(mode) }, - WebGLCommand::DepthFunc(func) => unsafe { gl.depth_func(func) }, - WebGLCommand::DepthMask(flag) => { - state.depth_write_mask = flag; - state.restore_depth_invariant(gl); - }, - WebGLCommand::DepthRange(near, far) => unsafe { - gl.depth_range(near.clamp(0., 1.) as f64, far.clamp(0., 1.) as f64) - }, - WebGLCommand::Disable(cap) => match cap { - gl::SCISSOR_TEST => { - state.scissor_test_enabled = false; - state.restore_scissor_invariant(gl); - }, - gl::DEPTH_TEST => { - state.depth_test_enabled = false; - state.restore_depth_invariant(gl); - }, - gl::STENCIL_TEST => { - state.stencil_test_enabled = false; - state.restore_stencil_invariant(gl); - }, - _ => unsafe { gl.disable(cap) }, - }, - WebGLCommand::Enable(cap) => match cap { - gl::SCISSOR_TEST => { - state.scissor_test_enabled = true; - state.restore_scissor_invariant(gl); - }, - gl::DEPTH_TEST => { - state.depth_test_enabled = true; - state.restore_depth_invariant(gl); - }, - gl::STENCIL_TEST => { - state.stencil_test_enabled = true; - state.restore_stencil_invariant(gl); - }, - _ => unsafe { gl.enable(cap) }, - }, - WebGLCommand::FramebufferRenderbuffer(target, attachment, renderbuffertarget, rb) => { - let attach = |attachment| unsafe { - gl.framebuffer_renderbuffer( - target, - attachment, - renderbuffertarget, - rb.map(WebGLRenderbufferId::glow), - ) - }; - if attachment == gl::DEPTH_STENCIL_ATTACHMENT { - attach(gl::DEPTH_ATTACHMENT); - attach(gl::STENCIL_ATTACHMENT); - } else { - attach(attachment); - } - }, - WebGLCommand::FramebufferTexture2D(target, attachment, textarget, texture, level) => { - let attach = |attachment| unsafe { - gl.framebuffer_texture_2d( - target, - attachment, - textarget, - texture.map(WebGLTextureId::glow), - level, - ) - }; - if attachment == gl::DEPTH_STENCIL_ATTACHMENT { - attach(gl::DEPTH_ATTACHMENT); - attach(gl::STENCIL_ATTACHMENT); - } else { - attach(attachment) - } - }, - WebGLCommand::FrontFace(mode) => unsafe { gl.front_face(mode) }, - WebGLCommand::DisableVertexAttribArray(attrib_id) => unsafe { - gl.disable_vertex_attrib_array(attrib_id) - }, - WebGLCommand::EnableVertexAttribArray(attrib_id) => unsafe { - gl.enable_vertex_attrib_array(attrib_id) - }, - WebGLCommand::Hint(name, val) => unsafe { gl.hint(name, val) }, - WebGLCommand::LineWidth(width) => { - unsafe { gl.line_width(width) }; - // In OpenGL Core Profile >3.2, any non-1.0 value will generate INVALID_VALUE. - if width != 1.0 { - let _ = unsafe { gl.get_error() }; - } - }, - WebGLCommand::PixelStorei(name, val) => unsafe { gl.pixel_store_i32(name, val) }, - WebGLCommand::PolygonOffset(factor, units) => unsafe { - gl.polygon_offset(factor, units) - }, - WebGLCommand::ReadPixels(rect, format, pixel_type, ref sender) => { - let len = bytes_per_type(pixel_type) * - components_per_format(format) * - rect.size.area() as usize; - let mut pixels = vec![0; len]; - unsafe { - // We don't want any alignment padding on pixel rows. - gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1); - gl.read_pixels( - rect.origin.x as i32, - rect.origin.y as i32, - rect.size.width as i32, - rect.size.height as i32, - format, - pixel_type, - glow::PixelPackData::Slice(Some(&mut pixels)), - ) - }; - let alpha_mode = match (attributes.alpha, attributes.premultiplied_alpha) { - (true, premultiplied) => snapshot::AlphaMode::Transparent { premultiplied }, - (false, _) => snapshot::AlphaMode::Opaque, - }; - sender - .send((IpcSharedMemory::from_bytes(&pixels), alpha_mode)) - .unwrap(); - }, - WebGLCommand::ReadPixelsPP(rect, format, pixel_type, offset) => unsafe { - gl.read_pixels( - rect.origin.x, - rect.origin.y, - rect.size.width, - rect.size.height, - format, - pixel_type, - glow::PixelPackData::BufferOffset(offset as u32), - ); - }, - WebGLCommand::RenderbufferStorage(target, format, width, height) => unsafe { - gl.renderbuffer_storage(target, format, width, height) - }, - WebGLCommand::RenderbufferStorageMultisample( - target, - samples, - format, - width, - height, - ) => unsafe { - gl.renderbuffer_storage_multisample(target, samples, format, width, height) - }, - WebGLCommand::SampleCoverage(value, invert) => unsafe { - gl.sample_coverage(value, invert) - }, - WebGLCommand::Scissor(x, y, width, height) => { - // FIXME(nox): Kinda unfortunate that some u32 values could - // end up as negative numbers here, but I don't even think - // that can happen in the real world. - unsafe { gl.scissor(x, y, width as i32, height as i32) }; - }, - WebGLCommand::StencilFunc(func, ref_, mask) => unsafe { - gl.stencil_func(func, ref_, mask) - }, - WebGLCommand::StencilFuncSeparate(face, func, ref_, mask) => unsafe { - gl.stencil_func_separate(face, func, ref_, mask) - }, - WebGLCommand::StencilMask(mask) => { - state.stencil_write_mask = (mask, mask); - state.restore_stencil_invariant(gl); - }, - WebGLCommand::StencilMaskSeparate(face, mask) => { - if face == gl::FRONT { - state.stencil_write_mask.0 = mask; - } else { - state.stencil_write_mask.1 = mask; - } - state.restore_stencil_invariant(gl); - }, - WebGLCommand::StencilOp(fail, zfail, zpass) => unsafe { - gl.stencil_op(fail, zfail, zpass) - }, - WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass) => unsafe { - gl.stencil_op_separate(face, fail, zfail, zpass) - }, - WebGLCommand::GetRenderbufferParameter(target, pname, ref chan) => { - Self::get_renderbuffer_parameter(gl, target, pname, chan) - }, - WebGLCommand::CreateTransformFeedback(ref sender) => { - let value = unsafe { gl.create_transform_feedback() }.ok(); - sender - .send(value.map(|ntf| ntf.0.get()).unwrap_or_default()) - .unwrap() - }, - WebGLCommand::DeleteTransformFeedback(id) => { - if let Some(tf) = NonZeroU32::new(id) { - unsafe { gl.delete_transform_feedback(NativeTransformFeedback(tf)) }; - } - }, - WebGLCommand::IsTransformFeedback(id, ref sender) => { - let value = NonZeroU32::new(id) - .map(|id| unsafe { gl.is_transform_feedback(NativeTransformFeedback(id)) }) - .unwrap_or_default(); - sender.send(value).unwrap() - }, - WebGLCommand::BindTransformFeedback(target, id) => { - unsafe { - gl.bind_transform_feedback( - target, - NonZeroU32::new(id).map(NativeTransformFeedback), - ) - }; - }, - WebGLCommand::BeginTransformFeedback(mode) => { - unsafe { gl.begin_transform_feedback(mode) }; - }, - WebGLCommand::EndTransformFeedback() => { - unsafe { gl.end_transform_feedback() }; - }, - WebGLCommand::PauseTransformFeedback() => { - unsafe { gl.pause_transform_feedback() }; - }, - WebGLCommand::ResumeTransformFeedback() => { - unsafe { gl.resume_transform_feedback() }; - }, - WebGLCommand::GetTransformFeedbackVarying(program, index, ref sender) => { - let ActiveTransformFeedback { size, tftype, name } = - unsafe { gl.get_transform_feedback_varying(program.glow(), index) }.unwrap(); - // We need to split, because the name starts with '_u' prefix. - let name = from_name_in_compiled_shader(&name); - sender.send((size, tftype, name)).unwrap(); - }, - WebGLCommand::TransformFeedbackVaryings(program, ref varyings, buffer_mode) => { - let varyings: Vec<String> = varyings - .iter() - .map(|varying| to_name_in_compiled_shader(varying)) - .collect(); - let varyings_refs: Vec<&str> = varyings.iter().map(String::as_ref).collect(); - unsafe { - gl.transform_feedback_varyings( - program.glow(), - varyings_refs.as_slice(), - buffer_mode, - ) - }; - }, - WebGLCommand::GetFramebufferAttachmentParameter( - target, - attachment, - pname, - ref chan, - ) => Self::get_framebuffer_attachment_parameter(gl, target, attachment, pname, chan), - WebGLCommand::GetShaderPrecisionFormat(shader_type, precision_type, ref chan) => { - Self::shader_precision_format(gl, shader_type, precision_type, chan) - }, - WebGLCommand::GetExtensions(ref chan) => Self::get_extensions(gl, chan), - WebGLCommand::GetFragDataLocation(program_id, ref name, ref sender) => { - let location = unsafe { - gl.get_frag_data_location(program_id.glow(), &to_name_in_compiled_shader(name)) - }; - sender.send(location).unwrap(); - }, - WebGLCommand::GetUniformLocation(program_id, ref name, ref chan) => { - Self::uniform_location(gl, program_id, name, chan) - }, - WebGLCommand::GetShaderInfoLog(shader_id, ref chan) => { - Self::shader_info_log(gl, shader_id, chan) - }, - WebGLCommand::GetProgramInfoLog(program_id, ref chan) => { - Self::program_info_log(gl, program_id, chan) - }, - WebGLCommand::CompileShader(shader_id, ref source) => { - Self::compile_shader(gl, shader_id, source) - }, - WebGLCommand::CreateBuffer(ref chan) => Self::create_buffer(gl, chan), - WebGLCommand::CreateFramebuffer(ref chan) => Self::create_framebuffer(gl, chan), - WebGLCommand::CreateRenderbuffer(ref chan) => Self::create_renderbuffer(gl, chan), - WebGLCommand::CreateTexture(ref chan) => Self::create_texture(gl, chan), - WebGLCommand::CreateProgram(ref chan) => Self::create_program(gl, chan), - WebGLCommand::CreateShader(shader_type, ref chan) => { - Self::create_shader(gl, shader_type, chan) - }, - WebGLCommand::DeleteBuffer(id) => unsafe { gl.delete_buffer(id.glow()) }, - WebGLCommand::DeleteFramebuffer(id) => unsafe { gl.delete_framebuffer(id.glow()) }, - WebGLCommand::DeleteRenderbuffer(id) => unsafe { gl.delete_renderbuffer(id.glow()) }, - WebGLCommand::DeleteTexture(id) => unsafe { gl.delete_texture(id.glow()) }, - WebGLCommand::DeleteProgram(id) => unsafe { gl.delete_program(id.glow()) }, - WebGLCommand::DeleteShader(id) => unsafe { gl.delete_shader(id.glow()) }, - WebGLCommand::BindBuffer(target, id) => unsafe { - gl.bind_buffer(target, id.map(WebGLBufferId::glow)) - }, - WebGLCommand::BindFramebuffer(target, request) => { - Self::bind_framebuffer(gl, target, request, ctx, device, state) - }, - WebGLCommand::BindRenderbuffer(target, id) => unsafe { - gl.bind_renderbuffer(target, id.map(WebGLRenderbufferId::glow)) - }, - WebGLCommand::BindTexture(target, id) => unsafe { - gl.bind_texture(target, id.map(WebGLTextureId::glow)) - }, - WebGLCommand::BlitFrameBuffer( - src_x0, - src_y0, - src_x1, - src_y1, - dst_x0, - dst_y0, - dst_x1, - dst_y1, - mask, - filter, - ) => unsafe { - gl.blit_framebuffer( - src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter, - ); - }, - WebGLCommand::Uniform1f(uniform_id, v) => unsafe { - gl.uniform_1_f32(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform1fv(uniform_id, ref v) => unsafe { - gl.uniform_1_f32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform1i(uniform_id, v) => unsafe { - gl.uniform_1_i32(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform1iv(uniform_id, ref v) => unsafe { - gl.uniform_1_i32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform1ui(uniform_id, v) => unsafe { - gl.uniform_1_u32(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform1uiv(uniform_id, ref v) => unsafe { - gl.uniform_1_u32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform2f(uniform_id, x, y) => unsafe { - gl.uniform_2_f32(native_uniform_location(uniform_id).as_ref(), x, y) - }, - WebGLCommand::Uniform2fv(uniform_id, ref v) => unsafe { - gl.uniform_2_f32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform2i(uniform_id, x, y) => unsafe { - gl.uniform_2_i32(native_uniform_location(uniform_id).as_ref(), x, y) - }, - WebGLCommand::Uniform2iv(uniform_id, ref v) => unsafe { - gl.uniform_2_i32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform2ui(uniform_id, x, y) => unsafe { - gl.uniform_2_u32(native_uniform_location(uniform_id).as_ref(), x, y) - }, - WebGLCommand::Uniform2uiv(uniform_id, ref v) => unsafe { - gl.uniform_2_u32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform3f(uniform_id, x, y, z) => unsafe { - gl.uniform_3_f32(native_uniform_location(uniform_id).as_ref(), x, y, z) - }, - WebGLCommand::Uniform3fv(uniform_id, ref v) => unsafe { - gl.uniform_3_f32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform3i(uniform_id, x, y, z) => unsafe { - gl.uniform_3_i32(native_uniform_location(uniform_id).as_ref(), x, y, z) - }, - WebGLCommand::Uniform3iv(uniform_id, ref v) => unsafe { - gl.uniform_3_i32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform3ui(uniform_id, x, y, z) => unsafe { - gl.uniform_3_u32(native_uniform_location(uniform_id).as_ref(), x, y, z) - }, - WebGLCommand::Uniform3uiv(uniform_id, ref v) => unsafe { - gl.uniform_3_u32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform4f(uniform_id, x, y, z, w) => unsafe { - gl.uniform_4_f32(native_uniform_location(uniform_id).as_ref(), x, y, z, w) - }, - WebGLCommand::Uniform4fv(uniform_id, ref v) => unsafe { - gl.uniform_4_f32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform4i(uniform_id, x, y, z, w) => unsafe { - gl.uniform_4_i32(native_uniform_location(uniform_id).as_ref(), x, y, z, w) - }, - WebGLCommand::Uniform4iv(uniform_id, ref v) => unsafe { - gl.uniform_4_i32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform4ui(uniform_id, x, y, z, w) => unsafe { - gl.uniform_4_u32(native_uniform_location(uniform_id).as_ref(), x, y, z, w) - }, - WebGLCommand::Uniform4uiv(uniform_id, ref v) => unsafe { - gl.uniform_4_u32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::UniformMatrix2fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_2_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix3fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_3_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix4fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_4_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix3x2fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_3x2_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix4x2fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_4x2_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix2x3fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_2x3_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix4x3fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_4x3_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix2x4fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_2x4_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix3x4fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_3x4_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::ValidateProgram(program_id) => unsafe { - gl.validate_program(program_id.glow()) - }, - WebGLCommand::VertexAttrib(attrib_id, x, y, z, w) => unsafe { - gl.vertex_attrib_4_f32(attrib_id, x, y, z, w) - }, - WebGLCommand::VertexAttribI(attrib_id, x, y, z, w) => unsafe { - gl.vertex_attrib_4_i32(attrib_id, x, y, z, w) - }, - WebGLCommand::VertexAttribU(attrib_id, x, y, z, w) => unsafe { - gl.vertex_attrib_4_u32(attrib_id, x, y, z, w) - }, - WebGLCommand::VertexAttribPointer2f(attrib_id, size, normalized, stride, offset) => unsafe { - gl.vertex_attrib_pointer_f32( - attrib_id, - size, - gl::FLOAT, - normalized, - stride, - offset as _, - ) - }, - WebGLCommand::VertexAttribPointer( - attrib_id, - size, - data_type, - normalized, - stride, - offset, - ) => unsafe { - gl.vertex_attrib_pointer_f32( - attrib_id, - size, - data_type, - normalized, - stride, - offset as _, - ) - }, - WebGLCommand::SetViewport(x, y, width, height) => unsafe { - gl.viewport(x, y, width, height) - }, - WebGLCommand::TexImage2D { - target, - level, - internal_format, - size, - format, - data_type, - effective_data_type, - unpacking_alignment, - alpha_treatment, - y_axis_treatment, - pixel_format, - ref data, - } => { - let pixels = prepare_pixels( - internal_format, - data_type, - size, - unpacking_alignment, - alpha_treatment, - y_axis_treatment, - pixel_format, - Cow::Borrowed(data), - ); - - unsafe { - gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32); - gl.tex_image_2d( - target, - level as i32, - internal_format.as_gl_constant() as i32, - size.width as i32, - size.height as i32, - 0, - format.as_gl_constant(), - effective_data_type, - PixelUnpackData::Slice(Some(&pixels)), - ); - } - }, - WebGLCommand::TexImage2DPBO { - target, - level, - internal_format, - size, - format, - effective_data_type, - unpacking_alignment, - offset, - } => unsafe { - gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32); - - gl.tex_image_2d( - target, - level as i32, - internal_format.as_gl_constant() as i32, - size.width as i32, - size.height as i32, - 0, - format.as_gl_constant(), - effective_data_type, - PixelUnpackData::BufferOffset(offset as u32), - ); - }, - WebGLCommand::TexSubImage2D { - target, - level, - xoffset, - yoffset, - size, - format, - data_type, - effective_data_type, - unpacking_alignment, - alpha_treatment, - y_axis_treatment, - pixel_format, - ref data, - } => { - let pixels = prepare_pixels( - format, - data_type, - size, - unpacking_alignment, - alpha_treatment, - y_axis_treatment, - pixel_format, - Cow::Borrowed(data), - ); - - unsafe { - gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32); - gl.tex_sub_image_2d( - target, - level as i32, - xoffset, - yoffset, - size.width as i32, - size.height as i32, - format.as_gl_constant(), - effective_data_type, - glow::PixelUnpackData::Slice(Some(&pixels)), - ); - } - }, - WebGLCommand::CompressedTexImage2D { - target, - level, - internal_format, - size, - ref data, - } => unsafe { - gl.compressed_tex_image_2d( - target, - level as i32, - internal_format as i32, - size.width as i32, - size.height as i32, - 0, - data.len() as i32, - data, - ) - }, - WebGLCommand::CompressedTexSubImage2D { - target, - level, - xoffset, - yoffset, - size, - format, - ref data, - } => { - unsafe { - gl.compressed_tex_sub_image_2d( - target, - level, - xoffset, - yoffset, - size.width as i32, - size.height as i32, - format, - glow::CompressedPixelUnpackData::Slice(data), - ) - }; - }, - WebGLCommand::TexStorage2D(target, levels, internal_format, width, height) => unsafe { - gl.tex_storage_2d( - target, - levels as i32, - internal_format.as_gl_constant(), - width as i32, - height as i32, - ) - }, - WebGLCommand::TexStorage3D(target, levels, internal_format, width, height, depth) => unsafe { - gl.tex_storage_3d( - target, - levels as i32, - internal_format.as_gl_constant(), - width as i32, - height as i32, - depth as i32, - ) - }, - WebGLCommand::DrawingBufferWidth(ref sender) => { - let size = device - .context_surface_info(ctx) - .unwrap() - .expect("Where's the front buffer?") - .size; - sender.send(size.width).unwrap() - }, - WebGLCommand::DrawingBufferHeight(ref sender) => { - let size = device - .context_surface_info(ctx) - .unwrap() - .expect("Where's the front buffer?") - .size; - sender.send(size.height).unwrap() - }, - WebGLCommand::Finish(ref sender) => Self::finish(gl, sender), - WebGLCommand::Flush => unsafe { gl.flush() }, - WebGLCommand::GenerateMipmap(target) => unsafe { gl.generate_mipmap(target) }, - WebGLCommand::CreateVertexArray(ref chan) => { - let id = Self::create_vertex_array(gl); - let _ = chan.send(id); - }, - WebGLCommand::DeleteVertexArray(id) => { - Self::delete_vertex_array(gl, id); - }, - WebGLCommand::BindVertexArray(id) => { - let id = id.map(WebGLVertexArrayId::glow).or(state.default_vao); - Self::bind_vertex_array(gl, id); - }, - WebGLCommand::GetParameterBool(param, ref sender) => { - let value = match param { - webgl::ParameterBool::DepthWritemask => state.depth_write_mask, - _ => unsafe { gl.get_parameter_bool(param as u32) }, - }; - sender.send(value).unwrap() - }, - WebGLCommand::FenceSync(ref sender) => { - let value = unsafe { gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0).unwrap() }; - sender.send(WebGLSyncId::from_glow(value)).unwrap(); - }, - WebGLCommand::IsSync(sync_id, ref sender) => { - let value = unsafe { gl.is_sync(sync_id.glow()) }; - sender.send(value).unwrap(); - }, - WebGLCommand::ClientWaitSync(sync_id, flags, timeout, ref sender) => { - let value = unsafe { gl.client_wait_sync(sync_id.glow(), flags, timeout as _) }; - sender.send(value).unwrap(); - }, - WebGLCommand::WaitSync(sync_id, flags, timeout) => { - unsafe { gl.wait_sync(sync_id.glow(), flags, timeout as u64) }; - }, - WebGLCommand::GetSyncParameter(sync_id, param, ref sender) => { - let value = unsafe { gl.get_sync_parameter_i32(sync_id.glow(), param) }; - sender.send(value as u32).unwrap(); - }, - WebGLCommand::DeleteSync(sync_id) => { - unsafe { gl.delete_sync(sync_id.glow()) }; - }, - WebGLCommand::GetParameterBool4(param, ref sender) => { - let value = match param { - webgl::ParameterBool4::ColorWritemask => state.color_write_mask, - }; - sender.send(value).unwrap() - }, - WebGLCommand::GetParameterInt(param, ref sender) => { - let value = match param { - webgl::ParameterInt::AlphaBits if state.fake_no_alpha() => 0, - webgl::ParameterInt::DepthBits if state.fake_no_depth() => 0, - webgl::ParameterInt::StencilBits if state.fake_no_stencil() => 0, - webgl::ParameterInt::StencilWritemask => state.stencil_write_mask.0 as i32, - webgl::ParameterInt::StencilBackWritemask => state.stencil_write_mask.1 as i32, - _ => unsafe { gl.get_parameter_i32(param as u32) }, - }; - sender.send(value).unwrap() - }, - WebGLCommand::GetParameterInt2(param, ref sender) => { - let mut value = [0; 2]; - unsafe { - gl.get_parameter_i32_slice(param as u32, &mut value); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetParameterInt4(param, ref sender) => { - let mut value = [0; 4]; - unsafe { - gl.get_parameter_i32_slice(param as u32, &mut value); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetParameterFloat(param, ref sender) => { - let mut value = [0.]; - unsafe { - gl.get_parameter_f32_slice(param as u32, &mut value); - } - sender.send(value[0]).unwrap() - }, - WebGLCommand::GetParameterFloat2(param, ref sender) => { - let mut value = [0.; 2]; - unsafe { - gl.get_parameter_f32_slice(param as u32, &mut value); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetParameterFloat4(param, ref sender) => { - let mut value = [0.; 4]; - unsafe { - gl.get_parameter_f32_slice(param as u32, &mut value); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetProgramValidateStatus(program, ref sender) => sender - .send(unsafe { gl.get_program_validate_status(program.glow()) }) - .unwrap(), - WebGLCommand::GetProgramActiveUniforms(program, ref sender) => sender - .send(unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORMS) }) - .unwrap(), - WebGLCommand::GetCurrentVertexAttrib(index, ref sender) => { - let mut value = [0.; 4]; - unsafe { - gl.get_vertex_attrib_parameter_f32_slice( - index, - gl::CURRENT_VERTEX_ATTRIB, - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetTexParameterFloat(target, param, ref sender) => { - sender - .send(unsafe { gl.get_tex_parameter_f32(target, param as u32) }) - .unwrap(); - }, - WebGLCommand::GetTexParameterInt(target, param, ref sender) => { - sender - .send(unsafe { gl.get_tex_parameter_i32(target, param as u32) }) - .unwrap(); - }, - WebGLCommand::GetTexParameterBool(target, param, ref sender) => { - sender - .send(unsafe { gl.get_tex_parameter_i32(target, param as u32) } != 0) - .unwrap(); - }, - WebGLCommand::GetInternalFormatIntVec(target, internal_format, param, ref sender) => { - match param { - InternalFormatIntVec::Samples => { - let mut count = [0; 1]; - unsafe { - gl.get_internal_format_i32_slice( - target, - internal_format, - gl::NUM_SAMPLE_COUNTS, - &mut count, - ) - }; - assert!(count[0] >= 0); - - let mut values = vec![0; count[0] as usize]; - unsafe { - gl.get_internal_format_i32_slice( - target, - internal_format, - param as u32, - &mut values, - ) - }; - sender.send(values).unwrap() - }, - } - }, - WebGLCommand::TexParameteri(target, param, value) => unsafe { - gl.tex_parameter_i32(target, param, value) - }, - WebGLCommand::TexParameterf(target, param, value) => unsafe { - gl.tex_parameter_f32(target, param, value) - }, - WebGLCommand::LinkProgram(program_id, ref sender) => { - return sender.send(Self::link_program(gl, program_id)).unwrap(); - }, - WebGLCommand::UseProgram(program_id) => unsafe { - gl.use_program(program_id.map(|p| p.glow())) - }, - WebGLCommand::DrawArrays { mode, first, count } => unsafe { - gl.draw_arrays(mode, first, count) - }, - WebGLCommand::DrawArraysInstanced { - mode, - first, - count, - primcount, - } => unsafe { gl.draw_arrays_instanced(mode, first, count, primcount) }, - WebGLCommand::DrawElements { - mode, - count, - type_, - offset, - } => unsafe { gl.draw_elements(mode, count, type_, offset as _) }, - WebGLCommand::DrawElementsInstanced { - mode, - count, - type_, - offset, - primcount, - } => unsafe { - gl.draw_elements_instanced(mode, count, type_, offset as i32, primcount) - }, - WebGLCommand::VertexAttribDivisor { index, divisor } => unsafe { - gl.vertex_attrib_divisor(index, divisor) - }, - WebGLCommand::GetUniformBool(program_id, loc, ref sender) => { - let mut value = [0]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value[0] != 0).unwrap(); - }, - WebGLCommand::GetUniformBool2(program_id, loc, ref sender) => { - let mut value = [0; 2]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - let value = [value[0] != 0, value[1] != 0]; - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformBool3(program_id, loc, ref sender) => { - let mut value = [0; 3]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - let value = [value[0] != 0, value[1] != 0, value[2] != 0]; - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformBool4(program_id, loc, ref sender) => { - let mut value = [0; 4]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - let value = [value[0] != 0, value[1] != 0, value[2] != 0, value[3] != 0]; - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformInt(program_id, loc, ref sender) => { - let mut value = [0]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value[0]).unwrap(); - }, - WebGLCommand::GetUniformInt2(program_id, loc, ref sender) => { - let mut value = [0; 2]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformInt3(program_id, loc, ref sender) => { - let mut value = [0; 3]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformInt4(program_id, loc, ref sender) => { - let mut value = [0; 4]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformUint(program_id, loc, ref sender) => { - let mut value = [0]; - unsafe { - gl.get_uniform_u32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value[0]).unwrap(); - }, - WebGLCommand::GetUniformUint2(program_id, loc, ref sender) => { - let mut value = [0; 2]; - unsafe { - gl.get_uniform_u32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformUint3(program_id, loc, ref sender) => { - let mut value = [0; 3]; - unsafe { - gl.get_uniform_u32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformUint4(program_id, loc, ref sender) => { - let mut value = [0; 4]; - unsafe { - gl.get_uniform_u32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformFloat(program_id, loc, ref sender) => { - let mut value = [0.]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value[0]).unwrap(); - }, - WebGLCommand::GetUniformFloat2(program_id, loc, ref sender) => { - let mut value = [0.; 2]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformFloat3(program_id, loc, ref sender) => { - let mut value = [0.; 3]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformFloat4(program_id, loc, ref sender) => { - let mut value = [0.; 4]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformFloat9(program_id, loc, ref sender) => { - let mut value = [0.; 9]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformFloat16(program_id, loc, ref sender) => { - let mut value = [0.; 16]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformFloat2x3(program_id, loc, ref sender) => { - let mut value = [0.; 2 * 3]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetUniformFloat2x4(program_id, loc, ref sender) => { - let mut value = [0.; 2 * 4]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetUniformFloat3x2(program_id, loc, ref sender) => { - let mut value = [0.; 3 * 2]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetUniformFloat3x4(program_id, loc, ref sender) => { - let mut value = [0.; 3 * 4]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetUniformFloat4x2(program_id, loc, ref sender) => { - let mut value = [0.; 4 * 2]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetUniformFloat4x3(program_id, loc, ref sender) => { - let mut value = [0.; 4 * 3]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetUniformBlockIndex(program_id, ref name, ref sender) => { - let name = to_name_in_compiled_shader(name); - let index = unsafe { gl.get_uniform_block_index(program_id.glow(), &name) }; - // TODO(#34300): use Option<u32> - sender.send(index.unwrap_or(gl::INVALID_INDEX)).unwrap(); - }, - WebGLCommand::GetUniformIndices(program_id, ref names, ref sender) => { - let names = names - .iter() - .map(|name| to_name_in_compiled_shader(name)) - .collect::<Vec<_>>(); - let name_strs = names.iter().map(|name| name.as_str()).collect::<Vec<_>>(); - let indices = unsafe { - gl.get_uniform_indices(program_id.glow(), &name_strs) - .iter() - .map(|index| index.unwrap_or(gl::INVALID_INDEX)) - .collect() - }; - sender.send(indices).unwrap(); - }, - WebGLCommand::GetActiveUniforms(program_id, ref indices, pname, ref sender) => { - let results = - unsafe { gl.get_active_uniforms_parameter(program_id.glow(), indices, pname) }; - sender.send(results).unwrap(); - }, - WebGLCommand::GetActiveUniformBlockName(program_id, block_idx, ref sender) => { - let name = - unsafe { gl.get_active_uniform_block_name(program_id.glow(), block_idx) }; - sender.send(name).unwrap(); - }, - WebGLCommand::GetActiveUniformBlockParameter( - program_id, - block_idx, - pname, - ref sender, - ) => { - let size = match pname { - gl::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES => unsafe { - gl.get_active_uniform_block_parameter_i32( - program_id.glow(), - block_idx, - gl::UNIFORM_BLOCK_ACTIVE_UNIFORMS, - ) as usize - }, - _ => 1, - }; - let mut result = vec![0; size]; - unsafe { - gl.get_active_uniform_block_parameter_i32_slice( - program_id.glow(), - block_idx, - pname, - &mut result, - ) - }; - sender.send(result).unwrap(); - }, - WebGLCommand::UniformBlockBinding(program_id, block_idx, block_binding) => unsafe { - gl.uniform_block_binding(program_id.glow(), block_idx, block_binding) - }, - WebGLCommand::InitializeFramebuffer { - color, - depth, - stencil, - } => Self::initialize_framebuffer(gl, state, color, depth, stencil), - WebGLCommand::BeginQuery(target, query_id) => { - unsafe { gl.begin_query(target, query_id.glow()) }; - }, - WebGLCommand::EndQuery(target) => { - unsafe { gl.end_query(target) }; - }, - WebGLCommand::DeleteQuery(query_id) => { - unsafe { gl.delete_query(query_id.glow()) }; - }, - WebGLCommand::GenerateQuery(ref sender) => { - // TODO(#34300): use Option<WebGLQueryId> - let id = unsafe { gl.create_query().unwrap() }; - sender.send(WebGLQueryId::from_glow(id)).unwrap() - }, - WebGLCommand::GetQueryState(ref sender, query_id, pname) => { - let value = unsafe { gl.get_query_parameter_u32(query_id.glow(), pname) }; - sender.send(value).unwrap() - }, - WebGLCommand::GenerateSampler(ref sender) => { - let id = unsafe { gl.create_sampler().unwrap() }; - sender.send(WebGLSamplerId::from_glow(id)).unwrap() - }, - WebGLCommand::DeleteSampler(sampler_id) => { - unsafe { gl.delete_sampler(sampler_id.glow()) }; - }, - WebGLCommand::BindSampler(unit, sampler_id) => { - unsafe { gl.bind_sampler(unit, Some(sampler_id.glow())) }; - }, - WebGLCommand::SetSamplerParameterInt(sampler_id, pname, value) => { - unsafe { gl.sampler_parameter_i32(sampler_id.glow(), pname, value) }; - }, - WebGLCommand::SetSamplerParameterFloat(sampler_id, pname, value) => { - unsafe { gl.sampler_parameter_f32(sampler_id.glow(), pname, value) }; - }, - WebGLCommand::GetSamplerParameterInt(sampler_id, pname, ref sender) => { - let value = unsafe { gl.get_sampler_parameter_i32(sampler_id.glow(), pname) }; - sender.send(value).unwrap(); - }, - WebGLCommand::GetSamplerParameterFloat(sampler_id, pname, ref sender) => { - let value = unsafe { gl.get_sampler_parameter_f32(sampler_id.glow(), pname) }; - sender.send(value).unwrap(); - }, - WebGLCommand::BindBufferBase(target, index, id) => { - // https://searchfox.org/mozilla-central/rev/13b081a62d3f3e3e3120f95564529257b0bf451c/dom/canvas/WebGLContextBuffers.cpp#208-210 - // BindBufferBase/Range will fail (on some drivers) if the buffer name has - // never been bound. (GenBuffers makes a name, but BindBuffer initializes - // that name as a real buffer object) - let id = id.map(WebGLBufferId::glow); - unsafe { - gl.bind_buffer(target, id); - gl.bind_buffer(target, None); - gl.bind_buffer_base(target, index, id); - } - }, - WebGLCommand::BindBufferRange(target, index, id, offset, size) => { - // https://searchfox.org/mozilla-central/rev/13b081a62d3f3e3e3120f95564529257b0bf451c/dom/canvas/WebGLContextBuffers.cpp#208-210 - // BindBufferBase/Range will fail (on some drivers) if the buffer name has - // never been bound. (GenBuffers makes a name, but BindBuffer initializes - // that name as a real buffer object) - let id = id.map(WebGLBufferId::glow); - unsafe { - gl.bind_buffer(target, id); - gl.bind_buffer(target, None); - gl.bind_buffer_range(target, index, id, offset as i32, size as i32); - } - }, - WebGLCommand::ClearBufferfv(buffer, draw_buffer, ref value) => unsafe { - gl.clear_buffer_f32_slice(buffer, draw_buffer as u32, value) - }, - WebGLCommand::ClearBufferiv(buffer, draw_buffer, ref value) => unsafe { - gl.clear_buffer_i32_slice(buffer, draw_buffer as u32, value) - }, - WebGLCommand::ClearBufferuiv(buffer, draw_buffer, ref value) => unsafe { - gl.clear_buffer_u32_slice(buffer, draw_buffer as u32, value) - }, - WebGLCommand::ClearBufferfi(buffer, draw_buffer, depth, stencil) => unsafe { - gl.clear_buffer_depth_stencil(buffer, draw_buffer as u32, depth, stencil) - }, - WebGLCommand::InvalidateFramebuffer(target, ref attachments) => unsafe { - gl.invalidate_framebuffer(target, attachments) - }, - WebGLCommand::InvalidateSubFramebuffer(target, ref attachments, x, y, w, h) => unsafe { - gl.invalidate_sub_framebuffer(target, attachments, x, y, w, h) - }, - WebGLCommand::FramebufferTextureLayer(target, attachment, tex_id, level, layer) => { - let tex_id = tex_id.map(WebGLTextureId::glow); - let attach = |attachment| unsafe { - gl.framebuffer_texture_layer(target, attachment, tex_id, level, layer) - }; - - if attachment == gl::DEPTH_STENCIL_ATTACHMENT { - attach(gl::DEPTH_ATTACHMENT); - attach(gl::STENCIL_ATTACHMENT); - } else { - attach(attachment) - } - }, - WebGLCommand::ReadBuffer(buffer) => unsafe { gl.read_buffer(buffer) }, - WebGLCommand::DrawBuffers(ref buffers) => unsafe { gl.draw_buffers(buffers) }, - } - - // If debug asertions are enabled, then check the error state. - #[cfg(debug_assertions)] - { - let error = unsafe { gl.get_error() }; - if error != gl::NO_ERROR { - error!("Last GL operation failed: {:?}", command); - if error == gl::INVALID_FRAMEBUFFER_OPERATION { - let framebuffer_bindings = - unsafe { gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING) }; - debug!( - "(thread {:?}) Current draw framebuffer binding: {:?}", - ::std::thread::current().id(), - framebuffer_bindings - ); - } - #[cfg(feature = "webgl_backtrace")] - { - error!("Backtrace from failed WebGL API:\n{}", _backtrace.backtrace); - if let Some(backtrace) = _backtrace.js_backtrace { - error!("JS backtrace from failed WebGL API:\n{}", backtrace); - } - } - // TODO(servo#30568) revert to panic!() once underlying bug is fixed - log::warn!( - "debug assertion failed! Unexpected WebGL error: 0x{:x} ({}) [{:?}]", - error, - error, - command - ); - } - } - } - - fn initialize_framebuffer(gl: &Gl, state: &GLState, color: bool, depth: bool, stencil: bool) { - let bits = [ - (color, gl::COLOR_BUFFER_BIT), - (depth, gl::DEPTH_BUFFER_BIT), - (stencil, gl::STENCIL_BUFFER_BIT), - ] - .iter() - .fold(0, |bits, &(enabled, bit)| { - bits | if enabled { bit } else { 0 } - }); - - unsafe { - gl.disable(gl::SCISSOR_TEST); - gl.color_mask(true, true, true, true); - gl.clear_color(0., 0., 0., 0.); - gl.depth_mask(true); - gl.clear_depth(1.); - gl.stencil_mask_separate(gl::FRONT, 0xFFFFFFFF); - gl.stencil_mask_separate(gl::BACK, 0xFFFFFFFF); - gl.clear_stencil(0); - gl.clear(bits); - } - - state.restore_invariant(gl); - } - - fn link_program(gl: &Gl, program: WebGLProgramId) -> ProgramLinkInfo { - unsafe { gl.link_program(program.glow()) }; - let linked = unsafe { gl.get_program_link_status(program.glow()) }; - if !linked { - return ProgramLinkInfo { - linked: false, - active_attribs: vec![].into(), - active_uniforms: vec![].into(), - active_uniform_blocks: vec![].into(), - transform_feedback_length: Default::default(), - transform_feedback_mode: Default::default(), - }; - } - let num_active_attribs = - unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_ATTRIBUTES) }; - let active_attribs = (0..num_active_attribs as u32) - .map(|i| { - let active_attribute = - unsafe { gl.get_active_attribute(program.glow(), i) }.unwrap(); - let name = &active_attribute.name; - let location = if name.starts_with("gl_") { - None - } else { - unsafe { gl.get_attrib_location(program.glow(), name) } - }; - ActiveAttribInfo { - name: from_name_in_compiled_shader(name), - size: active_attribute.size, - type_: active_attribute.atype, - location, - } - }) - .collect::<Vec<_>>() - .into(); - - let num_active_uniforms = - unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORMS) }; - let active_uniforms = (0..num_active_uniforms as u32) - .map(|i| { - let active_uniform = unsafe { gl.get_active_uniform(program.glow(), i) }.unwrap(); - let is_array = active_uniform.name.ends_with("[0]"); - let active_uniform_name = active_uniform - .name - .strip_suffix("[0]") - .unwrap_or_else(|| &active_uniform.name); - ActiveUniformInfo { - base_name: from_name_in_compiled_shader(active_uniform_name).into(), - size: if is_array { - Some(active_uniform.size) - } else { - None - }, - type_: active_uniform.utype, - bind_index: None, - } - }) - .collect::<Vec<_>>() - .into(); - - let num_active_uniform_blocks = - unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORM_BLOCKS) }; - let active_uniform_blocks = (0..num_active_uniform_blocks as u32) - .map(|i| { - let name = unsafe { gl.get_active_uniform_block_name(program.glow(), i) }; - let size = unsafe { - gl.get_active_uniform_block_parameter_i32( - program.glow(), - i, - gl::UNIFORM_BLOCK_DATA_SIZE, - ) - }; - ActiveUniformBlockInfo { name, size } - }) - .collect::<Vec<_>>() - .into(); - - let transform_feedback_length = unsafe { - gl.get_program_parameter_i32(program.glow(), gl::TRANSFORM_FEEDBACK_VARYINGS) - }; - let transform_feedback_mode = unsafe { - gl.get_program_parameter_i32(program.glow(), gl::TRANSFORM_FEEDBACK_BUFFER_MODE) - }; - - ProgramLinkInfo { - linked: true, - active_attribs, - active_uniforms, - active_uniform_blocks, - transform_feedback_length, - transform_feedback_mode, - } - } - - fn finish(gl: &Gl, chan: &WebGLSender<()>) { - unsafe { gl.finish() }; - chan.send(()).unwrap(); - } - - fn shader_precision_format( - gl: &Gl, - shader_type: u32, - precision_type: u32, - chan: &WebGLSender<(i32, i32, i32)>, - ) { - let ShaderPrecisionFormat { - range_min, - range_max, - precision, - } = unsafe { - gl.get_shader_precision_format(shader_type, precision_type) - .unwrap_or_else(|| { - ShaderPrecisionFormat::common_desktop_hardware( - precision_type, - gl.version().is_embedded, - ) - }) - }; - chan.send((range_min, range_max, precision)).unwrap(); - } - - fn get_extensions(gl: &Gl, chan: &WebGLSender<String>) { - let mut ext_count = [0]; - unsafe { - gl.get_parameter_i32_slice(gl::NUM_EXTENSIONS, &mut ext_count); - } - // Fall back to the depricated extensions API if that fails - if unsafe { gl.get_error() } != gl::NO_ERROR { - chan.send(unsafe { gl.get_parameter_string(gl::EXTENSIONS) }) - .unwrap(); - return; - } - let ext_count = ext_count[0] as usize; - let mut extensions = Vec::with_capacity(ext_count); - for idx in 0..ext_count { - extensions.push(unsafe { gl.get_parameter_indexed_string(gl::EXTENSIONS, idx as u32) }) - } - let extensions = extensions.join(" "); - chan.send(extensions).unwrap(); - } - - // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 - fn get_framebuffer_attachment_parameter( - gl: &Gl, - target: u32, - attachment: u32, - pname: u32, - chan: &WebGLSender<i32>, - ) { - let parameter = - unsafe { gl.get_framebuffer_attachment_parameter_i32(target, attachment, pname) }; - chan.send(parameter).unwrap(); - } - - // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7 - fn get_renderbuffer_parameter(gl: &Gl, target: u32, pname: u32, chan: &WebGLSender<i32>) { - let parameter = unsafe { gl.get_renderbuffer_parameter_i32(target, pname) }; - chan.send(parameter).unwrap(); - } - - fn uniform_location(gl: &Gl, program_id: WebGLProgramId, name: &str, chan: &WebGLSender<i32>) { - let location = unsafe { - gl.get_uniform_location(program_id.glow(), &to_name_in_compiled_shader(name)) - }; - // (#34300): replace this with WebGLUniformId - chan.send(location.map(|l| l.0).unwrap_or_default() as i32) - .unwrap(); - } - - fn shader_info_log(gl: &Gl, shader_id: WebGLShaderId, chan: &WebGLSender<String>) { - let log = unsafe { gl.get_shader_info_log(shader_id.glow()) }; - chan.send(log).unwrap(); - } - - fn program_info_log(gl: &Gl, program_id: WebGLProgramId, chan: &WebGLSender<String>) { - let log = unsafe { gl.get_program_info_log(program_id.glow()) }; - chan.send(log).unwrap(); - } - - fn create_buffer(gl: &Gl, chan: &WebGLSender<Option<WebGLBufferId>>) { - let buffer = unsafe { gl.create_buffer() } - .ok() - .map(WebGLBufferId::from_glow); - chan.send(buffer).unwrap(); - } - - fn create_framebuffer(gl: &Gl, chan: &WebGLSender<Option<WebGLFramebufferId>>) { - let framebuffer = unsafe { gl.create_framebuffer() } - .ok() - .map(WebGLFramebufferId::from_glow); - chan.send(framebuffer).unwrap(); - } - - fn create_renderbuffer(gl: &Gl, chan: &WebGLSender<Option<WebGLRenderbufferId>>) { - let renderbuffer = unsafe { gl.create_renderbuffer() } - .ok() - .map(WebGLRenderbufferId::from_glow); - chan.send(renderbuffer).unwrap(); - } - - fn create_texture(gl: &Gl, chan: &WebGLSender<Option<WebGLTextureId>>) { - let texture = unsafe { gl.create_texture() } - .ok() - .map(WebGLTextureId::from_glow); - chan.send(texture).unwrap(); - } - - fn create_program(gl: &Gl, chan: &WebGLSender<Option<WebGLProgramId>>) { - let program = unsafe { gl.create_program() } - .ok() - .map(WebGLProgramId::from_glow); - chan.send(program).unwrap(); - } - - fn create_shader(gl: &Gl, shader_type: u32, chan: &WebGLSender<Option<WebGLShaderId>>) { - let shader = unsafe { gl.create_shader(shader_type) } - .ok() - .map(WebGLShaderId::from_glow); - chan.send(shader).unwrap(); - } - - fn create_vertex_array(gl: &Gl) -> Option<WebGLVertexArrayId> { - let vao = unsafe { gl.create_vertex_array() } - .ok() - .map(WebGLVertexArrayId::from_glow); - if vao.is_none() { - let code = unsafe { gl.get_error() }; - warn!("Failed to create vertex array with error code {:x}", code); - } - vao - } - - fn bind_vertex_array(gl: &Gl, vao: Option<NativeVertexArray>) { - unsafe { gl.bind_vertex_array(vao) } - debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR); - } - - fn delete_vertex_array(gl: &Gl, vao: WebGLVertexArrayId) { - unsafe { gl.delete_vertex_array(vao.glow()) }; - debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR); - } - - #[inline] - fn bind_framebuffer( - gl: &Gl, - target: u32, - request: WebGLFramebufferBindingRequest, - ctx: &Context, - device: &Device, - state: &mut GLState, - ) { - let id = match request { - WebGLFramebufferBindingRequest::Explicit(id) => Some(id.glow()), - WebGLFramebufferBindingRequest::Default => { - device - .context_surface_info(ctx) - .unwrap() - .expect("No surface attached!") - .framebuffer_object - }, - }; - - debug!("WebGLImpl::bind_framebuffer: {:?}", id); - unsafe { gl.bind_framebuffer(target, id) }; - - if (target == gl::FRAMEBUFFER) || (target == gl::DRAW_FRAMEBUFFER) { - state.drawing_to_default_framebuffer = - request == WebGLFramebufferBindingRequest::Default; - state.restore_invariant(gl); - } - } - - #[inline] - fn compile_shader(gl: &Gl, shader_id: WebGLShaderId, source: &str) { - unsafe { - gl.shader_source(shader_id.glow(), source); - gl.compile_shader(shader_id.glow()); - } - } -} - -/// ANGLE adds a `_u` prefix to variable names: -/// -/// <https://chromium.googlesource.com/angle/angle/+/855d964bd0d05f6b2cb303f625506cf53d37e94f> -/// -/// To avoid hard-coding this we would need to use the `sh::GetAttributes` and `sh::GetUniforms` -/// API to look up the `x.name` and `x.mappedName` members. -const ANGLE_NAME_PREFIX: &str = "_u"; - -/// Adds `_u` prefix to variable names -fn to_name_in_compiled_shader(s: &str) -> String { - map_dot_separated(s, |s, mapped| { - mapped.push_str(ANGLE_NAME_PREFIX); - mapped.push_str(s); - }) -} - -/// Removes `_u` prefix from variable names -fn from_name_in_compiled_shader(s: &str) -> String { - map_dot_separated(s, |s, mapped| { - mapped.push_str(if let Some(stripped) = s.strip_prefix(ANGLE_NAME_PREFIX) { - stripped - } else { - s - }) - }) -} - -fn map_dot_separated<F: Fn(&str, &mut String)>(s: &str, f: F) -> String { - let mut iter = s.split('.'); - let mut mapped = String::new(); - f(iter.next().unwrap(), &mut mapped); - for s in iter { - mapped.push('.'); - f(s, &mut mapped); - } - mapped -} - -#[allow(clippy::too_many_arguments)] -fn prepare_pixels( - internal_format: TexFormat, - data_type: TexDataType, - size: Size2D<u32>, - unpacking_alignment: u32, - alpha_treatment: Option<AlphaTreatment>, - y_axis_treatment: YAxisTreatment, - pixel_format: Option<PixelFormat>, - mut pixels: Cow<[u8]>, -) -> Cow<[u8]> { - match alpha_treatment { - Some(AlphaTreatment::Premultiply) => { - if let Some(pixel_format) = pixel_format { - match pixel_format { - PixelFormat::BGRA8 | PixelFormat::RGBA8 => {}, - _ => unimplemented!("unsupported pixel format ({:?})", pixel_format), - } - premultiply_inplace(TexFormat::RGBA, TexDataType::UnsignedByte, pixels.to_mut()); - } else { - premultiply_inplace(internal_format, data_type, pixels.to_mut()); - } - }, - Some(AlphaTreatment::Unmultiply) => { - assert!(pixel_format.is_some()); - unmultiply_inplace::<false>(pixels.to_mut()); - }, - None => {}, - } - - if let Some(pixel_format) = pixel_format { - pixels = image_to_tex_image_data( - pixel_format, - internal_format, - data_type, - pixels.into_owned(), - ) - .into(); - } - - if y_axis_treatment == YAxisTreatment::Flipped { - // FINISHME: Consider doing premultiply and flip in a single mutable Vec. - pixels = flip_pixels_y( - internal_format, - data_type, - size.width as usize, - size.height as usize, - unpacking_alignment as usize, - pixels.into_owned(), - ) - .into(); - } - - pixels -} - -/// Translates an image in rgba8 (red in the first byte) format to -/// the format that was requested of TexImage. -fn image_to_tex_image_data( - pixel_format: PixelFormat, - format: TexFormat, - data_type: TexDataType, - mut pixels: Vec<u8>, -) -> Vec<u8> { - // hint for vector allocation sizing. - let pixel_count = pixels.len() / 4; - - match pixel_format { - PixelFormat::BGRA8 => pixels::rgba8_byte_swap_colors_inplace(&mut pixels), - PixelFormat::RGBA8 => {}, - _ => unimplemented!("unsupported pixel format ({:?})", pixel_format), - } - - match (format, data_type) { - (TexFormat::RGBA, TexDataType::UnsignedByte) | - (TexFormat::RGBA8, TexDataType::UnsignedByte) => pixels, - (TexFormat::RGB, TexDataType::UnsignedByte) | - (TexFormat::RGB8, TexDataType::UnsignedByte) => { - for i in 0..pixel_count { - let rgb = { - let rgb = &pixels[i * 4..i * 4 + 3]; - [rgb[0], rgb[1], rgb[2]] - }; - pixels[i * 3..i * 3 + 3].copy_from_slice(&rgb); - } - pixels.truncate(pixel_count * 3); - pixels - }, - (TexFormat::Alpha, TexDataType::UnsignedByte) => { - for i in 0..pixel_count { - let p = pixels[i * 4 + 3]; - pixels[i] = p; - } - pixels.truncate(pixel_count); - pixels - }, - (TexFormat::Luminance, TexDataType::UnsignedByte) => { - for i in 0..pixel_count { - let p = pixels[i * 4]; - pixels[i] = p; - } - pixels.truncate(pixel_count); - pixels - }, - (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => { - for i in 0..pixel_count { - let (lum, a) = { - let rgba = &pixels[i * 4..i * 4 + 4]; - (rgba[0], rgba[3]) - }; - pixels[i * 2] = lum; - pixels[i * 2 + 1] = a; - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::RGBA, TexDataType::UnsignedShort4444) => { - for i in 0..pixel_count { - let p = { - let rgba = &pixels[i * 4..i * 4 + 4]; - ((rgba[0] as u16 & 0xf0) << 8) | - ((rgba[1] as u16 & 0xf0) << 4) | - (rgba[2] as u16 & 0xf0) | - ((rgba[3] as u16 & 0xf0) >> 4) - }; - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::RGBA, TexDataType::UnsignedShort5551) => { - for i in 0..pixel_count { - let p = { - let rgba = &pixels[i * 4..i * 4 + 4]; - ((rgba[0] as u16 & 0xf8) << 8) | - ((rgba[1] as u16 & 0xf8) << 3) | - ((rgba[2] as u16 & 0xf8) >> 2) | - ((rgba[3] as u16) >> 7) - }; - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::RGB, TexDataType::UnsignedShort565) => { - for i in 0..pixel_count { - let p = { - let rgb = &pixels[i * 4..i * 4 + 3]; - ((rgb[0] as u16 & 0xf8) << 8) | - ((rgb[1] as u16 & 0xfc) << 3) | - ((rgb[2] as u16 & 0xf8) >> 3) - }; - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::RGBA, TexDataType::Float) | (TexFormat::RGBA32f, TexDataType::Float) => { - let mut rgbaf32 = Vec::<u8>::with_capacity(pixel_count * 16); - for rgba8 in pixels.chunks(4) { - rgbaf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap(); - rgbaf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap(); - rgbaf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap(); - rgbaf32.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap(); - } - rgbaf32 - }, - - (TexFormat::RGB, TexDataType::Float) | (TexFormat::RGB32f, TexDataType::Float) => { - let mut rgbf32 = Vec::<u8>::with_capacity(pixel_count * 12); - for rgba8 in pixels.chunks(4) { - rgbf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap(); - rgbf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap(); - rgbf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap(); - } - rgbf32 - }, - - (TexFormat::Alpha, TexDataType::Float) | (TexFormat::Alpha32f, TexDataType::Float) => { - for rgba8 in pixels.chunks_mut(4) { - let p = rgba8[3] as f32; - NativeEndian::write_f32(rgba8, p); - } - pixels - }, - - (TexFormat::Luminance, TexDataType::Float) | - (TexFormat::Luminance32f, TexDataType::Float) => { - for rgba8 in pixels.chunks_mut(4) { - let p = rgba8[0] as f32; - NativeEndian::write_f32(rgba8, p); - } - pixels - }, - - (TexFormat::LuminanceAlpha, TexDataType::Float) | - (TexFormat::LuminanceAlpha32f, TexDataType::Float) => { - let mut data = Vec::<u8>::with_capacity(pixel_count * 8); - for rgba8 in pixels.chunks(4) { - data.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap(); - data.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap(); - } - data - }, - - (TexFormat::RGBA, TexDataType::HalfFloat) | - (TexFormat::RGBA16f, TexDataType::HalfFloat) => { - let mut rgbaf16 = Vec::<u8>::with_capacity(pixel_count * 8); - for rgba8 in pixels.chunks(4) { - rgbaf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).to_bits()) - .unwrap(); - rgbaf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).to_bits()) - .unwrap(); - rgbaf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).to_bits()) - .unwrap(); - rgbaf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).to_bits()) - .unwrap(); - } - rgbaf16 - }, - - (TexFormat::RGB, TexDataType::HalfFloat) | (TexFormat::RGB16f, TexDataType::HalfFloat) => { - let mut rgbf16 = Vec::<u8>::with_capacity(pixel_count * 6); - for rgba8 in pixels.chunks(4) { - rgbf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).to_bits()) - .unwrap(); - rgbf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).to_bits()) - .unwrap(); - rgbf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).to_bits()) - .unwrap(); - } - rgbf16 - }, - (TexFormat::Alpha, TexDataType::HalfFloat) | - (TexFormat::Alpha16f, TexDataType::HalfFloat) => { - for i in 0..pixel_count { - let p = f16::from_f32(pixels[i * 4 + 3] as f32).to_bits(); - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::Luminance, TexDataType::HalfFloat) | - (TexFormat::Luminance16f, TexDataType::HalfFloat) => { - for i in 0..pixel_count { - let p = f16::from_f32(pixels[i * 4] as f32).to_bits(); - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::LuminanceAlpha, TexDataType::HalfFloat) | - (TexFormat::LuminanceAlpha16f, TexDataType::HalfFloat) => { - for rgba8 in pixels.chunks_mut(4) { - let lum = f16::from_f32(rgba8[0] as f32).to_bits(); - let a = f16::from_f32(rgba8[3] as f32).to_bits(); - NativeEndian::write_u16(&mut rgba8[0..2], lum); - NativeEndian::write_u16(&mut rgba8[2..4], a); - } - pixels - }, - - // Validation should have ensured that we only hit the - // above cases, but we haven't turned the (format, type) - // into an enum yet so there's a default case here. - _ => unreachable!("Unsupported formats {:?} {:?}", format, data_type), - } -} - -fn premultiply_inplace(format: TexFormat, data_type: TexDataType, pixels: &mut [u8]) { - match (format, data_type) { - (TexFormat::RGBA, TexDataType::UnsignedByte) => { - pixels::rgba8_premultiply_inplace(pixels); - }, - (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => { - for la in pixels.chunks_mut(2) { - la[0] = pixels::multiply_u8_color(la[0], la[1]); - } - }, - (TexFormat::RGBA, TexDataType::UnsignedShort5551) => { - for rgba in pixels.chunks_mut(2) { - if NativeEndian::read_u16(rgba) & 1 == 0 { - NativeEndian::write_u16(rgba, 0); - } - } - }, - (TexFormat::RGBA, TexDataType::UnsignedShort4444) => { - for rgba in pixels.chunks_mut(2) { - let pix = NativeEndian::read_u16(rgba); - let extend_to_8_bits = |val| (val | (val << 4)) as u8; - let r = extend_to_8_bits((pix >> 12) & 0x0f); - let g = extend_to_8_bits((pix >> 8) & 0x0f); - let b = extend_to_8_bits((pix >> 4) & 0x0f); - let a = extend_to_8_bits(pix & 0x0f); - NativeEndian::write_u16( - rgba, - (((pixels::multiply_u8_color(r, a) & 0xf0) as u16) << 8) | - (((pixels::multiply_u8_color(g, a) & 0xf0) as u16) << 4) | - ((pixels::multiply_u8_color(b, a) & 0xf0) as u16) | - ((a & 0x0f) as u16), - ); - } - }, - // Other formats don't have alpha, so return their data untouched. - _ => {}, - } -} - -/// Flips the pixels in the Vec on the Y axis. -fn flip_pixels_y( - internal_format: TexFormat, - data_type: TexDataType, - width: usize, - height: usize, - unpacking_alignment: usize, - pixels: Vec<u8>, -) -> Vec<u8> { - let cpp = (data_type.element_size() * internal_format.components() / - data_type.components_per_element()) as usize; - - let stride = (width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1); - - let mut flipped = Vec::<u8>::with_capacity(pixels.len()); - - for y in 0..height { - let flipped_y = height - 1 - y; - let start = flipped_y * stride; - - flipped.extend_from_slice(&pixels[start..(start + width * cpp)]); - flipped.extend(vec![0u8; stride - width * cpp]); - } - - flipped -} - -// Clamp a size to the current GL context's max viewport -fn clamp_viewport(gl: &Gl, size: Size2D<u32>) -> Size2D<u32> { - let mut max_viewport = [i32::MAX, i32::MAX]; - let mut max_renderbuffer = [i32::MAX]; - - unsafe { - gl.get_parameter_i32_slice(gl::MAX_VIEWPORT_DIMS, &mut max_viewport); - gl.get_parameter_i32_slice(gl::MAX_RENDERBUFFER_SIZE, &mut max_renderbuffer); - debug_assert_eq!(gl.get_error(), gl::NO_ERROR); - } - Size2D::new( - size.width - .min(max_viewport[0] as u32) - .min(max_renderbuffer[0] as u32) - .max(1), - size.height - .min(max_viewport[1] as u32) - .min(max_renderbuffer[0] as u32) - .max(1), - ) -} - -trait ToSurfmanVersion { - fn to_surfman_version(self, api_type: GlType) -> GLVersion; -} - -impl ToSurfmanVersion for WebGLVersion { - fn to_surfman_version(self, api_type: GlType) -> GLVersion { - if api_type == GlType::Gles { - return GLVersion::new(3, 0); - } - match self { - // We make use of GL_PACK_PIXEL_BUFFER, which needs at least GL2.1 - // We make use of compatibility mode, which needs at most GL3.0 - WebGLVersion::WebGL1 => GLVersion::new(2, 1), - // The WebGL2 conformance tests use std140 layout, which needs at GL3.1 - WebGLVersion::WebGL2 => GLVersion::new(3, 2), - } - } -} - -trait SurfmanContextAttributeFlagsConvert { - fn to_surfman_context_attribute_flags( - &self, - webgl_version: WebGLVersion, - api_type: GlType, - ) -> ContextAttributeFlags; -} - -impl SurfmanContextAttributeFlagsConvert for GLContextAttributes { - fn to_surfman_context_attribute_flags( - &self, - webgl_version: WebGLVersion, - api_type: GlType, - ) -> ContextAttributeFlags { - let mut flags = ContextAttributeFlags::empty(); - flags.set(ContextAttributeFlags::ALPHA, self.alpha); - flags.set(ContextAttributeFlags::DEPTH, self.depth); - flags.set(ContextAttributeFlags::STENCIL, self.stencil); - if (webgl_version == WebGLVersion::WebGL1) && (api_type == GlType::Gl) { - flags.set(ContextAttributeFlags::COMPATIBILITY_PROFILE, true); - } - flags - } -} - -bitflags! { - struct FramebufferRebindingFlags: u8 { - const REBIND_READ_FRAMEBUFFER = 0x1; - const REBIND_DRAW_FRAMEBUFFER = 0x2; - } -} - -struct FramebufferRebindingInfo { - flags: FramebufferRebindingFlags, - viewport: [GLint; 4], -} - -impl FramebufferRebindingInfo { - fn detect(device: &Device, context: &Context, gl: &Gl) -> FramebufferRebindingInfo { - unsafe { - let read_framebuffer = gl.get_parameter_framebuffer(gl::READ_FRAMEBUFFER_BINDING); - let draw_framebuffer = gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING); - - let context_surface_framebuffer = device - .context_surface_info(context) - .unwrap() - .unwrap() - .framebuffer_object; - - let mut flags = FramebufferRebindingFlags::empty(); - if context_surface_framebuffer == read_framebuffer { - flags.insert(FramebufferRebindingFlags::REBIND_READ_FRAMEBUFFER); - } - if context_surface_framebuffer == draw_framebuffer { - flags.insert(FramebufferRebindingFlags::REBIND_DRAW_FRAMEBUFFER); - } - - let mut viewport = [0; 4]; - gl.get_parameter_i32_slice(gl::VIEWPORT, &mut viewport); - - FramebufferRebindingInfo { flags, viewport } - } - } - - fn apply(self, device: &Device, context: &Context, gl: &Gl) { - if self.flags.is_empty() { - return; - } - - let context_surface_framebuffer = device - .context_surface_info(context) - .unwrap() - .unwrap() - .framebuffer_object; - if self - .flags - .contains(FramebufferRebindingFlags::REBIND_READ_FRAMEBUFFER) - { - unsafe { gl.bind_framebuffer(gl::READ_FRAMEBUFFER, context_surface_framebuffer) }; - } - if self - .flags - .contains(FramebufferRebindingFlags::REBIND_DRAW_FRAMEBUFFER) - { - unsafe { gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, context_surface_framebuffer) }; - } - - unsafe { - gl.viewport( - self.viewport[0], - self.viewport[1], - self.viewport[2], - self.viewport[3], - ) - }; - } -} diff --git a/components/canvas/webxr.rs b/components/canvas/webxr.rs deleted file mode 100644 index d43303e7393..00000000000 --- a/components/canvas/webxr.rs +++ /dev/null @@ -1,337 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::collections::HashMap; -use std::num::NonZeroU32; - -use canvas_traits::webgl::{ - WebGLContextId, WebGLMsg, WebGLSender, WebXRCommand, WebXRLayerManagerId, webgl_channel, -}; -use fnv::FnvHashMap; -use surfman::{Context, Device}; -use webxr::SurfmanGL as WebXRSurfman; -use webxr_api::{ - ContextId as WebXRContextId, Error as WebXRError, GLContexts as WebXRContexts, - GLTypes as WebXRTypes, LayerGrandManager as WebXRLayerGrandManager, - LayerGrandManagerAPI as WebXRLayerGrandManagerAPI, LayerId as WebXRLayerId, - LayerInit as WebXRLayerInit, LayerManager as WebXRLayerManager, - LayerManagerAPI as WebXRLayerManagerAPI, LayerManagerFactory as WebXRLayerManagerFactory, - SubImages as WebXRSubImages, -}; - -use crate::webgl_thread::{GLContextData, WebGLThread}; - -/// Bridge between WebGL and WebXR -pub(crate) struct WebXRBridge { - factory_receiver: crossbeam_channel::Receiver<WebXRLayerManagerFactory<WebXRSurfman>>, - managers: HashMap<WebXRLayerManagerId, Box<dyn WebXRLayerManagerAPI<WebXRSurfman>>>, - next_manager_id: NonZeroU32, -} - -impl WebXRBridge { - pub(crate) fn new(init: WebXRBridgeInit) -> WebXRBridge { - let WebXRBridgeInit { - factory_receiver, .. - } = init; - let managers = HashMap::new(); - let next_manager_id = NonZeroU32::MIN; - WebXRBridge { - factory_receiver, - managers, - next_manager_id, - } - } -} - -impl WebXRBridge { - #[allow(unsafe_code)] - pub(crate) fn create_layer_manager( - &mut self, - device: &mut Device, - contexts: &mut dyn WebXRContexts<WebXRSurfman>, - ) -> Result<WebXRLayerManagerId, WebXRError> { - let factory = self - .factory_receiver - .recv() - .map_err(|_| WebXRError::CommunicationError)?; - let manager = factory.build(device, contexts)?; - let manager_id = WebXRLayerManagerId::new(self.next_manager_id); - self.next_manager_id = self - .next_manager_id - .checked_add(1) - .expect("next_manager_id should not overflow"); - self.managers.insert(manager_id, manager); - Ok(manager_id) - } - - pub(crate) fn destroy_layer_manager(&mut self, manager_id: WebXRLayerManagerId) { - self.managers.remove(&manager_id); - } - - pub(crate) fn create_layer( - &mut self, - manager_id: WebXRLayerManagerId, - device: &mut Device, - contexts: &mut dyn WebXRContexts<WebXRSurfman>, - context_id: WebXRContextId, - layer_init: WebXRLayerInit, - ) -> Result<WebXRLayerId, WebXRError> { - let manager = self - .managers - .get_mut(&manager_id) - .ok_or(WebXRError::NoMatchingDevice)?; - manager.create_layer(device, contexts, context_id, layer_init) - } - - pub(crate) fn destroy_layer( - &mut self, - manager_id: WebXRLayerManagerId, - device: &mut Device, - contexts: &mut dyn WebXRContexts<WebXRSurfman>, - context_id: WebXRContextId, - layer_id: WebXRLayerId, - ) { - if let Some(manager) = self.managers.get_mut(&manager_id) { - manager.destroy_layer(device, contexts, context_id, layer_id); - } - } - - pub(crate) fn destroy_all_layers( - &mut self, - device: &mut Device, - contexts: &mut dyn WebXRContexts<WebXRSurfman>, - context_id: WebXRContextId, - ) { - for manager in self.managers.values_mut() { - #[allow(clippy::unnecessary_to_owned)] // Needs mutable borrow later in destroy - for (other_id, layer_id) in manager.layers().to_vec() { - if other_id == context_id { - manager.destroy_layer(device, contexts, context_id, layer_id); - } - } - } - } - - pub(crate) fn begin_frame( - &mut self, - manager_id: WebXRLayerManagerId, - device: &mut Device, - contexts: &mut dyn WebXRContexts<WebXRSurfman>, - layers: &[(WebXRContextId, WebXRLayerId)], - ) -> Result<Vec<WebXRSubImages>, WebXRError> { - let manager = self - .managers - .get_mut(&manager_id) - .ok_or(WebXRError::NoMatchingDevice)?; - manager.begin_frame(device, contexts, layers) - } - - pub(crate) fn end_frame( - &mut self, - manager_id: WebXRLayerManagerId, - device: &mut Device, - contexts: &mut dyn WebXRContexts<WebXRSurfman>, - layers: &[(WebXRContextId, WebXRLayerId)], - ) -> Result<(), WebXRError> { - let manager = self - .managers - .get_mut(&manager_id) - .ok_or(WebXRError::NoMatchingDevice)?; - manager.end_frame(device, contexts, layers) - } -} - -pub(crate) struct WebXRBridgeInit { - sender: WebGLSender<WebGLMsg>, - factory_receiver: crossbeam_channel::Receiver<WebXRLayerManagerFactory<WebXRSurfman>>, - factory_sender: crossbeam_channel::Sender<WebXRLayerManagerFactory<WebXRSurfman>>, -} - -impl WebXRBridgeInit { - pub(crate) fn new(sender: WebGLSender<WebGLMsg>) -> WebXRBridgeInit { - let (factory_sender, factory_receiver) = crossbeam_channel::unbounded(); - WebXRBridgeInit { - sender, - factory_sender, - factory_receiver, - } - } - - pub(crate) fn layer_grand_manager(&self) -> WebXRLayerGrandManager<WebXRSurfman> { - WebXRLayerGrandManager::new(WebXRBridgeGrandManager { - sender: self.sender.clone(), - factory_sender: self.factory_sender.clone(), - }) - } -} - -struct WebXRBridgeGrandManager { - sender: WebGLSender<WebGLMsg>, - // WebXR layer manager factories use generic trait objects under the - // hood, which aren't deserializable (even using typetag) - // so we can't send them over the regular webgl channel. - // Fortunately, the webgl thread runs in the same process as - // the webxr threads, so we can use a crossbeam channel to send - // factories. - factory_sender: crossbeam_channel::Sender<WebXRLayerManagerFactory<WebXRSurfman>>, -} - -impl WebXRLayerGrandManagerAPI<WebXRSurfman> for WebXRBridgeGrandManager { - fn create_layer_manager( - &self, - factory: WebXRLayerManagerFactory<WebXRSurfman>, - ) -> Result<WebXRLayerManager, WebXRError> { - let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?; - let _ = self.factory_sender.send(factory); - let _ = self - .sender - .send(WebGLMsg::WebXRCommand(WebXRCommand::CreateLayerManager( - sender, - ))); - let sender = self.sender.clone(); - let manager_id = receiver - .recv() - .map_err(|_| WebXRError::CommunicationError)??; - let layers = Vec::new(); - Ok(WebXRLayerManager::new(WebXRBridgeManager { - manager_id, - sender, - layers, - })) - } - - fn clone_layer_grand_manager(&self) -> WebXRLayerGrandManager<WebXRSurfman> { - WebXRLayerGrandManager::new(WebXRBridgeGrandManager { - sender: self.sender.clone(), - factory_sender: self.factory_sender.clone(), - }) - } -} - -struct WebXRBridgeManager { - sender: WebGLSender<WebGLMsg>, - manager_id: WebXRLayerManagerId, - layers: Vec<(WebXRContextId, WebXRLayerId)>, -} - -impl<GL: WebXRTypes> WebXRLayerManagerAPI<GL> for WebXRBridgeManager { - fn create_layer( - &mut self, - _: &mut GL::Device, - _: &mut dyn WebXRContexts<GL>, - context_id: WebXRContextId, - init: WebXRLayerInit, - ) -> Result<WebXRLayerId, WebXRError> { - let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?; - let _ = self - .sender - .send(WebGLMsg::WebXRCommand(WebXRCommand::CreateLayer( - self.manager_id, - context_id, - init, - sender, - ))); - let layer_id = receiver - .recv() - .map_err(|_| WebXRError::CommunicationError)??; - self.layers.push((context_id, layer_id)); - Ok(layer_id) - } - - fn destroy_layer( - &mut self, - _: &mut GL::Device, - _: &mut dyn WebXRContexts<GL>, - context_id: WebXRContextId, - layer_id: WebXRLayerId, - ) { - self.layers.retain(|&ids| ids != (context_id, layer_id)); - let _ = self - .sender - .send(WebGLMsg::WebXRCommand(WebXRCommand::DestroyLayer( - self.manager_id, - context_id, - layer_id, - ))); - } - - fn layers(&self) -> &[(WebXRContextId, WebXRLayerId)] { - &self.layers[..] - } - - fn begin_frame( - &mut self, - _: &mut GL::Device, - _: &mut dyn WebXRContexts<GL>, - layers: &[(WebXRContextId, WebXRLayerId)], - ) -> Result<Vec<WebXRSubImages>, WebXRError> { - let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?; - let _ = self - .sender - .send(WebGLMsg::WebXRCommand(WebXRCommand::BeginFrame( - self.manager_id, - layers.to_vec(), - sender, - ))); - receiver - .recv() - .map_err(|_| WebXRError::CommunicationError)? - } - - fn end_frame( - &mut self, - _: &mut GL::Device, - _: &mut dyn WebXRContexts<GL>, - layers: &[(WebXRContextId, WebXRLayerId)], - ) -> Result<(), WebXRError> { - let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?; - let _ = self - .sender - .send(WebGLMsg::WebXRCommand(WebXRCommand::EndFrame( - self.manager_id, - layers.to_vec(), - sender, - ))); - receiver - .recv() - .map_err(|_| WebXRError::CommunicationError)? - } -} - -impl Drop for WebXRBridgeManager { - fn drop(&mut self) { - let _ = self - .sender - .send(WebGLMsg::WebXRCommand(WebXRCommand::DestroyLayerManager( - self.manager_id, - ))); - } -} - -pub(crate) struct WebXRBridgeContexts<'a> { - pub(crate) contexts: &'a mut FnvHashMap<WebGLContextId, GLContextData>, - pub(crate) bound_context_id: &'a mut Option<WebGLContextId>, -} - -impl WebXRContexts<WebXRSurfman> for WebXRBridgeContexts<'_> { - fn context(&mut self, device: &Device, context_id: WebXRContextId) -> Option<&mut Context> { - let data = WebGLThread::make_current_if_needed_mut( - device, - WebGLContextId::from(context_id), - self.contexts, - self.bound_context_id, - )?; - Some(&mut data.ctx) - } - - fn bindings(&mut self, device: &Device, context_id: WebXRContextId) -> Option<&glow::Context> { - let data = WebGLThread::make_current_if_needed( - device, - WebGLContextId::from(context_id), - self.contexts, - self.bound_context_id, - )?; - Some(&data.gl) - } -} |