tor-browser

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

mod.rs (150374B)


      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 //! Tile cache types and descriptors
      6 //!
      7 //! This module contains the core tile caching infrastructure including:
      8 //! - Tile identification and coordinate types
      9 //! - Tile descriptors that track primitive dependencies
     10 //! - Comparison results for invalidation tracking
     11 
     12 // Existing tile cache slice builder (was previously tile_cache.rs)
     13 pub mod slice_builder;
     14 
     15 use api::{AlphaType, BorderRadius, ClipMode, ColorF, ColorDepth, DebugFlags, ImageKey, ImageRendering};
     16 use api::{PropertyBindingId, PrimitiveFlags, YuvFormat, YuvRangedColorSpace};
     17 use api::units::*;
     18 use crate::clip::{ClipNodeId, ClipLeafId, ClipItemKind, ClipSpaceConversion, ClipChainInstance, ClipStore};
     19 use crate::composite::{CompositorKind, CompositeState, CompositorSurfaceKind, ExternalSurfaceDescriptor};
     20 use crate::composite::{ExternalSurfaceDependency, NativeSurfaceId, NativeTileId};
     21 use crate::composite::{CompositorClipIndex, CompositorTransformIndex};
     22 use crate::composite::{CompositeTileDescriptor, CompositeTile};
     23 use crate::gpu_types::ZBufferId;
     24 use crate::internal_types::{FastHashMap, FrameId, Filter};
     25 use crate::invalidation::{InvalidationReason, DirtyRegion, PrimitiveCompareResult};
     26 use crate::invalidation::cached_surface::{CachedSurface, TileUpdateDirtyContext, TileUpdateDirtyState, PrimitiveDependencyInfo};
     27 use crate::invalidation::compare::{PrimitiveDependency, ImageDependency};
     28 use crate::invalidation::compare::{SpatialNodeComparer, PrimitiveComparisonKey};
     29 use crate::invalidation::compare::{OpacityBindingInfo, ColorBindingInfo};
     30 use crate::picture::{SurfaceTextureDescriptor, PictureCompositeMode, SurfaceIndex, clamp};
     31 use crate::picture::{get_relative_scale_offset, PicturePrimitive};
     32 use crate::picture::MAX_COMPOSITOR_SURFACES_SIZE;
     33 use crate::prim_store::{PrimitiveInstance, PrimitiveInstanceKind, PrimitiveScratchBuffer, PictureIndex};
     34 use crate::prim_store::{ColorBindingStorage, ColorBindingIndex, PrimitiveTemplateKind};
     35 use crate::print_tree::{PrintTreePrinter, PrintTree};
     36 use crate::{profiler, render_backend::DataStores};
     37 use crate::profiler::TransactionProfile;
     38 use crate::renderer::GpuBufferBuilderF;
     39 use crate::resource_cache::{ResourceCache, ImageRequest};
     40 use crate::scene_building::SliceFlags;
     41 use crate::space::SpaceMapper;
     42 use crate::spatial_tree::{SpatialNodeIndex, SpatialTree};
     43 use crate::surface::{SubpixelMode, SurfaceInfo};
     44 use crate::util::{ScaleOffset, MatrixHelpers, MaxRect};
     45 use crate::visibility::{FrameVisibilityContext, FrameVisibilityState, VisibilityState, PrimitiveVisibilityFlags};
     46 use euclid::approxeq::ApproxEq;
     47 use euclid::Box2D;
     48 use peek_poke::{PeekPoke, ensure_red_zone};
     49 use std::fmt::{Display, Error, Formatter};
     50 use std::{marker, mem};
     51 use std::sync::atomic::{AtomicUsize, Ordering};
     52 
     53 pub use self::slice_builder::{
     54    TileCacheBuilder, TileCacheConfig,
     55    PictureCacheDebugInfo, SliceDebugInfo, DirtyTileDebugInfo, TileDebugInfo,
     56 };
     57 
     58 pub use api::units::TileOffset;
     59 pub use api::units::TileRange as TileRect;
     60 
     61 /// The maximum number of compositor surfaces that are allowed per picture cache. This
     62 /// is an arbitrary number that should be enough for common cases, but low enough to
     63 /// prevent performance and memory usage drastically degrading in pathological cases.
     64 pub const MAX_COMPOSITOR_SURFACES: usize = 4;
     65 
     66 /// The size in device pixels of a normal cached tile.
     67 pub const TILE_SIZE_DEFAULT: DeviceIntSize = DeviceIntSize {
     68    width: 1024,
     69    height: 512,
     70    _unit: marker::PhantomData,
     71 };
     72 
     73 /// The size in device pixels of a tile for horizontal scroll bars
     74 pub const TILE_SIZE_SCROLLBAR_HORIZONTAL: DeviceIntSize = DeviceIntSize {
     75    width: 1024,
     76    height: 32,
     77    _unit: marker::PhantomData,
     78 };
     79 
     80 /// The size in device pixels of a tile for vertical scroll bars
     81 pub const TILE_SIZE_SCROLLBAR_VERTICAL: DeviceIntSize = DeviceIntSize {
     82    width: 32,
     83    height: 1024,
     84    _unit: marker::PhantomData,
     85 };
     86 
     87 /// The maximum size per axis of a surface, in DevicePixel coordinates.
     88 /// Render tasks larger than this size are scaled down to fit, which may cause
     89 /// some blurriness.
     90 pub const MAX_SURFACE_SIZE: usize = 4096;
     91 
     92 /// Used to get unique tile IDs, even when the tile cache is
     93 /// destroyed between display lists / scenes.
     94 static NEXT_TILE_ID: AtomicUsize = AtomicUsize::new(0);
     95 
     96 /// A unique identifier for a tile. These are stable across display lists and
     97 /// scenes.
     98 #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
     99 #[cfg_attr(feature = "capture", derive(Serialize))]
    100 #[cfg_attr(feature = "replay", derive(Deserialize))]
    101 pub struct TileId(pub usize);
    102 
    103 impl TileId {
    104    pub fn new() -> TileId {
    105        TileId(NEXT_TILE_ID.fetch_add(1, Ordering::Relaxed))
    106    }
    107 }
    108 
    109 // Internal function used by picture.rs for creating TileIds
    110 #[doc(hidden)]
    111 pub fn next_tile_id() -> usize {
    112    NEXT_TILE_ID.fetch_add(1, Ordering::Relaxed)
    113 }
    114 
    115 /// Uniquely identifies a tile within a picture cache slice
    116 #[cfg_attr(feature = "capture", derive(Serialize))]
    117 #[cfg_attr(feature = "replay", derive(Deserialize))]
    118 #[derive(Debug, Copy, Clone, PartialEq, Hash, Eq)]
    119 pub struct TileKey {
    120    // Tile index (x,y)
    121    pub tile_offset: TileOffset,
    122    // Sub-slice (z)
    123    pub sub_slice_index: SubSliceIndex,
    124 }
    125 
    126 /// Defines which sub-slice (effectively a z-index) a primitive exists on within
    127 /// a picture cache instance.
    128 #[cfg_attr(feature = "capture", derive(Serialize))]
    129 #[cfg_attr(feature = "replay", derive(Deserialize))]
    130 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PeekPoke)]
    131 pub struct SubSliceIndex(pub u8);
    132 
    133 impl SubSliceIndex {
    134    pub const DEFAULT: SubSliceIndex = SubSliceIndex(0);
    135 
    136    pub fn new(index: usize) -> Self {
    137        SubSliceIndex(index as u8)
    138    }
    139 
    140    /// Return true if this sub-slice is the primary sub-slice (for now, we assume
    141    /// that only the primary sub-slice may be opaque and support subpixel AA, for example).
    142    pub fn is_primary(&self) -> bool {
    143        self.0 == 0
    144    }
    145 
    146    /// Get an array index for this sub-slice
    147    pub fn as_usize(&self) -> usize {
    148        self.0 as usize
    149    }
    150 }
    151 
    152 /// The key that identifies a tile cache instance. For now, it's simple the index of
    153 /// the slice as it was created during scene building.
    154 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
    155 #[cfg_attr(feature = "capture", derive(Serialize))]
    156 #[cfg_attr(feature = "replay", derive(Deserialize))]
    157 pub struct SliceId(usize);
    158 
    159 impl SliceId {
    160    pub fn new(index: usize) -> Self {
    161        SliceId(index)
    162    }
    163 }
    164 
    165 /// Information that is required to reuse or create a new tile cache. Created
    166 /// during scene building and passed to the render backend / frame builder.
    167 pub struct TileCacheParams {
    168    // The current debug flags for the system.
    169    pub debug_flags: DebugFlags,
    170    // Index of the slice (also effectively the key of the tile cache, though we use SliceId where that matters)
    171    pub slice: usize,
    172    // Flags describing content of this cache (e.g. scrollbars)
    173    pub slice_flags: SliceFlags,
    174    // The anchoring spatial node / scroll root
    175    pub spatial_node_index: SpatialNodeIndex,
    176    // The space in which visibility/invalidation/clipping computations are done.
    177    pub visibility_node_index: SpatialNodeIndex,
    178    // Optional background color of this tilecache. If present, can be used as an optimization
    179    // to enable opaque blending and/or subpixel AA in more places.
    180    pub background_color: Option<ColorF>,
    181    // Node in the clip-tree that defines where we exclude clips from child prims
    182    pub shared_clip_node_id: ClipNodeId,
    183    // Clip leaf that is used to build the clip-chain for this tile cache.
    184    pub shared_clip_leaf_id: Option<ClipLeafId>,
    185    // Virtual surface sizes are always square, so this represents both the width and height
    186    pub virtual_surface_size: i32,
    187    // The number of Image surfaces that are being requested for this tile cache.
    188    // This is only a suggestion - the tile cache will clamp this as a reasonable number
    189    // and only promote a limited number of surfaces.
    190    pub image_surface_count: usize,
    191    // The number of YuvImage surfaces that are being requested for this tile cache.
    192    // This is only a suggestion - the tile cache will clamp this as a reasonable number
    193    // and only promote a limited number of surfaces.
    194    pub yuv_image_surface_count: usize,
    195 }
    196 
    197 /// The backing surface for this tile.
    198 #[derive(Debug)]
    199 pub enum TileSurface {
    200    Texture {
    201        /// Descriptor for the surface that this tile draws into.
    202        descriptor: SurfaceTextureDescriptor,
    203    },
    204    Color {
    205        color: ColorF,
    206    },
    207 }
    208 
    209 impl TileSurface {
    210    pub fn kind(&self) -> &'static str {
    211        match *self {
    212            TileSurface::Color { .. } => "Color",
    213            TileSurface::Texture { .. } => "Texture",
    214        }
    215    }
    216 }
    217 
    218 /// Information about a cached tile.
    219 pub struct Tile {
    220    /// The grid position of this tile within the picture cache
    221    pub tile_offset: TileOffset,
    222    /// The current world rect of this tile.
    223    pub world_tile_rect: WorldRect,
    224    /// The device space dirty rect for this tile.
    225    /// TODO(gw): We have multiple dirty rects available due to the quadtree above. In future,
    226    ///           expose these as multiple dirty rects, which will help in some cases.
    227    pub device_dirty_rect: DeviceRect,
    228    /// World space rect that contains valid pixels region of this tile.
    229    pub world_valid_rect: WorldRect,
    230    /// Device space rect that contains valid pixels region of this tile.
    231    pub device_valid_rect: DeviceRect,
    232    /// Handle to the backing surface for this tile.
    233    pub surface: Option<TileSurface>,
    234    /// If true, this tile intersects with the currently visible screen
    235    /// rect, and will be drawn.
    236    pub is_visible: bool,
    237    /// The tile id is stable between display lists and / or frames,
    238    /// if the tile is retained. Useful for debugging tile evictions.
    239    pub id: TileId,
    240    /// If true, the tile was determined to be opaque, which means blending
    241    /// can be disabled when drawing it.
    242    pub is_opaque: bool,
    243    /// z-buffer id for this tile
    244    pub z_id: ZBufferId,
    245    /// Cached surface state (content tracking, invalidation, dependencies)
    246    pub cached_surface: CachedSurface,
    247 }
    248 
    249 impl Tile {
    250    /// Construct a new, invalid tile.
    251    fn new(tile_offset: TileOffset) -> Self {
    252        let id = TileId(crate::tile_cache::next_tile_id());
    253 
    254        Tile {
    255            tile_offset,
    256            world_tile_rect: WorldRect::zero(),
    257            world_valid_rect: WorldRect::zero(),
    258            device_valid_rect: DeviceRect::zero(),
    259            device_dirty_rect: DeviceRect::zero(),
    260            surface: None,
    261            is_visible: false,
    262            id,
    263            is_opaque: false,
    264            z_id: ZBufferId::invalid(),
    265            cached_surface: CachedSurface::new(),
    266        }
    267    }
    268 
    269    /// Print debug information about this tile to a tree printer.
    270    fn print(&self, pt: &mut dyn PrintTreePrinter) {
    271        pt.new_level(format!("Tile {:?}", self.id));
    272        pt.add_item(format!("local_rect: {:?}", self.cached_surface.local_rect));
    273        self.cached_surface.print(pt);
    274        pt.end_level();
    275    }
    276 
    277    /// Invalidate a tile based on change in content. This
    278    /// must be called even if the tile is not currently
    279    /// visible on screen. We might be able to improve this
    280    /// later by changing how ComparableVec is used.
    281    fn update_content_validity(
    282        &mut self,
    283        ctx: &TileUpdateDirtyContext,
    284        state: &mut TileUpdateDirtyState,
    285        frame_context: &FrameVisibilityContext,
    286    ) {
    287        self.cached_surface.update_content_validity(
    288            ctx,
    289            state,
    290            frame_context,
    291        );
    292    }
    293 
    294    /// Invalidate this tile. If `invalidation_rect` is None, the entire
    295    /// tile is invalidated.
    296    pub fn invalidate(
    297        &mut self,
    298        invalidation_rect: Option<PictureRect>,
    299        reason: InvalidationReason,
    300    ) {
    301        self.cached_surface.invalidate(invalidation_rect, reason);
    302    }
    303 
    304    /// Called during pre_update of a tile cache instance. Allows the
    305    /// tile to setup state before primitive dependency calculations.
    306    fn pre_update(
    307        &mut self,
    308        ctx: &TilePreUpdateContext,
    309    ) {
    310        self.cached_surface.local_rect = PictureRect::new(
    311            PicturePoint::new(
    312                self.tile_offset.x as f32 * ctx.tile_size.width,
    313                self.tile_offset.y as f32 * ctx.tile_size.height,
    314            ),
    315            PicturePoint::new(
    316                (self.tile_offset.x + 1) as f32 * ctx.tile_size.width,
    317                (self.tile_offset.y + 1) as f32 * ctx.tile_size.height,
    318            ),
    319        );
    320 
    321        self.world_tile_rect = ctx.pic_to_world_mapper
    322            .map(&self.cached_surface.local_rect)
    323            .expect("bug: map local tile rect");
    324 
    325        // Check if this tile is currently on screen.
    326        self.is_visible = self.world_tile_rect.intersects(&ctx.global_screen_world_rect);
    327 
    328        // Delegate to CachedSurface for content tracking setup
    329        self.cached_surface.pre_update(
    330            ctx.background_color,
    331            self.cached_surface.local_rect,
    332            ctx.frame_id,
    333            self.is_visible,
    334        );
    335    }
    336 
    337    /// Add dependencies for a given primitive to this tile.
    338    fn add_prim_dependency(
    339        &mut self,
    340        info: &PrimitiveDependencyInfo,
    341    ) {
    342        // If this tile isn't currently visible, we don't want to update the dependencies
    343        // for this tile, as an optimization, since it won't be drawn anyway.
    344        if !self.is_visible {
    345            return;
    346        }
    347 
    348        self.cached_surface.add_prim_dependency(info, self.cached_surface.local_rect);
    349    }
    350 
    351    /// Called during tile cache instance post_update. Allows invalidation and dirty
    352    /// rect calculation after primitive dependencies have been updated.
    353    fn update_dirty_and_valid_rects(
    354        &mut self,
    355        ctx: &TileUpdateDirtyContext,
    356        state: &mut TileUpdateDirtyState,
    357        frame_context: &FrameVisibilityContext,
    358    ) {
    359        // Ensure peek-poke constraint is met, that `dep_data` is large enough
    360        ensure_red_zone::<PrimitiveDependency>(&mut self.cached_surface.current_descriptor.dep_data);
    361 
    362        // Register the frame id of this tile with the spatial node comparer, to ensure
    363        // that it doesn't GC any spatial nodes from the comparer that are referenced
    364        // by this tile. Must be done before we early exit below, so that we retain
    365        // spatial node info even for tiles that are currently not visible.
    366        state.spatial_node_comparer.retain_for_frame(self.cached_surface.current_descriptor.last_updated_frame_id);
    367 
    368        // If tile is not visible, just early out from here - we don't update dependencies
    369        // so don't want to invalidate, merge, split etc. The tile won't need to be drawn
    370        // (and thus updated / invalidated) until it is on screen again.
    371        if !self.is_visible {
    372            return;
    373        }
    374 
    375        // Calculate the overall valid rect for this tile.
    376        self.cached_surface.current_descriptor.local_valid_rect = self.cached_surface.local_valid_rect;
    377 
    378        // TODO(gw): In theory, the local tile rect should always have an
    379        //           intersection with the overall picture rect. In practice,
    380        //           due to some accuracy issues with how fract_offset (and
    381        //           fp accuracy) are used in the calling method, this isn't
    382        //           always true. In this case, it's safe to set the local
    383        //           valid rect to zero, which means it will be clipped out
    384        //           and not affect the scene. In future, we should fix the
    385        //           accuracy issue above, so that this assumption holds, but
    386        //           it shouldn't have any noticeable effect on performance
    387        //           or memory usage (textures should never get allocated).
    388        self.cached_surface.current_descriptor.local_valid_rect = self.cached_surface.local_rect
    389            .intersection(&ctx.local_rect)
    390            .and_then(|r| r.intersection(&self.cached_surface.current_descriptor.local_valid_rect))
    391            .unwrap_or_else(PictureRect::zero);
    392 
    393        // The device_valid_rect is referenced during `update_content_validity` so it
    394        // must be updated here first.
    395        self.world_valid_rect = ctx.pic_to_world_mapper
    396            .map(&self.cached_surface.current_descriptor.local_valid_rect)
    397            .expect("bug: map local valid rect");
    398 
    399        // The device rect is guaranteed to be aligned on a device pixel - the round
    400        // is just to deal with float accuracy. However, the valid rect is not
    401        // always aligned to a device pixel. To handle this, round out to get all
    402        // required pixels, and intersect with the tile device rect.
    403        let device_rect = (self.world_tile_rect * ctx.global_device_pixel_scale).round();
    404        self.device_valid_rect = (self.world_valid_rect * ctx.global_device_pixel_scale)
    405            .round_out()
    406            .intersection(&device_rect)
    407            .unwrap_or_else(DeviceRect::zero);
    408 
    409        // Invalidate the tile based on the content changing.
    410        self.update_content_validity(ctx, state, frame_context);
    411    }
    412 
    413    /// Called during tile cache instance post_update. Allows invalidation and dirty
    414    /// rect calculation after primitive dependencies have been updated.
    415    fn post_update(
    416        &mut self,
    417        ctx: &TilePostUpdateContext,
    418        state: &mut TilePostUpdateState,
    419        frame_context: &FrameVisibilityContext,
    420    ) {
    421        // If tile is not visible, just early out from here - we don't update dependencies
    422        // so don't want to invalidate, merge, split etc. The tile won't need to be drawn
    423        // (and thus updated / invalidated) until it is on screen again.
    424        if !self.is_visible {
    425            return;
    426        }
    427 
    428        // If there are no primitives there is no need to draw or cache it.
    429        // Bug 1719232 - The final device valid rect does not always describe a non-empty
    430        // region. Cull the tile as a workaround.
    431        if self.cached_surface.current_descriptor.prims.is_empty() || self.device_valid_rect.is_empty() {
    432            // If there is a native compositor surface allocated for this (now empty) tile
    433            // it must be freed here, otherwise the stale tile with previous contents will
    434            // be composited. If the tile subsequently gets new primitives added to it, the
    435            // surface will be re-allocated when it's added to the composite draw list.
    436            if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { mut id, .. }, .. }) = self.surface.take() {
    437                if let Some(id) = id.take() {
    438                    state.resource_cache.destroy_compositor_tile(id);
    439                }
    440            }
    441 
    442            self.is_visible = false;
    443            return;
    444        }
    445 
    446        // Check if this tile can be considered opaque. Opacity state must be updated only
    447        // after all early out checks have been performed. Otherwise, we might miss updating
    448        // the native surface next time this tile becomes visible.
    449        let clipped_rect = self.cached_surface.current_descriptor.local_valid_rect
    450            .intersection(&ctx.local_clip_rect)
    451            .unwrap_or_else(PictureRect::zero);
    452 
    453        let has_opaque_bg_color = self.cached_surface.background_color.map_or(false, |c| c.a >= 1.0);
    454        let has_opaque_backdrop = ctx.backdrop.map_or(false, |b| b.opaque_rect.contains_box(&clipped_rect));
    455        let mut is_opaque = has_opaque_bg_color || has_opaque_backdrop;
    456 
    457        // If this tile intersects with any underlay surfaces, we need to consider it
    458        // translucent, since it will contain an alpha cutout
    459        for underlay in ctx.underlays {
    460            if clipped_rect.intersects(&underlay.local_rect) {
    461                is_opaque = false;
    462                break;
    463            }
    464        }
    465 
    466        // Set the correct z_id for this tile
    467        self.z_id = ctx.z_id;
    468 
    469        if is_opaque != self.is_opaque {
    470            // If opacity changed, the native compositor surface and all tiles get invalidated.
    471            // (this does nothing if not using native compositor mode).
    472            // TODO(gw): This property probably changes very rarely, so it is OK to invalidate
    473            //           everything in this case. If it turns out that this isn't true, we could
    474            //           consider other options, such as per-tile opacity (natively supported
    475            //           on CoreAnimation, and supported if backed by non-virtual surfaces in
    476            //           DirectComposition).
    477            if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = self.surface {
    478                if let Some(id) = id.take() {
    479                    state.resource_cache.destroy_compositor_tile(id);
    480                }
    481            }
    482 
    483            // Invalidate the entire tile to force a redraw.
    484            self.invalidate(None, InvalidationReason::SurfaceOpacityChanged);
    485            self.is_opaque = is_opaque;
    486        }
    487 
    488        // Check if the selected composite mode supports dirty rect updates. For Draw composite
    489        // mode, we can always update the content with smaller dirty rects, unless there is a
    490        // driver bug to workaround. For native composite mode, we can only use dirty rects if
    491        // the compositor supports partial surface updates.
    492        let (supports_dirty_rects, supports_simple_prims) = match state.composite_state.compositor_kind {
    493            CompositorKind::Draw { .. } | CompositorKind::Layer { .. } => {
    494                (frame_context.config.gpu_supports_render_target_partial_update, true)
    495            }
    496            CompositorKind::Native { capabilities, .. } => {
    497                (capabilities.max_update_rects > 0, false)
    498            }
    499        };
    500 
    501        // TODO(gw): Consider using smaller tiles and/or tile splits for
    502        //           native compositors that don't support dirty rects.
    503        if supports_dirty_rects {
    504            // Only allow splitting for normal content sized tiles
    505            if ctx.current_tile_size == state.resource_cache.picture_textures.default_tile_size() {
    506                let max_split_level = 3;
    507 
    508                // Consider splitting / merging dirty regions
    509                self.cached_surface.root.maybe_merge_or_split(
    510                    0,
    511                    &self.cached_surface.current_descriptor.prims,
    512                    max_split_level,
    513                );
    514            }
    515        }
    516 
    517        // The dirty rect will be set correctly by now. If the underlying platform
    518        // doesn't support partial updates, and this tile isn't valid, force the dirty
    519        // rect to be the size of the entire tile.
    520        if !self.cached_surface.is_valid && !supports_dirty_rects {
    521            self.cached_surface.local_dirty_rect = self.cached_surface.local_rect;
    522        }
    523 
    524        // See if this tile is a simple color, in which case we can just draw
    525        // it as a rect, and avoid allocating a texture surface and drawing it.
    526        // TODO(gw): Initial native compositor interface doesn't support simple
    527        //           color tiles. We can definitely support this in DC, so this
    528        //           should be added as a follow up.
    529        let is_simple_prim =
    530            ctx.backdrop.map_or(false, |b| b.kind.is_some()) &&
    531            self.cached_surface.current_descriptor.prims.len() == 1 &&
    532            self.is_opaque &&
    533            supports_simple_prims;
    534 
    535        // Set up the backing surface for this tile.
    536        let surface = if is_simple_prim {
    537            // If we determine the tile can be represented by a color, set the
    538            // surface unconditionally (this will drop any previously used
    539            // texture cache backing surface).
    540            match ctx.backdrop.unwrap().kind {
    541                Some(BackdropKind::Color { color }) => {
    542                    TileSurface::Color {
    543                        color,
    544                    }
    545                }
    546                None => {
    547                    // This should be prevented by the is_simple_prim check above.
    548                    unreachable!();
    549                }
    550            }
    551        } else {
    552            // If this tile will be backed by a surface, we want to retain
    553            // the texture handle from the previous frame, if possible. If
    554            // the tile was previously a color, or not set, then just set
    555            // up a new texture cache handle.
    556            match self.surface.take() {
    557                Some(TileSurface::Texture { descriptor }) => {
    558                    // Reuse the existing descriptor and vis mask
    559                    TileSurface::Texture {
    560                        descriptor,
    561                    }
    562                }
    563                Some(TileSurface::Color { .. }) | None => {
    564                    // This is the case where we are constructing a tile surface that
    565                    // involves drawing to a texture. Create the correct surface
    566                    // descriptor depending on the compositing mode that will read
    567                    // the output.
    568                    let descriptor = match state.composite_state.compositor_kind {
    569                        CompositorKind::Draw { .. } | CompositorKind::Layer { .. } => {
    570                            // For a texture cache entry, create an invalid handle that
    571                            // will be allocated when update_picture_cache is called.
    572                            SurfaceTextureDescriptor::TextureCache {
    573                                handle: None,
    574                            }
    575                        }
    576                        CompositorKind::Native { .. } => {
    577                            // Create a native surface surface descriptor, but don't allocate
    578                            // a surface yet. The surface is allocated *after* occlusion
    579                            // culling occurs, so that only visible tiles allocate GPU memory.
    580                            SurfaceTextureDescriptor::Native {
    581                                id: None,
    582                            }
    583                        }
    584                    };
    585 
    586                    TileSurface::Texture {
    587                        descriptor,
    588                    }
    589                }
    590            }
    591        };
    592 
    593        // Store the current surface backing info for use during batching.
    594        self.surface = Some(surface);
    595    }
    596 }
    597 
    598 // TODO(gw): Tidy this up by:
    599 //      - Add an Other variant for things like opaque gradient backdrops
    600 #[derive(Debug, Copy, Clone)]
    601 pub enum BackdropKind {
    602    Color {
    603        color: ColorF,
    604    },
    605 }
    606 
    607 /// Stores information about the calculated opaque backdrop of this slice.
    608 #[derive(Debug, Copy, Clone)]
    609 pub struct BackdropInfo {
    610    /// The picture space rectangle that is known to be opaque. This is used
    611    /// to determine where subpixel AA can be used, and where alpha blending
    612    /// can be disabled.
    613    pub opaque_rect: PictureRect,
    614    /// If the backdrop covers the entire slice with an opaque color, this
    615    /// will be set and can be used as a clear color for the slice's tiles.
    616    pub spanning_opaque_color: Option<ColorF>,
    617    /// Kind of the backdrop
    618    pub kind: Option<BackdropKind>,
    619    /// The picture space rectangle of the backdrop, if kind is set.
    620    pub backdrop_rect: PictureRect,
    621 }
    622 
    623 impl BackdropInfo {
    624    fn empty() -> Self {
    625        BackdropInfo {
    626            opaque_rect: PictureRect::zero(),
    627            spanning_opaque_color: None,
    628            kind: None,
    629            backdrop_rect: PictureRect::zero(),
    630        }
    631    }
    632 }
    633 
    634 /// Represents the native surfaces created for a picture cache, if using
    635 /// a native compositor. An opaque and alpha surface is always created,
    636 /// but tiles are added to a surface based on current opacity. If the
    637 /// calculated opacity of a tile changes, the tile is invalidated and
    638 /// attached to a different native surface. This means that we don't
    639 /// need to invalidate the entire surface if only some tiles are changing
    640 /// opacity. It also means we can take advantage of opaque tiles on cache
    641 /// slices where only some of the tiles are opaque. There is an assumption
    642 /// that creating a native surface is cheap, and only when a tile is added
    643 /// to a surface is there a significant cost. This assumption holds true
    644 /// for the current native compositor implementations on Windows and Mac.
    645 pub struct NativeSurface {
    646    /// Native surface for opaque tiles
    647    pub opaque: NativeSurfaceId,
    648    /// Native surface for alpha tiles
    649    pub alpha: NativeSurfaceId,
    650 }
    651 
    652 /// Hash key for an external native compositor surface
    653 #[derive(PartialEq, Eq, Hash)]
    654 pub struct ExternalNativeSurfaceKey {
    655    /// The YUV/RGB image keys that are used to draw this surface.
    656    pub image_keys: [ImageKey; 3],
    657    /// If this is not an 'external' compositor surface created via
    658    /// Compositor::create_external_surface, this is set to the
    659    /// current device size of the surface.
    660    pub size: Option<DeviceIntSize>,
    661 }
    662 
    663 /// Information about a native compositor surface cached between frames.
    664 pub struct ExternalNativeSurface {
    665    /// If true, the surface was used this frame. Used for a simple form
    666    /// of GC to remove old surfaces.
    667    pub used_this_frame: bool,
    668    /// The native compositor surface handle
    669    pub native_surface_id: NativeSurfaceId,
    670    /// List of image keys, and current image generations, that are drawn in this surface.
    671    /// The image generations are used to check if the compositor surface is dirty and
    672    /// needs to be updated.
    673    pub image_dependencies: [ImageDependency; 3],
    674 }
    675 
    676 /// Wrapper struct around an external surface descriptor with a little more information
    677 /// that the picture caching code needs.
    678 pub struct CompositorSurface {
    679    // External surface descriptor used by compositing logic
    680    pub descriptor: ExternalSurfaceDescriptor,
    681    // The compositor surface rect + any intersecting prims. Later prims that intersect
    682    // with this must be added to the next sub-slice.
    683    prohibited_rect: PictureRect,
    684    // If the compositor surface content is opaque.
    685    pub is_opaque: bool,
    686 }
    687 
    688 pub struct BackdropSurface {
    689    pub id: NativeSurfaceId,
    690    pub color: ColorF,
    691    pub device_rect: DeviceRect,
    692 }
    693 
    694 /// In some cases, we need to know the dirty rect of all tiles in order
    695 /// to correctly invalidate a primitive.
    696 #[derive(Debug)]
    697 pub struct DeferredDirtyTest {
    698    /// The tile rect that the primitive being checked affects
    699    pub tile_rect: TileRect,
    700    /// The picture-cache local rect of the primitive being checked
    701    pub prim_rect: PictureRect,
    702 }
    703 
    704 /// Represents a cache of tiles that make up a picture primitives.
    705 pub struct TileCacheInstance {
    706    // The current debug flags for the system.
    707    pub debug_flags: DebugFlags,
    708    /// Index of the tile cache / slice for this frame builder. It's determined
    709    /// by the setup_picture_caching method during flattening, which splits the
    710    /// picture tree into multiple slices. It's used as a simple input to the tile
    711    /// keys. It does mean we invalidate tiles if a new layer gets inserted / removed
    712    /// between display lists - this seems very unlikely to occur on most pages, but
    713    /// can be revisited if we ever notice that.
    714    pub slice: usize,
    715    /// Propagated information about the slice
    716    pub slice_flags: SliceFlags,
    717    /// The currently selected tile size to use for this cache
    718    pub current_tile_size: DeviceIntSize,
    719    /// The list of sub-slices in this tile cache
    720    pub sub_slices: Vec<SubSlice>,
    721    /// The positioning node for this tile cache.
    722    pub spatial_node_index: SpatialNodeIndex,
    723    /// The coordinate space to do visibility/clipping/invalidation in.
    724    pub visibility_node_index: SpatialNodeIndex,
    725    /// List of opacity bindings, with some extra information
    726    /// about whether they changed since last frame.
    727    opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>,
    728    /// Switch back and forth between old and new bindings hashmaps to avoid re-allocating.
    729    old_opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>,
    730    /// A helper to compare transforms between previous and current frame.
    731    spatial_node_comparer: SpatialNodeComparer,
    732    /// List of color bindings, with some extra information
    733    /// about whether they changed since last frame.
    734    color_bindings: FastHashMap<PropertyBindingId, ColorBindingInfo>,
    735    /// Switch back and forth between old and new bindings hashmaps to avoid re-allocating.
    736    old_color_bindings: FastHashMap<PropertyBindingId, ColorBindingInfo>,
    737    /// The current dirty region tracker for this picture.
    738    pub dirty_region: DirtyRegion,
    739    /// Current size of tiles in picture units.
    740    tile_size: PictureSize,
    741    /// Tile coords of the currently allocated grid.
    742    tile_rect: TileRect,
    743    /// Pre-calculated versions of the tile_rect above, used to speed up the
    744    /// calculations in get_tile_coords_for_rect.
    745    tile_bounds_p0: TileOffset,
    746    tile_bounds_p1: TileOffset,
    747    /// Local rect (unclipped) of the picture this cache covers.
    748    pub local_rect: PictureRect,
    749    /// The local clip rect, from the shared clips of this picture.
    750    pub local_clip_rect: PictureRect,
    751    /// Registered clip in CompositeState for this picture cache
    752    pub compositor_clip: Option<CompositorClipIndex>,
    753    /// The screen rect, transformed to local picture space.
    754    pub screen_rect_in_pic_space: PictureRect,
    755    /// The surface index that this tile cache will be drawn into.
    756    surface_index: SurfaceIndex,
    757    /// The background color from the renderer. If this is set opaque, we know it's
    758    /// fine to clear the tiles to this and allow subpixel text on the first slice.
    759    pub background_color: Option<ColorF>,
    760    /// Information about the calculated backdrop content of this cache.
    761    pub backdrop: BackdropInfo,
    762    /// The allowed subpixel mode for this surface, which depends on the detected
    763    /// opacity of the background.
    764    pub subpixel_mode: SubpixelMode,
    765    // Node in the clip-tree that defines where we exclude clips from child prims
    766    pub shared_clip_node_id: ClipNodeId,
    767    // Clip leaf that is used to build the clip-chain for this tile cache.
    768    pub shared_clip_leaf_id: Option<ClipLeafId>,
    769    /// The number of frames until this cache next evaluates what tile size to use.
    770    /// If a picture rect size is regularly changing just around a size threshold,
    771    /// we don't want to constantly invalidate and reallocate different tile size
    772    /// configuration each frame.
    773    frames_until_size_eval: usize,
    774    /// For DirectComposition, virtual surfaces don't support negative coordinates. However,
    775    /// picture cache tile coordinates can be negative. To handle this, we apply an offset
    776    /// to each tile in DirectComposition. We want to change this as little as possible,
    777    /// to avoid invalidating tiles. However, if we have a picture cache tile coordinate
    778    /// which is outside the virtual surface bounds, we must change this to allow
    779    /// correct remapping of the coordinates passed to BeginDraw in DC.
    780    pub virtual_offset: DeviceIntPoint,
    781    /// keep around the hash map used as compare_cache to avoid reallocating it each
    782    /// frame.
    783    compare_cache: FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>,
    784    /// The currently considered tile size override. Used to check if we should
    785    /// re-evaluate tile size, even if the frame timer hasn't expired.
    786    tile_size_override: Option<DeviceIntSize>,
    787    /// A cache of compositor surfaces that are retained between frames
    788    pub external_native_surface_cache: FastHashMap<ExternalNativeSurfaceKey, ExternalNativeSurface>,
    789    /// Current frame ID of this tile cache instance. Used for book-keeping / garbage collecting
    790    frame_id: FrameId,
    791    /// Registered transform in CompositeState for this picture cache
    792    pub transform_index: CompositorTransformIndex,
    793    /// Current transform mapping local picture space to compositor surface raster space
    794    local_to_raster: ScaleOffset,
    795    /// Current transform mapping compositor surface raster space to final device space
    796    raster_to_device: ScaleOffset,
    797    /// If true, we need to invalidate all tiles during `post_update`
    798    invalidate_all_tiles: bool,
    799    /// The current raster scale for tiles in this cache
    800    pub current_raster_scale: f32,
    801    /// Depth of off-screen surfaces that are currently pushed during dependency updates
    802    current_surface_traversal_depth: usize,
    803    /// A list of extra dirty invalidation tests that can only be checked once we
    804    /// know the dirty rect of all tiles
    805    deferred_dirty_tests: Vec<DeferredDirtyTest>,
    806    /// Is there a backdrop associated with this cache
    807    pub found_prims_after_backdrop: bool,
    808    pub backdrop_surface: Option<BackdropSurface>,
    809    /// List of underlay compositor surfaces that exist in this picture cache
    810    pub underlays: Vec<ExternalSurfaceDescriptor>,
    811    /// "Region" (actually a spanning rect) containing all overlay promoted surfaces
    812    pub overlay_region: PictureRect,
    813    /// The number YuvImage prims in this cache, provided in our TileCacheParams.
    814    pub yuv_images_count: usize,
    815    /// The remaining number of YuvImage prims we will see this frame. We prioritize
    816    /// promoting these before promoting any Image prims.
    817    pub yuv_images_remaining: usize,
    818 }
    819 
    820 impl TileCacheInstance {
    821    pub fn new(params: TileCacheParams) -> Self {
    822        // Determine how many sub-slices we need. Clamp to an arbitrary limit to ensure
    823        // we don't create a huge number of OS compositor tiles and sub-slices.
    824        let sub_slice_count = (params.image_surface_count + params.yuv_image_surface_count).min(MAX_COMPOSITOR_SURFACES) + 1;
    825 
    826        let mut sub_slices = Vec::with_capacity(sub_slice_count);
    827        for _ in 0 .. sub_slice_count {
    828            sub_slices.push(SubSlice::new());
    829        }
    830 
    831        TileCacheInstance {
    832            debug_flags: params.debug_flags,
    833            slice: params.slice,
    834            slice_flags: params.slice_flags,
    835            spatial_node_index: params.spatial_node_index,
    836            visibility_node_index: params.visibility_node_index,
    837            sub_slices,
    838            opacity_bindings: FastHashMap::default(),
    839            old_opacity_bindings: FastHashMap::default(),
    840            spatial_node_comparer: SpatialNodeComparer::new(),
    841            color_bindings: FastHashMap::default(),
    842            old_color_bindings: FastHashMap::default(),
    843            dirty_region: DirtyRegion::new(params.visibility_node_index, params.spatial_node_index),
    844            tile_size: PictureSize::zero(),
    845            tile_rect: TileRect::zero(),
    846            tile_bounds_p0: TileOffset::zero(),
    847            tile_bounds_p1: TileOffset::zero(),
    848            local_rect: PictureRect::zero(),
    849            local_clip_rect: PictureRect::zero(),
    850            compositor_clip: None,
    851            screen_rect_in_pic_space: PictureRect::zero(),
    852            surface_index: SurfaceIndex(0),
    853            background_color: params.background_color,
    854            backdrop: BackdropInfo::empty(),
    855            subpixel_mode: SubpixelMode::Allow,
    856            shared_clip_node_id: params.shared_clip_node_id,
    857            shared_clip_leaf_id: params.shared_clip_leaf_id,
    858            current_tile_size: DeviceIntSize::zero(),
    859            frames_until_size_eval: 0,
    860            // Default to centering the virtual offset in the middle of the DC virtual surface
    861            virtual_offset: DeviceIntPoint::new(
    862                params.virtual_surface_size / 2,
    863                params.virtual_surface_size / 2,
    864            ),
    865            compare_cache: FastHashMap::default(),
    866            tile_size_override: None,
    867            external_native_surface_cache: FastHashMap::default(),
    868            frame_id: FrameId::INVALID,
    869            transform_index: CompositorTransformIndex::INVALID,
    870            raster_to_device: ScaleOffset::identity(),
    871            local_to_raster: ScaleOffset::identity(),
    872            invalidate_all_tiles: true,
    873            current_raster_scale: 1.0,
    874            current_surface_traversal_depth: 0,
    875            deferred_dirty_tests: Vec::new(),
    876            found_prims_after_backdrop: false,
    877            backdrop_surface: None,
    878            underlays: Vec::new(),
    879            overlay_region: PictureRect::zero(),
    880            yuv_images_count: params.yuv_image_surface_count,
    881            yuv_images_remaining: 0,
    882        }
    883    }
    884 
    885    /// Return the total number of tiles allocated by this tile cache
    886    pub fn tile_count(&self) -> usize {
    887        self.tile_rect.area() as usize * self.sub_slices.len()
    888    }
    889 
    890    /// Trims memory held by the tile cache, such as native surfaces.
    891    pub fn memory_pressure(&mut self, resource_cache: &mut ResourceCache) {
    892        for sub_slice in &mut self.sub_slices {
    893            for tile in sub_slice.tiles.values_mut() {
    894                if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface {
    895                    // Reseting the id to None with take() ensures that a new
    896                    // tile will be allocated during the next frame build.
    897                    if let Some(id) = id.take() {
    898                        resource_cache.destroy_compositor_tile(id);
    899                    }
    900                }
    901            }
    902            if let Some(native_surface) = sub_slice.native_surface.take() {
    903                resource_cache.destroy_compositor_surface(native_surface.opaque);
    904                resource_cache.destroy_compositor_surface(native_surface.alpha);
    905            }
    906        }
    907    }
    908 
    909    /// Reset this tile cache with the updated parameters from a new scene
    910    /// that has arrived. This allows the tile cache to be retained across
    911    /// new scenes.
    912    pub fn prepare_for_new_scene(
    913        &mut self,
    914        params: TileCacheParams,
    915        resource_cache: &mut ResourceCache,
    916    ) {
    917        // We should only receive updated state for matching slice key
    918        assert_eq!(self.slice, params.slice);
    919 
    920        // Determine how many sub-slices we need, based on how many compositor surface prims are
    921        // in the supplied primitive list.
    922        let required_sub_slice_count = (params.image_surface_count + params.yuv_image_surface_count).min(MAX_COMPOSITOR_SURFACES) + 1;
    923 
    924        if self.sub_slices.len() != required_sub_slice_count {
    925            self.tile_rect = TileRect::zero();
    926 
    927            if self.sub_slices.len() > required_sub_slice_count {
    928                let old_sub_slices = self.sub_slices.split_off(required_sub_slice_count);
    929 
    930                for mut sub_slice in old_sub_slices {
    931                    for tile in sub_slice.tiles.values_mut() {
    932                        if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface {
    933                            if let Some(id) = id.take() {
    934                                resource_cache.destroy_compositor_tile(id);
    935                            }
    936                        }
    937                    }
    938 
    939                    if let Some(native_surface) = sub_slice.native_surface {
    940                        resource_cache.destroy_compositor_surface(native_surface.opaque);
    941                        resource_cache.destroy_compositor_surface(native_surface.alpha);
    942                    }
    943                }
    944            } else {
    945                while self.sub_slices.len() < required_sub_slice_count {
    946                    self.sub_slices.push(SubSlice::new());
    947                }
    948            }
    949        }
    950 
    951        // Store the parameters from the scene builder for this slice. Other
    952        // params in the tile cache are retained and reused, or are always
    953        // updated during pre/post_update.
    954        self.slice_flags = params.slice_flags;
    955        self.spatial_node_index = params.spatial_node_index;
    956        self.background_color = params.background_color;
    957        self.shared_clip_leaf_id = params.shared_clip_leaf_id;
    958        self.shared_clip_node_id = params.shared_clip_node_id;
    959 
    960        // Since the slice flags may have changed, ensure we re-evaluate the
    961        // appropriate tile size for this cache next update.
    962        self.frames_until_size_eval = 0;
    963 
    964        // Update the number of YuvImage prims we have in the scene.
    965        self.yuv_images_count = params.yuv_image_surface_count;
    966    }
    967 
    968    /// Destroy any manually managed resources before this picture cache is
    969    /// destroyed, such as native compositor surfaces.
    970    pub fn destroy(
    971        self,
    972        resource_cache: &mut ResourceCache,
    973    ) {
    974        for sub_slice in self.sub_slices {
    975            if let Some(native_surface) = sub_slice.native_surface {
    976                resource_cache.destroy_compositor_surface(native_surface.opaque);
    977                resource_cache.destroy_compositor_surface(native_surface.alpha);
    978            }
    979        }
    980 
    981        for (_, external_surface) in self.external_native_surface_cache {
    982            resource_cache.destroy_compositor_surface(external_surface.native_surface_id)
    983        }
    984 
    985        if let Some(backdrop_surface) = &self.backdrop_surface {
    986            resource_cache.destroy_compositor_surface(backdrop_surface.id);
    987        }
    988    }
    989 
    990    /// Get the tile coordinates for a given rectangle.
    991    fn get_tile_coords_for_rect(
    992        &self,
    993        rect: &PictureRect,
    994    ) -> (TileOffset, TileOffset) {
    995        // Get the tile coordinates in the picture space.
    996        let mut p0 = TileOffset::new(
    997            (rect.min.x / self.tile_size.width).floor() as i32,
    998            (rect.min.y / self.tile_size.height).floor() as i32,
    999        );
   1000 
   1001        let mut p1 = TileOffset::new(
   1002            (rect.max.x / self.tile_size.width).ceil() as i32,
   1003            (rect.max.y / self.tile_size.height).ceil() as i32,
   1004        );
   1005 
   1006        // Clamp the tile coordinates here to avoid looping over irrelevant tiles later on.
   1007        p0.x = clamp(p0.x, self.tile_bounds_p0.x, self.tile_bounds_p1.x);
   1008        p0.y = clamp(p0.y, self.tile_bounds_p0.y, self.tile_bounds_p1.y);
   1009        p1.x = clamp(p1.x, self.tile_bounds_p0.x, self.tile_bounds_p1.x);
   1010        p1.y = clamp(p1.y, self.tile_bounds_p0.y, self.tile_bounds_p1.y);
   1011 
   1012        (p0, p1)
   1013    }
   1014 
   1015    /// Update transforms, opacity, color bindings and tile rects.
   1016    pub fn pre_update(
   1017        &mut self,
   1018        surface_index: SurfaceIndex,
   1019        frame_context: &FrameVisibilityContext,
   1020        frame_state: &mut FrameVisibilityState,
   1021    ) -> WorldRect {
   1022        let surface = &frame_state.surfaces[surface_index.0];
   1023        let pic_rect = surface.unclipped_local_rect;
   1024 
   1025        self.surface_index = surface_index;
   1026        self.local_rect = pic_rect;
   1027        self.local_clip_rect = PictureRect::max_rect();
   1028        self.deferred_dirty_tests.clear();
   1029        self.underlays.clear();
   1030        self.overlay_region = PictureRect::zero();
   1031        self.yuv_images_remaining = self.yuv_images_count;
   1032 
   1033        for sub_slice in &mut self.sub_slices {
   1034            sub_slice.reset();
   1035        }
   1036 
   1037        // Reset the opaque rect + subpixel mode, as they are calculated
   1038        // during the prim dependency checks.
   1039        self.backdrop = BackdropInfo::empty();
   1040 
   1041        // Calculate the screen rect in picture space, for later comparison against
   1042        // backdrops, and prims potentially covering backdrops.
   1043        let pic_to_world_mapper = SpaceMapper::new_with_target(
   1044            frame_context.root_spatial_node_index,
   1045            self.spatial_node_index,
   1046            frame_context.global_screen_world_rect,
   1047            frame_context.spatial_tree,
   1048        );
   1049        self.screen_rect_in_pic_space = pic_to_world_mapper
   1050            .unmap(&frame_context.global_screen_world_rect)
   1051            .expect("unable to unmap screen rect");
   1052 
   1053        let pic_to_vis_mapper = SpaceMapper::new_with_target(
   1054            // TODO: use the raster node instead of the root node.
   1055            frame_context.root_spatial_node_index,
   1056            self.spatial_node_index,
   1057            surface.culling_rect,
   1058            frame_context.spatial_tree,
   1059        );
   1060 
   1061        // If there is a valid set of shared clips, build a clip chain instance for this,
   1062        // which will provide a local clip rect. This is useful for establishing things
   1063        // like whether the backdrop rect supplied by Gecko can be considered opaque.
   1064        if let Some(shared_clip_leaf_id) = self.shared_clip_leaf_id {
   1065            let map_local_to_picture = SpaceMapper::new(
   1066                self.spatial_node_index,
   1067                pic_rect,
   1068            );
   1069 
   1070            frame_state.clip_store.set_active_clips(
   1071                self.spatial_node_index,
   1072                map_local_to_picture.ref_spatial_node_index,
   1073                surface.visibility_spatial_node_index,
   1074                shared_clip_leaf_id,
   1075                frame_context.spatial_tree,
   1076                &mut frame_state.data_stores.clip,
   1077                &frame_state.clip_tree,
   1078            );
   1079 
   1080            let clip_chain_instance = frame_state.clip_store.build_clip_chain_instance(
   1081                pic_rect.cast_unit(),
   1082                &map_local_to_picture,
   1083                &pic_to_vis_mapper,
   1084                frame_context.spatial_tree,
   1085                &mut frame_state.frame_gpu_data.f32,
   1086                frame_state.resource_cache,
   1087                frame_context.global_device_pixel_scale,
   1088                &surface.culling_rect,
   1089                &mut frame_state.data_stores.clip,
   1090                frame_state.rg_builder,
   1091                true,
   1092            );
   1093 
   1094            // Ensure that if the entire picture cache is clipped out, the local
   1095            // clip rect is zero. This makes sure we don't register any occluders
   1096            // that are actually off-screen.
   1097            self.local_clip_rect = PictureRect::zero();
   1098            self.compositor_clip = None;
   1099 
   1100            if let Some(clip_chain) = clip_chain_instance {
   1101                self.local_clip_rect = clip_chain.pic_coverage_rect;
   1102                self.compositor_clip = None;
   1103 
   1104                if clip_chain.needs_mask {
   1105                    for i in 0 .. clip_chain.clips_range.count {
   1106                        let clip_instance = frame_state
   1107                            .clip_store
   1108                            .get_instance_from_range(&clip_chain.clips_range, i);
   1109                        let clip_node = &frame_state.data_stores.clip[clip_instance.handle];
   1110 
   1111                        match clip_node.item.kind {
   1112                            ClipItemKind::RoundedRectangle { rect, radius, mode } => {
   1113                                assert_eq!(mode, ClipMode::Clip);
   1114 
   1115                                // Map the clip in to device space. We know from the shared
   1116                                // clip creation logic it's in root coord system, so only a
   1117                                // 2d axis-aligned transform can apply. For example, in the
   1118                                // case of a pinch-zoom effect.
   1119                                let map = ClipSpaceConversion::new(
   1120                                    frame_context.root_spatial_node_index,
   1121                                    clip_node.item.spatial_node_index,
   1122                                    frame_context.root_spatial_node_index,
   1123                                    frame_context.spatial_tree,
   1124                                );
   1125 
   1126                                let (rect, radius) = match map {
   1127                                    ClipSpaceConversion::Local => {
   1128                                        (rect.cast_unit(), radius)
   1129                                    }
   1130                                    ClipSpaceConversion::ScaleOffset(scale_offset) => {
   1131                                        (
   1132                                            scale_offset.map_rect(&rect),
   1133                                            BorderRadius {
   1134                                                top_left: scale_offset.map_size(&radius.top_left),
   1135                                                top_right: scale_offset.map_size(&radius.top_right),
   1136                                                bottom_left: scale_offset.map_size(&radius.bottom_left),
   1137                                                bottom_right: scale_offset.map_size(&radius.bottom_right),
   1138                                            },
   1139                                        )
   1140                                    }
   1141                                    ClipSpaceConversion::Transform(..) => {
   1142                                        unreachable!();
   1143                                    }
   1144                                };
   1145 
   1146                                self.compositor_clip = Some(frame_state.composite_state.register_clip(
   1147                                    rect,
   1148                                    radius,
   1149                                ));
   1150 
   1151                                break;
   1152                            }
   1153                            _ => {
   1154                                // The logic to check for shared clips excludes other mask
   1155                                // clip types (box-shadow, image-mask) and ensures that the
   1156                                // clip is in the root coord system (so rect clips can't
   1157                                // produce a mask).
   1158                            }
   1159                        }
   1160                    }
   1161                }
   1162            }
   1163        }
   1164 
   1165        // Advance the current frame ID counter for this picture cache (must be done
   1166        // after any retained prev state is taken above).
   1167        self.frame_id.advance();
   1168 
   1169        // Notify the spatial node comparer that a new frame has started, and the
   1170        // current reference spatial node for this tile cache.
   1171        self.spatial_node_comparer.next_frame(self.spatial_node_index);
   1172 
   1173        // At the start of the frame, step through each current compositor surface
   1174        // and mark it as unused. Later, this is used to free old compositor surfaces.
   1175        // TODO(gw): In future, we might make this more sophisticated - for example,
   1176        //           retaining them for >1 frame if unused, or retaining them in some
   1177        //           kind of pool to reduce future allocations.
   1178        for external_native_surface in self.external_native_surface_cache.values_mut() {
   1179            external_native_surface.used_this_frame = false;
   1180        }
   1181 
   1182        // Only evaluate what tile size to use fairly infrequently, so that we don't end
   1183        // up constantly invalidating and reallocating tiles if the picture rect size is
   1184        // changing near a threshold value.
   1185        if self.frames_until_size_eval == 0 ||
   1186           self.tile_size_override != frame_context.config.tile_size_override {
   1187 
   1188            // Work out what size tile is appropriate for this picture cache.
   1189            let desired_tile_size = match frame_context.config.tile_size_override {
   1190                Some(tile_size_override) => {
   1191                    tile_size_override
   1192                }
   1193                None => {
   1194                    if self.slice_flags.contains(SliceFlags::IS_SCROLLBAR) {
   1195                        if pic_rect.width() <= pic_rect.height() {
   1196                            TILE_SIZE_SCROLLBAR_VERTICAL
   1197                        } else {
   1198                            TILE_SIZE_SCROLLBAR_HORIZONTAL
   1199                        }
   1200                    } else {
   1201                        frame_state.resource_cache.picture_textures.default_tile_size()
   1202                    }
   1203                }
   1204            };
   1205 
   1206            // If the desired tile size has changed, then invalidate and drop any
   1207            // existing tiles.
   1208            if desired_tile_size != self.current_tile_size {
   1209                for sub_slice in &mut self.sub_slices {
   1210                    // Destroy any native surfaces on the tiles that will be dropped due
   1211                    // to resizing.
   1212                    if let Some(native_surface) = sub_slice.native_surface.take() {
   1213                        frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque);
   1214                        frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha);
   1215                    }
   1216                    sub_slice.tiles.clear();
   1217                }
   1218                self.tile_rect = TileRect::zero();
   1219                self.current_tile_size = desired_tile_size;
   1220            }
   1221 
   1222            // Reset counter until next evaluating the desired tile size. This is an
   1223            // arbitrary value.
   1224            self.frames_until_size_eval = 120;
   1225            self.tile_size_override = frame_context.config.tile_size_override;
   1226        }
   1227 
   1228        // Get the complete scale-offset from local space to device space
   1229        let local_to_device = get_relative_scale_offset(
   1230            self.spatial_node_index,
   1231            frame_context.root_spatial_node_index,
   1232            frame_context.spatial_tree,
   1233        );
   1234 
   1235        // Get the compositor transform, which depends on pinch-zoom mode
   1236        let mut raster_to_device = local_to_device;
   1237 
   1238        if frame_context.config.low_quality_pinch_zoom {
   1239            raster_to_device.scale.x /= self.current_raster_scale;
   1240            raster_to_device.scale.y /= self.current_raster_scale;
   1241        } else {
   1242            raster_to_device.scale.x = 1.0;
   1243            raster_to_device.scale.y = 1.0;
   1244        }
   1245 
   1246        // Use that compositor transform to calculate a relative local to surface
   1247        let local_to_raster = local_to_device.then(&raster_to_device.inverse());
   1248 
   1249        const EPSILON: f32 = 0.001;
   1250        let compositor_translation_changed =
   1251            !raster_to_device.offset.x.approx_eq_eps(&self.raster_to_device.offset.x, &EPSILON) ||
   1252            !raster_to_device.offset.y.approx_eq_eps(&self.raster_to_device.offset.y, &EPSILON);
   1253        let compositor_scale_changed =
   1254            !raster_to_device.scale.x.approx_eq_eps(&self.raster_to_device.scale.x, &EPSILON) ||
   1255            !raster_to_device.scale.y.approx_eq_eps(&self.raster_to_device.scale.y, &EPSILON);
   1256        let surface_scale_changed =
   1257            !local_to_raster.scale.x.approx_eq_eps(&self.local_to_raster.scale.x, &EPSILON) ||
   1258            !local_to_raster.scale.y.approx_eq_eps(&self.local_to_raster.scale.y, &EPSILON);
   1259 
   1260        if compositor_translation_changed ||
   1261           compositor_scale_changed ||
   1262           surface_scale_changed ||
   1263           frame_context.config.force_invalidation {
   1264            frame_state.composite_state.dirty_rects_are_valid = false;
   1265        }
   1266 
   1267        self.raster_to_device = raster_to_device;
   1268        self.local_to_raster = local_to_raster;
   1269        self.invalidate_all_tiles = surface_scale_changed || frame_context.config.force_invalidation;
   1270 
   1271        // Do a hacky diff of opacity binding values from the last frame. This is
   1272        // used later on during tile invalidation tests.
   1273        let current_properties = frame_context.scene_properties.float_properties();
   1274        mem::swap(&mut self.opacity_bindings, &mut self.old_opacity_bindings);
   1275 
   1276        self.opacity_bindings.clear();
   1277        for (id, value) in current_properties {
   1278            let changed = match self.old_opacity_bindings.get(id) {
   1279                Some(old_property) => !old_property.value.approx_eq(value),
   1280                None => true,
   1281            };
   1282            self.opacity_bindings.insert(*id, OpacityBindingInfo {
   1283                value: *value,
   1284                changed,
   1285            });
   1286        }
   1287 
   1288        // Do a hacky diff of color binding values from the last frame. This is
   1289        // used later on during tile invalidation tests.
   1290        let current_properties = frame_context.scene_properties.color_properties();
   1291        mem::swap(&mut self.color_bindings, &mut self.old_color_bindings);
   1292 
   1293        self.color_bindings.clear();
   1294        for (id, value) in current_properties {
   1295            let changed = match self.old_color_bindings.get(id) {
   1296                Some(old_property) => old_property.value != (*value).into(),
   1297                None => true,
   1298            };
   1299            self.color_bindings.insert(*id, ColorBindingInfo {
   1300                value: (*value).into(),
   1301                changed,
   1302            });
   1303        }
   1304 
   1305        let world_tile_size = WorldSize::new(
   1306            self.current_tile_size.width as f32 / frame_context.global_device_pixel_scale.0,
   1307            self.current_tile_size.height as f32 / frame_context.global_device_pixel_scale.0,
   1308        );
   1309 
   1310        self.tile_size = PictureSize::new(
   1311            world_tile_size.width / self.local_to_raster.scale.x,
   1312            world_tile_size.height / self.local_to_raster.scale.y,
   1313        );
   1314 
   1315        // Inflate the needed rect a bit, so that we retain tiles that we have drawn
   1316        // but have just recently gone off-screen. This means that we avoid re-drawing
   1317        // tiles if the user is scrolling up and down small amounts, at the cost of
   1318        // a bit of extra texture memory.
   1319        let desired_rect_in_pic_space = self.screen_rect_in_pic_space
   1320            .inflate(0.0, 1.0 * self.tile_size.height);
   1321 
   1322        let needed_rect_in_pic_space = desired_rect_in_pic_space
   1323            .intersection(&pic_rect)
   1324            .unwrap_or_else(Box2D::zero);
   1325 
   1326        let p0 = needed_rect_in_pic_space.min;
   1327        let p1 = needed_rect_in_pic_space.max;
   1328 
   1329        let x0 = (p0.x / self.tile_size.width).floor() as i32;
   1330        let x1 = (p1.x / self.tile_size.width).ceil() as i32;
   1331 
   1332        let y0 = (p0.y / self.tile_size.height).floor() as i32;
   1333        let y1 = (p1.y / self.tile_size.height).ceil() as i32;
   1334 
   1335        let new_tile_rect = TileRect {
   1336            min: TileOffset::new(x0, y0),
   1337            max: TileOffset::new(x1, y1),
   1338        };
   1339 
   1340        // Determine whether the current bounds of the tile grid will exceed the
   1341        // bounds of the DC virtual surface, taking into account the current
   1342        // virtual offset. If so, we need to invalidate all tiles, and set up
   1343        // a new virtual offset, centered around the current tile grid.
   1344 
   1345        let virtual_surface_size = frame_context.config.compositor_kind.get_virtual_surface_size();
   1346        // We only need to invalidate in this case if the underlying platform
   1347        // uses virtual surfaces.
   1348        if virtual_surface_size > 0 {
   1349            // Get the extremities of the tile grid after virtual offset is applied
   1350            let tx0 = self.virtual_offset.x + x0 * self.current_tile_size.width;
   1351            let ty0 = self.virtual_offset.y + y0 * self.current_tile_size.height;
   1352            let tx1 = self.virtual_offset.x + (x1+1) * self.current_tile_size.width;
   1353            let ty1 = self.virtual_offset.y + (y1+1) * self.current_tile_size.height;
   1354 
   1355            let need_new_virtual_offset = tx0 < 0 ||
   1356                                          ty0 < 0 ||
   1357                                          tx1 >= virtual_surface_size ||
   1358                                          ty1 >= virtual_surface_size;
   1359 
   1360            if need_new_virtual_offset {
   1361                // Calculate a new virtual offset, centered around the middle of the
   1362                // current tile grid. This means we won't need to invalidate and get
   1363                // a new offset for a long time!
   1364                self.virtual_offset = DeviceIntPoint::new(
   1365                    (virtual_surface_size/2) - ((x0 + x1) / 2) * self.current_tile_size.width,
   1366                    (virtual_surface_size/2) - ((y0 + y1) / 2) * self.current_tile_size.height,
   1367                );
   1368 
   1369                // Invalidate all native tile surfaces. They will be re-allocated next time
   1370                // they are scheduled to be rasterized.
   1371                for sub_slice in &mut self.sub_slices {
   1372                    for tile in sub_slice.tiles.values_mut() {
   1373                        if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface {
   1374                            if let Some(id) = id.take() {
   1375                                frame_state.resource_cache.destroy_compositor_tile(id);
   1376                                tile.surface = None;
   1377                                // Invalidate the entire tile to force a redraw.
   1378                                // TODO(gw): Add a new invalidation reason for virtual offset changing
   1379                                tile.invalidate(None, InvalidationReason::CompositorKindChanged);
   1380                            }
   1381                        }
   1382                    }
   1383 
   1384                    // Destroy the native virtual surfaces. They will be re-allocated next time a tile
   1385                    // that references them is scheduled to draw.
   1386                    if let Some(native_surface) = sub_slice.native_surface.take() {
   1387                        frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque);
   1388                        frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha);
   1389                    }
   1390                }
   1391            }
   1392        }
   1393 
   1394        // Rebuild the tile grid if the picture cache rect has changed.
   1395        if new_tile_rect != self.tile_rect {
   1396            for sub_slice in &mut self.sub_slices {
   1397                let mut old_tiles = sub_slice.resize(new_tile_rect);
   1398 
   1399                // When old tiles that remain after the loop, dirty rects are not valid.
   1400                if !old_tiles.is_empty() {
   1401                    frame_state.composite_state.dirty_rects_are_valid = false;
   1402                }
   1403 
   1404                // Any old tiles that remain after the loop above are going to be dropped. For
   1405                // simple composite mode, the texture cache handle will expire and be collected
   1406                // by the texture cache. For native compositor mode, we need to explicitly
   1407                // invoke a callback to the client to destroy that surface.
   1408                if let CompositorKind::Native { .. } = frame_state.composite_state.compositor_kind {
   1409                    for tile in old_tiles.values_mut() {
   1410                        // Only destroy native surfaces that have been allocated. It's
   1411                        // possible for display port tiles to be created that never
   1412                        // come on screen, and thus never get a native surface allocated.
   1413                        if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface {
   1414                            if let Some(id) = id.take() {
   1415                                frame_state.resource_cache.destroy_compositor_tile(id);
   1416                            }
   1417                        }
   1418                    }
   1419                }
   1420            }
   1421        }
   1422 
   1423        // This is duplicated information from tile_rect, but cached here to avoid
   1424        // redundant calculations during get_tile_coords_for_rect
   1425        self.tile_bounds_p0 = TileOffset::new(x0, y0);
   1426        self.tile_bounds_p1 = TileOffset::new(x1, y1);
   1427        self.tile_rect = new_tile_rect;
   1428 
   1429        let mut world_culling_rect = WorldRect::zero();
   1430 
   1431        let mut ctx = TilePreUpdateContext {
   1432            pic_to_world_mapper,
   1433            background_color: self.background_color,
   1434            global_screen_world_rect: frame_context.global_screen_world_rect,
   1435            tile_size: self.tile_size,
   1436            frame_id: self.frame_id,
   1437        };
   1438 
   1439        // Pre-update each tile
   1440        for sub_slice in &mut self.sub_slices {
   1441            for tile in sub_slice.tiles.values_mut() {
   1442                tile.pre_update(&ctx);
   1443 
   1444                // Only include the tiles that are currently in view into the world culling
   1445                // rect. This is a very important optimization for a couple of reasons:
   1446                // (1) Primitives that intersect with tiles in the grid that are not currently
   1447                //     visible can be skipped from primitive preparation, clip chain building
   1448                //     and tile dependency updates.
   1449                // (2) When we need to allocate an off-screen surface for a child picture (for
   1450                //     example a CSS filter) we clip the size of the GPU surface to the world
   1451                //     culling rect below (to ensure we draw enough of it to be sampled by any
   1452                //     tiles that reference it). Making the world culling rect only affected
   1453                //     by visible tiles (rather than the entire virtual tile display port) can
   1454                //     result in allocating _much_ smaller GPU surfaces for cases where the
   1455                //     true off-screen surface size is very large.
   1456                if tile.is_visible {
   1457                    world_culling_rect = world_culling_rect.union(&tile.world_tile_rect);
   1458                }
   1459            }
   1460 
   1461            // The background color can only be applied to the first sub-slice.
   1462            ctx.background_color = None;
   1463        }
   1464 
   1465        // If compositor mode is changed, need to drop all incompatible tiles.
   1466        match frame_context.config.compositor_kind {
   1467            CompositorKind::Draw { .. } | CompositorKind::Layer { .. } => {
   1468                for sub_slice in &mut self.sub_slices {
   1469                    for tile in sub_slice.tiles.values_mut() {
   1470                        if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface {
   1471                            if let Some(id) = id.take() {
   1472                                frame_state.resource_cache.destroy_compositor_tile(id);
   1473                            }
   1474                            tile.surface = None;
   1475                            // Invalidate the entire tile to force a redraw.
   1476                            tile.invalidate(None, InvalidationReason::CompositorKindChanged);
   1477                        }
   1478                    }
   1479 
   1480                    if let Some(native_surface) = sub_slice.native_surface.take() {
   1481                        frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque);
   1482                        frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha);
   1483                    }
   1484                }
   1485 
   1486                for (_, external_surface) in self.external_native_surface_cache.drain() {
   1487                    frame_state.resource_cache.destroy_compositor_surface(external_surface.native_surface_id)
   1488                }
   1489            }
   1490            CompositorKind::Native { .. } => {
   1491                // This could hit even when compositor mode is not changed,
   1492                // then we need to check if there are incompatible tiles.
   1493                for sub_slice in &mut self.sub_slices {
   1494                    for tile in sub_slice.tiles.values_mut() {
   1495                        if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::TextureCache { .. }, .. }) = tile.surface {
   1496                            tile.surface = None;
   1497                            // Invalidate the entire tile to force a redraw.
   1498                            tile.invalidate(None, InvalidationReason::CompositorKindChanged);
   1499                        }
   1500                    }
   1501                }
   1502            }
   1503        }
   1504 
   1505        world_culling_rect
   1506    }
   1507 
   1508    fn can_promote_to_surface(
   1509        &mut self,
   1510        prim_clip_chain: &ClipChainInstance,
   1511        prim_spatial_node_index: SpatialNodeIndex,
   1512        is_root_tile_cache: bool,
   1513        sub_slice_index: usize,
   1514        surface_kind: CompositorSurfaceKind,
   1515        pic_coverage_rect: PictureRect,
   1516        frame_context: &FrameVisibilityContext,
   1517        data_stores: &DataStores,
   1518        clip_store: &ClipStore,
   1519        composite_state: &CompositeState,
   1520        force: bool,
   1521    ) -> Result<CompositorSurfaceKind, SurfacePromotionFailure> {
   1522        use SurfacePromotionFailure::*;
   1523 
   1524        // Each strategy has different restrictions on whether we can promote
   1525        match surface_kind {
   1526            CompositorSurfaceKind::Overlay => {
   1527                // For now, only support a small (arbitrary) number of compositor surfaces.
   1528                // Non-opaque compositor surfaces require sub-slices, as they are drawn
   1529                // as overlays.
   1530                if sub_slice_index == self.sub_slices.len() - 1 {
   1531                    return Err(OverlaySurfaceLimit);
   1532                }
   1533 
   1534                // If a complex clip is being applied to this primitive, it can't be
   1535                // promoted directly to a compositor surface.
   1536                if prim_clip_chain.needs_mask {
   1537                    let mut is_supported_rounded_rect = false;
   1538                    if let CompositorKind::Layer { .. } = composite_state.compositor_kind {
   1539                        if prim_clip_chain.clips_range.count == 1 && self.compositor_clip.is_none() {
   1540                            let clip_instance = clip_store.get_instance_from_range(&prim_clip_chain.clips_range, 0);
   1541                            let clip_node = &data_stores.clip[clip_instance.handle];
   1542 
   1543                            if let ClipItemKind::RoundedRectangle { ref radius, mode: ClipMode::Clip, rect, .. } = clip_node.item.kind {
   1544                                let max_corner_width = radius.top_left.width
   1545                                                            .max(radius.bottom_left.width)
   1546                                                            .max(radius.top_right.width)
   1547                                                            .max(radius.bottom_right.width);
   1548                                let max_corner_height = radius.top_left.height
   1549                                                            .max(radius.bottom_left.height)
   1550                                                            .max(radius.top_right.height)
   1551                                                            .max(radius.bottom_right.height);
   1552 
   1553                                if max_corner_width <= 0.5 * rect.size().width &&
   1554                                    max_corner_height <= 0.5 * rect.size().height {
   1555                                    is_supported_rounded_rect = true;
   1556                                }
   1557                            }
   1558                        }
   1559                    }
   1560 
   1561                    if !is_supported_rounded_rect {
   1562                        return Err(OverlayNeedsMask);
   1563                    }
   1564                }
   1565            }
   1566            CompositorSurfaceKind::Underlay => {
   1567                // If a mask is needed, there are some restrictions.
   1568                if prim_clip_chain.needs_mask {
   1569                    // Need an opaque region behind this prim. The opaque region doesn't
   1570                    // need to span the entire visible region of the TileCacheInstance,
   1571                    // which would set self.backdrop.kind, but that also qualifies.
   1572                    if !self.backdrop.opaque_rect.contains_box(&pic_coverage_rect) {
   1573                        let result = Err(UnderlayAlphaBackdrop);
   1574                        // If we aren't forcing, give up and return Err.
   1575                        if !force {
   1576                            return result;
   1577                        }
   1578 
   1579                        // Log this but don't return an error.
   1580                        self.report_promotion_failure(result, pic_coverage_rect, true);
   1581                    }
   1582 
   1583                    // Only one masked underlay allowed.
   1584                    if !self.underlays.is_empty() {
   1585                        return Err(UnderlaySurfaceLimit);
   1586                    }
   1587                }
   1588 
   1589                // Underlays can't appear on top of overlays, because they can't punch
   1590                // through the existing overlay.
   1591                if self.overlay_region.intersects(&pic_coverage_rect) {
   1592                    let result = Err(UnderlayIntersectsOverlay);
   1593                    // If we aren't forcing, give up and return Err.
   1594                    if !force {
   1595                        return result;
   1596                    }
   1597 
   1598                    // Log this but don't return an error.
   1599                    self.report_promotion_failure(result, pic_coverage_rect, true);
   1600                }
   1601 
   1602                // Underlay cutouts are difficult to align with compositor surfaces when
   1603                // compositing during low-quality zoom, and the required invalidation
   1604                // whilst zooming would prevent low-quality zoom from working efficiently.
   1605                if frame_context.config.low_quality_pinch_zoom &&
   1606                    frame_context.spatial_tree.get_spatial_node(prim_spatial_node_index).is_ancestor_or_self_zooming
   1607                {
   1608                    return Err(UnderlayLowQualityZoom);
   1609                }
   1610            }
   1611            CompositorSurfaceKind::Blit => unreachable!(),
   1612        }
   1613 
   1614        // If not on the root picture cache, it has some kind of
   1615        // complex effect (such as a filter, mix-blend-mode or 3d transform).
   1616        if !is_root_tile_cache {
   1617            return Err(NotRootTileCache);
   1618        }
   1619 
   1620        let mapper : SpaceMapper<PicturePixel, WorldPixel> = SpaceMapper::new_with_target(
   1621            frame_context.root_spatial_node_index,
   1622            prim_spatial_node_index,
   1623            frame_context.global_screen_world_rect,
   1624            &frame_context.spatial_tree);
   1625        let transform = mapper.get_transform();
   1626        if !transform.is_2d_scale_translation() {
   1627            let result = Err(ComplexTransform);
   1628            // Unfortunately, ComplexTransform absolutely prevents proper
   1629 			    // functioning of surface promotion. Treating this as a warning
   1630            // instead of an error will cause a crash in get_relative_scale_offset.
   1631            return result;
   1632        }
   1633 
   1634        if self.slice_flags.contains(SliceFlags::IS_ATOMIC) {
   1635            return Err(SliceAtomic);
   1636        }
   1637 
   1638        Ok(surface_kind)
   1639    }
   1640 
   1641    fn setup_compositor_surfaces_yuv(
   1642        &mut self,
   1643        sub_slice_index: usize,
   1644        prim_info: &mut PrimitiveDependencyInfo,
   1645        flags: PrimitiveFlags,
   1646        local_prim_rect: LayoutRect,
   1647        prim_clip_chain: &ClipChainInstance,
   1648        prim_spatial_node_index: SpatialNodeIndex,
   1649        pic_coverage_rect: PictureRect,
   1650        frame_context: &FrameVisibilityContext,
   1651        data_stores: &DataStores,
   1652        clip_store: &ClipStore,
   1653        image_dependencies: &[ImageDependency;3],
   1654        api_keys: &[ImageKey; 3],
   1655        resource_cache: &mut ResourceCache,
   1656        composite_state: &mut CompositeState,
   1657        gpu_buffer: &mut GpuBufferBuilderF,
   1658        image_rendering: ImageRendering,
   1659        color_depth: ColorDepth,
   1660        color_space: YuvRangedColorSpace,
   1661        format: YuvFormat,
   1662        surface_kind: CompositorSurfaceKind,
   1663    ) -> Result<CompositorSurfaceKind, SurfacePromotionFailure> {
   1664        for &key in api_keys {
   1665            if key != ImageKey::DUMMY {
   1666                // TODO: See comment in setup_compositor_surfaces_rgb.
   1667                resource_cache.request_image(ImageRequest {
   1668                        key,
   1669                        rendering: image_rendering,
   1670                        tile: None,
   1671                    },
   1672                    gpu_buffer,
   1673                );
   1674            }
   1675        }
   1676 
   1677        self.setup_compositor_surfaces_impl(
   1678            sub_slice_index,
   1679            prim_info,
   1680            flags,
   1681            local_prim_rect,
   1682            prim_clip_chain,
   1683            prim_spatial_node_index,
   1684            pic_coverage_rect,
   1685            frame_context,
   1686            data_stores,
   1687            clip_store,
   1688            ExternalSurfaceDependency::Yuv {
   1689                image_dependencies: *image_dependencies,
   1690                color_space,
   1691                format,
   1692                channel_bit_depth: color_depth.bit_depth(),
   1693            },
   1694            api_keys,
   1695            resource_cache,
   1696            composite_state,
   1697            image_rendering,
   1698            true,
   1699            surface_kind,
   1700        )
   1701    }
   1702 
   1703    fn setup_compositor_surfaces_rgb(
   1704        &mut self,
   1705        sub_slice_index: usize,
   1706        prim_info: &mut PrimitiveDependencyInfo,
   1707        flags: PrimitiveFlags,
   1708        local_prim_rect: LayoutRect,
   1709        prim_clip_chain: &ClipChainInstance,
   1710        prim_spatial_node_index: SpatialNodeIndex,
   1711        pic_coverage_rect: PictureRect,
   1712        frame_context: &FrameVisibilityContext,
   1713        data_stores: &DataStores,
   1714        clip_store: &ClipStore,
   1715        image_dependency: ImageDependency,
   1716        api_key: ImageKey,
   1717        resource_cache: &mut ResourceCache,
   1718        composite_state: &mut CompositeState,
   1719        gpu_buffer: &mut GpuBufferBuilderF,
   1720        image_rendering: ImageRendering,
   1721        is_opaque: bool,
   1722        surface_kind: CompositorSurfaceKind,
   1723    ) -> Result<CompositorSurfaceKind, SurfacePromotionFailure> {
   1724        let mut api_keys = [ImageKey::DUMMY; 3];
   1725        api_keys[0] = api_key;
   1726 
   1727        // TODO: The picture compositing code requires images promoted
   1728        // into their own picture cache slices to be requested every
   1729        // frame even if they are not visible. However the image updates
   1730        // are only reached on the prepare pass for visible primitives.
   1731        // So we make sure to trigger an image request when promoting
   1732        // the image here.
   1733        resource_cache.request_image(ImageRequest {
   1734                key: api_key,
   1735                rendering: image_rendering,
   1736                tile: None,
   1737            },
   1738            gpu_buffer,
   1739        );
   1740 
   1741        self.setup_compositor_surfaces_impl(
   1742            sub_slice_index,
   1743            prim_info,
   1744            flags,
   1745            local_prim_rect,
   1746            prim_clip_chain,
   1747            prim_spatial_node_index,
   1748            pic_coverage_rect,
   1749            frame_context,
   1750            data_stores,
   1751            clip_store,
   1752            ExternalSurfaceDependency::Rgb {
   1753                image_dependency,
   1754            },
   1755            &api_keys,
   1756            resource_cache,
   1757            composite_state,
   1758            image_rendering,
   1759            is_opaque,
   1760            surface_kind,
   1761        )
   1762    }
   1763 
   1764    // returns false if composition is not available for this surface,
   1765    // and the non-compositor path should be used to draw it instead.
   1766    fn setup_compositor_surfaces_impl(
   1767        &mut self,
   1768        sub_slice_index: usize,
   1769        prim_info: &mut PrimitiveDependencyInfo,
   1770        flags: PrimitiveFlags,
   1771        local_prim_rect: LayoutRect,
   1772        prim_clip_chain: &ClipChainInstance,
   1773        prim_spatial_node_index: SpatialNodeIndex,
   1774        pic_coverage_rect: PictureRect,
   1775        frame_context: &FrameVisibilityContext,
   1776        data_stores: &DataStores,
   1777        clip_store: &ClipStore,
   1778        dependency: ExternalSurfaceDependency,
   1779        api_keys: &[ImageKey; 3],
   1780        resource_cache: &mut ResourceCache,
   1781        composite_state: &mut CompositeState,
   1782        image_rendering: ImageRendering,
   1783        is_opaque: bool,
   1784        surface_kind: CompositorSurfaceKind,
   1785    ) -> Result<CompositorSurfaceKind, SurfacePromotionFailure> {
   1786        use SurfacePromotionFailure::*;
   1787 
   1788        let map_local_to_picture = SpaceMapper::new_with_target(
   1789            self.spatial_node_index,
   1790            prim_spatial_node_index,
   1791            self.local_rect,
   1792            frame_context.spatial_tree,
   1793        );
   1794 
   1795        // Map the primitive local rect into picture space.
   1796        let prim_rect = match map_local_to_picture.map(&local_prim_rect) {
   1797            Some(rect) => rect,
   1798            None => return Ok(surface_kind),
   1799        };
   1800 
   1801        // If the rect is invalid, no need to create dependencies.
   1802        if prim_rect.is_empty() {
   1803            return Ok(surface_kind);
   1804        }
   1805 
   1806        let pic_to_world_mapper = SpaceMapper::new_with_target(
   1807            frame_context.root_spatial_node_index,
   1808            self.spatial_node_index,
   1809            frame_context.global_screen_world_rect,
   1810            frame_context.spatial_tree,
   1811        );
   1812 
   1813        let world_clip_rect = pic_to_world_mapper
   1814            .map(&prim_info.prim_clip_box)
   1815            .expect("bug: unable to map clip to world space");
   1816 
   1817        let is_visible = world_clip_rect.intersects(&frame_context.global_screen_world_rect);
   1818        if !is_visible {
   1819            return Ok(surface_kind);
   1820        }
   1821 
   1822        let prim_offset = ScaleOffset::from_offset(local_prim_rect.min.to_vector().cast_unit());
   1823 
   1824        let local_prim_to_device = get_relative_scale_offset(
   1825            prim_spatial_node_index,
   1826            frame_context.root_spatial_node_index,
   1827            frame_context.spatial_tree,
   1828        );
   1829 
   1830        let normalized_prim_to_device = prim_offset.then(&local_prim_to_device);
   1831 
   1832        let local_to_raster = ScaleOffset::identity();
   1833        let raster_to_device = normalized_prim_to_device;
   1834 
   1835        // If this primitive is an external image, and supports being used
   1836        // directly by a native compositor, then lookup the external image id
   1837        // so we can pass that through.
   1838        let mut external_image_id = if flags.contains(PrimitiveFlags::SUPPORTS_EXTERNAL_COMPOSITOR_SURFACE)
   1839            && image_rendering == ImageRendering::Auto {
   1840            resource_cache.get_image_properties(api_keys[0])
   1841                .and_then(|properties| properties.external_image)
   1842                .and_then(|image| Some(image.id))
   1843        } else {
   1844            None
   1845        };
   1846 
   1847        match composite_state.compositor_kind {
   1848            CompositorKind::Native { capabilities, .. } => {
   1849                if external_image_id.is_some() &&
   1850                !capabilities.supports_external_compositor_surface_negative_scaling &&
   1851                (raster_to_device.scale.x < 0.0 || raster_to_device.scale.y < 0.0) {
   1852                    external_image_id = None;
   1853                }
   1854            }
   1855            CompositorKind::Layer { .. } | CompositorKind::Draw { .. } => {}
   1856        }
   1857 
   1858        let compositor_transform_index = composite_state.register_transform(
   1859            local_to_raster,
   1860            raster_to_device,
   1861        );
   1862 
   1863        let surface_size = composite_state.get_surface_rect(
   1864            &local_prim_rect,
   1865            &local_prim_rect,
   1866            compositor_transform_index,
   1867        ).size();
   1868 
   1869        let clip_rect = (world_clip_rect * frame_context.global_device_pixel_scale).round();
   1870 
   1871 
   1872        let mut compositor_clip_index = None;
   1873 
   1874        if surface_kind == CompositorSurfaceKind::Overlay &&
   1875            prim_clip_chain.needs_mask {
   1876            assert!(prim_clip_chain.clips_range.count == 1);
   1877            assert!(self.compositor_clip.is_none());
   1878 
   1879            let clip_instance = clip_store.get_instance_from_range(&prim_clip_chain.clips_range, 0);
   1880            let clip_node = &data_stores.clip[clip_instance.handle];
   1881            if let ClipItemKind::RoundedRectangle { radius, mode: ClipMode::Clip, rect, .. } = clip_node.item.kind {
   1882                // Map the clip in to device space. We know from the shared
   1883                // clip creation logic it's in root coord system, so only a
   1884                // 2d axis-aligned transform can apply. For example, in the
   1885                // case of a pinch-zoom effect.
   1886                let map = ClipSpaceConversion::new(
   1887                    frame_context.root_spatial_node_index,
   1888                    clip_node.item.spatial_node_index,
   1889                    frame_context.root_spatial_node_index,
   1890                    frame_context.spatial_tree,
   1891                );
   1892 
   1893                let (rect, radius) = match map {
   1894                    ClipSpaceConversion::Local => {
   1895                        (rect.cast_unit(), radius)
   1896                    }
   1897                    ClipSpaceConversion::ScaleOffset(scale_offset) => {
   1898                        (
   1899                            scale_offset.map_rect(&rect),
   1900                            BorderRadius {
   1901                                top_left: scale_offset.map_size(&radius.top_left),
   1902                                top_right: scale_offset.map_size(&radius.top_right),
   1903                                bottom_left: scale_offset.map_size(&radius.bottom_left),
   1904                                bottom_right: scale_offset.map_size(&radius.bottom_right),
   1905                            },
   1906                        )
   1907                    }
   1908                    ClipSpaceConversion::Transform(..) => {
   1909                        unreachable!();
   1910                    }
   1911                };
   1912 
   1913                compositor_clip_index = Some(composite_state.register_clip(
   1914                    rect,
   1915                    radius,
   1916                ));
   1917            } else {
   1918                unreachable!();
   1919            }
   1920        }
   1921 
   1922        if surface_size.width >= MAX_COMPOSITOR_SURFACES_SIZE ||
   1923           surface_size.height >= MAX_COMPOSITOR_SURFACES_SIZE {
   1924           return Err(SizeTooLarge);
   1925        }
   1926 
   1927        // When using native compositing, we need to find an existing native surface
   1928        // handle to use, or allocate a new one. For existing native surfaces, we can
   1929        // also determine whether this needs to be updated, depending on whether the
   1930        // image generation(s) of the planes have changed since last composite.
   1931        let (native_surface_id, update_params) = match composite_state.compositor_kind {
   1932            CompositorKind::Draw { .. } | CompositorKind::Layer { .. } => {
   1933                (None, None)
   1934            }
   1935            CompositorKind::Native { .. } => {
   1936                let native_surface_size = surface_size.to_i32();
   1937 
   1938                let key = ExternalNativeSurfaceKey {
   1939                    image_keys: *api_keys,
   1940                    size: if external_image_id.is_some() { None } else { Some(native_surface_size) },
   1941                };
   1942 
   1943                let native_surface = self.external_native_surface_cache
   1944                    .entry(key)
   1945                    .or_insert_with(|| {
   1946                        // No existing surface, so allocate a new compositor surface.
   1947                        let native_surface_id = match external_image_id {
   1948                            Some(_external_image) => {
   1949                                // If we have a suitable external image, then create an external
   1950                                // surface to attach to.
   1951                                resource_cache.create_compositor_external_surface(is_opaque)
   1952                            }
   1953                            None => {
   1954                                // Otherwise create a normal compositor surface and a single
   1955                                // compositor tile that covers the entire surface.
   1956                                let native_surface_id =
   1957                                resource_cache.create_compositor_surface(
   1958                                    DeviceIntPoint::zero(),
   1959                                    native_surface_size,
   1960                                    is_opaque,
   1961                                );
   1962 
   1963                                let tile_id = NativeTileId {
   1964                                    surface_id: native_surface_id,
   1965                                    x: 0,
   1966                                    y: 0,
   1967                                };
   1968                                resource_cache.create_compositor_tile(tile_id);
   1969 
   1970                                native_surface_id
   1971                            }
   1972                        };
   1973 
   1974                        ExternalNativeSurface {
   1975                            used_this_frame: true,
   1976                            native_surface_id,
   1977                            image_dependencies: [ImageDependency::INVALID; 3],
   1978                        }
   1979                    });
   1980 
   1981                // Mark that the surface is referenced this frame so that the
   1982                // backing native surface handle isn't freed.
   1983                native_surface.used_this_frame = true;
   1984 
   1985                let update_params = match external_image_id {
   1986                    Some(external_image) => {
   1987                        // If this is an external image surface, then there's no update
   1988                        // to be done. Just attach the current external image to the surface
   1989                        // and we're done.
   1990                        resource_cache.attach_compositor_external_image(
   1991                            native_surface.native_surface_id,
   1992                            external_image,
   1993                        );
   1994                        None
   1995                    }
   1996                    None => {
   1997                        // If the image dependencies match, there is no need to update
   1998                        // the backing native surface.
   1999                        match dependency {
   2000                            ExternalSurfaceDependency::Yuv{ image_dependencies, .. } => {
   2001                                if image_dependencies == native_surface.image_dependencies {
   2002                                    None
   2003                                } else {
   2004                                    Some(native_surface_size)
   2005                                }
   2006                            },
   2007                            ExternalSurfaceDependency::Rgb{ image_dependency, .. } => {
   2008                                if image_dependency == native_surface.image_dependencies[0] {
   2009                                    None
   2010                                } else {
   2011                                    Some(native_surface_size)
   2012                                }
   2013                            },
   2014                        }
   2015                    }
   2016                };
   2017 
   2018                (Some(native_surface.native_surface_id), update_params)
   2019            }
   2020        };
   2021 
   2022        let descriptor = ExternalSurfaceDescriptor {
   2023            local_surface_size: local_prim_rect.size(),
   2024            local_rect: prim_rect,
   2025            local_clip_rect: prim_info.prim_clip_box,
   2026            dependency,
   2027            image_rendering,
   2028            clip_rect,
   2029            transform_index: compositor_transform_index,
   2030            compositor_clip_index: compositor_clip_index,
   2031            z_id: ZBufferId::invalid(),
   2032            native_surface_id,
   2033            update_params,
   2034            external_image_id,
   2035        };
   2036 
   2037        // If the surface is opaque, we can draw it an an underlay (which avoids
   2038        // additional sub-slice surfaces, and supports clip masks)
   2039        match surface_kind {
   2040            CompositorSurfaceKind::Underlay => {
   2041                self.underlays.push(descriptor);
   2042            }
   2043            CompositorSurfaceKind::Overlay => {
   2044                // For compositor surfaces, if we didn't find an earlier sub-slice to add to,
   2045                // we know we can append to the current slice.
   2046                assert!(sub_slice_index < self.sub_slices.len() - 1);
   2047                let sub_slice = &mut self.sub_slices[sub_slice_index];
   2048 
   2049                // Each compositor surface allocates a unique z-id
   2050                sub_slice.compositor_surfaces.push(CompositorSurface {
   2051                    prohibited_rect: pic_coverage_rect,
   2052                    is_opaque,
   2053                    descriptor,
   2054                });
   2055 
   2056                // Add the pic_coverage_rect to the overlay region. This prevents
   2057                // future promoted surfaces from becoming underlays if they would
   2058                // intersect with the overlay region.
   2059                self.overlay_region = self.overlay_region.union(&pic_coverage_rect);
   2060            }
   2061            CompositorSurfaceKind::Blit => unreachable!(),
   2062        }
   2063 
   2064        Ok(surface_kind)
   2065    }
   2066 
   2067    /// Push an estimated rect for an off-screen surface during dependency updates. This is
   2068    /// a workaround / hack that allows the picture cache code to know when it should be
   2069    /// processing primitive dependencies as a single atomic unit. In future, we aim to remove
   2070    /// this hack by having the primitive dependencies stored _within_ each owning picture.
   2071    /// This is part of the work required to support child picture caching anyway!
   2072    pub fn push_surface(
   2073        &mut self,
   2074        estimated_local_rect: LayoutRect,
   2075        surface_spatial_node_index: SpatialNodeIndex,
   2076        spatial_tree: &SpatialTree,
   2077    ) {
   2078        // Only need to evaluate sub-slice regions if we have compositor surfaces present
   2079        if self.current_surface_traversal_depth == 0 && self.sub_slices.len() > 1 {
   2080            let map_local_to_picture = SpaceMapper::new_with_target(
   2081                self.spatial_node_index,
   2082                surface_spatial_node_index,
   2083                self.local_rect,
   2084                spatial_tree,
   2085            );
   2086 
   2087            if let Some(pic_rect) = map_local_to_picture.map(&estimated_local_rect) {
   2088                // Find the first sub-slice we can add this primitive to (we want to add
   2089                // prims to the primary surface if possible, so they get subpixel AA).
   2090                for sub_slice in &mut self.sub_slices {
   2091                    let mut intersects_prohibited_region = false;
   2092 
   2093                    for surface in &mut sub_slice.compositor_surfaces {
   2094                        if pic_rect.intersects(&surface.prohibited_rect) {
   2095                            surface.prohibited_rect = surface.prohibited_rect.union(&pic_rect);
   2096 
   2097                            intersects_prohibited_region = true;
   2098                        }
   2099                    }
   2100 
   2101                    if !intersects_prohibited_region {
   2102                        break;
   2103                    }
   2104                }
   2105            }
   2106        }
   2107 
   2108        self.current_surface_traversal_depth += 1;
   2109    }
   2110 
   2111    /// Pop an off-screen surface off the stack during dependency updates
   2112    pub fn pop_surface(&mut self) {
   2113        self.current_surface_traversal_depth -= 1;
   2114    }
   2115 
   2116    fn report_promotion_failure(&self,
   2117                                result: Result<CompositorSurfaceKind, SurfacePromotionFailure>,
   2118                                rect: PictureRect,
   2119                                ignored: bool) {
   2120        if !self.debug_flags.contains(DebugFlags::SURFACE_PROMOTION_LOGGING) || result.is_ok() {
   2121            return;
   2122        }
   2123 
   2124        // Report this as a warning.
   2125        // TODO: Find a way to expose this to web authors.
   2126        let outcome = if ignored { "failure ignored" } else { "failed" };
   2127        warn!("Surface promotion of prim at {:?} {outcome} with: {}.", rect, result.unwrap_err());
   2128    }
   2129 
   2130    /// Update the dependencies for each tile for a given primitive instance.
   2131    pub fn update_prim_dependencies(
   2132        &mut self,
   2133        prim_instance: &mut PrimitiveInstance,
   2134        prim_spatial_node_index: SpatialNodeIndex,
   2135        local_prim_rect: LayoutRect,
   2136        frame_context: &FrameVisibilityContext,
   2137        data_stores: &DataStores,
   2138        clip_store: &ClipStore,
   2139        pictures: &[PicturePrimitive],
   2140        resource_cache: &mut ResourceCache,
   2141        color_bindings: &ColorBindingStorage,
   2142        surface_stack: &[(PictureIndex, SurfaceIndex)],
   2143        composite_state: &mut CompositeState,
   2144        gpu_buffer: &mut GpuBufferBuilderF,
   2145        scratch: &mut PrimitiveScratchBuffer,
   2146        is_root_tile_cache: bool,
   2147        surfaces: &mut [SurfaceInfo],
   2148        profile: &mut TransactionProfile,
   2149    ) -> VisibilityState {
   2150        use SurfacePromotionFailure::*;
   2151 
   2152        // This primitive exists on the last element on the current surface stack.
   2153        profile_scope!("update_prim_dependencies");
   2154        let prim_surface_index = surface_stack.last().unwrap().1;
   2155        let prim_clip_chain = &prim_instance.vis.clip_chain;
   2156 
   2157        // If the primitive is directly drawn onto this picture cache surface, then
   2158        // the pic_coverage_rect is in the same space. If not, we need to map it from
   2159        // the intermediate picture space into the picture cache space.
   2160        let on_picture_surface = prim_surface_index == self.surface_index;
   2161        let pic_coverage_rect = if on_picture_surface {
   2162            prim_clip_chain.pic_coverage_rect
   2163        } else {
   2164            // We want to get the rect in the tile cache picture space that this primitive
   2165            // occupies, in order to enable correct invalidation regions. Each surface
   2166            // that exists in the chain between this primitive and the tile cache surface
   2167            // may have an arbitrary inflation factor (for example, in the case of a series
   2168            // of nested blur elements). To account for this, step through the current
   2169            // surface stack, mapping the primitive rect into each picture space, including
   2170            // the inflation factor from each intermediate surface.
   2171            let mut current_pic_coverage_rect = prim_clip_chain.pic_coverage_rect;
   2172            let mut current_spatial_node_index = surfaces[prim_surface_index.0]
   2173                .surface_spatial_node_index;
   2174 
   2175            for (pic_index, surface_index) in surface_stack.iter().rev() {
   2176                let surface = &surfaces[surface_index.0];
   2177                let pic = &pictures[pic_index.0];
   2178 
   2179                let map_local_to_parent = SpaceMapper::new_with_target(
   2180                    surface.surface_spatial_node_index,
   2181                    current_spatial_node_index,
   2182                    surface.unclipped_local_rect,
   2183                    frame_context.spatial_tree,
   2184                );
   2185 
   2186                // Map the rect into the parent surface, and inflate if this surface requires
   2187                // it. If the rect can't be mapping (e.g. due to an invalid transform) then
   2188                // just bail out from the dependencies and cull this primitive.
   2189                current_pic_coverage_rect = match map_local_to_parent.map(&current_pic_coverage_rect) {
   2190                    Some(rect) => {
   2191                        // TODO(gw): The casts here are a hack. We have some interface inconsistencies
   2192                        //           between layout/picture rects which don't really work with the
   2193                        //           current unit system, since sometimes the local rect of a picture
   2194                        //           is a LayoutRect, and sometimes it's a PictureRect. Consider how
   2195                        //           we can improve this?
   2196                        pic.composite_mode.as_ref().unwrap().get_coverage(
   2197                            surface,
   2198                            Some(rect.cast_unit()),
   2199                        ).cast_unit()
   2200                    }
   2201                    None => {
   2202                        return VisibilityState::Culled;
   2203                    }
   2204                };
   2205 
   2206                current_spatial_node_index = surface.surface_spatial_node_index;
   2207            }
   2208 
   2209            current_pic_coverage_rect
   2210        };
   2211 
   2212        // Get the tile coordinates in the picture space.
   2213        let (p0, p1) = self.get_tile_coords_for_rect(&pic_coverage_rect);
   2214 
   2215        // If the primitive is outside the tiling rects, it's known to not
   2216        // be visible.
   2217        if p0.x == p1.x || p0.y == p1.y {
   2218            return VisibilityState::Culled;
   2219        }
   2220 
   2221        // Build the list of resources that this primitive has dependencies on.
   2222        let mut prim_info = PrimitiveDependencyInfo::new(
   2223            prim_instance.uid(),
   2224            pic_coverage_rect,
   2225        );
   2226 
   2227        let mut sub_slice_index = self.sub_slices.len() - 1;
   2228 
   2229        // Only need to evaluate sub-slice regions if we have compositor surfaces present
   2230        if sub_slice_index > 0 {
   2231            // Find the first sub-slice we can add this primitive to (we want to add
   2232            // prims to the primary surface if possible, so they get subpixel AA).
   2233            for (i, sub_slice) in self.sub_slices.iter_mut().enumerate() {
   2234                let mut intersects_prohibited_region = false;
   2235 
   2236                for surface in &mut sub_slice.compositor_surfaces {
   2237                    if pic_coverage_rect.intersects(&surface.prohibited_rect) {
   2238                        surface.prohibited_rect = surface.prohibited_rect.union(&pic_coverage_rect);
   2239 
   2240                        intersects_prohibited_region = true;
   2241                    }
   2242                }
   2243 
   2244                if !intersects_prohibited_region {
   2245                    sub_slice_index = i;
   2246                    break;
   2247                }
   2248            }
   2249        }
   2250 
   2251        // Include the prim spatial node, if differs relative to cache root.
   2252        if prim_spatial_node_index != self.spatial_node_index {
   2253            prim_info.spatial_nodes.push(prim_spatial_node_index);
   2254        }
   2255 
   2256        // If there was a clip chain, add any clip dependencies to the list for this tile.
   2257        let clip_instances = &clip_store
   2258            .clip_node_instances[prim_clip_chain.clips_range.to_range()];
   2259        for clip_instance in clip_instances {
   2260            let clip = &data_stores.clip[clip_instance.handle];
   2261 
   2262            prim_info.clips.push(clip_instance.handle.uid());
   2263 
   2264            // If the clip has the same spatial node, the relative transform
   2265            // will always be the same, so there's no need to depend on it.
   2266            if clip.item.spatial_node_index != self.spatial_node_index
   2267                && !prim_info.spatial_nodes.contains(&clip.item.spatial_node_index) {
   2268                prim_info.spatial_nodes.push(clip.item.spatial_node_index);
   2269            }
   2270        }
   2271 
   2272        // Certain primitives may select themselves to be a backdrop candidate, which is
   2273        // then applied below.
   2274        let mut backdrop_candidate = None;
   2275 
   2276        // For pictures, we don't (yet) know the valid clip rect, so we can't correctly
   2277        // use it to calculate the local bounding rect for the tiles. If we include them
   2278        // then we may calculate a bounding rect that is too large, since it won't include
   2279        // the clip bounds of the picture. Excluding them from the bounding rect here
   2280        // fixes any correctness issues (the clips themselves are considered when we
   2281        // consider the bounds of the primitives that are *children* of the picture),
   2282        // however it does potentially result in some un-necessary invalidations of a
   2283        // tile (in cases where the picture local rect affects the tile, but the clip
   2284        // rect eventually means it doesn't affect that tile).
   2285        // TODO(gw): Get picture clips earlier (during the initial picture traversal
   2286        //           pass) so that we can calculate these correctly.
   2287        match prim_instance.kind {
   2288            PrimitiveInstanceKind::Picture { pic_index,.. } => {
   2289                // Pictures can depend on animated opacity bindings.
   2290                let pic = &pictures[pic_index.0];
   2291                if let Some(PictureCompositeMode::Filter(Filter::Opacity(binding, _))) = pic.composite_mode {
   2292                    prim_info.opacity_bindings.push(binding.into());
   2293                }
   2294            }
   2295            PrimitiveInstanceKind::Rectangle { data_handle, color_binding_index, .. } => {
   2296                // Rectangles can only form a backdrop candidate if they are known opaque.
   2297                // TODO(gw): We could resolve the opacity binding here, but the common
   2298                //           case for background rects is that they don't have animated opacity.
   2299                let PrimitiveTemplateKind::Rectangle { color, .. } = data_stores.prim[data_handle].kind;
   2300                let color = frame_context.scene_properties.resolve_color(&color);
   2301                if color.a >= 1.0 {
   2302                    backdrop_candidate = Some(BackdropInfo {
   2303                        opaque_rect: pic_coverage_rect,
   2304                        spanning_opaque_color: None,
   2305                        kind: Some(BackdropKind::Color { color }),
   2306                        backdrop_rect: pic_coverage_rect,
   2307                    });
   2308                }
   2309 
   2310                if color_binding_index != ColorBindingIndex::INVALID {
   2311                    prim_info.color_binding = Some(color_bindings[color_binding_index].into());
   2312                }
   2313            }
   2314            PrimitiveInstanceKind::Image { data_handle, ref mut compositor_surface_kind, .. } => {
   2315                let image_key = &data_stores.image[data_handle];
   2316                let image_data = &image_key.kind;
   2317 
   2318                // For now, assume that for compositor surface purposes, any RGBA image may be
   2319                // translucent. See the comment in `add_prim` in this source file for more
   2320                // details. We'll leave the `is_opaque` code branches here, but disabled, as
   2321                // in future we will want to support this case correctly.
   2322                let mut is_opaque = false;
   2323 
   2324                if let Some(image_properties) = resource_cache.get_image_properties(image_data.key) {
   2325                    // For an image to be a possible opaque backdrop, it must:
   2326                    // - Have a valid, opaque image descriptor
   2327                    // - Not use tiling (since they can fail to draw)
   2328                    // - Not having any spacing / padding
   2329                    // - Have opaque alpha in the instance (flattened) color
   2330                    if image_properties.descriptor.is_opaque() &&
   2331                       image_properties.tiling.is_none() &&
   2332                       image_data.tile_spacing == LayoutSize::zero() &&
   2333                       image_data.color.a >= 1.0 {
   2334                        backdrop_candidate = Some(BackdropInfo {
   2335                            opaque_rect: pic_coverage_rect,
   2336                            spanning_opaque_color: None,
   2337                            kind: None,
   2338                            backdrop_rect: PictureRect::zero(),
   2339                        });
   2340                    }
   2341 
   2342                    is_opaque = image_properties.descriptor.is_opaque();
   2343                }
   2344 
   2345                let mut promotion_result: Result<CompositorSurfaceKind, SurfacePromotionFailure> = Ok(CompositorSurfaceKind::Blit);
   2346                if image_key.common.flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) {
   2347                    // Only consider promoting Images if all of our YuvImages have been
   2348                    // processed (whether they were promoted or not).
   2349                    if self.yuv_images_remaining > 0 {
   2350                        promotion_result = Err(ImageWaitingOnYuvImage);
   2351                    } else {
   2352                        promotion_result = self.can_promote_to_surface(prim_clip_chain,
   2353                                                          prim_spatial_node_index,
   2354                                                          is_root_tile_cache,
   2355                                                          sub_slice_index,
   2356                                                          CompositorSurfaceKind::Overlay,
   2357                                                          pic_coverage_rect,
   2358                                                          frame_context,
   2359                                                          data_stores,
   2360                                                          clip_store,
   2361                                                          composite_state,
   2362                                                          false);
   2363                    }
   2364 
   2365                    // Native OS compositors (DC and CA, at least) support premultiplied alpha
   2366                    // only. If we have an image that's not pre-multiplied alpha, we can't promote it.
   2367                    if image_data.alpha_type == AlphaType::Alpha {
   2368                        promotion_result = Err(NotPremultipliedAlpha);
   2369                    }
   2370 
   2371                    if let Ok(kind) = promotion_result {
   2372                        promotion_result = self.setup_compositor_surfaces_rgb(
   2373                            sub_slice_index,
   2374                            &mut prim_info,
   2375                            image_key.common.flags,
   2376                            local_prim_rect,
   2377                            prim_clip_chain,
   2378                            prim_spatial_node_index,
   2379                            pic_coverage_rect,
   2380                            frame_context,
   2381                            data_stores,
   2382                            clip_store,
   2383                            ImageDependency {
   2384                                key: image_data.key,
   2385                                generation: resource_cache.get_image_generation(image_data.key),
   2386                            },
   2387                            image_data.key,
   2388                            resource_cache,
   2389                            composite_state,
   2390                            gpu_buffer,
   2391                            image_data.image_rendering,
   2392                            is_opaque,
   2393                            kind,
   2394                        );
   2395                    }
   2396                }
   2397 
   2398                if let Ok(kind) = promotion_result {
   2399                    *compositor_surface_kind = kind;
   2400 
   2401                    if kind == CompositorSurfaceKind::Overlay {
   2402                        profile.inc(profiler::COMPOSITOR_SURFACE_OVERLAYS);
   2403                        return VisibilityState::Culled;
   2404                    }
   2405 
   2406                    assert!(kind == CompositorSurfaceKind::Blit, "Image prims should either be overlays or blits.");
   2407                } else {
   2408                    // In Err case, we handle as a blit, and proceed.
   2409                    self.report_promotion_failure(promotion_result, pic_coverage_rect, false);
   2410                    *compositor_surface_kind = CompositorSurfaceKind::Blit;
   2411                }
   2412 
   2413                if image_key.common.flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) {
   2414                    profile.inc(profiler::COMPOSITOR_SURFACE_BLITS);
   2415                }
   2416 
   2417                prim_info.images.push(ImageDependency {
   2418                    key: image_data.key,
   2419                    generation: resource_cache.get_image_generation(image_data.key),
   2420                });
   2421            }
   2422            PrimitiveInstanceKind::YuvImage { data_handle, ref mut compositor_surface_kind, .. } => {
   2423                let prim_data = &data_stores.yuv_image[data_handle];
   2424 
   2425                let mut promotion_result: Result<CompositorSurfaceKind, SurfacePromotionFailure> = Ok(CompositorSurfaceKind::Blit);
   2426                if prim_data.common.flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) {
   2427                    // Note if this is one of the YuvImages we were considering for
   2428                    // surface promotion. We only care for primitives that were added
   2429                    // to us, indicated by is_root_tile_cache. Those are the only ones
   2430                    // that were added to the TileCacheParams that configured the
   2431                    // current scene.
   2432                    if is_root_tile_cache {
   2433                        self.yuv_images_remaining -= 1;
   2434                    }
   2435 
   2436                    // Should we force the promotion of this surface? We'll force it if promotion
   2437                    // is necessary for correct color display.
   2438                    let force = prim_data.kind.color_depth.bit_depth() > 8;
   2439 
   2440                    let promotion_attempts =
   2441                        [CompositorSurfaceKind::Overlay, CompositorSurfaceKind::Underlay];
   2442 
   2443                    for kind in promotion_attempts {
   2444                        // Since this might be an attempt after an earlier error, clear the flag
   2445                        // so that we are allowed to report another error.
   2446                        promotion_result = self.can_promote_to_surface(
   2447                                                    prim_clip_chain,
   2448                                                    prim_spatial_node_index,
   2449                                                    is_root_tile_cache,
   2450                                                    sub_slice_index,
   2451                                                    kind,
   2452                                                    pic_coverage_rect,
   2453                                                    frame_context,
   2454                                                    data_stores,
   2455                                                    clip_store,
   2456                                                    composite_state,
   2457                                                    force);
   2458                        if promotion_result.is_ok() {
   2459                            break;
   2460                        }
   2461 
   2462                        // We couldn't promote, but did we give up because the slice is marked
   2463                        // atomic? If that was the reason, and the YuvImage is wide color,
   2464                        // failing to promote will flatten the colors and look terrible. Let's
   2465                        // ignore the atomic slice restriction in such a case.
   2466                        if let Err(SliceAtomic) = promotion_result {
   2467                            if prim_data.kind. color_depth != ColorDepth::Color8 {
   2468                                // Let's promote with the attempted kind.
   2469                                promotion_result = Ok(kind);
   2470                                break;
   2471                            }
   2472                        }
   2473                   }
   2474 
   2475                    // TODO(gw): When we support RGBA images for external surfaces, we also
   2476                    //           need to check if opaque (YUV images are implicitly opaque).
   2477 
   2478                    // If this primitive is being promoted to a surface, construct an external
   2479                    // surface descriptor for use later during batching and compositing. We only
   2480                    // add the image keys for this primitive as a dependency if this is _not_
   2481                    // a promoted surface, since we don't want the tiles to invalidate when the
   2482                    // video content changes, if it's a compositor surface!
   2483                    if let Ok(kind) = promotion_result {
   2484                        // Build dependency for each YUV plane, with current image generation for
   2485                        // later detection of when the composited surface has changed.
   2486                        let mut image_dependencies = [ImageDependency::INVALID; 3];
   2487                        for (key, dep) in prim_data.kind.yuv_key.iter().cloned().zip(image_dependencies.iter_mut()) {
   2488                            *dep = ImageDependency {
   2489                                key,
   2490                                generation: resource_cache.get_image_generation(key),
   2491                            }
   2492                        }
   2493 
   2494                        promotion_result = self.setup_compositor_surfaces_yuv(
   2495                            sub_slice_index,
   2496                            &mut prim_info,
   2497                            prim_data.common.flags,
   2498                            local_prim_rect,
   2499                            prim_clip_chain,
   2500                            prim_spatial_node_index,
   2501                            pic_coverage_rect,
   2502                            frame_context,
   2503                            data_stores,
   2504                            clip_store,
   2505                            &image_dependencies,
   2506                            &prim_data.kind.yuv_key,
   2507                            resource_cache,
   2508                            composite_state,
   2509                            gpu_buffer,
   2510                            prim_data.kind.image_rendering,
   2511                            prim_data.kind.color_depth,
   2512                            prim_data.kind.color_space.with_range(prim_data.kind.color_range),
   2513                            prim_data.kind.format,
   2514                            kind,
   2515                        );
   2516                    }
   2517                }
   2518 
   2519                // Store on the YUV primitive instance whether this is a promoted surface.
   2520                // This is used by the batching code to determine whether to draw the
   2521                // image to the content tiles, or just a transparent z-write.
   2522                if let Ok(kind) = promotion_result {
   2523                    *compositor_surface_kind = kind;
   2524                    if kind == CompositorSurfaceKind::Overlay {
   2525                        profile.inc(profiler::COMPOSITOR_SURFACE_OVERLAYS);
   2526                        return VisibilityState::Culled;
   2527                    }
   2528 
   2529                    profile.inc(profiler::COMPOSITOR_SURFACE_UNDERLAYS);
   2530                } else {
   2531                    // In Err case, we handle as a blit, and proceed.
   2532                    self.report_promotion_failure(promotion_result, pic_coverage_rect, false);
   2533                    *compositor_surface_kind = CompositorSurfaceKind::Blit;
   2534                    if prim_data.common.flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) {
   2535                        profile.inc(profiler::COMPOSITOR_SURFACE_BLITS);
   2536                    }
   2537                }
   2538 
   2539                if *compositor_surface_kind == CompositorSurfaceKind::Blit {
   2540                    prim_info.images.extend(
   2541                        prim_data.kind.yuv_key.iter().map(|key| {
   2542                            ImageDependency {
   2543                                key: *key,
   2544                                generation: resource_cache.get_image_generation(*key),
   2545                            }
   2546                        })
   2547                    );
   2548                }
   2549            }
   2550            PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
   2551                let border_data = &data_stores.image_border[data_handle].kind;
   2552                prim_info.images.push(ImageDependency {
   2553                    key: border_data.request.key,
   2554                    generation: resource_cache.get_image_generation(border_data.request.key),
   2555                });
   2556            }
   2557            PrimitiveInstanceKind::LinearGradient { data_handle, .. }
   2558            | PrimitiveInstanceKind::CachedLinearGradient { data_handle, .. } => {
   2559                let gradient_data = &data_stores.linear_grad[data_handle];
   2560                if gradient_data.stops_opacity.is_opaque
   2561                    && gradient_data.tile_spacing == LayoutSize::zero()
   2562                {
   2563                    backdrop_candidate = Some(BackdropInfo {
   2564                        opaque_rect: pic_coverage_rect,
   2565                        spanning_opaque_color: None,
   2566                        kind: None,
   2567                        backdrop_rect: PictureRect::zero(),
   2568                    });
   2569                }
   2570            }
   2571            PrimitiveInstanceKind::ConicGradient { data_handle, .. } => {
   2572                let gradient_data = &data_stores.conic_grad[data_handle];
   2573                if gradient_data.stops_opacity.is_opaque
   2574                    && gradient_data.tile_spacing == LayoutSize::zero()
   2575                {
   2576                    backdrop_candidate = Some(BackdropInfo {
   2577                        opaque_rect: pic_coverage_rect,
   2578                        spanning_opaque_color: None,
   2579                        kind: None,
   2580                        backdrop_rect: PictureRect::zero(),
   2581                    });
   2582                }
   2583            }
   2584            PrimitiveInstanceKind::RadialGradient { data_handle, .. } => {
   2585                let gradient_data = &data_stores.radial_grad[data_handle];
   2586                if gradient_data.stops_opacity.is_opaque
   2587                    && gradient_data.tile_spacing == LayoutSize::zero()
   2588                {
   2589                    backdrop_candidate = Some(BackdropInfo {
   2590                        opaque_rect: pic_coverage_rect,
   2591                        spanning_opaque_color: None,
   2592                        kind: None,
   2593                        backdrop_rect: PictureRect::zero(),
   2594                    });
   2595                }
   2596            }
   2597            PrimitiveInstanceKind::BackdropCapture { .. } => {}
   2598            PrimitiveInstanceKind::BackdropRender { pic_index, .. } => {
   2599                // If the area that the backdrop covers in the space of the surface it draws on
   2600                // is empty, skip any sub-graph processing. This is not just a performance win,
   2601                // it also ensures that we don't do a deferred dirty test that invalidates a tile
   2602                // even if the tile isn't actually dirty, which can cause panics later in the
   2603                // WR pipeline.
   2604                if !pic_coverage_rect.is_empty() {
   2605                    // Mark that we need the sub-graph this render depends on so that
   2606                    // we don't skip it during the prepare pass
   2607                    scratch.required_sub_graphs.insert(pic_index);
   2608 
   2609                    // If this is a sub-graph, register the bounds on any affected tiles
   2610                    // so we know how much to expand the content tile by.
   2611                    let sub_slice = &mut self.sub_slices[sub_slice_index];
   2612 
   2613                    let mut surface_info = Vec::new();
   2614                    for (pic_index, surface_index) in surface_stack.iter().rev() {
   2615                        let pic = &pictures[pic_index.0];
   2616                        surface_info.push((pic.composite_mode.as_ref().unwrap().clone(), *surface_index));
   2617                    }
   2618 
   2619                    for y in p0.y .. p1.y {
   2620                        for x in p0.x .. p1.x {
   2621                            let key = TileOffset::new(x, y);
   2622                            let tile = sub_slice.tiles.get_mut(&key).expect("bug: no tile");
   2623                            tile.cached_surface.sub_graphs.push((pic_coverage_rect, surface_info.clone()));
   2624                        }
   2625                    }
   2626 
   2627                    // For backdrop-filter, we need to check if any of the dirty rects
   2628                    // in tiles that are affected by the filter primitive are dirty.
   2629                    self.deferred_dirty_tests.push(DeferredDirtyTest {
   2630                        tile_rect: TileRect::new(p0, p1),
   2631                        prim_rect: pic_coverage_rect,
   2632                    });
   2633                }
   2634            }
   2635            PrimitiveInstanceKind::LineDecoration { .. } |
   2636            PrimitiveInstanceKind::NormalBorder { .. } |
   2637            PrimitiveInstanceKind::BoxShadow { .. } |
   2638            PrimitiveInstanceKind::TextRun { .. } => {
   2639                // These don't contribute dependencies
   2640            }
   2641        };
   2642 
   2643        // Calculate the screen rect in local space. When we calculate backdrops, we
   2644        // care only that they cover the visible rect (based off the local clip), and
   2645        // don't have any overlapping prims in the visible rect.
   2646        let visible_local_clip_rect = self.local_clip_rect.intersection(&self.screen_rect_in_pic_space).unwrap_or_default();
   2647        if pic_coverage_rect.intersects(&visible_local_clip_rect) {
   2648            self.found_prims_after_backdrop = true;
   2649        }
   2650 
   2651        // If this primitive considers itself a backdrop candidate, apply further
   2652        // checks to see if it matches all conditions to be a backdrop.
   2653        let mut vis_flags = PrimitiveVisibilityFlags::empty();
   2654        let sub_slice = &mut self.sub_slices[sub_slice_index];
   2655        if let Some(mut backdrop_candidate) = backdrop_candidate {
   2656            // Update whether the surface that this primitive exists on
   2657            // can be considered opaque. Any backdrop kind other than
   2658            // a clear primitive (e.g. color, gradient, image) can be
   2659            // considered.
   2660            match backdrop_candidate.kind {
   2661                Some(BackdropKind::Color { .. }) | None => {
   2662                    let surface = &mut surfaces[prim_surface_index.0];
   2663 
   2664                    let is_same_coord_system = frame_context.spatial_tree.is_matching_coord_system(
   2665                        prim_spatial_node_index,
   2666                        surface.surface_spatial_node_index,
   2667                    );
   2668 
   2669                    // To be an opaque backdrop, it must:
   2670                    // - Be the same coordinate system (axis-aligned)
   2671                    // - Have no clip mask
   2672                    // - Have a rect that covers the surface local rect
   2673                    if is_same_coord_system &&
   2674                       !prim_clip_chain.needs_mask &&
   2675                       prim_clip_chain.pic_coverage_rect.contains_box(&surface.unclipped_local_rect)
   2676                    {
   2677                        // Note that we use `prim_clip_chain.pic_clip_rect` here rather
   2678                        // than `backdrop_candidate.opaque_rect`. The former is in the
   2679                        // local space of the surface, the latter is in the local space
   2680                        // of the top level tile-cache.
   2681                        surface.is_opaque = true;
   2682                    }
   2683                }
   2684            }
   2685 
   2686            // Check a number of conditions to see if we can consider this
   2687            // primitive as an opaque backdrop rect. Several of these are conservative
   2688            // checks and could be relaxed in future. However, these checks
   2689            // are quick and capture the common cases of background rects and images.
   2690            // Specifically, we currently require:
   2691            //  - The primitive is on the main picture cache surface.
   2692            //  - Same coord system as picture cache (ensures rects are axis-aligned).
   2693            //  - No clip masks exist.
   2694            let same_coord_system = frame_context.spatial_tree.is_matching_coord_system(
   2695                prim_spatial_node_index,
   2696                self.spatial_node_index,
   2697            );
   2698 
   2699            let is_suitable_backdrop = same_coord_system && on_picture_surface;
   2700 
   2701            if sub_slice_index == 0 &&
   2702               is_suitable_backdrop &&
   2703               sub_slice.compositor_surfaces.is_empty() {
   2704 
   2705                // If the backdrop candidate has a clip-mask, try to extract an opaque inner
   2706                // rect that is safe to use for subpixel rendering
   2707                if prim_clip_chain.needs_mask {
   2708                    backdrop_candidate.opaque_rect = clip_store
   2709                        .get_inner_rect_for_clip_chain(
   2710                            prim_clip_chain,
   2711                            &data_stores.clip,
   2712                            frame_context.spatial_tree,
   2713                        )
   2714                        .unwrap_or(PictureRect::zero());
   2715                }
   2716 
   2717                // We set the backdrop opaque_rect here, indicating the coverage area, which
   2718                // is useful for calculate_subpixel_mode. We will only set the backdrop kind
   2719                // if it covers the visible rect.
   2720                if backdrop_candidate.opaque_rect.contains_box(&self.backdrop.opaque_rect) {
   2721                    self.backdrop.opaque_rect = backdrop_candidate.opaque_rect;
   2722                }
   2723 
   2724                if let Some(kind) = backdrop_candidate.kind {
   2725                    if backdrop_candidate.opaque_rect.contains_box(&visible_local_clip_rect) {
   2726                        self.found_prims_after_backdrop = false;
   2727                        self.backdrop.kind = Some(kind);
   2728                        self.backdrop.backdrop_rect = backdrop_candidate.opaque_rect;
   2729 
   2730                        // If we have a color backdrop that spans the entire local rect, mark
   2731                        // the visibility flags of the primitive so it is skipped during batching
   2732                        // (and also clears any previous primitives). Additionally, update our
   2733                        // background color to match the backdrop color, which will ensure that
   2734                        // our tiles are cleared to this color.
   2735                        let BackdropKind::Color { color } = kind;
   2736                        if backdrop_candidate.opaque_rect.contains_box(&self.local_rect) {
   2737                            vis_flags |= PrimitiveVisibilityFlags::IS_BACKDROP;
   2738                            self.backdrop.spanning_opaque_color = Some(color);
   2739                        }
   2740                    }
   2741                }
   2742            }
   2743        }
   2744 
   2745        // Record any new spatial nodes in the used list.
   2746        for spatial_node_index in &prim_info.spatial_nodes {
   2747            self.spatial_node_comparer.register_used_transform(
   2748                *spatial_node_index,
   2749                self.frame_id,
   2750                frame_context.spatial_tree,
   2751            );
   2752        }
   2753 
   2754        // Normalize the tile coordinates before adding to tile dependencies.
   2755        // For each affected tile, mark any of the primitive dependencies.
   2756        for y in p0.y .. p1.y {
   2757            for x in p0.x .. p1.x {
   2758                // TODO(gw): Convert to 2d array temporarily to avoid hash lookups per-tile?
   2759                let key = TileOffset::new(x, y);
   2760                let tile = sub_slice.tiles.get_mut(&key).expect("bug: no tile");
   2761 
   2762                tile.add_prim_dependency(&prim_info);
   2763            }
   2764        }
   2765 
   2766        VisibilityState::Visible {
   2767            vis_flags,
   2768            sub_slice_index: SubSliceIndex::new(sub_slice_index),
   2769        }
   2770    }
   2771 
   2772    /// Print debug information about this picture cache to a tree printer.
   2773    pub fn print(&self) {
   2774        // TODO(gw): This initial implementation is very basic - just printing
   2775        //           the picture cache state to stdout. In future, we can
   2776        //           make this dump each frame to a file, and produce a report
   2777        //           stating which frames had invalidations. This will allow
   2778        //           diff'ing the invalidation states in a visual tool.
   2779        let mut pt = PrintTree::new("Picture Cache");
   2780 
   2781        pt.new_level(format!("Slice {:?}", self.slice));
   2782 
   2783        pt.add_item(format!("background_color: {:?}", self.background_color));
   2784 
   2785        for (sub_slice_index, sub_slice) in self.sub_slices.iter().enumerate() {
   2786            pt.new_level(format!("SubSlice {:?}", sub_slice_index));
   2787 
   2788            for y in self.tile_bounds_p0.y .. self.tile_bounds_p1.y {
   2789                for x in self.tile_bounds_p0.x .. self.tile_bounds_p1.x {
   2790                    let key = TileOffset::new(x, y);
   2791                    let tile = &sub_slice.tiles[&key];
   2792                    tile.print(&mut pt);
   2793                }
   2794            }
   2795 
   2796            pt.end_level();
   2797        }
   2798 
   2799        pt.end_level();
   2800    }
   2801 
   2802    fn calculate_subpixel_mode(&self) -> SubpixelMode {
   2803        // We can only consider the full opaque cases if there's no underlays
   2804        if self.underlays.is_empty() {
   2805            let has_opaque_bg_color = self.background_color.map_or(false, |c| c.a >= 1.0);
   2806 
   2807            // If the overall tile cache is known opaque, subpixel AA is allowed everywhere
   2808            if has_opaque_bg_color {
   2809                return SubpixelMode::Allow;
   2810            }
   2811 
   2812            // If the opaque backdrop rect covers the entire tile cache surface,
   2813            // we can allow subpixel AA anywhere, skipping the per-text-run tests
   2814            // later on during primitive preparation.
   2815            if self.backdrop.opaque_rect.contains_box(&self.local_rect) {
   2816                return SubpixelMode::Allow;
   2817            }
   2818        }
   2819 
   2820        // If we didn't find any valid opaque backdrop, no subpixel AA allowed
   2821        if self.backdrop.opaque_rect.is_empty() {
   2822            return SubpixelMode::Deny;
   2823        }
   2824 
   2825        // Calculate a prohibited rect where we won't allow subpixel AA.
   2826        // TODO(gw): This is conservative - it will disallow subpixel AA if there
   2827        // are two underlay surfaces with text placed in between them. That's
   2828        // probably unlikely to be an issue in practice, but maybe we should support
   2829        // an array of prohibted rects?
   2830        let prohibited_rect = self
   2831            .underlays
   2832            .iter()
   2833            .fold(
   2834                PictureRect::zero(),
   2835                |acc, underlay| {
   2836                    acc.union(&underlay.local_rect)
   2837                }
   2838            );
   2839 
   2840        // If none of the simple cases above match, we need test where we can support subpixel AA.
   2841        // TODO(gw): In future, it may make sense to have > 1 inclusion rect,
   2842        //           but this handles the common cases.
   2843        // TODO(gw): If a text run gets animated such that it's moving in a way that is
   2844        //           sometimes intersecting with the video rect, this can result in subpixel
   2845        //           AA flicking on/off for that text run. It's probably very rare, but
   2846        //           something we should handle in future.
   2847        SubpixelMode::Conditional {
   2848            allowed_rect: self.backdrop.opaque_rect,
   2849            prohibited_rect,
   2850        }
   2851    }
   2852 
   2853    /// Apply any updates after prim dependency updates. This applies
   2854    /// any late tile invalidations, and sets up the dirty rect and
   2855    /// set of tile blits.
   2856    pub fn post_update(
   2857        &mut self,
   2858        frame_context: &FrameVisibilityContext,
   2859        composite_state: &mut CompositeState,
   2860        resource_cache: &mut ResourceCache,
   2861    ) {
   2862        assert!(self.current_surface_traversal_depth == 0);
   2863 
   2864        // TODO: Switch from the root node ot raster space.
   2865        let visibility_node = frame_context.spatial_tree.root_reference_frame_index();
   2866 
   2867        self.dirty_region.reset(visibility_node, self.spatial_node_index);
   2868        self.subpixel_mode = self.calculate_subpixel_mode();
   2869 
   2870        self.transform_index = composite_state.register_transform(
   2871            self.local_to_raster,
   2872            // TODO(gw): Once we support scaling of picture cache tiles during compositing,
   2873            //           that transform gets plugged in here!
   2874            self.raster_to_device,
   2875        );
   2876 
   2877        let map_pic_to_world = SpaceMapper::new_with_target(
   2878            frame_context.root_spatial_node_index,
   2879            self.spatial_node_index,
   2880            frame_context.global_screen_world_rect,
   2881            frame_context.spatial_tree,
   2882        );
   2883 
   2884        // A simple GC of the native external surface cache, to remove and free any
   2885        // surfaces that were not referenced during the update_prim_dependencies pass.
   2886        self.external_native_surface_cache.retain(|_, surface| {
   2887            if !surface.used_this_frame {
   2888                // If we removed an external surface, we need to mark the dirty rects as
   2889                // invalid so a full composite occurs on the next frame.
   2890                composite_state.dirty_rects_are_valid = false;
   2891 
   2892                resource_cache.destroy_compositor_surface(surface.native_surface_id);
   2893            }
   2894 
   2895            surface.used_this_frame
   2896        });
   2897 
   2898        let pic_to_world_mapper = SpaceMapper::new_with_target(
   2899            frame_context.root_spatial_node_index,
   2900            self.spatial_node_index,
   2901            frame_context.global_screen_world_rect,
   2902            frame_context.spatial_tree,
   2903        );
   2904 
   2905        let ctx = TileUpdateDirtyContext {
   2906            pic_to_world_mapper,
   2907            global_device_pixel_scale: frame_context.global_device_pixel_scale,
   2908            opacity_bindings: &self.opacity_bindings,
   2909            color_bindings: &self.color_bindings,
   2910            local_rect: self.local_rect,
   2911            invalidate_all: self.invalidate_all_tiles,
   2912        };
   2913 
   2914        let mut state = TileUpdateDirtyState {
   2915            resource_cache,
   2916            composite_state,
   2917            compare_cache: &mut self.compare_cache,
   2918            spatial_node_comparer: &mut self.spatial_node_comparer,
   2919        };
   2920 
   2921        // Step through each tile and invalidate if the dependencies have changed. Determine
   2922        // the current opacity setting and whether it's changed.
   2923        for sub_slice in &mut self.sub_slices {
   2924            for tile in sub_slice.tiles.values_mut() {
   2925                tile.update_dirty_and_valid_rects(&ctx, &mut state, frame_context);
   2926            }
   2927        }
   2928 
   2929        // Process any deferred dirty checks
   2930        for sub_slice in &mut self.sub_slices {
   2931            for dirty_test in self.deferred_dirty_tests.drain(..) {
   2932                // Calculate the total dirty rect from all tiles that this primitive affects
   2933                let mut total_dirty_rect = PictureRect::zero();
   2934 
   2935                for y in dirty_test.tile_rect.min.y .. dirty_test.tile_rect.max.y {
   2936                    for x in dirty_test.tile_rect.min.x .. dirty_test.tile_rect.max.x {
   2937                        let key = TileOffset::new(x, y);
   2938                        let tile = sub_slice.tiles.get_mut(&key).expect("bug: no tile");
   2939                        total_dirty_rect = total_dirty_rect.union(&tile.cached_surface.local_dirty_rect);
   2940                    }
   2941                }
   2942 
   2943                // If that dirty rect intersects with the local rect of the primitive
   2944                // being checked, invalidate that region in all of the affected tiles.
   2945                // TODO(gw): This is somewhat conservative, we could be more clever
   2946                //           here and avoid invalidating every tile when this changes.
   2947                //           We could also store the dirty rect only when the prim
   2948                //           is encountered, so that we don't invalidate if something
   2949                //           *after* the query in the rendering order affects invalidation.
   2950                if total_dirty_rect.intersects(&dirty_test.prim_rect) {
   2951                    for y in dirty_test.tile_rect.min.y .. dirty_test.tile_rect.max.y {
   2952                        for x in dirty_test.tile_rect.min.x .. dirty_test.tile_rect.max.x {
   2953                            let key = TileOffset::new(x, y);
   2954                            let tile = sub_slice.tiles.get_mut(&key).expect("bug: no tile");
   2955                            tile.invalidate(
   2956                                Some(dirty_test.prim_rect),
   2957                                InvalidationReason::SurfaceContentChanged,
   2958                            );
   2959                        }
   2960                    }
   2961                }
   2962            }
   2963        }
   2964 
   2965        let mut ctx = TilePostUpdateContext {
   2966            local_clip_rect: self.local_clip_rect,
   2967            backdrop: None,
   2968            current_tile_size: self.current_tile_size,
   2969            z_id: ZBufferId::invalid(),
   2970            underlays: &self.underlays,
   2971        };
   2972 
   2973        let mut state = TilePostUpdateState {
   2974            resource_cache,
   2975            composite_state,
   2976        };
   2977 
   2978        for (i, sub_slice) in self.sub_slices.iter_mut().enumerate().rev() {
   2979            // The backdrop is only relevant for the first sub-slice
   2980            if i == 0 {
   2981                ctx.backdrop = Some(self.backdrop);
   2982            }
   2983 
   2984            for compositor_surface in sub_slice.compositor_surfaces.iter_mut().rev() {
   2985                compositor_surface.descriptor.z_id = state.composite_state.z_generator.next();
   2986            }
   2987 
   2988            ctx.z_id = state.composite_state.z_generator.next();
   2989 
   2990            for tile in sub_slice.tiles.values_mut() {
   2991                tile.post_update(&ctx, &mut state, frame_context);
   2992            }
   2993        }
   2994 
   2995        // Assign z-order for each underlay
   2996        for underlay in self.underlays.iter_mut().rev() {
   2997            underlay.z_id = state.composite_state.z_generator.next();
   2998        }
   2999 
   3000        // Register any opaque external compositor surfaces as potential occluders. This
   3001        // is especially useful when viewing video in full-screen mode, as it is
   3002        // able to occlude every background tile (avoiding allocation, rasterizion
   3003        // and compositing).
   3004 
   3005        // Register any underlays as occluders where possible
   3006        for underlay in &self.underlays {
   3007            if let Some(world_surface_rect) = underlay.get_occluder_rect(
   3008                &self.local_clip_rect,
   3009                &map_pic_to_world,
   3010            ) {
   3011                composite_state.register_occluder(
   3012                    underlay.z_id,
   3013                    world_surface_rect,
   3014                    self.compositor_clip,
   3015                );
   3016            }
   3017        }
   3018 
   3019        for sub_slice in &self.sub_slices {
   3020            for compositor_surface in &sub_slice.compositor_surfaces {
   3021                if compositor_surface.is_opaque {
   3022                    if let Some(world_surface_rect) = compositor_surface.descriptor.get_occluder_rect(
   3023                        &self.local_clip_rect,
   3024                        &map_pic_to_world,
   3025                    ) {
   3026                        composite_state.register_occluder(
   3027                            compositor_surface.descriptor.z_id,
   3028                            world_surface_rect,
   3029                            self.compositor_clip,
   3030                        );
   3031                    }
   3032                }
   3033            }
   3034        }
   3035 
   3036        // Register the opaque region of this tile cache as an occluder, which
   3037        // is used later in the frame to occlude other tiles.
   3038        if !self.backdrop.opaque_rect.is_empty() {
   3039            let z_id_backdrop = composite_state.z_generator.next();
   3040 
   3041            let backdrop_rect = self.backdrop.opaque_rect
   3042                .intersection(&self.local_rect)
   3043                .and_then(|r| {
   3044                    r.intersection(&self.local_clip_rect)
   3045                });
   3046 
   3047            if let Some(backdrop_rect) = backdrop_rect {
   3048                let world_backdrop_rect = map_pic_to_world
   3049                    .map(&backdrop_rect)
   3050                    .expect("bug: unable to map backdrop to world space");
   3051 
   3052                // Since we register the entire backdrop rect, use the opaque z-id for the
   3053                // picture cache slice.
   3054                composite_state.register_occluder(
   3055                    z_id_backdrop,
   3056                    world_backdrop_rect,
   3057                    self.compositor_clip,
   3058                );
   3059            }
   3060        }
   3061    }
   3062 }
   3063 
   3064 
   3065 /// A SubSlice represents a potentially overlapping set of tiles within a picture cache. Most
   3066 /// picture cache instances will have only a single sub-slice. The exception to this is when
   3067 /// a picture cache has compositor surfaces, in which case sub slices are used to interleave
   3068 /// content under or order the compositor surface(s).
   3069 pub struct SubSlice {
   3070    /// Hash of tiles present in this picture.
   3071    pub tiles: FastHashMap<TileOffset, Box<Tile>>,
   3072    /// The allocated compositor surfaces for this picture cache. May be None if
   3073    /// not using native compositor, or if the surface was destroyed and needs
   3074    /// to be reallocated next time this surface contains valid tiles.
   3075    pub native_surface: Option<NativeSurface>,
   3076    /// List of compositor surfaces that have been promoted from primitives
   3077    /// in this tile cache.
   3078    pub compositor_surfaces: Vec<CompositorSurface>,
   3079    /// List of visible tiles to be composited for this subslice
   3080    pub composite_tiles: Vec<CompositeTile>,
   3081    /// Compositor descriptors of visible, opaque tiles (used by composite_state.push_surface)
   3082    pub opaque_tile_descriptors: Vec<CompositeTileDescriptor>,
   3083    /// Compositor descriptors of visible, alpha tiles (used by composite_state.push_surface)
   3084    pub alpha_tile_descriptors: Vec<CompositeTileDescriptor>,
   3085 }
   3086 
   3087 impl SubSlice {
   3088    /// Construct a new sub-slice
   3089    fn new() -> Self {
   3090        SubSlice {
   3091            tiles: FastHashMap::default(),
   3092            native_surface: None,
   3093            compositor_surfaces: Vec::new(),
   3094            composite_tiles: Vec::new(),
   3095            opaque_tile_descriptors: Vec::new(),
   3096            alpha_tile_descriptors: Vec::new(),
   3097        }
   3098    }
   3099 
   3100    /// Reset the list of compositor surfaces that follow this sub-slice.
   3101    /// Built per-frame, since APZ may change whether an image is suitable to be a compositor surface.
   3102    fn reset(&mut self) {
   3103        self.compositor_surfaces.clear();
   3104        self.composite_tiles.clear();
   3105        self.opaque_tile_descriptors.clear();
   3106        self.alpha_tile_descriptors.clear();
   3107    }
   3108 
   3109    /// Resize the tile grid to match a new tile bounds
   3110    fn resize(&mut self, new_tile_rect: TileRect) -> FastHashMap<TileOffset, Box<Tile>> {
   3111        let mut old_tiles = mem::replace(&mut self.tiles, FastHashMap::default());
   3112        self.tiles.reserve(new_tile_rect.area() as usize);
   3113 
   3114        for y in new_tile_rect.min.y .. new_tile_rect.max.y {
   3115            for x in new_tile_rect.min.x .. new_tile_rect.max.x {
   3116                let key = TileOffset::new(x, y);
   3117                let tile = old_tiles
   3118                    .remove(&key)
   3119                    .unwrap_or_else(|| {
   3120                        Box::new(Tile::new(key))
   3121                    });
   3122                self.tiles.insert(key, tile);
   3123            }
   3124        }
   3125 
   3126        old_tiles
   3127    }
   3128 }
   3129 
   3130 #[derive(Clone, Copy, Debug)]
   3131 enum SurfacePromotionFailure {
   3132    ImageWaitingOnYuvImage,
   3133    NotPremultipliedAlpha,
   3134    OverlaySurfaceLimit,
   3135    OverlayNeedsMask,
   3136    UnderlayAlphaBackdrop,
   3137    UnderlaySurfaceLimit,
   3138    UnderlayIntersectsOverlay,
   3139    UnderlayLowQualityZoom,
   3140    NotRootTileCache,
   3141    ComplexTransform,
   3142    SliceAtomic,
   3143    SizeTooLarge,
   3144 }
   3145 
   3146 impl Display for SurfacePromotionFailure {
   3147    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
   3148        write!(
   3149            f,
   3150            "{}",
   3151            match *self {
   3152                SurfacePromotionFailure::ImageWaitingOnYuvImage => "Image prim waiting for all YuvImage prims to be considered for promotion",
   3153                SurfacePromotionFailure::NotPremultipliedAlpha => "does not use premultiplied alpha",
   3154                SurfacePromotionFailure::OverlaySurfaceLimit => "hit the overlay surface limit",
   3155                SurfacePromotionFailure::OverlayNeedsMask => "overlay not allowed for prim with mask",
   3156                SurfacePromotionFailure::UnderlayAlphaBackdrop => "underlay requires an opaque backdrop",
   3157                SurfacePromotionFailure::UnderlaySurfaceLimit => "hit the underlay surface limit",
   3158                SurfacePromotionFailure::UnderlayIntersectsOverlay => "underlay intersects already-promoted overlay",
   3159                SurfacePromotionFailure::UnderlayLowQualityZoom => "underlay not allowed during low-quality pinch zoom",
   3160                SurfacePromotionFailure::NotRootTileCache => "is not on a root tile cache",
   3161                SurfacePromotionFailure::ComplexTransform => "has a complex transform",
   3162                SurfacePromotionFailure::SliceAtomic => "slice is atomic",
   3163                SurfacePromotionFailure::SizeTooLarge => "surface is too large for compositor",
   3164            }.to_owned()
   3165        )
   3166    }
   3167 }
   3168 
   3169 // Immutable context passed to picture cache tiles during pre_update
   3170 struct TilePreUpdateContext {
   3171    /// Maps from picture cache coords -> world space coords.
   3172    pic_to_world_mapper: SpaceMapper<PicturePixel, WorldPixel>,
   3173 
   3174    /// The optional background color of the picture cache instance
   3175    background_color: Option<ColorF>,
   3176 
   3177    /// The visible part of the screen in world coords.
   3178    global_screen_world_rect: WorldRect,
   3179 
   3180    /// Current size of tiles in picture units.
   3181    tile_size: PictureSize,
   3182 
   3183    /// The current frame id for this picture cache
   3184    frame_id: FrameId,
   3185 }
   3186 
   3187 // Immutable context passed to picture cache tiles during post_update
   3188 struct TilePostUpdateContext<'a> {
   3189    /// The local clip rect (in picture space) of the entire picture cache
   3190    local_clip_rect: PictureRect,
   3191 
   3192    /// The calculated backdrop information for this cache instance.
   3193    backdrop: Option<BackdropInfo>,
   3194 
   3195    /// Current size in device pixels of tiles for this cache
   3196    current_tile_size: DeviceIntSize,
   3197 
   3198    /// Pre-allocated z-id to assign to tiles during post_update.
   3199    z_id: ZBufferId,
   3200 
   3201    /// The list of compositor underlays for this picture cache
   3202    underlays: &'a [ExternalSurfaceDescriptor],
   3203 }
   3204 
   3205 // Mutable state passed to picture cache tiles during post_update
   3206 struct TilePostUpdateState<'a> {
   3207    /// Allow access to the texture cache for requesting tiles
   3208    resource_cache: &'a mut ResourceCache,
   3209 
   3210    /// Current configuration and setup for compositing all the picture cache tiles in renderer.
   3211    composite_state: &'a mut CompositeState,
   3212 }