diff options
author | Josh Matthews <josh@joshmatthews.net> | 2019-10-08 13:19:21 -0400 |
---|---|---|
committer | Josh Matthews <josh@joshmatthews.net> | 2019-10-10 09:57:20 -0400 |
commit | c53680b282aa5bf77ca15b8a4f9ae84afb8b9361 (patch) | |
tree | c41be0a3275b0ef09751a0c85517d4030545ec8a /components/canvas/webgl_thread.rs | |
parent | 4d7110aca53f54c5a9ee2bbbfbe800627c9af249 (diff) | |
download | servo-c53680b282aa5bf77ca15b8a4f9ae84afb8b9361.tar.gz servo-c53680b282aa5bf77ca15b8a4f9ae84afb8b9361.zip |
webgl: Lazily clear the canvas right before the first webgl command of the next frame.
Diffstat (limited to 'components/canvas/webgl_thread.rs')
-rw-r--r-- | components/canvas/webgl_thread.rs | 157 |
1 files changed, 77 insertions, 80 deletions
diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs index 0e930b1f947..a2e95955753 100644 --- a/components/canvas/webgl_thread.rs +++ b/components/canvas/webgl_thread.rs @@ -239,6 +239,12 @@ impl WebGLThread { let result = self.create_webgl_context(version, size, attributes); result_sender .send(result.map(|(id, limits, share_mode, framebuffer_format)| { + 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( id, &self.contexts, @@ -277,6 +283,7 @@ impl WebGLThread { glsl_version, api_type, framebuffer_format, + image_key, } })) .unwrap(); @@ -299,12 +306,12 @@ impl WebGLThread { WebGLMsg::Unlock(ctx_id) => { self.handle_unlock(ctx_id); }, - WebGLMsg::UpdateWebRenderImage(ctx_id, sender) => { - self.handle_update_wr_image(ctx_id, sender); - }, WebGLMsg::DOMToTextureCommand(command) => { self.handle_dom_to_texture(command); }, + WebGLMsg::SwapBuffers(context_ids, sender) => { + self.handle_swap_buffers(context_ids, sender); + }, WebGLMsg::Exit => { return true; }, @@ -326,6 +333,11 @@ impl WebGLThread { &mut self.bound_context_id, ); if let Some(data) = data { + let info = self.cached_context_info.get_mut(&context_id).unwrap(); + if info.clear_required { + info.clear_required = false; + Self::clear_drawing_buffer(data); + } data.ctx.apply_command( command, data.use_apple_vertex_arrays, @@ -400,32 +412,36 @@ impl WebGLThread { data.ctx.gl().delete_sync(gl_sync); debug_assert!(data.ctx.gl().get_error() == gl::NO_ERROR); } - - self.clear_drawing_buffer(context_id); } - fn clear_drawing_buffer(&mut self, context_id: WebGLContextId) { - let info = self.cached_context_info.get_mut(&context_id).unwrap(); - if info.preserve_drawing_buffer { - return; + #[allow(unsafe_code)] + fn clear_drawing_buffer(data: &mut GLContextData) { + trace!("clearing GL framebuffer"); + + // Ensure we're clearing the default framebuffer. + let mut fb = [0]; + unsafe { + data.ctx + .gl() + .get_integer_v(gl::FRAMEBUFFER_BINDING, &mut fb); } + data.ctx + .gl() + .bind_framebuffer(gl::FRAMEBUFFER, data.ctx.framebuffer()); - let data = Self::make_current_if_needed( - context_id, - &self.contexts, - &mut self.bound_context_id, - ) - .expect("WebGLContext not found when clearing drawing buffer"); - trace!("clearing GL framebuffer"); data.ctx.gl().clear_color(0., 0., 0., 0.); data.ctx.gl().clear_depth(1.0); data.ctx.gl().clear_stencil(0); - data.ctx.gl().clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); + data.ctx + .gl() + .clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); let (r, g, b, a) = data.state.clear_color; data.ctx.gl().clear_color(r, g, b, a); data.ctx.gl().clear_depth(data.state.depth_clear_value); data.ctx.gl().clear_stencil(data.state.stencil_clear_value); + + data.ctx.gl().bind_framebuffer(gl::FRAMEBUFFER, fb[0] as _); } /// Creates a new WebGLContext @@ -475,17 +491,33 @@ impl WebGLThread { }, ); + let image_key = match share_mode { + WebGLContextShareMode::Readback => Self::create_wr_readback_image( + &self.webrender_api, + size, + attributes.alpha, + vec![0; 4 * size.width as usize * size.height as usize], + ), + WebGLContextShareMode::SharedTexture => Self::create_wr_external_image( + &self.webrender_api, + size.to_i32(), + attributes.alpha, + id, + ), + }; + self.cached_context_info.insert( id, WebGLContextInfo { texture_id, size, alpha: attributes.alpha, - image_key: None, + image_key: image_key, share_mode, gl_sync: None, render_state: ContextRenderState::Unlocked, preserve_drawing_buffer, + clear_required: false, }, ); @@ -527,7 +559,7 @@ impl WebGLThread { // Readback mode already updates the image every frame to send the raw pixels. // See `handle_update_wr_image`. match (info.image_key, info.share_mode) { - (Some(image_key), WebGLContextShareMode::SharedTexture) => { + (image_key, WebGLContextShareMode::SharedTexture) => { Self::update_wr_external_image( &self.webrender_api, info.size, @@ -553,9 +585,7 @@ impl WebGLThread { if let Some(info) = self.cached_context_info.remove(&context_id) { let mut txn = webrender_api::Transaction::new(); - if let Some(image_key) = info.image_key { - txn.delete_image(image_key); - } + txn.delete_image(info.image_key); self.webrender_api.update_resources(txn.resource_updates) } @@ -571,68 +601,33 @@ impl WebGLThread { self.bound_context_id = None; } - /// Handles the creation/update of webrender_api::ImageKeys for a specific WebGLContext. - /// This method is invoked from a UpdateWebRenderImage message sent by the layout thread. - /// If SharedTexture is used the UpdateWebRenderImage message is sent only after a WebGLContext creation. - /// If Readback is used UpdateWebRenderImage message is sent always on each layout iteration in order to - /// submit the updated raw pixels. - fn handle_update_wr_image( + fn handle_swap_buffers( &mut self, - context_id: WebGLContextId, - sender: WebGLSender<webrender_api::ImageKey>, + context_ids: Vec<WebGLContextId>, + completed_sender: WebGLSender<()>, ) { - let info = self.cached_context_info.get_mut(&context_id).unwrap(); - let webrender_api = &self.webrender_api; - - let image_key = match info.share_mode { - WebGLContextShareMode::SharedTexture => { - let size = info.size; - let alpha = info.alpha; - // Reuse existing ImageKey or generate a new one. - // When using a shared texture ImageKeys are only generated after a WebGLContext creation. - *info.image_key.get_or_insert_with(|| { - Self::create_wr_external_image(webrender_api, size, alpha, context_id) - }) - }, - WebGLContextShareMode::Readback => { - let pixels = Self::raw_pixels(&self.contexts[&context_id].ctx, info.size); - match info.image_key.clone() { - Some(image_key) => { - // ImageKey was already created, but WR Images must - // be updated every frame in readback mode to send the new raw pixels. - Self::update_wr_readback_image( - webrender_api, - info.size, - info.alpha, - image_key, - pixels, - ); - - image_key - }, - None => { - // Generate a new ImageKey for Readback mode. - let image_key = Self::create_wr_readback_image( - webrender_api, - info.size, - info.alpha, - pixels, - ); - info.image_key = Some(image_key); - image_key - }, - } - }, - }; + for context_id in context_ids { + let info = self.cached_context_info.get_mut(&context_id).unwrap(); + let webrender_api = &self.webrender_api; - // Send the ImageKey to the Layout thread. - sender.send(image_key).unwrap(); + if let WebGLContextShareMode::Readback = info.share_mode { + let pixels = Self::raw_pixels(&self.contexts[&context_id].ctx, info.size); + // WR Images must be updated every frame in readback mode to send the new raw pixels. + Self::update_wr_readback_image( + webrender_api, + info.size, + info.alpha, + info.image_key, + pixels, + ); + } - if let WebGLContextShareMode::Readback = info.share_mode { - // Ensure that the drawing buffer is cleared when webrender isn't involved - // in drawing the GL texture. - self.clear_drawing_buffer(context_id); + if !info.preserve_drawing_buffer { + info.clear_required = true; + } } + + let _ = completed_sender.send(()); } fn handle_dom_to_texture(&mut self, command: DOMToTextureCommand) { @@ -911,7 +906,7 @@ struct WebGLContextInfo { /// True if the WebGLContext uses an alpha channel. alpha: bool, /// Currently used WebRender image key. - image_key: Option<webrender_api::ImageKey>, + image_key: webrender_api::ImageKey, /// The sharing mode used to send the image to WebRender. share_mode: WebGLContextShareMode, /// GLSync Object used for a correct synchronization with Webrender external image callbacks. @@ -920,6 +915,8 @@ struct WebGLContextInfo { render_state: ContextRenderState, /// Should the drawing buffer be preserved between frames? preserve_drawing_buffer: bool, + /// Does the canvas need to be cleared before executing further WebGL commands? + clear_required: bool, } /// Data about the linked DOM<->WebGLTexture elements. |