servo: Merge #18332 - Reuse WebGL ImageKeys after a resize (from MortimerGoro:webgl_reuse_image); r=glennw
authorImanol Fernandez <mortimergoro@gmail.com>
Fri, 01 Sep 2017 01:22:38 -0500
changeset 657388 4c1e09307af79bf05715c3f82d4bc71f591a19cb
parent 657387 4349fda0e3f2d2db5719515292f8dc66d52d0127
child 657389 38f284a549c5522f1e98ac049491178d88c34db0
push id77508
push userbmo:emilio@crisal.io
push dateFri, 01 Sep 2017 11:17:48 +0000
reviewersglennw
milestone57.0a1
servo: Merge #18332 - Reuse WebGL ImageKeys after a resize (from MortimerGoro:webgl_reuse_image); r=glennw <!-- Please describe your changes on the following line: --> WebRender does now support resizing with the same image key --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [x] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 3dceb111587178cdff60041d76a2544f275e6371
servo/components/canvas/webgl_thread.rs
servo/components/script/dom/webglrenderingcontext.rs
--- a/servo/components/canvas/webgl_thread.rs
+++ b/servo/components/canvas/webgl_thread.rs
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use canvas_traits::canvas::byte_swap;
 use canvas_traits::webgl::*;
 use euclid::Size2D;
 use fnv::FnvHashMap;
 use gleam::gl;
 use offscreen_gl_context::{GLContext, GLContextAttributes, GLLimits, NativeGLContextMethods};
-use std::mem;
 use std::thread;
 use super::gl_context::{GLContextFactory, GLContextWrapper};
 use webrender;
 use webrender_api;
 
 /// WebGL Threading API entry point that lives in the constellation.
 /// It allows to get a WebGLThread handle for each script pipeline.
 pub use ::webgl_mode::WebGLThreads;
@@ -197,18 +196,16 @@ impl<VR: WebVRRenderHandler + 'static, O
                 self.contexts.insert(id, ctx);
                 self.cached_context_info.insert(id, WebGLContextInfo {
                     texture_id,
                     size,
                     alpha: attributes.alpha,
                     image_key: None,
                     share_mode,
                     gl_sync: None,
-                    old_image_key: None,
-                    very_old_image_key: None,
                 });
 
                 self.observer.on_context_create(id, texture_id, size);
 
                 Ok((id, limits, share_mode))
             },
             Err(msg) => {
                 Err(msg.to_owned())
@@ -226,23 +223,28 @@ impl<VR: WebVRRenderHandler + 'static, O
             Ok(_) => {
                 let (real_size, texture_id, _) = ctx.get_info();
                 self.observer.on_context_resize(context_id, texture_id, real_size);
 
                 let info = self.cached_context_info.get_mut(&context_id).unwrap();
                 // Update webgl texture size. Texture id may change too.
                 info.texture_id = texture_id;
                 info.size = real_size;
-                // WR doesn't support resizing and requires to create a new `ImageKey`.
-                // Mark the current image_key to be deleted later in the next epoch.
-                if let Some(image_key) = info.image_key.take() {
-                    // If this executes, then we are in a new epoch since we last recreated the canvas,
-                    // so `old_image_key` must be `None`.
-                    debug_assert!(info.old_image_key.is_none());
-                    info.old_image_key = Some(image_key);
+                // Update WR image if needed. Resize image updates are only required for SharedTexture mode.
+                // 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) => {
+                        Self::update_wr_external_image(&self.webrender_api,
+                                                       info.size,
+                                                       info.alpha,
+                                                       context_id,
+                                                       image_key);
+                    },
+                    _ => {}
                 }
 
                 sender.send(Ok(())).unwrap();
             },
             Err(msg) => {
                 sender.send(Err(msg.into())).unwrap();
             }
         }
@@ -252,50 +254,44 @@ impl<VR: WebVRRenderHandler + 'static, O
     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 updates = webrender_api::ResourceUpdates::new();
 
             if let Some(image_key) = info.image_key {
                 updates.delete_image(image_key);
             }
-            if let Some(image_key) = info.old_image_key {
-                updates.delete_image(image_key);
-            }
-            if let Some(image_key) = info.very_old_image_key {
-                updates.delete_image(image_key);
-            }
 
             self.webrender_api.update_resources(updates)
         }
 
         // Release GL context.
         if self.contexts.remove(&context_id).is_some() {
             self.observer.on_context_delete(context_id);
         }
 
         // Removing a GLContext may make the current bound context_id dirty.
         self.bound_context_id = None;
     }
 
-    /// Handles the creation/update of webrender_api::ImageKeys fpr a specific WebGLContext.
+    /// 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 or resize.
+    /// 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(&mut self, context_id: WebGLContextId, sender: WebGLSender<webrender_api::ImageKey>) {
         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 or resize.
+                // 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], info.size);
                 match info.image_key.clone() {
                     Some(image_key) => {
@@ -317,23 +313,16 @@ impl<VR: WebVRRenderHandler + 'static, O
                                                                        pixels);
                         info.image_key = Some(image_key);
                         image_key
                     }
                 }
             }
         };
 
-        // Delete old image
-        if let Some(image_key) = mem::replace(&mut info.very_old_image_key, info.old_image_key.take()) {
-            let mut updates = webrender_api::ResourceUpdates::new();
-            updates.delete_image(image_key);
-            self.webrender_api.update_resources(updates);
-        }
-
         // Send the ImageKey to the Layout thread.
         sender.send(image_key).unwrap();
     }
 
     /// Gets a reference to a GLContextWrapper for a given WebGLContextId and makes it current if required.
     fn make_current_if_needed<'a>(context_id: WebGLContextId,
                                   contexts: &'a FnvHashMap<WebGLContextId, GLContextWrapper>,
                                   bound_id: &mut Option<WebGLContextId>) -> Option<&'a GLContextWrapper> {
