Source code

Revision control

Other Tools

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
5
use api::{ColorF, YuvColorSpace, YuvFormat, ImageRendering};
6
use api::units::{DeviceRect, DeviceIntSize, DeviceIntRect, DeviceIntPoint, WorldRect};
7
use api::units::{DevicePixelScale, DevicePoint, PictureRect, TexelRect};
8
use crate::batch::{resolve_image, get_buffer_kind};
9
use crate::gpu_cache::GpuCache;
10
use crate::gpu_types::{ZBufferId, ZBufferIdGenerator};
11
use crate::internal_types::TextureSource;
12
use crate::picture::{ImageDependency, ResolvedSurfaceTexture, TileCacheInstance, TileSurface};
13
use crate::prim_store::DeferredResolve;
14
use crate::renderer::ImageBufferKind;
15
use crate::resource_cache::{ImageRequest, ResourceCache};
16
use std::{ops, u64};
17
18
/*
19
Types and definitions related to compositing picture cache tiles
20
and/or OS compositor integration.
21
*/
22
23
/// Describes details of an operation to apply to a native surface
24
#[derive(Debug, Clone)]
25
#[cfg_attr(feature = "capture", derive(Serialize))]
26
#[cfg_attr(feature = "replay", derive(Deserialize))]
27
pub enum NativeSurfaceOperationDetails {
28
CreateSurface {
29
id: NativeSurfaceId,
30
virtual_offset: DeviceIntPoint,
31
tile_size: DeviceIntSize,
32
is_opaque: bool,
33
},
34
DestroySurface {
35
id: NativeSurfaceId,
36
},
37
CreateTile {
38
id: NativeTileId,
39
},
40
DestroyTile {
41
id: NativeTileId,
42
}
43
}
44
45
/// Describes an operation to apply to a native surface
46
#[derive(Debug, Clone)]
47
#[cfg_attr(feature = "capture", derive(Serialize))]
48
#[cfg_attr(feature = "replay", derive(Deserialize))]
49
pub struct NativeSurfaceOperation {
50
pub details: NativeSurfaceOperationDetails,
51
}
52
53
/// Describes the source surface information for a tile to be composited. This
54
/// is the analog of the TileSurface type, with target surface information
55
/// resolved such that it can be used by the renderer.
56
#[cfg_attr(feature = "capture", derive(Serialize))]
57
#[cfg_attr(feature = "replay", derive(Deserialize))]
58
pub enum CompositeTileSurface {
59
Texture {
60
surface: ResolvedSurfaceTexture,
61
},
62
Color {
63
color: ColorF,
64
},
65
Clear,
66
ExternalSurface {
67
external_surface_index: ResolvedExternalSurfaceIndex,
68
},
69
}
70
71
/// The surface format for a tile being composited.
72
#[derive(Debug, Copy, Clone, PartialEq)]
73
pub enum CompositeSurfaceFormat {
74
Rgba,
75
Yuv,
76
}
77
78
/// Describes the geometry and surface of a tile to be composited
79
#[cfg_attr(feature = "capture", derive(Serialize))]
80
#[cfg_attr(feature = "replay", derive(Deserialize))]
81
pub struct CompositeTile {
82
pub surface: CompositeTileSurface,
83
pub rect: DeviceRect,
84
pub clip_rect: DeviceRect,
85
pub dirty_rect: DeviceRect,
86
pub valid_rect: DeviceRect,
87
pub z_id: ZBufferId,
88
}
89
90
/// Describes information about drawing a primitive as a compositor surface.
91
/// For now, we support only YUV images as compositor surfaces, but in future
92
/// this will also support RGBA images.
93
pub struct ExternalSurfaceDescriptor {
94
pub local_rect: PictureRect,
95
pub world_rect: WorldRect,
96
pub device_rect: DeviceRect,
97
pub local_clip_rect: PictureRect,
98
pub clip_rect: DeviceRect,
99
pub image_dependencies: [ImageDependency; 3],
100
pub image_rendering: ImageRendering,
101
pub yuv_color_space: YuvColorSpace,
102
pub yuv_format: YuvFormat,
103
pub yuv_rescale: f32,
104
pub z_id: ZBufferId,
105
/// If native compositing is enabled, the native compositor surface handle.
106
/// Otherwise, this will be None
107
pub native_surface_id: Option<NativeSurfaceId>,
108
/// If the native surface needs to be updated, this will contain the size
109
/// of the native surface as Some(size). If not dirty, this is None.
110
pub update_params: Option<DeviceIntSize>,
111
}
112
113
/// Information about a plane in a YUV surface.
114
#[cfg_attr(feature = "capture", derive(Serialize))]
115
#[cfg_attr(feature = "replay", derive(Deserialize))]
116
pub struct YuvPlaneDescriptor {
117
pub texture: TextureSource,
118
pub texture_layer: i32,
119
pub uv_rect: TexelRect,
120
}
121
122
impl YuvPlaneDescriptor {
123
fn invalid() -> Self {
124
YuvPlaneDescriptor {
125
texture: TextureSource::Invalid,
126
texture_layer: 0,
127
uv_rect: TexelRect::invalid(),
128
}
129
}
130
}
131
132
#[cfg_attr(feature = "capture", derive(Serialize))]
133
#[cfg_attr(feature = "replay", derive(Deserialize))]
134
#[derive(Debug, Copy, Clone)]
135
pub struct ResolvedExternalSurfaceIndex(pub usize);
136
137
/// An ExternalSurfaceDescriptor that has had image keys
138
/// resolved to texture handles. This contains all the
139
/// information that the compositor step in renderer
140
/// needs to know.
141
#[cfg_attr(feature = "capture", derive(Serialize))]
142
#[cfg_attr(feature = "replay", derive(Deserialize))]
143
pub struct ResolvedExternalSurface {
144
// YUV specific information
145
pub image_dependencies: [ImageDependency; 3],
146
pub yuv_planes: [YuvPlaneDescriptor; 3],
147
pub yuv_color_space: YuvColorSpace,
148
pub yuv_format: YuvFormat,
149
pub yuv_rescale: f32,
150
pub image_buffer_kind: ImageBufferKind,
151
152
// Update information for a native surface if it's dirty
153
pub update_params: Option<(NativeSurfaceId, DeviceIntSize)>,
154
}
155
156
/// Public interface specified in `RendererOptions` that configures
157
/// how WR compositing will operate.
158
pub enum CompositorConfig {
159
/// Let WR draw tiles via normal batching. This requires no special OS support.
160
Draw {
161
/// If this is zero, a full screen present occurs at the end of the
162
/// frame. This is the simplest and default mode. If this is non-zero,
163
/// then the operating system supports a form of 'partial present' where
164
/// only dirty regions of the framebuffer need to be updated.
165
max_partial_present_rects: usize,
166
},
167
/// Use a native OS compositor to draw tiles. This requires clients to implement
168
/// the Compositor trait, but can be significantly more power efficient on operating
169
/// systems that support it.
170
Native {
171
/// The maximum number of dirty rects that can be provided per compositor
172
/// surface update. If this is zero, the entire compositor surface for
173
/// a given tile will be drawn if it's dirty.
174
max_update_rects: usize,
175
/// A client provided interface to a native / OS compositor.
176
compositor: Box<dyn Compositor>,
177
}
178
}
179
180
impl CompositorConfig {
181
pub fn compositor(&mut self) -> Option<&mut Box<dyn Compositor>> {
182
match self {
183
CompositorConfig::Native { ref mut compositor, .. } => {
184
Some(compositor)
185
}
186
CompositorConfig::Draw { .. } => {
187
None
188
}
189
}
190
}
191
}
192
193
impl Default for CompositorConfig {
194
/// Default compositor config is full present without partial present.
195
fn default() -> Self {
196
CompositorConfig::Draw {
197
max_partial_present_rects: 0,
198
}
199
}
200
}
201
202
/// This is a representation of `CompositorConfig` without the `Compositor` trait
203
/// present. This allows it to be freely copied to other threads, such as the render
204
/// backend where the frame builder can access it.
205
#[cfg_attr(feature = "capture", derive(Serialize))]
206
#[cfg_attr(feature = "replay", derive(Deserialize))]
207
#[derive(Debug, Copy, Clone, PartialEq)]
208
pub enum CompositorKind {
209
/// WR handles compositing via drawing.
210
Draw {
211
/// Partial present support.
212
max_partial_present_rects: usize,
213
},
214
/// Native OS compositor.
215
Native {
216
/// Maximum dirty rects per compositor surface.
217
max_update_rects: usize,
218
/// The virtual surface size used by underlying platform.
219
virtual_surface_size: i32,
220
},
221
}
222
223
impl Default for CompositorKind {
224
/// Default compositor config is full present without partial present.
225
fn default() -> Self {
226
CompositorKind::Draw {
227
max_partial_present_rects: 0,
228
}
229
}
230
}
231
232
impl CompositorKind {
233
pub fn get_virtual_surface_size(&self) -> i32 {
234
match self {
235
CompositorKind::Draw { .. } => 0,
236
CompositorKind::Native { virtual_surface_size, .. } => *virtual_surface_size,
237
}
238
}
239
}
240
241
/// Information about an opaque surface used to occlude tiles.
242
#[cfg_attr(feature = "capture", derive(Serialize))]
243
#[cfg_attr(feature = "replay", derive(Deserialize))]
244
struct Occluder {
245
z_id: ZBufferId,
246
device_rect: DeviceIntRect,
247
}
248
249
/// Describes the properties that identify a tile composition uniquely.
250
#[cfg_attr(feature = "capture", derive(Serialize))]
251
#[cfg_attr(feature = "replay", derive(Deserialize))]
252
#[derive(PartialEq, Clone)]
253
pub struct CompositeSurfaceDescriptor {
254
pub surface_id: Option<NativeSurfaceId>,
255
pub offset: DevicePoint,
256
pub clip_rect: DeviceRect,
257
// A list of image keys and generations that this compositor surface
258
// depends on. This avoids composites being skipped when the only
259
// thing that has changed is the generation of an compositor surface
260
// image dependency.
261
pub image_dependencies: [ImageDependency; 3],
262
}
263
264
/// Describes surface properties used to composite a frame. This
265
/// is used to compare compositions between frames.
266
#[cfg_attr(feature = "capture", derive(Serialize))]
267
#[cfg_attr(feature = "replay", derive(Deserialize))]
268
#[derive(PartialEq, Clone)]
269
pub struct CompositeDescriptor {
270
pub surfaces: Vec<CompositeSurfaceDescriptor>,
271
}
272
273
impl CompositeDescriptor {
274
/// Construct an empty descriptor.
275
pub fn empty() -> Self {
276
CompositeDescriptor {
277
surfaces: Vec::new(),
278
}
279
}
280
}
281
282
/// The list of tiles to be drawn this frame
283
#[cfg_attr(feature = "capture", derive(Serialize))]
284
#[cfg_attr(feature = "replay", derive(Deserialize))]
285
pub struct CompositeState {
286
// TODO(gw): Consider splitting up CompositeState into separate struct types depending
287
// on the selected compositing mode. Many of the fields in this state struct
288
// are only applicable to either Native or Draw compositing mode.
289
/// List of opaque tiles to be drawn by the Draw compositor.
290
pub opaque_tiles: Vec<CompositeTile>,
291
/// List of alpha tiles to be drawn by the Draw compositor.
292
pub alpha_tiles: Vec<CompositeTile>,
293
/// List of clear tiles to be drawn by the Draw compositor.
294
pub clear_tiles: Vec<CompositeTile>,
295
/// List of primitives that were promoted to be compositor surfaces.
296
pub external_surfaces: Vec<ResolvedExternalSurface>,
297
/// Used to generate z-id values for tiles in the Draw compositor mode.
298
pub z_generator: ZBufferIdGenerator,
299
// If false, we can't rely on the dirty rects in the CompositeTile
300
// instances. This currently occurs during a scroll event, as a
301
// signal to refresh the whole screen. This is only a temporary
302
// measure until we integrate with OS compositors. In the meantime
303
// it gives us the ability to partial present for any non-scroll
304
// case as a simple win (e.g. video, animation etc).
305
pub dirty_rects_are_valid: bool,
306
/// The kind of compositor for picture cache tiles (e.g. drawn by WR, or OS compositor)
307
pub compositor_kind: CompositorKind,
308
/// Picture caching may be disabled dynamically, based on debug flags, pinch zoom etc.
309
pub picture_caching_is_enabled: bool,
310
/// The overall device pixel scale, used for tile occlusion conversions.
311
global_device_pixel_scale: DevicePixelScale,
312
/// List of registered occluders
313
occluders: Vec<Occluder>,
314
/// Description of the surfaces and properties that are being composited.
315
pub descriptor: CompositeDescriptor,
316
}
317
318
impl CompositeState {
319
/// Construct a new state for compositing picture tiles. This is created
320
/// during each frame construction and passed to the renderer.
321
pub fn new(
322
compositor_kind: CompositorKind,
323
mut picture_caching_is_enabled: bool,
324
global_device_pixel_scale: DevicePixelScale,
325
max_depth_ids: i32,
326
) -> Self {
327
// The native compositor interface requires picture caching to work, so
328
// force it here and warn if it was disabled.
329
if let CompositorKind::Native { .. } = compositor_kind {
330
if !picture_caching_is_enabled {
331
warn!("Picture caching cannot be disabled in native compositor config");
332
}
333
picture_caching_is_enabled = true;
334
}
335
336
CompositeState {
337
opaque_tiles: Vec::new(),
338
alpha_tiles: Vec::new(),
339
clear_tiles: Vec::new(),
340
z_generator: ZBufferIdGenerator::new(0, max_depth_ids),
341
dirty_rects_are_valid: true,
342
compositor_kind,
343
picture_caching_is_enabled,
344
global_device_pixel_scale,
345
occluders: Vec::new(),
346
descriptor: CompositeDescriptor::empty(),
347
external_surfaces: Vec::new(),
348
}
349
}
350
351
/// Register an occluder during picture cache updates that can be
352
/// used during frame building to occlude tiles.
353
pub fn register_occluder(
354
&mut self,
355
z_id: ZBufferId,
356
rect: WorldRect,
357
) {
358
let device_rect = (rect * self.global_device_pixel_scale).round().to_i32();
359
360
self.occluders.push(Occluder {
361
device_rect,
362
z_id,
363
});
364
}
365
366
/// Returns true if a tile with the specified rectangle and z_id
367
/// is occluded by an opaque surface in front of it.
368
pub fn is_tile_occluded(
369
&self,
370
z_id: ZBufferId,
371
device_rect: DeviceRect,
372
) -> bool {
373
// It's often the case that a tile is only occluded by considering multiple
374
// picture caches in front of it (for example, the background tiles are
375
// often occluded by a combination of the content slice + the scrollbar slices).
376
377
// The basic algorithm is:
378
// For every occluder:
379
// If this occluder is in front of the tile we are querying:
380
// Clip the occluder rectangle to the query rectangle.
381
// Calculate the total non-overlapping area of those clipped occluders.
382
// If the cumulative area of those occluders is the same as the area of the query tile,
383
// Then the entire tile must be occluded and can be skipped during rasterization and compositing.
384
385
// Get the reference area we will compare against.
386
let device_rect = device_rect.round().to_i32();
387
let ref_area = device_rect.size.width * device_rect.size.height;
388
389
// Calculate the non-overlapping area of the valid occluders.
390
let cover_area = area_of_occluders(&self.occluders, z_id, &device_rect);
391
debug_assert!(cover_area <= ref_area);
392
393
// Check if the tile area is completely covered
394
ref_area == cover_area
395
}
396
397
/// Add a picture cache to be composited
398
pub fn push_surface(
399
&mut self,
400
tile_cache: &TileCacheInstance,
401
device_clip_rect: DeviceRect,
402
global_device_pixel_scale: DevicePixelScale,
403
resource_cache: &ResourceCache,
404
gpu_cache: &mut GpuCache,
405
deferred_resolves: &mut Vec<DeferredResolve>,
406
) {
407
let mut visible_opaque_tile_count = 0;
408
let mut visible_alpha_tile_count = 0;
409
410
for tile in tile_cache.tiles.values() {
411
if !tile.is_visible {
412
// This can occur when a tile is found to be occluded during frame building.
413
continue;
414
}
415
416
let device_rect = (tile.world_tile_rect * global_device_pixel_scale).round();
417
let surface = tile.surface.as_ref().expect("no tile surface set!");
418
419
let (surface, is_opaque) = match surface {
420
TileSurface::Color { color } => {
421
(CompositeTileSurface::Color { color: *color }, true)
422
}
423
TileSurface::Clear => {
424
(CompositeTileSurface::Clear, false)
425
}
426
TileSurface::Texture { descriptor, .. } => {
427
let surface = descriptor.resolve(resource_cache, tile_cache.current_tile_size);
428
(
429
CompositeTileSurface::Texture { surface },
430
tile.is_opaque || tile_cache.is_opaque(),
431
)
432
}
433
};
434
435
if is_opaque {
436
visible_opaque_tile_count += 1;
437
} else {
438
visible_alpha_tile_count += 1;
439
}
440
441
let tile = CompositeTile {
442
surface,
443
rect: device_rect,
444
valid_rect: tile.device_valid_rect.translate(-device_rect.origin.to_vector()),
445
dirty_rect: tile.device_dirty_rect.translate(-device_rect.origin.to_vector()),
446
clip_rect: device_clip_rect,
447
z_id: tile.z_id,
448
};
449
450
self.push_tile(tile, is_opaque);
451
}
452
453
// Add opaque surface before any compositor surfaces
454
if visible_opaque_tile_count > 0 {
455
self.descriptor.surfaces.push(
456
CompositeSurfaceDescriptor {
457
surface_id: tile_cache.native_surface.as_ref().map(|s| s.opaque),
458
offset: tile_cache.device_position,
459
clip_rect: device_clip_rect,
460
image_dependencies: [ImageDependency::INVALID; 3],
461
}
462
);
463
}
464
465
// For each compositor surface that was promoted, build the
466
// information required for the compositor to draw it
467
for external_surface in &tile_cache.external_surfaces {
468
let mut yuv_planes = [
469
YuvPlaneDescriptor::invalid(),
470
YuvPlaneDescriptor::invalid(),
471
YuvPlaneDescriptor::invalid(),
472
];
473
474
// Step through the image keys, and build a yuv plane descriptor for each
475
let required_plane_count = external_surface.yuv_format.get_plane_num();
476
let mut valid_plane_count = 0;
477
478
for i in 0 .. required_plane_count {
479
let key = external_surface.image_dependencies[i].key;
480
let plane = &mut yuv_planes[i];
481
482
let request = ImageRequest {
483
key,
484
rendering: external_surface.image_rendering,
485
tile: None,
486
};
487
488
let cache_item = resolve_image(
489
request,
490
resource_cache,
491
gpu_cache,
492
deferred_resolves,
493
);
494
495
if cache_item.texture_id != TextureSource::Invalid {
496
valid_plane_count += 1;
497
498
*plane = YuvPlaneDescriptor {
499
texture: cache_item.texture_id,
500
texture_layer: cache_item.texture_layer,
501
uv_rect: cache_item.uv_rect.into(),
502
};
503
}
504
}
505
506
// Check if there are valid images added for each YUV plane
507
if valid_plane_count < required_plane_count {
508
warn!("Warnings: skip a YUV compositor surface, found {}/{} valid images",
509
valid_plane_count,
510
required_plane_count,
511
);
512
continue;
513
}
514
515
let clip_rect = external_surface
516
.clip_rect
517
.intersection(&device_clip_rect)
518
.unwrap_or_else(DeviceRect::zero);
519
520
// Get a new z_id for each compositor surface, to ensure correct ordering
521
// when drawing with the simple (Draw) compositor.
522
523
let surface = CompositeTileSurface::ExternalSurface {
524
external_surface_index: ResolvedExternalSurfaceIndex(self.external_surfaces.len()),
525
};
526
527
// If the external surface descriptor reports that the native surface
528
// needs to be updated, create an update params tuple for the renderer
529
// to use.
530
let update_params = external_surface.update_params.map(|surface_size| {
531
(
532
external_surface.native_surface_id.expect("bug: no native surface!"),
533
surface_size
534
)
535
});
536
537
self.external_surfaces.push(ResolvedExternalSurface {
538
yuv_color_space: external_surface.yuv_color_space,
539
yuv_format: external_surface.yuv_format,
540
yuv_rescale: external_surface.yuv_rescale,
541
image_buffer_kind: get_buffer_kind(yuv_planes[0].texture),
542
image_dependencies: external_surface.image_dependencies,
543
yuv_planes,
544
update_params,
545
});
546
547
let tile = CompositeTile {
548
surface,
549
rect: external_surface.device_rect,
550
valid_rect: external_surface.device_rect.translate(-external_surface.device_rect.origin.to_vector()),
551
dirty_rect: external_surface.device_rect.translate(-external_surface.device_rect.origin.to_vector()),
552
clip_rect,
553
z_id: external_surface.z_id,
554
};
555
556
// Add a surface descriptor for each compositor surface. For the Draw
557
// compositor, this is used to avoid composites being skipped by adding
558
// a dependency on the compositor surface external image keys / generations.
559
self.descriptor.surfaces.push(
560
CompositeSurfaceDescriptor {
561
surface_id: external_surface.native_surface_id,
562
offset: tile.rect.origin,
563
clip_rect: tile.clip_rect,
564
image_dependencies: external_surface.image_dependencies,
565
}
566
);
567
568
self.push_tile(tile, true);
569
}
570
571
// Add alpha / overlay tiles after compositor surfaces
572
if visible_alpha_tile_count > 0 {
573
self.descriptor.surfaces.push(
574
CompositeSurfaceDescriptor {
575
surface_id: tile_cache.native_surface.as_ref().map(|s| s.alpha),
576
offset: tile_cache.device_position,
577
clip_rect: device_clip_rect,
578
image_dependencies: [ImageDependency::INVALID; 3],
579
}
580
);
581
}
582
}
583
584
/// Add a tile to the appropriate array, depending on tile properties and compositor mode.
585
fn push_tile(
586
&mut self,
587
tile: CompositeTile,
588
is_opaque: bool,
589
) {
590
match tile.surface {
591
CompositeTileSurface::Color { .. } => {
592
// Color tiles are, by definition, opaque. We might support non-opaque color
593
// tiles if we ever find pages that have a lot of these.
594
self.opaque_tiles.push(tile);
595
}
596
CompositeTileSurface::Clear => {
597
// Clear tiles have a special bucket
598
self.clear_tiles.push(tile);
599
}
600
CompositeTileSurface::Texture { .. } => {
601
// Texture surfaces get bucketed by opaque/alpha, for z-rejection
602
// on the Draw compositor mode.
603
if is_opaque {
604
self.opaque_tiles.push(tile);
605
} else {
606
self.alpha_tiles.push(tile);
607
}
608
}
609
CompositeTileSurface::ExternalSurface { .. } => {
610
self.opaque_tiles.push(tile);
611
}
612
}
613
}
614
}
615
616
/// An arbitrary identifier for a native (OS compositor) surface
617
#[repr(C)]
618
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
619
#[cfg_attr(feature = "capture", derive(Serialize))]
620
#[cfg_attr(feature = "replay", derive(Deserialize))]
621
pub struct NativeSurfaceId(pub u64);
622
623
impl NativeSurfaceId {
624
/// A special id for the native surface that is used for debug / profiler overlays.
625
pub const DEBUG_OVERLAY: NativeSurfaceId = NativeSurfaceId(u64::MAX);
626
}
627
628
#[repr(C)]
629
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
630
#[cfg_attr(feature = "capture", derive(Serialize))]
631
#[cfg_attr(feature = "replay", derive(Deserialize))]
632
pub struct NativeTileId {
633
pub surface_id: NativeSurfaceId,
634
pub x: i32,
635
pub y: i32,
636
}
637
638
impl NativeTileId {
639
/// A special id for the native surface that is used for debug / profiler overlays.
640
pub const DEBUG_OVERLAY: NativeTileId = NativeTileId {
641
surface_id: NativeSurfaceId::DEBUG_OVERLAY,
642
x: 0,
643
y: 0,
644
};
645
}
646
647
/// Information about a bound surface that the native compositor
648
/// returns to WR.
649
#[repr(C)]
650
#[derive(Copy, Clone)]
651
pub struct NativeSurfaceInfo {
652
/// An offset into the surface that WR should draw. Some compositing
653
/// implementations (notably, DirectComposition) use texture atlases
654
/// when the surface sizes are small. In this case, an offset can
655
/// be returned into the larger texture where WR should draw. This
656
/// can be (0, 0) if texture atlases are not used.
657
pub origin: DeviceIntPoint,
658
/// The ID of the FBO that WR should bind to, in order to draw to
659
/// the bound surface. On Windows (ANGLE) this will always be 0,
660
/// since creating a p-buffer sets the default framebuffer to
661
/// be the DirectComposition surface. On Mac, this will be non-zero,
662
/// since it identifies the IOSurface that has been bound to draw to.
663
// TODO(gw): This may need to be a larger / different type for WR
664
// backends that are not GL.
665
pub fbo_id: u32,
666
}
667
668
#[repr(C)]
669
pub struct CompositorCapabilities {
670
pub virtual_surface_size: i32,
671
}
672
673
/// Defines an interface to a native (OS level) compositor. If supplied
674
/// by the client application, then picture cache slices will be
675
/// composited by the OS compositor, rather than drawn via WR batches.
676
pub trait Compositor {
677
/// Create a new OS compositor surface with the given properties.
678
fn create_surface(
679
&mut self,
680
id: NativeSurfaceId,
681
virtual_offset: DeviceIntPoint,
682
tile_size: DeviceIntSize,
683
is_opaque: bool,
684
);
685
686
/// Destroy the surface with the specified id. WR may call this
687
/// at any time the surface is no longer required (including during
688
/// renderer deinit). It's the responsibility of the embedder
689
/// to ensure that the surface is only freed once the GPU is
690
/// no longer using the surface (if this isn't already handled
691
/// by the operating system).
692
fn destroy_surface(
693
&mut self,
694
id: NativeSurfaceId,
695
);
696
697
/// Create a new OS compositor tile with the given properties.
698
fn create_tile(
699
&mut self,
700
id: NativeTileId,
701
);
702
703
/// Destroy an existing compositor tile.
704
fn destroy_tile(
705
&mut self,
706
id: NativeTileId,
707
);
708
709
/// Bind this surface such that WR can issue OpenGL commands
710
/// that will target the surface. Returns an (x, y) offset
711
/// where WR should draw into the surface. This can be set
712
/// to (0, 0) if the OS doesn't use texture atlases. The dirty
713
/// rect is a local surface rect that specifies which part
714
/// of the surface needs to be updated. If max_update_rects
715
/// in CompositeConfig is 0, this will always be the size
716
/// of the entire surface. The returned offset is only
717
/// relevant to compositors that store surfaces in a texture
718
/// atlas (that is, WR expects that the dirty rect doesn't
719
/// affect the coordinates of the returned origin).
720
fn bind(
721
&mut self,
722
id: NativeTileId,
723
dirty_rect: DeviceIntRect,
724
valid_rect: DeviceIntRect,
725
) -> NativeSurfaceInfo;
726
727
/// Unbind the surface. This is called by WR when it has
728
/// finished issuing OpenGL commands on the current surface.
729
fn unbind(
730
&mut self,
731
);
732
733
/// Begin the frame
734
fn begin_frame(&mut self);
735
736
/// Add a surface to the visual tree to be composited. Visuals must
737
/// be added every frame, between the begin/end transaction call. The
738
/// z-order of the surfaces is determined by the order they are added
739
/// to the visual tree.
740
// TODO(gw): Adding visuals every frame makes the interface simple,
741
// but may have performance implications on some compositors?
742
// We might need to change the interface to maintain a visual
743
// tree that can be mutated?
744
// TODO(gw): We might need to add a concept of a hierachy in future.
745
// TODO(gw): In future, expand to support a more complete transform matrix.
746
fn add_surface(
747
&mut self,
748
id: NativeSurfaceId,
749
position: DeviceIntPoint,
750
clip_rect: DeviceIntRect,
751
);
752
753
/// Commit any changes in the compositor tree for this frame. WR calls
754
/// this once when all surface and visual updates are complete, to signal
755
/// that the OS composite transaction should be applied.
756
fn end_frame(&mut self);
757
758
/// Enable/disable native compositor usage
759
fn enable_native_compositor(&mut self, enable: bool);
760
761
/// Safely deinitialize any remaining resources owned by the compositor.
762
fn deinit(&mut self);
763
764
/// Get the capabilities struct for this compositor. This is used to
765
/// specify what features a compositor supports, depending on the
766
/// underlying platform
767
fn get_capabilities(&self) -> CompositorCapabilities;
768
}
769
770
/// Return the total area covered by a set of occluders, accounting for
771
/// overlapping areas between those rectangles.
772
fn area_of_occluders(
773
occluders: &[Occluder],
774
z_id: ZBufferId,
775
clip_rect: &DeviceIntRect,
776
) -> i32 {
777
// This implementation is based on the article https://leetcode.com/articles/rectangle-area-ii/.
778
// This is not a particularly efficient implementation (it skips building segment trees), however
779
// we typically use this where the length of the rectangles array is < 10, so simplicity is more important.
780
781
let mut area = 0;
782
783
// Whether this event is the start or end of a rectangle
784
#[derive(Debug)]
785
enum EventKind {
786
Begin,
787
End,
788
}
789
790
// A list of events on the y-axis, with the rectangle range that it affects on the x-axis
791
#[derive(Debug)]
792
struct Event {
793
y: i32,
794
x_range: ops::Range<i32>,
795
kind: EventKind,
796
}
797
798
impl Event {
799
fn new(y: i32, kind: EventKind, x0: i32, x1: i32) -> Self {
800
Event {
801
y,
802
x_range: ops::Range {
803
start: x0,
804
end: x1,
805
},
806
kind,
807
}
808
}
809
}
810
811
// Step through each rectangle and build the y-axis event list
812
let mut events = Vec::with_capacity(occluders.len() * 2);
813
for occluder in occluders {
814
// Only consider occluders in front of this rect
815
if occluder.z_id.0 > z_id.0 {
816
// Clip the source rect to the rectangle we care about, since we only
817
// want to record area for the tile we are comparing to.
818
if let Some(rect) = occluder.device_rect.intersection(clip_rect) {
819
let x0 = rect.origin.x;
820
let x1 = x0 + rect.size.width;
821
events.push(Event::new(rect.origin.y, EventKind::Begin, x0, x1));
822
events.push(Event::new(rect.origin.y + rect.size.height, EventKind::End, x0, x1));
823
}
824
}
825
}
826
827
// If we didn't end up with any valid events, the area must be 0
828
if events.is_empty() {
829
return 0;
830
}
831
832
// Sort the events by y-value
833
events.sort_by_key(|e| e.y);
834
let mut active: Vec<ops::Range<i32>> = Vec::new();
835
let mut cur_y = events[0].y;
836
837
// Step through each y interval
838
for event in &events {
839
// This is the dimension of the y-axis we are accumulating areas for
840
let dy = event.y - cur_y;
841
842
// If we have active events covering x-ranges in this y-interval, process them
843
if dy != 0 && !active.is_empty() {
844
assert!(dy > 0);
845
846
// Step through the x-ranges, ordered by x0 of each event
847
active.sort_by_key(|i| i.start);
848
let mut query = 0;
849
let mut cur = active[0].start;
850
851
// Accumulate the non-overlapping x-interval that contributes to area for this y-interval.
852
for interval in &active {
853
cur = interval.start.max(cur);
854
query += (interval.end - cur).max(0);
855
cur = cur.max(interval.end);
856
}
857
858
// Accumulate total area for this y-interval
859
area += query * dy;
860
}
861
862
// Update the active events list
863
match event.kind {
864
EventKind::Begin => {
865
active.push(event.x_range.clone());
866
}
867
EventKind::End => {
868
let index = active.iter().position(|i| *i == event.x_range).unwrap();
869
active.remove(index);
870
}
871
}
872
873
cur_y = event.y;
874
}
875
876
area
877
}