tor-browser

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

surface.rs (38261B)


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