Skip to main content

webrender/
composite.rs

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/. */
4
5use api::{BorderRadius, ColorF, ExternalImageId, ImageBufferKind, ImageKey, ImageRendering, YuvFormat, YuvRangedColorSpace};
6use api::units::*;
7use api::ColorDepth;
8use crate::image_source::resolve_image;
9use crate::picture::ResolvedSurfaceTexture;
10use crate::renderer::GpuBufferBuilderF;
11use euclid::Box2D;
12use crate::gpu_types::{ZBufferId, ZBufferIdGenerator};
13use crate::internal_types::{FrameAllocator, FrameMemory, FrameVec, TextureSource};
14use crate::invalidation::compare::ImageDependency;
15use crate::tile_cache::{TileCacheInstance, TileSurface};
16use crate::tile_cache::TileId;
17use crate::prim_store::{DeferredResolve, PrimitiveInstanceIndex};
18use crate::resource_cache::{ImageRequest, ResourceCache};
19use crate::segment::EdgeMask;
20use crate::util::{extract_inner_rect_safe, Preallocator, ScaleOffset};
21use crate::tile_cache::PictureCacheDebugInfo;
22use crate::device::Device;
23use crate::space::SpaceMapper;
24use std::{ops, u64, os::raw::c_void, hash};
25use std::num::NonZeroUsize;
26
27/*
28 Types and definitions related to compositing picture cache tiles
29 and/or OS compositor integration.
30 */
31
32/// Which method is being used to draw a requested compositor surface
33#[cfg_attr(feature = "capture", derive(Serialize))]
34#[cfg_attr(feature = "replay", derive(Deserialize))]
35#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)]
36pub enum CompositorSurfaceKind {
37    /// Don't create a native compositor surface, blit it as a regular primitive
38    Blit,
39    /// Create a native surface, draw it under content (must be opaque)
40    Underlay,
41    /// Create a native surface, draw it between sub-slices (supports transparent)
42    Overlay,
43}
44
45impl CompositorSurfaceKind {
46    pub fn is_composited(&self) -> bool {
47        match *self {
48            CompositorSurfaceKind::Blit => false,
49            CompositorSurfaceKind::Underlay | CompositorSurfaceKind::Overlay => true,
50        }
51    }
52}
53
54/// Describes details of an operation to apply to a native surface
55#[derive(Debug, Clone)]
56#[cfg_attr(feature = "capture", derive(Serialize))]
57#[cfg_attr(feature = "replay", derive(Deserialize))]
58pub enum NativeSurfaceOperationDetails {
59    CreateSurface {
60        id: NativeSurfaceId,
61        virtual_offset: DeviceIntPoint,
62        tile_size: DeviceIntSize,
63        is_opaque: bool,
64    },
65    CreateExternalSurface {
66        id: NativeSurfaceId,
67        is_opaque: bool,
68    },
69    CreateBackdropSurface {
70        id: NativeSurfaceId,
71        color: ColorF,
72    },
73    DestroySurface {
74        id: NativeSurfaceId,
75    },
76    CreateTile {
77        id: NativeTileId,
78    },
79    DestroyTile {
80        id: NativeTileId,
81    },
82    AttachExternalImage {
83        id: NativeSurfaceId,
84        external_image: ExternalImageId,
85    }
86}
87
88/// Describes an operation to apply to a native surface
89#[derive(Debug, Clone)]
90#[cfg_attr(feature = "capture", derive(Serialize))]
91#[cfg_attr(feature = "replay", derive(Deserialize))]
92pub struct NativeSurfaceOperation {
93    pub details: NativeSurfaceOperationDetails,
94}
95
96/// Describes the source surface information for a tile to be composited. This
97/// is the analog of the TileSurface type, with target surface information
98/// resolved such that it can be used by the renderer.
99#[cfg_attr(feature = "capture", derive(Serialize))]
100#[cfg_attr(feature = "replay", derive(Deserialize))]
101#[derive(Clone)]
102pub enum CompositeTileSurface {
103    Texture {
104        surface: ResolvedSurfaceTexture,
105    },
106    Color {
107        color: ColorF,
108    },
109    ExternalSurface {
110        external_surface_index: ResolvedExternalSurfaceIndex,
111    },
112}
113
114/// The surface format for a tile being composited.
115#[derive(Debug, Copy, Clone, PartialEq)]
116pub enum CompositeSurfaceFormat {
117    Rgba,
118    Yuv,
119}
120
121bitflags! {
122    /// Optional features that can be opted-out of when compositing,
123    /// possibly allowing a fast path to be selected.
124    #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
125    pub struct CompositeFeatures: u8 {
126        // UV coordinates do not require clamping, for example because the
127        // entire texture is being composited.
128        const NO_UV_CLAMP = 1 << 0;
129        // The texture sample should not be modulated by a specified color.
130        const NO_COLOR_MODULATION = 1 << 1;
131        // Can skip applying clip mask.
132        const NO_CLIP_MASK = 1 << 2;
133    }
134}
135
136#[derive(Copy, Clone, Debug, PartialEq)]
137#[cfg_attr(feature = "capture", derive(Serialize))]
138#[cfg_attr(feature = "replay", derive(Deserialize))]
139pub enum TileKind {
140    Opaque,
141    Alpha,
142}
143
144// Index in to the compositor transforms stored in `CompositeState`
145#[cfg_attr(feature = "capture", derive(Serialize))]
146#[cfg_attr(feature = "replay", derive(Deserialize))]
147#[derive(Debug, Copy, Clone)]
148pub struct CompositorTransformIndex(usize);
149
150impl CompositorTransformIndex {
151    pub const INVALID: CompositorTransformIndex = CompositorTransformIndex(!0);
152}
153
154// Index in to the compositor clips stored in `CompositeState`
155#[cfg_attr(feature = "capture", derive(Serialize))]
156#[cfg_attr(feature = "replay", derive(Deserialize))]
157#[derive(Debug, Copy, Clone)]
158pub struct CompositorClipIndex(NonZeroUsize);
159
160/// Describes the geometry and surface of a tile to be composited
161#[cfg_attr(feature = "capture", derive(Serialize))]
162#[cfg_attr(feature = "replay", derive(Deserialize))]
163#[derive(Clone)]
164pub struct CompositeTile {
165    pub surface: CompositeTileSurface,
166    pub local_rect: PictureRect,
167    pub local_valid_rect: PictureRect,
168    pub local_dirty_rect: PictureRect,
169    pub device_clip_rect: DeviceRect,
170    pub z_id: ZBufferId,
171    pub kind: TileKind,
172    pub transform_index: CompositorTransformIndex,
173    pub clip_index: Option<CompositorClipIndex>,
174    pub tile_id: Option<TileId>,
175}
176
177pub fn tile_kind(surface: &CompositeTileSurface, is_opaque: bool) -> TileKind {
178    match surface {
179        // Color tiles are, by definition, opaque. We might support non-opaque color
180        // tiles if we ever find pages that have a lot of these.
181        CompositeTileSurface::Color { .. } => TileKind::Opaque,
182        CompositeTileSurface::Texture { .. }
183        | CompositeTileSurface::ExternalSurface { .. } => {
184            // Texture surfaces get bucketed by opaque/alpha, for z-rejection
185            // on the Draw compositor mode.
186            if is_opaque {
187                TileKind::Opaque
188            } else {
189                TileKind::Alpha
190            }
191        }
192    }
193}
194
195#[derive(Clone)]
196pub enum ExternalSurfaceDependency {
197    Yuv {
198        image_dependencies: [ImageDependency; 3],
199        color_space: YuvRangedColorSpace,
200        format: YuvFormat,
201        channel_bit_depth: u32,
202    },
203    Rgb {
204        image_dependency: ImageDependency,
205    },
206}
207
208/// Describes information about drawing a primitive as a compositor surface.
209/// For now, we support only YUV images as compositor surfaces, but in future
210/// this will also support RGBA images.
211#[derive(Clone)]
212pub struct ExternalSurfaceDescriptor {
213    // Normalized rectangle of this surface in local coordinate space
214    // TODO(gw): Fix up local_rect unit kinds in ExternalSurfaceDescriptor (many flow on effects)
215    pub local_surface_size: LayoutSize,
216    pub local_rect: PictureRect,
217    pub local_clip_rect: PictureRect,
218    pub clip_rect: DeviceRect,
219    pub transform_index: CompositorTransformIndex,
220    pub compositor_clip_index: Option<CompositorClipIndex>,
221    pub image_rendering: ImageRendering,
222    pub z_id: ZBufferId,
223    pub dependency: ExternalSurfaceDependency,
224    /// If native compositing is enabled, the native compositor surface handle.
225    /// Otherwise, this will be None
226    pub native_surface_id: Option<NativeSurfaceId>,
227    /// If the native surface needs to be updated, this will contain the size
228    /// of the native surface as Some(size). If not dirty, this is None.
229    pub update_params: Option<DeviceIntSize>,
230    /// If using external compositing, a user key for the client
231    pub external_image_id: Option<ExternalImageId>,
232    pub prim_instance_index: PrimitiveInstanceIndex,
233}
234
235impl ExternalSurfaceDescriptor {
236    /// Calculate an optional occlusion rect for a given compositor surface
237    pub fn get_occluder_rect(
238        &self,
239        local_clip_rect: &PictureRect,
240        map_pic_to_world: &SpaceMapper<PicturePixel, WorldPixel>,
241    ) -> Option<WorldRect> {
242        let local_surface_rect = self
243            .local_rect
244            .intersection(&self.local_clip_rect)
245            .and_then(|r| {
246                r.intersection(local_clip_rect)
247            });
248
249        local_surface_rect.map(|local_surface_rect| {
250            map_pic_to_world
251                .map(&local_surface_rect)
252                .expect("bug: unable to map external surface to world space")
253        })
254    }
255}
256
257/// Information about a plane in a YUV or RGB surface.
258#[cfg_attr(feature = "capture", derive(Serialize))]
259#[cfg_attr(feature = "replay", derive(Deserialize))]
260#[derive(Debug, Copy, Clone)]
261pub struct ExternalPlaneDescriptor {
262    pub texture: TextureSource,
263    pub uv_rect: TexelRect,
264}
265
266impl ExternalPlaneDescriptor {
267    fn invalid() -> Self {
268        ExternalPlaneDescriptor {
269            texture: TextureSource::Invalid,
270            uv_rect: TexelRect::invalid(),
271        }
272    }
273}
274
275#[cfg_attr(feature = "capture", derive(Serialize))]
276#[cfg_attr(feature = "replay", derive(Deserialize))]
277#[derive(Debug, Copy, Clone, PartialEq)]
278pub struct ResolvedExternalSurfaceIndex(pub usize);
279
280impl ResolvedExternalSurfaceIndex {
281    pub const INVALID: ResolvedExternalSurfaceIndex = ResolvedExternalSurfaceIndex(usize::MAX);
282}
283
284#[cfg_attr(feature = "capture", derive(Serialize))]
285#[cfg_attr(feature = "replay", derive(Deserialize))]
286pub enum ResolvedExternalSurfaceColorData {
287    Yuv {
288        // YUV specific information
289        image_dependencies: [ImageDependency; 3],
290        planes: [ExternalPlaneDescriptor; 3],
291        color_space: YuvRangedColorSpace,
292        format: YuvFormat,
293        channel_bit_depth: u32,
294    },
295    Rgb {
296        image_dependency: ImageDependency,
297        plane: ExternalPlaneDescriptor,
298    },
299}
300
301/// An ExternalSurfaceDescriptor that has had image keys
302/// resolved to texture handles. This contains all the
303/// information that the compositor step in renderer
304/// needs to know.
305#[cfg_attr(feature = "capture", derive(Serialize))]
306#[cfg_attr(feature = "replay", derive(Deserialize))]
307pub struct ResolvedExternalSurface {
308    pub color_data: ResolvedExternalSurfaceColorData,
309    pub image_buffer_kind: ImageBufferKind,
310    // Update information for a native surface if it's dirty
311    pub update_params: Option<(NativeSurfaceId, DeviceIntSize)>,
312    /// If using external compositing, a user key for the client
313    pub external_image_id: Option<ExternalImageId>,
314}
315
316/// Public interface specified in `WebRenderOptions` that configures
317/// how WR compositing will operate.
318pub enum CompositorConfig {
319    /// Let WR draw tiles via normal batching. This requires no special OS support.
320    Draw {
321        /// If this is zero, a full screen present occurs at the end of the
322        /// frame. This is the simplest and default mode. If this is non-zero,
323        /// then the operating system supports a form of 'partial present' where
324        /// only dirty regions of the framebuffer need to be updated.
325        max_partial_present_rects: usize,
326        /// If this is true, WR must draw the previous frames' dirty regions when
327        /// doing a partial present. This is used for EGL which requires the front
328        /// buffer to always be fully consistent.
329        draw_previous_partial_present_regions: bool,
330        /// A client provided interface to a compositor handling partial present.
331        /// Required if webrender must query the backbuffer's age.
332        partial_present: Option<Box<dyn PartialPresentCompositor>>,
333    },
334    Layer {
335        /// If supplied, composite the frame using the new experimental compositing
336        /// interface. If this is set, it overrides `compositor_config`. These will
337        /// be unified as the interface stabilises.
338        compositor: Box<dyn LayerCompositor>,
339    },
340    /// Use a native OS compositor to draw tiles. This requires clients to implement
341    /// the Compositor trait, but can be significantly more power efficient on operating
342    /// systems that support it.
343    Native {
344        /// A client provided interface to a native / OS compositor.
345        compositor: Box<dyn Compositor>,
346    }
347}
348
349impl CompositorConfig {
350    pub fn compositor(&mut self) -> Option<&mut Box<dyn Compositor>> {
351        match self {
352            CompositorConfig::Native { ref mut compositor, .. } => {
353                Some(compositor)
354            }
355            CompositorConfig::Draw { .. } | CompositorConfig::Layer { .. } => {
356                None
357            }
358        }
359    }
360
361    pub fn partial_present(&mut self) -> Option<&mut Box<dyn PartialPresentCompositor>> {
362        match self {
363            CompositorConfig::Native { .. } => {
364                None
365            }
366            CompositorConfig::Draw { ref mut partial_present, .. } => {
367                partial_present.as_mut()
368            }
369            CompositorConfig::Layer { .. } => {
370                None
371            }
372        }
373    }
374
375    pub fn layer_compositor(&mut self) -> Option<&mut Box<dyn LayerCompositor>> {
376        match self {
377            CompositorConfig::Native { .. } => {
378                None
379            }
380            CompositorConfig::Draw { .. } => {
381                None
382            }
383            CompositorConfig::Layer { ref mut compositor } => {
384                Some(compositor)
385            }
386        }
387    }
388}
389
390impl Default for CompositorConfig {
391    /// Default compositor config is full present without partial present.
392    fn default() -> Self {
393        CompositorConfig::Draw {
394            max_partial_present_rects: 0,
395            draw_previous_partial_present_regions: false,
396            partial_present: None,
397        }
398    }
399}
400
401/// This is a representation of `CompositorConfig` without the `Compositor` trait
402/// present. This allows it to be freely copied to other threads, such as the render
403/// backend where the frame builder can access it.
404#[cfg_attr(feature = "capture", derive(Serialize))]
405#[cfg_attr(feature = "replay", derive(Deserialize))]
406#[derive(Debug, Copy, Clone, PartialEq)]
407pub enum CompositorKind {
408    /// WR handles compositing via drawing.
409    Draw {
410        /// Partial present support.
411        max_partial_present_rects: usize,
412        /// Draw previous regions when doing partial present.
413        draw_previous_partial_present_regions: bool,
414    },
415    Layer {
416
417    },
418    /// Native OS compositor.
419    Native {
420        /// The capabilities of the underlying platform.
421        capabilities: CompositorCapabilities,
422    },
423}
424
425impl Default for CompositorKind {
426    /// Default compositor config is full present without partial present.
427    fn default() -> Self {
428        CompositorKind::Draw {
429            max_partial_present_rects: 0,
430            draw_previous_partial_present_regions: false,
431        }
432    }
433}
434
435impl CompositorKind {
436    pub fn get_virtual_surface_size(&self) -> i32 {
437        match self {
438            CompositorKind::Draw { .. } | CompositorKind::Layer {  .. }=> 0,
439            CompositorKind::Native { capabilities, .. } => capabilities.virtual_surface_size,
440        }
441    }
442
443    pub fn should_redraw_on_invalidation(&self) -> bool {
444        match self {
445            CompositorKind::Draw { max_partial_present_rects, .. } => {
446                // When partial present is enabled, we need to force redraw.
447                *max_partial_present_rects > 0
448            }
449            CompositorKind::Layer {  } => false,    // TODO(gwc): Is this correct?
450            CompositorKind::Native { capabilities, .. } => capabilities.redraw_on_invalidation,
451        }
452    }
453}
454
455/// The backing surface kind for a tile. Same as `TileSurface`, minus
456/// the texture cache handles, visibility masks etc.
457#[cfg_attr(feature = "capture", derive(Serialize))]
458#[cfg_attr(feature = "replay", derive(Deserialize))]
459#[derive(PartialEq, Clone)]
460pub enum TileSurfaceKind {
461    Texture,
462    Color {
463        color: ColorF,
464    },
465}
466
467impl From<&TileSurface> for TileSurfaceKind {
468    fn from(surface: &TileSurface) -> Self {
469        match surface {
470            TileSurface::Texture { .. } => TileSurfaceKind::Texture,
471            TileSurface::Color { color } => TileSurfaceKind::Color { color: *color },
472        }
473    }
474}
475
476/// Describes properties that identify a tile composition uniquely.
477/// The backing surface for this tile.
478#[cfg_attr(feature = "capture", derive(Serialize))]
479#[cfg_attr(feature = "replay", derive(Deserialize))]
480#[derive(PartialEq, Clone)]
481pub struct CompositeTileDescriptor {
482    pub tile_id: TileId,
483    pub surface_kind: TileSurfaceKind,
484}
485
486// Whether a compositor surface / swapchain is being used
487// by WR to render content, or is an external swapchain for video
488#[cfg_attr(feature = "capture", derive(Serialize))]
489#[cfg_attr(feature = "replay", derive(Deserialize))]
490#[derive(Debug, Copy, Clone)]
491pub enum CompositorSurfaceUsage {
492    Content,
493    External {
494        image_key: ImageKey,
495        external_image_id: ExternalImageId,
496        transform_index: CompositorTransformIndex,
497    },
498    DebugOverlay,
499}
500
501impl CompositorSurfaceUsage {
502    // Returns true if usage is compatible
503    pub fn matches(&self, other: &CompositorSurfaceUsage) -> bool {
504        match (self, other) {
505            // Surfaces used for content are always compatible
506            (CompositorSurfaceUsage::Content, CompositorSurfaceUsage::Content) => true,
507
508            (CompositorSurfaceUsage::Content, CompositorSurfaceUsage::External { .. }) |
509            (CompositorSurfaceUsage::External { .. }, CompositorSurfaceUsage::Content) => false,
510
511            // External surfaces are matched by image-key (which doesn't change per-frame)
512            (CompositorSurfaceUsage::External { image_key: key1, .. }, CompositorSurfaceUsage::External { image_key: key2, .. }) => {
513                key1 == key2
514            }
515
516            (CompositorSurfaceUsage::DebugOverlay, CompositorSurfaceUsage::DebugOverlay) => true,
517
518            (CompositorSurfaceUsage::DebugOverlay, _) | (_, CompositorSurfaceUsage::DebugOverlay) => false,
519        }
520    }
521}
522
523/// Describes the properties that identify a surface composition uniquely.
524#[cfg_attr(feature = "capture", derive(Serialize))]
525#[cfg_attr(feature = "replay", derive(Deserialize))]
526#[derive(PartialEq, Clone)]
527pub struct CompositeSurfaceDescriptor {
528    pub surface_id: Option<NativeSurfaceId>,
529    pub clip_rect: DeviceRect,
530    pub transform: CompositorSurfaceTransform,
531    // A list of image keys and generations that this compositor surface
532    // depends on. This avoids composites being skipped when the only
533    // thing that has changed is the generation of an compositor surface
534    // image dependency.
535    pub image_dependencies: [ImageDependency; 3],
536    pub image_rendering: ImageRendering,
537    // List of the surface information for each tile added to this virtual surface
538    pub tile_descriptors: Vec<CompositeTileDescriptor>,
539    pub rounded_clip_rect: DeviceRect,
540    pub rounded_clip_radii: ClipRadius,
541}
542
543/// Describes surface properties used to composite a frame. This
544/// is used to compare compositions between frames.
545#[cfg_attr(feature = "capture", derive(Serialize))]
546#[cfg_attr(feature = "replay", derive(Deserialize))]
547#[derive(PartialEq, Clone)]
548pub struct CompositeDescriptor {
549    pub surfaces: Vec<CompositeSurfaceDescriptor>,
550    pub external_surfaces_rect: DeviceRect,
551}
552
553impl CompositeDescriptor {
554    /// Construct an empty descriptor.
555    pub fn empty() -> Self {
556        CompositeDescriptor {
557            surfaces: Vec::new(),
558            external_surfaces_rect: DeviceRect::zero(),
559        }
560    }
561}
562
563pub struct CompositeStatePreallocator {
564    tiles: Preallocator,
565    external_surfaces: Preallocator,
566    occluders: Preallocator,
567    occluders_events: Preallocator,
568    occluders_active: Preallocator,
569    descriptor_surfaces: Preallocator,
570}
571
572impl CompositeStatePreallocator {
573    pub fn record(&mut self, state: &CompositeState) {
574        self.tiles.record_vec(&state.tiles);
575        self.external_surfaces.record_vec(&state.external_surfaces);
576        self.occluders.record_vec(&state.occluders.occluders);
577        self.occluders_events.record_vec(&state.occluders.scratch.events);
578        self.occluders_active.record_vec(&state.occluders.scratch.active);
579        self.descriptor_surfaces.record_vec(&state.descriptor.surfaces);
580    }
581
582    pub fn preallocate(&self, state: &mut CompositeState) {
583        self.tiles.preallocate_framevec(&mut state.tiles);
584        self.external_surfaces.preallocate_framevec(&mut state.external_surfaces);
585        self.occluders.preallocate_framevec(&mut state.occluders.occluders);
586        self.occluders_events.preallocate_framevec(&mut state.occluders.scratch.events);
587        self.occluders_active.preallocate_framevec(&mut state.occluders.scratch.active);
588        self.descriptor_surfaces.preallocate_vec(&mut state.descriptor.surfaces);
589    }
590}
591
592impl Default for CompositeStatePreallocator {
593    fn default() -> Self {
594        CompositeStatePreallocator {
595            tiles: Preallocator::new(56),
596            external_surfaces: Preallocator::new(0),
597            occluders: Preallocator::new(16),
598            occluders_events: Preallocator::new(32),
599            occluders_active: Preallocator::new(16),
600            descriptor_surfaces: Preallocator::new(8),
601        }
602    }
603}
604
605/// A transform for either a picture cache or external compositor surface, stored
606/// in the `CompositeState` structure. This allows conversions from local rects
607/// to raster or device rects, without access to the spatial tree (e.g. during
608/// the render step where dirty rects are calculated). Since we know that we only
609/// handle scale and offset transforms for these types, we can store a single
610/// ScaleOffset rather than 4x4 matrix here for efficiency.
611#[cfg_attr(feature = "capture", derive(Serialize))]
612#[cfg_attr(feature = "replay", derive(Deserialize))]
613pub struct CompositorTransform {
614    // Map from local rect of a composite tile to the real backing surface coords
615    local_to_raster: ScaleOffset,
616    // Map from surface coords to the final device space position
617    raster_to_device: ScaleOffset,
618    // Combined local -> surface -> device transform
619    local_to_device: ScaleOffset,
620}
621
622#[cfg_attr(feature = "capture", derive(Serialize))]
623#[cfg_attr(feature = "replay", derive(Deserialize))]
624#[derive(Clone, Copy, Debug)]
625pub struct CompositorClip {
626    pub rect: DeviceRect,
627    pub radius: BorderRadius,
628}
629
630#[derive(PartialEq, Debug)]
631pub struct CompositeRoundedCorner {
632    pub rect: LayoutRect,
633    pub radius: LayoutSize,
634    pub edge_flags: EdgeMask,
635}
636
637impl Eq for CompositeRoundedCorner {}
638
639impl hash::Hash for CompositeRoundedCorner {
640    fn hash<H: hash::Hasher>(&self, state: &mut H) {
641        self.rect.min.x.to_bits().hash(state);
642        self.rect.min.y.to_bits().hash(state);
643        self.rect.max.x.to_bits().hash(state);
644        self.rect.max.y.to_bits().hash(state);
645        self.radius.width.to_bits().hash(state);
646        self.radius.height.to_bits().hash(state);
647        self.edge_flags.bits().hash(state);
648    }
649}
650
651/// The list of tiles to be drawn this frame
652#[cfg_attr(feature = "capture", derive(Serialize))]
653#[cfg_attr(feature = "replay", derive(Deserialize))]
654pub struct CompositeState {
655    // TODO(gw): Consider splitting up CompositeState into separate struct types depending
656    //           on the selected compositing mode. Many of the fields in this state struct
657    //           are only applicable to either Native or Draw compositing mode.
658    /// List of tiles to be drawn by the Draw compositor.
659    /// Tiles are accumulated in this vector and sorted from front to back at the end of the
660    /// frame.
661    pub tiles: FrameVec<CompositeTile>,
662    /// List of primitives that were promoted to be compositor surfaces.
663    pub external_surfaces: FrameVec<ResolvedExternalSurface>,
664    /// Used to generate z-id values for tiles in the Draw compositor mode.
665    pub z_generator: ZBufferIdGenerator,
666    // If false, we can't rely on the dirty rects in the CompositeTile
667    // instances. This currently occurs during a scroll event, as a
668    // signal to refresh the whole screen. This is only a temporary
669    // measure until we integrate with OS compositors. In the meantime
670    // it gives us the ability to partial present for any non-scroll
671    // case as a simple win (e.g. video, animation etc).
672    pub dirty_rects_are_valid: bool,
673    /// The kind of compositor for picture cache tiles (e.g. drawn by WR, or OS compositor)
674    pub compositor_kind: CompositorKind,
675    /// List of registered occluders
676    pub occluders: Occluders,
677    /// Description of the surfaces and properties that are being composited.
678    pub descriptor: CompositeDescriptor,
679    /// Debugging information about the state of the pictures cached for regression testing.
680    pub picture_cache_debug: PictureCacheDebugInfo,
681    /// List of registered transforms used by picture cache or external surfaces
682    pub transforms: FrameVec<CompositorTransform>,
683    /// Whether we have low quality pinch zoom enabled
684    low_quality_pinch_zoom: bool,
685    /// List of registered clips used by picture cache and/or external surfaces
686    pub clips: FrameVec<CompositorClip>,
687    /// Set to true when any tile is rasterized (has is_valid = false)
688    pub did_rasterize_any_tile: bool,
689}
690
691impl CompositeState {
692    /// Construct a new state for compositing picture tiles. This is created
693    /// during each frame construction and passed to the renderer.
694    pub fn new(
695        compositor_kind: CompositorKind,
696        max_depth_ids: i32,
697        dirty_rects_are_valid: bool,
698        low_quality_pinch_zoom: bool,
699        memory: &FrameMemory,
700    ) -> Self {
701        // Since CompositorClipIndex is NonZeroUSize, we need to
702        // push a dummy entry in to this array.
703        let mut clips = memory.new_vec();
704        clips.push(CompositorClip {
705            rect: DeviceRect::zero(),
706            radius: BorderRadius::zero(),
707        });
708
709        CompositeState {
710            tiles: memory.new_vec(),
711            z_generator: ZBufferIdGenerator::new(max_depth_ids),
712            dirty_rects_are_valid,
713            compositor_kind,
714            occluders: Occluders::new(memory),
715            descriptor: CompositeDescriptor::empty(),
716            external_surfaces: memory.new_vec(),
717            picture_cache_debug: PictureCacheDebugInfo::new(),
718            transforms: memory.new_vec(),
719            low_quality_pinch_zoom,
720            clips,
721            did_rasterize_any_tile: false,
722        }
723    }
724
725    pub fn compositor_clip_params(
726        &self,
727        clip_index: Option<CompositorClipIndex>,
728        default_rect: DeviceRect,
729    ) -> (DeviceRect, ClipRadius) {
730        match clip_index {
731            Some(clip_index) => {
732                let clip = self.get_compositor_clip(clip_index);
733
734                (
735                    clip.rect.cast_unit(),
736                    ClipRadius {
737                        top_left: clip.radius.top_left.width.round() as i32,
738                        top_right: clip.radius.top_right.width.round() as i32,
739                        bottom_left: clip.radius.bottom_left.width.round() as i32,
740                        bottom_right: clip.radius.bottom_right.width.round() as i32,
741                    }
742                )
743            }
744            None => {
745                (default_rect, ClipRadius::EMPTY)
746            }
747        }
748    }
749
750    /// Register use of a transform for a picture cache tile or external surface
751    pub fn register_transform(
752        &mut self,
753        local_to_raster: ScaleOffset,
754        raster_to_device: ScaleOffset,
755    ) -> CompositorTransformIndex {
756        let index = CompositorTransformIndex(self.transforms.len());
757
758        let local_to_device = local_to_raster.then(&raster_to_device);
759
760        self.transforms.push(CompositorTransform {
761            local_to_raster,
762            raster_to_device,
763            local_to_device,
764        });
765
766        index
767    }
768
769    /// Register use of a clip for a picture cache tile and/or external surface
770    pub fn register_clip(
771        &mut self,
772        rect: DeviceRect,
773        radius: BorderRadius,
774    ) -> CompositorClipIndex {
775        let index = CompositorClipIndex(NonZeroUsize::new(self.clips.len()).expect("bug"));
776
777        self.clips.push(CompositorClip {
778            rect,
779            radius,
780        });
781
782        index
783    }
784
785    /// Calculate the device-space rect of a local compositor surface rect
786    pub fn get_device_rect(
787        &self,
788        local_rect: &PictureRect,
789        transform_index: CompositorTransformIndex,
790    ) -> DeviceRect {
791        let transform = &self.transforms[transform_index.0];
792        transform.local_to_device.map_rect(&local_rect).round()
793    }
794
795    /// Calculate the device-space rect of a local compositor surface rect, normalized
796    /// to the origin of a given point
797    pub fn get_surface_rect<T>(
798        &self,
799        local_sub_rect: &Box2D<f32, T>,
800        local_bounds: &Box2D<f32, T>,
801        transform_index: CompositorTransformIndex,
802    ) -> DeviceRect {
803        let transform = &self.transforms[transform_index.0];
804
805        let surface_bounds = transform.local_to_raster.map_rect(&local_bounds);
806        let surface_rect = transform.local_to_raster.map_rect(&local_sub_rect);
807
808        surface_rect
809            .round_out()
810            .translate(-surface_bounds.min.to_vector())
811            .round_out()
812            .intersection(&surface_bounds.size().round().into())
813            .unwrap_or_else(DeviceRect::zero)
814    }
815
816    /// Get the local -> device compositor transform
817    pub fn get_device_transform(
818        &self,
819        transform_index: CompositorTransformIndex,
820    ) -> ScaleOffset {
821        let transform = &self.transforms[transform_index.0];
822        transform.local_to_device
823    }
824
825    /// Get the surface -> device compositor transform
826    pub fn get_compositor_transform(
827        &self,
828        transform_index: CompositorTransformIndex,
829    ) -> ScaleOffset {
830        let transform = &self.transforms[transform_index.0];
831        transform.raster_to_device
832    }
833
834    /// Get the compositor clip
835    pub fn get_compositor_clip(
836        &self,
837        clip_index: CompositorClipIndex,
838    ) -> &CompositorClip {
839        &self.clips[clip_index.0.get()]
840    }
841
842    /// Register an occluder during picture cache updates that can be
843    /// used during frame building to occlude tiles.
844    pub fn register_occluder(
845        &mut self,
846        z_id: ZBufferId,
847        rect: WorldRect,
848        compositor_clip: Option<CompositorClipIndex>,
849    ) {
850        let rect = match compositor_clip {
851            Some(clip_index) => {
852                let clip = self.get_compositor_clip(clip_index);
853
854                let inner_rect = match extract_inner_rect_safe(
855                    &clip.rect,
856                    &clip.radius,
857                ) {
858                    Some(rect) => rect,
859                    None => return,
860                };
861
862                match inner_rect.cast_unit().intersection(&rect) {
863                    Some(rect) => rect,
864                    None => return,
865                }
866            }
867            None => {
868                rect
869            }
870        };
871
872        let world_rect = rect.round().to_i32();
873
874        self.occluders.push(world_rect, z_id);
875    }
876
877    /// Push a compositor surface on to the list of tiles to be passed to the compositor
878    fn push_compositor_surface(
879        &mut self,
880        external_surface: &ExternalSurfaceDescriptor,
881        is_opaque: bool,
882        device_clip_rect: DeviceRect,
883        resource_cache: &ResourceCache,
884        gpu_buffer: &mut GpuBufferBuilderF,
885        deferred_resolves: &mut FrameVec<DeferredResolve>,
886        clip_index: Option<CompositorClipIndex>,
887    ) {
888        let clip_rect = external_surface
889            .clip_rect
890            .intersection(&device_clip_rect)
891            .unwrap_or_else(DeviceRect::zero);
892
893        // Skip compositor surfaces with empty clip rects.
894        if clip_rect.is_empty() {
895            return;
896        }
897
898        let required_plane_count =
899            match external_surface.dependency {
900                ExternalSurfaceDependency::Yuv { format, .. } => {
901                    format.get_plane_num()
902                },
903                ExternalSurfaceDependency::Rgb { .. } => {
904                    1
905                }
906            };
907
908        let mut image_dependencies = [ImageDependency::INVALID; 3];
909
910        for i in 0 .. required_plane_count {
911            let dependency = match external_surface.dependency {
912                ExternalSurfaceDependency::Yuv { image_dependencies, .. } => {
913                    image_dependencies[i]
914                },
915                ExternalSurfaceDependency::Rgb { image_dependency, .. } => {
916                    image_dependency
917                }
918            };
919            image_dependencies[i] = dependency;
920        }
921
922        // Get a new z_id for each compositor surface, to ensure correct ordering
923        // when drawing with the simple (Draw) compositor, and to schedule compositing
924        // of any required updates into the surfaces.
925        let needs_external_surface_update = match self.compositor_kind {
926            CompositorKind::Draw { .. } | CompositorKind::Layer { .. } => true,
927            _ => external_surface.update_params.is_some(),
928        };
929        let external_surface_index = if needs_external_surface_update {
930            let external_surface_index = self.compute_external_surface_dependencies(
931                &external_surface,
932                &image_dependencies,
933                required_plane_count,
934                resource_cache,
935                gpu_buffer,
936                deferred_resolves,
937            );
938            if external_surface_index == ResolvedExternalSurfaceIndex::INVALID {
939                return;
940            }
941            external_surface_index
942        } else {
943            ResolvedExternalSurfaceIndex::INVALID
944        };
945
946        let surface = CompositeTileSurface::ExternalSurface { external_surface_index };
947        let local_rect = external_surface.local_surface_size.cast_unit().into();
948
949        let tile = CompositeTile {
950            kind: tile_kind(&surface, is_opaque),
951            surface,
952            local_rect,
953            local_valid_rect: local_rect,
954            local_dirty_rect: local_rect,
955            device_clip_rect: clip_rect,
956            z_id: external_surface.z_id,
957            transform_index: external_surface.transform_index,
958            clip_index,
959            tile_id: None,
960        };
961
962        let (rounded_clip_rect, rounded_clip_radii) = self.compositor_clip_params(
963            clip_index,
964            clip_rect,
965        );
966
967        // Add a surface descriptor for each compositor surface. For the Draw
968        // compositor, this is used to avoid composites being skipped by adding
969        // a dependency on the compositor surface external image keys / generations.
970        self.descriptor.surfaces.push(
971            CompositeSurfaceDescriptor {
972                surface_id: external_surface.native_surface_id,
973                clip_rect,
974                transform: self.get_compositor_transform(external_surface.transform_index),
975                image_dependencies: image_dependencies,
976                image_rendering: external_surface.image_rendering,
977                tile_descriptors: Vec::new(),
978                rounded_clip_rect,
979                rounded_clip_radii,
980            }
981        );
982
983        let device_rect =
984            self.get_device_rect(&local_rect, external_surface.transform_index);
985        self.descriptor.external_surfaces_rect =
986            self.descriptor.external_surfaces_rect.union(&device_rect);
987
988        self.tiles.push(tile);
989    }
990
991    /// Add a picture cache to be composited
992    pub fn push_surface(
993        &mut self,
994        tile_cache: &TileCacheInstance,
995        device_clip_rect: DeviceRect,
996        resource_cache: &ResourceCache,
997        gpu_buffer: &mut GpuBufferBuilderF,
998        deferred_resolves: &mut FrameVec<DeferredResolve>,
999    ) {
1000        let slice_transform = self.get_compositor_transform(tile_cache.transform_index);
1001
1002        let image_rendering = if self.low_quality_pinch_zoom {
1003            ImageRendering::Auto
1004        } else {
1005            ImageRendering::CrispEdges
1006        };
1007
1008        if let Some(backdrop_surface) = &tile_cache.backdrop_surface {
1009            let (rounded_clip_rect, rounded_clip_radii) = self.compositor_clip_params(
1010                tile_cache.compositor_clip,
1011                backdrop_surface.device_rect,
1012            );
1013
1014            // Use the backdrop native surface we created and add that to the composite state.
1015            self.descriptor.surfaces.push(
1016                CompositeSurfaceDescriptor {
1017                    surface_id: Some(backdrop_surface.id),
1018                    clip_rect: backdrop_surface.device_rect,
1019                    transform: slice_transform,
1020                    image_dependencies: [ImageDependency::INVALID; 3],
1021                    image_rendering,
1022                    tile_descriptors: Vec::new(),
1023                    rounded_clip_rect,
1024                    rounded_clip_radii,
1025                }
1026            );
1027        }
1028
1029        // Add any underlay surfaces to the compositing tree
1030        for underlay in &tile_cache.underlays {
1031            self.push_compositor_surface(
1032                underlay,
1033                true,
1034                device_clip_rect,
1035                resource_cache,
1036                gpu_buffer,
1037                deferred_resolves,
1038                tile_cache.compositor_clip,
1039            );
1040        }
1041
1042        for sub_slice in &tile_cache.sub_slices {
1043            let mut surface_device_rect = DeviceRect::zero();
1044
1045            for tile in sub_slice.tiles.values() {
1046                if !tile.is_visible {
1047                    // This can occur when a tile is found to be occluded during frame building.
1048                    continue;
1049                }
1050
1051                // Accumulate this tile into the overall surface bounds. This is used below
1052                // to clamp the size of the supplied clip rect to a reasonable value.
1053                // NOTE: This clip rect must include the device_valid_rect rather than
1054                //       the tile device rect. This ensures that in the case of a picture
1055                //       cache slice that is smaller than a single tile, the clip rect in
1056                //       the composite descriptor will change if the position of that slice
1057                //       is changed. Otherwise, WR may conclude that no composite is needed
1058                //       if the tile itself was not invalidated due to changing content.
1059                //       See bug #1675414 for more detail.
1060                surface_device_rect = surface_device_rect.union(&tile.device_valid_rect);
1061            }
1062
1063            // Append the visible tiles from this sub-slice
1064            self.tiles.extend_from_slice(&sub_slice.composite_tiles);
1065
1066            // If the clip rect is too large, it can cause accuracy and correctness problems
1067            // for some native compositors (specifically, CoreAnimation in this case). To
1068            // work around that, intersect the supplied clip rect with the current bounds
1069            // of the native surface, which ensures it is a reasonable size.
1070            let surface_clip_rect = device_clip_rect
1071                .intersection(&surface_device_rect)
1072                .unwrap_or(DeviceRect::zero());
1073
1074            // Only push tiles if they have valid clip rects.
1075            if !surface_clip_rect.is_empty() {
1076                let (rounded_clip_rect, rounded_clip_radii) = self.compositor_clip_params(
1077                    tile_cache.compositor_clip,
1078                    surface_clip_rect,
1079                );
1080
1081                // Add opaque surface before any compositor surfaces
1082                if !sub_slice.opaque_tile_descriptors.is_empty() {
1083                    self.descriptor.surfaces.push(
1084                        CompositeSurfaceDescriptor {
1085                            surface_id: sub_slice.native_surface.as_ref().map(|s| s.opaque),
1086                            clip_rect: surface_clip_rect,
1087                            transform: slice_transform,
1088                            image_dependencies: [ImageDependency::INVALID; 3],
1089                            image_rendering,
1090                            tile_descriptors: sub_slice.opaque_tile_descriptors.clone(),
1091                            rounded_clip_rect,
1092                            rounded_clip_radii,
1093                        }
1094                    );
1095                }
1096
1097                // Add alpha tiles after opaque surfaces
1098                if !sub_slice.alpha_tile_descriptors.is_empty() {
1099                    self.descriptor.surfaces.push(
1100                        CompositeSurfaceDescriptor {
1101                            surface_id: sub_slice.native_surface.as_ref().map(|s| s.alpha),
1102                            clip_rect: surface_clip_rect,
1103                            transform: slice_transform,
1104                            image_dependencies: [ImageDependency::INVALID; 3],
1105                            image_rendering,
1106                            tile_descriptors: sub_slice.alpha_tile_descriptors.clone(),
1107                            rounded_clip_rect,
1108                            rounded_clip_radii,
1109                        }
1110                    );
1111                }
1112            }
1113
1114            // For each compositor surface that was promoted, build the
1115            // information required for the compositor to draw it
1116            for compositor_surface in &sub_slice.compositor_surfaces {
1117                let compositor_clip_index = if compositor_surface.descriptor.compositor_clip_index.is_some() {
1118                    assert!(tile_cache.compositor_clip.is_none());
1119                    compositor_surface.descriptor.compositor_clip_index
1120                } else {
1121                    tile_cache.compositor_clip
1122                };
1123
1124                self.push_compositor_surface(
1125                    &compositor_surface.descriptor,
1126                    compositor_surface.is_opaque,
1127                    device_clip_rect,
1128                    resource_cache,
1129                    gpu_buffer,
1130                    deferred_resolves,
1131                    compositor_clip_index,
1132                );
1133            }
1134        }
1135    }
1136
1137    /// Compare this state vs. a previous frame state, and invalidate dirty rects if
1138    /// the surface count has changed
1139    pub fn update_dirty_rect_validity(
1140        &mut self,
1141        old_descriptor: &CompositeDescriptor,
1142    ) {
1143        // TODO(gw): Make this more robust in other cases - there are other situations where
1144        //           the surface count may be the same but we still need to invalidate the
1145        //           dirty rects (e.g. if the surface ordering changed, or the external
1146        //           surface itself is animated?)
1147
1148        if old_descriptor.surfaces.len() != self.descriptor.surfaces.len() {
1149            self.dirty_rects_are_valid = false;
1150            return;
1151        }
1152
1153        // The entire area of external surfaces are treated as dirty, however,
1154        // if a surface has moved or shrunk that is no longer valid, as we
1155        // additionally need to ensure the area the surface used to occupy is
1156        // composited.
1157        if !self
1158            .descriptor
1159            .external_surfaces_rect
1160            .contains_box(&old_descriptor.external_surfaces_rect)
1161        {
1162            self.dirty_rects_are_valid = false;
1163            return;
1164        }
1165    }
1166
1167    fn compute_external_surface_dependencies(
1168        &mut self,
1169        external_surface: &ExternalSurfaceDescriptor,
1170        image_dependencies: &[ImageDependency; 3],
1171        required_plane_count: usize,
1172        resource_cache: &ResourceCache,
1173        gpu_buffer: &mut GpuBufferBuilderF,
1174        deferred_resolves: &mut FrameVec<DeferredResolve>,
1175    ) -> ResolvedExternalSurfaceIndex {
1176        let mut planes = [
1177            ExternalPlaneDescriptor::invalid(),
1178            ExternalPlaneDescriptor::invalid(),
1179            ExternalPlaneDescriptor::invalid(),
1180        ];
1181
1182        let mut valid_plane_count = 0;
1183        for i in 0 .. required_plane_count {
1184            let request = ImageRequest {
1185                key: image_dependencies[i].key,
1186                rendering: external_surface.image_rendering,
1187                tile: None,
1188            };
1189
1190            let cache_item = resolve_image(
1191                request,
1192                resource_cache,
1193                gpu_buffer,
1194                deferred_resolves,
1195                true,
1196            );
1197
1198            if cache_item.texture_id != TextureSource::Invalid {
1199                valid_plane_count += 1;
1200                let plane = &mut planes[i];
1201                *plane = ExternalPlaneDescriptor {
1202                    texture: cache_item.texture_id,
1203                    uv_rect: cache_item.uv_rect.into(),
1204                };
1205            }
1206        }
1207
1208        // Check if there are valid images added for each YUV plane
1209        if valid_plane_count < required_plane_count {
1210            warn!("Warnings: skip a YUV/RGB compositor surface, found {}/{} valid images",
1211                valid_plane_count,
1212                required_plane_count,
1213            );
1214            return ResolvedExternalSurfaceIndex::INVALID;
1215        }
1216
1217        let external_surface_index = ResolvedExternalSurfaceIndex(self.external_surfaces.len());
1218
1219        // If the external surface descriptor reports that the native surface
1220        // needs to be updated, create an update params tuple for the renderer
1221        // to use.
1222        let update_params = external_surface.update_params.map(|surface_size| {
1223            (
1224                external_surface.native_surface_id.expect("bug: no native surface!"),
1225                surface_size
1226            )
1227        });
1228
1229        match external_surface.dependency {
1230            ExternalSurfaceDependency::Yuv{ color_space, format, channel_bit_depth, .. } => {
1231
1232                let image_buffer_kind = planes[0].texture.image_buffer_kind();
1233
1234                self.external_surfaces.push(ResolvedExternalSurface {
1235                    color_data: ResolvedExternalSurfaceColorData::Yuv {
1236                        image_dependencies: *image_dependencies,
1237                        planes,
1238                        color_space,
1239                        format,
1240                        channel_bit_depth,
1241                        },
1242                    image_buffer_kind,
1243                    update_params,
1244                    external_image_id: external_surface.external_image_id,
1245                });
1246            },
1247            ExternalSurfaceDependency::Rgb { .. } => {
1248                let image_buffer_kind = planes[0].texture.image_buffer_kind();
1249
1250                self.external_surfaces.push(ResolvedExternalSurface {
1251                    color_data: ResolvedExternalSurfaceColorData::Rgb {
1252                        image_dependency: image_dependencies[0],
1253                        plane: planes[0],
1254                    },
1255                    image_buffer_kind,
1256                    update_params,
1257                    external_image_id: external_surface.external_image_id,
1258                });
1259            },
1260        }
1261        external_surface_index
1262    }
1263
1264    pub fn end_frame(&mut self) {
1265        // Sort tiles from front to back.
1266        self.tiles.sort_by_key(|tile| tile.z_id.0);
1267    }
1268
1269    #[cfg(feature = "debugger")]
1270    pub fn print_to_string(&self) -> String {
1271        use crate::print_tree::PrintTree;
1272        use crate::print_tree::PrintTreePrinter;
1273
1274        let mut buf = Vec::<u8>::new();
1275        {
1276            let mut pt = PrintTree::new_with_sink("composite config", &mut buf);
1277
1278            pt.new_level("tiles".into());
1279            for (i, tile) in self.tiles.iter().enumerate() {
1280                pt.new_level(format!("tile {}", i));
1281                pt.add_item(format!("local_rect = {:?}", tile.local_rect.to_rect()));
1282                pt.add_item(format!("local_valid_rect = {:?}", tile.local_valid_rect.to_rect()));
1283                pt.add_item(format!("local_dirty_rect = {:?}", tile.local_dirty_rect.to_rect()));
1284                pt.add_item(format!("device_clip_rect = {:?}", tile.device_clip_rect.to_rect()));
1285                pt.add_item(format!("z_id = {:?}", tile.z_id));
1286                pt.add_item(format!("kind = {:?}", tile.kind));
1287                pt.add_item(format!("tile_id = {:?}", tile.tile_id));
1288                pt.add_item(format!("clip = {:?}", tile.clip_index));
1289                pt.add_item(format!("transform = {:?}", tile.transform_index));
1290                pt.end_level();
1291            }
1292            pt.end_level();
1293
1294            pt.new_level("external_surfaces".into());
1295            for (i, surface) in self.external_surfaces.iter().enumerate() {
1296                pt.new_level(format!("surface {}", i));
1297                pt.add_item(format!("{:?}", surface.image_buffer_kind));
1298                pt.end_level();
1299            }
1300            pt.end_level();
1301
1302            pt.new_level("occluders".into());
1303            for (i, occluder) in self.occluders.occluders.iter().enumerate() {
1304                pt.new_level(format!("occluder {}", i));
1305                pt.add_item(format!("{:?}", occluder.z_id));
1306                pt.add_item(format!("{:?}", occluder.world_rect.to_rect()));
1307                pt.end_level();
1308            }
1309            pt.end_level();
1310
1311            pt.new_level("transforms".into());
1312            for (i, transform) in self.transforms.iter().enumerate() {
1313                pt.new_level(format!("transform {}", i));
1314                pt.add_item(format!("local_to_raster {:?}", transform.local_to_raster));
1315                pt.add_item(format!("raster_to_device {:?}", transform.raster_to_device));
1316                pt.add_item(format!("local_to_device {:?}", transform.local_to_device));
1317                pt.end_level();
1318            }
1319            pt.end_level();
1320
1321            pt.new_level("clips".into());
1322            for (i, clip) in self.clips.iter().enumerate() {
1323                pt.new_level(format!("clip {}", i));
1324                pt.add_item(format!("{:?}", clip.rect.to_rect()));
1325                pt.add_item(format!("{:?}", clip.radius));
1326                pt.end_level();
1327            }
1328            pt.end_level();
1329        }
1330
1331        std::str::from_utf8(&buf).unwrap_or("(Tree printer emitted non-utf8)").to_string()
1332    }
1333}
1334
1335/// An arbitrary identifier for a native (OS compositor) surface
1336#[repr(C)]
1337#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
1338#[cfg_attr(feature = "capture", derive(Serialize))]
1339#[cfg_attr(feature = "replay", derive(Deserialize))]
1340pub struct NativeSurfaceId(pub u64);
1341
1342impl NativeSurfaceId {
1343    /// A special id for the native surface that is used for debug / profiler overlays.
1344    pub const DEBUG_OVERLAY: NativeSurfaceId = NativeSurfaceId(u64::MAX);
1345}
1346
1347#[repr(C)]
1348#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
1349#[cfg_attr(feature = "capture", derive(Serialize))]
1350#[cfg_attr(feature = "replay", derive(Deserialize))]
1351pub struct NativeTileId {
1352    pub surface_id: NativeSurfaceId,
1353    pub x: i32,
1354    pub y: i32,
1355}
1356
1357impl NativeTileId {
1358    /// A special id for the native surface that is used for debug / profiler overlays.
1359    pub const DEBUG_OVERLAY: NativeTileId = NativeTileId {
1360        surface_id: NativeSurfaceId::DEBUG_OVERLAY,
1361        x: 0,
1362        y: 0,
1363    };
1364}
1365
1366/// Information about a bound surface that the native compositor
1367/// returns to WR.
1368#[repr(C)]
1369#[derive(Copy, Clone)]
1370pub struct NativeSurfaceInfo {
1371    /// An offset into the surface that WR should draw. Some compositing
1372    /// implementations (notably, DirectComposition) use texture atlases
1373    /// when the surface sizes are small. In this case, an offset can
1374    /// be returned into the larger texture where WR should draw. This
1375    /// can be (0, 0) if texture atlases are not used.
1376    pub origin: DeviceIntPoint,
1377    /// The ID of the FBO that WR should bind to, in order to draw to
1378    /// the bound surface. On Windows (ANGLE) this will always be 0,
1379    /// since creating a p-buffer sets the default framebuffer to
1380    /// be the DirectComposition surface. On Mac, this will be non-zero,
1381    /// since it identifies the IOSurface that has been bound to draw to.
1382    // TODO(gw): This may need to be a larger / different type for WR
1383    //           backends that are not GL.
1384    pub fbo_id: u32,
1385}
1386
1387#[repr(C)]
1388#[derive(Debug, Copy, Clone)]
1389pub struct WindowProperties {
1390    pub is_opaque: bool,
1391    pub enable_screenshot: bool,
1392}
1393
1394impl Default for WindowProperties {
1395    fn default() -> Self {
1396        WindowProperties {
1397            is_opaque: true,
1398            enable_screenshot: true,
1399        }
1400    }
1401}
1402
1403#[repr(C)]
1404#[derive(Debug, Copy, Clone, PartialEq)]
1405#[cfg_attr(feature = "capture", derive(Serialize))]
1406#[cfg_attr(feature = "replay", derive(Deserialize))]
1407pub struct CompositorCapabilities {
1408    /// The virtual surface size used by the underlying platform.
1409    pub virtual_surface_size: i32,
1410    /// Whether the compositor requires redrawing on invalidation.
1411    pub redraw_on_invalidation: bool,
1412    /// The maximum number of dirty rects that can be provided per compositor
1413    /// surface update. If this is zero, the entire compositor surface for
1414    /// a given tile will be drawn if it's dirty.
1415    pub max_update_rects: usize,
1416    /// Whether or not this compositor will create surfaces for backdrops.
1417    pub supports_surface_for_backdrop: bool,
1418    /// Whether external compositor surface supports negative scaling.
1419    pub supports_external_compositor_surface_negative_scaling: bool,
1420}
1421
1422impl Default for CompositorCapabilities {
1423    fn default() -> Self {
1424        // The default set of compositor capabilities for a given platform.
1425        // These should only be modified if a compositor diverges specifically
1426        // from the default behavior so that compositors don't have to track
1427        // which changes to this structure unless necessary.
1428        CompositorCapabilities {
1429            virtual_surface_size: 0,
1430            redraw_on_invalidation: false,
1431            // Assume compositors can do at least partial update of surfaces. If not,
1432            // the native compositor should override this to be 0.
1433            max_update_rects: 1,
1434            supports_surface_for_backdrop: false,
1435            supports_external_compositor_surface_negative_scaling: true,
1436        }
1437    }
1438}
1439
1440#[repr(C)]
1441#[derive(Copy, Clone, Debug)]
1442pub struct WindowVisibility {
1443    pub is_fully_occluded: bool,
1444}
1445
1446impl Default for WindowVisibility {
1447    fn default() -> Self {
1448        WindowVisibility {
1449            is_fully_occluded: false,
1450        }
1451    }
1452}
1453
1454/// The transform type to apply to Compositor surfaces.
1455// TODO: Should transform from CompositorSurfacePixel instead, but this requires a cleanup of the
1456// Compositor API to use CompositorSurface-space geometry instead of Device-space where necessary
1457// to avoid a bunch of noisy cast_unit calls and make it actually type-safe. May be difficult due
1458// to pervasive use of Device-space nomenclature inside WR.
1459// pub struct CompositorSurfacePixel;
1460pub type CompositorSurfaceTransform = ScaleOffset;
1461
1462#[repr(C)]
1463#[cfg_attr(feature = "capture", derive(Serialize))]
1464#[cfg_attr(feature = "replay", derive(Deserialize))]
1465#[derive(PartialEq, Copy, Clone, Debug)]
1466pub struct ClipRadius {
1467    pub top_left: i32,
1468    pub top_right: i32,
1469    pub bottom_left: i32,
1470    pub bottom_right: i32,
1471}
1472
1473impl ClipRadius {
1474    pub const EMPTY: ClipRadius = ClipRadius { top_left: 0, top_right: 0, bottom_left: 0, bottom_right: 0 };
1475}
1476
1477/// Defines an interface to a native (OS level) compositor. If supplied
1478/// by the client application, then picture cache slices will be
1479/// composited by the OS compositor, rather than drawn via WR batches.
1480pub trait Compositor {
1481    /// Create a new OS compositor surface with the given properties.
1482    fn create_surface(
1483        &mut self,
1484        device: &mut Device,
1485        id: NativeSurfaceId,
1486        virtual_offset: DeviceIntPoint,
1487        tile_size: DeviceIntSize,
1488        is_opaque: bool,
1489    );
1490
1491    /// Create a new OS compositor surface that can be used with an
1492    /// existing ExternalImageId, instead of being drawn to by WebRender.
1493    /// Surfaces created by this can only be used with attach_external_image,
1494    /// and not create_tile/destroy_tile/bind/unbind.
1495    fn create_external_surface(
1496        &mut self,
1497        device: &mut Device,
1498        id: NativeSurfaceId,
1499        is_opaque: bool,
1500    );
1501
1502    /// Create a new OS backdrop surface that will display a color.
1503    fn create_backdrop_surface(
1504        &mut self,
1505        device: &mut Device,
1506        id: NativeSurfaceId,
1507        color: ColorF,
1508    );
1509
1510    /// Destroy the surface with the specified id. WR may call this
1511    /// at any time the surface is no longer required (including during
1512    /// renderer deinit). It's the responsibility of the embedder
1513    /// to ensure that the surface is only freed once the GPU is
1514    /// no longer using the surface (if this isn't already handled
1515    /// by the operating system).
1516    fn destroy_surface(
1517        &mut self,
1518        device: &mut Device,
1519        id: NativeSurfaceId,
1520    );
1521
1522    /// Create a new OS compositor tile with the given properties.
1523    fn create_tile(
1524        &mut self,
1525        device: &mut Device,
1526        id: NativeTileId,
1527    );
1528
1529    /// Destroy an existing compositor tile.
1530    fn destroy_tile(
1531        &mut self,
1532        device: &mut Device,
1533        id: NativeTileId,
1534    );
1535
1536    /// Attaches an ExternalImageId to an OS compositor surface created
1537    /// by create_external_surface, and uses that as the contents of
1538    /// the surface. It is expected that a single surface will have
1539    /// many different images attached (like one for each video frame).
1540    fn attach_external_image(
1541        &mut self,
1542        device: &mut Device,
1543        id: NativeSurfaceId,
1544        external_image: ExternalImageId
1545    );
1546
1547    /// Mark a tile as invalid before any surfaces are queued for
1548    /// composition and before it is updated with bind. This is useful
1549    /// for early composition, allowing for dependency tracking of which
1550    /// surfaces can be composited early while others are still updating.
1551    fn invalidate_tile(
1552        &mut self,
1553        _device: &mut Device,
1554        _id: NativeTileId,
1555        _valid_rect: DeviceIntRect
1556    ) {}
1557
1558    /// Bind this surface such that WR can issue OpenGL commands
1559    /// that will target the surface. Returns an (x, y) offset
1560    /// where WR should draw into the surface. This can be set
1561    /// to (0, 0) if the OS doesn't use texture atlases. The dirty
1562    /// rect is a local surface rect that specifies which part
1563    /// of the surface needs to be updated. If max_update_rects
1564    /// in CompositeConfig is 0, this will always be the size
1565    /// of the entire surface. The returned offset is only
1566    /// relevant to compositors that store surfaces in a texture
1567    /// atlas (that is, WR expects that the dirty rect doesn't
1568    /// affect the coordinates of the returned origin).
1569    fn bind(
1570        &mut self,
1571        device: &mut Device,
1572        id: NativeTileId,
1573        dirty_rect: DeviceIntRect,
1574        valid_rect: DeviceIntRect,
1575    ) -> NativeSurfaceInfo;
1576
1577    /// Unbind the surface. This is called by WR when it has
1578    /// finished issuing OpenGL commands on the current surface.
1579    fn unbind(
1580        &mut self,
1581        device: &mut Device,
1582    );
1583
1584    /// Begin the frame
1585    fn begin_frame(&mut self, device: &mut Device);
1586
1587    /// Add a surface to the visual tree to be composited. Visuals must
1588    /// be added every frame, between the begin/end transaction call. The
1589    /// z-order of the surfaces is determined by the order they are added
1590    /// to the visual tree.
1591    // TODO(gw): Adding visuals every frame makes the interface simple,
1592    //           but may have performance implications on some compositors?
1593    //           We might need to change the interface to maintain a visual
1594    //           tree that can be mutated?
1595    // TODO(gw): We might need to add a concept of a hierachy in future.
1596    fn add_surface(
1597        &mut self,
1598        device: &mut Device,
1599        id: NativeSurfaceId,
1600        transform: CompositorSurfaceTransform,
1601        clip_rect: DeviceIntRect,
1602        image_rendering: ImageRendering,
1603        rounded_clip_rect: DeviceIntRect,
1604        rounded_clip_radii: ClipRadius,
1605    );
1606
1607    /// Notify the compositor that all tiles have been invalidated and all
1608    /// native surfaces have been added, thus it is safe to start compositing
1609    /// valid surfaces. The dirty rects array allows native compositors that
1610    /// support partial present to skip copying unchanged areas.
1611    /// Optionally provides a set of rectangles for the areas known to be
1612    /// opaque, this is currently only computed if the caller is SwCompositor.
1613    fn start_compositing(
1614        &mut self,
1615        _device: &mut Device,
1616        _clear_color: ColorF,
1617        _dirty_rects: &[DeviceIntRect],
1618        _opaque_rects: &[DeviceIntRect],
1619    ) {}
1620
1621    /// Commit any changes in the compositor tree for this frame. WR calls
1622    /// this once when all surface and visual updates are complete, to signal
1623    /// that the OS composite transaction should be applied.
1624    fn end_frame(&mut self, device: &mut Device);
1625
1626    /// Enable/disable native compositor usage
1627    fn enable_native_compositor(&mut self, device: &mut Device, enable: bool);
1628
1629    /// Safely deinitialize any remaining resources owned by the compositor.
1630    fn deinit(&mut self, device: &mut Device);
1631
1632    /// Get the capabilities struct for this compositor. This is used to
1633    /// specify what features a compositor supports, depending on the
1634    /// underlying platform
1635    fn get_capabilities(&self, device: &mut Device) -> CompositorCapabilities;
1636
1637    fn get_window_visibility(&self, device: &mut Device) -> WindowVisibility;
1638}
1639
1640// Describes the configuration for an input layer that the compositor
1641// implemention should prepare
1642#[derive(Debug)]
1643pub struct CompositorInputLayer {
1644    // Device space location of the layer (pre-clip)
1645    pub offset: DeviceIntPoint,
1646    // Device space clip-rect of the layer
1647    pub clip_rect: DeviceIntRect,
1648    // Whether a content or external surface
1649    pub usage: CompositorSurfaceUsage,
1650    // If true, layer is opaque, blend can be disabled
1651    pub is_opaque: bool,
1652    pub rounded_clip_rect: DeviceIntRect,
1653    pub rounded_clip_radii: ClipRadius,
1654}
1655
1656// Provides the parameters about the frame to the compositor implementation.
1657// TODO(gw): Include information about picture cache slices and external surfaces.
1658#[derive(Debug)]
1659pub struct CompositorInputConfig<'a> {
1660    pub enable_screenshot: bool,
1661    pub layers: &'a [CompositorInputLayer],
1662}
1663
1664// Trait for implementors of swapchain based compositing.
1665// TODO(gw): Extend to handle external surfaces, layers, swgl, etc.
1666pub trait LayerCompositor {
1667    // Prepare to composite a frame. Ensure that layers are constructed
1668    // to match the input config
1669    fn begin_frame(
1670        &mut self,
1671        input: &CompositorInputConfig,
1672    ) -> bool;
1673
1674    // Bind a layer (by index in the input config) to begin rendering
1675    // content to it.
1676    fn bind_layer(
1677        &mut self,
1678        index: usize,
1679        dirty_rects: &[DeviceIntRect],
1680    );
1681
1682    // Complete rendering of a layer and present / swap buffers
1683    fn present_layer(
1684        &mut self,
1685        index: usize,
1686        dirty_rects: &[DeviceIntRect],
1687    );
1688
1689    fn add_surface(
1690        &mut self,
1691        index: usize,
1692        transform: CompositorSurfaceTransform,
1693        clip_rect: DeviceIntRect,
1694        image_rendering: ImageRendering,
1695        rounded_clip_rect: DeviceIntRect,
1696        rounded_clip_radii: ClipRadius,
1697    );
1698
1699    // Finish compositing this frame - commit the visual tree to the OS
1700    fn end_frame(&mut self);
1701
1702    // Get current information about the window, such as opacity
1703    fn get_window_properties(&self) -> WindowProperties;
1704}
1705
1706/// Information about the underlying data buffer of a mapped tile.
1707#[repr(C)]
1708#[derive(Copy, Clone)]
1709pub struct MappedTileInfo {
1710    pub data: *mut c_void,
1711    pub stride: i32,
1712}
1713
1714/// Descriptor for a locked surface that will be directly composited by SWGL.
1715#[repr(C)]
1716pub struct SWGLCompositeSurfaceInfo {
1717    /// The number of YUV planes in the surface. 0 indicates non-YUV BGRA.
1718    /// 1 is interleaved YUV. 2 is NV12. 3 is planar YUV.
1719    pub yuv_planes: u32,
1720    /// Textures for planes of the surface, or 0 if not applicable.
1721    pub textures: [u32; 3],
1722    /// Color space of surface if using a YUV format.
1723    pub color_space: YuvRangedColorSpace,
1724    /// Color depth of surface if using a YUV format.
1725    pub color_depth: ColorDepth,
1726    /// The actual source surface size before transformation.
1727    pub size: DeviceIntSize,
1728}
1729
1730/// A Compositor variant that supports mapping tiles into CPU memory.
1731pub trait MappableCompositor: Compositor {
1732    /// Map a tile's underlying buffer so it can be used as the backing for
1733    /// a SWGL framebuffer. This is intended to be a replacement for 'bind'
1734    /// in any compositors that intend to directly interoperate with SWGL
1735    /// while supporting some form of native layers.
1736    fn map_tile(
1737        &mut self,
1738        device: &mut Device,
1739        id: NativeTileId,
1740        dirty_rect: DeviceIntRect,
1741        valid_rect: DeviceIntRect,
1742    ) -> Option<MappedTileInfo>;
1743
1744    /// Unmap a tile that was was previously mapped via map_tile to signal
1745    /// that SWGL is done rendering to the buffer.
1746    fn unmap_tile(&mut self, device: &mut Device);
1747
1748    fn lock_composite_surface(
1749        &mut self,
1750        device: &mut Device,
1751        ctx: *mut c_void,
1752        external_image_id: ExternalImageId,
1753        composite_info: *mut SWGLCompositeSurfaceInfo,
1754    ) -> bool;
1755    fn unlock_composite_surface(&mut self, device: &mut Device, ctx: *mut c_void, external_image_id: ExternalImageId);
1756}
1757
1758/// Defines an interface to a non-native (application-level) Compositor which handles
1759/// partial present. This is required if webrender must query the backbuffer's age.
1760/// TODO: Use the Compositor trait for native and non-native compositors, and integrate
1761/// this functionality there.
1762pub trait PartialPresentCompositor {
1763    /// Allows webrender to specify the total region that will be rendered to this frame,
1764    /// ie the frame's dirty region and some previous frames' dirty regions, if applicable
1765    /// (calculated using the buffer age). Must be called before anything has been rendered
1766    /// to the main framebuffer.
1767    fn set_buffer_damage_region(&mut self, rects: &[DeviceIntRect]);
1768}
1769
1770/// Information about an opaque surface used to occlude tiles.
1771#[cfg_attr(feature = "capture", derive(Serialize))]
1772#[cfg_attr(feature = "replay", derive(Deserialize))]
1773struct Occluder {
1774    z_id: ZBufferId,
1775    world_rect: WorldIntRect,
1776}
1777
1778// Whether this event is the start or end of a rectangle
1779#[derive(Debug)]
1780enum OcclusionEventKind {
1781    Begin,
1782    End,
1783}
1784
1785// A list of events on the y-axis, with the rectangle range that it affects on the x-axis
1786#[derive(Debug)]
1787struct OcclusionEvent {
1788    y: i32,
1789    x_range: ops::Range<i32>,
1790    kind: OcclusionEventKind,
1791}
1792
1793impl OcclusionEvent {
1794    fn new(y: i32, kind: OcclusionEventKind, x0: i32, x1: i32) -> Self {
1795        OcclusionEvent {
1796            y,
1797            x_range: ops::Range {
1798                start: x0,
1799                end: x1,
1800            },
1801            kind,
1802        }
1803    }
1804}
1805
1806/// This struct exists to provide a Default impl and allow #[serde(skip)]
1807/// on the two frame vectors. Unfortunately FrameVec does not have a Default
1808/// implementation (vectors only implement it with the global allocator).
1809pub struct OccludersScratchBuffers {
1810    events: FrameVec<OcclusionEvent>,
1811    active: FrameVec<ops::Range<i32>>,
1812}
1813
1814impl Default for OccludersScratchBuffers {
1815    fn default() -> Self {
1816        OccludersScratchBuffers {
1817            events: FrameVec::new_in(FrameAllocator::fallback()),
1818            active: FrameVec::new_in(FrameAllocator::fallback()),
1819        }
1820    }
1821}
1822
1823/// List of registered occluders.
1824///
1825/// Also store a couple of vectors for reuse.
1826#[cfg_attr(feature = "capture", derive(Serialize))]
1827#[cfg_attr(feature = "replay", derive(Deserialize))]
1828pub struct Occluders {
1829    occluders: FrameVec<Occluder>,
1830
1831    // The two vectors in scratch are kept to avoid unnecessary reallocations in area().
1832    #[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
1833    scratch: OccludersScratchBuffers,
1834}
1835
1836impl Occluders {
1837    fn new(memory: &FrameMemory) -> Self {
1838        Occluders {
1839            occluders: memory.new_vec(),
1840            scratch: OccludersScratchBuffers {
1841                events: memory.new_vec(),
1842                active: memory.new_vec(),
1843            }
1844        }
1845    }
1846
1847    fn push(&mut self, world_rect: WorldIntRect, z_id: ZBufferId) {
1848        self.occluders.push(Occluder { world_rect, z_id });
1849    }
1850
1851    /// Returns true if a tile with the specified rectangle and z_id
1852    /// is occluded by an opaque surface in front of it.
1853    pub fn is_tile_occluded(
1854        &mut self,
1855        z_id: ZBufferId,
1856        world_rect: WorldRect,
1857    ) -> bool {
1858        // It's often the case that a tile is only occluded by considering multiple
1859        // picture caches in front of it (for example, the background tiles are
1860        // often occluded by a combination of the content slice + the scrollbar slices).
1861
1862        // The basic algorithm is:
1863        //    For every occluder:
1864        //      If this occluder is in front of the tile we are querying:
1865        //         Clip the occluder rectangle to the query rectangle.
1866        //    Calculate the total non-overlapping area of those clipped occluders.
1867        //    If the cumulative area of those occluders is the same as the area of the query tile,
1868        //       Then the entire tile must be occluded and can be skipped during rasterization and compositing.
1869
1870        // Get the reference area we will compare against.
1871        let world_rect = world_rect.round().to_i32();
1872        let ref_area = world_rect.area();
1873
1874        // Calculate the non-overlapping area of the valid occluders.
1875        let cover_area = self.area(z_id, &world_rect);
1876        debug_assert!(cover_area <= ref_area);
1877
1878        // Check if the tile area is completely covered
1879        ref_area == cover_area
1880    }
1881
1882    /// Return the total area covered by a set of occluders, accounting for
1883    /// overlapping areas between those rectangles.
1884    fn area(
1885        &mut self,
1886        z_id: ZBufferId,
1887        clip_rect: &WorldIntRect,
1888    ) -> i32 {
1889        // This implementation is based on the article https://leetcode.com/articles/rectangle-area-ii/.
1890        // This is not a particularly efficient implementation (it skips building segment trees), however
1891        // we typically use this where the length of the rectangles array is < 10, so simplicity is more important.
1892
1893        self.scratch.events.clear();
1894        self.scratch.active.clear();
1895
1896        let mut area = 0;
1897
1898        // Step through each rectangle and build the y-axis event list
1899        for occluder in &self.occluders {
1900            // Only consider occluders in front of this rect
1901            if occluder.z_id.0 < z_id.0 {
1902                // Clip the source rect to the rectangle we care about, since we only
1903                // want to record area for the tile we are comparing to.
1904                if let Some(rect) = occluder.world_rect.intersection(clip_rect) {
1905                    let x0 = rect.min.x;
1906                    let x1 = x0 + rect.width();
1907                    self.scratch.events.push(OcclusionEvent::new(rect.min.y, OcclusionEventKind::Begin, x0, x1));
1908                    self.scratch.events.push(OcclusionEvent::new(rect.min.y + rect.height(), OcclusionEventKind::End, x0, x1));
1909                }
1910            }
1911        }
1912
1913        // If we didn't end up with any valid events, the area must be 0
1914        if self.scratch.events.is_empty() {
1915            return 0;
1916        }
1917
1918        // Sort the events by y-value
1919        self.scratch.events.sort_by_key(|e| e.y);
1920        let mut cur_y = self.scratch.events[0].y;
1921
1922        // Step through each y interval
1923        for event in &self.scratch.events {
1924            // This is the dimension of the y-axis we are accumulating areas for
1925            let dy = event.y - cur_y;
1926
1927            // If we have active events covering x-ranges in this y-interval, process them
1928            if dy != 0 && !self.scratch.active.is_empty() {
1929                assert!(dy > 0);
1930
1931                // Step through the x-ranges, ordered by x0 of each event
1932                self.scratch.active.sort_by_key(|i| i.start);
1933                let mut query = 0;
1934                let mut cur = self.scratch.active[0].start;
1935
1936                // Accumulate the non-overlapping x-interval that contributes to area for this y-interval.
1937                for interval in &self.scratch.active {
1938                    cur = interval.start.max(cur);
1939                    query += (interval.end - cur).max(0);
1940                    cur = cur.max(interval.end);
1941                }
1942
1943                // Accumulate total area for this y-interval
1944                area += query * dy;
1945            }
1946
1947            // Update the active events list
1948            match event.kind {
1949                OcclusionEventKind::Begin => {
1950                    self.scratch.active.push(event.x_range.clone());
1951                }
1952                OcclusionEventKind::End => {
1953                    let index = self.scratch.active.iter().position(|i| *i == event.x_range).unwrap();
1954                    self.scratch.active.remove(index);
1955                }
1956            }
1957
1958            cur_y = event.y;
1959        }
1960
1961        area
1962    }
1963}