diff options
author | Alan Jeffrey <ajeffrey@mozilla.com> | 2020-01-09 17:28:46 -0600 |
---|---|---|
committer | Alan Jeffrey <ajeffrey@mozilla.com> | 2020-04-17 23:44:53 -0500 |
commit | 8bb1732258c44e6850618a8f2fbb2927bc01b090 (patch) | |
tree | e4483e94fd5fbceb15fe9e35e3d5a085d3b2b814 /components | |
parent | 9dbc6554f087ca3675104fb1bac45b0c442a0158 (diff) | |
download | servo-8bb1732258c44e6850618a8f2fbb2927bc01b090.tar.gz servo-8bb1732258c44e6850618a8f2fbb2927bc01b090.zip |
Update surfman to 0.2 and remove glutin
Diffstat (limited to 'components')
-rw-r--r-- | components/canvas/Cargo.toml | 3 | ||||
-rw-r--r-- | components/canvas/webgl_mode/inprocess.rs | 46 | ||||
-rw-r--r-- | components/canvas/webgl_thread.rs | 442 | ||||
-rw-r--r-- | components/canvas_traits/webgl.rs | 2 | ||||
-rw-r--r-- | components/compositing/Cargo.toml | 1 | ||||
-rw-r--r-- | components/compositing/compositor.rs | 66 | ||||
-rw-r--r-- | components/compositing/compositor_thread.rs | 4 | ||||
-rw-r--r-- | components/compositing/lib.rs | 1 | ||||
-rw-r--r-- | components/compositing/windowing.rs | 21 | ||||
-rw-r--r-- | components/config/prefs.rs | 6 | ||||
-rw-r--r-- | components/script/dom/webgl_extensions/extensions.rs | 22 | ||||
-rw-r--r-- | components/script/dom/webglrenderbuffer.rs | 2 | ||||
-rw-r--r-- | components/script/dom/webglshader.rs | 45 | ||||
-rw-r--r-- | components/servo/Cargo.toml | 3 | ||||
-rw-r--r-- | components/servo/lib.rs | 95 | ||||
-rw-r--r-- | components/webrender_surfman/Cargo.toml | 17 | ||||
-rw-r--r-- | components/webrender_surfman/lib.rs | 213 | ||||
-rw-r--r-- | components/webrender_traits/Cargo.toml | 1 | ||||
-rw-r--r-- | components/webrender_traits/lib.rs | 2 |
19 files changed, 738 insertions, 254 deletions
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 0917ad981a0..9dba07b7a15 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -38,9 +38,10 @@ servo_config = {path = "../config"} sparkle = "0.1.22" webrender = {git = "https://github.com/servo/webrender"} webrender_api = {git = "https://github.com/servo/webrender"} +webrender_surfman = {path = "../webrender_surfman"} webrender_traits = {path = "../webrender_traits"} webxr-api = {git = "https://github.com/servo/webxr", features = ["ipc"]} # NOTE: the sm-angle feature only enables angle on windows, not other platforms! -surfman = { version = "0.1", features = ["sm-angle", "sm-osmesa"] } +surfman = { version = "0.2", features = ["sm-angle","sm-angle-default"] } surfman-chains = "0.3" surfman-chains-api = "0.2" diff --git a/components/canvas/webgl_mode/inprocess.rs b/components/canvas/webgl_mode/inprocess.rs index a5707c29069..ff744481770 100644 --- a/components/canvas/webgl_mode/inprocess.rs +++ b/components/canvas/webgl_mode/inprocess.rs @@ -15,19 +15,19 @@ use std::collections::HashMap; use std::default::Default; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use surfman::platform::generic::universal::context::Context; -use surfman::platform::generic::universal::device::Device; -use surfman::platform::generic::universal::surface::SurfaceTexture; +use surfman::Device; use surfman::SurfaceInfo; +use surfman::SurfaceTexture; use surfman_chains::SwapChains; use surfman_chains_api::SwapChainAPI; use surfman_chains_api::SwapChainsAPI; +use webrender_surfman::WebrenderSurfman; use webrender_traits::{WebrenderExternalImageApi, WebrenderExternalImageRegistry}; use webxr_api::SwapChainId as WebXRSwapChainId; pub struct WebGLComm { pub webgl_threads: WebGLThreads, - pub webxr_swap_chains: SwapChains<WebXRSwapChainId>, + pub webxr_swap_chains: SwapChains<WebXRSwapChainId, Device>, pub webxr_surface_providers: SurfaceProviders, pub image_handler: Box<dyn WebrenderExternalImageApi>, pub output_handler: Option<Box<dyn webrender_api::OutputImageHandler>>, @@ -37,8 +37,7 @@ pub struct WebGLComm { impl WebGLComm { /// Creates a new `WebGLComm` object. pub fn new( - device: Device, - context: Context, + surfman: WebrenderSurfman, webrender_gl: Rc<dyn gleam::gl::Gl>, webrender_api_sender: webrender_api::RenderApiSender, external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, @@ -60,8 +59,8 @@ impl WebGLComm { webrender_swap_chains: webrender_swap_chains.clone(), webxr_swap_chains: webxr_swap_chains.clone(), webxr_surface_providers: webxr_surface_providers.clone(), - connection: device.connection(), - adapter: device.adapter(), + connection: surfman.connection(), + adapter: surfman.adapter(), api_type, runnable_receiver, }; @@ -72,7 +71,7 @@ impl WebGLComm { None }; - let external = WebGLExternalImages::new(device, context, webrender_swap_chains); + let external = WebGLExternalImages::new(surfman, webrender_swap_chains); WebGLThread::run_on_own_thread(init); @@ -89,23 +88,15 @@ impl WebGLComm { /// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads. struct WebGLExternalImages { - device: Device, - context: Context, - swap_chains: SwapChains<WebGLContextId>, + surfman: WebrenderSurfman, + swap_chains: SwapChains<WebGLContextId, Device>, locked_front_buffers: FnvHashMap<WebGLContextId, SurfaceTexture>, } -impl Drop for WebGLExternalImages { - fn drop(&mut self) { - let _ = self.device.destroy_context(&mut self.context); - } -} - impl WebGLExternalImages { - fn new(device: Device, context: Context, swap_chains: SwapChains<WebGLContextId>) -> Self { + fn new(surfman: WebrenderSurfman, swap_chains: SwapChains<WebGLContextId, Device>) -> Self { Self { - device, - context, + surfman, swap_chains, locked_front_buffers: FnvHashMap::default(), } @@ -119,13 +110,10 @@ impl WebGLExternalImages { id: front_buffer_id, size, .. - } = self.device.surface_info(&front_buffer); + } = self.surfman.surface_info(&front_buffer); debug!("... getting texture for surface {:?}", front_buffer_id); - let front_buffer_texture = self - .device - .create_surface_texture(&mut self.context, front_buffer) - .unwrap(); - let gl_texture = front_buffer_texture.gl_texture(); + let front_buffer_texture = self.surfman.create_surface_texture(front_buffer).unwrap(); + let gl_texture = self.surfman.surface_texture_object(&front_buffer_texture); self.locked_front_buffers.insert(id, front_buffer_texture); @@ -135,8 +123,8 @@ impl WebGLExternalImages { fn unlock_swap_chain(&mut self, id: WebGLContextId) -> Option<()> { let locked_front_buffer = self.locked_front_buffers.remove(&id)?; let locked_front_buffer = self - .device - .destroy_surface_texture(&mut self.context, locked_front_buffer) + .surfman + .destroy_surface_texture(locked_front_buffer) .unwrap(); debug!("... unlocked chain {:?}", id); diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs index bb643bf3192..a8736e39280 100644 --- a/components/canvas/webgl_thread.rs +++ b/components/canvas/webgl_thread.rs @@ -47,7 +47,6 @@ use euclid::default::Size2D; use fnv::FnvHashMap; use half::f16; use pixels::{self, PixelFormat}; -use servo_config::opts; use sparkle::gl; use sparkle::gl::GLint; use sparkle::gl::GLuint; @@ -59,12 +58,12 @@ use std::slice; use std::sync::{Arc, Mutex}; use std::thread; use surfman; -use surfman::platform::generic::universal::adapter::Adapter; -use surfman::platform::generic::universal::connection::Connection; -use surfman::platform::generic::universal::context::Context; -use surfman::platform::generic::universal::device::Device; +use surfman::Adapter; +use surfman::Connection; +use surfman::Context; use surfman::ContextAttributeFlags; use surfman::ContextAttributes; +use surfman::Device; use surfman::GLVersion; use surfman::SurfaceAccess; use surfman::SurfaceInfo; @@ -87,30 +86,130 @@ struct GLContextData { 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: gl::GLuint, } +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; + gl.clear_color(r, g, b, a); + } + + fn restore_scissor_invariant(&self, gl: &Gl) { + if self.scissor_test_enabled { + gl.enable(gl::SCISSOR_TEST); + } else { + 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() { + gl.color_mask(r, g, b, false); + } else { + gl.color_mask(r, g, b, a); + } + } + + fn restore_depth_invariant(&self, gl: &Gl) { + 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) { + 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: 0, + drawing_to_default_framebuffer: true, } } } @@ -138,9 +237,9 @@ pub(crate) struct WebGLThread { /// 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>, + webrender_swap_chains: SwapChains<WebGLContextId, Device>, /// The swap chains used by webxr - webxr_swap_chains: SwapChains<WebXRSwapChainId>, + webxr_swap_chains: SwapChains<WebXRSwapChainId, Device>, /// The set of all surface providers corresponding to WebXR sessions. webxr_surface_providers: SurfaceProviders, /// A channel to allow arbitrary threads to execute tasks that run in the WebGL thread. @@ -150,9 +249,9 @@ pub(crate) struct WebGLThread { } pub type WebGlExecutor = crossbeam_channel::Sender<WebGlRunnable>; -pub type WebGlRunnable = Box<dyn FnOnce() + Send>; +pub type WebGlRunnable = Box<dyn FnOnce(&Device) + Send>; pub type SurfaceProviders = Arc<Mutex<HashMap<SessionId, SurfaceProvider>>>; -pub type SurfaceProvider = Box<dyn surfman_chains::SurfaceProvider + Send>; +pub type SurfaceProvider = Box<dyn surfman_chains::SurfaceProvider<Device> + Send>; /// The data required to initialize an instance of the WebGLThread type. pub(crate) struct WebGLThreadInit { @@ -161,8 +260,8 @@ pub(crate) struct WebGLThreadInit { pub external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, pub sender: WebGLSender<WebGLMsg>, pub receiver: WebGLReceiver<WebGLMsg>, - pub webrender_swap_chains: SwapChains<WebGLContextId>, - pub webxr_swap_chains: SwapChains<WebXRSwapChainId>, + pub webrender_swap_chains: SwapChains<WebGLContextId, Device>, + pub webxr_swap_chains: SwapChains<WebXRSwapChainId, Device>, pub connection: Connection, pub adapter: Adapter, pub api_type: gl::GlType, @@ -190,7 +289,9 @@ impl WebGLThread { }: WebGLThreadInit, ) -> Self { WebGLThread { - device: Device::new(&connection, &adapter).expect("Couldn't open WebGL device!"), + device: connection + .create_device(&adapter) + .expect("Couldn't open WebGL device!"), webrender_api: webrender_api_sender.create_api(), contexts: Default::default(), cached_context_info: Default::default(), @@ -236,7 +337,7 @@ impl WebGLThread { } recv(self.runnable_receiver) -> msg => { if let Ok(msg) = msg { - msg(); + msg(&self.device); } else { self.runnable_receiver = crossbeam_channel::never(); } @@ -303,7 +404,7 @@ impl WebGLThread { .unwrap(); }, WebGLMsg::ResizeContext(ctx_id, size, sender) => { - self.resize_webgl_context(ctx_id, size, sender); + let _ = sender.send(self.resize_webgl_context(ctx_id, size)); }, WebGLMsg::RemoveContext(ctx_id) => { self.remove_webgl_context(ctx_id); @@ -394,21 +495,35 @@ impl WebGLThread { requested_size: Size2D<u32>, attributes: GLContextAttributes, ) -> Result<(WebGLContextId, webgl::GLLimits), String> { - debug!("WebGLThread::create_webgl_context({:?})", requested_size); + 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(), - flags: attributes.to_surfman_context_attribute_flags(webgl_version), + version: webgl_version.to_surfman_version(self.api_type), + flags: flags, }; let context_descriptor = self .device .create_context_descriptor(&context_attributes) - .unwrap(); + .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), @@ -423,30 +538,30 @@ impl WebGLThread { let mut ctx = self .device .create_context(&context_descriptor) - .expect("Failed to create the GL context!"); + .map_err(|err| format!("Failed to create the GL context: {:?}", err))?; let surface = self .device - .create_surface(&ctx, surface_access, &surface_type) - .expect("Failed to create the initial surface!"); + .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) - .unwrap(); + .map_err(|err| format!("Failed to bind initial surface: {:?}", err))?; // https://github.com/pcwalton/surfman/issues/7 self.device .make_context_current(&ctx) - .expect("failed to make new context current"); + .map_err(|err| format!("Failed to make new context current: {:?}", err))?; let id = WebGLContextId( self.external_images .lock() - .unwrap() + .expect("Lock poisoned?") .next_id(WebrenderImageHandlerType::WebGL) .0, ); self.webrender_swap_chains .create_attached_swap_chain(id, &mut self.device, &mut ctx, surface_provider) - .expect("Failed to create the swap chain"); + .map_err(|err| format!("Failed to create swap chain: {:?}", err))?; let swap_chain = self .webrender_swap_chains @@ -456,7 +571,7 @@ impl WebGLThread { debug!( "Created webgl context {:?}/{:?}", id, - self.device.context_id(&ctx) + self.device.context_id(&ctx), ); let gl = match self.api_type { @@ -475,34 +590,33 @@ impl WebGLThread { debug!("Resizing swap chain from {} to {}", safe_size, size); swap_chain .resize(&mut self.device, &mut ctx, size.to_i32()) - .expect("Failed to resize swap chain"); + .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 texture_target = current_wr_texture_target(&self.device); + self.device.make_context_current(&ctx).unwrap(); let framebuffer = self .device .context_surface_info(&ctx) - .unwrap() - .unwrap() + .map_err(|err| format!("Failed to get context surface info: {:?}", err))? + .ok_or_else(|| format!("Failed to get context surface info"))? .framebuffer_object; + 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., 0.); + 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 descriptor = self.device.context_descriptor(&ctx); - let descriptor_attributes = self.device.context_descriptor_attributes(&descriptor); - - let gl_version = descriptor_attributes.version; - let has_alpha = descriptor_attributes - .flags - .contains(ContextAttributeFlags::ALPHA); - let texture_target = current_wr_texture_target(&self.device); - let use_apple_vertex_array = WebGLImpl::needs_apple_vertex_arrays(gl_version); let default_vao = if let Some(vao) = WebGLImpl::create_vertex_array(&gl, use_apple_vertex_array, webgl_version) @@ -517,9 +631,15 @@ impl WebGLThread { let state = GLState { gl_version, webgl_version, + requested_flags, default_vao, ..Default::default() }; + debug!("Created state {:?}", state); + + state.restore_invariant(&*gl); + debug_assert_eq!(gl.get_error(), gl::NO_ERROR); + self.contexts.insert( id, GLContextData { @@ -549,8 +669,7 @@ impl WebGLThread { &mut self, context_id: WebGLContextId, requested_size: Size2D<u32>, - sender: WebGLSender<Result<(), String>>, - ) { + ) -> Result<(), String> { let data = Self::make_current_if_needed_mut( &self.device, context_id, @@ -568,16 +687,17 @@ impl WebGLThread { // 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()) - .expect("Failed to resize swap chain"); - // temporary, till https://github.com/pcwalton/surfman/issues/35 is fixed - self.device - .make_context_current(&data.ctx) - .expect("Failed to make context current again"); + .map_err(|err| format!("Failed to resize swap chain: {:?}", err))?; swap_chain - .clear_surface(&mut self.device, &mut data.ctx, &*data.gl) - .expect("Failed to clear resized 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"); } @@ -587,11 +707,9 @@ impl WebGLThread { // Update WR image if needed. let info = self.cached_context_info.get_mut(&context_id).unwrap(); - let context_descriptor = self.device.context_descriptor(&data.ctx); - let has_alpha = self - .device - .context_descriptor_attributes(&context_descriptor) - .flags + let has_alpha = data + .state + .requested_flags .contains(ContextAttributeFlags::ALPHA); let texture_target = current_wr_texture_target(&self.device); Self::update_wr_external_image( @@ -605,7 +723,7 @@ impl WebGLThread { debug_assert_eq!(data.gl.get_error(), gl::NO_ERROR); - sender.send(Ok(())).unwrap(); + Ok(()) } /// Removes a WebGLContext and releases attached resources. @@ -697,8 +815,13 @@ impl WebGLThread { // TODO: if preserveDrawingBuffer is true, then blit the front buffer to the back buffer // https://github.com/servo/servo/issues/24604 debug!("Clearing {:?}", swap_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_surface(&mut self.device, &mut data.ctx, &*data.gl, clear_color) .unwrap(); debug_assert_eq!(data.gl.get_error(), gl::NO_ERROR); @@ -1084,7 +1207,10 @@ impl WebGLImpl { state.stencil_clear_value = stencil; gl.clear_stencil(stencil); }, - WebGLCommand::ColorMask(r, g, b, a) => gl.color_mask(r, g, b, a), + WebGLCommand::ColorMask(r, g, b, a) => { + state.color_write_mask = [r, g, b, a]; + state.restore_alpha_invariant(gl); + }, WebGLCommand::CopyTexImage2D( target, level, @@ -1109,22 +1235,40 @@ impl WebGLImpl { WebGLCommand::DepthFunc(func) => gl.depth_func(func), WebGLCommand::DepthMask(flag) => { state.depth_write_mask = flag; - gl.depth_mask(flag); + state.restore_depth_invariant(gl); }, WebGLCommand::DepthRange(near, far) => { gl.depth_range(near.max(0.).min(1.) as f64, far.max(0.).min(1.) as f64) }, - WebGLCommand::Disable(cap) => { - if cap == gl::SCISSOR_TEST { + WebGLCommand::Disable(cap) => match cap { + gl::SCISSOR_TEST => { state.scissor_test_enabled = false; - } - gl.disable(cap); + 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); + }, + _ => gl.disable(cap), }, - WebGLCommand::Enable(cap) => { - if cap == gl::SCISSOR_TEST { + WebGLCommand::Enable(cap) => match cap { + gl::SCISSOR_TEST => { state.scissor_test_enabled = true; - } - gl.enable(cap); + 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); + }, + _ => gl.enable(cap), }, WebGLCommand::FramebufferRenderbuffer(target, attachment, renderbuffertarget, rb) => { let attach = |attachment| { @@ -1221,7 +1365,7 @@ impl WebGLImpl { }, WebGLCommand::StencilMask(mask) => { state.stencil_write_mask = (mask, mask); - gl.stencil_mask(mask); + state.restore_stencil_invariant(gl); }, WebGLCommand::StencilMaskSeparate(face, mask) => { if face == gl::FRONT { @@ -1229,7 +1373,7 @@ impl WebGLImpl { } else { state.stencil_write_mask.1 = mask; } - gl.stencil_mask_separate(face, mask); + state.restore_stencil_invariant(gl); }, WebGLCommand::StencilOp(fail, zfail, zpass) => gl.stencil_op(fail, zfail, zpass), WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass) => { @@ -1321,7 +1465,7 @@ impl WebGLImpl { gl.bind_buffer(target, id.map_or(0, WebGLBufferId::get)) }, WebGLCommand::BindFramebuffer(target, request) => { - Self::bind_framebuffer(gl, target, request, ctx, device) + Self::bind_framebuffer(gl, target, request, ctx, device, state) }, WebGLCommand::BindRenderbuffer(target, id) => { gl.bind_renderbuffer(target, id.map_or(0, WebGLRenderbufferId::get)) @@ -1557,11 +1701,15 @@ impl WebGLImpl { Self::bind_vertex_array(gl, id, use_apple_vertex_array, state.webgl_version); }, WebGLCommand::GetParameterBool(param, ref sender) => { - let mut value = [0]; - unsafe { - gl.get_boolean_v(param as u32, &mut value); - } - sender.send(value[0] != 0).unwrap() + let value = match param { + webgl::ParameterBool::DepthWritemask => state.depth_write_mask, + _ => unsafe { + let mut value = [0]; + gl.get_boolean_v(param as u32, &mut value); + value[0] != 0 + }, + }; + sender.send(value).unwrap() }, WebGLCommand::FenceSync(ref sender) => { let value = gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0); @@ -1588,19 +1736,25 @@ impl WebGLImpl { gl.delete_sync(sync_id.get() as *const _); }, WebGLCommand::GetParameterBool4(param, ref sender) => { - let mut value = [0; 4]; - unsafe { - gl.get_boolean_v(param as u32, &mut value); - } - let value = [value[0] != 0, value[1] != 0, value[2] != 0, value[3] != 0]; + let value = match param { + webgl::ParameterBool4::ColorWritemask => state.color_write_mask, + }; sender.send(value).unwrap() }, WebGLCommand::GetParameterInt(param, ref sender) => { - let mut value = [0]; - unsafe { - gl.get_integer_v(param as u32, &mut value); - } - sender.send(value[0]).unwrap() + 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 { + let mut value = [0]; + gl.get_integer_v(param as u32, &mut value); + value[0] + }, + }; + sender.send(value).unwrap() }, WebGLCommand::GetParameterInt2(param, ref sender) => { let mut value = [0; 2]; @@ -1978,15 +2132,25 @@ impl WebGLImpl { sender.send(value).unwrap(); }, WebGLCommand::BindBufferBase(target, index, id) => { - gl.bind_buffer_base(target, index, id.map_or(0, WebGLBufferId::get)) + // 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_or(0, WebGLBufferId::get); + gl.bind_buffer(target, id); + gl.bind_buffer(target, 0); + 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_or(0, WebGLBufferId::get); + gl.bind_buffer(target, id); + gl.bind_buffer(target, 0); + gl.bind_buffer_range(target, index, id, offset as isize, size as isize); }, - WebGLCommand::BindBufferRange(target, index, id, offset, size) => gl.bind_buffer_range( - target, - index, - id.map_or(0, WebGLBufferId::get), - offset as isize, - size as isize, - ), WebGLCommand::ClearBufferfv(buffer, draw_buffer, ref value) => { gl.clear_buffer_fv(buffer, draw_buffer, value) }, @@ -2065,47 +2229,17 @@ impl WebGLImpl { bits | if enabled { bit } else { 0 } }); - if state.scissor_test_enabled { - gl.disable(gl::SCISSOR_TEST); - } - - if color { - gl.clear_color(0., 0., 0., 0.); - } - - if depth { - gl.depth_mask(true); - gl.clear_depth(1.); - } - - if stencil { - gl.stencil_mask_separate(gl::FRONT, 0xFFFFFFFF); - gl.stencil_mask_separate(gl::BACK, 0xFFFFFFFF); - gl.clear_stencil(0); - } - + 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); - if state.scissor_test_enabled { - gl.enable(gl::SCISSOR_TEST); - } - - if color { - let (r, g, b, a) = state.clear_color; - gl.clear_color(r, g, b, a); - } - - if depth { - gl.depth_mask(state.depth_write_mask); - gl.clear_depth(state.depth_clear_value); - } - - if stencil { - let (front, back) = state.stencil_write_mask; - gl.stencil_mask_separate(gl::FRONT, front); - gl.stencil_mask_separate(gl::BACK, back); - gl.clear_stencil(state.stencil_clear_value); - } + state.restore_invariant(gl); } #[allow(unsafe_code)] @@ -2213,6 +2347,7 @@ impl WebGLImpl { &mut transform_feedback_mode, ); } + ProgramLinkInfo { linked: true, active_attribs, @@ -2243,7 +2378,7 @@ impl WebGLImpl { // array object functions, but support a set of APPLE extension functions that // provide VAO support instead. fn needs_apple_vertex_arrays(gl_version: GLVersion) -> bool { - cfg!(target_os = "macos") && !opts::get().headless && gl_version.major < 3 + cfg!(target_os = "macos") && gl_version.major < 3 } #[allow(unsafe_code)] @@ -2427,8 +2562,8 @@ impl WebGLImpl { /// Updates the swap buffers if the context surface needs to be changed fn attach_surface( context_id: WebGLContextId, - webrender_swap_chains: &SwapChains<WebGLContextId>, - webxr_swap_chains: &SwapChains<WebXRSwapChainId>, + webrender_swap_chains: &SwapChains<WebGLContextId, Device>, + webxr_swap_chains: &SwapChains<WebXRSwapChainId, Device>, request: WebGLFramebufferBindingRequest, ctx: &mut Context, device: &mut Device, @@ -2479,6 +2614,7 @@ impl WebGLImpl { request: WebGLFramebufferBindingRequest, ctx: &Context, device: &Device, + state: &mut GLState, ) { let id = match request { WebGLFramebufferBindingRequest::Explicit(WebGLFramebufferId::Transparent(id)) => { @@ -2496,6 +2632,12 @@ impl WebGLImpl { debug!("WebGLImpl::bind_framebuffer: {:?}", id); 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] @@ -2885,27 +3027,41 @@ fn flip_pixels_y( // Clamp a size to the current GL context's max viewport fn clamp_viewport(gl: &Gl, size: Size2D<u32>) -> Size2D<u32> { - let mut max_size = [i32::max_value(), i32::max_value()]; + let mut max_viewport = [i32::max_value(), i32::max_value()]; + let mut max_renderbuffer = [i32::max_value()]; #[allow(unsafe_code)] unsafe { - gl.get_integer_v(gl::MAX_VIEWPORT_DIMS, &mut max_size); + gl.get_integer_v(gl::MAX_VIEWPORT_DIMS, &mut max_viewport); + gl.get_integer_v(gl::MAX_RENDERBUFFER_SIZE, &mut max_renderbuffer); debug_assert_eq!(gl.get_error(), gl::NO_ERROR); } Size2D::new( - size.width.min(max_size[0] as u32).max(1), - size.height.min(max_size[1] as u32).max(1), + 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) -> GLVersion; + fn to_surfman_version(self, api_type: gl::GlType) -> GLVersion; } impl ToSurfmanVersion for WebGLVersion { - fn to_surfman_version(self) -> GLVersion { + fn to_surfman_version(self, api_type: gl::GlType) -> GLVersion { + if api_type == gl::GlType::Gles { + return GLVersion::new(3, 0); + } match self { - WebGLVersion::WebGL1 => GLVersion::new(2, 0), - WebGLVersion::WebGL2 => GLVersion::new(3, 0), + // 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), } } } @@ -2914,6 +3070,7 @@ trait SurfmanContextAttributeFlagsConvert { fn to_surfman_context_attribute_flags( &self, webgl_version: WebGLVersion, + api_type: gl::GlType, ) -> ContextAttributeFlags; } @@ -2921,12 +3078,13 @@ impl SurfmanContextAttributeFlagsConvert for GLContextAttributes { fn to_surfman_context_attribute_flags( &self, webgl_version: WebGLVersion, + api_type: gl::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 { + if (webgl_version == WebGLVersion::WebGL1) && (api_type == gl::GlType::Gl) { flags.set(ContextAttributeFlags::COMPATIBILITY_PROFILE, true); } flags diff --git a/components/canvas_traits/webgl.rs b/components/canvas_traits/webgl.rs index e1537055280..510e20a590d 100644 --- a/components/canvas_traits/webgl.rs +++ b/components/canvas_traits/webgl.rs @@ -667,7 +667,7 @@ pub enum WebGLFramebufferId { Opaque(WebGLOpaqueFramebufferId), } -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] pub enum WebGLFramebufferBindingRequest { Explicit(WebGLFramebufferId), Default, diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index a6dcd9894d1..50f1d6f8456 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -40,6 +40,7 @@ style_traits = {path = "../style_traits"} time = "0.1.17" webrender = {git = "https://github.com/servo/webrender", features = ["capture"]} webrender_api = {git = "https://github.com/servo/webrender"} +webrender_surfman = {path = "../webrender_surfman"} webxr = {git = "https://github.com/servo/webxr"} [build-dependencies] diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 19898928420..5a495ed1aa8 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -19,6 +19,7 @@ use gfx_traits::Epoch; use image::{DynamicImage, ImageFormat}; use ipc_channel::ipc; use libc::c_void; +use log::warn; use msg::constellation_msg::{PipelineId, PipelineIndex, PipelineNamespaceId}; use net_traits::image::base::Image; use net_traits::image_cache::CorsStatus; @@ -46,6 +47,7 @@ use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor}; use time::{now, precise_time_ns, precise_time_s}; use webrender_api::units::{DeviceIntPoint, DeviceIntSize, DevicePoint, LayoutVector2D}; use webrender_api::{self, HitTestFlags, HitTestResult, ScrollLocation}; +use webrender_surfman::WebrenderSurfman; #[derive(Debug, PartialEq)] enum UnableToComposite { @@ -178,6 +180,12 @@ pub struct IOCompositor<Window: WindowMethods + ?Sized> { /// The webrender interface, if enabled. webrender_api: webrender_api::RenderApi, + /// The surfman instance that webrender targets + webrender_surfman: WebrenderSurfman, + + /// The GL bindings for webrender + webrender_gl: Rc<dyn gleam::gl::Gl>, + /// Some XR devices want to run on the main thread. pub webxr_main_thread: webxr::MainThreadRegistry, @@ -316,6 +324,8 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { webrender: state.webrender, webrender_document: state.webrender_document, webrender_api: state.webrender_api, + webrender_surfman: state.webrender_surfman, + webrender_gl: state.webrender_gl, webxr_main_thread: state.webxr_main_thread, pending_paint_metrics: HashMap::new(), cursor: Cursor::None, @@ -345,6 +355,9 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { convert_mouse_to_touch, ); + // Make sure the GL state is OK + compositor.assert_gl_framebuffer_complete(); + // Set the size of the root layer. compositor.update_zoom_transform(); @@ -352,7 +365,9 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { } pub fn deinit(self) { - self.window.make_gl_context_current(); + if let Err(err) = self.webrender_surfman.make_gl_context_current() { + warn!("Failed to make GL context current: {:?}", err); + } self.webrender.deinit(); } @@ -1238,7 +1253,22 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { ) -> Result<Option<Image>, UnableToComposite> { let size = self.embedder_coordinates.framebuffer.to_u32(); - self.window.make_gl_context_current(); + if let Err(err) = self.webrender_surfman.make_gl_context_current() { + warn!("Failed to make GL context current: {:?}", err); + } + self.assert_no_gl_error(); + + // Bind the webrender framebuffer + let framebuffer_object = self + .webrender_surfman + .context_surface_info() + .unwrap_or(None) + .map(|info| info.framebuffer_object) + .unwrap_or(0); + self.webrender_gl + .bind_framebuffer(gleam::gl::FRAMEBUFFER, framebuffer_object); + self.assert_gl_framebuffer_complete(); + self.webrender.update(); let wait_for_stable_image = match target { @@ -1266,7 +1296,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { CompositeTarget::Window => gl::RenderTargetInfo::default(), #[cfg(feature = "gl")] CompositeTarget::WindowAndPng | CompositeTarget::PngFile => gl::initialize_png( - &*self.window.gl(), + &*self.webrender_gl, FramebufferUintLength::new(size.width), FramebufferUintLength::new(size.height), ), @@ -1347,7 +1377,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { #[cfg(feature = "gl")] CompositeTarget::WindowAndPng => { let img = gl::draw_img( - &*self.window.gl(), + &*self.webrender_gl, rt_info, x, y, @@ -1365,7 +1395,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { }, #[cfg(feature = "gl")] CompositeTarget::PngFile => { - let gl = &*self.window.gl(); + let gl = &*self.webrender_gl; profile( ProfilerCategory::ImageSaving, None, @@ -1399,7 +1429,9 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { }; // Perform the page flip. This will likely block for a while. - self.window.present(); + if let Err(err) = self.webrender_surfman.present() { + warn!("Failed to present surface: {:?}", err); + } self.last_composite_time = precise_time_ns(); @@ -1426,11 +1458,13 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { } fn clear_background(&self) { - let gl = self.window.gl(); + let gl = &self.webrender_gl; + self.assert_gl_framebuffer_complete(); // Make framebuffer fully transparent. gl.clear_color(0.0, 0.0, 0.0, 0.0); gl.clear(gleam::gl::COLOR_BUFFER_BIT); + self.assert_gl_framebuffer_complete(); // Make the viewport white. let viewport = self.embedder_coordinates.get_flipped_viewport(); @@ -1444,6 +1478,24 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { gl.enable(gleam::gl::SCISSOR_TEST); gl.clear(gleam::gl::COLOR_BUFFER_BIT); gl.disable(gleam::gl::SCISSOR_TEST); + self.assert_gl_framebuffer_complete(); + } + + #[track_caller] + fn assert_no_gl_error(&self) { + debug_assert_eq!(self.webrender_gl.get_error(), gleam::gl::NO_ERROR); + } + + #[track_caller] + fn assert_gl_framebuffer_complete(&self) { + debug_assert_eq!( + ( + self.webrender_gl.get_error(), + self.webrender_gl + .check_frame_buffer_status(gleam::gl::FRAMEBUFFER) + ), + (gleam::gl::NO_ERROR, gleam::gl::FRAMEBUFFER_COMPLETE) + ); } fn get_root_pipeline_id(&self) -> Option<PipelineId> { diff --git a/components/compositing/compositor_thread.rs b/components/compositing/compositor_thread.rs index 245ed1055ea..cbd715af0dd 100644 --- a/components/compositing/compositor_thread.rs +++ b/components/compositing/compositor_thread.rs @@ -17,12 +17,14 @@ use profile_traits::mem; use profile_traits::time; use script_traits::{AnimationState, EventResult, MouseButton, MouseEventType}; use std::fmt::{Debug, Error, Formatter}; +use std::rc::Rc; use std::sync::atomic::AtomicBool; use std::sync::Arc; use style_traits::viewport::ViewportConstraints; use style_traits::CSSPixel; use webrender_api; use webrender_api::units::{DeviceIntPoint, DeviceIntSize}; +use webrender_surfman::WebrenderSurfman; /// Sends messages to the compositor. pub struct CompositorProxy { @@ -166,6 +168,8 @@ pub struct InitialCompositorState { pub webrender: webrender::Renderer, pub webrender_document: webrender_api::DocumentId, pub webrender_api: webrender_api::RenderApi, + pub webrender_surfman: WebrenderSurfman, + pub webrender_gl: Rc<dyn gleam::gl::Gl>, pub webxr_main_thread: webxr::MainThreadRegistry, pub pending_wr_frame: Arc<AtomicBool>, } diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index cc4f86922ff..da11e95fd55 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #![deny(unsafe_code)] +#![feature(track_caller)] #[macro_use] extern crate log; diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index b3a48026e30..7a2784e08fd 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -7,8 +7,6 @@ use canvas::{SurfaceProviders, WebGlExecutor}; use embedder_traits::{EmbedderProxy, EventLoopWaker}; use euclid::Scale; -#[cfg(feature = "gl")] -use gleam::gl; use keyboard_types::KeyboardEvent; use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId, TraversalDirection}; use script_traits::{MediaSessionActionType, MouseButton, TouchEventType, TouchId, WheelDelta}; @@ -16,14 +14,13 @@ use servo_geometry::DeviceIndependentPixel; use servo_media::player::context::{GlApi, GlContext, NativeDisplay}; use servo_url::ServoUrl; use std::fmt::{Debug, Error, Formatter}; -#[cfg(feature = "gl")] -use std::rc::Rc; use std::time::Duration; use style_traits::DevicePixel; use webrender_api::units::DevicePoint; use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize}; use webrender_api::ScrollLocation; +use webrender_surfman::WebrenderSurfman; #[derive(Clone)] pub enum MouseWindowEvent { @@ -148,14 +145,10 @@ pub enum AnimationState { Animating, } +// TODO: this trait assumes that the window is responsible +// for creating the GL context, making it current, buffer +// swapping, etc. Really that should all be done by surfman. pub trait WindowMethods { - /// Presents the window to the screen (perhaps by page flipping). - fn present(&self); - /// Make the OpenGL context current. - fn make_gl_context_current(&self); - /// Return the GL function pointer trait. - #[cfg(feature = "gl")] - fn gl(&self) -> Rc<dyn gl::Gl>; /// Get the coordinates of the native window, the screen and the framebuffer. fn get_coordinates(&self) -> EmbedderCoordinates; /// Set whether the application is currently animating. @@ -163,12 +156,14 @@ pub trait WindowMethods { /// will want to avoid blocking on UI events, and just /// run the event loop at the vsync interval. fn set_animation_state(&self, _state: AnimationState); - /// Get the GL context + /// Get the media GL context fn get_gl_context(&self) -> GlContext; - /// Get the native display + /// Get the media native display fn get_native_display(&self) -> NativeDisplay; /// Get the GL api fn get_gl_api(&self) -> GlApi; + /// Get the webrender surfman instance + fn webrender_surfman(&self) -> WebrenderSurfman; } pub trait EmbedderMethods { diff --git a/components/config/prefs.rs b/components/config/prefs.rs index 82f4d6a0057..2561ebf937f 100644 --- a/components/config/prefs.rs +++ b/components/config/prefs.rs @@ -321,7 +321,11 @@ mod gen { subpixel_text_antialiasing: { #[serde(rename = "gfx.subpixel-text-antialiasing.enabled")] enabled: bool, - } + }, + texture_swizzling: { + #[serde(rename = "gfx.texture-swizzling.enabled")] + enabled: bool, + }, }, js: { asmjs: { diff --git a/components/script/dom/webgl_extensions/extensions.rs b/components/script/dom/webgl_extensions/extensions.rs index 2dc3ef8f11f..04e110b7ed0 100644 --- a/components/script/dom/webgl_extensions/extensions.rs +++ b/components/script/dom/webgl_extensions/extensions.rs @@ -52,12 +52,24 @@ const DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL1: [GLenum; 3] = [ OESVertexArrayObjectConstants::VERTEX_ARRAY_BINDING_OES, ]; +// Param names that are implemented for glGetParameter in a WebGL 2.0 context +// but must trigger a InvalidEnum error until the related WebGL Extensions are enabled. +// Example: https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/ +const DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL2: [GLenum; 1] = + [EXTTextureFilterAnisotropicConstants::MAX_TEXTURE_MAX_ANISOTROPY_EXT]; + // Param names that are implemented for glGetTexParameter in a WebGL 1.0 context // but must trigger a InvalidEnum error until the related WebGL Extensions are enabled. // Example: https://www.khronos.org/registry/webgl/extensions/OES_standard_derivatives/ const DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL1: [GLenum; 1] = [EXTTextureFilterAnisotropicConstants::TEXTURE_MAX_ANISOTROPY_EXT]; +// Param names that are implemented for glGetTexParameter in a WebGL 2.0 context +// but must trigger a InvalidEnum error until the related WebGL Extensions are enabled. +// Example: https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/ +const DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL2: [GLenum; 1] = + [EXTTextureFilterAnisotropicConstants::TEXTURE_MAX_ANISOTROPY_EXT]; + // Param names that are implemented for glGetVertexAttrib in a WebGL 1.0 context // but must trigger a InvalidEnum error until the related WebGL Extensions are enabled. // Example: https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/ @@ -116,8 +128,14 @@ impl WebGLExtensionFeatures { ), WebGLVersion::WebGL2 => ( Default::default(), - Default::default(), - Default::default(), + DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL2 + .iter() + .cloned() + .collect(), + DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL2 + .iter() + .cloned() + .collect(), Default::default(), true, true, diff --git a/components/script/dom/webglrenderbuffer.rs b/components/script/dom/webglrenderbuffer.rs index 51f1bdbad4e..f1a3ba99c53 100644 --- a/components/script/dom/webglrenderbuffer.rs +++ b/components/script/dom/webglrenderbuffer.rs @@ -233,7 +233,7 @@ impl WebGLRenderbuffer { ), ); let samples = receiver.recv().unwrap(); - if sample_count < 0 || sample_count as usize > samples.len() { + if sample_count < 0 || sample_count > samples.get(0).cloned().unwrap_or(0) { return Err(WebGLError::InvalidOperation); } } diff --git a/components/script/dom/webglshader.rs b/components/script/dom/webglshader.rs index 5302c6c78ad..9be334f6595 100644 --- a/components/script/dom/webglshader.rs +++ b/components/script/dom/webglshader.rs @@ -277,8 +277,49 @@ impl WebGLShader { }, }; - match validator.compile_and_translate(&[&source]) { - Ok(translated_source) => { + // Replicating + // https://searchfox.org/mozilla-central/rev/c621276fbdd9591f52009042d959b9e19b66d49f/dom/canvas/WebGLShaderValidator.cpp#32 + let options = mozangle::shaders::ffi::SH_VARIABLES | + mozangle::shaders::ffi::SH_ENFORCE_PACKING_RESTRICTIONS | + mozangle::shaders::ffi::SH_OBJECT_CODE | + mozangle::shaders::ffi::SH_INIT_GL_POSITION | + mozangle::shaders::ffi::SH_INITIALIZE_UNINITIALIZED_LOCALS | + mozangle::shaders::ffi::SH_INIT_OUTPUT_VARIABLES | + mozangle::shaders::ffi::SH_LIMIT_EXPRESSION_COMPLEXITY | + mozangle::shaders::ffi::SH_LIMIT_CALL_STACK_DEPTH | + if cfg!(target_os = "macos") { + // Work around https://bugs.webkit.org/show_bug.cgi?id=124684, + // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb + mozangle::shaders::ffi::SH_UNFOLD_SHORT_CIRCUIT | + // Work around that Mac drivers handle struct scopes incorrectly. + mozangle::shaders::ffi::SH_REGENERATE_STRUCT_NAMES | + // Work around that Intel drivers on Mac OSX handle for-loop incorrectly. + mozangle::shaders::ffi::SH_ADD_AND_TRUE_TO_LOOP_CONDITION + } else { + // We want to do this everywhere, but to do this on Mac, we need + // to do it only on Mac OSX > 10.6 as this causes the shader + // compiler in 10.6 to crash + mozangle::shaders::ffi::SH_CLAMP_INDIRECT_ARRAY_BOUNDS + }; + + // Replicating + // https://github.com/servo/mozangle/blob/706a9baaf8026c1a3cb6c67ba63aa5f4734264d0/src/shaders/mod.rs#L226 + let options = options | + mozangle::shaders::ffi::SH_VALIDATE | + mozangle::shaders::ffi::SH_OBJECT_CODE | + mozangle::shaders::ffi::SH_VARIABLES | // For uniform_name_map() + mozangle::shaders::ffi::SH_EMULATE_ABS_INT_FUNCTION | // To workaround drivers + mozangle::shaders::ffi::SH_EMULATE_ISNAN_FLOAT_FUNCTION | // To workaround drivers + mozangle::shaders::ffi::SH_EMULATE_ATAN2_FLOAT_FUNCTION | // To workaround drivers + mozangle::shaders::ffi::SH_CLAMP_INDIRECT_ARRAY_BOUNDS | + mozangle::shaders::ffi::SH_INIT_GL_POSITION | + mozangle::shaders::ffi::SH_ENFORCE_PACKING_RESTRICTIONS | + mozangle::shaders::ffi::SH_LIMIT_EXPRESSION_COMPLEXITY | + mozangle::shaders::ffi::SH_LIMIT_CALL_STACK_DEPTH; + + match validator.compile(&[&source], options) { + Ok(()) => { + let translated_source = validator.object_code(); debug!("Shader translated: {}", translated_source); // NOTE: At this point we should be pretty sure that the compilation in the paint thread // will succeed. diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index 68d3fa2cd6a..ca933bb848c 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -81,11 +81,12 @@ style_traits = {path = "../style_traits", features = ["servo"]} webgpu = {path = "../webgpu"} webrender = {git = "https://github.com/servo/webrender"} webrender_api = {git = "https://github.com/servo/webrender"} +webrender_surfman = {path = "../webrender_surfman"} webrender_traits = {path = "../webrender_traits"} webdriver_server = {path = "../webdriver_server", optional = true} webxr-api = {git = "https://github.com/servo/webxr"} webxr = {git = "https://github.com/servo/webxr"} -surfman = { version = "0.1", features = ["sm-osmesa"] } +surfman = "0.2" gstreamer = { version = "0.15", optional = true } [target.'cfg(all(not(target_os = "windows"), not(target_os = "ios"), not(target_os="android"), not(target_arch="arm"), not(target_arch="aarch64")))'.dependencies] diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 1bd9ace8035..82ac035fc4c 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -51,6 +51,7 @@ pub use style; pub use style_traits; pub use webgpu; pub use webrender_api; +pub use webrender_surfman; pub use webrender_traits; #[cfg(feature = "webdriver")] @@ -116,14 +117,9 @@ use std::path::PathBuf; use std::rc::Rc; use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; -#[cfg(not(target_os = "windows"))] -use surfman::platform::default::device::Device as HWDevice; -#[cfg(not(target_os = "windows"))] -use surfman::platform::generic::osmesa::device::Device as SWDevice; -#[cfg(not(target_os = "windows"))] -use surfman::platform::generic::universal::context::Context; -use surfman::platform::generic::universal::device::Device; +use surfman::GLApi; use webrender::{RendererKind, ShaderPrecacheFlags}; +use webrender_surfman::WebrenderSurfman; use webrender_traits::WebrenderImageHandlerType; use webrender_traits::{WebrenderExternalImageHandlers, WebrenderExternalImageRegistry}; @@ -353,8 +349,28 @@ where .unwrap_or(default_user_agent_string_for(DEFAULT_USER_AGENT).into()), }; + // Initialize surfman + let webrender_surfman = window.webrender_surfman(); + + // Get GL bindings + let webrender_gl = match webrender_surfman.connection().gl_api() { + GLApi::GL => unsafe { gl::GlFns::load_with(|s| webrender_surfman.get_proc_address(s)) }, + GLApi::GLES => unsafe { + gl::GlesFns::load_with(|s| webrender_surfman.get_proc_address(s)) + }, + }; + // Make sure the gl context is made current. - window.make_gl_context_current(); + webrender_surfman.make_gl_context_current().unwrap(); + debug_assert_eq!(webrender_gl.get_error(), gleam::gl::NO_ERROR,); + + // Bind the webrender framebuffer + let framebuffer_object = webrender_surfman + .context_surface_info() + .unwrap_or(None) + .map(|info| info.framebuffer_object) + .unwrap_or(0); + webrender_gl.bind_framebuffer(gleam::gl::FRAMEBUFFER, framebuffer_object); // Reserving a namespace to create TopLevelBrowserContextId. PipelineNamespace::install(PipelineNamespaceId(0)); @@ -407,7 +423,7 @@ where let window_size = Size2D::from_untyped(viewport_size.to_i32().to_untyped()); webrender::Renderer::new( - window.gl(), + webrender_gl.clone(), render_notifier, webrender::RendererOptions { device_pixel_ratio, @@ -422,6 +438,7 @@ where }, renderer_kind: renderer_kind, enable_subpixel_aa: opts.enable_subpixel_text_antialiasing, + allow_texture_swizzling: pref!(gfx.texture_swizzling.enabled), clear_color: None, ..Default::default() }, @@ -451,7 +468,8 @@ where .expect("Failed to create WebXR device registry"); let (webgl_threads, webgl_extras) = create_webgl_threads( - &*window, + webrender_surfman.clone(), + webrender_gl.clone(), &mut webrender, webrender_api_sender.clone(), &mut webxr_main_thread, @@ -542,6 +560,8 @@ where webrender, webrender_document, webrender_api, + webrender_surfman, + webrender_gl, webxr_main_thread, pending_wr_frame, }, @@ -806,6 +826,10 @@ where log::set_max_level(filter); } + pub fn window(&self) -> &Window { + &self.compositor.window + } + pub fn deinit(self) { self.compositor.deinit(); } @@ -1030,8 +1054,9 @@ fn create_sandbox() { } // Initializes the WebGL thread. -fn create_webgl_threads<W>( - window: &W, +fn create_webgl_threads( + webrender_surfman: WebrenderSurfman, + webrender_gl: Rc<dyn gl::Gl>, webrender: &mut webrender::Renderer, webrender_api_sender: webrender_api::RenderApiSender, webxr_main_thread: &mut webxr::MainThreadRegistry, @@ -1040,45 +1065,8 @@ fn create_webgl_threads<W>( ) -> ( Option<WebGLThreads>, Option<(SurfaceProviders, WebGlExecutor)>, -) -where - W: WindowMethods + 'static + ?Sized, -{ - // Create a `surfman` device and context. - window.make_gl_context_current(); - - #[cfg(not(target_os = "windows"))] - let (device, context) = unsafe { - if opts::get().headless { - let (device, context) = match SWDevice::from_current_context() { - Ok(a) => a, - Err(e) => { - warn!("Failed to create software graphics context: {:?}", e); - return (None, None); - }, - }; - (Device::Software(device), Context::Software(context)) - } else { - let (device, context) = match HWDevice::from_current_context() { - Ok(a) => a, - Err(e) => { - warn!("Failed to create hardware graphics context: {:?}", e); - return (None, None); - }, - }; - (Device::Hardware(device), Context::Hardware(context)) - } - }; - #[cfg(target_os = "windows")] - let (device, context) = match unsafe { Device::from_current_context() } { - Ok(a) => a, - Err(e) => { - warn!("Failed to create graphics context: {:?}", e); - return (None, None); - }, - }; - - let gl_type = match window.gl().get_type() { +) { + let gl_type = match webrender_gl.get_type() { gleam::gl::GlType::Gl => sparkle::gl::GlType::Gl, gleam::gl::GlType::Gles => sparkle::gl::GlType::Gles, }; @@ -1091,9 +1079,8 @@ where output_handler, webgl_executor, } = WebGLComm::new( - device, - context, - window.gl(), + webrender_surfman, + webrender_gl, webrender_api_sender, external_images, gl_type, diff --git a/components/webrender_surfman/Cargo.toml b/components/webrender_surfman/Cargo.toml new file mode 100644 index 00000000000..e82f553d2cc --- /dev/null +++ b/components/webrender_surfman/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "webrender_surfman" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +edition = "2018" +publish = false + +[lib] +name = "webrender_surfman" +path = "lib.rs" + +[dependencies] +euclid = "0.20" +surfman = "0.2" +surfman-chains = "0.3" + diff --git a/components/webrender_surfman/lib.rs b/components/webrender_surfman/lib.rs new file mode 100644 index 00000000000..193298808bc --- /dev/null +++ b/components/webrender_surfman/lib.rs @@ -0,0 +1,213 @@ +/* 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/. */ + +#![deny(unsafe_code)] + +use euclid::default::Size2D; + +use std::cell::RefCell; +use std::ffi::c_void; +use std::rc::Rc; +use surfman::Adapter; +use surfman::Connection; +use surfman::Context; +use surfman::ContextAttributeFlags; +use surfman::ContextAttributes; +use surfman::Device; +use surfman::Error; +use surfman::GLApi; +use surfman::GLVersion; +use surfman::NativeContext; +use surfman::NativeDevice; +use surfman::NativeWidget; +use surfman::Surface; +use surfman::SurfaceAccess; +use surfman::SurfaceInfo; +use surfman::SurfaceTexture; +use surfman::SurfaceType; +use surfman_chains::SurfmanProvider; +use surfman_chains::SwapChain; + +/// A bridge between webrender and surfman +// TODO: move this into a different crate so that script doesn't depend on surfman +#[derive(Clone)] +pub struct WebrenderSurfman(Rc<WebrenderSurfmanData>); + +struct WebrenderSurfmanData { + device: RefCell<Device>, + context: RefCell<Context>, + // We either render to a swap buffer or to a native widget + swap_chain: Option<SwapChain<Device>>, +} + +impl Drop for WebrenderSurfmanData { + fn drop(&mut self) { + let ref mut device = self.device.borrow_mut(); + let ref mut context = self.context.borrow_mut(); + if let Some(ref swap_chain) = self.swap_chain { + let _ = swap_chain.destroy(device, context); + } + let _ = device.destroy_context(context); + } +} + +impl WebrenderSurfman { + pub fn create( + connection: &Connection, + adapter: &Adapter, + surface_type: SurfaceType<NativeWidget>, + ) -> Result<Self, Error> { + let mut device = connection.create_device(&adapter)?; + let flags = ContextAttributeFlags::ALPHA | + ContextAttributeFlags::DEPTH | + ContextAttributeFlags::STENCIL; + let version = match connection.gl_api() { + GLApi::GLES => GLVersion { major: 3, minor: 0 }, + GLApi::GL => GLVersion { major: 3, minor: 2 }, + }; + let context_attributes = ContextAttributes { flags, version }; + let context_descriptor = device.create_context_descriptor(&context_attributes)?; + let mut context = device.create_context(&context_descriptor)?; + let surface_access = SurfaceAccess::GPUOnly; + let headless = match surface_type { + SurfaceType::Widget { .. } => false, + SurfaceType::Generic { .. } => true, + }; + let surface = device.create_surface(&context, surface_access, surface_type)?; + device + .bind_surface_to_context(&mut context, surface) + .map_err(|(err, mut surface)| { + let _ = device.destroy_surface(&mut context, &mut surface); + err + })?; + let swap_chain = if headless { + let surface_provider = Box::new(SurfmanProvider::new(surface_access)); + Some(SwapChain::create_attached( + &mut device, + &mut context, + surface_provider, + )?) + } else { + None + }; + let device = RefCell::new(device); + let context = RefCell::new(context); + let data = WebrenderSurfmanData { + device, + context, + swap_chain, + }; + Ok(WebrenderSurfman(Rc::new(data))) + } + + pub fn create_surface_texture( + &self, + surface: Surface, + ) -> Result<SurfaceTexture, (Error, Surface)> { + let ref device = self.0.device.borrow(); + let ref mut context = self.0.context.borrow_mut(); + device.create_surface_texture(context, surface) + } + + pub fn destroy_surface_texture( + &self, + surface_texture: SurfaceTexture, + ) -> Result<Surface, (Error, SurfaceTexture)> { + let ref device = self.0.device.borrow(); + let ref mut context = self.0.context.borrow_mut(); + device.destroy_surface_texture(context, surface_texture) + } + + pub fn make_gl_context_current(&self) -> Result<(), Error> { + let ref device = self.0.device.borrow(); + let ref context = self.0.context.borrow(); + device.make_context_current(context) + } + + pub fn swap_chain(&self) -> Result<&SwapChain<Device>, Error> { + self.0.swap_chain.as_ref().ok_or(Error::WidgetAttached) + } + + pub fn resize(&self, size: Size2D<i32>) -> Result<(), Error> { + let ref mut device = self.0.device.borrow_mut(); + let ref mut context = self.0.context.borrow_mut(); + if let Some(swap_chain) = self.0.swap_chain.as_ref() { + return swap_chain.resize(device, context, size); + } + let mut surface = device.unbind_surface_from_context(context)?.unwrap(); + device.resize_surface(context, &mut surface, size)?; + device + .bind_surface_to_context(context, surface) + .map_err(|(err, mut surface)| { + let _ = device.destroy_surface(context, &mut surface); + err + }) + } + + pub fn present(&self) -> Result<(), Error> { + let ref mut device = self.0.device.borrow_mut(); + let ref mut context = self.0.context.borrow_mut(); + if let Some(ref swap_chain) = self.0.swap_chain { + return swap_chain.swap_buffers(device, context); + } + let mut surface = device.unbind_surface_from_context(context)?.unwrap(); + device.present_surface(context, &mut surface)?; + device + .bind_surface_to_context(context, surface) + .map_err(|(err, mut surface)| { + let _ = device.destroy_surface(context, &mut surface); + err + }) + } + + pub fn connection(&self) -> Connection { + let ref device = self.0.device.borrow(); + device.connection() + } + + pub fn adapter(&self) -> Adapter { + let ref device = self.0.device.borrow(); + device.adapter() + } + + pub fn native_context(&self) -> NativeContext { + let ref device = self.0.device.borrow(); + let ref context = self.0.context.borrow(); + device.native_context(context) + } + + pub fn native_device(&self) -> NativeDevice { + let ref device = self.0.device.borrow(); + device.native_device() + } + + pub fn context_attributes(&self) -> ContextAttributes { + let ref device = self.0.device.borrow(); + let ref context = self.0.context.borrow(); + let ref descriptor = device.context_descriptor(context); + device.context_descriptor_attributes(descriptor) + } + + pub fn context_surface_info(&self) -> Result<Option<SurfaceInfo>, Error> { + let ref device = self.0.device.borrow(); + let ref context = self.0.context.borrow(); + device.context_surface_info(context) + } + + pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { + let ref device = self.0.device.borrow(); + device.surface_info(surface) + } + + pub fn surface_texture_object(&self, surface: &SurfaceTexture) -> u32 { + let ref device = self.0.device.borrow(); + device.surface_texture_object(surface) + } + + pub fn get_proc_address(&self, name: &str) -> *const c_void { + let ref device = self.0.device.borrow(); + let ref context = self.0.context.borrow(); + device.get_proc_address(context, name) + } +} diff --git a/components/webrender_traits/Cargo.toml b/components/webrender_traits/Cargo.toml index 29d2bf8aa9f..bf4720038c5 100644 --- a/components/webrender_traits/Cargo.toml +++ b/components/webrender_traits/Cargo.toml @@ -12,5 +12,6 @@ path = "lib.rs" [dependencies] euclid = "0.20" +servo_geometry = {path = "../geometry"} webrender_api = {git = "https://github.com/servo/webrender"} diff --git a/components/webrender_traits/lib.rs b/components/webrender_traits/lib.rs index 75c5f60272e..b6dfea27169 100644 --- a/components/webrender_traits/lib.rs +++ b/components/webrender_traits/lib.rs @@ -5,8 +5,10 @@ #![deny(unsafe_code)] use euclid::default::Size2D; + use std::collections::HashMap; use std::sync::{Arc, Mutex}; + use webrender_api::units::TexelRect; /// This trait is used as a bridge between the different GL clients |