tor-browser

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

cached_surface.rs (17969B)


      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 use api::ColorF;
      6 use api::PropertyBindingId;
      7 use api::units::*;
      8 use smallvec::SmallVec;
      9 use crate::ItemUid;
     10 use crate::composite::CompositeState;
     11 use crate::internal_types::{FastHashMap, FrameId};
     12 use crate::invalidation::compare::ImageDependency;
     13 use crate::invalidation::compare::{ColorBinding, OpacityBinding, OpacityBindingInfo, PrimitiveComparisonKey};
     14 use crate::invalidation::compare::{SpatialNodeComparer, PrimitiveComparer, PrimitiveDependency, ColorBindingInfo};
     15 use crate::invalidation::{InvalidationReason, PrimitiveCompareResult, quadtree::TileNode};
     16 use crate::picture::{PictureCompositeMode, SurfaceIndex, clampf};
     17 use crate::print_tree::PrintTreePrinter;
     18 use crate::resource_cache::ResourceCache;
     19 use crate::space::SpaceMapper;
     20 use crate::spatial_tree::SpatialNodeIndex;
     21 use crate::visibility::FrameVisibilityContext;
     22 use peek_poke::poke_into_vec;
     23 use std::mem;
     24 
     25 pub struct CachedSurface {
     26    pub current_descriptor: CachedSurfaceDescriptor,
     27    pub prev_descriptor: CachedSurfaceDescriptor,
     28    pub is_valid: bool,
     29    pub local_valid_rect: PictureBox2D,
     30    pub local_dirty_rect: PictureRect,
     31    pub local_rect: PictureRect,
     32    pub root: TileNode,
     33    pub background_color: Option<ColorF>,
     34    pub invalidation_reason: Option<InvalidationReason>,
     35    pub sub_graphs: Vec<(PictureRect, Vec<(PictureCompositeMode, SurfaceIndex)>)>,
     36 }
     37 
     38 impl CachedSurface {
     39    pub fn new() -> Self {
     40        CachedSurface {
     41            current_descriptor: CachedSurfaceDescriptor::new(),
     42            prev_descriptor: CachedSurfaceDescriptor::new(),
     43            is_valid: false,
     44            local_valid_rect: PictureBox2D::zero(),
     45            local_dirty_rect: PictureRect::zero(),
     46            local_rect: PictureRect::zero(),
     47            root: TileNode::new_leaf(Vec::new()),
     48            background_color: None,
     49            invalidation_reason: None,
     50            sub_graphs: Vec::new(),
     51        }
     52    }
     53 
     54    pub fn print(&self, pt: &mut dyn PrintTreePrinter) {
     55        pt.add_item(format!("background_color: {:?}", self.background_color));
     56        pt.add_item(format!("invalidation_reason: {:?}", self.invalidation_reason));
     57        self.current_descriptor.print(pt);
     58    }
     59 
     60    /// Setup state before primitive dependency calculation.
     61    pub fn pre_update(
     62        &mut self,
     63        background_color: Option<ColorF>,
     64        local_tile_rect: PictureRect,
     65        frame_id: FrameId,
     66        is_visible: bool,
     67    ) {
     68        // TODO(gw): This is a hack / fix for Box2D::union in euclid not working with
     69        //           zero sized rect accumulation. Once that lands, we'll revert this
     70        //           to be zero.
     71        self.local_valid_rect = PictureBox2D::new(
     72            PicturePoint::new( 1.0e32,  1.0e32),
     73            PicturePoint::new(-1.0e32, -1.0e32),
     74        );
     75        self.invalidation_reason  = None;
     76        self.sub_graphs.clear();
     77 
     78        // If the tile isn't visible, early exit, skipping the normal set up to
     79        // validate dependencies. Instead, we will only compare the current tile
     80        // dependencies the next time it comes into view.
     81        if !is_visible {
     82            return;
     83        }
     84 
     85        if background_color != self.background_color {
     86            self.invalidate(None, InvalidationReason::BackgroundColor);
     87            self.background_color = background_color;
     88        }
     89 
     90        // Clear any dependencies so that when we rebuild them we
     91        // can compare if the tile has the same content.
     92        mem::swap(
     93            &mut self.current_descriptor,
     94            &mut self.prev_descriptor,
     95        );
     96        self.current_descriptor.clear();
     97        self.root.clear(local_tile_rect);
     98 
     99        self.current_descriptor.last_updated_frame_id = frame_id;
    100    }
    101 
    102    pub fn add_prim_dependency(
    103        &mut self,
    104        info: &PrimitiveDependencyInfo,
    105        local_tile_rect: PictureRect,
    106    ) {
    107        // Incorporate the bounding rect of the primitive in the local valid rect
    108        // for this tile. This is used to minimize the size of the scissor rect
    109        // during rasterization and the draw rect during composition of partial tiles.
    110        self.local_valid_rect = self.local_valid_rect.union(&info.prim_clip_box);
    111 
    112        // TODO(gw): The prim_clip_rect can be impacted by the clip rect of the display port,
    113        //           which can cause invalidations when a new display list with changed
    114        //           display port is received. To work around this, clamp the prim clip rect
    115        //           to the tile boundaries - if the clip hasn't affected the tile, then the
    116        //           changed clip can't affect the content of the primitive on this tile.
    117        //           In future, we could consider supplying the display port clip from Gecko
    118        //           in a different way (e.g. as a scroll frame clip) which still provides
    119        //           the desired clip for checkerboarding, but doesn't require this extra
    120        //           work below.
    121 
    122        // TODO(gw): This is a hot part of the code - we could probably optimize further by:
    123        //           - Using min/max instead of clamps below (if we guarantee the rects are well formed)
    124 
    125        let pmin = local_tile_rect.min;
    126        let pmax = local_tile_rect.max;
    127 
    128        let prim_clip_box = PictureBox2D::new(
    129            PicturePoint::new(
    130                clampf(info.prim_clip_box.min.x, pmin.x, pmax.x),
    131                clampf(info.prim_clip_box.min.y, pmin.y, pmax.y),
    132            ),
    133            PicturePoint::new(
    134                clampf(info.prim_clip_box.max.x, pmin.x, pmax.x),
    135                clampf(info.prim_clip_box.max.y, pmin.y, pmax.y),
    136            ),
    137        );
    138 
    139        // Update the tile descriptor, used for tile comparison during scene swaps.
    140        let prim_index = PrimitiveDependencyIndex(self.current_descriptor.prims.len() as u32);
    141 
    142        // Encode the deps for this primitive in the `dep_data` byte buffer.
    143        let dep_offset = self.current_descriptor.dep_data.len() as u32;
    144        let mut dep_count = 0;
    145 
    146        for clip in &info.clips {
    147            dep_count += 1;
    148            poke_into_vec(
    149                &PrimitiveDependency::Clip {
    150                    clip: *clip,
    151                },
    152                &mut self.current_descriptor.dep_data,
    153            );
    154        }
    155 
    156        for spatial_node_index in &info.spatial_nodes {
    157            dep_count += 1;
    158            poke_into_vec(
    159                &PrimitiveDependency::SpatialNode {
    160                    index: *spatial_node_index,
    161                },
    162                &mut self.current_descriptor.dep_data,
    163            );
    164        }
    165 
    166        for image in &info.images {
    167            dep_count += 1;
    168            poke_into_vec(
    169                &PrimitiveDependency::Image {
    170                    image: *image,
    171                },
    172                &mut self.current_descriptor.dep_data,
    173            );
    174        }
    175 
    176        for binding in &info.opacity_bindings {
    177            dep_count += 1;
    178            poke_into_vec(
    179                &PrimitiveDependency::OpacityBinding {
    180                    binding: *binding,
    181                },
    182                &mut self.current_descriptor.dep_data,
    183            );
    184        }
    185 
    186        if let Some(ref binding) = info.color_binding {
    187            dep_count += 1;
    188            poke_into_vec(
    189                &PrimitiveDependency::ColorBinding {
    190                    binding: *binding,
    191                },
    192                &mut self.current_descriptor.dep_data,
    193            );
    194        }
    195 
    196        self.current_descriptor.prims.push(PrimitiveDescriptor {
    197            prim_uid: info.prim_uid,
    198            prim_clip_box,
    199            dep_offset,
    200            dep_count,
    201        });
    202 
    203        // Add this primitive to the dirty rect quadtree.
    204        self.root.add_prim(prim_index, &info.prim_clip_box);
    205    }
    206 
    207    /// Check if the content of the previous and current tile descriptors match
    208    fn update_dirty_rects(
    209        &mut self,
    210        ctx: &TileUpdateDirtyContext,
    211        state: &mut TileUpdateDirtyState,
    212        invalidation_reason: &mut Option<InvalidationReason>,
    213        frame_context: &FrameVisibilityContext,
    214    ) -> PictureRect {
    215        let mut prim_comparer = PrimitiveComparer::new(
    216            &self.prev_descriptor,
    217            &self.current_descriptor,
    218            state.resource_cache,
    219            state.spatial_node_comparer,
    220            ctx.opacity_bindings,
    221            ctx.color_bindings,
    222        );
    223 
    224        let mut dirty_rect = PictureBox2D::zero();
    225        self.root.update_dirty_rects(
    226            &self.prev_descriptor.prims,
    227            &self.current_descriptor.prims,
    228            &mut prim_comparer,
    229            &mut dirty_rect,
    230            state.compare_cache,
    231            invalidation_reason,
    232            frame_context,
    233        );
    234 
    235        dirty_rect
    236    }
    237 
    238    /// Invalidate a tile based on change in content. This
    239    /// must be called even if the tile is not currently
    240    /// visible on screen. We might be able to improve this
    241    /// later by changing how ComparableVec is used.
    242    pub fn update_content_validity(
    243        &mut self,
    244        ctx: &TileUpdateDirtyContext,
    245        state: &mut TileUpdateDirtyState,
    246        frame_context: &FrameVisibilityContext,
    247    ) {
    248        // Check if the contents of the primitives, clips, and
    249        // other dependencies are the same.
    250        state.compare_cache.clear();
    251        let mut invalidation_reason = None;
    252        let dirty_rect = self.update_dirty_rects(
    253            ctx,
    254            state,
    255            &mut invalidation_reason,
    256            frame_context,
    257        );
    258 
    259        if !dirty_rect.is_empty() {
    260            self.invalidate(
    261                Some(dirty_rect),
    262                invalidation_reason.expect("bug: no invalidation_reason")
    263            );
    264        }
    265        if ctx.invalidate_all {
    266            self.invalidate(None, InvalidationReason::ScaleChanged);
    267        }
    268        // TODO(gw): We can avoid invalidating the whole tile in some cases here,
    269        //           but it should be a fairly rare invalidation case.
    270        if self.current_descriptor.local_valid_rect != self.prev_descriptor.local_valid_rect {
    271            self.invalidate(None, InvalidationReason::ValidRectChanged);
    272            state.composite_state.dirty_rects_are_valid = false;
    273        }
    274    }
    275 
    276    /// Invalidate this tile. If `invalidation_rect` is None, the entire
    277    /// tile is invalidated.
    278    pub fn invalidate(
    279        &mut self,
    280        invalidation_rect: Option<PictureRect>,
    281        reason: InvalidationReason,
    282    ) {
    283        self.is_valid = false;
    284 
    285        match invalidation_rect {
    286            Some(rect) => {
    287                self.local_dirty_rect = self.local_dirty_rect.union(&rect);
    288            }
    289            None => {
    290                self.local_dirty_rect = self.local_rect;
    291            }
    292        }
    293 
    294        if self.invalidation_reason.is_none() {
    295            self.invalidation_reason = Some(reason);
    296        }
    297    }
    298 }
    299 
    300 // Immutable context passed to picture cache tiles during update_dirty_and_valid_rects
    301 pub struct TileUpdateDirtyContext<'a> {
    302    /// Maps from picture cache coords -> world space coords.
    303    pub pic_to_world_mapper: SpaceMapper<PicturePixel, WorldPixel>,
    304 
    305    /// Global scale factor from world -> device pixels.
    306    pub global_device_pixel_scale: DevicePixelScale,
    307 
    308    /// Information about opacity bindings from the picture cache.
    309    pub opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>,
    310 
    311    /// Information about color bindings from the picture cache.
    312    pub color_bindings: &'a FastHashMap<PropertyBindingId, ColorBindingInfo>,
    313 
    314    /// The local rect of the overall picture cache
    315    pub local_rect: PictureRect,
    316 
    317    /// If true, the scale factor of the root transform for this picture
    318    /// cache changed, so we need to invalidate the tile and re-render.
    319    pub invalidate_all: bool,
    320 }
    321 
    322 // Mutable state passed to picture cache tiles during update_dirty_and_valid_rects
    323 pub struct TileUpdateDirtyState<'a> {
    324    /// Allow access to the texture cache for requesting tiles
    325    pub resource_cache: &'a mut ResourceCache,
    326 
    327    /// Current configuration and setup for compositing all the picture cache tiles in renderer.
    328    pub composite_state: &'a mut CompositeState,
    329 
    330    /// A cache of comparison results to avoid re-computation during invalidation.
    331    pub compare_cache: &'a mut FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>,
    332 
    333    /// Information about transform node differences from last frame.
    334    pub spatial_node_comparer: &'a mut SpatialNodeComparer,
    335 }
    336 
    337 /// Information about the dependencies of a single primitive instance.
    338 pub struct PrimitiveDependencyInfo {
    339    /// Unique content identifier of the primitive.
    340    pub prim_uid: ItemUid,
    341 
    342    /// The (conservative) clipped area in picture space this primitive occupies.
    343    pub prim_clip_box: PictureBox2D,
    344 
    345    /// Image keys this primitive depends on.
    346    pub images: SmallVec<[ImageDependency; 8]>,
    347 
    348    /// Opacity bindings this primitive depends on.
    349    pub opacity_bindings: SmallVec<[OpacityBinding; 4]>,
    350 
    351    /// Color binding this primitive depends on.
    352    pub color_binding: Option<ColorBinding>,
    353 
    354    /// Clips that this primitive depends on.
    355    pub clips: SmallVec<[ItemUid; 8]>,
    356 
    357    /// Spatial nodes references by the clip dependencies of this primitive.
    358    pub spatial_nodes: SmallVec<[SpatialNodeIndex; 4]>,
    359 }
    360 
    361 impl PrimitiveDependencyInfo {
    362    pub fn new(
    363        prim_uid: crate::intern::ItemUid,
    364        prim_clip_box: PictureBox2D,
    365    ) -> Self {
    366        PrimitiveDependencyInfo {
    367            prim_uid,
    368            prim_clip_box,
    369            images: smallvec::SmallVec::new(),
    370            opacity_bindings: smallvec::SmallVec::new(),
    371            color_binding: None,
    372            clips: smallvec::SmallVec::new(),
    373            spatial_nodes: smallvec::SmallVec::new(),
    374        }
    375    }
    376 }
    377 
    378 /// Information about a primitive that is a dependency for a cached surface.
    379 #[derive(Debug, Clone)]
    380 #[cfg_attr(feature = "capture", derive(Serialize))]
    381 #[cfg_attr(feature = "replay", derive(Deserialize))]
    382 pub struct PrimitiveDescriptor {
    383    pub prim_uid: ItemUid,
    384    pub prim_clip_box: PictureBox2D,
    385    // TODO(gw): These two fields could be packed as a u24/u8
    386    pub dep_offset: u32,
    387    pub dep_count: u32,
    388 }
    389 
    390 impl PartialEq for PrimitiveDescriptor {
    391    fn eq(&self, other: &Self) -> bool {
    392        const EPSILON: f32 = 0.001;
    393 
    394        if self.prim_uid != other.prim_uid {
    395            return false;
    396        }
    397 
    398        use euclid::approxeq::ApproxEq;
    399        if !self.prim_clip_box.min.x.approx_eq_eps(&other.prim_clip_box.min.x, &EPSILON) {
    400            return false;
    401        }
    402        if !self.prim_clip_box.min.y.approx_eq_eps(&other.prim_clip_box.min.y, &EPSILON) {
    403            return false;
    404        }
    405        if !self.prim_clip_box.max.x.approx_eq_eps(&other.prim_clip_box.max.x, &EPSILON) {
    406            return false;
    407        }
    408        if !self.prim_clip_box.max.y.approx_eq_eps(&other.prim_clip_box.max.y, &EPSILON) {
    409            return false;
    410        }
    411 
    412        if self.dep_count != other.dep_count {
    413            return false;
    414        }
    415 
    416        true
    417    }
    418 }
    419 
    420 impl PartialEq<PrimitiveDescriptor> for (&ItemUid, &PictureBox2D) {
    421    fn eq(&self, other: &PrimitiveDescriptor) -> bool {
    422        self.0 == &other.prim_uid && self.1 == &other.prim_clip_box
    423    }
    424 }
    425 
    426 /// An index into the prims array in a TileDescriptor.
    427 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
    428 #[cfg_attr(feature = "capture", derive(Serialize))]
    429 #[cfg_attr(feature = "replay", derive(Deserialize))]
    430 pub struct PrimitiveDependencyIndex(pub u32);
    431 
    432 /// Uniquely describes the content of this cached surface, in a way that can be
    433 /// (reasonably) efficiently hashed and compared.
    434 #[cfg_attr(any(feature="capture",feature="replay"), derive(Clone))]
    435 #[cfg_attr(feature = "capture", derive(Serialize))]
    436 #[cfg_attr(feature = "replay", derive(Deserialize))]
    437 pub struct CachedSurfaceDescriptor {
    438    /// List of primitive instance unique identifiers. The uid is guaranteed
    439    /// to uniquely describe the content of the primitive template, while
    440    /// the other parameters describe the clip chain and instance params.
    441    pub prims: Vec<PrimitiveDescriptor>,
    442 
    443    /// Picture space rect that contains valid pixels region of this tile.
    444    pub local_valid_rect: PictureRect,
    445 
    446    /// The last frame this tile had its dependencies updated (dependency updating is
    447    /// skipped if a tile is off-screen).
    448    pub last_updated_frame_id: FrameId,
    449 
    450    /// Packed per-prim dependency information
    451    pub dep_data: Vec<u8>,
    452 }
    453 
    454 impl CachedSurfaceDescriptor {
    455    pub fn new() -> Self {
    456        CachedSurfaceDescriptor {
    457            local_valid_rect: PictureRect::zero(),
    458            dep_data: Vec::new(),
    459            prims: Vec::new(),
    460            last_updated_frame_id: FrameId::INVALID,
    461        }
    462    }
    463 
    464    /// Print debug information about this tile descriptor to a tree printer.
    465    pub fn print(&self, pt: &mut dyn crate::print_tree::PrintTreePrinter) {
    466        pt.new_level("current_descriptor".to_string());
    467 
    468        pt.new_level("prims".to_string());
    469        for prim in &self.prims {
    470            pt.new_level(format!("prim uid={}", prim.prim_uid.get_uid()));
    471            pt.add_item(format!("clip: p0={},{} p1={},{}",
    472                prim.prim_clip_box.min.x,
    473                prim.prim_clip_box.min.y,
    474                prim.prim_clip_box.max.x,
    475                prim.prim_clip_box.max.y,
    476            ));
    477            pt.end_level();
    478        }
    479        pt.end_level();
    480 
    481        pt.end_level();
    482    }
    483 
    484    /// Clear the dependency information for a tile, when the dependencies
    485    /// are being rebuilt.
    486    pub fn clear(&mut self) {
    487        self.local_valid_rect = PictureRect::zero();
    488        self.prims.clear();
    489        self.dep_data.clear();
    490    }
    491 }