1use api::RasterSpace;
98use api::{DebugFlags, ColorF, PrimitiveFlags, SnapshotInfo};
99use api::units::*;
100use crate::command_buffer::PrimitiveCommand;
101use crate::renderer::GpuBufferBuilderF;
102use crate::box_shadow::BLUR_SAMPLE_SCALE;
103use crate::clip::{ClipNodeId, ClipTreeBuilder};
104use crate::spatial_tree::{SpatialTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace};
105use crate::composite::{tile_kind, CompositeTileSurface, CompositorKind, NativeTileId};
106use crate::composite::{CompositeTileDescriptor, CompositeTile};
107use crate::debug_colors;
108use euclid::{vec3, Scale, Vector2D, Box2D};
109use crate::internal_types::{FastHashMap, PlaneSplitter, Filter};
110use crate::internal_types::{PlaneSplitterIndex, PlaneSplitAnchor, TextureSource};
111use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext};
112use plane_split::{Clipper, Polygon};
113use crate::prim_store::{PictureIndex, PrimitiveInstance, PrimitiveKind};
114use crate::prim_store::PrimitiveScratchBuffer;
115use crate::prim_store::storage;
116use crate::print_tree::PrintTreePrinter;
117use crate::render_backend::DataStores;
118use crate::render_task_graph::RenderTaskId;
119use crate::render_task::{RenderTask, RenderTaskLocation};
120use crate::render_task::{StaticRenderTaskSurface, RenderTaskKind};
121use crate::renderer::GpuBufferAddress;
122use crate::resource_cache::ResourceCache;
123use crate::space::SpaceMapper;
124use crate::scene::SceneProperties;
125use crate::spatial_tree::CoordinateSystemId;
126use crate::surface::{SurfaceDescriptor, SurfaceTileDescriptor, get_surface_rects};
127pub use crate::surface::{SurfaceIndex, SurfaceInfo, SubpixelMode};
128pub use crate::surface::calculate_screen_uv;
129use smallvec::SmallVec;
130use std::{mem, u8, u32};
131use std::ops::Range;
132use crate::picture_textures::PictureCacheTextureHandle;
133use crate::util::{MaxRect, Recycler, ScaleOffset};
134use crate::tile_cache::{SliceDebugInfo, TileDebugInfo, DirtyTileDebugInfo, CompositorClipDebugInfo};
135use crate::tile_cache::{SliceId, TileCacheInstance, TileSurface, NativeSurface};
136use crate::tile_cache::{BackdropKind, BackdropSurface};
137use crate::tile_cache::{TileKey, SubSliceIndex};
138use crate::invalidation::InvalidationReason;
139use crate::tile_cache::MAX_SURFACE_SIZE;
140
141pub use crate::picture_composite_mode::{PictureCompositeMode, prepare_composite_mode};
142
143pub(crate) const MAX_BLUR_RADIUS: f32 = 100.;
146
147pub const MAX_COMPOSITOR_SURFACES_SIZE: f32 = 8192.0;
149
150pub fn clamp(value: i32, low: i32, high: i32) -> i32 {
151 value.max(low).min(high)
152}
153
154pub fn clampf(value: f32, low: f32, high: f32) -> f32 {
155 value.max(low).min(high)
156}
157
158#[derive(Debug)]
161pub enum SurfaceTextureDescriptor {
162 TextureCache {
165 handle: Option<PictureCacheTextureHandle>,
166 },
167 Native {
170 id: Option<NativeTileId>,
172 },
173}
174
175#[derive(Clone, Debug, Eq, PartialEq, Hash)]
179#[cfg_attr(feature = "capture", derive(Serialize))]
180#[cfg_attr(feature = "replay", derive(Deserialize))]
181pub enum ResolvedSurfaceTexture {
182 TextureCache {
183 texture: TextureSource,
185 },
186 Native {
187 id: NativeTileId,
189 size: DeviceIntSize,
191 }
192}
193
194impl SurfaceTextureDescriptor {
195 pub fn resolve(
197 &self,
198 resource_cache: &ResourceCache,
199 size: DeviceIntSize,
200 ) -> ResolvedSurfaceTexture {
201 match self {
202 SurfaceTextureDescriptor::TextureCache { handle } => {
203 let texture = resource_cache
204 .picture_textures
205 .get_texture_source(handle.as_ref().unwrap());
206
207 ResolvedSurfaceTexture::TextureCache { texture }
208 }
209 SurfaceTextureDescriptor::Native { id } => {
210 ResolvedSurfaceTexture::Native {
211 id: id.expect("bug: native surface not allocated"),
212 size,
213 }
214 }
215 }
216 }
217}
218
219pub struct PictureScratchBuffer {
220 surface_stack: Vec<SurfaceIndex>,
221}
222
223impl Default for PictureScratchBuffer {
224 fn default() -> Self {
225 PictureScratchBuffer {
226 surface_stack: Vec::new(),
227 }
228 }
229}
230
231impl PictureScratchBuffer {
232 pub fn begin_frame(&mut self) {
233 self.surface_stack.clear();
234 }
235
236 pub fn recycle(&mut self, recycler: &mut Recycler) {
237 recycler.recycle_vec(&mut self.surface_stack);
238 }
239}
240
241#[derive(Debug)]
242#[cfg_attr(feature = "capture", derive(Serialize))]
243pub struct RasterConfig {
244 pub composite_mode: PictureCompositeMode,
248 pub surface_index: SurfaceIndex,
251}
252
253bitflags! {
254 #[cfg_attr(feature = "capture", derive(Serialize))]
256 #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
257 pub struct BlitReason: u32 {
258 const BLEND_MODE = 1 << 0;
260 const CLIP = 1 << 1;
262 const PRESERVE3D = 1 << 2;
264 const FORCED_ISOLATION = 1 << 3;
266 const SNAPSHOT = 1 << 4;
268 }
269}
270
271#[derive(Clone, Debug)]
273#[cfg_attr(feature = "capture", derive(Serialize))]
274pub enum Picture3DContext<C> {
275 Out,
277 In {
279 root_data: Option<Vec<C>>,
281 ancestor_index: SpatialNodeIndex,
287 plane_splitter_index: PlaneSplitterIndex,
289 },
290}
291
292#[derive(Clone, Debug)]
295#[cfg_attr(feature = "capture", derive(Serialize))]
296pub struct OrderedPictureChild {
297 pub anchor: PlaneSplitAnchor,
298 pub gpu_address: GpuBufferAddress,
299}
300
301bitflags! {
302 #[cfg_attr(feature = "capture", derive(Serialize))]
304 #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
305 pub struct ClusterFlags: u32 {
306 const IS_BACKFACE_VISIBLE = 1;
308 const IS_VISIBLE = 2;
312 }
313}
314
315#[cfg_attr(feature = "capture", derive(Serialize))]
318pub struct PrimitiveCluster {
319 pub spatial_node_index: SpatialNodeIndex,
321 pub unsnapped_bounding_rect: LayoutRect,
327 pub snapped_bounding_rect: LayoutRect,
332 pub prim_range: Range<usize>,
334 pub flags: ClusterFlags,
336}
337
338impl PrimitiveCluster {
339 fn new(
341 spatial_node_index: SpatialNodeIndex,
342 flags: ClusterFlags,
343 first_instance_index: usize,
344 ) -> Self {
345 PrimitiveCluster {
346 unsnapped_bounding_rect: LayoutRect::zero(),
347 snapped_bounding_rect: LayoutRect::zero(),
348 spatial_node_index,
349 flags,
350 prim_range: first_instance_index..first_instance_index
351 }
352 }
353
354 pub fn is_compatible(
356 &self,
357 spatial_node_index: SpatialNodeIndex,
358 flags: ClusterFlags,
359 instance_index: usize,
360 ) -> bool {
361 self.flags == flags &&
362 self.spatial_node_index == spatial_node_index &&
363 instance_index == self.prim_range.end
364 }
365
366 pub fn prim_range(&self) -> Range<usize> {
367 self.prim_range.clone()
368 }
369
370 fn add_instance(
372 &mut self,
373 culling_rect: &LayoutRect,
374 instance_index: usize,
375 ) {
376 debug_assert_eq!(instance_index, self.prim_range.end);
377 self.unsnapped_bounding_rect = self.unsnapped_bounding_rect.union(culling_rect);
378 self.prim_range.end += 1;
379 }
380}
381
382#[cfg_attr(feature = "capture", derive(Serialize))]
387pub struct PrimitiveList {
388 pub clusters: Vec<PrimitiveCluster>,
390 pub child_pictures: Vec<PictureIndex>,
391 pub image_surface_count: usize,
394 pub yuv_image_surface_count: usize,
397 pub needs_scissor_rect: bool,
398}
399
400impl PrimitiveList {
401 pub fn empty() -> Self {
406 PrimitiveList {
407 clusters: Vec::new(),
408 child_pictures: Vec::new(),
409 image_surface_count: 0,
410 yuv_image_surface_count: 0,
411 needs_scissor_rect: false,
412 }
413 }
414
415 pub fn merge(&mut self, other: PrimitiveList) {
416 self.clusters.extend(other.clusters);
417 self.child_pictures.extend(other.child_pictures);
418 self.image_surface_count += other.image_surface_count;
419 self.yuv_image_surface_count += other.yuv_image_surface_count;
420 self.needs_scissor_rect |= other.needs_scissor_rect;
421 }
422
423 pub fn add_prim(
425 &mut self,
426 prim_instance: PrimitiveInstance,
427 prim_rect: LayoutRect,
428 spatial_node_index: SpatialNodeIndex,
429 prim_flags: PrimitiveFlags,
430 prim_instances: &mut Vec<PrimitiveInstance>,
431 clip_tree_builder: &ClipTreeBuilder,
432 ) {
433 let mut flags = ClusterFlags::empty();
434
435 match prim_instance.kind {
438 PrimitiveKind::Picture { pic_index, .. } => {
439 self.child_pictures.push(pic_index);
440 }
441 PrimitiveKind::TextRun { .. } => {
442 self.needs_scissor_rect = true;
443 }
444 PrimitiveKind::YuvImage { .. } => {
445 if prim_flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) {
452 self.yuv_image_surface_count += 1;
453 }
454 }
455 PrimitiveKind::Image { .. } => {
456 if prim_flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) {
462 self.image_surface_count += 1;
463 }
464 }
465 _ => {}
466 }
467
468 if prim_flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE) {
469 flags.insert(ClusterFlags::IS_BACKFACE_VISIBLE);
470 }
471
472 let clip_leaf = clip_tree_builder.get_leaf(prim_instance.clip_leaf_id);
473 let culling_rect = clip_leaf.unsnapped_local_clip_rect
478 .intersection(&prim_rect)
479 .unwrap_or_else(LayoutRect::zero);
480
481 let instance_index = prim_instances.len();
482 prim_instances.push(prim_instance);
483
484 if let Some(cluster) = self.clusters.last_mut() {
485 if cluster.is_compatible(spatial_node_index, flags, instance_index) {
486 cluster.add_instance(&culling_rect, instance_index);
487 return;
488 }
489 }
490
491 let clusters_len = self.clusters.len();
493 if clusters_len == self.clusters.capacity() {
494 let next_alloc = match clusters_len {
495 1 ..= 15 => 16 - clusters_len,
496 16 ..= 127 => 128 - clusters_len,
497 _ => clusters_len * 2,
498 };
499
500 self.clusters.reserve(next_alloc);
501 }
502
503 let mut cluster = PrimitiveCluster::new(
504 spatial_node_index,
505 flags,
506 instance_index,
507 );
508
509 cluster.add_instance(&culling_rect, instance_index);
510 self.clusters.push(cluster);
511 }
512
513 pub fn is_empty(&self) -> bool {
515 self.clusters.is_empty()
516 }
517}
518
519bitflags! {
520 #[cfg_attr(feature = "capture", derive(Serialize))]
521 #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
523 pub struct PictureFlags : u8 {
524 const IS_RESOLVE_TARGET = 1 << 0;
527 const IS_SUB_GRAPH = 1 << 1;
530 const DISABLE_SNAPPING = 1 << 2;
532 }
533}
534
535#[derive(Debug)]
539#[cfg_attr(feature = "capture", derive(Serialize))]
540pub struct PictureScratch {
541 pub primary_render_task_id: Option<RenderTaskId>,
543 pub secondary_render_task_id: Option<RenderTaskId>,
552 pub extra_gpu_data: SmallVec<[GpuBufferAddress; 1]>,
555}
556
557impl PictureScratch {
558 pub fn empty() -> Self {
559 PictureScratch {
560 primary_render_task_id: None,
561 secondary_render_task_id: None,
562 extra_gpu_data: SmallVec::new(),
563 }
564 }
565}
566
567#[cfg_attr(feature = "capture", derive(Serialize))]
568pub struct PictureInstance {
569 pub prim_list: PrimitiveList,
571
572 pub is_backface_visible: bool,
575
576 pub composite_mode: Option<PictureCompositeMode>,
579
580 pub raster_config: Option<RasterConfig>,
581 pub context_3d: Picture3DContext<OrderedPictureChild>,
582
583 pub spatial_node_index: SpatialNodeIndex,
586
587 pub prev_local_rect: LayoutRect,
591
592 pub segments_are_valid: bool,
597
598 pub raster_space: RasterSpace,
600
601 pub flags: PictureFlags,
603
604 pub clip_root: Option<ClipNodeId>,
608
609 pub snapshot: Option<SnapshotInfo>,
612}
613
614impl PictureInstance {
615 pub fn print<T: PrintTreePrinter>(
616 &self,
617 pictures: &[Self],
618 self_index: PictureIndex,
619 pt: &mut T,
620 ) {
621 pt.new_level(format!("{:?}", self_index));
622 pt.add_item(format!("cluster_count: {:?}", self.prim_list.clusters.len()));
623 pt.add_item(format!("spatial_node_index: {:?}", self.spatial_node_index));
624 pt.add_item(format!("raster_config: {:?}", self.raster_config));
625 pt.add_item(format!("composite_mode: {:?}", self.composite_mode));
626 pt.add_item(format!("flags: {:?}", self.flags));
627
628 for child_pic_index in &self.prim_list.child_pictures {
629 pictures[child_pic_index.0].print(pictures, *child_pic_index, pt);
630 }
631
632 pt.end_level();
633 }
634
635 pub fn resolve_scene_properties(&mut self, properties: &SceneProperties) {
636 match self.composite_mode {
637 Some(PictureCompositeMode::Filter(ref mut filter)) => {
638 match *filter {
639 Filter::Opacity(ref binding, ref mut value) => {
640 *value = properties.resolve_float(binding);
641 }
642 _ => {}
643 }
644 }
645 _ => {}
646 }
647 }
648
649 pub fn is_visible(
650 &self,
651 spatial_tree: &SpatialTree,
652 ) -> bool {
653 if let Some(PictureCompositeMode::Filter(ref filter)) = self.composite_mode {
654 if !filter.is_visible() {
655 return false;
656 }
657 }
658
659 if !self.is_backface_visible {
664 if let Picture3DContext::Out = self.context_3d {
665 match spatial_tree.get_local_visible_face(self.spatial_node_index) {
666 VisibleFace::Front => {}
667 VisibleFace::Back => return false,
668 }
669 }
670 }
671
672 true
673 }
674
675 pub fn new_image(
676 composite_mode: Option<PictureCompositeMode>,
677 context_3d: Picture3DContext<OrderedPictureChild>,
678 prim_flags: PrimitiveFlags,
679 prim_list: PrimitiveList,
680 spatial_node_index: SpatialNodeIndex,
681 raster_space: RasterSpace,
682 flags: PictureFlags,
683 snapshot: Option<SnapshotInfo>,
684 ) -> Self {
685 PictureInstance {
686 prim_list,
687 composite_mode,
688 raster_config: None,
689 context_3d,
690 is_backface_visible: prim_flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE),
691 spatial_node_index,
692 prev_local_rect: LayoutRect::zero(),
693 segments_are_valid: false,
694 raster_space,
695 flags,
696 clip_root: None,
697 snapshot,
698 }
699 }
700
701 pub fn take_context(
702 &mut self,
703 pic_index: PictureIndex,
704 parent_surface_index: Option<SurfaceIndex>,
705 parent_subpixel_mode: SubpixelMode,
706 frame_state: &mut FrameBuildingState,
707 frame_context: &FrameBuildingContext,
708 data_stores: &mut DataStores,
709 scratch: &mut PrimitiveScratchBuffer,
710 tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
711 ) -> Option<(PictureContext, PictureState, PrimitiveList, storage::Index<PictureScratch>)> {
712 let mut picture_scratch = PictureScratch::empty();
713
714 let dbg_flags = DebugFlags::PICTURE_CACHING_DBG | DebugFlags::PICTURE_BORDERS;
715 if frame_context.debug_flags.intersects(dbg_flags) {
716 self.draw_debug_overlay(
717 parent_surface_index,
718 frame_state,
719 frame_context,
720 tile_caches,
721 scratch,
722 );
723 }
724
725 if !self.is_visible(frame_context.spatial_tree) {
726 return None;
727 }
728
729 profile_scope!("take_context");
730
731 let surface_index = match self.raster_config {
732 Some(ref raster_config) => raster_config.surface_index,
733 None => parent_surface_index.expect("bug: no parent"),
734 };
735 let surface = &frame_state.surfaces[surface_index.0];
736 let surface_spatial_node_index = surface.surface_spatial_node_index;
737
738 let map_pic_to_world = SpaceMapper::new_with_target(
739 frame_context.root_spatial_node_index,
740 surface_spatial_node_index,
741 frame_context.global_screen_world_rect,
742 frame_context.spatial_tree,
743 );
744
745 let map_pic_to_vis = SpaceMapper::new_with_target(
746 frame_context.root_spatial_node_index,
748 surface_spatial_node_index,
749 surface.culling_rect,
750 frame_context.spatial_tree,
751 );
752
753 let pic_bounds = map_pic_to_world
757 .unmap(&map_pic_to_world.bounds)
758 .unwrap_or_else(PictureRect::max_rect);
759
760 let map_local_to_pic = SpaceMapper::new(
761 surface_spatial_node_index,
762 pic_bounds,
763 );
764
765 match self.raster_config {
766 Some(RasterConfig { surface_index, composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => {
767 prepare_tiled_picture_surface(
768 surface_index,
769 slice_id,
770 surface_spatial_node_index,
771 &map_pic_to_world,
772 frame_context,
773 frame_state,
774 tile_caches,
775 );
776 }
777 Some(ref mut raster_config) => {
778 let (pic_rect, force_scissor_rect) = {
779 let surface = &frame_state.surfaces[raster_config.surface_index.0];
780 (surface.clipped_local_rect, surface.force_scissor_rect)
781 };
782
783 let parent_surface_index = parent_surface_index.expect("bug: no parent for child surface");
784
785 let local_rect = pic_rect * Scale::new(1.0);
788
789 if local_rect != self.prev_local_rect {
798 self.segments_are_valid = false;
801 self.prev_local_rect = local_rect;
802 }
803
804 let max_surface_size = frame_context
805 .fb_config
806 .max_surface_override
807 .unwrap_or(MAX_SURFACE_SIZE) as f32;
808
809 let surface_rects = match get_surface_rects(
810 raster_config.surface_index,
811 &raster_config.composite_mode,
812 parent_surface_index,
813 &mut frame_state.surfaces,
814 frame_context.spatial_tree,
815 max_surface_size,
816 force_scissor_rect,
817 ) {
818 Some(rects) => rects,
819 None => return None,
820 };
821
822 if let PictureCompositeMode::IntermediateSurface = raster_config.composite_mode {
823 if !scratch.frame.required_sub_graphs.contains(&pic_index) {
824 return None;
825 }
826 }
827
828 let can_use_shared_surface = !self.flags.contains(PictureFlags::IS_RESOLVE_TARGET);
829 let (surface_descriptor, render_tasks) = prepare_composite_mode(
830 &raster_config.composite_mode,
831 surface_index,
832 parent_surface_index,
833 &surface_rects,
834 &self.snapshot,
835 can_use_shared_surface,
836 frame_context,
837 frame_state,
838 data_stores,
839 &mut picture_scratch.extra_gpu_data,
840 );
841
842 picture_scratch.primary_render_task_id = render_tasks[0];
843 picture_scratch.secondary_render_task_id = render_tasks[1];
844
845 let is_sub_graph = self.flags.contains(PictureFlags::IS_SUB_GRAPH);
846
847 frame_state.surface_builder.push_surface(
848 raster_config.surface_index,
849 is_sub_graph,
850 surface_rects.clipped_local,
851 Some(surface_descriptor),
852 frame_state.surfaces,
853 frame_state.rg_builder,
854 );
855 }
856 None => {}
857 };
858
859 let state = PictureState {
860 map_local_to_pic,
861 map_pic_to_vis,
862 };
863
864 let mut dirty_region_count = 0;
865
866 if let Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) = self.raster_config {
869 let dirty_region = tile_caches[&slice_id].dirty_region.clone();
870 frame_state.push_dirty_region(dirty_region);
871
872 dirty_region_count += 1;
873 }
874
875 let subpixel_mode = compute_subpixel_mode(
876 &self.raster_config,
877 tile_caches,
878 parent_subpixel_mode
879 );
880
881 let context = PictureContext {
882 pic_index,
883 raster_spatial_node_index: frame_state.surfaces[surface_index.0].raster_spatial_node_index,
884 visibility_spatial_node_index: frame_context.root_spatial_node_index,
886 surface_spatial_node_index,
887 surface_index,
888 dirty_region_count,
889 subpixel_mode,
890 };
891
892 let prim_list = mem::replace(&mut self.prim_list, PrimitiveList::empty());
893
894 let scratch_handle = scratch.frame.pictures.push(picture_scratch);
895 Some((context, state, prim_list, scratch_handle))
896 }
897
898 pub fn restore_context(
899 &mut self,
900 pic_index: PictureIndex,
901 prim_list: PrimitiveList,
902 context: PictureContext,
903 frame_context: &FrameBuildingContext,
904 frame_state: &mut FrameBuildingState,
905 scratch: &PrimitiveScratchBuffer,
906 ) {
907 for _ in 0 .. context.dirty_region_count {
909 frame_state.pop_dirty_region();
910 }
911
912 if self.raster_config.is_some() {
913 frame_state.surface_builder.pop_surface(
914 pic_index,
915 frame_state.rg_builder,
916 frame_state.cmd_buffers,
917 );
918 }
919
920 if let Picture3DContext::In { root_data: Some(ref mut list), plane_splitter_index, ancestor_index, .. } = self.context_3d {
921 let splitter = &mut frame_state.plane_splitters[plane_splitter_index.0];
922
923 PictureInstance::resolve_split_planes(
925 splitter,
926 list,
927 &mut frame_state.frame_gpu_data.f32,
928 ancestor_index,
929 &frame_context.spatial_tree,
930 );
931
932 let mut cmd_buffer_targets = Vec::new();
934 for child in list {
935 if frame_state.surface_builder.get_cmd_buffer_targets_for_prim(
936 &scratch.frame.draws[child.anchor.instance_index.0 as usize],
937 &mut cmd_buffer_targets,
938 ) {
939 let prim_cmd = PrimitiveCommand::complex(
940 storage::Index::from_u32(child.anchor.instance_index.0),
941 child.gpu_address
942 );
943
944 frame_state.push_prim(
945 &prim_cmd,
946 child.anchor.spatial_node_index,
947 &cmd_buffer_targets,
948 );
949 }
950 }
951 }
952
953 self.prim_list = prim_list;
954 }
955
956 pub fn add_split_plane(
960 splitter: &mut PlaneSplitter,
961 spatial_tree: &SpatialTree,
962 prim_spatial_node_index: SpatialNodeIndex,
963 ancestor_spatial_node_index: SpatialNodeIndex,
968 visibility_spatial_node_index: SpatialNodeIndex,
969 original_local_rect: LayoutRect,
970 combined_local_clip_rect: &LayoutRect,
971 dirty_rect: VisRect,
972 plane_split_anchor: PlaneSplitAnchor,
973 ) -> bool {
974 let prim_to_ancestor = spatial_tree.get_relative_transform(
975 prim_spatial_node_index,
976 ancestor_spatial_node_index
977 );
978
979 let ancestor_matrix = prim_to_ancestor.clone().into_transform().cast().to_untyped();
980
981 let local_rect = match original_local_rect
988 .intersection(combined_local_clip_rect)
989 {
990 Some(rect) => rect.cast(),
991 None => return false,
992 };
993
994 match prim_to_ancestor {
995 CoordinateSpaceMapping::Local => {
996 let polygon = Polygon::from_rect(
997 local_rect.to_rect() * Scale::new(1.0),
998 plane_split_anchor,
999 );
1000 splitter.add(polygon);
1001 }
1002 CoordinateSpaceMapping::ScaleOffset(scale_offset) if scale_offset.scale == Vector2D::new(1.0, 1.0) => {
1003 let inv_matrix = scale_offset.inverse().to_transform().cast();
1004 let polygon = Polygon::from_transformed_rect_with_inverse(
1005 local_rect.to_rect().to_untyped(),
1006 &ancestor_matrix,
1007 &inv_matrix,
1008 plane_split_anchor,
1009 ).unwrap();
1010 splitter.add(polygon);
1011 }
1012 CoordinateSpaceMapping::ScaleOffset(_) |
1013 CoordinateSpaceMapping::Transform(_) => {
1014 let map_ancestor_to_vis = SpaceMapper::<LayoutPixel, VisPixel>::new_with_target(
1024 visibility_spatial_node_index,
1025 ancestor_spatial_node_index,
1026 VisRect::max_rect(),
1027 spatial_tree,
1028 );
1029 let ancestor_dirty_rect = map_ancestor_to_vis.unmap(&dirty_rect);
1030
1031 let ancestor_bounds = ancestor_dirty_rect.map(|r| r.cast().to_rect().to_untyped());
1032
1033 let mut clipper = Clipper::<PlaneSplitAnchor>::new();
1034 let planes = match Clipper::<PlaneSplitAnchor>::frustum_planes(&ancestor_matrix, ancestor_bounds) {
1035 Ok(p) => p,
1036 Err(_) => return false,
1037 };
1038 for plane in planes {
1039 clipper.add(plane);
1040 }
1041
1042 let polygon = Polygon::from_rect(
1043 local_rect.to_rect().to_untyped(),
1044 plane_split_anchor,
1045 );
1046 let clipped: Vec<_> = clipper.clip(polygon).to_vec();
1047
1048 for poly in clipped {
1049 if let Some(transformed) = poly.transform(&ancestor_matrix) {
1050 splitter.add(transformed);
1051 }
1052 }
1053 }
1054 }
1055
1056 true
1057 }
1058
1059 fn resolve_split_planes(
1060 splitter: &mut PlaneSplitter,
1061 ordered: &mut Vec<OrderedPictureChild>,
1062 gpu_buffer: &mut GpuBufferBuilderF,
1063 ancestor_index: SpatialNodeIndex,
1064 spatial_tree: &SpatialTree,
1065 ) {
1066 ordered.clear();
1067
1068 let sorted = splitter.sort(vec3(0.0, 0.0, 1.0));
1071 ordered.reserve(sorted.len());
1072 for poly in sorted {
1073 let transform = match spatial_tree
1074 .get_relative_transform(poly.anchor.spatial_node_index, ancestor_index)
1075 .inverse()
1076 {
1077 Some(transform) => transform.into_transform(),
1078 None => continue,
1080 };
1081
1082 let local_points = [
1083 transform.transform_point3d(poly.points[0].cast_unit().to_f32()),
1084 transform.transform_point3d(poly.points[1].cast_unit().to_f32()),
1085 transform.transform_point3d(poly.points[2].cast_unit().to_f32()),
1086 transform.transform_point3d(poly.points[3].cast_unit().to_f32()),
1087 ];
1088
1089 if local_points.iter().any(|p| p.is_none()) {
1092 continue;
1093 }
1094
1095 let p0 = local_points[0].unwrap();
1096 let p1 = local_points[1].unwrap();
1097 let p2 = local_points[2].unwrap();
1098 let p3 = local_points[3].unwrap();
1099
1100 let mut writer = gpu_buffer.write_blocks(2);
1101 writer.push_one([p0.x, p0.y, p1.x, p1.y]);
1102 writer.push_one([p2.x, p2.y, p3.x, p3.y]);
1103 let gpu_address = writer.finish();
1104
1105 ordered.push(OrderedPictureChild {
1106 anchor: poly.anchor,
1107 gpu_address,
1108 });
1109 }
1110 }
1111
1112 pub fn assign_surface(
1116 &mut self,
1117 frame_context: &FrameBuildingContext,
1118 parent_surface_index: Option<SurfaceIndex>,
1119 tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
1120 surfaces: &mut Vec<SurfaceInfo>,
1121 ) -> Option<SurfaceIndex> {
1122 self.raster_config = None;
1124
1125 match self.composite_mode {
1126 Some(ref composite_mode) => {
1127 let surface_spatial_node_index = self.spatial_node_index;
1128
1129 let mut min_scale;
1131 let mut max_scale = 1.0e32;
1132
1133 let world_scale_factors = match parent_surface_index {
1137 Some(parent_surface_index) => {
1138 let parent_surface = &surfaces[parent_surface_index.0];
1139
1140 let local_to_surface = frame_context
1141 .spatial_tree
1142 .get_relative_transform(
1143 surface_spatial_node_index,
1144 parent_surface.surface_spatial_node_index,
1145 );
1146
1147 let scale_factors = if local_to_surface.is_perspective() {
1154 (1.0, 1.0)
1155 } else {
1156 local_to_surface.scale_factors()
1157 };
1158
1159 let scale_factors = (
1160 scale_factors.0 * parent_surface.world_scale_factors.0,
1161 scale_factors.1 * parent_surface.world_scale_factors.1,
1162 );
1163
1164 scale_factors
1165 }
1166 None => {
1167 let local_to_surface_scale_factors = frame_context
1168 .spatial_tree
1169 .get_relative_transform(
1170 surface_spatial_node_index,
1171 frame_context.spatial_tree.root_reference_frame_index(),
1172 )
1173 .scale_factors();
1174
1175 let scale_factors = (
1176 local_to_surface_scale_factors.0,
1177 local_to_surface_scale_factors.1,
1178 );
1179
1180 scale_factors
1181 }
1182 };
1183
1184 let allow_snapping = !self.flags.contains(PictureFlags::DISABLE_SNAPPING);
1191
1192 let force_scissor_rect = self.prim_list.needs_scissor_rect;
1201
1202 let (device_pixel_scale, raster_spatial_node_index, local_scale, world_scale_factors) = match composite_mode {
1205 PictureCompositeMode::TileCache { slice_id } => {
1206 let tile_cache = tile_caches.get_mut(&slice_id).unwrap();
1207
1208 let local_to_device = get_relative_scale_offset(
1210 tile_cache.spatial_node_index,
1211 frame_context.root_spatial_node_index,
1212 frame_context.spatial_tree,
1213 );
1214 let local_to_cur_raster_scale = local_to_device.scale.x / tile_cache.current_raster_scale;
1215
1216 if !frame_context.fb_config.low_quality_pinch_zoom
1224 || !frame_context
1225 .spatial_tree.get_spatial_node(tile_cache.spatial_node_index)
1226 .is_ancestor_or_self_zooming
1227 || local_to_cur_raster_scale <= 0.5
1228 || local_to_cur_raster_scale >= 2.0
1229 {
1230 tile_cache.current_raster_scale = local_to_device.scale.x;
1231 }
1232
1233 min_scale = 0.0;
1235
1236 if frame_context.fb_config.low_quality_pinch_zoom {
1237 min_scale = tile_cache.current_raster_scale;
1241 max_scale = tile_cache.current_raster_scale;
1242 }
1243
1244 let scaling_factor = world_scale_factors.0.max(world_scale_factors.1).max(min_scale).min(max_scale);
1246
1247 let device_pixel_scale = Scale::new(scaling_factor);
1248
1249 (device_pixel_scale, surface_spatial_node_index, (1.0, 1.0), world_scale_factors)
1250 }
1251 _ => {
1252 let surface_spatial_node = frame_context.spatial_tree.get_spatial_node(surface_spatial_node_index);
1253
1254 let enable_snapping =
1255 allow_snapping &&
1256 surface_spatial_node.coordinate_system_id == CoordinateSystemId::root() &&
1257 surface_spatial_node.snapping_transform.is_some();
1258
1259 if enable_snapping {
1260 let raster_spatial_node_index = frame_context.spatial_tree.root_reference_frame_index();
1261
1262 let local_to_raster_transform = frame_context
1263 .spatial_tree
1264 .get_relative_transform(
1265 self.spatial_node_index,
1266 raster_spatial_node_index,
1267 );
1268
1269 let local_scale = local_to_raster_transform.scale_factors();
1270
1271 (Scale::new(1.0), raster_spatial_node_index, local_scale, (1.0, 1.0))
1272 } else {
1273 let world_scale_factors = match self.raster_space {
1276 RasterSpace::Screen => world_scale_factors,
1277 RasterSpace::Local(scale) => (scale, scale),
1278 };
1279
1280 let device_pixel_scale = Scale::new(
1281 world_scale_factors.0.max(world_scale_factors.1).min(max_scale)
1282 );
1283
1284 (device_pixel_scale, surface_spatial_node_index, (1.0, 1.0), world_scale_factors)
1285 }
1286 }
1287 };
1288
1289 let surface = SurfaceInfo::new(
1290 surface_spatial_node_index,
1291 raster_spatial_node_index,
1292 frame_context.global_screen_world_rect,
1293 &frame_context.spatial_tree,
1294 device_pixel_scale,
1295 world_scale_factors,
1296 local_scale,
1297 allow_snapping,
1298 force_scissor_rect,
1299 );
1300
1301 let surface_index = SurfaceIndex(surfaces.len());
1302
1303 surfaces.push(surface);
1304
1305 self.raster_config = Some(RasterConfig {
1306 composite_mode: composite_mode.clone(),
1307 surface_index,
1308 });
1309
1310 Some(surface_index)
1311 }
1312 None => {
1313 None
1314 }
1315 }
1316 }
1317
1318 pub fn propagate_bounding_rect(
1323 &mut self,
1324 surface_index: SurfaceIndex,
1325 parent_surface_index: Option<SurfaceIndex>,
1326 surfaces: &mut [SurfaceInfo],
1327 frame_context: &FrameBuildingContext,
1328 ) {
1329 let surface = &mut surfaces[surface_index.0];
1330
1331 for cluster in &mut self.prim_list.clusters {
1332 cluster.flags.remove(ClusterFlags::IS_VISIBLE);
1333
1334 if !cluster.flags.contains(ClusterFlags::IS_BACKFACE_VISIBLE) {
1342 if let Picture3DContext::In { ancestor_index, .. } = self.context_3d {
1345 let mut face = VisibleFace::Front;
1346 frame_context.spatial_tree.get_relative_transform_with_face(
1347 cluster.spatial_node_index,
1348 ancestor_index,
1349 Some(&mut face),
1350 );
1351 if face == VisibleFace::Back {
1352 continue
1353 }
1354 }
1355 }
1356
1357 let spatial_node = &frame_context
1359 .spatial_tree
1360 .get_spatial_node(cluster.spatial_node_index);
1361 if !spatial_node.invertible {
1362 continue;
1363 }
1364
1365 surface.map_local_to_picture.set_target_spatial_node(
1368 cluster.spatial_node_index,
1369 frame_context.spatial_tree,
1370 );
1371
1372 cluster.flags.insert(ClusterFlags::IS_VISIBLE);
1375 if let Some(cluster_rect) = surface.map_local_to_picture.map(&cluster.snapped_bounding_rect) {
1376 surface.unclipped_local_rect = surface.unclipped_local_rect.union(&cluster_rect);
1377 }
1378 }
1379
1380 if let Some(ref mut raster_config) = self.raster_config {
1384 if let Some(parent_surface_index) = parent_surface_index {
1386 let surface_rect = raster_config.composite_mode.get_coverage(
1387 surface,
1388 Some(surface.unclipped_local_rect.cast_unit()),
1389 );
1390
1391 let parent_surface = &mut surfaces[parent_surface_index.0];
1392 parent_surface.map_local_to_picture.set_target_spatial_node(
1393 self.spatial_node_index,
1394 frame_context.spatial_tree,
1395 );
1396
1397 if let Some(parent_surface_rect) = parent_surface
1401 .map_local_to_picture
1402 .map(&surface_rect)
1403 {
1404 parent_surface.unclipped_local_rect =
1405 parent_surface.unclipped_local_rect.union(&parent_surface_rect);
1406 }
1407 }
1408 }
1409 }
1410
1411 pub fn write_gpu_blocks(
1412 &mut self,
1413 frame_state: &mut FrameBuildingState,
1414 data_stores: &mut DataStores,
1415 scratch: &mut PictureScratch,
1416 ) {
1417 let raster_config = match self.raster_config {
1418 Some(ref mut raster_config) => raster_config,
1419 None => {
1420 return;
1421 }
1422 };
1423
1424 raster_config.composite_mode.write_gpu_blocks(
1425 &frame_state.surfaces[raster_config.surface_index.0],
1426 &mut frame_state.frame_gpu_data,
1427 data_stores,
1428 &mut scratch.extra_gpu_data,
1429 );
1430 }
1431
1432 #[cold]
1433 fn draw_debug_overlay(
1434 &self,
1435 parent_surface_index: Option<SurfaceIndex>,
1436 frame_state: &FrameBuildingState,
1437 frame_context: &FrameBuildingContext,
1438 tile_caches: &FastHashMap<SliceId, Box<TileCacheInstance>>,
1439 scratch: &mut PrimitiveScratchBuffer,
1440 ) {
1441 fn draw_debug_border(
1442 local_rect: &PictureRect,
1443 thickness: i32,
1444 pic_to_world_mapper: &SpaceMapper<PicturePixel, WorldPixel>,
1445 global_device_pixel_scale: DevicePixelScale,
1446 color: ColorF,
1447 scratch: &mut PrimitiveScratchBuffer,
1448 ) {
1449 if let Some(world_rect) = pic_to_world_mapper.map(&local_rect) {
1450 let device_rect = world_rect * global_device_pixel_scale;
1451 scratch.push_debug_rect(
1452 device_rect,
1453 thickness,
1454 color,
1455 ColorF::TRANSPARENT,
1456 );
1457 }
1458 }
1459
1460 let flags = frame_context.debug_flags;
1461 let draw_borders = flags.contains(DebugFlags::PICTURE_BORDERS);
1462 let draw_tile_dbg = flags.contains(DebugFlags::PICTURE_CACHING_DBG);
1463
1464 let surface_index = match &self.raster_config {
1465 Some(raster_config) => raster_config.surface_index,
1466 None => parent_surface_index.expect("bug: no parent"),
1467 };
1468 let surface_spatial_node_index = frame_state
1469 .surfaces[surface_index.0]
1470 .surface_spatial_node_index;
1471
1472 let map_pic_to_world = SpaceMapper::new_with_target(
1473 frame_context.root_spatial_node_index,
1474 surface_spatial_node_index,
1475 frame_context.global_screen_world_rect,
1476 frame_context.spatial_tree,
1477 );
1478
1479 let Some(raster_config) = &self.raster_config else {
1480 return;
1481 };
1482
1483 if draw_borders {
1484 let layer_color;
1485 if let PictureCompositeMode::TileCache { slice_id } = &raster_config.composite_mode {
1486 layer_color = ColorF::new(0.0, 1.0, 0.0, 0.8);
1488
1489 let Some(tile_cache) = tile_caches.get(&slice_id) else {
1490 return;
1491 };
1492
1493 for (_, sub_slice) in tile_cache.sub_slices.iter().enumerate() {
1495 for tile in sub_slice.tiles.values() {
1496 if !tile.is_visible {
1497 continue;
1498 }
1499 let rect = tile.cached_surface.local_rect.intersection(&tile_cache.local_rect);
1500 if let Some(rect) = rect {
1501 draw_debug_border(
1502 &rect,
1503 1,
1504 &map_pic_to_world,
1505 frame_context.global_device_pixel_scale,
1506 ColorF::new(0.0, 1.0, 0.0, 0.2),
1507 scratch,
1508 );
1509 }
1510 }
1511 }
1512 } else {
1513 layer_color = ColorF::new(1.0, 0.0, 0.0, 0.5);
1515 }
1516
1517 let pic_rect = frame_state
1519 .surfaces[raster_config.surface_index.0]
1520 .unclipped_local_rect;
1521
1522 draw_debug_border(
1523 &pic_rect,
1524 3,
1525 &map_pic_to_world,
1526 frame_context.global_device_pixel_scale,
1527 layer_color,
1528 scratch,
1529 );
1530 }
1531
1532 if draw_tile_dbg && self.is_visible(frame_context.spatial_tree) {
1533 if let PictureCompositeMode::TileCache { slice_id } = &raster_config.composite_mode {
1534 let Some(tile_cache) = tile_caches.get(&slice_id) else {
1535 return;
1536 };
1537 for (sub_slice_index, sub_slice) in tile_cache.sub_slices.iter().enumerate() {
1538 for tile in sub_slice.tiles.values() {
1539 if !tile.is_visible {
1540 continue;
1541 }
1542 tile.cached_surface.root.draw_debug_rects(
1543 &map_pic_to_world,
1544 tile.is_opaque,
1545 tile.cached_surface.current_descriptor.local_valid_rect,
1546 scratch,
1547 frame_context.global_device_pixel_scale,
1548 );
1549
1550 let label_offset = DeviceVector2D::new(
1551 20.0 + sub_slice_index as f32 * 20.0,
1552 30.0 + sub_slice_index as f32 * 20.0,
1553 );
1554 let tile_device_rect = tile.world_tile_rect
1555 * frame_context.global_device_pixel_scale;
1556
1557 if tile_device_rect.height() >= label_offset.y {
1558 let surface = tile.surface.as_ref().expect("no tile surface set!");
1559
1560 scratch.push_debug_string(
1561 tile_device_rect.min + label_offset,
1562 debug_colors::RED,
1563 format!("{:?}: s={} is_opaque={} surface={} sub={}",
1564 tile.id,
1565 tile_cache.slice,
1566 tile.is_opaque,
1567 surface.kind(),
1568 sub_slice_index,
1569 ),
1570 );
1571 }
1572 }
1573 }
1574 }
1575 }
1576 }
1577}
1578
1579pub fn get_relative_scale_offset(
1580 child_spatial_node_index: SpatialNodeIndex,
1581 parent_spatial_node_index: SpatialNodeIndex,
1582 spatial_tree: &SpatialTree,
1583) -> ScaleOffset {
1584 let transform = spatial_tree.get_relative_transform(
1585 child_spatial_node_index,
1586 parent_spatial_node_index,
1587 );
1588 let mut scale_offset = match transform {
1589 CoordinateSpaceMapping::Local => ScaleOffset::identity(),
1590 CoordinateSpaceMapping::ScaleOffset(scale_offset) => scale_offset,
1591 CoordinateSpaceMapping::Transform(m) => {
1592 ScaleOffset::from_transform(&m).expect("bug: pictures caches don't support complex transforms")
1593 }
1594 };
1595
1596 scale_offset.offset = scale_offset.offset.round();
1600
1601 scale_offset
1602}
1603
1604fn prepare_tiled_picture_surface(
1607 surface_index: SurfaceIndex,
1608 slice_id: SliceId,
1609 surface_spatial_node_index: SpatialNodeIndex,
1610 map_pic_to_world: &SpaceMapper<PicturePixel, WorldPixel>,
1611 frame_context: &FrameBuildingContext,
1612 frame_state: &mut FrameBuildingState,
1613 tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
1614) {
1615 let tile_cache = tile_caches.get_mut(&slice_id).unwrap();
1616 let mut debug_info = SliceDebugInfo::new();
1617 let mut surface_render_tasks = FastHashMap::default();
1618 let mut surface_local_dirty_rect = PictureRect::zero();
1619 let device_pixel_scale = frame_state
1620 .surfaces[surface_index.0]
1621 .device_pixel_scale;
1622 let mut at_least_one_tile_visible = false;
1623
1624 let world_clip_rect = map_pic_to_world
1627 .map(&tile_cache.local_clip_rect)
1628 .expect("bug: unable to map clip rect")
1629 .round();
1630 let device_clip_rect = (world_clip_rect * frame_context.global_device_pixel_scale).round();
1631
1632 for (sub_slice_index, sub_slice) in tile_cache.sub_slices.iter_mut().enumerate() {
1633 for tile in sub_slice.tiles.values_mut() {
1634 tile.cached_surface.local_dirty_rect = tile.cached_surface.local_dirty_rect
1636 .intersection(&tile.cached_surface.current_descriptor.local_valid_rect)
1637 .unwrap_or_else(|| { tile.cached_surface.is_valid = true; PictureRect::zero() });
1638
1639 let valid_rect = frame_state.composite_state.get_surface_rect(
1640 &tile.cached_surface.current_descriptor.local_valid_rect,
1641 &tile.cached_surface.local_rect,
1642 tile_cache.transform_index,
1643 ).to_i32();
1644
1645 let scissor_rect = frame_state.composite_state.get_surface_rect(
1646 &tile.cached_surface.local_dirty_rect,
1647 &tile.cached_surface.local_rect,
1648 tile_cache.transform_index,
1649 ).to_i32().intersection(&valid_rect).unwrap_or_else(|| { Box2D::zero() });
1650
1651 if tile.is_visible {
1652 let world_draw_rect = world_clip_rect.intersection(&tile.world_valid_rect);
1654
1655 match world_draw_rect {
1660 Some(world_draw_rect) => {
1661 let check_occluded_tiles = match frame_state.composite_state.compositor_kind {
1662 CompositorKind::Layer { .. } => true,
1663 CompositorKind::Native { .. } | CompositorKind::Draw { .. } => {
1664 tile_cache.spatial_node_index == frame_context.root_spatial_node_index
1666 }
1667 };
1668 if check_occluded_tiles &&
1669 frame_state.composite_state.occluders.is_tile_occluded(tile.z_id, world_draw_rect) {
1670 let surface = tile.surface.as_mut().expect("no tile surface set!");
1675
1676 if let TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { id, .. }, .. } = surface {
1677 if let Some(id) = id.take() {
1678 frame_state.resource_cache.destroy_compositor_tile(id);
1679 }
1680 }
1681
1682 tile.is_visible = false;
1683
1684 if frame_context.fb_config.testing {
1685 debug_info.tiles.insert(
1686 tile.tile_offset,
1687 TileDebugInfo::Occluded,
1688 );
1689 }
1690
1691 continue;
1692 }
1693 }
1694 None => {
1695 tile.is_visible = false;
1696 }
1697 }
1698
1699 if !tile.cached_surface.is_valid && (scissor_rect.is_empty() || valid_rect.is_empty()) {
1707 tile.is_visible = false;
1708 }
1709 }
1710
1711 if let Some(TileSurface::Texture { descriptor, .. }) = tile.surface.as_ref() {
1716 if let SurfaceTextureDescriptor::TextureCache { handle: Some(handle), .. } = descriptor {
1717 frame_state.resource_cache
1718 .picture_textures.request(handle);
1719 }
1720 }
1721
1722 if !tile.is_visible {
1724 if frame_context.fb_config.testing {
1725 debug_info.tiles.insert(
1726 tile.tile_offset,
1727 TileDebugInfo::Culled,
1728 );
1729 }
1730
1731 continue;
1732 }
1733
1734 at_least_one_tile_visible = true;
1735
1736 if let TileSurface::Texture { descriptor, .. } = tile.surface.as_mut().unwrap() {
1737 match descriptor {
1738 SurfaceTextureDescriptor::TextureCache { ref handle, .. } => {
1739 let exists = handle.as_ref().map_or(false,
1740 |handle| frame_state.resource_cache.picture_textures.entry_exists(handle)
1741 );
1742 if exists {
1744 frame_state.resource_cache
1753 .picture_textures
1754 .request(handle.as_ref().unwrap());
1755 } else {
1756 tile.invalidate(None, InvalidationReason::NoTexture);
1759 }
1760 }
1761 SurfaceTextureDescriptor::Native { id, .. } => {
1762 if id.is_none() {
1763 tile.invalidate(None, InvalidationReason::NoSurface);
1765 }
1766 }
1767 }
1768 }
1769
1770 tile.cached_surface.local_dirty_rect = tile.cached_surface.local_dirty_rect
1773 .intersection(&tile.cached_surface.current_descriptor.local_valid_rect)
1774 .unwrap_or_else(|| { tile.cached_surface.is_valid = true; PictureRect::zero() });
1775
1776 surface_local_dirty_rect = surface_local_dirty_rect.union(&tile.cached_surface.local_dirty_rect);
1777
1778 let world_dirty_rect = map_pic_to_world.map(&tile.cached_surface.local_dirty_rect).expect("bug");
1780
1781 let device_rect = (tile.world_tile_rect * frame_context.global_device_pixel_scale).round();
1782 tile.device_dirty_rect = (world_dirty_rect * frame_context.global_device_pixel_scale)
1783 .round_out()
1784 .intersection(&device_rect)
1785 .unwrap_or_else(DeviceRect::zero);
1786
1787 if tile.cached_surface.is_valid {
1788 if frame_context.fb_config.testing {
1789 debug_info.tiles.insert(
1790 tile.tile_offset,
1791 TileDebugInfo::Valid,
1792 );
1793 }
1794 } else {
1795 frame_state.composite_state.did_rasterize_any_tile = true;
1797
1798 tile_cache.dirty_region.add_dirty_region(
1802 tile.cached_surface.local_dirty_rect,
1803 frame_context.spatial_tree,
1804 );
1805
1806 if let TileSurface::Texture { ref mut descriptor } = tile.surface.as_mut().unwrap() {
1808 match descriptor {
1809 SurfaceTextureDescriptor::TextureCache { ref mut handle } => {
1810
1811 frame_state.resource_cache.picture_textures.update(
1812 tile_cache.current_tile_size,
1813 handle,
1814 &mut frame_state.resource_cache.texture_cache.next_id,
1815 &mut frame_state.resource_cache.texture_cache.pending_updates,
1816 );
1817 }
1818 SurfaceTextureDescriptor::Native { id } => {
1819 if id.is_none() {
1820 if sub_slice.native_surface.is_none() {
1824 let opaque = frame_state
1825 .resource_cache
1826 .create_compositor_surface(
1827 tile_cache.virtual_offset,
1828 tile_cache.current_tile_size,
1829 true,
1830 );
1831
1832 let alpha = frame_state
1833 .resource_cache
1834 .create_compositor_surface(
1835 tile_cache.virtual_offset,
1836 tile_cache.current_tile_size,
1837 false,
1838 );
1839
1840 sub_slice.native_surface = Some(NativeSurface {
1841 opaque,
1842 alpha,
1843 });
1844 }
1845
1846 let surface_id = if tile.is_opaque {
1848 sub_slice.native_surface.as_ref().unwrap().opaque
1849 } else {
1850 sub_slice.native_surface.as_ref().unwrap().alpha
1851 };
1852
1853 let tile_id = NativeTileId {
1854 surface_id,
1855 x: tile.tile_offset.x,
1856 y: tile.tile_offset.y,
1857 };
1858
1859 frame_state.resource_cache.create_compositor_tile(tile_id);
1860
1861 *id = Some(tile_id);
1862 }
1863 }
1864 }
1865
1866 let content_origin_f = tile.cached_surface.local_rect.min.cast_unit() * device_pixel_scale;
1873 let content_origin = content_origin_f.round();
1874 debug_assert!((content_origin_f.x - content_origin.x).abs() < 0.15);
1879 debug_assert!((content_origin_f.y - content_origin.y).abs() < 0.15);
1880
1881 let surface = descriptor.resolve(
1882 frame_state.resource_cache,
1883 tile_cache.current_tile_size,
1884 );
1885
1886 let scissor_rect = frame_state.composite_state.get_surface_rect(
1888 &tile.cached_surface.local_dirty_rect,
1889 &tile.cached_surface.local_rect,
1890 tile_cache.transform_index,
1891 ).to_i32();
1892
1893 let composite_task_size = tile_cache.current_tile_size;
1894
1895 let tile_key = TileKey {
1896 sub_slice_index: SubSliceIndex::new(sub_slice_index),
1897 tile_offset: tile.tile_offset,
1898 };
1899
1900 let mut clear_color = ColorF::TRANSPARENT;
1901
1902 if SubSliceIndex::new(sub_slice_index).is_primary() {
1903 if let Some(background_color) = tile_cache.background_color {
1904 clear_color = background_color;
1905 }
1906
1907 if let Some(color) = tile_cache.backdrop.spanning_opaque_color {
1913 clear_color = color;
1914 }
1915 }
1916
1917 let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
1918
1919 let use_tile_composite = !tile.cached_surface.sub_graphs.is_empty();
1923
1924 if use_tile_composite {
1925 let mut local_content_rect = tile.cached_surface.local_dirty_rect;
1926
1927 for (sub_graph_rect, surface_stack) in &tile.cached_surface.sub_graphs {
1928 if let Some(dirty_sub_graph_rect) = sub_graph_rect.intersection(&tile.cached_surface.local_dirty_rect) {
1929 for (composite_mode, surface_index) in surface_stack {
1930 let surface = &frame_state.surfaces[surface_index.0];
1931
1932 let rect = composite_mode.get_coverage(
1933 surface,
1934 Some(dirty_sub_graph_rect.cast_unit()),
1935 ).cast_unit();
1936
1937 local_content_rect = local_content_rect.union(&rect);
1938 }
1939 }
1940 }
1941
1942 let max_content_rect = (tile.cached_surface.local_dirty_rect.cast_unit() * device_pixel_scale)
1946 .inflate(
1947 MAX_BLUR_RADIUS * BLUR_SAMPLE_SCALE,
1948 MAX_BLUR_RADIUS * BLUR_SAMPLE_SCALE,
1949 )
1950 .round_out()
1951 .to_i32();
1952
1953 let content_device_rect = (local_content_rect.cast_unit() * device_pixel_scale)
1954 .round_out()
1955 .to_i32();
1956
1957 let content_device_rect = content_device_rect
1958 .intersection(&max_content_rect)
1959 .expect("bug: no intersection with tile dirty rect: {content_device_rect:?} / {max_content_rect:?}");
1960
1961 let content_task_size = content_device_rect.size();
1962 let normalized_content_rect = content_task_size.into();
1963
1964 let inner_offset = content_origin + scissor_rect.min.to_vector().to_f32();
1965 let outer_offset = content_device_rect.min.to_f32();
1966 let sub_rect_offset = (inner_offset - outer_offset).round().to_i32();
1967
1968 let render_task_id = frame_state.rg_builder.add().init(
1969 RenderTask::new_dynamic(
1970 content_task_size,
1971 RenderTaskKind::new_picture(
1972 content_task_size,
1973 true,
1974 content_device_rect.min.to_f32(),
1975 surface_spatial_node_index,
1976 surface_spatial_node_index,
1978 device_pixel_scale,
1979 Some(normalized_content_rect),
1980 None,
1981 Some(clear_color),
1982 cmd_buffer_index,
1983 false,
1984 None,
1985 )
1986 ),
1987 );
1988
1989 let composite_task_id = frame_state.rg_builder.add().init(
1990 RenderTask::new(
1991 RenderTaskLocation::Static {
1992 surface: StaticRenderTaskSurface::PictureCache {
1993 surface,
1994 },
1995 rect: composite_task_size.into(),
1996 },
1997 RenderTaskKind::new_tile_composite(
1998 sub_rect_offset,
1999 scissor_rect,
2000 valid_rect,
2001 clear_color,
2002 ),
2003 ),
2004 );
2005
2006 surface_render_tasks.insert(
2007 tile_key,
2008 SurfaceTileDescriptor {
2009 current_task_id: render_task_id,
2010 composite_task_id: Some(composite_task_id),
2011 dirty_rect: tile.cached_surface.local_dirty_rect,
2012 },
2013 );
2014 } else {
2015 let render_task_id = frame_state.rg_builder.add().init(
2016 RenderTask::new(
2017 RenderTaskLocation::Static {
2018 surface: StaticRenderTaskSurface::PictureCache {
2019 surface,
2020 },
2021 rect: composite_task_size.into(),
2022 },
2023 RenderTaskKind::new_picture(
2024 composite_task_size,
2025 true,
2026 content_origin,
2027 surface_spatial_node_index,
2028 surface_spatial_node_index,
2030 device_pixel_scale,
2031 Some(scissor_rect),
2032 Some(valid_rect),
2033 Some(clear_color),
2034 cmd_buffer_index,
2035 false,
2036 None,
2037 )
2038 ),
2039 );
2040
2041 surface_render_tasks.insert(
2042 tile_key,
2043 SurfaceTileDescriptor {
2044 current_task_id: render_task_id,
2045 composite_task_id: None,
2046 dirty_rect: tile.cached_surface.local_dirty_rect,
2047 },
2048 );
2049 }
2050 }
2051
2052 if frame_context.fb_config.testing {
2053 debug_info.tiles.insert(
2054 tile.tile_offset,
2055 TileDebugInfo::Dirty(DirtyTileDebugInfo {
2056 local_valid_rect: tile.cached_surface.current_descriptor.local_valid_rect,
2057 local_dirty_rect: tile.cached_surface.local_dirty_rect,
2058 }),
2059 );
2060 }
2061 }
2062
2063 let surface = tile.surface.as_ref().expect("no tile surface set!");
2064
2065 let descriptor = CompositeTileDescriptor {
2066 surface_kind: surface.into(),
2067 tile_id: tile.id,
2068 };
2069
2070 let (surface, is_opaque) = match surface {
2071 TileSurface::Color { color } => {
2072 (CompositeTileSurface::Color { color: *color }, true)
2073 }
2074 TileSurface::Texture { descriptor, .. } => {
2075 let surface = descriptor.resolve(frame_state.resource_cache, tile_cache.current_tile_size);
2076 (
2077 CompositeTileSurface::Texture { surface },
2078 tile.is_opaque
2079 )
2080 }
2081 };
2082
2083 if is_opaque {
2084 sub_slice.opaque_tile_descriptors.push(descriptor);
2085 } else {
2086 sub_slice.alpha_tile_descriptors.push(descriptor);
2087 }
2088
2089 let composite_tile = CompositeTile {
2090 kind: tile_kind(&surface, is_opaque),
2091 surface,
2092 local_rect: tile.cached_surface.local_rect,
2093 local_valid_rect: tile.cached_surface.current_descriptor.local_valid_rect,
2094 local_dirty_rect: tile.cached_surface.local_dirty_rect,
2095 device_clip_rect,
2096 z_id: tile.z_id,
2097 transform_index: tile_cache.transform_index,
2098 clip_index: tile_cache.compositor_clip,
2099 tile_id: Some(tile.id),
2100 };
2101
2102 sub_slice.composite_tiles.push(composite_tile);
2103
2104 tile.cached_surface.local_dirty_rect = PictureRect::zero();
2106 tile.cached_surface.is_valid = true;
2107 }
2108
2109 sub_slice.opaque_tile_descriptors.sort_by_key(|desc| desc.tile_id);
2114 sub_slice.alpha_tile_descriptors.sort_by_key(|desc| desc.tile_id);
2115 }
2116
2117 let backdrop_rect = tile_cache.backdrop.backdrop_rect
2119 .intersection(&tile_cache.local_rect)
2120 .and_then(|r| {
2121 r.intersection(&tile_cache.local_clip_rect)
2122 });
2123
2124 let mut backdrop_in_use_and_visible = false;
2125 if let Some(backdrop_rect) = backdrop_rect {
2126 let supports_surface_for_backdrop = match frame_state.composite_state.compositor_kind {
2127 CompositorKind::Draw { .. } | CompositorKind::Layer { .. } => {
2128 false
2129 }
2130 CompositorKind::Native { capabilities, .. } => {
2131 capabilities.supports_surface_for_backdrop
2132 }
2133 };
2134 if supports_surface_for_backdrop && !tile_cache.found_prims_after_backdrop && at_least_one_tile_visible {
2135 if let Some(BackdropKind::Color { color }) = tile_cache.backdrop.kind {
2136 backdrop_in_use_and_visible = true;
2137
2138 for sub_slice in &mut tile_cache.sub_slices {
2141 for tile in sub_slice.tiles.values_mut() {
2142 tile.is_visible = false;
2143 }
2144 }
2145
2146 if let Some(backdrop_surface) = &tile_cache.backdrop_surface {
2149 if backdrop_surface.color != color {
2150 frame_state.resource_cache.destroy_compositor_surface(backdrop_surface.id);
2151 tile_cache.backdrop_surface = None;
2152 }
2153 }
2154
2155 let world_backdrop_rect = map_pic_to_world.map(&backdrop_rect).expect("bug: unable to map backdrop rect");
2158 let device_rect = (world_backdrop_rect * frame_context.global_device_pixel_scale).round();
2159
2160 if let Some(backdrop_surface) = &mut tile_cache.backdrop_surface {
2163 backdrop_surface.device_rect = device_rect;
2164 } else {
2165 tile_cache.backdrop_surface = Some(BackdropSurface {
2167 id: frame_state.resource_cache.create_compositor_backdrop_surface(color),
2168 color,
2169 device_rect,
2170 });
2171 }
2172 }
2173 }
2174 }
2175
2176 if !backdrop_in_use_and_visible {
2177 if let Some(backdrop_surface) = &tile_cache.backdrop_surface {
2178 frame_state.resource_cache.destroy_compositor_surface(backdrop_surface.id);
2181 tile_cache.backdrop_surface = None;
2182 }
2183 }
2184
2185 if frame_context.debug_flags.contains(DebugFlags::INVALIDATION_DBG) {
2187 tile_cache.print();
2188 }
2189
2190 if frame_context.fb_config.testing {
2193 debug_info.compositor_clip = tile_cache.compositor_clip.map(|clip_index| {
2194 let clip = frame_state.composite_state.get_compositor_clip(clip_index);
2195 CompositorClipDebugInfo {
2196 rect: clip.rect,
2197 radius: clip.radius,
2198 }
2199 });
2200
2201 frame_state.composite_state
2202 .picture_cache_debug
2203 .slices
2204 .insert(
2205 tile_cache.slice,
2206 debug_info,
2207 );
2208 }
2209
2210 let descriptor = SurfaceDescriptor::new_tiled(surface_render_tasks);
2211
2212 frame_state.surface_builder.push_surface(
2213 surface_index,
2214 false,
2215 surface_local_dirty_rect,
2216 Some(descriptor),
2217 frame_state.surfaces,
2218 frame_state.rg_builder,
2219 );
2220}
2221
2222fn compute_subpixel_mode(
2223 raster_config: &Option<RasterConfig>,
2224 tile_caches: &FastHashMap<SliceId, Box<TileCacheInstance>>,
2225 parent_subpixel_mode: SubpixelMode,
2226) -> SubpixelMode {
2227
2228 let subpixel_mode = match raster_config {
2231 Some(RasterConfig { ref composite_mode, .. }) => {
2232 let subpixel_mode = match composite_mode {
2233 PictureCompositeMode::TileCache { slice_id } => {
2234 tile_caches[&slice_id].subpixel_mode
2235 }
2236 PictureCompositeMode::Blit(..) |
2237 PictureCompositeMode::ComponentTransferFilter(..) |
2238 PictureCompositeMode::Filter(..) |
2239 PictureCompositeMode::MixBlend(..) |
2240 PictureCompositeMode::IntermediateSurface |
2241 PictureCompositeMode::SVGFEGraph(..) => {
2242 SubpixelMode::Deny
2247 }
2248 };
2249
2250 subpixel_mode
2251 }
2252 None => {
2253 SubpixelMode::Allow
2254 }
2255 };
2256
2257 let subpixel_mode = match (parent_subpixel_mode, subpixel_mode) {
2259 (SubpixelMode::Allow, SubpixelMode::Allow) => {
2260 SubpixelMode::Allow
2262 }
2263 (SubpixelMode::Allow, SubpixelMode::Conditional { allowed_rect, prohibited_rect }) => {
2264 SubpixelMode::Conditional {
2266 allowed_rect,
2267 prohibited_rect,
2268 }
2269 }
2270 (SubpixelMode::Conditional { allowed_rect, prohibited_rect }, SubpixelMode::Allow) => {
2271 SubpixelMode::Conditional {
2273 allowed_rect,
2274 prohibited_rect,
2275 }
2276 }
2277 (SubpixelMode::Conditional { .. }, SubpixelMode::Conditional { ..}) => {
2278 unreachable!("bug: only top level picture caches have conditional subpixel");
2279 }
2280 (SubpixelMode::Deny, _) | (_, SubpixelMode::Deny) => {
2281 SubpixelMode::Deny
2283 }
2284 };
2285
2286 subpixel_mode
2287}
2288
2289#[test]
2290fn test_large_surface_scale_1() {
2291 use crate::spatial_tree::{SceneSpatialTree, SpatialTree};
2292
2293 let mut cst = SceneSpatialTree::new();
2294 let root_reference_frame_index = cst.root_reference_frame_index();
2295
2296 let mut spatial_tree = SpatialTree::new();
2297 spatial_tree.apply_updates(cst.end_frame_and_get_pending_updates());
2298 spatial_tree.update_tree(&SceneProperties::new());
2299
2300 let map_local_to_picture = SpaceMapper::new_with_target(
2301 root_reference_frame_index,
2302 root_reference_frame_index,
2303 PictureRect::max_rect(),
2304 &spatial_tree,
2305 );
2306
2307 let mut surfaces = vec![
2308 SurfaceInfo {
2309 unclipped_local_rect: PictureRect::max_rect(),
2310 clipped_local_rect: PictureRect::max_rect(),
2311 is_opaque: true,
2312 clipping_rect: PictureRect::max_rect(),
2313 culling_rect: VisRect::max_rect(),
2314 map_local_to_picture: map_local_to_picture.clone(),
2315 raster_spatial_node_index: root_reference_frame_index,
2316 surface_spatial_node_index: root_reference_frame_index,
2317 visibility_spatial_node_index: root_reference_frame_index,
2318 device_pixel_scale: DevicePixelScale::new(1.0),
2319 world_scale_factors: (1.0, 1.0),
2320 local_scale: (1.0, 1.0),
2321 allow_snapping: true,
2322 force_scissor_rect: false,
2323 },
2324 SurfaceInfo {
2325 unclipped_local_rect: PictureRect::new(
2326 PicturePoint::new(52.76350021362305, 0.0),
2327 PicturePoint::new(159.6738739013672, 35.0),
2328 ),
2329 clipped_local_rect: PictureRect::max_rect(),
2330 is_opaque: true,
2331 clipping_rect: PictureRect::max_rect(),
2332 culling_rect: VisRect::max_rect(),
2333 map_local_to_picture,
2334 raster_spatial_node_index: root_reference_frame_index,
2335 surface_spatial_node_index: root_reference_frame_index,
2336 visibility_spatial_node_index: root_reference_frame_index,
2337 device_pixel_scale: DevicePixelScale::new(43.82798767089844),
2338 world_scale_factors: (1.0, 1.0),
2339 local_scale: (1.0, 1.0),
2340 allow_snapping: true,
2341 force_scissor_rect: false,
2342 },
2343 ];
2344
2345 get_surface_rects(
2346 SurfaceIndex(1),
2347 &PictureCompositeMode::Blit(BlitReason::BLEND_MODE),
2348 SurfaceIndex(0),
2349 &mut surfaces,
2350 &spatial_tree,
2351 MAX_SURFACE_SIZE as f32,
2352 false,
2353 );
2354}
2355
2356#[test]
2357fn test_drop_filter_dirty_region_outside_prim() {
2358 use api::Shadow;
2364 use crate::spatial_tree::{SceneSpatialTree, SpatialTree};
2365
2366 let mut cst = SceneSpatialTree::new();
2367 let root_reference_frame_index = cst.root_reference_frame_index();
2368
2369 let mut spatial_tree = SpatialTree::new();
2370 spatial_tree.apply_updates(cst.end_frame_and_get_pending_updates());
2371 spatial_tree.update_tree(&SceneProperties::new());
2372
2373 let map_local_to_picture = SpaceMapper::new_with_target(
2374 root_reference_frame_index,
2375 root_reference_frame_index,
2376 PictureRect::max_rect(),
2377 &spatial_tree,
2378 );
2379
2380 let mut surfaces = vec![
2381 SurfaceInfo {
2382 unclipped_local_rect: PictureRect::max_rect(),
2383 clipped_local_rect: PictureRect::max_rect(),
2384 is_opaque: true,
2385 clipping_rect: PictureRect::max_rect(),
2386 map_local_to_picture: map_local_to_picture.clone(),
2387 raster_spatial_node_index: root_reference_frame_index,
2388 surface_spatial_node_index: root_reference_frame_index,
2389 visibility_spatial_node_index: root_reference_frame_index,
2390 device_pixel_scale: DevicePixelScale::new(1.0),
2391 world_scale_factors: (1.0, 1.0),
2392 local_scale: (1.0, 1.0),
2393 allow_snapping: true,
2394 force_scissor_rect: false,
2395 culling_rect: VisRect::max_rect(),
2396 },
2397 SurfaceInfo {
2398 unclipped_local_rect: PictureRect::new(
2399 PicturePoint::new(0.0, 0.0),
2400 PicturePoint::new(750.0, 450.0),
2401 ),
2402 clipped_local_rect: PictureRect::new(
2403 PicturePoint::new(0.0, 0.0),
2404 PicturePoint::new(750.0, 450.0),
2405 ),
2406 is_opaque: true,
2407 clipping_rect: PictureRect::max_rect(),
2408 map_local_to_picture,
2409 raster_spatial_node_index: root_reference_frame_index,
2410 surface_spatial_node_index: root_reference_frame_index,
2411 visibility_spatial_node_index: root_reference_frame_index,
2412 device_pixel_scale: DevicePixelScale::new(1.0),
2413 world_scale_factors: (1.0, 1.0),
2414 local_scale: (1.0, 1.0),
2415 allow_snapping: true,
2416 force_scissor_rect: false,
2417 culling_rect: VisRect::max_rect(),
2418 },
2419 ];
2420
2421 let shadows = smallvec![
2422 Shadow {
2423 offset: LayoutVector2D::zero(),
2424 color: ColorF::BLACK,
2425 blur_radius: 75.0,
2426 },
2427 ];
2428
2429 let composite_mode = PictureCompositeMode::Filter(Filter::DropShadows(shadows));
2430
2431 let info = get_surface_rects(
2433 SurfaceIndex(1),
2434 &composite_mode,
2435 SurfaceIndex(0),
2436 &mut surfaces,
2437 &spatial_tree,
2438 MAX_SURFACE_SIZE as f32,
2439 false,
2440 ).expect("No surface rect");
2441 assert_eq!(info.task_size, DeviceIntSize::new(1200, 900));
2442
2443 surfaces[0].clipping_rect = PictureRect::new(
2445 PicturePoint::new(768.0, 128.0),
2446 PicturePoint::new(1024.0, 256.0),
2447 );
2448 let info = get_surface_rects(
2449 SurfaceIndex(1),
2450 &composite_mode,
2451 SurfaceIndex(0),
2452 &mut surfaces,
2453 &spatial_tree,
2454 MAX_SURFACE_SIZE as f32,
2455 false,
2456 ).expect("No surface rect");
2457 assert_eq!(info.task_size, DeviceIntSize::new(432, 578));
2458}
2459
2460#[test]
2461fn test_drop_filter_partial_dirty_content_inflate() {
2462 use api::Shadow;
2471 use crate::spatial_tree::{SceneSpatialTree, SpatialTree};
2472
2473 let mut cst = SceneSpatialTree::new();
2474 let root_reference_frame_index = cst.root_reference_frame_index();
2475
2476 let mut spatial_tree = SpatialTree::new();
2477 spatial_tree.apply_updates(cst.end_frame_and_get_pending_updates());
2478 spatial_tree.update_tree(&SceneProperties::new());
2479
2480 let map_local_to_picture = SpaceMapper::new_with_target(
2481 root_reference_frame_index,
2482 root_reference_frame_index,
2483 PictureRect::max_rect(),
2484 &spatial_tree,
2485 );
2486
2487 let mut surfaces = vec![
2489 SurfaceInfo {
2490 unclipped_local_rect: PictureRect::max_rect(),
2491 clipped_local_rect: PictureRect::max_rect(),
2492 is_opaque: true,
2493 clipping_rect: PictureRect::new(
2498 PicturePoint::new(0.0, 0.0),
2499 PicturePoint::new(683.0, 341.0),
2500 ),
2501 map_local_to_picture: map_local_to_picture.clone(),
2502 raster_spatial_node_index: root_reference_frame_index,
2503 surface_spatial_node_index: root_reference_frame_index,
2504 visibility_spatial_node_index: root_reference_frame_index,
2505 device_pixel_scale: DevicePixelScale::new(1.0),
2506 world_scale_factors: (1.0, 1.0),
2507 local_scale: (1.0, 1.0),
2508 allow_snapping: true,
2509 force_scissor_rect: false,
2510 culling_rect: VisRect::max_rect(),
2511 },
2512 SurfaceInfo {
2513 unclipped_local_rect: PictureRect::new(
2514 PicturePoint::new(0.0, 0.0),
2515 PicturePoint::new(500.0, 500.0),
2516 ),
2517 clipped_local_rect: PictureRect::new(
2518 PicturePoint::new(0.0, 0.0),
2519 PicturePoint::new(500.0, 500.0),
2520 ),
2521 is_opaque: true,
2522 clipping_rect: PictureRect::max_rect(),
2523 map_local_to_picture,
2524 raster_spatial_node_index: root_reference_frame_index,
2525 surface_spatial_node_index: root_reference_frame_index,
2526 visibility_spatial_node_index: root_reference_frame_index,
2527 device_pixel_scale: DevicePixelScale::new(1.0),
2528 world_scale_factors: (1.0, 1.0),
2529 local_scale: (1.0, 1.0),
2530 allow_snapping: true,
2531 force_scissor_rect: false,
2532 culling_rect: VisRect::max_rect(),
2533 },
2534 ];
2535
2536 let shadows = smallvec![
2537 Shadow {
2538 offset: LayoutVector2D::new(400.0, 100.0),
2539 color: ColorF::BLACK,
2540 blur_radius: 20.0,
2541 },
2542 ];
2543
2544 let composite_mode = PictureCompositeMode::Filter(Filter::DropShadows(shadows));
2545
2546 let info = get_surface_rects(
2547 SurfaceIndex(1),
2548 &composite_mode,
2549 SurfaceIndex(0),
2550 &mut surfaces,
2551 &spatial_tree,
2552 MAX_SURFACE_SIZE as f32,
2553 false,
2554 ).expect("No surface rect");
2555
2556 assert_eq!(info.task_size, DeviceIntSize::new(620, 461));
2564}