Bug 1528820 - Make Device::blit_render_target() require read and draw targets as arguments. r=kvark
authorJamie Nicol <jnicol@mozilla.com>
Thu, 07 Mar 2019 14:02:46 +0000
changeset 462874 912d0497234a65bafe04dfa5df6789beebb91d8e
parent 462873 26d4249db1c799d4312d061d656d7e65a6b78ba2
child 462875 cb745568d893c43e5ca7cb7dcda7d4c8464bd62b
push id35661
push useraiakab@mozilla.com
push dateThu, 07 Mar 2019 21:56:20 +0000
treeherdermozilla-central@ca64604d4b78 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskvark
bugs1528820
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1528820 - Make Device::blit_render_target() require read and draw targets as arguments. r=kvark Same for blit_render_target_invert_y(). Make them wrappers around the private function blit_render_target_impl(), which uses the currently bound read and draw targets as before. Differential Revision: https://phabricator.services.mozilla.com/D22304
gfx/wr/webrender/src/device/gl.rs
gfx/wr/webrender/src/renderer.rs
--- a/gfx/wr/webrender/src/device/gl.rs
+++ b/gfx/wr/webrender/src/device/gl.rs
@@ -982,16 +982,21 @@ pub enum DrawTarget<'a> {
     Texture {
         /// The target texture.
         texture: &'a Texture,
         /// The slice within the texture array to draw to.
         layer: LayerIndex,
         /// Whether to draw with the texture's associated depth target.
         with_depth: bool,
     },
