tor-browser

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

compare.rs (15007B)


      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 //! Dependency tracking for tile invalidation
      6 //!
      7 //! This module contains types and logic for tracking dependencies that affect
      8 //! tile invalidation, including transform comparisons, spatial node tracking,
      9 //! and primitive comparison.
     10 
     11 use api::{ImageKey, PropertyBindingId, ColorU};
     12 use euclid::approxeq::ApproxEq;
     13 use crate::invalidation::PrimitiveCompareResult;
     14 use crate::spatial_tree::{SpatialTree, SpatialNodeIndex, CoordinateSpaceMapping};
     15 use crate::internal_types::{FastHashMap, FastHashSet, FrameId};
     16 use crate::intern::ItemUid;
     17 use crate::resource_cache::{ResourceCache, ImageGeneration};
     18 use crate::invalidation::cached_surface::{PrimitiveDependencyIndex, PrimitiveDescriptor, CachedSurfaceDescriptor};
     19 use peek_poke::{PeekPoke, peek_from_slice};
     20 use std::collections::hash_map::Entry;
     21 
     22 /// A comparable transform matrix, that compares with epsilon checks.
     23 #[derive(Debug, Clone)]
     24 pub struct MatrixKey {
     25    pub m: [f32; 16],
     26 }
     27 
     28 impl PartialEq for MatrixKey {
     29    fn eq(&self, other: &Self) -> bool {
     30        const EPSILON: f32 = 0.001;
     31 
     32        // TODO(gw): It's possible that we may need to adjust the epsilon
     33        //           to be tighter on most of the matrix, except the
     34        //           translation parts?
     35        for (i, j) in self.m.iter().zip(other.m.iter()) {
     36            if !i.approx_eq_eps(j, &EPSILON) {
     37                return false;
     38            }
     39        }
     40 
     41        true
     42    }
     43 }
     44 
     45 /// A comparable scale-offset, that compares with epsilon checks.
     46 #[derive(Debug, Clone)]
     47 pub struct ScaleOffsetKey {
     48    pub sx: f32,
     49    pub sy: f32,
     50    pub tx: f32,
     51    pub ty: f32,
     52 }
     53 
     54 impl PartialEq for ScaleOffsetKey {
     55    fn eq(&self, other: &Self) -> bool {
     56        const EPSILON: f32 = 0.001;
     57 
     58        self.sx.approx_eq_eps(&other.sx, &EPSILON) &&
     59        self.sy.approx_eq_eps(&other.sy, &EPSILON) &&
     60        self.tx.approx_eq_eps(&other.tx, &EPSILON) &&
     61        self.ty.approx_eq_eps(&other.ty, &EPSILON)
     62    }
     63 }
     64 
     65 /// A comparable / hashable version of a coordinate space mapping. Used to determine
     66 /// if a transform dependency for a tile has changed.
     67 #[derive(Debug, PartialEq, Clone)]
     68 pub enum TransformKey {
     69    Local,
     70    ScaleOffset {
     71        so: ScaleOffsetKey,
     72    },
     73    Transform {
     74        m: MatrixKey,
     75    }
     76 }
     77 
     78 impl<Src, Dst> From<CoordinateSpaceMapping<Src, Dst>> for TransformKey {
     79    fn from(transform: CoordinateSpaceMapping<Src, Dst>) -> TransformKey {
     80        match transform {
     81            CoordinateSpaceMapping::Local => {
     82                TransformKey::Local
     83            }
     84            CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
     85                TransformKey::ScaleOffset {
     86                    so: ScaleOffsetKey {
     87                        sx: scale_offset.scale.x,
     88                        sy: scale_offset.scale.y,
     89                        tx: scale_offset.offset.x,
     90                        ty: scale_offset.offset.y,
     91                    }
     92                }
     93            }
     94            CoordinateSpaceMapping::Transform(ref m) => {
     95                TransformKey::Transform {
     96                    m: MatrixKey {
     97                        m: m.to_array(),
     98                    },
     99                }
    100            }
    101        }
    102    }
    103 }
    104 
    105 /// Get the transform key for a spatial node relative to a cache spatial node.
    106 pub fn get_transform_key(
    107    spatial_node_index: SpatialNodeIndex,
    108    cache_spatial_node_index: SpatialNodeIndex,
    109    spatial_tree: &SpatialTree,
    110 ) -> TransformKey {
    111    spatial_tree.get_relative_transform(
    112        spatial_node_index,
    113        cache_spatial_node_index,
    114    ).into()
    115 }
    116 
    117 
    118 /// Information about the state of a binding.
    119 #[derive(Debug)]
    120 pub struct BindingInfo<T> {
    121    /// The current value retrieved from dynamic scene properties.
    122    pub value: T,
    123    /// True if it was changed (or is new) since the last frame build.
    124    pub changed: bool,
    125 }
    126 
    127 /// Information stored in a tile descriptor for a binding.
    128 #[derive(Debug, PartialEq, Clone, Copy, PeekPoke)]
    129 #[cfg_attr(feature = "capture", derive(Serialize))]
    130 #[cfg_attr(feature = "replay", derive(Deserialize))]
    131 pub enum Binding<T> {
    132    Value(T),
    133    Binding(PropertyBindingId),
    134 }
    135 
    136 impl<T: Default> Default for Binding<T> {
    137    fn default() -> Self {
    138        Binding::Value(T::default())
    139    }
    140 }
    141 
    142 impl<T> From<api::PropertyBinding<T>> for Binding<T> {
    143    fn from(binding: api::PropertyBinding<T>) -> Binding<T> {
    144        match binding {
    145            api::PropertyBinding::Binding(key, _) => Binding::Binding(key.id),
    146            api::PropertyBinding::Value(value) => Binding::Value(value),
    147        }
    148    }
    149 }
    150 
    151 pub type OpacityBinding = Binding<f32>;
    152 pub type OpacityBindingInfo = BindingInfo<f32>;
    153 
    154 pub type ColorBinding = Binding<ColorU>;
    155 pub type ColorBindingInfo = BindingInfo<ColorU>;
    156 
    157 /// Types of dependencies that a primitive can have
    158 #[derive(PeekPoke)]
    159 pub enum PrimitiveDependency {
    160    OpacityBinding {
    161        binding: OpacityBinding,
    162    },
    163    ColorBinding {
    164        binding: ColorBinding,
    165    },
    166    SpatialNode {
    167        index: SpatialNodeIndex,
    168    },
    169    Clip {
    170        clip: ItemUid,
    171    },
    172    Image {
    173        image: ImageDependency,
    174    },
    175 }
    176 
    177 /// Information stored an image dependency
    178 #[derive(Debug, Copy, Clone, PartialEq, PeekPoke, Default)]
    179 #[cfg_attr(feature = "capture", derive(Serialize))]
    180 #[cfg_attr(feature = "replay", derive(Deserialize))]
    181 pub struct ImageDependency {
    182    pub key: ImageKey,
    183    pub generation: ImageGeneration,
    184 }
    185 
    186 impl ImageDependency {
    187    pub const INVALID: ImageDependency = ImageDependency {
    188        key: ImageKey::DUMMY,
    189        generation: ImageGeneration::INVALID,
    190    };
    191 }
    192 
    193 /// A dependency for a transform is defined by the spatial node index + frame it was used
    194 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PeekPoke, Default)]
    195 #[cfg_attr(feature = "capture", derive(Serialize))]
    196 #[cfg_attr(feature = "replay", derive(Deserialize))]
    197 pub struct SpatialNodeKey {
    198    pub spatial_node_index: SpatialNodeIndex,
    199    pub frame_id: FrameId,
    200 }
    201 
    202 /// A helper for comparing spatial nodes between frames. The comparisons
    203 /// are done by-value, so that if the shape of the spatial node tree
    204 /// changes, invalidations aren't done simply due to the spatial node
    205 /// index changing between display lists.
    206 pub struct SpatialNodeComparer {
    207    /// The root spatial node index of the tile cache
    208    ref_spatial_node_index: SpatialNodeIndex,
    209    /// Maintains a map of currently active transform keys
    210    spatial_nodes: FastHashMap<SpatialNodeKey, TransformKey>,
    211    /// A cache of recent comparisons between prev and current spatial nodes
    212    compare_cache: FastHashMap<(SpatialNodeKey, SpatialNodeKey), bool>,
    213    /// A set of frames that we need to retain spatial node entries for
    214    referenced_frames: FastHashSet<FrameId>,
    215 }
    216 
    217 impl SpatialNodeComparer {
    218    /// Construct a new comparer
    219    pub fn new() -> Self {
    220        SpatialNodeComparer {
    221            ref_spatial_node_index: SpatialNodeIndex::INVALID,
    222            spatial_nodes: FastHashMap::default(),
    223            compare_cache: FastHashMap::default(),
    224            referenced_frames: FastHashSet::default(),
    225        }
    226    }
    227 
    228    /// Advance to the next frame
    229    pub fn next_frame(
    230        &mut self,
    231        ref_spatial_node_index: SpatialNodeIndex,
    232    ) {
    233        // Drop any node information for unreferenced frames, to ensure that the
    234        // hashmap doesn't grow indefinitely!
    235        let referenced_frames = &self.referenced_frames;
    236        self.spatial_nodes.retain(|key, _| {
    237            referenced_frames.contains(&key.frame_id)
    238        });
    239 
    240        // Update the root spatial node for this comparer
    241        self.ref_spatial_node_index = ref_spatial_node_index;
    242        self.compare_cache.clear();
    243        self.referenced_frames.clear();
    244    }
    245 
    246    /// Register a transform that is used, and build the transform key for it if new.
    247    pub fn register_used_transform(
    248        &mut self,
    249        spatial_node_index: SpatialNodeIndex,
    250        frame_id: FrameId,
    251        spatial_tree: &SpatialTree,
    252    ) {
    253        let key = SpatialNodeKey {
    254            spatial_node_index,
    255            frame_id,
    256        };
    257 
    258        if let Entry::Vacant(entry) = self.spatial_nodes.entry(key) {
    259            entry.insert(
    260                get_transform_key(
    261                    spatial_node_index,
    262                    self.ref_spatial_node_index,
    263                    spatial_tree,
    264                )
    265            );
    266        }
    267    }
    268 
    269    /// Return true if the transforms for two given spatial nodes are considered equivalent
    270    pub fn are_transforms_equivalent(
    271        &mut self,
    272        prev_spatial_node_key: &SpatialNodeKey,
    273        curr_spatial_node_key: &SpatialNodeKey,
    274    ) -> bool {
    275        let key = (*prev_spatial_node_key, *curr_spatial_node_key);
    276        let spatial_nodes = &self.spatial_nodes;
    277 
    278        *self.compare_cache
    279            .entry(key)
    280            .or_insert_with(|| {
    281                let prev = &spatial_nodes[&prev_spatial_node_key];
    282                let curr = &spatial_nodes[&curr_spatial_node_key];
    283                curr == prev
    284            })
    285    }
    286 
    287    /// Ensure that the comparer won't GC any nodes for a given frame id
    288    pub fn retain_for_frame(&mut self, frame_id: FrameId) {
    289        self.referenced_frames.insert(frame_id);
    290    }
    291 }
    292 
    293 /// A key for storing primitive comparison results during tile dependency tests.
    294 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
    295 pub struct PrimitiveComparisonKey {
    296    pub prev_index: PrimitiveDependencyIndex,
    297    pub curr_index: PrimitiveDependencyIndex,
    298 }
    299 
    300 /// A helper struct to compare a primitive and all its sub-dependencies.
    301 pub struct PrimitiveComparer<'a> {
    302    prev_data: &'a [u8],
    303    curr_data: &'a [u8],
    304    prev_frame_id: FrameId,
    305    curr_frame_id: FrameId,
    306    resource_cache: &'a ResourceCache,
    307    spatial_node_comparer: &'a mut SpatialNodeComparer,
    308    opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>,
    309    color_bindings: &'a FastHashMap<PropertyBindingId, ColorBindingInfo>,
    310 }
    311 
    312 impl<'a> PrimitiveComparer<'a> {
    313    pub fn new(
    314        prev: &'a CachedSurfaceDescriptor,
    315        curr: &'a CachedSurfaceDescriptor,
    316        resource_cache: &'a ResourceCache,
    317        spatial_node_comparer: &'a mut SpatialNodeComparer,
    318        opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>,
    319        color_bindings: &'a FastHashMap<PropertyBindingId, ColorBindingInfo>,
    320    ) -> Self {
    321        PrimitiveComparer {
    322            prev_data: &prev.dep_data,
    323            curr_data: &curr.dep_data,
    324            prev_frame_id: prev.last_updated_frame_id,
    325            curr_frame_id: curr.last_updated_frame_id,
    326            resource_cache,
    327            spatial_node_comparer,
    328            opacity_bindings,
    329            color_bindings,
    330        }
    331    }
    332 
    333    /// Check if two primitive descriptors are the same.
    334    pub fn compare_prim(
    335        &mut self,
    336        prev_desc: &PrimitiveDescriptor,
    337        curr_desc: &PrimitiveDescriptor,
    338    ) -> PrimitiveCompareResult {
    339        let resource_cache = self.resource_cache;
    340        let spatial_node_comparer = &mut self.spatial_node_comparer;
    341        let opacity_bindings = self.opacity_bindings;
    342        let color_bindings = self.color_bindings;
    343 
    344        // Check equality of the PrimitiveDescriptor
    345        if prev_desc != curr_desc {
    346            return PrimitiveCompareResult::Descriptor;
    347        }
    348 
    349        let mut prev_dep_data = &self.prev_data[prev_desc.dep_offset as usize ..];
    350        let mut curr_dep_data = &self.curr_data[curr_desc.dep_offset as usize ..];
    351 
    352        let mut prev_dep = PrimitiveDependency::SpatialNode { index: SpatialNodeIndex::INVALID };
    353        let mut curr_dep = PrimitiveDependency::SpatialNode { index: SpatialNodeIndex::INVALID };
    354 
    355        debug_assert_eq!(prev_desc.dep_count, curr_desc.dep_count);
    356 
    357        for _ in 0 .. prev_desc.dep_count {
    358            prev_dep_data = peek_from_slice(prev_dep_data, &mut prev_dep);
    359            curr_dep_data = peek_from_slice(curr_dep_data, &mut curr_dep);
    360 
    361            match (&prev_dep, &curr_dep) {
    362                (PrimitiveDependency::Clip { clip: prev }, PrimitiveDependency::Clip { clip: curr }) => {
    363                    if prev != curr {
    364                        return PrimitiveCompareResult::Clip;
    365                    }
    366                }
    367                (PrimitiveDependency::SpatialNode { index: prev }, PrimitiveDependency::SpatialNode { index: curr }) => {
    368                    let prev_key = SpatialNodeKey {
    369                        spatial_node_index: *prev,
    370                        frame_id: self.prev_frame_id,
    371                    };
    372                    let curr_key = SpatialNodeKey {
    373                        spatial_node_index: *curr,
    374                        frame_id: self.curr_frame_id,
    375                    };
    376                    if !spatial_node_comparer.are_transforms_equivalent(&prev_key, &curr_key) {
    377                        return PrimitiveCompareResult::Transform;
    378                    }
    379                }
    380                (PrimitiveDependency::OpacityBinding { binding: prev }, PrimitiveDependency::OpacityBinding { binding: curr }) => {
    381                    if prev != curr {
    382                        return PrimitiveCompareResult::OpacityBinding;
    383                    }
    384 
    385                    if let OpacityBinding::Binding(id) = curr {
    386                        if opacity_bindings
    387                            .get(id)
    388                            .map_or(true, |info| info.changed) {
    389                            return PrimitiveCompareResult::OpacityBinding;
    390                        }
    391                    }
    392                }
    393                (PrimitiveDependency::ColorBinding { binding: prev }, PrimitiveDependency::ColorBinding { binding: curr }) => {
    394                    if prev != curr {
    395                        return PrimitiveCompareResult::ColorBinding;
    396                    }
    397 
    398                    if let ColorBinding::Binding(id) = curr {
    399                        if color_bindings
    400                            .get(id)
    401                            .map_or(true, |info| info.changed) {
    402                            return PrimitiveCompareResult::ColorBinding;
    403                        }
    404                    }
    405                }
    406                (PrimitiveDependency::Image { image: prev }, PrimitiveDependency::Image { image: curr }) => {
    407                    if prev != curr {
    408                        return PrimitiveCompareResult::Image;
    409                    }
    410 
    411                    if resource_cache.get_image_generation(curr.key) != curr.generation {
    412                        return PrimitiveCompareResult::Image;
    413                    }
    414                }
    415                _ => {
    416                    // There was a mismatch between types of dependencies, so something changed
    417                    return PrimitiveCompareResult::Descriptor;
    418                }
    419            }
    420        }
    421 
    422        PrimitiveCompareResult::Equal
    423    }
    424 }