Skip to main content

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