1use api::units::*;
12use crate::debug_colors;
13use crate::internal_types::FastHashMap;
14use crate::prim_store::PrimitiveScratchBuffer;
15use crate::space::SpaceMapper;
16use crate::invalidation::{InvalidationReason, PrimitiveCompareResult};
17use crate::invalidation::cached_surface::{PrimitiveDescriptor, PrimitiveDependencyIndex};
18use crate::invalidation::compare::{PrimitiveComparer, PrimitiveComparisonKey};
19use crate::visibility::FrameVisibilityContext;
20use std::mem;
21
22
23#[cfg_attr(any(feature="capture",feature="replay"), derive(Clone))]
25#[cfg_attr(feature = "capture", derive(Serialize))]
26#[cfg_attr(feature = "replay", derive(Deserialize))]
27pub enum TileNodeKind {
28 Leaf {
29 #[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
31 prev_indices: Vec<PrimitiveDependencyIndex>,
32 #[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
34 curr_indices: Vec<PrimitiveDependencyIndex>,
35 #[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
37 dirty_tracker: u64,
38 #[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
40 frames_since_modified: usize,
41 },
42 Node {
43 children: Vec<TileNode>,
45 },
46}
47
48#[derive(Copy, Clone, PartialEq, Debug)]
50enum TileModification {
51 Split,
52 Merge,
53}
54
55#[cfg_attr(any(feature="capture",feature="replay"), derive(Clone))]
57#[cfg_attr(feature = "capture", derive(Serialize))]
58#[cfg_attr(feature = "replay", derive(Deserialize))]
59pub struct TileNode {
60 pub kind: TileNodeKind,
62 pub rect: PictureBox2D,
64}
65
66impl TileNode {
67 pub fn new_leaf(curr_indices: Vec<PrimitiveDependencyIndex>) -> Self {
69 TileNode {
70 kind: TileNodeKind::Leaf {
71 prev_indices: Vec::new(),
72 curr_indices,
73 dirty_tracker: 0,
74 frames_since_modified: 0,
75 },
76 rect: PictureBox2D::zero(),
77 }
78 }
79
80 pub fn draw_debug_rects(
82 &self,
83 pic_to_world_mapper: &SpaceMapper<PicturePixel, WorldPixel>,
84 is_opaque: bool,
85 local_valid_rect: PictureRect,
86 scratch: &mut PrimitiveScratchBuffer,
87 global_device_pixel_scale: DevicePixelScale,
88 ) {
89 match self.kind {
90 TileNodeKind::Leaf { dirty_tracker, .. } => {
91 let color = if (dirty_tracker & 1) != 0 {
92 debug_colors::RED
93 } else if is_opaque {
94 debug_colors::GREEN
95 } else {
96 debug_colors::YELLOW
97 };
98
99 if let Some(local_rect) = local_valid_rect.intersection(&self.rect) {
100 let world_rect = pic_to_world_mapper
101 .map(&local_rect)
102 .unwrap();
103 let device_rect = world_rect * global_device_pixel_scale;
104
105 let outer_color = color.scale_alpha(0.3);
106 let inner_color = outer_color.scale_alpha(0.5);
107 scratch.push_debug_rect(
108 device_rect.inflate(-3.0, -3.0),
109 1,
110 outer_color,
111 inner_color
112 );
113 }
114 }
115 TileNodeKind::Node { ref children, .. } => {
116 for child in children.iter() {
117 child.draw_debug_rects(
118 pic_to_world_mapper,
119 is_opaque,
120 local_valid_rect,
121 scratch,
122 global_device_pixel_scale,
123 );
124 }
125 }
126 }
127 }
128
129 fn get_child_rects(
131 rect: &PictureBox2D,
132 result: &mut [PictureBox2D; 4],
133 ) {
134 let p0 = rect.min;
135 let p1 = rect.max;
136 let pc = p0 + rect.size() * 0.5;
137
138 *result = [
139 PictureBox2D::new(
140 p0,
141 pc,
142 ),
143 PictureBox2D::new(
144 PicturePoint::new(pc.x, p0.y),
145 PicturePoint::new(p1.x, pc.y),
146 ),
147 PictureBox2D::new(
148 PicturePoint::new(p0.x, pc.y),
149 PicturePoint::new(pc.x, p1.y),
150 ),
151 PictureBox2D::new(
152 pc,
153 p1,
154 ),
155 ];
156 }
157
158 pub fn clear(
160 &mut self,
161 rect: PictureBox2D,
162 ) {
163 self.rect = rect;
164
165 match self.kind {
166 TileNodeKind::Leaf { ref mut prev_indices, ref mut curr_indices, ref mut dirty_tracker, ref mut frames_since_modified } => {
167 mem::swap(prev_indices, curr_indices);
169 curr_indices.clear();
170 *dirty_tracker = *dirty_tracker << 1;
172 *frames_since_modified += 1;
173 }
174 TileNodeKind::Node { ref mut children, .. } => {
175 let mut child_rects = [PictureBox2D::zero(); 4];
176 TileNode::get_child_rects(&rect, &mut child_rects);
177 assert_eq!(child_rects.len(), children.len());
178
179 for (child, rect) in children.iter_mut().zip(child_rects.iter()) {
180 child.clear(*rect);
181 }
182 }
183 }
184 }
185
186 pub fn add_prim(
188 &mut self,
189 index: PrimitiveDependencyIndex,
190 prim_rect: &PictureBox2D,
191 ) {
192 match self.kind {
193 TileNodeKind::Leaf { ref mut curr_indices, .. } => {
194 curr_indices.push(index);
195 }
196 TileNodeKind::Node { ref mut children, .. } => {
197 for child in children.iter_mut() {
198 if child.rect.intersects(prim_rect) {
199 child.add_prim(index, prim_rect);
200 }
201 }
202 }
203 }
204 }
205
206 pub fn maybe_merge_or_split(
208 &mut self,
209 level: i32,
210 curr_prims: &[PrimitiveDescriptor],
211 max_split_levels: i32,
212 ) {
213 let mut tile_mod = None;
215
216 fn get_dirty_frames(
217 dirty_tracker: u64,
218 frames_since_modified: usize,
219 ) -> Option<u32> {
220 if frames_since_modified > 64 {
222 Some(dirty_tracker.count_ones())
224 } else {
225 None
226 }
227 }
228
229 match self.kind {
230 TileNodeKind::Leaf { dirty_tracker, frames_since_modified, .. } => {
231 if level < max_split_levels {
233 if let Some(dirty_frames) = get_dirty_frames(dirty_tracker, frames_since_modified) {
234 if dirty_frames > 32 {
236 tile_mod = Some(TileModification::Split);
237 }
238 }
239 }
240 }
241 TileNodeKind::Node { ref children, .. } => {
242 let mut static_count = 0;
249 let mut changing_count = 0;
250
251 for child in children {
252 if let TileNodeKind::Leaf { dirty_tracker, frames_since_modified, .. } = child.kind {
254 if let Some(dirty_frames) = get_dirty_frames(dirty_tracker, frames_since_modified) {
255 if dirty_frames == 0 {
256 static_count += 1;
258 } else if dirty_frames == 64 {
259 changing_count += 1;
261 }
262 }
263 }
264
265 if static_count == 4 || changing_count == 4 {
269 tile_mod = Some(TileModification::Merge);
270 }
271 }
272 }
273 }
274
275 match tile_mod {
276 Some(TileModification::Split) => {
277 let curr_indices = match self.kind {
280 TileNodeKind::Node { .. } => {
281 unreachable!("bug - only leaves can split");
282 }
283 TileNodeKind::Leaf { ref mut curr_indices, .. } => {
284 mem::take(curr_indices)
285 }
286 };
287
288 let mut child_rects = [PictureBox2D::zero(); 4];
289 TileNode::get_child_rects(&self.rect, &mut child_rects);
290
291 let mut child_indices = [
292 Vec::new(),
293 Vec::new(),
294 Vec::new(),
295 Vec::new(),
296 ];
297
298 for index in curr_indices {
301 let prim = &curr_prims[index.0 as usize];
302 for (child_rect, indices) in child_rects.iter().zip(child_indices.iter_mut()) {
303 if prim.prim_clip_box.intersects(child_rect) {
304 indices.push(index);
305 }
306 }
307 }
308
309 let children = child_indices
311 .iter_mut()
312 .map(|i| TileNode::new_leaf(mem::replace(i, Vec::new())))
313 .collect();
314
315 self.kind = TileNodeKind::Node {
316 children,
317 };
318 }
319 Some(TileModification::Merge) => {
320 let merged_indices = match self.kind {
323 TileNodeKind::Node { ref mut children, .. } => {
324 let mut merged_indices = Vec::new();
325
326 for child in children.iter() {
327 let child_indices = match child.kind {
328 TileNodeKind::Leaf { ref curr_indices, .. } => {
329 curr_indices
330 }
331 TileNodeKind::Node { .. } => {
332 unreachable!("bug: child is not a leaf");
333 }
334 };
335 merged_indices.extend_from_slice(child_indices);
336 }
337
338 merged_indices.sort();
339 merged_indices.dedup();
340
341 merged_indices
342 }
343 TileNodeKind::Leaf { .. } => {
344 unreachable!("bug - trying to merge a leaf");
345 }
346 };
347
348 self.kind = TileNodeKind::Leaf {
350 prev_indices: Vec::new(),
351 curr_indices: merged_indices,
352 dirty_tracker: 0,
353 frames_since_modified: 0,
354 };
355 }
356 None => {
357 if let TileNodeKind::Node { ref mut children, .. } = self.kind {
360 for child in children.iter_mut() {
361 child.maybe_merge_or_split(
362 level+1,
363 curr_prims,
364 max_split_levels,
365 );
366 }
367 }
368 }
369 }
370 }
371
372 pub fn update_dirty_rects(
374 &mut self,
375 prev_prims: &[PrimitiveDescriptor],
376 curr_prims: &[PrimitiveDescriptor],
377 prim_comparer: &mut PrimitiveComparer,
378 dirty_rect: &mut PictureBox2D,
379 compare_cache: &mut FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>,
380 invalidation_reason: &mut Option<InvalidationReason>,
381 frame_context: &FrameVisibilityContext,
382 ) {
383 match self.kind {
384 TileNodeKind::Node { ref mut children, .. } => {
385 for child in children.iter_mut() {
386 child.update_dirty_rects(
387 prev_prims,
388 curr_prims,
389 prim_comparer,
390 dirty_rect,
391 compare_cache,
392 invalidation_reason,
393 frame_context,
394 );
395 }
396 }
397 TileNodeKind::Leaf { ref prev_indices, ref curr_indices, ref mut dirty_tracker, .. } => {
398 if prev_indices.len() == curr_indices.len() {
400 for (prev_index, curr_index) in prev_indices.iter().zip(curr_indices.iter()) {
402 let i0 = prev_index.0 as usize;
403 let i1 = curr_index.0 as usize;
404
405 let key = PrimitiveComparisonKey {
408 prev_index: *prev_index,
409 curr_index: *curr_index,
410 };
411
412 let prim_compare_result = *compare_cache
413 .entry(key)
414 .or_insert_with(|| {
415 let prev = &prev_prims[i0];
416 let curr = &curr_prims[i1];
417 prim_comparer.compare_prim(prev, curr)
418 });
419
420 if prim_compare_result != PrimitiveCompareResult::Equal {
422 if invalidation_reason.is_none() {
423 *invalidation_reason = Some(InvalidationReason::Content);
424 }
425 *dirty_rect = self.rect.union(dirty_rect);
426 *dirty_tracker = *dirty_tracker | 1;
427 break;
428 }
429 }
430 } else {
431 if invalidation_reason.is_none() {
432 *invalidation_reason = Some(InvalidationReason::PrimCount);
433 }
434 *dirty_rect = self.rect.union(dirty_rect);
435 *dirty_tracker = *dirty_tracker | 1;
436 }
437 }
438 }
439 }
440}