tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

render_target.rs (50376B)


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