Bug 1706561 Part 5: Intern polygon data for image masks, and retrieve for hit tests...
[gecko.git] / gfx / wr / webrender / src / picture.rs
blob7527101881f302e4c917d5d016108e1a2306fd7d
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 //! A picture represents a dynamically rendered image.
6 //!
7 //! # Overview
8 //!
9 //! Pictures consists of:
10 //!
11 //! - A number of primitives that are drawn onto the picture.
12 //! - A composite operation describing how to composite this
13 //!   picture into its parent.
14 //! - A configuration describing how to draw the primitives on
15 //!   this picture (e.g. in screen space or local space).
16 //!
17 //! The tree of pictures are generated during scene building.
18 //!
19 //! Depending on their composite operations pictures can be rendered into
20 //! intermediate targets or folded into their parent picture.
21 //!
22 //! ## Picture caching
23 //!
24 //! Pictures can be cached to reduce the amount of rasterization happening per
25 //! frame.
26 //!
27 //! When picture caching is enabled, the scene is cut into a small number of slices,
28 //! typically:
29 //!
30 //! - content slice
31 //! - UI slice
32 //! - background UI slice which is hidden by the other two slices most of the time.
33 //!
34 //! Each of these slice is made up of fixed-size large tiles of 2048x512 pixels
35 //! (or 128x128 for the UI slice).
36 //!
37 //! Tiles can be either cached rasterized content into a texture or "clear tiles"
38 //! that contain only a solid color rectangle rendered directly during the composite
39 //! pass.
40 //!
41 //! ## Invalidation
42 //!
43 //! Each tile keeps track of the elements that affect it, which can be:
44 //!
45 //! - primitives
46 //! - clips
47 //! - image keys
48 //! - opacity bindings
49 //! - transforms
50 //!
51 //! These dependency lists are built each frame and compared to the previous frame to
52 //! see if the tile changed.
53 //!
54 //! The tile's primitive dependency information is organized in a quadtree, each node
55 //! storing an index buffer of tile primitive dependencies.
56 //!
57 //! The union of the invalidated leaves of each quadtree produces a per-tile dirty rect
58 //! which defines the scissor rect used when replaying the tile's drawing commands and
59 //! can be used for partial present.
60 //!
61 //! ## Display List shape
62 //!
63 //! WR will first look for an iframe item in the root stacking context to apply
64 //! picture caching to. If that's not found, it will apply to the entire root
65 //! stacking context of the display list. Apart from that, the format of the
66 //! display list is not important to picture caching. Each time a new scroll root
67 //! is encountered, a new picture cache slice will be created. If the display
68 //! list contains more than some arbitrary number of slices (currently 8), the
69 //! content will all be squashed into a single slice, in order to save GPU memory
70 //! and compositing performance.
71 //!
72 //! ## Compositor Surfaces
73 //!
74 //! Sometimes, a primitive would prefer to exist as a native compositor surface.
75 //! This allows a large and/or regularly changing primitive (such as a video, or
76 //! webgl canvas) to be updated each frame without invalidating the content of
77 //! tiles, and can provide a significant performance win and battery saving.
78 //!
79 //! Since drawing a primitive as a compositor surface alters the ordering of
80 //! primitives in a tile, we use 'overlay tiles' to ensure correctness. If a
81 //! tile has a compositor surface, _and_ that tile has primitives that overlap
82 //! the compositor surface rect, the tile switches to be drawn in alpha mode.
83 //!
84 //! We rely on only promoting compositor surfaces that are opaque primitives.
85 //! With this assumption, the tile(s) that intersect the compositor surface get
86 //! a 'cutout' in the rectangle where the compositor surface exists (not the
87 //! entire tile), allowing that tile to be drawn as an alpha tile after the
88 //! compositor surface.
89 //!
90 //! Tiles are only drawn in overlay mode if there is content that exists on top
91 //! of the compositor surface. Otherwise, we can draw the tiles in the normal fast
92 //! path before the compositor surface is drawn. Use of the per-tile valid and
93 //! dirty rects ensure that we do a minimal amount of per-pixel work here to
94 //! blend the overlay tile (this is not always optimal right now, but will be
95 //! improved as a follow up).
97 use api::{MixBlendMode, PremultipliedColorF, FilterPrimitiveKind};
98 use api::{PropertyBinding, PropertyBindingId, FilterPrimitive};
99 use api::{DebugFlags, ImageKey, ColorF, ColorU, PrimitiveFlags};
100 use api::{ImageRendering, ColorDepth, YuvColorSpace, YuvFormat, AlphaType};
101 use api::units::*;
102 use crate::batch::BatchFilter;
103 use crate::box_shadow::BLUR_SAMPLE_SCALE;
104 use crate::clip::{ClipStore, ClipChainInstance, ClipChainId, ClipInstance};
105 use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX,
106     SpatialTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace
108 use crate::composite::{CompositorKind, CompositeState, NativeSurfaceId, NativeTileId};
109 use crate::composite::{ExternalSurfaceDescriptor, ExternalSurfaceDependency};
110 use crate::debug_colors;
111 use euclid::{vec2, vec3, Point2D, Scale, Size2D, Vector2D, Vector3D, Rect, Transform3D, SideOffsets2D};
112 use euclid::approxeq::ApproxEq;
113 use crate::filterdata::SFilterData;
114 use crate::intern::ItemUid;
115 use crate::internal_types::{FastHashMap, FastHashSet, PlaneSplitter, Filter, PlaneSplitAnchor, TextureSource};
116 use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext};
117 use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
118 use crate::gpu_types::{UvRectKind, ZBufferId};
119 use plane_split::{Clipper, Polygon, Splitter};
120 use crate::prim_store::{PrimitiveTemplateKind, PictureIndex, PrimitiveInstance, PrimitiveInstanceKind};
121 use crate::prim_store::{ColorBindingStorage, ColorBindingIndex, PrimitiveScratchBuffer};
122 use crate::print_tree::{PrintTree, PrintTreePrinter};
123 use crate::render_backend::{DataStores, FrameId};
124 use crate::render_task_graph::RenderTaskId;
125 use crate::render_target::RenderTargetKind;
126 use crate::render_task::{BlurTask, RenderTask, RenderTaskLocation, BlurTaskCache};
127 use crate::render_task::{StaticRenderTaskSurface, RenderTaskKind};
128 use crate::renderer::BlendMode;
129 use crate::resource_cache::{ResourceCache, ImageGeneration, ImageRequest};
130 use crate::space::SpaceMapper;
131 use crate::scene::SceneProperties;
132 use smallvec::SmallVec;
133 use std::{mem, u8, marker, u32};
134 use std::sync::atomic::{AtomicUsize, Ordering};
135 use std::collections::hash_map::Entry;
136 use std::ops::Range;
137 use crate::texture_cache::TextureCacheHandle;
138 use crate::util::{MaxRect, VecHelper, MatrixHelpers, Recycler, raster_rect_to_device_pixels, ScaleOffset};
139 use crate::filterdata::{FilterDataHandle};
140 use crate::tile_cache::{SliceDebugInfo, TileDebugInfo, DirtyTileDebugInfo};
141 use crate::visibility::{PrimitiveVisibilityFlags, FrameVisibilityContext};
142 use crate::visibility::{VisibilityState, FrameVisibilityState};
143 #[cfg(any(feature = "capture", feature = "replay"))]
144 use ron;
145 #[cfg(feature = "capture")]
146 use crate::scene_builder_thread::InternerUpdates;
147 #[cfg(any(feature = "capture", feature = "replay"))]
148 use crate::intern::{Internable, UpdateList};
149 #[cfg(any(feature = "capture", feature = "replay"))]
150 use crate::clip::{ClipIntern, PolygonIntern};
151 #[cfg(any(feature = "capture", feature = "replay"))]
152 use crate::filterdata::FilterDataIntern;
153 #[cfg(any(feature = "capture", feature = "replay"))]
154 use api::PrimitiveKeyKind;
155 #[cfg(any(feature = "capture", feature = "replay"))]
156 use crate::prim_store::backdrop::Backdrop;
157 #[cfg(any(feature = "capture", feature = "replay"))]
158 use crate::prim_store::borders::{ImageBorder, NormalBorderPrim};
159 #[cfg(any(feature = "capture", feature = "replay"))]
160 use crate::prim_store::gradient::{LinearGradient, RadialGradient, ConicGradient};
161 #[cfg(any(feature = "capture", feature = "replay"))]
162 use crate::prim_store::image::{Image, YuvImage};
163 #[cfg(any(feature = "capture", feature = "replay"))]
164 use crate::prim_store::line_dec::LineDecoration;
165 #[cfg(any(feature = "capture", feature = "replay"))]
166 use crate::prim_store::picture::Picture;
167 #[cfg(any(feature = "capture", feature = "replay"))]
168 use crate::prim_store::text_run::TextRun;
170 #[cfg(feature = "capture")]
171 use std::fs::File;
172 #[cfg(feature = "capture")]
173 use std::io::prelude::*;
174 #[cfg(feature = "capture")]
175 use std::path::PathBuf;
176 use crate::scene_building::{SliceFlags};
178 #[cfg(feature = "replay")]
179 // used by tileview so don't use an internal_types FastHashMap
180 use std::collections::HashMap;
182 // Maximum blur radius for blur filter (different than box-shadow blur).
183 // Taken from FilterNodeSoftware.cpp in Gecko.
184 pub const MAX_BLUR_RADIUS: f32 = 100.;
186 /// Specify whether a surface allows subpixel AA text rendering.
187 #[derive(Debug, Copy, Clone)]
188 pub enum SubpixelMode {
189     /// This surface allows subpixel AA text
190     Allow,
191     /// Subpixel AA text cannot be drawn on this surface
192     Deny,
193     /// Subpixel AA can be drawn on this surface, if not intersecting
194     /// with the excluded regions, and inside the allowed rect.
195     Conditional {
196         allowed_rect: PictureRect,
197     },
200 /// A comparable transform matrix, that compares with epsilon checks.
201 #[derive(Debug, Clone)]
202 struct MatrixKey {
203     m: [f32; 16],
206 impl PartialEq for MatrixKey {
207     fn eq(&self, other: &Self) -> bool {
208         const EPSILON: f32 = 0.001;
210         // TODO(gw): It's possible that we may need to adjust the epsilon
211         //           to be tighter on most of the matrix, except the
212         //           translation parts?
213         for (i, j) in self.m.iter().zip(other.m.iter()) {
214             if !i.approx_eq_eps(j, &EPSILON) {
215                 return false;
216             }
217         }
219         true
220     }
223 /// A comparable / hashable version of a coordinate space mapping. Used to determine
224 /// if a transform dependency for a tile has changed.
225 #[derive(Debug, PartialEq, Clone)]
226 enum TransformKey {
227     Local,
228     ScaleOffset {
229         scale_x: f32,
230         scale_y: f32,
231         offset_x: f32,
232         offset_y: f32,
233     },
234     Transform {
235         m: MatrixKey,
236     }
239 impl<Src, Dst> From<CoordinateSpaceMapping<Src, Dst>> for TransformKey {
240     fn from(transform: CoordinateSpaceMapping<Src, Dst>) -> TransformKey {
241         match transform {
242             CoordinateSpaceMapping::Local => {
243                 TransformKey::Local
244             }
245             CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
246                 TransformKey::ScaleOffset {
247                     scale_x: scale_offset.scale.x,
248                     scale_y: scale_offset.scale.y,
249                     offset_x: scale_offset.offset.x,
250                     offset_y: scale_offset.offset.y,
251                 }
252             }
253             CoordinateSpaceMapping::Transform(ref m) => {
254                 TransformKey::Transform {
255                     m: MatrixKey {
256                         m: m.to_array(),
257                     },
258                 }
259             }
260         }
261     }
264 /// Unit for tile coordinates.
265 #[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
266 pub struct TileCoordinate;
268 // Geometry types for tile coordinates.
269 pub type TileOffset = Point2D<i32, TileCoordinate>;
270 // TileSize type is also used in used in lib.rs and cbindgen picks the wrong one when
271 // generating headers.
272 /// cbindgen:ignore
273 pub type TileSize = Size2D<i32, TileCoordinate>;
274 pub type TileRect = Rect<i32, TileCoordinate>;
276 /// The maximum number of compositor surfaces that are allowed per picture cache. This
277 /// is an arbitrary number that should be enough for common cases, but low enough to
278 /// prevent performance and memory usage drastically degrading in pathological cases.
279 const MAX_COMPOSITOR_SURFACES: usize = 4;
281 /// The size in device pixels of a normal cached tile.
282 pub const TILE_SIZE_DEFAULT: DeviceIntSize = DeviceIntSize {
283     width: 1024,
284     height: 512,
285     _unit: marker::PhantomData,
288 /// The size in device pixels of a tile for horizontal scroll bars
289 pub const TILE_SIZE_SCROLLBAR_HORIZONTAL: DeviceIntSize = DeviceIntSize {
290     width: 1024,
291     height: 32,
292     _unit: marker::PhantomData,
295 /// The size in device pixels of a tile for vertical scroll bars
296 pub const TILE_SIZE_SCROLLBAR_VERTICAL: DeviceIntSize = DeviceIntSize {
297     width: 32,
298     height: 1024,
299     _unit: marker::PhantomData,
302 /// The maximum size per axis of a surface,
303 ///  in WorldPixel coordinates.
304 const MAX_SURFACE_SIZE: f32 = 4096.0;
305 /// Maximum size of a compositor surface.
306 const MAX_COMPOSITOR_SURFACES_SIZE: f32 = 8192.0;
308 /// The maximum number of sub-dependencies (e.g. clips, transforms) we can handle
309 /// per-primitive. If a primitive has more than this, it will invalidate every frame.
310 const MAX_PRIM_SUB_DEPS: usize = u8::MAX as usize;
312 /// Used to get unique tile IDs, even when the tile cache is
313 /// destroyed between display lists / scenes.
314 static NEXT_TILE_ID: AtomicUsize = AtomicUsize::new(0);
316 fn clamp(value: i32, low: i32, high: i32) -> i32 {
317     value.max(low).min(high)
320 fn clampf(value: f32, low: f32, high: f32) -> f32 {
321     value.max(low).min(high)
324 /// Clamps the blur radius depending on scale factors.
325 fn clamp_blur_radius(blur_radius: f32, scale_factors: (f32, f32)) -> f32 {
326     // Clamping must occur after scale factors are applied, but scale factors are not applied
327     // until later on. To clamp the blur radius, we first apply the scale factors and then clamp
328     // and finally revert the scale factors.
330     // TODO: the clamping should be done on a per-axis basis, but WR currently only supports
331     // having a single value for both x and y blur.
332     let largest_scale_factor = f32::max(scale_factors.0, scale_factors.1);
333     let scaled_blur_radius = blur_radius * largest_scale_factor;
335     if scaled_blur_radius > MAX_BLUR_RADIUS {
336         MAX_BLUR_RADIUS / largest_scale_factor
337     } else {
338         // Return the original blur radius to avoid any rounding errors
339         blur_radius
340     }
343 /// An index into the prims array in a TileDescriptor.
344 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
345 #[cfg_attr(feature = "capture", derive(Serialize))]
346 #[cfg_attr(feature = "replay", derive(Deserialize))]
347 pub struct PrimitiveDependencyIndex(pub u32);
349 /// Information about the state of a binding.
350 #[derive(Debug)]
351 pub struct BindingInfo<T> {
352     /// The current value retrieved from dynamic scene properties.
353     value: T,
354     /// True if it was changed (or is new) since the last frame build.
355     changed: bool,
358 /// Information stored in a tile descriptor for a binding.
359 #[derive(Debug, PartialEq, Clone, Copy)]
360 #[cfg_attr(feature = "capture", derive(Serialize))]
361 #[cfg_attr(feature = "replay", derive(Deserialize))]
362 pub enum Binding<T> {
363     Value(T),
364     Binding(PropertyBindingId),
367 impl<T> From<PropertyBinding<T>> for Binding<T> {
368     fn from(binding: PropertyBinding<T>) -> Binding<T> {
369         match binding {
370             PropertyBinding::Binding(key, _) => Binding::Binding(key.id),
371             PropertyBinding::Value(value) => Binding::Value(value),
372         }
373     }
376 pub type OpacityBinding = Binding<f32>;
377 pub type OpacityBindingInfo = BindingInfo<f32>;
379 pub type ColorBinding = Binding<ColorU>;
380 pub type ColorBindingInfo = BindingInfo<ColorU>;
382 /// A dependency for a transform is defined by the spatial node index + frame it was used
383 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
384 #[cfg_attr(feature = "capture", derive(Serialize))]
385 #[cfg_attr(feature = "replay", derive(Deserialize))]
386 pub struct SpatialNodeKey {
387     spatial_node_index: SpatialNodeIndex,
388     frame_id: FrameId,
391 /// A helper for comparing spatial nodes between frames. The comparisons
392 /// are done by-value, so that if the shape of the spatial node tree
393 /// changes, invalidations aren't done simply due to the spatial node
394 /// index changing between display lists.
395 struct SpatialNodeComparer {
396     /// The root spatial node index of the tile cache
397     ref_spatial_node_index: SpatialNodeIndex,
398     /// Maintains a map of currently active transform keys
399     spatial_nodes: FastHashMap<SpatialNodeKey, TransformKey>,
400     /// A cache of recent comparisons between prev and current spatial nodes
401     compare_cache: FastHashMap<(SpatialNodeKey, SpatialNodeKey), bool>,
402     /// A set of frames that we need to retain spatial node entries for
403     referenced_frames: FastHashSet<FrameId>,
406 impl SpatialNodeComparer {
407     /// Construct a new comparer
408     fn new() -> Self {
409         SpatialNodeComparer {
410             ref_spatial_node_index: ROOT_SPATIAL_NODE_INDEX,
411             spatial_nodes: FastHashMap::default(),
412             compare_cache: FastHashMap::default(),
413             referenced_frames: FastHashSet::default(),
414         }
415     }
417     /// Advance to the next frame
418     fn next_frame(
419         &mut self,
420         ref_spatial_node_index: SpatialNodeIndex,
421     ) {
422         // Drop any node information for unreferenced frames, to ensure that the
423         // hashmap doesn't grow indefinitely!
424         let referenced_frames = &self.referenced_frames;
425         self.spatial_nodes.retain(|key, _| {
426             referenced_frames.contains(&key.frame_id)
427         });
429         // Update the root spatial node for this comparer
430         self.ref_spatial_node_index = ref_spatial_node_index;
431         self.compare_cache.clear();
432         self.referenced_frames.clear();
433     }
435     /// Register a transform that is used, and build the transform key for it if new.
436     fn register_used_transform(
437         &mut self,
438         spatial_node_index: SpatialNodeIndex,
439         frame_id: FrameId,
440         spatial_tree: &SpatialTree,
441     ) {
442         let key = SpatialNodeKey {
443             spatial_node_index,
444             frame_id,
445         };
447         if let Entry::Vacant(entry) = self.spatial_nodes.entry(key) {
448             entry.insert(
449                 get_transform_key(
450                     spatial_node_index,
451                     self.ref_spatial_node_index,
452                     spatial_tree,
453                 )
454             );
455         }
456     }
458     /// Return true if the transforms for two given spatial nodes are considered equivalent
459     fn are_transforms_equivalent(
460         &mut self,
461         prev_spatial_node_key: &SpatialNodeKey,
462         curr_spatial_node_key: &SpatialNodeKey,
463     ) -> bool {
464         let key = (*prev_spatial_node_key, *curr_spatial_node_key);
465         let spatial_nodes = &self.spatial_nodes;
467         *self.compare_cache
468             .entry(key)
469             .or_insert_with(|| {
470                 let prev = &spatial_nodes[&prev_spatial_node_key];
471                 let curr = &spatial_nodes[&curr_spatial_node_key];
472                 curr == prev
473             })
474     }
476     /// Ensure that the comparer won't GC any nodes for a given frame id
477     fn retain_for_frame(&mut self, frame_id: FrameId) {
478         self.referenced_frames.insert(frame_id);
479     }
482 // Immutable context passed to picture cache tiles during pre_update
483 struct TilePreUpdateContext {
484     /// Maps from picture cache coords -> world space coords.
485     pic_to_world_mapper: SpaceMapper<PicturePixel, WorldPixel>,
487     /// The fractional position of the picture cache, which may
488     /// require invalidation of all tiles.
489     fract_offset: PictureVector2D,
490     device_fract_offset: DeviceVector2D,
492     /// The optional background color of the picture cache instance
493     background_color: Option<ColorF>,
495     /// The visible part of the screen in world coords.
496     global_screen_world_rect: WorldRect,
498     /// Current size of tiles in picture units.
499     tile_size: PictureSize,
501     /// The current frame id for this picture cache
502     frame_id: FrameId,
505 // Immutable context passed to picture cache tiles during post_update
506 struct TilePostUpdateContext<'a> {
507     /// Maps from picture cache coords -> world space coords.
508     pic_to_world_mapper: SpaceMapper<PicturePixel, WorldPixel>,
510     /// Global scale factor from world -> device pixels.
511     global_device_pixel_scale: DevicePixelScale,
513     /// The local clip rect (in picture space) of the entire picture cache
514     local_clip_rect: PictureRect,
516     /// The calculated backdrop information for this cache instance.
517     backdrop: Option<BackdropInfo>,
519     /// Information about opacity bindings from the picture cache.
520     opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>,
522     /// Information about color bindings from the picture cache.
523     color_bindings: &'a FastHashMap<PropertyBindingId, ColorBindingInfo>,
525     /// Current size in device pixels of tiles for this cache
526     current_tile_size: DeviceIntSize,
528     /// The local rect of the overall picture cache
529     local_rect: PictureRect,
531     /// Pre-allocated z-id to assign to tiles during post_update.
532     z_id: ZBufferId,
534     /// If true, the scale factor of the root transform for this picture
535     /// cache changed, so we need to invalidate the tile and re-render.
536     invalidate_all: bool,
539 // Mutable state passed to picture cache tiles during post_update
540 struct TilePostUpdateState<'a> {
541     /// Allow access to the texture cache for requesting tiles
542     resource_cache: &'a mut ResourceCache,
544     /// Current configuration and setup for compositing all the picture cache tiles in renderer.
545     composite_state: &'a mut CompositeState,
547     /// A cache of comparison results to avoid re-computation during invalidation.
548     compare_cache: &'a mut FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>,
550     /// Information about transform node differences from last frame.
551     spatial_node_comparer: &'a mut SpatialNodeComparer,
554 /// Information about the dependencies of a single primitive instance.
555 struct PrimitiveDependencyInfo {
556     /// Unique content identifier of the primitive.
557     prim_uid: ItemUid,
559     /// The (conservative) clipped area in picture space this primitive occupies.
560     prim_clip_box: PictureBox2D,
562     /// Image keys this primitive depends on.
563     images: SmallVec<[ImageDependency; 8]>,
565     /// Opacity bindings this primitive depends on.
566     opacity_bindings: SmallVec<[OpacityBinding; 4]>,
568     /// Color binding this primitive depends on.
569     color_binding: Option<ColorBinding>,
571     /// Clips that this primitive depends on.
572     clips: SmallVec<[ItemUid; 8]>,
574     /// Spatial nodes references by the clip dependencies of this primitive.
575     spatial_nodes: SmallVec<[SpatialNodeIndex; 4]>,
578 impl PrimitiveDependencyInfo {
579     /// Construct dependency info for a new primitive.
580     fn new(
581         prim_uid: ItemUid,
582         prim_clip_box: PictureBox2D,
583     ) -> Self {
584         PrimitiveDependencyInfo {
585             prim_uid,
586             images: SmallVec::new(),
587             opacity_bindings: SmallVec::new(),
588             color_binding: None,
589             prim_clip_box,
590             clips: SmallVec::new(),
591             spatial_nodes: SmallVec::new(),
592         }
593     }
596 /// A stable ID for a given tile, to help debugging. These are also used
597 /// as unique identifiers for tile surfaces when using a native compositor.
598 #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
599 #[cfg_attr(feature = "capture", derive(Serialize))]
600 #[cfg_attr(feature = "replay", derive(Deserialize))]
601 pub struct TileId(pub usize);
603 /// A descriptor for the kind of texture that a picture cache tile will
604 /// be drawn into.
605 #[derive(Debug)]
606 pub enum SurfaceTextureDescriptor {
607     /// When using the WR compositor, the tile is drawn into an entry
608     /// in the WR texture cache.
609     TextureCache {
610         handle: TextureCacheHandle
611     },
612     /// When using an OS compositor, the tile is drawn into a native
613     /// surface identified by arbitrary id.
614     Native {
615         /// The arbitrary id of this tile.
616         id: Option<NativeTileId>,
617     },
620 /// This is the same as a `SurfaceTextureDescriptor` but has been resolved
621 /// into a texture cache handle (if appropriate) that can be used by the
622 /// batching and compositing code in the renderer.
623 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
624 #[cfg_attr(feature = "capture", derive(Serialize))]
625 #[cfg_attr(feature = "replay", derive(Deserialize))]
626 pub enum ResolvedSurfaceTexture {
627     TextureCache {
628         /// The texture ID to draw to.
629         texture: TextureSource,
630     },
631     Native {
632         /// The arbitrary id of this tile.
633         id: NativeTileId,
634         /// The size of the tile in device pixels.
635         size: DeviceIntSize,
636     }
639 impl SurfaceTextureDescriptor {
640     /// Create a resolved surface texture for this descriptor
641     pub fn resolve(
642         &self,
643         resource_cache: &ResourceCache,
644         size: DeviceIntSize,
645     ) -> ResolvedSurfaceTexture {
646         match self {
647             SurfaceTextureDescriptor::TextureCache { handle } => {
648                 let cache_item = resource_cache.texture_cache.get(handle);
650                 ResolvedSurfaceTexture::TextureCache {
651                     texture: cache_item.texture_id,
652                 }
653             }
654             SurfaceTextureDescriptor::Native { id } => {
655                 ResolvedSurfaceTexture::Native {
656                     id: id.expect("bug: native surface not allocated"),
657                     size,
658                 }
659             }
660         }
661     }
664 /// The backing surface for this tile.
665 #[derive(Debug)]
666 pub enum TileSurface {
667     Texture {
668         /// Descriptor for the surface that this tile draws into.
669         descriptor: SurfaceTextureDescriptor,
670     },
671     Color {
672         color: ColorF,
673     },
674     Clear,
677 impl TileSurface {
678     fn kind(&self) -> &'static str {
679         match *self {
680             TileSurface::Color { .. } => "Color",
681             TileSurface::Texture { .. } => "Texture",
682             TileSurface::Clear => "Clear",
683         }
684     }
687 /// Optional extra information returned by is_same when
688 /// logging is enabled.
689 #[derive(Debug, Copy, Clone, PartialEq)]
690 #[cfg_attr(feature = "capture", derive(Serialize))]
691 #[cfg_attr(feature = "replay", derive(Deserialize))]
692 pub enum CompareHelperResult<T> {
693     /// Primitives match
694     Equal,
695     /// Counts differ
696     Count {
697         prev_count: u8,
698         curr_count: u8,
699     },
700     /// Sentinel
701     Sentinel,
702     /// Two items are not equal
703     NotEqual {
704         prev: T,
705         curr: T,
706     },
707     /// User callback returned true on item
708     PredicateTrue {
709         curr: T
710     },
713 /// The result of a primitive dependency comparison. Size is a u8
714 /// since this is a hot path in the code, and keeping the data small
715 /// is a performance win.
716 #[derive(Debug, Copy, Clone, PartialEq)]
717 #[cfg_attr(feature = "capture", derive(Serialize))]
718 #[cfg_attr(feature = "replay", derive(Deserialize))]
719 #[repr(u8)]
720 pub enum PrimitiveCompareResult {
721     /// Primitives match
722     Equal,
723     /// Something in the PrimitiveDescriptor was different
724     Descriptor,
725     /// The clip node content or spatial node changed
726     Clip,
727     /// The value of the transform changed
728     Transform,
729     /// An image dependency was dirty
730     Image,
731     /// The value of an opacity binding changed
732     OpacityBinding,
733     /// The value of a color binding changed
734     ColorBinding,
737 /// A more detailed version of PrimitiveCompareResult used when
738 /// debug logging is enabled.
739 #[derive(Debug, Copy, Clone, PartialEq)]
740 #[cfg_attr(feature = "capture", derive(Serialize))]
741 #[cfg_attr(feature = "replay", derive(Deserialize))]
742 pub enum PrimitiveCompareResultDetail {
743     /// Primitives match
744     Equal,
745     /// Something in the PrimitiveDescriptor was different
746     Descriptor {
747         old: PrimitiveDescriptor,
748         new: PrimitiveDescriptor,
749     },
750     /// The clip node content or spatial node changed
751     Clip {
752         detail: CompareHelperResult<ItemUid>,
753     },
754     /// The value of the transform changed
755     Transform {
756         detail: CompareHelperResult<SpatialNodeKey>,
757     },
758     /// An image dependency was dirty
759     Image {
760         detail: CompareHelperResult<ImageDependency>,
761     },
762     /// The value of an opacity binding changed
763     OpacityBinding {
764         detail: CompareHelperResult<OpacityBinding>,
765     },
766     /// The value of a color binding changed
767     ColorBinding {
768         detail: CompareHelperResult<ColorBinding>,
769     },
772 /// Debugging information about why a tile was invalidated
773 #[derive(Debug,Clone)]
774 #[cfg_attr(feature = "capture", derive(Serialize))]
775 #[cfg_attr(feature = "replay", derive(Deserialize))]
776 pub enum InvalidationReason {
777     /// The fractional offset changed
778     FractionalOffset {
779         old: DeviceVector2D,
780         new: DeviceVector2D,
781     },
782     /// The background color changed
783     BackgroundColor {
784         old: Option<ColorF>,
785         new: Option<ColorF>,
786     },
787     /// The opaque state of the backing native surface changed
788     SurfaceOpacityChanged{
789         became_opaque: bool
790     },
791     /// There was no backing texture (evicted or never rendered)
792     NoTexture,
793     /// There was no backing native surface (never rendered, or recreated)
794     NoSurface,
795     /// The primitive count in the dependency list was different
796     PrimCount {
797         old: Option<Vec<ItemUid>>,
798         new: Option<Vec<ItemUid>>,
799     },
800     /// The content of one of the primitives was different
801     Content {
802         /// What changed in the primitive that was different
803         prim_compare_result: PrimitiveCompareResult,
804         prim_compare_result_detail: Option<PrimitiveCompareResultDetail>,
805     },
806     // The compositor type changed
807     CompositorKindChanged,
808     // The valid region of the tile changed
809     ValidRectChanged,
810     // The overall scale of the picture cache changed
811     ScaleChanged,
814 /// A minimal subset of Tile for debug capturing
815 #[cfg_attr(feature = "capture", derive(Serialize))]
816 #[cfg_attr(feature = "replay", derive(Deserialize))]
817 pub struct TileSerializer {
818     pub rect: PictureRect,
819     pub current_descriptor: TileDescriptor,
820     pub device_fract_offset: DeviceVector2D,
821     pub id: TileId,
822     pub root: TileNode,
823     pub background_color: Option<ColorF>,
824     pub invalidation_reason: Option<InvalidationReason>
827 /// A minimal subset of TileCacheInstance for debug capturing
828 #[cfg_attr(feature = "capture", derive(Serialize))]
829 #[cfg_attr(feature = "replay", derive(Deserialize))]
830 pub struct TileCacheInstanceSerializer {
831     pub slice: usize,
832     pub tiles: FastHashMap<TileOffset, TileSerializer>,
833     pub background_color: Option<ColorF>,
834     pub fract_offset: PictureVector2D,
837 /// Information about a cached tile.
838 pub struct Tile {
839     /// The grid position of this tile within the picture cache
840     pub tile_offset: TileOffset,
841     /// The current world rect of this tile.
842     pub world_tile_rect: WorldRect,
843     /// The current local rect of this tile.
844     pub local_tile_rect: PictureRect,
845     /// Same as local_tile_rect, but in min/max form as an optimization
846     pub local_tile_box: PictureBox2D,
847     /// The picture space dirty rect for this tile.
848     local_dirty_rect: PictureRect,
849     /// The device space dirty rect for this tile.
850     /// TODO(gw): We have multiple dirty rects available due to the quadtree above. In future,
851     ///           expose these as multiple dirty rects, which will help in some cases.
852     pub device_dirty_rect: DeviceRect,
853     /// Device space rect that contains valid pixels region of this tile.
854     pub device_valid_rect: DeviceRect,
855     /// Uniquely describes the content of this tile, in a way that can be
856     /// (reasonably) efficiently hashed and compared.
857     pub current_descriptor: TileDescriptor,
858     /// The content descriptor for this tile from the previous frame.
859     pub prev_descriptor: TileDescriptor,
860     /// Handle to the backing surface for this tile.
861     pub surface: Option<TileSurface>,
862     /// If true, this tile is marked valid, and the existing texture
863     /// cache handle can be used. Tiles are invalidated during the
864     /// build_dirty_regions method.
865     pub is_valid: bool,
866     /// If true, this tile intersects with the currently visible screen
867     /// rect, and will be drawn.
868     pub is_visible: bool,
869     /// The current fractional offset of the cache transform root. If this changes,
870     /// all tiles need to be invalidated and redrawn, since snapping differences are
871     /// likely to occur.
872     device_fract_offset: DeviceVector2D,
873     /// The tile id is stable between display lists and / or frames,
874     /// if the tile is retained. Useful for debugging tile evictions.
875     pub id: TileId,
876     /// If true, the tile was determined to be opaque, which means blending
877     /// can be disabled when drawing it.
878     pub is_opaque: bool,
879     /// Root node of the quadtree dirty rect tracker.
880     root: TileNode,
881     /// The last rendered background color on this tile.
882     background_color: Option<ColorF>,
883     /// The first reason the tile was invalidated this frame.
884     invalidation_reason: Option<InvalidationReason>,
885     /// The local space valid rect for all primitives that affect this tile.
886     local_valid_rect: PictureBox2D,
887     /// z-buffer id for this tile
888     pub z_id: ZBufferId,
889     /// The last frame this tile had its dependencies updated (dependency updating is
890     /// skipped if a tile is off-screen).
891     pub last_updated_frame_id: FrameId,
894 impl Tile {
895     /// Construct a new, invalid tile.
896     fn new(tile_offset: TileOffset) -> Self {
897         let id = TileId(NEXT_TILE_ID.fetch_add(1, Ordering::Relaxed));
899         Tile {
900             tile_offset,
901             local_tile_rect: PictureRect::zero(),
902             local_tile_box: PictureBox2D::zero(),
903             world_tile_rect: WorldRect::zero(),
904             device_valid_rect: DeviceRect::zero(),
905             local_dirty_rect: PictureRect::zero(),
906             device_dirty_rect: DeviceRect::zero(),
907             surface: None,
908             current_descriptor: TileDescriptor::new(),
909             prev_descriptor: TileDescriptor::new(),
910             is_valid: false,
911             is_visible: false,
912             device_fract_offset: DeviceVector2D::zero(),
913             id,
914             is_opaque: false,
915             root: TileNode::new_leaf(Vec::new()),
916             background_color: None,
917             invalidation_reason: None,
918             local_valid_rect: PictureBox2D::zero(),
919             z_id: ZBufferId::invalid(),
920             last_updated_frame_id: FrameId::INVALID,
921         }
922     }
924     /// Print debug information about this tile to a tree printer.
925     fn print(&self, pt: &mut dyn PrintTreePrinter) {
926         pt.new_level(format!("Tile {:?}", self.id));
927         pt.add_item(format!("local_tile_rect: {:?}", self.local_tile_rect));
928         pt.add_item(format!("device_fract_offset: {:?}", self.device_fract_offset));
929         pt.add_item(format!("background_color: {:?}", self.background_color));
930         pt.add_item(format!("invalidation_reason: {:?}", self.invalidation_reason));
931         self.current_descriptor.print(pt);
932         pt.end_level();
933     }
935     /// Check if the content of the previous and current tile descriptors match
936     fn update_dirty_rects(
937         &mut self,
938         ctx: &TilePostUpdateContext,
939         state: &mut TilePostUpdateState,
940         invalidation_reason: &mut Option<InvalidationReason>,
941         frame_context: &FrameVisibilityContext,
942     ) -> PictureRect {
943         let mut prim_comparer = PrimitiveComparer::new(
944             &self.prev_descriptor,
945             &self.current_descriptor,
946             state.resource_cache,
947             state.spatial_node_comparer,
948             ctx.opacity_bindings,
949             ctx.color_bindings,
950         );
952         let mut dirty_rect = PictureBox2D::zero();
953         self.root.update_dirty_rects(
954             &self.prev_descriptor.prims,
955             &self.current_descriptor.prims,
956             &mut prim_comparer,
957             &mut dirty_rect,
958             state.compare_cache,
959             invalidation_reason,
960             frame_context,
961         );
963         dirty_rect.to_rect()
964     }
966     /// Invalidate a tile based on change in content. This
967     /// must be called even if the tile is not currently
968     /// visible on screen. We might be able to improve this
969     /// later by changing how ComparableVec is used.
970     fn update_content_validity(
971         &mut self,
972         ctx: &TilePostUpdateContext,
973         state: &mut TilePostUpdateState,
974         frame_context: &FrameVisibilityContext,
975     ) {
976         // Check if the contents of the primitives, clips, and
977         // other dependencies are the same.
978         state.compare_cache.clear();
979         let mut invalidation_reason = None;
980         let dirty_rect = self.update_dirty_rects(
981             ctx,
982             state,
983             &mut invalidation_reason,
984             frame_context,
985         );
986         if !dirty_rect.is_empty() {
987             self.invalidate(
988                 Some(dirty_rect),
989                 invalidation_reason.expect("bug: no invalidation_reason"),
990             );
991         }
992         if ctx.invalidate_all {
993             self.invalidate(None, InvalidationReason::ScaleChanged);
994         }
995         // TODO(gw): We can avoid invalidating the whole tile in some cases here,
996         //           but it should be a fairly rare invalidation case.
997         if self.current_descriptor.local_valid_rect != self.prev_descriptor.local_valid_rect {
998             self.invalidate(None, InvalidationReason::ValidRectChanged);
999             state.composite_state.dirty_rects_are_valid = false;
1000         }
1001     }
1003     /// Invalidate this tile. If `invalidation_rect` is None, the entire
1004     /// tile is invalidated.
1005     fn invalidate(
1006         &mut self,
1007         invalidation_rect: Option<PictureRect>,
1008         reason: InvalidationReason,
1009     ) {
1010         self.is_valid = false;
1012         match invalidation_rect {
1013             Some(rect) => {
1014                 self.local_dirty_rect = self.local_dirty_rect.union(&rect);
1015             }
1016             None => {
1017                 self.local_dirty_rect = self.local_tile_rect;
1018             }
1019         }
1021         if self.invalidation_reason.is_none() {
1022             self.invalidation_reason = Some(reason);
1023         }
1024     }
1026     /// Called during pre_update of a tile cache instance. Allows the
1027     /// tile to setup state before primitive dependency calculations.
1028     fn pre_update(
1029         &mut self,
1030         ctx: &TilePreUpdateContext,
1031     ) {
1032         // Ensure each tile is offset by the appropriate amount from the
1033         // origin, such that the content origin will be a whole number and
1034         // the snapping will be consistent.
1035         self.local_tile_rect = PictureRect::new(
1036             PicturePoint::new(
1037                 self.tile_offset.x as f32 * ctx.tile_size.width + ctx.fract_offset.x,
1038                 self.tile_offset.y as f32 * ctx.tile_size.height + ctx.fract_offset.y,
1039             ),
1040             ctx.tile_size,
1041         );
1042         self.local_tile_box = PictureBox2D::new(
1043             self.local_tile_rect.origin,
1044             self.local_tile_rect.bottom_right(),
1045         );
1046         self.local_valid_rect = PictureBox2D::zero();
1047         self.invalidation_reason  = None;
1049         self.world_tile_rect = ctx.pic_to_world_mapper
1050             .map(&self.local_tile_rect)
1051             .expect("bug: map local tile rect");
1053         // Check if this tile is currently on screen.
1054         self.is_visible = self.world_tile_rect.intersects(&ctx.global_screen_world_rect);
1056         // If the tile isn't visible, early exit, skipping the normal set up to
1057         // validate dependencies. Instead, we will only compare the current tile
1058         // dependencies the next time it comes into view.
1059         if !self.is_visible {
1060             return;
1061         }
1063         // We may need to rerender if glyph subpixel positions have changed. Note
1064         // that we update the tile fract offset itself after we have completed
1065         // invalidation. This allows for other whole tile invalidation cases to
1066         // update the fract offset appropriately.
1067         let fract_delta = self.device_fract_offset - ctx.device_fract_offset;
1068         let fract_changed = fract_delta.x.abs() > 0.01 || fract_delta.y.abs() > 0.01;
1069         if fract_changed {
1070             self.invalidate(None, InvalidationReason::FractionalOffset {
1071                                     old: self.device_fract_offset,
1072                                     new: ctx.device_fract_offset });
1073         }
1075         if ctx.background_color != self.background_color {
1076             self.invalidate(None, InvalidationReason::BackgroundColor {
1077                                     old: self.background_color,
1078                                     new: ctx.background_color });
1079             self.background_color = ctx.background_color;
1080         }
1082         // Clear any dependencies so that when we rebuild them we
1083         // can compare if the tile has the same content.
1084         mem::swap(
1085             &mut self.current_descriptor,
1086             &mut self.prev_descriptor,
1087         );
1088         self.current_descriptor.clear();
1089         self.root.clear(self.local_tile_rect.to_box2d());
1091         // Since this tile is determined to be visible, it will get updated
1092         // dependencies, so update the frame id we are storing dependencies for.
1093         self.last_updated_frame_id = ctx.frame_id;
1094     }
1096     /// Add dependencies for a given primitive to this tile.
1097     fn add_prim_dependency(
1098         &mut self,
1099         info: &PrimitiveDependencyInfo,
1100     ) {
1101         // If this tile isn't currently visible, we don't want to update the dependencies
1102         // for this tile, as an optimization, since it won't be drawn anyway.
1103         if !self.is_visible {
1104             return;
1105         }
1107         // Incorporate the bounding rect of the primitive in the local valid rect
1108         // for this tile. This is used to minimize the size of the scissor rect
1109         // during rasterization and the draw rect during composition of partial tiles.
1110         self.local_valid_rect = self.local_valid_rect.union(&info.prim_clip_box);
1112         // Include any image keys this tile depends on.
1113         self.current_descriptor.images.extend_from_slice(&info.images);
1115         // Include any opacity bindings this primitive depends on.
1116         self.current_descriptor.opacity_bindings.extend_from_slice(&info.opacity_bindings);
1118         // Include any clip nodes that this primitive depends on.
1119         self.current_descriptor.clips.extend_from_slice(&info.clips);
1121         // Include any transforms that this primitive depends on.
1122         for spatial_node_index in &info.spatial_nodes {
1123             self.current_descriptor.transforms.push(
1124                 SpatialNodeKey {
1125                     spatial_node_index: *spatial_node_index,
1126                     frame_id: self.last_updated_frame_id,
1127                 }
1128             );
1129         }
1131         // Include any color bindings this primitive depends on.
1132         if info.color_binding.is_some() {
1133             self.current_descriptor.color_bindings.insert(
1134                 self.current_descriptor.color_bindings.len(), info.color_binding.unwrap());
1135         }
1137         // TODO(gw): The prim_clip_rect can be impacted by the clip rect of the display port,
1138         //           which can cause invalidations when a new display list with changed
1139         //           display port is received. To work around this, clamp the prim clip rect
1140         //           to the tile boundaries - if the clip hasn't affected the tile, then the
1141         //           changed clip can't affect the content of the primitive on this tile.
1142         //           In future, we could consider supplying the display port clip from Gecko
1143         //           in a different way (e.g. as a scroll frame clip) which still provides
1144         //           the desired clip for checkerboarding, but doesn't require this extra
1145         //           work below.
1147         // TODO(gw): This is a hot part of the code - we could probably optimize further by:
1148         //           - Using min/max instead of clamps below (if we guarantee the rects are well formed)
1150         let tile_p0 = self.local_tile_box.min;
1151         let tile_p1 = self.local_tile_box.max;
1153         let prim_clip_box = PictureBox2D::new(
1154             PicturePoint::new(
1155                 clampf(info.prim_clip_box.min.x, tile_p0.x, tile_p1.x),
1156                 clampf(info.prim_clip_box.min.y, tile_p0.y, tile_p1.y),
1157             ),
1158             PicturePoint::new(
1159                 clampf(info.prim_clip_box.max.x, tile_p0.x, tile_p1.x),
1160                 clampf(info.prim_clip_box.max.y, tile_p0.y, tile_p1.y),
1161             ),
1162         );
1164         // Update the tile descriptor, used for tile comparison during scene swaps.
1165         let prim_index = PrimitiveDependencyIndex(self.current_descriptor.prims.len() as u32);
1167         // We know that the casts below will never overflow because the array lengths are
1168         // truncated to MAX_PRIM_SUB_DEPS during update_prim_dependencies.
1169         debug_assert!(info.spatial_nodes.len() <= MAX_PRIM_SUB_DEPS);
1170         debug_assert!(info.clips.len() <= MAX_PRIM_SUB_DEPS);
1171         debug_assert!(info.images.len() <= MAX_PRIM_SUB_DEPS);
1172         debug_assert!(info.opacity_bindings.len() <= MAX_PRIM_SUB_DEPS);
1174         self.current_descriptor.prims.push(PrimitiveDescriptor {
1175             prim_uid: info.prim_uid,
1176             prim_clip_box,
1177             transform_dep_count: info.spatial_nodes.len()  as u8,
1178             clip_dep_count: info.clips.len() as u8,
1179             image_dep_count: info.images.len() as u8,
1180             opacity_binding_dep_count: info.opacity_bindings.len() as u8,
1181             color_binding_dep_count: if info.color_binding.is_some() { 1 } else { 0 } as u8,
1182         });
1184         // Add this primitive to the dirty rect quadtree.
1185         self.root.add_prim(prim_index, &info.prim_clip_box);
1186     }
1188     /// Called during tile cache instance post_update. Allows invalidation and dirty
1189     /// rect calculation after primitive dependencies have been updated.
1190     fn post_update(
1191         &mut self,
1192         ctx: &TilePostUpdateContext,
1193         state: &mut TilePostUpdateState,
1194         frame_context: &FrameVisibilityContext,
1195     ) -> bool {
1196         // Register the frame id of this tile with the spatial node comparer, to ensure
1197         // that it doesn't GC any spatial nodes from the comparer that are referenced
1198         // by this tile. Must be done before we early exit below, so that we retain
1199         // spatial node info even for tiles that are currently not visible.
1200         state.spatial_node_comparer.retain_for_frame(self.last_updated_frame_id);
1202         // If tile is not visible, just early out from here - we don't update dependencies
1203         // so don't want to invalidate, merge, split etc. The tile won't need to be drawn
1204         // (and thus updated / invalidated) until it is on screen again.
1205         if !self.is_visible {
1206             return false;
1207         }
1209         // Calculate the overall valid rect for this tile.
1210         self.current_descriptor.local_valid_rect = self.local_valid_rect.to_rect();
1212         // TODO(gw): In theory, the local tile rect should always have an
1213         //           intersection with the overall picture rect. In practice,
1214         //           due to some accuracy issues with how fract_offset (and
1215         //           fp accuracy) are used in the calling method, this isn't
1216         //           always true. In this case, it's safe to set the local
1217         //           valid rect to zero, which means it will be clipped out
1218         //           and not affect the scene. In future, we should fix the
1219         //           accuracy issue above, so that this assumption holds, but
1220         //           it shouldn't have any noticeable effect on performance
1221         //           or memory usage (textures should never get allocated).
1222         self.current_descriptor.local_valid_rect = self.local_tile_rect
1223             .intersection(&ctx.local_rect)
1224             .and_then(|r| r.intersection(&self.current_descriptor.local_valid_rect))
1225             .unwrap_or_else(PictureRect::zero);
1227         // The device_valid_rect is referenced during `update_content_validity` so it
1228         // must be updated here first.
1229         let world_valid_rect = ctx.pic_to_world_mapper
1230             .map(&self.current_descriptor.local_valid_rect)
1231             .expect("bug: map local valid rect");
1233         // The device rect is guaranteed to be aligned on a device pixel - the round
1234         // is just to deal with float accuracy. However, the valid rect is not
1235         // always aligned to a device pixel. To handle this, round out to get all
1236         // required pixels, and intersect with the tile device rect.
1237         let device_rect = (self.world_tile_rect * ctx.global_device_pixel_scale).round();
1238         self.device_valid_rect = (world_valid_rect * ctx.global_device_pixel_scale)
1239             .round_out()
1240             .intersection(&device_rect)
1241             .unwrap_or_else(DeviceRect::zero);
1243         // Invalidate the tile based on the content changing.
1244         self.update_content_validity(ctx, state, frame_context);
1246         // If there are no primitives there is no need to draw or cache it.
1247         if self.current_descriptor.prims.is_empty() {
1248             // If there is a native compositor surface allocated for this (now empty) tile
1249             // it must be freed here, otherwise the stale tile with previous contents will
1250             // be composited. If the tile subsequently gets new primitives added to it, the
1251             // surface will be re-allocated when it's added to the composite draw list.
1252             if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { mut id, .. }, .. }) = self.surface.take() {
1253                 if let Some(id) = id.take() {
1254                     state.resource_cache.destroy_compositor_tile(id);
1255                 }
1256             }
1258             self.is_visible = false;
1259             return false;
1260         }
1262         // Check if this tile can be considered opaque. Opacity state must be updated only
1263         // after all early out checks have been performed. Otherwise, we might miss updating
1264         // the native surface next time this tile becomes visible.
1265         let clipped_rect = self.current_descriptor.local_valid_rect
1266             .intersection(&ctx.local_clip_rect)
1267             .unwrap_or_else(PictureRect::zero);
1269         let has_opaque_bg_color = self.background_color.map_or(false, |c| c.a >= 1.0);
1270         let has_opaque_backdrop = ctx.backdrop.map_or(false, |b| b.opaque_rect.contains_rect(&clipped_rect));
1271         let is_opaque = has_opaque_bg_color || has_opaque_backdrop;
1273         // Set the correct z_id for this tile
1274         self.z_id = ctx.z_id;
1276         if is_opaque != self.is_opaque {
1277             // If opacity changed, the native compositor surface and all tiles get invalidated.
1278             // (this does nothing if not using native compositor mode).
1279             // TODO(gw): This property probably changes very rarely, so it is OK to invalidate
1280             //           everything in this case. If it turns out that this isn't true, we could
1281             //           consider other options, such as per-tile opacity (natively supported
1282             //           on CoreAnimation, and supported if backed by non-virtual surfaces in
1283             //           DirectComposition).
1284             if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = self.surface {
1285                 if let Some(id) = id.take() {
1286                     state.resource_cache.destroy_compositor_tile(id);
1287                 }
1288             }
1290             // Invalidate the entire tile to force a redraw.
1291             self.invalidate(None, InvalidationReason::SurfaceOpacityChanged { became_opaque: is_opaque });
1292             self.is_opaque = is_opaque;
1293         }
1295         // Check if the selected composite mode supports dirty rect updates. For Draw composite
1296         // mode, we can always update the content with smaller dirty rects, unless there is a
1297         // driver bug to workaround. For native composite mode, we can only use dirty rects if
1298         // the compositor supports partial surface updates.
1299         let (supports_dirty_rects, supports_simple_prims) = match state.composite_state.compositor_kind {
1300             CompositorKind::Draw { .. } => {
1301                 (frame_context.config.gpu_supports_render_target_partial_update, true)
1302             }
1303             CompositorKind::Native { max_update_rects, .. } => {
1304                 (max_update_rects > 0, false)
1305             }
1306         };
1308         // TODO(gw): Consider using smaller tiles and/or tile splits for
1309         //           native compositors that don't support dirty rects.
1310         if supports_dirty_rects {
1311             // Only allow splitting for normal content sized tiles
1312             if ctx.current_tile_size == state.resource_cache.texture_cache.default_picture_tile_size() {
1313                 let max_split_level = 3;
1315                 // Consider splitting / merging dirty regions
1316                 self.root.maybe_merge_or_split(
1317                     0,
1318                     &self.current_descriptor.prims,
1319                     max_split_level,
1320                 );
1321             }
1322         }
1324         // The dirty rect will be set correctly by now. If the underlying platform
1325         // doesn't support partial updates, and this tile isn't valid, force the dirty
1326         // rect to be the size of the entire tile.
1327         if !self.is_valid && !supports_dirty_rects {
1328             self.local_dirty_rect = self.local_tile_rect;
1329         }
1331         // See if this tile is a simple color, in which case we can just draw
1332         // it as a rect, and avoid allocating a texture surface and drawing it.
1333         // TODO(gw): Initial native compositor interface doesn't support simple
1334         //           color tiles. We can definitely support this in DC, so this
1335         //           should be added as a follow up.
1336         let is_simple_prim =
1337             ctx.backdrop.map_or(false, |b| b.kind.is_some()) &&
1338             self.current_descriptor.prims.len() == 1 &&
1339             self.is_opaque &&
1340             supports_simple_prims;
1342         // Set up the backing surface for this tile.
1343         let surface = if is_simple_prim {
1344             // If we determine the tile can be represented by a color, set the
1345             // surface unconditionally (this will drop any previously used
1346             // texture cache backing surface).
1347             match ctx.backdrop.unwrap().kind {
1348                 Some(BackdropKind::Color { color }) => {
1349                     TileSurface::Color {
1350                         color,
1351                     }
1352                 }
1353                 Some(BackdropKind::Clear) => {
1354                     TileSurface::Clear
1355                 }
1356                 None => {
1357                     // This should be prevented by the is_simple_prim check above.
1358                     unreachable!();
1359                 }
1360             }
1361         } else {
1362             // If this tile will be backed by a surface, we want to retain
1363             // the texture handle from the previous frame, if possible. If
1364             // the tile was previously a color, or not set, then just set
1365             // up a new texture cache handle.
1366             match self.surface.take() {
1367                 Some(TileSurface::Texture { descriptor }) => {
1368                     // Reuse the existing descriptor and vis mask
1369                     TileSurface::Texture {
1370                         descriptor,
1371                     }
1372                 }
1373                 Some(TileSurface::Color { .. }) | Some(TileSurface::Clear) | None => {
1374                     // This is the case where we are constructing a tile surface that
1375                     // involves drawing to a texture. Create the correct surface
1376                     // descriptor depending on the compositing mode that will read
1377                     // the output.
1378                     let descriptor = match state.composite_state.compositor_kind {
1379                         CompositorKind::Draw { .. } => {
1380                             // For a texture cache entry, create an invalid handle that
1381                             // will be allocated when update_picture_cache is called.
1382                             SurfaceTextureDescriptor::TextureCache {
1383                                 handle: TextureCacheHandle::invalid(),
1384                             }
1385                         }
1386                         CompositorKind::Native { .. } => {
1387                             // Create a native surface surface descriptor, but don't allocate
1388                             // a surface yet. The surface is allocated *after* occlusion
1389                             // culling occurs, so that only visible tiles allocate GPU memory.
1390                             SurfaceTextureDescriptor::Native {
1391                                 id: None,
1392                             }
1393                         }
1394                     };
1396                     TileSurface::Texture {
1397                         descriptor,
1398                     }
1399                 }
1400             }
1401         };
1403         // Store the current surface backing info for use during batching.
1404         self.surface = Some(surface);
1406         true
1407     }
1410 /// Defines a key that uniquely identifies a primitive instance.
1411 #[derive(Debug, Copy, Clone)]
1412 #[cfg_attr(feature = "capture", derive(Serialize))]
1413 #[cfg_attr(feature = "replay", derive(Deserialize))]
1414 pub struct PrimitiveDescriptor {
1415     /// Uniquely identifies the content of the primitive template.
1416     pub prim_uid: ItemUid,
1417     /// The clip rect for this primitive. Included here in
1418     /// dependencies since there is no entry in the clip chain
1419     /// dependencies for the local clip rect.
1420     pub prim_clip_box: PictureBox2D,
1421     /// The number of extra dependencies that this primitive has.
1422     transform_dep_count: u8,
1423     image_dep_count: u8,
1424     opacity_binding_dep_count: u8,
1425     clip_dep_count: u8,
1426     color_binding_dep_count: u8,
1429 impl PartialEq for PrimitiveDescriptor {
1430     fn eq(&self, other: &Self) -> bool {
1431         const EPSILON: f32 = 0.001;
1433         if self.prim_uid != other.prim_uid {
1434             return false;
1435         }
1437         if !self.prim_clip_box.min.x.approx_eq_eps(&other.prim_clip_box.min.x, &EPSILON) {
1438             return false;
1439         }
1440         if !self.prim_clip_box.min.y.approx_eq_eps(&other.prim_clip_box.min.y, &EPSILON) {
1441             return false;
1442         }
1443         if !self.prim_clip_box.max.x.approx_eq_eps(&other.prim_clip_box.max.x, &EPSILON) {
1444             return false;
1445         }
1446         if !self.prim_clip_box.max.y.approx_eq_eps(&other.prim_clip_box.max.y, &EPSILON) {
1447             return false;
1448         }
1450         true
1451     }
1454 /// A small helper to compare two arrays of primitive dependencies.
1455 struct CompareHelper<'a, T> where T: Copy {
1456     offset_curr: usize,
1457     offset_prev: usize,
1458     curr_items: &'a [T],
1459     prev_items: &'a [T],
1462 impl<'a, T> CompareHelper<'a, T> where T: Copy + PartialEq {
1463     /// Construct a new compare helper for a current / previous set of dependency information.
1464     fn new(
1465         prev_items: &'a [T],
1466         curr_items: &'a [T],
1467     ) -> Self {
1468         CompareHelper {
1469             offset_curr: 0,
1470             offset_prev: 0,
1471             curr_items,
1472             prev_items,
1473         }
1474     }
1476     /// Reset the current position in the dependency array to the start
1477     fn reset(&mut self) {
1478         self.offset_prev = 0;
1479         self.offset_curr = 0;
1480     }
1482     /// Test if two sections of the dependency arrays are the same, by checking both
1483     /// item equality, and a user closure to see if the content of the item changed.
1484     fn is_same<F>(
1485         &self,
1486         prev_count: u8,
1487         curr_count: u8,
1488         mut f: F,
1489         opt_detail: Option<&mut CompareHelperResult<T>>,
1490     ) -> bool where F: FnMut(&T, &T) -> bool {
1491         // If the number of items is different, trivial reject.
1492         if prev_count != curr_count {
1493             if let Some(detail) = opt_detail { *detail = CompareHelperResult::Count{ prev_count, curr_count }; }
1494             return false;
1495         }
1496         // If both counts are 0, then no need to check these dependencies.
1497         if curr_count == 0 {
1498             if let Some(detail) = opt_detail { *detail = CompareHelperResult::Equal; }
1499             return true;
1500         }
1501         // If both counts are u8::MAX, this is a sentinel that we can't compare these
1502         // deps, so just trivial reject.
1503         if curr_count as usize == MAX_PRIM_SUB_DEPS {
1504             if let Some(detail) = opt_detail { *detail = CompareHelperResult::Sentinel; }
1505             return false;
1506         }
1508         let end_prev = self.offset_prev + prev_count as usize;
1509         let end_curr = self.offset_curr + curr_count as usize;
1511         let curr_items = &self.curr_items[self.offset_curr .. end_curr];
1512         let prev_items = &self.prev_items[self.offset_prev .. end_prev];
1514         for (curr, prev) in curr_items.iter().zip(prev_items.iter()) {
1515             if !f(prev, curr) {
1516                 if let Some(detail) = opt_detail { *detail = CompareHelperResult::PredicateTrue{ curr: *curr }; }
1517                 return false;
1518             }
1519         }
1521         if let Some(detail) = opt_detail { *detail = CompareHelperResult::Equal; }
1522         true
1523     }
1525     // Advance the prev dependency array by a given amount
1526     fn advance_prev(&mut self, count: u8) {
1527         self.offset_prev += count as usize;
1528     }
1530     // Advance the current dependency array by a given amount
1531     fn advance_curr(&mut self, count: u8) {
1532         self.offset_curr  += count as usize;
1533     }
1536 /// Uniquely describes the content of this tile, in a way that can be
1537 /// (reasonably) efficiently hashed and compared.
1538 #[cfg_attr(any(feature="capture",feature="replay"), derive(Clone))]
1539 #[cfg_attr(feature = "capture", derive(Serialize))]
1540 #[cfg_attr(feature = "replay", derive(Deserialize))]
1541 pub struct TileDescriptor {
1542     /// List of primitive instance unique identifiers. The uid is guaranteed
1543     /// to uniquely describe the content of the primitive template, while
1544     /// the other parameters describe the clip chain and instance params.
1545     pub prims: Vec<PrimitiveDescriptor>,
1547     /// List of clip node descriptors.
1548     clips: Vec<ItemUid>,
1550     /// List of image keys that this tile depends on.
1551     images: Vec<ImageDependency>,
1553     /// The set of opacity bindings that this tile depends on.
1554     // TODO(gw): Ugh, get rid of all opacity binding support!
1555     opacity_bindings: Vec<OpacityBinding>,
1557     /// List of the effects of transforms that we care about
1558     /// tracking for this tile.
1559     transforms: Vec<SpatialNodeKey>,
1561     /// Picture space rect that contains valid pixels region of this tile.
1562     local_valid_rect: PictureRect,
1564     /// List of the effects of color that we care about
1565     /// tracking for this tile.
1566     color_bindings: Vec<ColorBinding>,
1569 impl TileDescriptor {
1570     fn new() -> Self {
1571         TileDescriptor {
1572             prims: Vec::new(),
1573             clips: Vec::new(),
1574             opacity_bindings: Vec::new(),
1575             images: Vec::new(),
1576             transforms: Vec::new(),
1577             local_valid_rect: PictureRect::zero(),
1578             color_bindings: Vec::new(),
1579         }
1580     }
1582     /// Print debug information about this tile descriptor to a tree printer.
1583     fn print(&self, pt: &mut dyn PrintTreePrinter) {
1584         pt.new_level("current_descriptor".to_string());
1586         pt.new_level("prims".to_string());
1587         for prim in &self.prims {
1588             pt.new_level(format!("prim uid={}", prim.prim_uid.get_uid()));
1589             pt.add_item(format!("clip: p0={},{} p1={},{}",
1590                 prim.prim_clip_box.min.x,
1591                 prim.prim_clip_box.min.y,
1592                 prim.prim_clip_box.max.x,
1593                 prim.prim_clip_box.max.y,
1594             ));
1595             pt.add_item(format!("deps: t={} i={} o={} c={} color={}",
1596                 prim.transform_dep_count,
1597                 prim.image_dep_count,
1598                 prim.opacity_binding_dep_count,
1599                 prim.clip_dep_count,
1600                 prim.color_binding_dep_count,
1601             ));
1602             pt.end_level();
1603         }
1604         pt.end_level();
1606         if !self.clips.is_empty() {
1607             pt.new_level("clips".to_string());
1608             for clip in &self.clips {
1609                 pt.new_level(format!("clip uid={}", clip.get_uid()));
1610                 pt.end_level();
1611             }
1612             pt.end_level();
1613         }
1615         if !self.images.is_empty() {
1616             pt.new_level("images".to_string());
1617             for info in &self.images {
1618                 pt.new_level(format!("key={:?}", info.key));
1619                 pt.add_item(format!("generation={:?}", info.generation));
1620                 pt.end_level();
1621             }
1622             pt.end_level();
1623         }
1625         if !self.opacity_bindings.is_empty() {
1626             pt.new_level("opacity_bindings".to_string());
1627             for opacity_binding in &self.opacity_bindings {
1628                 pt.new_level(format!("binding={:?}", opacity_binding));
1629                 pt.end_level();
1630             }
1631             pt.end_level();
1632         }
1634         if !self.transforms.is_empty() {
1635             pt.new_level("transforms".to_string());
1636             for transform in &self.transforms {
1637                 pt.new_level(format!("spatial_node={:?}", transform));
1638                 pt.end_level();
1639             }
1640             pt.end_level();
1641         }
1643         if !self.color_bindings.is_empty() {
1644             pt.new_level("color_bindings".to_string());
1645             for color_binding in &self.color_bindings {
1646                 pt.new_level(format!("binding={:?}", color_binding));
1647                 pt.end_level();
1648             }
1649             pt.end_level();
1650         }
1652         pt.end_level();
1653     }
1655     /// Clear the dependency information for a tile, when the dependencies
1656     /// are being rebuilt.
1657     fn clear(&mut self) {
1658         self.prims.clear();
1659         self.clips.clear();
1660         self.opacity_bindings.clear();
1661         self.images.clear();
1662         self.transforms.clear();
1663         self.local_valid_rect = PictureRect::zero();
1664         self.color_bindings.clear();
1665     }
1668 /// Represents the dirty region of a tile cache picture.
1669 #[derive(Clone)]
1670 pub struct DirtyRegion {
1671     /// The individual filters that make up this region.
1672     pub filters: Vec<BatchFilter>,
1674     /// The overall dirty rect, a combination of dirty_rects
1675     pub combined: WorldRect,
1677     /// Spatial node of the picture cache this region represents
1678     spatial_node_index: SpatialNodeIndex,
1681 impl DirtyRegion {
1682     /// Construct a new dirty region tracker.
1683     pub fn new(
1684         spatial_node_index: SpatialNodeIndex,
1685     ) -> Self {
1686         DirtyRegion {
1687             filters: Vec::with_capacity(16),
1688             combined: WorldRect::zero(),
1689             spatial_node_index,
1690         }
1691     }
1693     /// Reset the dirty regions back to empty
1694     pub fn reset(
1695         &mut self,
1696         spatial_node_index: SpatialNodeIndex,
1697     ) {
1698         self.filters.clear();
1699         self.combined = WorldRect::zero();
1700         self.spatial_node_index = spatial_node_index;
1701     }
1703     /// Add a dirty region to the tracker. Returns the visibility mask that corresponds to
1704     /// this region in the tracker.
1705     pub fn add_dirty_region(
1706         &mut self,
1707         rect_in_pic_space: PictureRect,
1708         sub_slice_index: SubSliceIndex,
1709         spatial_tree: &SpatialTree,
1710     ) {
1711         let map_pic_to_world = SpaceMapper::new_with_target(
1712             ROOT_SPATIAL_NODE_INDEX,
1713             self.spatial_node_index,
1714             WorldRect::max_rect(),
1715             spatial_tree,
1716         );
1718         let world_rect = map_pic_to_world
1719             .map(&rect_in_pic_space)
1720             .expect("bug");
1722         // Include this in the overall dirty rect
1723         self.combined = self.combined.union(&world_rect);
1725         self.filters.push(BatchFilter {
1726             rect_in_pic_space,
1727             sub_slice_index,
1728         });
1729     }
1731     // TODO(gw): This returns a heap allocated object. Perhaps we can simplify this
1732     //           logic? Although - it's only used very rarely so it may not be an issue.
1733     pub fn inflate(
1734         &self,
1735         inflate_amount: f32,
1736         spatial_tree: &SpatialTree,
1737     ) -> DirtyRegion {
1738         let map_pic_to_world = SpaceMapper::new_with_target(
1739             ROOT_SPATIAL_NODE_INDEX,
1740             self.spatial_node_index,
1741             WorldRect::max_rect(),
1742             spatial_tree,
1743         );
1745         let mut filters = Vec::with_capacity(self.filters.len());
1746         let mut combined = WorldRect::zero();
1748         for filter in &self.filters {
1749             let rect_in_pic_space = filter.rect_in_pic_space.inflate(inflate_amount, inflate_amount);
1751             let world_rect = map_pic_to_world
1752                 .map(&rect_in_pic_space)
1753                 .expect("bug");
1755             combined = combined.union(&world_rect);
1756             filters.push(BatchFilter {
1757                 rect_in_pic_space,
1758                 sub_slice_index: filter.sub_slice_index,
1759             });
1760         }
1762         DirtyRegion {
1763             filters,
1764             combined,
1765             spatial_node_index: self.spatial_node_index,
1766         }
1767     }
1770 #[derive(Debug, Copy, Clone)]
1771 pub enum BackdropKind {
1772     Color {
1773         color: ColorF,
1774     },
1775     Clear,
1778 /// Stores information about the calculated opaque backdrop of this slice.
1779 #[derive(Debug, Copy, Clone)]
1780 pub struct BackdropInfo {
1781     /// The picture space rectangle that is known to be opaque. This is used
1782     /// to determine where subpixel AA can be used, and where alpha blending
1783     /// can be disabled.
1784     pub opaque_rect: PictureRect,
1785     /// Kind of the backdrop
1786     pub kind: Option<BackdropKind>,
1789 impl BackdropInfo {
1790     fn empty() -> Self {
1791         BackdropInfo {
1792             opaque_rect: PictureRect::zero(),
1793             kind: None,
1794         }
1795     }
1798 #[derive(Clone)]
1799 pub struct TileCacheLoggerSlice {
1800     pub serialized_slice: String,
1801     pub local_to_world_transform: Transform3D<f32, PicturePixel, WorldPixel>,
1804 #[cfg(any(feature = "capture", feature = "replay"))]
1805 macro_rules! declare_tile_cache_logger_updatelists {
1806     ( $( $name:ident : $ty:ty, )+ ) => {
1807         #[cfg_attr(feature = "capture", derive(Serialize))]
1808         #[cfg_attr(feature = "replay", derive(Deserialize))]
1809         struct TileCacheLoggerUpdateListsSerializer {
1810             pub ron_string: Vec<String>,
1811         }
1813         pub struct TileCacheLoggerUpdateLists {
1814             $(
1815                 /// Generate storage, one per interner.
1816                 /// the tuple is a workaround to avoid the need for multiple
1817                 /// fields that start with $name (macro concatenation).
1818                 /// the string is .ron serialized updatelist at capture time;
1819                 /// the updates is the list of DataStore updates (avoid UpdateList
1820                 /// due to Default() requirements on the Keys) reconstructed at
1821                 /// load time.
1822                 pub $name: (Vec<String>, Vec<UpdateList<<$ty as Internable>::Key>>),
1823             )+
1824         }
1826         impl TileCacheLoggerUpdateLists {
1827             pub fn new() -> Self {
1828                 TileCacheLoggerUpdateLists {
1829                     $(
1830                         $name : ( Vec::new(), Vec::new() ),
1831                     )+
1832                 }
1833             }
1835             /// serialize all interners in updates to .ron
1836             #[cfg(feature = "capture")]
1837             fn serialize_updates(
1838                 &mut self,
1839                 updates: &InternerUpdates
1840             ) {
1841                 $(
1842                     self.$name.0.push(ron::ser::to_string_pretty(&updates.$name, Default::default()).unwrap());
1843                 )+
1844             }
1846             fn is_empty(&self) -> bool {
1847                 $(
1848                     if !self.$name.0.is_empty() { return false; }
1849                 )+
1850                 true
1851             }
1853             #[cfg(feature = "capture")]
1854             fn to_ron(&self) -> String {
1855                 let mut serializer =
1856                     TileCacheLoggerUpdateListsSerializer { ron_string: Vec::new() };
1857                 $(
1858                     serializer.ron_string.push(
1859                         ron::ser::to_string_pretty(&self.$name.0, Default::default()).unwrap());
1860                 )+
1861                 ron::ser::to_string_pretty(&serializer, Default::default()).unwrap()
1862             }
1864             #[cfg(feature = "replay")]
1865             pub fn from_ron(&mut self, text: &str) {
1866                 let serializer : TileCacheLoggerUpdateListsSerializer =
1867                     match ron::de::from_str(&text) {
1868                         Ok(data) => { data }
1869                         Err(e) => {
1870                             println!("ERROR: failed to deserialize updatelist: {:?}\n{:?}", &text, e);
1871                             return;
1872                         }
1873                     };
1874                 let mut index = 0;
1875                 $(
1876                     let ron_lists : Vec<String> = ron::de::from_str(&serializer.ron_string[index]).unwrap();
1877                     self.$name.1 = ron_lists.iter()
1878                                             .map( |list| ron::de::from_str(&list).unwrap() )
1879                                             .collect();
1880                     index = index + 1;
1881                 )+
1882                 // error: value assigned to `index` is never read
1883                 let _ = index;
1884             }
1886             /// helper method to add a stringified version of all interned keys into
1887             /// a lookup table based on ItemUid.  Use strings as a form of type erasure
1888             /// so all UpdateLists can go into a single map.
1889             /// Then during analysis, when we see an invalidation reason due to
1890             /// "ItemUid such and such was added to the tile primitive list", the lookup
1891             /// allows mapping that back into something readable.
1892             #[cfg(feature = "replay")]
1893             pub fn insert_in_lookup(
1894                         &mut self,
1895                         itemuid_to_string: &mut HashMap<ItemUid, String>)
1896             {
1897                 $(
1898                     {
1899                         for list in &self.$name.1 {
1900                             for insertion in &list.insertions {
1901                                 itemuid_to_string.insert(
1902                                     insertion.uid,
1903                                     format!("{:?}", insertion.value));
1904                             }
1905                         }
1906                     }
1907                 )+
1908             }
1909         }
1910     }
1913 #[cfg(any(feature = "capture", feature = "replay"))]
1914 crate::enumerate_interners!(declare_tile_cache_logger_updatelists);
1916 #[cfg(not(any(feature = "capture", feature = "replay")))]
1917 pub struct TileCacheLoggerUpdateLists {
1920 #[cfg(not(any(feature = "capture", feature = "replay")))]
1921 impl TileCacheLoggerUpdateLists {
1922     pub fn new() -> Self { TileCacheLoggerUpdateLists {} }
1923     fn is_empty(&self) -> bool { true }
1926 /// Log tile cache activity for one single frame.
1927 /// Also stores the commands sent to the interning data_stores
1928 /// so we can see which items were created or destroyed this frame,
1929 /// and correlate that with tile invalidation activity.
1930 pub struct TileCacheLoggerFrame {
1931     /// slices in the frame, one per take_context call
1932     pub slices: Vec<TileCacheLoggerSlice>,
1933     /// interning activity
1934     pub update_lists: TileCacheLoggerUpdateLists
1937 impl TileCacheLoggerFrame {
1938     pub fn new() -> Self {
1939         TileCacheLoggerFrame {
1940             slices: Vec::new(),
1941             update_lists: TileCacheLoggerUpdateLists::new()
1942         }
1943     }
1945     pub fn is_empty(&self) -> bool {
1946         self.slices.is_empty() && self.update_lists.is_empty()
1947     }
1950 /// Log tile cache activity whenever anything happens in take_context.
1951 pub struct TileCacheLogger {
1952     /// next write pointer
1953     pub write_index : usize,
1954     /// ron serialization of tile caches;
1955     pub frames: Vec<TileCacheLoggerFrame>
1958 impl TileCacheLogger {
1959     pub fn new(
1960         num_frames: usize
1961     ) -> Self {
1962         let mut frames = Vec::with_capacity(num_frames);
1963         for _i in 0..num_frames { // no Clone so no resize
1964             frames.push(TileCacheLoggerFrame::new());
1965         }
1966         TileCacheLogger {
1967             write_index: 0,
1968             frames
1969         }
1970     }
1972     pub fn is_enabled(&self) -> bool {
1973         !self.frames.is_empty()
1974     }
1976     #[cfg(feature = "capture")]
1977     pub fn add(
1978             &mut self,
1979             serialized_slice: String,
1980             local_to_world_transform: Transform3D<f32, PicturePixel, WorldPixel>
1981     ) {
1982         if !self.is_enabled() {
1983             return;
1984         }
1985         self.frames[self.write_index].slices.push(
1986             TileCacheLoggerSlice {
1987                 serialized_slice,
1988                 local_to_world_transform });
1989     }
1991     #[cfg(feature = "capture")]
1992     pub fn serialize_updates(&mut self, updates: &InternerUpdates) {
1993         if !self.is_enabled() {
1994             return;
1995         }
1996         self.frames[self.write_index].update_lists.serialize_updates(updates);
1997     }
1999     /// see if anything was written in this frame, and if so,
2000     /// advance the write index in a circular way and clear the
2001     /// recorded string.
2002     pub fn advance(&mut self) {
2003         if !self.is_enabled() || self.frames[self.write_index].is_empty() {
2004             return;
2005         }
2006         self.write_index = self.write_index + 1;
2007         if self.write_index >= self.frames.len() {
2008             self.write_index = 0;
2009         }
2010         self.frames[self.write_index] = TileCacheLoggerFrame::new();
2011     }
2013     #[cfg(feature = "capture")]
2014     pub fn save_capture(
2015         &self, root: &PathBuf
2016     ) {
2017         if !self.is_enabled() {
2018             return;
2019         }
2020         use std::fs;
2022         info!("saving tile cache log");
2023         let path_tile_cache = root.join("tile_cache");
2024         if !path_tile_cache.is_dir() {
2025             fs::create_dir(&path_tile_cache).unwrap();
2026         }
2028         let mut files_written = 0;
2029         for ix in 0..self.frames.len() {
2030             // ...and start with write_index, since that's the oldest entry
2031             // that we're about to overwrite. However when we get to
2032             // save_capture, we've add()ed entries but haven't advance()d yet,
2033             // so the actual oldest entry is write_index + 1
2034             let index = (self.write_index + 1 + ix) % self.frames.len();
2035             if self.frames[index].is_empty() {
2036                 continue;
2037             }
2039             let filename = path_tile_cache.join(format!("frame{:05}.ron", files_written));
2040             let mut output = File::create(filename).unwrap();
2041             output.write_all(b"// slice data\n").unwrap();
2042             output.write_all(b"[\n").unwrap();
2043             for item in &self.frames[index].slices {
2044                 output.write_all(b"( transform:\n").unwrap();
2045                 let transform =
2046                     ron::ser::to_string_pretty(
2047                         &item.local_to_world_transform, Default::default()).unwrap();
2048                 output.write_all(transform.as_bytes()).unwrap();
2049                 output.write_all(b",\n tile_cache:\n").unwrap();
2050                 output.write_all(item.serialized_slice.as_bytes()).unwrap();
2051                 output.write_all(b"\n),\n").unwrap();
2052             }
2053             output.write_all(b"]\n\n").unwrap();
2055             output.write_all(b"// @@@ chunk @@@\n\n").unwrap();
2057             output.write_all(b"// interning data\n").unwrap();
2058             output.write_all(self.frames[index].update_lists.to_ron().as_bytes()).unwrap();
2060             files_written = files_written + 1;
2061         }
2062     }
2065 /// Represents the native surfaces created for a picture cache, if using
2066 /// a native compositor. An opaque and alpha surface is always created,
2067 /// but tiles are added to a surface based on current opacity. If the
2068 /// calculated opacity of a tile changes, the tile is invalidated and
2069 /// attached to a different native surface. This means that we don't
2070 /// need to invalidate the entire surface if only some tiles are changing
2071 /// opacity. It also means we can take advantage of opaque tiles on cache
2072 /// slices where only some of the tiles are opaque. There is an assumption
2073 /// that creating a native surface is cheap, and only when a tile is added
2074 /// to a surface is there a significant cost. This assumption holds true
2075 /// for the current native compositor implementations on Windows and Mac.
2076 pub struct NativeSurface {
2077     /// Native surface for opaque tiles
2078     pub opaque: NativeSurfaceId,
2079     /// Native surface for alpha tiles
2080     pub alpha: NativeSurfaceId,
2083 /// Hash key for an external native compositor surface
2084 #[derive(PartialEq, Eq, Hash)]
2085 pub struct ExternalNativeSurfaceKey {
2086     /// The YUV/RGB image keys that are used to draw this surface.
2087     pub image_keys: [ImageKey; 3],
2088     /// The current device size of the surface.
2089     pub size: DeviceIntSize,
2090     /// True if this is an 'external' compositor surface created via
2091     /// Compositor::create_external_surface.
2092     pub is_external_surface: bool,
2095 /// Information about a native compositor surface cached between frames.
2096 pub struct ExternalNativeSurface {
2097     /// If true, the surface was used this frame. Used for a simple form
2098     /// of GC to remove old surfaces.
2099     pub used_this_frame: bool,
2100     /// The native compositor surface handle
2101     pub native_surface_id: NativeSurfaceId,
2102     /// List of image keys, and current image generations, that are drawn in this surface.
2103     /// The image generations are used to check if the compositor surface is dirty and
2104     /// needs to be updated.
2105     pub image_dependencies: [ImageDependency; 3],
2108 /// The key that identifies a tile cache instance. For now, it's simple the index of
2109 /// the slice as it was created during scene building.
2110 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2111 #[cfg_attr(feature = "capture", derive(Serialize))]
2112 #[cfg_attr(feature = "replay", derive(Deserialize))]
2113 pub struct SliceId(usize);
2115 impl SliceId {
2116     pub fn new(index: usize) -> Self {
2117         SliceId(index)
2118     }
2121 /// Information that is required to reuse or create a new tile cache. Created
2122 /// during scene building and passed to the render backend / frame builder.
2123 pub struct TileCacheParams {
2124     // Index of the slice (also effectively the key of the tile cache, though we use SliceId where that matters)
2125     pub slice: usize,
2126     // Flags describing content of this cache (e.g. scrollbars)
2127     pub slice_flags: SliceFlags,
2128     // The anchoring spatial node / scroll root
2129     pub spatial_node_index: SpatialNodeIndex,
2130     // Optional background color of this tilecache. If present, can be used as an optimization
2131     // to enable opaque blending and/or subpixel AA in more places.
2132     pub background_color: Option<ColorF>,
2133     // List of clips shared by all prims that are promoted to this tile cache
2134     pub shared_clips: Vec<ClipInstance>,
2135     // The clip chain handle representing `shared_clips`
2136     pub shared_clip_chain: ClipChainId,
2137     // Virtual surface sizes are always square, so this represents both the width and height
2138     pub virtual_surface_size: i32,
2139     // The number of compositor surfaces that are being requested for this tile cache.
2140     // This is only a suggestion - the tile cache will clamp this as a reasonable number
2141     // and only promote a limited number of surfaces.
2142     pub compositor_surface_count: usize,
2145 /// Defines which sub-slice (effectively a z-index) a primitive exists on within
2146 /// a picture cache instance.
2147 #[cfg_attr(feature = "capture", derive(Serialize))]
2148 #[cfg_attr(feature = "replay", derive(Deserialize))]
2149 #[derive(Debug, Copy, Clone, PartialEq)]
2150 pub struct SubSliceIndex(u8);
2152 impl SubSliceIndex {
2153     pub const DEFAULT: SubSliceIndex = SubSliceIndex(0);
2155     pub fn new(index: usize) -> Self {
2156         SubSliceIndex(index as u8)
2157     }
2159     /// Return true if this sub-slice is the primary sub-slice (for now, we assume
2160     /// that only the primary sub-slice may be opaque and support subpixel AA, for example).
2161     pub fn is_primary(&self) -> bool {
2162         self.0 == 0
2163     }
2166 /// Wrapper struct around an external surface descriptor with a little more information
2167 /// that the picture caching code needs.
2168 pub struct CompositorSurface {
2169     // External surface descriptor used by compositing logic
2170     pub descriptor: ExternalSurfaceDescriptor,
2171     // The compositor surface rect + any intersecting prims. Later prims that intersect
2172     // with this must be added to the next sub-slice.
2173     prohibited_rect: PictureRect,
2174     // If the compositor surface content is opaque.
2175     pub is_opaque: bool,
2178 /// A SubSlice represents a potentially overlapping set of tiles within a picture cache. Most
2179 /// picture cache instances will have only a single sub-slice. The exception to this is when
2180 /// a picture cache has compositor surfaces, in which case sub slices are used to interleave
2181 /// content under or order the compositor surface(s).
2182 pub struct SubSlice {
2183     /// Hash of tiles present in this picture.
2184     pub tiles: FastHashMap<TileOffset, Box<Tile>>,
2185     /// The allocated compositor surfaces for this picture cache. May be None if
2186     /// not using native compositor, or if the surface was destroyed and needs
2187     /// to be reallocated next time this surface contains valid tiles.
2188     pub native_surface: Option<NativeSurface>,
2189     /// List of compositor surfaces that have been promoted from primitives
2190     /// in this tile cache.
2191     pub compositor_surfaces: Vec<CompositorSurface>,
2194 impl SubSlice {
2195     /// Construct a new sub-slice
2196     fn new() -> Self {
2197         SubSlice {
2198             tiles: FastHashMap::default(),
2199             native_surface: None,
2200             compositor_surfaces: Vec::new(),
2201         }
2202     }
2204     /// Reset the list of compositor surfaces that follow this sub-slice.
2205     /// Built per-frame, since APZ may change whether an image is suitable to be a compositor surface.
2206     fn reset(&mut self) {
2207         self.compositor_surfaces.clear();
2208     }
2210     /// Resize the tile grid to match a new tile bounds
2211     fn resize(&mut self, new_tile_rect: TileRect) -> FastHashMap<TileOffset, Box<Tile>> {
2212         let mut old_tiles = mem::replace(&mut self.tiles, FastHashMap::default());
2213         self.tiles.reserve(new_tile_rect.size.area() as usize);
2215         for y in new_tile_rect.origin.y .. new_tile_rect.origin.y + new_tile_rect.size.height {
2216             for x in new_tile_rect.origin.x .. new_tile_rect.origin.x + new_tile_rect.size.width {
2217                 let key = TileOffset::new(x, y);
2218                 let tile = old_tiles
2219                     .remove(&key)
2220                     .unwrap_or_else(|| {
2221                         Box::new(Tile::new(key))
2222                     });
2223                 self.tiles.insert(key, tile);
2224             }
2225         }
2227         old_tiles
2228     }
2231 /// Represents a cache of tiles that make up a picture primitives.
2232 pub struct TileCacheInstance {
2233     /// Index of the tile cache / slice for this frame builder. It's determined
2234     /// by the setup_picture_caching method during flattening, which splits the
2235     /// picture tree into multiple slices. It's used as a simple input to the tile
2236     /// keys. It does mean we invalidate tiles if a new layer gets inserted / removed
2237     /// between display lists - this seems very unlikely to occur on most pages, but
2238     /// can be revisited if we ever notice that.
2239     pub slice: usize,
2240     /// Propagated information about the slice
2241     pub slice_flags: SliceFlags,
2242     /// The currently selected tile size to use for this cache
2243     pub current_tile_size: DeviceIntSize,
2244     /// The list of sub-slices in this tile cache
2245     pub sub_slices: Vec<SubSlice>,
2246     /// The positioning node for this tile cache.
2247     pub spatial_node_index: SpatialNodeIndex,
2248     /// List of opacity bindings, with some extra information
2249     /// about whether they changed since last frame.
2250     opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>,
2251     /// Switch back and forth between old and new bindings hashmaps to avoid re-allocating.
2252     old_opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>,
2253     /// A helper to compare transforms between previous and current frame.
2254     spatial_node_comparer: SpatialNodeComparer,
2255     /// List of color bindings, with some extra information
2256     /// about whether they changed since last frame.
2257     color_bindings: FastHashMap<PropertyBindingId, ColorBindingInfo>,
2258     /// Switch back and forth between old and new bindings hashmaps to avoid re-allocating.
2259     old_color_bindings: FastHashMap<PropertyBindingId, ColorBindingInfo>,
2260     /// The current dirty region tracker for this picture.
2261     pub dirty_region: DirtyRegion,
2262     /// Current size of tiles in picture units.
2263     tile_size: PictureSize,
2264     /// Tile coords of the currently allocated grid.
2265     tile_rect: TileRect,
2266     /// Pre-calculated versions of the tile_rect above, used to speed up the
2267     /// calculations in get_tile_coords_for_rect.
2268     tile_bounds_p0: TileOffset,
2269     tile_bounds_p1: TileOffset,
2270     /// Local rect (unclipped) of the picture this cache covers.
2271     pub local_rect: PictureRect,
2272     /// The local clip rect, from the shared clips of this picture.
2273     pub local_clip_rect: PictureRect,
2274     /// The surface index that this tile cache will be drawn into.
2275     surface_index: SurfaceIndex,
2276     /// The background color from the renderer. If this is set opaque, we know it's
2277     /// fine to clear the tiles to this and allow subpixel text on the first slice.
2278     pub background_color: Option<ColorF>,
2279     /// Information about the calculated backdrop content of this cache.
2280     pub backdrop: BackdropInfo,
2281     /// The allowed subpixel mode for this surface, which depends on the detected
2282     /// opacity of the background.
2283     pub subpixel_mode: SubpixelMode,
2284     /// A list of clip handles that exist on every (top-level) primitive in this picture.
2285     /// It's often the case that these are root / fixed position clips. By handling them
2286     /// here, we can avoid applying them to the items, which reduces work, but more importantly
2287     /// reduces invalidations.
2288     pub shared_clips: Vec<ClipInstance>,
2289     /// The clip chain that represents the shared_clips above. Used to build the local
2290     /// clip rect for this tile cache.
2291     shared_clip_chain: ClipChainId,
2292     /// The current transform of the picture cache root spatial node
2293     root_transform: ScaleOffset,
2294     /// The number of frames until this cache next evaluates what tile size to use.
2295     /// If a picture rect size is regularly changing just around a size threshold,
2296     /// we don't want to constantly invalidate and reallocate different tile size
2297     /// configuration each frame.
2298     frames_until_size_eval: usize,
2299     /// The current fractional offset of the cached picture
2300     fract_offset: PictureVector2D,
2301     /// The current device fractional offset of the cached picture
2302     device_fract_offset: DeviceVector2D,
2303     /// For DirectComposition, virtual surfaces don't support negative coordinates. However,
2304     /// picture cache tile coordinates can be negative. To handle this, we apply an offset
2305     /// to each tile in DirectComposition. We want to change this as little as possible,
2306     /// to avoid invalidating tiles. However, if we have a picture cache tile coordinate
2307     /// which is outside the virtual surface bounds, we must change this to allow
2308     /// correct remapping of the coordinates passed to BeginDraw in DC.
2309     virtual_offset: DeviceIntPoint,
2310     /// keep around the hash map used as compare_cache to avoid reallocating it each
2311     /// frame.
2312     compare_cache: FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>,
2313     /// The current device position of this cache. Used to set the compositor
2314     /// offset of the surface when building the visual tree.
2315     pub device_position: DevicePoint,
2316     /// The currently considered tile size override. Used to check if we should
2317     /// re-evaluate tile size, even if the frame timer hasn't expired.
2318     tile_size_override: Option<DeviceIntSize>,
2319     /// z-buffer ID assigned to the opaque backdrop, if there is one, in this slice
2320     pub z_id_backdrop: ZBufferId,
2321     /// A cache of compositor surfaces that are retained between frames
2322     pub external_native_surface_cache: FastHashMap<ExternalNativeSurfaceKey, ExternalNativeSurface>,
2323     /// Current frame ID of this tile cache instance. Used for book-keeping / garbage collecting
2324     frame_id: FrameId,
2327 enum SurfacePromotionResult {
2328     Failed,
2329     Success {
2330         flip_y: bool,
2331     }
2334 impl TileCacheInstance {
2335     pub fn new(params: TileCacheParams) -> Self {
2336         // Determine how many sub-slices we need. Clamp to an arbitrary limit to ensure
2337         // we don't create a huge number of OS compositor tiles and sub-slices.
2338         let sub_slice_count = params.compositor_surface_count.min(MAX_COMPOSITOR_SURFACES) + 1;
2340         let mut sub_slices = Vec::with_capacity(sub_slice_count);
2341         for _ in 0 .. sub_slice_count {
2342             sub_slices.push(SubSlice::new());
2343         }
2345         TileCacheInstance {
2346             slice: params.slice,
2347             slice_flags: params.slice_flags,
2348             spatial_node_index: params.spatial_node_index,
2349             sub_slices,
2350             opacity_bindings: FastHashMap::default(),
2351             old_opacity_bindings: FastHashMap::default(),
2352             spatial_node_comparer: SpatialNodeComparer::new(),
2353             color_bindings: FastHashMap::default(),
2354             old_color_bindings: FastHashMap::default(),
2355             dirty_region: DirtyRegion::new(params.spatial_node_index),
2356             tile_size: PictureSize::zero(),
2357             tile_rect: TileRect::zero(),
2358             tile_bounds_p0: TileOffset::zero(),
2359             tile_bounds_p1: TileOffset::zero(),
2360             local_rect: PictureRect::zero(),
2361             local_clip_rect: PictureRect::zero(),
2362             surface_index: SurfaceIndex(0),
2363             background_color: params.background_color,
2364             backdrop: BackdropInfo::empty(),
2365             subpixel_mode: SubpixelMode::Allow,
2366             root_transform: ScaleOffset::identity(),
2367             shared_clips: params.shared_clips,
2368             shared_clip_chain: params.shared_clip_chain,
2369             current_tile_size: DeviceIntSize::zero(),
2370             frames_until_size_eval: 0,
2371             fract_offset: PictureVector2D::zero(),
2372             device_fract_offset: DeviceVector2D::zero(),
2373             // Default to centering the virtual offset in the middle of the DC virtual surface
2374             virtual_offset: DeviceIntPoint::new(
2375                 params.virtual_surface_size / 2,
2376                 params.virtual_surface_size / 2,
2377             ),
2378             compare_cache: FastHashMap::default(),
2379             device_position: DevicePoint::zero(),
2380             tile_size_override: None,
2381             z_id_backdrop: ZBufferId::invalid(),
2382             external_native_surface_cache: FastHashMap::default(),
2383             frame_id: FrameId::INVALID,
2384         }
2385     }
2387     /// Return the total number of tiles allocated by this tile cache
2388     pub fn tile_count(&self) -> usize {
2389         self.tile_rect.size.area() as usize * self.sub_slices.len()
2390     }
2392     /// Reset this tile cache with the updated parameters from a new scene
2393     /// that has arrived. This allows the tile cache to be retained across
2394     /// new scenes.
2395     pub fn prepare_for_new_scene(
2396         &mut self,
2397         params: TileCacheParams,
2398         resource_cache: &mut ResourceCache,
2399     ) {
2400         // We should only receive updated state for matching slice key
2401         assert_eq!(self.slice, params.slice);
2403         // Determine how many sub-slices we need, based on how many compositor surface prims are
2404         // in the supplied primitive list.
2405         let required_sub_slice_count = params.compositor_surface_count.min(MAX_COMPOSITOR_SURFACES) + 1;
2407         if self.sub_slices.len() != required_sub_slice_count {
2408             self.tile_rect = TileRect::zero();
2410             if self.sub_slices.len() > required_sub_slice_count {
2411                 let old_sub_slices = self.sub_slices.split_off(required_sub_slice_count);
2413                 for mut sub_slice in old_sub_slices {
2414                     for tile in sub_slice.tiles.values_mut() {
2415                         if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface {
2416                             if let Some(id) = id.take() {
2417                                 resource_cache.destroy_compositor_tile(id);
2418                             }
2419                         }
2420                     }
2422                     if let Some(native_surface) = sub_slice.native_surface {
2423                         resource_cache.destroy_compositor_surface(native_surface.opaque);
2424                         resource_cache.destroy_compositor_surface(native_surface.alpha);
2425                     }
2426                 }
2427             } else {
2428                 while self.sub_slices.len() < required_sub_slice_count {
2429                     self.sub_slices.push(SubSlice::new());
2430                 }
2431             }
2432         }
2434         // Store the parameters from the scene builder for this slice. Other
2435         // params in the tile cache are retained and reused, or are always
2436         // updated during pre/post_update.
2437         self.slice_flags = params.slice_flags;
2438         self.spatial_node_index = params.spatial_node_index;
2439         self.background_color = params.background_color;
2440         self.shared_clips = params.shared_clips;
2441         self.shared_clip_chain = params.shared_clip_chain;
2443         // Since the slice flags may have changed, ensure we re-evaluate the
2444         // appropriate tile size for this cache next update.
2445         self.frames_until_size_eval = 0;
2446     }
2448     /// Destroy any manually managed resources before this picture cache is
2449     /// destroyed, such as native compositor surfaces.
2450     pub fn destroy(
2451         self,
2452         resource_cache: &mut ResourceCache,
2453     ) {
2454         for sub_slice in self.sub_slices {
2455             if let Some(native_surface) = sub_slice.native_surface {
2456                 resource_cache.destroy_compositor_surface(native_surface.opaque);
2457                 resource_cache.destroy_compositor_surface(native_surface.alpha);
2458             }
2459         }
2461         for (_, external_surface) in self.external_native_surface_cache {
2462             resource_cache.destroy_compositor_surface(external_surface.native_surface_id)
2463         }
2464     }
2466     /// Get the tile coordinates for a given rectangle.
2467     fn get_tile_coords_for_rect(
2468         &self,
2469         rect: &PictureRect,
2470     ) -> (TileOffset, TileOffset) {
2471         // Get the tile coordinates in the picture space.
2472         let mut p0 = TileOffset::new(
2473             (rect.origin.x / self.tile_size.width).floor() as i32,
2474             (rect.origin.y / self.tile_size.height).floor() as i32,
2475         );
2477         let mut p1 = TileOffset::new(
2478             ((rect.origin.x + rect.size.width) / self.tile_size.width).ceil() as i32,
2479             ((rect.origin.y + rect.size.height) / self.tile_size.height).ceil() as i32,
2480         );
2482         // Clamp the tile coordinates here to avoid looping over irrelevant tiles later on.
2483         p0.x = clamp(p0.x, self.tile_bounds_p0.x, self.tile_bounds_p1.x);
2484         p0.y = clamp(p0.y, self.tile_bounds_p0.y, self.tile_bounds_p1.y);
2485         p1.x = clamp(p1.x, self.tile_bounds_p0.x, self.tile_bounds_p1.x);
2486         p1.y = clamp(p1.y, self.tile_bounds_p0.y, self.tile_bounds_p1.y);
2488         (p0, p1)
2489     }
2491     /// Update transforms, opacity, color bindings and tile rects.
2492     pub fn pre_update(
2493         &mut self,
2494         pic_rect: PictureRect,
2495         surface_index: SurfaceIndex,
2496         frame_context: &FrameVisibilityContext,
2497         frame_state: &mut FrameVisibilityState,
2498     ) -> WorldRect {
2499         self.surface_index = surface_index;
2500         self.local_rect = pic_rect;
2501         self.local_clip_rect = PictureRect::max_rect();
2503         for sub_slice in &mut self.sub_slices {
2504             sub_slice.reset();
2505         }
2507         // Backdrop surfaces get the first z_id. Subslices and compositor surfaces then get
2508         // allocated a z_id each.
2509         self.z_id_backdrop = frame_state.composite_state.z_generator.next();
2511         // Reset the opaque rect + subpixel mode, as they are calculated
2512         // during the prim dependency checks.
2513         self.backdrop = BackdropInfo::empty();
2515         let pic_to_world_mapper = SpaceMapper::new_with_target(
2516             ROOT_SPATIAL_NODE_INDEX,
2517             self.spatial_node_index,
2518             frame_context.global_screen_world_rect,
2519             frame_context.spatial_tree,
2520         );
2522         // If there is a valid set of shared clips, build a clip chain instance for this,
2523         // which will provide a local clip rect. This is useful for establishing things
2524         // like whether the backdrop rect supplied by Gecko can be considered opaque.
2525         if self.shared_clip_chain != ClipChainId::NONE {
2526             let shared_clips = &mut frame_state.scratch.picture.clip_chain_ids;
2527             shared_clips.clear();
2529             let map_local_to_surface = SpaceMapper::new(
2530                 self.spatial_node_index,
2531                 pic_rect,
2532             );
2534             let mut current_clip_chain_id = self.shared_clip_chain;
2535             while current_clip_chain_id != ClipChainId::NONE {
2536                 shared_clips.push(current_clip_chain_id);
2537                 let clip_chain_node = &frame_state.clip_store.clip_chain_nodes[current_clip_chain_id.0 as usize];
2538                 current_clip_chain_id = clip_chain_node.parent_clip_chain_id;
2539             }
2541             frame_state.clip_store.set_active_clips(
2542                 LayoutRect::max_rect(),
2543                 self.spatial_node_index,
2544                 map_local_to_surface.ref_spatial_node_index,
2545                 &shared_clips,
2546                 frame_context.spatial_tree,
2547                 &mut frame_state.data_stores.clip,
2548             );
2550             let clip_chain_instance = frame_state.clip_store.build_clip_chain_instance(
2551                 pic_rect.cast_unit(),
2552                 &map_local_to_surface,
2553                 &pic_to_world_mapper,
2554                 frame_context.spatial_tree,
2555                 frame_state.gpu_cache,
2556                 frame_state.resource_cache,
2557                 frame_context.global_device_pixel_scale,
2558                 &frame_context.global_screen_world_rect,
2559                 &mut frame_state.data_stores.clip,
2560                 true,
2561                 false,
2562             );
2564             // Ensure that if the entire picture cache is clipped out, the local
2565             // clip rect is zero. This makes sure we don't register any occluders
2566             // that are actually off-screen.
2567             self.local_clip_rect = clip_chain_instance.map_or(PictureRect::zero(), |clip_chain_instance| {
2568                 clip_chain_instance.pic_clip_rect
2569             });
2570         }
2572         // Advance the current frame ID counter for this picture cache (must be done
2573         // after any retained prev state is taken above).
2574         self.frame_id.advance();
2576         // Notify the spatial node comparer that a new frame has started, and the
2577         // current reference spatial node for this tile cache.
2578         self.spatial_node_comparer.next_frame(self.spatial_node_index);
2580         // At the start of the frame, step through each current compositor surface
2581         // and mark it as unused. Later, this is used to free old compositor surfaces.
2582         // TODO(gw): In future, we might make this more sophisticated - for example,
2583         //           retaining them for >1 frame if unused, or retaining them in some
2584         //           kind of pool to reduce future allocations.
2585         for external_native_surface in self.external_native_surface_cache.values_mut() {
2586             external_native_surface.used_this_frame = false;
2587         }
2589         // Only evaluate what tile size to use fairly infrequently, so that we don't end
2590         // up constantly invalidating and reallocating tiles if the picture rect size is
2591         // changing near a threshold value.
2592         if self.frames_until_size_eval == 0 ||
2593            self.tile_size_override != frame_context.config.tile_size_override {
2595             // Work out what size tile is appropriate for this picture cache.
2596             let desired_tile_size = match frame_context.config.tile_size_override {
2597                 Some(tile_size_override) => {
2598                     tile_size_override
2599                 }
2600                 None => {
2601                     if self.slice_flags.contains(SliceFlags::IS_SCROLLBAR) {
2602                         if pic_rect.size.width <= pic_rect.size.height {
2603                             TILE_SIZE_SCROLLBAR_VERTICAL
2604                         } else {
2605                             TILE_SIZE_SCROLLBAR_HORIZONTAL
2606                         }
2607                     } else {
2608                         frame_state.resource_cache.texture_cache.default_picture_tile_size()
2609                     }
2610                 }
2611             };
2613             // If the desired tile size has changed, then invalidate and drop any
2614             // existing tiles.
2615             if desired_tile_size != self.current_tile_size {
2616                 for sub_slice in &mut self.sub_slices {
2617                     // Destroy any native surfaces on the tiles that will be dropped due
2618                     // to resizing.
2619                     if let Some(native_surface) = sub_slice.native_surface.take() {
2620                         frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque);
2621                         frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha);
2622                     }
2623                     sub_slice.tiles.clear();
2624                 }
2625                 self.tile_rect = TileRect::zero();
2626                 self.current_tile_size = desired_tile_size;
2627             }
2629             // Reset counter until next evaluating the desired tile size. This is an
2630             // arbitrary value.
2631             self.frames_until_size_eval = 120;
2632             self.tile_size_override = frame_context.config.tile_size_override;
2633         }
2635         // Map an arbitrary point in picture space to world space, to work out
2636         // what the fractional translation is that's applied by this scroll root.
2637         // TODO(gw): I'm not 100% sure this is right. At least, in future, we should
2638         //           make a specific API for this, and/or enforce that the picture
2639         //           cache transform only includes scale and/or translation (we
2640         //           already ensure it doesn't have perspective).
2641         let world_origin = pic_to_world_mapper
2642             .map(&PictureRect::new(PicturePoint::zero(), PictureSize::new(1.0, 1.0)))
2643             .expect("bug: unable to map origin to world space")
2644             .origin;
2646         // Get the desired integer device coordinate
2647         let device_origin = world_origin * frame_context.global_device_pixel_scale;
2648         let desired_device_origin = device_origin.round();
2649         self.device_position = desired_device_origin;
2650         self.device_fract_offset = desired_device_origin - device_origin;
2652         // Unmap from device space to world space rect
2653         let ref_world_rect = WorldRect::new(
2654             desired_device_origin / frame_context.global_device_pixel_scale,
2655             WorldSize::new(1.0, 1.0),
2656         );
2658         // Unmap from world space to picture space; this should be the fractional offset
2659         // required in picture space to align in device space
2660         self.fract_offset = pic_to_world_mapper
2661             .unmap(&ref_world_rect)
2662             .expect("bug: unable to unmap ref world rect")
2663             .origin
2664             .to_vector();
2666         // Do a hacky diff of opacity binding values from the last frame. This is
2667         // used later on during tile invalidation tests.
2668         let current_properties = frame_context.scene_properties.float_properties();
2669         mem::swap(&mut self.opacity_bindings, &mut self.old_opacity_bindings);
2671         self.opacity_bindings.clear();
2672         for (id, value) in current_properties {
2673             let changed = match self.old_opacity_bindings.get(id) {
2674                 Some(old_property) => !old_property.value.approx_eq(value),
2675                 None => true,
2676             };
2677             self.opacity_bindings.insert(*id, OpacityBindingInfo {
2678                 value: *value,
2679                 changed,
2680             });
2681         }
2683         // Do a hacky diff of color binding values from the last frame. This is
2684         // used later on during tile invalidation tests.
2685         let current_properties = frame_context.scene_properties.color_properties();
2686         mem::swap(&mut self.color_bindings, &mut self.old_color_bindings);
2688         self.color_bindings.clear();
2689         for (id, value) in current_properties {
2690             let changed = match self.old_color_bindings.get(id) {
2691                 Some(old_property) => old_property.value != (*value).into(),
2692                 None => true,
2693             };
2694             self.color_bindings.insert(*id, ColorBindingInfo {
2695                 value: (*value).into(),
2696                 changed,
2697             });
2698         }
2700         let world_tile_size = WorldSize::new(
2701             self.current_tile_size.width as f32 / frame_context.global_device_pixel_scale.0,
2702             self.current_tile_size.height as f32 / frame_context.global_device_pixel_scale.0,
2703         );
2705         // We know that this is an exact rectangle, since we (for now) only support tile
2706         // caches where the scroll root is in the root coordinate system.
2707         let local_tile_rect = pic_to_world_mapper
2708             .unmap(&WorldRect::new(WorldPoint::zero(), world_tile_size))
2709             .expect("bug: unable to get local tile rect");
2711         self.tile_size = local_tile_rect.size;
2713         let screen_rect_in_pic_space = pic_to_world_mapper
2714             .unmap(&frame_context.global_screen_world_rect)
2715             .expect("unable to unmap screen rect");
2717         // Inflate the needed rect a bit, so that we retain tiles that we have drawn
2718         // but have just recently gone off-screen. This means that we avoid re-drawing
2719         // tiles if the user is scrolling up and down small amounts, at the cost of
2720         // a bit of extra texture memory.
2721         let desired_rect_in_pic_space = screen_rect_in_pic_space
2722             .inflate(0.0, 1.0 * self.tile_size.height);
2724         let needed_rect_in_pic_space = desired_rect_in_pic_space
2725             .intersection(&pic_rect)
2726             .unwrap_or_else(PictureRect::zero);
2728         let p0 = needed_rect_in_pic_space.origin;
2729         let p1 = needed_rect_in_pic_space.bottom_right();
2731         let x0 = (p0.x / local_tile_rect.size.width).floor() as i32;
2732         let x1 = (p1.x / local_tile_rect.size.width).ceil() as i32;
2734         let y0 = (p0.y / local_tile_rect.size.height).floor() as i32;
2735         let y1 = (p1.y / local_tile_rect.size.height).ceil() as i32;
2737         let x_tiles = x1 - x0;
2738         let y_tiles = y1 - y0;
2739         let new_tile_rect = TileRect::new(
2740             TileOffset::new(x0, y0),
2741             TileSize::new(x_tiles, y_tiles),
2742         );
2744         // Determine whether the current bounds of the tile grid will exceed the
2745         // bounds of the DC virtual surface, taking into account the current
2746         // virtual offset. If so, we need to invalidate all tiles, and set up
2747         // a new virtual offset, centered around the current tile grid.
2749         let virtual_surface_size = frame_context.config.compositor_kind.get_virtual_surface_size();
2750         // We only need to invalidate in this case if the underlying platform
2751         // uses virtual surfaces.
2752         if virtual_surface_size > 0 {
2753             // Get the extremities of the tile grid after virtual offset is applied
2754             let tx0 = self.virtual_offset.x + x0 * self.current_tile_size.width;
2755             let ty0 = self.virtual_offset.y + y0 * self.current_tile_size.height;
2756             let tx1 = self.virtual_offset.x + (x1+1) * self.current_tile_size.width;
2757             let ty1 = self.virtual_offset.y + (y1+1) * self.current_tile_size.height;
2759             let need_new_virtual_offset = tx0 < 0 ||
2760                                           ty0 < 0 ||
2761                                           tx1 >= virtual_surface_size ||
2762                                           ty1 >= virtual_surface_size;
2764             if need_new_virtual_offset {
2765                 // Calculate a new virtual offset, centered around the middle of the
2766                 // current tile grid. This means we won't need to invalidate and get
2767                 // a new offset for a long time!
2768                 self.virtual_offset = DeviceIntPoint::new(
2769                     (virtual_surface_size/2) - ((x0 + x1) / 2) * self.current_tile_size.width,
2770                     (virtual_surface_size/2) - ((y0 + y1) / 2) * self.current_tile_size.height,
2771                 );
2773                 // Invalidate all native tile surfaces. They will be re-allocated next time
2774                 // they are scheduled to be rasterized.
2775                 for sub_slice in &mut self.sub_slices {
2776                     for tile in sub_slice.tiles.values_mut() {
2777                         if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface {
2778                             if let Some(id) = id.take() {
2779                                 frame_state.resource_cache.destroy_compositor_tile(id);
2780                                 tile.surface = None;
2781                                 // Invalidate the entire tile to force a redraw.
2782                                 // TODO(gw): Add a new invalidation reason for virtual offset changing
2783                                 tile.invalidate(None, InvalidationReason::CompositorKindChanged);
2784                             }
2785                         }
2786                     }
2788                     // Destroy the native virtual surfaces. They will be re-allocated next time a tile
2789                     // that references them is scheduled to draw.
2790                     if let Some(native_surface) = sub_slice.native_surface.take() {
2791                         frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque);
2792                         frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha);
2793                     }
2794                 }
2795             }
2796         }
2798         // Rebuild the tile grid if the picture cache rect has changed.
2799         if new_tile_rect != self.tile_rect {
2800             for sub_slice in &mut self.sub_slices {
2801                 let mut old_tiles = sub_slice.resize(new_tile_rect);
2803                 // When old tiles that remain after the loop, dirty rects are not valid.
2804                 if !old_tiles.is_empty() {
2805                     frame_state.composite_state.dirty_rects_are_valid = false;
2806                 }
2808                 // Any old tiles that remain after the loop above are going to be dropped. For
2809                 // simple composite mode, the texture cache handle will expire and be collected
2810                 // by the texture cache. For native compositor mode, we need to explicitly
2811                 // invoke a callback to the client to destroy that surface.
2812                 frame_state.composite_state.destroy_native_tiles(
2813                     old_tiles.values_mut(),
2814                     frame_state.resource_cache,
2815                 );
2816             }
2817         }
2819         // This is duplicated information from tile_rect, but cached here to avoid
2820         // redundant calculations during get_tile_coords_for_rect
2821         self.tile_bounds_p0 = TileOffset::new(x0, y0);
2822         self.tile_bounds_p1 = TileOffset::new(x1, y1);
2823         self.tile_rect = new_tile_rect;
2825         let mut world_culling_rect = WorldRect::zero();
2827         let mut ctx = TilePreUpdateContext {
2828             pic_to_world_mapper,
2829             fract_offset: self.fract_offset,
2830             device_fract_offset: self.device_fract_offset,
2831             background_color: self.background_color,
2832             global_screen_world_rect: frame_context.global_screen_world_rect,
2833             tile_size: self.tile_size,
2834             frame_id: self.frame_id,
2835         };
2837         // Pre-update each tile
2838         for sub_slice in &mut self.sub_slices {
2839             for tile in sub_slice.tiles.values_mut() {
2840                 tile.pre_update(&ctx);
2842                 // Only include the tiles that are currently in view into the world culling
2843                 // rect. This is a very important optimization for a couple of reasons:
2844                 // (1) Primitives that intersect with tiles in the grid that are not currently
2845                 //     visible can be skipped from primitive preparation, clip chain building
2846                 //     and tile dependency updates.
2847                 // (2) When we need to allocate an off-screen surface for a child picture (for
2848                 //     example a CSS filter) we clip the size of the GPU surface to the world
2849                 //     culling rect below (to ensure we draw enough of it to be sampled by any
2850                 //     tiles that reference it). Making the world culling rect only affected
2851                 //     by visible tiles (rather than the entire virtual tile display port) can
2852                 //     result in allocating _much_ smaller GPU surfaces for cases where the
2853                 //     true off-screen surface size is very large.
2854                 if tile.is_visible {
2855                     world_culling_rect = world_culling_rect.union(&tile.world_tile_rect);
2856                 }
2857             }
2859             // The background color can only be applied to the first sub-slice.
2860             ctx.background_color = None;
2861         }
2863         // If compositor mode is changed, need to drop all incompatible tiles.
2864         match frame_context.config.compositor_kind {
2865             CompositorKind::Draw { .. } => {
2866                 for sub_slice in &mut self.sub_slices {
2867                     for tile in sub_slice.tiles.values_mut() {
2868                         if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface {
2869                             if let Some(id) = id.take() {
2870                                 frame_state.resource_cache.destroy_compositor_tile(id);
2871                             }
2872                             tile.surface = None;
2873                             // Invalidate the entire tile to force a redraw.
2874                             tile.invalidate(None, InvalidationReason::CompositorKindChanged);
2875                         }
2876                     }
2878                     if let Some(native_surface) = sub_slice.native_surface.take() {
2879                         frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque);
2880                         frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha);
2881                     }
2882                 }
2884                 for (_, external_surface) in self.external_native_surface_cache.drain() {
2885                     frame_state.resource_cache.destroy_compositor_surface(external_surface.native_surface_id)
2886                 }
2887             }
2888             CompositorKind::Native { .. } => {
2889                 // This could hit even when compositor mode is not changed,
2890                 // then we need to check if there are incompatible tiles.
2891                 for sub_slice in &mut self.sub_slices {
2892                     for tile in sub_slice.tiles.values_mut() {
2893                         if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::TextureCache { .. }, .. }) = tile.surface {
2894                             tile.surface = None;
2895                             // Invalidate the entire tile to force a redraw.
2896                             tile.invalidate(None, InvalidationReason::CompositorKindChanged);
2897                         }
2898                     }
2899                 }
2900             }
2901         }
2903         world_culling_rect
2904     }
2906     fn can_promote_to_surface(
2907         &mut self,
2908         flags: PrimitiveFlags,
2909         prim_clip_chain: &ClipChainInstance,
2910         prim_spatial_node_index: SpatialNodeIndex,
2911         is_root_tile_cache: bool,
2912         sub_slice_index: usize,
2913         frame_context: &FrameVisibilityContext,
2914     ) -> SurfacePromotionResult {
2915         // Check if this primitive _wants_ to be promoted to a compositor surface.
2916         if !flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) {
2917             return SurfacePromotionResult::Failed;
2918         }
2920         // For now, only support a small (arbitrary) number of compositor surfaces.
2921         if sub_slice_index == MAX_COMPOSITOR_SURFACES {
2922             return SurfacePromotionResult::Failed;
2923         }
2925         // If a complex clip is being applied to this primitive, it can't be
2926         // promoted directly to a compositor surface (we might be able to
2927         // do this in limited cases in future, some native compositors do
2928         // support rounded rect clips, for example)
2929         if prim_clip_chain.needs_mask {
2930             return SurfacePromotionResult::Failed;
2931         }
2933         // If not on the root picture cache, it has some kind of
2934         // complex effect (such as a filter, mix-blend-mode or 3d transform).
2935         if !is_root_tile_cache {
2936             return SurfacePromotionResult::Failed;
2937         }
2939         let mapper : SpaceMapper<PicturePixel, WorldPixel> = SpaceMapper::new_with_target(
2940             ROOT_SPATIAL_NODE_INDEX,
2941             prim_spatial_node_index,
2942             frame_context.global_screen_world_rect,
2943             &frame_context.spatial_tree);
2944         let transform = mapper.get_transform();
2945         if !transform.is_2d_scale_translation() {
2946             return SurfacePromotionResult::Failed;
2947         }
2948         if transform.m11 < 0.0 {
2949             return SurfacePromotionResult::Failed;
2950         }
2952         if self.slice_flags.contains(SliceFlags::IS_BLEND_CONTAINER) {
2953             return SurfacePromotionResult::Failed;
2954         }
2956         SurfacePromotionResult::Success {
2957             flip_y: transform.m22 < 0.0,
2958         }
2959     }
2961     fn setup_compositor_surfaces_yuv(
2962         &mut self,
2963         sub_slice_index: usize,
2964         prim_info: &mut PrimitiveDependencyInfo,
2965         flags: PrimitiveFlags,
2966         local_prim_rect: LayoutRect,
2967         prim_spatial_node_index: SpatialNodeIndex,
2968         pic_clip_rect: PictureRect,
2969         frame_context: &FrameVisibilityContext,
2970         image_dependencies: &[ImageDependency;3],
2971         api_keys: &[ImageKey; 3],
2972         resource_cache: &mut ResourceCache,
2973         composite_state: &mut CompositeState,
2974         gpu_cache: &mut GpuCache,
2975         image_rendering: ImageRendering,
2976         color_depth: ColorDepth,
2977         color_space: YuvColorSpace,
2978         format: YuvFormat,
2979     ) -> bool {
2980         for &key in api_keys {
2981             // TODO: See comment in setup_compositor_surfaces_rgb.
2982             resource_cache.request_image(ImageRequest {
2983                     key,
2984                     rendering: image_rendering,
2985                     tile: None,
2986                 },
2987                 gpu_cache,
2988             );
2989         }
2991         self.setup_compositor_surfaces_impl(
2992             sub_slice_index,
2993             prim_info,
2994             flags,
2995             local_prim_rect,
2996             prim_spatial_node_index,
2997             pic_clip_rect,
2998             frame_context,
2999             ExternalSurfaceDependency::Yuv {
3000                 image_dependencies: *image_dependencies,
3001                 color_space,
3002                 format,
3003                 rescale: color_depth.rescaling_factor(),
3004             },
3005             api_keys,
3006             resource_cache,
3007             composite_state,
3008             image_rendering,
3009             true,
3010         )
3011     }
3013     fn setup_compositor_surfaces_rgb(
3014         &mut self,
3015         sub_slice_index: usize,
3016         prim_info: &mut PrimitiveDependencyInfo,
3017         flags: PrimitiveFlags,
3018         local_prim_rect: LayoutRect,
3019         prim_spatial_node_index: SpatialNodeIndex,
3020         pic_clip_rect: PictureRect,
3021         frame_context: &FrameVisibilityContext,
3022         image_dependency: ImageDependency,
3023         api_key: ImageKey,
3024         resource_cache: &mut ResourceCache,
3025         composite_state: &mut CompositeState,
3026         gpu_cache: &mut GpuCache,
3027         image_rendering: ImageRendering,
3028         flip_y: bool,
3029     ) -> bool {
3030         let mut api_keys = [ImageKey::DUMMY; 3];
3031         api_keys[0] = api_key;
3033         // TODO: The picture compositing code requires images promoted
3034         // into their own picture cache slices to be requested every
3035         // frame even if they are not visible. However the image updates
3036         // are only reached on the prepare pass for visible primitives.
3037         // So we make sure to trigger an image request when promoting
3038         // the image here.
3039         resource_cache.request_image(ImageRequest {
3040                 key: api_key,
3041                 rendering: image_rendering,
3042                 tile: None,
3043             },
3044             gpu_cache,
3045         );
3047         let is_opaque = resource_cache.get_image_properties(api_key)
3048             .map_or(false, |properties| properties.descriptor.is_opaque());
3050         self.setup_compositor_surfaces_impl(
3051             sub_slice_index,
3052             prim_info,
3053             flags,
3054             local_prim_rect,
3055             prim_spatial_node_index,
3056             pic_clip_rect,
3057             frame_context,
3058             ExternalSurfaceDependency::Rgb {
3059                 image_dependency,
3060                 flip_y,
3061             },
3062             &api_keys,
3063             resource_cache,
3064             composite_state,
3065             image_rendering,
3066             is_opaque,
3067         )
3068     }
3070     // returns false if composition is not available for this surface,
3071     // and the non-compositor path should be used to draw it instead.
3072     fn setup_compositor_surfaces_impl(
3073         &mut self,
3074         sub_slice_index: usize,
3075         prim_info: &mut PrimitiveDependencyInfo,
3076         flags: PrimitiveFlags,
3077         local_prim_rect: LayoutRect,
3078         prim_spatial_node_index: SpatialNodeIndex,
3079         pic_clip_rect: PictureRect,
3080         frame_context: &FrameVisibilityContext,
3081         dependency: ExternalSurfaceDependency,
3082         api_keys: &[ImageKey; 3],
3083         resource_cache: &mut ResourceCache,
3084         composite_state: &mut CompositeState,
3085         image_rendering: ImageRendering,
3086         is_opaque: bool,
3087     ) -> bool {
3088         let map_local_to_surface = SpaceMapper::new_with_target(
3089             self.spatial_node_index,
3090             prim_spatial_node_index,
3091             self.local_rect,
3092             frame_context.spatial_tree,
3093         );
3095         // Map the primitive local rect into picture space.
3096         let prim_rect = match map_local_to_surface.map(&local_prim_rect) {
3097             Some(rect) => rect,
3098             None => return true,
3099         };
3101         // If the rect is invalid, no need to create dependencies.
3102         if prim_rect.size.is_empty() {
3103             return true;
3104         }
3106         let pic_to_world_mapper = SpaceMapper::new_with_target(
3107             ROOT_SPATIAL_NODE_INDEX,
3108             self.spatial_node_index,
3109             frame_context.global_screen_world_rect,
3110             frame_context.spatial_tree,
3111         );
3113         let world_clip_rect = pic_to_world_mapper
3114             .map(&prim_info.prim_clip_box.to_rect())
3115             .expect("bug: unable to map clip to world space");
3117         let is_visible = world_clip_rect.intersects(&frame_context.global_screen_world_rect);
3118         if !is_visible {
3119             return true;
3120         }
3122         let world_rect = pic_to_world_mapper
3123             .map(&prim_rect)
3124             .expect("bug: unable to map the primitive to world space");
3125         let device_rect = (world_rect * frame_context.global_device_pixel_scale).round();
3127         // TODO(gw): Is there any case where if the primitive ends up on a fractional
3128         //           boundary we want to _skip_ promoting to a compositor surface and
3129         //           draw it as part of the content?
3130         let (surface_rect, transform) = match composite_state.compositor_kind {
3131             CompositorKind::Draw { .. } => {
3132                 (device_rect, Transform3D::identity())
3133             }
3134             CompositorKind::Native { .. } => {
3135                 // If we have a Native Compositor, then we can support doing the transformation
3136                 // as part of compositing. Use the local prim rect for the external surface, and
3137                 // compute the full local to device transform to provide to the compositor.
3138                 let surface_to_world_mapper : SpaceMapper<PicturePixel, WorldPixel> = SpaceMapper::new_with_target(
3139                     ROOT_SPATIAL_NODE_INDEX,
3140                     prim_spatial_node_index,
3141                     frame_context.global_screen_world_rect,
3142                     frame_context.spatial_tree,
3143                 );
3144                 let prim_origin = Vector3D::new(local_prim_rect.origin.x, local_prim_rect.origin.y, 0.0);
3145                 let world_to_device_scale = Transform3D::from_scale(frame_context.global_device_pixel_scale);
3146                 let transform = surface_to_world_mapper.get_transform().pre_translate(prim_origin).then(&world_to_device_scale);
3148                 (local_prim_rect.cast_unit(), transform)
3149             }
3150         };
3152         let clip_rect = (world_clip_rect * frame_context.global_device_pixel_scale).round();
3154         if surface_rect.size.width >= MAX_COMPOSITOR_SURFACES_SIZE ||
3155            surface_rect.size.height >= MAX_COMPOSITOR_SURFACES_SIZE {
3156                return false;
3157         }
3159         // If this primitive is an external image, and supports being used
3160         // directly by a native compositor, then lookup the external image id
3161         // so we can pass that through.
3162         let external_image_id = if flags.contains(PrimitiveFlags::SUPPORTS_EXTERNAL_COMPOSITOR_SURFACE) {
3163             resource_cache.get_image_properties(api_keys[0])
3164                 .and_then(|properties| properties.external_image)
3165                 .and_then(|image| Some(image.id))
3166         } else {
3167             None
3168         };
3170         // When using native compositing, we need to find an existing native surface
3171         // handle to use, or allocate a new one. For existing native surfaces, we can
3172         // also determine whether this needs to be updated, depending on whether the
3173         // image generation(s) of the planes have changed since last composite.
3174         let (native_surface_id, update_params) = match composite_state.compositor_kind {
3175             CompositorKind::Draw { .. } => {
3176                 (None, None)
3177             }
3178             CompositorKind::Native { .. } => {
3179                 let native_surface_size = surface_rect.size.round().to_i32();
3181                 let key = ExternalNativeSurfaceKey {
3182                     image_keys: *api_keys,
3183                     size: native_surface_size,
3184                     is_external_surface: external_image_id.is_some(),
3185                 };
3187                 let native_surface = self.external_native_surface_cache
3188                     .entry(key)
3189                     .or_insert_with(|| {
3190                         // No existing surface, so allocate a new compositor surface.
3191                         let native_surface_id = match external_image_id {
3192                             Some(_external_image) => {
3193                                 // If we have a suitable external image, then create an external
3194                                 // surface to attach to.
3195                                 resource_cache.create_compositor_external_surface(is_opaque)
3196                             }
3197                             None => {
3198                                 // Otherwise create a normal compositor surface and a single
3199                                 // compositor tile that covers the entire surface.
3200                                 let native_surface_id =
3201                                 resource_cache.create_compositor_surface(
3202                                     DeviceIntPoint::zero(),
3203                                     native_surface_size,
3204                                     is_opaque,
3205                                 );
3207                                 let tile_id = NativeTileId {
3208                                     surface_id: native_surface_id,
3209                                     x: 0,
3210                                     y: 0,
3211                                 };
3212                                 resource_cache.create_compositor_tile(tile_id);
3214                                 native_surface_id
3215                             }
3216                         };
3218                         ExternalNativeSurface {
3219                             used_this_frame: true,
3220                             native_surface_id,
3221                             image_dependencies: [ImageDependency::INVALID; 3],
3222                         }
3223                     });
3225                 // Mark that the surface is referenced this frame so that the
3226                 // backing native surface handle isn't freed.
3227                 native_surface.used_this_frame = true;
3229                 let update_params = match external_image_id {
3230                     Some(external_image) => {
3231                         // If this is an external image surface, then there's no update
3232                         // to be done. Just attach the current external image to the surface
3233                         // and we're done.
3234                         resource_cache.attach_compositor_external_image(
3235                             native_surface.native_surface_id,
3236                             external_image,
3237                         );
3238                         None
3239                     }
3240                     None => {
3241                         // If the image dependencies match, there is no need to update
3242                         // the backing native surface.
3243                         match dependency {
3244                             ExternalSurfaceDependency::Yuv{ image_dependencies, .. } => {
3245                                 if image_dependencies == native_surface.image_dependencies {
3246                                     None
3247                                 } else {
3248                                     Some(native_surface_size)
3249                                 }
3250                             },
3251                             ExternalSurfaceDependency::Rgb{ image_dependency, .. } => {
3252                                 if image_dependency == native_surface.image_dependencies[0] {
3253                                     None
3254                                 } else {
3255                                     Some(native_surface_size)
3256                                 }
3257                             },
3258                         }
3259                     }
3260                 };
3262                 (Some(native_surface.native_surface_id), update_params)
3263             }
3264         };
3266         // For compositor surfaces, if we didn't find an earlier sub-slice to add to,
3267         // we know we can append to the current slice.
3268         assert!(sub_slice_index < self.sub_slices.len() - 1);
3269         let sub_slice = &mut self.sub_slices[sub_slice_index];
3271         // Each compositor surface allocates a unique z-id
3272         sub_slice.compositor_surfaces.push(CompositorSurface {
3273             prohibited_rect: pic_clip_rect,
3274             is_opaque,
3275             descriptor: ExternalSurfaceDescriptor {
3276                 local_rect: prim_info.prim_clip_box.to_rect(),
3277                 local_clip_rect: prim_info.prim_clip_box.to_rect(),
3278                 dependency,
3279                 image_rendering,
3280                 device_rect,
3281                 surface_rect,
3282                 clip_rect,
3283                 transform: transform.cast_unit(),
3284                 z_id: ZBufferId::invalid(),
3285                 native_surface_id,
3286                 update_params,
3287             },
3288         });
3290         true
3291     }
3293     /// Update the dependencies for each tile for a given primitive instance.
3294     pub fn update_prim_dependencies(
3295         &mut self,
3296         prim_instance: &mut PrimitiveInstance,
3297         prim_spatial_node_index: SpatialNodeIndex,
3298         local_prim_rect: LayoutRect,
3299         frame_context: &FrameVisibilityContext,
3300         data_stores: &DataStores,
3301         clip_store: &ClipStore,
3302         pictures: &[PicturePrimitive],
3303         resource_cache: &mut ResourceCache,
3304         color_bindings: &ColorBindingStorage,
3305         surface_stack: &[SurfaceIndex],
3306         composite_state: &mut CompositeState,
3307         gpu_cache: &mut GpuCache,
3308         is_root_tile_cache: bool,
3309     ) {
3310         // This primitive exists on the last element on the current surface stack.
3311         profile_scope!("update_prim_dependencies");
3312         let prim_surface_index = *surface_stack.last().unwrap();
3313         let prim_clip_chain = &prim_instance.vis.clip_chain;
3315         // If the primitive is directly drawn onto this picture cache surface, then
3316         // the pic_clip_rect is in the same space. If not, we need to map it from
3317         // the surface space into the picture cache space.
3318         let on_picture_surface = prim_surface_index == self.surface_index;
3319         let pic_clip_rect = if on_picture_surface {
3320             prim_clip_chain.pic_clip_rect
3321         } else {
3322             // We want to get the rect in the tile cache surface space that this primitive
3323             // occupies, in order to enable correct invalidation regions. Each surface
3324             // that exists in the chain between this primitive and the tile cache surface
3325             // may have an arbitrary inflation factor (for example, in the case of a series
3326             // of nested blur elements). To account for this, step through the current
3327             // surface stack, mapping the primitive rect into each surface space, including
3328             // the inflation factor from each intermediate surface.
3329             let mut current_pic_clip_rect = prim_clip_chain.pic_clip_rect;
3330             let mut current_spatial_node_index = frame_context
3331                 .surfaces[prim_surface_index.0]
3332                 .surface_spatial_node_index;
3334             for surface_index in surface_stack.iter().rev() {
3335                 let surface = &frame_context.surfaces[surface_index.0];
3337                 let map_local_to_surface = SpaceMapper::new_with_target(
3338                     surface.surface_spatial_node_index,
3339                     current_spatial_node_index,
3340                     surface.rect,
3341                     frame_context.spatial_tree,
3342                 );
3344                 // Map the rect into the parent surface, and inflate if this surface requires
3345                 // it. If the rect can't be mapping (e.g. due to an invalid transform) then
3346                 // just bail out from the dependencies and cull this primitive.
3347                 current_pic_clip_rect = match map_local_to_surface.map(&current_pic_clip_rect) {
3348                     Some(rect) => {
3349                         rect.inflate(surface.inflation_factor, surface.inflation_factor)
3350                     }
3351                     None => {
3352                         return;
3353                     }
3354                 };
3356                 current_spatial_node_index = surface.surface_spatial_node_index;
3357             }
3359             current_pic_clip_rect
3360         };
3362         // Get the tile coordinates in the picture space.
3363         let (p0, p1) = self.get_tile_coords_for_rect(&pic_clip_rect);
3365         // If the primitive is outside the tiling rects, it's known to not
3366         // be visible.
3367         if p0.x == p1.x || p0.y == p1.y {
3368             return;
3369         }
3371         // Build the list of resources that this primitive has dependencies on.
3372         let mut prim_info = PrimitiveDependencyInfo::new(
3373             prim_instance.uid(),
3374             pic_clip_rect.to_box2d(),
3375         );
3377         let mut sub_slice_index = self.sub_slices.len() - 1;
3379         // Only need to evaluate sub-slice regions if we have compositor surfaces present
3380         if sub_slice_index > 0 {
3381             // Find the first sub-slice we can add this primitive to (we want to add
3382             // prims to the primary surface if possible, so they get subpixel AA).
3383             for (i, sub_slice) in self.sub_slices.iter_mut().enumerate() {
3384                 let mut intersects_prohibited_region = false;
3386                 for surface in &mut sub_slice.compositor_surfaces {
3387                     if pic_clip_rect.intersects(&surface.prohibited_rect) {
3388                         surface.prohibited_rect = surface.prohibited_rect.union(&pic_clip_rect);
3390                         intersects_prohibited_region = true;
3391                     }
3392                 }
3394                 if !intersects_prohibited_region {
3395                     sub_slice_index = i;
3396                     break;
3397                 }
3398             }
3399         }
3401         // Include the prim spatial node, if differs relative to cache root.
3402         if prim_spatial_node_index != self.spatial_node_index {
3403             prim_info.spatial_nodes.push(prim_spatial_node_index);
3404         }
3406         // If there was a clip chain, add any clip dependencies to the list for this tile.
3407         let clip_instances = &clip_store
3408             .clip_node_instances[prim_clip_chain.clips_range.to_range()];
3409         for clip_instance in clip_instances {
3410             prim_info.clips.push(clip_instance.handle.uid());
3412             // If the clip has the same spatial node, the relative transform
3413             // will always be the same, so there's no need to depend on it.
3414             if clip_instance.spatial_node_index != self.spatial_node_index
3415                 && !prim_info.spatial_nodes.contains(&clip_instance.spatial_node_index) {
3416                 prim_info.spatial_nodes.push(clip_instance.spatial_node_index);
3417             }
3418         }
3420         // Certain primitives may select themselves to be a backdrop candidate, which is
3421         // then applied below.
3422         let mut backdrop_candidate = None;
3424         // For pictures, we don't (yet) know the valid clip rect, so we can't correctly
3425         // use it to calculate the local bounding rect for the tiles. If we include them
3426         // then we may calculate a bounding rect that is too large, since it won't include
3427         // the clip bounds of the picture. Excluding them from the bounding rect here
3428         // fixes any correctness issues (the clips themselves are considered when we
3429         // consider the bounds of the primitives that are *children* of the picture),
3430         // however it does potentially result in some un-necessary invalidations of a
3431         // tile (in cases where the picture local rect affects the tile, but the clip
3432         // rect eventually means it doesn't affect that tile).
3433         // TODO(gw): Get picture clips earlier (during the initial picture traversal
3434         //           pass) so that we can calculate these correctly.
3435         match prim_instance.kind {
3436             PrimitiveInstanceKind::Picture { pic_index,.. } => {
3437                 // Pictures can depend on animated opacity bindings.
3438                 let pic = &pictures[pic_index.0];
3439                 if let Some(PictureCompositeMode::Filter(Filter::Opacity(binding, _))) = pic.requested_composite_mode {
3440                     prim_info.opacity_bindings.push(binding.into());
3441                 }
3442             }
3443             PrimitiveInstanceKind::Rectangle { data_handle, color_binding_index, .. } => {
3444                 // Rectangles can only form a backdrop candidate if they are known opaque.
3445                 // TODO(gw): We could resolve the opacity binding here, but the common
3446                 //           case for background rects is that they don't have animated opacity.
3447                 let color = match data_stores.prim[data_handle].kind {
3448                     PrimitiveTemplateKind::Rectangle { color, .. } => {
3449                         frame_context.scene_properties.resolve_color(&color)
3450                     }
3451                     _ => unreachable!(),
3452                 };
3453                 if color.a >= 1.0 {
3454                     backdrop_candidate = Some(BackdropInfo {
3455                         opaque_rect: pic_clip_rect,
3456                         kind: Some(BackdropKind::Color { color }),
3457                     });
3458                 }
3460                 if color_binding_index != ColorBindingIndex::INVALID {
3461                     prim_info.color_binding = Some(color_bindings[color_binding_index].into());
3462                 }
3463             }
3464             PrimitiveInstanceKind::Image { data_handle, ref mut is_compositor_surface, .. } => {
3465                 let image_key = &data_stores.image[data_handle];
3466                 let image_data = &image_key.kind;
3468                 let mut promote_to_surface = false;
3469                 let mut promote_with_flip_y = false;
3470                 match self.can_promote_to_surface(image_key.common.flags,
3471                                                   prim_clip_chain,
3472                                                   prim_spatial_node_index,
3473                                                   is_root_tile_cache,
3474                                                   sub_slice_index,
3475                                                   frame_context) {
3476                     SurfacePromotionResult::Failed => {
3477                     }
3478                     SurfacePromotionResult::Success{flip_y} => {
3479                         promote_to_surface = true;
3480                         promote_with_flip_y = flip_y;
3481                     }
3482                 }
3484                 // Native OS compositors (DC and CA, at least) support premultiplied alpha
3485                 // only. If we have an image that's not pre-multiplied alpha, we can't promote it.
3486                 if image_data.alpha_type == AlphaType::Alpha {
3487                     promote_to_surface = false;
3488                 }
3490                 if let Some(image_properties) = resource_cache.get_image_properties(image_data.key) {
3491                     // For an image to be a possible opaque backdrop, it must:
3492                     // - Have a valid, opaque image descriptor
3493                     // - Not use tiling (since they can fail to draw)
3494                     // - Not having any spacing / padding
3495                     if image_properties.descriptor.is_opaque() &&
3496                        image_properties.tiling.is_none() &&
3497                        image_data.tile_spacing == LayoutSize::zero() {
3498                         backdrop_candidate = Some(BackdropInfo {
3499                             opaque_rect: pic_clip_rect,
3500                             kind: None,
3501                         });
3502                     }
3503                 }
3505                 if promote_to_surface {
3506                     promote_to_surface = self.setup_compositor_surfaces_rgb(
3507                         sub_slice_index,
3508                         &mut prim_info,
3509                         image_key.common.flags,
3510                         local_prim_rect,
3511                         prim_spatial_node_index,
3512                         pic_clip_rect,
3513                         frame_context,
3514                         ImageDependency {
3515                             key: image_data.key,
3516                             generation: resource_cache.get_image_generation(image_data.key),
3517                         },
3518                         image_data.key,
3519                         resource_cache,
3520                         composite_state,
3521                         gpu_cache,
3522                         image_data.image_rendering,
3523                         promote_with_flip_y,
3524                     );
3525                 }
3527                 *is_compositor_surface = promote_to_surface;
3529                 if promote_to_surface {
3530                     prim_instance.vis.state = VisibilityState::Culled;
3531                     return;
3532                 } else {
3533                     prim_info.images.push(ImageDependency {
3534                         key: image_data.key,
3535                         generation: resource_cache.get_image_generation(image_data.key),
3536                     });
3537                 }
3538             }
3539             PrimitiveInstanceKind::YuvImage { data_handle, ref mut is_compositor_surface, .. } => {
3540                 let prim_data = &data_stores.yuv_image[data_handle];
3541                 let mut promote_to_surface = match self.can_promote_to_surface(
3542                                             prim_data.common.flags,
3543                                             prim_clip_chain,
3544                                             prim_spatial_node_index,
3545                                             is_root_tile_cache,
3546                                             sub_slice_index,
3547                                             frame_context) {
3548                     SurfacePromotionResult::Failed => false,
3549                     SurfacePromotionResult::Success{flip_y} => !flip_y,
3550                 };
3552                 // TODO(gw): When we support RGBA images for external surfaces, we also
3553                 //           need to check if opaque (YUV images are implicitly opaque).
3555                 // If this primitive is being promoted to a surface, construct an external
3556                 // surface descriptor for use later during batching and compositing. We only
3557                 // add the image keys for this primitive as a dependency if this is _not_
3558                 // a promoted surface, since we don't want the tiles to invalidate when the
3559                 // video content changes, if it's a compositor surface!
3560                 if promote_to_surface {
3561                     // Build dependency for each YUV plane, with current image generation for
3562                     // later detection of when the composited surface has changed.
3563                     let mut image_dependencies = [ImageDependency::INVALID; 3];
3564                     for (key, dep) in prim_data.kind.yuv_key.iter().cloned().zip(image_dependencies.iter_mut()) {
3565                         *dep = ImageDependency {
3566                             key,
3567                             generation: resource_cache.get_image_generation(key),
3568                         }
3569                     }
3571                     promote_to_surface = self.setup_compositor_surfaces_yuv(
3572                         sub_slice_index,
3573                         &mut prim_info,
3574                         prim_data.common.flags,
3575                         local_prim_rect,
3576                         prim_spatial_node_index,
3577                         pic_clip_rect,
3578                         frame_context,
3579                         &image_dependencies,
3580                         &prim_data.kind.yuv_key,
3581                         resource_cache,
3582                         composite_state,
3583                         gpu_cache,
3584                         prim_data.kind.image_rendering,
3585                         prim_data.kind.color_depth,
3586                         prim_data.kind.color_space,
3587                         prim_data.kind.format,
3588                     );
3589                 }
3591                 // Store on the YUV primitive instance whether this is a promoted surface.
3592                 // This is used by the batching code to determine whether to draw the
3593                 // image to the content tiles, or just a transparent z-write.
3594                 *is_compositor_surface = promote_to_surface;
3596                 if promote_to_surface {
3597                     prim_instance.vis.state = VisibilityState::Culled;
3598                     return;
3599                 } else {
3600                     prim_info.images.extend(
3601                         prim_data.kind.yuv_key.iter().map(|key| {
3602                             ImageDependency {
3603                                 key: *key,
3604                                 generation: resource_cache.get_image_generation(*key),
3605                             }
3606                         })
3607                     );
3608                 }
3609             }
3610             PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
3611                 let border_data = &data_stores.image_border[data_handle].kind;
3612                 prim_info.images.push(ImageDependency {
3613                     key: border_data.request.key,
3614                     generation: resource_cache.get_image_generation(border_data.request.key),
3615                 });
3616             }
3617             PrimitiveInstanceKind::Clear { .. } => {
3618                 backdrop_candidate = Some(BackdropInfo {
3619                     opaque_rect: pic_clip_rect,
3620                     kind: Some(BackdropKind::Clear),
3621                 });
3622             }
3623             PrimitiveInstanceKind::LinearGradient { data_handle, .. }
3624             | PrimitiveInstanceKind::CachedLinearGradient { data_handle, .. } => {
3625                 let gradient_data = &data_stores.linear_grad[data_handle];
3626                 if gradient_data.stops_opacity.is_opaque
3627                     && gradient_data.tile_spacing == LayoutSize::zero()
3628                 {
3629                     backdrop_candidate = Some(BackdropInfo {
3630                         opaque_rect: pic_clip_rect,
3631                         kind: None,
3632                     });
3633                 }
3634             }
3635             PrimitiveInstanceKind::ConicGradient { data_handle, .. } => {
3636                 let gradient_data = &data_stores.conic_grad[data_handle];
3637                 if gradient_data.stops_opacity.is_opaque
3638                     && gradient_data.tile_spacing == LayoutSize::zero()
3639                 {
3640                     backdrop_candidate = Some(BackdropInfo {
3641                         opaque_rect: pic_clip_rect,
3642                         kind: None,
3643                     });
3644                 }
3645             }
3646             PrimitiveInstanceKind::RadialGradient { data_handle, .. } => {
3647                 let gradient_data = &data_stores.radial_grad[data_handle];
3648                 if gradient_data.stops_opacity.is_opaque
3649                     && gradient_data.tile_spacing == LayoutSize::zero()
3650                 {
3651                     backdrop_candidate = Some(BackdropInfo {
3652                         opaque_rect: pic_clip_rect,
3653                         kind: None,
3654                     });
3655                 }
3656             }
3657             PrimitiveInstanceKind::LineDecoration { .. } |
3658             PrimitiveInstanceKind::NormalBorder { .. } |
3659             PrimitiveInstanceKind::TextRun { .. } |
3660             PrimitiveInstanceKind::Backdrop { .. } => {
3661                 // These don't contribute dependencies
3662             }
3663         };
3665         // If this primitive considers itself a backdrop candidate, apply further
3666         // checks to see if it matches all conditions to be a backdrop.
3667         let mut vis_flags = PrimitiveVisibilityFlags::empty();
3669         let sub_slice = &mut self.sub_slices[sub_slice_index];
3671         if let Some(backdrop_candidate) = backdrop_candidate {
3672             let is_suitable_backdrop = match backdrop_candidate.kind {
3673                 Some(BackdropKind::Clear) => {
3674                     // Clear prims are special - they always end up in their own slice,
3675                     // and always set the backdrop. In future, we hope to completely
3676                     // remove clear prims, since they don't integrate with the compositing
3677                     // system cleanly.
3678                     true
3679                 }
3680                 Some(BackdropKind::Color { .. }) | None => {
3681                     // Check a number of conditions to see if we can consider this
3682                     // primitive as an opaque backdrop rect. Several of these are conservative
3683                     // checks and could be relaxed in future. However, these checks
3684                     // are quick and capture the common cases of background rects and images.
3685                     // Specifically, we currently require:
3686                     //  - The primitive is on the main picture cache surface.
3687                     //  - Same coord system as picture cache (ensures rects are axis-aligned).
3688                     //  - No clip masks exist.
3689                     let same_coord_system = {
3690                         let prim_spatial_node = &frame_context.spatial_tree
3691                             .spatial_nodes[prim_spatial_node_index.0 as usize];
3692                         let surface_spatial_node = &frame_context.spatial_tree
3693                             .spatial_nodes[self.spatial_node_index.0 as usize];
3695                         prim_spatial_node.coordinate_system_id == surface_spatial_node.coordinate_system_id
3696                     };
3698                     same_coord_system && on_picture_surface
3699                 }
3700             };
3702             if sub_slice_index == 0 &&
3703                is_suitable_backdrop &&
3704                sub_slice.compositor_surfaces.is_empty() &&
3705                !prim_clip_chain.needs_mask {
3707                 if backdrop_candidate.opaque_rect.contains_rect(&self.backdrop.opaque_rect) {
3708                     self.backdrop.opaque_rect = backdrop_candidate.opaque_rect;
3709                 }
3711                 if let Some(kind) = backdrop_candidate.kind {
3712                     if backdrop_candidate.opaque_rect.contains_rect(&self.local_rect) {
3713                         // If we have a color backdrop, mark the visibility flags
3714                         // of the primitive so it is skipped during batching (and
3715                         // also clears any previous primitives).
3716                         if let BackdropKind::Color { .. } = kind {
3717                             vis_flags |= PrimitiveVisibilityFlags::IS_BACKDROP;
3718                         }
3720                         self.backdrop.kind = Some(kind);
3721                     }
3722                 }
3723             }
3724         }
3726         // Record any new spatial nodes in the used list.
3727         for spatial_node_index in &prim_info.spatial_nodes {
3728             self.spatial_node_comparer.register_used_transform(
3729                 *spatial_node_index,
3730                 self.frame_id,
3731                 frame_context.spatial_tree,
3732             );
3733         }
3735         // Truncate the lengths of dependency arrays to the max size we can handle.
3736         // Any arrays this size or longer will invalidate every frame.
3737         prim_info.clips.truncate(MAX_PRIM_SUB_DEPS);
3738         prim_info.opacity_bindings.truncate(MAX_PRIM_SUB_DEPS);
3739         prim_info.spatial_nodes.truncate(MAX_PRIM_SUB_DEPS);
3740         prim_info.images.truncate(MAX_PRIM_SUB_DEPS);
3742         // Normalize the tile coordinates before adding to tile dependencies.
3743         // For each affected tile, mark any of the primitive dependencies.
3744         for y in p0.y .. p1.y {
3745             for x in p0.x .. p1.x {
3746                 // TODO(gw): Convert to 2d array temporarily to avoid hash lookups per-tile?
3747                 let key = TileOffset::new(x, y);
3748                 let tile = sub_slice.tiles.get_mut(&key).expect("bug: no tile");
3750                 tile.add_prim_dependency(&prim_info);
3751             }
3752         }
3754         prim_instance.vis.state = VisibilityState::Coarse {
3755             filter: BatchFilter {
3756                 rect_in_pic_space: pic_clip_rect,
3757                 sub_slice_index: SubSliceIndex::new(sub_slice_index),
3758             },
3759             vis_flags,
3760         };
3761     }
3763     /// Print debug information about this picture cache to a tree printer.
3764     fn print(&self) {
3765         // TODO(gw): This initial implementation is very basic - just printing
3766         //           the picture cache state to stdout. In future, we can
3767         //           make this dump each frame to a file, and produce a report
3768         //           stating which frames had invalidations. This will allow
3769         //           diff'ing the invalidation states in a visual tool.
3770         let mut pt = PrintTree::new("Picture Cache");
3772         pt.new_level(format!("Slice {:?}", self.slice));
3774         pt.add_item(format!("fract_offset: {:?}", self.fract_offset));
3775         pt.add_item(format!("background_color: {:?}", self.background_color));
3777         for (sub_slice_index, sub_slice) in self.sub_slices.iter().enumerate() {
3778             pt.new_level(format!("SubSlice {:?}", sub_slice_index));
3780             for y in self.tile_bounds_p0.y .. self.tile_bounds_p1.y {
3781                 for x in self.tile_bounds_p0.x .. self.tile_bounds_p1.x {
3782                     let key = TileOffset::new(x, y);
3783                     let tile = &sub_slice.tiles[&key];
3784                     tile.print(&mut pt);
3785                 }
3786             }
3788             pt.end_level();
3789         }
3791         pt.end_level();
3792     }
3794     fn calculate_subpixel_mode(&self) -> SubpixelMode {
3795         let has_opaque_bg_color = self.background_color.map_or(false, |c| c.a >= 1.0);
3797         // If the overall tile cache is known opaque, subpixel AA is allowed everywhere
3798         if has_opaque_bg_color {
3799             return SubpixelMode::Allow;
3800         }
3802         // If we didn't find any valid opaque backdrop, no subpixel AA allowed
3803         if self.backdrop.opaque_rect.is_empty() {
3804             return SubpixelMode::Deny;
3805         }
3807         // If the opaque backdrop rect covers the entire tile cache surface,
3808         // we can allow subpixel AA anywhere, skipping the per-text-run tests
3809         // later on during primitive preparation.
3810         if self.backdrop.opaque_rect.contains_rect(&self.local_rect) {
3811             return SubpixelMode::Allow;
3812         }
3814         // If none of the simple cases above match, we need test where we can support subpixel AA.
3815         // TODO(gw): In future, it may make sense to have > 1 inclusion rect,
3816         //           but this handles the common cases.
3817         // TODO(gw): If a text run gets animated such that it's moving in a way that is
3818         //           sometimes intersecting with the video rect, this can result in subpixel
3819         //           AA flicking on/off for that text run. It's probably very rare, but
3820         //           something we should handle in future.
3821         SubpixelMode::Conditional {
3822             allowed_rect: self.backdrop.opaque_rect,
3823         }
3824     }
3826     /// Apply any updates after prim dependency updates. This applies
3827     /// any late tile invalidations, and sets up the dirty rect and
3828     /// set of tile blits.
3829     pub fn post_update(
3830         &mut self,
3831         frame_context: &FrameVisibilityContext,
3832         frame_state: &mut FrameVisibilityState,
3833     ) {
3834         self.dirty_region.reset(self.spatial_node_index);
3835         self.subpixel_mode = self.calculate_subpixel_mode();
3837         let map_pic_to_world = SpaceMapper::new_with_target(
3838             ROOT_SPATIAL_NODE_INDEX,
3839             self.spatial_node_index,
3840             frame_context.global_screen_world_rect,
3841             frame_context.spatial_tree,
3842         );
3844         // Register the opaque region of this tile cache as an occluder, which
3845         // is used later in the frame to occlude other tiles.
3846         if !self.backdrop.opaque_rect.is_empty() {
3847             let backdrop_rect = self.backdrop.opaque_rect
3848                 .intersection(&self.local_rect)
3849                 .and_then(|r| {
3850                     r.intersection(&self.local_clip_rect)
3851                 });
3853             if let Some(backdrop_rect) = backdrop_rect {
3854                 let world_backdrop_rect = map_pic_to_world
3855                     .map(&backdrop_rect)
3856                     .expect("bug: unable to map backdrop to world space");
3858                 // Since we register the entire backdrop rect, use the opaque z-id for the
3859                 // picture cache slice.
3860                 frame_state.composite_state.register_occluder(
3861                     self.z_id_backdrop,
3862                     world_backdrop_rect,
3863                 );
3864             }
3865         }
3867         // A simple GC of the native external surface cache, to remove and free any
3868         // surfaces that were not referenced during the update_prim_dependencies pass.
3869         self.external_native_surface_cache.retain(|_, surface| {
3870             if !surface.used_this_frame {
3871                 // If we removed an external surface, we need to mark the dirty rects as
3872                 // invalid so a full composite occurs on the next frame.
3873                 frame_state.composite_state.dirty_rects_are_valid = false;
3875                 frame_state.resource_cache.destroy_compositor_surface(surface.native_surface_id);
3876             }
3878             surface.used_this_frame
3879         });
3881         // Detect if the picture cache was scrolled or scaled. In this case,
3882         // the device space dirty rects aren't applicable (until we properly
3883         // integrate with OS compositors that can handle scrolling slices).
3884         let root_transform = frame_context
3885             .spatial_tree
3886             .get_relative_transform(
3887                 self.spatial_node_index,
3888                 ROOT_SPATIAL_NODE_INDEX,
3889             );
3890         let root_transform = match root_transform {
3891             CoordinateSpaceMapping::Local => ScaleOffset::identity(),
3892             CoordinateSpaceMapping::ScaleOffset(scale_offset) => scale_offset,
3893             CoordinateSpaceMapping::Transform(..) => panic!("bug: picture caches don't support complex transforms"),
3894         };
3895         const EPSILON: f32 = 0.001;
3896         let root_translation_changed =
3897             !root_transform.offset.x.approx_eq_eps(&self.root_transform.offset.x, &EPSILON) ||
3898             !root_transform.offset.y.approx_eq_eps(&self.root_transform.offset.y, &EPSILON);
3899         let root_scale_changed =
3900             !root_transform.scale.x.approx_eq_eps(&self.root_transform.scale.x, &EPSILON) ||
3901             !root_transform.scale.y.approx_eq_eps(&self.root_transform.scale.y, &EPSILON);
3903         if root_translation_changed || root_scale_changed || frame_context.config.force_invalidation {
3904             self.root_transform = root_transform;
3905             frame_state.composite_state.dirty_rects_are_valid = false;
3906         }
3908         let pic_to_world_mapper = SpaceMapper::new_with_target(
3909             ROOT_SPATIAL_NODE_INDEX,
3910             self.spatial_node_index,
3911             frame_context.global_screen_world_rect,
3912             frame_context.spatial_tree,
3913         );
3915         let mut ctx = TilePostUpdateContext {
3916             pic_to_world_mapper,
3917             global_device_pixel_scale: frame_context.global_device_pixel_scale,
3918             local_clip_rect: self.local_clip_rect,
3919             backdrop: Some(self.backdrop),
3920             opacity_bindings: &self.opacity_bindings,
3921             color_bindings: &self.color_bindings,
3922             current_tile_size: self.current_tile_size,
3923             local_rect: self.local_rect,
3924             z_id: self.z_id_backdrop,
3925             invalidate_all: root_scale_changed || frame_context.config.force_invalidation,
3926         };
3928         let mut state = TilePostUpdateState {
3929             resource_cache: frame_state.resource_cache,
3930             composite_state: frame_state.composite_state,
3931             compare_cache: &mut self.compare_cache,
3932             spatial_node_comparer: &mut self.spatial_node_comparer,
3933         };
3935         // Step through each tile and invalidate if the dependencies have changed. Determine
3936         // the current opacity setting and whether it's changed.
3937         for sub_slice in &mut self.sub_slices {
3938             for tile in sub_slice.tiles.values_mut() {
3939                 tile.post_update(&ctx, &mut state, frame_context);
3940             }
3942             for compositor_surface in &mut sub_slice.compositor_surfaces {
3943                 compositor_surface.descriptor.z_id = state.composite_state.z_generator.next();
3944             }
3946             // After the first sub-slice, the backdrop is no longer relevant
3947             ctx.backdrop = None;
3948             ctx.z_id = state.composite_state.z_generator.next();
3949         }
3951         // Register any opaque external compositor surfaces as potential occluders. This
3952         // is especially useful when viewing video in full-screen mode, as it is
3953         // able to occlude every background tile (avoiding allocation, rasterizion
3954         // and compositing).
3956         for sub_slice in &self.sub_slices {
3957             for compositor_surface in &sub_slice.compositor_surfaces {
3958                 if compositor_surface.is_opaque {
3959                     let local_surface_rect = compositor_surface
3960                         .descriptor
3961                         .local_rect
3962                         .intersection(&compositor_surface.descriptor.local_clip_rect)
3963                         .and_then(|r| {
3964                             r.intersection(&self.local_clip_rect)
3965                         });
3967                     if let Some(local_surface_rect) = local_surface_rect {
3968                         let world_surface_rect = map_pic_to_world
3969                             .map(&local_surface_rect)
3970                             .expect("bug: unable to map external surface to world space");
3972                         frame_state.composite_state.register_occluder(
3973                             compositor_surface.descriptor.z_id,
3974                             world_surface_rect,
3975                         );
3976                     }
3977                 }
3978             }
3979         }
3980     }
3983 pub struct PictureScratchBuffer {
3984     surface_stack: Vec<SurfaceIndex>,
3985     clip_chain_ids: Vec<ClipChainId>,
3988 impl Default for PictureScratchBuffer {
3989     fn default() -> Self {
3990         PictureScratchBuffer {
3991             surface_stack: Vec::new(),
3992             clip_chain_ids: Vec::new(),
3993         }
3994     }
3997 impl PictureScratchBuffer {
3998     pub fn begin_frame(&mut self) {
3999         self.surface_stack.clear();
4000         self.clip_chain_ids.clear();
4001     }
4003     pub fn recycle(&mut self, recycler: &mut Recycler) {
4004         recycler.recycle_vec(&mut self.surface_stack);
4005     }
4008 /// Maintains a stack of picture and surface information, that
4009 /// is used during the initial picture traversal.
4010 pub struct PictureUpdateState<'a> {
4011     surfaces: &'a mut Vec<SurfaceInfo>,
4012     surface_stack: Vec<SurfaceIndex>,
4015 impl<'a> PictureUpdateState<'a> {
4016     pub fn update_all(
4017         buffers: &mut PictureScratchBuffer,
4018         surfaces: &'a mut Vec<SurfaceInfo>,
4019         pic_index: PictureIndex,
4020         picture_primitives: &mut [PicturePrimitive],
4021         frame_context: &FrameBuildingContext,
4022         gpu_cache: &mut GpuCache,
4023         clip_store: &ClipStore,
4024         data_stores: &mut DataStores,
4025     ) {
4026         profile_scope!("UpdatePictures");
4027         profile_marker!("UpdatePictures");
4029         let mut state = PictureUpdateState {
4030             surfaces,
4031             surface_stack: buffers.surface_stack.take().cleared(),
4032         };
4034         state.surface_stack.push(SurfaceIndex(0));
4036         state.update(
4037             pic_index,
4038             picture_primitives,
4039             frame_context,
4040             gpu_cache,
4041             clip_store,
4042             data_stores,
4043         );
4045         buffers.surface_stack = state.surface_stack.take();
4046     }
4048     /// Return the current surface
4049     fn current_surface(&self) -> &SurfaceInfo {
4050         &self.surfaces[self.surface_stack.last().unwrap().0]
4051     }
4053     /// Return the current surface (mutable)
4054     fn current_surface_mut(&mut self) -> &mut SurfaceInfo {
4055         &mut self.surfaces[self.surface_stack.last().unwrap().0]
4056     }
4058     /// Push a new surface onto the update stack.
4059     fn push_surface(
4060         &mut self,
4061         surface: SurfaceInfo,
4062     ) -> SurfaceIndex {
4063         let surface_index = SurfaceIndex(self.surfaces.len());
4064         self.surfaces.push(surface);
4065         self.surface_stack.push(surface_index);
4066         surface_index
4067     }
4069     /// Pop a surface on the way up the picture traversal
4070     fn pop_surface(&mut self) -> SurfaceIndex{
4071         self.surface_stack.pop().unwrap()
4072     }
4074     /// Update a picture, determining surface configuration,
4075     /// rasterization roots, and (in future) whether there
4076     /// are cached surfaces that can be used by this picture.
4077     fn update(
4078         &mut self,
4079         pic_index: PictureIndex,
4080         picture_primitives: &mut [PicturePrimitive],
4081         frame_context: &FrameBuildingContext,
4082         gpu_cache: &mut GpuCache,
4083         clip_store: &ClipStore,
4084         data_stores: &mut DataStores,
4085     ) {
4086         if let Some(prim_list) = picture_primitives[pic_index.0].pre_update(
4087             self,
4088             frame_context,
4089         ) {
4090             for child_pic_index in &prim_list.child_pictures {
4091                 self.update(
4092                     *child_pic_index,
4093                     picture_primitives,
4094                     frame_context,
4095                     gpu_cache,
4096                     clip_store,
4097                     data_stores,
4098                 );
4099             }
4101             picture_primitives[pic_index.0].post_update(
4102                 prim_list,
4103                 self,
4104                 frame_context,
4105                 data_stores,
4106             );
4107         }
4108     }
4111 #[derive(Debug, Copy, Clone, PartialEq)]
4112 #[cfg_attr(feature = "capture", derive(Serialize))]
4113 pub struct SurfaceIndex(pub usize);
4115 pub const ROOT_SURFACE_INDEX: SurfaceIndex = SurfaceIndex(0);
4117 /// Describes the render task configuration for a picture surface.
4118 #[derive(Debug)]
4119 pub enum SurfaceRenderTasks {
4120     /// The common type of surface is a single render task
4121     Simple(RenderTaskId),
4122     /// Some surfaces draw their content, and then have further tasks applied
4123     /// to that input (such as blur passes for shadows). These tasks have a root
4124     /// (the output of the surface), and a port (for attaching child task dependencies
4125     /// to the content).
4126     Chained { root_task_id: RenderTaskId, port_task_id: RenderTaskId },
4127     /// Picture caches are a single surface consisting of multiple render
4128     /// tasks, one per tile with dirty content.
4129     Tiled(Vec<RenderTaskId>),
4132 /// Information about an offscreen surface. For now,
4133 /// it contains information about the size and coordinate
4134 /// system of the surface. In the future, it will contain
4135 /// information about the contents of the surface, which
4136 /// will allow surfaces to be cached / retained between
4137 /// frames and display lists.
4138 #[derive(Debug)]
4139 pub struct SurfaceInfo {
4140     /// A local rect defining the size of this surface, in the
4141     /// coordinate system of the surface itself.
4142     pub rect: PictureRect,
4143     /// Part of the surface that we know to be opaque.
4144     pub opaque_rect: PictureRect,
4145     /// Helper structs for mapping local rects in different
4146     /// coordinate systems into the surface coordinates.
4147     pub map_local_to_surface: SpaceMapper<LayoutPixel, PicturePixel>,
4148     /// Defines the positioning node for the surface itself,
4149     /// and the rasterization root for this surface.
4150     pub raster_spatial_node_index: SpatialNodeIndex,
4151     pub surface_spatial_node_index: SpatialNodeIndex,
4152     /// This is set when the render task is created.
4153     pub render_tasks: Option<SurfaceRenderTasks>,
4154     /// How much the local surface rect should be inflated (for blur radii).
4155     pub inflation_factor: f32,
4156     /// The device pixel ratio specific to this surface.
4157     pub device_pixel_scale: DevicePixelScale,
4158     /// The scale factors of the surface to raster transform.
4159     pub scale_factors: (f32, f32),
4160     /// The allocated device rect for this surface
4161     pub device_rect: Option<DeviceRect>,
4164 impl SurfaceInfo {
4165     pub fn new(
4166         surface_spatial_node_index: SpatialNodeIndex,
4167         raster_spatial_node_index: SpatialNodeIndex,
4168         inflation_factor: f32,
4169         world_rect: WorldRect,
4170         spatial_tree: &SpatialTree,
4171         device_pixel_scale: DevicePixelScale,
4172         scale_factors: (f32, f32),
4173     ) -> Self {
4174         let map_surface_to_world = SpaceMapper::new_with_target(
4175             ROOT_SPATIAL_NODE_INDEX,
4176             surface_spatial_node_index,
4177             world_rect,
4178             spatial_tree,
4179         );
4181         let pic_bounds = map_surface_to_world
4182             .unmap(&map_surface_to_world.bounds)
4183             .unwrap_or_else(PictureRect::max_rect);
4185         let map_local_to_surface = SpaceMapper::new(
4186             surface_spatial_node_index,
4187             pic_bounds,
4188         );
4190         SurfaceInfo {
4191             rect: PictureRect::zero(),
4192             opaque_rect: PictureRect::zero(),
4193             map_local_to_surface,
4194             render_tasks: None,
4195             raster_spatial_node_index,
4196             surface_spatial_node_index,
4197             inflation_factor,
4198             device_pixel_scale,
4199             scale_factors,
4200             device_rect: None,
4201         }
4202     }
4204     pub fn get_device_rect(&self) -> DeviceRect {
4205         self.device_rect.expect("bug: queried before surface was initialized")
4206     }
4209 #[derive(Debug)]
4210 #[cfg_attr(feature = "capture", derive(Serialize))]
4211 pub struct RasterConfig {
4212     /// How this picture should be composited into
4213     /// the parent surface.
4214     pub composite_mode: PictureCompositeMode,
4215     /// Index to the surface descriptor for this
4216     /// picture.
4217     pub surface_index: SurfaceIndex,
4218     /// Whether this picture establishes a rasterization root.
4219     pub establishes_raster_root: bool,
4220     /// Scaling factor applied to fit within MAX_SURFACE_SIZE when
4221     /// establishing a raster root.
4222     /// Most code doesn't need to know about it, since it is folded
4223     /// into device_pixel_scale when the rendertask is set up.
4224     /// However e.g. text rasterization uses it to ensure consistent
4225     /// on-screen font size.
4226     pub root_scaling_factor: f32,
4227     /// The world rect of this picture clipped to the current culling
4228     /// rect. This is used for determining the size of the render
4229     /// target rect for this surface, and calculating raster scale
4230     /// factors.
4231     pub clipped_bounding_rect: WorldRect,
4234 bitflags! {
4235     /// A set of flags describing why a picture may need a backing surface.
4236     #[cfg_attr(feature = "capture", derive(Serialize))]
4237     pub struct BlitReason: u32 {
4238         /// Mix-blend-mode on a child that requires isolation.
4239         const ISOLATE = 1;
4240         /// Clip node that _might_ require a surface.
4241         const CLIP = 2;
4242         /// Preserve-3D requires a surface for plane-splitting.
4243         const PRESERVE3D = 4;
4244         /// A backdrop that is reused which requires a surface.
4245         const BACKDROP = 8;
4246     }
4249 /// Specifies how this Picture should be composited
4250 /// onto the target it belongs to.
4251 #[allow(dead_code)]
4252 #[derive(Debug, Clone)]
4253 #[cfg_attr(feature = "capture", derive(Serialize))]
4254 pub enum PictureCompositeMode {
4255     /// Apply CSS mix-blend-mode effect.
4256     MixBlend(MixBlendMode),
4257     /// Apply a CSS filter (except component transfer).
4258     Filter(Filter),
4259     /// Apply a component transfer filter.
4260     ComponentTransferFilter(FilterDataHandle),
4261     /// Draw to intermediate surface, copy straight across. This
4262     /// is used for CSS isolation, and plane splitting.
4263     Blit(BlitReason),
4264     /// Used to cache a picture as a series of tiles.
4265     TileCache {
4266         slice_id: SliceId,
4267     },
4268     /// Apply an SVG filter
4269     SvgFilter(Vec<FilterPrimitive>, Vec<SFilterData>),
4272 impl PictureCompositeMode {
4273     pub fn inflate_picture_rect(&self, picture_rect: PictureRect, scale_factors: (f32, f32)) -> PictureRect {
4274         let mut result_rect = picture_rect;
4275         match self {
4276             PictureCompositeMode::Filter(filter) => match filter {
4277                 Filter::Blur(width, height) => {
4278                     let width_factor = clamp_blur_radius(*width, scale_factors).ceil() * BLUR_SAMPLE_SCALE;
4279                     let height_factor = clamp_blur_radius(*height, scale_factors).ceil() * BLUR_SAMPLE_SCALE;
4280                     result_rect = picture_rect.inflate(width_factor, height_factor);
4281                 },
4282                 Filter::DropShadows(shadows) => {
4283                     let mut max_inflation: f32 = 0.0;
4284                     for shadow in shadows {
4285                         max_inflation = max_inflation.max(shadow.blur_radius);
4286                     }
4287                     max_inflation = clamp_blur_radius(max_inflation, scale_factors).ceil() * BLUR_SAMPLE_SCALE;
4288                     result_rect = picture_rect.inflate(max_inflation, max_inflation);
4289                 },
4290                 _ => {}
4291             }
4292             PictureCompositeMode::SvgFilter(primitives, _) => {
4293                 let mut output_rects = Vec::with_capacity(primitives.len());
4294                 for (cur_index, primitive) in primitives.iter().enumerate() {
4295                     let output_rect = match primitive.kind {
4296                         FilterPrimitiveKind::Blur(ref primitive) => {
4297                             let input = primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect);
4298                             let width_factor = primitive.width.round() * BLUR_SAMPLE_SCALE;
4299                             let height_factor = primitive.height.round() * BLUR_SAMPLE_SCALE;
4300                             input.inflate(width_factor, height_factor)
4301                         }
4302                         FilterPrimitiveKind::DropShadow(ref primitive) => {
4303                             let inflation_factor = primitive.shadow.blur_radius.ceil() * BLUR_SAMPLE_SCALE;
4304                             let input = primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect);
4305                             let shadow_rect = input.inflate(inflation_factor, inflation_factor);
4306                             input.union(&shadow_rect.translate(primitive.shadow.offset * Scale::new(1.0)))
4307                         }
4308                         FilterPrimitiveKind::Blend(ref primitive) => {
4309                             primitive.input1.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect)
4310                                 .union(&primitive.input2.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect))
4311                         }
4312                         FilterPrimitiveKind::Composite(ref primitive) => {
4313                             primitive.input1.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect)
4314                                 .union(&primitive.input2.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect))
4315                         }
4316                         FilterPrimitiveKind::Identity(ref primitive) =>
4317                             primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect),
4318                         FilterPrimitiveKind::Opacity(ref primitive) =>
4319                             primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect),
4320                         FilterPrimitiveKind::ColorMatrix(ref primitive) =>
4321                             primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect),
4322                         FilterPrimitiveKind::ComponentTransfer(ref primitive) =>
4323                             primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect),
4324                         FilterPrimitiveKind::Offset(ref primitive) => {
4325                             let input_rect = primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect);
4326                             input_rect.translate(primitive.offset * Scale::new(1.0))
4327                         },
4329                         FilterPrimitiveKind::Flood(..) => picture_rect,
4330                     };
4331                     output_rects.push(output_rect);
4332                     result_rect = result_rect.union(&output_rect);
4333                 }
4334             }
4335             _ => {},
4336         }
4337         result_rect
4338     }
4341 /// Enum value describing the place of a picture in a 3D context.
4342 #[derive(Clone, Debug)]
4343 #[cfg_attr(feature = "capture", derive(Serialize))]
4344 pub enum Picture3DContext<C> {
4345     /// The picture is not a part of 3D context sub-hierarchy.
4346     Out,
4347     /// The picture is a part of 3D context.
4348     In {
4349         /// Additional data per child for the case of this a root of 3D hierarchy.
4350         root_data: Option<Vec<C>>,
4351         /// The spatial node index of an "ancestor" element, i.e. one
4352         /// that establishes the transformed element's containing block.
4353         ///
4354         /// See CSS spec draft for more details:
4355         /// https://drafts.csswg.org/css-transforms-2/#accumulated-3d-transformation-matrix-computation
4356         ancestor_index: SpatialNodeIndex,
4357     },
4360 /// Information about a preserve-3D hierarchy child that has been plane-split
4361 /// and ordered according to the view direction.
4362 #[derive(Clone, Debug)]
4363 #[cfg_attr(feature = "capture", derive(Serialize))]
4364 pub struct OrderedPictureChild {
4365     pub anchor: PlaneSplitAnchor,
4366     pub spatial_node_index: SpatialNodeIndex,
4367     pub gpu_address: GpuCacheAddress,
4370 bitflags! {
4371     /// A set of flags describing why a picture may need a backing surface.
4372     #[cfg_attr(feature = "capture", derive(Serialize))]
4373     pub struct ClusterFlags: u32 {
4374         /// Whether this cluster is visible when the position node is a backface.
4375         const IS_BACKFACE_VISIBLE = 1;
4376         /// This flag is set during the first pass picture traversal, depending on whether
4377         /// the cluster is visible or not. It's read during the second pass when primitives
4378         /// consult their owning clusters to see if the primitive itself is visible.
4379         const IS_VISIBLE = 2;
4380         /// Is a backdrop-filter cluster that requires special handling during post_update.
4381         const IS_BACKDROP_FILTER = 4;
4382     }
4385 /// Descriptor for a cluster of primitives. For now, this is quite basic but will be
4386 /// extended to handle more spatial clustering of primitives.
4387 #[cfg_attr(feature = "capture", derive(Serialize))]
4388 pub struct PrimitiveCluster {
4389     /// The positioning node for this cluster.
4390     pub spatial_node_index: SpatialNodeIndex,
4391     /// The bounding rect of the cluster, in the local space of the spatial node.
4392     /// This is used to quickly determine the overall bounding rect for a picture
4393     /// during the first picture traversal, which is needed for local scale
4394     /// determination, and render task size calculations.
4395     bounding_rect: LayoutRect,
4396     /// a part of the cluster that we know to be opaque if any. Does not always
4397     /// describe the entire opaque region, but all content within that rect must
4398     /// be opaque.
4399     pub opaque_rect: LayoutRect,
4400     /// The range of primitive instance indices associated with this cluster.
4401     pub prim_range: Range<usize>,
4402     /// Various flags / state for this cluster.
4403     pub flags: ClusterFlags,
4406 impl PrimitiveCluster {
4407     /// Construct a new primitive cluster for a given positioning node.
4408     fn new(
4409         spatial_node_index: SpatialNodeIndex,
4410         flags: ClusterFlags,
4411         first_instance_index: usize,
4412     ) -> Self {
4413         PrimitiveCluster {
4414             bounding_rect: LayoutRect::zero(),
4415             opaque_rect: LayoutRect::zero(),
4416             spatial_node_index,
4417             flags,
4418             prim_range: first_instance_index..first_instance_index
4419         }
4420     }
4422     /// Return true if this cluster is compatible with the given params
4423     pub fn is_compatible(
4424         &self,
4425         spatial_node_index: SpatialNodeIndex,
4426         flags: ClusterFlags,
4427     ) -> bool {
4428         self.flags == flags && self.spatial_node_index == spatial_node_index
4429     }
4431     pub fn prim_range(&self) -> Range<usize> {
4432         self.prim_range.clone()
4433     }
4435     /// Add a primitive instance to this cluster, at the start or end
4436     fn add_instance(
4437         &mut self,
4438         culling_rect: &LayoutRect,
4439         instance_index: usize,
4440     ) {
4441         debug_assert_eq!(instance_index, self.prim_range.end);
4442         self.bounding_rect = self.bounding_rect.union(culling_rect);
4443         self.prim_range.end += 1;
4444     }
4447 /// A list of primitive instances that are added to a picture
4448 /// This ensures we can keep a list of primitives that
4449 /// are pictures, for a fast initial traversal of the picture
4450 /// tree without walking the instance list.
4451 #[cfg_attr(feature = "capture", derive(Serialize))]
4452 pub struct PrimitiveList {
4453     /// List of primitives grouped into clusters.
4454     pub clusters: Vec<PrimitiveCluster>,
4455     pub prim_instances: Vec<PrimitiveInstance>,
4456     pub child_pictures: Vec<PictureIndex>,
4457     /// The number of preferred compositor surfaces that were found when
4458     /// adding prims to this list.
4459     pub compositor_surface_count: usize,
4462 impl PrimitiveList {
4463     /// Construct an empty primitive list. This is
4464     /// just used during the take_context / restore_context
4465     /// borrow check dance, which will be removed as the
4466     /// picture traversal pass is completed.
4467     pub fn empty() -> Self {
4468         PrimitiveList {
4469             clusters: Vec::new(),
4470             prim_instances: Vec::new(),
4471             child_pictures: Vec::new(),
4472             compositor_surface_count: 0,
4473         }
4474     }
4476     /// Add a primitive instance to the end of the list
4477     pub fn add_prim(
4478         &mut self,
4479         prim_instance: PrimitiveInstance,
4480         prim_rect: LayoutRect,
4481         spatial_node_index: SpatialNodeIndex,
4482         prim_flags: PrimitiveFlags,
4483     ) {
4484         let mut flags = ClusterFlags::empty();
4486         // Pictures are always put into a new cluster, to make it faster to
4487         // iterate all pictures in a given primitive list.
4488         match prim_instance.kind {
4489             PrimitiveInstanceKind::Picture { pic_index, .. } => {
4490                 self.child_pictures.push(pic_index);
4491             }
4492             PrimitiveInstanceKind::Backdrop { .. } => {
4493                 flags.insert(ClusterFlags::IS_BACKDROP_FILTER);
4494             }
4495             _ => {}
4496         }
4498         if prim_flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE) {
4499             flags.insert(ClusterFlags::IS_BACKFACE_VISIBLE);
4500         }
4502         if prim_flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) {
4503             self.compositor_surface_count += 1;
4504         }
4506         let culling_rect = prim_instance.clip_set.local_clip_rect
4507             .intersection(&prim_rect)
4508             .unwrap_or_else(LayoutRect::zero);
4510         // Primitive lengths aren't evenly distributed among primitive lists:
4511         // We often have a large amount of single primitive lists, a
4512         // few below 20~30 primitives, and even fewer lists (maybe a couple)
4513         // in the multiple hundreds with nothing in between.
4514         // We can see in profiles that reallocating vectors while pushing
4515         // primitives is taking a large amount of the total scene build time,
4516         // so we take advantage of what we know about the length distributions
4517         // to go for an adapted vector growth pattern that avoids over-allocating
4518         // for the many small allocations while avoiding a lot of reallocation by
4519         // quickly converging to the common sizes.
4520         // Rust's default vector growth strategy (when pushing elements one by one)
4521         // is to double the capacity every time.
4522         let prims_len = self.prim_instances.len();
4523         if prims_len == self.prim_instances.capacity() {
4524             let next_alloc = match prims_len {
4525                 1 ..= 31 => 32 - prims_len,
4526                 32 ..= 256 => 512 - prims_len,
4527                 _ => prims_len * 2,
4528             };
4530             self.prim_instances.reserve(next_alloc);
4531         }
4533         let instance_index = prims_len;
4534         self.prim_instances.push(prim_instance);
4536         if let Some(cluster) = self.clusters.last_mut() {
4537             if cluster.is_compatible(spatial_node_index, flags) {
4538                 cluster.add_instance(&culling_rect, instance_index);
4539                 return;
4540             }
4541         }
4543         // Same idea with clusters, using a different distribution.
4544         let clusters_len = self.clusters.len();
4545         if clusters_len == self.clusters.capacity() {
4546             let next_alloc = match clusters_len {
4547                 1 ..= 15 => 16 - clusters_len,
4548                 16 ..= 127 => 128 - clusters_len,
4549                 _ => clusters_len * 2,
4550             };
4552             self.clusters.reserve(next_alloc);
4553         }
4555         let mut cluster = PrimitiveCluster::new(
4556             spatial_node_index,
4557             flags,
4558             instance_index,
4559         );
4561         cluster.add_instance(&culling_rect, instance_index);
4562         self.clusters.push(cluster);
4563     }
4565     /// Returns true if there are no clusters (and thus primitives)
4566     pub fn is_empty(&self) -> bool {
4567         self.clusters.is_empty()
4568     }
4571 /// Defines configuration options for a given picture primitive.
4572 #[cfg_attr(feature = "capture", derive(Serialize))]
4573 pub struct PictureOptions {
4574     /// If true, WR should inflate the bounding rect of primitives when
4575     /// using a filter effect that requires inflation.
4576     pub inflate_if_required: bool,
4579 impl Default for PictureOptions {
4580     fn default() -> Self {
4581         PictureOptions {
4582             inflate_if_required: true,
4583         }
4584     }
4587 #[cfg_attr(feature = "capture", derive(Serialize))]
4588 pub struct PicturePrimitive {
4589     /// List of primitives, and associated info for this picture.
4590     pub prim_list: PrimitiveList,
4592     #[cfg_attr(feature = "capture", serde(skip))]
4593     pub state: Option<PictureState>,
4595     /// If true, apply the local clip rect to primitive drawn
4596     /// in this picture.
4597     pub apply_local_clip_rect: bool,
4598     /// If false and transform ends up showing the back of the picture,
4599     /// it will be considered invisible.
4600     pub is_backface_visible: bool,
4602     pub primary_render_task_id: Option<RenderTaskId>,
4603     /// If a mix-blend-mode, contains the render task for
4604     /// the readback of the framebuffer that we use to sample
4605     /// from in the mix-blend-mode shader.
4606     /// For drop-shadow filter, this will store the original
4607     /// picture task which would be rendered on screen after
4608     /// blur pass.
4609     pub secondary_render_task_id: Option<RenderTaskId>,
4610     /// How this picture should be composited.
4611     /// If None, don't composite - just draw directly on parent surface.
4612     pub requested_composite_mode: Option<PictureCompositeMode>,
4614     pub raster_config: Option<RasterConfig>,
4615     pub context_3d: Picture3DContext<OrderedPictureChild>,
4617     // Optional cache handles for storing extra data
4618     // in the GPU cache, depending on the type of
4619     // picture.
4620     pub extra_gpu_data_handles: SmallVec<[GpuCacheHandle; 1]>,
4622     /// The spatial node index of this picture when it is
4623     /// composited into the parent picture.
4624     pub spatial_node_index: SpatialNodeIndex,
4626     /// The conservative local rect of this picture. It is
4627     /// built dynamically during the first picture traversal.
4628     /// It is composed of already snapped primitives.
4629     pub estimated_local_rect: LayoutRect,
4631     /// The local rect of this picture. It is built
4632     /// dynamically during the frame visibility update. It
4633     /// differs from the estimated_local_rect because it
4634     /// will not contain culled primitives, takes into
4635     /// account surface inflation and the whole clip chain.
4636     /// It is frequently the same, but may be quite
4637     /// different depending on how much was culled.
4638     pub precise_local_rect: LayoutRect,
4640     /// Store the state of the previous precise local rect
4641     /// for this picture. We need this in order to know when
4642     /// to invalidate segments / drop-shadow gpu cache handles.
4643     pub prev_precise_local_rect: LayoutRect,
4645     /// If false, this picture needs to (re)build segments
4646     /// if it supports segment rendering. This can occur
4647     /// if the local rect of the picture changes due to
4648     /// transform animation and/or scrolling.
4649     pub segments_are_valid: bool,
4651     /// The config options for this picture.
4652     pub options: PictureOptions,
4654     /// Set to true if we know for sure the picture is fully opaque.
4655     pub is_opaque: bool,
4658 impl PicturePrimitive {
4659     pub fn print<T: PrintTreePrinter>(
4660         &self,
4661         pictures: &[Self],
4662         self_index: PictureIndex,
4663         pt: &mut T,
4664     ) {
4665         pt.new_level(format!("{:?}", self_index));
4666         pt.add_item(format!("cluster_count: {:?}", self.prim_list.clusters.len()));
4667         pt.add_item(format!("estimated_local_rect: {:?}", self.estimated_local_rect));
4668         pt.add_item(format!("precise_local_rect: {:?}", self.precise_local_rect));
4669         pt.add_item(format!("spatial_node_index: {:?}", self.spatial_node_index));
4670         pt.add_item(format!("raster_config: {:?}", self.raster_config));
4671         pt.add_item(format!("requested_composite_mode: {:?}", self.requested_composite_mode));
4673         for child_pic_index in &self.prim_list.child_pictures {
4674             pictures[child_pic_index.0].print(pictures, *child_pic_index, pt);
4675         }
4677         pt.end_level();
4678     }
4680     /// Returns true if this picture supports segmented rendering.
4681     pub fn can_use_segments(&self) -> bool {
4682         match self.raster_config {
4683             // TODO(gw): Support brush segment rendering for filter and mix-blend
4684             //           shaders. It's possible this already works, but I'm just
4685             //           applying this optimization to Blit mode for now.
4686             Some(RasterConfig { composite_mode: PictureCompositeMode::MixBlend(..), .. }) |
4687             Some(RasterConfig { composite_mode: PictureCompositeMode::Filter(..), .. }) |
4688             Some(RasterConfig { composite_mode: PictureCompositeMode::ComponentTransferFilter(..), .. }) |
4689             Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { .. }, .. }) |
4690             Some(RasterConfig { composite_mode: PictureCompositeMode::SvgFilter(..), .. }) |
4691             None => {
4692                 false
4693             }
4694             Some(RasterConfig { composite_mode: PictureCompositeMode::Blit(reason), ..}) => {
4695                 reason == BlitReason::CLIP
4696             }
4697         }
4698     }
4700     fn resolve_scene_properties(&mut self, properties: &SceneProperties) -> bool {
4701         match self.requested_composite_mode {
4702             Some(PictureCompositeMode::Filter(ref mut filter)) => {
4703                 match *filter {
4704                     Filter::Opacity(ref binding, ref mut value) => {
4705                         *value = properties.resolve_float(binding);
4706                     }
4707                     _ => {}
4708                 }
4710                 filter.is_visible()
4711             }
4712             _ => true,
4713         }
4714     }
4716     pub fn is_visible(&self) -> bool {
4717         match self.requested_composite_mode {
4718             Some(PictureCompositeMode::Filter(ref filter)) => {
4719                 filter.is_visible()
4720             }
4721             _ => true,
4722         }
4723     }
4725     // TODO(gw): We have the PictureOptions struct available. We
4726     //           should move some of the parameter list in this
4727     //           method to be part of the PictureOptions, and
4728     //           avoid adding new parameters here.
4729     pub fn new_image(
4730         requested_composite_mode: Option<PictureCompositeMode>,
4731         context_3d: Picture3DContext<OrderedPictureChild>,
4732         apply_local_clip_rect: bool,
4733         flags: PrimitiveFlags,
4734         prim_list: PrimitiveList,
4735         spatial_node_index: SpatialNodeIndex,
4736         options: PictureOptions,
4737     ) -> Self {
4738         PicturePrimitive {
4739             prim_list,
4740             state: None,
4741             primary_render_task_id: None,
4742             secondary_render_task_id: None,
4743             requested_composite_mode,
4744             raster_config: None,
4745             context_3d,
4746             extra_gpu_data_handles: SmallVec::new(),
4747             apply_local_clip_rect,
4748             is_backface_visible: flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE),
4749             spatial_node_index,
4750             estimated_local_rect: LayoutRect::zero(),
4751             precise_local_rect: LayoutRect::zero(),
4752             prev_precise_local_rect: LayoutRect::zero(),
4753             options,
4754             segments_are_valid: false,
4755             is_opaque: false,
4756         }
4757     }
4759     pub fn take_context(
4760         &mut self,
4761         pic_index: PictureIndex,
4762         surface_spatial_node_index: SpatialNodeIndex,
4763         raster_spatial_node_index: SpatialNodeIndex,
4764         parent_surface_index: SurfaceIndex,
4765         parent_subpixel_mode: SubpixelMode,
4766         frame_state: &mut FrameBuildingState,
4767         frame_context: &FrameBuildingContext,
4768         scratch: &mut PrimitiveScratchBuffer,
4769         tile_cache_logger: &mut TileCacheLogger,
4770         tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
4771     ) -> Option<(PictureContext, PictureState, PrimitiveList)> {
4772         self.primary_render_task_id = None;
4773         self.secondary_render_task_id = None;
4775         if !self.is_visible() {
4776             return None;
4777         }
4779         profile_scope!("take_context");
4781         // Extract the raster and surface spatial nodes from the raster
4782         // config, if this picture establishes a surface. Otherwise just
4783         // pass in the spatial node indices from the parent context.
4784         let (raster_spatial_node_index, surface_spatial_node_index, surface_index, inflation_factor) = match self.raster_config {
4785             Some(ref raster_config) => {
4786                 let surface = &frame_state.surfaces[raster_config.surface_index.0];
4788                 (
4789                     surface.raster_spatial_node_index,
4790                     self.spatial_node_index,
4791                     raster_config.surface_index,
4792                     surface.inflation_factor,
4793                 )
4794             }
4795             None => {
4796                 (
4797                     raster_spatial_node_index,
4798                     surface_spatial_node_index,
4799                     parent_surface_index,
4800                     0.0,
4801                 )
4802             }
4803         };
4805         let map_pic_to_world = SpaceMapper::new_with_target(
4806             ROOT_SPATIAL_NODE_INDEX,
4807             surface_spatial_node_index,
4808             frame_context.global_screen_world_rect,
4809             frame_context.spatial_tree,
4810         );
4812         let pic_bounds = map_pic_to_world.unmap(&map_pic_to_world.bounds)
4813                                          .unwrap_or_else(PictureRect::max_rect);
4815         let map_local_to_pic = SpaceMapper::new(
4816             surface_spatial_node_index,
4817             pic_bounds,
4818         );
4820         let (map_raster_to_world, map_pic_to_raster) = create_raster_mappers(
4821             surface_spatial_node_index,
4822             raster_spatial_node_index,
4823             frame_context.global_screen_world_rect,
4824             frame_context.spatial_tree,
4825         );
4827         let plane_splitter = match self.context_3d {
4828             Picture3DContext::Out => {
4829                 None
4830             }
4831             Picture3DContext::In { root_data: Some(_), .. } => {
4832                 Some(PlaneSplitter::new())
4833             }
4834             Picture3DContext::In { root_data: None, .. } => {
4835                 None
4836             }
4837         };
4839         match self.raster_config {
4840             Some(RasterConfig { surface_index, composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => {
4841                 let tile_cache = tile_caches.get_mut(&slice_id).unwrap();
4842                 let mut debug_info = SliceDebugInfo::new();
4843                 let mut surface_tasks = Vec::with_capacity(tile_cache.tile_count());
4844                 let mut surface_device_rect = DeviceRect::zero();
4845                 let device_pixel_scale = frame_state
4846                     .surfaces[surface_index.0]
4847                     .device_pixel_scale;
4849                 // Get the overall world space rect of the picture cache. Used to clip
4850                 // the tile rects below for occlusion testing to the relevant area.
4851                 let world_clip_rect = map_pic_to_world
4852                     .map(&tile_cache.local_clip_rect)
4853                     .expect("bug: unable to map clip rect");
4854                 let device_clip_rect = (world_clip_rect * frame_context.global_device_pixel_scale).round();
4856                 for (sub_slice_index, sub_slice) in tile_cache.sub_slices.iter_mut().enumerate() {
4857                     for tile in sub_slice.tiles.values_mut() {
4858                         surface_device_rect = surface_device_rect.union(&tile.device_valid_rect);
4860                         if tile.is_visible {
4861                             // Get the world space rect that this tile will actually occupy on screem
4862                             let device_draw_rect = device_clip_rect.intersection(&tile.device_valid_rect);
4864                             // If that draw rect is occluded by some set of tiles in front of it,
4865                             // then mark it as not visible and skip drawing. When it's not occluded
4866                             // it will fail this test, and get rasterized by the render task setup
4867                             // code below.
4868                             match device_draw_rect {
4869                                 Some(device_draw_rect) => {
4870                                     // Only check for occlusion on visible tiles that are fixed position.
4871                                     if tile_cache.spatial_node_index == ROOT_SPATIAL_NODE_INDEX &&
4872                                        frame_state.composite_state.occluders.is_tile_occluded(tile.z_id, device_draw_rect) {
4873                                         // If this tile has an allocated native surface, free it, since it's completely
4874                                         // occluded. We will need to re-allocate this surface if it becomes visible,
4875                                         // but that's likely to be rare (e.g. when there is no content display list
4876                                         // for a frame or two during a tab switch).
4877                                         let surface = tile.surface.as_mut().expect("no tile surface set!");
4879                                         if let TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { id, .. }, .. } = surface {
4880                                             if let Some(id) = id.take() {
4881                                                 frame_state.resource_cache.destroy_compositor_tile(id);
4882                                             }
4883                                         }
4885                                         tile.is_visible = false;
4887                                         if frame_context.fb_config.testing {
4888                                             debug_info.tiles.insert(
4889                                                 tile.tile_offset,
4890                                                 TileDebugInfo::Occluded,
4891                                             );
4892                                         }
4894                                         continue;
4895                                     }
4896                                 }
4897                                 None => {
4898                                     tile.is_visible = false;
4899                                 }
4900                             }
4901                         }
4903                         // If we get here, we want to ensure that the surface remains valid in the texture
4904                         // cache, _even if_ it's not visible due to clipping or being scrolled off-screen.
4905                         // This ensures that we retain valid tiles that are off-screen, but still in the
4906                         // display port of this tile cache instance.
4907                         if let Some(TileSurface::Texture { descriptor, .. }) = tile.surface.as_ref() {
4908                             if let SurfaceTextureDescriptor::TextureCache { ref handle, .. } = descriptor {
4909                                 frame_state.resource_cache.texture_cache.request(
4910                                     handle,
4911                                     frame_state.gpu_cache,
4912                                 );
4913                             }
4914                         }
4916                         // If the tile has been found to be off-screen / clipped, skip any further processing.
4917                         if !tile.is_visible {
4918                             if frame_context.fb_config.testing {
4919                                 debug_info.tiles.insert(
4920                                     tile.tile_offset,
4921                                     TileDebugInfo::Culled,
4922                                 );
4923                             }
4925                             continue;
4926                         }
4928                         if frame_context.debug_flags.contains(DebugFlags::PICTURE_CACHING_DBG) {
4929                             tile.root.draw_debug_rects(
4930                                 &map_pic_to_world,
4931                                 tile.is_opaque,
4932                                 tile.current_descriptor.local_valid_rect,
4933                                 scratch,
4934                                 frame_context.global_device_pixel_scale,
4935                             );
4937                             let label_offset = DeviceVector2D::new(
4938                                 20.0 + sub_slice_index as f32 * 20.0,
4939                                 30.0 + sub_slice_index as f32 * 20.0,
4940                             );
4941                             let tile_device_rect = tile.world_tile_rect * frame_context.global_device_pixel_scale;
4942                             if tile_device_rect.size.height >= label_offset.y {
4943                                 let surface = tile.surface.as_ref().expect("no tile surface set!");
4945                                 scratch.push_debug_string(
4946                                     tile_device_rect.origin + label_offset,
4947                                     debug_colors::RED,
4948                                     format!("{:?}: s={} is_opaque={} surface={} sub={}",
4949                                             tile.id,
4950                                             tile_cache.slice,
4951                                             tile.is_opaque,
4952                                             surface.kind(),
4953                                             sub_slice_index,
4954                                     ),
4955                                 );
4956                             }
4957                         }
4959                         if let TileSurface::Texture { descriptor, .. } = tile.surface.as_mut().unwrap() {
4960                             match descriptor {
4961                                 SurfaceTextureDescriptor::TextureCache { ref handle, .. } => {
4962                                     // Invalidate if the backing texture was evicted.
4963                                     if frame_state.resource_cache.texture_cache.is_allocated(handle) {
4964                                         // Request the backing texture so it won't get evicted this frame.
4965                                         // We specifically want to mark the tile texture as used, even
4966                                         // if it's detected not visible below and skipped. This is because
4967                                         // we maintain the set of tiles we care about based on visibility
4968                                         // during pre_update. If a tile still exists after that, we are
4969                                         // assuming that it's either visible or we want to retain it for
4970                                         // a while in case it gets scrolled back onto screen soon.
4971                                         // TODO(gw): Consider switching to manual eviction policy?
4972                                         frame_state.resource_cache.texture_cache.request(handle, frame_state.gpu_cache);
4973                                     } else {
4974                                         // If the texture was evicted on a previous frame, we need to assume
4975                                         // that the entire tile rect is dirty.
4976                                         tile.invalidate(None, InvalidationReason::NoTexture);
4977                                     }
4978                                 }
4979                                 SurfaceTextureDescriptor::Native { id, .. } => {
4980                                     if id.is_none() {
4981                                         // There is no current surface allocation, so ensure the entire tile is invalidated
4982                                         tile.invalidate(None, InvalidationReason::NoSurface);
4983                                     }
4984                                 }
4985                             }
4986                         }
4988                         // Ensure that the dirty rect doesn't extend outside the local valid rect.
4989                         tile.local_dirty_rect = tile.local_dirty_rect
4990                             .intersection(&tile.current_descriptor.local_valid_rect)
4991                             .unwrap_or_else(PictureRect::zero);
4993                         // Update the world/device dirty rect
4994                         let world_dirty_rect = map_pic_to_world.map(&tile.local_dirty_rect).expect("bug");
4996                         let device_rect = (tile.world_tile_rect * frame_context.global_device_pixel_scale).round();
4997                         tile.device_dirty_rect = (world_dirty_rect * frame_context.global_device_pixel_scale)
4998                             .round_out()
4999                             .intersection(&device_rect)
5000                             .unwrap_or_else(DeviceRect::zero);
5002                         if tile.is_valid {
5003                             if frame_context.fb_config.testing {
5004                                 debug_info.tiles.insert(
5005                                     tile.tile_offset,
5006                                     TileDebugInfo::Valid,
5007                                 );
5008                             }
5010                             continue;
5011                         }
5013                         // Add this dirty rect to the dirty region tracker. This must be done outside the if statement below,
5014                         // so that we include in the dirty region tiles that are handled by a background color only (no
5015                         // surface allocation).
5016                         tile_cache.dirty_region.add_dirty_region(
5017                             tile.local_dirty_rect,
5018                             SubSliceIndex::new(sub_slice_index),
5019                             frame_context.spatial_tree,
5020                         );
5022                         // Ensure that this texture is allocated.
5023                         if let TileSurface::Texture { ref mut descriptor } = tile.surface.as_mut().unwrap() {
5024                             match descriptor {
5025                                 SurfaceTextureDescriptor::TextureCache { ref mut handle } => {
5026                                     if !frame_state.resource_cache.texture_cache.is_allocated(handle) {
5027                                         frame_state.resource_cache.texture_cache.update_picture_cache(
5028                                             tile_cache.current_tile_size,
5029                                             handle,
5030                                             frame_state.gpu_cache,
5031                                         );
5032                                     }
5033                                 }
5034                                 SurfaceTextureDescriptor::Native { id } => {
5035                                     if id.is_none() {
5036                                         // Allocate a native surface id if we're in native compositing mode,
5037                                         // and we don't have a surface yet (due to first frame, or destruction
5038                                         // due to tile size changing etc).
5039                                         if sub_slice.native_surface.is_none() {
5040                                             let opaque = frame_state
5041                                                 .resource_cache
5042                                                 .create_compositor_surface(
5043                                                     tile_cache.virtual_offset,
5044                                                     tile_cache.current_tile_size,
5045                                                     true,
5046                                                 );
5048                                             let alpha = frame_state
5049                                                 .resource_cache
5050                                                 .create_compositor_surface(
5051                                                     tile_cache.virtual_offset,
5052                                                     tile_cache.current_tile_size,
5053                                                     false,
5054                                                 );
5056                                             sub_slice.native_surface = Some(NativeSurface {
5057                                                 opaque,
5058                                                 alpha,
5059                                             });
5060                                         }
5062                                         // Create the tile identifier and allocate it.
5063                                         let surface_id = if tile.is_opaque {
5064                                             sub_slice.native_surface.as_ref().unwrap().opaque
5065                                         } else {
5066                                             sub_slice.native_surface.as_ref().unwrap().alpha
5067                                         };
5069                                         let tile_id = NativeTileId {
5070                                             surface_id,
5071                                             x: tile.tile_offset.x,
5072                                             y: tile.tile_offset.y,
5073                                         };
5075                                         frame_state.resource_cache.create_compositor_tile(tile_id);
5077                                         *id = Some(tile_id);
5078                                     }
5079                                 }
5080                             }
5082                             let content_origin_f = tile.world_tile_rect.origin * device_pixel_scale;
5083                             let content_origin = content_origin_f.round();
5084                             debug_assert!((content_origin_f.x - content_origin.x).abs() < 0.01);
5085                             debug_assert!((content_origin_f.y - content_origin.y).abs() < 0.01);
5087                             let surface = descriptor.resolve(
5088                                 frame_state.resource_cache,
5089                                 tile_cache.current_tile_size,
5090                             );
5092                             let scissor_rect = tile.device_dirty_rect
5093                                 .translate(-device_rect.origin.to_vector())
5094                                 .round()
5095                                 .to_i32();
5097                             let valid_rect = tile.device_valid_rect
5098                                 .translate(-device_rect.origin.to_vector())
5099                                 .round()
5100                                 .to_i32();
5102                             let task_size = tile_cache.current_tile_size;
5104                             let batch_filter = BatchFilter {
5105                                 rect_in_pic_space: tile.local_dirty_rect,
5106                                 sub_slice_index: SubSliceIndex::new(sub_slice_index),
5107                             };
5109                             let render_task_id = frame_state.rg_builder.add().init(
5110                                 RenderTask::new(
5111                                     RenderTaskLocation::Static {
5112                                         surface: StaticRenderTaskSurface::PictureCache {
5113                                             surface,
5114                                         },
5115                                         rect: task_size.into(),
5116                                     },
5117                                     RenderTaskKind::new_picture(
5118                                         task_size,
5119                                         tile_cache.current_tile_size.to_f32(),
5120                                         pic_index,
5121                                         content_origin,
5122                                         surface_spatial_node_index,
5123                                         device_pixel_scale,
5124                                         Some(batch_filter),
5125                                         Some(scissor_rect),
5126                                         Some(valid_rect),
5127                                     )
5128                                 ),
5129                             );
5131                             surface_tasks.push(render_task_id);
5132                         }
5134                         if frame_context.fb_config.testing {
5135                             debug_info.tiles.insert(
5136                                 tile.tile_offset,
5137                                 TileDebugInfo::Dirty(DirtyTileDebugInfo {
5138                                     local_valid_rect: tile.current_descriptor.local_valid_rect,
5139                                     local_dirty_rect: tile.local_dirty_rect,
5140                                 }),
5141                             );
5142                         }
5144                         // If the entire tile valid region is dirty, we can update the fract offset
5145                         // at which the tile was rendered.
5146                         if tile.device_dirty_rect.contains_rect(&tile.device_valid_rect) {
5147                             tile.device_fract_offset = tile_cache.device_fract_offset;
5148                         }
5150                         // Now that the tile is valid, reset the dirty rect.
5151                         tile.local_dirty_rect = PictureRect::zero();
5152                         tile.is_valid = true;
5153                     }
5154                 }
5156                 // If invalidation debugging is enabled, dump the picture cache state to a tree printer.
5157                 if frame_context.debug_flags.contains(DebugFlags::INVALIDATION_DBG) {
5158                     tile_cache.print();
5159                 }
5161                 // If testing mode is enabled, write some information about the current state
5162                 // of this picture cache (made available in RenderResults).
5163                 if frame_context.fb_config.testing {
5164                     frame_state.composite_state
5165                         .picture_cache_debug
5166                         .slices
5167                         .insert(
5168                             tile_cache.slice,
5169                             debug_info,
5170                         );
5171                 }
5173                 frame_state.init_surface_tiled(
5174                     surface_index,
5175                     surface_tasks,
5176                     surface_device_rect,
5177                 );
5178             }
5179             Some(ref mut raster_config) => {
5180                 let pic_rect = self.precise_local_rect.cast_unit();
5182                 let mut device_pixel_scale = frame_state
5183                     .surfaces[raster_config.surface_index.0]
5184                     .device_pixel_scale;
5186                 let scale_factors = frame_state
5187                     .surfaces[raster_config.surface_index.0]
5188                     .scale_factors;
5190                 // If the primitive has a filter that can sample with an offset, the clip rect has
5191                 // to take it into account.
5192                 let clip_inflation = match raster_config.composite_mode {
5193                     PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
5194                         let mut max_offset = vec2(0.0, 0.0);
5195                         let mut min_offset = vec2(0.0, 0.0);
5196                         for shadow in shadows {
5197                             let offset = layout_vector_as_picture_vector(shadow.offset);
5198                             max_offset = max_offset.max(offset);
5199                             min_offset = min_offset.min(offset);
5200                         }
5202                         // Get the shadow offsets in world space.
5203                         let raster_min = map_pic_to_raster.map_vector(min_offset);
5204                         let raster_max = map_pic_to_raster.map_vector(max_offset);
5205                         let world_min = map_raster_to_world.map_vector(raster_min);
5206                         let world_max = map_raster_to_world.map_vector(raster_max);
5208                         // Grow the clip in the opposite direction of the shadow's offset.
5209                         SideOffsets2D::from_vectors_outer(
5210                             -world_max.max(vec2(0.0, 0.0)),
5211                             -world_min.min(vec2(0.0, 0.0)),
5212                         )
5213                     }
5214                     _ => SideOffsets2D::zero(),
5215                 };
5217                 let (mut clipped, mut unclipped) = match get_raster_rects(
5218                     pic_rect,
5219                     &map_pic_to_raster,
5220                     &map_raster_to_world,
5221                     raster_config.clipped_bounding_rect.outer_rect(clip_inflation),
5222                     device_pixel_scale,
5223                 ) {
5224                     Some(info) => info,
5225                     None => {
5226                         return None
5227                     }
5228                 };
5229                 let transform = map_pic_to_raster.get_transform();
5231                 /// If the picture (raster_config) establishes a raster root,
5232                 /// its requested resolution won't be clipped by the parent or
5233                 /// viewport; so we need to make sure the requested resolution is
5234                 /// "reasonable", ie. <= MAX_SURFACE_SIZE.  If not, scale the
5235                 /// picture down until it fits that limit.  This results in a new
5236                 /// device_rect, a new unclipped rect, and a new device_pixel_scale.
5237                 ///
5238                 /// Since the adjusted device_pixel_scale is passed into the
5239                 /// RenderTask (and then the shader via RenderTaskData) this mostly
5240                 /// works transparently, reusing existing support for variable DPI
5241                 /// support.  The on-the-fly scaling can be seen as on-the-fly,
5242                 /// per-task DPI adjustment.  Logical pixels are unaffected.
5243                 ///
5244                 /// The scaling factor is returned to the caller; blur radius,
5245                 /// font size, etc. need to be scaled accordingly.
5246                 fn adjust_scale_for_max_surface_size(
5247                     raster_config: &RasterConfig,
5248                     max_target_size: i32,
5249                     pic_rect: PictureRect,
5250                     map_pic_to_raster: &SpaceMapper<PicturePixel, RasterPixel>,
5251                     map_raster_to_world: &SpaceMapper<RasterPixel, WorldPixel>,
5252                     clipped_prim_bounding_rect: WorldRect,
5253                     device_pixel_scale : &mut DevicePixelScale,
5254                     device_rect: &mut DeviceRect,
5255                     unclipped: &mut DeviceRect) -> Option<f32>
5256                 {
5257                     let limit = if raster_config.establishes_raster_root {
5258                         MAX_SURFACE_SIZE
5259                     } else {
5260                         max_target_size as f32
5261                     };
5262                     if device_rect.size.width > limit || device_rect.size.height > limit {
5263                         // round_out will grow by 1 integer pixel if origin is on a
5264                         // fractional position, so keep that margin for error with -1:
5265                         let scale = (limit as f32 - 1.0) /
5266                                     (f32::max(device_rect.size.width, device_rect.size.height));
5267                         *device_pixel_scale = *device_pixel_scale * Scale::new(scale);
5268                         let new_device_rect = device_rect.to_f32() * Scale::new(scale);
5269                         *device_rect = new_device_rect.round_out();
5271                         *unclipped = match get_raster_rects(
5272                             pic_rect,
5273                             &map_pic_to_raster,
5274                             &map_raster_to_world,
5275                             clipped_prim_bounding_rect,
5276                             *device_pixel_scale
5277                         ) {
5278                             Some(info) => info.1,
5279                             None => {
5280                                 return None
5281                             }
5282                         };
5283                         Some(scale)
5284                     }
5285                     else
5286                     {
5287                         None
5288                     }
5289                 }
5291                 let primary_render_task_id;
5292                 match raster_config.composite_mode {
5293                     PictureCompositeMode::TileCache { .. } => {
5294                         unreachable!("handled above");
5295                     }
5296                     PictureCompositeMode::Filter(Filter::Blur(width, height)) => {
5297                         let width_std_deviation = clamp_blur_radius(width, scale_factors) * device_pixel_scale.0;
5298                         let height_std_deviation = clamp_blur_radius(height, scale_factors) * device_pixel_scale.0;
5299                         let mut blur_std_deviation = DeviceSize::new(
5300                             width_std_deviation * scale_factors.0,
5301                             height_std_deviation * scale_factors.1
5302                         );
5303                         let mut device_rect = if self.options.inflate_if_required {
5304                             let inflation_factor = frame_state.surfaces[raster_config.surface_index.0].inflation_factor;
5305                             let inflation_factor = inflation_factor * device_pixel_scale.0;
5307                             // The clipped field is the part of the picture that is visible
5308                             // on screen. The unclipped field is the screen-space rect of
5309                             // the complete picture, if no screen / clip-chain was applied
5310                             // (this includes the extra space for blur region). To ensure
5311                             // that we draw a large enough part of the picture to get correct
5312                             // blur results, inflate that clipped area by the blur range, and
5313                             // then intersect with the total screen rect, to minimize the
5314                             // allocation size.
5315                             clipped
5316                                 .inflate(inflation_factor * scale_factors.0, inflation_factor * scale_factors.1)
5317                                 .intersection(&unclipped)
5318                                 .unwrap()
5319                         } else {
5320                             clipped
5321                         };
5323                         let mut original_size = device_rect.size;
5325                         // Adjust the size to avoid introducing sampling errors during the down-scaling passes.
5326                         // what would be even better is to rasterize the picture at the down-scaled size
5327                         // directly.
5328                         device_rect.size = BlurTask::adjusted_blur_source_size(
5329                             device_rect.size,
5330                             blur_std_deviation,
5331                         );
5333                         if let Some(scale) = adjust_scale_for_max_surface_size(
5334                             raster_config, frame_context.fb_config.max_target_size,
5335                             pic_rect, &map_pic_to_raster, &map_raster_to_world,
5336                             raster_config.clipped_bounding_rect,
5337                             &mut device_pixel_scale, &mut device_rect, &mut unclipped,
5338                         ) {
5339                             blur_std_deviation = blur_std_deviation * scale;
5340                             original_size = original_size.to_f32() * scale;
5341                             raster_config.root_scaling_factor = scale;
5342                         }
5344                         let uv_rect_kind = calculate_uv_rect_kind(
5345                             &pic_rect,
5346                             &transform,
5347                             &device_rect,
5348                             device_pixel_scale,
5349                         );
5351                         let task_size = device_rect.size.to_i32();
5353                         let picture_task_id = frame_state.rg_builder.add().init(
5354                             RenderTask::new_dynamic(
5355                                 task_size,
5356                                 RenderTaskKind::new_picture(
5357                                     task_size,
5358                                     unclipped.size,
5359                                     pic_index,
5360                                     device_rect.origin,
5361                                     surface_spatial_node_index,
5362                                     device_pixel_scale,
5363                                     None,
5364                                     None,
5365                                     None,
5366                                 )
5367                             ).with_uv_rect_kind(uv_rect_kind)
5368                         );
5370                         let blur_render_task_id = RenderTask::new_blur(
5371                             blur_std_deviation,
5372                             picture_task_id,
5373                             frame_state.rg_builder,
5374                             RenderTargetKind::Color,
5375                             None,
5376                             original_size.to_i32(),
5377                         );
5379                         primary_render_task_id = Some(blur_render_task_id);
5381                         frame_state.init_surface_chain(
5382                             raster_config.surface_index,
5383                             blur_render_task_id,
5384                             picture_task_id,
5385                             parent_surface_index,
5386                             device_rect,
5387                         );
5388                     }
5389                     PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
5390                         let mut max_std_deviation = 0.0;
5391                         for shadow in shadows {
5392                             max_std_deviation = f32::max(max_std_deviation, shadow.blur_radius);
5393                         }
5394                         max_std_deviation = clamp_blur_radius(max_std_deviation, scale_factors) * device_pixel_scale.0;
5395                         let max_blur_range = max_std_deviation * BLUR_SAMPLE_SCALE;
5397                         // We cast clipped to f32 instead of casting unclipped to i32
5398                         // because unclipped can overflow an i32.
5399                         let mut device_rect = clipped
5400                                 .inflate(max_blur_range * scale_factors.0, max_blur_range * scale_factors.1)
5401                                 .intersection(&unclipped)
5402                                 .unwrap();
5404                         device_rect.size = BlurTask::adjusted_blur_source_size(
5405                             device_rect.size,
5406                             DeviceSize::new(
5407                                 max_std_deviation * scale_factors.0,
5408                                 max_std_deviation * scale_factors.1
5409                             ),
5410                         );
5412                         if let Some(scale) = adjust_scale_for_max_surface_size(
5413                             raster_config, frame_context.fb_config.max_target_size,
5414                             pic_rect, &map_pic_to_raster, &map_raster_to_world,
5415                             raster_config.clipped_bounding_rect,
5416                             &mut device_pixel_scale, &mut device_rect, &mut unclipped,
5417                         ) {
5418                             // std_dev adjusts automatically from using device_pixel_scale
5419                             raster_config.root_scaling_factor = scale;
5420                         }
5422                         let uv_rect_kind = calculate_uv_rect_kind(
5423                             &pic_rect,
5424                             &transform,
5425                             &device_rect,
5426                             device_pixel_scale,
5427                         );
5429                         let task_size = device_rect.size.to_i32();
5431                         let picture_task_id = frame_state.rg_builder.add().init(
5432                             RenderTask::new_dynamic(
5433                                 task_size,
5434                                 RenderTaskKind::new_picture(
5435                                     task_size,
5436                                     unclipped.size,
5437                                     pic_index,
5438                                     device_rect.origin,
5439                                     surface_spatial_node_index,
5440                                     device_pixel_scale,
5441                                     None,
5442                                     None,
5443                                     None,
5444                                 ),
5445                             ).with_uv_rect_kind(uv_rect_kind)
5446                         );
5448                         // Add this content picture as a dependency of the parent surface, to
5449                         // ensure it isn't free'd after the shadow uses it as an input.
5450                         frame_state.add_child_render_task(
5451                             parent_surface_index,
5452                             picture_task_id,
5453                         );
5455                         let mut blur_tasks = BlurTaskCache::default();
5457                         self.extra_gpu_data_handles.resize(shadows.len(), GpuCacheHandle::new());
5459                         let mut blur_render_task_id = picture_task_id;
5460                         for shadow in shadows {
5461                             let blur_radius = clamp_blur_radius(shadow.blur_radius, scale_factors) * device_pixel_scale.0;
5462                             blur_render_task_id = RenderTask::new_blur(
5463                                 DeviceSize::new(
5464                                     blur_radius * scale_factors.0,
5465                                     blur_radius * scale_factors.1,
5466                                 ),
5467                                 picture_task_id,
5468                                 frame_state.rg_builder,
5469                                 RenderTargetKind::Color,
5470                                 Some(&mut blur_tasks),
5471                                 device_rect.size.to_i32(),
5472                             );
5473                         }
5475                         primary_render_task_id = Some(blur_render_task_id);
5476                         self.secondary_render_task_id = Some(picture_task_id);
5478                         frame_state.init_surface_chain(
5479                             raster_config.surface_index,
5480                             blur_render_task_id,
5481                             picture_task_id,
5482                             parent_surface_index,
5483                             device_rect,
5484                         );
5485                     }
5486                     PictureCompositeMode::MixBlend(mode) if BlendMode::from_mix_blend_mode(
5487                         mode,
5488                         frame_context.fb_config.gpu_supports_advanced_blend,
5489                         frame_context.fb_config.advanced_blend_is_coherent,
5490                         frame_context.fb_config.dual_source_blending_is_enabled &&
5491                             frame_context.fb_config.dual_source_blending_is_supported,
5492                     ).is_none() => {
5493                         if let Some(scale) = adjust_scale_for_max_surface_size(
5494                             raster_config, frame_context.fb_config.max_target_size,
5495                             pic_rect, &map_pic_to_raster, &map_raster_to_world,
5496                             raster_config.clipped_bounding_rect,
5497                             &mut device_pixel_scale, &mut clipped, &mut unclipped,
5498                         ) {
5499                             raster_config.root_scaling_factor = scale;
5500                         }
5502                         let uv_rect_kind = calculate_uv_rect_kind(
5503                             &pic_rect,
5504                             &transform,
5505                             &clipped,
5506                             device_pixel_scale,
5507                         );
5509                         let parent_surface = &frame_state.surfaces[parent_surface_index.0];
5510                         let parent_raster_spatial_node_index = parent_surface.raster_spatial_node_index;
5511                         let parent_device_pixel_scale = parent_surface.device_pixel_scale;
5513                         // Create a space mapper that will allow mapping from the local rect
5514                         // of the mix-blend primitive into the space of the surface that we
5515                         // need to read back from. Note that we use the parent's raster spatial
5516                         // node here, so that we are in the correct device space of the parent
5517                         // surface, whether it establishes a raster root or not.
5518                         let map_pic_to_parent = SpaceMapper::new_with_target(
5519                             parent_raster_spatial_node_index,
5520                             self.spatial_node_index,
5521                             RasterRect::max_rect(),         // TODO(gw): May need a conservative estimate?
5522                             frame_context.spatial_tree,
5523                         );
5524                         let pic_in_raster_space = map_pic_to_parent
5525                             .map(&pic_rect)
5526                             .expect("bug: unable to map mix-blend content into parent");
5528                         // Apply device pixel ratio for parent surface to get into device
5529                         // pixels for that surface.
5530                         let backdrop_rect = raster_rect_to_device_pixels(
5531                             pic_in_raster_space,
5532                             parent_device_pixel_scale,
5533                         );
5535                         let parent_surface_rect = parent_surface.get_device_rect();
5537                         // If there is no available parent surface to read back from (for example, if
5538                         // the parent surface is affected by a clip that doesn't affect the child
5539                         // surface), then create a dummy 16x16 readback. In future, we could alter
5540                         // the composite mode of this primitive to skip the mix-blend, but for simplicity
5541                         // we just create a dummy readback for now.
5543                         let readback_task_id = match backdrop_rect.intersection(&parent_surface_rect) {
5544                             Some(available_rect) => {
5545                                 // Calculate the UV coords necessary for the shader to sampler
5546                                 // from the primitive rect within the readback region. This is
5547                                 // 0..1 for aligned surfaces, but doing it this way allows
5548                                 // accurate sampling if the primitive bounds have fractional values.
5549                                 let backdrop_uv = calculate_uv_rect_kind(
5550                                     &pic_rect,
5551                                     &map_pic_to_parent.get_transform(),
5552                                     &available_rect,
5553                                     parent_device_pixel_scale,
5554                                 );
5556                                 frame_state.rg_builder.add().init(
5557                                     RenderTask::new_dynamic(
5558                                         available_rect.size.to_i32(),
5559                                         RenderTaskKind::new_readback(Some(available_rect.origin)),
5560                                     ).with_uv_rect_kind(backdrop_uv)
5561                                 )
5562                             }
5563                             None => {
5564                                 frame_state.rg_builder.add().init(
5565                                     RenderTask::new_dynamic(
5566                                         DeviceIntSize::new(16, 16),
5567                                         RenderTaskKind::new_readback(None),
5568                                     )
5569                                 )
5570                             }
5571                         };
5573                         frame_state.add_child_render_task(
5574                             parent_surface_index,
5575                             readback_task_id,
5576                         );
5578                         self.secondary_render_task_id = Some(readback_task_id);
5580                         let task_size = clipped.size.to_i32();
5582                         let render_task_id = frame_state.rg_builder.add().init(
5583                             RenderTask::new_dynamic(
5584                                 task_size,
5585                                 RenderTaskKind::new_picture(
5586                                     task_size,
5587                                     unclipped.size,
5588                                     pic_index,
5589                                     clipped.origin,
5590                                     surface_spatial_node_index,
5591                                     device_pixel_scale,
5592                                     None,
5593                                     None,
5594                                     None,
5595                                 )
5596                             ).with_uv_rect_kind(uv_rect_kind)
5597                         );
5599                         primary_render_task_id = Some(render_task_id);
5601                         frame_state.init_surface(
5602                             raster_config.surface_index,
5603                             render_task_id,
5604                             parent_surface_index,
5605                             clipped,
5606                         );
5607                     }
5608                     PictureCompositeMode::Filter(..) => {
5610                         if let Some(scale) = adjust_scale_for_max_surface_size(
5611                             raster_config, frame_context.fb_config.max_target_size,
5612                             pic_rect, &map_pic_to_raster, &map_raster_to_world,
5613                             raster_config.clipped_bounding_rect,
5614                             &mut device_pixel_scale, &mut clipped, &mut unclipped,
5615                         ) {
5616                             raster_config.root_scaling_factor = scale;
5617                         }
5619                         let uv_rect_kind = calculate_uv_rect_kind(
5620                             &pic_rect,
5621                             &transform,
5622                             &clipped,
5623                             device_pixel_scale,
5624                         );
5626                         let task_size = clipped.size.to_i32();
5628                         let render_task_id = frame_state.rg_builder.add().init(
5629                             RenderTask::new_dynamic(
5630                                 task_size,
5631                                 RenderTaskKind::new_picture(
5632                                     task_size,
5633                                     unclipped.size,
5634                                     pic_index,
5635                                     clipped.origin,
5636                                     surface_spatial_node_index,
5637                                     device_pixel_scale,
5638                                     None,
5639                                     None,
5640                                     None,
5641                                 )
5642                             ).with_uv_rect_kind(uv_rect_kind)
5643                         );
5645                         primary_render_task_id = Some(render_task_id);
5647                         frame_state.init_surface(
5648                             raster_config.surface_index,
5649                             render_task_id,
5650                             parent_surface_index,
5651                             clipped,
5652                         );
5653                     }
5654                     PictureCompositeMode::ComponentTransferFilter(..) => {
5655                         if let Some(scale) = adjust_scale_for_max_surface_size(
5656                             raster_config, frame_context.fb_config.max_target_size,
5657                             pic_rect, &map_pic_to_raster, &map_raster_to_world,
5658                             raster_config.clipped_bounding_rect,
5659                             &mut device_pixel_scale, &mut clipped, &mut unclipped,
5660                         ) {
5661                             raster_config.root_scaling_factor = scale;
5662                         }
5664                         let uv_rect_kind = calculate_uv_rect_kind(
5665                             &pic_rect,
5666                             &transform,
5667                             &clipped,
5668                             device_pixel_scale,
5669                         );
5671                         let task_size = clipped.size.to_i32();
5673                         let render_task_id = frame_state.rg_builder.add().init(
5674                             RenderTask::new_dynamic(
5675                                 task_size,
5676                                 RenderTaskKind::new_picture(
5677                                     task_size,
5678                                     unclipped.size,
5679                                     pic_index,
5680                                     clipped.origin,
5681                                     surface_spatial_node_index,
5682                                     device_pixel_scale,
5683                                     None,
5684                                     None,
5685                                     None,
5686                                 )
5687                             ).with_uv_rect_kind(uv_rect_kind)
5688                         );
5690                         primary_render_task_id = Some(render_task_id);
5692                         frame_state.init_surface(
5693                             raster_config.surface_index,
5694                             render_task_id,
5695                             parent_surface_index,
5696                             clipped,
5697                         );
5698                     }
5699                     PictureCompositeMode::MixBlend(..) |
5700                     PictureCompositeMode::Blit(_) => {
5701                         if let Some(scale) = adjust_scale_for_max_surface_size(
5702                             raster_config, frame_context.fb_config.max_target_size,
5703                             pic_rect, &map_pic_to_raster, &map_raster_to_world,
5704                             raster_config.clipped_bounding_rect,
5705                             &mut device_pixel_scale, &mut clipped, &mut unclipped,
5706                         ) {
5707                             raster_config.root_scaling_factor = scale;
5708                         }
5710                         let uv_rect_kind = calculate_uv_rect_kind(
5711                             &pic_rect,
5712                             &transform,
5713                             &clipped,
5714                             device_pixel_scale,
5715                         );
5717                         let task_size = clipped.size.to_i32();
5719                         let render_task_id = frame_state.rg_builder.add().init(
5720                             RenderTask::new_dynamic(
5721                                 task_size,
5722                                 RenderTaskKind::new_picture(
5723                                     task_size,
5724                                     unclipped.size,
5725                                     pic_index,
5726                                     clipped.origin,
5727                                     surface_spatial_node_index,
5728                                     device_pixel_scale,
5729                                     None,
5730                                     None,
5731                                     None,
5732                                 )
5733                             ).with_uv_rect_kind(uv_rect_kind)
5734                         );
5736                         primary_render_task_id = Some(render_task_id);
5738                         frame_state.init_surface(
5739                             raster_config.surface_index,
5740                             render_task_id,
5741                             parent_surface_index,
5742                             clipped,
5743                         );
5744                     }
5745                     PictureCompositeMode::SvgFilter(ref primitives, ref filter_datas) => {
5747                         if let Some(scale) = adjust_scale_for_max_surface_size(
5748                             raster_config, frame_context.fb_config.max_target_size,
5749                             pic_rect, &map_pic_to_raster, &map_raster_to_world,
5750                             raster_config.clipped_bounding_rect,
5751                             &mut device_pixel_scale, &mut clipped, &mut unclipped,
5752                         ) {
5753                             raster_config.root_scaling_factor = scale;
5754                         }
5756                         let uv_rect_kind = calculate_uv_rect_kind(
5757                             &pic_rect,
5758                             &transform,
5759                             &clipped,
5760                             device_pixel_scale,
5761                         );
5763                         let task_size = clipped.size.to_i32();
5765                         let picture_task_id = frame_state.rg_builder.add().init(
5766                             RenderTask::new_dynamic(
5767                                 task_size,
5768                                 RenderTaskKind::new_picture(
5769                                     task_size,
5770                                     unclipped.size,
5771                                     pic_index,
5772                                     clipped.origin,
5773                                     surface_spatial_node_index,
5774                                     device_pixel_scale,
5775                                     None,
5776                                     None,
5777                                     None,
5778                                 )
5779                             ).with_uv_rect_kind(uv_rect_kind)
5780                         );
5782                         let filter_task_id = RenderTask::new_svg_filter(
5783                             primitives,
5784                             filter_datas,
5785                             frame_state.rg_builder,
5786                             clipped.size.to_i32(),
5787                             uv_rect_kind,
5788                             picture_task_id,
5789                             device_pixel_scale,
5790                         );
5792                         primary_render_task_id = Some(filter_task_id);
5794                         frame_state.init_surface_chain(
5795                             raster_config.surface_index,
5796                             filter_task_id,
5797                             picture_task_id,
5798                             parent_surface_index,
5799                             clipped,
5800                         );
5801                     }
5802                 }
5804                 self.primary_render_task_id = primary_render_task_id;
5806                 // Update the device pixel ratio in the surface, in case it was adjusted due
5807                 // to the surface being too large. This ensures the correct scale is available
5808                 // in case it's used as input to a parent mix-blend-mode readback.
5809                 frame_state
5810                     .surfaces[raster_config.surface_index.0]
5811                     .device_pixel_scale = device_pixel_scale;
5812             }
5813             None => {}
5814         };
5817         #[cfg(feature = "capture")]
5818         {
5819             if frame_context.debug_flags.contains(DebugFlags::TILE_CACHE_LOGGING_DBG) {
5820                 if let Some(PictureCompositeMode::TileCache { slice_id }) = self.requested_composite_mode {
5821                     if let Some(ref tile_cache) = tile_caches.get(&slice_id) {
5822                         // extract just the fields that we're interested in
5823                         let mut tile_cache_tiny = TileCacheInstanceSerializer {
5824                             slice: tile_cache.slice,
5825                             tiles: FastHashMap::default(),
5826                             background_color: tile_cache.background_color,
5827                             fract_offset: tile_cache.fract_offset
5828                         };
5829                         // TODO(gw): Debug output only writes the primary sub-slice for now
5830                         for (key, tile) in &tile_cache.sub_slices.first().unwrap().tiles {
5831                             tile_cache_tiny.tiles.insert(*key, TileSerializer {
5832                                 rect: tile.local_tile_rect,
5833                                 current_descriptor: tile.current_descriptor.clone(),
5834                                 device_fract_offset: tile.device_fract_offset,
5835                                 id: tile.id,
5836                                 root: tile.root.clone(),
5837                                 background_color: tile.background_color,
5838                                 invalidation_reason: tile.invalidation_reason.clone()
5839                             });
5840                         }
5841                         let text = ron::ser::to_string_pretty(&tile_cache_tiny, Default::default()).unwrap();
5842                         tile_cache_logger.add(text, map_pic_to_world.get_transform());
5843                     }
5844                 }
5845             }
5846         }
5847         #[cfg(not(feature = "capture"))]
5848         {
5849             let _tile_cache_logger = tile_cache_logger;   // unused variable fix
5850         }
5852         let state = PictureState {
5853             //TODO: check for MAX_CACHE_SIZE here?
5854             map_local_to_pic,
5855             map_pic_to_world,
5856             map_pic_to_raster,
5857             map_raster_to_world,
5858             plane_splitter,
5859         };
5861         let mut dirty_region_count = 0;
5863         // If this is a picture cache, push the dirty region to ensure any
5864         // child primitives are culled and clipped to the dirty rect(s).
5865         if let Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) = self.raster_config {
5866             let dirty_region = tile_caches[&slice_id].dirty_region.clone();
5867             frame_state.push_dirty_region(dirty_region);
5868             dirty_region_count += 1;
5869         }
5871         if inflation_factor > 0.0 {
5872             let inflated_region = frame_state.current_dirty_region().inflate(
5873                 inflation_factor,
5874                 frame_context.spatial_tree,
5875             );
5876             frame_state.push_dirty_region(inflated_region);
5877             dirty_region_count += 1;
5878         }
5880         // Disallow subpixel AA if an intermediate surface is needed.
5881         // TODO(lsalzman): allow overriding parent if intermediate surface is opaque
5882         let subpixel_mode = match self.raster_config {
5883             Some(RasterConfig { ref composite_mode, .. }) => {
5884                 let subpixel_mode = match composite_mode {
5885                     PictureCompositeMode::TileCache { slice_id } => {
5886                         tile_caches[&slice_id].subpixel_mode
5887                     }
5888                     PictureCompositeMode::Blit(..) |
5889                     PictureCompositeMode::ComponentTransferFilter(..) |
5890                     PictureCompositeMode::Filter(..) |
5891                     PictureCompositeMode::MixBlend(..) |
5892                     PictureCompositeMode::SvgFilter(..) => {
5893                         // TODO(gw): We can take advantage of the same logic that
5894                         //           exists in the opaque rect detection for tile
5895                         //           caches, to allow subpixel text on other surfaces
5896                         //           that can be detected as opaque.
5897                         SubpixelMode::Deny
5898                     }
5899                 };
5901                 subpixel_mode
5902             }
5903             None => {
5904                 SubpixelMode::Allow
5905             }
5906         };
5908         // Still disable subpixel AA if parent forbids it
5909         let subpixel_mode = match (parent_subpixel_mode, subpixel_mode) {
5910             (SubpixelMode::Allow, SubpixelMode::Allow) => {
5911                 // Both parent and this surface unconditionally allow subpixel AA
5912                 SubpixelMode::Allow
5913             }
5914             (SubpixelMode::Allow, SubpixelMode::Conditional { allowed_rect }) => {
5915                 // Parent allows, but we are conditional subpixel AA
5916                 SubpixelMode::Conditional {
5917                     allowed_rect,
5918                 }
5919             }
5920             (SubpixelMode::Conditional { allowed_rect }, SubpixelMode::Allow) => {
5921                 // Propagate conditional subpixel mode to child pictures that allow subpixel AA
5922                 SubpixelMode::Conditional {
5923                     allowed_rect,
5924                 }
5925             }
5926             (SubpixelMode::Conditional { .. }, SubpixelMode::Conditional { ..}) => {
5927                 unreachable!("bug: only top level picture caches have conditional subpixel");
5928             }
5929             (SubpixelMode::Deny, _) | (_, SubpixelMode::Deny) => {
5930                 // Either parent or this surface explicitly deny subpixel, these take precedence
5931                 SubpixelMode::Deny
5932             }
5933         };
5935         let context = PictureContext {
5936             pic_index,
5937             apply_local_clip_rect: self.apply_local_clip_rect,
5938             raster_spatial_node_index,
5939             surface_spatial_node_index,
5940             surface_index,
5941             dirty_region_count,
5942             subpixel_mode,
5943         };
5945         let prim_list = mem::replace(&mut self.prim_list, PrimitiveList::empty());
5947         Some((context, state, prim_list))
5948     }
5950     pub fn restore_context(
5951         &mut self,
5952         prim_list: PrimitiveList,
5953         context: PictureContext,
5954         state: PictureState,
5955         frame_state: &mut FrameBuildingState,
5956     ) {
5957         // Pop any dirty regions this picture set
5958         for _ in 0 .. context.dirty_region_count {
5959             frame_state.pop_dirty_region();
5960         }
5962         self.prim_list = prim_list;
5963         self.state = Some(state);
5964     }
5966     pub fn take_state(&mut self) -> PictureState {
5967         self.state.take().expect("bug: no state present!")
5968     }
5970     /// Add a primitive instance to the plane splitter. The function would generate
5971     /// an appropriate polygon, clip it against the frustum, and register with the
5972     /// given plane splitter.
5973     pub fn add_split_plane(
5974         splitter: &mut PlaneSplitter,
5975         spatial_tree: &SpatialTree,
5976         prim_spatial_node_index: SpatialNodeIndex,
5977         original_local_rect: LayoutRect,
5978         combined_local_clip_rect: &LayoutRect,
5979         world_rect: WorldRect,
5980         plane_split_anchor: PlaneSplitAnchor,
5981     ) -> bool {
5982         let transform = spatial_tree
5983             .get_world_transform(prim_spatial_node_index);
5984         let matrix = transform.clone().into_transform().cast();
5986         // Apply the local clip rect here, before splitting. This is
5987         // because the local clip rect can't be applied in the vertex
5988         // shader for split composites, since we are drawing polygons
5989         // rather that rectangles. The interpolation still works correctly
5990         // since we determine the UVs by doing a bilerp with a factor
5991         // from the original local rect.
5992         let local_rect = match original_local_rect
5993             .intersection(combined_local_clip_rect)
5994         {
5995             Some(rect) => rect.cast(),
5996             None => return false,
5997         };
5998         let world_rect = world_rect.cast();
6000         match transform {
6001             CoordinateSpaceMapping::Local => {
6002                 let polygon = Polygon::from_rect(
6003                     local_rect * Scale::new(1.0),
6004                     plane_split_anchor,
6005                 );
6006                 splitter.add(polygon);
6007             }
6008             CoordinateSpaceMapping::ScaleOffset(scale_offset) if scale_offset.scale == Vector2D::new(1.0, 1.0) => {
6009                 let inv_matrix = scale_offset.inverse().to_transform().cast();
6010                 let polygon = Polygon::from_transformed_rect_with_inverse(
6011                     local_rect,
6012                     &matrix,
6013                     &inv_matrix,
6014                     plane_split_anchor,
6015                 ).unwrap();
6016                 splitter.add(polygon);
6017             }
6018             CoordinateSpaceMapping::ScaleOffset(_) |
6019             CoordinateSpaceMapping::Transform(_) => {
6020                 let mut clipper = Clipper::new();
6021                 let results = clipper.clip_transformed(
6022                     Polygon::from_rect(
6023                         local_rect,
6024                         plane_split_anchor,
6025                     ),
6026                     &matrix,
6027                     Some(world_rect),
6028                 );
6029                 if let Ok(results) = results {
6030                     for poly in results {
6031                         splitter.add(poly);
6032                     }
6033                 }
6034             }
6035         }
6037         true
6038     }
6040     pub fn resolve_split_planes(
6041         &mut self,
6042         splitter: &mut PlaneSplitter,
6043         gpu_cache: &mut GpuCache,
6044         spatial_tree: &SpatialTree,
6045     ) {
6046         let ordered = match self.context_3d {
6047             Picture3DContext::In { root_data: Some(ref mut list), .. } => list,
6048             _ => panic!("Expected to find 3D context root"),
6049         };
6050         ordered.clear();
6052         // Process the accumulated split planes and order them for rendering.
6053         // Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
6054         let sorted = splitter.sort(vec3(0.0, 0.0, 1.0));
6055         ordered.reserve(sorted.len());
6056         for poly in sorted {
6057             let cluster = &self.prim_list.clusters[poly.anchor.cluster_index];
6058             let spatial_node_index = cluster.spatial_node_index;
6059             let transform = match spatial_tree
6060                 .get_world_transform(spatial_node_index)
6061                 .inverse()
6062             {
6063                 Some(transform) => transform.into_transform(),
6064                 // logging this would be a bit too verbose
6065                 None => continue,
6066             };
6068             let local_points = [
6069                 transform.transform_point3d(poly.points[0].cast()),
6070                 transform.transform_point3d(poly.points[1].cast()),
6071                 transform.transform_point3d(poly.points[2].cast()),
6072                 transform.transform_point3d(poly.points[3].cast()),
6073             ];
6075             // If any of the points are un-transformable, just drop this
6076             // plane from drawing.
6077             if local_points.iter().any(|p| p.is_none()) {
6078                 continue;
6079             }
6081             let p0 = local_points[0].unwrap();
6082             let p1 = local_points[1].unwrap();
6083             let p2 = local_points[2].unwrap();
6084             let p3 = local_points[3].unwrap();
6085             let gpu_blocks = [
6086                 [p0.x, p0.y, p1.x, p1.y].into(),
6087                 [p2.x, p2.y, p3.x, p3.y].into(),
6088             ];
6089             let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
6090             let gpu_address = gpu_cache.get_address(&gpu_handle);
6092             ordered.push(OrderedPictureChild {
6093                 anchor: poly.anchor,
6094                 spatial_node_index,
6095                 gpu_address,
6096             });
6097         }
6098     }
6100     /// Called during initial picture traversal, before we know the
6101     /// bounding rect of children. It is possible to determine the
6102     /// surface / raster config now though.
6103     fn pre_update(
6104         &mut self,
6105         state: &mut PictureUpdateState,
6106         frame_context: &FrameBuildingContext,
6107     ) -> Option<PrimitiveList> {
6108         // Reset raster config in case we early out below.
6109         self.raster_config = None;
6111         // Resolve animation properties, and early out if the filter
6112         // properties make this picture invisible.
6113         if !self.resolve_scene_properties(frame_context.scene_properties) {
6114             return None;
6115         }
6117         // For out-of-preserve-3d pictures, the backface visibility is determined by
6118         // the local transform only.
6119         // Note: we aren't taking the transform relativce to the parent picture,
6120         // since picture tree can be more dense than the corresponding spatial tree.
6121         if !self.is_backface_visible {
6122             if let Picture3DContext::Out = self.context_3d {
6123                 match frame_context.spatial_tree.get_local_visible_face(self.spatial_node_index) {
6124                     VisibleFace::Front => {}
6125                     VisibleFace::Back => return None,
6126                 }
6127             }
6128         }
6130         // See if this picture actually needs a surface for compositing.
6131         // TODO(gw): FPC: Remove the actual / requested composite mode distinction.
6132         let actual_composite_mode = self.requested_composite_mode.clone();
6134         if let Some(composite_mode) = actual_composite_mode {
6135             // Retrieve the positioning node information for the parent surface.
6136             let parent_raster_node_index = state.current_surface().raster_spatial_node_index;
6137             let parent_device_pixel_scale = state.current_surface().device_pixel_scale;
6138             let surface_spatial_node_index = self.spatial_node_index;
6140             let surface_to_parent_transform = frame_context.spatial_tree
6141                 .get_relative_transform(surface_spatial_node_index, parent_raster_node_index);
6143             // Check if there is perspective or if an SVG filter is applied, and thus whether a new
6144             // rasterization root should be established.
6145             let establishes_raster_root = match composite_mode {
6146                 PictureCompositeMode::TileCache { .. } => {
6147                     // Picture caches are special cased - they never need to establish a raster root. In future,
6148                     // we will probably remove TileCache as a specific composite mode.
6149                     false
6150                 }
6151                 PictureCompositeMode::SvgFilter(..) => {
6152                     // Filters must be applied before transforms, to do this, we can mark this picture as establishing a raster root.
6153                     true
6154                 }
6155                 PictureCompositeMode::MixBlend(..) |
6156                 PictureCompositeMode::Filter(..) |
6157                 PictureCompositeMode::ComponentTransferFilter(..) |
6158                 PictureCompositeMode::Blit(..) => {
6159                     // TODO(gw): As follow ups, individually move each of these composite modes to create raster roots.
6160                     surface_to_parent_transform.is_perspective()
6161                 }
6162             };
6164             let (raster_spatial_node_index, device_pixel_scale) = if establishes_raster_root {
6165                 // If a raster root is established, this surface should be scaled based on the scale factors of the surface raster to parent raster transform.
6166                 // This scaling helps ensure that the content in this surface does not become blurry or pixelated when composited in the parent surface.
6167                 let scale_factors = surface_to_parent_transform.scale_factors();
6169                 // Pick the largest scale factor of the transform for the scaling factor.
6170                 // Currently, we ensure that the scaling factor is >= 1.0 as a smaller scale factor can result in blurry output.
6171                 let scaling_factor = scale_factors.0.max(scale_factors.1).max(1.0);
6173                 let device_pixel_scale = parent_device_pixel_scale * Scale::new(scaling_factor);
6174                 (surface_spatial_node_index, device_pixel_scale)
6175             } else {
6176                 (parent_raster_node_index, parent_device_pixel_scale)
6177             };
6179             let scale_factors = frame_context
6180                     .spatial_tree
6181                     .get_relative_transform(surface_spatial_node_index, raster_spatial_node_index)
6182                     .scale_factors();
6184             // This inflation factor is to be applied to all primitives within the surface.
6185             // Only inflate if the caller hasn't already inflated the bounding rects for this filter.
6186             let mut inflation_factor = 0.0;
6187             if self.options.inflate_if_required {
6188                 match composite_mode {
6189                     PictureCompositeMode::Filter(Filter::Blur(width, height)) => {
6190                         let blur_radius = f32::max(clamp_blur_radius(width, scale_factors), clamp_blur_radius(height, scale_factors));
6191                         // The amount of extra space needed for primitives inside
6192                         // this picture to ensure the visibility check is correct.
6193                         inflation_factor = blur_radius * BLUR_SAMPLE_SCALE;
6194                     }
6195                     PictureCompositeMode::SvgFilter(ref primitives, _) => {
6196                         let mut max = 0.0;
6197                         for primitive in primitives {
6198                             if let FilterPrimitiveKind::Blur(ref blur) = primitive.kind {
6199                                 max = f32::max(max, blur.width);
6200                                 max = f32::max(max, blur.height);
6201                             }
6202                         }
6203                         inflation_factor = clamp_blur_radius(max, scale_factors) * BLUR_SAMPLE_SCALE;
6204                     }
6205                     PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
6206                         // TODO(gw): This is incorrect, since we don't consider the drop shadow
6207                         //           offset. However, fixing that is a larger task, so this is
6208                         //           an improvement on the current case (this at least works where
6209                         //           the offset of the drop-shadow is ~0, which is often true).
6211                         // Can't use max_by_key here since f32 isn't Ord
6212                         let mut max_blur_radius: f32 = 0.0;
6213                         for shadow in shadows {
6214                             max_blur_radius = max_blur_radius.max(shadow.blur_radius);
6215                         }
6217                         inflation_factor = clamp_blur_radius(max_blur_radius, scale_factors) * BLUR_SAMPLE_SCALE;
6218                     }
6219                     _ => {}
6220                 }
6221             }
6223             let surface = SurfaceInfo::new(
6224                 surface_spatial_node_index,
6225                 raster_spatial_node_index,
6226                 inflation_factor,
6227                 frame_context.global_screen_world_rect,
6228                 &frame_context.spatial_tree,
6229                 device_pixel_scale,
6230                 scale_factors,
6231             );
6233             self.raster_config = Some(RasterConfig {
6234                 composite_mode,
6235                 establishes_raster_root,
6236                 surface_index: state.push_surface(surface),
6237                 root_scaling_factor: 1.0,
6238                 clipped_bounding_rect: WorldRect::zero(),
6239             });
6240         }
6242         Some(mem::replace(&mut self.prim_list, PrimitiveList::empty()))
6243     }
6245     /// Called after updating child pictures during the initial
6246     /// picture traversal.
6247     fn post_update(
6248         &mut self,
6249         prim_list: PrimitiveList,
6250         state: &mut PictureUpdateState,
6251         frame_context: &FrameBuildingContext,
6252         data_stores: &mut DataStores,
6253     ) {
6254         // Restore the pictures list used during recursion.
6255         self.prim_list = prim_list;
6257         let surface = state.current_surface_mut();
6259         for cluster in &mut self.prim_list.clusters {
6260             cluster.flags.remove(ClusterFlags::IS_VISIBLE);
6262             // Skip the cluster if backface culled.
6263             if !cluster.flags.contains(ClusterFlags::IS_BACKFACE_VISIBLE) {
6264                 // For in-preserve-3d primitives and pictures, the backface visibility is
6265                 // evaluated relative to the containing block.
6266                 if let Picture3DContext::In { ancestor_index, .. } = self.context_3d {
6267                     let mut face = VisibleFace::Front;
6268                     frame_context.spatial_tree.get_relative_transform_with_face(
6269                         cluster.spatial_node_index,
6270                         ancestor_index,
6271                         Some(&mut face),
6272                     );
6273                     if face == VisibleFace::Back {
6274                         continue
6275                     }
6276                 }
6277             }
6279             // No point including this cluster if it can't be transformed
6280             let spatial_node = &frame_context
6281                 .spatial_tree
6282                 .spatial_nodes[cluster.spatial_node_index.0 as usize];
6283             if !spatial_node.invertible {
6284                 continue;
6285             }
6287             // Update any primitives/cluster bounding rects that can only be done
6288             // with information available during frame building.
6289             if cluster.flags.contains(ClusterFlags::IS_BACKDROP_FILTER) {
6290                 let backdrop_to_world_mapper = SpaceMapper::new_with_target(
6291                     ROOT_SPATIAL_NODE_INDEX,
6292                     cluster.spatial_node_index,
6293                     LayoutRect::max_rect(),
6294                     frame_context.spatial_tree,
6295                 );
6297                 for prim_instance in &mut self.prim_list.prim_instances[cluster.prim_range()] {
6298                     match prim_instance.kind {
6299                         PrimitiveInstanceKind::Backdrop { data_handle, .. } => {
6300                             // The actual size and clip rect of this primitive are determined by computing the bounding
6301                             // box of the projected rect of the backdrop-filter element onto the backdrop.
6302                             let prim_data = &mut data_stores.backdrop[data_handle];
6303                             let spatial_node_index = prim_data.kind.spatial_node_index;
6305                             // We cannot use the relative transform between the backdrop and the element because
6306                             // that doesn't take into account any projection transforms that both spatial nodes are children of.
6307                             // Instead, we first project from the element to the world space and get a flattened 2D bounding rect
6308                             // in the screen space, we then map this rect from the world space to the backdrop space to get the
6309                             // proper bounding box where the backdrop-filter needs to be processed.
6311                             let prim_to_world_mapper = SpaceMapper::new_with_target(
6312                                 ROOT_SPATIAL_NODE_INDEX,
6313                                 spatial_node_index,
6314                                 LayoutRect::max_rect(),
6315                                 frame_context.spatial_tree,
6316                             );
6318                             // First map to the screen and get a flattened rect
6319                             let prim_rect = prim_to_world_mapper.map(&prim_data.kind.border_rect).unwrap_or_else(LayoutRect::zero);
6320                             // Backwards project the flattened rect onto the backdrop
6321                             let prim_rect = backdrop_to_world_mapper.unmap(&prim_rect).unwrap_or_else(LayoutRect::zero);
6323                             // TODO(aosmond): Is this safe? Updating the primitive size during
6324                             // frame building is usually problematic since scene building will cache
6325                             // the primitive information in the GPU already.
6326                             prim_data.common.prim_rect = prim_rect;
6327                             prim_instance.clip_set.local_clip_rect = prim_rect;
6329                             // Update the cluster bounding rect now that we have the backdrop rect.
6330                             cluster.bounding_rect = cluster.bounding_rect.union(&prim_rect);
6331                         }
6332                         _ => {
6333                             panic!("BUG: unexpected deferred primitive kind for cluster updates");
6334                         }
6335                     }
6336                 }
6337             }
6339             // Map the cluster bounding rect into the space of the surface, and
6340             // include it in the surface bounding rect.
6341             surface.map_local_to_surface.set_target_spatial_node(
6342                 cluster.spatial_node_index,
6343                 frame_context.spatial_tree,
6344             );
6346             // Mark the cluster visible, since it passed the invertible and
6347             // backface checks.
6348             cluster.flags.insert(ClusterFlags::IS_VISIBLE);
6349             if let Some(cluster_rect) = surface.map_local_to_surface.map(&cluster.bounding_rect) {
6350                 surface.rect = surface.rect.union(&cluster_rect);
6351             }
6352         }
6354         // If this picture establishes a surface, then map the surface bounding
6355         // rect into the parent surface coordinate space, and propagate that up
6356         // to the parent.
6357         if let Some(ref mut raster_config) = self.raster_config {
6358             let surface = state.current_surface_mut();
6359             // Inflate the local bounding rect if required by the filter effect.
6360             if self.options.inflate_if_required {
6361                 surface.rect = raster_config.composite_mode.inflate_picture_rect(surface.rect, surface.scale_factors);
6362             }
6364             let mut surface_rect = surface.rect * Scale::new(1.0);
6366             // Pop this surface from the stack
6367             let surface_index = state.pop_surface();
6368             debug_assert_eq!(surface_index, raster_config.surface_index);
6370             // Set the estimated and precise local rects. The precise local rect
6371             // may be changed again during frame visibility.
6372             self.estimated_local_rect = surface_rect;
6373             self.precise_local_rect = surface_rect;
6375             // Drop shadows draw both a content and shadow rect, so need to expand the local
6376             // rect of any surfaces to be composited in parent surfaces correctly.
6377             match raster_config.composite_mode {
6378                 PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
6379                     for shadow in shadows {
6380                         let shadow_rect = self.estimated_local_rect.translate(shadow.offset);
6381                         surface_rect = surface_rect.union(&shadow_rect);
6382                     }
6383                 }
6384                 _ => {}
6385             }
6387             // Propagate up to parent surface, now that we know this surface's static rect
6388             let parent_surface = state.current_surface_mut();
6389             parent_surface.map_local_to_surface.set_target_spatial_node(
6390                 self.spatial_node_index,
6391                 frame_context.spatial_tree,
6392             );
6393             if let Some(parent_surface_rect) = parent_surface
6394                 .map_local_to_surface
6395                 .map(&surface_rect)
6396             {
6397                 parent_surface.rect = parent_surface.rect.union(&parent_surface_rect);
6398             }
6399         }
6400     }
6402     pub fn prepare_for_render(
6403         &mut self,
6404         frame_context: &FrameBuildingContext,
6405         frame_state: &mut FrameBuildingState,
6406         data_stores: &mut DataStores,
6407     ) -> bool {
6408         let mut pic_state_for_children = self.take_state();
6410         if let Some(ref mut splitter) = pic_state_for_children.plane_splitter {
6411             self.resolve_split_planes(
6412                 splitter,
6413                 &mut frame_state.gpu_cache,
6414                 &frame_context.spatial_tree,
6415             );
6416         }
6418         let raster_config = match self.raster_config {
6419             Some(ref mut raster_config) => raster_config,
6420             None => {
6421                 return true
6422             }
6423         };
6425         // TODO(gw): Almost all of the Picture types below use extra_gpu_cache_data
6426         //           to store the same type of data. The exception is the filter
6427         //           with a ColorMatrix, which stores the color matrix here. It's
6428         //           probably worth tidying this code up to be a bit more consistent.
6429         //           Perhaps store the color matrix after the common data, even though
6430         //           it's not used by that shader.
6432         match raster_config.composite_mode {
6433             PictureCompositeMode::TileCache { .. } => {}
6434             PictureCompositeMode::Filter(Filter::Blur(..)) => {}
6435             PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
6436                 self.extra_gpu_data_handles.resize(shadows.len(), GpuCacheHandle::new());
6437                 for (shadow, extra_handle) in shadows.iter().zip(self.extra_gpu_data_handles.iter_mut()) {
6438                     if let Some(mut request) = frame_state.gpu_cache.request(extra_handle) {
6439                         // Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
6440                         //  [brush specific data]
6441                         //  [segment_rect, segment data]
6442                         let shadow_rect = self.precise_local_rect.translate(shadow.offset);
6444                         // ImageBrush colors
6445                         request.push(shadow.color.premultiplied());
6446                         request.push(PremultipliedColorF::WHITE);
6447                         request.push([
6448                             self.precise_local_rect.size.width,
6449                             self.precise_local_rect.size.height,
6450                             0.0,
6451                             0.0,
6452                         ]);
6454                         // segment rect / extra data
6455                         request.push(shadow_rect);
6456                         request.push([0.0, 0.0, 0.0, 0.0]);
6457                     }
6458                 }
6459             }
6460             PictureCompositeMode::Filter(ref filter) => {
6461                 match *filter {
6462                     Filter::ColorMatrix(ref m) => {
6463                         if self.extra_gpu_data_handles.is_empty() {
6464                             self.extra_gpu_data_handles.push(GpuCacheHandle::new());
6465                         }
6466                         if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handles[0]) {
6467                             for i in 0..5 {
6468                                 request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
6469                             }
6470                         }
6471                     }
6472                     Filter::Flood(ref color) => {
6473                         if self.extra_gpu_data_handles.is_empty() {
6474                             self.extra_gpu_data_handles.push(GpuCacheHandle::new());
6475                         }
6476                         if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handles[0]) {
6477                             request.push(color.to_array());
6478                         }
6479                     }
6480                     _ => {}
6481                 }
6482             }
6483             PictureCompositeMode::ComponentTransferFilter(handle) => {
6484                 let filter_data = &mut data_stores.filter_data[handle];
6485                 filter_data.update(frame_state);
6486             }
6487             PictureCompositeMode::MixBlend(..) |
6488             PictureCompositeMode::Blit(_) |
6489             PictureCompositeMode::SvgFilter(..) => {}
6490         }
6492         true
6493     }
6496 // Calculate a single homogeneous screen-space UV for a picture.
6497 fn calculate_screen_uv(
6498     local_pos: &PicturePoint,
6499     transform: &PictureToRasterTransform,
6500     rendered_rect: &DeviceRect,
6501     device_pixel_scale: DevicePixelScale,
6502 ) -> DeviceHomogeneousVector {
6503     let raster_pos = transform.transform_point2d_homogeneous(*local_pos);
6505     DeviceHomogeneousVector::new(
6506         (raster_pos.x * device_pixel_scale.0 - rendered_rect.origin.x * raster_pos.w) / rendered_rect.size.width,
6507         (raster_pos.y * device_pixel_scale.0 - rendered_rect.origin.y * raster_pos.w) / rendered_rect.size.height,
6508         0.0,
6509         raster_pos.w,
6510     )
6513 // Calculate a UV rect within an image based on the screen space
6514 // vertex positions of a picture.
6515 fn calculate_uv_rect_kind(
6516     pic_rect: &PictureRect,
6517     transform: &PictureToRasterTransform,
6518     rendered_rect: &DeviceRect,
6519     device_pixel_scale: DevicePixelScale,
6520 ) -> UvRectKind {
6521     let top_left = calculate_screen_uv(
6522         &pic_rect.origin,
6523         transform,
6524         &rendered_rect,
6525         device_pixel_scale,
6526     );
6528     let top_right = calculate_screen_uv(
6529         &pic_rect.top_right(),
6530         transform,
6531         &rendered_rect,
6532         device_pixel_scale,
6533     );
6535     let bottom_left = calculate_screen_uv(
6536         &pic_rect.bottom_left(),
6537         transform,
6538         &rendered_rect,
6539         device_pixel_scale,
6540     );
6542     let bottom_right = calculate_screen_uv(
6543         &pic_rect.bottom_right(),
6544         transform,
6545         &rendered_rect,
6546         device_pixel_scale,
6547     );
6549     UvRectKind::Quad {
6550         top_left,
6551         top_right,
6552         bottom_left,
6553         bottom_right,
6554     }
6557 fn create_raster_mappers(
6558     surface_spatial_node_index: SpatialNodeIndex,
6559     raster_spatial_node_index: SpatialNodeIndex,
6560     world_rect: WorldRect,
6561     spatial_tree: &SpatialTree,
6562 ) -> (SpaceMapper<RasterPixel, WorldPixel>, SpaceMapper<PicturePixel, RasterPixel>) {
6563     let map_raster_to_world = SpaceMapper::new_with_target(
6564         ROOT_SPATIAL_NODE_INDEX,
6565         raster_spatial_node_index,
6566         world_rect,
6567         spatial_tree,
6568     );
6570     let raster_bounds = map_raster_to_world.unmap(&world_rect)
6571                                            .unwrap_or_else(RasterRect::max_rect);
6573     let map_pic_to_raster = SpaceMapper::new_with_target(
6574         raster_spatial_node_index,
6575         surface_spatial_node_index,
6576         raster_bounds,
6577         spatial_tree,
6578     );
6580     (map_raster_to_world, map_pic_to_raster)
6583 fn get_transform_key(
6584     spatial_node_index: SpatialNodeIndex,
6585     cache_spatial_node_index: SpatialNodeIndex,
6586     spatial_tree: &SpatialTree,
6587 ) -> TransformKey {
6588     // Note: this is the only place where we don't know beforehand if the tile-affecting
6589     // spatial node is below or above the current picture.
6590     let transform = if cache_spatial_node_index >= spatial_node_index {
6591         spatial_tree
6592             .get_relative_transform(
6593                 cache_spatial_node_index,
6594                 spatial_node_index,
6595             )
6596     } else {
6597         spatial_tree
6598             .get_relative_transform(
6599                 spatial_node_index,
6600                 cache_spatial_node_index,
6601             )
6602     };
6603     transform.into()
6606 /// A key for storing primitive comparison results during tile dependency tests.
6607 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
6608 struct PrimitiveComparisonKey {
6609     prev_index: PrimitiveDependencyIndex,
6610     curr_index: PrimitiveDependencyIndex,
6613 /// Information stored an image dependency
6614 #[derive(Debug, Copy, Clone, PartialEq)]
6615 #[cfg_attr(feature = "capture", derive(Serialize))]
6616 #[cfg_attr(feature = "replay", derive(Deserialize))]
6617 pub struct ImageDependency {
6618     pub key: ImageKey,
6619     pub generation: ImageGeneration,
6622 impl ImageDependency {
6623     pub const INVALID: ImageDependency = ImageDependency {
6624         key: ImageKey::DUMMY,
6625         generation: ImageGeneration::INVALID,
6626     };
6629 /// A helper struct to compare a primitive and all its sub-dependencies.
6630 struct PrimitiveComparer<'a> {
6631     clip_comparer: CompareHelper<'a, ItemUid>,
6632     transform_comparer: CompareHelper<'a, SpatialNodeKey>,
6633     image_comparer: CompareHelper<'a, ImageDependency>,
6634     opacity_comparer: CompareHelper<'a, OpacityBinding>,
6635     color_comparer: CompareHelper<'a, ColorBinding>,
6636     resource_cache: &'a ResourceCache,
6637     spatial_node_comparer: &'a mut SpatialNodeComparer,
6638     opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>,
6639     color_bindings: &'a FastHashMap<PropertyBindingId, ColorBindingInfo>,
6642 impl<'a> PrimitiveComparer<'a> {
6643     fn new(
6644         prev: &'a TileDescriptor,
6645         curr: &'a TileDescriptor,
6646         resource_cache: &'a ResourceCache,
6647         spatial_node_comparer: &'a mut SpatialNodeComparer,
6648         opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>,
6649         color_bindings: &'a FastHashMap<PropertyBindingId, ColorBindingInfo>,
6650     ) -> Self {
6651         let clip_comparer = CompareHelper::new(
6652             &prev.clips,
6653             &curr.clips,
6654         );
6656         let transform_comparer = CompareHelper::new(
6657             &prev.transforms,
6658             &curr.transforms,
6659         );
6661         let image_comparer = CompareHelper::new(
6662             &prev.images,
6663             &curr.images,
6664         );
6666         let opacity_comparer = CompareHelper::new(
6667             &prev.opacity_bindings,
6668             &curr.opacity_bindings,
6669         );
6671         let color_comparer = CompareHelper::new(
6672             &prev.color_bindings,
6673             &curr.color_bindings,
6674         );
6676         PrimitiveComparer {
6677             clip_comparer,
6678             transform_comparer,
6679             image_comparer,
6680             opacity_comparer,
6681             color_comparer,
6682             resource_cache,
6683             spatial_node_comparer,
6684             opacity_bindings,
6685             color_bindings,
6686         }
6687     }
6689     fn reset(&mut self) {
6690         self.clip_comparer.reset();
6691         self.transform_comparer.reset();
6692         self.image_comparer.reset();
6693         self.opacity_comparer.reset();
6694         self.color_comparer.reset();
6695     }
6697     fn advance_prev(&mut self, prim: &PrimitiveDescriptor) {
6698         self.clip_comparer.advance_prev(prim.clip_dep_count);
6699         self.transform_comparer.advance_prev(prim.transform_dep_count);
6700         self.image_comparer.advance_prev(prim.image_dep_count);
6701         self.opacity_comparer.advance_prev(prim.opacity_binding_dep_count);
6702         self.color_comparer.advance_prev(prim.color_binding_dep_count);
6703     }
6705     fn advance_curr(&mut self, prim: &PrimitiveDescriptor) {
6706         self.clip_comparer.advance_curr(prim.clip_dep_count);
6707         self.transform_comparer.advance_curr(prim.transform_dep_count);
6708         self.image_comparer.advance_curr(prim.image_dep_count);
6709         self.opacity_comparer.advance_curr(prim.opacity_binding_dep_count);
6710         self.color_comparer.advance_curr(prim.color_binding_dep_count);
6711     }
6713     /// Check if two primitive descriptors are the same.
6714     fn compare_prim(
6715         &mut self,
6716         prev: &PrimitiveDescriptor,
6717         curr: &PrimitiveDescriptor,
6718         opt_detail: Option<&mut PrimitiveCompareResultDetail>,
6719     ) -> PrimitiveCompareResult {
6720         let resource_cache = self.resource_cache;
6721         let spatial_node_comparer = &mut self.spatial_node_comparer;
6722         let opacity_bindings = self.opacity_bindings;
6723         let color_bindings = self.color_bindings;
6725         // Check equality of the PrimitiveDescriptor
6726         if prev != curr {
6727             if let Some(detail) = opt_detail {
6728                 *detail = PrimitiveCompareResultDetail::Descriptor{ old: *prev, new: *curr };
6729             }
6730             return PrimitiveCompareResult::Descriptor;
6731         }
6733         // Check if any of the clips  this prim has are different.
6734         let mut clip_result = CompareHelperResult::Equal;
6735         if !self.clip_comparer.is_same(
6736             prev.clip_dep_count,
6737             curr.clip_dep_count,
6738             |prev, curr| {
6739                 prev == curr
6740             },
6741             if opt_detail.is_some() { Some(&mut clip_result) } else { None }
6742         ) {
6743             if let Some(detail) = opt_detail { *detail = PrimitiveCompareResultDetail::Clip{ detail: clip_result }; }
6744             return PrimitiveCompareResult::Clip;
6745         }
6747         // Check if any of the transforms  this prim has are different.
6748         let mut transform_result = CompareHelperResult::Equal;
6749         if !self.transform_comparer.is_same(
6750             prev.transform_dep_count,
6751             curr.transform_dep_count,
6752             |prev, curr| {
6753                 spatial_node_comparer.are_transforms_equivalent(prev, curr)
6754             },
6755             if opt_detail.is_some() { Some(&mut transform_result) } else { None },
6756         ) {
6757             if let Some(detail) = opt_detail {
6758                 *detail = PrimitiveCompareResultDetail::Transform{ detail: transform_result };
6759             }
6760             return PrimitiveCompareResult::Transform;
6761         }
6763         // Check if any of the images this prim has are different.
6764         let mut image_result = CompareHelperResult::Equal;
6765         if !self.image_comparer.is_same(
6766             prev.image_dep_count,
6767             curr.image_dep_count,
6768             |prev, curr| {
6769                 prev == curr &&
6770                 resource_cache.get_image_generation(curr.key) == curr.generation
6771             },
6772             if opt_detail.is_some() { Some(&mut image_result) } else { None },
6773         ) {
6774             if let Some(detail) = opt_detail {
6775                 *detail = PrimitiveCompareResultDetail::Image{ detail: image_result };
6776             }
6777             return PrimitiveCompareResult::Image;
6778         }
6780         // Check if any of the opacity bindings this prim has are different.
6781         let mut bind_result = CompareHelperResult::Equal;
6782         if !self.opacity_comparer.is_same(
6783             prev.opacity_binding_dep_count,
6784             curr.opacity_binding_dep_count,
6785             |prev, curr| {
6786                 if prev != curr {
6787                     return false;
6788                 }
6790                 if let OpacityBinding::Binding(id) = curr {
6791                     if opacity_bindings
6792                         .get(id)
6793                         .map_or(true, |info| info.changed) {
6794                         return false;
6795                     }
6796                 }
6798                 true
6799             },
6800             if opt_detail.is_some() { Some(&mut bind_result) } else { None },
6801         ) {
6802             if let Some(detail) = opt_detail {
6803                 *detail = PrimitiveCompareResultDetail::OpacityBinding{ detail: bind_result };
6804             }
6805             return PrimitiveCompareResult::OpacityBinding;
6806         }
6808         // Check if any of the color bindings this prim has are different.
6809         let mut bind_result = CompareHelperResult::Equal;
6810         if !self.color_comparer.is_same(
6811             prev.color_binding_dep_count,
6812             curr.color_binding_dep_count,
6813             |prev, curr| {
6814                 if prev != curr {
6815                     return false;
6816                 }
6818                 if let ColorBinding::Binding(id) = curr {
6819                     if color_bindings
6820                         .get(id)
6821                         .map_or(true, |info| info.changed) {
6822                         return false;
6823                     }
6824                 }
6826                 true
6827             },
6828             if opt_detail.is_some() { Some(&mut bind_result) } else { None },
6829         ) {
6830             if let Some(detail) = opt_detail {
6831                 *detail = PrimitiveCompareResultDetail::ColorBinding{ detail: bind_result };
6832             }
6833             return PrimitiveCompareResult::ColorBinding;
6834         }
6836         PrimitiveCompareResult::Equal
6837     }
6840 /// Details for a node in a quadtree that tracks dirty rects for a tile.
6841 #[cfg_attr(any(feature="capture",feature="replay"), derive(Clone))]
6842 #[cfg_attr(feature = "capture", derive(Serialize))]
6843 #[cfg_attr(feature = "replay", derive(Deserialize))]
6844 pub enum TileNodeKind {
6845     Leaf {
6846         /// The index buffer of primitives that affected this tile previous frame
6847         #[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
6848         prev_indices: Vec<PrimitiveDependencyIndex>,
6849         /// The index buffer of primitives that affect this tile on this frame
6850         #[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
6851         curr_indices: Vec<PrimitiveDependencyIndex>,
6852         /// A bitset of which of the last 64 frames have been dirty for this leaf.
6853         #[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
6854         dirty_tracker: u64,
6855         /// The number of frames since this node split or merged.
6856         #[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
6857         frames_since_modified: usize,
6858     },
6859     Node {
6860         /// The four children of this node
6861         children: Vec<TileNode>,
6862     },
6865 /// The kind of modification that a tile wants to do
6866 #[derive(Copy, Clone, PartialEq, Debug)]
6867 enum TileModification {
6868     Split,
6869     Merge,
6872 /// A node in the dirty rect tracking quadtree.
6873 #[cfg_attr(any(feature="capture",feature="replay"), derive(Clone))]
6874 #[cfg_attr(feature = "capture", derive(Serialize))]
6875 #[cfg_attr(feature = "replay", derive(Deserialize))]
6876 pub struct TileNode {
6877     /// Leaf or internal node
6878     pub kind: TileNodeKind,
6879     /// Rect of this node in the same space as the tile cache picture
6880     pub rect: PictureBox2D,
6883 impl TileNode {
6884     /// Construct a new leaf node, with the given primitive dependency index buffer
6885     fn new_leaf(curr_indices: Vec<PrimitiveDependencyIndex>) -> Self {
6886         TileNode {
6887             kind: TileNodeKind::Leaf {
6888                 prev_indices: Vec::new(),
6889                 curr_indices,
6890                 dirty_tracker: 0,
6891                 frames_since_modified: 0,
6892             },
6893             rect: PictureBox2D::zero(),
6894         }
6895     }
6897     /// Draw debug information about this tile node
6898     fn draw_debug_rects(
6899         &self,
6900         pic_to_world_mapper: &SpaceMapper<PicturePixel, WorldPixel>,
6901         is_opaque: bool,
6902         local_valid_rect: PictureRect,
6903         scratch: &mut PrimitiveScratchBuffer,
6904         global_device_pixel_scale: DevicePixelScale,
6905     ) {
6906         match self.kind {
6907             TileNodeKind::Leaf { dirty_tracker, .. } => {
6908                 let color = if (dirty_tracker & 1) != 0 {
6909                     debug_colors::RED
6910                 } else if is_opaque {
6911                     debug_colors::GREEN
6912                 } else {
6913                     debug_colors::YELLOW
6914                 };
6916                 if let Some(local_rect) = local_valid_rect.intersection(&self.rect.to_rect()) {
6917                     let world_rect = pic_to_world_mapper
6918                         .map(&local_rect)
6919                         .unwrap();
6920                     let device_rect = world_rect * global_device_pixel_scale;
6922                     let outer_color = color.scale_alpha(0.3);
6923                     let inner_color = outer_color.scale_alpha(0.5);
6924                     scratch.push_debug_rect(
6925                         device_rect.inflate(-3.0, -3.0),
6926                         outer_color,
6927                         inner_color
6928                     );
6929                 }
6930             }
6931             TileNodeKind::Node { ref children, .. } => {
6932                 for child in children.iter() {
6933                     child.draw_debug_rects(
6934                         pic_to_world_mapper,
6935                         is_opaque,
6936                         local_valid_rect,
6937                         scratch,
6938                         global_device_pixel_scale,
6939                     );
6940                 }
6941             }
6942         }
6943     }
6945     /// Calculate the four child rects for a given node
6946     fn get_child_rects(
6947         rect: &PictureBox2D,
6948         result: &mut [PictureBox2D; 4],
6949     ) {
6950         let p0 = rect.min;
6951         let p1 = rect.max;
6952         let pc = p0 + rect.size() * 0.5;
6954         *result = [
6955             PictureBox2D::new(
6956                 p0,
6957                 pc,
6958             ),
6959             PictureBox2D::new(
6960                 PicturePoint::new(pc.x, p0.y),
6961                 PicturePoint::new(p1.x, pc.y),
6962             ),
6963             PictureBox2D::new(
6964                 PicturePoint::new(p0.x, pc.y),
6965                 PicturePoint::new(pc.x, p1.y),
6966             ),
6967             PictureBox2D::new(
6968                 pc,
6969                 p1,
6970             ),
6971         ];
6972     }
6974     /// Called during pre_update, to clear the current dependencies
6975     fn clear(
6976         &mut self,
6977         rect: PictureBox2D,
6978     ) {
6979         self.rect = rect;
6981         match self.kind {
6982             TileNodeKind::Leaf { ref mut prev_indices, ref mut curr_indices, ref mut dirty_tracker, ref mut frames_since_modified } => {
6983                 // Swap current dependencies to be the previous frame
6984                 mem::swap(prev_indices, curr_indices);
6985                 curr_indices.clear();
6986                 // Note that another frame has passed in the dirty bit trackers
6987                 *dirty_tracker = *dirty_tracker << 1;
6988                 *frames_since_modified += 1;
6989             }
6990             TileNodeKind::Node { ref mut children, .. } => {
6991                 let mut child_rects = [PictureBox2D::zero(); 4];
6992                 TileNode::get_child_rects(&rect, &mut child_rects);
6993                 assert_eq!(child_rects.len(), children.len());
6995                 for (child, rect) in children.iter_mut().zip(child_rects.iter()) {
6996                     child.clear(*rect);
6997                 }
6998             }
6999         }
7000     }
7002     /// Add a primitive dependency to this node
7003     fn add_prim(
7004         &mut self,
7005         index: PrimitiveDependencyIndex,
7006         prim_rect: &PictureBox2D,
7007     ) {
7008         match self.kind {
7009             TileNodeKind::Leaf { ref mut curr_indices, .. } => {
7010                 curr_indices.push(index);
7011             }
7012             TileNodeKind::Node { ref mut children, .. } => {
7013                 for child in children.iter_mut() {
7014                     if child.rect.intersects(prim_rect) {
7015                         child.add_prim(index, prim_rect);
7016                     }
7017                 }
7018             }
7019         }
7020     }
7022     /// Apply a merge or split operation to this tile, if desired
7023     fn maybe_merge_or_split(
7024         &mut self,
7025         level: i32,
7026         curr_prims: &[PrimitiveDescriptor],
7027         max_split_levels: i32,
7028     ) {
7029         // Determine if this tile wants to split or merge
7030         let mut tile_mod = None;
7032         fn get_dirty_frames(
7033             dirty_tracker: u64,
7034             frames_since_modified: usize,
7035         ) -> Option<u32> {
7036             // Only consider splitting or merging at least 64 frames since we last changed
7037             if frames_since_modified > 64 {
7038                 // Each bit in the tracker is a frame that was recently invalidated
7039                 Some(dirty_tracker.count_ones())
7040             } else {
7041                 None
7042             }
7043         }
7045         match self.kind {
7046             TileNodeKind::Leaf { dirty_tracker, frames_since_modified, .. } => {
7047                 // Only consider splitting if the tree isn't too deep.
7048                 if level < max_split_levels {
7049                     if let Some(dirty_frames) = get_dirty_frames(dirty_tracker, frames_since_modified) {
7050                         // If the tile has invalidated > 50% of the recent number of frames, split.
7051                         if dirty_frames > 32 {
7052                             tile_mod = Some(TileModification::Split);
7053                         }
7054                     }
7055                 }
7056             }
7057             TileNodeKind::Node { ref children, .. } => {
7058                 // There's two conditions that cause a node to merge its children:
7059                 // (1) If _all_ the child nodes are constantly invalidating, then we are wasting
7060                 //     CPU time tracking dependencies for each child, so merge them.
7061                 // (2) If _none_ of the child nodes are recently invalid, then the page content
7062                 //     has probably changed, and we no longer need to track fine grained dependencies here.
7064                 let mut static_count = 0;
7065                 let mut changing_count = 0;
7067                 for child in children {
7068                     // Only consider merging nodes at the edge of the tree.
7069                     if let TileNodeKind::Leaf { dirty_tracker, frames_since_modified, .. } = child.kind {
7070                         if let Some(dirty_frames) = get_dirty_frames(dirty_tracker, frames_since_modified) {
7071                             if dirty_frames == 0 {
7072                                 // Hasn't been invalidated for some time
7073                                 static_count += 1;
7074                             } else if dirty_frames == 64 {
7075                                 // Is constantly being invalidated
7076                                 changing_count += 1;
7077                             }
7078                         }
7079                     }
7081                     // Only merge if all the child tiles are in agreement. Otherwise, we have some
7082                     // that are invalidating / static, and it's worthwhile tracking dependencies for
7083                     // them individually.
7084                     if static_count == 4 || changing_count == 4 {
7085                         tile_mod = Some(TileModification::Merge);
7086                     }
7087                 }
7088             }
7089         }
7091         match tile_mod {
7092             Some(TileModification::Split) => {
7093                 // To split a node, take the current dependency index buffer for this node, and
7094                 // split it into child index buffers.
7095                 let curr_indices = match self.kind {
7096                     TileNodeKind::Node { .. } => {
7097                         unreachable!("bug - only leaves can split");
7098                     }
7099                     TileNodeKind::Leaf { ref mut curr_indices, .. } => {
7100                         curr_indices.take()
7101                     }
7102                 };
7104                 let mut child_rects = [PictureBox2D::zero(); 4];
7105                 TileNode::get_child_rects(&self.rect, &mut child_rects);
7107                 let mut child_indices = [
7108                     Vec::new(),
7109                     Vec::new(),
7110                     Vec::new(),
7111                     Vec::new(),
7112                 ];
7114                 // Step through the index buffer, and add primitives to each of the children
7115                 // that they intersect.
7116                 for index in curr_indices {
7117                     let prim = &curr_prims[index.0 as usize];
7118                     for (child_rect, indices) in child_rects.iter().zip(child_indices.iter_mut()) {
7119                         if prim.prim_clip_box.intersects(child_rect) {
7120                             indices.push(index);
7121                         }
7122                     }
7123                 }
7125                 // Create the child nodes and switch from leaf -> node.
7126                 let children = child_indices
7127                     .iter_mut()
7128                     .map(|i| TileNode::new_leaf(mem::replace(i, Vec::new())))
7129                     .collect();
7131                 self.kind = TileNodeKind::Node {
7132                     children,
7133                 };
7134             }
7135             Some(TileModification::Merge) => {
7136                 // Construct a merged index buffer by collecting the dependency index buffers
7137                 // from each child, and merging them into a de-duplicated index buffer.
7138                 let merged_indices = match self.kind {
7139                     TileNodeKind::Node { ref mut children, .. } => {
7140                         let mut merged_indices = Vec::new();
7142                         for child in children.iter() {
7143                             let child_indices = match child.kind {
7144                                 TileNodeKind::Leaf { ref curr_indices, .. } => {
7145                                     curr_indices
7146                                 }
7147                                 TileNodeKind::Node { .. } => {
7148                                     unreachable!("bug: child is not a leaf");
7149                                 }
7150                             };
7151                             merged_indices.extend_from_slice(child_indices);
7152                         }
7154                         merged_indices.sort();
7155                         merged_indices.dedup();
7157                         merged_indices
7158                     }
7159                     TileNodeKind::Leaf { .. } => {
7160                         unreachable!("bug - trying to merge a leaf");
7161                     }
7162                 };
7164                 // Switch from a node to a leaf, with the combined index buffer
7165                 self.kind = TileNodeKind::Leaf {
7166                     prev_indices: Vec::new(),
7167                     curr_indices: merged_indices,
7168                     dirty_tracker: 0,
7169                     frames_since_modified: 0,
7170                 };
7171             }
7172             None => {
7173                 // If this node didn't merge / split, then recurse into children
7174                 // to see if they want to split / merge.
7175                 if let TileNodeKind::Node { ref mut children, .. } = self.kind {
7176                     for child in children.iter_mut() {
7177                         child.maybe_merge_or_split(
7178                             level+1,
7179                             curr_prims,
7180                             max_split_levels,
7181                         );
7182                     }
7183                 }
7184             }
7185         }
7186     }
7188     /// Update the dirty state of this node, building the overall dirty rect
7189     fn update_dirty_rects(
7190         &mut self,
7191         prev_prims: &[PrimitiveDescriptor],
7192         curr_prims: &[PrimitiveDescriptor],
7193         prim_comparer: &mut PrimitiveComparer,
7194         dirty_rect: &mut PictureBox2D,
7195         compare_cache: &mut FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>,
7196         invalidation_reason: &mut Option<InvalidationReason>,
7197         frame_context: &FrameVisibilityContext,
7198     ) {
7199         match self.kind {
7200             TileNodeKind::Node { ref mut children, .. } => {
7201                 for child in children.iter_mut() {
7202                     child.update_dirty_rects(
7203                         prev_prims,
7204                         curr_prims,
7205                         prim_comparer,
7206                         dirty_rect,
7207                         compare_cache,
7208                         invalidation_reason,
7209                         frame_context,
7210                     );
7211                 }
7212             }
7213             TileNodeKind::Leaf { ref prev_indices, ref curr_indices, ref mut dirty_tracker, .. } => {
7214                 // If the index buffers are of different length, they must be different
7215                 if prev_indices.len() == curr_indices.len() {
7216                     let mut prev_i0 = 0;
7217                     let mut prev_i1 = 0;
7218                     prim_comparer.reset();
7220                     // Walk each index buffer, comparing primitives
7221                     for (prev_index, curr_index) in prev_indices.iter().zip(curr_indices.iter()) {
7222                         let i0 = prev_index.0 as usize;
7223                         let i1 = curr_index.0 as usize;
7225                         // Advance the dependency arrays for each primitive (this handles
7226                         // prims that may be skipped by these index buffers).
7227                         for i in prev_i0 .. i0 {
7228                             prim_comparer.advance_prev(&prev_prims[i]);
7229                         }
7230                         for i in prev_i1 .. i1 {
7231                             prim_comparer.advance_curr(&curr_prims[i]);
7232                         }
7234                         // Compare the primitives, caching the result in a hash map
7235                         // to save comparisons in other tree nodes.
7236                         let key = PrimitiveComparisonKey {
7237                             prev_index: *prev_index,
7238                             curr_index: *curr_index,
7239                         };
7241                         #[cfg(any(feature = "capture", feature = "replay"))]
7242                         let mut compare_detail = PrimitiveCompareResultDetail::Equal;
7243                         #[cfg(any(feature = "capture", feature = "replay"))]
7244                         let prim_compare_result_detail =
7245                             if frame_context.debug_flags.contains(DebugFlags::TILE_CACHE_LOGGING_DBG) {
7246                                 Some(&mut compare_detail)
7247                             } else {
7248                                 None
7249                             };
7251                         #[cfg(not(any(feature = "capture", feature = "replay")))]
7252                         let compare_detail = PrimitiveCompareResultDetail::Equal;
7253                         #[cfg(not(any(feature = "capture", feature = "replay")))]
7254                         let prim_compare_result_detail = None;
7256                         let prim_compare_result = *compare_cache
7257                             .entry(key)
7258                             .or_insert_with(|| {
7259                                 let prev = &prev_prims[i0];
7260                                 let curr = &curr_prims[i1];
7261                                 prim_comparer.compare_prim(prev, curr, prim_compare_result_detail)
7262                             });
7264                         // If not the same, mark this node as dirty and update the dirty rect
7265                         if prim_compare_result != PrimitiveCompareResult::Equal {
7266                             if invalidation_reason.is_none() {
7267                                 *invalidation_reason = Some(InvalidationReason::Content {
7268                                     prim_compare_result,
7269                                     prim_compare_result_detail: Some(compare_detail)
7270                                 });
7271                             }
7272                             *dirty_rect = self.rect.union(dirty_rect);
7273                             *dirty_tracker = *dirty_tracker | 1;
7274                             break;
7275                         }
7277                         prev_i0 = i0;
7278                         prev_i1 = i1;
7279                     }
7280                 } else {
7281                     if invalidation_reason.is_none() {
7282                         // if and only if tile logging is enabled, do the expensive step of
7283                         // converting indices back to ItemUids and allocating old and new vectors
7284                         // to store them in.
7285                         #[cfg(any(feature = "capture", feature = "replay"))]
7286                         {
7287                             if frame_context.debug_flags.contains(DebugFlags::TILE_CACHE_LOGGING_DBG) {
7288                                 let old = prev_indices.iter().map( |i| prev_prims[i.0 as usize].prim_uid ).collect();
7289                                 let new = curr_indices.iter().map( |i| curr_prims[i.0 as usize].prim_uid ).collect();
7290                                 *invalidation_reason = Some(InvalidationReason::PrimCount {
7291                                                                 old: Some(old),
7292                                                                 new: Some(new) });
7293                             } else {
7294                                 *invalidation_reason = Some(InvalidationReason::PrimCount {
7295                                                                 old: None,
7296                                                                 new: None });
7297                             }
7298                         }
7299                         #[cfg(not(any(feature = "capture", feature = "replay")))]
7300                         {
7301                             *invalidation_reason = Some(InvalidationReason::PrimCount {
7302                                                                 old: None,
7303                                                                 new: None });
7304                         }
7305                     }
7306                     *dirty_rect = self.rect.union(dirty_rect);
7307                     *dirty_tracker = *dirty_tracker | 1;
7308                 }
7309             }
7310         }
7311     }
7314 impl CompositeState {
7315     // A helper function to destroy all native surfaces for a given list of tiles
7316     pub fn destroy_native_tiles<'a, I: Iterator<Item = &'a mut Box<Tile>>>(
7317         &mut self,
7318         tiles_iter: I,
7319         resource_cache: &mut ResourceCache,
7320     ) {
7321         // Any old tiles that remain after the loop above are going to be dropped. For
7322         // simple composite mode, the texture cache handle will expire and be collected
7323         // by the texture cache. For native compositor mode, we need to explicitly
7324         // invoke a callback to the client to destroy that surface.
7325         if let CompositorKind::Native { .. } = self.compositor_kind {
7326             for tile in tiles_iter {
7327                 // Only destroy native surfaces that have been allocated. It's
7328                 // possible for display port tiles to be created that never
7329                 // come on screen, and thus never get a native surface allocated.
7330                 if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface {
7331                     if let Some(id) = id.take() {
7332                         resource_cache.destroy_compositor_tile(id);
7333                     }
7334                 }
7335             }
7336         }
7337     }
7340 pub fn get_raster_rects(
7341     pic_rect: PictureRect,
7342     map_to_raster: &SpaceMapper<PicturePixel, RasterPixel>,
7343     map_to_world: &SpaceMapper<RasterPixel, WorldPixel>,
7344     prim_bounding_rect: WorldRect,
7345     device_pixel_scale: DevicePixelScale,
7346 ) -> Option<(DeviceRect, DeviceRect)> {
7347     let unclipped_raster_rect = map_to_raster.map(&pic_rect)?;
7349     let unclipped = raster_rect_to_device_pixels(
7350         unclipped_raster_rect,
7351         device_pixel_scale,
7352     );
7354     let unclipped_world_rect = map_to_world.map(&unclipped_raster_rect)?;
7355     let clipped_world_rect = unclipped_world_rect.intersection(&prim_bounding_rect)?;
7357     // We don't have to be able to do the back-projection from world into raster.
7358     // Rendering only cares one way, so if that fails, we fall back to the full rect.
7359     let clipped_raster_rect = match map_to_world.unmap(&clipped_world_rect) {
7360         Some(rect) => rect.intersection(&unclipped_raster_rect)?,
7361         None => return Some((unclipped, unclipped)),
7362     };
7364     let clipped = raster_rect_to_device_pixels(
7365         clipped_raster_rect,
7366         device_pixel_scale,
7367     );
7369     // Ensure that we won't try to allocate a zero-sized clip render task.
7370     if clipped.is_empty() {
7371         return None;
7372     }
7374     Some((clipped, unclipped))