+    /// Use an FBO attached to an external texture.
+    External {
+        fbo: FBOId,
+        size: FramebufferIntSize,
+    },
 }
 
 impl<'a> DrawTarget<'a> {
     pub fn new_default(size: FramebufferIntSize) -> Self {
         DrawTarget::Default {
             rect: size.into(),
             total_size: size,
         }
@@ -1005,28 +1010,29 @@ impl<'a> DrawTarget<'a> {
         }
     }
 
     /// Returns the dimensions of this draw-target.
     pub fn dimensions(&self) -> DeviceIntSize {
         match *self {
             DrawTarget::Default { total_size, .. } => DeviceIntSize::from_untyped(&total_size.to_untyped()),
             DrawTarget::Texture { texture, .. } => texture.get_dimensions(),
+            DrawTarget::External { size, .. } => DeviceIntSize::from_untyped(&size.to_untyped()),
         }
     }
 
     pub fn to_framebuffer_rect(&self, device_rect: DeviceIntRect) -> FramebufferIntRect {
         let mut fb_rect = FramebufferIntRect::from_untyped(&device_rect.to_untyped());
         match *self {
             DrawTarget::Default { ref rect, .. } => {
                 // perform a Y-flip here
                 fb_rect.origin.y = rect.origin.y + rect.size.height - fb_rect.origin.y - fb_rect.size.height;
                 fb_rect.origin.x += rect.origin.x;
             }
-            DrawTarget::Texture { .. } => (),
+            DrawTarget::Texture { .. } | DrawTarget::External { .. } => (),
         }
         fb_rect
     }
 
     /// Given a scissor rect, convert it to the right coordinate space
     /// depending on the draw target kind. If no scissor rect was supplied,
     /// returns a scissor rect that encloses the entire render target.
     pub fn build_scissor_rect(
@@ -1038,17 +1044,17 @@ impl<'a> DrawTarget<'a> {
 
         match scissor_rect {
             Some(scissor_rect) => match *self {
                 DrawTarget::Default { ref rect, .. } => {
                     self.to_framebuffer_rect(scissor_rect.translate(&-content_origin.to_vector()))
                         .intersection(rect)
                         .unwrap_or_else(FramebufferIntRect::zero)
                 }
-                DrawTarget::Texture { .. } => {
+                DrawTarget::Texture { .. } | DrawTarget::External { .. } => {
                     FramebufferIntRect::from_untyped(&scissor_rect.to_untyped())
                 }
             }
             None => {
                 FramebufferIntRect::new(
                     FramebufferIntPoint::zero(),
                     FramebufferIntSize::from_untyped(&dimensions.to_untyped()),
                 )
@@ -1063,25 +1069,31 @@ pub enum ReadTarget<'a> {
     /// Use the device's default draw target.
     Default,
     /// Use the provided texture,
     Texture {
         /// The source texture.
         texture: &'a Texture,
         /// The slice within the texture array to read from.
         layer: LayerIndex,
-    }
+    },
+    /// Use an FBO attached to an external texture.
+    External {
+        fbo: FBOId,
+    },
 }
 
 impl<'a> From<DrawTarget<'a>> for ReadTarget<'a> {
     fn from(t: DrawTarget<'a>) -> Self {
         match t {
             DrawTarget::Default { .. } => ReadTarget::Default,
             DrawTarget::Texture { texture, layer, .. } =>
                 ReadTarget::Texture { texture, layer },
+            DrawTarget::External { fbo, .. } =>
+                ReadTarget::External { fbo },
         }
     }
 }
 
 impl Device {
     pub fn new(
         mut gl: Rc<gl::Gl>,
         resource_override_path: Option<PathBuf>,
@@ -1413,16 +1425,17 @@ impl Device {
             fbo_id.bind(self.gl(), FBOTarget::Read);
         }
     }
 
     pub fn bind_read_target(&mut self, target: ReadTarget) {
         let fbo_id = match target {
             ReadTarget::Default => self.default_read_fbo,
             ReadTarget::Texture { texture, layer } => texture.fbos[layer],
+            ReadTarget::External { fbo } => fbo,
         };
 
         self.bind_read_target_impl(fbo_id)
     }
 
     fn bind_draw_target_impl(&mut self, fbo_id: FBOId) {
         debug_assert!(self.inside_frame);
 
@@ -1455,17 +1468,18 @@ impl Device {
                     FramebufferIntPoint::zero(),
                     FramebufferIntSize::from_untyped(&texture.get_dimensions().to_untyped()),
                 );
                 if with_depth {
                     (texture.fbos_with_depth[layer], rect, true)
                 } else {
                     (texture.fbos[layer], rect, false)
                 }
-            }
+            },
+            DrawTarget::External { fbo, size } => (fbo, size.into(), false),
         };
 
         self.depth_available = depth_available;
         self.bind_draw_target_impl(fbo_id);
         self.gl.viewport(
             rect.origin.x,
             rect.origin.y,
             rect.size.width,
@@ -1814,25 +1828,28 @@ impl Device {
             unsafe {
                 self.gl.copy_image_sub_data(src.id, src.target, 0,
                                             0, 0, 0,
                                             dst.id, dst.target, 0,
                                             0, 0, 0,
                                             src.size.width as _, src.size.height as _, src.layer_count);
             }
         } else {
-            // Note that zip() truncates to the shorter of the two iterators.
             let rect = FramebufferIntRect::new(
                 FramebufferIntPoint::zero(),
                 FramebufferIntSize::from_untyped(&src.get_dimensions().to_untyped()),
             );
-            for (read_fbo, draw_fbo) in src.fbos.iter().zip(&dst.fbos) {
-                self.bind_read_target_impl(*read_fbo);
-                self.bind_draw_target_impl(*draw_fbo);
-                self.blit_render_target(rect, rect, TextureFilter::Linear);
+            for layer in 0..src.layer_count.min(dst.layer_count) as LayerIndex {
+                self.blit_render_target(
+                    ReadTarget::Texture { texture: src, layer },
+                    rect,
+                    DrawTarget::Texture { texture: dst, layer, with_depth: false },
+                    rect,
+                    TextureFilter::Linear
+                );
             }
             self.reset_draw_target();
             self.reset_read_target();
         }
     }
 
     /// Notifies the device that the contents of a render target are no longer
     /// needed.
@@ -1973,17 +1990,18 @@ impl Device {
         entry.get_mut().refcount -= 1;
         if entry.get().refcount == 0 {
             let (dimensions, target) = entry.remove_entry();
             self.gl.delete_renderbuffers(&[target.rbo_id.0]);
             record_gpu_free(depth_target_size_in_bytes(&dimensions));
         }
     }
 
-    pub fn blit_render_target(
+    /// Perform a blit between self.bound_read_fbo and self.bound_draw_fbo.
+    fn blit_render_target_impl(
         &mut self,
         src_rect: FramebufferIntRect,
         dest_rect: FramebufferIntRect,
         filter: TextureFilter,
     ) {
         debug_assert!(self.inside_frame);
 
         let filter = match filter {
@@ -2000,36 +2018,56 @@ impl Device {
             dest_rect.origin.y,
             dest_rect.origin.x + dest_rect.size.width,
             dest_rect.origin.y + dest_rect.size.height,
             gl::COLOR_BUFFER_BIT,
             filter,
         );
     }
 
+    /// Perform a blit between src_target and dest_target.
+    /// This will overwrite self.bound_read_fbo and self.bound_draw_fbo.
+    pub fn blit_render_target(
+        &mut self,
+        src_target: ReadTarget,
+        src_rect: FramebufferIntRect,
+        dest_target: DrawTarget,
+        dest_rect: FramebufferIntRect,
+        filter: TextureFilter,
+    ) {
+        debug_assert!(self.inside_frame);
+
+        self.bind_read_target(src_target);
+        self.bind_draw_target(dest_target);
+
+        self.blit_render_target_impl(src_rect, dest_rect, filter);
+    }
+
     /// Performs a blit while flipping vertically. Useful for blitting textures
     /// (which use origin-bottom-left) to the main framebuffer (which uses
     /// origin-top-left).
     pub fn blit_render_target_invert_y(
         &mut self,
+        src_target: ReadTarget,
         src_rect: FramebufferIntRect,
+        dest_target: DrawTarget,
         dest_rect: FramebufferIntRect,
     ) {
         debug_assert!(self.inside_frame);
-        self.gl.blit_framebuffer(
-            src_rect.origin.x,
-            src_rect.origin.y,
-            src_rect.origin.x + src_rect.size.width,
-            src_rect.origin.y + src_rect.size.height,
-            dest_rect.origin.x,
-            dest_rect.origin.y + dest_rect.size.height,
-            dest_rect.origin.x + dest_rect.size.width,
-            dest_rect.origin.y,
-            gl::COLOR_BUFFER_BIT,
-            gl::LINEAR,
+
+        let mut inverted_dest_rect = dest_rect;
+        inverted_dest_rect.origin.y = dest_rect.max_y();
+        inverted_dest_rect.size.height *= -1;
+
+        self.blit_render_target(
+            src_target,
+            src_rect,
+            dest_target,
+            inverted_dest_rect,
+            TextureFilter::Linear,
         );
     }
 
     pub fn delete_texture(&mut self, mut texture: Texture) {
         debug_assert!(self.inside_frame);
         record_gpu_free(texture.size_in_bytes());
         let had_depth = texture.supports_depth();
         self.deinit_fbos(&mut texture.fbos);
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -3117,19 +3117,20 @@ impl Renderer {
                 }
             };
             debug_assert_eq!(source_rect.size, blit.target_rect.size);
             let texture = texture_resolver
                 .resolve(&source)
                 .expect("BUG: invalid source texture");
             let read_target = DrawTarget::Texture { texture, layer, with_depth: false };
 
-            device.bind_read_target(read_target.into());
             device.blit_render_target(
+                read_target.into(),
                 read_target.to_framebuffer_rect(source_rect),
+                draw_target,
                 draw_target.to_framebuffer_rect(blit.target_rect.translate(&-content_origin.to_vector())),
                 TextureFilter::Linear,
             );
         }
     }
 
     fn handle_scaling(
         &mut self,
@@ -3218,17 +3219,17 @@ impl Renderer {
                     // and consider different code paths.
                     //
                     // Note: The above measurements were taken when render
                     // target slices were minimum 2048x2048. Now that we size
                     // them adaptively, this may be less of a win (except perhaps
                     // on a mostly-unused last slice of a large texture array).
                     Some(draw_target.to_framebuffer_rect(target.used_rect()))
                 }
-                DrawTarget::Texture { .. } => {
+                DrawTarget::Texture { .. } | DrawTarget::External { .. } => {
                     None
                 }
             };
 
             self.device.clear_target(
                 clear_color,
                 clear_depth,
                 clear_rect,
@@ -3467,44 +3468,43 @@ impl Renderer {
                 self.device.disable_scissor();
             }
 
             // At the end of rendering a container, blit across any cache tiles
             // to the texture cache for use on subsequent frames.
             if !alpha_batch_container.tile_blits.is_empty() {
                 let _timer = self.gpu_profile.start_timer(GPU_TAG_BLIT);
 
-                self.device.bind_read_target(draw_target.into());
-
                 for blit in &alpha_batch_container.tile_blits {
                     let texture = self.texture_resolver
                         .resolve(&blit.target.texture_id)
                         .expect("BUG: invalid target texture");
 
                     let blit_target = DrawTarget::Texture {
                         texture,
                         layer: blit.target.texture_layer as usize,
                         with_depth: false,
                     };
-                    self.device.bind_draw_target(blit_target);
 
                     let src_rect = draw_target.to_framebuffer_rect(DeviceIntRect::new(
                         blit.src_offset - content_origin.to_vector(),
                         blit.size,
                     ));
 
                     let target_rect = blit.target.uv_rect.to_i32();
 
                     let dest_rect = blit_target.to_framebuffer_rect(DeviceIntRect::new(
                         blit.dest_offset + (target_rect.origin - content_origin),
                         blit.size,
                     ));
 
                     self.device.blit_render_target_invert_y(
+                        draw_target.into(),
                         src_rect,
+                        blit_target,
                         dest_rect,
                     );
                 }
 
                 self.device.bind_draw_target(draw_target);
             }
         }
 
@@ -3526,20 +3526,20 @@ impl Renderer {
                     }
                     Entry::Occupied(mut entry) => {
                         let target = entry.get_mut();
                         target.last_access = frame_id;
                         target.fbo_id
                     }
                 };
                 let (src_rect, _) = render_tasks[output.task_id].get_target_rect();
-                self.device.bind_read_target(draw_target.into());
-                self.device.bind_external_draw_target(fbo_id);
                 self.device.blit_render_target_invert_y(
+                    draw_target.into(),
                     draw_target.to_framebuffer_rect(src_rect.translate(&-content_origin.to_vector())),
+                    DrawTarget::External { fbo: fbo_id, size: output_size.into() },
                     output_size.into(),
                 );
                 handler.unlock(output.pipeline_id);
             }
         }
     }
 
     /// Draw all the instances in a clip batcher list to the current target.
@@ -4442,36 +4442,36 @@ impl Renderer {
                 1,
             );
 
             self.zoom_debug_texture = Some(texture);
         }
 
         // Copy frame buffer into the zoom texture
         let read_target = DrawTarget::new_default(framebuffer_size);
