1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
use cranpose_core::NodeId;
use cranpose_ui_graphics::{Point, Rect};
/// The device scale a layer raster is drawn at, compared exactly: a raster
/// drawn at one scale places its edges and glyphs where no other scale does,
/// so it serves only that scale.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RasterScale(u32);
impl RasterScale {
/// The raster scale of `scale`; a scale no raster can be drawn at (zero,
/// negative or not finite) is the unit scale.
pub fn from_scale(scale: f32) -> Self {
let normalized = if scale.is_finite() && scale > 0.0 {
scale
} else {
1.0
};
Self(normalized.to_bits())
}
/// The scale's bit pattern.
pub fn raw(self) -> u32 {
self.0
}
}
/// Hashes of a layer's content and of its render effect, which key its raster cache entries.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct LayerRasterCacheHashes {
/// Hash of what the layer draws into its target, as `LayerNode::target_content_hash`
/// returns it.
pub target_content: u64,
/// Hash of the layer's render effect.
pub effect: u64,
}
/// Number of distinct [`LayerRasterCacheKey`] kinds; see
/// [`LayerRasterCacheKey::kind_slot`] and [`LAYER_RASTER_CACHE_KIND_LABELS`].
pub const LAYER_RASTER_CACHE_KIND_COUNT: usize = 5;
/// Short labels per kind slot, in [`LayerRasterCacheKey::kind_slot`] order.
pub const LAYER_RASTER_CACHE_KIND_LABELS: [&str; LAYER_RASTER_CACHE_KIND_COUNT] =
["src", "backdrop", "range", "prefix", "effect"];
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum LayerRasterCacheKind {
SourceContent,
BackdropEffect,
SceneRange,
PrefixSnapshot,
LayerEffect,
}
impl LayerRasterCacheKind {
fn identity_kind(self) -> u8 {
match self {
Self::SourceContent => 0,
Self::BackdropEffect => 1,
Self::SceneRange => 2,
Self::PrefixSnapshot => 3,
Self::LayerEffect => 4,
}
}
}
/// Identity of a node's cached raster of one kind. It stays the same while the content changes.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LayerRasterCacheIdentity {
stable_id: NodeId,
kind: u8,
}
/// Key of one cached layer raster. It covers the kind of entry, the owning node, the content and
/// effect hashes, the layer bounds, the pixel size, the raster scale and the sub-pixel phase.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LayerRasterCacheKey {
kind: LayerRasterCacheKind,
stable_id: Option<NodeId>,
content_hash: u64,
effect_hash: u64,
local_bounds_bits: [u32; 4],
pixel_size: [u32; 2],
raster_scale: RasterScale,
device_phase_steps: [u32; 2],
}
const DEVICE_PHASE_STEPS: f32 = 16.0;
fn device_phase_steps(phase: Point) -> [u32; 2] {
let steps = |value: f32| {
((value.rem_euclid(1.0) * DEVICE_PHASE_STEPS).round() as u32) % DEVICE_PHASE_STEPS as u32
};
[steps(phase.x), steps(phase.y)]
}
fn local_bounds_bits(local_bounds: Rect) -> [u32; 4] {
[
local_bounds.x.to_bits(),
local_bounds.y.to_bits(),
local_bounds.width.to_bits(),
local_bounds.height.to_bits(),
]
}
impl LayerRasterCacheKey {
/// Key for the rasterized content of a layer. `content_hash` hashes the content, `local_bounds`
/// is the layer's bounds, `pixel_size` is the raster size in pixels, and `device_phase` is the
/// layer's sub-pixel position, kept in sixteenth-pixel steps.
pub fn source_content(
stable_id: Option<NodeId>,
content_hash: u64,
local_bounds: Rect,
pixel_size: (u32, u32),
raster_scale: RasterScale,
device_phase: Point,
) -> Self {
Self {
kind: LayerRasterCacheKind::SourceContent,
stable_id,
content_hash,
effect_hash: 0,
local_bounds_bits: local_bounds_bits(local_bounds),
pixel_size: [pixel_size.0, pixel_size.1],
raster_scale,
device_phase_steps: device_phase_steps(device_phase),
}
}
/// Key for the output of a backdrop effect. `input_hash` hashes the backdrop that the effect
/// reads and `effect_hash` hashes the effect.
pub fn backdrop_effect(
stable_id: Option<NodeId>,
input_hash: u64,
effect_hash: u64,
local_bounds: Rect,
pixel_size: (u32, u32),
raster_scale: RasterScale,
) -> Self {
Self::effect(
LayerRasterCacheKind::BackdropEffect,
stable_id,
input_hash,
effect_hash,
local_bounds,
pixel_size,
raster_scale,
)
}
/// A layer's render effect applied over its retained surface: the output
/// is a pure function of the surface's content, the effect and the
/// layer's pixel rect within the surface.
pub fn layer_effect(
stable_id: Option<NodeId>,
input_hash: u64,
effect_hash: u64,
local_bounds: Rect,
pixel_size: (u32, u32),
raster_scale: RasterScale,
) -> Self {
Self::effect(
LayerRasterCacheKind::LayerEffect,
stable_id,
input_hash,
effect_hash,
local_bounds,
pixel_size,
raster_scale,
)
}
fn effect(
kind: LayerRasterCacheKind,
stable_id: Option<NodeId>,
input_hash: u64,
effect_hash: u64,
local_bounds: Rect,
pixel_size: (u32, u32),
raster_scale: RasterScale,
) -> Self {
Self {
kind,
stable_id,
content_hash: input_hash,
effect_hash,
local_bounds_bits: local_bounds_bits(local_bounds),
pixel_size: [pixel_size.0, pixel_size.1],
raster_scale,
device_phase_steps: [0; 2],
}
}
/// Key for a cached range of the scene, identified by the hash of its content and owned by no
/// node.
pub fn scene_range(
content_hash: u64,
local_bounds: Rect,
pixel_size: (u32, u32),
raster_scale: RasterScale,
) -> Self {
Self {
kind: LayerRasterCacheKind::SceneRange,
stable_id: None,
content_hash,
effect_hash: 0,
local_bounds_bits: local_bounds_bits(local_bounds),
pixel_size: [pixel_size.0, pixel_size.1],
raster_scale,
device_phase_steps: [0; 2],
}
}
/// A snapshot of the scene's rendered prefix: the bytes the target held
/// after drawing ops `[0, prefix_len)` over the pass's clear color. A
/// replay of captured bytes is identical to direct rendering by
/// construction — no flattening, so none of the chained-rounding
/// divergence flatten entries carry.
pub fn prefix_snapshot(
content_hash: u64,
prefix_len: u64,
local_bounds: Rect,
pixel_size: (u32, u32),
raster_scale: RasterScale,
) -> Self {
Self {
kind: LayerRasterCacheKind::PrefixSnapshot,
stable_id: None,
content_hash,
effect_hash: prefix_len,
local_bounds_bits: local_bounds_bits(local_bounds),
pixel_size: [pixel_size.0, pixel_size.1],
raster_scale,
device_phase_steps: [0; 2],
}
}
/// Index of this key's kind in `0..LAYER_RASTER_CACHE_KIND_COUNT`, for
/// per-kind accounting.
pub fn kind_slot(self) -> usize {
self.kind.identity_kind() as usize
}
/// The node that owns the entry, or `None` when the entry belongs to no single node.
pub fn stable_id(self) -> Option<NodeId> {
self.stable_id
}
/// Whether the key is for a scene range or a prefix snapshot.
pub fn is_scene_range(self) -> bool {
matches!(
self.kind,
LayerRasterCacheKind::SceneRange | LayerRasterCacheKind::PrefixSnapshot
)
}
/// Whether the key is for the rasterized content of a layer.
pub fn is_source_content(self) -> bool {
self.kind == LayerRasterCacheKind::SourceContent
}
/// The identity of the entry, or `None` when it has no owning node.
pub fn identity(self) -> Option<LayerRasterCacheIdentity> {
Some(LayerRasterCacheIdentity {
stable_id: self.stable_id?,
kind: self.kind.identity_kind(),
})
}
/// Raster size in pixels as width and height.
pub fn pixel_size(self) -> (u32, u32) {
(self.pixel_size[0], self.pixel_size[1])
}
/// The bit pattern of the entry's local bounds: the place a keyless entry
/// occupies, stable while its content changes.
pub fn local_bounds_bits(self) -> [u32; 4] {
self.local_bounds_bits
}
/// The device scale the raster is drawn at.
pub fn raster_scale(self) -> RasterScale {
self.raster_scale
}
/// Whether `other` rasterizes other content than this key: another
/// kind, node, content, effect or bounds. Keys that differ only in the
/// scale or the sub-pixel phase they are drawn at hold the same content.
pub fn draws_other_content(self, other: Self) -> bool {
Self {
pixel_size: other.pixel_size,
raster_scale: other.raster_scale,
device_phase_steps: other.device_phase_steps,
..self
} != other
}
}
#[cfg(test)]
#[path = "tests/raster_cache_tests.rs"]
mod tests;