Bug 1522022 - Inflate picture caching dirty rects when encountering a blur filter. r=emilio
authorGlenn Watson <github@intuitionlibrary.com>
Wed, 23 Jan 2019 03:17:56 +0000
changeset 514947 4c5506d94111dc00ed8bfa200d82606e5d5aab3a
parent 514946 349af469066d1d484381e988b38de70d98ca2e06
child 514948 221f90b839ec18b16a58bd24f7827f4c51dfca48
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1522022
milestone66.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 1522022 - Inflate picture caching dirty rects when encountering a blur filter. r=emilio Differential Revision: https://phabricator.services.mozilla.com/D17316
gfx/wr/webrender/src/batch.rs
gfx/wr/webrender/src/frame_builder.rs
gfx/wr/webrender/src/picture.rs
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -1142,17 +1142,19 @@ impl AlphaBatchBuilder {
                                         }
 
                                         // Collect the list of regions to scissor and repeat
                                         // the draw calls into, based on dirty rects.
                                         let batch_regions = tile_cache
                                             .dirty_region
                                             .dirty_rects
                                             .iter()
-                                            .map(|dirty_rect| dirty_rect.device_rect)
+                                            .map(|dirty_rect| {
+                                                (dirty_rect.world_rect * ctx.device_pixel_scale).round().to_i32()
+                                            })
                                             .collect();
 
                                         self.push_new_batch_list(
                                             batch_regions,
                                             tile_blits,
                                         );
 
                                         self.add_pic_to_batch(
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -136,16 +136,17 @@ pub struct PictureContext {
     pub apply_local_clip_rect: bool,
     pub allow_subpixel_aa: bool,
     pub is_passthrough: bool,
     pub raster_space: RasterSpace,
     pub surface_spatial_node_index: SpatialNodeIndex,
     pub raster_spatial_node_index: SpatialNodeIndex,
     /// The surface that this picture will render on.
     pub surface_index: SurfaceIndex,
+    pub dirty_region_count: usize,
 }
 
 /// Mutable state of a picture that gets modified when
 /// the children are processed.
 pub struct PictureState {
     pub is_cacheable: bool,
     pub map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel>,
     pub map_pic_to_world: SpaceMapper<PicturePixel, WorldPixel>,
@@ -367,17 +368,16 @@ impl FrameBuilder {
         };
 
         // Push a default dirty region which culls primitives
         // against the screen world rect, in absence of any
         // other dirty regions.
         let mut default_dirty_region = DirtyRegion::new();
         default_dirty_region.push(
             frame_context.screen_world_rect,
-            frame_context.device_pixel_scale,
         );
         frame_state.push_dirty_region(default_dirty_region);
 
         let (pic_context, mut pic_state, mut prim_list) = self
             .prim_store
             .pictures[self.root_pic_index.0]
             .take_context(
                 self.root_pic_index,
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -344,17 +344,16 @@ impl TileDescriptor {
         true
     }
 }
 
 /// Stores both the world and devices rects for a single dirty rect.
 #[derive(Debug, Clone)]
 pub struct DirtyRegionRect {
     pub world_rect: WorldRect,
-    pub device_rect: DeviceIntRect,
 }
 
 /// Represents the dirty region of a tile cache picture.
 #[derive(Debug, Clone)]
 pub struct DirtyRegion {
     /// The individual dirty rects of this region.
     pub dirty_rects: Vec<DirtyRegionRect>,
 
@@ -364,80 +363,93 @@ pub struct DirtyRegion {
 
 impl DirtyRegion {
     /// Construct a new dirty region tracker.
     pub fn new() -> Self {
         DirtyRegion {
             dirty_rects: Vec::with_capacity(MAX_DIRTY_RECTS),
             combined: DirtyRegionRect {
                 world_rect: WorldRect::zero(),
-                device_rect: DeviceIntRect::zero(),
             },
         }
     }
 
     /// Reset the dirty regions back to empty
     pub fn clear(&mut self) {
         self.dirty_rects.clear();
         self.combined = DirtyRegionRect {
             world_rect: WorldRect::zero(),
-            device_rect: DeviceIntRect::zero(),
         }
     }
 
     /// Push a dirty rect into this region
     pub fn push(
         &mut self,
         rect: WorldRect,
-        device_pixel_scale: DevicePixelScale,
     ) {
-        let device_rect = (rect * device_pixel_scale).round().to_i32();
-
         // Include this in the overall dirty rect
         self.combined.world_rect = self.combined.world_rect.union(&rect);
-        self.combined.device_rect = self.combined.device_rect.union(&device_rect);
 
         // Store the individual dirty rect.
         self.dirty_rects.push(DirtyRegionRect {
             world_rect: rect,
-            device_rect,
         });
     }
 
     /// Returns true if this region has no dirty rects
     pub fn is_empty(&self) -> bool {
         self.dirty_rects.is_empty()
     }
 
     /// Collapse all dirty rects into a single dirty rect.
     pub fn collapse(&mut self) {
         self.dirty_rects.clear();
         self.dirty_rects.push(self.combined.clone());
     }
+
+    pub fn inflate(
+        &self,
+        inflate_amount: f32,
+    ) -> DirtyRegion {
+        let mut dirty_rects = Vec::with_capacity(self.dirty_rects.len());
+        let mut combined = DirtyRegionRect {
+            world_rect: WorldRect::zero(),
+        };
+
+        for rect in &self.dirty_rects {
+            let world_rect = rect.world_rect.inflate(inflate_amount, inflate_amount);
+            combined.world_rect = combined.world_rect.union(&world_rect);
+            dirty_rects.push(DirtyRegionRect {
+                world_rect,
+            });
+        }
+
+        DirtyRegion {
+            dirty_rects,
+            combined,
+        }
+    }
 }
 
 /// A helper struct to build a (roughly) minimal set of dirty rectangles
 /// from a list of individual dirty rectangles. This minimizes the number
 /// of scissors rects and batch resubmissions that are needed.
 struct DirtyRegionBuilder<'a> {
     tiles: &'a mut [Tile],
     tile_count: TileSize,
-    device_pixel_scale: DevicePixelScale,
 }
 
 impl<'a> DirtyRegionBuilder<'a> {
     fn new(
         tiles: &'a mut [Tile],
         tile_count: TileSize,
-        device_pixel_scale: DevicePixelScale,
     ) -> Self {
         DirtyRegionBuilder {
             tiles,
             tile_count,
-            device_pixel_scale,
         }
     }
 
     fn tile_index(&self, x: i32, y: i32) -> usize {
         (y * self.tile_count.width + x) as usize
     }
 
     fn is_dirty(&self, x: i32, y: i32) -> bool {
@@ -485,17 +497,17 @@ impl<'a> DirtyRegionBuilder<'a> {
                 let tile = self.get_tile_mut(x, y);
                 tile.consider_for_dirty_rect = false;
                 if let Some(visible_rect) = tile.visible_rect {
                     dirty_world_rect = dirty_world_rect.union(&visible_rect);
                 }
             }
         }
 
-        dirty_region.push(dirty_world_rect, self.device_pixel_scale);
+        dirty_region.push(dirty_world_rect);
     }
 
     /// Simple sweep through the tile grid to try and coalesce individual
     /// dirty rects into a smaller number of larger dirty rectangles.
     fn build(&mut self, dirty_region: &mut DirtyRegion) {
         for x0 in 0 .. self.tile_count.width {
             for y0 in 0 .. self.tile_count.height {
                 let mut y1 = y0;
@@ -1455,17 +1467,16 @@ impl TileCache {
             }
         }
 
         // Build a minimal set of dirty rects from the set of dirty tiles that
         // were found above.
         let mut builder = DirtyRegionBuilder::new(
             &mut self.tiles,
             self.tile_count,
-            frame_context.device_pixel_scale,
         );
 
         builder.build(&mut self.dirty_region);
 
         // If we end up with too many dirty rects, then it's going to be a lot
         // of extra draw calls to submit (since we currently just submit every
         // draw call for every dirty rect). In this case, bail out and work
         // with a single, large dirty rect. In future we can consider improving
@@ -2082,24 +2093,34 @@ impl PicturePrimitive {
     ) -> Option<(PictureContext, PictureState, PrimitiveList)> {
         if !self.is_visible() {
             return None;
         }
 
         // Extract the raster and surface spatial nodes from the raster
         // config, if this picture establishes a surface. Otherwise just
         // pass in the spatial node indices from the parent context.
-        let (raster_spatial_node_index, surface_spatial_node_index, surface_index) = match self.raster_config {
+        let (raster_spatial_node_index, surface_spatial_node_index, surface_index, inflation_factor) = match self.raster_config {
             Some(ref raster_config) => {
                 let surface = &frame_state.surfaces[raster_config.surface_index.0];
 
-                (surface.raster_spatial_node_index, self.spatial_node_index, raster_config.surface_index)
+                (
+                    surface.raster_spatial_node_index,
+                    self.spatial_node_index,
+                    raster_config.surface_index,
+                    surface.inflation_factor,
+                )
             }
             None => {
-                (raster_spatial_node_index, surface_spatial_node_index, surface_index)
+                (
+                    raster_spatial_node_index,
+                    surface_spatial_node_index,
+                    surface_index,
+                    0.0,
+                )
             }
         };
 
         let map_pic_to_world = SpaceMapper::new_with_target(
             ROOT_SPATIAL_NODE_INDEX,
             surface_spatial_node_index,
             frame_context.screen_world_rect,
             frame_context.clip_scroll_tree,
@@ -2155,48 +2176,58 @@ impl PicturePrimitive {
             }
             None => {
                 true
             }
         };
         // Still disable subpixel AA if parent forbids it
         let allow_subpixel_aa = parent_allows_subpixel_aa && allow_subpixel_aa;
 
+        let mut dirty_region_count = 0;
+
+        // If this is a picture cache, push the dirty region to ensure any
+        // child primitives are culled and clipped to the dirty rect(s).
+        if let Some(ref tile_cache) = self.tile_cache {
+            frame_state.push_dirty_region(tile_cache.dirty_region.clone());
+            dirty_region_count += 1;
+        }
+
+        if inflation_factor > 0.0 {
+            let inflated_region = frame_state.current_dirty_region().inflate(inflation_factor);
+            frame_state.push_dirty_region(inflated_region);
+            dirty_region_count += 1;
+        }
+
         let context = PictureContext {
             pic_index,
             pipeline_id: self.pipeline_id,
             apply_local_clip_rect: self.apply_local_clip_rect,
             allow_subpixel_aa,
             is_passthrough: self.raster_config.is_none(),
             raster_space: self.requested_raster_space,
             raster_spatial_node_index,
             surface_spatial_node_index,
             surface_index,
+            dirty_region_count,
         };
 
-        // If this is a picture cache, push the dirty region to ensure any
-        // child primitives are culled and clipped to the dirty rect(s).
-        if let Some(ref tile_cache) = self.tile_cache {
-            frame_state.push_dirty_region(tile_cache.dirty_region.clone());
-        }
-
         let prim_list = mem::replace(&mut self.prim_list, PrimitiveList::empty());
 
         Some((context, state, prim_list))
     }
 
     pub fn restore_context(
         &mut self,
         prim_list: PrimitiveList,
         context: PictureContext,
         state: PictureState,
         frame_state: &mut FrameBuildingState,
     ) {
-        // Pop the dirty region for this picture cache
-        if self.tile_cache.is_some() {
+        // Pop any dirty regions this picture set
+        for _ in 0 .. context.dirty_region_count {
             frame_state.pop_dirty_region();
         }
 
         self.prim_list = prim_list;
         self.state = Some((state, context));
     }
 
     pub fn take_state_and_context(&mut self) -> (PictureState, PictureContext) {