-        self.device.bind_read_target(read_target.into());
-        self.device.bind_draw_target(DrawTarget::Texture {
-            texture: self.zoom_debug_texture.as_ref().unwrap(),
-            layer: 0,
-            with_depth: false,
-        });
         self.device.blit_render_target(
+            read_target.into(),
             read_target.to_framebuffer_rect(source_rect),
+            DrawTarget::Texture {
+                texture: self.zoom_debug_texture.as_ref().unwrap(),
+                layer: 0,
+                with_depth: false,
+            },
             texture_rect,
             TextureFilter::Nearest,
         );
 
         // Draw the zoom texture back to the framebuffer
-        self.device.bind_read_target(ReadTarget::Texture {
-            texture: self.zoom_debug_texture.as_ref().unwrap(),
-            layer: 0,
-        });
-        self.device.bind_draw_target(read_target);
         self.device.blit_render_target(
+            ReadTarget::Texture {
+                texture: self.zoom_debug_texture.as_ref().unwrap(),
+                layer: 0,
+            },
             texture_rect,
+            read_target,
             read_target.to_framebuffer_rect(target_rect),
             TextureFilter::Nearest,
         );
     }
 
     fn draw_texture_cache_debug(&mut self, framebuffer_size: FramebufferIntSize) {
         if !self.debug_flags.contains(DebugFlags::TEXTURE_CACHE_DBG) {
             return;
@@ -4539,18 +4539,16 @@ impl Renderer {
             let dimensions = texture.get_dimensions();
             let src_rect = FramebufferIntRect::new(
                 FramebufferIntPoint::zero(),
                 FramebufferIntSize::new(dimensions.width as i32, dimensions.height as i32),
             );
 
             let layer_count = texture.get_layer_count() as usize;
             for layer in 0 .. layer_count {
-                device.bind_read_target(ReadTarget::Texture { texture, layer});
-
                 let x = fb_width - (spacing + size) * (i as i32 + 1);
 
                 // If we have more targets than fit on one row in screen, just early exit.
                 if x > fb_width {
                     return;
                 }
 
                 //TODO: properly use FramebufferPixel coordinates
@@ -4580,17 +4578,19 @@ impl Renderer {
                     Some(text_rect.to_f32())
                 );
 
                 // Blit the contents of the layer. We need to invert Y because
                 // we're blitting from a texture to the main framebuffer, which
                 // use different conventions.
                 let dest_rect = rect(x, y + tag_height, size, size);
                 device.blit_render_target_invert_y(
+                    ReadTarget::Texture { texture, layer },
                     src_rect,
+                    DrawTarget::new_default(framebuffer_size),
                     FramebufferIntRect::from_untyped(&dest_rect),
                 );
                 i += 1;
             }
         }
     }
 
     fn draw_epoch_debug(&mut self) {