1use api::{BorderRadius, ClipId, ClipMode, ColorF, DebugFlags, PrimitiveFlags, QualitySettings, RasterSpace};
6use api::units::*;
7use crate::clip::{clamped_radius, ClipItemKeyKind, ClipNodeId, ClipTreeBuilder, intersect_rounded_rects};
8use crate::frame_builder::FrameBuilderConfig;
9use crate::internal_types::FastHashMap;
10use crate::picture::{PrimitiveList, PictureCompositeMode, PictureInstance, Picture3DContext, PictureFlags};
11use crate::tile_cache::{SliceId, TileCacheParams};
12use crate::prim_store::{PrimitiveInstance, PrimitiveStore, PictureIndex};
13use crate::scene_building::SliceFlags;
14use crate::scene_builder_thread::Interners;
15use crate::spatial_tree::{SpatialNodeIndex, SceneSpatialTree};
16use crate::util::VecHelper;
17use std::mem;
18
19const MAX_CACHE_SLICES: usize = 16;
33
34struct SliceDescriptor {
35 prim_list: PrimitiveList,
36 scroll_root: SpatialNodeIndex,
37}
38
39enum SliceKind {
40 Default {
41 secondary_slices: Vec<SliceDescriptor>,
42 },
43 Atomic {
44 prim_list: PrimitiveList,
45 },
46}
47
48impl SliceKind {
49 fn default() -> Self {
50 SliceKind::Default {
51 secondary_slices: Vec::new(),
52 }
53 }
54}
55
56struct PrimarySlice {
57 kind: SliceKind,
59 background_color: Option<ColorF>,
61 iframe_clip: Option<ClipId>,
63 slice_flags: SliceFlags,
65}
66
67impl PrimarySlice {
68 fn new(
69 slice_flags: SliceFlags,
70 iframe_clip: Option<ClipId>,
71 background_color: Option<ColorF>,
72 ) -> Self {
73 PrimarySlice {
74 kind: SliceKind::default(),
75 background_color,
76 iframe_clip,
77 slice_flags,
78 }
79 }
80
81 fn has_too_many_slices(&self) -> bool {
82 match self.kind {
83 SliceKind::Atomic { .. } => false,
84 SliceKind::Default { ref secondary_slices } => secondary_slices.len() > MAX_CACHE_SLICES,
85 }
86 }
87
88 fn merge(&mut self) {
89 self.slice_flags |= SliceFlags::IS_ATOMIC;
90
91 let old = mem::replace(
92 &mut self.kind,
93 SliceKind::Default { secondary_slices: Vec::new() },
94 );
95
96 self.kind = match old {
97 SliceKind::Default { mut secondary_slices } => {
98 let mut prim_list = PrimitiveList::empty();
99
100 for descriptor in secondary_slices.drain(..) {
101 prim_list.merge(descriptor.prim_list);
102 }
103
104 SliceKind::Atomic {
105 prim_list,
106 }
107 }
108 atomic => atomic,
109 }
110 }
111}
112
113pub struct TileCacheBuilder {
115 primary_slices: Vec<PrimarySlice>,
117 prev_scroll_root_cache: (SpatialNodeIndex, SpatialNodeIndex),
119 root_spatial_node_index: SpatialNodeIndex,
121 debug_flags: DebugFlags,
123}
124
125pub struct TileCacheConfig {
129 pub tile_caches: FastHashMap<SliceId, TileCacheParams>,
131 pub picture_cache_slice_count: usize,
133}
134
135impl TileCacheConfig {
136 pub fn new(picture_cache_slice_count: usize) -> Self {
137 TileCacheConfig {
138 tile_caches: FastHashMap::default(),
139 picture_cache_slice_count,
140 }
141 }
142}
143
144impl TileCacheBuilder {
145 pub fn new(
147 root_spatial_node_index: SpatialNodeIndex,
148 background_color: Option<ColorF>,
149 debug_flags: DebugFlags,
150 ) -> Self {
151 TileCacheBuilder {
152 primary_slices: vec![PrimarySlice::new(SliceFlags::empty(), None, background_color)],
153 prev_scroll_root_cache: (SpatialNodeIndex::INVALID, SpatialNodeIndex::INVALID),
154 root_spatial_node_index,
155 debug_flags,
156 }
157 }
158
159 pub fn make_current_slice_atomic(&mut self) {
160 self.primary_slices
161 .last_mut()
162 .unwrap()
163 .merge();
164 }
165
166 pub fn is_current_slice_empty(&self) -> bool {
168 match self.primary_slices.last() {
169 Some(slice) => {
170 match slice.kind {
171 SliceKind::Default { ref secondary_slices } => {
172 secondary_slices.is_empty()
173 }
174 SliceKind::Atomic { ref prim_list } => {
175 prim_list.is_empty()
176 }
177 }
178 }
179 None => {
180 true
181 }
182 }
183 }
184
185 pub fn add_tile_cache_barrier(
187 &mut self,
188 slice_flags: SliceFlags,
189 iframe_clip: Option<ClipId>,
190 ) {
191 let new_slice = PrimarySlice::new(
192 slice_flags,
193 iframe_clip,
194 None,
195 );
196
197 self.primary_slices.push(new_slice);
198 }
199
200 fn build_tile_cache(
202 &mut self,
203 prim_list: PrimitiveList,
204 spatial_tree: &SceneSpatialTree,
205 ) -> Option<SliceDescriptor> {
206 if prim_list.is_empty() {
207 return None;
208 }
209
210 let mut scroll_root_occurrences = FastHashMap::default();
216
217 for cluster in &prim_list.clusters {
218 if cluster.spatial_node_index == SpatialNodeIndex::UNKNOWN {
223 continue;
224 }
225
226 let scroll_root = find_scroll_root(
227 cluster.spatial_node_index,
228 &mut self.prev_scroll_root_cache,
229 spatial_tree,
230 true,
231 );
232
233 *scroll_root_occurrences.entry(scroll_root).or_insert(0) += 1;
234 }
235
236 let scroll_roots: Vec<SpatialNodeIndex> = scroll_root_occurrences
243 .keys()
244 .cloned()
245 .collect();
246
247 scroll_root_occurrences.retain(|parent_spatial_node_index, _| {
248 scroll_roots.iter().all(|child_spatial_node_index| {
249 parent_spatial_node_index == child_spatial_node_index ||
250 spatial_tree.is_ancestor(
251 *parent_spatial_node_index,
252 *child_spatial_node_index,
253 )
254 })
255 });
256
257 let scroll_root = scroll_root_occurrences
259 .iter()
260 .max_by_key(|entry | entry.1)
261 .map(|(spatial_node_index, _)| *spatial_node_index)
262 .unwrap_or(self.root_spatial_node_index);
263
264 Some(SliceDescriptor {
265 scroll_root,
266 prim_list,
267 })
268 }
269
270 pub fn add_prim(
272 &mut self,
273 prim_instance: PrimitiveInstance,
274 prim_rect: LayoutRect,
275 spatial_node_index: SpatialNodeIndex,
276 prim_flags: PrimitiveFlags,
277 spatial_tree: &SceneSpatialTree,
278 quality_settings: &QualitySettings,
279 prim_instances: &mut Vec<PrimitiveInstance>,
280 clip_tree_builder: &ClipTreeBuilder,
281 ) {
282 let primary_slice = self.primary_slices.last_mut().unwrap();
283
284 match primary_slice.kind {
285 SliceKind::Atomic { ref mut prim_list } => {
286 prim_list.add_prim(
287 prim_instance,
288 prim_rect,
289 spatial_node_index,
290 prim_flags,
291 prim_instances,
292 clip_tree_builder,
293 );
294 }
295 SliceKind::Default { ref mut secondary_slices } => {
296 assert_ne!(spatial_node_index, SpatialNodeIndex::UNKNOWN);
297
298 let scroll_root = find_scroll_root(
300 spatial_node_index,
301 &mut self.prev_scroll_root_cache,
302 spatial_tree,
303 !quality_settings.force_subpixel_aa_where_possible,
306 );
307
308 let current_scroll_root = secondary_slices
309 .last()
310 .map(|p| p.scroll_root);
311
312 let mut want_new_tile_cache = secondary_slices.is_empty();
313
314 if let Some(current_scroll_root) = current_scroll_root {
315 want_new_tile_cache |= match (current_scroll_root, scroll_root) {
316 (_, _) if current_scroll_root == self.root_spatial_node_index && scroll_root == self.root_spatial_node_index => {
317 false
319 }
320 (_, _) if current_scroll_root == self.root_spatial_node_index => {
321 true
323 }
324 (_, _) if scroll_root == self.root_spatial_node_index => {
325 if quality_settings.force_subpixel_aa_where_possible {
328 false
329 } else {
330 let mut create_slice = true;
338
339 let leaf = clip_tree_builder.get_leaf(prim_instance.clip_leaf_id);
340 let mut current_node_id = leaf.node_id;
341
342 while current_node_id != ClipNodeId::NONE {
343 let node = clip_tree_builder.get_node(current_node_id);
344
345 let spatial_root = find_scroll_root(
346 node.spatial_node_index,
347 &mut self.prev_scroll_root_cache,
348 spatial_tree,
349 true,
350 );
351
352 if spatial_root != self.root_spatial_node_index {
353 create_slice = false;
354 break;
355 }
356
357 current_node_id = node.parent;
358 }
359
360 create_slice
361 }
362 }
363 (curr_scroll_root, scroll_root) => {
364 curr_scroll_root != scroll_root
366 }
367 };
368 }
369
370 if want_new_tile_cache {
371 secondary_slices.push(SliceDescriptor {
372 prim_list: PrimitiveList::empty(),
373 scroll_root,
374 });
375 }
376
377 secondary_slices
378 .last_mut()
379 .unwrap()
380 .prim_list
381 .add_prim(
382 prim_instance,
383 prim_rect,
384 spatial_node_index,
385 prim_flags,
386 prim_instances,
387 clip_tree_builder,
388 );
389 }
390 }
391 }
392
393 pub fn build(
395 mut self,
396 config: &FrameBuilderConfig,
397 prim_store: &mut PrimitiveStore,
398 spatial_tree: &SceneSpatialTree,
399 prim_instances: &[PrimitiveInstance],
400 clip_tree_builder: &mut ClipTreeBuilder,
401 interners: &Interners,
402 ) -> (TileCacheConfig, Vec<PictureIndex>) {
403 let mut result = TileCacheConfig::new(self.primary_slices.len());
404 let mut tile_cache_pictures = Vec::new();
405 let primary_slices = std::mem::replace(&mut self.primary_slices, Vec::new());
406
407 let visibility_node = spatial_tree.root_reference_frame_index();
411
412 for mut primary_slice in primary_slices {
413
414 if primary_slice.has_too_many_slices() {
415 primary_slice.merge();
416 }
417
418 match primary_slice.kind {
419 SliceKind::Atomic { prim_list } => {
420 if let Some(descriptor) = self.build_tile_cache(
421 prim_list,
422 spatial_tree,
423 ) {
424 create_tile_cache(
425 self.debug_flags,
426 primary_slice.slice_flags,
427 descriptor.scroll_root,
428 visibility_node,
429 primary_slice.iframe_clip,
430 descriptor.prim_list,
431 primary_slice.background_color,
432 prim_store,
433 prim_instances,
434 config,
435 &mut result.tile_caches,
436 &mut tile_cache_pictures,
437 clip_tree_builder,
438 interners,
439 spatial_tree,
440 );
441 }
442 }
443 SliceKind::Default { secondary_slices } => {
444 for descriptor in secondary_slices {
445 create_tile_cache(
446 self.debug_flags,
447 primary_slice.slice_flags,
448 descriptor.scroll_root,
449 visibility_node,
450 primary_slice.iframe_clip,
451 descriptor.prim_list,
452 primary_slice.background_color,
453 prim_store,
454 prim_instances,
455 config,
456 &mut result.tile_caches,
457 &mut tile_cache_pictures,
458 clip_tree_builder,
459 interners,
460 spatial_tree,
461 );
462 }
463 }
464 }
465 }
466
467 (result, tile_cache_pictures)
468 }
469}
470
471fn find_scroll_root(
473 spatial_node_index: SpatialNodeIndex,
474 prev_scroll_root_cache: &mut (SpatialNodeIndex, SpatialNodeIndex),
475 spatial_tree: &SceneSpatialTree,
476 allow_sticky_frames: bool,
477) -> SpatialNodeIndex {
478 if prev_scroll_root_cache.0 == spatial_node_index {
479 return prev_scroll_root_cache.1;
480 }
481
482 let scroll_root = spatial_tree.find_scroll_root(spatial_node_index, allow_sticky_frames);
483 *prev_scroll_root_cache = (spatial_node_index, scroll_root);
484
485 scroll_root
486}
487
488fn create_tile_cache(
491 debug_flags: DebugFlags,
492 slice_flags: SliceFlags,
493 scroll_root: SpatialNodeIndex,
494 visibility_node: SpatialNodeIndex,
495 iframe_clip: Option<ClipId>,
496 prim_list: PrimitiveList,
497 background_color: Option<ColorF>,
498 prim_store: &mut PrimitiveStore,
499 prim_instances: &[PrimitiveInstance],
500 frame_builder_config: &FrameBuilderConfig,
501 tile_caches: &mut FastHashMap<SliceId, TileCacheParams>,
502 tile_cache_pictures: &mut Vec<PictureIndex>,
503 clip_tree_builder: &mut ClipTreeBuilder,
504 interners: &Interners,
505 spatial_tree: &SceneSpatialTree,
506) {
507 let mut additional_clips = Vec::new();
510
511 if let Some(clip_id) = iframe_clip {
512 additional_clips.push(clip_id);
513 }
514
515 let mut shared_clip_node_id = None;
522
523 for cluster in &prim_list.clusters {
524 for prim_instance in &prim_instances[cluster.prim_range()] {
525 let leaf = clip_tree_builder.get_leaf(prim_instance.clip_leaf_id);
526
527 shared_clip_node_id = match shared_clip_node_id {
529 Some(current) => {
530 Some(clip_tree_builder.find_lowest_common_ancestor(current, leaf.node_id))
531 }
532 None => {
533 Some(leaf.node_id)
534 }
535 }
536 }
537 }
538
539 let mut shared_clip_node_id = shared_clip_node_id.unwrap_or(ClipNodeId::NONE);
546 let mut current_node_id = shared_clip_node_id;
547 let mut rounded_rect_count = 0;
548
549 let mut accumulated_rounded_rect: Option<(LayoutRect, BorderRadius)> = None;
552
553 while current_node_id != ClipNodeId::NONE {
562 let node = clip_tree_builder.get_node(current_node_id);
563 let clip_node_data = &interners.clip[node.handle];
564
565 let is_rcs = spatial_tree.is_root_coord_system(node.spatial_node_index);
567
568 let node_valid = if is_rcs {
569 match clip_node_data.key.kind {
570 ClipItemKeyKind::ImageMask(..) |
571 ClipItemKeyKind::Rectangle(ClipMode::ClipOut) |
572 ClipItemKeyKind::RoundedRectangle(_, ClipMode::ClipOut) => {
573 false
575 }
576 ClipItemKeyKind::RoundedRectangle(radius, ClipMode::Clip) => {
577 let br = clamped_radius(&BorderRadius::from(radius), node.unsnapped_clip_rect.size());
580 if br.can_use_fast_path_in(&node.unsnapped_clip_rect) {
581 rounded_rect_count += 1;
582
583 if accumulated_rounded_rect.is_none() {
584 accumulated_rounded_rect = Some((node.unsnapped_clip_rect, br));
585 }
586
587 true
588 } else {
589 false
590 }
591 }
592 ClipItemKeyKind::Rectangle(ClipMode::Clip) => {
593 true
596 }
597 }
598 } else {
599 false
601 };
602
603 if node_valid {
604 if rounded_rect_count > 1 {
606 let can_combine = match (accumulated_rounded_rect, clip_node_data.key.kind) {
612 (
613 Some((acc_rect, acc_radius)),
614 ClipItemKeyKind::RoundedRectangle(radius, ClipMode::Clip),
615 ) => {
616 let radius = clamped_radius(&BorderRadius::from(radius), node.unsnapped_clip_rect.size());
617 intersect_rounded_rects(
618 acc_rect, acc_radius,
619 node.unsnapped_clip_rect, radius,
620 )
621 }
622 _ => None,
623 };
624
625 if let Some((combined_rect, combined_radius)) = can_combine {
626 rounded_rect_count = 1;
630 accumulated_rounded_rect = Some((combined_rect, combined_radius));
631 } else {
632 shared_clip_node_id = current_node_id;
634 rounded_rect_count = 1;
635 if let ClipItemKeyKind::RoundedRectangle(radius, ClipMode::Clip) = clip_node_data.key.kind {
636 let radius = clamped_radius(&BorderRadius::from(radius), node.unsnapped_clip_rect.size());
637 accumulated_rounded_rect = Some((node.unsnapped_clip_rect, radius));
638 }
639 }
640 }
641 } else {
642 shared_clip_node_id = node.parent;
646 rounded_rect_count = 0;
647 accumulated_rounded_rect = None;
648 }
649
650 current_node_id = node.parent;
651 }
652
653 let shared_clip_leaf_id = Some(clip_tree_builder.build_for_tile_cache(
654 shared_clip_node_id,
655 &additional_clips,
656 ));
657
658 let slice = tile_cache_pictures.len();
666
667 let background_color = if slice == 0 {
668 background_color
669 } else {
670 None
671 };
672
673 let slice_id = SliceId::new(slice);
674
675 tile_caches.insert(slice_id, TileCacheParams {
678 debug_flags,
679 slice,
680 slice_flags,
681 spatial_node_index: scroll_root,
682 visibility_node_index: visibility_node,
683 background_color,
684 shared_clip_node_id,
685 shared_clip_leaf_id,
686 virtual_surface_size: frame_builder_config.compositor_kind.get_virtual_surface_size(),
687 image_surface_count: prim_list.image_surface_count,
688 yuv_image_surface_count: prim_list.yuv_image_surface_count,
689 });
690
691 let pic_index = prim_store.pictures.alloc().init(PictureInstance::new_image(
692 Some(PictureCompositeMode::TileCache { slice_id }),
693 Picture3DContext::Out,
694 PrimitiveFlags::IS_BACKFACE_VISIBLE,
695 prim_list,
696 scroll_root,
697 RasterSpace::Screen,
698 PictureFlags::empty(),
699 None,
700 ));
701
702 tile_cache_pictures.push(PictureIndex(pic_index));
703}
704
705#[derive(Debug)]
707#[cfg_attr(feature = "capture", derive(Serialize))]
708#[cfg_attr(feature = "replay", derive(Deserialize))]
709pub struct PictureCacheDebugInfo {
710 pub slices: FastHashMap<usize, SliceDebugInfo>,
711}
712
713impl PictureCacheDebugInfo {
714 pub fn new() -> Self {
715 PictureCacheDebugInfo {
716 slices: FastHashMap::default(),
717 }
718 }
719
720 pub fn slice(&self, slice: usize) -> &SliceDebugInfo {
723 &self.slices[&slice]
724 }
725}
726
727impl Default for PictureCacheDebugInfo {
728 fn default() -> PictureCacheDebugInfo {
729 PictureCacheDebugInfo::new()
730 }
731}
732
733#[derive(Debug, Clone, PartialEq)]
735#[cfg_attr(feature = "capture", derive(Serialize))]
736#[cfg_attr(feature = "replay", derive(Deserialize))]
737pub struct CompositorClipDebugInfo {
738 pub rect: DeviceRect,
739 pub radius: BorderRadius,
740}
741
742#[derive(Debug)]
744#[cfg_attr(feature = "capture", derive(Serialize))]
745#[cfg_attr(feature = "replay", derive(Deserialize))]
746pub struct SliceDebugInfo {
747 pub tiles: FastHashMap<TileOffset, TileDebugInfo>,
748 pub compositor_clip: Option<CompositorClipDebugInfo>,
749}
750
751impl SliceDebugInfo {
752 pub fn new() -> Self {
753 SliceDebugInfo {
754 tiles: FastHashMap::default(),
755 compositor_clip: None,
756 }
757 }
758
759 pub fn tile(&self, x: i32, y: i32) -> &TileDebugInfo {
762 &self.tiles[&TileOffset::new(x, y)]
763 }
764}
765
766#[derive(Debug, PartialEq)]
768#[cfg_attr(feature = "capture", derive(Serialize))]
769#[cfg_attr(feature = "replay", derive(Deserialize))]
770pub struct DirtyTileDebugInfo {
771 pub local_valid_rect: PictureRect,
772 pub local_dirty_rect: PictureRect,
773}
774
775#[derive(Debug, PartialEq)]
777#[cfg_attr(feature = "capture", derive(Serialize))]
778#[cfg_attr(feature = "replay", derive(Deserialize))]
779pub enum TileDebugInfo {
780 Occluded,
782 Culled,
784 Valid,
786 Dirty(DirtyTileDebugInfo),
788}
789
790impl TileDebugInfo {
791 pub fn is_occluded(&self) -> bool {
792 match self {
793 TileDebugInfo::Occluded => true,
794 TileDebugInfo::Culled |
795 TileDebugInfo::Valid |
796 TileDebugInfo::Dirty(..) => false,
797 }
798 }
799
800 pub fn is_valid(&self) -> bool {
801 match self {
802 TileDebugInfo::Valid => true,
803 TileDebugInfo::Culled |
804 TileDebugInfo::Occluded |
805 TileDebugInfo::Dirty(..) => false,
806 }
807 }
808
809 pub fn is_culled(&self) -> bool {
810 match self {
811 TileDebugInfo::Culled => true,
812 TileDebugInfo::Valid |
813 TileDebugInfo::Occluded |
814 TileDebugInfo::Dirty(..) => false,
815 }
816 }
817
818 pub fn as_dirty(&self) -> &DirtyTileDebugInfo {
819 match self {
820 TileDebugInfo::Occluded |
821 TileDebugInfo::Culled |
822 TileDebugInfo::Valid => {
823 panic!("not a dirty tile!");
824 }
825 TileDebugInfo::Dirty(ref info) => {
826 info
827 }
828 }
829 }
830}