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