1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use api::{AlphaType, DeviceIntRect, DeviceIntSize, LayerToWorldScale};
6 use api::{DeviceUintRect, DeviceUintPoint, DeviceUintSize, ExternalImageType, FilterOp, ImageRendering, LayerRect};
7 use api::{DeviceIntPoint, LayerPoint, SubpixelDirection, YuvColorSpace, YuvFormat};
8 use api::{LayerToWorldTransform, WorldPixel};
9 use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind};
10 use clip::{ClipSource, ClipStore, ClipWorkItem};
11 use clip_scroll_tree::{CoordinateSystemId};
12 use euclid::{TypedTransform3D, vec3};
13 use glyph_rasterizer::GlyphFormat;
14 use gpu_cache::{GpuCache, GpuCacheAddress};
15 use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex};
16 use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex};
17 use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
18 use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
19 use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive};
20 use plane_split::{BspSplitter, Polygon, Splitter};
21 use prim_store::{CachedGradient, ImageSource, PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
22 use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, EdgeAaSegmentMask, PrimitiveRun};
23 use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind, RenderTaskTree};
24 use renderer::{BlendMode, ImageBufferKind};
25 use renderer::BLOCKS_PER_UV_RECT;
26 use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache};
27 use std::{usize, f32, i32};
28 use tiling::{RenderTargetContext};
29 use util::{MatrixHelpers, TransformedRectKind};
30 
31 // Special sentinel value recognized by the shader. It is considered to be
32 // a dummy task that doesn't mask out anything.
33 const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fff);
34 
35 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
36 #[cfg_attr(feature = "capture", derive(Serialize))]
37 #[cfg_attr(feature = "replay", derive(Deserialize))]
38 pub enum TransformBatchKind {
39     TextRun(GlyphFormat),
40     Image(ImageBufferKind),
41     BorderCorner,
42     BorderEdge,
43 }
44 
45 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
46 #[cfg_attr(feature = "capture", derive(Serialize))]
47 #[cfg_attr(feature = "replay", derive(Deserialize))]
48 pub enum BrushImageSourceKind {
49     Color = 0,
50     //Alpha = 1,            // Unused for now, but left here as shaders need to match.
51     ColorAlphaMask = 2,
52 }
53 
54 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
55 #[cfg_attr(feature = "capture", derive(Serialize))]
56 #[cfg_attr(feature = "replay", derive(Deserialize))]
57 pub enum BrushBatchKind {
58     Solid,
59     Line,
60     Image(ImageBufferKind),
61     Blend,
62     MixBlend {
63         task_id: RenderTaskId,
64         source_id: RenderTaskId,
65         backdrop_id: RenderTaskId,
66     },
67     YuvImage(ImageBufferKind, YuvFormat, YuvColorSpace),
68     RadialGradient,
69     LinearGradient,
70 }
71 
72 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
73 #[cfg_attr(feature = "capture", derive(Serialize))]
74 #[cfg_attr(feature = "replay", derive(Deserialize))]
75 pub enum BatchKind {
76     HardwareComposite,
77     SplitComposite,
78     Transformable(TransformedRectKind, TransformBatchKind),
79     Brush(BrushBatchKind),
80 }
81 
82 /// Optional textures that can be used as a source in the shaders.
83 /// Textures that are not used by the batch are equal to TextureId::invalid().
84 #[derive(Copy, Clone, Debug)]
85 #[cfg_attr(feature = "capture", derive(Serialize))]
86 #[cfg_attr(feature = "replay", derive(Deserialize))]
87 pub struct BatchTextures {
88     pub colors: [SourceTexture; 3],
89 }
90 
91 impl BatchTextures {
no_texture() -> Self92     pub fn no_texture() -> Self {
93         BatchTextures {
94             colors: [SourceTexture::Invalid; 3],
95         }
96     }
97 
render_target_cache() -> Self98     pub fn render_target_cache() -> Self {
99         BatchTextures {
100             colors: [
101                 SourceTexture::CacheRGBA8,
102                 SourceTexture::CacheA8,
103                 SourceTexture::Invalid,
104             ],
105         }
106     }
107 
color(texture: SourceTexture) -> Self108     pub fn color(texture: SourceTexture) -> Self {
109         BatchTextures {
110             colors: [texture, texture, SourceTexture::Invalid],
111         }
112     }
113 }
114 
115 #[derive(Copy, Clone, Debug)]
116 #[cfg_attr(feature = "capture", derive(Serialize))]
117 #[cfg_attr(feature = "replay", derive(Deserialize))]
118 pub struct BatchKey {
119     pub kind: BatchKind,
120     pub blend_mode: BlendMode,
121     pub textures: BatchTextures,
122 }
123 
124 impl BatchKey {
new(kind: BatchKind, blend_mode: BlendMode, textures: BatchTextures) -> Self125     pub fn new(kind: BatchKind, blend_mode: BlendMode, textures: BatchTextures) -> Self {
126         BatchKey {
127             kind,
128             blend_mode,
129             textures,
130         }
131     }
132 
is_compatible_with(&self, other: &BatchKey) -> bool133     pub fn is_compatible_with(&self, other: &BatchKey) -> bool {
134         self.kind == other.kind && self.blend_mode == other.blend_mode &&
135             textures_compatible(self.textures.colors[0], other.textures.colors[0]) &&
136             textures_compatible(self.textures.colors[1], other.textures.colors[1]) &&
137             textures_compatible(self.textures.colors[2], other.textures.colors[2])
138     }
139 }
140 
141 #[inline]
textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool142 fn textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool {
143     t1 == SourceTexture::Invalid || t2 == SourceTexture::Invalid || t1 == t2
144 }
145 
146 pub struct AlphaBatchList {
147     pub batches: Vec<PrimitiveBatch>,
148     pub item_rects: Vec<Vec<DeviceIntRect>>,
149 }
150 
151 impl AlphaBatchList {
new() -> Self152     fn new() -> Self {
153         AlphaBatchList {
154             batches: Vec::new(),
155             item_rects: Vec::new(),
156         }
157     }
158 
get_suitable_batch( &mut self, key: BatchKey, task_relative_bounding_rect: &DeviceIntRect, ) -> &mut Vec<PrimitiveInstance>159     pub fn get_suitable_batch(
160         &mut self,
161         key: BatchKey,
162         task_relative_bounding_rect: &DeviceIntRect,
163     ) -> &mut Vec<PrimitiveInstance> {
164         let mut selected_batch_index = None;
165 
166         match (key.kind, key.blend_mode) {
167             (BatchKind::Transformable(_, TransformBatchKind::TextRun(_)), BlendMode::SubpixelWithBgColor) |
168             (BatchKind::Transformable(_, TransformBatchKind::TextRun(_)), BlendMode::SubpixelVariableTextColor) => {
169                 'outer_text: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
170                     // Subpixel text is drawn in two passes. Because of this, we need
171                     // to check for overlaps with every batch (which is a bit different
172                     // than the normal batching below).
173                     for item_rect in &self.item_rects[batch_index] {
174                         if item_rect.intersects(task_relative_bounding_rect) {
175                             break 'outer_text;
176                         }
177                     }
178 
179                     if batch.key.is_compatible_with(&key) {
180                         selected_batch_index = Some(batch_index);
181                         break;
182                     }
183                 }
184             }
185             _ => {
186                 'outer_default: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
187                     // For normal batches, we only need to check for overlaps for batches
188                     // other than the first batch we consider. If the first batch
189                     // is compatible, then we know there isn't any potential overlap
190                     // issues to worry about.
191                     if batch.key.is_compatible_with(&key) {
192                         selected_batch_index = Some(batch_index);
193                         break;
194                     }
195 
196                     // check for intersections
197                     for item_rect in &self.item_rects[batch_index] {
198                         if item_rect.intersects(task_relative_bounding_rect) {
199                             break 'outer_default;
200                         }
201                     }
202                 }
203             }
204         }
205 
206         if selected_batch_index.is_none() {
207             let new_batch = PrimitiveBatch::new(key);
208             selected_batch_index = Some(self.batches.len());
209             self.batches.push(new_batch);
210             self.item_rects.push(Vec::new());
211         }
212 
213         let selected_batch_index = selected_batch_index.unwrap();
214         self.item_rects[selected_batch_index].push(*task_relative_bounding_rect);
215         &mut self.batches[selected_batch_index].instances
216     }
217 }
218 
219 pub struct OpaqueBatchList {
220     pub pixel_area_threshold_for_new_batch: f32,
221     pub batches: Vec<PrimitiveBatch>,
222 }
223 
224 impl OpaqueBatchList {
new(pixel_area_threshold_for_new_batch: f32) -> Self225     fn new(pixel_area_threshold_for_new_batch: f32) -> Self {
226         OpaqueBatchList {
227             batches: Vec::new(),
228             pixel_area_threshold_for_new_batch,
229         }
230     }
231 
get_suitable_batch( &mut self, key: BatchKey, task_relative_bounding_rect: &DeviceIntRect ) -> &mut Vec<PrimitiveInstance>232     pub fn get_suitable_batch(
233         &mut self,
234         key: BatchKey,
235         task_relative_bounding_rect: &DeviceIntRect
236     ) -> &mut Vec<PrimitiveInstance> {
237         let mut selected_batch_index = None;
238         let item_area = task_relative_bounding_rect.size.to_f32().area();
239 
240         // If the area of this primitive is larger than the given threshold,
241         // then it is large enough to warrant breaking a batch for. In this
242         // case we just see if it can be added to the existing batch or
243         // create a new one.
244         if item_area > self.pixel_area_threshold_for_new_batch {
245             if let Some(ref batch) = self.batches.last() {
246                 if batch.key.is_compatible_with(&key) {
247                     selected_batch_index = Some(self.batches.len() - 1);
248                 }
249             }
250         } else {
251             // Otherwise, look back through a reasonable number of batches.
252             for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
253                 if batch.key.is_compatible_with(&key) {
254                     selected_batch_index = Some(batch_index);
255                     break;
256                 }
257             }
258         }
259 
260         if selected_batch_index.is_none() {
261             let new_batch = PrimitiveBatch::new(key);
262             selected_batch_index = Some(self.batches.len());
263             self.batches.push(new_batch);
264         }
265 
266         let batch = &mut self.batches[selected_batch_index.unwrap()];
267 
268         &mut batch.instances
269     }
270 
finalize(&mut self)271     fn finalize(&mut self) {
272         // Reverse the instance arrays in the opaque batches
273         // to get maximum z-buffer efficiency by drawing
274         // front-to-back.
275         // TODO(gw): Maybe we can change the batch code to
276         //           build these in reverse and avoid having
277         //           to reverse the instance array here.
278         for batch in &mut self.batches {
279             batch.instances.reverse();
280         }
281     }
282 }
283 
284 pub struct BatchList {
285     pub alpha_batch_list: AlphaBatchList,
286     pub opaque_batch_list: OpaqueBatchList,
287     pub combined_bounding_rect: DeviceIntRect,
288 }
289 
290 impl BatchList {
new(screen_size: DeviceIntSize) -> Self291     pub fn new(screen_size: DeviceIntSize) -> Self {
292         // The threshold for creating a new batch is
293         // one quarter the screen size.
294         let batch_area_threshold = (screen_size.width * screen_size.height) as f32 / 4.0;
295 
296         BatchList {
297             alpha_batch_list: AlphaBatchList::new(),
298             opaque_batch_list: OpaqueBatchList::new(batch_area_threshold),
299             combined_bounding_rect: DeviceIntRect::zero(),
300         }
301     }
302 
add_bounding_rect( &mut self, task_relative_bounding_rect: &DeviceIntRect, )303     fn add_bounding_rect(
304         &mut self,
305         task_relative_bounding_rect: &DeviceIntRect,
306     ) {
307         self.combined_bounding_rect = self.combined_bounding_rect.union(task_relative_bounding_rect);
308     }
309 
get_suitable_batch( &mut self, key: BatchKey, task_relative_bounding_rect: &DeviceIntRect, ) -> &mut Vec<PrimitiveInstance>310     pub fn get_suitable_batch(
311         &mut self,
312         key: BatchKey,
313         task_relative_bounding_rect: &DeviceIntRect,
314     ) -> &mut Vec<PrimitiveInstance> {
315         self.add_bounding_rect(task_relative_bounding_rect);
316 
317         match key.blend_mode {
318             BlendMode::None => {
319                 self.opaque_batch_list
320                     .get_suitable_batch(key, task_relative_bounding_rect)
321             }
322             BlendMode::Alpha |
323             BlendMode::PremultipliedAlpha |
324             BlendMode::PremultipliedDestOut |
325             BlendMode::SubpixelConstantTextColor(..) |
326             BlendMode::SubpixelVariableTextColor |
327             BlendMode::SubpixelWithBgColor |
328             BlendMode::SubpixelDualSource => {
329                 self.alpha_batch_list
330                     .get_suitable_batch(key, task_relative_bounding_rect)
331             }
332         }
333     }
334 
finalize(&mut self)335     fn finalize(&mut self) {
336         self.opaque_batch_list.finalize()
337     }
338 }
339 
340 #[cfg_attr(feature = "capture", derive(Serialize))]
341 #[cfg_attr(feature = "replay", derive(Deserialize))]
342 pub struct PrimitiveBatch {
343     pub key: BatchKey,
344     pub instances: Vec<PrimitiveInstance>,
345 }
346 
347 impl PrimitiveBatch {
new(key: BatchKey) -> PrimitiveBatch348     fn new(key: BatchKey) -> PrimitiveBatch {
349         PrimitiveBatch {
350             key,
351             instances: Vec::new(),
352         }
353     }
354 }
355 
356 #[cfg_attr(feature = "capture", derive(Serialize))]
357 #[cfg_attr(feature = "replay", derive(Deserialize))]
358 pub struct AlphaBatchContainer {
359     pub text_run_cache_prims: FastHashMap<SourceTexture, Vec<PrimitiveInstance>>,
360     pub opaque_batches: Vec<PrimitiveBatch>,
361     pub alpha_batches: Vec<PrimitiveBatch>,
362     pub target_rect: Option<DeviceIntRect>,
363 }
364 
365 impl AlphaBatchContainer {
new(target_rect: Option<DeviceIntRect>) -> AlphaBatchContainer366     pub fn new(target_rect: Option<DeviceIntRect>) -> AlphaBatchContainer {
367         AlphaBatchContainer {
368             text_run_cache_prims: FastHashMap::default(),
369             opaque_batches: Vec::new(),
370             alpha_batches: Vec::new(),
371             target_rect,
372         }
373     }
374 
merge(&mut self, builder: AlphaBatchBuilder)375     fn merge(&mut self, builder: AlphaBatchBuilder) {
376         self.text_run_cache_prims.extend(builder.text_run_cache_prims);
377 
378         for other_batch in builder.batch_list.opaque_batch_list.batches {
379             let batch_index = self.opaque_batches.iter().position(|batch| {
380                 batch.key.is_compatible_with(&other_batch.key)
381             });
382 
383             match batch_index {
384                 Some(batch_index) => {
385                     self.opaque_batches[batch_index].instances.extend(other_batch.instances);
386                 }
387                 None => {
388                     self.opaque_batches.push(other_batch);
389                 }
390             }
391         }
392 
393         let mut min_batch_index = 0;
394 
395         for other_batch in builder.batch_list.alpha_batch_list.batches {
396             let batch_index = self.alpha_batches.iter().skip(min_batch_index).position(|batch| {
397                 batch.key.is_compatible_with(&other_batch.key)
398             });
399 
400             match batch_index {
401                 Some(batch_index) => {
402                     let batch_index = batch_index + min_batch_index;
403                     self.alpha_batches[batch_index].instances.extend(other_batch.instances);
404                     min_batch_index = batch_index;
405                 }
406                 None => {
407                     self.alpha_batches.push(other_batch);
408                     min_batch_index = self.alpha_batches.len();
409                 }
410             }
411         }
412     }
413 }
414 
415 /// Encapsulates the logic of building batches for items that are blended.
416 pub struct AlphaBatchBuilder {
417     pub batch_list: BatchList,
418     pub text_run_cache_prims: FastHashMap<SourceTexture, Vec<PrimitiveInstance>>,
419     glyph_fetch_buffer: Vec<GlyphFetchResult>,
420     target_rect: DeviceIntRect,
421 }
422 
423 impl AlphaBatchBuilder {
new( screen_size: DeviceIntSize, target_rect: DeviceIntRect, ) -> Self424     pub fn new(
425         screen_size: DeviceIntSize,
426         target_rect: DeviceIntRect,
427     ) -> Self {
428         AlphaBatchBuilder {
429             batch_list: BatchList::new(screen_size),
430             glyph_fetch_buffer: Vec::new(),
431             text_run_cache_prims: FastHashMap::default(),
432             target_rect,
433         }
434     }
435 
build(mut self, merged_batches: &mut AlphaBatchContainer) -> Option<AlphaBatchContainer>436     pub fn build(mut self, merged_batches: &mut AlphaBatchContainer) -> Option<AlphaBatchContainer> {
437         self.batch_list.finalize();
438 
439         let task_relative_target_rect = DeviceIntRect::new(
440             DeviceIntPoint::zero(),
441             self.target_rect.size,
442         );
443 
444         let can_merge = task_relative_target_rect.contains_rect(&self.batch_list.combined_bounding_rect);
445 
446         if can_merge {
447             merged_batches.merge(self);
448             None
449         } else {
450             Some(AlphaBatchContainer {
451                 alpha_batches: self.batch_list.alpha_batch_list.batches,
452                 opaque_batches: self.batch_list.opaque_batch_list.batches,
453                 target_rect: Some(self.target_rect),
454                 text_run_cache_prims: self.text_run_cache_prims,
455             })
456         }
457     }
458 
add_pic_to_batch( &mut self, pic: &PicturePrimitive, task_id: RenderTaskId, ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, deferred_resolves: &mut Vec<DeferredResolve>, )459     pub fn add_pic_to_batch(
460         &mut self,
461         pic: &PicturePrimitive,
462         task_id: RenderTaskId,
463         ctx: &RenderTargetContext,
464         gpu_cache: &mut GpuCache,
465         render_tasks: &RenderTaskTree,
466         deferred_resolves: &mut Vec<DeferredResolve>,
467     ) {
468         let task_address = render_tasks.get_task_address(task_id);
469 
470         let task = &render_tasks[task_id];
471         let content_origin = match task.kind {
472             RenderTaskKind::Picture(ref pic_task) => {
473                 pic_task.content_origin
474             }
475             _ => {
476                 panic!("todo: tidy this up");
477             }
478         };
479 
480         // Even though most of the time a splitter isn't used or needed,
481         // they are cheap to construct so we will always pass one down.
482         let mut splitter = BspSplitter::new();
483 
484         // Add each run in this picture to the batch.
485         for run in &pic.runs {
486             let scroll_node = &ctx.clip_scroll_tree.nodes[run.clip_and_scroll.scroll_node_id.0];
487             let scroll_id = scroll_node.node_data_index;
488             self.add_run_to_batch(
489                 run,
490                 scroll_id,
491                 ctx,
492                 gpu_cache,
493                 render_tasks,
494                 task_id,
495                 task_address,
496                 deferred_resolves,
497                 &mut splitter,
498                 pic,
499                 content_origin,
500             );
501         }
502 
503         // Flush the accumulated plane splits onto the task tree.
504         // Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
505         for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) {
506             let prim_index = PrimitiveIndex(poly.anchor);
507             debug!("process sorted poly {:?} {:?}", prim_index, poly.points);
508             let pp = &poly.points;
509             let gpu_blocks = [
510                 [pp[0].x as f32, pp[0].y as f32, pp[0].z as f32, pp[1].x as f32].into(),
511                 [pp[1].y as f32, pp[1].z as f32, pp[2].x as f32, pp[2].y as f32].into(),
512                 [pp[2].z as f32, pp[3].x as f32, pp[3].y as f32, pp[3].z as f32].into(),
513             ];
514             let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
515             let key = BatchKey::new(
516                 BatchKind::SplitComposite,
517                 BlendMode::PremultipliedAlpha,
518                 BatchTextures::no_texture(),
519             );
520             let pic_metadata = &ctx.prim_store.cpu_metadata[prim_index.0];
521             let pic = &ctx.prim_store.cpu_pictures[pic_metadata.cpu_prim_index.0];
522             let batch = self.batch_list.get_suitable_batch(key, &pic_metadata.screen_rect.as_ref().expect("bug").clipped);
523 
524             let render_task_id = pic.surface.expect("BUG: unexpected surface in splitting");
525             let source_task_address = render_tasks.get_task_address(render_task_id);
526             let gpu_address = gpu_handle.as_int(gpu_cache);
527 
528             let instance = CompositePrimitiveInstance::new(
529                 task_address,
530                 source_task_address,
531                 RenderTaskAddress(0),
532                 gpu_address,
533                 0,
534                 prim_index.0 as i32,
535                 0,
536                 0,
537             );
538 
539             batch.push(PrimitiveInstance::from(instance));
540         }
541     }
542 
543     // Helper to add an entire primitive run to a batch list.
544     // TODO(gw): Restructure this so the param list isn't quite
545     //           so daunting!
add_run_to_batch( &mut self, run: &PrimitiveRun, scroll_id: ClipScrollNodeIndex, ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, task_id: RenderTaskId, task_address: RenderTaskAddress, deferred_resolves: &mut Vec<DeferredResolve>, splitter: &mut BspSplitter<f64, WorldPixel>, pic: &PicturePrimitive, content_origin: ContentOrigin, )546     fn add_run_to_batch(
547         &mut self,
548         run: &PrimitiveRun,
549         scroll_id: ClipScrollNodeIndex,
550         ctx: &RenderTargetContext,
551         gpu_cache: &mut GpuCache,
552         render_tasks: &RenderTaskTree,
553         task_id: RenderTaskId,
554         task_address: RenderTaskAddress,
555         deferred_resolves: &mut Vec<DeferredResolve>,
556         splitter: &mut BspSplitter<f64, WorldPixel>,
557         pic: &PicturePrimitive,
558         content_origin: ContentOrigin,
559     ) {
560         for i in 0 .. run.count {
561             let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
562 
563             let metadata = &ctx.prim_store.cpu_metadata[prim_index.0];
564 
565             // Now that we walk the primitive runs in order to add
566             // items to batches, we need to check if they are
567             // visible here.
568             // We currently only support culling on normal (Image)
569             // picture types.
570             // TODO(gw): Support culling on shadow image types.
571             let is_image = match pic.kind {
572                 PictureKind::Image { .. } => true,
573                 PictureKind::TextShadow { .. } => false,
574             };
575 
576             if !is_image || metadata.screen_rect.is_some() {
577                 self.add_prim_to_batch(
578                     metadata.clip_chain_rect_index,
579                     scroll_id,
580                     prim_index,
581                     ctx,
582                     gpu_cache,
583                     render_tasks,
584                     task_id,
585                     task_address,
586                     deferred_resolves,
587                     splitter,
588                     content_origin,
589                     pic,
590                 );
591             }
592         }
593     }
594 
595     // Adds a primitive to a batch.
596     // It can recursively call itself in some situations, for
597     // example if it encounters a picture where the items
598     // in that picture are being drawn into the same target.
add_prim_to_batch( &mut self, clip_chain_rect_index: ClipChainRectIndex, scroll_id: ClipScrollNodeIndex, prim_index: PrimitiveIndex, ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, task_id: RenderTaskId, task_address: RenderTaskAddress, deferred_resolves: &mut Vec<DeferredResolve>, splitter: &mut BspSplitter<f64, WorldPixel>, content_origin: ContentOrigin, pic: &PicturePrimitive, )599     fn add_prim_to_batch(
600         &mut self,
601         clip_chain_rect_index: ClipChainRectIndex,
602         scroll_id: ClipScrollNodeIndex,
603         prim_index: PrimitiveIndex,
604         ctx: &RenderTargetContext,
605         gpu_cache: &mut GpuCache,
606         render_tasks: &RenderTaskTree,
607         task_id: RenderTaskId,
608         task_address: RenderTaskAddress,
609         deferred_resolves: &mut Vec<DeferredResolve>,
610         splitter: &mut BspSplitter<f64, WorldPixel>,
611         content_origin: ContentOrigin,
612         pic: &PicturePrimitive,
613     ) {
614         let z = prim_index.0 as i32;
615         let prim_metadata = ctx.prim_store.get_metadata(prim_index);
616         let scroll_node = &ctx.node_data[scroll_id.0 as usize];
617         // TODO(gw): Calculating this for every primitive is a bit
618         //           wasteful. We should probably cache this in
619         //           the scroll node...
620         let transform_kind = scroll_node.transform.transform_kind();
621 
622         let task_relative_bounding_rect = match content_origin {
623             ContentOrigin::Screen(point) => {
624                 // translate by content-origin
625                 let screen_rect = prim_metadata.screen_rect.expect("bug");
626                 DeviceIntRect::new(
627                     DeviceIntPoint::new(
628                         screen_rect.unclipped.origin.x - point.x,
629                         screen_rect.unclipped.origin.y - point.y,
630                     ),
631                     screen_rect.unclipped.size,
632                 )
633             }
634             ContentOrigin::Local(point) => {
635                 // scale local rect by device pixel ratio
636                 let content_rect = LayerRect::new(
637                     LayerPoint::new(
638                         prim_metadata.local_rect.origin.x - point.x,
639                         prim_metadata.local_rect.origin.y - point.y,
640                     ),
641                     prim_metadata.local_rect.size,
642                 );
643                 (content_rect * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round().to_i32()
644             }
645         };
646 
647         let prim_cache_address = gpu_cache.get_address(&prim_metadata.gpu_location);
648         let no_textures = BatchTextures::no_texture();
649         let clip_task_address = prim_metadata
650             .clip_task_id
651             .map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id));
652         let base_instance = SimplePrimitiveInstance::new(
653             prim_cache_address,
654             task_address,
655             clip_task_address,
656             clip_chain_rect_index,
657             scroll_id,
658             z,
659         );
660 
661         let specified_blend_mode = ctx.prim_store.get_blend_mode(prim_metadata);
662 
663         let non_segmented_blend_mode = if !prim_metadata.opacity.is_opaque ||
664             prim_metadata.clip_task_id.is_some() ||
665             transform_kind == TransformedRectKind::Complex {
666             specified_blend_mode
667         } else {
668             BlendMode::None
669         };
670 
671         match prim_metadata.prim_kind {
672             PrimitiveKind::Brush => {
673                 let brush = &ctx.prim_store.cpu_brushes[prim_metadata.cpu_prim_index.0];
674                 if let Some((batch_kind, textures, user_data)) = brush.get_batch_params(
675                     ctx.resource_cache,
676                     gpu_cache,
677                     deferred_resolves,
678                     &ctx.cached_gradients,
679                 ) {
680                     self.add_brush_to_batch(
681                         brush,
682                         prim_metadata,
683                         batch_kind,
684                         specified_blend_mode,
685                         non_segmented_blend_mode,
686                         textures,
687                         clip_chain_rect_index,
688                         clip_task_address,
689                         &task_relative_bounding_rect,
690                         prim_cache_address,
691                         scroll_id,
692                         task_address,
693                         transform_kind,
694                         z,
695                         render_tasks,
696                         user_data,
697                     );
698                 }
699             }
700             PrimitiveKind::Border => {
701                 let border_cpu =
702                     &ctx.prim_store.cpu_borders[prim_metadata.cpu_prim_index.0];
703                 // TODO(gw): Select correct blend mode for edges and corners!!
704                 let corner_kind = BatchKind::Transformable(
705                     transform_kind,
706                     TransformBatchKind::BorderCorner,
707                 );
708                 let corner_key = BatchKey::new(corner_kind, non_segmented_blend_mode, no_textures);
709                 let edge_kind = BatchKind::Transformable(
710                     transform_kind,
711                     TransformBatchKind::BorderEdge,
712                 );
713                 let edge_key = BatchKey::new(edge_kind, non_segmented_blend_mode, no_textures);
714 
715                 // Work around borrow ck on borrowing batch_list twice.
716                 {
717                     let batch =
718                         self.batch_list.get_suitable_batch(corner_key, &task_relative_bounding_rect);
719                     for (i, instance_kind) in border_cpu.corner_instances.iter().enumerate()
720                     {
721                         let sub_index = i as i32;
722                         match *instance_kind {
723                             BorderCornerInstance::None => {}
724                             BorderCornerInstance::Single => {
725                                 batch.push(base_instance.build(
726                                     sub_index,
727                                     BorderCornerSide::Both as i32,
728                                     0,
729                                 ));
730                             }
731                             BorderCornerInstance::Double => {
732                                 batch.push(base_instance.build(
733                                     sub_index,
734                                     BorderCornerSide::First as i32,
735                                     0,
736                                 ));
737                                 batch.push(base_instance.build(
738                                     sub_index,
739                                     BorderCornerSide::Second as i32,
740                                     0,
741                                 ));
742                             }
743                         }
744                     }
745                 }
746 
747                 let batch = self.batch_list.get_suitable_batch(edge_key, &task_relative_bounding_rect);
748                 for (border_segment, instance_kind) in border_cpu.edges.iter().enumerate() {
749                     match *instance_kind {
750                         BorderEdgeKind::None => {},
751                         _ => {
752                           batch.push(base_instance.build(border_segment as i32, 0, 0));
753                         }
754                     }
755                 }
756             }
757             PrimitiveKind::Image => {
758                 let image_cpu = &ctx.prim_store.cpu_images[prim_metadata.cpu_prim_index.0];
759 
760                 let cache_item = match image_cpu.source {
761                     ImageSource::Default => {
762                         resolve_image(
763                             image_cpu.key.request,
764                             ctx.resource_cache,
765                             gpu_cache,
766                             deferred_resolves,
767                         )
768                     }
769                     ImageSource::Cache { ref item, .. } => {
770                         item.clone()
771                     }
772                 };
773 
774                 if cache_item.texture_id == SourceTexture::Invalid {
775                     warn!("Warnings: skip a PrimitiveKind::Image");
776                     debug!("at {:?}.", task_relative_bounding_rect);
777                     return;
778                 }
779 
780                 let batch_kind = TransformBatchKind::Image(get_buffer_kind(cache_item.texture_id));
781                 let key = BatchKey::new(
782                     BatchKind::Transformable(transform_kind, batch_kind),
783                     non_segmented_blend_mode,
784                     BatchTextures {
785                         colors: [
786                             cache_item.texture_id,
787                             SourceTexture::Invalid,
788                             SourceTexture::Invalid,
789                         ],
790                     },
791                 );
792                 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
793                 batch.push(base_instance.build(cache_item.uv_rect_handle.as_int(gpu_cache), 0, 0));
794             }
795             PrimitiveKind::TextRun => {
796                 let text_cpu =
797                     &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
798                 let is_shadow = match pic.kind {
799                     PictureKind::TextShadow { .. } => true,
800                     PictureKind::Image { .. } => false,
801                 };
802 
803                 // TODO(gw): It probably makes sense to base this decision on the content
804                 //           origin field in the future (once that's configurable).
805                 let font_transform = if is_shadow {
806                     None
807                 } else {
808                     Some(scroll_node.transform)
809                 };
810 
811                 let font = text_cpu.get_font(
812                     ctx.device_pixel_scale,
813                     font_transform,
814                 );
815 
816                 let glyph_fetch_buffer = &mut self.glyph_fetch_buffer;
817                 let batch_list = &mut self.batch_list;
818                 let text_run_cache_prims = &mut self.text_run_cache_prims;
819 
820                 ctx.resource_cache.fetch_glyphs(
821                     font,
822                     &text_cpu.glyph_keys,
823                     glyph_fetch_buffer,
824                     gpu_cache,
825                     |texture_id, mut glyph_format, glyphs| {
826                         debug_assert_ne!(texture_id, SourceTexture::Invalid);
827 
828                         // Ignore color and only sample alpha when shadowing.
829                         if text_cpu.shadow {
830                             glyph_format = glyph_format.ignore_color();
831                         }
832 
833                         let subpx_dir = match glyph_format {
834                             GlyphFormat::Bitmap |
835                             GlyphFormat::ColorBitmap => SubpixelDirection::None,
836                             _ => text_cpu.font.subpx_dir.limit_by(text_cpu.font.render_mode),
837                         };
838 
839                         let batch = if is_shadow {
840                             text_run_cache_prims
841                                 .entry(texture_id)
842                                 .or_insert(Vec::new())
843                         } else {
844                             let textures = BatchTextures {
845                                 colors: [
846                                     texture_id,
847                                     SourceTexture::Invalid,
848                                     SourceTexture::Invalid,
849                                 ],
850                             };
851 
852                             let kind = BatchKind::Transformable(
853                                 transform_kind,
854                                 TransformBatchKind::TextRun(glyph_format),
855                             );
856 
857                             let blend_mode = match glyph_format {
858                                 GlyphFormat::Subpixel |
859                                 GlyphFormat::TransformedSubpixel => {
860                                     if text_cpu.font.bg_color.a != 0 {
861                                         BlendMode::SubpixelWithBgColor
862                                     } else if ctx.use_dual_source_blending {
863                                         BlendMode::SubpixelDualSource
864                                     } else {
865                                         BlendMode::SubpixelConstantTextColor(text_cpu.font.color.into())
866                                     }
867                                 }
868                                 GlyphFormat::Alpha |
869                                 GlyphFormat::TransformedAlpha |
870                                 GlyphFormat::Bitmap |
871                                 GlyphFormat::ColorBitmap => BlendMode::PremultipliedAlpha,
872                             };
873 
874                             let key = BatchKey::new(kind, blend_mode, textures);
875                             batch_list.get_suitable_batch(key, &task_relative_bounding_rect)
876                         };
877 
878                         for glyph in glyphs {
879                             batch.push(base_instance.build(
880                                 glyph.index_in_text_run,
881                                 glyph.uv_rect_address.as_int(),
882                                 subpx_dir as u32 as i32,
883                             ));
884                         }
885                     },
886                 );
887             }
888             PrimitiveKind::Picture => {
889                 let picture =
890                     &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
891 
892                 match picture.surface {
893                     Some(cache_task_id) => {
894                         let cache_task_address = render_tasks.get_task_address(cache_task_id);
895                         let textures = BatchTextures::render_target_cache();
896 
897                         match picture.kind {
898                             PictureKind::TextShadow { .. } => {
899                                 let kind = BatchKind::Brush(
900                                     BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
901                                 );
902                                 let key = BatchKey::new(kind, non_segmented_blend_mode, textures);
903                                 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
904 
905                                 let uv_rect_address = render_tasks[cache_task_id]
906                                     .get_texture_handle()
907                                     .as_int(gpu_cache);
908 
909                                 let instance = BrushInstance {
910                                     picture_address: task_address,
911                                     prim_address: prim_cache_address,
912                                     clip_chain_rect_index,
913                                     scroll_id,
914                                     clip_task_address,
915                                     z,
916                                     segment_index: 0,
917                                     edge_flags: EdgeAaSegmentMask::empty(),
918                                     brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
919                                     user_data: [
920                                         uv_rect_address,
921                                         BrushImageSourceKind::Color as i32,
922                                         0,
923                                     ],
924                                 };
925                                 batch.push(PrimitiveInstance::from(instance));
926                             }
927                             PictureKind::Image {
928                                 composite_mode,
929                                 secondary_render_task_id,
930                                 is_in_3d_context,
931                                 reference_frame_index,
932                                 real_local_rect,
933                                 ref extra_gpu_data_handle,
934                                 ..
935                             } => {
936                                 // If this picture is participating in a 3D rendering context,
937                                 // then don't add it to any batches here. Instead, create a polygon
938                                 // for it and add it to the current plane splitter.
939                                 if is_in_3d_context {
940                                     // Push into parent plane splitter.
941 
942                                     let real_xf = &ctx.clip_scroll_tree
943                                         .nodes[reference_frame_index.0]
944                                         .world_content_transform
945                                         .into();
946                                     let polygon = make_polygon(
947                                         real_local_rect,
948                                         &real_xf,
949                                         prim_index.0,
950                                     );
951 
952                                     splitter.add(polygon);
953 
954                                     return;
955                                 }
956 
957                                 // Depending on the composite mode of the picture, we generate the
958                                 // old style Composite primitive instances. In the future, we'll
959                                 // remove these and pass them through the brush batching pipeline.
960                                 // This will allow us to unify some of the shaders, apply clip masks
961                                 // when compositing pictures, and also correctly apply pixel snapping
962                                 // to picture compositing operations.
963                                 let source_id = cache_task_id;
964 
965                                 match composite_mode.expect("bug: only composites here") {
966                                     PictureCompositeMode::Filter(filter) => {
967                                         match filter {
968                                             FilterOp::Blur(..) => {
969                                                 let src_task_address = render_tasks.get_task_address(source_id);
970                                                 let key = BatchKey::new(
971                                                     BatchKind::HardwareComposite,
972                                                     BlendMode::PremultipliedAlpha,
973                                                     BatchTextures::render_target_cache(),
974                                                 );
975                                                 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
976                                                 let item_bounding_rect = prim_metadata.screen_rect.expect("bug!!").clipped;
977                                                 let instance = CompositePrimitiveInstance::new(
978                                                     task_address,
979                                                     src_task_address,
980                                                     RenderTaskAddress(0),
981                                                     item_bounding_rect.origin.x,
982                                                     item_bounding_rect.origin.y,
983                                                     z,
984                                                     item_bounding_rect.size.width,
985                                                     item_bounding_rect.size.height,
986                                                 );
987 
988                                                 batch.push(PrimitiveInstance::from(instance));
989                                             }
990                                             FilterOp::DropShadow(offset, _, _) => {
991                                                 let kind = BatchKind::Brush(
992                                                     BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
993                                                 );
994                                                 let key = BatchKey::new(kind, non_segmented_blend_mode, textures);
995 
996                                                 let uv_rect_address = render_tasks[cache_task_id]
997                                                     .get_texture_handle()
998                                                     .as_int(gpu_cache);
999 
1000                                                 let instance = BrushInstance {
1001                                                     picture_address: task_address,
1002                                                     prim_address: prim_cache_address,
1003                                                     clip_chain_rect_index,
1004                                                     scroll_id,
1005                                                     clip_task_address,
1006                                                     z,
1007                                                     segment_index: 0,
1008                                                     edge_flags: EdgeAaSegmentMask::empty(),
1009                                                     brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
1010                                                     user_data: [
1011                                                         uv_rect_address,
1012                                                         BrushImageSourceKind::ColorAlphaMask as i32,
1013                                                         0,
1014                                                     ],
1015                                                 };
1016 
1017                                                 {
1018                                                     let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
1019                                                     batch.push(PrimitiveInstance::from(instance));
1020                                                 }
1021 
1022                                                 let secondary_id = secondary_render_task_id.expect("no secondary!?");
1023                                                 let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?");
1024                                                 debug_assert_ne!(saved_index, SavedTargetIndex::PENDING);
1025                                                 let secondary_task_address = render_tasks.get_task_address(secondary_id);
1026                                                 let secondary_textures = BatchTextures {
1027                                                     colors: [
1028                                                         SourceTexture::RenderTaskCache(saved_index),
1029                                                         SourceTexture::Invalid,
1030                                                         SourceTexture::Invalid,
1031                                                     ],
1032                                                 };
1033                                                 let key = BatchKey::new(
1034                                                     BatchKind::HardwareComposite,
1035                                                     BlendMode::PremultipliedAlpha,
1036                                                     secondary_textures,
1037                                                 );
1038                                                 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
1039                                                 let content_rect = prim_metadata.local_rect.translate(&-offset);
1040                                                 let rect =
1041                                                     (content_rect * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round()
1042                                                                                                                          .to_i32();
1043 
1044                                                 let instance = CompositePrimitiveInstance::new(
1045                                                     task_address,
1046                                                     secondary_task_address,
1047                                                     RenderTaskAddress(0),
1048                                                     rect.origin.x,
1049                                                     rect.origin.y,
1050                                                     z,
1051                                                     rect.size.width,
1052                                                     rect.size.height,
1053                                                 );
1054 
1055                                                 batch.push(PrimitiveInstance::from(instance));
1056                                             }
1057                                             _ => {
1058                                                 let key = BatchKey::new(
1059                                                     BatchKind::Brush(BrushBatchKind::Blend),
1060                                                     BlendMode::PremultipliedAlpha,
1061                                                     BatchTextures::render_target_cache(),
1062                                                 );
1063 
1064                                                 let (filter_mode, extra_cache_address) = match filter {
1065                                                     FilterOp::Blur(..) => (0, 0),
1066                                                     FilterOp::Contrast(..) => (1, 0),
1067                                                     FilterOp::Grayscale(..) => (2, 0),
1068                                                     FilterOp::HueRotate(..) => (3, 0),
1069                                                     FilterOp::Invert(..) => (4, 0),
1070                                                     FilterOp::Saturate(..) => (5, 0),
1071                                                     FilterOp::Sepia(..) => (6, 0),
1072                                                     FilterOp::Brightness(..) => (7, 0),
1073                                                     FilterOp::Opacity(..) => (8, 0),
1074                                                     FilterOp::DropShadow(..) => (9, 0),
1075                                                     FilterOp::ColorMatrix(..) => {
1076                                                         (10, extra_gpu_data_handle.as_int(gpu_cache))
1077                                                     }
1078                                                 };
1079 
1080                                                 let instance = BrushInstance {
1081                                                     picture_address: task_address,
1082                                                     prim_address: prim_cache_address,
1083                                                     clip_chain_rect_index,
1084                                                     scroll_id,
1085                                                     clip_task_address,
1086                                                     z,
1087                                                     segment_index: 0,
1088                                                     edge_flags: EdgeAaSegmentMask::empty(),
1089                                                     brush_flags: BrushFlags::empty(),
1090                                                     user_data: [
1091                                                         cache_task_address.0 as i32,
1092                                                         filter_mode,
1093                                                         extra_cache_address,
1094                                                     ],
1095                                                 };
1096 
1097                                                 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
1098                                                 batch.push(PrimitiveInstance::from(instance));
1099                                             }
1100                                         }
1101                                     }
1102                                     PictureCompositeMode::MixBlend(mode) => {
1103                                         let backdrop_id = secondary_render_task_id.expect("no backdrop!?");
1104 
1105                                         let key = BatchKey::new(
1106                                             BatchKind::Brush(
1107                                                 BrushBatchKind::MixBlend {
1108                                                     task_id,
1109                                                     source_id,
1110                                                     backdrop_id,
1111                                                 },
1112                                             ),
1113                                             BlendMode::PremultipliedAlpha,
1114                                             BatchTextures::no_texture(),
1115                                         );
1116                                         let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
1117                                         let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
1118                                         let source_task_address = render_tasks.get_task_address(source_id);
1119 
1120                                         let instance = BrushInstance {
1121                                             picture_address: task_address,
1122                                             prim_address: prim_cache_address,
1123                                             clip_chain_rect_index,
1124                                             scroll_id,
1125                                             clip_task_address,
1126                                             z,
1127                                             segment_index: 0,
1128                                             edge_flags: EdgeAaSegmentMask::empty(),
1129                                             brush_flags: BrushFlags::empty(),
1130                                             user_data: [
1131                                                 mode as u32 as i32,
1132                                                 backdrop_task_address.0 as i32,
1133                                                 source_task_address.0 as i32,
1134                                             ],
1135                                         };
1136 
1137                                         batch.push(PrimitiveInstance::from(instance));
1138                                     }
1139                                     PictureCompositeMode::Blit => {
1140                                         let src_task_address = render_tasks.get_task_address(source_id);
1141                                         let key = BatchKey::new(
1142                                             BatchKind::HardwareComposite,
1143                                             BlendMode::PremultipliedAlpha,
1144                                             BatchTextures::render_target_cache(),
1145                                         );
1146                                         let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
1147                                         let item_bounding_rect = prim_metadata.screen_rect.expect("bug!!").clipped;
1148                                         let instance = CompositePrimitiveInstance::new(
1149                                             task_address,
1150                                             src_task_address,
1151                                             RenderTaskAddress(0),
1152                                             item_bounding_rect.origin.x,
1153                                             item_bounding_rect.origin.y,
1154                                             z,
1155                                             item_bounding_rect.size.width,
1156                                             item_bounding_rect.size.height,
1157                                         );
1158 
1159                                         batch.push(PrimitiveInstance::from(instance));
1160                                     }
1161                                 }
1162                             }
1163                         }
1164                     }
1165                     None => {
1166                         // If this picture is being drawn into an existing target (i.e. with
1167                         // no composition operation), recurse and add to the current batch list.
1168                         self.add_pic_to_batch(
1169                             picture,
1170                             task_id,
1171                             ctx,
1172                             gpu_cache,
1173                             render_tasks,
1174                             deferred_resolves,
1175                         );
1176                     }
1177                 }
1178             }
1179         }
1180     }
1181 
add_brush_to_batch( &mut self, brush: &BrushPrimitive, prim_metadata: &PrimitiveMetadata, batch_kind: BrushBatchKind, alpha_blend_mode: BlendMode, non_segmented_blend_mode: BlendMode, textures: BatchTextures, clip_chain_rect_index: ClipChainRectIndex, clip_task_address: RenderTaskAddress, task_relative_bounding_rect: &DeviceIntRect, prim_cache_address: GpuCacheAddress, scroll_id: ClipScrollNodeIndex, task_address: RenderTaskAddress, transform_kind: TransformedRectKind, z: i32, render_tasks: &RenderTaskTree, user_data: [i32; 3], )1182     fn add_brush_to_batch(
1183         &mut self,
1184         brush: &BrushPrimitive,
1185         prim_metadata: &PrimitiveMetadata,
1186         batch_kind: BrushBatchKind,
1187         alpha_blend_mode: BlendMode,
1188         non_segmented_blend_mode: BlendMode,
1189         textures: BatchTextures,
1190         clip_chain_rect_index: ClipChainRectIndex,
1191         clip_task_address: RenderTaskAddress,
1192         task_relative_bounding_rect: &DeviceIntRect,
1193         prim_cache_address: GpuCacheAddress,
1194         scroll_id: ClipScrollNodeIndex,
1195         task_address: RenderTaskAddress,
1196         transform_kind: TransformedRectKind,
1197         z: i32,
1198         render_tasks: &RenderTaskTree,
1199         user_data: [i32; 3],
1200     ) {
1201         let base_instance = BrushInstance {
1202             picture_address: task_address,
1203             prim_address: prim_cache_address,
1204             clip_chain_rect_index,
1205             scroll_id,
1206             clip_task_address,
1207             z,
1208             segment_index: 0,
1209             edge_flags: EdgeAaSegmentMask::all(),
1210             brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
1211             user_data,
1212         };
1213 
1214         self.batch_list.add_bounding_rect(task_relative_bounding_rect);
1215 
1216         match brush.segment_desc {
1217             Some(ref segment_desc) => {
1218                 let alpha_batch_key = BatchKey {
1219                     blend_mode: alpha_blend_mode,
1220                     kind: BatchKind::Brush(batch_kind),
1221                     textures,
1222                 };
1223 
1224                 let alpha_batch = self.batch_list.alpha_batch_list.get_suitable_batch(
1225                     alpha_batch_key,
1226                     task_relative_bounding_rect
1227                 );
1228 
1229                 let opaque_batch_key = BatchKey {
1230                     blend_mode: BlendMode::None,
1231                     kind: BatchKind::Brush(batch_kind),
1232                     textures,
1233                 };
1234 
1235                 let opaque_batch = self.batch_list.opaque_batch_list.get_suitable_batch(
1236                     opaque_batch_key,
1237                     task_relative_bounding_rect
1238                 );
1239 
1240                 for (i, segment) in segment_desc.segments.iter().enumerate() {
1241                     let is_inner = segment.edge_flags.is_empty();
1242                     let needs_blending = !prim_metadata.opacity.is_opaque ||
1243                                          segment.clip_task_id.is_some() ||
1244                                          (!is_inner && transform_kind == TransformedRectKind::Complex);
1245 
1246                     let clip_task_address = segment
1247                         .clip_task_id
1248                         .map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id));
1249 
1250                     let instance = PrimitiveInstance::from(BrushInstance {
1251                         segment_index: i as i32,
1252                         edge_flags: segment.edge_flags,
1253                         clip_task_address,
1254                         ..base_instance
1255                     });
1256 
1257                     if needs_blending {
1258                         alpha_batch.push(instance);
1259                     } else {
1260                         opaque_batch.push(instance);
1261                     }
1262                 }
1263             }
1264             None => {
1265                 let batch_key = BatchKey {
1266                     blend_mode: non_segmented_blend_mode,
1267                     kind: BatchKind::Brush(batch_kind),
1268                     textures,
1269                 };
1270                 let batch = self.batch_list.get_suitable_batch(batch_key, task_relative_bounding_rect);
1271                 batch.push(PrimitiveInstance::from(base_instance));
1272             }
1273         }
1274     }
1275 }
1276 
1277 impl BrushPrimitive {
get_batch_params( &self, resource_cache: &ResourceCache, gpu_cache: &mut GpuCache, deferred_resolves: &mut Vec<DeferredResolve>, cached_gradients: &[CachedGradient], ) -> Option<(BrushBatchKind, BatchTextures, [i32; 3])>1278     fn get_batch_params(
1279         &self,
1280         resource_cache: &ResourceCache,
1281         gpu_cache: &mut GpuCache,
1282         deferred_resolves: &mut Vec<DeferredResolve>,
1283         cached_gradients: &[CachedGradient],
1284     ) -> Option<(BrushBatchKind, BatchTextures, [i32; 3])> {
1285         match self.kind {
1286             BrushKind::Line { .. } => {
1287                 Some((
1288                     BrushBatchKind::Line,
1289                     BatchTextures::no_texture(),
1290                     [0; 3],
1291                 ))
1292             }
1293             BrushKind::Image { request, .. } => {
1294                 let cache_item = resolve_image(
1295                     request,
1296                     resource_cache,
1297                     gpu_cache,
1298                     deferred_resolves,
1299                 );
1300 
1301                 if cache_item.texture_id == SourceTexture::Invalid {
1302                     None
1303                 } else {
1304                     let textures = BatchTextures::color(cache_item.texture_id);
1305 
1306                     Some((
1307                         BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
1308                         textures,
1309                         [
1310                             cache_item.uv_rect_handle.as_int(gpu_cache),
1311                             BrushImageSourceKind::Color as i32,
1312                             0,
1313                         ],
1314                     ))
1315                 }
1316             }
1317             BrushKind::Picture => {
1318                 panic!("bug: get_batch_key is handled at higher level for pictures");
1319             }
1320             BrushKind::Solid { .. } => {
1321                 Some((
1322                     BrushBatchKind::Solid,
1323                     BatchTextures::no_texture(),
1324                     [0; 3],
1325                 ))
1326             }
1327             BrushKind::Clear => {
1328                 Some((
1329                     BrushBatchKind::Solid,
1330                     BatchTextures::no_texture(),
1331                     [0; 3],
1332                 ))
1333             }
1334             BrushKind::RadialGradient { gradient_index, .. } => {
1335                 let stops_handle = &cached_gradients[gradient_index.0].handle;
1336                 Some((
1337                     BrushBatchKind::RadialGradient,
1338                     BatchTextures::no_texture(),
1339                     [
1340                         stops_handle.as_int(gpu_cache),
1341                         0,
1342                         0,
1343                     ],
1344                 ))
1345             }
1346             BrushKind::LinearGradient { gradient_index, .. } => {
1347                 let stops_handle = &cached_gradients[gradient_index.0].handle;
1348                 Some((
1349                     BrushBatchKind::LinearGradient,
1350                     BatchTextures::no_texture(),
1351                     [
1352                         stops_handle.as_int(gpu_cache),
1353                         0,
1354                         0,
1355                     ],
1356                 ))
1357             }
1358             BrushKind::YuvImage { format, yuv_key, image_rendering, color_space } => {
1359                 let mut textures = BatchTextures::no_texture();
1360                 let mut uv_rect_addresses = [0; 3];
1361 
1362                 //yuv channel
1363                 let channel_count = format.get_plane_num();
1364                 debug_assert!(channel_count <= 3);
1365                 for channel in 0 .. channel_count {
1366                     let image_key = yuv_key[channel];
1367 
1368                     let cache_item = resolve_image(
1369                         ImageRequest {
1370                             key: image_key,
1371                             rendering: image_rendering,
1372                             tile: None,
1373                         },
1374                         resource_cache,
1375                         gpu_cache,
1376                         deferred_resolves,
1377                     );
1378 
1379                     if cache_item.texture_id == SourceTexture::Invalid {
1380                         warn!("Warnings: skip a PrimitiveKind::YuvImage");
1381                         return None;
1382                     }
1383 
1384                     textures.colors[channel] = cache_item.texture_id;
1385                     uv_rect_addresses[channel] = cache_item.uv_rect_handle.as_int(gpu_cache);
1386                 }
1387 
1388                 // All yuv textures should be the same type.
1389                 let buffer_kind = get_buffer_kind(textures.colors[0]);
1390                 assert!(
1391                     textures.colors[1 .. format.get_plane_num()]
1392                         .iter()
1393                         .all(|&tid| buffer_kind == get_buffer_kind(tid))
1394                 );
1395 
1396                 let kind = BrushBatchKind::YuvImage(
1397                     buffer_kind,
1398                     format,
1399                     color_space,
1400                 );
1401 
1402                 Some((
1403                     kind,
1404                     textures,
1405                     [
1406                         uv_rect_addresses[0],
1407                         uv_rect_addresses[1],
1408                         uv_rect_addresses[2],
1409                     ],
1410                 ))
1411             }
1412         }
1413     }
1414 }
1415 
1416 trait AlphaBatchHelpers {
get_blend_mode( &self, metadata: &PrimitiveMetadata, ) -> BlendMode1417     fn get_blend_mode(
1418         &self,
1419         metadata: &PrimitiveMetadata,
1420     ) -> BlendMode;
1421 }
1422 
1423 impl AlphaBatchHelpers for PrimitiveStore {
get_blend_mode(&self, metadata: &PrimitiveMetadata) -> BlendMode1424     fn get_blend_mode(&self, metadata: &PrimitiveMetadata) -> BlendMode {
1425         match metadata.prim_kind {
1426             // Can only resolve the TextRun's blend mode once glyphs are fetched.
1427             PrimitiveKind::TextRun => {
1428                 BlendMode::PremultipliedAlpha
1429             }
1430 
1431             PrimitiveKind::Border |
1432             PrimitiveKind::Picture => {
1433                 BlendMode::PremultipliedAlpha
1434             }
1435 
1436             PrimitiveKind::Brush => {
1437                 let brush = &self.cpu_brushes[metadata.cpu_prim_index.0];
1438                 match brush.kind {
1439                     BrushKind::Clear => {
1440                         BlendMode::PremultipliedDestOut
1441                     }
1442                     BrushKind::Image { alpha_type, .. } => {
1443                         match alpha_type {
1444                             AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
1445                             AlphaType::Alpha => BlendMode::Alpha,
1446                         }
1447                     }
1448                     BrushKind::Solid { .. } |
1449                     BrushKind::Line { .. } |
1450                     BrushKind::YuvImage { .. } |
1451                     BrushKind::RadialGradient { .. } |
1452                     BrushKind::LinearGradient { .. } |
1453                     BrushKind::Picture => {
1454                         BlendMode::PremultipliedAlpha
1455                     }
1456                 }
1457             }
1458             PrimitiveKind::Image => {
1459                 let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0];
1460                 match image_cpu.alpha_type {
1461                     AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
1462                     AlphaType::Alpha => BlendMode::Alpha,
1463                 }
1464             }
1465         }
1466     }
1467 }
1468 
resolve_image( request: ImageRequest, resource_cache: &ResourceCache, gpu_cache: &mut GpuCache, deferred_resolves: &mut Vec<DeferredResolve>, ) -> CacheItem1469 pub fn resolve_image(
1470     request: ImageRequest,
1471     resource_cache: &ResourceCache,
1472     gpu_cache: &mut GpuCache,
1473     deferred_resolves: &mut Vec<DeferredResolve>,
1474 ) -> CacheItem {
1475     match resource_cache.get_image_properties(request.key) {
1476         Some(image_properties) => {
1477             // Check if an external image that needs to be resolved
1478             // by the render thread.
1479             match image_properties.external_image {
1480                 Some(external_image) => {
1481                     // This is an external texture - we will add it to
1482                     // the deferred resolves list to be patched by
1483                     // the render thread...
1484                     let cache_handle = gpu_cache.push_deferred_per_frame_blocks(BLOCKS_PER_UV_RECT);
1485                     let cache_item = CacheItem {
1486                         texture_id: SourceTexture::External(external_image),
1487                         uv_rect_handle: cache_handle,
1488                         uv_rect: DeviceUintRect::new(
1489                             DeviceUintPoint::zero(),
1490                             DeviceUintSize::new(
1491                                 image_properties.descriptor.width,
1492                                 image_properties.descriptor.height,
1493                             )
1494                         ),
1495                         texture_layer: 0,
1496                     };
1497 
1498                     deferred_resolves.push(DeferredResolve {
1499                         image_properties,
1500                         address: gpu_cache.get_address(&cache_handle),
1501                     });
1502 
1503                     cache_item
1504                 }
1505                 None => {
1506                     if let Ok(cache_item) = resource_cache.get_cached_image(request) {
1507                         cache_item
1508                     } else {
1509                         // There is no usable texture entry for the image key. Just return an invalid texture here.
1510                         CacheItem::invalid()
1511                     }
1512                 }
1513             }
1514         }
1515         None => {
1516             CacheItem::invalid()
1517         }
1518     }
1519 }
1520 
1521 /// Construct a polygon from stacking context boundaries.
1522 /// `anchor` here is an index that's going to be preserved in all the
1523 /// splits of the polygon.
make_polygon( rect: LayerRect, transform: &LayerToWorldTransform, anchor: usize, ) -> Polygon<f64, WorldPixel>1524 fn make_polygon(
1525     rect: LayerRect,
1526     transform: &LayerToWorldTransform,
1527     anchor: usize,
1528 ) -> Polygon<f64, WorldPixel> {
1529     let mat = TypedTransform3D::row_major(
1530         transform.m11 as f64,
1531         transform.m12 as f64,
1532         transform.m13 as f64,
1533         transform.m14 as f64,
1534         transform.m21 as f64,
1535         transform.m22 as f64,
1536         transform.m23 as f64,
1537         transform.m24 as f64,
1538         transform.m31 as f64,
1539         transform.m32 as f64,
1540         transform.m33 as f64,
1541         transform.m34 as f64,
1542         transform.m41 as f64,
1543         transform.m42 as f64,
1544         transform.m43 as f64,
1545         transform.m44 as f64);
1546     Polygon::from_transformed_rect(rect.cast().unwrap(), mat, anchor)
1547 }
1548 
1549 /// Batcher managing draw calls into the clip mask (in the RT cache).
1550 #[derive(Debug)]
1551 #[cfg_attr(feature = "capture", derive(Serialize))]
1552 #[cfg_attr(feature = "replay", derive(Deserialize))]
1553 pub struct ClipBatcher {
1554     /// Rectangle draws fill up the rectangles with rounded corners.
1555     pub rectangles: Vec<ClipMaskInstance>,
1556     /// Image draws apply the image masking.
1557     pub images: FastHashMap<SourceTexture, Vec<ClipMaskInstance>>,
1558     pub border_clears: Vec<ClipMaskInstance>,
1559     pub borders: Vec<ClipMaskInstance>,
1560     pub box_shadows: FastHashMap<SourceTexture, Vec<ClipMaskInstance>>,
1561 }
1562 
1563 impl ClipBatcher {
new() -> Self1564     pub fn new() -> Self {
1565         ClipBatcher {
1566             rectangles: Vec::new(),
1567             images: FastHashMap::default(),
1568             border_clears: Vec::new(),
1569             borders: Vec::new(),
1570             box_shadows: FastHashMap::default(),
1571         }
1572     }
1573 
add_clip_region( &mut self, task_address: RenderTaskAddress, clip_data_address: GpuCacheAddress, )1574     pub fn add_clip_region(
1575         &mut self,
1576         task_address: RenderTaskAddress,
1577         clip_data_address: GpuCacheAddress,
1578     ) {
1579         let instance = ClipMaskInstance {
1580             render_task_address: task_address,
1581             scroll_node_data_index: ClipScrollNodeIndex(0),
1582             segment: 0,
1583             clip_data_address,
1584             resource_address: GpuCacheAddress::invalid(),
1585         };
1586 
1587         self.rectangles.push(instance);
1588     }
1589 
add( &mut self, task_address: RenderTaskAddress, clips: &[ClipWorkItem], coordinate_system_id: CoordinateSystemId, resource_cache: &ResourceCache, gpu_cache: &GpuCache, clip_store: &ClipStore, )1590     pub fn add(
1591         &mut self,
1592         task_address: RenderTaskAddress,
1593         clips: &[ClipWorkItem],
1594         coordinate_system_id: CoordinateSystemId,
1595         resource_cache: &ResourceCache,
1596         gpu_cache: &GpuCache,
1597         clip_store: &ClipStore,
1598     ) {
1599         let mut coordinate_system_id = coordinate_system_id;
1600         for work_item in clips.iter() {
1601             let instance = ClipMaskInstance {
1602                 render_task_address: task_address,
1603                 scroll_node_data_index: work_item.scroll_node_data_index,
1604                 segment: 0,
1605                 clip_data_address: GpuCacheAddress::invalid(),
1606                 resource_address: GpuCacheAddress::invalid(),
1607             };
1608             let info = clip_store
1609                 .get_opt(&work_item.clip_sources)
1610                 .expect("bug: clip handle should be valid");
1611 
1612             for &(ref source, ref handle) in &info.clips {
1613                 let gpu_address = gpu_cache.get_address(handle);
1614 
1615                 match *source {
1616                     ClipSource::Image(ref mask) => {
1617                         if let Ok(cache_item) = resource_cache.get_cached_image(
1618                             ImageRequest {
1619                                 key: mask.image,
1620                                 rendering: ImageRendering::Auto,
1621                                 tile: None,
1622                             }
1623                         ) {
1624                             self.images
1625                                 .entry(cache_item.texture_id)
1626                                 .or_insert(Vec::new())
1627                                 .push(ClipMaskInstance {
1628                                     clip_data_address: gpu_address,
1629                                     resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
1630                                     ..instance
1631                                 });
1632                         } else {
1633                             warn!("Warnings: skip a image mask");
1634                             debug!("Key:{:?} Rect::{:?}", mask.image, mask.rect);
1635                             continue;
1636                         }
1637                     }
1638                     ClipSource::BoxShadow(ref info) => {
1639                         debug_assert_ne!(info.cache_item.texture_id, SourceTexture::Invalid);
1640 
1641                         self.box_shadows
1642                             .entry(info.cache_item.texture_id)
1643                             .or_insert(Vec::new())
1644                             .push(ClipMaskInstance {
1645                                 clip_data_address: gpu_address,
1646                                 resource_address: gpu_cache.get_address(&info.cache_item.uv_rect_handle),
1647                                 ..instance
1648                             });
1649                     }
1650                     ClipSource::Rectangle(..) => {
1651                         if work_item.coordinate_system_id != coordinate_system_id {
1652                             self.rectangles.push(ClipMaskInstance {
1653                                 clip_data_address: gpu_address,
1654                                 ..instance
1655                             });
1656                             coordinate_system_id = work_item.coordinate_system_id;
1657                         }
1658                     }
1659                     ClipSource::RoundedRectangle(..) => {
1660                         self.rectangles.push(ClipMaskInstance {
1661                             clip_data_address: gpu_address,
1662                             ..instance
1663                         });
1664                     }
1665                     ClipSource::BorderCorner(ref source) => {
1666                         self.border_clears.push(ClipMaskInstance {
1667                             clip_data_address: gpu_address,
1668                             segment: 0,
1669                             ..instance
1670                         });
1671                         for clip_index in 0 .. source.actual_clip_count {
1672                             self.borders.push(ClipMaskInstance {
1673                                 clip_data_address: gpu_address,
1674                                 segment: 1 + clip_index as i32,
1675                                 ..instance
1676                             })
1677                         }
1678                     }
1679                 }
1680             }
1681         }
1682     }
1683 }
1684 
get_buffer_kind(texture: SourceTexture) -> ImageBufferKind1685 fn get_buffer_kind(texture: SourceTexture) -> ImageBufferKind {
1686     match texture {
1687         SourceTexture::External(ext_image) => {
1688             match ext_image.image_type {
1689                 ExternalImageType::TextureHandle(target) => {
1690                     target.into()
1691                 }
1692                 ExternalImageType::Buffer => {
1693                     // The ExternalImageType::Buffer should be handled by resource_cache.
1694                     // It should go through the non-external case.
1695                     panic!("Unexpected non-texture handle type");
1696                 }
1697             }
1698         }
1699         _ => ImageBufferKind::Texture2DArray,
1700     }
1701 }
1702