Skip to main content

webrender/
surface.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
5//! Contains functionality to help building the render task graph from a series of off-screen
6//! surfaces that are created during the prepare pass, and other surface related types and
7//! helpers.
8
9use api::units::*;
10use crate::box_shadow::BLUR_SAMPLE_SCALE;
11use crate::command_buffer::{CommandBufferBuilderKind, CommandBufferList, CommandBufferBuilder, CommandBufferIndex};
12use crate::internal_types::{FastHashMap, Filter};
13use crate::picture::PictureCompositeMode;
14use crate::tile_cache::{TileKey, SubSliceIndex, MAX_COMPOSITOR_SURFACES};
15use crate::prim_store::PictureIndex;
16use crate::render_task_graph::{RenderTaskId, RenderTaskGraphBuilder};
17use crate::render_target::ResolveOp;
18use crate::render_task::{RenderTask, RenderTaskKind, RenderTaskLocation};
19use crate::space::SpaceMapper;
20use crate::spatial_tree::{SpatialTree, SpatialNodeIndex};
21use crate::util::MaxRect;
22use crate::visibility::{DrawState, PrimitiveDrawHeader, FrameVisibilityContext};
23pub use crate::picture_composite_mode::get_surface_rects;
24
25
26/// Maximum blur radius for blur filter
27const MAX_BLUR_RADIUS: f32 = 100.;
28
29/// An index into the surface array
30#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
31#[cfg_attr(feature = "capture", derive(Serialize))]
32#[cfg_attr(feature = "replay", derive(Deserialize))]
33pub struct SurfaceIndex(pub usize);
34
35/// Specify whether a surface allows subpixel AA text rendering.
36#[derive(Debug, Copy, Clone)]
37pub enum SubpixelMode {
38    /// This surface allows subpixel AA text
39    Allow,
40    /// Subpixel AA text cannot be drawn on this surface
41    Deny,
42    /// Subpixel AA can be drawn on this surface, if not intersecting
43    /// with the excluded regions, and inside the allowed rect.
44    Conditional {
45        allowed_rect: PictureRect,
46        prohibited_rect: PictureRect,
47    },
48}
49
50/// Information about an offscreen surface. For now,
51/// it contains information about the size and coordinate
52/// system of the surface. In the future, it will contain
53/// information about the contents of the surface, which
54/// will allow surfaces to be cached / retained between
55/// frames and display lists.
56pub struct SurfaceInfo {
57    /// A local rect defining the size of this surface, in the
58    /// coordinate system of the parent surface. This contains
59    /// the unclipped bounding rect of child primitives.
60    ///
61    /// SNAPTODO: This rect is built by mapping per-cluster bounding
62    /// rects (and child-surface coverage rects) into this surface's
63    /// picture space via `map_local_to_picture`. Even once the source
64    /// cluster bound is a true union of per-prim *snapped* local
65    /// rects, the resulting `unclipped_local_rect` is not guaranteed
66    /// to be snapped: any 2D transform that isn't an axis-aligned,
67    /// integer-pixel translation between the cluster/child-surface
68    /// spatial node and this surface's spatial node will produce
69    /// sub-pixel edges in picture space. Float blur-margin inflation
70    /// inside `composite_mode.get_coverage` can also break the snap.
71    /// Consumers that need a snapped value will either need to
72    /// re-snap in surface space or restrict the snap path to surfaces
73    /// where the cross-space mapping preserves grid alignment (see
74    /// `SurfaceInfo.allow_snapping`).
75    pub unclipped_local_rect: PictureRect,
76    /// The local space coverage of child primitives after they are
77    /// are clipped to their owning clip-chain.
78    pub clipped_local_rect: PictureRect,
79    /// The (conservative) valid part of this surface rect. Used
80    /// to reduce the size of render target allocation.
81    pub clipping_rect: PictureRect,
82    /// The rectangle to use for culling and clipping.
83    pub culling_rect: VisRect,
84    /// Helper structs for mapping local rects in different
85    /// coordinate systems into the picture coordinates.
86    pub map_local_to_picture: SpaceMapper<LayoutPixel, PicturePixel>,
87    /// The positioning node for the surface itself,
88    pub surface_spatial_node_index: SpatialNodeIndex,
89    /// The rasterization root for this surface.
90    pub raster_spatial_node_index: SpatialNodeIndex,
91    /// The spatial node for culling and clipping (anything using VisPixel).
92    /// TODO: Replace with the raster spatial node.
93    pub visibility_spatial_node_index: SpatialNodeIndex,
94    /// The device pixel ratio specific to this surface.
95    pub device_pixel_scale: DevicePixelScale,
96    /// The scale factors of the surface to world transform.
97    pub world_scale_factors: (f32, f32),
98    /// Local scale factors surface to raster transform
99    pub local_scale: (f32, f32),
100    /// If true, we know this surface is completely opaque.
101    pub is_opaque: bool,
102    /// If true, allow snapping on this and child surfaces
103    pub allow_snapping: bool,
104    /// If true, the scissor rect must be set when drawing this surface
105    pub force_scissor_rect: bool,
106}
107
108impl SurfaceInfo {
109    pub fn new(
110        surface_spatial_node_index: SpatialNodeIndex,
111        raster_spatial_node_index: SpatialNodeIndex,
112        world_rect: WorldRect,
113        spatial_tree: &SpatialTree,
114        device_pixel_scale: DevicePixelScale,
115        world_scale_factors: (f32, f32),
116        local_scale: (f32, f32),
117        allow_snapping: bool,
118        force_scissor_rect: bool,
119    ) -> Self {
120        let map_surface_to_world = SpaceMapper::new_with_target(
121            spatial_tree.root_reference_frame_index(),
122            surface_spatial_node_index,
123            world_rect,
124            spatial_tree,
125        );
126
127        let pic_bounds = map_surface_to_world
128            .unmap(&map_surface_to_world.bounds)
129            .unwrap_or_else(PictureRect::max_rect);
130
131        let map_local_to_picture = SpaceMapper::new(
132            surface_spatial_node_index,
133            pic_bounds,
134        );
135
136        // TODO: replace the root with raster space.
137        let visibility_spatial_node_index = spatial_tree.root_reference_frame_index();
138
139        SurfaceInfo {
140            unclipped_local_rect: PictureRect::zero(),
141            clipped_local_rect: PictureRect::zero(),
142            is_opaque: false,
143            clipping_rect: PictureRect::zero(),
144            map_local_to_picture,
145            raster_spatial_node_index,
146            surface_spatial_node_index,
147            visibility_spatial_node_index,
148            device_pixel_scale,
149            world_scale_factors,
150            local_scale,
151            allow_snapping,
152            force_scissor_rect,
153            // TODO: At the moment all culling is done in world space but
154            // but the plan is to move it to raster space.
155            culling_rect: world_rect.cast_unit(),
156        }
157    }
158
159    /// Clamps the blur radius depending on scale factors.
160    pub fn clamp_blur_radius(
161        &self,
162        x_blur_radius: f32,
163        y_blur_radius: f32,
164    ) -> (f32, f32) {
165        // Clamping must occur after scale factors are applied, but scale factors are not applied
166        // until later on. To clamp the blur radius, we first apply the scale factors and then clamp
167        // and finally revert the scale factors.
168
169        let sx_blur_radius = x_blur_radius * self.local_scale.0;
170        let sy_blur_radius = y_blur_radius * self.local_scale.1;
171
172        let largest_scaled_blur_radius = f32::max(
173            sx_blur_radius * self.world_scale_factors.0,
174            sy_blur_radius * self.world_scale_factors.1,
175        );
176
177        if largest_scaled_blur_radius > MAX_BLUR_RADIUS {
178            let sf = MAX_BLUR_RADIUS / largest_scaled_blur_radius;
179            (x_blur_radius * sf, y_blur_radius * sf)
180        } else {
181            // Return the original blur radius to avoid any rounding errors
182            (x_blur_radius, y_blur_radius)
183        }
184    }
185
186    pub fn update_culling_rect(
187        &mut self,
188        parent_culling_rect: VisRect,
189        composite_mode: &PictureCompositeMode,
190        frame_context: &FrameVisibilityContext,
191    ) {
192        // Set the default culling rect to be the parent, in case we fail
193        // any mappings below due to weird perspective or invalid transforms.
194        self.culling_rect = parent_culling_rect;
195
196        if let PictureCompositeMode::Filter(Filter::Blur { width, height, should_inflate, .. }) = composite_mode {
197            if *should_inflate {
198                // Space mapping vis <-> picture space
199                let map_surface_to_vis = SpaceMapper::new_with_target(
200                    // TODO: switch from root to raster space.
201                    frame_context.root_spatial_node_index,
202                    self.surface_spatial_node_index,
203                    parent_culling_rect,
204                    frame_context.spatial_tree,
205                );
206
207                // Unmap the parent culling rect to surface space. Note that this may be
208                // quite conservative in the case of a complex transform, especially perspective.
209                if let Some(local_parent_culling_rect) = map_surface_to_vis.unmap(&parent_culling_rect) {
210                    let (width_factor, height_factor) = self.clamp_blur_radius(*width, *height);
211
212                    // Inflate by the local-space amount this surface extends.
213                    let expanded_rect: PictureBox2D = local_parent_culling_rect.inflate(
214                        width_factor.ceil() * BLUR_SAMPLE_SCALE,
215                        height_factor.ceil() * BLUR_SAMPLE_SCALE,
216                    );
217
218                    // Map back to the expected vis-space culling rect
219                    if let Some(rect) = map_surface_to_vis.map(&expanded_rect) {
220                        self.culling_rect = rect;
221                    }
222                }
223            }
224        }
225    }
226
227    pub fn map_to_device_rect(
228        &self,
229        picture_rect: &PictureRect,
230        spatial_tree: &SpatialTree,
231    ) -> DeviceRect {
232        let raster_rect = if self.raster_spatial_node_index != self.surface_spatial_node_index {
233            // Currently, the surface's spatial node can be different from its raster node only
234            // for surfaces in the root coordinate system for snapping reasons.
235            // See `PictureInstance::assign_surface`.
236            assert_eq!(self.device_pixel_scale.0, 1.0);
237            assert_eq!(self.raster_spatial_node_index, spatial_tree.root_reference_frame_index());
238
239            let pic_to_raster = SpaceMapper::new_with_target(
240                self.raster_spatial_node_index,
241                self.surface_spatial_node_index,
242                WorldRect::max_rect(),
243                spatial_tree,
244            );
245
246            pic_to_raster.map(&picture_rect).unwrap()
247        } else {
248            picture_rect.cast_unit()
249        };
250
251        raster_rect * self.device_pixel_scale
252    }
253
254    /// Clip and transform a local rect to a device rect suitable for allocating
255    /// a child off-screen surface of this surface (e.g. for clip-masks)
256    pub fn get_surface_rect(
257        &self,
258        local_rect: &PictureRect,
259        spatial_tree: &SpatialTree,
260    ) -> Option<DeviceIntRect> {
261        let local_rect = match local_rect.intersection(&self.clipping_rect) {
262            Some(rect) => rect,
263            None => return None,
264        };
265
266        let raster_rect = if self.raster_spatial_node_index != self.surface_spatial_node_index {
267            assert_eq!(self.device_pixel_scale.0, 1.0);
268
269            let local_to_world = SpaceMapper::new_with_target(
270                spatial_tree.root_reference_frame_index(),
271                self.surface_spatial_node_index,
272                WorldRect::max_rect(),
273                spatial_tree,
274            );
275
276            local_to_world.map(&local_rect).unwrap()
277        } else {
278            // The content should have been culled out earlier.
279            assert!(self.device_pixel_scale.0 > 0.0);
280
281            local_rect.cast_unit()
282        };
283
284        let surface_rect = (raster_rect * self.device_pixel_scale).round_out().to_i32();
285        if surface_rect.is_empty() {
286            // The local_rect computed above may have non-empty size that is very
287            // close to zero. Due to limited arithmetic precision, the SpaceMapper
288            // might transform the near-zero-sized rect into a zero-sized one.
289            return None;
290        }
291
292        Some(surface_rect)
293    }
294}
295
296// Information about the render task(s) for a given tile
297#[cfg_attr(feature = "capture", derive(Serialize))]
298#[cfg_attr(feature = "replay", derive(Deserialize))]
299pub struct SurfaceTileDescriptor {
300    /// Target render task for commands added to this tile. This is changed
301    /// each time a sub-graph is encountered on this tile
302    pub current_task_id: RenderTaskId,
303    /// The compositing task for this tile, if required. This is only needed
304    /// when a tile contains one or more sub-graphs.
305    pub composite_task_id: Option<RenderTaskId>,
306    /// Dirty rect for this tile
307    pub dirty_rect: PictureRect,
308}
309
310// Details of how a surface is rendered
311pub enum SurfaceDescriptorKind {
312    // Picture cache tiles
313    Tiled {
314        tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
315    },
316    // A single surface (e.g. for an opacity filter)
317    Simple {
318        render_task_id: RenderTaskId,
319        dirty_rect: PictureRect,
320    },
321    // A surface with 1+ intermediate tasks (e.g. blur)
322    Chained {
323        render_task_id: RenderTaskId,
324        root_task_id: RenderTaskId,
325        dirty_rect: PictureRect,
326    },
327}
328
329// Describes how a surface is rendered
330pub struct SurfaceDescriptor {
331    kind: SurfaceDescriptorKind,
332}
333
334impl SurfaceDescriptor {
335    // Create a picture cache tiled surface
336    pub fn new_tiled(
337        tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
338    ) -> Self {
339        SurfaceDescriptor {
340            kind: SurfaceDescriptorKind::Tiled {
341                tiles,
342            },
343        }
344    }
345
346    // Create a chained surface (e.g. blur)
347    pub fn new_chained(
348        render_task_id: RenderTaskId,
349        root_task_id: RenderTaskId,
350        dirty_rect: PictureRect,
351    ) -> Self {
352        SurfaceDescriptor {
353            kind: SurfaceDescriptorKind::Chained {
354                render_task_id,
355                root_task_id,
356                dirty_rect,
357            },
358        }
359    }
360
361    // Create a simple surface (e.g. opacity)
362    pub fn new_simple(
363        render_task_id: RenderTaskId,
364        dirty_rect: PictureRect,
365    ) -> Self {
366        SurfaceDescriptor {
367            kind: SurfaceDescriptorKind::Simple {
368                render_task_id,
369                dirty_rect,
370            },
371        }
372    }
373}
374
375// Describes a list of command buffers that we are adding primitives to
376// for a given surface. These are created from a command buffer builder
377// as an optimization - skipping the indirection pic_task -> cmd_buffer_index
378struct CommandBufferTargets {
379    available_cmd_buffers: Vec<Vec<(PictureRect, CommandBufferIndex)>>,
380}
381
382impl CommandBufferTargets {
383    fn new() -> Self {
384        CommandBufferTargets {
385            available_cmd_buffers: vec![Vec::new(); MAX_COMPOSITOR_SURFACES+1],
386        }
387    }
388
389    fn init(
390        &mut self,
391        cb: &CommandBufferBuilder,
392        rg_builder: &RenderTaskGraphBuilder,
393    ) {
394        for available_cmd_buffers in &mut self.available_cmd_buffers {
395            available_cmd_buffers.clear();
396        }
397
398        match cb.kind {
399            CommandBufferBuilderKind::Tiled { ref tiles, .. } => {
400                for (key, desc) in tiles {
401                    let task = rg_builder.get_task(desc.current_task_id);
402                    match task.kind {
403                        RenderTaskKind::Picture(ref info) => {
404                            let available_cmd_buffers = &mut self.available_cmd_buffers[key.sub_slice_index.as_usize()];
405                            available_cmd_buffers.push((desc.dirty_rect, info.cmd_buffer_index));
406                        }
407                        _ => unreachable!("bug: not a picture"),
408                    }
409                }
410            }
411            CommandBufferBuilderKind::Simple { render_task_id, dirty_rect, .. } => {
412                let task = rg_builder.get_task(render_task_id);
413                match task.kind {
414                    RenderTaskKind::Picture(ref info) => {
415                        for sub_slice_buffer in &mut self.available_cmd_buffers {
416                            sub_slice_buffer.push((dirty_rect, info.cmd_buffer_index));
417                        }
418                    }
419                    _ => unreachable!("bug: not a picture"),
420                }
421            }
422            CommandBufferBuilderKind::Invalid => {}
423        };
424    }
425
426    /// For a given rect and sub-slice, get a list of command buffers to write commands to
427    fn get_cmd_buffer_targets_for_rect(
428        &mut self,
429        rect: &PictureRect,
430        sub_slice_index: SubSliceIndex,
431        targets: &mut Vec<CommandBufferIndex>,
432    ) -> bool {
433
434        for (dirty_rect, cmd_buffer_index) in &self.available_cmd_buffers[sub_slice_index.as_usize()] {
435            if dirty_rect.intersects(rect) {
436                targets.push(*cmd_buffer_index);
437            }
438        }
439
440        !targets.is_empty()
441    }
442}
443
444// Main helper interface to build a graph of surfaces. In future patches this
445// will support building sub-graphs.
446pub struct SurfaceBuilder {
447    // The currently set cmd buffer targets (updated during push/pop)
448    current_cmd_buffers: CommandBufferTargets,
449    // Stack of surfaces that are parents to the current targets
450    builder_stack: Vec<CommandBufferBuilder>,
451    // A map of the output render tasks from any sub-graphs that haven't
452    // been consumed by BackdropRender prims yet
453    pub sub_graph_output_map: FastHashMap<PictureIndex, RenderTaskId>,
454}
455
456impl SurfaceBuilder {
457    pub fn new() -> Self {
458        SurfaceBuilder {
459            current_cmd_buffers: CommandBufferTargets::new(),
460            builder_stack: Vec::new(),
461            sub_graph_output_map: FastHashMap::default(),
462        }
463    }
464
465    /// Register the current surface as the source of a resolve for the task sub-graph that
466    /// is currently on the surface builder stack.
467    pub fn register_resolve_source(
468        &mut self,
469    ) {
470        let surface_task_id = match self.builder_stack.last().unwrap().kind {
471            CommandBufferBuilderKind::Tiled { .. } | CommandBufferBuilderKind::Invalid => {
472                panic!("bug: only supported for non-tiled surfaces");
473            }
474            CommandBufferBuilderKind::Simple { render_task_id, .. } => render_task_id,
475        };
476
477        for builder in self.builder_stack.iter_mut().rev() {
478            if builder.establishes_sub_graph {
479                assert_eq!(builder.resolve_source, None);
480                builder.resolve_source = Some(surface_task_id);
481                return;
482            }
483        }
484
485        unreachable!("bug: resolve source with no sub-graph");
486    }
487
488    pub fn push_surface(
489        &mut self,
490        surface_index: SurfaceIndex,
491        is_sub_graph: bool,
492        clipping_rect: PictureRect,
493        descriptor: Option<SurfaceDescriptor>,
494        surfaces: &mut [SurfaceInfo],
495        rg_builder: &RenderTaskGraphBuilder,
496    ) {
497        // Init the surface
498        surfaces[surface_index.0].clipping_rect = clipping_rect;
499
500        let builder = if let Some(descriptor) = descriptor {
501            match descriptor.kind {
502                SurfaceDescriptorKind::Tiled { tiles } => {
503                    CommandBufferBuilder::new_tiled(
504                        tiles,
505                    )
506                }
507                SurfaceDescriptorKind::Simple { render_task_id, dirty_rect, .. } => {
508                    CommandBufferBuilder::new_simple(
509                        render_task_id,
510                        is_sub_graph,
511                        None,
512                        dirty_rect,
513                    )
514                }
515                SurfaceDescriptorKind::Chained { render_task_id, root_task_id, dirty_rect, .. } => {
516                    CommandBufferBuilder::new_simple(
517                        render_task_id,
518                        is_sub_graph,
519                        Some(root_task_id),
520                        dirty_rect,
521                    )
522                }
523            }
524        } else {
525            CommandBufferBuilder::empty()
526        };
527
528        self.current_cmd_buffers.init(&builder, rg_builder);
529        self.builder_stack.push(builder);
530    }
531
532    // Add a child render task (e.g. a render task cache item, or a clip mask) as a
533    // dependency of the current surface
534    pub fn add_child_render_task(
535        &mut self,
536        child_task_id: RenderTaskId,
537        rg_builder: &mut RenderTaskGraphBuilder,
538    ) {
539        let builder = self.builder_stack.last().unwrap();
540
541        match builder.kind {
542            CommandBufferBuilderKind::Tiled { ref tiles } => {
543                for (_, descriptor) in tiles {
544                    rg_builder.add_dependency(
545                        descriptor.current_task_id,
546                        child_task_id,
547                    );
548                }
549            }
550            CommandBufferBuilderKind::Simple { render_task_id, .. } => {
551                rg_builder.add_dependency(
552                    render_task_id,
553                    child_task_id,
554                );
555            }
556            CommandBufferBuilderKind::Invalid { .. } => {}
557        }
558    }
559
560    // Add a picture render task as a dependency of the parent surface. This is a
561    // special case with extra complexity as the root of the surface may change
562    // when inside a sub-graph. It's currently only needed for drop-shadow effects.
563    pub fn add_picture_render_task(
564        &mut self,
565        child_task_id: RenderTaskId,
566    ) {
567        self.builder_stack
568            .last_mut()
569            .unwrap()
570            .extra_dependencies
571            .push(child_task_id);
572    }
573
574    // Get a list of command buffer indices that primitives should be pushed
575    // to for a given current visbility / dirty state
576    pub fn get_cmd_buffer_targets_for_prim(
577        &mut self,
578        vis: &PrimitiveDrawHeader,
579        targets: &mut Vec<CommandBufferIndex>,
580    ) -> bool {
581        targets.clear();
582
583        match vis.state {
584            DrawState::Unset => {
585                panic!("bug: invalid vis state");
586            }
587            DrawState::Culled => {
588                false
589            }
590            DrawState::Visible { sub_slice_index, .. } => {
591                self.current_cmd_buffers.get_cmd_buffer_targets_for_rect(
592                    &vis.clip_chain.pic_coverage_rect,
593                    sub_slice_index,
594                    targets,
595                )
596            }
597            DrawState::PassThrough => {
598                true
599            }
600        }
601    }
602
603    pub fn pop_empty_surface(&mut self) {
604        let builder = self.builder_stack.pop().unwrap();
605        assert!(!builder.establishes_sub_graph);
606    }
607
608    // Finish adding primitives and child tasks to a surface and pop it off the stack
609    pub fn pop_surface(
610        &mut self,
611        pic_index: PictureIndex,
612        rg_builder: &mut RenderTaskGraphBuilder,
613        cmd_buffers: &mut CommandBufferList,
614    ) {
615        let builder = self.builder_stack.pop().unwrap();
616
617        if builder.establishes_sub_graph {
618            // If we are popping a sub-graph off the stack the dependency setup is rather more complex...
619            match builder.kind {
620                CommandBufferBuilderKind::Tiled { .. } | CommandBufferBuilderKind::Invalid => {
621                    unreachable!("bug: sub-graphs can only be simple surfaces");
622                }
623                CommandBufferBuilderKind::Simple { render_task_id: child_render_task_id, root_task_id: child_root_task_id, .. } => {
624                    // Get info about the resolve operation to copy from parent surface or tiles to the picture cache task
625                    if let Some(resolve_task_id) = builder.resolve_source {
626                        let mut src_task_ids = Vec::new();
627
628                        // Make the output of the sub-graph a dependency of the new replacement tile task
629                        let _old = self.sub_graph_output_map.insert(
630                            pic_index,
631                            child_root_task_id.unwrap_or(child_render_task_id),
632                        );
633                        debug_assert!(_old.is_none());
634
635                        // Set up dependencies for the sub-graph. The basic concepts below are the same, but for
636                        // tiled surfaces are a little more complex as there are multiple tasks to set up.
637                        //  (a) Set up new task(s) on parent surface that write to the same location
638                        //  (b) Set up a resolve target to copy from parent surface tasks(s) to the resolve target
639                        //  (c) Make the old parent surface tasks input dependencies of the resolve target
640                        //  (d) Make the sub-graph output an input dependency of the new task(s).
641
642                        match self.builder_stack.last_mut().unwrap().kind {
643                            CommandBufferBuilderKind::Tiled { ref mut tiles } => {
644                                let keys: Vec<TileKey> = tiles.keys().cloned().collect();
645
646                                // For each tile in parent surface
647                                for key in keys {
648                                    let descriptor = tiles.remove(&key).unwrap();
649                                    let parent_task_id = descriptor.current_task_id;
650                                    let parent_task = rg_builder.get_task_mut(parent_task_id);
651
652                                    match parent_task.location {
653                                        RenderTaskLocation::Unallocated { .. } | RenderTaskLocation::Existing { .. } => {
654                                            // Get info about the parent tile task location and params
655                                            let location = RenderTaskLocation::Existing {
656                                                parent_task_id,
657                                                size: parent_task.location.size(),
658                                            };
659
660                                            let pic_task = match parent_task.kind {
661                                                RenderTaskKind::Picture(ref mut pic_task) => {
662                                                    let cmd_buffer_index = cmd_buffers.create_cmd_buffer();
663                                                    let new_pic_task = pic_task.duplicate(cmd_buffer_index);
664
665                                                    // Add the resolve src to copy from tile -> picture input task
666                                                    src_task_ids.push(parent_task_id);
667
668                                                    new_pic_task
669                                                }
670                                                _ => panic!("bug: not a picture"),
671                                            };
672
673                                            // Make the existing tile an input dependency of the resolve target
674                                            rg_builder.add_dependency(
675                                                resolve_task_id,
676                                                parent_task_id,
677                                            );
678
679                                            // Create the new task to replace the tile task
680                                            let new_task_id = rg_builder.add().init(
681                                                RenderTask::new(
682                                                    location,          // draw to same place
683                                                    RenderTaskKind::Picture(pic_task),
684                                                ),
685                                            );
686
687                                            // Ensure that the parent task will get scheduled earlier during
688                                            // pass assignment since we are reusing the existing surface,
689                                            // even though it's not technically needed for rendering order.
690                                            rg_builder.add_dependency(
691                                                new_task_id,
692                                                parent_task_id,
693                                            );
694
695                                            // Update the surface builder with the now current target for future primitives
696                                            tiles.insert(
697                                                key,
698                                                SurfaceTileDescriptor {
699                                                    current_task_id: new_task_id,
700                                                    ..descriptor
701                                                },
702                                            );
703                                        }
704                                        RenderTaskLocation::Static { .. } => {
705                                            // Update the surface builder with the now current target for future primitives
706                                            tiles.insert(
707                                                key,
708                                                descriptor,
709                                            );
710                                        }
711                                        _ => {
712                                            panic!("bug: unexpected task location");
713                                        }
714                                    }
715                                }
716                            }
717                            CommandBufferBuilderKind::Simple { render_task_id: ref mut parent_task_id, root_task_id: ref parent_root_task_id, .. } => {
718                                let parent_task = rg_builder.get_task_mut(*parent_task_id);
719
720                                // Get info about the parent tile task location and params
721                                let location = RenderTaskLocation::Existing {
722                                    parent_task_id: *parent_task_id,
723                                    size: parent_task.location.size(),
724                                };
725                                let pic_task = match parent_task.kind {
726                                    RenderTaskKind::Picture(ref mut pic_task) => {
727                                        let cmd_buffer_index = cmd_buffers.create_cmd_buffer();
728
729                                        let new_pic_task = pic_task.duplicate(cmd_buffer_index);
730
731                                        // Add the resolve src to copy from tile -> picture input task
732                                        src_task_ids.push(*parent_task_id);
733
734                                        new_pic_task
735                                    }
736                                    _ => panic!("bug: not a picture"),
737                                };
738
739                                // Make the existing surface an input dependency of the resolve target
740                                rg_builder.add_dependency(
741                                    resolve_task_id,
742                                    *parent_task_id,
743                                );
744
745                                // Create the new task to replace the parent surface task
746                                let new_task_id = rg_builder.add().init(
747                                    RenderTask::new(
748                                        location,          // draw to same place
749                                        RenderTaskKind::Picture(pic_task),
750                                    ),
751                                );
752
753                                // Ensure that the parent task will get scheduled earlier during
754                                // pass assignment since we are reusing the existing surface,
755                                // even though it's not technically needed for rendering order.
756                                rg_builder.add_dependency(
757                                    new_task_id,
758                                    *parent_task_id,
759                                );
760
761                                // If the parent is a chained surface (e.g. a CSS blur or drop-shadow
762                                // filter), its filter pass (root_task_id) reads from the same texture
763                                // as parent_task_id. Ensure it executes after new_task_id has written
764                                // post-backdrop-capture content (e.g. backdrop-filter children) to
765                                // that texture, otherwise those primitives will be missing from the
766                                // filter output.
767                                if let Some(root_task_id) = *parent_root_task_id {
768                                    rg_builder.add_dependency(
769                                        root_task_id,
770                                        new_task_id,
771                                    );
772                                }
773
774                                // Update the surface builder with the now current target for future primitives
775                                *parent_task_id = new_task_id;
776                            }
777                            CommandBufferBuilderKind::Invalid => {
778                                unreachable!();
779                            }
780                        }
781
782                        let dest_task = rg_builder.get_task_mut(resolve_task_id);
783
784                        match dest_task.kind {
785                            RenderTaskKind::Picture(ref mut dest_task_info) => {
786                                assert!(dest_task_info.resolve_op.is_none());
787                                dest_task_info.resolve_op = Some(ResolveOp {
788                                    src_task_ids,
789                                    dest_task_id: resolve_task_id,
790                                })
791                            }
792                            _ => {
793                                unreachable!("bug: not a picture");
794                            }
795                        }
796                    }
797
798                    // This can occur if there is an edge case where the resolve target is found
799                    // not visible even though the filter chain was (for example, in the case of
800                    // an extreme scale causing floating point inaccuracies). Adding a dependency
801                    // here is also a safety in case for some reason the backdrop render primitive
802                    // doesn't pick up the dependency, ensuring that it gets scheduled and freed
803                    // as early as possible.
804                    match self.builder_stack.last().unwrap().kind {
805                        CommandBufferBuilderKind::Tiled { ref tiles } => {
806                            // For a tiled render task, add as a dependency to every tile.
807                            for (_, descriptor) in tiles {
808                                rg_builder.add_dependency(
809                                    descriptor.current_task_id,
810                                    child_root_task_id.unwrap_or(child_render_task_id),
811                                );
812                            }
813                        }
814                        CommandBufferBuilderKind::Simple { render_task_id: parent_task_id, .. } => {
815                            rg_builder.add_dependency(
816                                parent_task_id,
817                                child_root_task_id.unwrap_or(child_render_task_id),
818                            );
819                        }
820                        CommandBufferBuilderKind::Invalid => {
821                            unreachable!();
822                        }
823                    }
824                }
825            }
826        } else {
827            match builder.kind {
828                CommandBufferBuilderKind::Tiled { ref tiles } => {
829                    for (_, descriptor) in tiles {
830                        if let Some(composite_task_id) = descriptor.composite_task_id {
831                            rg_builder.add_dependency(
832                                composite_task_id,
833                                descriptor.current_task_id,
834                            );
835
836                            let composite_task = rg_builder.get_task_mut(composite_task_id);
837                            match composite_task.kind {
838                                RenderTaskKind::TileComposite(ref mut info) => {
839                                    info.task_id = Some(descriptor.current_task_id);
840                                }
841                                _ => unreachable!("bug: not a tile composite"),
842                            }
843                        }
844                    }
845                }
846                CommandBufferBuilderKind::Simple { render_task_id: child_task_id, root_task_id: child_root_task_id, .. } => {
847                    match self.builder_stack.last().unwrap().kind {
848                        CommandBufferBuilderKind::Tiled { ref tiles } => {
849                            // For a tiled render task, add as a dependency to every tile.
850                            for (_, descriptor) in tiles {
851                                rg_builder.add_dependency(
852                                    descriptor.current_task_id,
853                                    child_root_task_id.unwrap_or(child_task_id),
854                                );
855                            }
856                        }
857                        CommandBufferBuilderKind::Simple { render_task_id: parent_task_id, .. } => {
858                            rg_builder.add_dependency(
859                                parent_task_id,
860                                child_root_task_id.unwrap_or(child_task_id),
861                            );
862                        }
863                        CommandBufferBuilderKind::Invalid => {
864                        }
865                    }
866                }
867                CommandBufferBuilderKind::Invalid => {
868                }
869            }
870        }
871
872        // Step through the dependencies for this builder and add them to the finalized
873        // render task root(s) for this surface
874        match builder.kind {
875            CommandBufferBuilderKind::Tiled { ref tiles } => {
876                for (_, descriptor) in tiles {
877                    for task_id in &builder.extra_dependencies {
878                        rg_builder.add_dependency(
879                            descriptor.current_task_id,
880                            *task_id,
881                        );
882                    }
883                }
884            }
885            CommandBufferBuilderKind::Simple { render_task_id, .. } => {
886                for task_id in &builder.extra_dependencies {
887                    rg_builder.add_dependency(
888                        render_task_id,
889                        *task_id,
890                    );
891                }
892            }
893            CommandBufferBuilderKind::Invalid { .. } => {}
894        }
895
896        // Set up the cmd-buffer targets to write prims into the popped surface
897        self.current_cmd_buffers.init(
898            self.builder_stack.last().unwrap_or(&CommandBufferBuilder::empty()), rg_builder
899        );
900    }
901
902    pub fn finalize(self) {
903        assert!(self.builder_stack.is_empty());
904    }
905}
906
907
908pub fn calculate_screen_uv(
909    p: DevicePoint,
910    clipped: DeviceRect,
911) -> DeviceHomogeneousVector {
912    // TODO(gw): Switch to a simple mix, no bilerp / homogeneous vec needed anymore
913    DeviceHomogeneousVector::new(
914        (p.x - clipped.min.x) / (clipped.max.x - clipped.min.x),
915        (p.y - clipped.min.y) / (clipped.max.y - clipped.min.y),
916        0.0,
917        1.0,
918    )
919}