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