Skip to main content

webrender/invalidation/
compare.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5//! Dependency tracking for tile invalidation
6//!
7//! This module contains types and logic for tracking dynamic content dependencies
8//! (images, opacity bindings, color bindings, clip corners) used to determine
9//! what needs to be redrawn each frame.
10
11use api::{ImageKey, PropertyBindingId, ColorU};
12use crate::invalidation::PrimitiveCompareResult;
13use crate::internal_types::FastHashMap;
14use crate::resource_cache::{ResourceCache, ImageGeneration};
15use crate::invalidation::cached_surface::{PrimitiveDependencyIndex, PrimitiveDescriptor, CachedSurfaceDescriptor};
16use crate::intern::ItemUid;
17use crate::invalidation::vert_buffer::VertRange;
18use peek_poke::{PeekPoke, peek_from_slice};
19
20
21/// Information about the state of a binding.
22#[derive(Debug)]
23pub struct BindingInfo<T> {
24    /// The current value retrieved from dynamic scene properties.
25    pub value: T,
26    /// True if it was changed (or is new) since the last frame build.
27    pub changed: bool,
28}
29
30/// Information stored in a tile descriptor for a binding.
31#[derive(Debug, PartialEq, Clone, Copy, PeekPoke)]
32#[cfg_attr(feature = "capture", derive(Serialize))]
33#[cfg_attr(feature = "replay", derive(Deserialize))]
34pub enum Binding<T> {
35    Value(T),
36    Binding(PropertyBindingId),
37}
38
39impl<T: Default> Default for Binding<T> {
40    fn default() -> Self {
41        Binding::Value(T::default())
42    }
43}
44
45impl<T> From<api::PropertyBinding<T>> for Binding<T> {
46    fn from(binding: api::PropertyBinding<T>) -> Binding<T> {
47        match binding {
48            api::PropertyBinding::Binding(key, _) => Binding::Binding(key.id),
49            api::PropertyBinding::Value(value) => Binding::Value(value),
50        }
51    }
52}
53
54pub type OpacityBinding = Binding<f32>;
55pub type OpacityBindingInfo = BindingInfo<f32>;
56
57pub type ColorBinding = Binding<ColorU>;
58pub type ColorBindingInfo = BindingInfo<ColorU>;
59
60/// Types of dependencies that a primitive can have
61#[derive(PeekPoke)]
62pub enum PrimitiveDependency {
63    OpacityBinding {
64        binding: OpacityBinding,
65    },
66    ColorBinding {
67        binding: ColorBinding,
68    },
69    Image {
70        image: ImageDependency,
71    },
72    /// Clip dependency: prim_uid is the clip's intern uid; position is captured
73    /// as quantized raster-space corners in vert_data. See PrimitiveDependencyInfo::prim_uid
74    /// for why this uid is stable across scroll events.
75    Clip {
76        prim_uid: ItemUid,
77        vert_range: VertRange,
78    },
79}
80
81/// Information stored an image dependency
82#[derive(Debug, Copy, Clone, PartialEq, PeekPoke, Default)]
83#[cfg_attr(feature = "capture", derive(Serialize))]
84#[cfg_attr(feature = "replay", derive(Deserialize))]
85pub struct ImageDependency {
86    pub key: ImageKey,
87    pub generation: ImageGeneration,
88}
89
90impl ImageDependency {
91    pub const INVALID: ImageDependency = ImageDependency {
92        key: ImageKey::DUMMY,
93        generation: ImageGeneration::INVALID,
94    };
95}
96
97/// A key for storing primitive comparison results during tile dependency tests.
98#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
99pub struct PrimitiveComparisonKey {
100    pub prev_index: PrimitiveDependencyIndex,
101    pub curr_index: PrimitiveDependencyIndex,
102}
103
104/// A helper struct to compare a primitive and all its sub-dependencies.
105pub struct PrimitiveComparer<'a> {
106    prev: &'a CachedSurfaceDescriptor,
107    curr: &'a CachedSurfaceDescriptor,
108    resource_cache: &'a ResourceCache,
109    opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>,
110    color_bindings: &'a FastHashMap<PropertyBindingId, ColorBindingInfo>,
111}
112
113impl<'a> PrimitiveComparer<'a> {
114    pub fn new(
115        prev: &'a CachedSurfaceDescriptor,
116        curr: &'a CachedSurfaceDescriptor,
117        resource_cache: &'a ResourceCache,
118        opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>,
119        color_bindings: &'a FastHashMap<PropertyBindingId, ColorBindingInfo>,
120    ) -> Self {
121        PrimitiveComparer {
122            prev,
123            curr,
124            resource_cache,
125            opacity_bindings,
126            color_bindings,
127        }
128    }
129
130    /// Compares two primitive descriptors using prim_uid + quantized raster-space
131    /// vert corners. Transform changes are covered by vert corners; dynamic content
132    /// changes (images, opacity/color bindings) are checked via the dep stream.
133    pub fn compare_prim(
134        &mut self,
135        prev_desc: &PrimitiveDescriptor,
136        curr_desc: &PrimitiveDescriptor,
137    ) -> PrimitiveCompareResult {
138        if prev_desc.prim_uid != curr_desc.prim_uid {
139            return PrimitiveCompareResult::Descriptor;
140        }
141
142        // Compare quantized raster-space corners (all corners live in the per-tile vert_data).
143        let prev_range = prev_desc.prim_corners;
144        let prev_end = (prev_range.offset + prev_range.count) as usize;
145        let prev_verts = &self.prev.vert_data[prev_range.offset as usize .. prev_end];
146
147        let curr_range = curr_desc.prim_corners;
148        let curr_end = (curr_range.offset + curr_range.count) as usize;
149        let curr_verts = &self.curr.vert_data[curr_range.offset as usize .. curr_end];
150
151        if prev_verts != curr_verts {
152            return PrimitiveCompareResult::Descriptor;
153        }
154
155        let prev_range = prev_desc.coverage_corners;
156        let prev_end = (prev_range.offset + prev_range.count) as usize;
157        let prev_verts = &self.prev.vert_data[prev_range.offset as usize .. prev_end];
158
159        let curr_range = curr_desc.coverage_corners;
160        let curr_end = (curr_range.offset + curr_range.count) as usize;
161        let curr_verts = &self.curr.vert_data[curr_range.offset as usize .. curr_end];
162
163        if prev_verts != curr_verts {
164            return PrimitiveCompareResult::Descriptor;
165        }
166
167        // Conservative guard: if dep counts differ fall back to invalidation
168        // rather than misaligning the dep stream.
169        if prev_desc.dep_count != curr_desc.dep_count {
170            return PrimitiveCompareResult::Descriptor;
171        }
172
173        // Check dynamic deps (Image, Opacity, Color) that aren't captured in
174        // prim_uid or vert corners.
175        let mut prev_dep_data = &self.prev.dep_data[prev_desc.dep_offset as usize ..];
176        let mut curr_dep_data = &self.curr.dep_data[curr_desc.dep_offset as usize ..];
177
178        let mut prev_dep = PrimitiveDependency::Image { image: ImageDependency::INVALID };
179        let mut curr_dep = PrimitiveDependency::Image { image: ImageDependency::INVALID };
180
181        for _ in 0 .. prev_desc.dep_count {
182            prev_dep_data = peek_from_slice(prev_dep_data, &mut prev_dep);
183            curr_dep_data = peek_from_slice(curr_dep_data, &mut curr_dep);
184
185            match (&prev_dep, &curr_dep) {
186                (PrimitiveDependency::Clip { prim_uid: prev_uid, vert_range: prev_range }, PrimitiveDependency::Clip { prim_uid: curr_uid, vert_range: curr_range }) => {
187                    if prev_uid != curr_uid {
188                        return PrimitiveCompareResult::Clip;
189                    }
190                    let prev_end = (prev_range.offset + prev_range.count) as usize;
191                    let prev_verts: &[i32] = if prev_range.is_valid() && prev_end <= self.prev.vert_data.len() {
192                        &self.prev.vert_data[prev_range.offset as usize .. prev_end]
193                    } else {
194                        &[]
195                    };
196                    let curr_end = (curr_range.offset + curr_range.count) as usize;
197                    let curr_verts: &[i32] = if curr_range.is_valid() && curr_end <= self.curr.vert_data.len() {
198                        &self.curr.vert_data[curr_range.offset as usize .. curr_end]
199                    } else {
200                        &[]
201                    };
202                    if prev_verts != curr_verts {
203                        return PrimitiveCompareResult::Clip;
204                    }
205                }
206                (PrimitiveDependency::Image { image: prev }, PrimitiveDependency::Image { image: curr }) => {
207                    if prev != curr {
208                        return PrimitiveCompareResult::Image;
209                    }
210                    if self.resource_cache.get_image_generation(curr.key) != curr.generation {
211                        return PrimitiveCompareResult::Image;
212                    }
213                }
214                (PrimitiveDependency::OpacityBinding { binding: prev }, PrimitiveDependency::OpacityBinding { binding: curr }) => {
215                    if prev != curr {
216                        return PrimitiveCompareResult::OpacityBinding;
217                    }
218                    if let OpacityBinding::Binding(id) = curr {
219                        if self.opacity_bindings.get(id).map_or(true, |info| info.changed) {
220                            return PrimitiveCompareResult::OpacityBinding;
221                        }
222                    }
223                }
224                (PrimitiveDependency::ColorBinding { binding: prev }, PrimitiveDependency::ColorBinding { binding: curr }) => {
225                    if prev != curr {
226                        return PrimitiveCompareResult::ColorBinding;
227                    }
228                    if let ColorBinding::Binding(id) = curr {
229                        if self.color_bindings.get(id).map_or(true, |info| info.changed) {
230                            return PrimitiveCompareResult::ColorBinding;
231                        }
232                    }
233                }
234                _ => {
235                    return PrimitiveCompareResult::Descriptor;
236                }
237            }
238        }
239
240        PrimitiveCompareResult::Equal
241    }
242}