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        if let PrimitiveInstanceKind::Picture { pic_index, .. } = prim_instance.kind {
987            let picture = &ctx.prim_store.pictures[pic_index.0];
988            if let Some(snapshot) = picture.snapshot {
989                if snapshot.detached {
990                    return;
991                }
992            }
993
994            let blend_mode = BlendMode::PremultipliedAlpha;
995            let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_image_handle);
996
997            match picture.raster_config {
998                Some(ref raster_config) => {
999                    // If the child picture was rendered in local space, we can safely
1000                    // interpolate the UV coordinates with perspective correction.
1001                    let brush_flags = brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION;
1002
1003                    let surface = &ctx.surfaces[raster_config.surface_index.0];
1004                    let mut local_clip_rect = prim_info.clip_chain.local_clip_rect;
1005
1006                    // If we are drawing with snapping enabled, form a simple transform that just applies
1007                    // the scale / translation from the raster transform. Otherwise, in edge cases where the
1008                    // intermediate surface has a non-identity but axis-aligned transform (e.g. a 180 degree
1009                    // rotation) it can be applied twice.
1010                    let transform_id = if surface.surface_spatial_node_index == surface.raster_spatial_node_index {
1011                        transform_id
1012                    } else {
1013                        let map_local_to_raster = SpaceMapper::new_with_target(
1014                            root_spatial_node_index,
1015                            surface.surface_spatial_node_index,
1016                            LayoutRect::max_rect(),
1017                            ctx.spatial_tree,
1018                        );
1019
1020                        let raster_rect = map_local_to_raster
1021                            .map(&prim_rect)
1022                            .unwrap();
1023
1024                        let sx = (raster_rect.max.x - raster_rect.min.x) / (prim_rect.max.x - prim_rect.min.x);
1025                        let sy = (raster_rect.max.y - raster_rect.min.y) / (prim_rect.max.y - prim_rect.min.y);
1026
1027                        let tx = raster_rect.min.x - sx * prim_rect.min.x;
1028                        let ty = raster_rect.min.y - sy * prim_rect.min.y;
1029
1030                        let transform = ScaleOffset::new(sx, sy, tx, ty);
1031
1032                        let raster_clip_rect = map_local_to_raster
1033                            .map(&prim_info.clip_chain.local_clip_rect)
1034                            .unwrap();
1035                        local_clip_rect = transform.unmap_rect(&raster_clip_rect);
1036
1037                        transforms.get_custom(transform.to_transform())
1038                    };
1039
1040                    let prim_header = PrimitiveHeader {
1041                        local_rect: prim_rect,
1042                        local_clip_rect,
1043                        specific_prim_address: prim_cache_address,
1044                        transform_id,
1045                    };
1046
1047                    let mut is_opaque = prim_info.clip_task_index == ClipTaskIndex::INVALID
1048                        && surface.is_opaque
1049                        && transform_kind == TransformedRectKind::AxisAligned
1050                        && !is_anti_aliased;
1051
1052                    let pic_task_id = picture.primary_render_task_id.unwrap();
1053
1054                    let pic_task = &render_tasks[pic_task_id];
1055                    match pic_task.sub_pass {
1056                        Some(SubPass::Masks { .. }) => {
1057                            is_opaque = false;
1058                        }
1059                        None => {}
1060                    }
1061                    match raster_config.composite_mode {
1062                        PictureCompositeMode::TileCache { .. } => {
1063                            // TODO(gw): For now, TileCache is still a composite mode, even though
1064                            //           it will only exist as a top level primitive and never
1065                            //           be encountered during batching. Consider making TileCache
1066                            //           a standalone type, not a picture.
1067                            return;
1068                        }
1069                        PictureCompositeMode::IntermediateSurface { .. } => {
1070                            // TODO(gw): As an optimization, support making this a pass-through
1071                            //           and/or drawing directly from here when possible
1072                            //           (e.g. if not wrapped by filters / different spatial node).
1073                            return;
1074                        }
1075                        _=>{}
1076                    }
1077
1078                    let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1079                        prim_info.clip_task_index,
1080                        render_tasks,
1081                    ).unwrap();
1082
1083                    let (uv_rect_address, texture) = render_tasks.resolve_location(
1084                        pic_task_id,
1085                        gpu_cache,
1086                    ).unwrap();
1087
1088                    // The set of input textures that most composite modes use,
1089                    // howevr some override it.
1090                    let textures = BatchTextures::prim_textured(
1091                        texture,
1092                        clip_mask_texture_id,
1093                    );
1094
1095                    match raster_config.composite_mode {
1096                        PictureCompositeMode::TileCache { .. } => {}
1097                        PictureCompositeMode::IntermediateSurface { .. } => {}
1098                        PictureCompositeMode::Filter(ref filter) => {
1099                            assert!(filter.is_visible());
1100                            match filter {
1101                                Filter::Blur { .. } => {
1102                                    let kind = BatchKind::Brush(
1103                                        BrushBatchKind::Image(ImageBufferKind::Texture2D)
1104                                    );
1105
1106                                    let key = BatchKey::new(
1107                                        kind,
1108                                        blend_mode,
1109                                        textures,
1110                                    );
1111                                    let prim_header_index = prim_headers.push(
1112                                        &prim_header,
1113                                        z_id,
1114                                        self.batcher.render_task_address,
1115                                        ImageBrushData {
1116                                            color_mode: ShaderColorMode::Image,
1117                                            alpha_type: AlphaType::PremultipliedAlpha,
1118                                            raster_space: RasterizationSpace::Screen,
1119                                            opacity: 1.0,
1120                                        }.encode(),
1121                                    );
1122
1123                                    self.add_brush_instance_to_batches(
1124                                        key,
1125                                        batch_features,
1126                                        bounding_rect,
1127                                        z_id,
1128                                        INVALID_SEGMENT_INDEX,
1129                                        EdgeAaSegmentMask::all(),
1130                                        clip_task_address,
1131                                        brush_flags,
1132                                        prim_header_index,
1133                                        uv_rect_address.as_int(),
1134                                    );
1135                                }
1136                                Filter::DropShadows(shadows) => {
1137                                    // Draw an instance per shadow first, following by the content.
1138
1139                                    // The shadows and the content get drawn as a brush image.
1140                                    let kind = BatchKind::Brush(
1141                                        BrushBatchKind::Image(ImageBufferKind::Texture2D),
1142                                    );
1143
1144                                    // Gets the saved render task ID of the content, which is
1145                                    // deeper in the render task graph than the direct child.
1146                                    let secondary_id = picture.secondary_render_task_id.expect("no secondary!?");
1147                                    let content_source = {
1148                                        let secondary_task = &render_tasks[secondary_id];
1149                                        let texture_id = secondary_task.get_target_texture();
1150                                        TextureSource::TextureCache(
1151                                            texture_id,
1152                                            Swizzle::default(),
1153                                        )
1154                                    };
1155
1156                                    // Retrieve the UV rect addresses for shadow/content.
1157                                    let shadow_uv_rect_address = uv_rect_address;
1158                                    let shadow_textures = textures;
1159
1160                                    let content_uv_rect_address = render_tasks[secondary_id]
1161                                        .get_texture_address(gpu_cache)
1162                                        .as_int();
1163
1164                                    // Build BatchTextures for shadow/content
1165                                    let content_textures = BatchTextures::prim_textured(
1166                                        content_source,
1167                                        clip_mask_texture_id,
1168                                    );
1169
1170                                    // Build batch keys for shadow/content
1171                                    let shadow_key = BatchKey::new(kind, blend_mode, shadow_textures);
1172                                    let content_key = BatchKey::new(kind, blend_mode, content_textures);
1173
1174                                    for (shadow, shadow_gpu_data) in shadows.iter().zip(picture.extra_gpu_data_handles.iter()) {
1175                                        // Get the GPU cache address of the extra data handle.
1176                                        let shadow_prim_address = gpu_cache.get_address(shadow_gpu_data);
1177
1178                                        let shadow_rect = prim_header.local_rect.translate(shadow.offset);
1179
1180                                        let shadow_prim_header = PrimitiveHeader {
1181                                            local_rect: shadow_rect,
1182                                            specific_prim_address: shadow_prim_address,
1183                                            ..prim_header
1184                                        };
1185
1186                                        let shadow_prim_header_index = prim_headers.push(
1187                                            &shadow_prim_header,
1188                                            z_id,
1189                                            self.batcher.render_task_address,
1190                                            ImageBrushData {
1191                                                color_mode: ShaderColorMode::Alpha,
1192                                                alpha_type: AlphaType::PremultipliedAlpha,
1193                                                raster_space: RasterizationSpace::Screen,
1194                                                opacity: 1.0,
1195                                            }.encode(),
1196                                        );
1197
1198                                        self.add_brush_instance_to_batches(
1199                                            shadow_key,
1200                                            batch_features,
1201                                            bounding_rect,
1202                                            z_id,
1203                                            INVALID_SEGMENT_INDEX,
1204                                            EdgeAaSegmentMask::all(),
1205                                            clip_task_address,
1206                                            brush_flags,
1207                                            shadow_prim_header_index,
1208                                            shadow_uv_rect_address.as_int(),
1209                                        );
1210                                    }
1211                                    let z_id_content = z_generator.next();
1212
1213                                    let content_prim_header_index = prim_headers.push(
1214                                        &prim_header,
1215                                        z_id_content,
1216                                        self.batcher.render_task_address,
1217                                        ImageBrushData {
1218                                            color_mode: ShaderColorMode::Image,
1219                                            alpha_type: AlphaType::PremultipliedAlpha,
1220                                            raster_space: RasterizationSpace::Screen,
1221                                            opacity: 1.0,
1222                                        }.encode(),
1223                                    );
1224
1225                                    self.add_brush_instance_to_batches(
1226                                        content_key,
1227                                        batch_features,
1228                                        bounding_rect,
1229                                        z_id_content,
1230                                        INVALID_SEGMENT_INDEX,
1231                                        EdgeAaSegmentMask::all(),
1232                                        clip_task_address,
1233                                        brush_flags,
1234                                        content_prim_header_index,
1235                                        content_uv_rect_address,
1236                                    );
1237                                }
1238                                Filter::Opacity(_, amount) => {
1239                                    let amount = (amount * 65536.0) as i32;
1240
1241                                    let key = BatchKey::new(
1242                                        BatchKind::Brush(BrushBatchKind::Opacity),
1243                                        BlendMode::PremultipliedAlpha,
1244                                        textures,
1245                                    );
1246
1247                                    let prim_header_index = prim_headers.push(
1248                                        &prim_header,
1249                                        z_id,
1250                                        self.batcher.render_task_address,
1251                                        [
1252                                            uv_rect_address.as_int(),
1253                                            amount,
1254                                            0,
1255                                            0,
1256                                        ]
1257                                    );
1258
1259                                    self.add_brush_instance_to_batches(
1260                                        key,
1261                                        batch_features,
1262                                        bounding_rect,
1263                                        z_id,
1264                                        INVALID_SEGMENT_INDEX,
1265                                        EdgeAaSegmentMask::all(),
1266                                        clip_task_address,
1267                                        brush_flags,
1268                                        prim_header_index,
1269                                        0,
1270                                    );
1271                                }
1272                                _ => {
1273                                    // Must be kept in sync with brush_blend.glsl
1274                                    let filter_mode = filter.as_int();
1275
1276                                    let user_data = match filter {
1277                                        Filter::Identity => 0x10000i32, // matches `Contrast(1)`
1278                                        Filter::Contrast(amount) |
1279                                        Filter::Grayscale(amount) |
1280                                        Filter::Invert(amount) |
1281                                        Filter::Saturate(amount) |
1282                                        Filter::Sepia(amount) |
1283                                        Filter::Brightness(amount) => {
1284                                            (amount * 65536.0) as i32
1285                                        }
1286                                        Filter::SrgbToLinear | Filter::LinearToSrgb => 0,
1287                                        Filter::HueRotate(angle) => {
1288                                            (0.01745329251 * angle * 65536.0) as i32
1289                                        }
1290                                        Filter::ColorMatrix(_) => {
1291                                            picture.extra_gpu_data_handles[0].as_int(gpu_cache)
1292                                        }
1293                                        Filter::Flood(_) => {
1294                                            picture.extra_gpu_data_handles[0].as_int(gpu_cache)
1295                                        }
1296
1297                                        // These filters are handled via different paths.
1298                                        Filter::ComponentTransfer |
1299                                        Filter::Blur { .. } |
1300                                        Filter::DropShadows(..) |
1301                                        Filter::Opacity(..) |
1302                                        Filter::SVGGraphNode(..) => unreachable!(),
1303                                    };
1304
1305                                    // Other filters that may introduce opacity are handled via different
1306                                    // paths.
1307                                    if let Filter::ColorMatrix(..) = filter {
1308                                        is_opaque = false;
1309                                    }
1310
1311                                    let blend_mode = if is_opaque {
1312                                        BlendMode::None
1313                                    } else {
1314                                        BlendMode::PremultipliedAlpha
1315                                    };
1316
1317                                    let key = BatchKey::new(
1318                                        BatchKind::Brush(BrushBatchKind::Blend),
1319                                        blend_mode,
1320                                        textures,
1321                                    );
1322
1323                                    let prim_header_index = prim_headers.push(
1324                                        &prim_header,
1325                                        z_id,
1326                                        self.batcher.render_task_address,
1327                                        [
1328                                            uv_rect_address.as_int(),
1329                                            filter_mode,
1330                                            user_data,
1331                                            0,
1332                                        ]
1333                                    );
1334
1335                                    self.add_brush_instance_to_batches(
1336                                        key,
1337                                        batch_features,
1338                                        bounding_rect,
1339                                        z_id,
1340                                        INVALID_SEGMENT_INDEX,
1341                                        EdgeAaSegmentMask::all(),
1342                                        clip_task_address,
1343                                        brush_flags,
1344                                        prim_header_index,
1345                                        0,
1346                                    );
1347                                }
1348                            }
1349                        }
1350                        PictureCompositeMode::ComponentTransferFilter(handle) => {
1351                            // This is basically the same as the general filter case above
1352                            // except we store a little more data in the filter mode and
1353                            // a gpu cache handle in the user data.
1354                            let filter_data = &ctx.data_stores.filter_data[handle];
1355                            let filter_mode : i32 = Filter::ComponentTransfer.as_int() |
1356                                ((filter_data.data.r_func.to_int() << 28 |
1357                                  filter_data.data.g_func.to_int() << 24 |
1358                                  filter_data.data.b_func.to_int() << 20 |
1359                                  filter_data.data.a_func.to_int() << 16) as i32);
1360
1361                            let user_data = filter_data.gpu_cache_handle.as_int(gpu_cache);
1362
1363                            let key = BatchKey::new(
1364                                BatchKind::Brush(BrushBatchKind::Blend),
1365                                BlendMode::PremultipliedAlpha,
1366                                textures,
1367                            );
1368
1369                            let prim_header_index = prim_headers.push(
1370                                &prim_header,
1371                                z_id,
1372                                self.batcher.render_task_address,
1373                                [
1374                                    uv_rect_address.as_int(),
1375                                    filter_mode,
1376                                    user_data,
1377                                    0,
1378                                ]
1379                            );
1380
1381                            self.add_brush_instance_to_batches(
1382                                key,
1383                                batch_features,
1384                                bounding_rect,
1385                                z_id,
1386                                INVALID_SEGMENT_INDEX,
1387                                EdgeAaSegmentMask::all(),
1388                                clip_task_address,
1389                                brush_flags,
1390                                prim_header_index,
1391                                0,
1392                            );
1393                        }
1394                        PictureCompositeMode::MixBlend(mode) if BlendMode::from_mix_blend_mode(
1395                            mode,
1396                            ctx.use_advanced_blending,
1397                            !ctx.break_advanced_blend_batches,
1398                            ctx.use_dual_source_blending,
1399                        ).is_some() => {
1400                            let key = BatchKey::new(
1401                                BatchKind::Brush(
1402                                    BrushBatchKind::Image(ImageBufferKind::Texture2D),
1403                                ),
1404                                BlendMode::from_mix_blend_mode(
1405                                    mode,
1406                                    ctx.use_advanced_blending,
1407                                    !ctx.break_advanced_blend_batches,
1408                                    ctx.use_dual_source_blending,
1409                                ).unwrap(),
1410                                textures,
1411                            );
1412                            let prim_header_index = prim_headers.push(
1413                                &prim_header,
1414                                z_id,
1415                                self.batcher.render_task_address,
1416                                ImageBrushData {
1417                                    color_mode: match key.blend_mode {
1418                                        BlendMode::MultiplyDualSource => ShaderColorMode::MultiplyDualSource,
1419                                        _ => ShaderColorMode::Image,
1420                                    },
1421                                    alpha_type: AlphaType::PremultipliedAlpha,
1422                                    raster_space: RasterizationSpace::Screen,
1423                                    opacity: 1.0,
1424                                }.encode(),
1425                            );
1426
1427                            self.add_brush_instance_to_batches(
1428                                key,
1429                                batch_features,
1430                                bounding_rect,
1431                                z_id,
1432                                INVALID_SEGMENT_INDEX,
1433                                EdgeAaSegmentMask::all(),
1434                                clip_task_address,
1435                                brush_flags,
1436                                prim_header_index,
1437                                uv_rect_address.as_int(),
1438                            );
1439                        }
1440                        PictureCompositeMode::MixBlend(mode) => {
1441                            let backdrop_id = picture.secondary_render_task_id.expect("no backdrop!?");
1442
1443                            let color0 = render_tasks[backdrop_id].get_target_texture();
1444                            let color1 = render_tasks[pic_task_id].get_target_texture();
1445
1446                            // Create a separate brush instance for each batcher. For most cases,
1447                            // there is only one batcher. However, in the case of drawing onto
1448                            // a picture cache, there is one batcher per tile. Although not
1449                            // currently used, the implementation of mix-blend-mode now supports
1450                            // doing partial readbacks per-tile. In future, this will be enabled
1451                            // and allow mix-blends to operate on picture cache surfaces without
1452                            // a separate isolated intermediate surface.
1453
1454                            let batch_key = BatchKey::new(
1455                                BatchKind::Brush(
1456                                    BrushBatchKind::MixBlend {
1457                                        task_id: self.batcher.render_task_id,
1458                                        backdrop_id,
1459                                    },
1460                                ),
1461                                BlendMode::PremultipliedAlpha,
1462                                BatchTextures {
1463                                    input: TextureSet {
1464                                        colors: [
1465                                            TextureSource::TextureCache(
1466                                                color0,
1467                                                Swizzle::default(),
1468                                            ),
1469                                            TextureSource::TextureCache(
1470                                                color1,
1471                                                Swizzle::default(),
1472                                            ),
1473                                            TextureSource::Invalid,
1474                                        ],
1475                                    },
1476                                    clip_mask: clip_mask_texture_id,
1477                                },
1478                            );
1479                            let src_uv_address = render_tasks[pic_task_id].get_texture_address(gpu_cache);
1480                            let readback_uv_address = render_tasks[backdrop_id].get_texture_address(gpu_cache);
1481                            let prim_header_index = prim_headers.push(
1482                                &prim_header,
1483                                z_id,
1484                                self.batcher.render_task_address,
1485                                [
1486                                    mode as u32 as i32,
1487                                    readback_uv_address.as_int(),
1488                                    src_uv_address.as_int(),
1489                                    0,
1490                                ]
1491                            );
1492
1493                            let instance = BrushInstance {
1494                                segment_index: INVALID_SEGMENT_INDEX,
1495                                edge_flags: EdgeAaSegmentMask::all(),
1496                                clip_task_address,
1497                                brush_flags,
1498                                prim_header_index,
1499                                resource_address: 0,
1500                            };
1501
1502                            self.batcher.push_single_instance(
1503                                batch_key,
1504                                batch_features,
1505                                bounding_rect,
1506                                z_id,
1507                                PrimitiveInstanceData::from(instance),
1508                            );
1509                        }
1510                        PictureCompositeMode::Blit(_) => {
1511                            match picture.context_3d {
1512                                Picture3DContext::In { root_data: Some(_), .. } => {
1513                                    unreachable!("bug: should not have a raster_config");
1514                                }
1515                                Picture3DContext::In { root_data: None, .. } => {
1516                                    // TODO(gw): Store this inside the split picture so that we
1517                                    //           don't need to pass in extra_prim_gpu_address for
1518                                    //           every prim instance.
1519                                    // TODO(gw): Ideally we'd skip adding 3d child prims to batches
1520                                    //           without gpu cache address but it's currently
1521                                    //           used by the prepare pass. Refactor this!
1522                                    let extra_prim_gpu_address = match extra_prim_gpu_address {
1523                                        Some(prim_address) => prim_address,
1524                                        None => return,
1525                                    };
1526
1527                                    let prim_header = PrimitiveHeader {
1528                                        local_rect: prim_rect,
1529                                        local_clip_rect: prim_info.clip_chain.local_clip_rect,
1530                                        specific_prim_address: GpuCacheAddress::INVALID,
1531                                        transform_id: transforms
1532                                            .get_id(
1533                                                prim_spatial_node_index,
1534                                                root_spatial_node_index,
1535                                                ctx.spatial_tree,
1536                                            ),
1537                                    };
1538
1539                                    // Need a new z-id for each child preserve-3d context added
1540                                    // by this inner loop.
1541                                    let z_id = z_generator.next();
1542
1543                                    let prim_header_index = prim_headers.push(
1544                                        &prim_header,
1545                                        z_id,
1546                                        self.batcher.render_task_address,
1547                                        [
1548                                            uv_rect_address.as_int(),
1549                                            BrushFlags::PERSPECTIVE_INTERPOLATION.bits() as i32,
1550                                            0,
1551                                            clip_task_address.0 as i32,
1552                                        ]
1553                                    );
1554
1555                                    let key = BatchKey::new(
1556                                        BatchKind::SplitComposite,
1557                                        BlendMode::PremultipliedAlpha,
1558                                        textures,
1559                                    );
1560
1561                                    self.add_split_composite_instance_to_batches(
1562                                        key,
1563                                        BatchFeatures::CLIP_MASK,
1564                                        &prim_info.clip_chain.pic_coverage_rect,
1565                                        z_id,
1566                                        prim_header_index,
1567                                        extra_prim_gpu_address,
1568                                    );
1569                                }
1570                                Picture3DContext::Out { .. } => {
1571                                    let textures = TextureSet {
1572                                        colors: [
1573                                            texture,
1574                                            TextureSource::Invalid,
1575                                            TextureSource::Invalid,
1576                                        ],
1577                                    };
1578                                    let batch_params = BrushBatchParameters::shared(
1579                                        BrushBatchKind::Image(ImageBufferKind::Texture2D),
1580                                        textures,
1581                                        ImageBrushData {
1582                                            color_mode: ShaderColorMode::Image,
1583                                            alpha_type: AlphaType::PremultipliedAlpha,
1584                                            raster_space: RasterizationSpace::Screen,
1585                                            opacity: 1.0,
1586                                        }.encode(),
1587                                        uv_rect_address.as_int(),
1588                                    );
1589
1590                                    let prim_header = PrimitiveHeader {
1591                                        specific_prim_address: prim_cache_address,
1592                                        ..prim_header
1593                                    };
1594
1595                                    let prim_header_index = prim_headers.push(
1596                                        &prim_header,
1597                                        z_id,
1598                                        self.batcher.render_task_address,
1599                                        batch_params.prim_user_data,
1600                                    );
1601
1602                                    let (opacity, blend_mode) = if is_opaque {
1603                                        (PrimitiveOpacity::opaque(), BlendMode::None)
1604                                    } else {
1605                                        (PrimitiveOpacity::translucent(), BlendMode::PremultipliedAlpha)
1606                                    };
1607
1608                                    self.add_segmented_prim_to_batch(
1609                                        None,
1610                                        opacity,
1611                                        &batch_params,
1612                                        blend_mode,
1613                                        batch_features,
1614                                        brush_flags,
1615                                        EdgeAaSegmentMask::all(),
1616                                        prim_header_index,
1617                                        bounding_rect,
1618                                        transform_kind,
1619                                        z_id,
1620                                        prim_info.clip_task_index,
1621                                        ctx,
1622                                        render_tasks,
1623                                    );
1624                                }
1625                            }
1626                        }
1627                        PictureCompositeMode::SvgFilter(..) => {
1628                            let kind = BatchKind::Brush(
1629                                BrushBatchKind::Image(ImageBufferKind::Texture2D)
1630                            );
1631                            let key = BatchKey::new(
1632                                kind,
1633                                blend_mode,
1634                                textures,
1635                            );
1636                            let prim_header_index = prim_headers.push(
1637                                &prim_header,
1638                                z_id,
1639                                self.batcher.render_task_address,
1640                                ImageBrushData {
1641                                    color_mode: ShaderColorMode::Image,
1642                                    alpha_type: AlphaType::PremultipliedAlpha,
1643                                    raster_space: RasterizationSpace::Screen,
1644                                    opacity: 1.0,
1645                                }.encode(),
1646                            );
1647
1648                            self.add_brush_instance_to_batches(
1649                                key,
1650                                batch_features,
1651                                bounding_rect,
1652                                z_id,
1653                                INVALID_SEGMENT_INDEX,
1654                                EdgeAaSegmentMask::all(),
1655                                clip_task_address,
1656                                brush_flags,
1657                                prim_header_index,
1658                                uv_rect_address.as_int(),
1659                            );
1660                        }
1661                        PictureCompositeMode::SVGFEGraph(..) => {
1662                            let kind = BatchKind::Brush(
1663                                BrushBatchKind::Image(ImageBufferKind::Texture2D)
1664                            );
1665                            let key = BatchKey::new(
1666                                kind,
1667                                blend_mode,
1668                                textures,
1669                            );
1670                            let prim_header_index = prim_headers.push(
1671                                &prim_header,
1672                                z_id,
1673                                self.batcher.render_task_address,
1674                                ImageBrushData {
1675                                    color_mode: ShaderColorMode::Image,
1676                                    alpha_type: AlphaType::PremultipliedAlpha,
1677                                    raster_space: RasterizationSpace::Screen,
1678                                    opacity: 1.0,
1679                                }.encode(),
1680                            );
1681
1682                            self.add_brush_instance_to_batches(
1683                                key,
1684                                batch_features,
1685                                bounding_rect,
1686                                z_id,
1687                                INVALID_SEGMENT_INDEX,
1688                                EdgeAaSegmentMask::all(),
1689                                clip_task_address,
1690                                brush_flags,
1691                                prim_header_index,
1692                                uv_rect_address.as_int(),
1693                            );
1694                        }
1695                    }
1696                }
1697                None => {
1698                    unreachable!();
1699                }
1700            }
1701
1702            return;
1703        }
1704
1705        let common_data = ctx.data_stores.as_common_data(prim_instance);
1706
1707        let needs_blending = !common_data.opacity.is_opaque ||
1708            prim_info.clip_task_index != ClipTaskIndex::INVALID ||
1709            transform_kind == TransformedRectKind::Complex ||
1710            is_anti_aliased;
1711
1712        let blend_mode = if needs_blending {
1713            BlendMode::PremultipliedAlpha
1714        } else {
1715            BlendMode::None
1716        };
1717
1718        let segment_instance_index = match prim_instance.kind {
1719            PrimitiveInstanceKind::Rectangle { segment_instance_index, .. }
1720            | PrimitiveInstanceKind::YuvImage { segment_instance_index, .. } => segment_instance_index,
1721            _ => SegmentInstanceIndex::UNUSED,
1722        };
1723
1724        let (prim_cache_address, segments) = if segment_instance_index == SegmentInstanceIndex::UNUSED {
1725            (gpu_cache.try_get_address(&common_data.gpu_cache_handle), None)
1726        } else {
1727            let segment_instance = &ctx.scratch.segment_instances[segment_instance_index];
1728            let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
1729            (Some(gpu_cache.get_address(&segment_instance.gpu_cache_handle)), segments)
1730        };
1731
1732        // The following primitives lower to the image brush shader in the same way.
1733        let img_brush_data = match prim_instance.kind {
1734            PrimitiveInstanceKind::CachedLinearGradient { data_handle, ref visible_tiles_range, .. } => {
1735                let prim_data = &ctx.data_stores.linear_grad[data_handle];
1736                Some((prim_data.src_color, Some(visible_tiles_range), prim_data.brush_segments.as_slice()))
1737            }
1738            PrimitiveInstanceKind::RadialGradient { data_handle, ref visible_tiles_range, .. } => {
1739                let prim_data = &ctx.data_stores.radial_grad[data_handle];
1740                Some((prim_data.src_color, Some(visible_tiles_range), prim_data.brush_segments.as_slice()))
1741            }
1742            PrimitiveInstanceKind::ConicGradient { data_handle, ref visible_tiles_range, .. } => {
1743                let prim_data = &ctx.data_stores.conic_grad[data_handle];
1744                Some((prim_data.src_color, Some(visible_tiles_range), prim_data.brush_segments.as_slice()))
1745            }
1746            PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
1747                let prim_data = &ctx.data_stores.image_border[data_handle];
1748                Some((prim_data.kind.src_color, None, prim_data.kind.brush_segments.as_slice()))
1749            }
1750            _ => None,
1751        };
1752
1753        if let Some((src_color, visible_tiles_range, brush_segments)) = img_brush_data {
1754            let src_color = render_tasks.resolve_location(src_color, gpu_cache);
1755
1756            let (uv_rect_address, texture_source) = match src_color {
1757                Some(src) => src,
1758                None => {
1759                    return;
1760                }
1761            };
1762
1763            let textures = TextureSet::prim_textured(texture_source);
1764
1765            let prim_header = PrimitiveHeader {
1766                local_rect: prim_rect,
1767                local_clip_rect: prim_info.clip_chain.local_clip_rect,
1768                specific_prim_address: gpu_cache.get_address(&common_data.gpu_cache_handle),
1769                transform_id,
1770            };
1771
1772            let prim_user_data = ImageBrushData {
1773                color_mode: ShaderColorMode::Image,
1774                alpha_type: AlphaType::PremultipliedAlpha,
1775                raster_space: RasterizationSpace::Local,
1776                opacity: 1.0,
1777            }.encode();
1778
1779            let batch_kind = BrushBatchKind::Image(texture_source.image_buffer_kind());
1780
1781            if visible_tiles_range.map_or(true, |r| r.is_empty()) {
1782                let batch_params = BrushBatchParameters::shared(
1783                    batch_kind,
1784                    textures,
1785                    prim_user_data,
1786                    uv_rect_address.as_int(),
1787                );
1788
1789                let segments = if brush_segments.is_empty() {
1790                    None
1791                } else {
1792                    Some(&brush_segments[..])
1793                };
1794
1795                let prim_header_index = prim_headers.push(
1796                    &prim_header,
1797                    z_id,
1798                    self.batcher.render_task_address,
1799                    batch_params.prim_user_data,
1800                );
1801
1802                self.add_segmented_prim_to_batch(
1803                    segments,
1804                    common_data.opacity,
1805                    &batch_params,
1806                    blend_mode,
1807                    batch_features,
1808                    brush_flags,
1809                    common_data.edge_aa_mask,
1810                    prim_header_index,
1811                    bounding_rect,
1812                    transform_kind,
1813                    z_id,
1814                    prim_info.clip_task_index,
1815                    ctx,
1816                    render_tasks,
1817                );
1818            } else {
1819                let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range.unwrap()];
1820
1821                let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
1822                    prim_info.clip_task_index,
1823                    render_tasks,
1824                ).unwrap();
1825
1826                let batch_key = BatchKey {
1827                    blend_mode,
1828                    kind: BatchKind::Brush(batch_kind),
1829                    textures: BatchTextures {
1830                        input: textures,
1831                        clip_mask,
1832                    },
1833                };
1834
1835                for tile in visible_tiles {
1836                    let tile_prim_header = PrimitiveHeader {
1837                        local_rect: tile.local_rect,
1838                        local_clip_rect: tile.local_clip_rect,
1839                        ..prim_header
1840                    };
1841                    let prim_header_index = prim_headers.push(
1842                        &tile_prim_header,
1843                        z_id,
1844                        self.batcher.render_task_address,
1845                        prim_user_data,
1846                    );
1847
1848                    self.add_brush_instance_to_batches(
1849                        batch_key,
1850                        batch_features,
1851                        bounding_rect,
1852                        z_id,
1853                        INVALID_SEGMENT_INDEX,
1854                        common_data.edge_aa_mask,
1855                        clip_task_address,
1856                        brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
1857                        prim_header_index,
1858                        uv_rect_address.as_int(),
1859                    );
1860                }
1861            }
1862
1863            return;
1864        }
1865
1866        match prim_instance.kind {
1867            // Handled above.
1868            PrimitiveInstanceKind::Picture { .. } => {}
1869            PrimitiveInstanceKind::CachedLinearGradient { .. } => { }
1870            PrimitiveInstanceKind::RadialGradient { .. } => { }
1871            PrimitiveInstanceKind::ConicGradient { .. } => { }
1872            PrimitiveInstanceKind::ImageBorder { .. } => {}
1873            PrimitiveInstanceKind::BoxShadow { .. } => {
1874                unreachable!("BUG: Should not hit box-shadow here as they are handled by quad infra");
1875            }
1876            PrimitiveInstanceKind::Clear { .. } => {
1877                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1878                    prim_info.clip_task_index,
1879                    render_tasks,
1880                ).unwrap();
1881
1882                // TODO(gw): We can abstract some of the common code below into
1883                //           helper methods, as we port more primitives to make
1884                //           use of interning.
1885
1886                let prim_header = PrimitiveHeader {
1887                    local_rect: prim_rect,
1888                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
1889                    specific_prim_address: prim_cache_address.unwrap(),
1890                    transform_id,
1891                };
1892
1893                let prim_header_index = prim_headers.push(
1894                    &prim_header,
1895                    z_id,
1896                    self.batcher.render_task_address,
1897                    [get_shader_opacity(1.0), 0, 0, 0],
1898                );
1899
1900                let batch_key = BatchKey {
1901                    blend_mode: BlendMode::PremultipliedDestOut,
1902                    kind: BatchKind::Brush(BrushBatchKind::Solid),
1903                    textures: BatchTextures::prim_untextured(clip_mask_texture_id),
1904                };
1905
1906                self.add_brush_instance_to_batches(
1907                    batch_key,
1908                    batch_features,
1909                    bounding_rect,
1910                    z_id,
1911                    INVALID_SEGMENT_INDEX,
1912                    common_data.edge_aa_mask,
1913                    clip_task_address,
1914                    brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
1915                    prim_header_index,
1916                    0,
1917                );
1918            }
1919            PrimitiveInstanceKind::NormalBorder { data_handle, ref render_task_ids, .. } => {
1920                let prim_data = &ctx.data_stores.normal_border[data_handle];
1921                let task_ids = &ctx.scratch.border_cache_handles[*render_task_ids];
1922                let mut segment_data: SmallVec<[SegmentInstanceData; 8]> = SmallVec::new();
1923
1924                // Collect the segment instance data from each render
1925                // task for each valid edge / corner of the border.
1926
1927                for task_id in task_ids {
1928                    if let Some((uv_rect_address, texture)) = render_tasks.resolve_location(*task_id, gpu_cache) {
1929                        segment_data.push(
1930                            SegmentInstanceData {
1931                                textures: TextureSet::prim_textured(texture),
1932                                specific_resource_address: uv_rect_address.as_int(),
1933                            }
1934                        );
1935                    }
1936                }
1937
1938                // TODO: it would be less error-prone to get this info from the texture cache.
1939                let image_buffer_kind = ImageBufferKind::Texture2D;
1940
1941                let prim_header = PrimitiveHeader {
1942                    local_rect: prim_rect,
1943                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
1944                    specific_prim_address: prim_cache_address.unwrap(),
1945                    transform_id,
1946                };
1947
1948                let batch_params = BrushBatchParameters::instanced(
1949                    BrushBatchKind::Image(image_buffer_kind),
1950                    ImageBrushData {
1951                        color_mode: ShaderColorMode::Image,
1952                        alpha_type: AlphaType::PremultipliedAlpha,
1953                        raster_space: RasterizationSpace::Local,
1954                        opacity: 1.0,
1955                    }.encode(),
1956                    segment_data,
1957                );
1958
1959                let prim_header_index = prim_headers.push(
1960                    &prim_header,
1961                    z_id,
1962                    self.batcher.render_task_address,
1963                    batch_params.prim_user_data,
1964                );
1965
1966                let border_data = &prim_data.kind;
1967                self.add_segmented_prim_to_batch(
1968                    Some(border_data.brush_segments.as_slice()),
1969                    common_data.opacity,
1970                    &batch_params,
1971                    blend_mode,
1972                    batch_features,
1973                    brush_flags,
1974                    common_data.edge_aa_mask,
1975                    prim_header_index,
1976                    bounding_rect,
1977                    transform_kind,
1978                    z_id,
1979                    prim_info.clip_task_index,
1980                    ctx,
1981                    render_tasks,
1982                );
1983            }
1984            PrimitiveInstanceKind::TextRun { data_handle, run_index, .. } => {
1985                let run = &ctx.prim_store.text_runs[run_index];
1986                let subpx_dir = run.used_font.get_subpx_dir();
1987
1988                // The GPU cache data is stored in the template and reused across
1989                // frames and display lists.
1990                let prim_data = &ctx.data_stores.text_run[data_handle];
1991
1992                // The local prim rect is only informative for text primitives, as
1993                // thus is not directly necessary for any drawing of the text run.
1994                // However the glyph offsets are relative to the prim rect origin
1995                // less the unsnapped reference frame offset. We also want the
1996                // the snapped reference frame offset, because cannot recalculate
1997                // it as it ignores the animated components for the transform. As
1998                // such, we adjust the prim rect origin here, and replace the size
1999                // with the unsnapped and snapped offsets respectively. This has
2000                // the added bonus of avoiding quantization effects when storing
2001                // floats in the extra header integers.
2002                let prim_header = PrimitiveHeader {
2003                    local_rect: LayoutRect {
2004                        min: prim_rect.min - run.reference_frame_relative_offset,
2005                        max: run.snapped_reference_frame_relative_offset.to_point(),
2006                    },
2007                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
2008                    specific_prim_address: prim_cache_address.unwrap(),
2009                    transform_id,
2010                };
2011
2012                let glyph_keys = &ctx.scratch.glyph_keys[run.glyph_keys_range];
2013                let prim_header_index = prim_headers.push(
2014                    &prim_header,
2015                    z_id,
2016                    self.batcher.render_task_address,
2017                    [
2018                        (run.raster_scale * 65535.0).round() as i32,
2019                        0,
2020                        0,
2021                        0,
2022                    ],
2023                );
2024                let base_instance = GlyphInstance::new(
2025                    prim_header_index,
2026                );
2027                let batcher = &mut self.batcher;
2028
2029                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2030                    prim_info.clip_task_index,
2031                    render_tasks,
2032                ).unwrap();
2033
2034                // The run.used_font.clone() is here instead of instead of inline in the `fetch_glyph`
2035                // function call to work around a miscompilation.
2036                // https://github.com/rust-lang/rust/issues/80111
2037                let font = run.used_font.clone();
2038                ctx.resource_cache.fetch_glyphs(
2039                    font,
2040                    &glyph_keys,
2041                    &mut self.glyph_fetch_buffer,
2042                    gpu_cache,
2043                    |texture_id, glyph_format, glyphs| {
2044                        debug_assert_ne!(texture_id, TextureSource::Invalid);
2045
2046                        let subpx_dir = subpx_dir.limit_by(glyph_format);
2047
2048                        let textures = BatchTextures::prim_textured(
2049                            texture_id,
2050                            clip_mask_texture_id,
2051                        );
2052
2053                        let kind = BatchKind::TextRun(glyph_format);
2054
2055                        let (blend_mode, color_mode) = match glyph_format {
2056                            GlyphFormat::Subpixel |
2057                            GlyphFormat::TransformedSubpixel => {
2058                                debug_assert!(ctx.use_dual_source_blending);
2059                                (
2060                                    BlendMode::SubpixelDualSource,
2061                                    ShaderColorMode::SubpixelDualSource,
2062                                )
2063                            }
2064                            GlyphFormat::Alpha |
2065                            GlyphFormat::TransformedAlpha |
2066                            GlyphFormat::Bitmap => {
2067                                (
2068                                    BlendMode::PremultipliedAlpha,
2069                                    ShaderColorMode::Alpha,
2070                                )
2071                            }
2072                            GlyphFormat::ColorBitmap => {
2073                                (
2074                                    BlendMode::PremultipliedAlpha,
2075                                    if run.shadow {
2076                                        // Ignore color and only sample alpha when shadowing.
2077                                        ShaderColorMode::BitmapShadow
2078                                    } else {
2079                                        ShaderColorMode::ColorBitmap
2080                                    },
2081                                )
2082                            }
2083                        };
2084
2085                        // Calculate a tighter bounding rect of just the glyphs passed to this
2086                        // callback from request_glyphs(), rather than using the bounds of the
2087                        // entire text run. This improves batching when glyphs are fragmented
2088                        // over multiple textures in the texture cache.
2089                        // This code is taken from the ps_text_run shader.
2090                        let tight_bounding_rect = {
2091                            let snap_bias = match subpx_dir {
2092                                SubpixelDirection::None => DeviceVector2D::new(0.5, 0.5),
2093                                SubpixelDirection::Horizontal => DeviceVector2D::new(0.125, 0.5),
2094                                SubpixelDirection::Vertical => DeviceVector2D::new(0.5, 0.125),
2095                                SubpixelDirection::Mixed => DeviceVector2D::new(0.125, 0.125),
2096                            };
2097                            let text_offset = prim_header.local_rect.max.to_vector();
2098
2099                            let pic_bounding_rect = if run.used_font.flags.contains(FontInstanceFlags::TRANSFORM_GLYPHS) {
2100                                let mut device_bounding_rect = DeviceRect::default();
2101
2102                                let glyph_transform = ctx.spatial_tree.get_relative_transform(
2103                                    prim_spatial_node_index,
2104                                    root_spatial_node_index,
2105                                ).into_transform()
2106                                    .with_destination::<WorldPixel>()
2107                                    .then(&euclid::Transform3D::from_scale(ctx.global_device_pixel_scale));
2108
2109                                let glyph_translation = DeviceVector2D::new(glyph_transform.m41, glyph_transform.m42);
2110
2111                                let mut use_tight_bounding_rect = true;
2112                                for glyph in glyphs {
2113                                    let glyph_offset = prim_data.glyphs[glyph.index_in_text_run as usize].point + prim_header.local_rect.min.to_vector();
2114
2115                                    let transformed_offset = match glyph_transform.transform_point2d(glyph_offset) {
2116                                        Some(transformed_offset) => transformed_offset,
2117                                        None => {
2118                                            use_tight_bounding_rect = false;
2119                                            break;
2120                                        }
2121                                    };
2122                                    let raster_glyph_offset = (transformed_offset + snap_bias).floor();
2123                                    let raster_text_offset = (
2124                                        glyph_transform.transform_vector2d(text_offset) +
2125                                        glyph_translation +
2126                                        DeviceVector2D::new(0.5, 0.5)
2127                                    ).floor() - glyph_translation;
2128
2129                                    let device_glyph_rect = DeviceRect::from_origin_and_size(
2130                                        glyph.offset + raster_glyph_offset.to_vector() + raster_text_offset,
2131                                        glyph.size.to_f32(),
2132                                    );
2133
2134                                    device_bounding_rect = device_bounding_rect.union(&device_glyph_rect);
2135                                }
2136
2137                                if use_tight_bounding_rect {
2138                                    let map_device_to_surface: SpaceMapper<PicturePixel, DevicePixel> = SpaceMapper::new_with_target(
2139                                        root_spatial_node_index,
2140                                        surface_spatial_node_index,
2141                                        device_bounding_rect,
2142                                        ctx.spatial_tree,
2143                                    );
2144
2145                                    match map_device_to_surface.unmap(&device_bounding_rect) {
2146                                        Some(r) => r.intersection(bounding_rect),
2147                                        None => Some(*bounding_rect),
2148                                    }
2149                                } else {
2150                                    Some(*bounding_rect)
2151                                }
2152                            } else {
2153                                let mut local_bounding_rect = LayoutRect::default();
2154
2155                                let glyph_raster_scale = run.raster_scale * ctx.global_device_pixel_scale.get();
2156
2157                                for glyph in glyphs {
2158                                    let glyph_offset = prim_data.glyphs[glyph.index_in_text_run as usize].point + prim_header.local_rect.min.to_vector();
2159                                    let glyph_scale = LayoutToDeviceScale::new(glyph_raster_scale / glyph.scale);
2160                                    let raster_glyph_offset = (glyph_offset * LayoutToDeviceScale::new(glyph_raster_scale) + snap_bias).floor() / glyph.scale;
2161                                    let local_glyph_rect = LayoutRect::from_origin_and_size(
2162                                        (glyph.offset + raster_glyph_offset.to_vector()) / glyph_scale + text_offset,
2163                                        glyph.size.to_f32() / glyph_scale,
2164                                    );
2165
2166                                    local_bounding_rect = local_bounding_rect.union(&local_glyph_rect);
2167                                }
2168
2169                                let map_prim_to_surface: SpaceMapper<LayoutPixel, PicturePixel> = SpaceMapper::new_with_target(
2170                                    surface_spatial_node_index,
2171                                    prim_spatial_node_index,
2172                                    *bounding_rect,
2173                                    ctx.spatial_tree,
2174                                );
2175                                map_prim_to_surface.map(&local_bounding_rect)
2176                            };
2177
2178                            let intersected = match pic_bounding_rect {
2179                                // The text run may have been clipped, for example if part of it is offscreen.
2180                                // So intersect our result with the original bounding rect.
2181                                Some(rect) => rect.intersection(bounding_rect).unwrap_or_else(PictureRect::zero),
2182                                // If space mapping went off the rails, fall back to the old behavior.
2183                                //TODO: consider skipping the glyph run completely in this case.
2184                                None => *bounding_rect,
2185                            };
2186
2187                            intersected
2188                        };
2189
2190                        let key = BatchKey::new(kind, blend_mode, textures);
2191
2192                        let batch = batcher.alpha_batch_list.set_params_and_get_batch(
2193                            key,
2194                            batch_features,
2195                            &tight_bounding_rect,
2196                            z_id,
2197                        );
2198
2199                        batch.reserve(glyphs.len());
2200                        for glyph in glyphs {
2201                            batch.push(base_instance.build(
2202                                clip_task_address,
2203                                subpx_dir,
2204                                glyph.index_in_text_run,
2205                                glyph.uv_rect_address,
2206                                color_mode,
2207                            ));
2208                        }
2209                    },
2210                );
2211            }
2212            PrimitiveInstanceKind::LineDecoration { ref render_task, .. } => {
2213                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2214                    prim_info.clip_task_index,
2215                    render_tasks,
2216                ).unwrap();
2217
2218                let (batch_kind, textures, prim_user_data, specific_resource_address) = match render_task {
2219                    Some(task_id) => {
2220                        let (uv_rect_address, texture) = render_tasks.resolve_location(*task_id, gpu_cache).unwrap();
2221                        let textures = BatchTextures::prim_textured(
2222                            texture,
2223                            clip_mask_texture_id,
2224                        );
2225                        (
2226                            BrushBatchKind::Image(texture.image_buffer_kind()),
2227                            textures,
2228                            ImageBrushData {
2229                                color_mode: ShaderColorMode::Image,
2230                                alpha_type: AlphaType::PremultipliedAlpha,
2231                                raster_space: RasterizationSpace::Local,
2232                                opacity: 1.0,
2233                            }.encode(),
2234                            uv_rect_address.as_int(),
2235                        )
2236                    }
2237                    None => {
2238                        (
2239                            BrushBatchKind::Solid,
2240                            BatchTextures::prim_untextured(clip_mask_texture_id),
2241                            [get_shader_opacity(1.0), 0, 0, 0],
2242                            0,
2243                        )
2244                    }
2245                };
2246
2247                let prim_header = PrimitiveHeader {
2248                    local_rect: prim_rect,
2249                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
2250                    specific_prim_address: prim_cache_address.unwrap(),
2251                    transform_id,
2252                };
2253
2254                let prim_header_index = prim_headers.push(
2255                    &prim_header,
2256                    z_id,
2257                    self.batcher.render_task_address,
2258                    prim_user_data,
2259                );
2260
2261                let batch_key = BatchKey {
2262                    blend_mode,
2263                    kind: BatchKind::Brush(batch_kind),
2264                    textures,
2265                };
2266
2267                self.add_brush_instance_to_batches(
2268                    batch_key,
2269                    batch_features,
2270                    bounding_rect,
2271                    z_id,
2272                    INVALID_SEGMENT_INDEX,
2273                    common_data.edge_aa_mask,
2274                    clip_task_address,
2275                    brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
2276                    prim_header_index,
2277                    specific_resource_address,
2278                );
2279            }
2280            PrimitiveInstanceKind::Rectangle { use_legacy_path, .. } => {
2281                debug_assert!(use_legacy_path);
2282                let batch_params = BrushBatchParameters::shared(
2283                    BrushBatchKind::Solid,
2284                    TextureSet::UNTEXTURED,
2285                    [get_shader_opacity(1.0), 0, 0, 0],
2286                    0,
2287                );
2288
2289                let prim_header = PrimitiveHeader {
2290                    local_rect: prim_rect,
2291                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
2292                    specific_prim_address: prim_cache_address.unwrap(),
2293                    transform_id,
2294                };
2295
2296                let prim_header_index = prim_headers.push(
2297                    &prim_header,
2298                    z_id,
2299                    self.batcher.render_task_address,
2300                    batch_params.prim_user_data,
2301                );
2302
2303                self.add_segmented_prim_to_batch(
2304                    segments,
2305                    common_data.opacity,
2306                    &batch_params,
2307                    blend_mode,
2308                    batch_features,
2309                    brush_flags,
2310                    common_data.edge_aa_mask,
2311                    prim_header_index,
2312                    bounding_rect,
2313                    transform_kind,
2314                    z_id,
2315                    prim_info.clip_task_index,
2316                    ctx,
2317                    render_tasks,
2318                );
2319            }
2320            PrimitiveInstanceKind::YuvImage { data_handle, segment_instance_index, compositor_surface_kind, .. } => {
2321                if compositor_surface_kind.needs_cutout() {
2322                    self.add_compositor_surface_cutout(
2323                        prim_rect,
2324                        prim_info.clip_chain.local_clip_rect,
2325                        prim_info.clip_task_index,
2326                        transform_id,
2327                        z_id,
2328                        bounding_rect,
2329                        ctx,
2330                        gpu_cache,
2331                        render_tasks,
2332                        prim_headers,
2333                    );
2334
2335                    return;
2336                }
2337
2338                let yuv_image_data = &ctx.data_stores.yuv_image[data_handle].kind;
2339                let mut textures = TextureSet::UNTEXTURED;
2340                let mut uv_rect_addresses = [0; 3];
2341
2342                //yuv channel
2343                let channel_count = yuv_image_data.format.get_plane_num();
2344                debug_assert!(channel_count <= 3);
2345                for channel in 0 .. channel_count {
2346
2347                    let src_channel = render_tasks.resolve_location(yuv_image_data.src_yuv[channel], gpu_cache);
2348
2349                    let (uv_rect_address, texture_source) = match src_channel {
2350                        Some(src) => src,
2351                        None => {
2352                            warn!("Warnings: skip a PrimitiveKind::YuvImage");
2353                            return;
2354                        }
2355                    };
2356
2357                    textures.colors[channel] = texture_source;
2358                    uv_rect_addresses[channel] = uv_rect_address.as_int();
2359                }
2360
2361                // All yuv textures should be the same type.
2362                let buffer_kind = textures.colors[0].image_buffer_kind();
2363                assert!(
2364                    textures.colors[1 .. yuv_image_data.format.get_plane_num()]
2365                        .iter()
2366                        .all(|&tid| buffer_kind == tid.image_buffer_kind())
2367                );
2368
2369                let kind = BrushBatchKind::YuvImage(
2370                    buffer_kind,
2371                    yuv_image_data.format,
2372                    yuv_image_data.color_depth,
2373                    yuv_image_data.color_space,
2374                    yuv_image_data.color_range,
2375                );
2376
2377                let batch_params = BrushBatchParameters::shared(
2378                    kind,
2379                    textures,
2380                    [
2381                        uv_rect_addresses[0],
2382                        uv_rect_addresses[1],
2383                        uv_rect_addresses[2],
2384                        0,
2385                    ],
2386                    0,
2387                );
2388
2389                debug_assert_ne!(segment_instance_index, SegmentInstanceIndex::INVALID);
2390
2391                let prim_header = PrimitiveHeader {
2392                    local_rect: prim_rect,
2393                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
2394                    specific_prim_address: prim_cache_address.unwrap(),
2395                    transform_id,
2396                };
2397
2398                let prim_header_index = prim_headers.push(
2399                    &prim_header,
2400                    z_id,
2401                    self.batcher.render_task_address,
2402                    batch_params.prim_user_data,
2403                );
2404
2405                self.add_segmented_prim_to_batch(
2406                    segments,
2407                    common_data.opacity,
2408                    &batch_params,
2409                    blend_mode,
2410                    batch_features,
2411                    brush_flags,
2412                    common_data.edge_aa_mask,
2413                    prim_header_index,
2414                    bounding_rect,
2415                    transform_kind,
2416                    z_id,
2417                    prim_info.clip_task_index,
2418                    ctx,
2419                    render_tasks,
2420                );
2421            }
2422            PrimitiveInstanceKind::Image { data_handle, image_instance_index, compositor_surface_kind, .. } => {
2423                if compositor_surface_kind.needs_cutout() {
2424                    self.add_compositor_surface_cutout(
2425                        prim_rect,
2426                        prim_info.clip_chain.local_clip_rect,
2427                        prim_info.clip_task_index,
2428                        transform_id,
2429                        z_id,
2430                        bounding_rect,
2431                        ctx,
2432                        gpu_cache,
2433                        render_tasks,
2434                        prim_headers,
2435                    );
2436
2437                    return;
2438                }
2439
2440                let image_data = &ctx.data_stores.image[data_handle].kind;
2441                let image_instance = &ctx.prim_store.images[image_instance_index];
2442                let prim_user_data = ImageBrushData {
2443                    color_mode: ShaderColorMode::Image,
2444                    alpha_type: image_data.alpha_type,
2445                    raster_space: RasterizationSpace::Local,
2446                    opacity: 1.0,
2447                }.encode();
2448
2449                let blend_mode = if needs_blending {
2450                    match image_data.alpha_type {
2451                        AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
2452                        AlphaType::Alpha => BlendMode::Alpha,
2453                    }
2454                } else {
2455                    BlendMode::None
2456                };
2457
2458                if image_instance.visible_tiles.is_empty() {
2459                    if cfg!(debug_assertions) {
2460                        match ctx.resource_cache.get_image_properties(image_data.key) {
2461                            Some(ImageProperties { tiling: None, .. }) | None => (),
2462                            other => panic!("Non-tiled image with no visible images detected! Properties {:?}", other),
2463                        }
2464                    }
2465
2466                    let src_color = render_tasks.resolve_location(image_instance.src_color, gpu_cache);
2467
2468                    let (uv_rect_address, texture_source) = match src_color {
2469                        Some(src) => src,
2470                        None => {
2471                            return;
2472                        }
2473                    };
2474
2475                    let batch_params = BrushBatchParameters::shared(
2476                        BrushBatchKind::Image(texture_source.image_buffer_kind()),
2477                        TextureSet::prim_textured(texture_source),
2478                        prim_user_data,
2479                        uv_rect_address.as_int(),
2480                    );
2481
2482                    debug_assert_ne!(image_instance.segment_instance_index, SegmentInstanceIndex::INVALID);
2483                    let (prim_cache_address, segments) = if image_instance.segment_instance_index == SegmentInstanceIndex::UNUSED {
2484                        (prim_cache_address.unwrap(), None)
2485                    } else {
2486                        let segment_instance = &ctx.scratch.segment_instances[image_instance.segment_instance_index];
2487                        let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
2488                        (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
2489                    };
2490
2491                    let local_rect = image_instance.adjustment.map_local_rect(&prim_rect);
2492                    let local_clip_rect = image_instance.tight_local_clip_rect
2493                        .intersection_unchecked(&local_rect);
2494
2495                    let prim_header = PrimitiveHeader {
2496                        local_rect,
2497                        local_clip_rect,
2498                        specific_prim_address: prim_cache_address,
2499                        transform_id,
2500                    };
2501
2502                    let prim_header_index = prim_headers.push(
2503                        &prim_header,
2504                        z_id,
2505                        self.batcher.render_task_address,
2506                        batch_params.prim_user_data,
2507                    );
2508
2509                    let brush_flags = match image_instance.normalized_uvs {
2510                        true => brush_flags | BrushFlags::NORMALIZED_UVS,
2511                        false => brush_flags,
2512                    };
2513
2514                    self.add_segmented_prim_to_batch(
2515                        segments,
2516                        common_data.opacity,
2517                        &batch_params,
2518                        blend_mode,
2519                        batch_features,
2520                        brush_flags,
2521                        common_data.edge_aa_mask,
2522                        prim_header_index,
2523                        bounding_rect,
2524                        transform_kind,
2525                        z_id,
2526                        prim_info.clip_task_index,
2527                        ctx,
2528                        render_tasks,
2529                    );
2530                } else {
2531                    const VECS_PER_SPECIFIC_BRUSH: usize = 3;
2532                    let max_tiles_per_header = (MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_SPECIFIC_BRUSH) / VECS_PER_SEGMENT;
2533
2534                    let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2535                        prim_info.clip_task_index,
2536                        render_tasks,
2537                    ).unwrap();
2538
2539                    // use temporary block storage since we don't know the number of visible tiles beforehand
2540                    let mut gpu_blocks = Vec::<GpuBlockData>::with_capacity(3 + max_tiles_per_header * 2);
2541                    for chunk in image_instance.visible_tiles.chunks(max_tiles_per_header) {
2542                        gpu_blocks.clear();
2543                        gpu_blocks.push(image_data.color.premultiplied().into()); //color
2544                        gpu_blocks.push(PremultipliedColorF::WHITE.into()); //bg color
2545                        gpu_blocks.push([-1.0, 0.0, 0.0, 0.0].into()); //stretch size
2546                        // negative first value makes the shader code ignore it and use the local size instead
2547                        for tile in chunk {
2548                            let tile_rect = tile.local_rect.translate(-prim_rect.min.to_vector());
2549                            gpu_blocks.push(tile_rect.into());
2550                            gpu_blocks.push(GpuBlockData::EMPTY);
2551                        }
2552
2553                        let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
2554                        let prim_header = PrimitiveHeader {
2555                            local_rect: prim_rect,
2556                            local_clip_rect: image_instance.tight_local_clip_rect,
2557                            specific_prim_address: gpu_cache.get_address(&gpu_handle),
2558                            transform_id,
2559                        };
2560                        let prim_header_index = prim_headers.push(
2561                            &prim_header,
2562                            z_id,
2563                            self.batcher.render_task_address,
2564                            prim_user_data,
2565                        );
2566
2567                        for (i, tile) in chunk.iter().enumerate() {
2568                            let (uv_rect_address, texture) = match render_tasks.resolve_location(tile.src_color, gpu_cache) {
2569                                Some(result) => result,
2570                                None => {
2571                                    return;
2572                                }
2573                            };
2574
2575                            let textures = BatchTextures::prim_textured(
2576                                texture,
2577                                clip_mask_texture_id,
2578                            );
2579
2580                            let batch_key = BatchKey {
2581                                blend_mode,
2582                                kind: BatchKind::Brush(BrushBatchKind::Image(texture.image_buffer_kind())),
2583                                textures,
2584                            };
2585
2586                            self.add_brush_instance_to_batches(
2587                                batch_key,
2588                                batch_features,
2589                                bounding_rect,
2590                                z_id,
2591                                i as i32,
2592                                tile.edge_flags,
2593                                clip_task_address,
2594                                brush_flags | BrushFlags::SEGMENT_RELATIVE | BrushFlags::PERSPECTIVE_INTERPOLATION,
2595                                prim_header_index,
2596                                uv_rect_address.as_int(),
2597                            );
2598                        }
2599                    }
2600                }
2601            }
2602            PrimitiveInstanceKind::LinearGradient { data_handle, ref visible_tiles_range, .. } => {
2603                let prim_data = &ctx.data_stores.linear_grad[data_handle];
2604
2605                let mut prim_header = PrimitiveHeader {
2606                    local_rect: prim_rect,
2607                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
2608                    specific_prim_address: GpuCacheAddress::INVALID,
2609                    transform_id,
2610                };
2611
2612                let user_data = [extra_prim_gpu_address.unwrap(), 0, 0, 0];
2613
2614                if visible_tiles_range.is_empty() {
2615                    let batch_params = BrushBatchParameters::shared(
2616                        BrushBatchKind::LinearGradient,
2617                        TextureSet::UNTEXTURED,
2618                        user_data,
2619                        0,
2620                    );
2621
2622                    prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
2623
2624                    let prim_header_index = prim_headers.push(
2625                        &prim_header,
2626                        z_id,
2627                        self.batcher.render_task_address,
2628                        user_data,
2629                    );
2630
2631                    let segments = if prim_data.brush_segments.is_empty() {
2632                        None
2633                    } else {
2634                        Some(prim_data.brush_segments.as_slice())
2635                    };
2636                    self.add_segmented_prim_to_batch(
2637                        segments,
2638                        prim_data.opacity,
2639                        &batch_params,
2640                        blend_mode,
2641                        batch_features,
2642                        brush_flags,
2643                        prim_data.edge_aa_mask,
2644                        prim_header_index,
2645                        bounding_rect,
2646                        transform_kind,
2647                        z_id,
2648                        prim_info.clip_task_index,
2649                        ctx,
2650                        render_tasks,
2651                    );
2652                } else {
2653                    let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
2654
2655                    let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2656                        prim_info.clip_task_index,
2657                        render_tasks,
2658                    ).unwrap();
2659
2660                    let key = BatchKey {
2661                        blend_mode,
2662                        kind: BatchKind::Brush(BrushBatchKind::LinearGradient),
2663                        textures: BatchTextures::prim_untextured(clip_mask_texture_id),
2664                    };
2665
2666                    for tile in visible_tiles {
2667                        let tile_prim_header = PrimitiveHeader {
2668                            specific_prim_address: gpu_cache.get_address(&tile.handle),
2669                            local_rect: tile.local_rect,
2670                            local_clip_rect: tile.local_clip_rect,
2671                            ..prim_header
2672                        };
2673                        let prim_header_index = prim_headers.push(
2674                            &tile_prim_header,
2675                            z_id,
2676                            self.batcher.render_task_address,
2677                            user_data,
2678                        );
2679
2680                        self.add_brush_instance_to_batches(
2681                            key,
2682                            batch_features,
2683                            bounding_rect,
2684                            z_id,
2685                            INVALID_SEGMENT_INDEX,
2686                            prim_data.edge_aa_mask,
2687                            clip_task_address,
2688                            brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
2689                            prim_header_index,
2690                            0,
2691                        );
2692                    }
2693                }
2694            }
2695            PrimitiveInstanceKind::BackdropCapture { .. } => {}
2696            PrimitiveInstanceKind::BackdropRender { pic_index, .. } => {
2697                let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_image_handle);
2698                let blend_mode = BlendMode::PremultipliedAlpha;
2699                let pic_task_id = ctx.prim_store.pictures[pic_index.0].primary_render_task_id;
2700
2701                let prim_header = PrimitiveHeader {
2702                    local_rect: prim_rect,
2703                    local_clip_rect: prim_info.clip_chain.local_clip_rect,
2704                    specific_prim_address: prim_cache_address,
2705                    transform_id,
2706                };
2707
2708                let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2709                    prim_info.clip_task_index,
2710                    render_tasks,
2711                ).unwrap();
2712
2713                let kind = BatchKind::Brush(
2714                    BrushBatchKind::Image(ImageBufferKind::Texture2D)
2715                );
2716                let (_, texture) = render_tasks.resolve_location(
2717                    pic_task_id,
2718                    gpu_cache,
2719                ).unwrap();
2720                let textures = BatchTextures::prim_textured(
2721                    texture,
2722                    clip_mask_texture_id,
2723                );
2724                let key = BatchKey::new(
2725                    kind,
2726                    blend_mode,
2727                    textures,
2728                );
2729                let prim_header_index = prim_headers.push(
2730                    &prim_header,
2731                    z_id,
2732                    self.batcher.render_task_address,
2733                    ImageBrushData {
2734                        color_mode: ShaderColorMode::Image,
2735                        alpha_type: AlphaType::PremultipliedAlpha,
2736                        raster_space: RasterizationSpace::Screen,
2737                        opacity: 1.0,
2738                    }.encode(),
2739                );
2740
2741                let pic_task = &render_tasks[pic_task_id.unwrap()];
2742                let pic_info = match pic_task.kind {
2743                    RenderTaskKind::Picture(ref info) => info,
2744                    _ => panic!("bug: not a picture"),
2745                };
2746                let target_rect = pic_task.get_target_rect();
2747
2748                let backdrop_rect = DeviceRect::from_origin_and_size(
2749                    pic_info.content_origin,
2750                    target_rect.size().to_f32(),
2751                );
2752
2753                let map_prim_to_backdrop = SpaceMapper::new_with_target(
2754                    pic_info.surface_spatial_node_index,
2755                    prim_spatial_node_index,
2756                    WorldRect::max_rect(),
2757                    ctx.spatial_tree,
2758                );
2759
2760                let points = [
2761                    map_prim_to_backdrop.map_point(prim_rect.top_left()),
2762                    map_prim_to_backdrop.map_point(prim_rect.top_right()),
2763                    map_prim_to_backdrop.map_point(prim_rect.bottom_left()),
2764                    map_prim_to_backdrop.map_point(prim_rect.bottom_right()),
2765                ];
2766
2767                if points.iter().any(|p| p.is_none()) {
2768                    return;
2769                }
2770
2771                let uvs = [
2772                    calculate_screen_uv(points[0].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
2773                    calculate_screen_uv(points[1].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
2774                    calculate_screen_uv(points[2].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
2775                    calculate_screen_uv(points[3].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
2776                ];
2777
2778                // TODO (gw): This is a hack that provides the GPU cache blocks for an
2779                //            ImageSource. We should update the GPU cache interfaces to
2780                //            allow pushing per-frame blocks via a request interface.
2781                let gpu_blocks = &[
2782                    GpuBlockData::from([
2783                        target_rect.min.x as f32,
2784                        target_rect.min.y as f32,
2785                        target_rect.max.x as f32,
2786                        target_rect.max.y as f32,
2787                    ]),
2788                    GpuBlockData::from([0.0; 4]),
2789                    GpuBlockData::from(uvs[0]),
2790                    GpuBlockData::from(uvs[1]),
2791                    GpuBlockData::from(uvs[2]),
2792                    GpuBlockData::from(uvs[3]),
2793                ];
2794                let uv_rect_handle = gpu_cache.push_per_frame_blocks(gpu_blocks);
2795
2796                self.add_brush_instance_to_batches(
2797                    key,
2798                    batch_features,
2799                    bounding_rect,
2800                    z_id,
2801                    INVALID_SEGMENT_INDEX,
2802                    EdgeAaSegmentMask::all(),
2803                    clip_task_address,
2804                    brush_flags,
2805                    prim_header_index,
2806                    uv_rect_handle.as_int(gpu_cache),
2807                );
2808            }
2809        }
2810    }
2811
2812    /// Draw a (potentially masked) alpha cutout so that a video underlay will be blended
2813    /// through by the compositor
2814    fn add_compositor_surface_cutout(
2815        &mut self,
2816        prim_rect: LayoutRect,
2817        local_clip_rect: LayoutRect,
2818        clip_task_index: ClipTaskIndex,
2819        transform_id: TransformPaletteId,
2820        z_id: ZBufferId,
2821        bounding_rect: &PictureRect,
2822        ctx: &RenderTargetContext,
2823        gpu_cache: &mut GpuCache,
2824        render_tasks: &RenderTaskGraph,
2825        prim_headers: &mut PrimitiveHeaders,
2826    ) {
2827        let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_black_rect_handle);
2828
2829        let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2830            clip_task_index,
2831            render_tasks,
2832        ).unwrap();
2833
2834        let prim_header = PrimitiveHeader {
2835            local_rect: prim_rect,
2836            local_clip_rect,
2837            specific_prim_address: prim_cache_address,
2838            transform_id,
2839        };
2840
2841        let prim_header_index = prim_headers.push(
2842            &prim_header,
2843            z_id,
2844            self.batcher.render_task_address,
2845            [get_shader_opacity(1.0), 0, 0, 0],
2846        );
2847
2848        let batch_key = BatchKey {
2849            blend_mode: BlendMode::PremultipliedDestOut,
2850            kind: BatchKind::Brush(BrushBatchKind::Solid),
2851            textures: BatchTextures::prim_untextured(clip_mask_texture_id),
2852        };
2853
2854        self.add_brush_instance_to_batches(
2855            batch_key,
2856            BatchFeatures::ALPHA_PASS | BatchFeatures::CLIP_MASK,
2857            bounding_rect,
2858            z_id,
2859            INVALID_SEGMENT_INDEX,
2860            EdgeAaSegmentMask::empty(),
2861            clip_task_address,
2862            BrushFlags::empty(),
2863            prim_header_index,
2864            0,
2865        );
2866    }
2867
2868    /// Add a single segment instance to a batch.
2869    ///
2870    /// `edge_aa_mask` Specifies the edges that are *allowed* to have anti-aliasing, if and only
2871    /// if the segments enable it.
2872    /// In other words passing EdgeAaSegmentFlags::all() does not necessarily mean all edges will
2873    /// be anti-aliased, only that they could be.
2874    fn add_segment_to_batch(
2875        &mut self,
2876        segment: &BrushSegment,
2877        segment_data: &SegmentInstanceData,
2878        segment_index: i32,
2879        batch_kind: BrushBatchKind,
2880        prim_header_index: PrimitiveHeaderIndex,
2881        alpha_blend_mode: BlendMode,
2882        features: BatchFeatures,
2883        brush_flags: BrushFlags,
2884        edge_aa_mask: EdgeAaSegmentMask,
2885        bounding_rect: &PictureRect,
2886        transform_kind: TransformedRectKind,
2887        z_id: ZBufferId,
2888        prim_opacity: PrimitiveOpacity,
2889        clip_task_index: ClipTaskIndex,
2890        ctx: &RenderTargetContext,
2891        render_tasks: &RenderTaskGraph,
2892    ) {
2893        debug_assert!(clip_task_index != ClipTaskIndex::INVALID);
2894
2895        // Get GPU address of clip task for this segment, or None if
2896        // the entire segment is clipped out.
2897        if let Some((clip_task_address, clip_mask)) = ctx.get_clip_task_and_texture(
2898            clip_task_index,
2899            segment_index,
2900            render_tasks,
2901        ) {
2902            // If a got a valid (or OPAQUE) clip task address, add the segment.
2903            let is_inner = segment.edge_flags.is_empty();
2904            let needs_blending = !prim_opacity.is_opaque ||
2905                                 clip_task_address != OPAQUE_TASK_ADDRESS ||
2906                                 (!is_inner && transform_kind == TransformedRectKind::Complex) ||
2907                                 brush_flags.contains(BrushFlags::FORCE_AA);
2908
2909            let textures = BatchTextures {
2910                input: segment_data.textures,
2911                clip_mask,
2912            };
2913
2914            let batch_key = BatchKey {
2915                blend_mode: if needs_blending { alpha_blend_mode } else { BlendMode::None },
2916                kind: BatchKind::Brush(batch_kind),
2917                textures,
2918            };
2919
2920            self.add_brush_instance_to_batches(
2921                batch_key,
2922                features,
2923                bounding_rect,
2924                z_id,
2925                segment_index,
2926                segment.edge_flags & edge_aa_mask,
2927                clip_task_address,
2928                brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION | segment.brush_flags,
2929                prim_header_index,
2930                segment_data.specific_resource_address,
2931            );
2932        }
2933    }
2934
2935    /// Add any segment(s) from a brush to batches.
2936    ///
2937    /// `edge_aa_mask` Specifies the edges that are *allowed* to have anti-aliasing, if and only
2938    /// if the segments enable it.
2939    /// In other words passing EdgeAaSegmentFlags::all() does not necessarily mean all edges will
2940    /// be anti-aliased, only that they could be.
2941    fn add_segmented_prim_to_batch(
2942        &mut self,
2943        brush_segments: Option<&[BrushSegment]>,
2944        prim_opacity: PrimitiveOpacity,
2945        params: &BrushBatchParameters,
2946        blend_mode: BlendMode,
2947        features: BatchFeatures,
2948        brush_flags: BrushFlags,
2949        edge_aa_mask: EdgeAaSegmentMask,
2950        prim_header_index: PrimitiveHeaderIndex,
2951        bounding_rect: &PictureRect,
2952        transform_kind: TransformedRectKind,
2953        z_id: ZBufferId,
2954        clip_task_index: ClipTaskIndex,
2955        ctx: &RenderTargetContext,
2956        render_tasks: &RenderTaskGraph,
2957    ) {
2958        match (brush_segments, &params.segment_data) {
2959            (Some(ref brush_segments), SegmentDataKind::Instanced(ref segment_data)) => {
2960                // In this case, we have both a list of segments, and a list of
2961                // per-segment instance data. Zip them together to build batches.
2962                debug_assert_eq!(brush_segments.len(), segment_data.len());
2963                for (segment_index, (segment, segment_data)) in brush_segments
2964                    .iter()
2965                    .zip(segment_data.iter())
2966                    .enumerate()
2967                {
2968                    self.add_segment_to_batch(
2969                        segment,
2970                        segment_data,
2971                        segment_index as i32,
2972                        params.batch_kind,
2973                        prim_header_index,
2974                        blend_mode,
2975                        features,
2976                        brush_flags,
2977                        edge_aa_mask,
2978                        bounding_rect,
2979                        transform_kind,
2980                        z_id,
2981                        prim_opacity,
2982                        clip_task_index,
2983                        ctx,
2984                        render_tasks,
2985                    );
2986                }
2987            }
2988            (Some(ref brush_segments), SegmentDataKind::Shared(ref segment_data)) => {
2989                // A list of segments, but the per-segment data is common
2990                // between all segments.
2991                for (segment_index, segment) in brush_segments
2992                    .iter()
2993                    .enumerate()
2994                {
2995                    self.add_segment_to_batch(
2996                        segment,
2997                        segment_data,
2998                        segment_index as i32,
2999                        params.batch_kind,
3000                        prim_header_index,
3001                        blend_mode,
3002                        features,
3003                        brush_flags,
3004                        edge_aa_mask,
3005                        bounding_rect,
3006                        transform_kind,
3007                        z_id,
3008                        prim_opacity,
3009                        clip_task_index,
3010                        ctx,
3011                        render_tasks,
3012                    );
3013                }
3014            }
3015            (None, SegmentDataKind::Shared(ref segment_data)) => {
3016                // No segments, and thus no per-segment instance data.
3017                // Note: the blend mode already takes opacity into account
3018
3019                let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
3020                    clip_task_index,
3021                    render_tasks,
3022                ).unwrap();
3023
3024                let textures = BatchTextures {
3025                    input: segment_data.textures,
3026                    clip_mask,
3027                };
3028
3029                let batch_key = BatchKey {
3030                    blend_mode,
3031                    kind: BatchKind::Brush(params.batch_kind),
3032                    textures,
3033                };
3034
3035                self.add_brush_instance_to_batches(
3036                    batch_key,
3037                    features,
3038                    bounding_rect,
3039                    z_id,
3040                    INVALID_SEGMENT_INDEX,
3041                    edge_aa_mask,
3042                    clip_task_address,
3043                    brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
3044                    prim_header_index,
3045                    segment_data.specific_resource_address,
3046                );
3047            }
3048            (None, SegmentDataKind::Instanced(..)) => {
3049                // We should never hit the case where there are no segments,
3050                // but a list of segment instance data.
3051                unreachable!();
3052            }
3053        }
3054    }
3055}
3056
3057/// Either a single texture / user data for all segments,
3058/// or a list of one per segment.
3059enum SegmentDataKind {
3060    Shared(SegmentInstanceData),
3061    Instanced(SmallVec<[SegmentInstanceData; 8]>),
3062}
3063
3064/// The parameters that are specific to a kind of brush,
3065/// used by the common method to add a brush to batches.
3066struct BrushBatchParameters {
3067    batch_kind: BrushBatchKind,
3068    prim_user_data: [i32; 4],
3069    segment_data: SegmentDataKind,
3070}
3071
3072impl BrushBatchParameters {
3073    /// This brush instance has a list of per-segment
3074    /// instance data.
3075    fn instanced(
3076        batch_kind: BrushBatchKind,
3077        prim_user_data: [i32; 4],
3078        segment_data: SmallVec<[SegmentInstanceData; 8]>,
3079    ) -> Self {
3080        BrushBatchParameters {
3081            batch_kind,
3082            prim_user_data,
3083            segment_data: SegmentDataKind::Instanced(segment_data),
3084        }
3085    }
3086
3087    /// This brush instance shares the per-segment data
3088    /// across all segments.
3089    fn shared(
3090        batch_kind: BrushBatchKind,
3091        textures: TextureSet,
3092        prim_user_data: [i32; 4],
3093        specific_resource_address: i32,
3094    ) -> Self {
3095        BrushBatchParameters {
3096            batch_kind,
3097            prim_user_data,
3098            segment_data: SegmentDataKind::Shared(
3099                SegmentInstanceData {
3100                    textures,
3101                    specific_resource_address,
3102                }
3103            ),
3104        }
3105    }
3106}
3107
3108/// A list of clip instances to be drawn into a target.
3109#[cfg_attr(feature = "capture", derive(Serialize))]
3110#[cfg_attr(feature = "replay", derive(Deserialize))]
3111pub struct ClipMaskInstanceList {
3112    pub mask_instances_fast: FrameVec<MaskInstance>,
3113    pub mask_instances_slow: FrameVec<MaskInstance>,
3114
3115    pub mask_instances_fast_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
3116    pub mask_instances_slow_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
3117
3118    pub image_mask_instances: FastHashMap<TextureSource, FrameVec<PrimitiveInstanceData>>,
3119    pub image_mask_instances_with_scissor: FastHashMap<(DeviceIntRect, TextureSource), FrameVec<PrimitiveInstanceData>>,
3120}
3121
3122impl ClipMaskInstanceList {
3123    pub fn new(memory: &FrameMemory) -> Self {
3124        ClipMaskInstanceList {
3125            mask_instances_fast: memory.new_vec(),
3126            mask_instances_slow: memory.new_vec(),
3127            mask_instances_fast_with_scissor: FastHashMap::default(),
3128            mask_instances_slow_with_scissor: FastHashMap::default(),
3129            image_mask_instances: FastHashMap::default(),
3130            image_mask_instances_with_scissor: FastHashMap::default(),
3131        }
3132    }
3133
3134    pub fn is_empty(&self) -> bool {
3135        // Destructure self to make sure we don't forget to update this method if
3136        // a new member is added.
3137        let ClipMaskInstanceList {
3138            mask_instances_fast,
3139            mask_instances_slow,
3140            mask_instances_fast_with_scissor,
3141            mask_instances_slow_with_scissor,
3142            image_mask_instances,
3143            image_mask_instances_with_scissor,
3144        } = self;
3145
3146        mask_instances_fast.is_empty()
3147            && mask_instances_slow.is_empty()
3148            && mask_instances_fast_with_scissor.is_empty()
3149            && mask_instances_slow_with_scissor.is_empty()
3150            && image_mask_instances.is_empty()
3151            && image_mask_instances_with_scissor.is_empty()
3152    }
3153}
3154
3155/// A list of clip instances to be drawn into a target.
3156#[derive(Debug)]
3157#[cfg_attr(feature = "capture", derive(Serialize))]
3158#[cfg_attr(feature = "replay", derive(Deserialize))]
3159pub struct ClipBatchList {
3160    /// Rectangle draws fill up the rectangles with rounded corners.
3161    pub slow_rectangles: FrameVec<ClipMaskInstanceRect>,
3162    pub fast_rectangles: FrameVec<ClipMaskInstanceRect>,
3163    pub box_shadows: FastHashMap<TextureSource, FrameVec<ClipMaskInstanceBoxShadow>>,
3164}
3165
3166impl ClipBatchList {
3167    fn new(memory: &FrameMemory) -> Self {
3168        ClipBatchList {
3169            slow_rectangles: memory.new_vec(),
3170            fast_rectangles: memory.new_vec(),
3171            box_shadows: FastHashMap::default(),
3172        }
3173    }
3174
3175    pub fn is_empty(&self) -> bool {
3176        self.slow_rectangles.is_empty()
3177          && self.fast_rectangles.is_empty()
3178          && self.box_shadows.is_empty()
3179    }
3180}
3181
3182/// Batcher managing draw calls into the clip mask (in the RT cache).
3183#[derive(Debug)]
3184#[cfg_attr(feature = "capture", derive(Serialize))]
3185#[cfg_attr(feature = "replay", derive(Deserialize))]
3186pub struct ClipBatcher {
3187    /// The first clip in each clip task. This will overwrite all pixels
3188    /// in the clip region, so we can skip doing a clear and write with
3189    /// blending disabled, which is a big performance win on Intel GPUs.
3190    pub primary_clips: ClipBatchList,
3191    /// Any subsequent clip masks (rare) for a clip task get drawn in
3192    /// a second pass with multiplicative blending enabled.
3193    pub secondary_clips: ClipBatchList,
3194
3195    gpu_supports_fast_clears: bool,
3196}
3197
3198impl ClipBatcher {
3199    pub fn new(
3200        gpu_supports_fast_clears: bool,
3201        memory: &FrameMemory,
3202    ) -> Self {
3203        ClipBatcher {
3204            primary_clips: ClipBatchList::new(memory),
3205            secondary_clips: ClipBatchList::new(memory),
3206            gpu_supports_fast_clears,
3207        }
3208    }
3209
3210    pub fn add_clip_region(
3211        &mut self,
3212        local_pos: LayoutPoint,
3213        sub_rect: DeviceRect,
3214        clip_data: ClipData,
3215        task_origin: DevicePoint,
3216        screen_origin: DevicePoint,
3217        device_pixel_scale: f32,
3218    ) {
3219        let instance = ClipMaskInstanceRect {
3220            common: ClipMaskInstanceCommon {
3221                clip_transform_id: TransformPaletteId::IDENTITY,
3222                prim_transform_id: TransformPaletteId::IDENTITY,
3223                sub_rect,
3224                task_origin,
3225                screen_origin,
3226                device_pixel_scale,
3227            },
3228            local_pos,
3229            clip_data,
3230        };
3231
3232        self.primary_clips.slow_rectangles.push(instance);
3233    }
3234
3235    /// Where appropriate, draw a clip rectangle as a small series of tiles,
3236    /// instead of one large rectangle.
3237    fn add_tiled_clip_mask(
3238        &mut self,
3239        mask_screen_rect: DeviceRect,
3240        local_clip_rect: LayoutRect,
3241        clip_spatial_node_index: SpatialNodeIndex,
3242        spatial_tree: &SpatialTree,
3243        world_rect: &WorldRect,
3244        global_device_pixel_scale: DevicePixelScale,
3245        common: &ClipMaskInstanceCommon,
3246        is_first_clip: bool,
3247    ) -> bool {
3248        // Only try to draw in tiles if the clip mark is big enough.
3249        if mask_screen_rect.area() < CLIP_RECTANGLE_AREA_THRESHOLD {
3250            return false;
3251        }
3252
3253        let mask_screen_rect_size = mask_screen_rect.size().to_i32();
3254        let clip_spatial_node = spatial_tree.get_spatial_node(clip_spatial_node_index);
3255
3256        // Only support clips that are axis-aligned to the root coordinate space,
3257        // for now, to simplify the logic below. This handles the vast majority
3258        // of real world cases, but could be expanded in future if needed.
3259        if clip_spatial_node.coordinate_system_id != CoordinateSystemId::root() {
3260            return false;
3261        }
3262
3263        // Get the world rect of the clip rectangle. If we can't transform it due
3264        // to the matrix, just fall back to drawing the entire clip mask.
3265        let transform = spatial_tree.get_world_transform(
3266            clip_spatial_node_index,
3267        );
3268        let world_clip_rect = match project_rect(
3269            &transform.into_transform(),
3270            &local_clip_rect,
3271            &world_rect,
3272        ) {
3273            Some(rect) => rect,
3274            None => return false,
3275        };
3276
3277        // Work out how many tiles to draw this clip mask in, stretched across the
3278        // device rect of the primitive clip mask.
3279        let world_device_rect = world_clip_rect * global_device_pixel_scale;
3280        let x_tiles = (mask_screen_rect_size.width + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE;
3281        let y_tiles = (mask_screen_rect_size.height + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE;
3282
3283        // Because we only run this code path for axis-aligned rects (the root coord system check above),
3284        // and only for rectangles (not rounded etc), the world_device_rect is not conservative - we know
3285        // that there is no inner_rect, and the world_device_rect should be the real, axis-aligned clip rect.
3286        let mask_origin = mask_screen_rect.min.to_vector();
3287        let clip_list = self.get_batch_list(is_first_clip);
3288
3289        for y in 0 .. y_tiles {
3290            for x in 0 .. x_tiles {
3291                let p0 = DeviceIntPoint::new(
3292                    x * CLIP_RECTANGLE_TILE_SIZE,
3293                    y * CLIP_RECTANGLE_TILE_SIZE,
3294                );
3295                let p1 = DeviceIntPoint::new(
3296                    (p0.x + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect_size.width),
3297                    (p0.y + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect_size.height),
3298                );
3299                let normalized_sub_rect = DeviceIntRect {
3300                    min: p0,
3301                    max: p1,
3302                }.to_f32();
3303                let world_sub_rect = normalized_sub_rect.translate(mask_origin);
3304
3305                // If the clip rect completely contains this tile rect, then drawing
3306                // these pixels would be redundant - since this clip can't possibly
3307                // affect the pixels in this tile, skip them!
3308                if !world_device_rect.contains_box(&world_sub_rect) {
3309                    clip_list.slow_rectangles.push(ClipMaskInstanceRect {
3310                        common: ClipMaskInstanceCommon {
3311                            sub_rect: normalized_sub_rect,
3312                            ..*common
3313                        },
3314                        local_pos: local_clip_rect.min,
3315                        clip_data: ClipData::uniform(local_clip_rect.size(), 0.0, ClipMode::Clip),
3316                    });
3317                }
3318            }
3319        }
3320
3321        true
3322    }
3323
3324    /// Retrieve the correct clip batch list to append to, depending
3325    /// on whether this is the first clip mask for a clip task.
3326    fn get_batch_list(
3327        &mut self,
3328        is_first_clip: bool,
3329    ) -> &mut ClipBatchList {
3330        if is_first_clip && !self.gpu_supports_fast_clears {
3331            &mut self.primary_clips
3332        } else {
3333            &mut self.secondary_clips
3334        }
3335    }
3336
3337    pub fn add(
3338        &mut self,
3339        clip_node_range: ClipNodeRange,
3340        root_spatial_node_index: SpatialNodeIndex,
3341        render_tasks: &RenderTaskGraph,
3342        gpu_cache: &GpuCache,
3343        clip_store: &ClipStore,
3344        transforms: &mut TransformPalette,
3345        actual_rect: DeviceRect,
3346        surface_device_pixel_scale: DevicePixelScale,
3347        task_origin: DevicePoint,
3348        screen_origin: DevicePoint,
3349        ctx: &RenderTargetContext,
3350    ) -> bool {
3351        let mut is_first_clip = true;
3352        let mut clear_to_one = false;
3353
3354        for i in 0 .. clip_node_range.count {
3355            let clip_instance = clip_store.get_instance_from_range(&clip_node_range, i);
3356            let clip_node = &ctx.data_stores.clip[clip_instance.handle];
3357
3358            let clip_transform_id = transforms.get_id(
3359                clip_node.item.spatial_node_index,
3360                ctx.root_spatial_node_index,
3361                ctx.spatial_tree,
3362            );
3363
3364            let prim_transform_id = transforms.get_id(
3365                root_spatial_node_index,
3366                ctx.root_spatial_node_index,
3367                ctx.spatial_tree,
3368            );
3369
3370            let common = ClipMaskInstanceCommon {
3371                sub_rect: DeviceRect::from_size(actual_rect.size()),
3372                task_origin,
3373                screen_origin,
3374                device_pixel_scale: surface_device_pixel_scale.0,
3375                clip_transform_id,
3376                prim_transform_id,
3377            };
3378
3379            let added_clip = match clip_node.item.kind {
3380                ClipItemKind::Image { .. } => {
3381                    unreachable!();
3382                }
3383                ClipItemKind::BoxShadow { ref source }  => {
3384                    let task_id = source
3385                        .render_task
3386                        .expect("bug: render task handle not allocated");
3387                    let (uv_rect_address, texture) = render_tasks.resolve_location(task_id, gpu_cache).unwrap();
3388
3389                    self.get_batch_list(is_first_clip)
3390                        .box_shadows
3391                        .entry(texture)
3392                        .or_insert_with(|| ctx.frame_memory.new_vec())
3393                        .push(ClipMaskInstanceBoxShadow {
3394                            common,
3395                            resource_address: uv_rect_address,
3396                            shadow_data: BoxShadowData {
3397                                src_rect_size: source.original_alloc_size,
3398                                clip_mode: source.clip_mode as i32,
3399                                stretch_mode_x: source.stretch_mode_x as i32,
3400                                stretch_mode_y: source.stretch_mode_y as i32,
3401                                dest_rect: source.prim_shadow_rect,
3402                            },
3403                        });
3404
3405                    true
3406                }
3407                ClipItemKind::Rectangle { rect, mode: ClipMode::ClipOut } => {
3408                    self.get_batch_list(is_first_clip)
3409                        .slow_rectangles
3410                        .push(ClipMaskInstanceRect {
3411                            common,
3412                            local_pos: rect.min,
3413                            clip_data: ClipData::uniform(rect.size(), 0.0, ClipMode::ClipOut),
3414                        });
3415
3416                    true
3417                }
3418                ClipItemKind::Rectangle { rect, mode: ClipMode::Clip } => {
3419                    if clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) {
3420                        false
3421                    } else {
3422                        if self.add_tiled_clip_mask(
3423                            actual_rect,
3424                            rect,
3425                            clip_node.item.spatial_node_index,
3426                            ctx.spatial_tree,
3427                            &ctx.screen_world_rect,
3428                            ctx.global_device_pixel_scale,
3429                            &common,
3430                            is_first_clip,
3431                        ) {
3432                            clear_to_one |= is_first_clip;
3433                        } else {
3434                            self.get_batch_list(is_first_clip)
3435                                .slow_rectangles
3436                                .push(ClipMaskInstanceRect {
3437                                    common,
3438                                    local_pos: rect.min,
3439                                    clip_data: ClipData::uniform(rect.size(), 0.0, ClipMode::Clip),
3440                                });
3441                        }
3442
3443                        true
3444                    }
3445                }
3446                ClipItemKind::RoundedRectangle { rect, ref radius, mode, .. } => {
3447                    let batch_list = self.get_batch_list(is_first_clip);
3448                    let instance = ClipMaskInstanceRect {
3449                        common,
3450                        local_pos: rect.min,
3451                        clip_data: ClipData::rounded_rect(rect.size(), radius, mode),
3452                    };
3453                    if clip_instance.flags.contains(ClipNodeFlags::USE_FAST_PATH) {
3454                        batch_list.fast_rectangles.push(instance);
3455                    } else {
3456                        batch_list.slow_rectangles.push(instance);
3457                    }
3458
3459                    true
3460                }
3461            };
3462
3463            is_first_clip &= !added_clip;
3464        }
3465
3466        clear_to_one
3467    }
3468}
3469
3470impl<'a, 'rc> RenderTargetContext<'a, 'rc> {
3471    /// Retrieve the GPU task address for a given clip task instance.
3472    /// Returns None if the segment was completely clipped out.
3473    /// Returns Some(OPAQUE_TASK_ADDRESS) if no clip mask is needed.
3474    /// Returns Some(task_address) if there was a valid clip mask.
3475    fn get_clip_task_and_texture(
3476        &self,
3477        clip_task_index: ClipTaskIndex,
3478        offset: i32,
3479        render_tasks: &RenderTaskGraph,
3480    ) -> Option<(RenderTaskAddress, TextureSource)> {
3481        match self.scratch.clip_mask_instances[clip_task_index.0 as usize + offset as usize] {
3482            ClipMaskKind::Mask(task_id) => {
3483                Some((
3484                    task_id.into(),
3485                    TextureSource::TextureCache(
3486                        render_tasks[task_id].get_target_texture(),
3487                        Swizzle::default(),
3488                    )
3489                ))
3490            }
3491            ClipMaskKind::None => {
3492                Some((OPAQUE_TASK_ADDRESS, TextureSource::Invalid))
3493            }
3494            ClipMaskKind::Clipped => {
3495                None
3496            }
3497        }
3498    }
3499
3500    /// Helper function to get the clip task address for a
3501    /// non-segmented primitive.
3502    fn get_prim_clip_task_and_texture(
3503        &self,
3504        clip_task_index: ClipTaskIndex,
3505        render_tasks: &RenderTaskGraph,
3506    ) -> Option<(RenderTaskAddress, TextureSource)> {
3507        self.get_clip_task_and_texture(
3508            clip_task_index,
3509            0,
3510            render_tasks,
3511        )
3512    }
3513}
3514
3515impl CompositorSurfaceKind {
3516    /// Returns true if the type of compositor surface needs an alpha cutout rendered
3517    fn needs_cutout(&self) -> bool {
3518        match self {
3519            CompositorSurfaceKind::Underlay => true,
3520            CompositorSurfaceKind::Overlay | CompositorSurfaceKind::Blit => false,
3521        }
3522    }
3523}