azul_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::{ColorF, YuvRangedColorSpace, YuvFormat, ImageRendering, ExternalImageId, ImageBufferKind};
6use api::units::*;
7use api::ColorDepth;
8use crate::image_source::resolve_image;
9use euclid::{Box2D, Transform3D};
10use crate::gpu_cache::GpuCache;
11use crate::gpu_types::{ZBufferId, ZBufferIdGenerator};
12use crate::internal_types::TextureSource;
13use crate::picture::{ImageDependency, ResolvedSurfaceTexture, TileCacheInstance, TileId, TileSurface};
14use crate::prim_store::DeferredResolve;
15use crate::resource_cache::{ImageRequest, ResourceCache};
16use crate::util::{Preallocator, ScaleOffset};
17use crate::tile_cache::PictureCacheDebugInfo;
18use std::{ops, u64, os::raw::c_void};
19
20/*
21 Types and definitions related to compositing picture cache tiles
22 and/or OS compositor integration.
23 */
24
25/// Describes details of an operation to apply to a native surface
26#[derive(Debug, Clone)]
27#[cfg_attr(feature = "capture", derive(Serialize))]
28#[cfg_attr(feature = "replay", derive(Deserialize))]
29pub enum NativeSurfaceOperationDetails {
30    CreateSurface {
31        id: NativeSurfaceId,
32        virtual_offset: DeviceIntPoint,
33        tile_size: DeviceIntSize,
34        is_opaque: bool,
35    },
36    CreateExternalSurface {
37        id: NativeSurfaceId,
38        is_opaque: bool,
39    },
40    DestroySurface {
41        id: NativeSurfaceId,
42    },
43    CreateTile {
44        id: NativeTileId,
45    },
46    DestroyTile {
47        id: NativeTileId,
48    },
49    AttachExternalImage {
50        id: NativeSurfaceId,
51        external_image: ExternalImageId,
52    }
53}
54
55/// Describes an operation to apply to a native surface
56#[derive(Debug, Clone)]
57#[cfg_attr(feature = "capture", derive(Serialize))]
58#[cfg_attr(feature = "replay", derive(Deserialize))]
59pub struct NativeSurfaceOperation {
60    pub details: NativeSurfaceOperationDetails,
61}
62
63/// Describes the source surface information for a tile to be composited. This
64/// is the analog of the TileSurface type, with target surface information
65/// resolved such that it can be used by the renderer.
66#[cfg_attr(feature = "capture", derive(Serialize))]
67#[cfg_attr(feature = "replay", derive(Deserialize))]
68#[derive(Clone)]
69pub enum CompositeTileSurface {
70    Texture {
71        surface: ResolvedSurfaceTexture,
72    },
73    Color {
74        color: ColorF,
75    },
76    Clear,
77    ExternalSurface {
78        external_surface_index: ResolvedExternalSurfaceIndex,
79    },
80}
81
82/// The surface format for a tile being composited.
83#[derive(Debug, Copy, Clone, PartialEq)]
84pub enum CompositeSurfaceFormat {
85    Rgba,
86    Yuv,
87}
88
89bitflags! {
90    /// Optional features that can be opted-out of when compositing,
91    /// possibly allowing a fast path to be selected.
92    pub struct CompositeFeatures: u8 {
93        // UV coordinates do not require clamping, for example because the
94        // entire texture is being composited.
95        const NO_UV_CLAMP = 1 << 0;
96        // The texture sample should not be modulated by a specified color.
97        const NO_COLOR_MODULATION = 1 << 1;
98    }
99}
100
101#[derive(Copy, Clone, Debug, PartialEq)]
102#[cfg_attr(feature = "capture", derive(Serialize))]
103#[cfg_attr(feature = "replay", derive(Deserialize))]
104pub enum TileKind {
105    Opaque,
106    Alpha,
107    Clear,
108}
109
110// Index in to the compositor transforms stored in `CompositeState`
111#[cfg_attr(feature = "capture", derive(Serialize))]
112#[cfg_attr(feature = "replay", derive(Deserialize))]
113#[derive(Debug, Copy, Clone)]
114pub struct CompositorTransformIndex(usize);
115
116impl CompositorTransformIndex {
117    pub const INVALID: CompositorTransformIndex = CompositorTransformIndex(!0);
118}
119
120/// Describes the geometry and surface of a tile to be composited
121#[cfg_attr(feature = "capture", derive(Serialize))]
122#[cfg_attr(feature = "replay", derive(Deserialize))]
123#[derive(Clone)]
124pub struct CompositeTile {
125    pub surface: CompositeTileSurface,
126    pub local_rect: PictureRect,
127    pub local_valid_rect: PictureRect,
128    pub local_dirty_rect: PictureRect,
129    pub device_clip_rect: DeviceRect,
130    pub z_id: ZBufferId,
131    pub kind: TileKind,
132    pub transform_index: CompositorTransformIndex,
133}
134
135pub fn tile_kind(surface: &CompositeTileSurface, is_opaque: bool) -> TileKind {
136    match surface {
137        // Color tiles are, by definition, opaque. We might support non-opaque color
138        // tiles if we ever find pages that have a lot of these.
139        CompositeTileSurface::Color { .. } => TileKind::Opaque,
140        // Clear tiles have a special bucket
141        CompositeTileSurface::Clear => TileKind::Clear,
142        CompositeTileSurface::Texture { .. }
143        | CompositeTileSurface::ExternalSurface { .. } => {
144            // Texture surfaces get bucketed by opaque/alpha, for z-rejection
145            // on the Draw compositor mode.
146            if is_opaque {
147                TileKind::Opaque
148            } else {
149                TileKind::Alpha
150            }
151        }
152    }
153}
154
155pub enum ExternalSurfaceDependency {
156    Yuv {
157        image_dependencies: [ImageDependency; 3],
158        color_space: YuvRangedColorSpace,
159        format: YuvFormat,
160        channel_bit_depth: u32,
161    },
162    Rgb {
163        image_dependency: ImageDependency,
164    },
165}
166
167/// Describes information about drawing a primitive as a compositor surface.
168/// For now, we support only YUV images as compositor surfaces, but in future
169/// this will also support RGBA images.
170pub struct ExternalSurfaceDescriptor {
171    // Normalized rectangle of this surface in local coordinate space
172    // TODO(gw): Fix up local_rect unit kinds in ExternalSurfaceDescriptor (many flow on effects)
173    pub local_surface_size: LayoutSize,
174    pub local_rect: PictureRect,
175    pub local_clip_rect: PictureRect,
176    pub clip_rect: DeviceRect,
177    pub transform_index: CompositorTransformIndex,
178    pub image_rendering: ImageRendering,
179    pub z_id: ZBufferId,
180    pub dependency: ExternalSurfaceDependency,
181    /// If native compositing is enabled, the native compositor surface handle.
182    /// Otherwise, this will be None
183    pub native_surface_id: Option<NativeSurfaceId>,
184    /// If the native surface needs to be updated, this will contain the size
185    /// of the native surface as Some(size). If not dirty, this is None.
186    pub update_params: Option<DeviceIntSize>,
187}
188
189/// Information about a plane in a YUV or RGB surface.
190#[cfg_attr(feature = "capture", derive(Serialize))]
191#[cfg_attr(feature = "replay", derive(Deserialize))]
192#[derive(Debug, Copy, Clone)]
193pub struct ExternalPlaneDescriptor {
194    pub texture: TextureSource,
195    pub uv_rect: TexelRect,
196}
197
198impl ExternalPlaneDescriptor {
199    fn invalid() -> Self {
200        ExternalPlaneDescriptor {
201            texture: TextureSource::Invalid,
202            uv_rect: TexelRect::invalid(),
203        }
204    }
205}
206
207#[cfg_attr(feature = "capture", derive(Serialize))]
208#[cfg_attr(feature = "replay", derive(Deserialize))]
209#[derive(Debug, Copy, Clone, PartialEq)]
210pub struct ResolvedExternalSurfaceIndex(pub usize);
211
212impl ResolvedExternalSurfaceIndex {
213    pub const INVALID: ResolvedExternalSurfaceIndex = ResolvedExternalSurfaceIndex(usize::MAX);
214}
215
216#[cfg_attr(feature = "capture", derive(Serialize))]
217#[cfg_attr(feature = "replay", derive(Deserialize))]
218pub enum ResolvedExternalSurfaceColorData {
219    Yuv {
220        // YUV specific information
221        image_dependencies: [ImageDependency; 3],
222        planes: [ExternalPlaneDescriptor; 3],
223        color_space: YuvRangedColorSpace,
224        format: YuvFormat,
225        channel_bit_depth: u32,
226    },
227    Rgb {
228        image_dependency: ImageDependency,
229        plane: ExternalPlaneDescriptor,
230    },
231}
232
233/// An ExternalSurfaceDescriptor that has had image keys
234/// resolved to texture handles. This contains all the
235/// information that the compositor step in renderer
236/// needs to know.
237#[cfg_attr(feature = "capture", derive(Serialize))]
238#[cfg_attr(feature = "replay", derive(Deserialize))]
239pub struct ResolvedExternalSurface {
240    pub color_data: ResolvedExternalSurfaceColorData,
241    pub image_buffer_kind: ImageBufferKind,
242    // Update information for a native surface if it's dirty
243    pub update_params: Option<(NativeSurfaceId, DeviceIntSize)>,
244}
245
246/// Public interface specified in `RendererOptions` that configures
247/// how WR compositing will operate.
248pub enum CompositorConfig {
249    /// Let WR draw tiles via normal batching. This requires no special OS support.
250    Draw {
251        /// If this is zero, a full screen present occurs at the end of the
252        /// frame. This is the simplest and default mode. If this is non-zero,
253        /// then the operating system supports a form of 'partial present' where
254        /// only dirty regions of the framebuffer need to be updated.
255        max_partial_present_rects: usize,
256        /// If this is true, WR must draw the previous frames' dirty regions when
257        /// doing a partial present. This is used for EGL which requires the front
258        /// buffer to always be fully consistent.
259        draw_previous_partial_present_regions: bool,
260        /// A client provided interface to a compositor handling partial present.
261        /// Required if webrender must query the backbuffer's age.
262        partial_present: Option<Box<dyn PartialPresentCompositor>>,
263    },
264    /// Use a native OS compositor to draw tiles. This requires clients to implement
265    /// the Compositor trait, but can be significantly more power efficient on operating
266    /// systems that support it.
267    Native {
268        /// A client provided interface to a native / OS compositor.
269        compositor: Box<dyn Compositor>,
270    }
271}
272
273impl CompositorConfig {
274    pub fn compositor(&mut self) -> Option<&mut Box<dyn Compositor>> {
275        match self {
276            CompositorConfig::Native { ref mut compositor, .. } => {
277                Some(compositor)
278            }
279            CompositorConfig::Draw { .. } => {
280                None
281            }
282        }
283    }
284
285    pub fn partial_present(&mut self) -> Option<&mut Box<dyn PartialPresentCompositor>> {
286        match self {
287            CompositorConfig::Native { .. } => {
288                None
289            }
290            CompositorConfig::Draw { ref mut partial_present, .. } => {
291                partial_present.as_mut()
292            }
293        }
294    }
295
296}
297
298impl Default for CompositorConfig {
299    /// Default compositor config is full present without partial present.
300    fn default() -> Self {
301        CompositorConfig::Draw {
302            max_partial_present_rects: 0,
303            draw_previous_partial_present_regions: false,
304            partial_present: None,
305        }
306    }
307}
308
309/// This is a representation of `CompositorConfig` without the `Compositor` trait
310/// present. This allows it to be freely copied to other threads, such as the render
311/// backend where the frame builder can access it.
312#[cfg_attr(feature = "capture", derive(Serialize))]
313#[cfg_attr(feature = "replay", derive(Deserialize))]
314#[derive(Debug, Copy, Clone, PartialEq)]
315pub enum CompositorKind {
316    /// WR handles compositing via drawing.
317    Draw {
318        /// Partial present support.
319        max_partial_present_rects: usize,
320        /// Draw previous regions when doing partial present.
321        draw_previous_partial_present_regions: bool,
322    },
323    /// Native OS compositor.
324    Native {
325        /// The capabilities of the underlying platform.
326        capabilities: CompositorCapabilities,
327    },
328}
329
330impl Default for CompositorKind {
331    /// Default compositor config is full present without partial present.
332    fn default() -> Self {
333        CompositorKind::Draw {
334            max_partial_present_rects: 0,
335            draw_previous_partial_present_regions: false,
336        }
337    }
338}
339
340impl CompositorKind {
341    pub fn get_virtual_surface_size(&self) -> i32 {
342        match self {
343            CompositorKind::Draw { .. } => 0,
344            CompositorKind::Native { capabilities, .. } => capabilities.virtual_surface_size,
345        }
346    }
347
348    pub fn should_redraw_on_invalidation(&self) -> bool {
349        match self {
350            CompositorKind::Draw { max_partial_present_rects, .. } => {
351                // When partial present is enabled, we need to force redraw.
352                *max_partial_present_rects > 0
353            }
354            CompositorKind::Native { capabilities, .. } => capabilities.redraw_on_invalidation,
355        }
356    }
357}
358
359/// The backing surface kind for a tile. Same as `TileSurface`, minus
360/// the texture cache handles, visibility masks etc.
361#[cfg_attr(feature = "capture", derive(Serialize))]
362#[cfg_attr(feature = "replay", derive(Deserialize))]
363#[derive(PartialEq, Clone)]
364pub enum TileSurfaceKind {
365    Texture,
366    Color {
367        color: ColorF,
368    },
369    Clear,
370}
371
372impl From<&TileSurface> for TileSurfaceKind {
373    fn from(surface: &TileSurface) -> Self {
374        match surface {
375            TileSurface::Texture { .. } => TileSurfaceKind::Texture,
376            TileSurface::Color { color } => TileSurfaceKind::Color { color: *color },
377            TileSurface::Clear => TileSurfaceKind::Clear,
378        }
379    }
380}
381
382/// Describes properties that identify a tile composition uniquely.
383/// The backing surface for this tile.
384#[cfg_attr(feature = "capture", derive(Serialize))]
385#[cfg_attr(feature = "replay", derive(Deserialize))]
386#[derive(PartialEq, Clone)]
387pub struct CompositeTileDescriptor {
388    pub tile_id: TileId,
389    pub surface_kind: TileSurfaceKind,
390}
391
392/// Describes the properties that identify a surface composition uniquely.
393#[cfg_attr(feature = "capture", derive(Serialize))]
394#[cfg_attr(feature = "replay", derive(Deserialize))]
395#[derive(PartialEq, Clone)]
396pub struct CompositeSurfaceDescriptor {
397    pub surface_id: Option<NativeSurfaceId>,
398    pub clip_rect: DeviceRect,
399    pub transform: CompositorSurfaceTransform,
400    // A list of image keys and generations that this compositor surface
401    // depends on. This avoids composites being skipped when the only
402    // thing that has changed is the generation of an compositor surface
403    // image dependency.
404    pub image_dependencies: [ImageDependency; 3],
405    pub image_rendering: ImageRendering,
406    // List of the surface information for each tile added to this virtual surface
407    pub tile_descriptors: Vec<CompositeTileDescriptor>,
408}
409
410/// Describes surface properties used to composite a frame. This
411/// is used to compare compositions between frames.
412#[cfg_attr(feature = "capture", derive(Serialize))]
413#[cfg_attr(feature = "replay", derive(Deserialize))]
414#[derive(PartialEq, Clone)]
415pub struct CompositeDescriptor {
416    pub surfaces: Vec<CompositeSurfaceDescriptor>,
417}
418
419impl CompositeDescriptor {
420    /// Construct an empty descriptor.
421    pub fn empty() -> Self {
422        CompositeDescriptor {
423            surfaces: Vec::new(),
424        }
425    }
426}
427
428pub struct CompositeStatePreallocator {
429    tiles: Preallocator,
430    external_surfaces: Preallocator,
431    occluders: Preallocator,
432    occluders_events: Preallocator,
433    occluders_active: Preallocator,
434    descriptor_surfaces: Preallocator,
435}
436
437impl CompositeStatePreallocator {
438    pub fn record(&mut self, state: &CompositeState) {
439        self.tiles.record_vec(&state.tiles);
440        self.external_surfaces.record_vec(&state.external_surfaces);
441        self.occluders.record_vec(&state.occluders.occluders);
442        self.occluders_events.record_vec(&state.occluders.events);
443        self.occluders_active.record_vec(&state.occluders.active);
444        self.descriptor_surfaces.record_vec(&state.descriptor.surfaces);
445    }
446
447    pub fn preallocate(&self, state: &mut CompositeState) {
448        self.tiles.preallocate_vec(&mut state.tiles);
449        self.external_surfaces.preallocate_vec(&mut state.external_surfaces);
450        self.occluders.preallocate_vec(&mut state.occluders.occluders);
451        self.occluders_events.preallocate_vec(&mut state.occluders.events);
452        self.occluders_active.preallocate_vec(&mut state.occluders.active);
453        self.descriptor_surfaces.preallocate_vec(&mut state.descriptor.surfaces);
454    }
455}
456
457impl Default for CompositeStatePreallocator {
458    fn default() -> Self {
459        CompositeStatePreallocator {
460            tiles: Preallocator::new(56),
461            external_surfaces: Preallocator::new(0),
462            occluders: Preallocator::new(16),
463            occluders_events: Preallocator::new(32),
464            occluders_active: Preallocator::new(16),
465            descriptor_surfaces: Preallocator::new(8),
466        }
467    }
468}
469
470/// A transform for either a picture cache or external compositor surface, stored
471/// in the `CompositeState` structure. This allows conversions from local rects
472/// to raster or device rects, without access to the spatial tree (e.g. during
473/// the render step where dirty rects are calculated). Since we know that we only
474/// handle scale and offset transforms for these types, we can store a single
475/// ScaleOffset rather than 4x4 matrix here for efficiency.
476#[cfg_attr(feature = "capture", derive(Serialize))]
477#[cfg_attr(feature = "replay", derive(Deserialize))]
478pub struct CompositorTransform {
479    // Map from local rect of a composite tile to the real backing surface coords
480    local_to_surface: ScaleOffset,
481    // Map from surface coords to the final device space position
482    surface_to_device: ScaleOffset,
483    // Combined local -> surface -> device transform
484    local_to_device: ScaleOffset,
485}
486
487/// The list of tiles to be drawn this frame
488#[cfg_attr(feature = "capture", derive(Serialize))]
489#[cfg_attr(feature = "replay", derive(Deserialize))]
490pub struct CompositeState {
491    // TODO(gw): Consider splitting up CompositeState into separate struct types depending
492    //           on the selected compositing mode. Many of the fields in this state struct
493    //           are only applicable to either Native or Draw compositing mode.
494    /// List of tiles to be drawn by the Draw compositor.
495    /// Tiles are accumulated in this vector and sorted from front to back at the end of the
496    /// frame.
497    pub tiles: Vec<CompositeTile>,
498    /// List of primitives that were promoted to be compositor surfaces.
499    pub external_surfaces: Vec<ResolvedExternalSurface>,
500    /// Used to generate z-id values for tiles in the Draw compositor mode.
501    pub z_generator: ZBufferIdGenerator,
502    // If false, we can't rely on the dirty rects in the CompositeTile
503    // instances. This currently occurs during a scroll event, as a
504    // signal to refresh the whole screen. This is only a temporary
505    // measure until we integrate with OS compositors. In the meantime
506    // it gives us the ability to partial present for any non-scroll
507    // case as a simple win (e.g. video, animation etc).
508    pub dirty_rects_are_valid: bool,
509    /// The kind of compositor for picture cache tiles (e.g. drawn by WR, or OS compositor)
510    pub compositor_kind: CompositorKind,
511    /// List of registered occluders
512    pub occluders: Occluders,
513    /// Description of the surfaces and properties that are being composited.
514    pub descriptor: CompositeDescriptor,
515    /// Debugging information about the state of the pictures cached for regression testing.
516    pub picture_cache_debug: PictureCacheDebugInfo,
517    /// List of registered transforms used by picture cache or external surfaces
518    pub transforms: Vec<CompositorTransform>,
519    /// Whether we have low quality pinch zoom enabled
520    low_quality_pinch_zoom: bool,
521}
522
523impl CompositeState {
524    /// Construct a new state for compositing picture tiles. This is created
525    /// during each frame construction and passed to the renderer.
526    pub fn new(
527        compositor_kind: CompositorKind,
528        max_depth_ids: i32,
529        dirty_rects_are_valid: bool,
530        low_quality_pinch_zoom: bool,
531    ) -> Self {
532        CompositeState {
533            tiles: Vec::new(),
534            z_generator: ZBufferIdGenerator::new(max_depth_ids),
535            dirty_rects_are_valid,
536            compositor_kind,
537            occluders: Occluders::new(),
538            descriptor: CompositeDescriptor::empty(),
539            external_surfaces: Vec::new(),
540            picture_cache_debug: PictureCacheDebugInfo::new(),
541            transforms: Vec::new(),
542            low_quality_pinch_zoom,
543        }
544    }
545
546    /// Register use of a transform for a picture cache tile or external surface
547    pub fn register_transform(
548        &mut self,
549        local_to_surface: ScaleOffset,
550        surface_to_device: ScaleOffset,
551    ) -> CompositorTransformIndex {
552        let index = CompositorTransformIndex(self.transforms.len());
553
554        let local_to_device = local_to_surface.accumulate(&surface_to_device);
555
556        self.transforms.push(CompositorTransform {
557            local_to_surface,
558            surface_to_device,
559            local_to_device,
560        });
561
562        index
563    }
564
565    /// Calculate the device-space rect of a local compositor surface rect
566    pub fn get_device_rect(
567        &self,
568        local_rect: &PictureRect,
569        transform_index: CompositorTransformIndex,
570    ) -> DeviceRect {
571        let transform = &self.transforms[transform_index.0];
572        transform.local_to_device.map_rect(&local_rect).round()
573    }
574
575    /// Calculate the device-space rect of a local compositor surface rect, normalized
576    /// to the origin of a given point
577    pub fn get_surface_rect<T>(
578        &self,
579        local_sub_rect: &Box2D<f32, T>,
580        local_bounds: &Box2D<f32, T>,
581        transform_index: CompositorTransformIndex,
582    ) -> DeviceRect {
583        let transform = &self.transforms[transform_index.0];
584
585        let surface_bounds = transform.local_to_surface.map_rect(&local_bounds);
586        let surface_rect = transform.local_to_surface.map_rect(&local_sub_rect);
587
588        surface_rect
589            .translate(-surface_bounds.min.to_vector())
590            .round_out()
591            .intersection(&surface_bounds.size().round().into())
592            .unwrap_or_else(DeviceRect::zero)
593    }
594
595    /// Get the local -> device compositor transform
596    pub fn get_device_transform(
597        &self,
598        transform_index: CompositorTransformIndex,
599    ) -> ScaleOffset {
600        let transform = &self.transforms[transform_index.0];
601        transform.local_to_device
602    }
603
604    /// Get the surface -> device compositor transform
605    pub fn get_compositor_transform(
606        &self,
607        transform_index: CompositorTransformIndex,
608    ) -> ScaleOffset {
609        let transform = &self.transforms[transform_index.0];
610        transform.surface_to_device
611    }
612
613    /// Register an occluder during picture cache updates that can be
614    /// used during frame building to occlude tiles.
615    pub fn register_occluder(
616        &mut self,
617        z_id: ZBufferId,
618        rect: WorldRect,
619    ) {
620        let world_rect = rect.round().to_i32();
621
622        self.occluders.push(world_rect, z_id);
623    }
624
625    /// Add a picture cache to be composited
626    pub fn push_surface(
627        &mut self,
628        tile_cache: &TileCacheInstance,
629        device_clip_rect: DeviceRect,
630        resource_cache: &ResourceCache,
631        gpu_cache: &mut GpuCache,
632        deferred_resolves: &mut Vec<DeferredResolve>,
633    ) {
634        let slice_transform = self.get_compositor_transform(tile_cache.transform_index).to_transform();
635
636        let image_rendering = if self.low_quality_pinch_zoom {
637            ImageRendering::Auto
638        } else {
639            ImageRendering::CrispEdges
640        };
641
642        for sub_slice in &tile_cache.sub_slices {
643            let mut surface_device_rect = DeviceRect::zero();
644
645            for tile in sub_slice.tiles.values() {
646                if !tile.is_visible {
647                    // This can occur when a tile is found to be occluded during frame building.
648                    continue;
649                }
650
651                // Accumulate this tile into the overall surface bounds. This is used below
652                // to clamp the size of the supplied clip rect to a reasonable value.
653                // NOTE: This clip rect must include the device_valid_rect rather than
654                //       the tile device rect. This ensures that in the case of a picture
655                //       cache slice that is smaller than a single tile, the clip rect in
656                //       the composite descriptor will change if the position of that slice
657                //       is changed. Otherwise, WR may conclude that no composite is needed
658                //       if the tile itself was not invalidated due to changing content.
659                //       See bug #1675414 for more detail.
660                surface_device_rect = surface_device_rect.union(&tile.device_valid_rect);
661            }
662
663            // Append the visible tiles from this sub-slice
664            self.tiles.extend_from_slice(&sub_slice.composite_tiles);
665
666            // If the clip rect is too large, it can cause accuracy and correctness problems
667            // for some native compositors (specifically, CoreAnimation in this case). To
668            // work around that, intersect the supplied clip rect with the current bounds
669            // of the native surface, which ensures it is a reasonable size.
670            let surface_clip_rect = device_clip_rect
671                .intersection(&surface_device_rect)
672                .unwrap_or(DeviceRect::zero());
673
674            // Add opaque surface before any compositor surfaces
675            if !sub_slice.opaque_tile_descriptors.is_empty() {
676                self.descriptor.surfaces.push(
677                    CompositeSurfaceDescriptor {
678                        surface_id: sub_slice.native_surface.as_ref().map(|s| s.opaque),
679                        clip_rect: surface_clip_rect,
680                        transform: slice_transform,
681                        image_dependencies: [ImageDependency::INVALID; 3],
682                        image_rendering,
683                        tile_descriptors: sub_slice.opaque_tile_descriptors.clone(),
684                    }
685                );
686            }
687
688            // Add alpha tiles after opaque surfaces
689            if !sub_slice.alpha_tile_descriptors.is_empty() {
690                self.descriptor.surfaces.push(
691                    CompositeSurfaceDescriptor {
692                        surface_id: sub_slice.native_surface.as_ref().map(|s| s.alpha),
693                        clip_rect: surface_clip_rect,
694                        transform: slice_transform,
695                        image_dependencies: [ImageDependency::INVALID; 3],
696                        image_rendering,
697                        tile_descriptors: sub_slice.alpha_tile_descriptors.clone(),
698                    }
699                );
700            }
701
702            // For each compositor surface that was promoted, build the
703            // information required for the compositor to draw it
704            for compositor_surface in &sub_slice.compositor_surfaces {
705                let external_surface = &compositor_surface.descriptor;
706
707                let clip_rect = external_surface
708                    .clip_rect
709                    .intersection(&device_clip_rect)
710                    .unwrap_or_else(DeviceRect::zero);
711
712                let required_plane_count =
713                    match external_surface.dependency {
714                        ExternalSurfaceDependency::Yuv { format, .. } => {
715                            format.get_plane_num()
716                        },
717                        ExternalSurfaceDependency::Rgb { .. } => {
718                            1
719                        }
720                    };
721
722                let mut image_dependencies = [ImageDependency::INVALID; 3];
723
724                for i in 0 .. required_plane_count {
725                    let dependency = match external_surface.dependency {
726                        ExternalSurfaceDependency::Yuv { image_dependencies, .. } => {
727                            image_dependencies[i]
728                        },
729                        ExternalSurfaceDependency::Rgb { image_dependency, .. } => {
730                            image_dependency
731                        }
732                    };
733                    image_dependencies[i] = dependency;
734                }
735
736                // Get a new z_id for each compositor surface, to ensure correct ordering
737                // when drawing with the simple (Draw) compositor, and to schedule compositing
738                // of any required updates into the surfaces.
739                let needs_external_surface_update = match self.compositor_kind {
740                    CompositorKind::Draw { .. } => true,
741                    _ => external_surface.update_params.is_some(),
742                };
743                let external_surface_index = if needs_external_surface_update {
744                    let external_surface_index = self.compute_external_surface_dependencies(
745                        &external_surface,
746                        &image_dependencies,
747                        required_plane_count,
748                        resource_cache,
749                        gpu_cache,
750                        deferred_resolves,
751                    );
752                    if external_surface_index == ResolvedExternalSurfaceIndex::INVALID {
753                        continue;
754                    }
755                    external_surface_index
756                } else {
757                    ResolvedExternalSurfaceIndex::INVALID
758                };
759
760                let surface = CompositeTileSurface::ExternalSurface { external_surface_index };
761                let local_rect = external_surface.local_surface_size.cast_unit().into();
762
763                let tile = CompositeTile {
764                    kind: tile_kind(&surface, compositor_surface.is_opaque),
765                    surface,
766                    local_rect,
767                    local_valid_rect: local_rect,
768                    local_dirty_rect: local_rect,
769                    device_clip_rect: clip_rect,
770                    z_id: external_surface.z_id,
771                    transform_index: external_surface.transform_index,
772                };
773
774                // Add a surface descriptor for each compositor surface. For the Draw
775                // compositor, this is used to avoid composites being skipped by adding
776                // a dependency on the compositor surface external image keys / generations.
777                self.descriptor.surfaces.push(
778                    CompositeSurfaceDescriptor {
779                        surface_id: external_surface.native_surface_id,
780                        clip_rect,
781                        transform: self.get_compositor_transform(external_surface.transform_index).to_transform(),
782                        image_dependencies: image_dependencies,
783                        image_rendering: external_surface.image_rendering,
784                        tile_descriptors: Vec::new(),
785                    }
786                );
787
788                self.tiles.push(tile);
789            }
790        }
791    }
792
793    fn compute_external_surface_dependencies(
794        &mut self,
795        external_surface: &ExternalSurfaceDescriptor,
796        image_dependencies: &[ImageDependency; 3],
797        required_plane_count: usize,
798        resource_cache: &ResourceCache,
799        gpu_cache: &mut GpuCache,
800        deferred_resolves: &mut Vec<DeferredResolve>,
801    ) -> ResolvedExternalSurfaceIndex {
802        let mut planes = [
803            ExternalPlaneDescriptor::invalid(),
804            ExternalPlaneDescriptor::invalid(),
805            ExternalPlaneDescriptor::invalid(),
806        ];
807
808        let mut valid_plane_count = 0;
809        for i in 0 .. required_plane_count {
810            let request = ImageRequest {
811                key: image_dependencies[i].key,
812                rendering: external_surface.image_rendering,
813                tile: None,
814            };
815
816            let cache_item = resolve_image(
817                request,
818                resource_cache,
819                gpu_cache,
820                deferred_resolves,
821            );
822
823            if cache_item.texture_id != TextureSource::Invalid {
824                valid_plane_count += 1;
825                let plane = &mut planes[i];
826                *plane = ExternalPlaneDescriptor {
827                    texture: cache_item.texture_id,
828                    uv_rect: cache_item.uv_rect.into(),
829                };
830            }
831        }
832
833        // Check if there are valid images added for each YUV plane
834        if valid_plane_count < required_plane_count {
835            warn!("Warnings: skip a YUV/RGB compositor surface, found {}/{} valid images",
836                valid_plane_count,
837                required_plane_count,
838            );
839            return ResolvedExternalSurfaceIndex::INVALID;
840        }
841
842        let external_surface_index = ResolvedExternalSurfaceIndex(self.external_surfaces.len());
843
844        // If the external surface descriptor reports that the native surface
845        // needs to be updated, create an update params tuple for the renderer
846        // to use.
847        let update_params = external_surface.update_params.map(|surface_size| {
848            (
849                external_surface.native_surface_id.expect("bug: no native surface!"),
850                surface_size
851            )
852        });
853
854        match external_surface.dependency {
855            ExternalSurfaceDependency::Yuv{ color_space, format, channel_bit_depth, .. } => {
856
857                let image_buffer_kind = planes[0].texture.image_buffer_kind();
858
859                self.external_surfaces.push(ResolvedExternalSurface {
860                    color_data: ResolvedExternalSurfaceColorData::Yuv {
861                        image_dependencies: *image_dependencies,
862                        planes,
863                        color_space,
864                        format,
865                        channel_bit_depth,
866                        },
867                    image_buffer_kind,
868                    update_params,
869                });
870            },
871            ExternalSurfaceDependency::Rgb { .. } => {
872                let image_buffer_kind = planes[0].texture.image_buffer_kind();
873
874                self.external_surfaces.push(ResolvedExternalSurface {
875                    color_data: ResolvedExternalSurfaceColorData::Rgb {
876                        image_dependency: image_dependencies[0],
877                        plane: planes[0],
878                    },
879                    image_buffer_kind,
880                    update_params,
881                });
882            },
883        }
884        external_surface_index
885    }
886
887    pub fn end_frame(&mut self) {
888        // Sort tiles from front to back.
889        self.tiles.sort_by_key(|tile| tile.z_id.0);
890    }
891}
892
893/// An arbitrary identifier for a native (OS compositor) surface
894#[repr(C)]
895#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
896#[cfg_attr(feature = "capture", derive(Serialize))]
897#[cfg_attr(feature = "replay", derive(Deserialize))]
898pub struct NativeSurfaceId(pub u64);
899
900impl NativeSurfaceId {
901    /// A special id for the native surface that is used for debug / profiler overlays.
902    pub const DEBUG_OVERLAY: NativeSurfaceId = NativeSurfaceId(u64::MAX);
903}
904
905#[repr(C)]
906#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
907#[cfg_attr(feature = "capture", derive(Serialize))]
908#[cfg_attr(feature = "replay", derive(Deserialize))]
909pub struct NativeTileId {
910    pub surface_id: NativeSurfaceId,
911    pub x: i32,
912    pub y: i32,
913}
914
915impl NativeTileId {
916    /// A special id for the native surface that is used for debug / profiler overlays.
917    pub const DEBUG_OVERLAY: NativeTileId = NativeTileId {
918        surface_id: NativeSurfaceId::DEBUG_OVERLAY,
919        x: 0,
920        y: 0,
921    };
922}
923
924/// Information about a bound surface that the native compositor
925/// returns to WR.
926#[repr(C)]
927#[derive(Copy, Clone)]
928pub struct NativeSurfaceInfo {
929    /// An offset into the surface that WR should draw. Some compositing
930    /// implementations (notably, DirectComposition) use texture atlases
931    /// when the surface sizes are small. In this case, an offset can
932    /// be returned into the larger texture where WR should draw. This
933    /// can be (0, 0) if texture atlases are not used.
934    pub origin: DeviceIntPoint,
935    /// The ID of the FBO that WR should bind to, in order to draw to
936    /// the bound surface. On Windows (ANGLE) this will always be 0,
937    /// since creating a p-buffer sets the default framebuffer to
938    /// be the DirectComposition surface. On Mac, this will be non-zero,
939    /// since it identifies the IOSurface that has been bound to draw to.
940    // TODO(gw): This may need to be a larger / different type for WR
941    //           backends that are not GL.
942    pub fbo_id: u32,
943}
944
945#[repr(C)]
946#[derive(Debug, Copy, Clone, PartialEq)]
947#[cfg_attr(feature = "capture", derive(Serialize))]
948#[cfg_attr(feature = "replay", derive(Deserialize))]
949pub struct CompositorCapabilities {
950    /// The virtual surface size used by the underlying platform.
951    pub virtual_surface_size: i32,
952    /// Whether the compositor requires redrawing on invalidation.
953    pub redraw_on_invalidation: bool,
954    /// The maximum number of dirty rects that can be provided per compositor
955    /// surface update. If this is zero, the entire compositor surface for
956    /// a given tile will be drawn if it's dirty.
957    pub max_update_rects: usize,
958}
959
960impl Default for CompositorCapabilities {
961    fn default() -> Self {
962        // The default set of compositor capabilities for a given platform.
963        // These should only be modified if a compositor diverges specifically
964        // from the default behavior so that compositors don't have to track
965        // which changes to this structure unless necessary.
966        CompositorCapabilities {
967            virtual_surface_size: 0,
968            redraw_on_invalidation: false,
969            // Assume compositors can do at least partial update of surfaces. If not,
970            // the native compositor should override this to be 0.
971            max_update_rects: 1,
972        }
973    }
974}
975
976/// The transform type to apply to Compositor surfaces.
977// TODO: Should transform from CompositorSurfacePixel instead, but this requires a cleanup of the
978// Compositor API to use CompositorSurface-space geometry instead of Device-space where necessary
979// to avoid a bunch of noisy cast_unit calls and make it actually type-safe. May be difficult due
980// to pervasive use of Device-space nomenclature inside WR.
981// pub struct CompositorSurfacePixel;
982// pub type CompositorSurfaceTransform = Transform3D<f32, CompositorSurfacePixel, DevicePixel>;
983pub type CompositorSurfaceTransform = Transform3D<f32, DevicePixel, DevicePixel>;
984
985/// Defines an interface to a native (OS level) compositor. If supplied
986/// by the client application, then picture cache slices will be
987/// composited by the OS compositor, rather than drawn via WR batches.
988pub trait Compositor {
989    /// Create a new OS compositor surface with the given properties.
990    fn create_surface(
991        &mut self,
992        id: NativeSurfaceId,
993        virtual_offset: DeviceIntPoint,
994        tile_size: DeviceIntSize,
995        is_opaque: bool,
996    );
997
998    /// Create a new OS compositor surface that can be used with an
999    /// existing ExternalImageId, instead of being drawn to by WebRender.
1000    /// Surfaces created by this can only be used with attach_external_image,
1001    /// and not create_tile/destroy_tile/bind/unbind.
1002    fn create_external_surface(
1003        &mut self,
1004        id: NativeSurfaceId,
1005        is_opaque: bool,
1006    );
1007
1008    /// Destroy the surface with the specified id. WR may call this
1009    /// at any time the surface is no longer required (including during
1010    /// renderer deinit). It's the responsibility of the embedder
1011    /// to ensure that the surface is only freed once the GPU is
1012    /// no longer using the surface (if this isn't already handled
1013    /// by the operating system).
1014    fn destroy_surface(
1015        &mut self,
1016        id: NativeSurfaceId,
1017    );
1018
1019    /// Create a new OS compositor tile with the given properties.
1020    fn create_tile(
1021        &mut self,
1022        id: NativeTileId,
1023    );
1024
1025    /// Destroy an existing compositor tile.
1026    fn destroy_tile(
1027        &mut self,
1028        id: NativeTileId,
1029    );
1030
1031    /// Attaches an ExternalImageId to an OS compositor surface created
1032    /// by create_external_surface, and uses that as the contents of
1033    /// the surface. It is expected that a single surface will have
1034    /// many different images attached (like one for each video frame).
1035    fn attach_external_image(
1036        &mut self,
1037        id: NativeSurfaceId,
1038        external_image: ExternalImageId
1039    );
1040
1041    /// Mark a tile as invalid before any surfaces are queued for
1042    /// composition and before it is updated with bind. This is useful
1043    /// for early composition, allowing for dependency tracking of which
1044    /// surfaces can be composited early while others are still updating.
1045    fn invalidate_tile(
1046        &mut self,
1047        _id: NativeTileId,
1048        _valid_rect: DeviceIntRect
1049    ) {}
1050
1051    /// Bind this surface such that WR can issue OpenGL commands
1052    /// that will target the surface. Returns an (x, y) offset
1053    /// where WR should draw into the surface. This can be set
1054    /// to (0, 0) if the OS doesn't use texture atlases. The dirty
1055    /// rect is a local surface rect that specifies which part
1056    /// of the surface needs to be updated. If max_update_rects
1057    /// in CompositeConfig is 0, this will always be the size
1058    /// of the entire surface. The returned offset is only
1059    /// relevant to compositors that store surfaces in a texture
1060    /// atlas (that is, WR expects that the dirty rect doesn't
1061    /// affect the coordinates of the returned origin).
1062    fn bind(
1063        &mut self,
1064        id: NativeTileId,
1065        dirty_rect: DeviceIntRect,
1066        valid_rect: DeviceIntRect,
1067    ) -> NativeSurfaceInfo;
1068
1069    /// Unbind the surface. This is called by WR when it has
1070    /// finished issuing OpenGL commands on the current surface.
1071    fn unbind(
1072        &mut self,
1073    );
1074
1075    /// Begin the frame
1076    fn begin_frame(&mut self);
1077
1078    /// Add a surface to the visual tree to be composited. Visuals must
1079    /// be added every frame, between the begin/end transaction call. The
1080    /// z-order of the surfaces is determined by the order they are added
1081    /// to the visual tree.
1082    // TODO(gw): Adding visuals every frame makes the interface simple,
1083    //           but may have performance implications on some compositors?
1084    //           We might need to change the interface to maintain a visual
1085    //           tree that can be mutated?
1086    // TODO(gw): We might need to add a concept of a hierachy in future.
1087    fn add_surface(
1088        &mut self,
1089        id: NativeSurfaceId,
1090        transform: CompositorSurfaceTransform,
1091        clip_rect: DeviceIntRect,
1092        image_rendering: ImageRendering,
1093    );
1094
1095    /// Notify the compositor that all tiles have been invalidated and all
1096    /// native surfaces have been added, thus it is safe to start compositing
1097    /// valid surfaces. The dirty rects array allows native compositors that
1098    /// support partial present to skip copying unchanged areas.
1099    /// Optionally provides a set of rectangles for the areas known to be
1100    /// opaque, this is currently only computed if the caller is SwCompositor.
1101    fn start_compositing(
1102        &mut self,
1103        _clear_color: ColorF,
1104        _dirty_rects: &[DeviceIntRect],
1105        _opaque_rects: &[DeviceIntRect],
1106    ) {}
1107
1108    /// Commit any changes in the compositor tree for this frame. WR calls
1109    /// this once when all surface and visual updates are complete, to signal
1110    /// that the OS composite transaction should be applied.
1111    fn end_frame(&mut self);
1112
1113    /// Enable/disable native compositor usage
1114    fn enable_native_compositor(&mut self, enable: bool);
1115
1116    /// Safely deinitialize any remaining resources owned by the compositor.
1117    fn deinit(&mut self);
1118
1119    /// Get the capabilities struct for this compositor. This is used to
1120    /// specify what features a compositor supports, depending on the
1121    /// underlying platform
1122    fn get_capabilities(&self) -> CompositorCapabilities;
1123}
1124
1125/// Information about the underlying data buffer of a mapped tile.
1126#[repr(C)]
1127#[derive(Copy, Clone)]
1128pub struct MappedTileInfo {
1129    pub data: *mut c_void,
1130    pub stride: i32,
1131}
1132
1133/// Descriptor for a locked surface that will be directly composited by SWGL.
1134#[repr(C)]
1135pub struct SWGLCompositeSurfaceInfo {
1136    /// The number of YUV planes in the surface. 0 indicates non-YUV BGRA.
1137    /// 1 is interleaved YUV. 2 is NV12. 3 is planar YUV.
1138    pub yuv_planes: u32,
1139    /// Textures for planes of the surface, or 0 if not applicable.
1140    pub textures: [u32; 3],
1141    /// Color space of surface if using a YUV format.
1142    pub color_space: YuvRangedColorSpace,
1143    /// Color depth of surface if using a YUV format.
1144    pub color_depth: ColorDepth,
1145    /// The actual source surface size before transformation.
1146    pub size: DeviceIntSize,
1147}
1148
1149/// A Compositor variant that supports mapping tiles into CPU memory.
1150pub trait MappableCompositor: Compositor {
1151    /// Map a tile's underlying buffer so it can be used as the backing for
1152    /// a SWGL framebuffer. This is intended to be a replacement for 'bind'
1153    /// in any compositors that intend to directly interoperate with SWGL
1154    /// while supporting some form of native layers.
1155    fn map_tile(
1156        &mut self,
1157        id: NativeTileId,
1158        dirty_rect: DeviceIntRect,
1159        valid_rect: DeviceIntRect,
1160    ) -> Option<MappedTileInfo>;
1161
1162    /// Unmap a tile that was was previously mapped via map_tile to signal
1163    /// that SWGL is done rendering to the buffer.
1164    fn unmap_tile(&mut self);
1165
1166    fn lock_composite_surface(
1167        &mut self,
1168        ctx: *mut c_void,
1169        external_image_id: ExternalImageId,
1170        composite_info: *mut SWGLCompositeSurfaceInfo,
1171    ) -> bool;
1172    fn unlock_composite_surface(&mut self, ctx: *mut c_void, external_image_id: ExternalImageId);
1173}
1174
1175/// Defines an interface to a non-native (application-level) Compositor which handles
1176/// partial present. This is required if webrender must query the backbuffer's age.
1177/// TODO: Use the Compositor trait for native and non-native compositors, and integrate
1178/// this functionality there.
1179pub trait PartialPresentCompositor {
1180    /// Allows webrender to specify the total region that will be rendered to this frame,
1181    /// ie the frame's dirty region and some previous frames' dirty regions, if applicable
1182    /// (calculated using the buffer age). Must be called before anything has been rendered
1183    /// to the main framebuffer.
1184    fn set_buffer_damage_region(&mut self, rects: &[DeviceIntRect]);
1185}
1186
1187/// Information about an opaque surface used to occlude tiles.
1188#[cfg_attr(feature = "capture", derive(Serialize))]
1189#[cfg_attr(feature = "replay", derive(Deserialize))]
1190struct Occluder {
1191    z_id: ZBufferId,
1192    world_rect: WorldIntRect,
1193}
1194
1195// Whether this event is the start or end of a rectangle
1196#[derive(Debug)]
1197enum OcclusionEventKind {
1198    Begin,
1199    End,
1200}
1201
1202// A list of events on the y-axis, with the rectangle range that it affects on the x-axis
1203#[derive(Debug)]
1204struct OcclusionEvent {
1205    y: i32,
1206    x_range: ops::Range<i32>,
1207    kind: OcclusionEventKind,
1208}
1209
1210impl OcclusionEvent {
1211    fn new(y: i32, kind: OcclusionEventKind, x0: i32, x1: i32) -> Self {
1212        OcclusionEvent {
1213            y,
1214            x_range: ops::Range {
1215                start: x0,
1216                end: x1,
1217            },
1218            kind,
1219        }
1220    }
1221}
1222
1223/// List of registered occluders.
1224///
1225/// Also store a couple of vectors for reuse.
1226#[cfg_attr(feature = "capture", derive(Serialize))]
1227#[cfg_attr(feature = "replay", derive(Deserialize))]
1228pub struct Occluders {
1229    occluders: Vec<Occluder>,
1230
1231    // The two vectors below are kept to avoid unnecessary reallocations in area().
1232
1233    #[cfg_attr(feature = "serde", serde(skip))]
1234    events: Vec<OcclusionEvent>,
1235
1236    #[cfg_attr(feature = "serde", serde(skip))]
1237    active: Vec<ops::Range<i32>>,
1238}
1239
1240impl Occluders {
1241    fn new() -> Self {
1242        Occluders {
1243            occluders: Vec::new(),
1244            events: Vec::new(),
1245            active: Vec::new(),
1246        }
1247    }
1248
1249    fn push(&mut self, world_rect: WorldIntRect, z_id: ZBufferId) {
1250        self.occluders.push(Occluder { world_rect, z_id });
1251    }
1252
1253    /// Returns true if a tile with the specified rectangle and z_id
1254    /// is occluded by an opaque surface in front of it.
1255    pub fn is_tile_occluded(
1256        &mut self,
1257        z_id: ZBufferId,
1258        world_rect: WorldRect,
1259    ) -> bool {
1260        // It's often the case that a tile is only occluded by considering multiple
1261        // picture caches in front of it (for example, the background tiles are
1262        // often occluded by a combination of the content slice + the scrollbar slices).
1263
1264        // The basic algorithm is:
1265        //    For every occluder:
1266        //      If this occluder is in front of the tile we are querying:
1267        //         Clip the occluder rectangle to the query rectangle.
1268        //    Calculate the total non-overlapping area of those clipped occluders.
1269        //    If the cumulative area of those occluders is the same as the area of the query tile,
1270        //       Then the entire tile must be occluded and can be skipped during rasterization and compositing.
1271
1272        // Get the reference area we will compare against.
1273        let world_rect = world_rect.round().to_i32();
1274        let ref_area = world_rect.area();
1275
1276        // Calculate the non-overlapping area of the valid occluders.
1277        let cover_area = self.area(z_id, &world_rect);
1278        debug_assert!(cover_area <= ref_area);
1279
1280        // Check if the tile area is completely covered
1281        ref_area == cover_area
1282    }
1283
1284    /// Return the total area covered by a set of occluders, accounting for
1285    /// overlapping areas between those rectangles.
1286    fn area(
1287        &mut self,
1288        z_id: ZBufferId,
1289        clip_rect: &WorldIntRect,
1290    ) -> i32 {
1291        // This implementation is based on the article https://leetcode.com/articles/rectangle-area-ii/.
1292        // This is not a particularly efficient implementation (it skips building segment trees), however
1293        // we typically use this where the length of the rectangles array is < 10, so simplicity is more important.
1294
1295        self.events.clear();
1296        self.active.clear();
1297
1298        let mut area = 0;
1299
1300        // Step through each rectangle and build the y-axis event list
1301        for occluder in &self.occluders {
1302            // Only consider occluders in front of this rect
1303            if occluder.z_id.0 < z_id.0 {
1304                // Clip the source rect to the rectangle we care about, since we only
1305                // want to record area for the tile we are comparing to.
1306                if let Some(rect) = occluder.world_rect.intersection(clip_rect) {
1307                    let x0 = rect.min.x;
1308                    let x1 = x0 + rect.width();
1309                    self.events.push(OcclusionEvent::new(rect.min.y, OcclusionEventKind::Begin, x0, x1));
1310                    self.events.push(OcclusionEvent::new(rect.min.y + rect.height(), OcclusionEventKind::End, x0, x1));
1311                }
1312            }
1313        }
1314
1315        // If we didn't end up with any valid events, the area must be 0
1316        if self.events.is_empty() {
1317            return 0;
1318        }
1319
1320        // Sort the events by y-value
1321        self.events.sort_by_key(|e| e.y);
1322        let mut cur_y = self.events[0].y;
1323
1324        // Step through each y interval
1325        for event in &self.events {
1326            // This is the dimension of the y-axis we are accumulating areas for
1327            let dy = event.y - cur_y;
1328
1329            // If we have active events covering x-ranges in this y-interval, process them
1330            if dy != 0 && !self.active.is_empty() {
1331                assert!(dy > 0);
1332
1333                // Step through the x-ranges, ordered by x0 of each event
1334                self.active.sort_by_key(|i| i.start);
1335                let mut query = 0;
1336                let mut cur = self.active[0].start;
1337
1338                // Accumulate the non-overlapping x-interval that contributes to area for this y-interval.
1339                for interval in &self.active {
1340                    cur = interval.start.max(cur);
1341                    query += (interval.end - cur).max(0);
1342                    cur = cur.max(interval.end);
1343                }
1344
1345                // Accumulate total area for this y-interval
1346                area += query * dy;
1347            }
1348
1349            // Update the active events list
1350            match event.kind {
1351                OcclusionEventKind::Begin => {
1352                    self.active.push(event.x_range.clone());
1353                }
1354                OcclusionEventKind::End => {
1355                    let index = self.active.iter().position(|i| *i == event.x_range).unwrap();
1356                    self.active.remove(index);
1357                }
1358            }
1359
1360            cur_y = event.y;
1361        }
1362
1363        area
1364    }
1365}