visibility.rs (17887B)
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 //! # Visibility pass 6 //! 7 //! TODO: document what this pass does! 8 //! 9 10 use api::DebugFlags; 11 use api::units::*; 12 use std::usize; 13 use crate::clip::ClipStore; 14 use crate::composite::CompositeState; 15 use crate::profiler::TransactionProfile; 16 use crate::renderer::GpuBufferBuilder; 17 use crate::spatial_tree::{SpatialTree, SpatialNodeIndex}; 18 use crate::clip::{ClipChainInstance, ClipTree}; 19 use crate::frame_builder::FrameBuilderConfig; 20 use crate::picture::{PictureCompositeMode, ClusterFlags, SurfaceInfo}; 21 use crate::tile_cache::TileCacheInstance; 22 use crate::picture::{SurfaceIndex, RasterConfig}; 23 use crate::tile_cache::SubSliceIndex; 24 use crate::prim_store::{ClipTaskIndex, PictureIndex, PrimitiveInstanceKind}; 25 use crate::prim_store::{PrimitiveStore, PrimitiveInstance}; 26 use crate::render_backend::{DataStores, ScratchBuffer}; 27 use crate::render_task_graph::RenderTaskGraphBuilder; 28 use crate::resource_cache::ResourceCache; 29 use crate::scene::SceneProperties; 30 use crate::space::SpaceMapper; 31 use crate::util::MaxRect; 32 33 pub struct FrameVisibilityContext<'a> { 34 pub spatial_tree: &'a SpatialTree, 35 pub global_screen_world_rect: WorldRect, 36 pub global_device_pixel_scale: DevicePixelScale, 37 pub debug_flags: DebugFlags, 38 pub scene_properties: &'a SceneProperties, 39 pub config: FrameBuilderConfig, 40 pub root_spatial_node_index: SpatialNodeIndex, 41 } 42 43 pub struct FrameVisibilityState<'a> { 44 pub clip_store: &'a mut ClipStore, 45 pub resource_cache: &'a mut ResourceCache, 46 pub frame_gpu_data: &'a mut GpuBufferBuilder, 47 pub data_stores: &'a mut DataStores, 48 pub clip_tree: &'a mut ClipTree, 49 pub composite_state: &'a mut CompositeState, 50 pub rg_builder: &'a mut RenderTaskGraphBuilder, 51 pub prim_instances: &'a mut [PrimitiveInstance], 52 pub surfaces: &'a mut [SurfaceInfo], 53 /// A stack of currently active off-screen surfaces during the 54 /// visibility frame traversal. 55 pub surface_stack: Vec<(PictureIndex, SurfaceIndex)>, 56 pub profile: &'a mut TransactionProfile, 57 pub scratch: &'a mut ScratchBuffer, 58 pub visited_pictures: &'a mut[bool], 59 } 60 61 impl<'a> FrameVisibilityState<'a> { 62 pub fn push_surface( 63 &mut self, 64 pic_index: PictureIndex, 65 surface_index: SurfaceIndex, 66 ) { 67 self.surface_stack.push((pic_index, surface_index)); 68 } 69 70 pub fn pop_surface(&mut self) { 71 self.surface_stack.pop().unwrap(); 72 } 73 } 74 75 bitflags! { 76 /// A set of bitflags that can be set in the visibility information 77 /// for a primitive instance. This can be used to control how primitives 78 /// are treated during batching. 79 // TODO(gw): We should also move `is_compositor_surface` to be part of 80 // this flags struct. 81 #[cfg_attr(feature = "capture", derive(Serialize))] 82 #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] 83 pub struct PrimitiveVisibilityFlags: u8 { 84 /// Implies that this primitive covers the entire picture cache slice, 85 /// and can thus be dropped during batching and drawn with clear color. 86 const IS_BACKDROP = 1; 87 } 88 } 89 90 /// Contains the current state of the primitive's visibility. 91 #[derive(Debug)] 92 #[cfg_attr(feature = "capture", derive(Serialize))] 93 pub enum VisibilityState { 94 /// Uninitialized - this should never be encountered after prim reset 95 Unset, 96 /// Culled for being off-screen, or not possible to render (e.g. missing image resource) 97 Culled, 98 /// A picture that doesn't have a surface - primitives are composed into the 99 /// parent picture with a surface. 100 PassThrough, 101 /// A primitive that has been found to be visible 102 Visible { 103 /// A set of flags that define how this primitive should be handled 104 /// during batching of visible primitives. 105 vis_flags: PrimitiveVisibilityFlags, 106 107 /// Sub-slice within the picture cache that this prim exists on 108 sub_slice_index: SubSliceIndex, 109 }, 110 } 111 112 /// Information stored for a visible primitive about the visible 113 /// rect and associated clip information. 114 #[derive(Debug)] 115 #[cfg_attr(feature = "capture", derive(Serialize))] 116 pub struct PrimitiveVisibility { 117 /// The clip chain instance that was built for this primitive. 118 pub clip_chain: ClipChainInstance, 119 120 /// Current visibility state of the primitive. 121 // TODO(gw): Move more of the fields from this struct into 122 // the state enum. 123 pub state: VisibilityState, 124 125 /// An index into the clip task instances array in the primitive 126 /// store. If this is ClipTaskIndex::INVALID, then the primitive 127 /// has no clip mask. Otherwise, it may store the offset of the 128 /// global clip mask task for this primitive, or the first of 129 /// a list of clip task ids (one per segment). 130 pub clip_task_index: ClipTaskIndex, 131 } 132 133 impl PrimitiveVisibility { 134 pub fn new() -> Self { 135 PrimitiveVisibility { 136 state: VisibilityState::Unset, 137 clip_chain: ClipChainInstance::empty(), 138 clip_task_index: ClipTaskIndex::INVALID, 139 } 140 } 141 142 pub fn reset(&mut self) { 143 self.state = VisibilityState::Culled; 144 self.clip_task_index = ClipTaskIndex::INVALID; 145 } 146 } 147 148 pub fn update_prim_visibility( 149 pic_index: PictureIndex, 150 parent_surface_index: Option<SurfaceIndex>, 151 world_culling_rect: &WorldRect, 152 store: &PrimitiveStore, 153 is_root_tile_cache: bool, 154 frame_context: &FrameVisibilityContext, 155 frame_state: &mut FrameVisibilityState, 156 tile_cache: &mut Option<&mut TileCacheInstance>, 157 ) { 158 if frame_state.visited_pictures[pic_index.0] { 159 return; 160 } 161 frame_state.visited_pictures[pic_index.0] = true; 162 let pic = &store.pictures[pic_index.0]; 163 164 let (surface_index, pop_surface) = match pic.raster_config { 165 Some(RasterConfig { surface_index, composite_mode: PictureCompositeMode::TileCache { .. }, .. }) => { 166 (surface_index, false) 167 } 168 Some(ref raster_config) => { 169 frame_state.push_surface( 170 pic_index, 171 raster_config.surface_index, 172 ); 173 174 if let Some(parent_surface_index) = parent_surface_index { 175 let parent_culling_rect = frame_state 176 .surfaces[parent_surface_index.0] 177 .culling_rect; 178 179 let surface = &mut frame_state 180 .surfaces[raster_config.surface_index.0 as usize]; 181 182 surface.update_culling_rect( 183 parent_culling_rect, 184 &raster_config.composite_mode, 185 frame_context, 186 ); 187 } 188 189 let surface_local_rect = frame_state.surfaces[raster_config.surface_index.0] 190 .unclipped_local_rect 191 .cast_unit(); 192 193 // Let the picture cache know that we are pushing an off-screen 194 // surface, so it can treat dependencies of surface atomically. 195 if let Some(tile_cache) = tile_cache { 196 tile_cache.push_surface( 197 surface_local_rect, 198 pic.spatial_node_index, 199 frame_context.spatial_tree, 200 ); 201 } 202 203 (raster_config.surface_index, true) 204 } 205 None => { 206 (parent_surface_index.expect("bug: pass-through with no parent"), false) 207 } 208 }; 209 210 let surface = &frame_state.surfaces[surface_index.0 as usize]; 211 let surface_culling_rect = surface.culling_rect; 212 213 let device_pixel_scale = surface.device_pixel_scale; 214 let mut map_local_to_picture = surface.map_local_to_picture.clone(); 215 216 let map_surface_to_vis = SpaceMapper::new_with_target( 217 // TODO: switch from root to raster space. 218 frame_context.root_spatial_node_index, 219 surface.surface_spatial_node_index, 220 surface.culling_rect, 221 frame_context.spatial_tree, 222 ); 223 let visibility_spatial_node_index = surface.visibility_spatial_node_index; 224 225 for cluster in &pic.prim_list.clusters { 226 profile_scope!("cluster"); 227 228 // Each prim instance must have reset called each frame, to clear 229 // indices into various scratch buffers. If this doesn't occur, 230 // the primitive may incorrectly be considered visible, which can 231 // cause unexpected conditions to occur later during the frame. 232 // Primitive instances are normally reset in the main loop below, 233 // but we must also reset them in the rare case that the cluster 234 // visibility has changed (due to an invalid transform and/or 235 // backface visibility changing for this cluster). 236 // TODO(gw): This is difficult to test for in CI - as a follow up, 237 // we should add a debug flag that validates the prim 238 // instance is always reset every frame to catch similar 239 // issues in future. 240 for prim_instance in &mut frame_state.prim_instances[cluster.prim_range()] { 241 prim_instance.reset(); 242 } 243 244 // Get the cluster and see if is visible 245 if !cluster.flags.contains(ClusterFlags::IS_VISIBLE) { 246 continue; 247 } 248 249 map_local_to_picture.set_target_spatial_node( 250 cluster.spatial_node_index, 251 frame_context.spatial_tree, 252 ); 253 254 for prim_instance_index in cluster.prim_range() { 255 if let PrimitiveInstanceKind::Picture { pic_index, .. } = frame_state.prim_instances[prim_instance_index].kind { 256 if !store.pictures[pic_index.0].is_visible(frame_context.spatial_tree) { 257 continue; 258 } 259 260 let is_passthrough = match store.pictures[pic_index.0].raster_config { 261 Some(..) => false, 262 None => true, 263 }; 264 265 if !is_passthrough { 266 let clip_root = store 267 .pictures[pic_index.0] 268 .clip_root 269 .unwrap_or_else(|| { 270 // If we couldn't find a common ancestor then just use the 271 // clip node of the picture primitive itself 272 let leaf_id = frame_state.prim_instances[prim_instance_index].clip_leaf_id; 273 frame_state.clip_tree.get_leaf(leaf_id).node_id 274 } 275 ); 276 277 frame_state.clip_tree.push_clip_root_node(clip_root); 278 } 279 280 update_prim_visibility( 281 pic_index, 282 Some(surface_index), 283 world_culling_rect, 284 store, 285 false, 286 frame_context, 287 frame_state, 288 tile_cache, 289 ); 290 291 if is_passthrough { 292 // Pass through pictures are always considered visible in all dirty tiles. 293 frame_state.prim_instances[prim_instance_index].vis.state = VisibilityState::PassThrough; 294 295 continue; 296 } else { 297 frame_state.clip_tree.pop_clip_root(); 298 } 299 } 300 301 let prim_instance = &mut frame_state.prim_instances[prim_instance_index]; 302 303 let local_coverage_rect = frame_state.data_stores.get_local_prim_coverage_rect( 304 prim_instance, 305 &store.pictures, 306 frame_state.surfaces, 307 ); 308 309 frame_state.clip_store.set_active_clips( 310 cluster.spatial_node_index, 311 map_local_to_picture.ref_spatial_node_index, 312 visibility_spatial_node_index, 313 prim_instance.clip_leaf_id, 314 &frame_context.spatial_tree, 315 &frame_state.data_stores.clip, 316 frame_state.clip_tree, 317 ); 318 319 let clip_chain = frame_state 320 .clip_store 321 .build_clip_chain_instance( 322 local_coverage_rect, 323 &map_local_to_picture, 324 &map_surface_to_vis, 325 &frame_context.spatial_tree, 326 &mut frame_state.frame_gpu_data.f32, 327 frame_state.resource_cache, 328 device_pixel_scale, 329 &surface_culling_rect, 330 &mut frame_state.data_stores.clip, 331 frame_state.rg_builder, 332 true, 333 ); 334 335 prim_instance.vis.clip_chain = match clip_chain { 336 Some(clip_chain) => clip_chain, 337 None => { 338 continue; 339 } 340 }; 341 342 { 343 let prim_surface_index = frame_state.surface_stack.last().unwrap().1; 344 let prim_clip_chain = &prim_instance.vis.clip_chain; 345 346 // Accumulate the exact (clipped) local rect into the parent surface. 347 let surface = &mut frame_state.surfaces[prim_surface_index.0]; 348 surface.clipped_local_rect = surface.clipped_local_rect.union(&prim_clip_chain.pic_coverage_rect); 349 } 350 351 prim_instance.vis.state = match tile_cache { 352 Some(tile_cache) => { 353 tile_cache.update_prim_dependencies( 354 prim_instance, 355 cluster.spatial_node_index, 356 // It's OK to pass the local_coverage_rect here as it's only 357 // used by primitives (for compositor surfaces) that don't 358 // have inflation anyway. 359 local_coverage_rect, 360 frame_context, 361 frame_state.data_stores, 362 frame_state.clip_store, 363 &store.pictures, 364 frame_state.resource_cache, 365 &store.color_bindings, 366 &frame_state.surface_stack, 367 &mut frame_state.composite_state, 368 &mut frame_state.frame_gpu_data.f32, 369 &mut frame_state.scratch.primitive, 370 is_root_tile_cache, 371 frame_state.surfaces, 372 frame_state.profile, 373 ) 374 } 375 None => { 376 VisibilityState::Visible { 377 vis_flags: PrimitiveVisibilityFlags::empty(), 378 sub_slice_index: SubSliceIndex::DEFAULT, 379 } 380 } 381 }; 382 } 383 } 384 385 if let Some(snapshot) = &pic.snapshot { 386 if snapshot.detached { 387 // If the snapshot is detached, then the contents of the stacking 388 // context will only be shown via the snapshot, so there is no point 389 // to rendering anything outside of the snapshot area. 390 let prim_surface_index = frame_state.surface_stack.last().unwrap().1; 391 let surface = &mut frame_state.surfaces[prim_surface_index.0]; 392 let clip = snapshot.area.round_out().cast_unit(); 393 surface.clipped_local_rect = surface.clipped_local_rect.intersection_unchecked(&clip); 394 } 395 } 396 397 if pop_surface { 398 frame_state.pop_surface(); 399 } 400 401 if let Some(ref rc) = pic.raster_config { 402 if let Some(tile_cache) = tile_cache { 403 match rc.composite_mode { 404 PictureCompositeMode::TileCache { .. } => {} 405 _ => { 406 // Pop the off-screen surface from the picture cache stack 407 tile_cache.pop_surface(); 408 } 409 } 410 } 411 } 412 } 413 414 pub fn compute_conservative_visible_rect( 415 clip_chain: &ClipChainInstance, 416 culling_rect: VisRect, 417 visibility_node_index: SpatialNodeIndex, 418 prim_spatial_node_index: SpatialNodeIndex, 419 spatial_tree: &SpatialTree, 420 ) -> LayoutRect { 421 // Mapping from picture space -> world space 422 let map_pic_to_vis: SpaceMapper<PicturePixel, VisPixel> = SpaceMapper::new_with_target( 423 visibility_node_index, 424 clip_chain.pic_spatial_node_index, 425 culling_rect, 426 spatial_tree, 427 ); 428 429 // Mapping from local space -> picture space 430 let map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel> = SpaceMapper::new_with_target( 431 clip_chain.pic_spatial_node_index, 432 prim_spatial_node_index, 433 PictureRect::max_rect(), 434 spatial_tree, 435 ); 436 437 // Unmap the world culling rect from world -> picture space. If this mapping fails due 438 // to matrix weirdness, best we can do is use the clip chain's local clip rect. 439 let pic_culling_rect = match map_pic_to_vis.unmap(&culling_rect) { 440 Some(rect) => rect, 441 None => return clip_chain.local_clip_rect, 442 }; 443 444 // Intersect the unmapped world culling rect with the primitive's clip chain rect that 445 // is in picture space (the clip-chain already takes into account the bounds of the 446 // primitive local_rect and local_clip_rect). If there is no intersection here, the 447 // primitive is not visible at all. 448 let pic_culling_rect = match pic_culling_rect.intersection(&clip_chain.pic_coverage_rect) { 449 Some(rect) => rect, 450 None => return LayoutRect::zero(), 451 }; 452 453 // Unmap the picture culling rect from picture -> local space. If this mapping fails due 454 // to matrix weirdness, best we can do is use the clip chain's local clip rect. 455 match map_local_to_pic.unmap(&pic_culling_rect) { 456 Some(rect) => rect, 457 None => clip_chain.local_clip_rect, 458 } 459 }