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