tor-browser

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

render_task.rs (109644B)


      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 use api::{LineStyle, LineOrientation, ClipMode, ColorF, FilterOpGraphPictureBufferId};
      6 use api::{MAX_RENDER_TASK_SIZE, SVGFE_GRAPH_MAX};
      7 use api::units::*;
      8 use std::time::Duration;
      9 use crate::box_shadow::BLUR_SAMPLE_SCALE;
     10 use crate::clip::{ClipDataStore, ClipItemKind, ClipStore, ClipNodeRange};
     11 use crate::command_buffer::{CommandBufferIndex, QuadFlags};
     12 use crate::pattern::{PatternKind, PatternShaderInput};
     13 use crate::profiler::{add_text_marker};
     14 use crate::spatial_tree::SpatialNodeIndex;
     15 use crate::frame_builder::FrameBuilderConfig;
     16 use crate::gpu_types::{BorderInstance, UvRectKind, TransformPaletteId, BlurEdgeMode};
     17 use crate::internal_types::{CacheTextureId, FastHashMap, TextureSource, Swizzle};
     18 use crate::svg_filter::{FilterGraphNode, FilterGraphOp, FilterGraphPictureReference, SVGFE_CONVOLVE_VALUES_LIMIT};
     19 use crate::picture::ResolvedSurfaceTexture;
     20 use crate::tile_cache::MAX_SURFACE_SIZE;
     21 use crate::prim_store::ClipData;
     22 use crate::prim_store::gradient::{
     23    FastLinearGradientTask, RadialGradientTask,
     24    ConicGradientTask, LinearGradientTask,
     25 };
     26 use crate::resource_cache::{ResourceCache, ImageRequest};
     27 use std::{usize, f32, i32, u32};
     28 use crate::renderer::{GpuBufferAddress, GpuBufferBuilder, GpuBufferBuilderF};
     29 use crate::render_backend::DataStores;
     30 use crate::render_target::{ResolveOp, RenderTargetKind};
     31 use crate::render_task_graph::{PassId, RenderTaskId, RenderTaskGraphBuilder};
     32 use crate::render_task_cache::{RenderTaskCacheEntryHandle, RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent};
     33 use crate::segment::EdgeAaSegmentMask;
     34 use crate::surface::SurfaceBuilder;
     35 use smallvec::SmallVec;
     36 
     37 const FLOATS_PER_RENDER_TASK_INFO: usize = 8;
     38 pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0;
     39 pub const MIN_DOWNSCALING_RT_SIZE: i32 = 8;
     40 
     41 fn render_task_sanity_check(size: &DeviceIntSize) {
     42    if size.width > MAX_RENDER_TASK_SIZE ||
     43        size.height > MAX_RENDER_TASK_SIZE {
     44        error!("Attempting to create a render task of size {}x{}", size.width, size.height);
     45        panic!();
     46    }
     47 }
     48 
     49 #[derive(Debug, Copy, Clone, PartialEq)]
     50 #[repr(C)]
     51 #[cfg_attr(feature = "capture", derive(Serialize))]
     52 #[cfg_attr(feature = "replay", derive(Deserialize))]
     53 pub struct RenderTaskAddress(pub i32);
     54 
     55 impl Into<RenderTaskAddress> for RenderTaskId {
     56    fn into(self) -> RenderTaskAddress {
     57        RenderTaskAddress(self.index as i32)
     58    }
     59 }
     60 
     61 /// A render task location that targets a persistent output buffer which
     62 /// will be retained over multiple frames.
     63 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
     64 #[cfg_attr(feature = "capture", derive(Serialize))]
     65 #[cfg_attr(feature = "replay", derive(Deserialize))]
     66 pub enum StaticRenderTaskSurface {
     67    /// The output of the `RenderTask` will be persisted beyond this frame, and
     68    /// thus should be drawn into the `TextureCache`.
     69    TextureCache {
     70        /// Which texture in the texture cache should be drawn into.
     71        texture: CacheTextureId,
     72        /// What format this texture cache surface is
     73        target_kind: RenderTargetKind,
     74    },
     75    /// Only used as a source for render tasks, can be any texture including an
     76    /// external one.
     77    ReadOnly {
     78        source: TextureSource,
     79    },
     80    /// This render task will be drawn to a picture cache texture that is
     81    /// persisted between both frames and scenes, if the content remains valid.
     82    PictureCache {
     83        /// Describes either a WR texture or a native OS compositor target
     84        surface: ResolvedSurfaceTexture,
     85    },
     86 }
     87 
     88 /// Identifies the output buffer location for a given `RenderTask`.
     89 #[derive(Clone, Debug)]
     90 #[cfg_attr(feature = "capture", derive(Serialize))]
     91 #[cfg_attr(feature = "replay", derive(Deserialize))]
     92 pub enum RenderTaskLocation {
     93    // Towards the beginning of the frame, most task locations are typically not
     94    // known yet, in which case they are set to one of the following variants:
     95 
     96    /// A dynamic task that has not yet been allocated a texture and rect.
     97    Unallocated {
     98        /// Requested size of this render task
     99        size: DeviceIntSize,
    100    },
    101    /// Will be replaced by a Static location after the texture cache update.
    102    CacheRequest {
    103        size: DeviceIntSize,
    104    },
    105    /// Same allocation as an existing task deeper in the dependency graph
    106    Existing {
    107        parent_task_id: RenderTaskId,
    108        /// Requested size of this render task
    109        size: DeviceIntSize,
    110    },
    111 
    112    // Before batching begins, we expect that locations have been resolved to
    113    // one of the following variants:
    114 
    115    /// The `RenderTask` should be drawn to a target provided by the atlas
    116    /// allocator. This is the most common case.
    117    Dynamic {
    118        /// Texture that this task was allocated to render on
    119        texture_id: CacheTextureId,
    120        /// Rectangle in the texture this task occupies
    121        rect: DeviceIntRect,
    122    },
    123    /// A task that is output to a persistent / retained target.
    124    Static {
    125        /// Target to draw to
    126        surface: StaticRenderTaskSurface,
    127        /// Rectangle in the texture this task occupies
    128        rect: DeviceIntRect,
    129    },
    130 }
    131 
    132 impl RenderTaskLocation {
    133    /// Returns true if this is a dynamic location.
    134    pub fn is_dynamic(&self) -> bool {
    135        match *self {
    136            RenderTaskLocation::Dynamic { .. } => true,
    137            _ => false,
    138        }
    139    }
    140 
    141    pub fn size(&self) -> DeviceIntSize {
    142        match self {
    143            RenderTaskLocation::Unallocated { size } => *size,
    144            RenderTaskLocation::Dynamic { rect, .. } => rect.size(),
    145            RenderTaskLocation::Static { rect, .. } => rect.size(),
    146            RenderTaskLocation::CacheRequest { size } => *size,
    147            RenderTaskLocation::Existing { size, .. } => *size,
    148        }
    149    }
    150 }
    151 
    152 #[derive(Debug)]
    153 #[cfg_attr(feature = "capture", derive(Serialize))]
    154 #[cfg_attr(feature = "replay", derive(Deserialize))]
    155 pub struct CachedTask {
    156    pub target_kind: RenderTargetKind,
    157 }
    158 
    159 #[derive(Debug)]
    160 #[cfg_attr(feature = "capture", derive(Serialize))]
    161 #[cfg_attr(feature = "replay", derive(Deserialize))]
    162 pub struct ImageRequestTask {
    163    pub request: ImageRequest,
    164    pub is_composited: bool,
    165 }
    166 
    167 #[derive(Debug)]
    168 #[cfg_attr(feature = "capture", derive(Serialize))]
    169 #[cfg_attr(feature = "replay", derive(Deserialize))]
    170 pub struct CacheMaskTask {
    171    pub actual_rect: DeviceRect,
    172    pub root_spatial_node_index: SpatialNodeIndex,
    173    pub clip_node_range: ClipNodeRange,
    174    pub device_pixel_scale: DevicePixelScale,
    175    pub clear_to_one: bool,
    176 }
    177 
    178 #[derive(Debug)]
    179 #[cfg_attr(feature = "capture", derive(Serialize))]
    180 #[cfg_attr(feature = "replay", derive(Deserialize))]
    181 pub struct ClipRegionTask {
    182    pub local_pos: LayoutPoint,
    183    pub device_pixel_scale: DevicePixelScale,
    184    pub clip_data: ClipData,
    185    pub clear_to_one: bool,
    186 }
    187 
    188 #[cfg_attr(feature = "capture", derive(Serialize))]
    189 #[cfg_attr(feature = "replay", derive(Deserialize))]
    190 pub struct EmptyTask {
    191    pub content_origin: DevicePoint,
    192    pub device_pixel_scale: DevicePixelScale,
    193    pub raster_spatial_node_index: SpatialNodeIndex,
    194 }
    195 
    196 #[cfg_attr(feature = "capture", derive(Serialize))]
    197 #[cfg_attr(feature = "replay", derive(Deserialize))]
    198 pub struct PrimTask {
    199    pub pattern: PatternKind,
    200    pub pattern_input: PatternShaderInput,
    201    pub device_pixel_scale: DevicePixelScale,
    202    pub content_origin: DevicePoint,
    203    pub prim_address_f: GpuBufferAddress,
    204    pub raster_spatial_node_index: SpatialNodeIndex,
    205    pub transform_id: TransformPaletteId,
    206    pub edge_flags: EdgeAaSegmentMask,
    207    pub quad_flags: QuadFlags,
    208    pub prim_needs_scissor_rect: bool,
    209    pub texture_input: RenderTaskId,
    210 }
    211 
    212 #[cfg_attr(feature = "capture", derive(Serialize))]
    213 #[cfg_attr(feature = "replay", derive(Deserialize))]
    214 pub struct TileCompositeTask {
    215    pub clear_color: ColorF,
    216    pub scissor_rect: DeviceIntRect,
    217    pub valid_rect: DeviceIntRect,
    218    pub task_id: Option<RenderTaskId>,
    219    pub sub_rect_offset: DeviceIntVector2D,
    220 }
    221 
    222 #[cfg_attr(feature = "capture", derive(Serialize))]
    223 #[cfg_attr(feature = "replay", derive(Deserialize))]
    224 pub struct PictureTask {
    225    pub can_merge: bool,
    226    pub content_origin: DevicePoint,
    227    pub surface_spatial_node_index: SpatialNodeIndex,
    228    pub raster_spatial_node_index: SpatialNodeIndex,
    229    pub device_pixel_scale: DevicePixelScale,
    230    pub clear_color: Option<ColorF>,
    231    pub scissor_rect: Option<DeviceIntRect>,
    232    pub valid_rect: Option<DeviceIntRect>,
    233    pub cmd_buffer_index: CommandBufferIndex,
    234    pub resolve_op: Option<ResolveOp>,
    235    pub content_size: DeviceIntSize,
    236    pub can_use_shared_surface: bool,
    237 }
    238 
    239 impl PictureTask {
    240    /// Copy an existing picture task, but set a new command buffer for it to build in to.
    241    /// Used for pictures that are split between render tasks (e.g. pre/post a backdrop
    242    /// filter). Subsequent picture tasks never have a clear color as they are by definition
    243    /// going to write to an existing target
    244    pub fn duplicate(
    245        &self,
    246        cmd_buffer_index: CommandBufferIndex,
    247    ) -> Self {
    248        assert_eq!(self.resolve_op, None);
    249 
    250        PictureTask {
    251            clear_color: None,
    252            cmd_buffer_index,
    253            resolve_op: None,
    254            can_use_shared_surface: false,
    255            ..*self
    256        }
    257    }
    258 }
    259 
    260 #[derive(Debug)]
    261 #[cfg_attr(feature = "capture", derive(Serialize))]
    262 #[cfg_attr(feature = "replay", derive(Deserialize))]
    263 pub struct BlurTask {
    264    pub blur_std_deviation: f32,
    265    pub target_kind: RenderTargetKind,
    266    pub blur_region: DeviceIntSize,
    267    pub edge_mode: BlurEdgeMode,
    268 }
    269 
    270 impl BlurTask {
    271    // In order to do the blur down-scaling passes without introducing errors, we need the
    272    // source of each down-scale pass to be a multuple of two. If need be, this inflates
    273    // the source size so that each down-scale pass will sample correctly.
    274    pub fn adjusted_blur_source_size(original_size: DeviceSize, mut std_dev: DeviceSize) -> DeviceSize {
    275        let mut adjusted_size = original_size;
    276        let mut scale_factor = 1.0;
    277        while std_dev.width > MAX_BLUR_STD_DEVIATION && std_dev.height > MAX_BLUR_STD_DEVIATION {
    278            if adjusted_size.width < MIN_DOWNSCALING_RT_SIZE as f32 ||
    279               adjusted_size.height < MIN_DOWNSCALING_RT_SIZE as f32 {
    280                break;
    281            }
    282            std_dev = std_dev * 0.5;
    283            scale_factor *= 2.0;
    284            adjusted_size = (original_size.to_f32() / scale_factor).ceil();
    285        }
    286 
    287        (adjusted_size * scale_factor).round()
    288    }
    289 }
    290 
    291 #[derive(Debug)]
    292 #[cfg_attr(feature = "capture", derive(Serialize))]
    293 #[cfg_attr(feature = "replay", derive(Deserialize))]
    294 pub struct ScalingTask {
    295    pub target_kind: RenderTargetKind,
    296    pub padding: DeviceIntSideOffsets,
    297 }
    298 
    299 #[derive(Debug)]
    300 #[cfg_attr(feature = "capture", derive(Serialize))]
    301 #[cfg_attr(feature = "replay", derive(Deserialize))]
    302 pub struct BorderTask {
    303    pub instances: Vec<BorderInstance>,
    304 }
    305 
    306 #[derive(Debug)]
    307 #[cfg_attr(feature = "capture", derive(Serialize))]
    308 #[cfg_attr(feature = "replay", derive(Deserialize))]
    309 pub struct BlitTask {
    310    pub source: RenderTaskId,
    311    // Normalized rect within the source task to blit from
    312    pub source_rect: DeviceIntRect,
    313 }
    314 
    315 #[derive(Debug)]
    316 #[cfg_attr(feature = "capture", derive(Serialize))]
    317 #[cfg_attr(feature = "replay", derive(Deserialize))]
    318 pub struct LineDecorationTask {
    319    pub wavy_line_thickness: f32,
    320    pub style: LineStyle,
    321    pub orientation: LineOrientation,
    322    pub local_size: LayoutSize,
    323 }
    324 
    325 #[derive(Debug)]
    326 #[cfg_attr(feature = "capture", derive(Serialize))]
    327 #[cfg_attr(feature = "replay", derive(Deserialize))]
    328 pub struct SVGFEFilterTask {
    329    pub node: FilterGraphNode,
    330    pub op: FilterGraphOp,
    331    pub content_origin: DevicePoint,
    332    pub extra_gpu_data: Option<GpuBufferAddress>,
    333 }
    334 
    335 #[cfg_attr(feature = "capture", derive(Serialize))]
    336 #[cfg_attr(feature = "replay", derive(Deserialize))]
    337 pub struct ReadbackTask {
    338    // The offset of the rect that needs to be read back, in the
    339    // device space of the surface that will be read back from.
    340    // If this is None, there is no readback surface available
    341    // and this is a dummy (empty) readback.
    342    pub readback_origin: Option<DevicePoint>,
    343 }
    344 
    345 #[derive(Debug)]
    346 #[cfg_attr(feature = "capture", derive(Serialize))]
    347 #[cfg_attr(feature = "replay", derive(Deserialize))]
    348 pub struct RenderTaskData {
    349    pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
    350 }
    351 
    352 #[cfg_attr(feature = "capture", derive(Serialize))]
    353 #[cfg_attr(feature = "replay", derive(Deserialize))]
    354 pub enum RenderTaskKind {
    355    Image(ImageRequestTask),
    356    Cached(CachedTask),
    357    Picture(PictureTask),
    358    CacheMask(CacheMaskTask),
    359    ClipRegion(ClipRegionTask),
    360    VerticalBlur(BlurTask),
    361    HorizontalBlur(BlurTask),
    362    Readback(ReadbackTask),
    363    Scaling(ScalingTask),
    364    Blit(BlitTask),
    365    Border(BorderTask),
    366    LineDecoration(LineDecorationTask),
    367    FastLinearGradient(FastLinearGradientTask),
    368    LinearGradient(LinearGradientTask),
    369    RadialGradient(RadialGradientTask),
    370    ConicGradient(ConicGradientTask),
    371    SVGFENode(SVGFEFilterTask),
    372    TileComposite(TileCompositeTask),
    373    Prim(PrimTask),
    374    Empty(EmptyTask),
    375    #[cfg(test)]
    376    Test(RenderTargetKind),
    377 }
    378 
    379 impl RenderTaskKind {
    380    pub fn is_a_rendering_operation(&self) -> bool {
    381        match self {
    382            &RenderTaskKind::Image(..) => false,
    383            &RenderTaskKind::Cached(..) => false,
    384            _ => true,
    385        }
    386    }
    387 
    388    /// Whether this task can be allocated on a shared render target surface
    389    pub fn can_use_shared_surface(&self) -> bool {
    390        match self {
    391            &RenderTaskKind::Picture(ref info) => info.can_use_shared_surface,
    392            _ => true,
    393        }
    394    }
    395 
    396    pub fn should_advance_pass(&self) -> bool {
    397        match self {
    398            &RenderTaskKind::Image(..) => false,
    399            &RenderTaskKind::Cached(..) => false,
    400            _ => true,
    401        }
    402    }
    403 
    404    pub fn as_str(&self) -> &'static str {
    405        match *self {
    406            RenderTaskKind::Image(..) => "Image",
    407            RenderTaskKind::Cached(..) => "Cached",
    408            RenderTaskKind::Picture(..) => "Picture",
    409            RenderTaskKind::CacheMask(..) => "CacheMask",
    410            RenderTaskKind::ClipRegion(..) => "ClipRegion",
    411            RenderTaskKind::VerticalBlur(..) => "VerticalBlur",
    412            RenderTaskKind::HorizontalBlur(..) => "HorizontalBlur",
    413            RenderTaskKind::Readback(..) => "Readback",
    414            RenderTaskKind::Scaling(..) => "Scaling",
    415            RenderTaskKind::Blit(..) => "Blit",
    416            RenderTaskKind::Border(..) => "Border",
    417            RenderTaskKind::LineDecoration(..) => "LineDecoration",
    418            RenderTaskKind::FastLinearGradient(..) => "FastLinearGradient",
    419            RenderTaskKind::LinearGradient(..) => "LinearGradient",
    420            RenderTaskKind::RadialGradient(..) => "RadialGradient",
    421            RenderTaskKind::ConicGradient(..) => "ConicGradient",
    422            RenderTaskKind::SVGFENode(..) => "SVGFENode",
    423            RenderTaskKind::TileComposite(..) => "TileComposite",
    424            RenderTaskKind::Prim(..) => "Prim",
    425            RenderTaskKind::Empty(..) => "Empty",
    426            #[cfg(test)]
    427            RenderTaskKind::Test(..) => "Test",
    428        }
    429    }
    430 
    431    pub fn target_kind(&self) -> RenderTargetKind {
    432        match *self {
    433            RenderTaskKind::Image(..) |
    434            RenderTaskKind::LineDecoration(..) |
    435            RenderTaskKind::Readback(..) |
    436            RenderTaskKind::Border(..) |
    437            RenderTaskKind::FastLinearGradient(..) |
    438            RenderTaskKind::LinearGradient(..) |
    439            RenderTaskKind::RadialGradient(..) |
    440            RenderTaskKind::ConicGradient(..) |
    441            RenderTaskKind::Picture(..) |
    442            RenderTaskKind::Blit(..) |
    443            RenderTaskKind::TileComposite(..) |
    444            RenderTaskKind::Prim(..)  => {
    445                RenderTargetKind::Color
    446            }
    447            RenderTaskKind::SVGFENode(..) => {
    448                RenderTargetKind::Color
    449            }
    450 
    451            RenderTaskKind::ClipRegion(..) |
    452            RenderTaskKind::CacheMask(..) |
    453            RenderTaskKind::Empty(..) => {
    454                RenderTargetKind::Alpha
    455            }
    456 
    457            RenderTaskKind::VerticalBlur(ref task_info) |
    458            RenderTaskKind::HorizontalBlur(ref task_info) => {
    459                task_info.target_kind
    460            }
    461 
    462            RenderTaskKind::Scaling(ref task_info) => {
    463                task_info.target_kind
    464            }
    465 
    466            RenderTaskKind::Cached(ref task_info) => {
    467                task_info.target_kind
    468            }
    469 
    470            #[cfg(test)]
    471            RenderTaskKind::Test(kind) => kind,
    472        }
    473    }
    474 
    475    pub fn new_tile_composite(
    476        sub_rect_offset: DeviceIntVector2D,
    477        scissor_rect: DeviceIntRect,
    478        valid_rect: DeviceIntRect,
    479        clear_color: ColorF,
    480    ) -> Self {
    481        RenderTaskKind::TileComposite(TileCompositeTask {
    482            task_id: None,
    483            sub_rect_offset,
    484            scissor_rect,
    485            valid_rect,
    486            clear_color,
    487        })
    488    }
    489 
    490    pub fn new_picture(
    491        size: DeviceIntSize,
    492        needs_scissor_rect: bool,
    493        content_origin: DevicePoint,
    494        surface_spatial_node_index: SpatialNodeIndex,
    495        raster_spatial_node_index: SpatialNodeIndex,
    496        device_pixel_scale: DevicePixelScale,
    497        scissor_rect: Option<DeviceIntRect>,
    498        valid_rect: Option<DeviceIntRect>,
    499        clear_color: Option<ColorF>,
    500        cmd_buffer_index: CommandBufferIndex,
    501        can_use_shared_surface: bool,
    502        content_size: Option<DeviceIntSize>,
    503    ) -> Self {
    504        render_task_sanity_check(&size);
    505 
    506        RenderTaskKind::Picture(PictureTask {
    507            content_origin,
    508            can_merge: !needs_scissor_rect,
    509            surface_spatial_node_index,
    510            raster_spatial_node_index,
    511            device_pixel_scale,
    512            scissor_rect,
    513            valid_rect,
    514            clear_color,
    515            cmd_buffer_index,
    516            resolve_op: None,
    517            can_use_shared_surface,
    518            content_size: content_size.unwrap_or(size),
    519        })
    520    }
    521 
    522    pub fn new_prim(
    523        pattern: PatternKind,
    524        pattern_input: PatternShaderInput,
    525        raster_spatial_node_index: SpatialNodeIndex,
    526        device_pixel_scale: DevicePixelScale,
    527        content_origin: DevicePoint,
    528        prim_address_f: GpuBufferAddress,
    529        transform_id: TransformPaletteId,
    530        edge_flags: EdgeAaSegmentMask,
    531        quad_flags: QuadFlags,
    532        prim_needs_scissor_rect: bool,
    533        texture_input: RenderTaskId,
    534    ) -> Self {
    535        RenderTaskKind::Prim(PrimTask {
    536            pattern,
    537            pattern_input,
    538            raster_spatial_node_index,
    539            device_pixel_scale,
    540            content_origin,
    541            prim_address_f,
    542            transform_id,
    543            edge_flags,
    544            quad_flags,
    545            prim_needs_scissor_rect,
    546            texture_input,
    547        })
    548    }
    549 
    550    pub fn new_readback(
    551        readback_origin: Option<DevicePoint>,
    552    ) -> Self {
    553        RenderTaskKind::Readback(
    554            ReadbackTask {
    555                readback_origin,
    556            }
    557        )
    558    }
    559 
    560    pub fn new_line_decoration(
    561        style: LineStyle,
    562        orientation: LineOrientation,
    563        wavy_line_thickness: f32,
    564        local_size: LayoutSize,
    565    ) -> Self {
    566        RenderTaskKind::LineDecoration(LineDecorationTask {
    567            style,
    568            orientation,
    569            wavy_line_thickness,
    570            local_size,
    571        })
    572    }
    573 
    574    pub fn new_border_segment(
    575        instances: Vec<BorderInstance>,
    576    ) -> Self {
    577        RenderTaskKind::Border(BorderTask {
    578            instances,
    579        })
    580    }
    581 
    582    pub fn new_rounded_rect_mask(
    583        local_pos: LayoutPoint,
    584        clip_data: ClipData,
    585        device_pixel_scale: DevicePixelScale,
    586        fb_config: &FrameBuilderConfig,
    587    ) -> Self {
    588        RenderTaskKind::ClipRegion(ClipRegionTask {
    589            local_pos,
    590            device_pixel_scale,
    591            clip_data,
    592            clear_to_one: fb_config.gpu_supports_fast_clears,
    593        })
    594    }
    595 
    596    pub fn new_mask(
    597        outer_rect: DeviceIntRect,
    598        clip_node_range: ClipNodeRange,
    599        root_spatial_node_index: SpatialNodeIndex,
    600        clip_store: &mut ClipStore,
    601        gpu_buffer_builder: &mut GpuBufferBuilderF,
    602        resource_cache: &mut ResourceCache,
    603        rg_builder: &mut RenderTaskGraphBuilder,
    604        clip_data_store: &mut ClipDataStore,
    605        device_pixel_scale: DevicePixelScale,
    606        fb_config: &FrameBuilderConfig,
    607        surface_builder: &mut SurfaceBuilder,
    608    ) -> RenderTaskId {
    609        // Step through the clip sources that make up this mask. If we find
    610        // any box-shadow clip sources, request that image from the render
    611        // task cache. This allows the blurred box-shadow rect to be cached
    612        // in the texture cache across frames.
    613        // TODO(gw): Consider moving this logic outside this function, especially
    614        //           as we add more clip sources that depend on render tasks.
    615        // TODO(gw): If this ever shows up in a profile, we could pre-calculate
    616        //           whether a ClipSources contains any box-shadows and skip
    617        //           this iteration for the majority of cases.
    618        let task_size = outer_rect.size();
    619 
    620        // If we have a potentially tiled clip mask, clear the mask area first. Otherwise,
    621        // the first (primary) clip mask will overwrite all the clip mask pixels with
    622        // blending disabled to set to the initial value.
    623 
    624        let clip_task_id = rg_builder.add().init(
    625            RenderTask::new_dynamic(
    626                task_size,
    627                RenderTaskKind::CacheMask(CacheMaskTask {
    628                    actual_rect: outer_rect.to_f32(),
    629                    clip_node_range,
    630                    root_spatial_node_index,
    631                    device_pixel_scale,
    632                    clear_to_one: fb_config.gpu_supports_fast_clears,
    633                }),
    634            )
    635        );
    636 
    637        for i in 0 .. clip_node_range.count {
    638            let clip_instance = clip_store.get_instance_from_range(&clip_node_range, i);
    639            let clip_node = &mut clip_data_store[clip_instance.handle];
    640            match clip_node.item.kind {
    641                ClipItemKind::BoxShadow { ref mut source } => {
    642                    let (cache_size, cache_key) = source.cache_key
    643                        .as_ref()
    644                        .expect("bug: no cache key set")
    645                        .clone();
    646                    let blur_radius_dp = cache_key.blur_radius_dp as f32;
    647                    let device_pixel_scale = DevicePixelScale::new(cache_key.device_pixel_scale.to_f32_px());
    648 
    649                    // Request a cacheable render task with a blurred, minimal
    650                    // sized box-shadow rect.
    651                    source.render_task = Some(resource_cache.request_render_task(
    652                        Some(RenderTaskCacheKey {
    653                            size: cache_size,
    654                            kind: RenderTaskCacheKeyKind::BoxShadow(cache_key),
    655                        }),
    656                        false,
    657                        RenderTaskParent::RenderTask(clip_task_id),
    658                        gpu_buffer_builder,
    659                        rg_builder,
    660                        surface_builder,
    661                        &mut |rg_builder, _| {
    662                            let clip_data = ClipData::rounded_rect(
    663                                source.minimal_shadow_rect.size(),
    664                                &source.shadow_radius,
    665                                ClipMode::Clip,
    666                            );
    667 
    668                            // Draw the rounded rect.
    669                            let mask_task_id = rg_builder.add().init(RenderTask::new_dynamic(
    670                                cache_size,
    671                                RenderTaskKind::new_rounded_rect_mask(
    672                                    source.minimal_shadow_rect.min,
    673                                    clip_data,
    674                                    device_pixel_scale,
    675                                    fb_config,
    676                                ),
    677                            ));
    678 
    679                            // Blur it
    680                            RenderTask::new_blur(
    681                                DeviceSize::new(blur_radius_dp, blur_radius_dp),
    682                                mask_task_id,
    683                                rg_builder,
    684                                RenderTargetKind::Alpha,
    685                                None,
    686                                cache_size,
    687                                BlurEdgeMode::Duplicate,
    688                            )
    689                        }
    690                    ));
    691                }
    692                ClipItemKind::Rectangle { .. } |
    693                ClipItemKind::RoundedRectangle { .. } |
    694                ClipItemKind::Image { .. } => {}
    695            }
    696        }
    697 
    698        clip_task_id
    699    }
    700 
    701    // Write (up to) 8 floats of data specific to the type
    702    // of render task that is provided to the GPU shaders
    703    // via a vertex texture.
    704    pub fn write_task_data(
    705        &self,
    706        target_rect: DeviceIntRect,
    707    ) -> RenderTaskData {
    708        // NOTE: The ordering and layout of these structures are
    709        //       required to match both the GPU structures declared
    710        //       in prim_shared.glsl, and also the uses in submit_batch()
    711        //       in renderer.rs.
    712        // TODO(gw): Maybe there's a way to make this stuff a bit
    713        //           more type-safe. Although, it will always need
    714        //           to be kept in sync with the GLSL code anyway.
    715 
    716        let data = match self {
    717            RenderTaskKind::Picture(ref task) => {
    718                // Note: has to match `PICTURE_TYPE_*` in shaders
    719                [
    720                    task.device_pixel_scale.0,
    721                    task.content_origin.x,
    722                    task.content_origin.y,
    723                    0.0,
    724                ]
    725            }
    726            RenderTaskKind::Prim(ref task) => {
    727                [
    728                    // NOTE: This must match the render task data format for Picture tasks currently
    729                    task.device_pixel_scale.0,
    730                    task.content_origin.x,
    731                    task.content_origin.y,
    732                    0.0,
    733                ]
    734            }
    735            RenderTaskKind::Empty(ref task) => {
    736                [
    737                    // NOTE: This must match the render task data format for Picture tasks currently
    738                    task.device_pixel_scale.0,
    739                    task.content_origin.x,
    740                    task.content_origin.y,
    741                    0.0,
    742                ]
    743            }
    744            RenderTaskKind::CacheMask(ref task) => {
    745                [
    746                    task.device_pixel_scale.0,
    747                    task.actual_rect.min.x,
    748                    task.actual_rect.min.y,
    749                    0.0,
    750                ]
    751            }
    752            RenderTaskKind::ClipRegion(ref task) => {
    753                [
    754                    task.device_pixel_scale.0,
    755                    0.0,
    756                    0.0,
    757                    0.0,
    758                ]
    759            }
    760            RenderTaskKind::VerticalBlur(_) |
    761            RenderTaskKind::HorizontalBlur(_) => {
    762                // TODO(gw): Make this match Picture tasks so that we can draw
    763                //           sub-passes on them to apply box-shadow masks.
    764                [
    765                    0.0,
    766                    0.0,
    767                    0.0,
    768                    0.0,
    769                ]
    770            }
    771            RenderTaskKind::Image(..) |
    772            RenderTaskKind::Cached(..) |
    773            RenderTaskKind::Readback(..) |
    774            RenderTaskKind::Scaling(..) |
    775            RenderTaskKind::Border(..) |
    776            RenderTaskKind::LineDecoration(..) |
    777            RenderTaskKind::FastLinearGradient(..) |
    778            RenderTaskKind::LinearGradient(..) |
    779            RenderTaskKind::RadialGradient(..) |
    780            RenderTaskKind::ConicGradient(..) |
    781            RenderTaskKind::TileComposite(..) |
    782            RenderTaskKind::Blit(..) => {
    783                [0.0; 4]
    784            }
    785 
    786            RenderTaskKind::SVGFENode(_task) => {
    787                // we don't currently use this for SVGFE filters.
    788                // see SVGFEFilterInstance instead
    789                [0.0; 4]
    790            }
    791 
    792            #[cfg(test)]
    793            RenderTaskKind::Test(..) => {
    794                [0.0; 4]
    795            }
    796        };
    797 
    798        RenderTaskData {
    799            data: [
    800                target_rect.min.x as f32,
    801                target_rect.min.y as f32,
    802                target_rect.max.x as f32,
    803                target_rect.max.y as f32,
    804                data[0],
    805                data[1],
    806                data[2],
    807                data[3],
    808            ]
    809        }
    810    }
    811 
    812    pub fn write_gpu_blocks(
    813        &mut self,
    814        gpu_buffer: &mut GpuBufferBuilder,
    815    ) {
    816        match self {
    817            RenderTaskKind::SVGFENode(ref mut filter_task) => {
    818                match filter_task.op {
    819                    FilterGraphOp::SVGFEBlendDarken => {}
    820                    FilterGraphOp::SVGFEBlendLighten => {}
    821                    FilterGraphOp::SVGFEBlendMultiply => {}
    822                    FilterGraphOp::SVGFEBlendNormal => {}
    823                    FilterGraphOp::SVGFEBlendScreen => {}
    824                    FilterGraphOp::SVGFEBlendOverlay => {}
    825                    FilterGraphOp::SVGFEBlendColorDodge => {}
    826                    FilterGraphOp::SVGFEBlendColorBurn => {}
    827                    FilterGraphOp::SVGFEBlendHardLight => {}
    828                    FilterGraphOp::SVGFEBlendSoftLight => {}
    829                    FilterGraphOp::SVGFEBlendDifference => {}
    830                    FilterGraphOp::SVGFEBlendExclusion => {}
    831                    FilterGraphOp::SVGFEBlendHue => {}
    832                    FilterGraphOp::SVGFEBlendSaturation => {}
    833                    FilterGraphOp::SVGFEBlendColor => {}
    834                    FilterGraphOp::SVGFEBlendLuminosity => {}
    835                    FilterGraphOp::SVGFEColorMatrix { values: matrix } => {
    836                        let mut writer = gpu_buffer.f32.write_blocks(5);
    837                        for i in 0..5 {
    838                            writer.push_one([matrix[i*4], matrix[i*4+1], matrix[i*4+2], matrix[i*4+3]]);
    839                        }
    840                        filter_task.extra_gpu_data = Some(writer.finish());
    841                    }
    842                    FilterGraphOp::SVGFEComponentTransfer => unreachable!(),
    843                    FilterGraphOp::SVGFEComponentTransferInterned{..} => {}
    844                    FilterGraphOp::SVGFECompositeArithmetic{k1, k2, k3, k4} => {
    845                        let mut writer = gpu_buffer.f32.write_blocks(1);
    846                        writer.push_one([k1, k2, k3, k4]);
    847                        filter_task.extra_gpu_data = Some(writer.finish());
    848                    }
    849                    FilterGraphOp::SVGFECompositeATop => {}
    850                    FilterGraphOp::SVGFECompositeIn => {}
    851                    FilterGraphOp::SVGFECompositeLighter => {}
    852                    FilterGraphOp::SVGFECompositeOut => {}
    853                    FilterGraphOp::SVGFECompositeOver => {}
    854                    FilterGraphOp::SVGFECompositeXOR => {}
    855                    FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha} |
    856                    FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha} |
    857                    FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha} => {
    858                        let mut writer = gpu_buffer.f32.write_blocks(8);
    859                        assert!(SVGFE_CONVOLVE_VALUES_LIMIT == 25);
    860                        writer.push_one([-target_x as f32, -target_y as f32, order_x as f32, order_y as f32]);
    861                        writer.push_one([kernel_unit_length_x as f32, kernel_unit_length_y as f32, 1.0 / divisor, bias]);
    862                        writer.push_one([kernel[0], kernel[1], kernel[2], kernel[3]]);
    863                        writer.push_one([kernel[4], kernel[5], kernel[6], kernel[7]]);
    864                        writer.push_one([kernel[8], kernel[9], kernel[10], kernel[11]]);
    865                        writer.push_one([kernel[12], kernel[13], kernel[14], kernel[15]]);
    866                        writer.push_one([kernel[16], kernel[17], kernel[18], kernel[19]]);
    867                        writer.push_one([kernel[20], 0.0, 0.0, preserve_alpha as f32]);
    868                        filter_task.extra_gpu_data = Some(writer.finish());
    869                    }
    870                    FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {}
    871                    FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {}
    872                    FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {}
    873                    FilterGraphOp::SVGFEDisplacementMap{scale, x_channel_selector, y_channel_selector} => {
    874                        let mut writer = gpu_buffer.f32.write_blocks(1);
    875                        writer.push_one([x_channel_selector as f32, y_channel_selector as f32, scale, 0.0]);
    876                        filter_task.extra_gpu_data = Some(writer.finish());
    877                    }
    878                    FilterGraphOp::SVGFEDropShadow { color, .. } |
    879                    FilterGraphOp::SVGFEFlood { color } => {
    880                        let mut writer = gpu_buffer.f32.write_blocks(1);
    881                        writer.push_one(color.to_array());
    882                        filter_task.extra_gpu_data = Some(writer.finish());
    883                     }
    884                    FilterGraphOp::SVGFEGaussianBlur{..} => {}
    885                    FilterGraphOp::SVGFEIdentity => {}
    886                    FilterGraphOp::SVGFEImage {..} => {}
    887                    FilterGraphOp::SVGFEMorphologyDilate { radius_x, radius_y } |
    888                    FilterGraphOp::SVGFEMorphologyErode { radius_x, radius_y } => {
    889                        let mut writer = gpu_buffer.f32.write_blocks(1);
    890                        writer.push_one([radius_x, radius_y, 0.0, 0.0]);
    891                        filter_task.extra_gpu_data = Some(writer.finish());
    892                    }
    893                    FilterGraphOp::SVGFEOpacity{..} => {}
    894                    FilterGraphOp::SVGFESourceAlpha => {}
    895                    FilterGraphOp::SVGFESourceGraphic => {}
    896                    FilterGraphOp::SVGFESpecularLightingDistant{..} => {}
    897                    FilterGraphOp::SVGFESpecularLightingPoint{..} => {}
    898                    FilterGraphOp::SVGFESpecularLightingSpot{..} => {}
    899                    FilterGraphOp::SVGFETile => {}
    900                    FilterGraphOp::SVGFEToAlpha{..} => {}
    901                    FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} => {}
    902                    FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} => {}
    903                    FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} => {}
    904                    FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {}
    905                }
    906            }
    907            _ => {}
    908        }
    909    }
    910 }
    911 
    912 /// In order to avoid duplicating the down-scaling and blur passes when a picture has several blurs,
    913 /// we use a local (primitive-level) cache of the render tasks generated for a single shadowed primitive
    914 /// in a single frame.
    915 pub type BlurTaskCache = FastHashMap<BlurTaskKey, RenderTaskId>;
    916 
    917 /// Since we only use it within a single primitive, the key only needs to contain the down-scaling level
    918 /// and the blur std deviation.
    919 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
    920 pub enum BlurTaskKey {
    921    DownScale(u32),
    922    Blur { downscale_level: u32, stddev_x: u32, stddev_y: u32 },
    923 }
    924 
    925 impl BlurTaskKey {
    926    fn downscale_and_blur(downscale_level: u32, blur_stddev: DeviceSize) -> Self {
    927        // Quantise the std deviations and store it as integers to work around
    928        // Eq and Hash's f32 allergy.
    929        // The blur radius is rounded before RenderTask::new_blur so we don't need
    930        // a lot of precision.
    931        const QUANTIZATION_FACTOR: f32 = 1024.0;
    932        let stddev_x = (blur_stddev.width * QUANTIZATION_FACTOR) as u32;
    933        let stddev_y = (blur_stddev.height * QUANTIZATION_FACTOR) as u32;
    934        BlurTaskKey::Blur { downscale_level, stddev_x, stddev_y }
    935    }
    936 }
    937 
    938 // The majority of render tasks have 0, 1 or 2 dependencies, except for pictures that
    939 // typically have dozens to hundreds of dependencies. SmallVec with 2 inline elements
    940 // avoids many tiny heap allocations in pages with a lot of text shadows and other
    941 // types of render tasks.
    942 pub type TaskDependencies = SmallVec<[RenderTaskId;2]>;
    943 
    944 #[cfg_attr(feature = "capture", derive(Serialize))]
    945 #[cfg_attr(feature = "replay", derive(Deserialize))]
    946 pub struct MaskSubPass {
    947    pub clip_node_range: ClipNodeRange,
    948    pub prim_spatial_node_index: SpatialNodeIndex,
    949    pub prim_address_f: GpuBufferAddress,
    950 }
    951 
    952 #[cfg_attr(feature = "capture", derive(Serialize))]
    953 #[cfg_attr(feature = "replay", derive(Deserialize))]
    954 pub enum SubPass {
    955    Masks {
    956        masks: MaskSubPass,
    957    },
    958 }
    959 
    960 #[cfg_attr(feature = "capture", derive(Serialize))]
    961 #[cfg_attr(feature = "replay", derive(Deserialize))]
    962 pub struct RenderTask {
    963    pub location: RenderTaskLocation,
    964    pub children: TaskDependencies,
    965    pub kind: RenderTaskKind,
    966    pub sub_pass: Option<SubPass>,
    967 
    968    // TODO(gw): These fields and perhaps others can become private once the
    969    //           frame_graph / render_task source files are unified / cleaned up.
    970    pub free_after: PassId,
    971    pub render_on: PassId,
    972 
    973    /// The gpu cache handle for the render task's destination rect.
    974    ///
    975    /// Will be set to None if the render task is cached, in which case the texture cache
    976    /// manages the handle.
    977    pub uv_rect_handle: GpuBufferAddress,
    978    pub cache_handle: Option<RenderTaskCacheEntryHandle>,
    979    pub uv_rect_kind: UvRectKind,
    980 }
    981 
    982 impl RenderTask {
    983    pub fn new(
    984        location: RenderTaskLocation,
    985        kind: RenderTaskKind,
    986    ) -> Self {
    987        render_task_sanity_check(&location.size());
    988 
    989        RenderTask {
    990            location,
    991            children: TaskDependencies::new(),
    992            kind,
    993            free_after: PassId::MAX,
    994            render_on: PassId::MIN,
    995            uv_rect_handle: GpuBufferAddress::INVALID,
    996            uv_rect_kind: UvRectKind::Rect,
    997            cache_handle: None,
    998            sub_pass: None,
    999        }
   1000    }
   1001 
   1002    pub fn new_dynamic(
   1003        size: DeviceIntSize,
   1004        kind: RenderTaskKind,
   1005    ) -> Self {
   1006        assert!(!size.is_empty(), "Bad {} render task size: {:?}", kind.as_str(), size);
   1007        RenderTask::new(
   1008            RenderTaskLocation::Unallocated { size },
   1009            kind,
   1010        )
   1011    }
   1012 
   1013    pub fn with_uv_rect_kind(mut self, uv_rect_kind: UvRectKind) -> Self {
   1014        self.uv_rect_kind = uv_rect_kind;
   1015        self
   1016    }
   1017 
   1018    pub fn new_image(
   1019        size: DeviceIntSize,
   1020        request: ImageRequest,
   1021        is_composited: bool,
   1022    ) -> Self {
   1023        // Note: this is a special constructor for image render tasks that does not
   1024        // do the render task size sanity check. This is because with SWGL we purposefully
   1025        // avoid tiling large images. There is no upload with SWGL so whatever was
   1026        // successfully allocated earlier will be what shaders read, regardless of the size
   1027        // and copying into tiles would only slow things down.
   1028        // As a result we can run into very large images being added to the frame graph
   1029        // (this is covered by a few reftests on the CI).
   1030 
   1031        RenderTask {
   1032            location: RenderTaskLocation::CacheRequest { size, },
   1033            children: TaskDependencies::new(),
   1034            kind: RenderTaskKind::Image(ImageRequestTask {
   1035                request,
   1036                is_composited,
   1037            }),
   1038            free_after: PassId::MAX,
   1039            render_on: PassId::MIN,
   1040            uv_rect_handle: GpuBufferAddress::INVALID,
   1041            uv_rect_kind: UvRectKind::Rect,
   1042            cache_handle: None,
   1043            sub_pass: None,
   1044        }
   1045    }
   1046 
   1047 
   1048    #[cfg(test)]
   1049    pub fn new_test(
   1050        location: RenderTaskLocation,
   1051        target: RenderTargetKind,
   1052    ) -> Self {
   1053        RenderTask {
   1054            location,
   1055            children: TaskDependencies::new(),
   1056            kind: RenderTaskKind::Test(target),
   1057            free_after: PassId::MAX,
   1058            render_on: PassId::MIN,
   1059            uv_rect_handle: GpuBufferAddress::INVALID,
   1060            uv_rect_kind: UvRectKind::Rect,
   1061            cache_handle: None,
   1062            sub_pass: None,
   1063        }
   1064    }
   1065 
   1066    pub fn new_blit(
   1067        size: DeviceIntSize,
   1068        source: RenderTaskId,
   1069        source_rect: DeviceIntRect,
   1070        rg_builder: &mut RenderTaskGraphBuilder,
   1071    ) -> RenderTaskId {
   1072        // If this blit uses a render task as a source,
   1073        // ensure it's added as a child task. This will
   1074        // ensure it gets allocated in the correct pass
   1075        // and made available as an input when this task
   1076        // executes.
   1077 
   1078        let blit_task_id = rg_builder.add().init(RenderTask::new_dynamic(
   1079            size,
   1080            RenderTaskKind::Blit(BlitTask { source, source_rect }),
   1081        ));
   1082 
   1083        rg_builder.add_dependency(blit_task_id, source);
   1084 
   1085        blit_task_id
   1086    }
   1087 
   1088    // Construct a render task to apply a blur to a primitive.
   1089    // The render task chain that is constructed looks like:
   1090    //
   1091    //    PrimitiveCacheTask: Draw the primitives.
   1092    //           ^
   1093    //           |
   1094    //    DownscalingTask(s): Each downscaling task reduces the size of render target to
   1095    //           ^            half. Also reduce the std deviation to half until the std
   1096    //           |            deviation less than 4.0.
   1097    //           |
   1098    //           |
   1099    //    VerticalBlurTask: Apply the separable vertical blur to the primitive.
   1100    //           ^
   1101    //           |
   1102    //    HorizontalBlurTask: Apply the separable horizontal blur to the vertical blur.
   1103    //           |
   1104    //           +---- This is stored as the input task to the primitive shader.
   1105    //
   1106    pub fn new_blur(
   1107        blur_std_deviation: DeviceSize,
   1108        src_task_id: RenderTaskId,
   1109        rg_builder: &mut RenderTaskGraphBuilder,
   1110        target_kind: RenderTargetKind,
   1111        mut blur_cache: Option<&mut BlurTaskCache>,
   1112        blur_region: DeviceIntSize,
   1113        edge_mode: BlurEdgeMode,
   1114    ) -> RenderTaskId {
   1115        // Adjust large std deviation value.
   1116        let mut adjusted_blur_std_deviation = blur_std_deviation;
   1117        let (blur_target_size, uv_rect_kind) = {
   1118            let src_task = rg_builder.get_task(src_task_id);
   1119            (src_task.location.size(), src_task.uv_rect_kind())
   1120        };
   1121        let mut adjusted_blur_target_size = blur_target_size;
   1122        let mut downscaling_src_task_id = src_task_id;
   1123        let mut scale_factor = 1.0;
   1124        let mut n_downscales = 1;
   1125        while adjusted_blur_std_deviation.width > MAX_BLUR_STD_DEVIATION &&
   1126              adjusted_blur_std_deviation.height > MAX_BLUR_STD_DEVIATION {
   1127            if adjusted_blur_target_size.width < MIN_DOWNSCALING_RT_SIZE ||
   1128               adjusted_blur_target_size.height < MIN_DOWNSCALING_RT_SIZE {
   1129                break;
   1130            }
   1131            adjusted_blur_std_deviation = adjusted_blur_std_deviation * 0.5;
   1132            scale_factor *= 2.0;
   1133            adjusted_blur_target_size = (blur_target_size.to_f32() / scale_factor).to_i32();
   1134 
   1135            let cached_task = match blur_cache {
   1136                Some(ref mut cache) => cache.get(&BlurTaskKey::DownScale(n_downscales)).cloned(),
   1137                None => None,
   1138            };
   1139 
   1140            downscaling_src_task_id = cached_task.unwrap_or_else(|| {
   1141                RenderTask::new_scaling(
   1142                    downscaling_src_task_id,
   1143                    rg_builder,
   1144                    target_kind,
   1145                    adjusted_blur_target_size,
   1146                )
   1147            });
   1148 
   1149            if let Some(ref mut cache) = blur_cache {
   1150                cache.insert(BlurTaskKey::DownScale(n_downscales), downscaling_src_task_id);
   1151            }
   1152 
   1153            n_downscales += 1;
   1154        }
   1155 
   1156 
   1157        let blur_key = BlurTaskKey::downscale_and_blur(n_downscales, adjusted_blur_std_deviation);
   1158 
   1159        let cached_task = match blur_cache {
   1160            Some(ref mut cache) => cache.get(&blur_key).cloned(),
   1161            None => None,
   1162        };
   1163 
   1164        let blur_region = blur_region / (scale_factor as i32);
   1165 
   1166        let blur_task_id = cached_task.unwrap_or_else(|| {
   1167            let blur_task_v = rg_builder.add().init(RenderTask::new_dynamic(
   1168                adjusted_blur_target_size,
   1169                RenderTaskKind::VerticalBlur(BlurTask {
   1170                    blur_std_deviation: adjusted_blur_std_deviation.height,
   1171                    target_kind,
   1172                    blur_region,
   1173                    edge_mode,
   1174                }),
   1175            ).with_uv_rect_kind(uv_rect_kind));
   1176            rg_builder.add_dependency(blur_task_v, downscaling_src_task_id);
   1177 
   1178            let task_id = rg_builder.add().init(RenderTask::new_dynamic(
   1179                adjusted_blur_target_size,
   1180                RenderTaskKind::HorizontalBlur(BlurTask {
   1181                    blur_std_deviation: adjusted_blur_std_deviation.width,
   1182                    target_kind,
   1183                    blur_region,
   1184                    edge_mode,
   1185                }),
   1186            ).with_uv_rect_kind(uv_rect_kind));
   1187            rg_builder.add_dependency(task_id, blur_task_v);
   1188 
   1189            task_id
   1190        });
   1191 
   1192        if let Some(ref mut cache) = blur_cache {
   1193            cache.insert(blur_key, blur_task_id);
   1194        }
   1195 
   1196        blur_task_id
   1197    }
   1198 
   1199    pub fn new_scaling(
   1200        src_task_id: RenderTaskId,
   1201        rg_builder: &mut RenderTaskGraphBuilder,
   1202        target_kind: RenderTargetKind,
   1203        size: DeviceIntSize,
   1204    ) -> RenderTaskId {
   1205        Self::new_scaling_with_padding(
   1206            src_task_id,
   1207            rg_builder,
   1208            target_kind,
   1209            size,
   1210            DeviceIntSideOffsets::zero(),
   1211        )
   1212    }
   1213 
   1214    pub fn new_scaling_with_padding(
   1215        source: RenderTaskId,
   1216        rg_builder: &mut RenderTaskGraphBuilder,
   1217        target_kind: RenderTargetKind,
   1218        padded_size: DeviceIntSize,
   1219        padding: DeviceIntSideOffsets,
   1220    ) -> RenderTaskId {
   1221        let uv_rect_kind = rg_builder.get_task(source).uv_rect_kind();
   1222 
   1223        let task_id = rg_builder.add().init(
   1224            RenderTask::new_dynamic(
   1225                padded_size,
   1226                RenderTaskKind::Scaling(ScalingTask {
   1227                    target_kind,
   1228                    padding,
   1229                }),
   1230            ).with_uv_rect_kind(uv_rect_kind)
   1231        );
   1232 
   1233        rg_builder.add_dependency(task_id, source);
   1234 
   1235        task_id
   1236    }
   1237 
   1238    pub fn add_sub_pass(
   1239        &mut self,
   1240        sub_pass: SubPass,
   1241    ) {
   1242        assert!(self.sub_pass.is_none(), "multiple sub-passes are not supported for now");
   1243        self.sub_pass = Some(sub_pass);
   1244    }
   1245 
   1246    /// Creates render tasks from PictureCompositeMode::SVGFEGraph.
   1247    ///
   1248    /// The interesting parts of the handling of SVG filters are:
   1249    /// * scene_building.rs : wrap_prim_with_filters
   1250    /// * picture.rs : get_coverage_svgfe
   1251    /// * render_task.rs : new_svg_filter_graph (you are here)
   1252    /// * render_target.rs : add_svg_filter_node_instances
   1253    pub fn new_svg_filter_graph(
   1254        filter_nodes: &[(FilterGraphNode, FilterGraphOp)],
   1255        rg_builder: &mut RenderTaskGraphBuilder,
   1256        gpu_buffer: &mut GpuBufferBuilderF,
   1257        data_stores: &mut DataStores,
   1258        _uv_rect_kind: UvRectKind,
   1259        original_task_id: RenderTaskId,
   1260        source_subregion: LayoutRect,
   1261        target_subregion: LayoutRect,
   1262        prim_subregion: LayoutRect,
   1263        subregion_to_device_scale_x: f32,
   1264        subregion_to_device_scale_y: f32,
   1265        subregion_to_device_offset_x: f32,
   1266        subregion_to_device_offset_y: f32,
   1267    ) -> RenderTaskId {
   1268        const BUFFER_LIMIT: usize = SVGFE_GRAPH_MAX;
   1269        let mut task_by_buffer_id: [RenderTaskId; BUFFER_LIMIT] = [RenderTaskId::INVALID; BUFFER_LIMIT];
   1270        let mut subregion_by_buffer_id: [LayoutRect; BUFFER_LIMIT] = [LayoutRect::zero(); BUFFER_LIMIT];
   1271        // If nothing replaces this value (all node subregions are empty), we
   1272        // can just return the original picture
   1273        let mut output_task_id = original_task_id;
   1274 
   1275        // By this point we assume the following about the graph:
   1276        // * BUFFER_LIMIT here should be >= BUFFER_LIMIT in the scene_building.rs code.
   1277        // * input buffer id < output buffer id
   1278        // * output buffer id between 0 and BUFFER_LIMIT
   1279        // * the number of filter_datas matches the number of kept nodes with op
   1280        //   SVGFEComponentTransfer.
   1281        //
   1282        // These assumptions are verified with asserts in this function as
   1283        // appropriate.
   1284 
   1285        // Make a UvRectKind::Quad that represents a task for a node, which may
   1286        // have an inflate border, must be a Quad because the surface_rects
   1287        // compositing shader expects it to be one, we don't actually use this
   1288        // internally as we use subregions, see calculate_uv_rect_kind for how
   1289        // this works, it projects from clipped rect to unclipped rect, where
   1290        // our clipped rect is simply task_size minus the inflate, and unclipped
   1291        // is our full task_size
   1292        fn uv_rect_kind_for_task_size(clipped: DeviceRect, unclipped: DeviceRect) -> UvRectKind {
   1293            let scale_x = 1.0 / clipped.width();
   1294            let scale_y = 1.0 / clipped.height();
   1295            UvRectKind::Quad{
   1296                top_left: DeviceHomogeneousVector::new(
   1297                    (unclipped.min.x - clipped.min.x) * scale_x,
   1298                    (unclipped.min.y - clipped.min.y) * scale_y,
   1299                    0.0, 1.0),
   1300                top_right: DeviceHomogeneousVector::new(
   1301                    (unclipped.max.x - clipped.min.x) * scale_x,
   1302                    (unclipped.min.y - clipped.min.y) * scale_y,
   1303                    0.0, 1.0),
   1304                bottom_left: DeviceHomogeneousVector::new(
   1305                    (unclipped.min.x - clipped.min.x) * scale_x,
   1306                    (unclipped.max.y - clipped.min.y) * scale_y,
   1307                    0.0, 1.0),
   1308                bottom_right: DeviceHomogeneousVector::new(
   1309                    (unclipped.max.x - clipped.min.x) * scale_x,
   1310                    (unclipped.max.y - clipped.min.y) * scale_y,
   1311                    0.0, 1.0),
   1312            }
   1313        }
   1314 
   1315        // Iterate the filter nodes and create tasks
   1316        let mut made_dependency_on_source = false;
   1317        for (filter_index, (filter_node, op)) in filter_nodes.iter().enumerate() {
   1318            let node = &filter_node;
   1319            let is_output = filter_index == filter_nodes.len() - 1;
   1320 
   1321            // Note that this is never set on the final output by design.
   1322            if !node.kept_by_optimizer {
   1323                continue;
   1324            }
   1325 
   1326            // Certain ops have parameters that need to be scaled to device
   1327            // space.
   1328            let op = match op {
   1329                FilterGraphOp::SVGFEBlendColor => op.clone(),
   1330                FilterGraphOp::SVGFEBlendColorBurn => op.clone(),
   1331                FilterGraphOp::SVGFEBlendColorDodge => op.clone(),
   1332                FilterGraphOp::SVGFEBlendDarken => op.clone(),
   1333                FilterGraphOp::SVGFEBlendDifference => op.clone(),
   1334                FilterGraphOp::SVGFEBlendExclusion => op.clone(),
   1335                FilterGraphOp::SVGFEBlendHardLight => op.clone(),
   1336                FilterGraphOp::SVGFEBlendHue => op.clone(),
   1337                FilterGraphOp::SVGFEBlendLighten => op.clone(),
   1338                FilterGraphOp::SVGFEBlendLuminosity => op.clone(),
   1339                FilterGraphOp::SVGFEBlendMultiply => op.clone(),
   1340                FilterGraphOp::SVGFEBlendNormal => op.clone(),
   1341                FilterGraphOp::SVGFEBlendOverlay => op.clone(),
   1342                FilterGraphOp::SVGFEBlendSaturation => op.clone(),
   1343                FilterGraphOp::SVGFEBlendScreen => op.clone(),
   1344                FilterGraphOp::SVGFEBlendSoftLight => op.clone(),
   1345                FilterGraphOp::SVGFEColorMatrix{..} => op.clone(),
   1346                FilterGraphOp::SVGFEComponentTransfer => unreachable!(),
   1347                FilterGraphOp::SVGFEComponentTransferInterned{..} => op.clone(),
   1348                FilterGraphOp::SVGFECompositeArithmetic{..} => op.clone(),
   1349                FilterGraphOp::SVGFECompositeATop => op.clone(),
   1350                FilterGraphOp::SVGFECompositeIn => op.clone(),
   1351                FilterGraphOp::SVGFECompositeLighter => op.clone(),
   1352                FilterGraphOp::SVGFECompositeOut => op.clone(),
   1353                FilterGraphOp::SVGFECompositeOver => op.clone(),
   1354                FilterGraphOp::SVGFECompositeXOR => op.clone(),
   1355                FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{
   1356                    kernel_unit_length_x, kernel_unit_length_y, order_x,
   1357                    order_y, kernel, divisor, bias, target_x, target_y,
   1358                    preserve_alpha} => {
   1359                    FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{
   1360                        kernel_unit_length_x:
   1361                            (kernel_unit_length_x * subregion_to_device_scale_x).round(),
   1362                        kernel_unit_length_y:
   1363                            (kernel_unit_length_y * subregion_to_device_scale_y).round(),
   1364                        order_x: *order_x, order_y: *order_y, kernel: *kernel,
   1365                        divisor: *divisor, bias: *bias, target_x: *target_x,
   1366                        target_y: *target_y, preserve_alpha: *preserve_alpha}
   1367                },
   1368                FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{
   1369                    kernel_unit_length_x, kernel_unit_length_y, order_x,
   1370                    order_y, kernel, divisor, bias, target_x, target_y,
   1371                    preserve_alpha} => {
   1372                    FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{
   1373                        kernel_unit_length_x:
   1374                            (kernel_unit_length_x * subregion_to_device_scale_x).round(),
   1375                        kernel_unit_length_y:
   1376                            (kernel_unit_length_y * subregion_to_device_scale_y).round(),
   1377                        order_x: *order_x, order_y: *order_y, kernel: *kernel,
   1378                        divisor: *divisor, bias: *bias, target_x: *target_x,
   1379                        target_y: *target_y, preserve_alpha: *preserve_alpha}
   1380                },
   1381                FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{
   1382                    kernel_unit_length_x, kernel_unit_length_y, order_x,
   1383                    order_y, kernel, divisor, bias, target_x, target_y,
   1384                    preserve_alpha} => {
   1385                    FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{
   1386                        kernel_unit_length_x:
   1387                            (kernel_unit_length_x * subregion_to_device_scale_x).round(),
   1388                        kernel_unit_length_y:
   1389                            (kernel_unit_length_y * subregion_to_device_scale_y).round(),
   1390                        order_x: *order_x, order_y: *order_y, kernel: *kernel,
   1391                        divisor: *divisor, bias: *bias, target_x: *target_x,
   1392                        target_y: *target_y, preserve_alpha: *preserve_alpha}
   1393                },
   1394                FilterGraphOp::SVGFEDiffuseLightingDistant{
   1395                    surface_scale, diffuse_constant, kernel_unit_length_x,
   1396                    kernel_unit_length_y, azimuth, elevation} => {
   1397                    FilterGraphOp::SVGFEDiffuseLightingDistant{
   1398                        surface_scale: *surface_scale,
   1399                        diffuse_constant: *diffuse_constant,
   1400                        kernel_unit_length_x:
   1401                            (kernel_unit_length_x * subregion_to_device_scale_x).round(),
   1402                        kernel_unit_length_y:
   1403                            (kernel_unit_length_y * subregion_to_device_scale_y).round(),
   1404                        azimuth: *azimuth, elevation: *elevation}
   1405                },
   1406                FilterGraphOp::SVGFEDiffuseLightingPoint{
   1407                    surface_scale, diffuse_constant, kernel_unit_length_x,
   1408                    kernel_unit_length_y, x, y, z} => {
   1409                    FilterGraphOp::SVGFEDiffuseLightingPoint{
   1410                        surface_scale: *surface_scale,
   1411                        diffuse_constant: *diffuse_constant,
   1412                        kernel_unit_length_x:
   1413                            (kernel_unit_length_x * subregion_to_device_scale_x).round(),
   1414                        kernel_unit_length_y:
   1415                            (kernel_unit_length_y * subregion_to_device_scale_y).round(),
   1416                        x: x * subregion_to_device_scale_x + subregion_to_device_offset_x,
   1417                        y: y * subregion_to_device_scale_y + subregion_to_device_offset_y,
   1418                        z: *z}
   1419                },
   1420                FilterGraphOp::SVGFEDiffuseLightingSpot{
   1421                    surface_scale, diffuse_constant, kernel_unit_length_x,
   1422                    kernel_unit_length_y, x, y, z, points_at_x, points_at_y,
   1423                    points_at_z, cone_exponent, limiting_cone_angle} => {
   1424                    FilterGraphOp::SVGFEDiffuseLightingSpot{
   1425                        surface_scale: *surface_scale,
   1426                        diffuse_constant: *diffuse_constant,
   1427                        kernel_unit_length_x:
   1428                            (kernel_unit_length_x * subregion_to_device_scale_x).round(),
   1429                        kernel_unit_length_y:
   1430                            (kernel_unit_length_y * subregion_to_device_scale_y).round(),
   1431                        x: x * subregion_to_device_scale_x + subregion_to_device_offset_x,
   1432                        y: y * subregion_to_device_scale_y + subregion_to_device_offset_y,
   1433                        z: *z,
   1434                        points_at_x: points_at_x * subregion_to_device_scale_x + subregion_to_device_offset_x,
   1435                        points_at_y: points_at_y * subregion_to_device_scale_y + subregion_to_device_offset_y,
   1436                        points_at_z: *points_at_z,
   1437                        cone_exponent: *cone_exponent,
   1438                        limiting_cone_angle: *limiting_cone_angle}
   1439                },
   1440                FilterGraphOp::SVGFEFlood{..} => op.clone(),
   1441                FilterGraphOp::SVGFEDisplacementMap{
   1442                    scale, x_channel_selector, y_channel_selector} => {
   1443                    FilterGraphOp::SVGFEDisplacementMap{
   1444                        scale: scale * subregion_to_device_scale_x,
   1445                        x_channel_selector: *x_channel_selector,
   1446                        y_channel_selector: *y_channel_selector}
   1447                },
   1448                FilterGraphOp::SVGFEDropShadow{
   1449                    color, dx, dy, std_deviation_x, std_deviation_y} => {
   1450                    FilterGraphOp::SVGFEDropShadow{
   1451                        color: *color,
   1452                        dx: dx * subregion_to_device_scale_x,
   1453                        dy: dy * subregion_to_device_scale_y,
   1454                        std_deviation_x: std_deviation_x * subregion_to_device_scale_x,
   1455                        std_deviation_y: std_deviation_y * subregion_to_device_scale_y}
   1456                },
   1457                FilterGraphOp::SVGFEGaussianBlur{std_deviation_x, std_deviation_y} => {
   1458                    let std_deviation_x = std_deviation_x * subregion_to_device_scale_x;
   1459                    let std_deviation_y = std_deviation_y * subregion_to_device_scale_y;
   1460                    // For blurs that effectively have no radius in display
   1461                    // space, we can convert to identity.
   1462                    if std_deviation_x + std_deviation_y >= 0.125 {
   1463                        FilterGraphOp::SVGFEGaussianBlur{
   1464                            std_deviation_x,
   1465                            std_deviation_y}
   1466                    } else {
   1467                        FilterGraphOp::SVGFEIdentity
   1468                    }
   1469                },
   1470                FilterGraphOp::SVGFEIdentity => op.clone(),
   1471                FilterGraphOp::SVGFEImage{..} => op.clone(),
   1472                FilterGraphOp::SVGFEMorphologyDilate{radius_x, radius_y} => {
   1473                    FilterGraphOp::SVGFEMorphologyDilate{
   1474                        radius_x: (radius_x * subregion_to_device_scale_x).round(),
   1475                        radius_y: (radius_y * subregion_to_device_scale_y).round()}
   1476                },
   1477                FilterGraphOp::SVGFEMorphologyErode{radius_x, radius_y} => {
   1478                    FilterGraphOp::SVGFEMorphologyErode{
   1479                        radius_x: (radius_x * subregion_to_device_scale_x).round(),
   1480                        radius_y: (radius_y * subregion_to_device_scale_y).round()}
   1481                },
   1482                FilterGraphOp::SVGFEOpacity{..} => op.clone(),
   1483                FilterGraphOp::SVGFESourceAlpha => op.clone(),
   1484                FilterGraphOp::SVGFESourceGraphic => op.clone(),
   1485                FilterGraphOp::SVGFESpecularLightingDistant{
   1486                    surface_scale, specular_constant, specular_exponent,
   1487                    kernel_unit_length_x, kernel_unit_length_y, azimuth,
   1488                    elevation} => {
   1489                    FilterGraphOp::SVGFESpecularLightingDistant{
   1490                        surface_scale: *surface_scale,
   1491                        specular_constant: *specular_constant,
   1492                        specular_exponent: *specular_exponent,
   1493                        kernel_unit_length_x:
   1494                            (kernel_unit_length_x * subregion_to_device_scale_x).round(),
   1495                        kernel_unit_length_y:
   1496                            (kernel_unit_length_y * subregion_to_device_scale_y).round(),
   1497                        azimuth: *azimuth, elevation: *elevation}
   1498                },
   1499                FilterGraphOp::SVGFESpecularLightingPoint{
   1500                    surface_scale, specular_constant, specular_exponent,
   1501                    kernel_unit_length_x, kernel_unit_length_y, x, y, z } => {
   1502                    FilterGraphOp::SVGFESpecularLightingPoint{
   1503                        surface_scale: *surface_scale,
   1504                        specular_constant: *specular_constant,
   1505                        specular_exponent: *specular_exponent,
   1506                        kernel_unit_length_x:
   1507                            (kernel_unit_length_x * subregion_to_device_scale_x).round(),
   1508                        kernel_unit_length_y:
   1509                            (kernel_unit_length_y * subregion_to_device_scale_y).round(),
   1510                        x: x * subregion_to_device_scale_x + subregion_to_device_offset_x,
   1511                        y: y * subregion_to_device_scale_y + subregion_to_device_offset_y,
   1512                        z: *z }
   1513                },
   1514                FilterGraphOp::SVGFESpecularLightingSpot{
   1515                    surface_scale, specular_constant, specular_exponent,
   1516                    kernel_unit_length_x, kernel_unit_length_y, x, y, z,
   1517                    points_at_x, points_at_y, points_at_z, cone_exponent,
   1518                    limiting_cone_angle} => {
   1519                    FilterGraphOp::SVGFESpecularLightingSpot{
   1520                        surface_scale: *surface_scale,
   1521                        specular_constant: *specular_constant,
   1522                        specular_exponent: *specular_exponent,
   1523                        kernel_unit_length_x:
   1524                            (kernel_unit_length_x * subregion_to_device_scale_x).round(),
   1525                        kernel_unit_length_y:
   1526                            (kernel_unit_length_y * subregion_to_device_scale_y).round(),
   1527                        x: x * subregion_to_device_scale_x + subregion_to_device_offset_x,
   1528                        y: y * subregion_to_device_scale_y + subregion_to_device_offset_y,
   1529                        z: *z,
   1530                        points_at_x: points_at_x * subregion_to_device_scale_x + subregion_to_device_offset_x,
   1531                        points_at_y: points_at_y * subregion_to_device_scale_y + subregion_to_device_offset_y,
   1532                        points_at_z: *points_at_z,
   1533                        cone_exponent: *cone_exponent,
   1534                        limiting_cone_angle: *limiting_cone_angle}
   1535                },
   1536                FilterGraphOp::SVGFETile => op.clone(),
   1537                FilterGraphOp::SVGFEToAlpha => op.clone(),
   1538                FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{
   1539                    base_frequency_x, base_frequency_y, num_octaves, seed} => {
   1540                    FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{
   1541                        base_frequency_x:
   1542                            base_frequency_x * subregion_to_device_scale_x,
   1543                        base_frequency_y:
   1544                            base_frequency_y * subregion_to_device_scale_y,
   1545                        num_octaves: *num_octaves, seed: *seed}
   1546                },
   1547                FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{
   1548                    base_frequency_x, base_frequency_y, num_octaves, seed} => {
   1549                    FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{
   1550                        base_frequency_x:
   1551                            base_frequency_x * subregion_to_device_scale_x,
   1552                        base_frequency_y:
   1553                            base_frequency_y * subregion_to_device_scale_y,
   1554                        num_octaves: *num_octaves, seed: *seed}
   1555                },
   1556                FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{
   1557                    base_frequency_x, base_frequency_y, num_octaves, seed} => {
   1558                    FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{
   1559                        base_frequency_x:
   1560                            base_frequency_x * subregion_to_device_scale_x,
   1561                        base_frequency_y:
   1562                            base_frequency_y * subregion_to_device_scale_y,
   1563                        num_octaves: *num_octaves, seed: *seed}
   1564                },
   1565                FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{
   1566                    base_frequency_x, base_frequency_y, num_octaves, seed} => {
   1567                    FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{
   1568                        base_frequency_x:
   1569                            base_frequency_x * subregion_to_device_scale_x,
   1570                        base_frequency_y:
   1571                            base_frequency_y * subregion_to_device_scale_y,
   1572                        num_octaves: *num_octaves, seed: *seed}
   1573                },
   1574            };
   1575 
   1576            // Process the inputs and figure out their new subregion, because
   1577            // the SourceGraphic subregion is smaller than it was in scene build
   1578            // now that it reflects the invalidation rect
   1579            //
   1580            // Also look up the child tasks while we are here.
   1581            let mut used_subregion = LayoutRect::zero();
   1582            let mut combined_input_subregion = LayoutRect::zero();
   1583            let node_inputs: Vec<(FilterGraphPictureReference, RenderTaskId)> = node.inputs.iter().map(|input| {
   1584                let (subregion, task) =
   1585                    match input.buffer_id {
   1586                        FilterOpGraphPictureBufferId::BufferId(id) => {
   1587                            (subregion_by_buffer_id[id as usize], task_by_buffer_id[id as usize])
   1588                        }
   1589                        FilterOpGraphPictureBufferId::None => {
   1590                            // Task must resolve so we use the SourceGraphic as
   1591                            // a placeholder for these, they don't actually
   1592                            // contribute anything to the output
   1593                            (LayoutRect::zero(), original_task_id)
   1594                        }
   1595                    };
   1596                // Convert offset to device coordinates.
   1597                let offset = LayoutVector2D::new(
   1598                        (input.offset.x * subregion_to_device_scale_x).round(),
   1599                        (input.offset.y * subregion_to_device_scale_y).round(),
   1600                    );
   1601                // To figure out the portion of the node subregion used by this
   1602                // source image we need to apply the target padding.  Note that
   1603                // this does not affect the subregion of the input, as that
   1604                // can't be modified as it is used for placement (offset).
   1605                let target_padding = input.target_padding
   1606                    .scale(subregion_to_device_scale_x, subregion_to_device_scale_y)
   1607                    .round();
   1608                let target_subregion =
   1609                    LayoutRect::new(
   1610                        LayoutPoint::new(
   1611                            subregion.min.x + target_padding.min.x,
   1612                            subregion.min.y + target_padding.min.y,
   1613                        ),
   1614                        LayoutPoint::new(
   1615                            subregion.max.x + target_padding.max.x,
   1616                            subregion.max.y + target_padding.max.y,
   1617                        ),
   1618                    );
   1619                used_subregion = used_subregion.union(&target_subregion);
   1620                combined_input_subregion = combined_input_subregion.union(&subregion);
   1621                (FilterGraphPictureReference{
   1622                    buffer_id: input.buffer_id,
   1623                    // Apply offset to the placement of the input subregion.
   1624                    subregion: subregion.translate(offset),
   1625                    offset: LayoutVector2D::zero(),
   1626                    inflate: input.inflate,
   1627                    // Nothing past this point uses the padding.
   1628                    source_padding: LayoutRect::zero(),
   1629                    target_padding: LayoutRect::zero(),
   1630                }, task)
   1631            }).collect();
   1632 
   1633            // Convert subregion from PicturePixels to DevicePixels and round.
   1634            let full_subregion = node.subregion
   1635                .scale(subregion_to_device_scale_x, subregion_to_device_scale_y)
   1636                .translate(LayoutVector2D::new(subregion_to_device_offset_x, subregion_to_device_offset_y))
   1637                .round();
   1638 
   1639            // Clip the used subregion we calculated from the inputs to fit
   1640            // within the node's specified subregion, but we want to keep a copy
   1641            // of the combined input subregion for sizing tasks that involve
   1642            // blurs as their intermediate stages will have to be downscaled if
   1643            // very large, and we want that to be at the same alignment as the
   1644            // node output itself.
   1645            used_subregion = used_subregion
   1646                .intersection(&full_subregion)
   1647                .unwrap_or(LayoutRect::zero())
   1648                .round();
   1649 
   1650            // Certain filters need to override the used_subregion directly.
   1651            match op {
   1652                FilterGraphOp::SVGFEBlendColor => {},
   1653                FilterGraphOp::SVGFEBlendColorBurn => {},
   1654                FilterGraphOp::SVGFEBlendColorDodge => {},
   1655                FilterGraphOp::SVGFEBlendDarken => {},
   1656                FilterGraphOp::SVGFEBlendDifference => {},
   1657                FilterGraphOp::SVGFEBlendExclusion => {},
   1658                FilterGraphOp::SVGFEBlendHardLight => {},
   1659                FilterGraphOp::SVGFEBlendHue => {},
   1660                FilterGraphOp::SVGFEBlendLighten => {},
   1661                FilterGraphOp::SVGFEBlendLuminosity => {},
   1662                FilterGraphOp::SVGFEBlendMultiply => {},
   1663                FilterGraphOp::SVGFEBlendNormal => {},
   1664                FilterGraphOp::SVGFEBlendOverlay => {},
   1665                FilterGraphOp::SVGFEBlendSaturation => {},
   1666                FilterGraphOp::SVGFEBlendScreen => {},
   1667                FilterGraphOp::SVGFEBlendSoftLight => {},
   1668                FilterGraphOp::SVGFEColorMatrix{values} => {
   1669                    if values[19] > 0.0 {
   1670                        // Manipulating alpha offset can easily create new
   1671                        // pixels outside of input subregions
   1672                        used_subregion = full_subregion;
   1673                    }
   1674                },
   1675                FilterGraphOp::SVGFEComponentTransfer => unreachable!(),
   1676                FilterGraphOp::SVGFEComponentTransferInterned{handle: _, creates_pixels} => {
   1677                    // Check if the value of alpha[0] is modified, if so
   1678                    // the whole subregion is used because it will be
   1679                    // creating new pixels outside of input subregions
   1680                    if creates_pixels {
   1681                        used_subregion = full_subregion;
   1682                    }
   1683                },
   1684                FilterGraphOp::SVGFECompositeArithmetic { k1, k2, k3, k4 } => {
   1685                    // Optimize certain cases of Arithmetic operator
   1686                    //
   1687                    // See logic for SVG_FECOMPOSITE_OPERATOR_ARITHMETIC
   1688                    // in FilterSupport.cpp for more information.
   1689                    //
   1690                    // Any other case uses the union of input subregions
   1691                    if k4 > 0.0 {
   1692                        // Can produce pixels anywhere in the subregion.
   1693                        used_subregion = full_subregion;
   1694                    } else  if k1 > 0.0 && k2 == 0.0 && k3 == 0.0 {
   1695                        // Can produce pixels where both exist.
   1696                        used_subregion = full_subregion
   1697                            .intersection(&node_inputs[0].0.subregion)
   1698                            .unwrap_or(LayoutRect::zero())
   1699                            .intersection(&node_inputs[1].0.subregion)
   1700                            .unwrap_or(LayoutRect::zero());
   1701                    }
   1702                    else if k2 > 0.0 && k3 == 0.0 {
   1703                        // Can produce pixels where source exists.
   1704                        used_subregion = full_subregion
   1705                            .intersection(&node_inputs[0].0.subregion)
   1706                            .unwrap_or(LayoutRect::zero());
   1707                    }
   1708                    else if k2 == 0.0 && k3 > 0.0 {
   1709                        // Can produce pixels where background exists.
   1710                        used_subregion = full_subregion
   1711                            .intersection(&node_inputs[1].0.subregion)
   1712                            .unwrap_or(LayoutRect::zero());
   1713                    }
   1714                },
   1715                FilterGraphOp::SVGFECompositeATop => {
   1716                    // Can only produce pixels where background exists.
   1717                    used_subregion = full_subregion
   1718                        .intersection(&node_inputs[1].0.subregion)
   1719                        .unwrap_or(LayoutRect::zero());
   1720                },
   1721                FilterGraphOp::SVGFECompositeIn => {
   1722                    // Can only produce pixels where both exist.
   1723                    used_subregion = used_subregion
   1724                        .intersection(&node_inputs[0].0.subregion)
   1725                        .unwrap_or(LayoutRect::zero())
   1726                        .intersection(&node_inputs[1].0.subregion)
   1727                        .unwrap_or(LayoutRect::zero());
   1728                },
   1729                FilterGraphOp::SVGFECompositeLighter => {},
   1730                FilterGraphOp::SVGFECompositeOut => {
   1731                    // Can only produce pixels where source exists.
   1732                    used_subregion = full_subregion
   1733                        .intersection(&node_inputs[0].0.subregion)
   1734                        .unwrap_or(LayoutRect::zero());
   1735                },
   1736                FilterGraphOp::SVGFECompositeOver => {},
   1737                FilterGraphOp::SVGFECompositeXOR => {},
   1738                FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => {},
   1739                FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => {},
   1740                FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => {},
   1741                FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {},
   1742                FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {},
   1743                FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {},
   1744                FilterGraphOp::SVGFEDisplacementMap{..} => {},
   1745                FilterGraphOp::SVGFEDropShadow{..} => {},
   1746                FilterGraphOp::SVGFEFlood { color } => {
   1747                    // Subregion needs to be set to the full node
   1748                    // subregion for fills (unless the fill is a no-op),
   1749                    // we know at this point that it has no inputs, so the
   1750                    // used_region is empty unless we set it here.
   1751                    if color.a > 0.0 {
   1752                        used_subregion = full_subregion;
   1753                    }
   1754                },
   1755                FilterGraphOp::SVGFEIdentity => {},
   1756                FilterGraphOp::SVGFEImage { sampling_filter: _sampling_filter, matrix: _matrix } => {
   1757                    // TODO: calculate the actual subregion
   1758                    used_subregion = full_subregion;
   1759                },
   1760                FilterGraphOp::SVGFEGaussianBlur{..} => {},
   1761                FilterGraphOp::SVGFEMorphologyDilate{..} => {},
   1762                FilterGraphOp::SVGFEMorphologyErode{..} => {},
   1763                FilterGraphOp::SVGFEOpacity{valuebinding: _valuebinding, value} => {
   1764                    // If fully transparent, we can ignore this node
   1765                    if value <= 0.0 {
   1766                        used_subregion = LayoutRect::zero();
   1767                    }
   1768                },
   1769                FilterGraphOp::SVGFESourceAlpha |
   1770                FilterGraphOp::SVGFESourceGraphic => {
   1771                    used_subregion = source_subregion
   1772                        .intersection(&full_subregion)
   1773                        .unwrap_or(LayoutRect::zero());
   1774                },
   1775                FilterGraphOp::SVGFESpecularLightingDistant{..} => {},
   1776                FilterGraphOp::SVGFESpecularLightingPoint{..} => {},
   1777                FilterGraphOp::SVGFESpecularLightingSpot{..} => {},
   1778                FilterGraphOp::SVGFETile => {
   1779                    if !used_subregion.is_empty() {
   1780                        // This fills the entire target, at least if there are
   1781                        // any input pixels to work with.
   1782                        used_subregion = full_subregion;
   1783                    }
   1784                },
   1785                FilterGraphOp::SVGFEToAlpha => {},
   1786                FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} |
   1787                FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} |
   1788                FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} |
   1789                FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {
   1790                    // Turbulence produces pixel values throughout the
   1791                    // node subregion.
   1792                    used_subregion = full_subregion;
   1793                },
   1794            }
   1795 
   1796            add_text_marker(
   1797                "SVGFEGraph",
   1798                &format!("{}({})", op.kind(), filter_index),
   1799                Duration::from_micros((used_subregion.width() * used_subregion.height() / 1000.0) as u64),
   1800            );
   1801 
   1802            // SVG spec requires that a later node sampling pixels outside
   1803            // this node's subregion will receive a transparent black color
   1804            // for those samples, we achieve this by adding a 1 pixel inflate
   1805            // around the target rect, which works fine with the
   1806            // edgemode=duplicate behavior of the texture fetch in the shader,
   1807            // all of the out of bounds reads are transparent black.
   1808            //
   1809            // If this is the output node, we don't apply the inflate, knowing
   1810            // that the pixels outside of the invalidation rect will not be used
   1811            // so it is okay if they duplicate outside the view.
   1812            let mut node_inflate = node.inflate;
   1813            if is_output {
   1814                // Use the provided target subregion (invalidation rect)
   1815                used_subregion = target_subregion;
   1816                node_inflate = 0;
   1817            }
   1818 
   1819            // We can't render tasks larger than a certain size, if this node
   1820            // is too large (particularly with blur padding), we need to render
   1821            // at a reduced resolution, later nodes can still be full resolution
   1822            // but for example blurs are not significantly harmed by reduced
   1823            // resolution in most cases.
   1824            let mut device_to_render_scale = 1.0;
   1825            let mut render_to_device_scale = 1.0;
   1826            let mut subregion = used_subregion;
   1827            let padded_subregion = match op {
   1828                FilterGraphOp::SVGFEGaussianBlur{std_deviation_x, std_deviation_y} |
   1829                FilterGraphOp::SVGFEDropShadow{std_deviation_x, std_deviation_y, ..} => {
   1830                    used_subregion
   1831                    .inflate(
   1832                        std_deviation_x.ceil() * BLUR_SAMPLE_SCALE,
   1833                        std_deviation_y.ceil() * BLUR_SAMPLE_SCALE)
   1834                }
   1835                _ => used_subregion,
   1836            }.union(&combined_input_subregion);
   1837            while
   1838                padded_subregion.scale(device_to_render_scale, device_to_render_scale).round().width() + node_inflate as f32 * 2.0 > MAX_SURFACE_SIZE as f32 ||
   1839                padded_subregion.scale(device_to_render_scale, device_to_render_scale).round().height() + node_inflate as f32 * 2.0 > MAX_SURFACE_SIZE as f32 {
   1840                device_to_render_scale *= 0.5;
   1841                render_to_device_scale *= 2.0;
   1842                // If the rendering was scaled, we need to snap used_subregion
   1843                // to the correct granularity or we'd have misaligned sampling
   1844                // when this is used as an input later.
   1845                subregion = used_subregion
   1846                    .scale(device_to_render_scale, device_to_render_scale)
   1847                    .round()
   1848                    .scale(render_to_device_scale, render_to_device_scale);
   1849            }
   1850 
   1851            // This is the rect we will be actually producing as a render task,
   1852            // it is sometimes the case that subregion is empty, but we
   1853            // must make a task or else the earlier tasks would not be properly
   1854            // linked into the frametree, causing a leak.
   1855            let node_task_rect: DeviceRect =
   1856                subregion
   1857                .scale(device_to_render_scale, device_to_render_scale)
   1858                .round()
   1859                .inflate(node_inflate as f32, node_inflate as f32)
   1860                .cast_unit();
   1861            let node_task_size = node_task_rect.to_i32().size();
   1862            let node_task_size =
   1863                if node_task_size.width < 1 || node_task_size.height < 1 {
   1864                    DeviceIntSize::new(1, 1)
   1865                } else {
   1866                    node_task_size
   1867                };
   1868 
   1869            // Make the uv_rect_kind for this node's task to use, this matters
   1870            // only on the final node because we don't use it internally
   1871            let node_uv_rect_kind = uv_rect_kind_for_task_size(
   1872                subregion
   1873                .scale(device_to_render_scale, device_to_render_scale)
   1874                .round()
   1875                .inflate(node_inflate as f32, node_inflate as f32)
   1876                .cast_unit(),
   1877                prim_subregion
   1878                .scale(device_to_render_scale, device_to_render_scale)
   1879                .round()
   1880                .inflate(node_inflate as f32, node_inflate as f32)
   1881                .cast_unit(),
   1882            );
   1883 
   1884            // Create task for this node
   1885            let task_id;
   1886            match op {
   1887                FilterGraphOp::SVGFEGaussianBlur { std_deviation_x, std_deviation_y } => {
   1888                    // Note: wrap_prim_with_filters copies the SourceGraphic to
   1889                    // a node to apply the transparent border around the image,
   1890                    // we rely on that behavior here as the Blur filter is a
   1891                    // different shader without awareness of the subregion
   1892                    // rules in the SVG spec.
   1893 
   1894                    // Find the input task id
   1895                    assert!(node_inputs.len() == 1);
   1896                    let blur_input = &node_inputs[0].0;
   1897                    let source_task_id = node_inputs[0].1;
   1898 
   1899                    // We have to make a copy of the input that is padded with
   1900                    // transparent black for the area outside the subregion, so
   1901                    // that the blur task does not duplicate at the edges
   1902                    let adjusted_blur_std_deviation = DeviceSize::new(
   1903                        std_deviation_x.clamp(0.0, (i32::MAX / 2) as f32) * device_to_render_scale,
   1904                        std_deviation_y.clamp(0.0, (i32::MAX / 2) as f32) * device_to_render_scale,
   1905                    );
   1906                    let blur_subregion = blur_input.subregion
   1907                        .scale(device_to_render_scale, device_to_render_scale)
   1908                        .inflate(
   1909                            adjusted_blur_std_deviation.width * BLUR_SAMPLE_SCALE,
   1910                            adjusted_blur_std_deviation.height * BLUR_SAMPLE_SCALE)
   1911                        .round_out();
   1912                    let blur_task_size = blur_subregion
   1913                        .size()
   1914                        .cast_unit()
   1915                        .max(DeviceSize::new(1.0, 1.0));
   1916                    // Adjust task size to prevent potential sampling errors
   1917                    let adjusted_blur_task_size =
   1918                        BlurTask::adjusted_blur_source_size(
   1919                            blur_task_size,
   1920                            adjusted_blur_std_deviation,
   1921                        ).max(DeviceSize::new(1.0, 1.0));
   1922                    // Now change the subregion to match the revised task size,
   1923                    // keeping it centered should keep animated radius smooth.
   1924                    let corner = LayoutPoint::new(
   1925                            blur_subregion.min.x.floor() + ((
   1926                                blur_task_size.width -
   1927                                adjusted_blur_task_size.width) * 0.5).floor(),
   1928                            blur_subregion.min.y.floor() + ((
   1929                                blur_task_size.height -
   1930                                adjusted_blur_task_size.height) * 0.5).floor(),
   1931                        );
   1932                    // Recalculate the blur_subregion to match, and if render
   1933                    // scale is used, undo that so it is in the same subregion
   1934                    // coordinate system as the node
   1935                    let blur_subregion =
   1936                        LayoutRect::new(
   1937                            corner,
   1938                            LayoutPoint::new(
   1939                                corner.x + adjusted_blur_task_size.width,
   1940                                corner.y + adjusted_blur_task_size.height,
   1941                            ),
   1942                        )
   1943                        .scale(render_to_device_scale, render_to_device_scale);
   1944 
   1945                    let input_subregion_task_id = rg_builder.add().init(RenderTask::new_dynamic(
   1946                        adjusted_blur_task_size.to_i32(),
   1947                        RenderTaskKind::SVGFENode(
   1948                            SVGFEFilterTask{
   1949                                node: FilterGraphNode{
   1950                                    kept_by_optimizer: true,
   1951                                    linear: false,
   1952                                    inflate: 0,
   1953                                    inputs: [blur_input.clone()].to_vec(),
   1954                                    subregion: blur_subregion,
   1955                                },
   1956                                op: FilterGraphOp::SVGFEIdentity,
   1957                                content_origin: DevicePoint::zero(),
   1958                                extra_gpu_data: None,
   1959                            }
   1960                        ),
   1961                    ).with_uv_rect_kind(UvRectKind::Rect));
   1962                    // Adding the dependencies sets the inputs for this task
   1963                    rg_builder.add_dependency(input_subregion_task_id, source_task_id);
   1964 
   1965                    // TODO: We should do this blur in the correct
   1966                    // colorspace, linear=true is the default in SVG and
   1967                    // new_blur does not currently support it.  If the nodes
   1968                    // that consume the result only use the alpha channel, it
   1969                    // does not matter, but when they use the RGB it matters.
   1970                    let blur_task_id =
   1971                        RenderTask::new_blur(
   1972                            adjusted_blur_std_deviation,
   1973                            input_subregion_task_id,
   1974                            rg_builder,
   1975                            RenderTargetKind::Color,
   1976                            None,
   1977                            adjusted_blur_task_size.to_i32(),
   1978                            BlurEdgeMode::Duplicate,
   1979                        );
   1980 
   1981                    task_id = rg_builder.add().init(RenderTask::new_dynamic(
   1982                        node_task_size,
   1983                        RenderTaskKind::SVGFENode(
   1984                            SVGFEFilterTask{
   1985                                node: FilterGraphNode{
   1986                                    kept_by_optimizer: true,
   1987                                    linear: node.linear,
   1988                                    inflate: node_inflate,
   1989                                    inputs: [
   1990                                        FilterGraphPictureReference{
   1991                                            buffer_id: blur_input.buffer_id,
   1992                                            subregion: blur_subregion,
   1993                                            inflate: 0,
   1994                                            offset: LayoutVector2D::zero(),
   1995                                            source_padding: LayoutRect::zero(),
   1996                                            target_padding: LayoutRect::zero(),
   1997                                        }].to_vec(),
   1998                                    subregion,
   1999                                },
   2000                                op: FilterGraphOp::SVGFEIdentity,
   2001                                content_origin: node_task_rect.min,
   2002                                extra_gpu_data: None,
   2003                            }
   2004                        ),
   2005                    ).with_uv_rect_kind(node_uv_rect_kind));
   2006                    // Adding the dependencies sets the inputs for this task
   2007                    rg_builder.add_dependency(task_id, blur_task_id);
   2008                }
   2009                FilterGraphOp::SVGFEDropShadow { color, dx, dy, std_deviation_x, std_deviation_y } => {
   2010                    // Note: wrap_prim_with_filters copies the SourceGraphic to
   2011                    // a node to apply the transparent border around the image,
   2012                    // we rely on that behavior here as the Blur filter is a
   2013                    // different shader without awareness of the subregion
   2014                    // rules in the SVG spec.
   2015 
   2016                    // Find the input task id
   2017                    assert!(node_inputs.len() == 1);
   2018                    let blur_input = &node_inputs[0].0;
   2019                    let source_task_id = node_inputs[0].1;
   2020 
   2021                    // We have to make a copy of the input that is padded with
   2022                    // transparent black for the area outside the subregion, so
   2023                    // that the blur task does not duplicate at the edges
   2024                    let adjusted_blur_std_deviation = DeviceSize::new(
   2025                        std_deviation_x.clamp(0.0, (i32::MAX / 2) as f32) * device_to_render_scale,
   2026                        std_deviation_y.clamp(0.0, (i32::MAX / 2) as f32) * device_to_render_scale,
   2027                    );
   2028                    let blur_subregion = blur_input.subregion
   2029                        .scale(device_to_render_scale, device_to_render_scale)
   2030                        .inflate(
   2031                            adjusted_blur_std_deviation.width * BLUR_SAMPLE_SCALE,
   2032                            adjusted_blur_std_deviation.height * BLUR_SAMPLE_SCALE)
   2033                        .round_out();
   2034                    let blur_task_size = blur_subregion
   2035                        .size()
   2036                        .cast_unit()
   2037                        .max(DeviceSize::new(1.0, 1.0));
   2038                    // Adjust task size to prevent potential sampling errors
   2039                    let adjusted_blur_task_size =
   2040                        BlurTask::adjusted_blur_source_size(
   2041                            blur_task_size,
   2042                            adjusted_blur_std_deviation,
   2043                        ).max(DeviceSize::new(1.0, 1.0));
   2044                    // Now change the subregion to match the revised task size,
   2045                    // keeping it centered should keep animated radius smooth.
   2046                    let corner = LayoutPoint::new(
   2047                            blur_subregion.min.x.floor() + ((
   2048                                blur_task_size.width -
   2049                                adjusted_blur_task_size.width) * 0.5).floor(),
   2050                            blur_subregion.min.y.floor() + ((
   2051                                blur_task_size.height -
   2052                                adjusted_blur_task_size.height) * 0.5).floor(),
   2053                        );
   2054                    // Recalculate the blur_subregion to match, and if render
   2055                    // scale is used, undo that so it is in the same subregion
   2056                    // coordinate system as the node
   2057                    let blur_subregion =
   2058                        LayoutRect::new(
   2059                            corner,
   2060                            LayoutPoint::new(
   2061                                corner.x + adjusted_blur_task_size.width,
   2062                                corner.y + adjusted_blur_task_size.height,
   2063                            ),
   2064                        )
   2065                        .scale(render_to_device_scale, render_to_device_scale);
   2066 
   2067                    let input_subregion_task_id = rg_builder.add().init(RenderTask::new_dynamic(
   2068                        adjusted_blur_task_size.to_i32(),
   2069                        RenderTaskKind::SVGFENode(
   2070                            SVGFEFilterTask{
   2071                                node: FilterGraphNode{
   2072                                    kept_by_optimizer: true,
   2073                                    linear: false,
   2074                                    inputs: [
   2075                                        FilterGraphPictureReference{
   2076                                            buffer_id: blur_input.buffer_id,
   2077                                            subregion: blur_input.subregion,
   2078                                            offset: LayoutVector2D::zero(),
   2079                                            inflate: blur_input.inflate,
   2080                                            source_padding: LayoutRect::zero(),
   2081                                            target_padding: LayoutRect::zero(),
   2082                                        }].to_vec(),
   2083                                    subregion: blur_subregion,
   2084                                    inflate: 0,
   2085                                },
   2086                                op: FilterGraphOp::SVGFEIdentity,
   2087                                content_origin: node_task_rect.min,
   2088                                extra_gpu_data: None,
   2089                            }
   2090                        ),
   2091                    ).with_uv_rect_kind(UvRectKind::Rect));
   2092                    // Adding the dependencies sets the inputs for this task
   2093                    rg_builder.add_dependency(input_subregion_task_id, source_task_id);
   2094 
   2095                    // The shadow compositing only cares about alpha channel
   2096                    // which is always linear, so we can blur this in sRGB or
   2097                    // linear color space and the result is the same as we will
   2098                    // be replacing the rgb completely.
   2099                    let blur_task_id =
   2100                        RenderTask::new_blur(
   2101                            adjusted_blur_std_deviation,
   2102                            input_subregion_task_id,
   2103                            rg_builder,
   2104                            RenderTargetKind::Color,
   2105                            None,
   2106                            adjusted_blur_task_size.to_i32(),
   2107                            BlurEdgeMode::Duplicate,
   2108                        );
   2109 
   2110                    // Now we make the compositing task, for this we need to put
   2111                    // the blurred shadow image at the correct subregion offset
   2112                    let blur_subregion_translated = blur_subregion
   2113                        .translate(LayoutVector2D::new(dx, dy));
   2114                    task_id = rg_builder.add().init(RenderTask::new_dynamic(
   2115                        node_task_size,
   2116                        RenderTaskKind::SVGFENode(
   2117                            SVGFEFilterTask{
   2118                                node: FilterGraphNode{
   2119                                    kept_by_optimizer: true,
   2120                                    linear: node.linear,
   2121                                    inflate: node_inflate,
   2122                                    inputs: [
   2123                                        // Original picture
   2124                                        *blur_input,
   2125                                        // Shadow picture
   2126                                        FilterGraphPictureReference{
   2127                                            buffer_id: blur_input.buffer_id,
   2128                                            subregion: blur_subregion_translated,
   2129                                            inflate: 0,
   2130                                            offset: LayoutVector2D::zero(),
   2131                                            source_padding: LayoutRect::zero(),
   2132                                            target_padding: LayoutRect::zero(),
   2133                                        }].to_vec(),
   2134                                    subregion,
   2135                                },
   2136                                op: FilterGraphOp::SVGFEDropShadow{
   2137                                    color,
   2138                                    // These parameters don't matter here
   2139                                    dx: 0.0, dy: 0.0,
   2140                                    std_deviation_x: 0.0, std_deviation_y: 0.0,
   2141                                },
   2142                                content_origin: node_task_rect.min,
   2143                                extra_gpu_data: None,
   2144                            }
   2145                        ),
   2146                    ).with_uv_rect_kind(node_uv_rect_kind));
   2147                    // Adding the dependencies sets the inputs for this task
   2148                    rg_builder.add_dependency(task_id, source_task_id);
   2149                    rg_builder.add_dependency(task_id, blur_task_id);
   2150                }
   2151                FilterGraphOp::SVGFESourceAlpha |
   2152                FilterGraphOp::SVGFESourceGraphic => {
   2153                    // These copy from the original task, we have to synthesize
   2154                    // a fake input binding to make the shader do the copy.  In
   2155                    // the case of SourceAlpha the shader will zero the RGB but
   2156                    // we don't have to care about that distinction here.
   2157                    task_id = rg_builder.add().init(RenderTask::new_dynamic(
   2158                        node_task_size,
   2159                        RenderTaskKind::SVGFENode(
   2160                            SVGFEFilterTask{
   2161                                node: FilterGraphNode{
   2162                                    kept_by_optimizer: true,
   2163                                    linear: node.linear,
   2164                                    inflate: node_inflate,
   2165                                    inputs: [
   2166                                        FilterGraphPictureReference{
   2167                                            buffer_id: FilterOpGraphPictureBufferId::None,
   2168                                            // This is what makes the mapping
   2169                                            // actually work.
   2170                                            subregion: source_subregion.cast_unit(),
   2171                                            offset: LayoutVector2D::zero(),
   2172                                            inflate: 0,
   2173                                            source_padding: LayoutRect::zero(),
   2174                                            target_padding: LayoutRect::zero(),
   2175                                        }
   2176                                    ].to_vec(),
   2177                                    subregion: source_subregion.cast_unit(),
   2178                                },
   2179                                op: op.clone(),
   2180                                content_origin: source_subregion.min.cast_unit(),
   2181                                extra_gpu_data: None,
   2182                            }
   2183                        ),
   2184                    ).with_uv_rect_kind(node_uv_rect_kind));
   2185                    rg_builder.add_dependency(task_id, original_task_id);
   2186                    made_dependency_on_source = true;
   2187                }
   2188                FilterGraphOp::SVGFEComponentTransferInterned { handle, creates_pixels: _ } => {
   2189                    // FIXME: Doing this in prepare_interned_prim_for_render
   2190                    // doesn't seem to be enough, where should it be done?
   2191                    let filter_data = &mut data_stores.filter_data[handle];
   2192                    filter_data.write_gpu_blocks(gpu_buffer);
   2193                    // ComponentTransfer has a gpu buffer address that we need to
   2194                    // pass along
   2195                    task_id = rg_builder.add().init(RenderTask::new_dynamic(
   2196                        node_task_size,
   2197                        RenderTaskKind::SVGFENode(
   2198                            SVGFEFilterTask {
   2199                                node: FilterGraphNode{
   2200                                    kept_by_optimizer: true,
   2201                                    linear: node.linear,
   2202                                    inputs: node_inputs.iter().map(|input| {input.0}).collect(),
   2203                                    subregion,
   2204                                    inflate: node_inflate,
   2205                                },
   2206                                op: op.clone(),
   2207                                content_origin: node_task_rect.min,
   2208                                extra_gpu_data: Some(filter_data.gpu_buffer_address),
   2209                            }
   2210                        ),
   2211                    ).with_uv_rect_kind(node_uv_rect_kind));
   2212 
   2213                    // Add the dependencies for inputs of this node, which will
   2214                    // be used by add_svg_filter_node_instances later
   2215                    for (_input, input_task) in &node_inputs {
   2216                        if *input_task == original_task_id {
   2217                            made_dependency_on_source = true;
   2218                        }
   2219                        if *input_task != RenderTaskId::INVALID {
   2220                            rg_builder.add_dependency(task_id, *input_task);
   2221                        }
   2222                    }
   2223                }
   2224                _ => {
   2225                    // This is the usual case - zero, one or two inputs that
   2226                    // reference earlier node results.
   2227                    task_id = rg_builder.add().init(RenderTask::new_dynamic(
   2228                        node_task_size,
   2229                        RenderTaskKind::SVGFENode(
   2230                            SVGFEFilterTask{
   2231                                node: FilterGraphNode{
   2232                                    kept_by_optimizer: true,
   2233                                    linear: node.linear,
   2234                                    inputs: node_inputs.iter().map(|input| {input.0}).collect(),
   2235                                    subregion,
   2236                                    inflate: node_inflate,
   2237                                },
   2238                                op: op.clone(),
   2239                                content_origin: node_task_rect.min,
   2240                                extra_gpu_data: None,
   2241                            }
   2242                        ),
   2243                    ).with_uv_rect_kind(node_uv_rect_kind));
   2244 
   2245                    // Add the dependencies for inputs of this node, which will
   2246                    // be used by add_svg_filter_node_instances later
   2247                    for (_input, input_task) in &node_inputs {
   2248                        if *input_task == original_task_id {
   2249                            made_dependency_on_source = true;
   2250                        }
   2251                        if *input_task != RenderTaskId::INVALID {
   2252                            rg_builder.add_dependency(task_id, *input_task);
   2253                        }
   2254                    }
   2255                }
   2256            }
   2257 
   2258            // We track the tasks we created by output buffer id to make it easy
   2259            // to look them up quickly, since nodes can only depend on previous
   2260            // nodes in the same list
   2261            task_by_buffer_id[filter_index] = task_id;
   2262            subregion_by_buffer_id[filter_index] = subregion;
   2263 
   2264            // The final task we create is the output picture.
   2265            output_task_id = task_id;
   2266        }
   2267 
   2268        // If no tasks referenced the SourceGraphic, we actually have to create
   2269        // a fake dependency so that it does not leak.
   2270        if !made_dependency_on_source && output_task_id != original_task_id {
   2271            rg_builder.add_dependency(output_task_id, original_task_id);
   2272        }
   2273 
   2274        output_task_id
   2275   }
   2276 
   2277    pub fn uv_rect_kind(&self) -> UvRectKind {
   2278        self.uv_rect_kind
   2279    }
   2280 
   2281    pub fn get_texture_address(&self) -> GpuBufferAddress {
   2282        self.uv_rect_handle
   2283    }
   2284 
   2285    pub fn get_target_texture(&self) -> CacheTextureId {
   2286        match self.location {
   2287            RenderTaskLocation::Dynamic { texture_id, .. } => {
   2288                assert_ne!(texture_id, CacheTextureId::INVALID);
   2289                texture_id
   2290            }
   2291            RenderTaskLocation::Static { surface: StaticRenderTaskSurface::TextureCache { texture, .. }, .. } => {
   2292                texture
   2293            }
   2294            _ => {
   2295                unreachable!();
   2296            }
   2297        }
   2298    }
   2299 
   2300    pub fn get_texture_source(&self) -> TextureSource {
   2301        match self.location {
   2302            RenderTaskLocation::Dynamic { texture_id, .. } => {
   2303                assert_ne!(texture_id, CacheTextureId::INVALID);
   2304                TextureSource::TextureCache(texture_id, Swizzle::default())
   2305            }
   2306            RenderTaskLocation::Static { surface:  StaticRenderTaskSurface::ReadOnly { source }, .. } => {
   2307                source
   2308            }
   2309            RenderTaskLocation::Static { surface: StaticRenderTaskSurface::TextureCache { texture, .. }, .. } => {
   2310                TextureSource::TextureCache(texture, Swizzle::default())
   2311            }
   2312            RenderTaskLocation::Existing { .. } |
   2313            RenderTaskLocation::Static { .. } |
   2314            RenderTaskLocation::CacheRequest { .. } |
   2315            RenderTaskLocation::Unallocated { .. } => {
   2316                unreachable!();
   2317            }
   2318        }
   2319    }
   2320 
   2321    pub fn get_target_rect(&self) -> DeviceIntRect {
   2322        match self.location {
   2323            // Previously, we only added render tasks after the entire
   2324            // primitive chain was determined visible. This meant that
   2325            // we could assert any render task in the list was also
   2326            // allocated (assigned to passes). Now, we add render
   2327            // tasks earlier, and the picture they belong to may be
   2328            // culled out later, so we can't assert that the task
   2329            // has been allocated.
   2330            // Render tasks that are created but not assigned to
   2331            // passes consume a row in the render task texture, but
   2332            // don't allocate any space in render targets nor
   2333            // draw any pixels.
   2334            // TODO(gw): Consider some kind of tag or other method
   2335            //           to mark a task as unused explicitly. This
   2336            //           would allow us to restore this debug check.
   2337            RenderTaskLocation::Dynamic { rect, .. } => rect,
   2338            RenderTaskLocation::Static { rect, .. } => rect,
   2339            RenderTaskLocation::Existing { .. } |
   2340            RenderTaskLocation::CacheRequest { .. } |
   2341            RenderTaskLocation::Unallocated { .. } => {
   2342                panic!("bug: get_target_rect called before allocating");
   2343            }
   2344        }
   2345    }
   2346 
   2347    pub fn get_target_size(&self) -> DeviceIntSize {
   2348        match self.location {
   2349            RenderTaskLocation::Dynamic { rect, .. } => rect.size(),
   2350            RenderTaskLocation::Static { rect, .. } => rect.size(),
   2351            RenderTaskLocation::Existing { size, .. } => size,
   2352            RenderTaskLocation::CacheRequest { size } => size,
   2353            RenderTaskLocation::Unallocated { size } => size,
   2354        }
   2355    }
   2356 
   2357    pub fn target_kind(&self) -> RenderTargetKind {
   2358        self.kind.target_kind()
   2359    }
   2360 
   2361    /// Called by the render task cache.
   2362    ///
   2363    /// Tells the render task that it is cached (which means its gpu cache
   2364    /// handle is managed by the texture cache).
   2365    pub fn mark_cached(&mut self, handle: RenderTaskCacheEntryHandle) {
   2366        self.cache_handle = Some(handle);
   2367    }
   2368 }