Bug 1549921 - Refactor batch building code in preparation for multiple alpha batchers per picture. r=kvark
authorGlenn Watson <github@intuitionlibrary.com>
Wed, 08 May 2019 20:32:39 +0000
changeset 531935 b54d90e1c6814f37918a8a6a1e6fc5054ea6e868
parent 531934 6dd8d9bf3e308e62c4a6f2eacd7b7379d1ad3484
child 531936 7e3b4a30cfd741ff493ca9100bf82c4bb30fe31b
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskvark
bugs1549921
milestone68.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 1549921 - Refactor batch building code in preparation for multiple alpha batchers per picture. r=kvark This patch is scaffolding only - there shouldn't be any functional changes as a result of these changes. Differential Revision: https://phabricator.services.mozilla.com/D30330
gfx/wr/webrender/src/batch.rs
gfx/wr/webrender/src/render_task.rs
gfx/wr/webrender/src/tiling.rs
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -461,41 +461,36 @@ struct SegmentInstanceData {
     textures: BatchTextures,
     user_data: i32,
 }
 
 /// Encapsulates the logic of building batches for items that are blended.
 pub struct AlphaBatchBuilder {
     pub batch_lists: Vec<BatchList>,
     screen_size: DeviceIntSize,
-    task_scissor_rect: Option<DeviceIntRect>,
-    glyph_fetch_buffer: Vec<GlyphFetchResult>,
     break_advanced_blend_batches: bool,
 }
 
 impl AlphaBatchBuilder {
     pub fn new(
         screen_size: DeviceIntSize,
-        task_scissor_rect: Option<DeviceIntRect>,
         break_advanced_blend_batches: bool,
     ) -> Self {
         let batch_lists = vec![
             BatchList::new(
                 screen_size,
                 Vec::new(),
                 Vec::new(),
                 break_advanced_blend_batches,
             ),
         ];
 
         AlphaBatchBuilder {
             batch_lists,
-            task_scissor_rect,
             screen_size,
-            glyph_fetch_buffer: Vec::new(),
             break_advanced_blend_batches,
         }
     }
 
     fn push_new_batch_list(
         &mut self,
         regions: Vec<DeviceIntRect>,
         tile_blits: Vec<TileBlit>,
@@ -508,67 +503,89 @@ impl AlphaBatchBuilder {
         ));
     }
 
     fn current_batch_list(&mut self) -> &mut BatchList {
         self.batch_lists.last_mut().unwrap()
     }
 
     fn can_merge(&self) -> bool {
-        self.task_scissor_rect.is_none() &&
         self.batch_lists.len() == 1
     }
 
     pub fn build(
         mut self,
         batch_containers: &mut Vec<AlphaBatchContainer>,
         merged_batches: &mut AlphaBatchContainer,
         task_rect: DeviceIntRect,
+        task_scissor_rect: Option<DeviceIntRect>,
     ) {
         for batch_list in &mut self.batch_lists {
             batch_list.finalize();
         }
 
-        if self.can_merge() {
+        if task_scissor_rect.is_none() && self.can_merge() {
             let batch_list = self.batch_lists.pop().unwrap();
             debug_assert!(batch_list.tile_blits.is_empty());
             merged_batches.merge(batch_list, &task_rect);
         } else {
             for batch_list in self.batch_lists {
                 batch_containers.push(AlphaBatchContainer {
                     alpha_batches: batch_list.alpha_batch_list.batches,
                     opaque_batches: batch_list.opaque_batch_list.batches,
-                    task_scissor_rect: self.task_scissor_rect,
+                    task_scissor_rect,
                     regions: batch_list.regions,
                     tile_blits: batch_list.tile_blits,
                     task_rect,
                 });
             }
         }
     }
+}
 
