1use api::{ColorF, PrimitiveFlags, QualitySettings};
6use api::units::*;
7use crate::clip::{ClipChainId, ClipNodeKind, ClipStore, ClipInstance};
8use crate::frame_builder::FrameBuilderConfig;
9use crate::internal_types::{FastHashMap, FastHashSet};
10use crate::picture::{PrimitiveList, PictureCompositeMode, PictureOptions, PicturePrimitive, SliceId};
11use crate::picture::{Picture3DContext, TileCacheParams, TileOffset};
12use crate::prim_store::{PrimitiveInstance, PrimitiveStore, PictureIndex};
13use crate::scene_building::SliceFlags;
14use crate::scene_builder_thread::Interners;
15use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex, SpatialTree};
16use crate::util::VecHelper;
17
18const MAX_CACHE_SLICES: usize = 12;
32
33pub struct PendingTileCache {
35 pub prim_list: PrimitiveList,
37 pub params: TileCacheParams,
39 pub iframe_clip: Option<ClipChainId>,
41}
42
43pub struct TileCacheBuilder {
45 force_new_tile_cache: Option<SliceFlags>,
47 pending_tile_caches: Vec<PendingTileCache>,
49
50 prev_scroll_root_cache: (SpatialNodeIndex, SpatialNodeIndex),
52 prim_clips_buffer: Vec<ClipInstance>,
54 last_checked_clip_chain: ClipChainId,
56}
57
58pub struct TileCacheConfig {
62 pub tile_caches: FastHashMap<SliceId, TileCacheParams>,
64 pub picture_cache_spatial_nodes: FastHashSet<SpatialNodeIndex>,
71 pub picture_cache_slice_count: usize,
73}
74
75impl TileCacheConfig {
76 pub fn new(picture_cache_slice_count: usize) -> Self {
77 TileCacheConfig {
78 tile_caches: FastHashMap::default(),
79 picture_cache_spatial_nodes: FastHashSet::default(),
80 picture_cache_slice_count,
81 }
82 }
83}
84
85impl TileCacheBuilder {
86 pub fn new() -> Self {
88 TileCacheBuilder {
89 force_new_tile_cache: None,
90 pending_tile_caches: Vec::new(),
91 prev_scroll_root_cache: (ROOT_SPATIAL_NODE_INDEX, ROOT_SPATIAL_NODE_INDEX),
92 prim_clips_buffer: Vec::new(),
93 last_checked_clip_chain: ClipChainId::INVALID,
94 }
95 }
96
97 pub fn add_tile_cache_barrier(
99 &mut self,
100 slice_flags: SliceFlags,
101 ) {
102 self.force_new_tile_cache = Some(slice_flags);
103 }
104
105 pub fn can_add_container_tile_cache(&self) -> bool {
108 self.pending_tile_caches.len() < MAX_CACHE_SLICES-1
111 }
112
113 pub fn add_tile_cache(
115 &mut self,
116 prim_list: PrimitiveList,
117 clip_chain_id: ClipChainId,
118 spatial_tree: &SpatialTree,
119 clip_store: &ClipStore,
120 interners: &Interners,
121 config: &FrameBuilderConfig,
122 iframe_clip: Option<ClipChainId>,
123 slice_flags: SliceFlags,
124 ) {
125 assert!(self.can_add_container_tile_cache());
126
127 if prim_list.is_empty() {
128 return;
129 }
130
131 let mut scroll_root_occurrences = FastHashMap::default();
137
138 for cluster in &prim_list.clusters {
139 let scroll_root = self.find_scroll_root(
140 cluster.spatial_node_index,
141 spatial_tree,
142 );
143
144 *scroll_root_occurrences.entry(scroll_root).or_insert(0) += 1;
145 }
146
147 let scroll_roots: Vec<SpatialNodeIndex> = scroll_root_occurrences
154 .keys()
155 .cloned()
156 .collect();
157
158 scroll_root_occurrences.retain(|parent_spatial_node_index, _| {
159 scroll_roots.iter().all(|child_spatial_node_index| {
160 parent_spatial_node_index == child_spatial_node_index ||
161 spatial_tree.is_ancestor(
162 *parent_spatial_node_index,
163 *child_spatial_node_index,
164 )
165 })
166 });
167
168 let scroll_root = scroll_root_occurrences
170 .iter()
171 .max_by_key(|entry | entry.1)
172 .map(|(spatial_node_index, _)| *spatial_node_index)
173 .unwrap_or(ROOT_SPATIAL_NODE_INDEX);
174
175 let mut first = true;
176 let prim_clips_buffer = &mut self.prim_clips_buffer;
177 let mut shared_clips = Vec::new();
178
179 for cluster in &prim_list.clusters {
184 for prim_instance in &prim_list.prim_instances[cluster.prim_range()] {
185 if first {
186 add_clips(
187 scroll_root,
188 prim_instance.clip_set.clip_chain_id,
189 &mut shared_clips,
190 clip_store,
191 interners,
192 spatial_tree,
193 );
194
195 self.last_checked_clip_chain = prim_instance.clip_set.clip_chain_id;
196 first = false;
197 } else {
198 if self.last_checked_clip_chain != prim_instance.clip_set.clip_chain_id {
199 prim_clips_buffer.clear();
200
201 add_clips(
202 scroll_root,
203 prim_instance.clip_set.clip_chain_id,
204 prim_clips_buffer,
205 clip_store,
206 interners,
207 spatial_tree,
208 );
209
210 shared_clips.retain(|h1: &ClipInstance| {
211 let uid = h1.handle.uid();
212 prim_clips_buffer.iter().any(|h2| {
213 uid == h2.handle.uid() &&
214 h1.spatial_node_index == h2.spatial_node_index
215 })
216 });
217
218 self.last_checked_clip_chain = prim_instance.clip_set.clip_chain_id;
219 }
220 }
221 }
222 }
223
224 let mut current_clip_chain_id = clip_chain_id;
228 while current_clip_chain_id != ClipChainId::NONE {
229 let clip_chain_node = &clip_store
230 .clip_chain_nodes[current_clip_chain_id.0 as usize];
231
232 let clip_node_data = &interners.clip[clip_chain_node.handle];
233 if let ClipNodeKind::Rectangle = clip_node_data.clip_node_kind {
234 shared_clips.push(ClipInstance::new(clip_chain_node.handle, clip_chain_node.spatial_node_index));
235 }
236
237 current_clip_chain_id = clip_chain_node.parent_clip_chain_id;
238 }
239
240 let slice = self.pending_tile_caches.len();
242
243 let params = TileCacheParams {
244 slice,
245 slice_flags,
246 spatial_node_index: scroll_root,
247 background_color: None,
248 shared_clips,
249 shared_clip_chain: ClipChainId::NONE,
250 virtual_surface_size: config.compositor_kind.get_virtual_surface_size(),
251 compositor_surface_count: prim_list.compositor_surface_count,
252 };
253
254 self.pending_tile_caches.push(PendingTileCache {
255 prim_list,
256 params,
257 iframe_clip,
258 });
259
260 self.force_new_tile_cache = Some(SliceFlags::empty());
263 }
264
265 pub fn add_prim(
267 &mut self,
268 prim_instance: PrimitiveInstance,
269 prim_rect: LayoutRect,
270 spatial_node_index: SpatialNodeIndex,
271 prim_flags: PrimitiveFlags,
272 spatial_tree: &SpatialTree,
273 clip_store: &ClipStore,
274 interners: &Interners,
275 config: &FrameBuilderConfig,
276 quality_settings: &QualitySettings,
277 iframe_clip: Option<ClipChainId>,
278 ) {
279 let scroll_root = self.find_scroll_root(spatial_node_index, spatial_tree);
281
282 let mut want_new_tile_cache =
284 self.force_new_tile_cache.is_some() ||
285 self.pending_tile_caches.is_empty();
286
287 let current_scroll_root = self.pending_tile_caches
288 .last()
289 .map(|p| p.params.spatial_node_index);
290
291 if let Some(current_scroll_root) = current_scroll_root {
292 want_new_tile_cache |= match (current_scroll_root, scroll_root) {
293 (ROOT_SPATIAL_NODE_INDEX, ROOT_SPATIAL_NODE_INDEX) => {
294 false
296 }
297 (ROOT_SPATIAL_NODE_INDEX, _) => {
298 true
300 }
301 (_, ROOT_SPATIAL_NODE_INDEX) => {
302 if quality_settings.force_subpixel_aa_where_possible {
305 false
306 } else {
307 let mut create_slice = true;
315 let mut current_clip_chain_id = prim_instance.clip_set.clip_chain_id;
316
317 while current_clip_chain_id != ClipChainId::NONE {
318 let clip_chain_node = &clip_store.clip_chain_nodes[current_clip_chain_id.0 as usize];
319 let spatial_root = self.find_scroll_root(clip_chain_node.spatial_node_index, spatial_tree);
320 if spatial_root != ROOT_SPATIAL_NODE_INDEX {
321 create_slice = false;
322 break;
323 }
324 current_clip_chain_id = clip_chain_node.parent_clip_chain_id;
325 }
326
327 create_slice
328 }
329 }
330 (curr_scroll_root, scroll_root) => {
331 curr_scroll_root != scroll_root
333 }
334 };
335
336 if self.last_checked_clip_chain != prim_instance.clip_set.clip_chain_id {
339 let prim_clips_buffer = &mut self.prim_clips_buffer;
340 prim_clips_buffer.clear();
341 add_clips(
342 current_scroll_root,
343 prim_instance.clip_set.clip_chain_id,
344 prim_clips_buffer,
345 clip_store,
346 interners,
347 spatial_tree,
348 );
349
350 let current_shared_clips = &self.pending_tile_caches
351 .last()
352 .unwrap()
353 .params
354 .shared_clips;
355
356 want_new_tile_cache |= current_shared_clips != prim_clips_buffer;
362
363 self.last_checked_clip_chain = prim_instance.clip_set.clip_chain_id;
364 }
365 }
366
367 if want_new_tile_cache {
368 let slice = self.pending_tile_caches.len();
369
370 if slice < MAX_CACHE_SLICES {
373 let (params, iframe_clip) = if slice == MAX_CACHE_SLICES-1 {
382 let params = TileCacheParams {
383 slice,
384 slice_flags: SliceFlags::empty(),
385 spatial_node_index: ROOT_SPATIAL_NODE_INDEX,
386 background_color: None,
387 shared_clips: Vec::new(),
388 shared_clip_chain: ClipChainId::NONE,
389 virtual_surface_size: config.compositor_kind.get_virtual_surface_size(),
390 compositor_surface_count: 0,
391 };
392
393 (params, None)
394 } else {
395 let slice_flags = self.force_new_tile_cache.unwrap_or(SliceFlags::empty());
396
397 let background_color = if slice == 0 {
398 config.background_color
399 } else {
400 None
401 };
402
403 let mut shared_clips = Vec::new();
404 add_clips(
405 scroll_root,
406 prim_instance.clip_set.clip_chain_id,
407 &mut shared_clips,
408 clip_store,
409 interners,
410 spatial_tree,
411 );
412
413 self.last_checked_clip_chain = prim_instance.clip_set.clip_chain_id;
414
415 let params = TileCacheParams {
416 slice,
417 slice_flags,
418 spatial_node_index: scroll_root,
419 background_color,
420 shared_clips,
421 shared_clip_chain: ClipChainId::NONE,
422 virtual_surface_size: config.compositor_kind.get_virtual_surface_size(),
423 compositor_surface_count: 0,
424 };
425
426 (params, iframe_clip)
427 };
428
429 self.pending_tile_caches.push(PendingTileCache {
430 prim_list: PrimitiveList::empty(),
431 params,
432 iframe_clip,
433 });
434
435 self.force_new_tile_cache = None;
436 }
437 }
438
439 self.pending_tile_caches
440 .last_mut()
441 .unwrap()
442 .prim_list
443 .add_prim(
444 prim_instance,
445 prim_rect,
446 spatial_node_index,
447 prim_flags,
448 );
449 }
450
451 pub fn build(
453 self,
454 config: &FrameBuilderConfig,
455 clip_store: &mut ClipStore,
456 prim_store: &mut PrimitiveStore,
457 interners: &Interners,
458 ) -> (TileCacheConfig, Vec<PictureIndex>) {
459 let mut result = TileCacheConfig::new(self.pending_tile_caches.len());
460 let mut tile_cache_pictures = Vec::new();
461
462 for mut pending_tile_cache in self.pending_tile_caches {
463 if let Some(clip_chain_id) = pending_tile_cache.iframe_clip {
466 add_all_rect_clips(
467 clip_chain_id,
468 &mut pending_tile_cache.params.shared_clips,
469 clip_store,
470 interners,
471 );
472 }
473
474 let pic_index = create_tile_cache(
475 pending_tile_cache.params.slice,
476 pending_tile_cache.params.slice_flags,
477 pending_tile_cache.params.spatial_node_index,
478 pending_tile_cache.prim_list,
479 pending_tile_cache.params.background_color,
480 pending_tile_cache.params.shared_clips,
481 prim_store,
482 clip_store,
483 &mut result.picture_cache_spatial_nodes,
484 config,
485 &mut result.tile_caches,
486 );
487
488 tile_cache_pictures.push(pic_index);
489 }
490
491 (result, tile_cache_pictures)
492 }
493
494 fn find_scroll_root(
496 &mut self,
497 spatial_node_index: SpatialNodeIndex,
498 spatial_tree: &SpatialTree,
499 ) -> SpatialNodeIndex {
500 if self.prev_scroll_root_cache.0 == spatial_node_index {
501 return self.prev_scroll_root_cache.1;
502 }
503
504 let scroll_root = spatial_tree.find_scroll_root(spatial_node_index);
505 self.prev_scroll_root_cache = (spatial_node_index, scroll_root);
506
507 scroll_root
508 }
509}
510
511fn add_clips(
513 scroll_root: SpatialNodeIndex,
514 clip_chain_id: ClipChainId,
515 prim_clips: &mut Vec<ClipInstance>,
516 clip_store: &ClipStore,
517 interners: &Interners,
518 spatial_tree: &SpatialTree,
519) {
520 let mut current_clip_chain_id = clip_chain_id;
521
522 while current_clip_chain_id != ClipChainId::NONE {
523 let clip_chain_node = &clip_store
524 .clip_chain_nodes[current_clip_chain_id.0 as usize];
525
526 let clip_node_data = &interners.clip[clip_chain_node.handle];
527 if let ClipNodeKind::Rectangle = clip_node_data.clip_node_kind {
528 if spatial_tree.is_ancestor(
529 clip_chain_node.spatial_node_index,
530 scroll_root,
531 ) {
532 prim_clips.push(ClipInstance::new(clip_chain_node.handle, clip_chain_node.spatial_node_index));
533 }
534 }
535
536 current_clip_chain_id = clip_chain_node.parent_clip_chain_id;
537 }
538}
539
540fn add_all_rect_clips(
542 clip_chain_id: ClipChainId,
543 prim_clips: &mut Vec<ClipInstance>,
544 clip_store: &ClipStore,
545 interners: &Interners,
546) {
547 let mut current_clip_chain_id = clip_chain_id;
548
549 while current_clip_chain_id != ClipChainId::NONE {
550 let clip_chain_node = &clip_store
551 .clip_chain_nodes[current_clip_chain_id.0 as usize];
552
553 let clip_node_data = &interners.clip[clip_chain_node.handle];
554 if let ClipNodeKind::Rectangle = clip_node_data.clip_node_kind {
555 prim_clips.push(ClipInstance::new(clip_chain_node.handle, clip_chain_node.spatial_node_index));
556 }
557
558 current_clip_chain_id = clip_chain_node.parent_clip_chain_id;
559 }
560}
561
562fn create_tile_cache(
565 slice: usize,
566 slice_flags: SliceFlags,
567 scroll_root: SpatialNodeIndex,
568 prim_list: PrimitiveList,
569 background_color: Option<ColorF>,
570 shared_clips: Vec<ClipInstance>,
571 prim_store: &mut PrimitiveStore,
572 clip_store: &mut ClipStore,
573 picture_cache_spatial_nodes: &mut FastHashSet<SpatialNodeIndex>,
574 frame_builder_config: &FrameBuilderConfig,
575 tile_caches: &mut FastHashMap<SliceId, TileCacheParams>,
576) -> PictureIndex {
577 picture_cache_spatial_nodes.insert(scroll_root);
580
581 let mut parent_clip_chain_id = ClipChainId::NONE;
589 for clip_instance in &shared_clips {
590 picture_cache_spatial_nodes.insert(clip_instance.spatial_node_index);
593
594 parent_clip_chain_id = clip_store.add_clip_chain_node(
595 clip_instance.handle,
596 clip_instance.spatial_node_index,
597 parent_clip_chain_id,
598 );
599 }
600
601 let slice_id = SliceId::new(slice);
602
603 tile_caches.insert(slice_id, TileCacheParams {
606 slice,
607 slice_flags,
608 spatial_node_index: scroll_root,
609 background_color,
610 shared_clips,
611 shared_clip_chain: parent_clip_chain_id,
612 virtual_surface_size: frame_builder_config.compositor_kind.get_virtual_surface_size(),
613 compositor_surface_count: prim_list.compositor_surface_count,
614 });
615
616 let pic_index = prim_store.pictures.alloc().init(PicturePrimitive::new_image(
617 Some(PictureCompositeMode::TileCache { slice_id }),
618 Picture3DContext::Out,
619 true,
620 PrimitiveFlags::IS_BACKFACE_VISIBLE,
621 prim_list,
622 scroll_root,
623 PictureOptions::default(),
624 ));
625
626 PictureIndex(pic_index)
627}
628
629#[derive(Debug)]
631#[cfg_attr(feature = "capture", derive(Serialize))]
632#[cfg_attr(feature = "replay", derive(Deserialize))]
633pub struct PictureCacheDebugInfo {
634 pub slices: FastHashMap<usize, SliceDebugInfo>,
635}
636
637impl PictureCacheDebugInfo {
638 pub fn new() -> Self {
639 PictureCacheDebugInfo {
640 slices: FastHashMap::default(),
641 }
642 }
643
644 pub fn slice(&self, slice: usize) -> &SliceDebugInfo {
647 &self.slices[&slice]
648 }
649}
650
651impl Default for PictureCacheDebugInfo {
652 fn default() -> PictureCacheDebugInfo {
653 PictureCacheDebugInfo::new()
654 }
655}
656
657#[derive(Debug)]
659#[cfg_attr(feature = "capture", derive(Serialize))]
660#[cfg_attr(feature = "replay", derive(Deserialize))]
661pub struct SliceDebugInfo {
662 pub tiles: FastHashMap<TileOffset, TileDebugInfo>,
663}
664
665impl SliceDebugInfo {
666 pub fn new() -> Self {
667 SliceDebugInfo {
668 tiles: FastHashMap::default(),
669 }
670 }
671
672 pub fn tile(&self, x: i32, y: i32) -> &TileDebugInfo {
675 &self.tiles[&TileOffset::new(x, y)]
676 }
677}
678
679#[derive(Debug, PartialEq)]
681#[cfg_attr(feature = "capture", derive(Serialize))]
682#[cfg_attr(feature = "replay", derive(Deserialize))]
683pub struct DirtyTileDebugInfo {
684 pub local_valid_rect: PictureRect,
685 pub local_dirty_rect: PictureRect,
686}
687
688#[derive(Debug, PartialEq)]
690#[cfg_attr(feature = "capture", derive(Serialize))]
691#[cfg_attr(feature = "replay", derive(Deserialize))]
692pub enum TileDebugInfo {
693 Occluded,
695 Culled,
697 Valid,
699 Dirty(DirtyTileDebugInfo),
701}
702
703impl TileDebugInfo {
704 pub fn is_occluded(&self) -> bool {
705 match self {
706 TileDebugInfo::Occluded => true,
707 TileDebugInfo::Culled |
708 TileDebugInfo::Valid |
709 TileDebugInfo::Dirty(..) => false,
710 }
711 }
712
713 pub fn is_valid(&self) -> bool {
714 match self {
715 TileDebugInfo::Valid => true,
716 TileDebugInfo::Culled |
717 TileDebugInfo::Occluded |
718 TileDebugInfo::Dirty(..) => false,
719 }
720 }
721
722 pub fn is_culled(&self) -> bool {
723 match self {
724 TileDebugInfo::Culled => true,
725 TileDebugInfo::Valid |
726 TileDebugInfo::Occluded |
727 TileDebugInfo::Dirty(..) => false,
728 }
729 }
730
731 pub fn as_dirty(&self) -> &DirtyTileDebugInfo {
732 match self {
733 TileDebugInfo::Occluded |
734 TileDebugInfo::Culled |
735 TileDebugInfo::Valid => {
736 panic!("not a dirty tile!");
737 }
738 TileDebugInfo::Dirty(ref info) => {
739 info
740 }
741 }
742 }
743}