@@ -360,35 +349,46 @@ impl<VR: WebVRRenderHandler + 'static, O
     }
 
     /// Creates a `webrender_api::ImageKey` that uses shared textures.
     fn create_wr_external_image(webrender_api: &webrender_api::RenderApi,
                                 size: Size2D<i32>,
                                 alpha: bool,
                                 context_id: WebGLContextId) -> webrender_api::ImageKey {
         let descriptor = Self::image_descriptor(size, alpha);
-
-        let data = webrender_api::ExternalImageData {
-            id: webrender_api::ExternalImageId(context_id.0 as u64),
-            channel_index: 0,
-            image_type: webrender_api::ExternalImageType::Texture2DHandle,
-        };
-        let data = webrender_api::ImageData::External(data);
+        let data = Self::external_image_data(context_id);
 
         let image_key = webrender_api.generate_image_key();
         let mut updates = webrender_api::ResourceUpdates::new();
         updates.add_image(image_key,
                           descriptor,
                           data,
                           None);
         webrender_api.update_resources(updates);
 
         image_key
     }
 
+    /// Updates a `webrender_api::ImageKey` that uses shared textures.
+    fn update_wr_external_image(webrender_api: &webrender_api::RenderApi,
+                                size: Size2D<i32>,
+                                alpha: bool,
+                                context_id: WebGLContextId,
+                                image_key: webrender_api::ImageKey) {
+        let descriptor = Self::image_descriptor(size, alpha);
+        let data = Self::external_image_data(context_id);
+
+        let mut updates = webrender_api::ResourceUpdates::new();
+        updates.update_image(image_key,
+                             descriptor,
+                             data,
+                             None);
+        webrender_api.update_resources(updates);
+    }
+
     /// Creates a `webrender_api::ImageKey` that uses raw pixels.
     fn create_wr_readback_image(webrender_api: &webrender_api::RenderApi,
                                 size: Size2D<i32>,
                                 alpha: bool,
                                 data: Vec<u8>) -> webrender_api::ImageKey {
         let descriptor = Self::image_descriptor(size, alpha);
         let data = webrender_api::ImageData::new(data);
 
@@ -427,16 +427,26 @@ impl<VR: WebVRRenderHandler + 'static, O
             height: size.height as u32,
             stride: None,
             format: if alpha { webrender_api::ImageFormat::BGRA8 } else { webrender_api::ImageFormat::RGB8 },
             offset: 0,
             is_opaque: !alpha,
         }
     }
 
+    /// Helper function to create a `webrender_api::ImageData::External` instance.
+    fn external_image_data(context_id: WebGLContextId) -> webrender_api::ImageData {
+        let data = webrender_api::ExternalImageData {
+            id: webrender_api::ExternalImageId(context_id.0 as u64),
+            channel_index: 0,
+            image_type: webrender_api::ExternalImageType::Texture2DHandle,
+        };
+        webrender_api::ImageData::External(data)
+    }
+
     /// Helper function to fetch the raw pixels used in readback mode.
     fn raw_pixels(context: &GLContextWrapper, size: Size2D<i32>) -> Vec<u8> {
         let width = size.width as usize;
         let height = size.height as usize;
 
         let mut pixels = context.gl().read_pixels(0, 0,
                                                   size.width as gl::GLsizei,
                                                   size.height as gl::GLsizei,
@@ -474,20 +484,16 @@ struct WebGLContextInfo {
     /// True if the WebGLContext uses an alpha channel.
     alpha: bool,
     /// Currently used WebRender image key.
     image_key: Option<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.
     gl_sync: Option<gl::GLsync>,
-    /// An old WebRender image key that can be deleted when the next epoch ends.
-    old_image_key: Option<webrender_api::ImageKey>,
-    /// An old WebRender image key that can be deleted when the current epoch ends.
-    very_old_image_key: Option<webrender_api::ImageKey>,
 }
 
 /// Trait used to observe events in a WebGL Thread.
 /// Used in webrender::ExternalImageHandler when multiple WebGL threads are used.
 pub trait WebGLThreadObserver: Send + 'static {
     fn on_context_create(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>);
     fn on_context_resize(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>);
     fn on_context_delete(&mut self, ctx_id: WebGLContextId);
--- a/servo/components/script/dom/webglrenderingcontext.rs
+++ b/servo/components/script/dom/webglrenderingcontext.rs
@@ -259,20 +259,16 @@ impl WebGLRenderingContext {
         let (sender, receiver) = webgl_channel().unwrap();
         self.webgl_sender.send_resize(size, sender).unwrap();
 
         if let Err(msg) = receiver.recv().unwrap() {
             error!("Error resizing WebGLContext: {}", msg);
             return;
         };
 
-        // Reset webrender_image because resize creates a new image_key.
-        // The new image key is set in the next handle_layout() method.
-        self.webrender_image.set(None);
-
         // ClearColor needs to be restored because after a resize the GLContext is recreated
         // and the framebuffer is cleared using the default black transparent color.
         let color = self.current_clear_color.get();
         self.send_command(WebGLCommand::ClearColor(color.0, color.1, color.2, color.3));
 
         // WebGL Spec: Scissor rect must not change if the canvas is resized.
         // See: webgl/conformance-1.0.3/conformance/rendering/gl-scissor-canvas-dimensions.html
         // NativeContext handling library changes the scissor after a resize, so we need to reset the