webrender/
render_task.rs

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