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, BlurEdgeMode};
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(), 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                    info.edge_mode,
411                    task_id.into(),
412                    task.children[0],
413                    render_tasks,
414                    &ctx.frame_memory,
415                );
416            }
417            RenderTaskKind::HorizontalBlur(ref info) => {
418                if self.target_kind == RenderTargetKind::Alpha {
419                    self.clears.push((target_rect, ColorF::TRANSPARENT));
420                }
421                add_blur_instances(
422                    &mut self.horizontal_blurs,
423                    BlurDirection::Horizontal,
424                    info.blur_std_deviation,
425                    info.blur_region,
426                    info.edge_mode,
427                    task_id.into(),
428                    task.children[0],
429                    render_tasks,
430                    &ctx.frame_memory,
431                );
432            }
433            RenderTaskKind::Picture(ref pic_task) => {
434                if let Some(ref resolve_op) = pic_task.resolve_op {
435                    self.resolve_ops.push(resolve_op.clone());
436                }
437                self.alpha_tasks.push(task_id);
438            }
439            RenderTaskKind::SvgFilter(ref task_info) => {
440                add_svg_filter_instances(
441                    &mut self.svg_filters,
442                    render_tasks,
443                    &task_info.info,
444                    task_id,
445                    task.children.get(0).cloned(),
446                    task.children.get(1).cloned(),
447                    task_info.extra_gpu_cache_handle.map(|handle| gpu_cache.get_address(&handle)),
448                    &ctx.frame_memory,
449                )
450            }
451            RenderTaskKind::SVGFENode(ref task_info) => {
452                add_svg_filter_node_instances(
453                    &mut self.svg_nodes,
454                    render_tasks,
455                    &task_info,
456                    task,
457                    task.children.get(0).cloned(),
458                    task.children.get(1).cloned(),
459                    task_info.extra_gpu_cache_handle.map(|handle| gpu_cache.get_address(&handle)),
460                    &ctx.frame_memory,
461                )
462            }
463            RenderTaskKind::Empty(..) => {
464                // TODO(gw): Could likely be more efficient by choosing to clear to 0 or 1
465                //           based on the clip chain, or even skipping clear and masking the
466                //           prim region with blend disabled.
467                self.clears.push((target_rect, ColorF::WHITE));
468            }
469            RenderTaskKind::CacheMask(ref task_info) => {
470                let clear_to_one = self.clip_batcher.add(
471                    task_info.clip_node_range,
472                    task_info.root_spatial_node_index,
473                    render_tasks,
474                    gpu_cache,
475                    clip_store,
476                    transforms,
477                    task_info.actual_rect,
478                    task_info.device_pixel_scale,
479                    target_rect.min.to_f32(),
480                    task_info.actual_rect.min,
481                    ctx,
482                );
483                if task_info.clear_to_one || clear_to_one {
484                    self.clears.push((target_rect, ColorF::WHITE));
485                }
486            }
487            RenderTaskKind::ClipRegion(ref region_task) => {
488                if region_task.clear_to_one {
489                    self.clears.push((target_rect, ColorF::WHITE));
490                }
491                let device_rect = DeviceRect::from_size(
492                    target_rect.size().to_f32(),
493                );
494                self.clip_batcher.add_clip_region(
495                    region_task.local_pos,
496                    device_rect,
497                    region_task.clip_data.clone(),
498                    target_rect.min.to_f32(),
499                    DevicePoint::zero(),
500                    region_task.device_pixel_scale.0,
501                );
502            }
503            RenderTaskKind::Scaling(ref info) => {
504                add_scaling_instances(
505                    info,
506                    &mut self.scalings,
507                    task,
508                    task.children.first().map(|&child| &render_tasks[child]),
509                    &ctx.frame_memory,
510                );
511            }
512            RenderTaskKind::Blit(ref task_info) => {
513                let target_rect = task.get_target_rect();
514                self.blits.push(BlitJob {
515                    source: task_info.source,
516                    source_rect: task_info.source_rect,
517                    target_rect,
518                });
519            }
520            RenderTaskKind::LineDecoration(ref info) => {
521                self.clears.push((target_rect, ColorF::TRANSPARENT));
522
523                self.line_decorations.push(LineDecorationJob {
524                    task_rect: target_rect.to_f32(),
525                    local_size: info.local_size,
526                    style: info.style as i32,
527                    axis_select: match info.orientation {
528                        LineOrientation::Horizontal => 0.0,
529                        LineOrientation::Vertical => 1.0,
530                    },
531                    wavy_line_thickness: info.wavy_line_thickness,
532                });
533            }
534            RenderTaskKind::Border(ref task_info) => {
535                self.clears.push((target_rect, ColorF::TRANSPARENT));
536
537                let task_origin = target_rect.min.to_f32();
538                // TODO(gw): Clone here instead of a move of this vec, since the frame
539                //           graph is immutable by this point. It's rare that borders
540                //           are drawn since they are persisted in the texture cache,
541                //           but perhaps this could be improved in future.
542                let instances = task_info.instances.clone();
543                for mut instance in instances {
544                    // TODO(gw): It may be better to store the task origin in
545                    //           the render task data instead of per instance.
546                    instance.task_origin = task_origin;
547                    if instance.flags & STYLE_MASK == STYLE_SOLID {
548                        self.border_segments_solid.push(instance);
549                    } else {
550                        self.border_segments_complex.push(instance);
551                    }
552                }
553            }
554            RenderTaskKind::FastLinearGradient(ref task_info) => {
555                self.fast_linear_gradients.push(task_info.to_instance(&target_rect));
556            }
557            RenderTaskKind::LinearGradient(ref task_info) => {
558                self.linear_gradients.push(task_info.to_instance(&target_rect));
559            }
560            RenderTaskKind::RadialGradient(ref task_info) => {
561                self.radial_gradients.push(task_info.to_instance(&target_rect));
562            }
563            RenderTaskKind::ConicGradient(ref task_info) => {
564                self.conic_gradients.push(task_info.to_instance(&target_rect));
565            }
566            RenderTaskKind::Image(..) |
567            RenderTaskKind::Cached(..) |
568            RenderTaskKind::TileComposite(..) => {
569                panic!("Should not be added to color target!");
570            }
571            RenderTaskKind::Readback(..) => {}
572            #[cfg(test)]
573            RenderTaskKind::Test(..) => {}
574        }
575
576        build_sub_pass(
577            task_id,
578            task,
579            gpu_buffer_builder,
580            render_tasks,
581            transforms,
582            ctx,
583            &mut self.clip_masks,
584        );
585    }
586
587    pub fn needs_depth(&self) -> bool {
588        self.alpha_batch_containers.iter().any(|ab| {
589            !ab.opaque_batches.is_empty()
590        })
591    }
592}
593
594#[cfg_attr(feature = "capture", derive(Serialize))]
595#[cfg_attr(feature = "replay", derive(Deserialize))]
596#[derive(Debug, PartialEq, Clone)]
597pub struct ResolveOp {
598    pub src_task_ids: Vec<RenderTaskId>,
599    pub dest_task_id: RenderTaskId,
600}
601
602#[cfg_attr(feature = "capture", derive(Serialize))]
603#[cfg_attr(feature = "replay", derive(Deserialize))]
604pub enum PictureCacheTargetKind {
605    Draw {
606        alpha_batch_container: AlphaBatchContainer,
607    },
608    Blit {
609        task_id: RenderTaskId,
610        sub_rect_offset: DeviceIntVector2D,
611    },
612}
613
614#[cfg_attr(feature = "capture", derive(Serialize))]
615#[cfg_attr(feature = "replay", derive(Deserialize))]
616pub struct PictureCacheTarget {
617    pub surface: ResolvedSurfaceTexture,
618    pub kind: PictureCacheTargetKind,
619    pub clear_color: Option<ColorF>,
620    pub dirty_rect: DeviceIntRect,
621    pub valid_rect: DeviceIntRect,
622}
623
624fn add_blur_instances(
625    instances: &mut FastHashMap<TextureSource, FrameVec<BlurInstance>>,
626    blur_direction: BlurDirection,
627    blur_std_deviation: f32,
628    blur_region: DeviceIntSize,
629    edge_mode: BlurEdgeMode,
630    task_address: RenderTaskAddress,
631    src_task_id: RenderTaskId,
632    render_tasks: &RenderTaskGraph,
633    memory: &FrameMemory,
634) {
635    let source = render_tasks[src_task_id].get_texture_source();
636
637    let instance = BlurInstance {
638        task_address,
639        src_task_address: src_task_id.into(),
640        blur_direction: blur_direction.as_int(),
641        blur_std_deviation,
642        edge_mode: edge_mode.as_int(),
643        blur_region: blur_region.to_f32(),
644    };
645
646    instances
647        .entry(source)
648        .or_insert_with(|| memory.new_vec())
649        .push(instance);
650}
651
652fn add_scaling_instances(
653    task: &ScalingTask,
654    instances: &mut FastHashMap<TextureSource, FrameVec<ScalingInstance>>,
655    target_task: &RenderTask,
656    source_task: Option<&RenderTask>,
657    memory: &FrameMemory,
658) {
659    let target_rect = target_task
660        .get_target_rect()
661        .inner_box(task.padding)
662        .to_f32();
663
664    let source = source_task.unwrap().get_texture_source();
665
666    let source_rect = source_task.unwrap().get_target_rect().to_f32();
667
668    instances
669        .entry(source)
670        .or_insert_with(|| memory.new_vec())
671        .push(ScalingInstance::new(
672            target_rect,
673            source_rect,
674            source.uses_normalized_uvs(),
675        ));
676}
677
678fn add_svg_filter_instances(
679    instances: &mut FrameVec<(BatchTextures, FrameVec<SvgFilterInstance>)>,
680    render_tasks: &RenderTaskGraph,
681    filter: &SvgFilterInfo,
682    task_id: RenderTaskId,
683    input_1_task: Option<RenderTaskId>,
684    input_2_task: Option<RenderTaskId>,
685    extra_data_address: Option<GpuCacheAddress>,
686    memory: &FrameMemory,
687) {
688    let mut textures = BatchTextures::empty();
689
690    if let Some(id) = input_1_task {
691        textures.input.colors[0] = render_tasks[id].get_texture_source();
692    }
693
694    if let Some(id) = input_2_task {
695        textures.input.colors[1] = render_tasks[id].get_texture_source();
696    }
697
698    let kind = match filter {
699        SvgFilterInfo::Blend(..) => 0,
700        SvgFilterInfo::Flood(..) => 1,
701        SvgFilterInfo::LinearToSrgb => 2,
702        SvgFilterInfo::SrgbToLinear => 3,
703        SvgFilterInfo::Opacity(..) => 4,
704        SvgFilterInfo::ColorMatrix(..) => 5,
705        SvgFilterInfo::DropShadow(..) => 6,
706        SvgFilterInfo::Offset(..) => 7,
707        SvgFilterInfo::ComponentTransfer(..) => 8,
708        SvgFilterInfo::Identity => 9,
709        SvgFilterInfo::Composite(..) => 10,
710    };
711
712    let input_count = match filter {
713        SvgFilterInfo::Flood(..) => 0,
714
715        SvgFilterInfo::LinearToSrgb |
716        SvgFilterInfo::SrgbToLinear |
717        SvgFilterInfo::Opacity(..) |
718        SvgFilterInfo::ColorMatrix(..) |
719        SvgFilterInfo::Offset(..) |
720        SvgFilterInfo::ComponentTransfer(..) |
721        SvgFilterInfo::Identity => 1,
722
723        // Not techincally a 2 input filter, but we have 2 inputs here: original content & blurred content.
724        SvgFilterInfo::DropShadow(..) |
725        SvgFilterInfo::Blend(..) |
726        SvgFilterInfo::Composite(..) => 2,
727    };
728
729    let generic_int = match filter {
730        SvgFilterInfo::Blend(mode) => *mode as u16,
731        SvgFilterInfo::ComponentTransfer(data) =>
732            (data.r_func.to_int() << 12 |
733             data.g_func.to_int() << 8 |
734             data.b_func.to_int() << 4 |
735             data.a_func.to_int()) as u16,
736        SvgFilterInfo::Composite(operator) =>
737            operator.as_int() as u16,
738        SvgFilterInfo::LinearToSrgb |
739        SvgFilterInfo::SrgbToLinear |
740        SvgFilterInfo::Flood(..) |
741        SvgFilterInfo::Opacity(..) |
742        SvgFilterInfo::ColorMatrix(..) |
743        SvgFilterInfo::DropShadow(..) |
744        SvgFilterInfo::Offset(..) |
745        SvgFilterInfo::Identity => 0,
746    };
747
748    let instance = SvgFilterInstance {
749        task_address: task_id.into(),
750        input_1_task_address: input_1_task.map(|id| id.into()).unwrap_or(RenderTaskAddress(0)),
751        input_2_task_address: input_2_task.map(|id| id.into()).unwrap_or(RenderTaskAddress(0)),
752        kind,
753        input_count,
754        generic_int,
755        padding: 0,
756        extra_data_address: extra_data_address.unwrap_or(GpuCacheAddress::INVALID),
757    };
758
759    for (ref mut batch_textures, ref mut batch) in instances.iter_mut() {
760        if let Some(combined_textures) = batch_textures.combine_textures(textures) {
761            batch.push(instance);
762            // Update the batch textures to the newly combined batch textures
763            *batch_textures = combined_textures;
764            return;
765        }
766    }
767
768    let mut vec = memory.new_vec();
769    vec.push(instance);
770
771    instances.push((textures, vec));
772}
773
774/// Generates SVGFEFilterInstances from a single SVGFEFilterTask, this is what
775/// prepares vertex data for the shader, and adds it to the appropriate batch.
776///
777/// The interesting parts of the handling of SVG filters are:
778/// * scene_building.rs : wrap_prim_with_filters
779/// * picture.rs : get_coverage_svgfe
780/// * render_task.rs : new_svg_filter_graph
781/// * render_target.rs : add_svg_filter_node_instances (you are here)
782fn add_svg_filter_node_instances(
783    instances: &mut FrameVec<(BatchTextures, FrameVec<SVGFEFilterInstance>)>,
784    render_tasks: &RenderTaskGraph,
785    task_info: &SVGFEFilterTask,
786    target_task: &RenderTask,
787    input_1_task: Option<RenderTaskId>,
788    input_2_task: Option<RenderTaskId>,
789    extra_data_address: Option<GpuCacheAddress>,
790    memory: &FrameMemory,
791) {
792    let node = &task_info.node;
793    let op = &task_info.op;
794    let mut textures = BatchTextures::empty();
795
796    // We have to undo the inflate here as the inflated target rect is meant to
797    // have a blank border
798    let target_rect = target_task
799        .get_target_rect()
800        .inner_box(DeviceIntSideOffsets::new(node.inflate as i32, node.inflate as i32, node.inflate as i32, node.inflate as i32))
801        .to_f32();
802
803    let mut instance = SVGFEFilterInstance {
804        target_rect,
805        input_1_content_scale_and_offset: [0.0; 4],
806        input_2_content_scale_and_offset: [0.0; 4],
807        input_1_task_address: RenderTaskId::INVALID.into(),
808        input_2_task_address: RenderTaskId::INVALID.into(),
809        kind: 0,
810        input_count: node.inputs.len() as u16,
811        extra_data_address: extra_data_address.unwrap_or(GpuCacheAddress::INVALID),
812    };
813
814    // Must match FILTER_* in cs_svg_filter_node.glsl
815    instance.kind = match op {
816        // Identity does not modify color, no linear case
817        FilterGraphOp::SVGFEIdentity => 0,
818        // SourceGraphic does not have its own shader mode, it uses Identity.
819        FilterGraphOp::SVGFESourceGraphic => 0,
820        // SourceAlpha does not have its own shader mode, it uses ToAlpha.
821        FilterGraphOp::SVGFESourceAlpha => 4,
822        // Opacity scales the entire rgba color, so it does not need a linear
823        // case as the rgb / a ratio does not change (sRGB is a curve on the RGB
824        // before alpha multiply, not after)
825        FilterGraphOp::SVGFEOpacity{..} => 2,
826        FilterGraphOp::SVGFEToAlpha => 4,
827        FilterGraphOp::SVGFEBlendColor => {match node.linear {false => 6, true => 7}},
828        FilterGraphOp::SVGFEBlendColorBurn => {match node.linear {false => 8, true => 9}},
829        FilterGraphOp::SVGFEBlendColorDodge => {match node.linear {false => 10, true => 11}},
830        FilterGraphOp::SVGFEBlendDarken => {match node.linear {false => 12, true => 13}},
831        FilterGraphOp::SVGFEBlendDifference => {match node.linear {false => 14, true => 15}},
832        FilterGraphOp::SVGFEBlendExclusion => {match node.linear {false => 16, true => 17}},
833        FilterGraphOp::SVGFEBlendHardLight => {match node.linear {false => 18, true => 19}},
834        FilterGraphOp::SVGFEBlendHue => {match node.linear {false => 20, true => 21}},
835        FilterGraphOp::SVGFEBlendLighten => {match node.linear {false => 22, true => 23}},
836        FilterGraphOp::SVGFEBlendLuminosity => {match node.linear {false => 24, true => 25}},
837        FilterGraphOp::SVGFEBlendMultiply => {match node.linear {false => 26, true => 27}},
838        FilterGraphOp::SVGFEBlendNormal => {match node.linear {false => 28, true => 29}},
839        FilterGraphOp::SVGFEBlendOverlay => {match node.linear {false => 30, true => 31}},
840        FilterGraphOp::SVGFEBlendSaturation => {match node.linear {false => 32, true => 33}},
841        FilterGraphOp::SVGFEBlendScreen => {match node.linear {false => 34, true => 35}},
842        FilterGraphOp::SVGFEBlendSoftLight => {match node.linear {false => 36, true => 37}},
843        FilterGraphOp::SVGFEColorMatrix{..} => {match node.linear {false => 38, true => 39}},
844        FilterGraphOp::SVGFEComponentTransfer => unreachable!(),
845        FilterGraphOp::SVGFEComponentTransferInterned{..} => {match node.linear {false => 40, true => 41}},
846        FilterGraphOp::SVGFECompositeArithmetic{..} => {match node.linear {false => 42, true => 43}},
847        FilterGraphOp::SVGFECompositeATop => {match node.linear {false => 44, true => 45}},
848        FilterGraphOp::SVGFECompositeIn => {match node.linear {false => 46, true => 47}},
849        FilterGraphOp::SVGFECompositeLighter => {match node.linear {false => 48, true => 49}},
850        FilterGraphOp::SVGFECompositeOut => {match node.linear {false => 50, true => 51}},
851        FilterGraphOp::SVGFECompositeOver => {match node.linear {false => 52, true => 53}},
852        FilterGraphOp::SVGFECompositeXOR => {match node.linear {false => 54, true => 55}},
853        FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => {match node.linear {false => 56, true => 57}},
854        FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => {match node.linear {false => 58, true => 59}},
855        FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => {match node.linear {false => 60, true => 61}},
856        FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {match node.linear {false => 62, true => 63}},
857        FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {match node.linear {false => 64, true => 65}},
858        FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {match node.linear {false => 66, true => 67}},
859        FilterGraphOp::SVGFEDisplacementMap{..} => {match node.linear {false => 68, true => 69}},
860        FilterGraphOp::SVGFEDropShadow{..} => {match node.linear {false => 70, true => 71}},
861        // feFlood takes an sRGB color and does no math on it, no linear case
862        FilterGraphOp::SVGFEFlood{..} => 72,
863        FilterGraphOp::SVGFEGaussianBlur{..} => {match node.linear {false => 74, true => 75}},
864        // feImage does not meaningfully modify the color of its input, though a
865        // case could be made for gamma-correct image scaling, that's a bit out
866        // of scope for now
867        FilterGraphOp::SVGFEImage{..} => 76,
868        FilterGraphOp::SVGFEMorphologyDilate{..} => {match node.linear {false => 80, true => 81}},
869        FilterGraphOp::SVGFEMorphologyErode{..} => {match node.linear {false => 82, true => 83}},
870        FilterGraphOp::SVGFESpecularLightingDistant{..} => {match node.linear {false => 86, true => 87}},
871        FilterGraphOp::SVGFESpecularLightingPoint{..} => {match node.linear {false => 88, true => 89}},
872        FilterGraphOp::SVGFESpecularLightingSpot{..} => {match node.linear {false => 90, true => 91}},
873        // feTile does not modify color, no linear case
874        FilterGraphOp::SVGFETile => 92,
875        FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} => {match node.linear {false => 94, true => 95}},
876        FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} => {match node.linear {false => 96, true => 97}},
877        FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} => {match node.linear {false => 98, true => 99}},
878        FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {match node.linear {false => 100, true => 101}},
879    };
880
881    // This is a bit of an ugly way to do this, but avoids code duplication.
882    let mut resolve_input = |index: usize, src_task: Option<RenderTaskId>| -> (RenderTaskAddress, [f32; 4]) {
883        let mut src_task_id = RenderTaskId::INVALID;
884        let mut resolved_scale_and_offset: [f32; 4] = [0.0; 4];
885        if let Some(input) = node.inputs.get(index) {
886            src_task_id = src_task.unwrap();
887            let src_task = &render_tasks[src_task_id];
888
889            textures.input.colors[index] = src_task.get_texture_source();
890            let src_task_size = src_task.location.size();
891            let src_scale_x = (src_task_size.width as f32 - input.inflate as f32 * 2.0) / input.subregion.width();
892            let src_scale_y = (src_task_size.height as f32 - input.inflate as f32 * 2.0) / input.subregion.height();
893            let scale_x = src_scale_x * node.subregion.width();
894            let scale_y = src_scale_y * node.subregion.height();
895            let offset_x = src_scale_x * (node.subregion.min.x - input.subregion.min.x) + input.inflate as f32;
896            let offset_y = src_scale_y * (node.subregion.min.y - input.subregion.min.y) + input.inflate as f32;
897            resolved_scale_and_offset = [
898                scale_x,
899                scale_y,
900                offset_x,
901                offset_y];
902        }
903        let address: RenderTaskAddress = src_task_id.into();
904        (address, resolved_scale_and_offset)
905    };
906    (instance.input_1_task_address, instance.input_1_content_scale_and_offset) = resolve_input(0, input_1_task);
907    (instance.input_2_task_address, instance.input_2_content_scale_and_offset) = resolve_input(1, input_2_task);
908
909    // Additional instance modifications for certain filters
910    match op {
911        FilterGraphOp::SVGFEOpacity { valuebinding: _, value } => {
912            // opacity only has one input so we can use the other
913            // components to store the opacity value
914            instance.input_2_content_scale_and_offset = [*value, 0.0, 0.0, 0.0];
915        },
916        FilterGraphOp::SVGFEMorphologyDilate { radius_x, radius_y } |
917        FilterGraphOp::SVGFEMorphologyErode { radius_x, radius_y } => {
918            // morphology filters only use one input, so we use the
919            // second offset coord to store the radius values.
920            instance.input_2_content_scale_and_offset = [*radius_x, *radius_y, 0.0, 0.0];
921        },
922        FilterGraphOp::SVGFEFlood { color } => {
923            // flood filters don't use inputs, so we store color here.
924            // We can't do the same trick on DropShadow because it does have two
925            // inputs.
926            instance.input_2_content_scale_and_offset = [color.r, color.g, color.b, color.a];
927        },
928        _ => {},
929    }
930
931    for (ref mut batch_textures, ref mut batch) in instances.iter_mut() {
932        if let Some(combined_textures) = batch_textures.combine_textures(textures) {
933            batch.push(instance);
934            // Update the batch textures to the newly combined batch textures
935            *batch_textures = combined_textures;
936            // is this really the intended behavior?
937            return;
938        }
939    }
940
941    let mut vec = memory.new_vec();
942    vec.push(instance);
943
944    instances.push((textures, vec));
945}
946
947// Information required to do a blit from a source to a target.
948#[cfg_attr(feature = "capture", derive(Serialize))]
949#[cfg_attr(feature = "replay", derive(Deserialize))]
950pub struct BlitJob {
951    pub source: RenderTaskId,
952    // Normalized region within the source task to blit from
953    pub source_rect: DeviceIntRect,
954    pub target_rect: DeviceIntRect,
955}
956
957#[cfg_attr(feature = "capture", derive(Serialize))]
958#[cfg_attr(feature = "replay", derive(Deserialize))]
959#[repr(C)]
960#[derive(Clone, Debug)]
961pub struct LineDecorationJob {
962    pub task_rect: DeviceRect,
963    pub local_size: LayoutSize,
964    pub wavy_line_thickness: f32,
965    pub style: i32,
966    pub axis_select: f32,
967}
968
969fn build_mask_tasks(
970    info: &MaskSubPass,
971    render_task_address: RenderTaskAddress,
972    task_world_rect: WorldRect,
973    target_rect: DeviceIntRect,
974    main_prim_address: GpuBufferAddress,
975    prim_spatial_node_index: SpatialNodeIndex,
976    raster_spatial_node_index: SpatialNodeIndex,
977    clip_store: &ClipStore,
978    data_stores: &DataStores,
979    spatial_tree: &SpatialTree,
980    gpu_buffer_builder: &mut GpuBufferBuilder,
981    transforms: &mut TransformPalette,
982    render_tasks: &RenderTaskGraph,
983    results: &mut ClipMaskInstanceList,
984    memory: &FrameMemory,
985) {
986    for i in 0 .. info.clip_node_range.count {
987        let clip_instance = clip_store.get_instance_from_range(&info.clip_node_range, i);
988        let clip_node = &data_stores.clip[clip_instance.handle];
989
990        let (clip_address, fast_path) = match clip_node.item.kind {
991            ClipItemKind::RoundedRectangle { rect, radius, mode } => {
992                let (fast_path, clip_address) = if radius.can_use_fast_path_in(&rect) {
993                    let mut writer = gpu_buffer_builder.f32.write_blocks(3);
994                    writer.push_one(rect);
995                    writer.push_one([
996                        radius.bottom_right.width,
997                        radius.top_right.width,
998                        radius.bottom_left.width,
999                        radius.top_left.width,
1000                    ]);
1001                    writer.push_one([mode as i32 as f32, 0.0, 0.0, 0.0]);
1002                    let clip_address = writer.finish();
1003
1004                    (true, clip_address)
1005                } else {
1006                    let mut writer = gpu_buffer_builder.f32.write_blocks(4);
1007                    writer.push_one(rect);
1008                    writer.push_one([
1009                        radius.top_left.width,
1010                        radius.top_left.height,
1011                        radius.top_right.width,
1012                        radius.top_right.height,
1013                    ]);
1014                    writer.push_one([
1015                        radius.bottom_left.width,
1016                        radius.bottom_left.height,
1017                        radius.bottom_right.width,
1018                        radius.bottom_right.height,
1019                    ]);
1020                    writer.push_one([mode as i32 as f32, 0.0, 0.0, 0.0]);
1021                    let clip_address = writer.finish();
1022
1023                    (false, clip_address)
1024                };
1025
1026                (clip_address, fast_path)
1027            }
1028            ClipItemKind::Rectangle { rect, mode, .. } => {
1029                let mut writer = gpu_buffer_builder.f32.write_blocks(3);
1030                writer.push_one(rect);
1031                writer.push_one([0.0, 0.0, 0.0, 0.0]);
1032                writer.push_one([mode as i32 as f32, 0.0, 0.0, 0.0]);
1033                let clip_address = writer.finish();
1034
1035                (clip_address, true)
1036            }
1037            ClipItemKind::BoxShadow { .. } => {
1038                panic!("bug: box-shadow clips not expected on non-legacy rect/quads");
1039            }
1040            ClipItemKind::Image { rect, .. } => {
1041                let clip_transform_id = transforms.get_id(
1042                    clip_node.item.spatial_node_index,
1043                    raster_spatial_node_index,
1044                    spatial_tree,
1045                );
1046
1047                let is_same_coord_system = spatial_tree.is_matching_coord_system(
1048                    prim_spatial_node_index,
1049                    raster_spatial_node_index,
1050                );
1051
1052                let pattern = Pattern::color(ColorF::WHITE);
1053                let clip_needs_scissor_rect = !is_same_coord_system;
1054                let mut quad_flags = QuadFlags::IS_MASK;
1055
1056                if is_same_coord_system {
1057                    quad_flags |= QuadFlags::APPLY_RENDER_TASK_CLIP;
1058                }
1059
1060                for tile in clip_store.visible_mask_tiles(&clip_instance) {
1061                    let clip_prim_address = quad::write_prim_blocks(
1062                        &mut gpu_buffer_builder.f32,
1063                        rect,
1064                        rect,
1065                        pattern.base_color,
1066                        pattern.texture_input.task_id,
1067                        &[QuadSegment {
1068                            rect: tile.tile_rect,
1069                            task_id: tile.task_id,
1070                        }],
1071                        ScaleOffset::identity(),
1072                    );
1073
1074                    let texture = render_tasks
1075                        .resolve_texture(tile.task_id)
1076                        .expect("bug: texture not found for tile");
1077
1078                    quad::add_to_batch(
1079                        PatternKind::ColorOrTexture,
1080                        PatternShaderInput::default(),
1081                        render_task_address,
1082                        clip_transform_id,
1083                        clip_prim_address,
1084                        quad_flags,
1085                        EdgeAaSegmentMask::empty(),
1086                        0,
1087                        tile.task_id,
1088                        ZBufferId(0),
1089                        render_tasks,
1090                        gpu_buffer_builder,
1091                        |_, prim| {
1092                            if clip_needs_scissor_rect {
1093                                results
1094                                    .image_mask_instances_with_scissor
1095                                    .entry((target_rect, texture))
1096                                    .or_insert_with(|| memory.new_vec())
1097                                    .push(prim);
1098                            } else {
1099                                results
1100                                    .image_mask_instances
1101                                    .entry(texture)
1102                                    .or_insert_with(|| memory.new_vec())
1103                                    .push(prim);
1104                            }
1105                        }
1106                    );
1107                }
1108
1109                // TODO(gw): For now, we skip the main mask prim below for image masks. Perhaps
1110                //           we can better merge the logic together?
1111                // TODO(gw): How to efficiently handle if the image-mask rect doesn't cover local prim rect?
1112                continue;
1113            }
1114        };
1115
1116        let prim_spatial_node = spatial_tree.get_spatial_node(prim_spatial_node_index);
1117        let clip_spatial_node = spatial_tree.get_spatial_node(clip_node.item.spatial_node_index);
1118        let raster_spatial_node = spatial_tree.get_spatial_node(raster_spatial_node_index);
1119        let raster_clip = raster_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id;
1120
1121        let (clip_space, clip_transform_id, main_prim_address, prim_transform_id, is_same_coord_system) = if raster_clip {
1122            let prim_transform_id = TransformPaletteId::IDENTITY;
1123            let pattern = Pattern::color(ColorF::WHITE);
1124
1125            let clip_transform_id = transforms.get_id(
1126                raster_spatial_node_index,
1127                clip_node.item.spatial_node_index,
1128                spatial_tree,
1129            );
1130
1131            let main_prim_address = quad::write_prim_blocks(
1132                &mut gpu_buffer_builder.f32,
1133                task_world_rect.cast_unit(),
1134                task_world_rect.cast_unit(),
1135                pattern.base_color,
1136                pattern.texture_input.task_id,
1137                &[],
1138                ScaleOffset::identity(),
1139            );
1140
1141            (ClipSpace::Raster, clip_transform_id, main_prim_address, prim_transform_id, true)
1142        } else {
1143            let prim_transform_id = transforms.get_id(
1144                prim_spatial_node_index,
1145                raster_spatial_node_index,
1146                spatial_tree,
1147            );
1148
1149            let clip_transform_id = if prim_spatial_node.coordinate_system_id < clip_spatial_node.coordinate_system_id {
1150                transforms.get_id(
1151                    clip_node.item.spatial_node_index,
1152                    prim_spatial_node_index,
1153                    spatial_tree,
1154                )
1155            } else {
1156                transforms.get_id(
1157                    prim_spatial_node_index,
1158                    clip_node.item.spatial_node_index,
1159                    spatial_tree,
1160                )
1161            };
1162
1163            let is_same_coord_system = spatial_tree.is_matching_coord_system(
1164                prim_spatial_node_index,
1165                raster_spatial_node_index,
1166            );
1167
1168            (ClipSpace::Primitive, clip_transform_id, main_prim_address, prim_transform_id, is_same_coord_system)
1169        };
1170
1171        let clip_needs_scissor_rect = !is_same_coord_system;
1172
1173        let quad_flags = if is_same_coord_system {
1174            QuadFlags::APPLY_RENDER_TASK_CLIP
1175        } else {
1176            QuadFlags::empty()
1177        };
1178
1179        quad::add_to_batch(
1180            PatternKind::Mask,
1181            PatternShaderInput::default(),
1182            render_task_address,
1183            prim_transform_id,
1184            main_prim_address,
1185            quad_flags,
1186            EdgeAaSegmentMask::all(),
1187            INVALID_SEGMENT_INDEX as u8,
1188            RenderTaskId::INVALID,
1189            ZBufferId(0),
1190            render_tasks,
1191            gpu_buffer_builder,
1192            |_, prim| {
1193                let instance = MaskInstance {
1194                    prim,
1195                    clip_transform_id,
1196                    clip_address: clip_address.as_int(),
1197                    clip_space: clip_space.as_int(),
1198                    unused: 0,
1199                };
1200
1201                if clip_needs_scissor_rect {
1202                    if fast_path {
1203                        results.mask_instances_fast_with_scissor
1204                               .entry(target_rect)
1205                               .or_insert_with(|| memory.new_vec())
1206                               .push(instance);
1207                    } else {
1208                        results.mask_instances_slow_with_scissor
1209                               .entry(target_rect)
1210                               .or_insert_with(|| memory.new_vec())
1211                               .push(instance);
1212                    }
1213                } else {
1214                    if fast_path {
1215                        results.mask_instances_fast.push(instance);
1216                    } else {
1217                        results.mask_instances_slow.push(instance);
1218                    }
1219                }
1220            }
1221        );
1222    }
1223}
1224
1225fn build_sub_pass(
1226    task_id: RenderTaskId,
1227    task: &RenderTask,
1228    gpu_buffer_builder: &mut GpuBufferBuilder,
1229    render_tasks: &RenderTaskGraph,
1230    transforms: &mut TransformPalette,
1231    ctx: &RenderTargetContext,
1232    output: &mut ClipMaskInstanceList,
1233) {
1234    if let Some(ref sub_pass) = task.sub_pass {
1235        match sub_pass {
1236            SubPass::Masks { ref masks } => {
1237                let render_task_address = task_id.into();
1238                let target_rect = task.get_target_rect();
1239
1240                let (device_pixel_scale, content_origin, raster_spatial_node_index) = match task.kind {
1241                    RenderTaskKind::Picture(ref info) => {
1242                        (info.device_pixel_scale, info.content_origin, info.raster_spatial_node_index)
1243                    }
1244                    RenderTaskKind::Empty(ref info) => {
1245                        (info.device_pixel_scale, info.content_origin, info.raster_spatial_node_index)
1246                    }
1247                    RenderTaskKind::Prim(ref info) => {
1248                        (info.device_pixel_scale, info.content_origin, info.raster_spatial_node_index)
1249                    }
1250                    _ => panic!("unexpected: {}", task.kind.as_str()),
1251                };
1252
1253                let content_rect = DeviceRect::new(
1254                    content_origin,
1255                    content_origin + target_rect.size().to_f32(),
1256                );
1257
1258                build_mask_tasks(
1259                    masks,
1260                    render_task_address,
1261                    content_rect / device_pixel_scale,
1262                    target_rect,
1263                    masks.prim_address_f,
1264                    masks.prim_spatial_node_index,
1265                    raster_spatial_node_index,
1266                    ctx.clip_store,
1267                    ctx.data_stores,
1268                    ctx.spatial_tree,
1269                    gpu_buffer_builder,
1270                    transforms,
1271                    render_tasks,
1272                    output,
1273                    &ctx.frame_memory,
1274                );
1275            }
1276        }
1277    }
1278}