+/// Supports (recursively) adding a list of primitives and pictures to an alpha batch
+/// builder. In future, it will support multiple dirty regions / slices, allowing the
+/// contents of a picture to be spliced into multiple batch builders.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct BatchBuilder {
+    /// A temporary buffer that is used during glyph fetching, stored here
+    /// to reduce memory allocations.
+    glyph_fetch_buffer: Vec<GlyphFetchResult>,
+}
+
+impl BatchBuilder {
+    pub fn new() -> Self {
+        BatchBuilder {
+            glyph_fetch_buffer: Vec::new(),
+        }
+    }
+
+    /// Add a picture to a given batch builder.
     pub fn add_pic_to_batch(
         &mut self,
         pic: &PicturePrimitive,
+        batcher: &mut AlphaBatchBuilder,
         task_id: RenderTaskId,
         ctx: &RenderTargetContext,
         gpu_cache: &mut GpuCache,
         render_tasks: &RenderTaskTree,
         deferred_resolves: &mut Vec<DeferredResolve>,
         prim_headers: &mut PrimitiveHeaders,
         transforms: &mut TransformPalette,
         root_spatial_node_index: SpatialNodeIndex,
         z_generator: &mut ZBufferIdGenerator,
     ) {
         let task_address = render_tasks.get_task_address(task_id);
 
         // Add each run in this picture to the batch.
         for prim_instance in &pic.prim_list.prim_instances {
             self.add_prim_to_batch(
                 prim_instance,
+                batcher,
                 ctx,
                 gpu_cache,
                 render_tasks,
                 task_id,
                 task_address,
                 deferred_resolves,
                 prim_headers,
                 transforms,
@@ -580,16 +597,17 @@ impl AlphaBatchBuilder {
 
     // Adds a primitive to a batch.
     // It can recursively call itself in some situations, for
     // example if it encounters a picture where the items
     // in that picture are being drawn into the same target.
     fn add_prim_to_batch(
         &mut self,
         prim_instance: &PrimitiveInstance,
+        batcher: &mut AlphaBatchBuilder,
         ctx: &RenderTargetContext,
         gpu_cache: &mut GpuCache,
         render_tasks: &RenderTaskTree,
         task_id: RenderTaskId,
         task_address: RenderTaskAddress,
         deferred_resolves: &mut Vec<DeferredResolve>,
         prim_headers: &mut PrimitiveHeaders,
         transforms: &mut TransformPalette,
@@ -671,17 +689,17 @@ impl AlphaBatchBuilder {
                     segment_index: INVALID_SEGMENT_INDEX,
                     edge_flags: EdgeAaSegmentMask::all(),
                     clip_task_address,
                     brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
                     prim_header_index,
                     user_data: 0,
                 });
 
-                self.current_batch_list().push_single_instance(
+                batcher.current_batch_list().push_single_instance(
                     batch_key,
                     bounding_rect,
                     z_id,
                     PrimitiveInstanceData::from(instance),
                 );
             }
             PrimitiveInstanceKind::NormalBorder { data_handle, ref cache_handles, .. } => {
                 let prim_data = &ctx.data_stores.normal_border[data_handle];
@@ -739,16 +757,17 @@ impl AlphaBatchBuilder {
                 let prim_header_index = prim_headers.push(
                     &prim_header,
                     z_id,
                     batch_params.prim_user_data,
                 );
 
                 let border_data = &prim_data.kind;
                 self.add_segmented_prim_to_batch(
+                    batcher,
                     Some(border_data.brush_segments.as_slice()),
                     common_data.opacity,
                     &batch_params,
                     specified_blend_mode,
                     non_segmented_blend_mode,
                     prim_header_index,
                     bounding_rect,
                     transform_kind,
@@ -760,18 +779,17 @@ impl AlphaBatchBuilder {
             }
             PrimitiveInstanceKind::TextRun { data_handle, run_index, .. } => {
                 let run = &ctx.prim_store.text_runs[run_index];
                 let subpx_dir = run.used_font.get_subpx_dir();
 
                 // The GPU cache data is stored in the template and reused across
                 // frames and display lists.
                 let prim_data = &ctx.data_stores.text_run[data_handle];
-                let glyph_fetch_buffer = &mut self.glyph_fetch_buffer;
-                let alpha_batch_list = &mut self.batch_lists.last_mut().unwrap().alpha_batch_list;
+                let alpha_batch_list = &mut batcher.batch_lists.last_mut().unwrap().alpha_batch_list;
                 let prim_cache_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
 
                 let prim_header = PrimitiveHeader {
                     local_rect: prim_rect,
                     local_clip_rect: prim_info.combined_local_clip_rect,
                     snap_offsets,
                     task_address,
                     specific_prim_address: prim_cache_address,
@@ -783,17 +801,17 @@ impl AlphaBatchBuilder {
                     render_tasks,
                 ).unwrap_or(OPAQUE_TASK_ADDRESS);
 
                 let glyph_keys = &ctx.scratch.glyph_keys[run.glyph_keys_range];
 
                 ctx.resource_cache.fetch_glyphs(
                     run.used_font.clone(),
                     &glyph_keys,
-                    glyph_fetch_buffer,
+                    &mut self.glyph_fetch_buffer,
                     gpu_cache,
                     |texture_id, mut glyph_format, glyphs| {
                         debug_assert_ne!(texture_id, TextureSource::Invalid);
 
                         // Ignore color and only sample alpha when shadowing.
                         if run.shadow {
                             glyph_format = glyph_format.ignore_color();
                         }
@@ -967,17 +985,17 @@ impl AlphaBatchBuilder {
                     segment_index: INVALID_SEGMENT_INDEX,
                     edge_flags: EdgeAaSegmentMask::all(),
                     clip_task_address,
                     brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
                     prim_header_index,
                     user_data: segment_user_data,
                 });
 
-                self.current_batch_list().push_single_instance(
+                batcher.current_batch_list().push_single_instance(
                     batch_key,
                     bounding_rect,
                     z_id,
                     PrimitiveInstanceData::from(instance),
                 );
             }
             PrimitiveInstanceKind::Picture { pic_index, segment_instance_index, .. } => {
                 let picture = &ctx.prim_store.pictures[pic_index.0];
@@ -1063,17 +1081,17 @@ impl AlphaBatchBuilder {
                             );
 
                             let instance = SplitCompositeInstance::new(
                                 prim_header_index,
                                 child.gpu_address,
                                 z_id,
                             );
 
-                            self.current_batch_list().push_single_instance(
+                            batcher.current_batch_list().push_single_instance(
                                 key,
                                 &prim_info.clip_chain.pic_clip_rect,
                                 z_id,
                                 PrimitiveInstanceData::from(instance),
                             );
                         }
                     }
                     // Ignore the 3D pictures that are not in the root of preserve-3D
@@ -1108,16 +1126,17 @@ impl AlphaBatchBuilder {
                                 let tile_cache = picture.tile_cache.as_ref().unwrap();
 
                                 // If the tile cache is disabled, just recurse into the
                                 // picture like a normal pass-through picture, adding
                                 // any child primitives into the parent surface batches.
                                 if !tile_cache.is_enabled {
                                     self.add_pic_to_batch(
                                         picture,
+                                        batcher,
                                         task_id,
                                         ctx,
                                         gpu_cache,
                                         render_tasks,
                                         deferred_resolves,
                                         prim_headers,
                                         transforms,
                                         root_spatial_node_index,
@@ -1195,17 +1214,17 @@ impl AlphaBatchBuilder {
                                             brush_flags,
                                             user_data: uv_rect_address,
                                         };
 
                                         // Instead of retrieving the batch once and adding each tile instance,
                                         // use this API to get an appropriate batch for each tile, since
                                         // the batch textures may be different. The batch list internally
                                         // caches the current batch if the key hasn't changed.
-                                        let batch = self.current_batch_list().set_params_and_get_batch(
+                                        let batch = batcher.current_batch_list().set_params_and_get_batch(
                                             key,
                                             bounding_rect,
                                             z_id,
                                         );
 
                                         batch.push(PrimitiveInstanceData::from(instance));
                                     }
 
@@ -1234,35 +1253,36 @@ impl AlphaBatchBuilder {
                                             .dirty_region
                                             .dirty_rects
                                             .iter()
                                             .map(|dirty_rect| {
                                                 (dirty_rect.world_rect * ctx.global_device_pixel_scale).round().to_i32()
                                             })
                                             .collect();
 
-                                        self.push_new_batch_list(
+                                        batcher.push_new_batch_list(
                                             batch_regions,
                                             tile_blits,
                                         );
 
                                         self.add_pic_to_batch(
                                             picture,
+                                            batcher,
                                             task_id,
                                             ctx,
                                             gpu_cache,
                                             render_tasks,
                                             deferred_resolves,
                                             prim_headers,
                                             transforms,
                                             root_spatial_node_index,
                                             z_generator,
                                         );
 
-                                        self.push_new_batch_list(
+                                        batcher.push_new_batch_list(
                                             Vec::new(),
                                             Vec::new(),
                                         );
                                     }
                                 }
                             }
                             PictureCompositeMode::Filter(ref filter) => {
                                 assert!(filter.is_visible());
@@ -1291,17 +1311,17 @@ impl AlphaBatchBuilder {
                                             prim_header_index,
                                             segment_index: INVALID_SEGMENT_INDEX,
                                             edge_flags: EdgeAaSegmentMask::empty(),
                                             brush_flags,
                                             clip_task_address,
                                             user_data: uv_rect_address.as_int(),
                                         };
 
-                                        self.current_batch_list().push_single_instance(
+                                        batcher.current_batch_list().push_single_instance(
                                             key,
                                             bounding_rect,
                                             z_id,
                                             PrimitiveInstanceData::from(instance),
                                         );
                                     }
                                     FilterOp::DropShadow(offset, ..) => {
                                         // Draw an instance of the shadow first, following by the content.
@@ -1382,24 +1402,24 @@ impl AlphaBatchBuilder {
                                             prim_header_index: content_prim_header_index,
                                             clip_task_address,
                                             segment_index: INVALID_SEGMENT_INDEX,
                                             edge_flags: EdgeAaSegmentMask::empty(),
                                             brush_flags,
                                             user_data: content_uv_rect_address,
                                         };
 
-                                        self.current_batch_list().push_single_instance(
+                                        batcher.current_batch_list().push_single_instance(
                                             shadow_key,
                                             bounding_rect,
                                             z_id_shadow,
                                             PrimitiveInstanceData::from(shadow_instance),
                                         );
 
-                                        self.current_batch_list().push_single_instance(
+                                        batcher.current_batch_list().push_single_instance(
                                             content_key,
                                             bounding_rect,
                                             z_id_content,
                                             PrimitiveInstanceData::from(content_instance),
                                         );
                                     }
                                     _ => {
                                         let filter_mode = match filter {
@@ -1468,17 +1488,17 @@ impl AlphaBatchBuilder {
                                             prim_header_index,
                                             clip_task_address,
                                             segment_index: INVALID_SEGMENT_INDEX,
                                             edge_flags: EdgeAaSegmentMask::empty(),
                                             brush_flags,
                                             user_data: 0,
                                         };
 
-                                        self.current_batch_list().push_single_instance(
+                                        batcher.current_batch_list().push_single_instance(
                                             key,
                                             bounding_rect,
                                             z_id,
                                             PrimitiveInstanceData::from(instance),
                                         );
                                     }
                                 }
                             }
@@ -1517,17 +1537,17 @@ impl AlphaBatchBuilder {
                                     prim_header_index,
                                     clip_task_address,
                                     segment_index: INVALID_SEGMENT_INDEX,
                                     edge_flags: EdgeAaSegmentMask::empty(),
                                     brush_flags,
                                     user_data: 0,
                                 };
 
-                                self.current_batch_list().push_single_instance(
+                                batcher.current_batch_list().push_single_instance(
                                     key,
                                     bounding_rect,
                                     z_id,
                                     PrimitiveInstanceData::from(instance),
                                 );
                             }
                             PictureCompositeMode::MixBlend(mode) if ctx.use_advanced_blending => {
                                 let (uv_rect_address, textures) = render_tasks.resolve_surface(
@@ -1552,17 +1572,17 @@ impl AlphaBatchBuilder {
                                     prim_header_index,
                                     clip_task_address,
                                     segment_index: INVALID_SEGMENT_INDEX,
                                     edge_flags: EdgeAaSegmentMask::empty(),
                                     brush_flags,
                                     user_data: uv_rect_address.as_int(),
                                 };
 
-                                self.current_batch_list().push_single_instance(
+                                batcher.current_batch_list().push_single_instance(
                                     key,
                                     bounding_rect,
                                     z_id,
                                     PrimitiveInstanceData::from(instance),
                                 );
                             }
                             PictureCompositeMode::MixBlend(mode) => {
                                 let cache_task_id = surface.expect("bug: surface must be allocated by now");
@@ -1592,17 +1612,17 @@ impl AlphaBatchBuilder {
                                     prim_header_index,
                                     clip_task_address,
                                     segment_index: INVALID_SEGMENT_INDEX,
                                     edge_flags: EdgeAaSegmentMask::empty(),
                                     brush_flags,
                                     user_data: 0,
                                 };
 
-                                self.current_batch_list().push_single_instance(
+                                batcher.current_batch_list().push_single_instance(
                                     key,
                                     bounding_rect,
                                     z_id,
                                     PrimitiveInstanceData::from(instance),
                                 );
                             }
                             PictureCompositeMode::Blit(_) => {
                                 let cache_task_id = surface.expect("bug: surface must be allocated by now");
@@ -1653,16 +1673,17 @@ impl AlphaBatchBuilder {
                                 //           simple, common cases) if the picture content is opaque.
                                 //           That would allow inner segments of pictures to be drawn
                                 //           with blend disabled, which is a big performance win on
                                 //           integrated GPUs.
                                 let opacity = PrimitiveOpacity::translucent();
                                 let specified_blend_mode = BlendMode::PremultipliedAlpha;
 
                                 self.add_segmented_prim_to_batch(
+                                    batcher,
                                     segments,
                                     opacity,
                                     &batch_params,
                                     specified_blend_mode,
                                     non_segmented_blend_mode,
                                     prim_header_index,
                                     bounding_rect,
                                     transform_kind,
@@ -1674,16 +1695,17 @@ impl AlphaBatchBuilder {
                             }
                         }
                     }
                     None => {
                         // If this picture is being drawn into an existing target (i.e. with
                         // no composition operation), recurse and add to the current batch list.
                         self.add_pic_to_batch(
                             picture,
+                            batcher,
                             task_id,
                             ctx,
                             gpu_cache,
                             render_tasks,
                             deferred_resolves,
                             prim_headers,
                             transforms,
                             root_spatial_node_index,
@@ -1742,16 +1764,17 @@ impl AlphaBatchBuilder {
 
                 let prim_header_index = prim_headers.push(
                     &prim_header,
                     z_id,
                     batch_params.prim_user_data,
                 );
 
                 self.add_segmented_prim_to_batch(
+                    batcher,
                     Some(border_data.brush_segments.as_slice()),
                     common_data.opacity,
                     &batch_params,
                     specified_blend_mode,
                     non_segmented_blend_mode,
                     prim_header_index,
                     bounding_rect,
                     transform_kind,
@@ -1804,16 +1827,17 @@ impl AlphaBatchBuilder {
 
                 let prim_header_index = prim_headers.push(
                     &prim_header,
                     z_id,
                     batch_params.prim_user_data,
                 );
 
                 self.add_segmented_prim_to_batch(
+                    batcher,
                     segments,
                     opacity,
                     &batch_params,
                     specified_blend_mode,
                     non_segmented_blend_mode,
                     prim_header_index,
                     bounding_rect,
                     transform_kind,
@@ -1912,16 +1936,17 @@ impl AlphaBatchBuilder {
 
                 let prim_header_index = prim_headers.push(
                     &prim_header,
                     z_id,
                     batch_params.prim_user_data,
                 );
 
                 self.add_segmented_prim_to_batch(
+                    batcher,
                     segments,
                     prim_common_data.opacity,
                     &batch_params,
                     specified_blend_mode,
                     non_segmented_blend_mode,
                     prim_header_index,
                     bounding_rect,
                     transform_kind,
@@ -2017,16 +2042,17 @@ impl AlphaBatchBuilder {
 
                     let prim_header_index = prim_headers.push(
                         &prim_header,
                         z_id,
                         batch_params.prim_user_data,
                     );
 
                     self.add_segmented_prim_to_batch(
+                        batcher,
                         segments,
                         opacity,
                         &batch_params,
                         specified_blend_mode,
                         non_segmented_blend_mode,
                         prim_header_index,
                         bounding_rect,
                         transform_kind,
@@ -2084,17 +2110,17 @@ impl AlphaBatchBuilder {
                                     brush_flags: BrushFlags::SEGMENT_RELATIVE | BrushFlags::PERSPECTIVE_INTERPOLATION,
                                     user_data: uv_rect_address.as_int(),
                                 };
                                 let batch_key = BatchKey {
                                     blend_mode: specified_blend_mode,
                                     kind: BatchKind::Brush(batch_kind),
                                     textures,
                                 };
-                                self.current_batch_list().push_single_instance(
+                                batcher.current_batch_list().push_single_instance(
                                     batch_key,
                                     bounding_rect,
                                     z_id,
                                     base_instance.into(),
                                 );
                             }
                         }
                     }
@@ -2165,17 +2191,17 @@ impl AlphaBatchBuilder {
                         segment_index: INVALID_SEGMENT_INDEX,
                         edge_flags: EdgeAaSegmentMask::all(),
                         clip_task_address,
                         brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
                         prim_header_index,
                         user_data: segment_user_data,
                     });
 
-                    self.current_batch_list().push_single_instance(
+                    batcher.current_batch_list().push_single_instance(
                         batch_key,
                         bounding_rect,
                         z_id,
                         PrimitiveInstanceData::from(instance),
                     );
                 } else if gradient.visible_tiles_range.is_empty() {
                     let batch_params = BrushBatchParameters::shared(
                         BrushBatchKind::LinearGradient,
@@ -2199,16 +2225,17 @@ impl AlphaBatchBuilder {
 
                     let segments = if prim_data.brush_segments.is_empty() {
                         None
                     } else {
                         Some(prim_data.brush_segments.as_slice())
                     };
 
                     self.add_segmented_prim_to_batch(
+                        batcher,
                         segments,
                         prim_data.opacity,
                         &batch_params,
                         specified_blend_mode,
                         non_segmented_blend_mode,
                         prim_header_index,
                         bounding_rect,
                         transform_kind,
@@ -2228,17 +2255,17 @@ impl AlphaBatchBuilder {
                     add_gradient_tiles(
                         visible_tiles,
                         &prim_data.stops_handle,
                         BrushBatchKind::LinearGradient,
                         specified_blend_mode,
                         bounding_rect,
                         clip_task_address,
                         gpu_cache,
-                        self.current_batch_list(),
+                        batcher.current_batch_list(),
                         &prim_header,
                         prim_headers,
                         z_id,
                     );
                 }
             }
             PrimitiveInstanceKind::RadialGradient { data_handle, ref visible_tiles_range, .. } => {
                 let prim_data = &ctx.data_stores.radial_grad[data_handle];
@@ -2285,16 +2312,17 @@ impl AlphaBatchBuilder {
 
                     let segments = if prim_data.brush_segments.is_empty() {
                         None
                     } else {
                         Some(prim_data.brush_segments.as_slice())
                     };
 
                     self.add_segmented_prim_to_batch(
+                        batcher,
                         segments,
                         prim_data.opacity,
                         &batch_params,
                         specified_blend_mode,
                         non_segmented_blend_mode,
                         prim_header_index,
                         bounding_rect,
                         transform_kind,
@@ -2314,29 +2342,30 @@ impl AlphaBatchBuilder {
                     add_gradient_tiles(
                         visible_tiles,
                         &prim_data.stops_handle,
                         BrushBatchKind::RadialGradient,
                         specified_blend_mode,
                         bounding_rect,
                         clip_task_address,
                         gpu_cache,
-                        self.current_batch_list(),
+                        batcher.current_batch_list(),
                         &prim_header,
                         prim_headers,
                         z_id,
                     );
                 }
             }
         }
     }
 
     /// Add a single segment instance to a batch.
     fn add_segment_to_batch(
         &mut self,
+        batcher: &mut AlphaBatchBuilder,
         segment: &BrushSegment,
         segment_data: &SegmentInstanceData,
         segment_index: i32,
         batch_kind: BrushBatchKind,
         prim_header_index: PrimitiveHeaderIndex,
         alpha_blend_mode: BlendMode,
         bounding_rect: &PictureRect,
         transform_kind: TransformedRectKind,
@@ -2375,27 +2404,28 @@ impl AlphaBatchBuilder {
         });
 
         let batch_key = BatchKey {
             blend_mode: if needs_blending { alpha_blend_mode } else { BlendMode::None },
             kind: BatchKind::Brush(batch_kind),
             textures: segment_data.textures,
         };
 
-        self.current_batch_list().push_single_instance(
+        batcher.current_batch_list().push_single_instance(
             batch_key,
             bounding_rect,
             z_id,
             instance,
         );
     }
 
     /// Add any segment(s) from a brush to batches.
     fn add_segmented_prim_to_batch(
         &mut self,
+        batcher: &mut AlphaBatchBuilder,
         brush_segments: Option<&[BrushSegment]>,
         prim_opacity: PrimitiveOpacity,
         params: &BrushBatchParameters,
         alpha_blend_mode: BlendMode,
         non_segmented_blend_mode: BlendMode,
         prim_header_index: PrimitiveHeaderIndex,
         bounding_rect: &PictureRect,
         transform_kind: TransformedRectKind,
@@ -2410,16 +2440,17 @@ impl AlphaBatchBuilder {
                 // per-segment instance data. Zip them together to build batches.
                 debug_assert_eq!(brush_segments.len(), segment_data.len());
                 for (segment_index, (segment, segment_data)) in brush_segments
                     .iter()
                     .zip(segment_data.iter())
                     .enumerate()
                 {
                     self.add_segment_to_batch(
+                        batcher,
                         segment,
                         segment_data,
                         segment_index as i32,
                         params.batch_kind,
                         prim_header_index,
                         alpha_blend_mode,
                         bounding_rect,
                         transform_kind,
@@ -2434,16 +2465,17 @@ impl AlphaBatchBuilder {
             (Some(ref brush_segments), SegmentDataKind::Shared(ref segment_data)) => {
                 // A list of segments, but the per-segment data is common
                 // between all segments.
                 for (segment_index, segment) in brush_segments
                     .iter()
                     .enumerate()
                 {
                     self.add_segment_to_batch(
+                        batcher,
                         segment,
                         segment_data,
                         segment_index as i32,
                         params.batch_kind,
                         prim_header_index,
                         alpha_blend_mode,
                         bounding_rect,
                         transform_kind,
@@ -2470,17 +2502,17 @@ impl AlphaBatchBuilder {
                 let instance = PrimitiveInstanceData::from(BrushInstance {
                     segment_index: INVALID_SEGMENT_INDEX,
                     edge_flags: EdgeAaSegmentMask::all(),
                     clip_task_address,
                     brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
                     prim_header_index,
                     user_data: segment_data.user_data,
                 });
-                self.current_batch_list().push_single_instance(
+                batcher.current_batch_list().push_single_instance(
                     batch_key,
                     bounding_rect,
                     z_id,
                     PrimitiveInstanceData::from(instance),
                 );
             }
             (None, SegmentDataKind::Instanced(..)) => {
                 // We should never hit the case where there are no segments,
@@ -3155,8 +3187,9 @@ impl<'a, 'rc> RenderTargetContext<'a, 'r
     ) -> Option<RenderTaskAddress> {
         self.get_clip_task_address(
             clip_task_index,
             0,
             render_tasks,
         )
     }
 }
+
--- a/gfx/wr/webrender/src/render_task.rs
+++ b/gfx/wr/webrender/src/render_task.rs
@@ -538,17 +538,16 @@ impl BlurTask {
     }
 }
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct ScalingTask {
     pub target_kind: RenderTargetKind,
-    pub uv_rect_handle: GpuCacheHandle,
     uv_rect_kind: UvRectKind,
 }
 
 #[derive(Debug)]
 #[cfg(feature = "pathfinder")]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct GlyphTask {
@@ -1101,17 +1100,16 @@ impl RenderTask {
     ) -> Self {
         let uv_rect_kind = render_tasks[src_task_id].uv_rect_kind();
 
         RenderTask::with_dynamic_location(
             target_size,
             vec![src_task_id],
             RenderTaskKind::Scaling(ScalingTask {
                 target_kind,
-                uv_rect_handle: GpuCacheHandle::new(),
                 uv_rect_kind,
             }),
             ClearMode::DontCare,
         )
     }
 
     #[cfg(feature = "pathfinder")]
     pub fn new_glyph(
--- a/gfx/wr/webrender/src/tiling.rs
+++ b/gfx/wr/webrender/src/tiling.rs
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ColorF, BorderStyle, MixBlendMode, PipelineId, PremultipliedColorF};
 use api::{DocumentLayer, FilterData, FilterOp, ImageFormat, LineOrientation};
 use api::units::*;
 #[cfg(feature = "pathfinder")]
 use api::FontRenderMode;
-use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, ClipBatcher, resolve_image};
+use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, ClipBatcher, resolve_image, BatchBuilder};
 use crate::clip::ClipStore;
 use crate::clip_scroll_tree::{ClipScrollTree};
 use crate::debug_render::DebugItem;
 use crate::device::{Texture};
 #[cfg(feature = "pathfinder")]
 use euclid::{TypedPoint2D, TypedVector2D};
 use crate::frame_builder::FrameGlobalResources;
 use crate::gpu_cache::{GpuCache};
@@ -369,16 +369,20 @@ pub struct ColorRenderTarget {
     // List of frame buffer outputs for this render target.
     pub outputs: Vec<FrameOutput>,
     alpha_tasks: Vec<RenderTaskId>,
     screen_size: DeviceIntSize,
     // Track the used rect of the render target, so that
     // we can set a scissor rect and only clear to the
     // used portion of the target as an optimization.
     pub used_rect: DeviceIntRect,
+    // This is used to build batches for this render target. In future,
+    // this will be used to support splitting a single picture primitive
+    // list into multiple batch sets.
+    batch_builder: BatchBuilder,
 }
 
 impl RenderTarget for ColorRenderTarget {
     fn new(
         screen_size: DeviceIntSize,
         _: bool,
     ) -> Self {
         ColorRenderTarget {
@@ -387,16 +391,17 @@ impl RenderTarget for ColorRenderTarget 
             horizontal_blurs: Vec::new(),
             readbacks: Vec::new(),
             scalings: Vec::new(),
             blits: Vec::new(),
             outputs: Vec::new(),
             alpha_tasks: Vec::new(),
             screen_size,
             used_rect: DeviceIntRect::zero(),
+            batch_builder: BatchBuilder::new(),
         }
     }
 
     fn build(
         &mut self,
         ctx: &mut RenderTargetContext,
         gpu_cache: &mut GpuCache,
         render_tasks: &mut RenderTaskTree,
@@ -420,45 +425,46 @@ impl RenderTarget for ColorRenderTarget 
             }
 
             match task.kind {
                 RenderTaskKind::Picture(ref pic_task) => {
                     let pic = &ctx.prim_store.pictures[pic_task.pic_index.0];
 
                     let (target_rect, _) = task.get_target_rect();
 
-                    let scisor_rect = if pic_task.can_merge {
+                    let scissor_rect = if pic_task.can_merge {
                         None
                     } else {
                         Some(target_rect)
                     };
 
-                    let mut batch_builder = AlphaBatchBuilder::new(
+                    let mut alpha_batch_builder = AlphaBatchBuilder::new(
                         self.screen_size,
-                        scisor_rect,
                         ctx.break_advanced_blend_batches,
                     );
 
-                    batch_builder.add_pic_to_batch(
+                    self.batch_builder.add_pic_to_batch(
                         pic,
+                        &mut alpha_batch_builder,
                         *task_id,
                         ctx,
                         gpu_cache,
                         render_tasks,
                         deferred_resolves,
                         prim_headers,
                         transforms,
                         pic_task.root_spatial_node_index,
                         z_generator,
                     );
 
-                    batch_builder.build(
+                    alpha_batch_builder.build(
                         &mut self.alpha_batch_containers,
                         &mut merged_batches,
                         target_rect,
+                        scissor_rect,
                     );
                 }
                 _ => {
                     unreachable!();
                 }
             }
         }