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