1use api::{BorderRadius, ClipId, ClipMode, ColorF, DebugFlags, PrimitiveFlags, QualitySettings, RasterSpace};
6use api::units::*;
7use crate::clip::{ClipItemKeyKind, ClipNodeId, ClipTreeBuilder};
8use crate::frame_builder::FrameBuilderConfig;
9use crate::internal_types::FastHashMap;
10use crate::picture::{PrimitiveList, PictureCompositeMode, PicturePrimitive, SliceId};
11use crate::picture::{Picture3DContext, TileCacheParams, TileOffset, PictureFlags};
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 = 12;
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 interners: &Interners,
279 quality_settings: &QualitySettings,
280 prim_instances: &mut Vec<PrimitiveInstance>,
281 clip_tree_builder: &ClipTreeBuilder,
282 ) {
283 let primary_slice = self.primary_slices.last_mut().unwrap();
284
285 match primary_slice.kind {
286 SliceKind::Atomic { ref mut prim_list } => {
287 prim_list.add_prim(
288 prim_instance,
289 prim_rect,
290 spatial_node_index,
291 prim_flags,
292 prim_instances,
293 clip_tree_builder,
294 );
295 }
296 SliceKind::Default { ref mut secondary_slices } => {
297 assert_ne!(spatial_node_index, SpatialNodeIndex::UNKNOWN);
298
299 let scroll_root = find_scroll_root(
301 spatial_node_index,
302 &mut self.prev_scroll_root_cache,
303 spatial_tree,
304 !quality_settings.force_subpixel_aa_where_possible,
307 );
308
309 let current_scroll_root = secondary_slices
310 .last()
311 .map(|p| p.scroll_root);
312
313 let mut want_new_tile_cache = secondary_slices.is_empty();
314
315 if let Some(current_scroll_root) = current_scroll_root {
316 want_new_tile_cache |= match (current_scroll_root, scroll_root) {
317 (_, _) if current_scroll_root == self.root_spatial_node_index && scroll_root == self.root_spatial_node_index => {
318 false
320 }
321 (_, _) if current_scroll_root == self.root_spatial_node_index => {
322 true
324 }
325 (_, _) if scroll_root == self.root_spatial_node_index => {
326 if quality_settings.force_subpixel_aa_where_possible {
329 false
330 } else {
331 let mut create_slice = true;
339
340 let leaf = clip_tree_builder.get_leaf(prim_instance.clip_leaf_id);
341 let mut current_node_id = leaf.node_id;
342
343 while current_node_id != ClipNodeId::NONE {
344 let node = clip_tree_builder.get_node(current_node_id);
345
346 let clip_node_data = &interners.clip[node.handle];
347
348 let spatial_root = find_scroll_root(
349 clip_node_data.key.spatial_node_index,
350 &mut self.prev_scroll_root_cache,
351 spatial_tree,
352 true,
353 );
354
355 if spatial_root != self.root_spatial_node_index {
356 create_slice = false;
357 break;
358 }
359
360 current_node_id = node.parent;
361 }
362
363 create_slice
364 }
365 }
366 (curr_scroll_root, scroll_root) => {
367 curr_scroll_root != scroll_root
369 }
370 };
371 }
372
373 if want_new_tile_cache {
374 secondary_slices.push(SliceDescriptor {
375 prim_list: PrimitiveList::empty(),
376 scroll_root,
377 });
378 }
379
380 secondary_slices
381 .last_mut()
382 .unwrap()
383 .prim_list
384 .add_prim(
385 prim_instance,
386 prim_rect,
387 spatial_node_index,
388 prim_flags,
389 prim_instances,
390 clip_tree_builder,
391 );
392 }
393 }
394 }
395
396 pub fn build(
398 mut self,
399 config: &FrameBuilderConfig,
400 prim_store: &mut PrimitiveStore,
401 spatial_tree: &SceneSpatialTree,
402 prim_instances: &[PrimitiveInstance],
403 clip_tree_builder: &mut ClipTreeBuilder,
404 interners: &Interners,
405 ) -> (TileCacheConfig, Vec<PictureIndex>) {
406 let mut result = TileCacheConfig::new(self.primary_slices.len());
407 let mut tile_cache_pictures = Vec::new();
408 let primary_slices = std::mem::replace(&mut self.primary_slices, Vec::new());
409
410 let visibility_node = spatial_tree.root_reference_frame_index();
414
415 for mut primary_slice in primary_slices {
416
417 if primary_slice.has_too_many_slices() {
418 primary_slice.merge();
419 }
420
421 match primary_slice.kind {
422 SliceKind::Atomic { prim_list } => {
423 if let Some(descriptor) = self.build_tile_cache(
424 prim_list,
425 spatial_tree,
426 ) {
427 create_tile_cache(
428 self.debug_flags,
429 primary_slice.slice_flags,
430 descriptor.scroll_root,
431 visibility_node,
432 primary_slice.iframe_clip,
433 descriptor.prim_list,
434 primary_slice.background_color,
435 prim_store,
436 prim_instances,
437 config,
438 &mut result.tile_caches,
439 &mut tile_cache_pictures,
440 clip_tree_builder,
441 interners,
442 spatial_tree,
443 );
444 }
445 }
446 SliceKind::Default { secondary_slices } => {
447 for descriptor in secondary_slices {
448 create_tile_cache(
449 self.debug_flags,
450 primary_slice.slice_flags,
451 descriptor.scroll_root,
452 visibility_node,
453 primary_slice.iframe_clip,
454 descriptor.prim_list,
455 primary_slice.background_color,
456 prim_store,
457 prim_instances,
458 config,
459 &mut result.tile_caches,
460 &mut tile_cache_pictures,
461 clip_tree_builder,
462 interners,
463 spatial_tree,
464 );
465 }
466 }
467 }
468 }
469
470 (result, tile_cache_pictures)
471 }
472}
473
474fn find_scroll_root(
476 spatial_node_index: SpatialNodeIndex,
477 prev_scroll_root_cache: &mut (SpatialNodeIndex, SpatialNodeIndex),
478 spatial_tree: &SceneSpatialTree,
479 allow_sticky_frames: bool,
480) -> SpatialNodeIndex {
481 if prev_scroll_root_cache.0 == spatial_node_index {
482 return prev_scroll_root_cache.1;
483 }
484
485 let scroll_root = spatial_tree.find_scroll_root(spatial_node_index, allow_sticky_frames);
486 *prev_scroll_root_cache = (spatial_node_index, scroll_root);
487
488 scroll_root
489}
490
491fn create_tile_cache(
494 debug_flags: DebugFlags,
495 slice_flags: SliceFlags,
496 scroll_root: SpatialNodeIndex,
497 visibility_node: SpatialNodeIndex,
498 iframe_clip: Option<ClipId>,
499 prim_list: PrimitiveList,
500 background_color: Option<ColorF>,
501 prim_store: &mut PrimitiveStore,
502 prim_instances: &[PrimitiveInstance],
503 frame_builder_config: &FrameBuilderConfig,
504 tile_caches: &mut FastHashMap<SliceId, TileCacheParams>,
505 tile_cache_pictures: &mut Vec<PictureIndex>,
506 clip_tree_builder: &mut ClipTreeBuilder,
507 interners: &Interners,
508 spatial_tree: &SceneSpatialTree,
509) {
510 let mut additional_clips = Vec::new();
513
514 if let Some(clip_id) = iframe_clip {
515 additional_clips.push(clip_id);
516 }
517
518 let mut shared_clip_node_id = None;
525
526 for cluster in &prim_list.clusters {
527 for prim_instance in &prim_instances[cluster.prim_range()] {
528 let leaf = clip_tree_builder.get_leaf(prim_instance.clip_leaf_id);
529
530 shared_clip_node_id = match shared_clip_node_id {
532 Some(current) => {
533 Some(clip_tree_builder.find_lowest_common_ancestor(current, leaf.node_id))
534 }
535 None => {
536 Some(leaf.node_id)
537 }
538 }
539 }
540 }
541
542 let mut shared_clip_node_id = shared_clip_node_id.unwrap_or(ClipNodeId::NONE);
551 let mut current_node_id = shared_clip_node_id;
552 let mut rounded_rect_count = 0;
553
554 while current_node_id != ClipNodeId::NONE {
556 let node = clip_tree_builder.get_node(current_node_id);
557 let clip_node_data = &interners.clip[node.handle];
558
559 let is_rcs = spatial_tree.is_root_coord_system(clip_node_data.key.spatial_node_index);
561
562 let node_valid = if is_rcs {
563 match clip_node_data.key.kind {
564 ClipItemKeyKind::BoxShadow(..) |
565 ClipItemKeyKind::ImageMask(..) |
566 ClipItemKeyKind::Rectangle(_, ClipMode::ClipOut) |
567 ClipItemKeyKind::RoundedRectangle(_, _, ClipMode::ClipOut) => {
568 false
570 }
571 ClipItemKeyKind::RoundedRectangle(rect, radius, ClipMode::Clip) => {
572 if BorderRadius::from(radius).can_use_fast_path_in(&rect.into()) {
575 rounded_rect_count += 1;
576
577 false
579 } else {
580 false
581 }
582 }
583 ClipItemKeyKind::Rectangle(_, ClipMode::Clip) => {
584 true
587 }
588 }
589 } else {
590 false
592 };
593
594 if node_valid {
595 if rounded_rect_count > 1 {
597 shared_clip_node_id = current_node_id;
601 rounded_rect_count = 1;
602 }
603 } else {
604 shared_clip_node_id = node.parent;
608 rounded_rect_count = 0;
609 }
610
611 current_node_id = node.parent;
612 }
613
614 let shared_clip_leaf_id = Some(clip_tree_builder.build_for_tile_cache(
615 shared_clip_node_id,
616 &additional_clips,
617 ));
618
619 let slice = tile_cache_pictures.len();
627
628 let background_color = if slice == 0 {
629 background_color
630 } else {
631 None
632 };
633
634 let slice_id = SliceId::new(slice);
635
636 tile_caches.insert(slice_id, TileCacheParams {
639 debug_flags,
640 slice,
641 slice_flags,
642 spatial_node_index: scroll_root,
643 visibility_node_index: visibility_node,
644 background_color,
645 shared_clip_node_id,
646 shared_clip_leaf_id,
647 virtual_surface_size: frame_builder_config.compositor_kind.get_virtual_surface_size(),
648 image_surface_count: prim_list.image_surface_count,
649 yuv_image_surface_count: prim_list.yuv_image_surface_count,
650 });
651
652 let pic_index = prim_store.pictures.alloc().init(PicturePrimitive::new_image(
653 Some(PictureCompositeMode::TileCache { slice_id }),
654 Picture3DContext::Out,
655 PrimitiveFlags::IS_BACKFACE_VISIBLE,
656 prim_list,
657 scroll_root,
658 RasterSpace::Screen,
659 PictureFlags::empty(),
660 None,
661 ));
662
663 tile_cache_pictures.push(PictureIndex(pic_index));
664}
665
666#[derive(Debug)]
668#[cfg_attr(feature = "capture", derive(Serialize))]
669#[cfg_attr(feature = "replay", derive(Deserialize))]
670pub struct PictureCacheDebugInfo {
671 pub slices: FastHashMap<usize, SliceDebugInfo>,
672}
673
674impl PictureCacheDebugInfo {
675 pub fn new() -> Self {
676 PictureCacheDebugInfo {
677 slices: FastHashMap::default(),
678 }
679 }
680
681 pub fn slice(&self, slice: usize) -> &SliceDebugInfo {
684 &self.slices[&slice]
685 }
686}
687
688impl Default for PictureCacheDebugInfo {
689 fn default() -> PictureCacheDebugInfo {
690 PictureCacheDebugInfo::new()
691 }
692}
693
694#[derive(Debug)]
696#[cfg_attr(feature = "capture", derive(Serialize))]
697#[cfg_attr(feature = "replay", derive(Deserialize))]
698pub struct SliceDebugInfo {
699 pub tiles: FastHashMap<TileOffset, TileDebugInfo>,
700}
701
702impl SliceDebugInfo {
703 pub fn new() -> Self {
704 SliceDebugInfo {
705 tiles: FastHashMap::default(),
706 }
707 }
708
709 pub fn tile(&self, x: i32, y: i32) -> &TileDebugInfo {
712 &self.tiles[&TileOffset::new(x, y)]
713 }
714}
715
716#[derive(Debug, PartialEq)]
718#[cfg_attr(feature = "capture", derive(Serialize))]
719#[cfg_attr(feature = "replay", derive(Deserialize))]
720pub struct DirtyTileDebugInfo {
721 pub local_valid_rect: PictureRect,
722 pub local_dirty_rect: PictureRect,
723}
724
725#[derive(Debug, PartialEq)]
727#[cfg_attr(feature = "capture", derive(Serialize))]
728#[cfg_attr(feature = "replay", derive(Deserialize))]
729pub enum TileDebugInfo {
730 Occluded,
732 Culled,
734 Valid,
736 Dirty(DirtyTileDebugInfo),
738}
739
740impl TileDebugInfo {
741 pub fn is_occluded(&self) -> bool {
742 match self {
743 TileDebugInfo::Occluded => true,
744 TileDebugInfo::Culled |
745 TileDebugInfo::Valid |
746 TileDebugInfo::Dirty(..) => false,
747 }
748 }
749
750 pub fn is_valid(&self) -> bool {
751 match self {
752 TileDebugInfo::Valid => true,
753 TileDebugInfo::Culled |
754 TileDebugInfo::Occluded |
755 TileDebugInfo::Dirty(..) => false,
756 }
757 }
758
759 pub fn is_culled(&self) -> bool {
760 match self {
761 TileDebugInfo::Culled => true,
762 TileDebugInfo::Valid |
763 TileDebugInfo::Occluded |
764 TileDebugInfo::Dirty(..) => false,
765 }
766 }
767
768 pub fn as_dirty(&self) -> &DirtyTileDebugInfo {
769 match self {
770 TileDebugInfo::Occluded |
771 TileDebugInfo::Culled |
772 TileDebugInfo::Valid => {
773 panic!("not a dirty tile!");
774 }
775 TileDebugInfo::Dirty(ref info) => {
776 info
777 }
778 }
779 }
780}