webrender/
render_target.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
5
6use api::units::*;
7use api::{ColorF, LineOrientation, BorderStyle};
8use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures};
9use crate::batch::{ClipBatcher, BatchBuilder, INVALID_SEGMENT_INDEX, ClipMaskInstanceList};
10use crate::command_buffer::{CommandBufferList, QuadFlags};
11use crate::pattern::{Pattern, PatternKind, PatternShaderInput};
12use crate::segment::EdgeAaSegmentMask;
13use crate::spatial_tree::SpatialTree;
14use crate::clip::{ClipStore, ClipItemKind};
15use crate::frame_builder::FrameGlobalResources;
16use crate::gpu_cache::{GpuCache, GpuCacheAddress};
17use crate::gpu_types::{BorderInstance, SvgFilterInstance, SVGFEFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
18use crate::gpu_types::{TransformPalette, ZBufferIdGenerator, MaskInstance, ClipSpace};
19use crate::gpu_types::{ZBufferId, QuadSegment, PrimitiveInstanceData, TransformPaletteId};
20use crate::internal_types::{CacheTextureId, FastHashMap, FilterGraphOp, FrameAllocator, FrameMemory, FrameVec, TextureSource};
21use crate::picture::{SliceId, SurfaceInfo, ResolvedSurfaceTexture, TileCacheInstance};
22use crate::quad;
23use crate::prim_store::{PrimitiveInstance, PrimitiveStore, PrimitiveScratchBuffer};
24use crate::prim_store::gradient::{
25    FastLinearGradientInstance, LinearGradientInstance, RadialGradientInstance,
26    ConicGradientInstance,
27};
28use crate::renderer::{GpuBufferAddress, GpuBufferBuilder};
29use crate::render_backend::DataStores;
30use crate::render_task::{RenderTaskKind, RenderTaskAddress, SubPass};
31use crate::render_task::{RenderTask, ScalingTask, SvgFilterInfo, MaskSubPass, SVGFEFilterTask};
32use crate::render_task_graph::{RenderTaskGraph, RenderTaskId};
33use crate::resource_cache::ResourceCache;
34use crate::spatial_tree::SpatialNodeIndex;
35use crate::util::ScaleOffset;
36
37
38const STYLE_SOLID: i32 = ((BorderStyle::Solid as i32) << 8) | ((BorderStyle::Solid as i32) << 16);
39const STYLE_MASK: i32 = 0x00FF_FF00;
40
41/// A tag used to identify the output format of a `RenderTarget`.
42#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
43#[cfg_attr(feature = "capture", derive(Serialize))]
44#[cfg_attr(feature = "replay", derive(Deserialize))]
45pub enum RenderTargetKind {
46    Color, // RGBA8
47    Alpha, // R8
48}
49
50pub struct RenderTargetContext<'a, 'rc> {
51    pub global_device_pixel_scale: DevicePixelScale,
52    pub prim_store: &'a PrimitiveStore,
53    pub clip_store: &'a ClipStore,
54    pub resource_cache: &'rc mut ResourceCache,
55    pub use_dual_source_blending: bool,
56    pub use_advanced_blending: bool,
57    pub break_advanced_blend_batches: bool,
58    pub batch_lookback_count: usize,
59    pub spatial_tree: &'a SpatialTree,
60    pub data_stores: &'a DataStores,
61    pub surfaces: &'a [SurfaceInfo],
62    pub scratch: &'a PrimitiveScratchBuffer,
63    pub screen_world_rect: WorldRect,
64    pub globals: &'a FrameGlobalResources,
65    pub tile_caches: &'a FastHashMap<SliceId, Box<TileCacheInstance>>,
66    pub root_spatial_node_index: SpatialNodeIndex,
67    pub frame_memory: &'a mut FrameMemory,
68}
69
70/// A series of `RenderTarget` instances, serving as the high-level container
71/// into which `RenderTasks` are assigned.
72///
73/// During the build phase, we iterate over the tasks in each `RenderPass`. For
74/// each task, we invoke `allocate()` on the `RenderTargetList`, which in turn
75/// attempts to allocate an output region in the last `RenderTarget` in the
76/// list. If allocation fails (or if the list is empty), a new `RenderTarget` is
77/// created and appended to the list. The build phase then assign the task into
78/// the target associated with the final allocation.
79///
80/// The result is that each `RenderPass` is associated with one or two
81/// `RenderTargetLists`, depending on whether we have all our tasks have the
82/// same `RenderTargetKind`. The lists are then shipped to the `Renderer`, which
83/// allocates a device texture array, with one slice per render target in the
84/// list.
85///
86/// The upshot of this scheme is that it maximizes batching. In a given pass,
87/// we need to do a separate batch for each individual render target. But with
88/// the texture array, we can expose the entirety of the previous pass to each
89/// task in the current pass in a single batch, which generally allows each
90/// task to be drawn in a single batch regardless of how many results from the
91/// previous pass it depends on.
92///
93/// Note that in some cases (like drop-shadows), we can depend on the output of
94/// a pass earlier than the immediately-preceding pass.
95#[cfg_attr(feature = "capture", derive(Serialize))]
96#[cfg_attr(feature = "replay", derive(Deserialize))]
97pub struct RenderTargetList {
98    pub targets: FrameVec<RenderTarget>,
99}
100
101impl RenderTargetList {
102    pub fn new(allocator: FrameAllocator) -> Self {
103        RenderTargetList {
104            targets: allocator.new_vec(),
105        }
106    }
107
108    pub fn build(
109        &mut self,
110        ctx: &mut RenderTargetContext,
111        gpu_cache: &mut GpuCache,
112        render_tasks: &RenderTaskGraph,
113        prim_headers: &mut PrimitiveHeaders,
114        transforms: &mut TransformPalette,
115        z_generator: &mut ZBufferIdGenerator,
116        prim_instances: &[PrimitiveInstance],
117        cmd_buffers: &CommandBufferList,
118        gpu_buffer_builder: &mut GpuBufferBuilder,
119    ) {
120        if self.targets.is_empty() {
121            return;
122        }
123
124        for target in &mut self.targets {
125            target.build(
126                ctx,
127                gpu_cache,
128                render_tasks,
129                prim_headers,
130                transforms,
131                z_generator,
132                prim_instances,
133                cmd_buffers,
134                gpu_buffer_builder,
135            );
136        }
137    }
138}
139
140const NUM_PATTERNS: usize = crate::pattern::NUM_PATTERNS as usize;
141
142/// Contains the work (in the form of instance arrays) needed to fill a color
143/// color (RGBA8) or alpha output surface.
144///
145/// In graphics parlance, a "render target" usually means "a surface (texture or
146/// framebuffer) bound to the output of a shader". This struct has a slightly
147/// different meaning, in that it represents the operations on that surface
148/// _before_ it's actually bound and rendered. So a `RenderTarget` is built by
149/// the `RenderBackend` by inserting tasks, and then shipped over to the
150/// `Renderer` where a device surface is resolved and the tasks are transformed
151/// into draw commands on that surface.
152#[cfg_attr(feature = "capture", derive(Serialize))]
153#[cfg_attr(feature = "replay", derive(Deserialize))]
154pub struct RenderTarget {
155    pub target_kind: RenderTargetKind,
156    pub cached: bool,
157    screen_size: DeviceIntSize,
158    pub texture_id: CacheTextureId,
159
160    pub alpha_batch_containers: FrameVec<AlphaBatchContainer>,
161    // List of blur operations to apply for this render target.
162    pub vertical_blurs: FastHashMap<TextureSource, FrameVec<BlurInstance>>,
163    pub horizontal_blurs: FastHashMap<TextureSource, FrameVec<BlurInstance>>,
164    pub scalings: FastHashMap<TextureSource, FrameVec<ScalingInstance>>,
165    pub svg_filters: FrameVec<(BatchTextures, FrameVec<SvgFilterInstance>)>,
166    pub svg_nodes: FrameVec<(BatchTextures, FrameVec<SVGFEFilterInstance>)>,
167    pub blits: FrameVec<BlitJob>,
168    alpha_tasks: FrameVec<RenderTaskId>,
169    pub resolve_ops: FrameVec<ResolveOp>,
170
171    pub prim_instances: [FastHashMap<TextureSource, FrameVec<PrimitiveInstanceData>>; NUM_PATTERNS],
172    pub prim_instances_with_scissor: FastHashMap<(DeviceIntRect, PatternKind), FastHashMap<TextureSource, FrameVec<PrimitiveInstanceData>>>,
173
174    pub clip_masks: ClipMaskInstanceList,
175
176    pub border_segments_complex: FrameVec<BorderInstance>,
177    pub border_segments_solid: FrameVec<BorderInstance>,
178    pub line_decorations: FrameVec<LineDecorationJob>,
179    pub fast_linear_gradients: FrameVec<FastLinearGradientInstance>,
180    pub linear_gradients: FrameVec<LinearGradientInstance>,
181    pub radial_gradients: FrameVec<RadialGradientInstance>,
182    pub conic_gradients: FrameVec<ConicGradientInstance>,
183
184    pub clip_batcher: ClipBatcher,
185
186    // Clearing render targets has a fair amount of special cases.
187    // The general rules are:
188    // - Depth (for at least the used potion of the target) is always cleared if it
189    //   is used by the target. The rest of this explaination focuses on clearing
190    //   color/alpha textures.
191    // - For non-cached targets we either clear the entire target or the used portion
192    //   (unless clear_color is None).
193    // - Cached render targets require precise partial clears which are specified
194    //   via the vectors below (if clearing is needed at all).
195    //
196    // See also: Renderer::clear_render_target
197
198    // Areas that *must* be cleared.
199    // Even if a global target clear is done, we try to honor clearing the rects that
200    // have a different color than the global clear color.
201    pub clears: FrameVec<(DeviceIntRect, ColorF)>,
202
203    // Optionally track the used rect of the render target, to give the renderer
204    // an opportunity to only clear the used portion of the target as an optimization.
205    // Note: We make the simplifying assumption that if clear vectors AND used_rect
206    // are specified, then the rects from the clear vectors are contained in
207    // used_rect.
208    pub used_rect: Option<DeviceIntRect>,
209    // The global clear color is Some(TRANSPARENT) by default. If we are drawing
210    // a single render task in this target, it can be set to something else.
211    // If clear_color is None, only the clears/zero_clears/one_clears are done.
212    pub clear_color: Option<ColorF>,
213}
214
215impl RenderTarget {
216    pub fn new(
217        target_kind: RenderTargetKind,
218        cached: bool,
219        texture_id: CacheTextureId,
220        screen_size: DeviceIntSize,
221        gpu_supports_fast_clears: bool,
222        used_rect: Option<DeviceIntRect>,
223        memory: &FrameMemory,
224    ) -> Self {
225        RenderTarget {
226            target_kind,
227            cached,
228            screen_size,
229            texture_id,
230            alpha_batch_containers: memory.new_vec(),
231            vertical_blurs: FastHashMap::default(),
232            horizontal_blurs: FastHashMap::default(),
233            scalings: FastHashMap::default(),
234            svg_filters: memory.new_vec(),
235            svg_nodes: memory.new_vec(),
236            blits: memory.new_vec(),
237            alpha_tasks: memory.new_vec(),
238            used_rect,
239            resolve_ops: memory.new_vec(),
240            clear_color: Some(ColorF::TRANSPARENT),
241            prim_instances: [FastHashMap::default(), FastHashMap::default(), FastHashMap::default(), FastHashMap::default()],
242            prim_instances_with_scissor: FastHashMap::default(),
243            clip_masks: ClipMaskInstanceList::new(memory),
244            clip_batcher: ClipBatcher::new(gpu_supports_fast_clears, memory),
245            border_segments_complex: memory.new_vec(),
246            border_segments_solid: memory.new_vec(),
247            clears: memory.new_vec(),
248            line_decorations: memory.new_vec(),
249            fast_linear_gradients: memory.new_vec(),
250            linear_gradients: memory.new_vec(),
251            radial_gradients: memory.new_vec(),
252            conic_gradients: memory.new_vec(),
253        }
254    }
255
256    pub fn build(
257        &mut self,
258        ctx: &mut RenderTargetContext,
259        gpu_cache: &mut GpuCache,
260        render_tasks: &RenderTaskGraph,
261        prim_headers: &mut PrimitiveHeaders,
262        transforms: &mut TransformPalette,
263        z_generator: &mut ZBufferIdGenerator,
264        prim_instances: &[PrimitiveInstance],
265        cmd_buffers: &CommandBufferList,
266        gpu_buffer_builder: &mut GpuBufferBuilder,
267    ) {
268        profile_scope!("build");
269        let mut merged_batches = AlphaBatchContainer::new(None, &ctx.frame_memory);
270
271        for task_id in &self.alpha_tasks {
272            profile_scope!("alpha_task");
273            let task = &render_tasks[*task_id];
274
275            match task.kind {
276                RenderTaskKind::Picture(ref pic_task) => {
277                    let target_rect = task.get_target_rect();
278
279                    let scissor_rect = if pic_task.can_merge {
280                        None
281                    } else {
282                        Some(target_rect)
283                    };
284
285                    if !pic_task.can_use_shared_surface {
286                        self.clear_color = pic_task.clear_color;
287                    }
288                    if let Some(clear_color) = pic_task.clear_color {
289                        self.clears.push((target_rect, clear_color));
290                    } else if self.cached {
291                        self.clears.push((target_rect, ColorF::TRANSPARENT));
292                    }
293
294                    // TODO(gw): The type names of AlphaBatchBuilder and BatchBuilder
295                    //           are still confusing. Once more of the picture caching
296                    //           improvement code lands, the AlphaBatchBuilder and
297                    //           AlphaBatchList types will be collapsed into one, which
298                    //           should simplify coming up with better type names.
299                    let alpha_batch_builder = AlphaBatchBuilder::new(
300                        self.screen_size,
301                        ctx.break_advanced_blend_batches,
302                        ctx.batch_lookback_count,
303                        *task_id,
304                        (*task_id).into(),
305                        &ctx.frame_memory,
306                    );
307
308                    let mut batch_builder = BatchBuilder::new(alpha_batch_builder);
309                    let cmd_buffer = cmd_buffers.get(pic_task.cmd_buffer_index);
310
311                    cmd_buffer.iter_prims(&mut |cmd, spatial_node_index, segments| {
312                        batch_builder.add_prim_to_batch(
313                            cmd,
314                            spatial_node_index,
315                            ctx,
316                            gpu_cache,
317                            render_tasks,
318                            prim_headers,
319                            transforms,
320                            pic_task.raster_spatial_node_index,
321                            pic_task.surface_spatial_node_index,
322                            z_generator,
323                            prim_instances,
324                            gpu_buffer_builder,
325                            segments,
326                        );
327                    });
328
329                    let alpha_batch_builder = batch_builder.finalize();
330
331                    alpha_batch_builder.build(
332                        &mut self.alpha_batch_containers,
333                        &mut merged_batches,
334                        target_rect,
335                        scissor_rect,
336                    );
337                }
338                _ => {
339                    unreachable!();
340                }
341            }
342        }
343
344        if !merged_batches.is_empty() {
345            self.alpha_batch_containers.push(merged_batches);
346        }
347    }
348
349    pub fn texture_id(&self) -> CacheTextureId {
350        self.texture_id
351    }
352
353    pub fn add_task(
354        &mut self,
355        task_id: RenderTaskId,
356        ctx: &RenderTargetContext,
357        gpu_cache: &mut GpuCache,
358        gpu_buffer_builder: &mut GpuBufferBuilder,
359        render_tasks: &RenderTaskGraph,
360        clip_store: &ClipStore,
361        transforms: &mut TransformPalette,
362    ) {
363        profile_scope!("add_task");
364        let task = &render_tasks[task_id];
365        let target_rect = task.get_target_rect();
366
367        match task.kind {
368            RenderTaskKind::Prim(ref info) => {
369                let render_task_address = task_id.into();
370
371                quad::add_to_batch(
372                    info.pattern,
373                    info.pattern_input,
374                    render_task_address,
375                    info.transform_id,
376                    info.prim_address_f,
377                    info.quad_flags,
378                    info.edge_flags,
379                    INVALID_SEGMENT_INDEX as u8,
380                    info.texture_input,
381                    ZBufferId(0),
382                    render_tasks,
383                    gpu_buffer_builder,
384                    |key, instance| {
385                        if info.prim_needs_scissor_rect {
386                            self.prim_instances_with_scissor
387                                .entry((target_rect, info.pattern))
388                                .or_insert(FastHashMap::default())
389                                .entry(key.textures.input.colors[0])
390                                .or_insert_with(|| ctx.frame_memory.new_vec())
391                                .push(instance);
392                        } else {
393                            self.prim_instances[info.pattern as usize]
394                                .entry(key.textures.input.colors[0])
395                                .or_insert_with(|| ctx.frame_memory.new_vec())
396                                .push(instance);
397                        }
398                    }
399                );
400            }
401            RenderTaskKind::VerticalBlur(ref info) => {
402                if self.target_kind == RenderTargetKind::Alpha {
403                    self.clears.push((target_rect, ColorF::TRANSPARENT));
404                }
405                add_blur_instances(
406                    &mut self.vertical_blurs,
407                    BlurDirection::Vertical,
408                    info.blur_std_deviation,
409                    info.blur_region,
410                    task_id.into(),
411                    task.children[0],
412                    render_tasks,
413                    &ctx.frame_memory,
414                );
415            }
416            RenderTaskKind::HorizontalBlur(ref info) => {
417                if self.target_kind == RenderTargetKind::Alpha {
418                    self.clears.push((target_rect, ColorF::TRANSPARENT));
419                }
420                add_blur_instances(
421                    &mut self.horizontal_blurs,
422                    BlurDirection::Horizontal,
423                    info.blur_std_deviation,
424                    info.blur_region,
425                    task_id.into(),
426                    task.children[0],
427                    render_tasks,
428                    &ctx.frame_memory,
429                );
430            }
431            RenderTaskKind::Picture(ref pic_task) => {
432                if let Some(ref resolve_op) = pic_task.resolve_op {
433                    self.resolve_ops.push(resolve_op.clone());
434                }
435                self.alpha_tasks.push(task_id);
436            }
437            RenderTaskKind::SvgFilter(ref task_info) => {
438                add_svg_filter_instances(
439                    &mut self.svg_filters,
440                    render_tasks,
441                    &task_info.info,
442                    task_id,
443                    task.children.get(0).cloned(),
444                    task.children.get(1).cloned(),
445                    task_info.extra_gpu_cache_handle.map(|handle| gpu_cache.get_address(&handle)),
446                    &ctx.frame_memory,
447                )
448            }
449            RenderTaskKind::SVGFENode(ref task_info) => {
450                add_svg_filter_node_instances(
451                    &mut self.svg_nodes,
452                    render_tasks,
453                    &task_info,
454                    task,
455                    task.children.get(0).cloned(),
456                    task.children.get(1).cloned(),
457                    task_info.extra_gpu_cache_handle.map(|handle| gpu_cache.get_address(&handle)),
458                    &ctx.frame_memory,
459                )
460            }
461            RenderTaskKind::Empty(..) => {
462                // TODO(gw): Could likely be more efficient by choosing to clear to 0 or 1
463                //           based on the clip chain, or even skipping clear and masking the
464                //           prim region with blend disabled.
465                self.clears.push((target_rect, ColorF::WHITE));
466            }
467            RenderTaskKind::CacheMask(ref task_info) => {
468                let clear_to_one = self.clip_batcher.add(
469                    task_info.clip_node_range,
470                    task_info.root_spatial_node_index,
471                    render_tasks,
472                    gpu_cache,
473                    clip_store,
474                    transforms,
475                    task_info.actual_rect,
476                    task_info.device_pixel_scale,
477                    target_rect.min.to_f32(),
478                    task_info.actual_rect.min,
479                    ctx,
480                );
481                if task_info.clear_to_one || clear_to_one {
482                    self.clears.push((target_rect, ColorF::WHITE));
483                }
484            }
485            RenderTaskKind::ClipRegion(ref region_task) => {
486                if region_task.clear_to_one {
487                    self.clears.push((target_rect, ColorF::WHITE));
488                }
489                let device_rect = DeviceRect::from_size(
490                    target_rect.size().to_f32(),
491                );
492                self.clip_batcher.add_clip_region(
493                    region_task.local_pos,
494                    device_rect,
495                    region_task.clip_data.clone(),
496                    target_rect.min.to_f32(),
497                    DevicePoint::zero(),
498                    region_task.device_pixel_scale.0,
499                );
500            }
501            RenderTaskKind::Scaling(ref info) => {
502                add_scaling_instances(
503                    info,
504                    &mut self.scalings,
505                    task,
506                    task.children.first().map(|&child| &render_tasks[child]),
507                    &ctx.frame_memory,
508                );
509            }
510            RenderTaskKind::Blit(ref task_info) => {
511                let target_rect = task.get_target_rect();
512                self.blits.push(BlitJob {
513                    source: task_info.source,
514                    source_rect: task_info.source_rect,
515                    target_rect,
516                });
517            }
518            RenderTaskKind::LineDecoration(ref info) => {
519                self.clears.push((target_rect, ColorF::TRANSPARENT));
520
521                self.line_decorations.push(LineDecorationJob {
522                    task_rect: target_rect.to_f32(),
523                    local_size: info.local_size,
524                    style: info.style as i32,
525                    axis_select: match info.orientation {
526                        LineOrientation::Horizontal => 0.0,
527                        LineOrientation::Vertical => 1.0,
528                    },
529                    wavy_line_thickness: info.wavy_line_thickness,
530                });
531            }
532            RenderTaskKind::Border(ref task_info) => {
533                self.clears.push((target_rect, ColorF::TRANSPARENT));
534
535                let task_origin = target_rect.min.to_f32();
536                // TODO(gw): Clone here instead of a move of this vec, since the frame
537                //           graph is immutable by this point. It's rare that borders
538                //           are drawn since they are persisted in the texture cache,
539                //           but perhaps this could be improved in future.
540                let instances = task_info.instances.clone();
541                for mut instance in instances {
542                    // TODO(gw): It may be better to store the task origin in
543                    //           the render task data instead of per instance.
544                    instance.task_origin = task_origin;
545                    if instance.flags & STYLE_MASK == STYLE_SOLID {
546                        self.border_segments_solid.push(instance);
547                    } else {
548                        self.border_segments_complex.push(instance);
549                    }
550                }
551            }
552            RenderTaskKind::FastLinearGradient(ref task_info) => {
553                self.fast_linear_gradients.push(task_info.to_instance(&target_rect));
554            }
555            RenderTaskKind::LinearGradient(ref task_info) => {
556                self.linear_gradients.push(task_info.to_instance(&target_rect));
557            }
558            RenderTaskKind::RadialGradient(ref task_info) => {
559                self.radial_gradients.push(task_info.to_instance(&target_rect));
560            }
561            RenderTaskKind::ConicGradient(ref task_info) => {
562                self.conic_gradients.push(task_info.to_instance(&target_rect));
563            }
564            RenderTaskKind::Image(..) |
565            RenderTaskKind::Cached(..) |
566            RenderTaskKind::TileComposite(..) => {
567                panic!("Should not be added to color target!");
568            }
569            RenderTaskKind::Readback(..) => {}
570            #[cfg(test)]
571            RenderTaskKind::Test(..) => {}
572        }
573
574        build_sub_pass(
575            task_id,
576            task,
577            gpu_buffer_builder,
578            render_tasks,
579            transforms,
580            ctx,
581            &mut self.clip_masks,
582        );
583    }
584
585    pub fn needs_depth(&self) -> bool {
586        self.alpha_batch_containers.iter().any(|ab| {
587            !ab.opaque_batches.is_empty()
588        })
589    }
590}
591
592#[cfg_attr(feature = "capture", derive(Serialize))]
593#[cfg_attr(feature = "replay", derive(Deserialize))]
594#[derive(Debug, PartialEq, Clone)]
595pub struct ResolveOp {
596    pub src_task_ids: Vec<RenderTaskId>,
597    pub dest_task_id: RenderTaskId,
598}
599
600#[cfg_attr(feature = "capture", derive(Serialize))]
601#[cfg_attr(feature = "replay", derive(Deserialize))]
602pub enum PictureCacheTargetKind {
603    Draw {
604        alpha_batch_container: AlphaBatchContainer,
605    },
606    Blit {
607        task_id: RenderTaskId,
608        sub_rect_offset: DeviceIntVector2D,
609    },
610}
611
612#[cfg_attr(feature = "capture", derive(Serialize))]
613#[cfg_attr(feature = "replay", derive(Deserialize))]
614pub struct PictureCacheTarget {
615    pub surface: ResolvedSurfaceTexture,
616    pub kind: PictureCacheTargetKind,
617    pub clear_color: Option<ColorF>,
618    pub dirty_rect: DeviceIntRect,
619    pub valid_rect: DeviceIntRect,
620}
621
622fn add_blur_instances(
623    instances: &mut FastHashMap<TextureSource, FrameVec<BlurInstance>>,
624    blur_direction: BlurDirection,
625    blur_std_deviation: f32,
626    blur_region: DeviceIntSize,
627    task_address: RenderTaskAddress,
628    src_task_id: RenderTaskId,
629    render_tasks: &RenderTaskGraph,
630    memory: &FrameMemory,
631) {
632    let source = render_tasks[src_task_id].get_texture_source();
633
634    let instance = BlurInstance {
635        task_address,
636        src_task_address: src_task_id.into(),
637        blur_direction: blur_direction.as_int(),
638        blur_std_deviation,
639        blur_region: blur_region.to_f32(),
640    };
641
642    instances
643        .entry(source)
644        .or_insert_with(|| memory.new_vec())
645        .push(instance);
646}
647
648fn add_scaling_instances(
649    task: &ScalingTask,
650    instances: &mut FastHashMap<TextureSource, FrameVec<ScalingInstance>>,
651    target_task: &RenderTask,
652    source_task: Option<&RenderTask>,
653    memory: &FrameMemory,
654) {
655    let target_rect = target_task
656        .get_target_rect()
657        .inner_box(task.padding)
658        .to_f32();
659
660    let source = source_task.unwrap().get_texture_source();
661
662    let source_rect = source_task.unwrap().get_target_rect().to_f32();
663
664    instances
665        .entry(source)
666        .or_insert_with(|| memory.new_vec())
667        .push(ScalingInstance::new(
668            target_rect,
669            source_rect,
670            source.uses_normalized_uvs(),
671        ));
672}
673
674fn add_svg_filter_instances(
675    instances: &mut FrameVec<(BatchTextures, FrameVec<SvgFilterInstance>)>,
676    render_tasks: &RenderTaskGraph,
677    filter: &SvgFilterInfo,
678    task_id: RenderTaskId,
679    input_1_task: Option<RenderTaskId>,
680    input_2_task: Option<RenderTaskId>,
681    extra_data_address: Option<GpuCacheAddress>,
682    memory: &FrameMemory,
683) {
684    let mut textures = BatchTextures::empty();
685
686    if let Some(id) = input_1_task {
687        textures.input.colors[0] = render_tasks[id].get_texture_source();
688    }
689
690    if let Some(id) = input_2_task {
691        textures.input.colors[1] = render_tasks[id].get_texture_source();
692    }
693
694    let kind = match filter {
695        SvgFilterInfo::Blend(..) => 0,
696        SvgFilterInfo::Flood(..) => 1,
697        SvgFilterInfo::LinearToSrgb => 2,
698        SvgFilterInfo::SrgbToLinear => 3,
699        SvgFilterInfo::Opacity(..) => 4,
700        SvgFilterInfo::ColorMatrix(..) => 5,
701        SvgFilterInfo::DropShadow(..) => 6,
702        SvgFilterInfo::Offset(..) => 7,
703        SvgFilterInfo::ComponentTransfer(..) => 8,
704        SvgFilterInfo::Identity => 9,
705        SvgFilterInfo::Composite(..) => 10,
706    };
707
708    let input_count = match filter {
709        SvgFilterInfo::Flood(..) => 0,
710
711        SvgFilterInfo::LinearToSrgb |
712        SvgFilterInfo::SrgbToLinear |
713        SvgFilterInfo::Opacity(..) |
714        SvgFilterInfo::ColorMatrix(..) |
715        SvgFilterInfo::Offset(..) |
716        SvgFilterInfo::ComponentTransfer(..) |
717        SvgFilterInfo::Identity => 1,
718
719        // Not techincally a 2 input filter, but we have 2 inputs here: original content & blurred content.
720        SvgFilterInfo::DropShadow(..) |
721        SvgFilterInfo::Blend(..) |
722        SvgFilterInfo::Composite(..) => 2,
723    };
724
725    let generic_int = match filter {
726        SvgFilterInfo::Blend(mode) => *mode as u16,
727        SvgFilterInfo::ComponentTransfer(data) =>
728            (data.r_func.to_int() << 12 |
729             data.g_func.to_int() << 8 |
730             data.b_func.to_int() << 4 |
731             data.a_func.to_int()) as u16,
732        SvgFilterInfo::Composite(operator) =>
733            operator.as_int() as u16,
734        SvgFilterInfo::LinearToSrgb |
735        SvgFilterInfo::SrgbToLinear |
736        SvgFilterInfo::Flood(..) |
737        SvgFilterInfo::Opacity(..) |
738        SvgFilterInfo::ColorMatrix(..) |
739        SvgFilterInfo::DropShadow(..) |
740        SvgFilterInfo::Offset(..) |
741        SvgFilterInfo::Identity => 0,
742    };
743
744    let instance = SvgFilterInstance {
745        task_address: task_id.into(),
746        input_1_task_address: input_1_task.map(|id| id.into()).unwrap_or(RenderTaskAddress(0)),
747        input_2_task_address: input_2_task.map(|id| id.into()).unwrap_or(RenderTaskAddress(0)),
748        kind,
749        input_count,
750        generic_int,
751        padding: 0,
752        extra_data_address: extra_data_address.unwrap_or(GpuCacheAddress::INVALID),
753    };
754
755    for (ref mut batch_textures, ref mut batch) in instances.iter_mut() {
756        if let Some(combined_textures) = batch_textures.combine_textures(textures) {
757            batch.push(instance);
758            // Update the batch textures to the newly combined batch textures
759            *batch_textures = combined_textures;
760            return;
761        }
762    }
763
764    let mut vec = memory.new_vec();
765    vec.push(instance);
766
767    instances.push((textures, vec));
768}
769
770/// Generates SVGFEFilterInstances from a single SVGFEFilterTask, this is what
771/// prepares vertex data for the shader, and adds it to the appropriate batch.
772///
773/// The interesting parts of the handling of SVG filters are:
774/// * scene_building.rs : wrap_prim_with_filters
775/// * picture.rs : get_coverage_svgfe
776/// * render_task.rs : new_svg_filter_graph
777/// * render_target.rs : add_svg_filter_node_instances (you are here)
778fn add_svg_filter_node_instances(
779    instances: &mut FrameVec<(BatchTextures, FrameVec<SVGFEFilterInstance>)>,
780    render_tasks: &RenderTaskGraph,
781    task_info: &SVGFEFilterTask,
782    target_task: &RenderTask,
783    input_1_task: Option<RenderTaskId>,
784    input_2_task: Option<RenderTaskId>,
785    extra_data_address: Option<GpuCacheAddress>,
786    memory: &FrameMemory,
787) {
788    let node = &task_info.node;
789    let op = &task_info.op;
790    let mut textures = BatchTextures::empty();
791
792    // We have to undo the inflate here as the inflated target rect is meant to
793    // have a blank border
794    let target_rect = target_task
795        .get_target_rect()
796        .inner_box(DeviceIntSideOffsets::new(node.inflate as i32, node.inflate as i32, node.inflate as i32, node.inflate as i32))
797        .to_f32();
798
799    let mut instance = SVGFEFilterInstance {
800        target_rect,
801        input_1_content_scale_and_offset: [0.0; 4],
802        input_2_content_scale_and_offset: [0.0; 4],
803        input_1_task_address: RenderTaskId::INVALID.into(),
804        input_2_task_address: RenderTaskId::INVALID.into(),
805        kind: 0,
806        input_count: node.inputs.len() as u16,
807        extra_data_address: extra_data_address.unwrap_or(GpuCacheAddress::INVALID),
808    };
809
810    // Must match FILTER_* in cs_svg_filter_node.glsl
811    instance.kind = match op {
812        // Identity does not modify color, no linear case
813        FilterGraphOp::SVGFEIdentity => 0,
814        // SourceGraphic does not have its own shader mode, it uses Identity.
815        FilterGraphOp::SVGFESourceGraphic => 0,
816        // SourceAlpha does not have its own shader mode, it uses ToAlpha.
817        FilterGraphOp::SVGFESourceAlpha => 4,
818        // Opacity scales the entire rgba color, so it does not need a linear
819        // case as the rgb / a ratio does not change (sRGB is a curve on the RGB
820        // before alpha multiply, not after)
821        FilterGraphOp::SVGFEOpacity{..} => 2,
822        FilterGraphOp::SVGFEToAlpha => 4,
823        FilterGraphOp::SVGFEBlendColor => {match node.linear {false => 6, true => 7}},
824        FilterGraphOp::SVGFEBlendColorBurn => {match node.linear {false => 8, true => 9}},
825        FilterGraphOp::SVGFEBlendColorDodge => {match node.linear {false => 10, true => 11}},
826        FilterGraphOp::SVGFEBlendDarken => {match node.linear {false => 12, true => 13}},
827        FilterGraphOp::SVGFEBlendDifference => {match node.linear {false => 14, true => 15}},
828        FilterGraphOp::SVGFEBlendExclusion => {match node.linear {false => 16, true => 17}},
829        FilterGraphOp::SVGFEBlendHardLight => {match node.linear {false => 18, true => 19}},
830        FilterGraphOp::SVGFEBlendHue => {match node.linear {false => 20, true => 21}},
831        FilterGraphOp::SVGFEBlendLighten => {match node.linear {false => 22, true => 23}},
832        FilterGraphOp::SVGFEBlendLuminosity => {match node.linear {false => 24, true => 25}},
833        FilterGraphOp::SVGFEBlendMultiply => {match node.linear {false => 26, true => 27}},
834        FilterGraphOp::SVGFEBlendNormal => {match node.linear {false => 28, true => 29}},
835        FilterGraphOp::SVGFEBlendOverlay => {match node.linear {false => 30, true => 31}},
836        FilterGraphOp::SVGFEBlendSaturation => {match node.linear {false => 32, true => 33}},
837        FilterGraphOp::SVGFEBlendScreen => {match node.linear {false => 34, true => 35}},
838        FilterGraphOp::SVGFEBlendSoftLight => {match node.linear {false => 36, true => 37}},
839        FilterGraphOp::SVGFEColorMatrix{..} => {match node.linear {false => 38, true => 39}},
840        FilterGraphOp::SVGFEComponentTransfer => unreachable!(),
841        FilterGraphOp::SVGFEComponentTransferInterned{..} => {match node.linear {false => 40, true => 41}},
842        FilterGraphOp::SVGFECompositeArithmetic{..} => {match node.linear {false => 42, true => 43}},
843        FilterGraphOp::SVGFECompositeATop => {match node.linear {false => 44, true => 45}},
844        FilterGraphOp::SVGFECompositeIn => {match node.linear {false => 46, true => 47}},
845        FilterGraphOp::SVGFECompositeLighter => {match node.linear {false => 48, true => 49}},
846        FilterGraphOp::SVGFECompositeOut => {match node.linear {false => 50, true => 51}},
847        FilterGraphOp::SVGFECompositeOver => {match node.linear {false => 52, true => 53}},
848        FilterGraphOp::SVGFECompositeXOR => {match node.linear {false => 54, true => 55}},
849        FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => {match node.linear {false => 56, true => 57}},
850        FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => {match node.linear {false => 58, true => 59}},
851        FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => {match node.linear {false => 60, true => 61}},
852        FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {match node.linear {false => 62, true => 63}},
853        FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {match node.linear {false => 64, true => 65}},
854        FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {match node.linear {false => 66, true => 67}},
855        FilterGraphOp::SVGFEDisplacementMap{..} => {match node.linear {false => 68, true => 69}},
856        FilterGraphOp::SVGFEDropShadow{..} => {match node.linear {false => 70, true => 71}},
857        // feFlood takes an sRGB color and does no math on it, no linear case
858        FilterGraphOp::SVGFEFlood{..} => 72,
859        FilterGraphOp::SVGFEGaussianBlur{..} => {match node.linear {false => 74, true => 75}},
860        // feImage does not meaningfully modify the color of its input, though a
861        // case could be made for gamma-correct image scaling, that's a bit out
862        // of scope for now
863        FilterGraphOp::SVGFEImage{..} => 76,
864        FilterGraphOp::SVGFEMorphologyDilate{..} => {match node.linear {false => 80, true => 81}},
865        FilterGraphOp::SVGFEMorphologyErode{..} => {match node.linear {false => 82, true => 83}},
866        FilterGraphOp::SVGFESpecularLightingDistant{..} => {match node.linear {false => 86, true => 87}},
867        FilterGraphOp::SVGFESpecularLightingPoint{..} => {match node.linear {false => 88, true => 89}},
868        FilterGraphOp::SVGFESpecularLightingSpot{..} => {match node.linear {false => 90, true => 91}},
869        // feTile does not modify color, no linear case
870        FilterGraphOp::SVGFETile => 92,
871        FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} => {match node.linear {false => 94, true => 95}},
872        FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} => {match node.linear {false => 96, true => 97}},
873        FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} => {match node.linear {false => 98, true => 99}},
874        FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {match node.linear {false => 100, true => 101}},
875    };
876
877    // This is a bit of an ugly way to do this, but avoids code duplication.
878    let mut resolve_input = |index: usize, src_task: Option<RenderTaskId>| -> (RenderTaskAddress, [f32; 4]) {
879        let mut src_task_id = RenderTaskId::INVALID;
880        let mut resolved_scale_and_offset: [f32; 4] = [0.0; 4];
881        if let Some(input) = node.inputs.get(index) {
882            src_task_id = src_task.unwrap();
883            let src_task = &render_tasks[src_task_id];
884
885            textures.input.colors[index] = src_task.get_texture_source();
886            let src_task_size = src_task.location.size();
887            let src_scale_x = (src_task_size.width as f32 - input.inflate as f32 * 2.0) / input.subregion.width();
888            let src_scale_y = (src_task_size.height as f32 - input.inflate as f32 * 2.0) / input.subregion.height();
889            let scale_x = src_scale_x * node.subregion.width();
890            let scale_y = src_scale_y * node.subregion.height();
891            let offset_x = src_scale_x * (node.subregion.min.x - input.subregion.min.x) + input.inflate as f32;
892            let offset_y = src_scale_y * (node.subregion.min.y - input.subregion.min.y) + input.inflate as f32;
893            resolved_scale_and_offset = [
894                scale_x,
895                scale_y,
896                offset_x,
897                offset_y];
898        }
899        let address: RenderTaskAddress = src_task_id.into();
900        (address, resolved_scale_and_offset)
901    };
902    (instance.input_1_task_address, instance.input_1_content_scale_and_offset) = resolve_input(0, input_1_task);
903    (instance.input_2_task_address, instance.input_2_content_scale_and_offset) = resolve_input(1, input_2_task);
904
905    // Additional instance modifications for certain filters
906    match op {
907        FilterGraphOp::SVGFEOpacity { valuebinding: _, value } => {
908            // opacity only has one input so we can use the other
909            // components to store the opacity value
910            instance.input_2_content_scale_and_offset = [*value, 0.0, 0.0, 0.0];
911        },
912        FilterGraphOp::SVGFEMorphologyDilate { radius_x, radius_y } |
913        FilterGraphOp::SVGFEMorphologyErode { radius_x, radius_y } => {
914            // morphology filters only use one input, so we use the
915            // second offset coord to store the radius values.
916            instance.input_2_content_scale_and_offset = [*radius_x, *radius_y, 0.0, 0.0];
917        },
918        FilterGraphOp::SVGFEFlood { color } => {
919            // flood filters don't use inputs, so we store color here.
920            // We can't do the same trick on DropShadow because it does have two
921            // inputs.
922            instance.input_2_content_scale_and_offset = [color.r, color.g, color.b, color.a];
923        },
924        _ => {},
925    }
926
927    for (ref mut batch_textures, ref mut batch) in instances.iter_mut() {
928        if let Some(combined_textures) = batch_textures.combine_textures(textures) {
929            batch.push(instance);
930            // Update the batch textures to the newly combined batch textures
931            *batch_textures = combined_textures;
932            // is this really the intended behavior?
933            return;
934        }
935    }
936
937    let mut vec = memory.new_vec();
938    vec.push(instance);
939
940    instances.push((textures, vec));
941}
942
943// Information required to do a blit from a source to a target.
944#[cfg_attr(feature = "capture", derive(Serialize))]
945#[cfg_attr(feature = "replay", derive(Deserialize))]
946pub struct BlitJob {
947    pub source: RenderTaskId,
948    // Normalized region within the source task to blit from
949    pub source_rect: DeviceIntRect,
950    pub target_rect: DeviceIntRect,
951}
952
953#[cfg_attr(feature = "capture", derive(Serialize))]
954#[cfg_attr(feature = "replay", derive(Deserialize))]
955#[repr(C)]
956#[derive(Clone, Debug)]
957pub struct LineDecorationJob {
958    pub task_rect: DeviceRect,
959    pub local_size: LayoutSize,
960    pub wavy_line_thickness: f32,
961    pub style: i32,
962    pub axis_select: f32,
963}
964
965fn build_mask_tasks(
966    info: &MaskSubPass,
967    render_task_address: RenderTaskAddress,
968    task_world_rect: WorldRect,
969    target_rect: DeviceIntRect,
970    main_prim_address: GpuBufferAddress,
971    prim_spatial_node_index: SpatialNodeIndex,
972    raster_spatial_node_index: SpatialNodeIndex,
973    clip_store: &ClipStore,
974    data_stores: &DataStores,
975    spatial_tree: &SpatialTree,
976    gpu_buffer_builder: &mut GpuBufferBuilder,
977    transforms: &mut TransformPalette,
978    render_tasks: &RenderTaskGraph,
979    results: &mut ClipMaskInstanceList,
980    memory: &FrameMemory,
981) {
982    for i in 0 .. info.clip_node_range.count {
983        let clip_instance = clip_store.get_instance_from_range(&info.clip_node_range, i);
984        let clip_node = &data_stores.clip[clip_instance.handle];
985
986        let (clip_address, fast_path) = match clip_node.item.kind {
987            ClipItemKind::RoundedRectangle { rect, radius, mode } => {
988                let (fast_path, clip_address) = if radius.can_use_fast_path_in(&rect) {
989                    let mut writer = gpu_buffer_builder.f32.write_blocks(3);
990                    writer.push_one(rect);
991                    writer.push_one([
992                        radius.bottom_right.width,
993                        radius.top_right.width,
994                        radius.bottom_left.width,
995                        radius.top_left.width,
996                    ]);
997                    writer.push_one([mode as i32 as f32, 0.0, 0.0, 0.0]);
998                    let clip_address = writer.finish();
999
1000                    (true, clip_address)
1001                } else {
1002                    let mut writer = gpu_buffer_builder.f32.write_blocks(4);
1003                    writer.push_one(rect);
1004                    writer.push_one([
1005                        radius.top_left.width,
1006                        radius.top_left.height,
1007                        radius.top_right.width,
1008                        radius.top_right.height,
1009                    ]);
1010                    writer.push_one([
1011                        radius.bottom_left.width,
1012                        radius.bottom_left.height,
1013                        radius.bottom_right.width,
1014                        radius.bottom_right.height,
1015                    ]);
1016                    writer.push_one([mode as i32 as f32, 0.0, 0.0, 0.0]);
1017                    let clip_address = writer.finish();
1018
1019                    (false, clip_address)
1020                };
1021
1022                (clip_address, fast_path)
1023            }
1024            ClipItemKind::Rectangle { rect, mode, .. } => {
1025                let mut writer = gpu_buffer_builder.f32.write_blocks(3);
1026                writer.push_one(rect);
1027                writer.push_one([0.0, 0.0, 0.0, 0.0]);
1028                writer.push_one([mode as i32 as f32, 0.0, 0.0, 0.0]);
1029                let clip_address = writer.finish();
1030
1031                (clip_address, true)
1032            }
1033            ClipItemKind::BoxShadow { .. } => {
1034                panic!("bug: box-shadow clips not expected on non-legacy rect/quads");
1035            }
1036            ClipItemKind::Image { rect, .. } => {
1037                let clip_transform_id = transforms.get_id(
1038                    clip_node.item.spatial_node_index,
1039                    raster_spatial_node_index,
1040                    spatial_tree,
1041                );
1042
1043                let is_same_coord_system = spatial_tree.is_matching_coord_system(
1044                    prim_spatial_node_index,
1045                    raster_spatial_node_index,
1046                );
1047
1048                let pattern = Pattern::color(ColorF::WHITE);
1049                let clip_needs_scissor_rect = !is_same_coord_system;
1050                let mut quad_flags = QuadFlags::IS_MASK;
1051
1052                if is_same_coord_system {
1053                    quad_flags |= QuadFlags::APPLY_RENDER_TASK_CLIP;
1054                }
1055
1056                for tile in clip_store.visible_mask_tiles(&clip_instance) {
1057                    let clip_prim_address = quad::write_prim_blocks(
1058                        &mut gpu_buffer_builder.f32,
1059                        rect,
1060                        rect,
1061                        pattern.base_color,
1062                        pattern.texture_input.task_id,
1063                        &[QuadSegment {
1064                            rect: tile.tile_rect,
1065                            task_id: tile.task_id,
1066                        }],
1067                        ScaleOffset::identity(),
1068                    );
1069
1070                    let texture = render_tasks
1071                        .resolve_texture(tile.task_id)
1072                        .expect("bug: texture not found for tile");
1073
1074                    quad::add_to_batch(
1075                        PatternKind::ColorOrTexture,
1076                        PatternShaderInput::default(),
1077                        render_task_address,
1078                        clip_transform_id,
1079                        clip_prim_address,
1080                        quad_flags,
1081                        EdgeAaSegmentMask::empty(),
1082                        0,
1083                        tile.task_id,
1084                        ZBufferId(0),
1085                        render_tasks,
1086                        gpu_buffer_builder,
1087                        |_, prim| {
1088                            if clip_needs_scissor_rect {
1089                                results
1090                                    .image_mask_instances_with_scissor
1091                                    .entry((target_rect, texture))
1092                                    .or_insert_with(|| memory.new_vec())
1093                                    .push(prim);
1094                            } else {
1095                                results
1096                                    .image_mask_instances
1097                                    .entry(texture)
1098                                    .or_insert_with(|| memory.new_vec())
1099                                    .push(prim);
1100                            }
1101                        }
1102                    );
1103                }
1104
1105                // TODO(gw): For now, we skip the main mask prim below for image masks. Perhaps
1106                //           we can better merge the logic together?
1107                // TODO(gw): How to efficiently handle if the image-mask rect doesn't cover local prim rect?
1108                continue;
1109            }
1110        };
1111
1112        let prim_spatial_node = spatial_tree.get_spatial_node(prim_spatial_node_index);
1113        let clip_spatial_node = spatial_tree.get_spatial_node(clip_node.item.spatial_node_index);
1114        let raster_spatial_node = spatial_tree.get_spatial_node(raster_spatial_node_index);
1115        let raster_clip = raster_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id;
1116
1117        let (clip_space, clip_transform_id, main_prim_address, prim_transform_id, is_same_coord_system) = if raster_clip {
1118            let prim_transform_id = TransformPaletteId::IDENTITY;
1119            let pattern = Pattern::color(ColorF::WHITE);
1120
1121            let clip_transform_id = transforms.get_id(
1122                raster_spatial_node_index,
1123                clip_node.item.spatial_node_index,
1124                spatial_tree,
1125            );
1126
1127            let main_prim_address = quad::write_prim_blocks(
1128                &mut gpu_buffer_builder.f32,
1129                task_world_rect.cast_unit(),
1130                task_world_rect.cast_unit(),
1131                pattern.base_color,
1132                pattern.texture_input.task_id,
1133                &[],
1134                ScaleOffset::identity(),
1135            );
1136
1137            (ClipSpace::Raster, clip_transform_id, main_prim_address, prim_transform_id, true)
1138        } else {
1139            let prim_transform_id = transforms.get_id(
1140                prim_spatial_node_index,
1141                raster_spatial_node_index,
1142                spatial_tree,
1143            );
1144
1145            let clip_transform_id = if prim_spatial_node.coordinate_system_id < clip_spatial_node.coordinate_system_id {
1146                transforms.get_id(
1147                    clip_node.item.spatial_node_index,
1148                    prim_spatial_node_index,
1149                    spatial_tree,
1150                )
1151            } else {
1152                transforms.get_id(
1153                    prim_spatial_node_index,
1154                    clip_node.item.spatial_node_index,
1155                    spatial_tree,
1156                )
1157            };
1158
1159            let is_same_coord_system = spatial_tree.is_matching_coord_system(
1160                prim_spatial_node_index,
1161                raster_spatial_node_index,
1162            );
1163
1164            (ClipSpace::Primitive, clip_transform_id, main_prim_address, prim_transform_id, is_same_coord_system)
1165        };
1166
1167        let clip_needs_scissor_rect = !is_same_coord_system;
1168
1169        let quad_flags = if is_same_coord_system {
1170            QuadFlags::APPLY_RENDER_TASK_CLIP
1171        } else {
1172            QuadFlags::empty()
1173        };
1174
1175        quad::add_to_batch(
1176            PatternKind::Mask,
1177            PatternShaderInput::default(),
1178            render_task_address,
1179            prim_transform_id,
1180            main_prim_address,
1181            quad_flags,
1182            EdgeAaSegmentMask::all(),
1183            INVALID_SEGMENT_INDEX as u8,
1184            RenderTaskId::INVALID,
1185            ZBufferId(0),
1186            render_tasks,
1187            gpu_buffer_builder,
1188            |_, prim| {
1189                let instance = MaskInstance {
1190                    prim,
1191                    clip_transform_id,
1192                    clip_address: clip_address.as_int(),
1193                    clip_space: clip_space.as_int(),
1194                    unused: 0,
1195                };
1196
1197                if clip_needs_scissor_rect {
1198                    if fast_path {
1199                        results.mask_instances_fast_with_scissor
1200                               .entry(target_rect)
1201                               .or_insert_with(|| memory.new_vec())
1202                               .push(instance);
1203                    } else {
1204                        results.mask_instances_slow_with_scissor
1205                               .entry(target_rect)
1206                               .or_insert_with(|| memory.new_vec())
1207                               .push(instance);
1208                    }
1209                } else {
1210                    if fast_path {
1211                        results.mask_instances_fast.push(instance);
1212                    } else {
1213                        results.mask_instances_slow.push(instance);
1214                    }
1215                }
1216            }
1217        );
1218    }
1219}
1220
1221fn build_sub_pass(
1222    task_id: RenderTaskId,
1223    task: &RenderTask,
1224    gpu_buffer_builder: &mut GpuBufferBuilder,
1225    render_tasks: &RenderTaskGraph,
1226    transforms: &mut TransformPalette,
1227    ctx: &RenderTargetContext,
1228    output: &mut ClipMaskInstanceList,
1229) {
1230    if let Some(ref sub_pass) = task.sub_pass {
1231        match sub_pass {
1232            SubPass::Masks { ref masks } => {
1233                let render_task_address = task_id.into();
1234                let target_rect = task.get_target_rect();
1235
1236                let (device_pixel_scale, content_origin, raster_spatial_node_index) = match task.kind {
1237                    RenderTaskKind::Picture(ref info) => {
1238                        (info.device_pixel_scale, info.content_origin, info.raster_spatial_node_index)
1239                    }
1240                    RenderTaskKind::Empty(ref info) => {
1241                        (info.device_pixel_scale, info.content_origin, info.raster_spatial_node_index)
1242                    }
1243                    RenderTaskKind::Prim(ref info) => {
1244                        (info.device_pixel_scale, info.content_origin, info.raster_spatial_node_index)
1245                    }
1246                    _ => panic!("unexpected: {}", task.kind.as_str()),
1247                };
1248
1249                let content_rect = DeviceRect::new(
1250                    content_origin,
1251                    content_origin + target_rect.size().to_f32(),
1252                );
1253
1254                build_mask_tasks(
1255                    masks,
1256                    render_task_address,
1257                    content_rect / device_pixel_scale,
1258                    target_rect,
1259                    masks.prim_address_f,
1260                    masks.prim_spatial_node_index,
1261                    raster_spatial_node_index,
1262                    ctx.clip_store,
1263                    ctx.data_stores,
1264                    ctx.spatial_tree,
1265                    gpu_buffer_builder,
1266                    transforms,
1267                    render_tasks,
1268                    output,
1269                    &ctx.frame_memory,
1270                );
1271            }
1272        }
1273    }
1274}