webrender/
batch.rs

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
5use api::{AlphaType, ClipMode, ImageBufferKind};
6use api::{FontInstanceFlags, YuvColorSpace, YuvFormat, ColorDepth, ColorRange, PremultipliedColorF};
7use api::units::*;
8use crate::clip::{ClipNodeFlags, ClipNodeRange, ClipItemKind, ClipStore};
9use crate::command_buffer::PrimitiveCommand;
10use crate::composite::CompositorSurfaceKind;
11use crate::pattern::PatternKind;
12use crate::spatial_tree::{SpatialTree, SpatialNodeIndex, CoordinateSystemId};
13use glyph_rasterizer::{GlyphFormat, SubpixelDirection};
14use crate::gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress};
15use crate::gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders, ZBufferId, ZBufferIdGenerator};
16use crate::gpu_types::SplitCompositeInstance;
17use crate::gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance};
18use crate::gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette};
19use crate::gpu_types::{ImageBrushData, get_shader_opacity, BoxShadowData, MaskInstance};
20use crate::gpu_types::{ClipMaskInstanceCommon, ClipMaskInstanceRect, ClipMaskInstanceBoxShadow};
21use crate::internal_types::{FastHashMap, Filter, FrameAllocator, FrameMemory, FrameVec, Swizzle, TextureSource};
22use crate::picture::{Picture3DContext, PictureCompositeMode, calculate_screen_uv};
23use crate::prim_store::{PrimitiveInstanceKind, ClipData};
24use crate::prim_store::{PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex};
25use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex};
26use crate::prim_store::VECS_PER_SEGMENT;
27use crate::quad;
28use crate::render_target::RenderTargetContext;
29use crate::render_task_graph::{RenderTaskId, RenderTaskGraph};
30use crate::render_task::{RenderTaskAddress, RenderTaskKind, SubPass};
31use crate::renderer::{BlendMode, GpuBufferBuilder, ShaderColorMode};
32use crate::renderer::MAX_VERTEX_TEXTURE_WIDTH;
33use crate::resource_cache::{GlyphFetchResult, ImageProperties};
34use crate::space::SpaceMapper;
35use crate::visibility::{PrimitiveVisibilityFlags, VisibilityState};
36use smallvec::SmallVec;
37use std::{f32, i32, usize};
38use crate::util::{project_rect, MaxRect, TransformedRectKind, ScaleOffset};
39use crate::segment::EdgeAaSegmentMask;
40
41// Special sentinel value recognized by the shader. It is considered to be
42// a dummy task that doesn't mask out anything.
43const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fffffff);
44
45/// Used to signal there are no segments provided with this primitive.
46pub const INVALID_SEGMENT_INDEX: i32 = 0xffff;
47
48/// Size in device pixels for tiles that clip masks are drawn in.
49const CLIP_RECTANGLE_TILE_SIZE: i32 = 128;
50
51/// The minimum size of a clip mask before trying to draw in tiles.
52const CLIP_RECTANGLE_AREA_THRESHOLD: f32 = (CLIP_RECTANGLE_TILE_SIZE * CLIP_RECTANGLE_TILE_SIZE * 4) as f32;
53
54#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
55#[cfg_attr(feature = "capture", derive(Serialize))]
56#[cfg_attr(feature = "replay", derive(Deserialize))]
57pub enum BrushBatchKind {
58    Solid,
59    Image(ImageBufferKind),
60    Blend,
61    MixBlend {
62        task_id: RenderTaskId,
63        backdrop_id: RenderTaskId,
64    },
65    YuvImage(ImageBufferKind, YuvFormat, ColorDepth, YuvColorSpace, ColorRange),
66    LinearGradient,
67    Opacity,
68}
69
70#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
71#[cfg_attr(feature = "capture", derive(Serialize))]
72#[cfg_attr(feature = "replay", derive(Deserialize))]
73pub enum BatchKind {
74    SplitComposite,
75    TextRun(GlyphFormat),
76    Brush(BrushBatchKind),
77    Quad(PatternKind),
78}
79
80/// Input textures for a primitive, without consideration of clip mask
81#[derive(Copy, Clone, Debug)]
82#[cfg_attr(feature = "capture", derive(Serialize))]
83#[cfg_attr(feature = "replay", derive(Deserialize))]
84pub struct TextureSet {
85    pub colors: [TextureSource; 3],
86}
87
88impl TextureSet {
89    const UNTEXTURED: TextureSet = TextureSet {
90        colors: [
91            TextureSource::Invalid,
92            TextureSource::Invalid,
93            TextureSource::Invalid,
94        ],
95    };
96
97    /// A textured primitive
98    fn prim_textured(
99        color: TextureSource,
100    ) -> Self {
101        TextureSet {
102            colors: [
103                color,
104                TextureSource::Invalid,
105                TextureSource::Invalid,
106            ],
107        }
108    }
109
110    fn is_compatible_with(&self, other: &TextureSet) -> bool {
111        self.colors[0].is_compatible(&other.colors[0]) &&
112        self.colors[1].is_compatible(&other.colors[1]) &&
113        self.colors[2].is_compatible(&other.colors[2])
114    }
115}
116
117impl TextureSource {
118    fn combine(&self, other: TextureSource) -> TextureSource {
119        if other == TextureSource::Invalid {
120            *self
121        } else {
122            other
123        }
124    }
125}
126
127/// Optional textures that can be used as a source in the shaders.
128/// Textures that are not used by the batch are equal to TextureId::invalid().
129#[derive(Copy, Clone, Debug)]
130#[cfg_attr(feature = "capture", derive(Serialize))]
131#[cfg_attr(feature = "replay", derive(Deserialize))]
132pub struct BatchTextures {
133    pub input: TextureSet,
134    pub clip_mask: TextureSource,
135}
136
137impl BatchTextures {
138    /// An empty batch textures (no binding slots set)
139    pub fn empty() -> BatchTextures {
140        BatchTextures {
141            input: TextureSet::UNTEXTURED,
142            clip_mask: TextureSource::Invalid,
143        }
144    }
145
146    /// A textured primitive with optional clip mask
147    pub fn prim_textured(
148        color: TextureSource,
149        clip_mask: TextureSource,
150    ) -> BatchTextures {
151        BatchTextures {
152            input: TextureSet::prim_textured(color),
153            clip_mask,
154        }
155    }
156
157    /// An untextured primitive with optional clip mask
158    pub fn prim_untextured(
159        clip_mask: TextureSource,
160    ) -> BatchTextures {
161        BatchTextures {
162            input: TextureSet::UNTEXTURED,
163            clip_mask,
164        }
165    }
166
167    /// A composite style effect with single input texture
168    pub fn composite_rgb(
169        texture: TextureSource,
170    ) -> BatchTextures {
171        BatchTextures {
172            input: TextureSet {
173                colors: [
174                    texture,
175                    TextureSource::Invalid,
176                    TextureSource::Invalid,
177                ],
178            },
179            clip_mask: TextureSource::Invalid,
180        }
181    }
182
183    /// A composite style effect with up to 3 input textures
184    pub fn composite_yuv(
185        color0: TextureSource,
186        color1: TextureSource,
187        color2: TextureSource,
188    ) -> BatchTextures {
189        BatchTextures {
190            input: TextureSet {
191                colors: [color0, color1, color2],
192            },
193            clip_mask: TextureSource::Invalid,
194        }
195    }
196
197    pub fn is_compatible_with(&self, other: &BatchTextures) -> bool {
198        if !self.clip_mask.is_compatible(&other.clip_mask) {
199            return false;
200        }
201
202        self.input.is_compatible_with(&other.input)
203    }
204
205    pub fn combine_textures(&self, other: BatchTextures) -> Option<BatchTextures> {
206        if !self.is_compatible_with(&other) {
207            return None;
208        }
209
210        let mut new_textures = BatchTextures::empty();
211
212        new_textures.clip_mask = self.clip_mask.combine(other.clip_mask);
213
214        for i in 0 .. 3 {
215            new_textures.input.colors[i] = self.input.colors[i].combine(other.input.colors[i]);
216        }
217
218        Some(new_textures)
219    }
220
221    fn merge(&mut self, other: &BatchTextures) {
222        self.clip_mask = self.clip_mask.combine(other.clip_mask);
223
224        for (s, o) in self.input.colors.iter_mut().zip(other.input.colors.iter()) {
225            *s = s.combine(*o);
226        }
227    }
228}
229
230#[derive(Copy, Clone, Debug)]
231#[cfg_attr(feature = "capture", derive(Serialize))]
232#[cfg_attr(feature = "replay", derive(Deserialize))]
233pub struct BatchKey {
234    pub kind: BatchKind,
235    pub blend_mode: BlendMode,
236    pub textures: BatchTextures,
237}
238
239impl BatchKey {
240    pub fn new(kind: BatchKind, blend_mode: BlendMode, textures: BatchTextures) -> Self {
241        BatchKey {
242            kind,
243            blend_mode,
244            textures,
245        }
246    }
247
248    pub fn is_compatible_with(&self, other: &BatchKey) -> bool {
249        self.kind == other.kind && self.blend_mode == other.blend_mode && self.textures.is_compatible_with(&other.textures)
250    }
251}
252
253pub struct BatchRects {
254    /// Union of all of the batch's item rects.
255    ///
256    /// Very often we can skip iterating over item rects by testing against
257    /// this one first.
258    batch: PictureRect,
259    /// When the batch rectangle above isn't a good enough approximation, we
260    /// store per item rects.
261    items: Option<FrameVec<PictureRect>>,
262    // TODO: batch rects don't need to be part of the frame but they currently
263    // are. It may be cleaner to remove them from the frame's final data structure
264    // and not use the frame's allocator.
265    allocator: FrameAllocator,
266}
267
268impl BatchRects {
269    fn new(allocator: FrameAllocator) -> Self {
270        BatchRects {
271            batch: PictureRect::zero(),
272            items: None,
273            allocator,
274        }
275    }
276
277    #[inline]
278    fn add_rect(&mut self, rect: &PictureRect) {
279        let union = self.batch.union(rect);
280        // If we have already started storing per-item rects, continue doing so.
281        // Otherwise, check whether only storing the batch rect is a good enough
282        // approximation.
283        if let Some(items) = &mut self.items {
284            items.push(*rect);
285        } else if self.batch.area() + rect.area() < union.area() {
286            let mut items = self.allocator.clone().new_vec_with_capacity(16);
287            items.push(self.batch);
288            items.push(*rect);
289            self.items = Some(items);
290        }
291
292        self.batch = union;
293    }
294
295    #[inline]
296    fn intersects(&mut self, rect: &PictureRect) -> bool {
297        if !self.batch.intersects(rect) {
298            return false;
299        }
300
301        if let Some(items) = &self.items {
302            items.iter().any(|item| item.intersects(rect))
303        } else {
304            // If we don't have per-item rects it means the batch rect is a good
305            // enough approximation and we didn't bother storing per-rect items.
306            true
307        }
308    }
309}
310
311
312pub struct AlphaBatchList {
313    pub batches: FrameVec<PrimitiveBatch>,
314    pub batch_rects: FrameVec<BatchRects>,
315    current_batch_index: usize,
316    current_z_id: ZBufferId,
317    break_advanced_blend_batches: bool,
318}
319
320impl AlphaBatchList {
321    fn new(break_advanced_blend_batches: bool, preallocate: usize, memory: &FrameMemory) -> Self {
322        AlphaBatchList {
323            batches: memory.new_vec_with_capacity(preallocate),
324            batch_rects: memory.new_vec_with_capacity(preallocate),
325            current_z_id: ZBufferId::invalid(),
326            current_batch_index: usize::MAX,
327            break_advanced_blend_batches,
328        }
329    }
330
331    /// Clear all current batches in this list. This is typically used
332    /// when a primitive is encountered that occludes all previous
333    /// content in this batch list.
334    fn clear(&mut self) {
335        self.current_batch_index = usize::MAX;
336        self.current_z_id = ZBufferId::invalid();
337        self.batches.clear();
338        self.batch_rects.clear();
339    }
340
341    pub fn set_params_and_get_batch(
342        &mut self,
343        key: BatchKey,
344        features: BatchFeatures,
345        // The bounding box of everything at this Z plane. We expect potentially
346        // multiple primitive segments coming with the same `z_id`.
347        z_bounding_rect: &PictureRect,
348        z_id: ZBufferId,
349    ) -> &mut FrameVec<PrimitiveInstanceData> {
350        if z_id != self.current_z_id ||
351           self.current_batch_index == usize::MAX ||
352           !self.batches[self.current_batch_index].key.is_compatible_with(&key)
353        {
354            let mut selected_batch_index = None;
355
356            match key.blend_mode {
357                BlendMode::Advanced(_) if self.break_advanced_blend_batches => {
358                    // don't try to find a batch
359                }
360                _ => {
361                    for (batch_index, batch) in self.batches.iter().enumerate().rev() {
362                        // For normal batches, we only need to check for overlaps for batches
363                        // other than the first batch we consider. If the first batch
364                        // is compatible, then we know there isn't any potential overlap
365                        // issues to worry about.
366                        if batch.key.is_compatible_with(&key) {
367                            selected_batch_index = Some(batch_index);
368                            break;
369                        }
370
371                        // check for intersections
372                        if self.batch_rects[batch_index].intersects(z_bounding_rect) {
373                            break;
374                        }
375                    }
376                }
377            }
378
379            if selected_batch_index.is_none() {
380                // Text runs tend to have a lot of instances per batch, causing a lot of reallocation
381                // churn as items are added one by one, so we give it a head start. Ideally we'd start
382                // with a larger number, closer to 1k but in some bad cases with lots of batch break
383                // we would be wasting a lot of memory.
384                // Generally it is safe to preallocate small-ish values for other batch kinds because
385                // the items are small and there are no zero-sized batches so there will always be
386                // at least one allocation.
387                let prealloc = match key.kind {
388                    BatchKind::TextRun(..) => 128,
389                    _ => 16,
390                };
391                let mut new_batch = PrimitiveBatch::new(key, self.batches.allocator().clone());
392                new_batch.instances.reserve(prealloc);
393                selected_batch_index = Some(self.batches.len());
394                self.batches.push(new_batch);
395                self.batch_rects.push(BatchRects::new(self.batches.allocator().clone()));
396            }
397
398            self.current_batch_index = selected_batch_index.unwrap();
399            self.batch_rects[self.current_batch_index].add_rect(z_bounding_rect);
400            self.current_z_id = z_id;
401        }
402
403        let batch = &mut self.batches[self.current_batch_index];
404        batch.features |= features;
405        batch.key.textures.merge(&key.textures);
406
407        &mut batch.instances
408    }
409}
410
411pub struct OpaqueBatchList {
412    pub pixel_area_threshold_for_new_batch: f32,
413    pub batches: FrameVec<PrimitiveBatch>,
414    pub current_batch_index: usize,
415    lookback_count: usize,
416}
417
418impl OpaqueBatchList {
419    fn new(pixel_area_threshold_for_new_batch: f32, lookback_count: usize, memory: &FrameMemory) -> Self {
420        OpaqueBatchList {
421            batches: memory.new_vec(),
422            pixel_area_threshold_for_new_batch,
423            current_batch_index: usize::MAX,
424            lookback_count,
425        }
426    }
427
428    /// Clear all current batches in this list. This is typically used
429    /// when a primitive is encountered that occludes all previous
430    /// content in this batch list.
431    fn clear(&mut self) {
432        self.current_batch_index = usize::MAX;
433        self.batches.clear();
434    }
435
436    pub fn set_params_and_get_batch(
437        &mut self,
438        key: BatchKey,
439        features: BatchFeatures,
440        // The bounding box of everything at the current Z, whatever it is. We expect potentially
441        // multiple primitive segments produced by a primitive, which we allow to check
442        // `current_batch_index` instead of iterating the batches.
443        z_bounding_rect: &PictureRect,
444    ) -> &mut FrameVec<PrimitiveInstanceData> {
445        // If the area of this primitive is larger than the given threshold,
446        // then it is large enough to warrant breaking a batch for. In this
447        // case we just see if it can be added to the existing batch or
448        // create a new one.
449        let is_large_occluder = z_bounding_rect.area() > self.pixel_area_threshold_for_new_batch;
450        // Since primitives of the same kind tend to come in succession, we keep track
451        // of the current batch index to skip the search in some cases. We ignore the
452        // current batch index in the case of large occluders to make sure they get added
453        // at the top of the bach list.
454        if is_large_occluder || self.current_batch_index == usize::MAX ||
455           !self.batches[self.current_batch_index].key.is_compatible_with(&key) {
456            let mut selected_batch_index = None;
457            if is_large_occluder {
458                if let Some(batch) = self.batches.last() {
459                    if batch.key.is_compatible_with(&key) {
460                        selected_batch_index = Some(self.batches.len() - 1);
461                    }
462                }
463            } else {
464                // Otherwise, look back through a reasonable number of batches.
465                for (batch_index, batch) in self.batches.iter().enumerate().rev().take(self.lookback_count) {
466                    if batch.key.is_compatible_with(&key) {
467                        selected_batch_index = Some(batch_index);
468                        break;
469                    }
470                }
471            }
472
473            if selected_batch_index.is_none() {
474                let new_batch = PrimitiveBatch::new(key, self.batches.allocator().clone());
475                selected_batch_index = Some(self.batches.len());
476                self.batches.push(new_batch);
477            }
478
479            self.current_batch_index = selected_batch_index.unwrap();
480        }
481
482        let batch = &mut self.batches[self.current_batch_index];
483        batch.features |= features;
484        batch.key.textures.merge(&key.textures);
485
486        &mut batch.instances
487    }
488
489    fn finalize(&mut self) {
490        // Reverse the instance arrays in the opaque batches
491        // to get maximum z-buffer efficiency by drawing
492        // front-to-back.
493        // TODO(gw): Maybe we can change the batch code to
494        //           build these in reverse and avoid having
495        //           to reverse the instance array here.
496        for batch in &mut self.batches {
497            batch.instances.reverse();
498        }
499    }
500}
501
502#[cfg_attr(feature = "capture", derive(Serialize))]
503#[cfg_attr(feature = "replay", derive(Deserialize))]
504pub struct PrimitiveBatch {
505    pub key: BatchKey,
506    pub instances: FrameVec<PrimitiveInstanceData>,
507    pub features: BatchFeatures,
508}
509
510bitflags! {
511    /// Features of the batch that, if not requested, may allow a fast-path.
512    ///
513    /// Rather than breaking batches when primitives request different features,
514    /// we always request the minimum amount of features to satisfy all items in
515    /// the batch.
516    /// The goal is to let the renderer be optionally select more specialized
517    /// versions of a shader if the batch doesn't require code certain code paths.
518    /// Not all shaders necessarily implement all of these features.
519    #[cfg_attr(feature = "capture", derive(Serialize))]
520    #[cfg_attr(feature = "replay", derive(Deserialize))]
521    #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
522    pub struct BatchFeatures: u8 {
523        const ALPHA_PASS = 1 << 0;
524        const ANTIALIASING = 1 << 1;
525        const REPETITION = 1 << 2;
526        /// Indicates a primitive in this batch may use a clip mask.
527        const CLIP_MASK = 1 << 3;
528    }
529}
530
531impl PrimitiveBatch {
532    fn new(key: BatchKey, allocator: FrameAllocator) -> PrimitiveBatch {
533        PrimitiveBatch {
534            key,
535            instances: FrameVec::new_in(allocator),
536            features: BatchFeatures::empty(),
537        }
538    }
539
540    fn merge(&mut self, other: PrimitiveBatch) {
541        self.instances.extend(other.instances);
542        self.features |= other.features;
543        self.key.textures.merge(&other.key.textures);
544    }
545}
546
547#[cfg_attr(feature = "capture", derive(Serialize))]
548#[cfg_attr(feature = "replay", derive(Deserialize))]
549pub struct AlphaBatchContainer {
550    pub opaque_batches: FrameVec<PrimitiveBatch>,
551    pub alpha_batches: FrameVec<PrimitiveBatch>,
552    /// The overall scissor rect for this render task, if one
553    /// is required.
554    pub task_scissor_rect: Option<DeviceIntRect>,
555    /// The rectangle of the owning render target that this
556    /// set of batches affects.
557    pub task_rect: DeviceIntRect,
558}
559
560impl AlphaBatchContainer {
561    pub fn new(
562        task_scissor_rect: Option<DeviceIntRect>,
563        memory: &FrameMemory,
564    ) -> AlphaBatchContainer {
565        AlphaBatchContainer {
566            opaque_batches: memory.new_vec(),
567            alpha_batches: memory.new_vec(),
568            task_scissor_rect,
569            task_rect: DeviceIntRect::zero(),
570        }
571    }
572
573    pub fn is_empty(&self) -> bool {
574        self.opaque_batches.is_empty() &&
575        self.alpha_batches.is_empty()
576    }
577
578    fn merge(&mut self, builder: AlphaBatchBuilder, task_rect: &DeviceIntRect) {
579        self.task_rect = self.task_rect.union(task_rect);
580
581        for other_batch in builder.opaque_batch_list.batches {
582            let batch_index = self.opaque_batches.iter().position(|batch| {
583                batch.key.is_compatible_with(&other_batch.key)
584            });
585
586            match batch_index {
587                Some(batch_index) => {
588                    self.opaque_batches[batch_index].merge(other_batch);
589                }
590                None => {
591                    self.opaque_batches.push(other_batch);
592                }
593            }
594        }
595
596        let mut min_batch_index = 0;
597
598        for other_batch in builder.alpha_batch_list.batches {
599            let batch_index = self.alpha_batches.iter().skip(min_batch_index).position(|batch| {
600                batch.key.is_compatible_with(&other_batch.key)
601            });
602
603            match batch_index {
604                Some(batch_index) => {
605                    let index = batch_index + min_batch_index;
606                    self.alpha_batches[index].merge(other_batch);
607                    min_batch_index = index;
608                }
609                None => {
610                    self.alpha_batches.push(other_batch);
611                    min_batch_index = self.alpha_batches.len();
612                }
613            }
614        }
615    }
616}
617
618/// Each segment can optionally specify a per-segment
619/// texture set and one user data field.
620#[derive(Debug, Copy, Clone)]
621struct SegmentInstanceData {
622    textures: TextureSet,
623    specific_resource_address: i32,
624}
625
626/// Encapsulates the logic of building batches for items that are blended.
627pub struct AlphaBatchBuilder {
628    pub alpha_batch_list: AlphaBatchList,
629    pub opaque_batch_list: OpaqueBatchList,
630    pub render_task_id: RenderTaskId,
631    render_task_address: RenderTaskAddress,
632}
633
634impl AlphaBatchBuilder {
635    pub fn new(
636        screen_size: DeviceIntSize,
637        break_advanced_blend_batches: bool,
638        lookback_count: usize,
639        render_task_id: RenderTaskId,
640        render_task_address: RenderTaskAddress,
641        memory: &FrameMemory,
642    ) -> Self {
643        // The threshold for creating a new batch is
644        // one quarter the screen size.
645        let batch_area_threshold = (screen_size.width * screen_size.height) as f32 / 4.0;
646
647        AlphaBatchBuilder {
648            alpha_batch_list: AlphaBatchList::new(break_advanced_blend_batches, 128, memory),
649            opaque_batch_list: OpaqueBatchList::new(batch_area_threshold, lookback_count, memory),
650            render_task_id,
651            render_task_address,
652        }
653    }
654
655    /// Clear all current batches in this builder. This is typically used
656    /// when a primitive is encountered that occludes all previous
657    /// content in this batch list.
658    fn clear(&mut self) {
659        self.alpha_batch_list.clear();
660        self.opaque_batch_list.clear();
661    }
662
663    pub fn build(
664        mut self,
665        batch_containers: &mut FrameVec<AlphaBatchContainer>,
666        merged_batches: &mut AlphaBatchContainer,
667        task_rect: DeviceIntRect,
668        task_scissor_rect: Option<DeviceIntRect>,
669    ) {
670        self.opaque_batch_list.finalize();
671
672        if task_scissor_rect.is_none() {
673            merged_batches.merge(self, &task_rect);
674        } else {
675            batch_containers.push(AlphaBatchContainer {
676                alpha_batches: self.alpha_batch_list.batches,
677                opaque_batches: self.opaque_batch_list.batches,
678                task_scissor_rect,
679                task_rect,
680            });
681        }
682    }
683
684    pub fn push_single_instance(
685        &mut self,
686        key: BatchKey,
687        features: BatchFeatures,
688        bounding_rect: &PictureRect,
689        z_id: ZBufferId,
690        instance: PrimitiveInstanceData,
691    ) {
692        self.set_params_and_get_batch(key, features, bounding_rect, z_id)
693            .push(instance);
694    }
695
696    pub fn set_params_and_get_batch(
697        &mut self,
698        key: BatchKey,
699        features: BatchFeatures,
700        bounding_rect: &PictureRect,
701        z_id: ZBufferId,
702    ) -> &mut FrameVec<PrimitiveInstanceData> {
703        match key.blend_mode {
704            BlendMode::None => {
705                self.opaque_batch_list
706                    .set_params_and_get_batch(key, features, bounding_rect)
707            }
708            BlendMode::Alpha |
709            BlendMode::PremultipliedAlpha |
710            BlendMode::PremultipliedDestOut |
711            BlendMode::SubpixelDualSource |
712            BlendMode::Advanced(_) |
713            BlendMode::MultiplyDualSource |
714            BlendMode::Screen |
715            BlendMode::Exclusion |
716            BlendMode::PlusLighter => {
717                self.alpha_batch_list
718                    .set_params_and_get_batch(key, features, bounding_rect, z_id)
719            }
720        }
721    }
722}
723
724/// Supports (recursively) adding a list of primitives and pictures to an alpha batch
725/// builder. In future, it will support multiple dirty regions / slices, allowing the
726/// contents of a picture to be spliced into multiple batch builders.
727pub struct BatchBuilder {
728    /// A temporary buffer that is used during glyph fetching, stored here
729    /// to reduce memory allocations.
730    glyph_fetch_buffer: Vec<GlyphFetchResult>,
731
732    batcher: AlphaBatchBuilder,
733}
734
735impl BatchBuilder {
736    pub fn new(batcher: AlphaBatchBuilder) -> Self {
737        BatchBuilder {
738            glyph_fetch_buffer: Vec::new(),
739            batcher,
740        }
741    }
742
743    pub fn finalize(self) -> AlphaBatchBuilder {
744        self.batcher
745    }
746
747    fn add_brush_instance_to_batches(
748        &mut self,
749        batch_key: BatchKey,
750        features: BatchFeatures,
751        bounding_rect: &PictureRect,
752        z_id: ZBufferId,
753        segment_index: i32,
754        edge_flags: EdgeAaSegmentMask,
755        clip_task_address: RenderTaskAddress,
756        brush_flags: BrushFlags,
757        prim_header_index: PrimitiveHeaderIndex,
758        resource_address: i32,
759    ) {
760        assert!(
761            !(brush_flags.contains(BrushFlags::NORMALIZED_UVS)
762                && features.contains(BatchFeatures::REPETITION)),
763            "Normalized UVs are not supported with repetition."
764        );
765        let instance = BrushInstance {
766            segment_index,
767            edge_flags,
768            clip_task_address,
769            brush_flags,
770            prim_header_index,
771            resource_address,
772        };
773
774        self.batcher.push_single_instance(
775            batch_key,
776            features,
777            bounding_rect,
778            z_id,
779            PrimitiveInstanceData::from(instance),
780        );
781    }
782
783    fn add_split_composite_instance_to_batches(
784        &mut self,
785        batch_key: BatchKey,
786        features: BatchFeatures,
787        bounding_rect: &PictureRect,
788        z_id: ZBufferId,
789        prim_header_index: PrimitiveHeaderIndex,
790        polygons_address: i32,
791    ) {
792        let render_task_address = self.batcher.render_task_address;
793
794        self.batcher.push_single_instance(
795            batch_key,
796            features,
797            bounding_rect,
798            z_id,
799            PrimitiveInstanceData::from(SplitCompositeInstance {
800                prim_header_index,
801                render_task_address,
802                polygons_address,
803                z: z_id,
804            }),
805        );
806    }
807
808    /// Clear all current batchers. This is typically used when a primitive
809    /// is encountered that occludes all previous content in this batch list.
810    fn clear_batches(&mut self) {
811        self.batcher.clear();
812    }
813
814    // Adds a primitive to a batch.
815    // It can recursively call itself in some situations, for
816    // example if it encounters a picture where the items
817    // in that picture are being drawn into the same target.
818    pub fn add_prim_to_batch(
819        &mut self,
820        cmd: &PrimitiveCommand,
821        prim_spatial_node_index: SpatialNodeIndex,
822        ctx: &RenderTargetContext,
823        gpu_cache: &mut GpuCache,
824        render_tasks: &RenderTaskGraph,
825        prim_headers: &mut PrimitiveHeaders,
826        transforms: &mut TransformPalette,
827        root_spatial_node_index: SpatialNodeIndex,
828        surface_spatial_node_index: SpatialNodeIndex,
829        z_generator: &mut ZBufferIdGenerator,
830        prim_instances: &[PrimitiveInstance],
831        gpu_buffer_builder: &mut GpuBufferBuilder,
832        segments: &[RenderTaskId],
833    ) {
834        let (prim_instance_index, extra_prim_gpu_address) = match cmd {
835            PrimitiveCommand::Simple { prim_instance_index } => {
836                (prim_instance_index, None)
837            }
838            PrimitiveCommand::Complex { prim_instance_index, gpu_address } => {
839                (prim_instance_index, Some(gpu_address.as_int()))
840            }
841            PrimitiveCommand::Instance { prim_instance_index, gpu_buffer_address } => {
842                (prim_instance_index, Some(gpu_buffer_address.as_int()))
843            }
844            PrimitiveCommand::Quad { pattern, pattern_input, prim_instance_index, gpu_buffer_address, quad_flags, edge_flags, transform_id, src_color_task_id } => {
845                let prim_instance = &prim_instances[prim_instance_index.0 as usize];
846                let prim_info = &prim_instance.vis;
847                let bounding_rect = &prim_info.clip_chain.pic_coverage_rect;
848                let render_task_address = self.batcher.render_task_address;
849
850                if segments.is_empty() {
851                    let z_id = z_generator.next();
852
853                    quad::add_to_batch(
854                        *pattern,
855                        *pattern_input,
856                        render_task_address,
857                        *transform_id,
858                        *gpu_buffer_address,
859                        *quad_flags,
860                        *edge_flags,
861                        INVALID_SEGMENT_INDEX as u8,
862                        *src_color_task_id,
863                        z_id,
864                        render_tasks,
865                        gpu_buffer_builder,
866                        |key, instance| {
867                            let batch = self.batcher.set_params_and_get_batch(
868                                key,
869                                BatchFeatures::empty(),
870                                bounding_rect,
871                                z_id,
872                            );
873                            batch.push(instance);
874                        },
875                    );
876                } else {
877                    for (i, task_id) in segments.iter().enumerate() {
878                        // TODO(gw): edge_flags should be per-segment, when used for more than composites
879                        debug_assert!(edge_flags.is_empty());
880
881                        let z_id = z_generator.next();
882
883                        quad::add_to_batch(
884                            *pattern,
885                            *pattern_input,
886                            render_task_address,
887                            *transform_id,
888                            *gpu_buffer_address,
889                            *quad_flags,
890                            *edge_flags,
891                            i as u8,
892                            *task_id,
893                            z_id,
894                            render_tasks,
895                            gpu_buffer_builder,
896                            |key, instance| {
897                                let batch = self.batcher.set_params_and_get_batch(
898                                    key,
899                                    BatchFeatures::empty(),
900                                    bounding_rect,
901                                    z_id,
902                                );
903                                batch.push(instance);
904                            },
905                        );
906                    }
907                }
908
909                return;
910            }
911        };
912
913        let prim_instance = &prim_instances[prim_instance_index.0 as usize];
914        let is_anti_aliased = ctx.data_stores.prim_has_anti_aliasing(prim_instance);
915
916        let brush_flags = if is_anti_aliased {
917            BrushFlags::FORCE_AA
918        } else {
919            BrushFlags::empty()
920        };
921
922        let vis_flags = match prim_instance.vis.state {
923            VisibilityState::Culled => {
924                return;
925            }
926            VisibilityState::PassThrough |
927            VisibilityState::Unset => {
928                panic!("bug: invalid visibility state");
929            }
930            VisibilityState::Visible { vis_flags, .. } => {
931                vis_flags
932            }
933        };
934
935        // If this primitive is a backdrop, that means that it is known to cover
936        // the entire picture cache background. In that case, the renderer will
937        // use the backdrop color as a clear color, and so we can drop this
938        // primitive and any prior primitives from the batch lists for this
939        // picture cache slice.
940        if vis_flags.contains(PrimitiveVisibilityFlags::IS_BACKDROP) {
941            self.clear_batches();
942            return;
943        }
944
945        let transform_id = transforms
946            .get_id(
947                prim_spatial_node_index,
948                root_spatial_node_index,
949                ctx.spatial_tree,
950            );
951
952        // TODO(gw): Calculating this for every primitive is a bit
953        //           wasteful. We should probably cache this in
954        //           the scroll node...
955        let transform_kind = transform_id.transform_kind();
956        let prim_info = &prim_instance.vis;
957        let bounding_rect = &prim_info.clip_chain.pic_coverage_rect;
958
959        let z_id = z_generator.next();
960
961        let prim_rect = ctx.data_stores.get_local_prim_rect(
962            prim_instance,
963            &ctx.prim_store.pictures,
964            ctx.surfaces,
965        );
966
967        let mut batch_features = BatchFeatures::empty();
968        if ctx.data_stores.prim_may_need_repetition(prim_instance) {
969            batch_features |= BatchFeatures::REPETITION;
970        }
971
972        if transform_kind != TransformedRectKind::AxisAligned || is_anti_aliased {
973            batch_features |= BatchFeatures::ANTIALIASING;
974        }
975
976        // Check if the primitive might require a clip mask.
977        if prim_info.clip_task_index != ClipTaskIndex::INVALID {
978            batch_features |= BatchFeatures::CLIP_MASK;
979        }
980
981        if !bounding_rect.is_empty() {
982            debug_assert_eq!(prim_info.clip_chain.pic_spatial_node_index, surface_spatial_node_index,
983                "The primitive's bounding box is specified in a different coordinate system from the current batch!");
984        }
985
986        match prim_instance.kind {
987            PrimitiveInstanceKind::BoxShadow { .. } => {
988                unreachable!("BUG: Should not hit box-shadow here as they are handled by quad infra");
989            }
990            PrimitiveInstanceKind::Clear { data_handle } => {
991                let prim_data = &ctx.data_stores.prim[data_handle];
992                let prim_cache_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
993
994                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
995                    prim_info.clip_task_index,
996                    render_tasks,
997                ).unwrap();
998
999                // TODO(gw): We can abstract some of the common code below into
1000                //           helper methods, as we port more primitives to make
1001                //           use of interning.
1002
1003                let prim_header = PrimitiveHeader {
1004                    local_rect: prim_rect,
1005                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
1006                    specific_prim_address: prim_cache_address,
1007                    transform_id,
1008                };
1009
1010                let prim_header_index = prim_headers.push(
1011                    &prim_header,
1012                    z_id,
1013                    self.batcher.render_task_address,
1014                    [get_shader_opacity(1.0), 0, 0, 0],
1015                );
1016
1017                let batch_key = BatchKey {
1018                    blend_mode: BlendMode::PremultipliedDestOut,
1019                    kind: BatchKind::Brush(BrushBatchKind::Solid),
1020                    textures: BatchTextures::prim_untextured(clip_mask_texture_id),
1021                };
1022
1023                self.add_brush_instance_to_batches(
1024                    batch_key,
1025                    batch_features,
1026                    bounding_rect,
1027                    z_id,
1028                    INVALID_SEGMENT_INDEX,
1029                    prim_data.edge_aa_mask,
1030                    clip_task_address,
1031                    brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
1032                    prim_header_index,
1033                    0,
1034                );
1035            }
1036            PrimitiveInstanceKind::NormalBorder { data_handle, ref render_task_ids, .. } => {
1037                let prim_data = &ctx.data_stores.normal_border[data_handle];
1038                let common_data = &prim_data.common;
1039                let prim_cache_address = gpu_cache.get_address(&common_data.gpu_cache_handle);
1040                let task_ids = &ctx.scratch.border_cache_handles[*render_task_ids];
1041                let specified_blend_mode = BlendMode::PremultipliedAlpha;
1042                let mut segment_data: SmallVec<[SegmentInstanceData; 8]> = SmallVec::new();
1043
1044                // Collect the segment instance data from each render
1045                // task for each valid edge / corner of the border.
1046
1047                for task_id in task_ids {
1048                    if let Some((uv_rect_address, texture)) = render_tasks.resolve_location(*task_id, gpu_cache) {
1049                        segment_data.push(
1050                            SegmentInstanceData {
1051                                textures: TextureSet::prim_textured(texture),
1052                                specific_resource_address: uv_rect_address.as_int(),
1053                            }
1054                        );
1055                    }
1056                }
1057
1058                // TODO: it would be less error-prone to get this info from the texture cache.
1059                let image_buffer_kind = ImageBufferKind::Texture2D;
1060
1061                let blend_mode = if !common_data.opacity.is_opaque ||
1062                    prim_info.clip_task_index != ClipTaskIndex::INVALID ||
1063                    transform_kind == TransformedRectKind::Complex ||
1064                    is_anti_aliased
1065                {
1066                    specified_blend_mode
1067                } else {
1068                    BlendMode::None
1069                };
1070
1071                let prim_header = PrimitiveHeader {
1072                    local_rect: prim_rect,
1073                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
1074                    specific_prim_address: prim_cache_address,
1075                    transform_id,
1076                };
1077
1078                let batch_params = BrushBatchParameters::instanced(
1079                    BrushBatchKind::Image(image_buffer_kind),
1080                    ImageBrushData {
1081                        color_mode: ShaderColorMode::Image,
1082                        alpha_type: AlphaType::PremultipliedAlpha,
1083                        raster_space: RasterizationSpace::Local,
1084                        opacity: 1.0,
1085                    }.encode(),
1086                    segment_data,
1087                );
1088
1089                let prim_header_index = prim_headers.push(
1090                    &prim_header,
1091                    z_id,
1092                    self.batcher.render_task_address,
1093                    batch_params.prim_user_data,
1094                );
1095
1096                let border_data = &prim_data.kind;
1097                self.add_segmented_prim_to_batch(
1098                    Some(border_data.brush_segments.as_slice()),
1099                    common_data.opacity,
1100                    &batch_params,
1101                    blend_mode,
1102                    batch_features,
1103                    brush_flags,
1104                    common_data.edge_aa_mask,
1105                    prim_header_index,
1106                    bounding_rect,
1107                    transform_kind,
1108                    z_id,
1109                    prim_info.clip_task_index,
1110                    ctx,
1111                    render_tasks,
1112                );
1113            }
1114            PrimitiveInstanceKind::TextRun { data_handle, run_index, .. } => {
1115                let run = &ctx.prim_store.text_runs[run_index];
1116                let subpx_dir = run.used_font.get_subpx_dir();
1117
1118                // The GPU cache data is stored in the template and reused across
1119                // frames and display lists.
1120                let prim_data = &ctx.data_stores.text_run[data_handle];
1121                let prim_cache_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
1122
1123                // The local prim rect is only informative for text primitives, as
1124                // thus is not directly necessary for any drawing of the text run.
1125                // However the glyph offsets are relative to the prim rect origin
1126                // less the unsnapped reference frame offset. We also want the
1127                // the snapped reference frame offset, because cannot recalculate
1128                // it as it ignores the animated components for the transform. As
1129                // such, we adjust the prim rect origin here, and replace the size
1130                // with the unsnapped and snapped offsets respectively. This has
1131                // the added bonus of avoiding quantization effects when storing
1132                // floats in the extra header integers.
1133                let prim_header = PrimitiveHeader {
1134                    local_rect: LayoutRect {
1135                        min: prim_rect.min - run.reference_frame_relative_offset,
1136                        max: run.snapped_reference_frame_relative_offset.to_point(),
1137                    },
1138                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
1139                    specific_prim_address: prim_cache_address,
1140                    transform_id,
1141                };
1142
1143                let glyph_keys = &ctx.scratch.glyph_keys[run.glyph_keys_range];
1144                let prim_header_index = prim_headers.push(
1145                    &prim_header,
1146                    z_id,
1147                    self.batcher.render_task_address,
1148                    [
1149                        (run.raster_scale * 65535.0).round() as i32,
1150                        0,
1151                        0,
1152                        0,
1153                    ],
1154                );
1155                let base_instance = GlyphInstance::new(
1156                    prim_header_index,
1157                );
1158                let batcher = &mut self.batcher;
1159
1160                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1161                    prim_info.clip_task_index,
1162                    render_tasks,
1163                ).unwrap();
1164
1165                // The run.used_font.clone() is here instead of instead of inline in the `fetch_glyph`
1166                // function call to work around a miscompilation.
1167                // https://github.com/rust-lang/rust/issues/80111
1168                let font = run.used_font.clone();
1169                ctx.resource_cache.fetch_glyphs(
1170                    font,
1171                    &glyph_keys,
1172                    &mut self.glyph_fetch_buffer,
1173                    gpu_cache,
1174                    |texture_id, glyph_format, glyphs| {
1175                        debug_assert_ne!(texture_id, TextureSource::Invalid);
1176
1177                        let subpx_dir = subpx_dir.limit_by(glyph_format);
1178
1179                        let textures = BatchTextures::prim_textured(
1180                            texture_id,
1181                            clip_mask_texture_id,
1182                        );
1183
1184                        let kind = BatchKind::TextRun(glyph_format);
1185
1186                        let (blend_mode, color_mode) = match glyph_format {
1187                            GlyphFormat::Subpixel |
1188                            GlyphFormat::TransformedSubpixel => {
1189                                debug_assert!(ctx.use_dual_source_blending);
1190                                (
1191                                    BlendMode::SubpixelDualSource,
1192                                    ShaderColorMode::SubpixelDualSource,
1193                                )
1194                            }
1195                            GlyphFormat::Alpha |
1196                            GlyphFormat::TransformedAlpha |
1197                            GlyphFormat::Bitmap => {
1198                                (
1199                                    BlendMode::PremultipliedAlpha,
1200                                    ShaderColorMode::Alpha,
1201                                )
1202                            }
1203                            GlyphFormat::ColorBitmap => {
1204                                (
1205                                    BlendMode::PremultipliedAlpha,
1206                                    if run.shadow {
1207                                        // Ignore color and only sample alpha when shadowing.
1208                                        ShaderColorMode::BitmapShadow
1209                                    } else {
1210                                        ShaderColorMode::ColorBitmap
1211                                    },
1212                                )
1213                            }
1214                        };
1215
1216                        // Calculate a tighter bounding rect of just the glyphs passed to this
1217                        // callback from request_glyphs(), rather than using the bounds of the
1218                        // entire text run. This improves batching when glyphs are fragmented
1219                        // over multiple textures in the texture cache.
1220                        // This code is taken from the ps_text_run shader.
1221                        let tight_bounding_rect = {
1222                            let snap_bias = match subpx_dir {
1223                                SubpixelDirection::None => DeviceVector2D::new(0.5, 0.5),
1224                                SubpixelDirection::Horizontal => DeviceVector2D::new(0.125, 0.5),
1225                                SubpixelDirection::Vertical => DeviceVector2D::new(0.5, 0.125),
1226                                SubpixelDirection::Mixed => DeviceVector2D::new(0.125, 0.125),
1227                            };
1228                            let text_offset = prim_header.local_rect.max.to_vector();
1229
1230                            let pic_bounding_rect = if run.used_font.flags.contains(FontInstanceFlags::TRANSFORM_GLYPHS) {
1231                                let mut device_bounding_rect = DeviceRect::default();
1232
1233                                let glyph_transform = ctx.spatial_tree.get_relative_transform(
1234                                    prim_spatial_node_index,
1235                                    root_spatial_node_index,
1236                                ).into_transform()
1237                                    .with_destination::<WorldPixel>()
1238                                    .then(&euclid::Transform3D::from_scale(ctx.global_device_pixel_scale));
1239
1240                                let glyph_translation = DeviceVector2D::new(glyph_transform.m41, glyph_transform.m42);
1241
1242                                let mut use_tight_bounding_rect = true;
1243                                for glyph in glyphs {
1244                                    let glyph_offset = prim_data.glyphs[glyph.index_in_text_run as usize].point + prim_header.local_rect.min.to_vector();
1245
1246                                    let transformed_offset = match glyph_transform.transform_point2d(glyph_offset) {
1247                                        Some(transformed_offset) => transformed_offset,
1248                                        None => {
1249                                            use_tight_bounding_rect = false;
1250                                            break;
1251                                        }
1252                                    };
1253                                    let raster_glyph_offset = (transformed_offset + snap_bias).floor();
1254                                    let raster_text_offset = (
1255                                        glyph_transform.transform_vector2d(text_offset) +
1256                                        glyph_translation +
1257                                        DeviceVector2D::new(0.5, 0.5)
1258                                    ).floor() - glyph_translation;
1259
1260                                    let device_glyph_rect = DeviceRect::from_origin_and_size(
1261                                        glyph.offset + raster_glyph_offset.to_vector() + raster_text_offset,
1262                                        glyph.size.to_f32(),
1263                                    );
1264
1265                                    device_bounding_rect = device_bounding_rect.union(&device_glyph_rect);
1266                                }
1267
1268                                if use_tight_bounding_rect {
1269                                    let map_device_to_surface: SpaceMapper<PicturePixel, DevicePixel> = SpaceMapper::new_with_target(
1270                                        root_spatial_node_index,
1271                                        surface_spatial_node_index,
1272                                        device_bounding_rect,
1273                                        ctx.spatial_tree,
1274                                    );
1275
1276                                    match map_device_to_surface.unmap(&device_bounding_rect) {
1277                                        Some(r) => r.intersection(bounding_rect),
1278                                        None => Some(*bounding_rect),
1279                                    }
1280                                } else {
1281                                    Some(*bounding_rect)
1282                                }
1283                            } else {
1284                                let mut local_bounding_rect = LayoutRect::default();
1285
1286                                let glyph_raster_scale = run.raster_scale * ctx.global_device_pixel_scale.get();
1287
1288                                for glyph in glyphs {
1289                                    let glyph_offset = prim_data.glyphs[glyph.index_in_text_run as usize].point + prim_header.local_rect.min.to_vector();
1290                                    let glyph_scale = LayoutToDeviceScale::new(glyph_raster_scale / glyph.scale);
1291                                    let raster_glyph_offset = (glyph_offset * LayoutToDeviceScale::new(glyph_raster_scale) + snap_bias).floor() / glyph.scale;
1292                                    let local_glyph_rect = LayoutRect::from_origin_and_size(
1293                                        (glyph.offset + raster_glyph_offset.to_vector()) / glyph_scale + text_offset,
1294                                        glyph.size.to_f32() / glyph_scale,
1295                                    );
1296
1297                                    local_bounding_rect = local_bounding_rect.union(&local_glyph_rect);
1298                                }
1299
1300                                let map_prim_to_surface: SpaceMapper<LayoutPixel, PicturePixel> = SpaceMapper::new_with_target(
1301                                    surface_spatial_node_index,
1302                                    prim_spatial_node_index,
1303                                    *bounding_rect,
1304                                    ctx.spatial_tree,
1305                                );
1306                                map_prim_to_surface.map(&local_bounding_rect)
1307                            };
1308
1309                            let intersected = match pic_bounding_rect {
1310                                // The text run may have been clipped, for example if part of it is offscreen.
1311                                // So intersect our result with the original bounding rect.
1312                                Some(rect) => rect.intersection(bounding_rect).unwrap_or_else(PictureRect::zero),
1313                                // If space mapping went off the rails, fall back to the old behavior.
1314                                //TODO: consider skipping the glyph run completely in this case.
1315                                None => *bounding_rect,
1316                            };
1317
1318                            intersected
1319                        };
1320
1321                        let key = BatchKey::new(kind, blend_mode, textures);
1322
1323                        let batch = batcher.alpha_batch_list.set_params_and_get_batch(
1324                            key,
1325                            batch_features,
1326                            &tight_bounding_rect,
1327                            z_id,
1328                        );
1329
1330                        batch.reserve(glyphs.len());
1331                        for glyph in glyphs {
1332                            batch.push(base_instance.build(
1333                                clip_task_address,
1334                                subpx_dir,
1335                                glyph.index_in_text_run,
1336                                glyph.uv_rect_address,
1337                                color_mode,
1338                            ));
1339                        }
1340                    },
1341                );
1342            }
1343            PrimitiveInstanceKind::LineDecoration { data_handle, ref render_task, .. } => {
1344                // The GPU cache data is stored in the template and reused across
1345                // frames and display lists.
1346                let common_data = &ctx.data_stores.line_decoration[data_handle].common;
1347                let prim_cache_address = gpu_cache.get_address(&common_data.gpu_cache_handle);
1348
1349                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1350                    prim_info.clip_task_index,
1351                    render_tasks,
1352                ).unwrap();
1353
1354                let (batch_kind, textures, prim_user_data, specific_resource_address) = match render_task {
1355                    Some(task_id) => {
1356                        let (uv_rect_address, texture) = render_tasks.resolve_location(*task_id, gpu_cache).unwrap();
1357                        let textures = BatchTextures::prim_textured(
1358                            texture,
1359                            clip_mask_texture_id,
1360                        );
1361                        (
1362                            BrushBatchKind::Image(texture.image_buffer_kind()),
1363                            textures,
1364                            ImageBrushData {
1365                                color_mode: ShaderColorMode::Image,
1366                                alpha_type: AlphaType::PremultipliedAlpha,
1367                                raster_space: RasterizationSpace::Local,
1368                                opacity: 1.0,
1369                            }.encode(),
1370                            uv_rect_address.as_int(),
1371                        )
1372                    }
1373                    None => {
1374                        (
1375                            BrushBatchKind::Solid,
1376                            BatchTextures::prim_untextured(clip_mask_texture_id),
1377                            [get_shader_opacity(1.0), 0, 0, 0],
1378                            0,
1379                        )
1380                    }
1381                };
1382
1383                // TODO(gw): We can abstract some of the common code below into
1384                //           helper methods, as we port more primitives to make
1385                //           use of interning.
1386                let blend_mode = if !common_data.opacity.is_opaque ||
1387                    prim_info.clip_task_index != ClipTaskIndex::INVALID ||
1388                    transform_kind == TransformedRectKind::Complex ||
1389                    is_anti_aliased
1390                {
1391                    BlendMode::PremultipliedAlpha
1392                } else {
1393                    BlendMode::None
1394                };
1395
1396                let prim_header = PrimitiveHeader {
1397                    local_rect: prim_rect,
1398                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
1399                    specific_prim_address: prim_cache_address,
1400                    transform_id,
1401                };
1402
1403                let prim_header_index = prim_headers.push(
1404                    &prim_header,
1405                    z_id,
1406                    self.batcher.render_task_address,
1407                    prim_user_data,
1408                );
1409
1410                let batch_key = BatchKey {
1411                    blend_mode,
1412                    kind: BatchKind::Brush(batch_kind),
1413                    textures,
1414                };
1415
1416                self.add_brush_instance_to_batches(
1417                    batch_key,
1418                    batch_features,
1419                    bounding_rect,
1420                    z_id,
1421                    INVALID_SEGMENT_INDEX,
1422                    common_data.edge_aa_mask,
1423                    clip_task_address,
1424                    brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
1425                    prim_header_index,
1426                    specific_resource_address,
1427                );
1428            }
1429            PrimitiveInstanceKind::Picture { pic_index, .. } => {
1430                let picture = &ctx.prim_store.pictures[pic_index.0];
1431                if let Some(snapshot) = picture.snapshot {
1432                    if snapshot.detached {
1433                        return;
1434                    }
1435                }
1436
1437                let blend_mode = BlendMode::PremultipliedAlpha;
1438                let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_image_handle);
1439
1440                match picture.raster_config {
1441                    Some(ref raster_config) => {
1442                        // If the child picture was rendered in local space, we can safely
1443                        // interpolate the UV coordinates with perspective correction.
1444                        let brush_flags = brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION;
1445
1446                        let surface = &ctx.surfaces[raster_config.surface_index.0];
1447                        let mut local_clip_rect = prim_info.clip_chain.local_clip_rect;
1448
1449                        // If we are drawing with snapping enabled, form a simple transform that just applies
1450                        // the scale / translation from the raster transform. Otherwise, in edge cases where the
1451                        // intermediate surface has a non-identity but axis-aligned transform (e.g. a 180 degree
1452                        // rotation) it can be applied twice.
1453                        let transform_id = if surface.surface_spatial_node_index == surface.raster_spatial_node_index {
1454                            transform_id
1455                        } else {
1456                            let map_local_to_raster = SpaceMapper::new_with_target(
1457                                root_spatial_node_index,
1458                                surface.surface_spatial_node_index,
1459                                LayoutRect::max_rect(),
1460                                ctx.spatial_tree,
1461                            );
1462
1463                            let raster_rect = map_local_to_raster
1464                                .map(&prim_rect)
1465                                .unwrap();
1466
1467                            let sx = (raster_rect.max.x - raster_rect.min.x) / (prim_rect.max.x - prim_rect.min.x);
1468                            let sy = (raster_rect.max.y - raster_rect.min.y) / (prim_rect.max.y - prim_rect.min.y);
1469
1470                            let tx = raster_rect.min.x - sx * prim_rect.min.x;
1471                            let ty = raster_rect.min.y - sy * prim_rect.min.y;
1472
1473                            let transform = ScaleOffset::new(sx, sy, tx, ty);
1474
1475                            let raster_clip_rect = map_local_to_raster
1476                                .map(&prim_info.clip_chain.local_clip_rect)
1477                                .unwrap();
1478                            local_clip_rect = transform.unmap_rect(&raster_clip_rect);
1479
1480                            transforms.get_custom(transform.to_transform())
1481                        };
1482
1483                        let prim_header = PrimitiveHeader {
1484                            local_rect: prim_rect,
1485                            local_clip_rect,
1486                            specific_prim_address: prim_cache_address,
1487                            transform_id,
1488                        };
1489
1490                        let mut is_opaque = prim_info.clip_task_index == ClipTaskIndex::INVALID
1491                            && surface.is_opaque
1492                            && transform_kind == TransformedRectKind::AxisAligned
1493                            && !is_anti_aliased;
1494
1495                        let pic_task_id = picture.primary_render_task_id.unwrap();
1496
1497                        let pic_task = &render_tasks[pic_task_id];
1498                        match pic_task.sub_pass {
1499                            Some(SubPass::Masks { .. }) => {
1500                                is_opaque = false;
1501                            }
1502                            None => {}
1503                        }
1504
1505                        match raster_config.composite_mode {
1506                            PictureCompositeMode::TileCache { .. } => {
1507                                // TODO(gw): For now, TileCache is still a composite mode, even though
1508                                //           it will only exist as a top level primitive and never
1509                                //           be encountered during batching. Consider making TileCache
1510                                //           a standalone type, not a picture.
1511                            }
1512                            PictureCompositeMode::IntermediateSurface { .. } => {
1513                                // TODO(gw): As an optimization, support making this a pass-through
1514                                //           and/or drawing directly from here when possible
1515                                //           (e.g. if not wrapped by filters / different spatial node).
1516                            }
1517                            PictureCompositeMode::Filter(ref filter) => {
1518                                assert!(filter.is_visible());
1519                                match filter {
1520                                    Filter::Blur { .. } => {
1521                                        let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1522                                            prim_info.clip_task_index,
1523                                            render_tasks,
1524                                        ).unwrap();
1525
1526                                        let kind = BatchKind::Brush(
1527                                            BrushBatchKind::Image(ImageBufferKind::Texture2D)
1528                                        );
1529
1530                                        let (uv_rect_address, texture) = render_tasks.resolve_location(
1531                                            pic_task_id,
1532                                            gpu_cache,
1533                                        ).unwrap();
1534                                        let textures = BatchTextures::prim_textured(
1535                                            texture,
1536                                            clip_mask_texture_id,
1537                                        );
1538
1539                                        let key = BatchKey::new(
1540                                            kind,
1541                                            blend_mode,
1542                                            textures,
1543                                        );
1544                                        let prim_header_index = prim_headers.push(
1545                                            &prim_header,
1546                                            z_id,
1547                                            self.batcher.render_task_address,
1548                                            ImageBrushData {
1549                                                color_mode: ShaderColorMode::Image,
1550                                                alpha_type: AlphaType::PremultipliedAlpha,
1551                                                raster_space: RasterizationSpace::Screen,
1552                                                opacity: 1.0,
1553                                            }.encode(),
1554                                        );
1555
1556                                        self.add_brush_instance_to_batches(
1557                                            key,
1558                                            batch_features,
1559                                            bounding_rect,
1560                                            z_id,
1561                                            INVALID_SEGMENT_INDEX,
1562                                            EdgeAaSegmentMask::all(),
1563                                            clip_task_address,
1564                                            brush_flags,
1565                                            prim_header_index,
1566                                            uv_rect_address.as_int(),
1567                                        );
1568                                    }
1569                                    Filter::DropShadows(shadows) => {
1570                                        let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1571                                            prim_info.clip_task_index,
1572                                            render_tasks,
1573                                        ).unwrap();
1574
1575                                        // Draw an instance per shadow first, following by the content.
1576
1577                                        // The shadows and the content get drawn as a brush image.
1578                                        let kind = BatchKind::Brush(
1579                                            BrushBatchKind::Image(ImageBufferKind::Texture2D),
1580                                        );
1581
1582                                        // Gets the saved render task ID of the content, which is
1583                                        // deeper in the render task graph than the direct child.
1584                                        let secondary_id = picture.secondary_render_task_id.expect("no secondary!?");
1585                                        let content_source = {
1586                                            let secondary_task = &render_tasks[secondary_id];
1587                                            let texture_id = secondary_task.get_target_texture();
1588                                            TextureSource::TextureCache(
1589                                                texture_id,
1590                                                Swizzle::default(),
1591                                            )
1592                                        };
1593
1594                                        // Retrieve the UV rect addresses for shadow/content.
1595                                        let (shadow_uv_rect_address, shadow_texture) = render_tasks.resolve_location(
1596                                            pic_task_id,
1597                                            gpu_cache,
1598                                        ).unwrap();
1599                                        let shadow_textures = BatchTextures::prim_textured(
1600                                            shadow_texture,
1601                                            clip_mask_texture_id,
1602                                        );
1603
1604                                        let content_uv_rect_address = render_tasks[secondary_id]
1605                                            .get_texture_address(gpu_cache)
1606                                            .as_int();
1607
1608                                        // Build BatchTextures for shadow/content
1609                                        let content_textures = BatchTextures::prim_textured(
1610                                            content_source,
1611                                            clip_mask_texture_id,
1612                                        );
1613
1614                                        // Build batch keys for shadow/content
1615                                        let shadow_key = BatchKey::new(kind, blend_mode, shadow_textures);
1616                                        let content_key = BatchKey::new(kind, blend_mode, content_textures);
1617
1618                                        for (shadow, shadow_gpu_data) in shadows.iter().zip(picture.extra_gpu_data_handles.iter()) {
1619                                            // Get the GPU cache address of the extra data handle.
1620                                            let shadow_prim_address = gpu_cache.get_address(shadow_gpu_data);
1621
1622                                            let shadow_rect = prim_header.local_rect.translate(shadow.offset);
1623
1624                                            let shadow_prim_header = PrimitiveHeader {
1625                                                local_rect: shadow_rect,
1626                                                specific_prim_address: shadow_prim_address,
1627                                                ..prim_header
1628                                            };
1629
1630                                            let shadow_prim_header_index = prim_headers.push(
1631                                                &shadow_prim_header,
1632                                                z_id,
1633                                                self.batcher.render_task_address,
1634                                                ImageBrushData {
1635                                                    color_mode: ShaderColorMode::Alpha,
1636                                                    alpha_type: AlphaType::PremultipliedAlpha,
1637                                                    raster_space: RasterizationSpace::Screen,
1638                                                    opacity: 1.0,
1639                                                }.encode(),
1640                                            );
1641
1642                                            self.add_brush_instance_to_batches(
1643                                                shadow_key,
1644                                                batch_features,
1645                                                bounding_rect,
1646                                                z_id,
1647                                                INVALID_SEGMENT_INDEX,
1648                                                EdgeAaSegmentMask::all(),
1649                                                clip_task_address,
1650                                                brush_flags,
1651                                                shadow_prim_header_index,
1652                                                shadow_uv_rect_address.as_int(),
1653                                            );
1654                                        }
1655                                        let z_id_content = z_generator.next();
1656
1657                                        let content_prim_header_index = prim_headers.push(
1658                                            &prim_header,
1659                                            z_id_content,
1660                                            self.batcher.render_task_address,
1661                                            ImageBrushData {
1662                                                color_mode: ShaderColorMode::Image,
1663                                                alpha_type: AlphaType::PremultipliedAlpha,
1664                                                raster_space: RasterizationSpace::Screen,
1665                                                opacity: 1.0,
1666                                            }.encode(),
1667                                        );
1668
1669                                        self.add_brush_instance_to_batches(
1670                                            content_key,
1671                                            batch_features,
1672                                            bounding_rect,
1673                                            z_id_content,
1674                                            INVALID_SEGMENT_INDEX,
1675                                            EdgeAaSegmentMask::all(),
1676                                            clip_task_address,
1677                                            brush_flags,
1678                                            content_prim_header_index,
1679                                            content_uv_rect_address,
1680                                        );
1681                                    }
1682                                    Filter::Opacity(_, amount) => {
1683                                        let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1684                                            prim_info.clip_task_index,
1685                                            render_tasks,
1686                                        ).unwrap();
1687
1688                                        let amount = (amount * 65536.0) as i32;
1689
1690                                        let (uv_rect_address, texture) = render_tasks.resolve_location(
1691                                            pic_task_id,
1692                                            gpu_cache,
1693                                        ).unwrap();
1694                                        let textures = BatchTextures::prim_textured(
1695                                            texture,
1696                                            clip_mask_texture_id,
1697                                        );
1698
1699
1700                                        let key = BatchKey::new(
1701                                            BatchKind::Brush(BrushBatchKind::Opacity),
1702                                            BlendMode::PremultipliedAlpha,
1703                                            textures,
1704                                        );
1705
1706                                        let prim_header_index = prim_headers.push(
1707                                            &prim_header,
1708                                            z_id,
1709                                            self.batcher.render_task_address,
1710                                            [
1711                                                uv_rect_address.as_int(),
1712                                                amount,
1713                                                0,
1714                                                0,
1715                                            ]
1716                                        );
1717
1718                                        self.add_brush_instance_to_batches(
1719                                            key,
1720                                            batch_features,
1721                                            bounding_rect,
1722                                            z_id,
1723                                            INVALID_SEGMENT_INDEX,
1724                                            EdgeAaSegmentMask::all(),
1725                                            clip_task_address,
1726                                            brush_flags,
1727                                            prim_header_index,
1728                                            0,
1729                                        );
1730                                    }
1731                                    _ => {
1732                                        let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1733                                            prim_info.clip_task_index,
1734                                            render_tasks,
1735                                        ).unwrap();
1736
1737                                        // Must be kept in sync with brush_blend.glsl
1738                                        let filter_mode = filter.as_int();
1739
1740                                        let user_data = match filter {
1741                                            Filter::Identity => 0x10000i32, // matches `Contrast(1)`
1742                                            Filter::Contrast(amount) |
1743                                            Filter::Grayscale(amount) |
1744                                            Filter::Invert(amount) |
1745                                            Filter::Saturate(amount) |
1746                                            Filter::Sepia(amount) |
1747                                            Filter::Brightness(amount) => {
1748                                                (amount * 65536.0) as i32
1749                                            }
1750                                            Filter::SrgbToLinear | Filter::LinearToSrgb => 0,
1751                                            Filter::HueRotate(angle) => {
1752                                                (0.01745329251 * angle * 65536.0) as i32
1753                                            }
1754                                            Filter::ColorMatrix(_) => {
1755                                                picture.extra_gpu_data_handles[0].as_int(gpu_cache)
1756                                            }
1757                                            Filter::Flood(_) => {
1758                                                picture.extra_gpu_data_handles[0].as_int(gpu_cache)
1759                                            }
1760
1761                                            // These filters are handled via different paths.
1762                                            Filter::ComponentTransfer |
1763                                            Filter::Blur { .. } |
1764                                            Filter::DropShadows(..) |
1765                                            Filter::Opacity(..) |
1766                                            Filter::SVGGraphNode(..) => unreachable!(),
1767                                        };
1768
1769                                        // Other filters that may introduce opacity are handled via different
1770                                        // paths.
1771                                        if let Filter::ColorMatrix(..) = filter {
1772                                            is_opaque = false;
1773                                        }
1774
1775                                        let (uv_rect_address, texture) = render_tasks.resolve_location(
1776                                            pic_task_id,
1777                                            gpu_cache,
1778                                        ).unwrap();
1779                                        let textures = BatchTextures::prim_textured(
1780                                            texture,
1781                                            clip_mask_texture_id,
1782                                        );
1783
1784                                        let blend_mode = if is_opaque {
1785                                            BlendMode::None
1786                                        } else {
1787                                            BlendMode::PremultipliedAlpha
1788                                        };
1789
1790                                        let key = BatchKey::new(
1791                                            BatchKind::Brush(BrushBatchKind::Blend),
1792                                            blend_mode,
1793                                            textures,
1794                                        );
1795
1796                                        let prim_header_index = prim_headers.push(
1797                                            &prim_header,
1798                                            z_id,
1799                                            self.batcher.render_task_address,
1800                                            [
1801                                                uv_rect_address.as_int(),
1802                                                filter_mode,
1803                                                user_data,
1804                                                0,
1805                                            ]
1806                                        );
1807
1808                                        self.add_brush_instance_to_batches(
1809                                            key,
1810                                            batch_features,
1811                                            bounding_rect,
1812                                            z_id,
1813                                            INVALID_SEGMENT_INDEX,
1814                                            EdgeAaSegmentMask::all(),
1815                                            clip_task_address,
1816                                            brush_flags,
1817                                            prim_header_index,
1818                                            0,
1819                                        );
1820                                    }
1821                                }
1822                            }
1823                            PictureCompositeMode::ComponentTransferFilter(handle) => {
1824                                // This is basically the same as the general filter case above
1825                                // except we store a little more data in the filter mode and
1826                                // a gpu cache handle in the user data.
1827                                let filter_data = &ctx.data_stores.filter_data[handle];
1828                                let filter_mode : i32 = Filter::ComponentTransfer.as_int() |
1829                                    ((filter_data.data.r_func.to_int() << 28 |
1830                                      filter_data.data.g_func.to_int() << 24 |
1831                                      filter_data.data.b_func.to_int() << 20 |
1832                                      filter_data.data.a_func.to_int() << 16) as i32);
1833
1834                                let user_data = filter_data.gpu_cache_handle.as_int(gpu_cache);
1835
1836                                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1837                                    prim_info.clip_task_index,
1838                                    render_tasks,
1839                                ).unwrap();
1840
1841                                let (uv_rect_address, texture) = render_tasks.resolve_location(
1842                                    pic_task_id,
1843                                    gpu_cache,
1844                                ).unwrap();
1845                                let textures = BatchTextures::prim_textured(
1846                                    texture,
1847                                    clip_mask_texture_id,
1848                                );
1849
1850                                let key = BatchKey::new(
1851                                    BatchKind::Brush(BrushBatchKind::Blend),
1852                                    BlendMode::PremultipliedAlpha,
1853                                    textures,
1854                                );
1855
1856                                let prim_header_index = prim_headers.push(
1857                                    &prim_header,
1858                                    z_id,
1859                                    self.batcher.render_task_address,
1860                                    [
1861                                        uv_rect_address.as_int(),
1862                                        filter_mode,
1863                                        user_data,
1864                                        0,
1865                                    ]
1866                                );
1867
1868                                self.add_brush_instance_to_batches(
1869                                    key,
1870                                    batch_features,
1871                                    bounding_rect,
1872                                    z_id,
1873                                    INVALID_SEGMENT_INDEX,
1874                                    EdgeAaSegmentMask::all(),
1875                                    clip_task_address,
1876                                    brush_flags,
1877                                    prim_header_index,
1878                                    0,
1879                                );
1880                            }
1881                            PictureCompositeMode::MixBlend(mode) if BlendMode::from_mix_blend_mode(
1882                                mode,
1883                                ctx.use_advanced_blending,
1884                                !ctx.break_advanced_blend_batches,
1885                                ctx.use_dual_source_blending,
1886                            ).is_some() => {
1887                                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1888                                    prim_info.clip_task_index,
1889                                    render_tasks,
1890                                ).unwrap();
1891
1892                                let (uv_rect_address, texture) = render_tasks.resolve_location(
1893                                    pic_task_id,
1894                                    gpu_cache,
1895                                ).unwrap();
1896                                let textures = BatchTextures::prim_textured(
1897                                    texture,
1898                                    clip_mask_texture_id,
1899                                );
1900
1901
1902                                let key = BatchKey::new(
1903                                    BatchKind::Brush(
1904                                        BrushBatchKind::Image(ImageBufferKind::Texture2D),
1905                                    ),
1906                                    BlendMode::from_mix_blend_mode(
1907                                        mode,
1908                                        ctx.use_advanced_blending,
1909                                        !ctx.break_advanced_blend_batches,
1910                                        ctx.use_dual_source_blending,
1911                                    ).unwrap(),
1912                                    textures,
1913                                );
1914                                let prim_header_index = prim_headers.push(
1915                                    &prim_header,
1916                                    z_id,
1917                                    self.batcher.render_task_address,
1918                                    ImageBrushData {
1919                                        color_mode: match key.blend_mode {
1920                                            BlendMode::MultiplyDualSource => ShaderColorMode::MultiplyDualSource,
1921                                            _ => ShaderColorMode::Image,
1922                                        },
1923                                        alpha_type: AlphaType::PremultipliedAlpha,
1924                                        raster_space: RasterizationSpace::Screen,
1925                                        opacity: 1.0,
1926                                    }.encode(),
1927                                );
1928
1929                                self.add_brush_instance_to_batches(
1930                                    key,
1931                                    batch_features,
1932                                    bounding_rect,
1933                                    z_id,
1934                                    INVALID_SEGMENT_INDEX,
1935                                    EdgeAaSegmentMask::all(),
1936                                    clip_task_address,
1937                                    brush_flags,
1938                                    prim_header_index,
1939                                    uv_rect_address.as_int(),
1940                                );
1941                            }
1942                            PictureCompositeMode::MixBlend(mode) => {
1943                                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1944                                    prim_info.clip_task_index,
1945                                    render_tasks,
1946                                ).unwrap();
1947                                let backdrop_id = picture.secondary_render_task_id.expect("no backdrop!?");
1948
1949                                let color0 = render_tasks[backdrop_id].get_target_texture();
1950                                let color1 = render_tasks[pic_task_id].get_target_texture();
1951
1952                                // Create a separate brush instance for each batcher. For most cases,
1953                                // there is only one batcher. However, in the case of drawing onto
1954                                // a picture cache, there is one batcher per tile. Although not
1955                                // currently used, the implementation of mix-blend-mode now supports
1956                                // doing partial readbacks per-tile. In future, this will be enabled
1957                                // and allow mix-blends to operate on picture cache surfaces without
1958                                // a separate isolated intermediate surface.
1959
1960                                let batch_key = BatchKey::new(
1961                                    BatchKind::Brush(
1962                                        BrushBatchKind::MixBlend {
1963                                            task_id: self.batcher.render_task_id,
1964                                            backdrop_id,
1965                                        },
1966                                    ),
1967                                    BlendMode::PremultipliedAlpha,
1968                                    BatchTextures {
1969                                        input: TextureSet {
1970                                            colors: [
1971                                                TextureSource::TextureCache(
1972                                                    color0,
1973                                                    Swizzle::default(),
1974                                                ),
1975                                                TextureSource::TextureCache(
1976                                                    color1,
1977                                                    Swizzle::default(),
1978                                                ),
1979                                                TextureSource::Invalid,
1980                                            ],
1981                                        },
1982                                        clip_mask: clip_mask_texture_id,
1983                                    },
1984                                );
1985                                let src_uv_address = render_tasks[pic_task_id].get_texture_address(gpu_cache);
1986                                let readback_uv_address = render_tasks[backdrop_id].get_texture_address(gpu_cache);
1987                                let prim_header_index = prim_headers.push(
1988                                    &prim_header,
1989                                    z_id,
1990                                    self.batcher.render_task_address,
1991                                    [
1992                                        mode as u32 as i32,
1993                                        readback_uv_address.as_int(),
1994                                        src_uv_address.as_int(),
1995                                        0,
1996                                    ]
1997                                );
1998
1999                                let instance = BrushInstance {
2000                                    segment_index: INVALID_SEGMENT_INDEX,
2001                                    edge_flags: EdgeAaSegmentMask::all(),
2002                                    clip_task_address,
2003                                    brush_flags,
2004                                    prim_header_index,
2005                                    resource_address: 0,
2006                                };
2007
2008                                self.batcher.push_single_instance(
2009                                    batch_key,
2010                                    batch_features,
2011                                    bounding_rect,
2012                                    z_id,
2013                                    PrimitiveInstanceData::from(instance),
2014                                );
2015                            }
2016                            PictureCompositeMode::Blit(_) => {
2017                                match picture.context_3d {
2018                                    Picture3DContext::In { root_data: Some(_), .. } => {
2019                                        unreachable!("bug: should not have a raster_config");
2020                                    }
2021                                    Picture3DContext::In { root_data: None, .. } => {
2022                                        // TODO(gw): Store this inside the split picture so that we
2023                                        //           don't need to pass in extra_prim_gpu_address for
2024                                        //           every prim instance.
2025                                        // TODO(gw): Ideally we'd skip adding 3d child prims to batches
2026                                        //           without gpu cache address but it's currently
2027                                        //           used by the prepare pass. Refactor this!
2028                                        let extra_prim_gpu_address = match extra_prim_gpu_address {
2029                                            Some(prim_address) => prim_address,
2030                                            None => return,
2031                                        };
2032
2033                                        // Get clip task, if set, for the picture primitive.
2034                                        let (child_clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2035                                            prim_info.clip_task_index,
2036                                            render_tasks,
2037                                        ).unwrap();
2038
2039                                        let prim_header = PrimitiveHeader {
2040                                            local_rect: prim_rect,
2041                                            local_clip_rect: prim_info.clip_chain.local_clip_rect,
2042                                            specific_prim_address: GpuCacheAddress::INVALID,
2043                                            transform_id: transforms
2044                                                .get_id(
2045                                                    prim_spatial_node_index,
2046                                                    root_spatial_node_index,
2047                                                    ctx.spatial_tree,
2048                                                ),
2049                                        };
2050
2051                                        let child_pic_task_id = picture
2052                                            .primary_render_task_id
2053                                            .unwrap();
2054
2055                                        let (uv_rect_address, texture) = render_tasks.resolve_location(
2056                                            child_pic_task_id,
2057                                            gpu_cache,
2058                                        ).unwrap();
2059                                        let textures = BatchTextures::prim_textured(
2060                                            texture,
2061                                            clip_mask_texture_id,
2062                                        );
2063
2064                                        // Need a new z-id for each child preserve-3d context added
2065                                        // by this inner loop.
2066                                        let z_id = z_generator.next();
2067
2068                                        let prim_header_index = prim_headers.push(
2069                                            &prim_header,
2070                                            z_id,
2071                                            self.batcher.render_task_address,
2072                                            [
2073                                                uv_rect_address.as_int(),
2074                                                BrushFlags::PERSPECTIVE_INTERPOLATION.bits() as i32,
2075                                                0,
2076                                                child_clip_task_address.0 as i32,
2077                                            ]
2078                                        );
2079
2080                                        let key = BatchKey::new(
2081                                            BatchKind::SplitComposite,
2082                                            BlendMode::PremultipliedAlpha,
2083                                            textures,
2084                                        );
2085
2086                                        self.add_split_composite_instance_to_batches(
2087                                            key,
2088                                            BatchFeatures::CLIP_MASK,
2089                                            &prim_info.clip_chain.pic_coverage_rect,
2090                                            z_id,
2091                                            prim_header_index,
2092                                            extra_prim_gpu_address,
2093                                        );
2094                                    }
2095                                    Picture3DContext::Out { .. } => {
2096                                        let uv_rect_address = render_tasks[pic_task_id]
2097                                            .get_texture_address(gpu_cache)
2098                                            .as_int();
2099                                        let cache_render_task = &render_tasks[pic_task_id];
2100                                        let texture_id = cache_render_task.get_target_texture();
2101                                        let textures = TextureSet {
2102                                            colors: [
2103                                                TextureSource::TextureCache(
2104                                                    texture_id,
2105                                                    Swizzle::default(),
2106                                                ),
2107                                                TextureSource::Invalid,
2108                                                TextureSource::Invalid,
2109                                            ],
2110                                        };
2111                                        let batch_params = BrushBatchParameters::shared(
2112                                            BrushBatchKind::Image(ImageBufferKind::Texture2D),
2113                                            textures,
2114                                            ImageBrushData {
2115                                                color_mode: ShaderColorMode::Image,
2116                                                alpha_type: AlphaType::PremultipliedAlpha,
2117                                                raster_space: RasterizationSpace::Screen,
2118                                                opacity: 1.0,
2119                                            }.encode(),
2120                                            uv_rect_address,
2121                                        );
2122
2123                                        let prim_header = PrimitiveHeader {
2124                                            specific_prim_address: prim_cache_address,
2125                                            ..prim_header
2126                                        };
2127
2128                                        let prim_header_index = prim_headers.push(
2129                                            &prim_header,
2130                                            z_id,
2131                                            self.batcher.render_task_address,
2132                                            batch_params.prim_user_data,
2133                                        );
2134
2135                                        let (opacity, blend_mode) = if is_opaque {
2136                                            (PrimitiveOpacity::opaque(), BlendMode::None)
2137                                        } else {
2138                                            (PrimitiveOpacity::translucent(), BlendMode::PremultipliedAlpha)
2139                                        };
2140
2141                                        self.add_segmented_prim_to_batch(
2142                                            None,
2143                                            opacity,
2144                                            &batch_params,
2145                                            blend_mode,
2146                                            batch_features,
2147                                            brush_flags,
2148                                            EdgeAaSegmentMask::all(),
2149                                            prim_header_index,
2150                                            bounding_rect,
2151                                            transform_kind,
2152                                            z_id,
2153                                            prim_info.clip_task_index,
2154                                            ctx,
2155                                            render_tasks,
2156                                        );
2157                                    }
2158                                }
2159                            }
2160                            PictureCompositeMode::SvgFilter(..) => {
2161                                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2162                                    prim_info.clip_task_index,
2163                                    render_tasks,
2164                                ).unwrap();
2165
2166                                let kind = BatchKind::Brush(
2167                                    BrushBatchKind::Image(ImageBufferKind::Texture2D)
2168                                );
2169                                let (uv_rect_address, texture) = render_tasks.resolve_location(
2170                                    pic_task_id,
2171                                    gpu_cache,
2172                                ).unwrap();
2173                                let textures = BatchTextures::prim_textured(
2174                                    texture,
2175                                    clip_mask_texture_id,
2176                                );
2177                                let key = BatchKey::new(
2178                                    kind,
2179                                    blend_mode,
2180                                    textures,
2181                                );
2182                                let prim_header_index = prim_headers.push(
2183                                    &prim_header,
2184                                    z_id,
2185                                    self.batcher.render_task_address,
2186                                    ImageBrushData {
2187                                        color_mode: ShaderColorMode::Image,
2188                                        alpha_type: AlphaType::PremultipliedAlpha,
2189                                        raster_space: RasterizationSpace::Screen,
2190                                        opacity: 1.0,
2191                                    }.encode(),
2192                                );
2193
2194                                self.add_brush_instance_to_batches(
2195                                    key,
2196                                    batch_features,
2197                                    bounding_rect,
2198                                    z_id,
2199                                    INVALID_SEGMENT_INDEX,
2200                                    EdgeAaSegmentMask::all(),
2201                                    clip_task_address,
2202                                    brush_flags,
2203                                    prim_header_index,
2204                                    uv_rect_address.as_int(),
2205                                );
2206                            }
2207                            PictureCompositeMode::SVGFEGraph(..) => {
2208                                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2209                                    prim_info.clip_task_index,
2210                                    render_tasks,
2211                                ).unwrap();
2212
2213                                let kind = BatchKind::Brush(
2214                                    BrushBatchKind::Image(ImageBufferKind::Texture2D)
2215                                );
2216                                let (uv_rect_address, texture) = render_tasks.resolve_location(
2217                                    pic_task_id,
2218                                    gpu_cache,
2219                                ).unwrap();
2220                                let textures = BatchTextures::prim_textured(
2221                                    texture,
2222                                    clip_mask_texture_id,
2223                                );
2224                                let key = BatchKey::new(
2225                                    kind,
2226                                    blend_mode,
2227                                    textures,
2228                                );
2229                                let prim_header_index = prim_headers.push(
2230                                    &prim_header,
2231                                    z_id,
2232                                    self.batcher.render_task_address,
2233                                    ImageBrushData {
2234                                        color_mode: ShaderColorMode::Image,
2235                                        alpha_type: AlphaType::PremultipliedAlpha,
2236                                        raster_space: RasterizationSpace::Screen,
2237                                        opacity: 1.0,
2238                                    }.encode(),
2239                                );
2240
2241                                self.add_brush_instance_to_batches(
2242                                    key,
2243                                    batch_features,
2244                                    bounding_rect,
2245                                    z_id,
2246                                    INVALID_SEGMENT_INDEX,
2247                                    EdgeAaSegmentMask::all(),
2248                                    clip_task_address,
2249                                    brush_flags,
2250                                    prim_header_index,
2251                                    uv_rect_address.as_int(),
2252                                );
2253                            }
2254                        }
2255                    }
2256                    None => {
2257                        unreachable!();
2258                    }
2259                }
2260            }
2261            PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
2262                let prim_data = &ctx.data_stores.image_border[data_handle];
2263                let common_data = &prim_data.common;
2264                let border_data = &prim_data.kind;
2265
2266                let (uv_rect_address, texture) = match render_tasks.resolve_location(border_data.src_color, gpu_cache) {
2267                    Some(src) => src,
2268                    None => {
2269                        return;
2270                    }
2271                };
2272
2273                let textures = TextureSet::prim_textured(texture);
2274                let prim_cache_address = gpu_cache.get_address(&common_data.gpu_cache_handle);
2275                let blend_mode = if !common_data.opacity.is_opaque ||
2276                    prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2277                    transform_kind == TransformedRectKind::Complex ||
2278                    is_anti_aliased
2279                {
2280                    BlendMode::PremultipliedAlpha
2281                } else {
2282                    BlendMode::None
2283                };
2284
2285                let prim_header = PrimitiveHeader {
2286                    local_rect: prim_rect,
2287                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
2288                    specific_prim_address: prim_cache_address,
2289                    transform_id,
2290                };
2291
2292                let batch_params = BrushBatchParameters::shared(
2293                    BrushBatchKind::Image(texture.image_buffer_kind()),
2294                    textures,
2295                    ImageBrushData {
2296                        color_mode: ShaderColorMode::Image,
2297                        alpha_type: AlphaType::PremultipliedAlpha,
2298                        raster_space: RasterizationSpace::Local,
2299                        opacity: 1.0,
2300                    }.encode(),
2301                    uv_rect_address.as_int(),
2302                );
2303
2304                let prim_header_index = prim_headers.push(
2305                    &prim_header,
2306                    z_id,
2307                    self.batcher.render_task_address,
2308                    batch_params.prim_user_data,
2309                );
2310
2311                self.add_segmented_prim_to_batch(
2312                    Some(border_data.brush_segments.as_slice()),
2313                    common_data.opacity,
2314                    &batch_params,
2315                    blend_mode,
2316                    batch_features,
2317                    brush_flags,
2318                    common_data.edge_aa_mask,
2319                    prim_header_index,
2320                    bounding_rect,
2321                    transform_kind,
2322                    z_id,
2323                    prim_info.clip_task_index,
2324                    ctx,
2325                    render_tasks,
2326                );
2327            }
2328            PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, use_legacy_path, .. } => {
2329                debug_assert!(use_legacy_path);
2330                let prim_data = &ctx.data_stores.prim[data_handle];
2331
2332                let blend_mode = if !prim_data.opacity.is_opaque ||
2333                    prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2334                    transform_kind == TransformedRectKind::Complex ||
2335                    is_anti_aliased
2336                {
2337                    BlendMode::PremultipliedAlpha
2338                } else {
2339                    BlendMode::None
2340                };
2341
2342                let batch_params = BrushBatchParameters::shared(
2343                    BrushBatchKind::Solid,
2344                    TextureSet::UNTEXTURED,
2345                    [get_shader_opacity(1.0), 0, 0, 0],
2346                    0,
2347                );
2348
2349                let (prim_cache_address, segments) = if segment_instance_index == SegmentInstanceIndex::UNUSED {
2350                    (gpu_cache.get_address(&prim_data.gpu_cache_handle), None)
2351                } else {
2352                    let segment_instance = &ctx.scratch.segment_instances[segment_instance_index];
2353                    let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
2354                    (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
2355                };
2356
2357                let prim_header = PrimitiveHeader {
2358                    local_rect: prim_rect,
2359                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
2360                    specific_prim_address: prim_cache_address,
2361                    transform_id,
2362                };
2363
2364                let prim_header_index = prim_headers.push(
2365                    &prim_header,
2366                    z_id,
2367                    self.batcher.render_task_address,
2368                    batch_params.prim_user_data,
2369                );
2370
2371                self.add_segmented_prim_to_batch(
2372                    segments,
2373                    prim_data.opacity,
2374                    &batch_params,
2375                    blend_mode,
2376                    batch_features,
2377                    brush_flags,
2378                    prim_data.edge_aa_mask,
2379                    prim_header_index,
2380                    bounding_rect,
2381                    transform_kind,
2382                    z_id,
2383                    prim_info.clip_task_index,
2384                    ctx,
2385                    render_tasks,
2386                );
2387            }
2388            PrimitiveInstanceKind::YuvImage { data_handle, segment_instance_index, compositor_surface_kind, .. } => {
2389                if compositor_surface_kind.needs_cutout() {
2390                    self.add_compositor_surface_cutout(
2391                        prim_rect,
2392                        prim_info.clip_chain.local_clip_rect,
2393                        prim_info.clip_task_index,
2394                        transform_id,
2395                        z_id,
2396                        bounding_rect,
2397                        ctx,
2398                        gpu_cache,
2399                        render_tasks,
2400                        prim_headers,
2401                    );
2402
2403                    return;
2404                }
2405
2406                let yuv_image_data = &ctx.data_stores.yuv_image[data_handle].kind;
2407                let mut textures = TextureSet::UNTEXTURED;
2408                let mut uv_rect_addresses = [0; 3];
2409
2410                //yuv channel
2411                let channel_count = yuv_image_data.format.get_plane_num();
2412                debug_assert!(channel_count <= 3);
2413                for channel in 0 .. channel_count {
2414
2415                    let src_channel = render_tasks.resolve_location(yuv_image_data.src_yuv[channel], gpu_cache);
2416
2417                    let (uv_rect_address, texture_source) = match src_channel {
2418                        Some(src) => src,
2419                        None => {
2420                            warn!("Warnings: skip a PrimitiveKind::YuvImage");
2421                            return;
2422                        }
2423                    };
2424
2425                    textures.colors[channel] = texture_source;
2426                    uv_rect_addresses[channel] = uv_rect_address.as_int();
2427                }
2428
2429                // All yuv textures should be the same type.
2430                let buffer_kind = textures.colors[0].image_buffer_kind();
2431                assert!(
2432                    textures.colors[1 .. yuv_image_data.format.get_plane_num()]
2433                        .iter()
2434                        .all(|&tid| buffer_kind == tid.image_buffer_kind())
2435                );
2436
2437                let kind = BrushBatchKind::YuvImage(
2438                    buffer_kind,
2439                    yuv_image_data.format,
2440                    yuv_image_data.color_depth,
2441                    yuv_image_data.color_space,
2442                    yuv_image_data.color_range,
2443                );
2444
2445                let batch_params = BrushBatchParameters::shared(
2446                    kind,
2447                    textures,
2448                    [
2449                        uv_rect_addresses[0],
2450                        uv_rect_addresses[1],
2451                        uv_rect_addresses[2],
2452                        0,
2453                    ],
2454                    0,
2455                );
2456
2457                let prim_common_data = ctx.data_stores.as_common_data(&prim_instance);
2458
2459                let blend_mode = if !prim_common_data.opacity.is_opaque ||
2460                    prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2461                    transform_kind == TransformedRectKind::Complex ||
2462                    is_anti_aliased
2463                {
2464                    BlendMode::PremultipliedAlpha
2465                } else {
2466                    BlendMode::None
2467                };
2468
2469                debug_assert_ne!(segment_instance_index, SegmentInstanceIndex::INVALID);
2470                let (prim_cache_address, segments) = if segment_instance_index == SegmentInstanceIndex::UNUSED {
2471                    (gpu_cache.get_address(&prim_common_data.gpu_cache_handle), None)
2472                } else {
2473                    let segment_instance = &ctx.scratch.segment_instances[segment_instance_index];
2474                    let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
2475                    (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
2476                };
2477
2478                let prim_header = PrimitiveHeader {
2479                    local_rect: prim_rect,
2480                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
2481                    specific_prim_address: prim_cache_address,
2482                    transform_id,
2483                };
2484
2485                let prim_header_index = prim_headers.push(
2486                    &prim_header,
2487                    z_id,
2488                    self.batcher.render_task_address,
2489                    batch_params.prim_user_data,
2490                );
2491
2492                self.add_segmented_prim_to_batch(
2493                    segments,
2494                    prim_common_data.opacity,
2495                    &batch_params,
2496                    blend_mode,
2497                    batch_features,
2498                    brush_flags,
2499                    prim_common_data.edge_aa_mask,
2500                    prim_header_index,
2501                    bounding_rect,
2502                    transform_kind,
2503                    z_id,
2504                    prim_info.clip_task_index,
2505                    ctx,
2506                    render_tasks,
2507                );
2508            }
2509            PrimitiveInstanceKind::Image { data_handle, image_instance_index, compositor_surface_kind, .. } => {
2510                if compositor_surface_kind.needs_cutout() {
2511                    self.add_compositor_surface_cutout(
2512                        prim_rect,
2513                        prim_info.clip_chain.local_clip_rect,
2514                        prim_info.clip_task_index,
2515                        transform_id,
2516                        z_id,
2517                        bounding_rect,
2518                        ctx,
2519                        gpu_cache,
2520                        render_tasks,
2521                        prim_headers,
2522                    );
2523
2524                    return;
2525                }
2526
2527                let image_data = &ctx.data_stores.image[data_handle].kind;
2528                let common_data = &ctx.data_stores.image[data_handle].common;
2529                let image_instance = &ctx.prim_store.images[image_instance_index];
2530                let prim_user_data = ImageBrushData {
2531                    color_mode: ShaderColorMode::Image,
2532                    alpha_type: image_data.alpha_type,
2533                    raster_space: RasterizationSpace::Local,
2534                    opacity: 1.0,
2535                }.encode();
2536
2537                let blend_mode = if !common_data.opacity.is_opaque ||
2538                    prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2539                    transform_kind == TransformedRectKind::Complex ||
2540                    is_anti_aliased
2541                {
2542                    match image_data.alpha_type {
2543                        AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
2544                        AlphaType::Alpha => BlendMode::Alpha,
2545                    }
2546                } else {
2547                    BlendMode::None
2548                };
2549
2550                if image_instance.visible_tiles.is_empty() {
2551                    if cfg!(debug_assertions) {
2552                        match ctx.resource_cache.get_image_properties(image_data.key) {
2553                            Some(ImageProperties { tiling: None, .. }) | None => (),
2554                            other => panic!("Non-tiled image with no visible images detected! Properties {:?}", other),
2555                        }
2556                    }
2557
2558                    let src_color = render_tasks.resolve_location(image_instance.src_color, gpu_cache);
2559
2560                    let (uv_rect_address, texture_source) = match src_color {
2561                        Some(src) => src,
2562                        None => {
2563                            return;
2564                        }
2565                    };
2566
2567                    let batch_params = BrushBatchParameters::shared(
2568                        BrushBatchKind::Image(texture_source.image_buffer_kind()),
2569                        TextureSet::prim_textured(texture_source),
2570                        prim_user_data,
2571                        uv_rect_address.as_int(),
2572                    );
2573
2574                    debug_assert_ne!(image_instance.segment_instance_index, SegmentInstanceIndex::INVALID);
2575                    let (prim_cache_address, segments) = if image_instance.segment_instance_index == SegmentInstanceIndex::UNUSED {
2576                        (gpu_cache.get_address(&common_data.gpu_cache_handle), None)
2577                    } else {
2578                        let segment_instance = &ctx.scratch.segment_instances[image_instance.segment_instance_index];
2579                        let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
2580                        (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
2581                    };
2582
2583                    let local_rect = image_instance.adjustment.map_local_rect(&prim_rect);
2584                    let local_clip_rect = image_instance.tight_local_clip_rect
2585                        .intersection_unchecked(&local_rect);
2586
2587                    let prim_header = PrimitiveHeader {
2588                        local_rect,
2589                        local_clip_rect,
2590                        specific_prim_address: prim_cache_address,
2591                        transform_id,
2592                    };
2593
2594                    let prim_header_index = prim_headers.push(
2595                        &prim_header,
2596                        z_id,
2597                        self.batcher.render_task_address,
2598                        batch_params.prim_user_data,
2599                    );
2600
2601                    let brush_flags = match image_instance.normalized_uvs {
2602                        true => brush_flags | BrushFlags::NORMALIZED_UVS,
2603                        false => brush_flags,
2604                    };
2605
2606                    self.add_segmented_prim_to_batch(
2607                        segments,
2608                        common_data.opacity,
2609                        &batch_params,
2610                        blend_mode,
2611                        batch_features,
2612                        brush_flags,
2613                        common_data.edge_aa_mask,
2614                        prim_header_index,
2615                        bounding_rect,
2616                        transform_kind,
2617                        z_id,
2618                        prim_info.clip_task_index,
2619                        ctx,
2620                        render_tasks,
2621                    );
2622                } else {
2623                    const VECS_PER_SPECIFIC_BRUSH: usize = 3;
2624                    let max_tiles_per_header = (MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_SPECIFIC_BRUSH) / VECS_PER_SEGMENT;
2625
2626                    let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2627                        prim_info.clip_task_index,
2628                        render_tasks,
2629                    ).unwrap();
2630
2631                    // use temporary block storage since we don't know the number of visible tiles beforehand
2632                    let mut gpu_blocks = Vec::<GpuBlockData>::with_capacity(3 + max_tiles_per_header * 2);
2633                    for chunk in image_instance.visible_tiles.chunks(max_tiles_per_header) {
2634                        gpu_blocks.clear();
2635                        gpu_blocks.push(image_data.color.premultiplied().into()); //color
2636                        gpu_blocks.push(PremultipliedColorF::WHITE.into()); //bg color
2637                        gpu_blocks.push([-1.0, 0.0, 0.0, 0.0].into()); //stretch size
2638                        // negative first value makes the shader code ignore it and use the local size instead
2639                        for tile in chunk {
2640                            let tile_rect = tile.local_rect.translate(-prim_rect.min.to_vector());
2641                            gpu_blocks.push(tile_rect.into());
2642                            gpu_blocks.push(GpuBlockData::EMPTY);
2643                        }
2644
2645                        let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
2646                        let prim_header = PrimitiveHeader {
2647                            local_rect: prim_rect,
2648                            local_clip_rect: image_instance.tight_local_clip_rect,
2649                            specific_prim_address: gpu_cache.get_address(&gpu_handle),
2650                            transform_id,
2651                        };
2652                        let prim_header_index = prim_headers.push(
2653                            &prim_header,
2654                            z_id,
2655                            self.batcher.render_task_address,
2656                            prim_user_data,
2657                        );
2658
2659                        for (i, tile) in chunk.iter().enumerate() {
2660                            let (uv_rect_address, texture) = match render_tasks.resolve_location(tile.src_color, gpu_cache) {
2661                                Some(result) => result,
2662                                None => {
2663                                    return;
2664                                }
2665                            };
2666
2667                            let textures = BatchTextures::prim_textured(
2668                                texture,
2669                                clip_mask_texture_id,
2670                            );
2671
2672                            let batch_key = BatchKey {
2673                                blend_mode,
2674                                kind: BatchKind::Brush(BrushBatchKind::Image(texture.image_buffer_kind())),
2675                                textures,
2676                            };
2677
2678                            self.add_brush_instance_to_batches(
2679                                batch_key,
2680                                batch_features,
2681                                bounding_rect,
2682                                z_id,
2683                                i as i32,
2684                                tile.edge_flags,
2685                                clip_task_address,
2686                                brush_flags | BrushFlags::SEGMENT_RELATIVE | BrushFlags::PERSPECTIVE_INTERPOLATION,
2687                                prim_header_index,
2688                                uv_rect_address.as_int(),
2689                            );
2690                        }
2691                    }
2692                }
2693            }
2694            PrimitiveInstanceKind::LinearGradient { data_handle, ref visible_tiles_range, .. } => {
2695                let prim_data = &ctx.data_stores.linear_grad[data_handle];
2696
2697                let mut prim_header = PrimitiveHeader {
2698                    local_rect: prim_rect,
2699                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
2700                    specific_prim_address: GpuCacheAddress::INVALID,
2701                    transform_id,
2702                };
2703
2704                let blend_mode = if !prim_data.opacity.is_opaque ||
2705                    prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2706                    transform_kind == TransformedRectKind::Complex ||
2707                    is_anti_aliased
2708                {
2709                    BlendMode::PremultipliedAlpha
2710                } else {
2711                    BlendMode::None
2712                };
2713
2714                let user_data = [extra_prim_gpu_address.unwrap(), 0, 0, 0];
2715
2716                if visible_tiles_range.is_empty() {
2717                    let batch_params = BrushBatchParameters::shared(
2718                        BrushBatchKind::LinearGradient,
2719                        TextureSet::UNTEXTURED,
2720                        user_data,
2721                        0,
2722                    );
2723
2724                    prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
2725
2726                    let prim_header_index = prim_headers.push(
2727                        &prim_header,
2728                        z_id,
2729                        self.batcher.render_task_address,
2730                        user_data,
2731                    );
2732
2733                    let segments = if prim_data.brush_segments.is_empty() {
2734                        None
2735                    } else {
2736                        Some(prim_data.brush_segments.as_slice())
2737                    };
2738                    self.add_segmented_prim_to_batch(
2739                        segments,
2740                        prim_data.opacity,
2741                        &batch_params,
2742                        blend_mode,
2743                        batch_features,
2744                        brush_flags,
2745                        prim_data.edge_aa_mask,
2746                        prim_header_index,
2747                        bounding_rect,
2748                        transform_kind,
2749                        z_id,
2750                        prim_info.clip_task_index,
2751                        ctx,
2752                        render_tasks,
2753                    );
2754                } else {
2755                    let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
2756
2757                    let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2758                        prim_info.clip_task_index,
2759                        render_tasks,
2760                    ).unwrap();
2761
2762                    let key = BatchKey {
2763                        blend_mode,
2764                        kind: BatchKind::Brush(BrushBatchKind::LinearGradient),
2765                        textures: BatchTextures::prim_untextured(clip_mask_texture_id),
2766                    };
2767
2768                    for tile in visible_tiles {
2769                        let tile_prim_header = PrimitiveHeader {
2770                            specific_prim_address: gpu_cache.get_address(&tile.handle),
2771                            local_rect: tile.local_rect,
2772                            local_clip_rect: tile.local_clip_rect,
2773                            ..prim_header
2774                        };
2775                        let prim_header_index = prim_headers.push(
2776                            &tile_prim_header,
2777                            z_id,
2778                            self.batcher.render_task_address,
2779                            user_data,
2780                        );
2781
2782                        self.add_brush_instance_to_batches(
2783                            key,
2784                            batch_features,
2785                            bounding_rect,
2786                            z_id,
2787                            INVALID_SEGMENT_INDEX,
2788                            prim_data.edge_aa_mask,
2789                            clip_task_address,
2790                            brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
2791                            prim_header_index,
2792                            0,
2793                        );
2794                    }
2795                }
2796            }
2797            PrimitiveInstanceKind::CachedLinearGradient { data_handle, ref visible_tiles_range, .. } => {
2798                let prim_data = &ctx.data_stores.linear_grad[data_handle];
2799                let common_data = &prim_data.common;
2800
2801                let src_color = render_tasks.resolve_location(prim_data.src_color, gpu_cache);
2802
2803                let (uv_rect_address, texture_source) = match src_color {
2804                    Some(src) => src,
2805                    None => {
2806                        return;
2807                    }
2808                };
2809
2810                let textures = TextureSet::prim_textured(texture_source);
2811
2812                let prim_header = PrimitiveHeader {
2813                    local_rect: prim_rect,
2814                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
2815                    specific_prim_address: gpu_cache.get_address(&common_data.gpu_cache_handle),
2816                    transform_id,
2817                };
2818
2819                let prim_user_data = ImageBrushData {
2820                    color_mode: ShaderColorMode::Image,
2821                    alpha_type: AlphaType::PremultipliedAlpha,
2822                    raster_space: RasterizationSpace::Local,
2823                    opacity: 1.0,
2824                }.encode();
2825
2826                let blend_mode = if !common_data.opacity.is_opaque ||
2827                    prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2828                    transform_kind == TransformedRectKind::Complex ||
2829                    is_anti_aliased
2830                {
2831                    BlendMode::PremultipliedAlpha
2832                } else {
2833                    BlendMode::None
2834                };
2835
2836                let batch_kind = BrushBatchKind::Image(texture_source.image_buffer_kind());
2837
2838                if visible_tiles_range.is_empty() {
2839                    let batch_params = BrushBatchParameters::shared(
2840                        batch_kind,
2841                        textures,
2842                        prim_user_data,
2843                        uv_rect_address.as_int(),
2844                    );
2845
2846                    let segments = if prim_data.brush_segments.is_empty() {
2847                        None
2848                    } else {
2849                        Some(&prim_data.brush_segments[..])
2850                    };
2851
2852                    let prim_header_index = prim_headers.push(
2853                        &prim_header,
2854                        z_id,
2855                        self.batcher.render_task_address,
2856                        batch_params.prim_user_data,
2857                    );
2858
2859                    self.add_segmented_prim_to_batch(
2860                        segments,
2861                        common_data.opacity,
2862                        &batch_params,
2863                        blend_mode,
2864                        batch_features,
2865                        brush_flags,
2866                        common_data.edge_aa_mask,
2867                        prim_header_index,
2868                        bounding_rect,
2869                        transform_kind,
2870                        z_id,
2871                        prim_info.clip_task_index,
2872                        ctx,
2873                        render_tasks,
2874                    );
2875                } else {
2876                    let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
2877
2878                    let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
2879                        prim_info.clip_task_index,
2880                        render_tasks,
2881                    ).unwrap();
2882
2883                    let batch_key = BatchKey {
2884                        blend_mode,
2885                        kind: BatchKind::Brush(batch_kind),
2886                        textures: BatchTextures {
2887                            input: textures,
2888                            clip_mask,
2889                        },
2890                    };
2891
2892                    for tile in visible_tiles {
2893                        let tile_prim_header = PrimitiveHeader {
2894                            local_rect: tile.local_rect,
2895                            local_clip_rect: tile.local_clip_rect,
2896                            ..prim_header
2897                        };
2898                        let prim_header_index = prim_headers.push(
2899                            &tile_prim_header,
2900                            z_id,
2901                            self.batcher.render_task_address,
2902                            prim_user_data,
2903                        );
2904
2905                        self.add_brush_instance_to_batches(
2906                            batch_key,
2907                            batch_features,
2908                            bounding_rect,
2909                            z_id,
2910                            INVALID_SEGMENT_INDEX,
2911                            prim_data.edge_aa_mask,
2912                            clip_task_address,
2913                            brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
2914                            prim_header_index,
2915                            uv_rect_address.as_int(),
2916                        );
2917                    }
2918                }
2919            }
2920            PrimitiveInstanceKind::RadialGradient { data_handle, ref visible_tiles_range, .. } => {
2921                let prim_data = &ctx.data_stores.radial_grad[data_handle];
2922                let common_data = &prim_data.common;
2923
2924                let src_color = render_tasks.resolve_location(prim_data.src_color, gpu_cache);
2925
2926                let (uv_rect_address, texture_source) = match src_color {
2927                    Some(src) => src,
2928                    None => {
2929                        return;
2930                    }
2931                };
2932
2933                let textures = TextureSet::prim_textured(texture_source);
2934
2935                let prim_header = PrimitiveHeader {
2936                    local_rect: prim_rect,
2937                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
2938                    specific_prim_address: gpu_cache.get_address(&common_data.gpu_cache_handle),
2939                    transform_id,
2940                };
2941
2942                let prim_user_data = ImageBrushData {
2943                    color_mode: ShaderColorMode::Image,
2944                    alpha_type: AlphaType::PremultipliedAlpha,
2945                    raster_space: RasterizationSpace::Local,
2946                    opacity: 1.0,
2947                }.encode();
2948
2949
2950                let blend_mode = if !common_data.opacity.is_opaque ||
2951                    prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2952                    transform_kind == TransformedRectKind::Complex ||
2953                    is_anti_aliased
2954                {
2955                    BlendMode::PremultipliedAlpha
2956                } else {
2957                    BlendMode::None
2958                };
2959
2960                let batch_kind = BrushBatchKind::Image(texture_source.image_buffer_kind());
2961
2962                if visible_tiles_range.is_empty() {
2963                    let batch_params = BrushBatchParameters::shared(
2964                        batch_kind,
2965                        textures,
2966                        prim_user_data,
2967                        uv_rect_address.as_int(),
2968                    );
2969
2970                    let segments = if prim_data.brush_segments.is_empty() {
2971                        None
2972                    } else {
2973                        Some(&prim_data.brush_segments[..])
2974                    };
2975
2976                    let prim_header_index = prim_headers.push(
2977                        &prim_header,
2978                        z_id,
2979                        self.batcher.render_task_address,
2980                        batch_params.prim_user_data,
2981                    );
2982
2983                    self.add_segmented_prim_to_batch(
2984                        segments,
2985                        common_data.opacity,
2986                        &batch_params,
2987                        blend_mode,
2988                        batch_features,
2989                        brush_flags,
2990                        prim_data.edge_aa_mask,
2991                        prim_header_index,
2992                        bounding_rect,
2993                        transform_kind,
2994                        z_id,
2995                        prim_info.clip_task_index,
2996                        ctx,
2997                        render_tasks,
2998                    );
2999                } else {
3000                    let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
3001
3002                    let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
3003                        prim_info.clip_task_index,
3004                        render_tasks,
3005                    ).unwrap();
3006
3007                    let batch_key = BatchKey {
3008                        blend_mode,
3009                        kind: BatchKind::Brush(batch_kind),
3010                        textures: BatchTextures {
3011                            input: textures,
3012                            clip_mask,
3013                        },
3014                    };
3015
3016                    for tile in visible_tiles {
3017                        let tile_prim_header = PrimitiveHeader {
3018                            local_rect: tile.local_rect,
3019                            local_clip_rect: tile.local_clip_rect,
3020                            ..prim_header
3021                        };
3022                        let prim_header_index = prim_headers.push(
3023                            &tile_prim_header,
3024                            z_id,
3025                            self.batcher.render_task_address,
3026                            prim_user_data,
3027                        );
3028
3029                        self.add_brush_instance_to_batches(
3030                            batch_key,
3031                            batch_features,
3032                            bounding_rect,
3033                            z_id,
3034                            INVALID_SEGMENT_INDEX,
3035                            prim_data.edge_aa_mask,
3036                            clip_task_address,
3037                            brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
3038                            prim_header_index,
3039                            uv_rect_address.as_int(),
3040                        );
3041                    }
3042                }
3043
3044            }
3045            PrimitiveInstanceKind::ConicGradient { data_handle, ref visible_tiles_range, .. } => {
3046                let prim_data = &ctx.data_stores.conic_grad[data_handle];
3047                let common_data = &prim_data.common;
3048
3049                let src_color = render_tasks.resolve_location(prim_data.src_color, gpu_cache);
3050
3051                let (uv_rect_address, texture_source) = match src_color {
3052                    Some(src) => src,
3053                    None => {
3054                        return;
3055                    }
3056                };
3057
3058                let textures = TextureSet::prim_textured(texture_source);
3059
3060                let prim_header = PrimitiveHeader {
3061                    local_rect: prim_rect,
3062                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
3063                    specific_prim_address: gpu_cache.get_address(&common_data.gpu_cache_handle),
3064                    transform_id,
3065                };
3066
3067                let prim_user_data = ImageBrushData {
3068                    color_mode: ShaderColorMode::Image,
3069                    alpha_type: AlphaType::PremultipliedAlpha,
3070                    raster_space: RasterizationSpace::Local,
3071                    opacity: 1.0,
3072                }.encode();
3073
3074
3075                let blend_mode = if !common_data.opacity.is_opaque ||
3076                    prim_info.clip_task_index != ClipTaskIndex::INVALID ||
3077                    transform_kind == TransformedRectKind::Complex ||
3078                    is_anti_aliased
3079                {
3080                    BlendMode::PremultipliedAlpha
3081                } else {
3082                    BlendMode::None
3083                };
3084
3085                let batch_kind = BrushBatchKind::Image(texture_source.image_buffer_kind());
3086
3087                if visible_tiles_range.is_empty() {
3088                    let batch_params = BrushBatchParameters::shared(
3089                        batch_kind,
3090                        textures,
3091                        prim_user_data,
3092                        uv_rect_address.as_int(),
3093                    );
3094
3095                    let segments = if prim_data.brush_segments.is_empty() {
3096                        None
3097                    } else {
3098                        Some(&prim_data.brush_segments[..])
3099                    };
3100
3101                    let prim_header_index = prim_headers.push(
3102                        &prim_header,
3103                        z_id,
3104                        self.batcher.render_task_address,
3105                        batch_params.prim_user_data,
3106                    );
3107
3108                    self.add_segmented_prim_to_batch(
3109                        segments,
3110                        common_data.opacity,
3111                        &batch_params,
3112                        blend_mode,
3113                        batch_features,
3114                        brush_flags,
3115                        prim_data.edge_aa_mask,
3116                        prim_header_index,
3117                        bounding_rect,
3118                        transform_kind,
3119                        z_id,
3120                        prim_info.clip_task_index,
3121                        ctx,
3122                        render_tasks,
3123                    );
3124                } else {
3125                    let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
3126
3127                    let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
3128                        prim_info.clip_task_index,
3129                        render_tasks,
3130                    ).unwrap();
3131
3132                    let batch_key = BatchKey {
3133                        blend_mode,
3134                        kind: BatchKind::Brush(batch_kind),
3135                        textures: BatchTextures {
3136                            input: textures,
3137                            clip_mask,
3138                        },
3139                    };
3140
3141                    for tile in visible_tiles {
3142                        let tile_prim_header = PrimitiveHeader {
3143                            local_rect: tile.local_rect,
3144                            local_clip_rect: tile.local_clip_rect,
3145                            ..prim_header
3146                        };
3147                        let prim_header_index = prim_headers.push(
3148                            &tile_prim_header,
3149                            z_id,
3150                            self.batcher.render_task_address,
3151                            prim_user_data,
3152                        );
3153
3154                        self.add_brush_instance_to_batches(
3155                            batch_key,
3156                            batch_features,
3157                            bounding_rect,
3158                            z_id,
3159                            INVALID_SEGMENT_INDEX,
3160                            prim_data.edge_aa_mask,
3161                            clip_task_address,
3162                            brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
3163                            prim_header_index,
3164                            uv_rect_address.as_int(),
3165                        );
3166                    }
3167                }
3168            }
3169            PrimitiveInstanceKind::BackdropCapture { .. } => {}
3170            PrimitiveInstanceKind::BackdropRender { pic_index, .. } => {
3171                let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_image_handle);
3172                let blend_mode = BlendMode::PremultipliedAlpha;
3173                let pic_task_id = ctx.prim_store.pictures[pic_index.0].primary_render_task_id;
3174
3175                let prim_header = PrimitiveHeader {
3176                    local_rect: prim_rect,
3177                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
3178                    specific_prim_address: prim_cache_address,
3179                    transform_id,
3180                };
3181
3182                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
3183                    prim_info.clip_task_index,
3184                    render_tasks,
3185                ).unwrap();
3186
3187                let kind = BatchKind::Brush(
3188                    BrushBatchKind::Image(ImageBufferKind::Texture2D)
3189                );
3190                let (_, texture) = render_tasks.resolve_location(
3191                    pic_task_id,
3192                    gpu_cache,
3193                ).unwrap();
3194                let textures = BatchTextures::prim_textured(
3195                    texture,
3196                    clip_mask_texture_id,
3197                );
3198                let key = BatchKey::new(
3199                    kind,
3200                    blend_mode,
3201                    textures,
3202                );
3203                let prim_header_index = prim_headers.push(
3204                    &prim_header,
3205                    z_id,
3206                    self.batcher.render_task_address,
3207                    ImageBrushData {
3208                        color_mode: ShaderColorMode::Image,
3209                        alpha_type: AlphaType::PremultipliedAlpha,
3210                        raster_space: RasterizationSpace::Screen,
3211                        opacity: 1.0,
3212                    }.encode(),
3213                );
3214
3215                let pic_task = &render_tasks[pic_task_id.unwrap()];
3216                let pic_info = match pic_task.kind {
3217                    RenderTaskKind::Picture(ref info) => info,
3218                    _ => panic!("bug: not a picture"),
3219                };
3220                let target_rect = pic_task.get_target_rect();
3221
3222                let backdrop_rect = DeviceRect::from_origin_and_size(
3223                    pic_info.content_origin,
3224                    target_rect.size().to_f32(),
3225                );
3226
3227                let map_prim_to_backdrop = SpaceMapper::new_with_target(
3228                    pic_info.surface_spatial_node_index,
3229                    prim_spatial_node_index,
3230                    WorldRect::max_rect(),
3231                    ctx.spatial_tree,
3232                );
3233
3234                let points = [
3235                    map_prim_to_backdrop.map_point(prim_rect.top_left()),
3236                    map_prim_to_backdrop.map_point(prim_rect.top_right()),
3237                    map_prim_to_backdrop.map_point(prim_rect.bottom_left()),
3238                    map_prim_to_backdrop.map_point(prim_rect.bottom_right()),
3239                ];
3240
3241                if points.iter().any(|p| p.is_none()) {
3242                    return;
3243                }
3244
3245                let uvs = [
3246                    calculate_screen_uv(points[0].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
3247                    calculate_screen_uv(points[1].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
3248                    calculate_screen_uv(points[2].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
3249                    calculate_screen_uv(points[3].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
3250                ];
3251
3252                // TODO (gw): This is a hack that provides the GPU cache blocks for an
3253                //            ImageSource. We should update the GPU cache interfaces to
3254                //            allow pushing per-frame blocks via a request interface.
3255                let gpu_blocks = &[
3256                    GpuBlockData::from([
3257                        target_rect.min.x as f32,
3258                        target_rect.min.y as f32,
3259                        target_rect.max.x as f32,
3260                        target_rect.max.y as f32,
3261                    ]),
3262                    GpuBlockData::from([0.0; 4]),
3263                    GpuBlockData::from(uvs[0]),
3264                    GpuBlockData::from(uvs[1]),
3265                    GpuBlockData::from(uvs[2]),
3266                    GpuBlockData::from(uvs[3]),
3267                ];
3268                let uv_rect_handle = gpu_cache.push_per_frame_blocks(gpu_blocks);
3269
3270                self.add_brush_instance_to_batches(
3271                    key,
3272                    batch_features,
3273                    bounding_rect,
3274                    z_id,
3275                    INVALID_SEGMENT_INDEX,
3276                    EdgeAaSegmentMask::all(),
3277                    clip_task_address,
3278                    brush_flags,
3279                    prim_header_index,
3280                    uv_rect_handle.as_int(gpu_cache),
3281                );
3282            }
3283        }
3284    }
3285
3286    /// Draw a (potentially masked) alpha cutout so that a video underlay will be blended
3287    /// through by the compositor
3288    fn add_compositor_surface_cutout(
3289        &mut self,
3290        prim_rect: LayoutRect,
3291        local_clip_rect: LayoutRect,
3292        clip_task_index: ClipTaskIndex,
3293        transform_id: TransformPaletteId,
3294        z_id: ZBufferId,
3295        bounding_rect: &PictureRect,
3296        ctx: &RenderTargetContext,
3297        gpu_cache: &mut GpuCache,
3298        render_tasks: &RenderTaskGraph,
3299        prim_headers: &mut PrimitiveHeaders,
3300    ) {
3301        let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_black_rect_handle);
3302
3303        let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
3304            clip_task_index,
3305            render_tasks,
3306        ).unwrap();
3307
3308        let prim_header = PrimitiveHeader {
3309            local_rect: prim_rect,
3310            local_clip_rect,
3311            specific_prim_address: prim_cache_address,
3312            transform_id,
3313        };
3314
3315        let prim_header_index = prim_headers.push(
3316            &prim_header,
3317            z_id,
3318            self.batcher.render_task_address,
3319            [get_shader_opacity(1.0), 0, 0, 0],
3320        );
3321
3322        let batch_key = BatchKey {
3323            blend_mode: BlendMode::PremultipliedDestOut,
3324            kind: BatchKind::Brush(BrushBatchKind::Solid),
3325            textures: BatchTextures::prim_untextured(clip_mask_texture_id),
3326        };
3327
3328        self.add_brush_instance_to_batches(
3329            batch_key,
3330            BatchFeatures::ALPHA_PASS | BatchFeatures::CLIP_MASK,
3331            bounding_rect,
3332            z_id,
3333            INVALID_SEGMENT_INDEX,
3334            EdgeAaSegmentMask::empty(),
3335            clip_task_address,
3336            BrushFlags::empty(),
3337            prim_header_index,
3338            0,
3339        );
3340    }
3341
3342    /// Add a single segment instance to a batch.
3343    ///
3344    /// `edge_aa_mask` Specifies the edges that are *allowed* to have anti-aliasing, if and only
3345    /// if the segments enable it.
3346    /// In other words passing EdgeAaSegmentFlags::all() does not necessarily mean all edges will
3347    /// be anti-aliased, only that they could be.
3348    fn add_segment_to_batch(
3349        &mut self,
3350        segment: &BrushSegment,
3351        segment_data: &SegmentInstanceData,
3352        segment_index: i32,
3353        batch_kind: BrushBatchKind,
3354        prim_header_index: PrimitiveHeaderIndex,
3355        alpha_blend_mode: BlendMode,
3356        features: BatchFeatures,
3357        brush_flags: BrushFlags,
3358        edge_aa_mask: EdgeAaSegmentMask,
3359        bounding_rect: &PictureRect,
3360        transform_kind: TransformedRectKind,
3361        z_id: ZBufferId,
3362        prim_opacity: PrimitiveOpacity,
3363        clip_task_index: ClipTaskIndex,
3364        ctx: &RenderTargetContext,
3365        render_tasks: &RenderTaskGraph,
3366    ) {
3367        debug_assert!(clip_task_index != ClipTaskIndex::INVALID);
3368
3369        // Get GPU address of clip task for this segment, or None if
3370        // the entire segment is clipped out.
3371        if let Some((clip_task_address, clip_mask)) = ctx.get_clip_task_and_texture(
3372            clip_task_index,
3373            segment_index,
3374            render_tasks,
3375        ) {
3376            // If a got a valid (or OPAQUE) clip task address, add the segment.
3377            let is_inner = segment.edge_flags.is_empty();
3378            let needs_blending = !prim_opacity.is_opaque ||
3379                                 clip_task_address != OPAQUE_TASK_ADDRESS ||
3380                                 (!is_inner && transform_kind == TransformedRectKind::Complex) ||
3381                                 brush_flags.contains(BrushFlags::FORCE_AA);
3382
3383            let textures = BatchTextures {
3384                input: segment_data.textures,
3385                clip_mask,
3386            };
3387
3388            let batch_key = BatchKey {
3389                blend_mode: if needs_blending { alpha_blend_mode } else { BlendMode::None },
3390                kind: BatchKind::Brush(batch_kind),
3391                textures,
3392            };
3393
3394            self.add_brush_instance_to_batches(
3395                batch_key,
3396                features,
3397                bounding_rect,
3398                z_id,
3399                segment_index,
3400                segment.edge_flags & edge_aa_mask,
3401                clip_task_address,
3402                brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION | segment.brush_flags,
3403                prim_header_index,
3404                segment_data.specific_resource_address,
3405            );
3406        }
3407    }
3408
3409    /// Add any segment(s) from a brush to batches.
3410    ///
3411    /// `edge_aa_mask` Specifies the edges that are *allowed* to have anti-aliasing, if and only
3412    /// if the segments enable it.
3413    /// In other words passing EdgeAaSegmentFlags::all() does not necessarily mean all edges will
3414    /// be anti-aliased, only that they could be.
3415    fn add_segmented_prim_to_batch(
3416        &mut self,
3417        brush_segments: Option<&[BrushSegment]>,
3418        prim_opacity: PrimitiveOpacity,
3419        params: &BrushBatchParameters,
3420        blend_mode: BlendMode,
3421        features: BatchFeatures,
3422        brush_flags: BrushFlags,
3423        edge_aa_mask: EdgeAaSegmentMask,
3424        prim_header_index: PrimitiveHeaderIndex,
3425        bounding_rect: &PictureRect,
3426        transform_kind: TransformedRectKind,
3427        z_id: ZBufferId,
3428        clip_task_index: ClipTaskIndex,
3429        ctx: &RenderTargetContext,
3430        render_tasks: &RenderTaskGraph,
3431    ) {
3432        match (brush_segments, &params.segment_data) {
3433            (Some(ref brush_segments), SegmentDataKind::Instanced(ref segment_data)) => {
3434                // In this case, we have both a list of segments, and a list of
3435                // per-segment instance data. Zip them together to build batches.
3436                debug_assert_eq!(brush_segments.len(), segment_data.len());
3437                for (segment_index, (segment, segment_data)) in brush_segments
3438                    .iter()
3439                    .zip(segment_data.iter())
3440                    .enumerate()
3441                {
3442                    self.add_segment_to_batch(
3443                        segment,
3444                        segment_data,
3445                        segment_index as i32,
3446                        params.batch_kind,
3447                        prim_header_index,
3448                        blend_mode,
3449                        features,
3450                        brush_flags,
3451                        edge_aa_mask,
3452                        bounding_rect,
3453                        transform_kind,
3454                        z_id,
3455                        prim_opacity,
3456                        clip_task_index,
3457                        ctx,
3458                        render_tasks,
3459                    );
3460                }
3461            }
3462            (Some(ref brush_segments), SegmentDataKind::Shared(ref segment_data)) => {
3463                // A list of segments, but the per-segment data is common
3464                // between all segments.
3465                for (segment_index, segment) in brush_segments
3466                    .iter()
3467                    .enumerate()
3468                {
3469                    self.add_segment_to_batch(
3470                        segment,
3471                        segment_data,
3472                        segment_index as i32,
3473                        params.batch_kind,
3474                        prim_header_index,
3475                        blend_mode,
3476                        features,
3477                        brush_flags,
3478                        edge_aa_mask,
3479                        bounding_rect,
3480                        transform_kind,
3481                        z_id,
3482                        prim_opacity,
3483                        clip_task_index,
3484                        ctx,
3485                        render_tasks,
3486                    );
3487                }
3488            }
3489            (None, SegmentDataKind::Shared(ref segment_data)) => {
3490                // No segments, and thus no per-segment instance data.
3491                // Note: the blend mode already takes opacity into account
3492
3493                let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
3494                    clip_task_index,
3495                    render_tasks,
3496                ).unwrap();
3497
3498                let textures = BatchTextures {
3499                    input: segment_data.textures,
3500                    clip_mask,
3501                };
3502
3503                let batch_key = BatchKey {
3504                    blend_mode,
3505                    kind: BatchKind::Brush(params.batch_kind),
3506                    textures,
3507                };
3508
3509                self.add_brush_instance_to_batches(
3510                    batch_key,
3511                    features,
3512                    bounding_rect,
3513                    z_id,
3514                    INVALID_SEGMENT_INDEX,
3515                    edge_aa_mask,
3516                    clip_task_address,
3517                    brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
3518                    prim_header_index,
3519                    segment_data.specific_resource_address,
3520                );
3521            }
3522            (None, SegmentDataKind::Instanced(..)) => {
3523                // We should never hit the case where there are no segments,
3524                // but a list of segment instance data.
3525                unreachable!();
3526            }
3527        }
3528    }
3529}
3530
3531/// Either a single texture / user data for all segments,
3532/// or a list of one per segment.
3533enum SegmentDataKind {
3534    Shared(SegmentInstanceData),
3535    Instanced(SmallVec<[SegmentInstanceData; 8]>),
3536}
3537
3538/// The parameters that are specific to a kind of brush,
3539/// used by the common method to add a brush to batches.
3540struct BrushBatchParameters {
3541    batch_kind: BrushBatchKind,
3542    prim_user_data: [i32; 4],
3543    segment_data: SegmentDataKind,
3544}
3545
3546impl BrushBatchParameters {
3547    /// This brush instance has a list of per-segment
3548    /// instance data.
3549    fn instanced(
3550        batch_kind: BrushBatchKind,
3551        prim_user_data: [i32; 4],
3552        segment_data: SmallVec<[SegmentInstanceData; 8]>,
3553    ) -> Self {
3554        BrushBatchParameters {
3555            batch_kind,
3556            prim_user_data,
3557            segment_data: SegmentDataKind::Instanced(segment_data),
3558        }
3559    }
3560
3561    /// This brush instance shares the per-segment data
3562    /// across all segments.
3563    fn shared(
3564        batch_kind: BrushBatchKind,
3565        textures: TextureSet,
3566        prim_user_data: [i32; 4],
3567        specific_resource_address: i32,
3568    ) -> Self {
3569        BrushBatchParameters {
3570            batch_kind,
3571            prim_user_data,
3572            segment_data: SegmentDataKind::Shared(
3573                SegmentInstanceData {
3574                    textures,
3575                    specific_resource_address,
3576                }
3577            ),
3578        }
3579    }
3580}
3581
3582/// A list of clip instances to be drawn into a target.
3583#[cfg_attr(feature = "capture", derive(Serialize))]
3584#[cfg_attr(feature = "replay", derive(Deserialize))]
3585pub struct ClipMaskInstanceList {
3586    pub mask_instances_fast: FrameVec<MaskInstance>,
3587    pub mask_instances_slow: FrameVec<MaskInstance>,
3588
3589    pub mask_instances_fast_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
3590    pub mask_instances_slow_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
3591
3592    pub image_mask_instances: FastHashMap<TextureSource, FrameVec<PrimitiveInstanceData>>,
3593    pub image_mask_instances_with_scissor: FastHashMap<(DeviceIntRect, TextureSource), FrameVec<PrimitiveInstanceData>>,
3594}
3595
3596impl ClipMaskInstanceList {
3597    pub fn new(memory: &FrameMemory) -> Self {
3598        ClipMaskInstanceList {
3599            mask_instances_fast: memory.new_vec(),
3600            mask_instances_slow: memory.new_vec(),
3601            mask_instances_fast_with_scissor: FastHashMap::default(),
3602            mask_instances_slow_with_scissor: FastHashMap::default(),
3603            image_mask_instances: FastHashMap::default(),
3604            image_mask_instances_with_scissor: FastHashMap::default(),
3605        }
3606    }
3607}
3608
3609/// A list of clip instances to be drawn into a target.
3610#[derive(Debug)]
3611#[cfg_attr(feature = "capture", derive(Serialize))]
3612#[cfg_attr(feature = "replay", derive(Deserialize))]
3613pub struct ClipBatchList {
3614    /// Rectangle draws fill up the rectangles with rounded corners.
3615    pub slow_rectangles: FrameVec<ClipMaskInstanceRect>,
3616    pub fast_rectangles: FrameVec<ClipMaskInstanceRect>,
3617    pub box_shadows: FastHashMap<TextureSource, FrameVec<ClipMaskInstanceBoxShadow>>,
3618}
3619
3620impl ClipBatchList {
3621    fn new(memory: &FrameMemory) -> Self {
3622        ClipBatchList {
3623            slow_rectangles: memory.new_vec(),
3624            fast_rectangles: memory.new_vec(),
3625            box_shadows: FastHashMap::default(),
3626        }
3627    }
3628
3629    pub fn is_empty(&self) -> bool {
3630        self.slow_rectangles.is_empty()
3631          && self.fast_rectangles.is_empty()
3632          && self.box_shadows.is_empty()
3633    }
3634}
3635
3636/// Batcher managing draw calls into the clip mask (in the RT cache).
3637#[derive(Debug)]
3638#[cfg_attr(feature = "capture", derive(Serialize))]
3639#[cfg_attr(feature = "replay", derive(Deserialize))]
3640pub struct ClipBatcher {
3641    /// The first clip in each clip task. This will overwrite all pixels
3642    /// in the clip region, so we can skip doing a clear and write with
3643    /// blending disabled, which is a big performance win on Intel GPUs.
3644    pub primary_clips: ClipBatchList,
3645    /// Any subsequent clip masks (rare) for a clip task get drawn in
3646    /// a second pass with multiplicative blending enabled.
3647    pub secondary_clips: ClipBatchList,
3648
3649    gpu_supports_fast_clears: bool,
3650}
3651
3652impl ClipBatcher {
3653    pub fn new(
3654        gpu_supports_fast_clears: bool,
3655        memory: &FrameMemory,
3656    ) -> Self {
3657        ClipBatcher {
3658            primary_clips: ClipBatchList::new(memory),
3659            secondary_clips: ClipBatchList::new(memory),
3660            gpu_supports_fast_clears,
3661        }
3662    }
3663
3664    pub fn add_clip_region(
3665        &mut self,
3666        local_pos: LayoutPoint,
3667        sub_rect: DeviceRect,
3668        clip_data: ClipData,
3669        task_origin: DevicePoint,
3670        screen_origin: DevicePoint,
3671        device_pixel_scale: f32,
3672    ) {
3673        let instance = ClipMaskInstanceRect {
3674            common: ClipMaskInstanceCommon {
3675                clip_transform_id: TransformPaletteId::IDENTITY,
3676                prim_transform_id: TransformPaletteId::IDENTITY,
3677                sub_rect,
3678                task_origin,
3679                screen_origin,
3680                device_pixel_scale,
3681            },
3682            local_pos,
3683            clip_data,
3684        };
3685
3686        self.primary_clips.slow_rectangles.push(instance);
3687    }
3688
3689    /// Where appropriate, draw a clip rectangle as a small series of tiles,
3690    /// instead of one large rectangle.
3691    fn add_tiled_clip_mask(
3692        &mut self,
3693        mask_screen_rect: DeviceRect,
3694        local_clip_rect: LayoutRect,
3695        clip_spatial_node_index: SpatialNodeIndex,
3696        spatial_tree: &SpatialTree,
3697        world_rect: &WorldRect,
3698        global_device_pixel_scale: DevicePixelScale,
3699        common: &ClipMaskInstanceCommon,
3700        is_first_clip: bool,
3701    ) -> bool {
3702        // Only try to draw in tiles if the clip mark is big enough.
3703        if mask_screen_rect.area() < CLIP_RECTANGLE_AREA_THRESHOLD {
3704            return false;
3705        }
3706
3707        let mask_screen_rect_size = mask_screen_rect.size().to_i32();
3708        let clip_spatial_node = spatial_tree.get_spatial_node(clip_spatial_node_index);
3709
3710        // Only support clips that are axis-aligned to the root coordinate space,
3711        // for now, to simplify the logic below. This handles the vast majority
3712        // of real world cases, but could be expanded in future if needed.
3713        if clip_spatial_node.coordinate_system_id != CoordinateSystemId::root() {
3714            return false;
3715        }
3716
3717        // Get the world rect of the clip rectangle. If we can't transform it due
3718        // to the matrix, just fall back to drawing the entire clip mask.
3719        let transform = spatial_tree.get_world_transform(
3720            clip_spatial_node_index,
3721        );
3722        let world_clip_rect = match project_rect(
3723            &transform.into_transform(),
3724            &local_clip_rect,
3725            &world_rect,
3726        ) {
3727            Some(rect) => rect,
3728            None => return false,
3729        };
3730
3731        // Work out how many tiles to draw this clip mask in, stretched across the
3732        // device rect of the primitive clip mask.
3733        let world_device_rect = world_clip_rect * global_device_pixel_scale;
3734        let x_tiles = (mask_screen_rect_size.width + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE;
3735        let y_tiles = (mask_screen_rect_size.height + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE;
3736
3737        // Because we only run this code path for axis-aligned rects (the root coord system check above),
3738        // and only for rectangles (not rounded etc), the world_device_rect is not conservative - we know
3739        // that there is no inner_rect, and the world_device_rect should be the real, axis-aligned clip rect.
3740        let mask_origin = mask_screen_rect.min.to_vector();
3741        let clip_list = self.get_batch_list(is_first_clip);
3742
3743        for y in 0 .. y_tiles {
3744            for x in 0 .. x_tiles {
3745                let p0 = DeviceIntPoint::new(
3746                    x * CLIP_RECTANGLE_TILE_SIZE,
3747                    y * CLIP_RECTANGLE_TILE_SIZE,
3748                );
3749                let p1 = DeviceIntPoint::new(
3750                    (p0.x + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect_size.width),
3751                    (p0.y + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect_size.height),
3752                );
3753                let normalized_sub_rect = DeviceIntRect {
3754                    min: p0,
3755                    max: p1,
3756                }.to_f32();
3757                let world_sub_rect = normalized_sub_rect.translate(mask_origin);
3758
3759                // If the clip rect completely contains this tile rect, then drawing
3760                // these pixels would be redundant - since this clip can't possibly
3761                // affect the pixels in this tile, skip them!
3762                if !world_device_rect.contains_box(&world_sub_rect) {
3763                    clip_list.slow_rectangles.push(ClipMaskInstanceRect {
3764                        common: ClipMaskInstanceCommon {
3765                            sub_rect: normalized_sub_rect,
3766                            ..*common
3767                        },
3768                        local_pos: local_clip_rect.min,
3769                        clip_data: ClipData::uniform(local_clip_rect.size(), 0.0, ClipMode::Clip),
3770                    });
3771                }
3772            }
3773        }
3774
3775        true
3776    }
3777
3778    /// Retrieve the correct clip batch list to append to, depending
3779    /// on whether this is the first clip mask for a clip task.
3780    fn get_batch_list(
3781        &mut self,
3782        is_first_clip: bool,
3783    ) -> &mut ClipBatchList {
3784        if is_first_clip && !self.gpu_supports_fast_clears {
3785            &mut self.primary_clips
3786        } else {
3787            &mut self.secondary_clips
3788        }
3789    }
3790
3791    pub fn add(
3792        &mut self,
3793        clip_node_range: ClipNodeRange,
3794        root_spatial_node_index: SpatialNodeIndex,
3795        render_tasks: &RenderTaskGraph,
3796        gpu_cache: &GpuCache,
3797        clip_store: &ClipStore,
3798        transforms: &mut TransformPalette,
3799        actual_rect: DeviceRect,
3800        surface_device_pixel_scale: DevicePixelScale,
3801        task_origin: DevicePoint,
3802        screen_origin: DevicePoint,
3803        ctx: &RenderTargetContext,
3804    ) -> bool {
3805        let mut is_first_clip = true;
3806        let mut clear_to_one = false;
3807
3808        for i in 0 .. clip_node_range.count {
3809            let clip_instance = clip_store.get_instance_from_range(&clip_node_range, i);
3810            let clip_node = &ctx.data_stores.clip[clip_instance.handle];
3811
3812            let clip_transform_id = transforms.get_id(
3813                clip_node.item.spatial_node_index,
3814                ctx.root_spatial_node_index,
3815                ctx.spatial_tree,
3816            );
3817
3818            let prim_transform_id = transforms.get_id(
3819                root_spatial_node_index,
3820                ctx.root_spatial_node_index,
3821                ctx.spatial_tree,
3822            );
3823
3824            let common = ClipMaskInstanceCommon {
3825                sub_rect: DeviceRect::from_size(actual_rect.size()),
3826                task_origin,
3827                screen_origin,
3828                device_pixel_scale: surface_device_pixel_scale.0,
3829                clip_transform_id,
3830                prim_transform_id,
3831            };
3832
3833            let added_clip = match clip_node.item.kind {
3834                ClipItemKind::Image { .. } => {
3835                    unreachable!();
3836                }
3837                ClipItemKind::BoxShadow { ref source }  => {
3838                    let task_id = source
3839                        .render_task
3840                        .expect("bug: render task handle not allocated");
3841                    let (uv_rect_address, texture) = render_tasks.resolve_location(task_id, gpu_cache).unwrap();
3842
3843                    self.get_batch_list(is_first_clip)
3844                        .box_shadows
3845                        .entry(texture)
3846                        .or_insert_with(|| ctx.frame_memory.new_vec())
3847                        .push(ClipMaskInstanceBoxShadow {
3848                            common,
3849                            resource_address: uv_rect_address,
3850                            shadow_data: BoxShadowData {
3851                                src_rect_size: source.original_alloc_size,
3852                                clip_mode: source.clip_mode as i32,
3853                                stretch_mode_x: source.stretch_mode_x as i32,
3854                                stretch_mode_y: source.stretch_mode_y as i32,
3855                                dest_rect: source.prim_shadow_rect,
3856                            },
3857                        });
3858
3859                    true
3860                }
3861                ClipItemKind::Rectangle { rect, mode: ClipMode::ClipOut } => {
3862                    self.get_batch_list(is_first_clip)
3863                        .slow_rectangles
3864                        .push(ClipMaskInstanceRect {
3865                            common,
3866                            local_pos: rect.min,
3867                            clip_data: ClipData::uniform(rect.size(), 0.0, ClipMode::ClipOut),
3868                        });
3869
3870                    true
3871                }
3872                ClipItemKind::Rectangle { rect, mode: ClipMode::Clip } => {
3873                    if clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) {
3874                        false
3875                    } else {
3876                        if self.add_tiled_clip_mask(
3877                            actual_rect,
3878                            rect,
3879                            clip_node.item.spatial_node_index,
3880                            ctx.spatial_tree,
3881                            &ctx.screen_world_rect,
3882                            ctx.global_device_pixel_scale,
3883                            &common,
3884                            is_first_clip,
3885                        ) {
3886                            clear_to_one |= is_first_clip;
3887                        } else {
3888                            self.get_batch_list(is_first_clip)
3889                                .slow_rectangles
3890                                .push(ClipMaskInstanceRect {
3891                                    common,
3892                                    local_pos: rect.min,
3893                                    clip_data: ClipData::uniform(rect.size(), 0.0, ClipMode::Clip),
3894                                });
3895                        }
3896
3897                        true
3898                    }
3899                }
3900                ClipItemKind::RoundedRectangle { rect, ref radius, mode, .. } => {
3901                    let batch_list = self.get_batch_list(is_first_clip);
3902                    let instance = ClipMaskInstanceRect {
3903                        common,
3904                        local_pos: rect.min,
3905                        clip_data: ClipData::rounded_rect(rect.size(), radius, mode),
3906                    };
3907                    if clip_instance.flags.contains(ClipNodeFlags::USE_FAST_PATH) {
3908                        batch_list.fast_rectangles.push(instance);
3909                    } else {
3910                        batch_list.slow_rectangles.push(instance);
3911                    }
3912
3913                    true
3914                }
3915            };
3916
3917            is_first_clip &= !added_clip;
3918        }
3919
3920        clear_to_one
3921    }
3922}
3923
3924impl<'a, 'rc> RenderTargetContext<'a, 'rc> {
3925    /// Retrieve the GPU task address for a given clip task instance.
3926    /// Returns None if the segment was completely clipped out.
3927    /// Returns Some(OPAQUE_TASK_ADDRESS) if no clip mask is needed.
3928    /// Returns Some(task_address) if there was a valid clip mask.
3929    fn get_clip_task_and_texture(
3930        &self,
3931        clip_task_index: ClipTaskIndex,
3932        offset: i32,
3933        render_tasks: &RenderTaskGraph,
3934    ) -> Option<(RenderTaskAddress, TextureSource)> {
3935        match self.scratch.clip_mask_instances[clip_task_index.0 as usize + offset as usize] {
3936            ClipMaskKind::Mask(task_id) => {
3937                Some((
3938                    task_id.into(),
3939                    TextureSource::TextureCache(
3940                        render_tasks[task_id].get_target_texture(),
3941                        Swizzle::default(),
3942                    )
3943                ))
3944            }
3945            ClipMaskKind::None => {
3946                Some((OPAQUE_TASK_ADDRESS, TextureSource::Invalid))
3947            }
3948            ClipMaskKind::Clipped => {
3949                None
3950            }
3951        }
3952    }
3953
3954    /// Helper function to get the clip task address for a
3955    /// non-segmented primitive.
3956    fn get_prim_clip_task_and_texture(
3957        &self,
3958        clip_task_index: ClipTaskIndex,
3959        render_tasks: &RenderTaskGraph,
3960    ) -> Option<(RenderTaskAddress, TextureSource)> {
3961        self.get_clip_task_and_texture(
3962            clip_task_index,
3963            0,
3964            render_tasks,
3965        )
3966    }
3967}
3968
3969impl CompositorSurfaceKind {
3970    /// Returns true if the type of compositor surface needs an alpha cutout rendered
3971    fn needs_cutout(&self) -> bool {
3972        match self {
3973            CompositorSurfaceKind::Underlay => true,
3974            CompositorSurfaceKind::Overlay | CompositorSurfaceKind::Blit => false,
3975        }
3976    }
3977}