render_backend.rs (84995B)
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 //! The high-level module responsible for managing the pipeline and preparing 6 //! commands to be issued by the `Renderer`. 7 //! 8 //! See the comment at the top of the `renderer` module for a description of 9 //! how these two pieces interact. 10 11 use api::{DebugFlags, Parameter, BoolParameter, PrimitiveFlags, MinimapData}; 12 use api::{DocumentId, ExternalScrollId, HitTestResult}; 13 use api::{IdNamespace, PipelineId, RenderNotifier, SampledScrollOffset}; 14 use api::{NotificationRequest, Checkpoint, QualitySettings}; 15 use api::{FramePublishId, PrimitiveKeyKind, RenderReasons}; 16 use api::units::*; 17 use api::channel::{single_msg_channel, Sender, Receiver}; 18 use crate::bump_allocator::ChunkPool; 19 use crate::AsyncPropertySampler; 20 use crate::box_shadow::BoxShadow; 21 #[cfg(any(feature = "capture", feature = "replay"))] 22 use crate::render_api::CaptureBits; 23 #[cfg(feature = "replay")] 24 use crate::render_api::CapturedDocument; 25 use crate::render_api::{MemoryReport, TransactionMsg, ResourceUpdate, ApiMsg, FrameMsg, ClearCache, DebugCommand}; 26 use crate::clip::{ClipIntern, PolygonIntern, ClipStoreScratchBuffer}; 27 use crate::filterdata::FilterDataIntern; 28 #[cfg(any(feature = "capture", feature = "replay"))] 29 use crate::capture::CaptureConfig; 30 use crate::composite::{CompositorKind, CompositeDescriptor}; 31 use crate::frame_builder::{FrameBuilder, FrameBuilderConfig, FrameScratchBuffer}; 32 use glyph_rasterizer::FontInstance; 33 use crate::hit_test::{HitTest, HitTester, SharedHitTester}; 34 use crate::intern::DataStore; 35 #[cfg(any(feature = "capture", feature = "replay"))] 36 use crate::internal_types::DebugOutput; 37 use crate::internal_types::{FastHashMap, FrameId, FrameStamp, RenderedDocument, ResultMsg}; 38 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; 39 use crate::picture::{PictureScratchBuffer, SurfaceInfo, RasterConfig}; 40 use crate::tile_cache::{SliceId, TileCacheInstance, TileCacheParams}; 41 use crate::picture::PicturePrimitive; 42 use crate::prim_store::{PrimitiveScratchBuffer, PrimitiveInstance}; 43 use crate::prim_store::{PrimitiveInstanceKind, PrimTemplateCommonData}; 44 use crate::prim_store::interned::*; 45 use crate::profiler::{self, TransactionProfile}; 46 use crate::render_task_graph::RenderTaskGraphBuilder; 47 use crate::renderer::{FullFrameStats, PipelineInfo}; 48 use crate::resource_cache::ResourceCache; 49 #[cfg(feature = "replay")] 50 use crate::resource_cache::PlainCacheOwn; 51 #[cfg(feature = "replay")] 52 use crate::resource_cache::PlainResources; 53 #[cfg(feature = "replay")] 54 use crate::scene::Scene; 55 use crate::scene::{BuiltScene, SceneProperties}; 56 use crate::scene_builder_thread::*; 57 use crate::spatial_tree::SpatialTree; 58 #[cfg(feature = "replay")] 59 use crate::spatial_tree::SceneSpatialTree; 60 use crate::telemetry::Telemetry; 61 #[cfg(feature = "capture")] 62 use serde::Serialize; 63 #[cfg(feature = "replay")] 64 use serde::Deserialize; 65 #[cfg(feature = "replay")] 66 use std::collections::hash_map::Entry::{Occupied, Vacant}; 67 use std::sync::Arc; 68 use std::sync::atomic::{AtomicUsize, Ordering}; 69 use std::{mem, u32}; 70 #[cfg(feature = "capture")] 71 use std::path::PathBuf; 72 #[cfg(feature = "replay")] 73 use crate::frame_builder::Frame; 74 use core::time::Duration; 75 use crate::util::{Recycler, VecHelper, drain_filter}; 76 #[cfg(feature = "debugger")] 77 use crate::debugger::DebugQueryKind; 78 79 #[cfg_attr(feature = "capture", derive(Serialize))] 80 #[cfg_attr(feature = "replay", derive(Deserialize))] 81 #[derive(Copy, Clone)] 82 pub struct DocumentView { 83 scene: SceneView, 84 } 85 86 /// Some rendering parameters applying at the scene level. 87 #[cfg_attr(feature = "capture", derive(Serialize))] 88 #[cfg_attr(feature = "replay", derive(Deserialize))] 89 #[derive(Copy, Clone)] 90 pub struct SceneView { 91 pub device_rect: DeviceIntRect, 92 pub quality_settings: QualitySettings, 93 } 94 95 enum RenderBackendStatus { 96 Continue, 97 StopRenderBackend, 98 ShutDown(Option<Sender<()>>), 99 } 100 101 macro_rules! declare_data_stores { 102 ( $( $name:ident : $ty:ty, )+ ) => { 103 /// A collection of resources that are shared by clips, primitives 104 /// between display lists. 105 #[cfg_attr(feature = "capture", derive(Serialize))] 106 #[cfg_attr(feature = "replay", derive(Deserialize))] 107 #[derive(Default)] 108 pub struct DataStores { 109 $( 110 pub $name: DataStore<$ty>, 111 )+ 112 } 113 114 impl DataStores { 115 /// Reports CPU heap usage. 116 fn report_memory(&self, ops: &mut MallocSizeOfOps, r: &mut MemoryReport) { 117 $( 118 r.interning.data_stores.$name += self.$name.size_of(ops); 119 )+ 120 } 121 122 fn apply_updates( 123 &mut self, 124 updates: InternerUpdates, 125 profile: &mut TransactionProfile, 126 ) { 127 $( 128 self.$name.apply_updates( 129 updates.$name, 130 profile, 131 ); 132 )+ 133 } 134 } 135 } 136 } 137 138 crate::enumerate_interners!(declare_data_stores); 139 140 impl DataStores { 141 /// Returns the local rect for a primitive. For most primitives, this is 142 /// stored in the template. For pictures, this is stored inside the picture 143 /// primitive instance itself, since this is determined during frame building. 144 pub fn get_local_prim_rect( 145 &self, 146 prim_instance: &PrimitiveInstance, 147 pictures: &[PicturePrimitive], 148 surfaces: &[SurfaceInfo], 149 ) -> LayoutRect { 150 match prim_instance.kind { 151 PrimitiveInstanceKind::Picture { pic_index, .. } => { 152 let pic = &pictures[pic_index.0]; 153 154 match pic.raster_config { 155 Some(RasterConfig { surface_index, ref composite_mode, .. }) => { 156 let surface = &surfaces[surface_index.0]; 157 158 composite_mode.get_rect(surface, None) 159 } 160 None => { 161 panic!("bug: get_local_prim_rect should not be called for pass-through pictures"); 162 } 163 } 164 } 165 _ => { 166 self.as_common_data(prim_instance).prim_rect 167 } 168 } 169 } 170 171 /// Returns the local coverage (space occupied) for a primitive. For most primitives, 172 /// this is stored in the template. For pictures, this is stored inside the picture 173 /// primitive instance itself, since this is determined during frame building. 174 pub fn get_local_prim_coverage_rect( 175 &self, 176 prim_instance: &PrimitiveInstance, 177 pictures: &[PicturePrimitive], 178 surfaces: &[SurfaceInfo], 179 ) -> LayoutRect { 180 match prim_instance.kind { 181 PrimitiveInstanceKind::Picture { pic_index, .. } => { 182 let pic = &pictures[pic_index.0]; 183 184 match pic.raster_config { 185 Some(RasterConfig { surface_index, ref composite_mode, .. }) => { 186 let surface = &surfaces[surface_index.0]; 187 188 composite_mode.get_coverage(surface, None) 189 } 190 None => { 191 panic!("bug: get_local_prim_coverage_rect should not be called for pass-through pictures"); 192 } 193 } 194 } 195 _ => { 196 self.as_common_data(prim_instance).prim_rect 197 } 198 } 199 } 200 201 /// Returns true if this primitive might need repition. 202 // TODO(gw): This seems like the wrong place for this - maybe this flag should 203 // not be in the common prim template data? 204 pub fn prim_may_need_repetition( 205 &self, 206 prim_instance: &PrimitiveInstance, 207 ) -> bool { 208 match prim_instance.kind { 209 PrimitiveInstanceKind::Picture { .. } => { 210 false 211 } 212 _ => { 213 self.as_common_data(prim_instance).may_need_repetition 214 } 215 } 216 } 217 218 /// Returns true if this primitive has anti-aliasing enabled. 219 pub fn prim_has_anti_aliasing( 220 &self, 221 prim_instance: &PrimitiveInstance, 222 ) -> bool { 223 match prim_instance.kind { 224 PrimitiveInstanceKind::Picture { .. } => { 225 false 226 } 227 _ => { 228 self.as_common_data(prim_instance).flags.contains(PrimitiveFlags::ANTIALISED) 229 } 230 } 231 } 232 233 pub fn as_common_data( 234 &self, 235 prim_inst: &PrimitiveInstance 236 ) -> &PrimTemplateCommonData { 237 match prim_inst.kind { 238 PrimitiveInstanceKind::Rectangle { data_handle, .. } => { 239 let prim_data = &self.prim[data_handle]; 240 &prim_data.common 241 } 242 PrimitiveInstanceKind::Image { data_handle, .. } => { 243 let prim_data = &self.image[data_handle]; 244 &prim_data.common 245 } 246 PrimitiveInstanceKind::ImageBorder { data_handle, .. } => { 247 let prim_data = &self.image_border[data_handle]; 248 &prim_data.common 249 } 250 PrimitiveInstanceKind::LineDecoration { data_handle, .. } => { 251 let prim_data = &self.line_decoration[data_handle]; 252 &prim_data.common 253 } 254 PrimitiveInstanceKind::LinearGradient { data_handle, .. } 255 | PrimitiveInstanceKind::CachedLinearGradient { data_handle, .. } => { 256 let prim_data = &self.linear_grad[data_handle]; 257 &prim_data.common 258 } 259 PrimitiveInstanceKind::NormalBorder { data_handle, .. } => { 260 let prim_data = &self.normal_border[data_handle]; 261 &prim_data.common 262 } 263 PrimitiveInstanceKind::Picture { .. } => { 264 panic!("BUG: picture prims don't have common data!"); 265 } 266 PrimitiveInstanceKind::RadialGradient { data_handle, .. } => { 267 let prim_data = &self.radial_grad[data_handle]; 268 &prim_data.common 269 } 270 PrimitiveInstanceKind::ConicGradient { data_handle, .. } => { 271 let prim_data = &self.conic_grad[data_handle]; 272 &prim_data.common 273 } 274 PrimitiveInstanceKind::TextRun { data_handle, .. } => { 275 let prim_data = &self.text_run[data_handle]; 276 &prim_data.common 277 } 278 PrimitiveInstanceKind::YuvImage { data_handle, .. } => { 279 let prim_data = &self.yuv_image[data_handle]; 280 &prim_data.common 281 } 282 PrimitiveInstanceKind::BackdropCapture { data_handle, .. } => { 283 let prim_data = &self.backdrop_capture[data_handle]; 284 &prim_data.common 285 } 286 PrimitiveInstanceKind::BackdropRender { data_handle, .. } => { 287 let prim_data = &self.backdrop_render[data_handle]; 288 &prim_data.common 289 } 290 PrimitiveInstanceKind::BoxShadow { data_handle, .. } => { 291 let prim_data = &self.box_shadow[data_handle]; 292 &prim_data.common 293 } 294 } 295 } 296 } 297 298 #[derive(Default)] 299 pub struct ScratchBuffer { 300 pub primitive: PrimitiveScratchBuffer, 301 pub picture: PictureScratchBuffer, 302 pub frame: FrameScratchBuffer, 303 pub clip_store: ClipStoreScratchBuffer, 304 } 305 306 impl ScratchBuffer { 307 pub fn begin_frame(&mut self) { 308 self.primitive.begin_frame(); 309 self.picture.begin_frame(); 310 self.frame.begin_frame(); 311 } 312 313 pub fn end_frame(&mut self) { 314 self.primitive.end_frame(); 315 } 316 317 pub fn recycle(&mut self, recycler: &mut Recycler) { 318 self.primitive.recycle(recycler); 319 self.picture.recycle(recycler); 320 } 321 322 pub fn memory_pressure(&mut self) { 323 // TODO: causes browser chrome test crashes on windows. 324 //self.primitive = Default::default(); 325 self.picture = Default::default(); 326 self.frame = Default::default(); 327 self.clip_store = Default::default(); 328 } 329 } 330 331 struct Document { 332 /// The id of this document 333 id: DocumentId, 334 335 /// Temporary list of removed pipelines received from the scene builder 336 /// thread and forwarded to the renderer. 337 removed_pipelines: Vec<(PipelineId, DocumentId)>, 338 339 view: DocumentView, 340 341 /// The id and time of the current frame. 342 stamp: FrameStamp, 343 344 /// The latest built scene, usable to build frames. 345 /// received from the scene builder thread. 346 scene: BuiltScene, 347 348 /// The builder object that prodces frames, kept around to preserve some retained state. 349 frame_builder: FrameBuilder, 350 351 /// Allows graphs of render tasks to be created, and then built into an immutable graph output. 352 rg_builder: RenderTaskGraphBuilder, 353 354 /// A data structure to allow hit testing against rendered frames. This is updated 355 /// every time we produce a fully rendered frame. 356 hit_tester: Option<Arc<HitTester>>, 357 /// To avoid synchronous messaging we update a shared hit-tester that other threads 358 /// can query. 359 shared_hit_tester: Arc<SharedHitTester>, 360 361 /// Properties that are resolved during frame building and can be changed at any time 362 /// without requiring the scene to be re-built. 363 dynamic_properties: SceneProperties, 364 365 /// Track whether the last built frame is up to date or if it will need to be re-built 366 /// before rendering again. 367 frame_is_valid: bool, 368 hit_tester_is_valid: bool, 369 rendered_frame_is_valid: bool, 370 /// We track this information to be able to display debugging information from the 371 /// renderer. 372 has_built_scene: bool, 373 374 data_stores: DataStores, 375 376 /// Retained frame-building version of the spatial tree 377 spatial_tree: SpatialTree, 378 379 minimap_data: FastHashMap<ExternalScrollId, MinimapData>, 380 381 /// Contains various vecs of data that is used only during frame building, 382 /// where we want to recycle the memory each new display list, to avoid constantly 383 /// re-allocating and moving memory around. 384 scratch: ScratchBuffer, 385 386 #[cfg(feature = "replay")] 387 loaded_scene: Scene, 388 389 /// Tracks the state of the picture cache tiles that were composited on the previous frame. 390 prev_composite_descriptor: CompositeDescriptor, 391 392 /// Tracks if we need to invalidate dirty rects for this document, due to the picture 393 /// cache slice configuration having changed when a new scene is swapped in. 394 dirty_rects_are_valid: bool, 395 396 profile: TransactionProfile, 397 frame_stats: Option<FullFrameStats>, 398 } 399 400 impl Document { 401 pub fn new( 402 id: DocumentId, 403 size: DeviceIntSize, 404 ) -> Self { 405 Document { 406 id, 407 removed_pipelines: Vec::new(), 408 view: DocumentView { 409 scene: SceneView { 410 device_rect: size.into(), 411 quality_settings: QualitySettings::default(), 412 }, 413 }, 414 stamp: FrameStamp::first(id), 415 scene: BuiltScene::empty(), 416 frame_builder: FrameBuilder::new(), 417 hit_tester: None, 418 shared_hit_tester: Arc::new(SharedHitTester::new()), 419 dynamic_properties: SceneProperties::new(), 420 frame_is_valid: false, 421 hit_tester_is_valid: false, 422 rendered_frame_is_valid: false, 423 has_built_scene: false, 424 data_stores: DataStores::default(), 425 spatial_tree: SpatialTree::new(), 426 minimap_data: FastHashMap::default(), 427 scratch: ScratchBuffer::default(), 428 #[cfg(feature = "replay")] 429 loaded_scene: Scene::new(), 430 prev_composite_descriptor: CompositeDescriptor::empty(), 431 dirty_rects_are_valid: true, 432 profile: TransactionProfile::new(), 433 rg_builder: RenderTaskGraphBuilder::new(), 434 frame_stats: None, 435 } 436 } 437 438 fn can_render(&self) -> bool { 439 self.scene.has_root_pipeline 440 } 441 442 fn has_pixels(&self) -> bool { 443 !self.view.scene.device_rect.is_empty() 444 } 445 446 fn process_frame_msg( 447 &mut self, 448 message: FrameMsg, 449 ) -> DocumentOps { 450 match message { 451 FrameMsg::UpdateEpoch(pipeline_id, epoch) => { 452 self.scene.pipeline_epochs.insert(pipeline_id, epoch); 453 } 454 FrameMsg::HitTest(point, tx) => { 455 if !self.hit_tester_is_valid { 456 self.rebuild_hit_tester(); 457 } 458 459 let result = match self.hit_tester { 460 Some(ref hit_tester) => { 461 hit_tester.hit_test(HitTest::new(point)) 462 } 463 None => HitTestResult { items: Vec::new() }, 464 }; 465 466 tx.send(result).unwrap(); 467 } 468 FrameMsg::RequestHitTester(tx) => { 469 tx.send(self.shared_hit_tester.clone()).unwrap(); 470 } 471 FrameMsg::SetScrollOffsets(id, offset) => { 472 profile_scope!("SetScrollOffset"); 473 474 if self.set_scroll_offsets(id, offset) { 475 self.hit_tester_is_valid = false; 476 self.frame_is_valid = false; 477 } 478 479 return DocumentOps { 480 scroll: true, 481 ..DocumentOps::nop() 482 }; 483 } 484 FrameMsg::ResetDynamicProperties => { 485 self.dynamic_properties.reset_properties(); 486 } 487 FrameMsg::AppendDynamicProperties(property_bindings) => { 488 self.dynamic_properties.add_properties(property_bindings); 489 } 490 FrameMsg::AppendDynamicTransformProperties(property_bindings) => { 491 self.dynamic_properties.add_transforms(property_bindings); 492 } 493 FrameMsg::SetIsTransformAsyncZooming(is_zooming, animation_id) => { 494 if let Some(node_index) = self.spatial_tree.find_spatial_node_by_anim_id(animation_id) { 495 let node = self.spatial_tree.get_spatial_node_mut(node_index); 496 497 if node.is_async_zooming != is_zooming { 498 node.is_async_zooming = is_zooming; 499 self.frame_is_valid = false; 500 } 501 } 502 } 503 FrameMsg::SetMinimapData(id, minimap_data) => { 504 self.minimap_data.insert(id, minimap_data); 505 } 506 } 507 508 DocumentOps::nop() 509 } 510 511 fn build_frame( 512 &mut self, 513 resource_cache: &mut ResourceCache, 514 debug_flags: DebugFlags, 515 tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>, 516 frame_stats: Option<FullFrameStats>, 517 present: bool, 518 render_reasons: RenderReasons, 519 chunk_pool: Arc<ChunkPool>, 520 ) -> RenderedDocument { 521 let frame_build_start_time = zeitstempel::now(); 522 523 // Advance to the next frame. 524 self.stamp.advance(); 525 526 assert!(self.stamp.frame_id() != FrameId::INVALID, 527 "First frame increment must happen before build_frame()"); 528 529 let frame = { 530 let frame = self.frame_builder.build( 531 &mut self.scene, 532 present, 533 resource_cache, 534 &mut self.rg_builder, 535 self.stamp, 536 self.view.scene.device_rect.min, 537 &self.dynamic_properties, 538 &mut self.data_stores, 539 &mut self.scratch, 540 debug_flags, 541 tile_caches, 542 &mut self.spatial_tree, 543 self.dirty_rects_are_valid, 544 &mut self.profile, 545 // Consume the minimap data. If APZ wants a minimap rendered 546 // on the next frame, it will add new entries to the minimap 547 // data during sampling. 548 mem::take(&mut self.minimap_data), 549 chunk_pool, 550 ); 551 552 frame 553 }; 554 555 self.frame_is_valid = true; 556 self.dirty_rects_are_valid = true; 557 558 self.has_built_scene = false; 559 560 let frame_build_time_ms = 561 profiler::ns_to_ms(zeitstempel::now() - frame_build_start_time); 562 self.profile.set(profiler::FRAME_BUILDING_TIME, frame_build_time_ms); 563 self.profile.start_time(profiler::FRAME_SEND_TIME); 564 565 let frame_stats = frame_stats.map(|mut stats| { 566 stats.frame_build_time += frame_build_time_ms; 567 stats 568 }); 569 570 RenderedDocument { 571 frame, 572 profile: self.profile.take_and_reset(), 573 frame_stats: frame_stats, 574 render_reasons, 575 } 576 } 577 578 /// Build a frame without changing the state of the current scene. 579 /// 580 /// This is useful to render arbitrary content into to images in 581 /// the resource cache for later use without affecting what is 582 /// currently being displayed. 583 fn process_offscreen_scene( 584 &mut self, 585 mut txn: OffscreenBuiltScene, 586 resource_cache: &mut ResourceCache, 587 chunk_pool: Arc<ChunkPool>, 588 debug_flags: DebugFlags, 589 ) -> RenderedDocument { 590 let mut profile = TransactionProfile::new(); 591 self.stamp.advance(); 592 593 let mut data_stores = DataStores::default(); 594 data_stores.apply_updates(txn.interner_updates, &mut profile); 595 596 let mut spatial_tree = SpatialTree::new(); 597 spatial_tree.apply_updates(txn.spatial_tree_updates); 598 599 let mut tile_caches = FastHashMap::default(); 600 self.update_tile_caches_for_new_scene( 601 mem::take(&mut txn.scene.tile_cache_config.tile_caches), 602 &mut tile_caches, 603 resource_cache, 604 ); 605 606 let present = false; 607 608 let frame = self.frame_builder.build( 609 &mut txn.scene, 610 present, 611 resource_cache, 612 &mut self.rg_builder, 613 self.stamp, // TODO(nical) 614 self.view.scene.device_rect.min, 615 &self.dynamic_properties, 616 &mut data_stores, 617 &mut self.scratch, 618 debug_flags, 619 &mut tile_caches, 620 &mut spatial_tree, 621 self.dirty_rects_are_valid, 622 &mut profile, 623 // Consume the minimap data. If APZ wants a minimap rendered 624 // on the next frame, it will add new entries to the minimap 625 // data during sampling. 626 mem::take(&mut self.minimap_data), 627 chunk_pool, 628 ); 629 630 RenderedDocument { 631 frame, 632 profile, 633 render_reasons: RenderReasons::SNAPSHOT, 634 frame_stats: None, 635 } 636 } 637 638 639 fn rebuild_hit_tester(&mut self) { 640 self.spatial_tree.update_tree(&self.dynamic_properties); 641 642 let hit_tester = Arc::new(self.scene.create_hit_tester(&self.spatial_tree)); 643 self.hit_tester = Some(Arc::clone(&hit_tester)); 644 self.shared_hit_tester.update(hit_tester); 645 self.hit_tester_is_valid = true; 646 } 647 648 pub fn updated_pipeline_info(&mut self) -> PipelineInfo { 649 let removed_pipelines = self.removed_pipelines.take_and_preallocate(); 650 PipelineInfo { 651 epochs: self.scene.pipeline_epochs.iter() 652 .map(|(&pipeline_id, &epoch)| ((pipeline_id, self.id), epoch)).collect(), 653 removed_pipelines, 654 } 655 } 656 657 /// Returns true if the node actually changed position or false otherwise. 658 pub fn set_scroll_offsets( 659 &mut self, 660 id: ExternalScrollId, 661 offsets: Vec<SampledScrollOffset>, 662 ) -> bool { 663 self.spatial_tree.set_scroll_offsets(id, offsets) 664 } 665 666 /// Update the state of tile caches when a new scene is being swapped in to 667 /// the render backend. Retain / reuse existing caches if possible, and 668 /// destroy any now unused caches. 669 fn update_tile_caches_for_new_scene( 670 &mut self, 671 mut requested_tile_caches: FastHashMap<SliceId, TileCacheParams>, 672 tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>, 673 resource_cache: &mut ResourceCache, 674 ) { 675 let mut new_tile_caches = FastHashMap::default(); 676 new_tile_caches.reserve(requested_tile_caches.len()); 677 678 // Step through the tile caches that are needed for the new scene, and see 679 // if we have an existing cache that can be reused. 680 for (slice_id, params) in requested_tile_caches.drain() { 681 let tile_cache = match tile_caches.remove(&slice_id) { 682 Some(mut existing_tile_cache) => { 683 // Found an existing cache - update the cache params and reuse it 684 existing_tile_cache.prepare_for_new_scene( 685 params, 686 resource_cache, 687 ); 688 existing_tile_cache 689 } 690 None => { 691 // No cache exists so create a new one 692 Box::new(TileCacheInstance::new(params)) 693 } 694 }; 695 696 new_tile_caches.insert(slice_id, tile_cache); 697 } 698 699 // Replace current tile cache map, and return what was left over, 700 // which are now unused. 701 let unused_tile_caches = mem::replace( 702 tile_caches, 703 new_tile_caches, 704 ); 705 706 if !unused_tile_caches.is_empty() { 707 // If the slice configuration changed, assume we can't rely on the 708 // current dirty rects for next composite 709 self.dirty_rects_are_valid = false; 710 711 // Destroy any native surfaces allocated by these unused caches 712 for (_, tile_cache) in unused_tile_caches { 713 tile_cache.destroy(resource_cache); 714 } 715 } 716 } 717 718 pub fn new_async_scene_ready( 719 &mut self, 720 mut built_scene: BuiltScene, 721 recycler: &mut Recycler, 722 tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>, 723 resource_cache: &mut ResourceCache, 724 ) { 725 self.frame_is_valid = false; 726 self.hit_tester_is_valid = false; 727 728 self.update_tile_caches_for_new_scene( 729 mem::replace(&mut built_scene.tile_cache_config.tile_caches, FastHashMap::default()), 730 tile_caches, 731 resource_cache, 732 ); 733 734 735 let old_scene = std::mem::replace(&mut self.scene, built_scene); 736 old_scene.recycle(); 737 738 self.scratch.recycle(recycler); 739 } 740 } 741 742 struct DocumentOps { 743 scroll: bool, 744 } 745 746 impl DocumentOps { 747 fn nop() -> Self { 748 DocumentOps { 749 scroll: false, 750 } 751 } 752 } 753 754 /// The unique id for WR resource identification. 755 /// The namespace_id should start from 1. 756 static NEXT_NAMESPACE_ID: AtomicUsize = AtomicUsize::new(1); 757 758 #[cfg(any(feature = "capture", feature = "replay"))] 759 #[cfg_attr(feature = "capture", derive(Serialize))] 760 #[cfg_attr(feature = "replay", derive(Deserialize))] 761 struct PlainRenderBackend { 762 frame_config: FrameBuilderConfig, 763 documents: FastHashMap<DocumentId, DocumentView>, 764 resource_sequence_id: u32, 765 } 766 767 /// The render backend is responsible for transforming high level display lists into 768 /// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame. 769 /// 770 /// The render backend operates on its own thread. 771 pub struct RenderBackend { 772 api_rx: Receiver<ApiMsg>, 773 result_tx: Sender<ResultMsg>, 774 scene_tx: Sender<SceneBuilderRequest>, 775 776 resource_cache: ResourceCache, 777 chunk_pool: Arc<ChunkPool>, 778 779 frame_config: FrameBuilderConfig, 780 default_compositor_kind: CompositorKind, 781 documents: FastHashMap<DocumentId, Document>, 782 783 notifier: Box<dyn RenderNotifier>, 784 sampler: Option<Box<dyn AsyncPropertySampler + Send>>, 785 size_of_ops: Option<MallocSizeOfOps>, 786 debug_flags: DebugFlags, 787 namespace_alloc_by_client: bool, 788 789 recycler: Recycler, 790 791 #[cfg(feature = "capture")] 792 /// If `Some`, do 'sequence capture' logging, recording updated documents, 793 /// frames, etc. This is set only through messages from the scene builder, 794 /// so all control of sequence capture goes through there. 795 capture_config: Option<CaptureConfig>, 796 797 #[cfg(feature = "replay")] 798 loaded_resource_sequence_id: u32, 799 800 /// A map of tile caches. These are stored in the backend as they are 801 /// persisted between both frame and scenes. 802 tile_caches: FastHashMap<SliceId, Box<TileCacheInstance>>, 803 804 /// The id of the latest PublishDocument 805 frame_publish_id: FramePublishId, 806 } 807 808 impl RenderBackend { 809 pub fn new( 810 api_rx: Receiver<ApiMsg>, 811 result_tx: Sender<ResultMsg>, 812 scene_tx: Sender<SceneBuilderRequest>, 813 resource_cache: ResourceCache, 814 chunk_pool: Arc<ChunkPool>, 815 notifier: Box<dyn RenderNotifier>, 816 frame_config: FrameBuilderConfig, 817 sampler: Option<Box<dyn AsyncPropertySampler + Send>>, 818 size_of_ops: Option<MallocSizeOfOps>, 819 debug_flags: DebugFlags, 820 namespace_alloc_by_client: bool, 821 ) -> RenderBackend { 822 RenderBackend { 823 api_rx, 824 result_tx, 825 scene_tx, 826 resource_cache, 827 chunk_pool, 828 frame_config, 829 default_compositor_kind : frame_config.compositor_kind, 830 documents: FastHashMap::default(), 831 notifier, 832 sampler, 833 size_of_ops, 834 debug_flags, 835 namespace_alloc_by_client, 836 recycler: Recycler::new(), 837 #[cfg(feature = "capture")] 838 capture_config: None, 839 #[cfg(feature = "replay")] 840 loaded_resource_sequence_id: 0, 841 tile_caches: FastHashMap::default(), 842 frame_publish_id: FramePublishId::first(), 843 } 844 } 845 846 pub fn next_namespace_id() -> IdNamespace { 847 IdNamespace(NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed) as u32) 848 } 849 850 pub fn run(&mut self) { 851 let mut frame_counter: u32 = 0; 852 let mut status = RenderBackendStatus::Continue; 853 854 if let Some(ref sampler) = self.sampler { 855 sampler.register(); 856 } 857 858 while let RenderBackendStatus::Continue = status { 859 status = match self.api_rx.recv() { 860 Ok(msg) => { 861 self.process_api_msg(msg, &mut frame_counter) 862 } 863 Err(..) => { RenderBackendStatus::ShutDown(None) } 864 }; 865 } 866 867 if let RenderBackendStatus::StopRenderBackend = status { 868 while let Ok(msg) = self.api_rx.recv() { 869 match msg { 870 ApiMsg::SceneBuilderResult(SceneBuilderResult::ExternalEvent(evt)) => { 871 self.notifier.external_event(evt); 872 } 873 ApiMsg::SceneBuilderResult(SceneBuilderResult::FlushComplete(tx)) => { 874 // If somebody's blocked waiting for a flush, how did they 875 // trigger the RB thread to shut down? This shouldn't happen 876 // but handle it gracefully anyway. 877 debug_assert!(false); 878 tx.send(()).ok(); 879 } 880 ApiMsg::SceneBuilderResult(SceneBuilderResult::ShutDown(sender)) => { 881 info!("Recycling stats: {:?}", self.recycler); 882 status = RenderBackendStatus::ShutDown(sender); 883 break; 884 } 885 _ => {}, 886 } 887 } 888 } 889 890 // Ensure we read everything the scene builder is sending us from 891 // inflight messages, otherwise the scene builder might panic. 892 while let Ok(msg) = self.api_rx.try_recv() { 893 match msg { 894 ApiMsg::SceneBuilderResult(SceneBuilderResult::FlushComplete(tx)) => { 895 // If somebody's blocked waiting for a flush, how did they 896 // trigger the RB thread to shut down? This shouldn't happen 897 // but handle it gracefully anyway. 898 debug_assert!(false); 899 tx.send(()).ok(); 900 } 901 _ => {}, 902 } 903 } 904 905 self.documents.clear(); 906 907 self.notifier.shut_down(); 908 909 if let Some(ref sampler) = self.sampler { 910 sampler.deregister(); 911 } 912 913 914 if let RenderBackendStatus::ShutDown(Some(sender)) = status { 915 let _ = sender.send(()); 916 } 917 } 918 919 fn process_transaction( 920 &mut self, 921 mut txns: Vec<Box<BuiltTransaction>>, 922 result_tx: Option<Sender<SceneSwapResult>>, 923 frame_counter: &mut u32, 924 ) -> bool { 925 self.maybe_force_nop_documents( 926 frame_counter, 927 |document_id| txns.iter().any(|txn| txn.document_id == document_id)); 928 929 let mut built_frame = false; 930 for mut txn in txns.drain(..) { 931 let has_built_scene = txn.built_scene.is_some(); 932 933 if let Some(doc) = self.documents.get_mut(&txn.document_id) { 934 doc.removed_pipelines.append(&mut txn.removed_pipelines); 935 doc.view.scene = txn.view; 936 doc.profile.merge(&mut txn.profile); 937 938 doc.frame_stats = if let Some(stats) = &doc.frame_stats { 939 Some(stats.merge(&txn.frame_stats)) 940 } else { 941 Some(txn.frame_stats) 942 }; 943 944 // Before updating the spatial tree, save the most recently sampled 945 // scroll offsets (which include async deltas). 946 let last_sampled_scroll_offsets = if self.sampler.is_some() { 947 Some(doc.spatial_tree.get_last_sampled_scroll_offsets()) 948 } else { 949 None 950 }; 951 952 if let Some(updates) = txn.spatial_tree_updates.take() { 953 doc.spatial_tree.apply_updates(updates); 954 } 955 956 if let Some(built_scene) = txn.built_scene.take() { 957 doc.new_async_scene_ready( 958 built_scene, 959 &mut self.recycler, 960 &mut self.tile_caches, 961 &mut self.resource_cache, 962 ); 963 } 964 965 // If there are any additions or removals of clip modes 966 // during the scene build, apply them to the data store now. 967 // This needs to happen before we build the hit tester. 968 if let Some(updates) = txn.interner_updates.take() { 969 doc.data_stores.apply_updates(updates, &mut doc.profile); 970 } 971 972 // Apply the last sampled scroll offsets from the previous scene, 973 // to the current scene. The offsets are identified by scroll ids 974 // which are stable across scenes. This ensures that a hit test, 975 // which could occur in between post-swap hook and the call to 976 // update_document() below, does not observe raw main-thread offsets 977 // from the new scene that don't have async deltas applied to them. 978 if let Some(last_sampled) = last_sampled_scroll_offsets { 979 doc.spatial_tree 980 .apply_last_sampled_scroll_offsets(last_sampled); 981 } 982 983 // Build the hit tester while the APZ lock is held so that its content 984 // is in sync with the gecko APZ tree. 985 if !doc.hit_tester_is_valid { 986 doc.rebuild_hit_tester(); 987 } 988 989 if let Some(ref tx) = result_tx { 990 let (resume_tx, resume_rx) = single_msg_channel(); 991 tx.send(SceneSwapResult::Complete(resume_tx)).unwrap(); 992 // Block until the post-swap hook has completed on 993 // the scene builder thread. We need to do this before 994 // we can sample from the sampler hook which might happen 995 // in the update_document call below. 996 resume_rx.recv().ok(); 997 } 998 999 self.resource_cache.add_rasterized_blob_images( 1000 txn.rasterized_blobs.take(), 1001 &mut doc.profile, 1002 ); 1003 1004 for offscreen_scene in txn.offscreen_scenes.drain(..) { 1005 self.resource_cache.post_scene_building_update( 1006 txn.resource_updates.take(), 1007 &mut doc.profile, 1008 ); 1009 1010 let rendered_document = doc.process_offscreen_scene( 1011 offscreen_scene, 1012 &mut self.resource_cache, 1013 self.chunk_pool.clone(), 1014 self.debug_flags, 1015 ); 1016 1017 let pending_update = self.resource_cache.pending_updates(); 1018 1019 let msg = ResultMsg::PublishDocument( 1020 self.frame_publish_id, 1021 txn.document_id, 1022 rendered_document, 1023 pending_update, 1024 ); 1025 self.result_tx.send(msg).unwrap(); 1026 1027 let params = api::FrameReadyParams { 1028 present: false, 1029 render: true, 1030 scrolled: false, 1031 tracked: false, 1032 }; 1033 1034 self.notifier.new_frame_ready( 1035 txn.document_id, 1036 self.frame_publish_id, 1037 ¶ms 1038 ); 1039 } 1040 } else { 1041 // The document was removed while we were building it, skip it. 1042 // TODO: we might want to just ensure that removed documents are 1043 // always forwarded to the scene builder thread to avoid this case. 1044 if let Some(ref tx) = result_tx { 1045 tx.send(SceneSwapResult::Aborted).unwrap(); 1046 } 1047 continue; 1048 } 1049 1050 built_frame |= self.update_document( 1051 txn.document_id, 1052 txn.resource_updates.take(), 1053 txn.frame_ops.take(), 1054 txn.notifications.take(), 1055 txn.render_frame, 1056 txn.present, 1057 txn.tracked, 1058 RenderReasons::SCENE, 1059 None, 1060 txn.invalidate_rendered_frame, 1061 frame_counter, 1062 has_built_scene, 1063 None, 1064 ); 1065 } 1066 1067 built_frame 1068 } 1069 1070 fn process_api_msg( 1071 &mut self, 1072 msg: ApiMsg, 1073 frame_counter: &mut u32, 1074 ) -> RenderBackendStatus { 1075 match msg { 1076 ApiMsg::CloneApi(sender) => { 1077 assert!(!self.namespace_alloc_by_client); 1078 sender.send(Self::next_namespace_id()).unwrap(); 1079 } 1080 ApiMsg::CloneApiByClient(namespace_id) => { 1081 assert!(self.namespace_alloc_by_client); 1082 debug_assert!(!self.documents.iter().any(|(did, _doc)| did.namespace_id == namespace_id)); 1083 } 1084 ApiMsg::AddDocument(document_id, initial_size) => { 1085 let document = Document::new( 1086 document_id, 1087 initial_size, 1088 ); 1089 let old = self.documents.insert(document_id, document); 1090 debug_assert!(old.is_none()); 1091 } 1092 ApiMsg::MemoryPressure => { 1093 // This is drastic. It will basically flush everything out of the cache, 1094 // and the next frame will have to rebuild all of its resources. 1095 // We may want to look into something less extreme, but on the other hand this 1096 // should only be used in situations where are running low enough on memory 1097 // that we risk crashing if we don't do something about it. 1098 // The advantage of clearing the cache completely is that it gets rid of any 1099 // remaining fragmentation that could have persisted if we kept around the most 1100 // recently used resources. 1101 self.resource_cache.clear(ClearCache::all()); 1102 1103 for (_, doc) in &mut self.documents { 1104 doc.scratch.memory_pressure(); 1105 for tile_cache in self.tile_caches.values_mut() { 1106 tile_cache.memory_pressure(&mut self.resource_cache); 1107 } 1108 } 1109 1110 let resource_updates = self.resource_cache.pending_updates(); 1111 let msg = ResultMsg::UpdateResources { 1112 resource_updates, 1113 memory_pressure: true, 1114 }; 1115 self.result_tx.send(msg).unwrap(); 1116 self.notifier.wake_up(false); 1117 1118 self.chunk_pool.purge_all_chunks(); 1119 } 1120 ApiMsg::ReportMemory(tx) => { 1121 self.report_memory(tx); 1122 } 1123 ApiMsg::DebugCommand(option) => { 1124 let msg = match option { 1125 DebugCommand::SetPictureTileSize(tile_size) => { 1126 self.frame_config.tile_size_override = tile_size; 1127 self.update_frame_builder_config(); 1128 1129 return RenderBackendStatus::Continue; 1130 } 1131 DebugCommand::SetMaximumSurfaceSize(surface_size) => { 1132 self.frame_config.max_surface_override = surface_size; 1133 self.update_frame_builder_config(); 1134 1135 return RenderBackendStatus::Continue; 1136 } 1137 DebugCommand::GenerateFrame => { 1138 let documents: Vec<DocumentId> = self.documents.keys() 1139 .cloned() 1140 .collect(); 1141 for document_id in documents { 1142 let mut invalidation_config = false; 1143 if let Some(doc) = self.documents.get_mut(&document_id) { 1144 doc.frame_is_valid = false; 1145 invalidation_config = doc.scene.config.force_invalidation; 1146 doc.scene.config.force_invalidation = true; 1147 } 1148 1149 self.update_document( 1150 document_id, 1151 Vec::default(), 1152 Vec::default(), 1153 Vec::default(), 1154 true, 1155 true, 1156 false, 1157 RenderReasons::empty(), 1158 None, 1159 true, 1160 frame_counter, 1161 false, 1162 None, 1163 ); 1164 1165 if let Some(doc) = self.documents.get_mut(&document_id) { 1166 doc.scene.config.force_invalidation = invalidation_config; 1167 } 1168 } 1169 1170 return RenderBackendStatus::Continue; 1171 } 1172 #[cfg(feature = "capture")] 1173 DebugCommand::SaveCapture(root, bits) => { 1174 let output = self.save_capture(root, bits); 1175 ResultMsg::DebugOutput(output) 1176 }, 1177 #[cfg(feature = "capture")] 1178 DebugCommand::StartCaptureSequence(root, bits) => { 1179 self.start_capture_sequence(root, bits); 1180 return RenderBackendStatus::Continue; 1181 }, 1182 #[cfg(feature = "capture")] 1183 DebugCommand::StopCaptureSequence => { 1184 self.stop_capture_sequence(); 1185 return RenderBackendStatus::Continue; 1186 }, 1187 #[cfg(feature = "replay")] 1188 DebugCommand::LoadCapture(path, ids, tx) => { 1189 NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed); 1190 *frame_counter += 1; 1191 1192 let mut config = CaptureConfig::new(path, CaptureBits::all()); 1193 if let Some((scene_id, frame_id)) = ids { 1194 config.scene_id = scene_id; 1195 config.frame_id = frame_id; 1196 } 1197 1198 self.load_capture(config); 1199 1200 for (id, doc) in &self.documents { 1201 let captured = CapturedDocument { 1202 document_id: *id, 1203 root_pipeline_id: doc.loaded_scene.root_pipeline_id, 1204 }; 1205 tx.send(captured).unwrap(); 1206 } 1207 1208 // Note: we can't pass `LoadCapture` here since it needs to arrive 1209 // before the `PublishDocument` messages sent by `load_capture`. 1210 return RenderBackendStatus::Continue; 1211 } 1212 #[cfg(feature = "debugger")] 1213 DebugCommand::Query(ref query) => { 1214 match query.kind { 1215 DebugQueryKind::SpatialTree { .. } => { 1216 if let Some(doc) = self.documents.values().next() { 1217 let result = doc.spatial_tree.print_to_string(); 1218 query.result.send(result).ok(); 1219 } 1220 return RenderBackendStatus::Continue; 1221 } 1222 DebugQueryKind::CompositorView { .. } | 1223 DebugQueryKind::CompositorConfig { .. } | 1224 DebugQueryKind::Textures { .. } => { 1225 ResultMsg::DebugCommand(option) 1226 } 1227 } 1228 } 1229 DebugCommand::ClearCaches(mask) => { 1230 self.resource_cache.clear(mask); 1231 return RenderBackendStatus::Continue; 1232 } 1233 DebugCommand::EnableNativeCompositor(enable) => { 1234 // Default CompositorKind should be Native 1235 if let CompositorKind::Draw { .. } = self.default_compositor_kind { 1236 unreachable!(); 1237 } 1238 1239 let compositor_kind = if enable { 1240 self.default_compositor_kind 1241 } else { 1242 CompositorKind::default() 1243 }; 1244 1245 for (_, doc) in &mut self.documents { 1246 doc.scene.config.compositor_kind = compositor_kind; 1247 doc.frame_is_valid = false; 1248 } 1249 1250 self.frame_config.compositor_kind = compositor_kind; 1251 self.update_frame_builder_config(); 1252 1253 // We don't want to forward this message to the renderer. 1254 return RenderBackendStatus::Continue; 1255 } 1256 DebugCommand::SetBatchingLookback(count) => { 1257 self.frame_config.batch_lookback_count = count as usize; 1258 self.update_frame_builder_config(); 1259 1260 return RenderBackendStatus::Continue; 1261 } 1262 DebugCommand::SimulateLongSceneBuild(time_ms) => { 1263 let _ = self.scene_tx.send(SceneBuilderRequest::SimulateLongSceneBuild(time_ms)); 1264 return RenderBackendStatus::Continue; 1265 } 1266 DebugCommand::SetFlags(flags) => { 1267 self.resource_cache.set_debug_flags(flags); 1268 1269 let force_invalidation = flags.contains(DebugFlags::FORCE_PICTURE_INVALIDATION); 1270 if self.frame_config.force_invalidation != force_invalidation { 1271 self.frame_config.force_invalidation = force_invalidation; 1272 for doc in self.documents.values_mut() { 1273 doc.scene.config.force_invalidation = force_invalidation; 1274 } 1275 self.update_frame_builder_config(); 1276 } 1277 1278 self.debug_flags = flags; 1279 1280 ResultMsg::DebugCommand(option) 1281 } 1282 _ => ResultMsg::DebugCommand(option), 1283 }; 1284 self.result_tx.send(msg).unwrap(); 1285 self.notifier.wake_up(true); 1286 } 1287 ApiMsg::UpdateDocuments(transaction_msgs) => { 1288 self.prepare_transactions( 1289 transaction_msgs, 1290 frame_counter, 1291 ); 1292 } 1293 ApiMsg::SceneBuilderResult(msg) => { 1294 return self.process_scene_builder_result(msg, frame_counter); 1295 } 1296 } 1297 1298 // Now that we are likely out of the critical path, purge a few chunks 1299 // from the pool. The underlying deallocation can be expensive, especially 1300 // with build configurations where all of the memory is zeroed, so we 1301 // spread the load over potentially many iterations of the event loop. 1302 self.chunk_pool.purge_chunks(2, 3); 1303 1304 RenderBackendStatus::Continue 1305 } 1306 1307 fn process_scene_builder_result( 1308 &mut self, 1309 msg: SceneBuilderResult, 1310 frame_counter: &mut u32, 1311 ) -> RenderBackendStatus { 1312 profile_scope!("sb_msg"); 1313 1314 match msg { 1315 SceneBuilderResult::Transactions(txns, result_tx) => { 1316 self.process_transaction( 1317 txns, 1318 result_tx, 1319 frame_counter, 1320 ); 1321 }, 1322 #[cfg(feature = "capture")] 1323 SceneBuilderResult::CapturedTransactions(txns, capture_config, result_tx) => { 1324 if let Some(ref mut old_config) = self.capture_config { 1325 assert!(old_config.scene_id <= capture_config.scene_id); 1326 if old_config.scene_id < capture_config.scene_id { 1327 old_config.scene_id = capture_config.scene_id; 1328 old_config.frame_id = 0; 1329 } 1330 } else { 1331 self.capture_config = Some(capture_config); 1332 } 1333 1334 let built_frame = self.process_transaction( 1335 txns, 1336 result_tx, 1337 frame_counter, 1338 ); 1339 1340 if built_frame { 1341 self.save_capture_sequence(); 1342 } 1343 }, 1344 #[cfg(feature = "capture")] 1345 SceneBuilderResult::StopCaptureSequence => { 1346 self.capture_config = None; 1347 } 1348 SceneBuilderResult::GetGlyphDimensions(request) => { 1349 let mut glyph_dimensions = Vec::with_capacity(request.glyph_indices.len()); 1350 let instance_key = self.resource_cache.map_font_instance_key(request.key); 1351 if let Some(base) = self.resource_cache.get_font_instance(instance_key) { 1352 let font = FontInstance::from_base(Arc::clone(&base)); 1353 for glyph_index in &request.glyph_indices { 1354 let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, *glyph_index); 1355 glyph_dimensions.push(glyph_dim); 1356 } 1357 } 1358 request.sender.send(glyph_dimensions).unwrap(); 1359 } 1360 SceneBuilderResult::GetGlyphIndices(request) => { 1361 let mut glyph_indices = Vec::with_capacity(request.text.len()); 1362 let font_key = self.resource_cache.map_font_key(request.key); 1363 for ch in request.text.chars() { 1364 let index = self.resource_cache.get_glyph_index(font_key, ch); 1365 glyph_indices.push(index); 1366 } 1367 request.sender.send(glyph_indices).unwrap(); 1368 } 1369 SceneBuilderResult::FlushComplete(tx) => { 1370 tx.send(()).ok(); 1371 } 1372 SceneBuilderResult::ExternalEvent(evt) => { 1373 self.notifier.external_event(evt); 1374 } 1375 SceneBuilderResult::ClearNamespace(id) => { 1376 self.resource_cache.clear_namespace(id); 1377 self.documents.retain(|doc_id, _doc| doc_id.namespace_id != id); 1378 } 1379 SceneBuilderResult::DeleteDocument(document_id) => { 1380 self.documents.remove(&document_id); 1381 } 1382 SceneBuilderResult::SetParameter(param) => { 1383 if let Parameter::Bool(BoolParameter::Multithreading, enabled) = param { 1384 self.resource_cache.enable_multithreading(enabled); 1385 } 1386 let _ = self.result_tx.send(ResultMsg::SetParameter(param)); 1387 } 1388 SceneBuilderResult::StopRenderBackend => { 1389 return RenderBackendStatus::StopRenderBackend; 1390 } 1391 SceneBuilderResult::ShutDown(sender) => { 1392 info!("Recycling stats: {:?}", self.recycler); 1393 return RenderBackendStatus::ShutDown(sender); 1394 } 1395 } 1396 1397 RenderBackendStatus::Continue 1398 } 1399 1400 fn update_frame_builder_config(&self) { 1401 self.send_backend_message( 1402 SceneBuilderRequest::SetFrameBuilderConfig( 1403 self.frame_config.clone() 1404 ) 1405 ); 1406 } 1407 1408 fn requires_frame_build(&mut self) -> bool { 1409 false // TODO(nical) 1410 } 1411 1412 fn prepare_transactions( 1413 &mut self, 1414 txns: Vec<Box<TransactionMsg>>, 1415 frame_counter: &mut u32, 1416 ) { 1417 self.maybe_force_nop_documents( 1418 frame_counter, 1419 |document_id| txns.iter().any(|txn| txn.document_id == document_id)); 1420 1421 let mut built_frame = false; 1422 for mut txn in txns { 1423 if txn.generate_frame.as_bool() { 1424 txn.profile.end_time(profiler::API_SEND_TIME); 1425 } 1426 1427 self.documents.get_mut(&txn.document_id).unwrap().profile.merge(&mut txn.profile); 1428 1429 built_frame |= self.update_document( 1430 txn.document_id, 1431 txn.resource_updates.take(), 1432 txn.frame_ops.take(), 1433 txn.notifications.take(), 1434 txn.generate_frame.as_bool(), 1435 txn.generate_frame.present(), 1436 txn.generate_frame.tracked(), 1437 txn.render_reasons, 1438 txn.generate_frame.id(), 1439 txn.invalidate_rendered_frame, 1440 frame_counter, 1441 false, 1442 txn.creation_time, 1443 ); 1444 } 1445 if built_frame { 1446 #[cfg(feature = "capture")] 1447 self.save_capture_sequence(); 1448 } 1449 } 1450 1451 /// In certain cases, resources shared by multiple documents have to run 1452 /// maintenance operations, like cleaning up unused cache items. In those 1453 /// cases, we are forced to build frames for all documents, however we 1454 /// may not have a transaction ready for every document - this method 1455 /// calls update_document with the details of a fake, nop transaction just 1456 /// to force a frame build. 1457 fn maybe_force_nop_documents<F>(&mut self, 1458 frame_counter: &mut u32, 1459 document_already_present: F) where 1460 F: Fn(DocumentId) -> bool { 1461 if self.requires_frame_build() { 1462 let nop_documents : Vec<DocumentId> = self.documents.keys() 1463 .cloned() 1464 .filter(|key| !document_already_present(*key)) 1465 .collect(); 1466 #[allow(unused_variables)] 1467 let mut built_frame = false; 1468 for &document_id in &nop_documents { 1469 built_frame |= self.update_document( 1470 document_id, 1471 Vec::default(), 1472 Vec::default(), 1473 Vec::default(), 1474 false, 1475 false, 1476 false, 1477 RenderReasons::empty(), 1478 None, 1479 false, 1480 frame_counter, 1481 false, 1482 None); 1483 } 1484 #[cfg(feature = "capture")] 1485 match built_frame { 1486 true => self.save_capture_sequence(), 1487 _ => {}, 1488 } 1489 } 1490 } 1491 1492 fn update_document( 1493 &mut self, 1494 document_id: DocumentId, 1495 resource_updates: Vec<ResourceUpdate>, 1496 mut frame_ops: Vec<FrameMsg>, 1497 mut notifications: Vec<NotificationRequest>, 1498 mut render_frame: bool, 1499 mut present: bool, 1500 tracked: bool, 1501 render_reasons: RenderReasons, 1502 generated_frame_id: Option<u64>, 1503 invalidate_rendered_frame: bool, 1504 frame_counter: &mut u32, 1505 has_built_scene: bool, 1506 start_time: Option<u64> 1507 ) -> bool { 1508 let update_doc_start = zeitstempel::now(); 1509 1510 let requested_frame = render_frame || self.frame_config.force_invalidation; 1511 1512 let requires_frame_build = self.requires_frame_build(); 1513 let doc = self.documents.get_mut(&document_id).unwrap(); 1514 1515 // If we have a sampler, get more frame ops from it and add them 1516 // to the transaction. This is a hook to allow the WR user code to 1517 // fiddle with things after a potentially long scene build, but just 1518 // before rendering. This is useful for rendering with the latest 1519 // async transforms. 1520 if requested_frame { 1521 if let Some(ref sampler) = self.sampler { 1522 frame_ops.append(&mut sampler.sample(document_id, generated_frame_id)); 1523 } 1524 } 1525 1526 doc.has_built_scene |= has_built_scene; 1527 1528 // TODO: this scroll variable doesn't necessarily mean we scrolled. It is only used 1529 // for something wrench specific and we should remove it. 1530 let mut scroll = false; 1531 for frame_msg in frame_ops { 1532 let op = doc.process_frame_msg(frame_msg); 1533 scroll |= op.scroll; 1534 } 1535 1536 for update in &resource_updates { 1537 if let ResourceUpdate::UpdateImage(..) = update { 1538 doc.frame_is_valid = false; 1539 } 1540 } 1541 1542 self.resource_cache.post_scene_building_update( 1543 resource_updates, 1544 &mut doc.profile, 1545 ); 1546 1547 if doc.dynamic_properties.flush_pending_updates() { 1548 doc.frame_is_valid = false; 1549 doc.hit_tester_is_valid = false; 1550 } 1551 1552 if !doc.can_render() { 1553 // TODO: this happens if we are building the first scene asynchronously and 1554 // scroll at the same time. we should keep track of the fact that we skipped 1555 // composition here and do it as soon as we receive the scene. 1556 render_frame = false; 1557 } 1558 1559 // Avoid re-building the frame if the current built frame is still valid. 1560 // However, if the resource_cache requires a frame build, _always_ do that, unless 1561 // doc.can_render() is false, as in that case a frame build can't happen anyway. 1562 // We want to ensure we do this because even if the doc doesn't have pixels it 1563 // can still try to access stale texture cache items. 1564 let build_frame = (render_frame && !doc.frame_is_valid && doc.has_pixels()) || 1565 (requires_frame_build && doc.can_render()); 1566 1567 // Request composite is true when we want to composite frame even when 1568 // there is no frame update. This happens when video frame is updated under 1569 // external image with NativeTexture or when platform requested to composite frame. 1570 if invalidate_rendered_frame { 1571 doc.rendered_frame_is_valid = false; 1572 if doc.scene.config.compositor_kind.should_redraw_on_invalidation() { 1573 let msg = ResultMsg::ForceRedraw; 1574 self.result_tx.send(msg).unwrap(); 1575 } 1576 } 1577 1578 if build_frame { 1579 if !requested_frame { 1580 // When we don't request a frame, present defaults to false. If for some 1581 // reason we did not request the frame but must render it anyway, set 1582 // present to true (it was false as a byproduct of expecting we wouldn't 1583 // produce the frame but we did not explicitly opt out of it). 1584 present = true; 1585 } 1586 1587 if start_time.is_some() { 1588 Telemetry::record_time_to_frame_build(Duration::from_nanos(zeitstempel::now() - start_time.unwrap())); 1589 } 1590 profile_scope!("generate frame"); 1591 1592 *frame_counter += 1; 1593 1594 // borrow ck hack for profile_counters 1595 let (pending_update, mut rendered_document) = { 1596 let timer_id = Telemetry::start_framebuild_time(); 1597 1598 let frame_stats = doc.frame_stats.take(); 1599 1600 let rendered_document = doc.build_frame( 1601 &mut self.resource_cache, 1602 self.debug_flags, 1603 &mut self.tile_caches, 1604 frame_stats, 1605 present, 1606 render_reasons, 1607 self.chunk_pool.clone(), 1608 ); 1609 1610 debug!("generated frame for document {:?} with {} passes", 1611 document_id, rendered_document.frame.passes.len()); 1612 1613 Telemetry::stop_and_accumulate_framebuild_time(timer_id); 1614 1615 let pending_update = self.resource_cache.pending_updates(); 1616 (pending_update, rendered_document) 1617 }; 1618 1619 // Invalidate dirty rects if the compositing config has changed significantly 1620 rendered_document 1621 .frame 1622 .composite_state 1623 .update_dirty_rect_validity(&doc.prev_composite_descriptor); 1624 1625 // Build a small struct that represents the state of the tiles to be composited. 1626 let composite_descriptor = rendered_document 1627 .frame 1628 .composite_state 1629 .descriptor 1630 .clone(); 1631 1632 // If there are texture cache updates to apply, or if the produced 1633 // frame is not a no-op, or the compositor state has changed, 1634 // then we cannot skip compositing this frame. 1635 if !pending_update.is_nop() || 1636 !rendered_document.frame.is_nop() || 1637 composite_descriptor != doc.prev_composite_descriptor { 1638 doc.rendered_frame_is_valid = false; 1639 } 1640 doc.prev_composite_descriptor = composite_descriptor; 1641 1642 #[cfg(feature = "capture")] 1643 match self.capture_config { 1644 Some(ref mut config) => { 1645 // FIXME(aosmond): document splitting causes multiple prepare frames 1646 config.prepare_frame(); 1647 1648 if config.bits.contains(CaptureBits::FRAME) { 1649 let file_name = format!("frame-{}-{}", document_id.namespace_id.0, document_id.id); 1650 config.serialize_for_frame(&rendered_document.frame, file_name); 1651 } 1652 1653 let data_stores_name = format!("data-stores-{}-{}", document_id.namespace_id.0, document_id.id); 1654 config.serialize_for_frame(&doc.data_stores, data_stores_name); 1655 1656 let frame_spatial_tree_name = format!("frame-spatial-tree-{}-{}", document_id.namespace_id.0, document_id.id); 1657 config.serialize_for_frame::<SpatialTree, _>(&doc.spatial_tree, frame_spatial_tree_name); 1658 1659 let properties_name = format!("properties-{}-{}", document_id.namespace_id.0, document_id.id); 1660 config.serialize_for_frame(&doc.dynamic_properties, properties_name); 1661 }, 1662 None => {}, 1663 } 1664 1665 let update_doc_time = profiler::ns_to_ms(zeitstempel::now() - update_doc_start); 1666 rendered_document.profile.set(profiler::UPDATE_DOCUMENT_TIME, update_doc_time); 1667 1668 let msg = ResultMsg::PublishPipelineInfo(doc.updated_pipeline_info()); 1669 self.result_tx.send(msg).unwrap(); 1670 1671 // Publish the frame 1672 self.frame_publish_id.advance(); 1673 let msg = ResultMsg::PublishDocument( 1674 self.frame_publish_id, 1675 document_id, 1676 rendered_document, 1677 pending_update, 1678 ); 1679 self.result_tx.send(msg).unwrap(); 1680 } else if requested_frame { 1681 // WR-internal optimization to avoid doing a bunch of render work if 1682 // there's no pixels. We still want to pretend to render and request 1683 // a render to make sure that the callbacks (particularly the 1684 // new_frame_ready callback below) has the right flags. 1685 let msg = ResultMsg::PublishPipelineInfo(doc.updated_pipeline_info()); 1686 self.result_tx.send(msg).unwrap(); 1687 } 1688 1689 drain_filter( 1690 &mut notifications, 1691 |n| { n.when() == Checkpoint::FrameBuilt }, 1692 |n| { n.notify(); }, 1693 ); 1694 1695 if !notifications.is_empty() { 1696 self.result_tx.send(ResultMsg::AppendNotificationRequests(notifications)).unwrap(); 1697 } 1698 1699 // Always forward the transaction to the renderer if a frame was requested, 1700 // otherwise gecko can get into a state where it waits (forever) for the 1701 // transaction to complete before sending new work. 1702 if requested_frame { 1703 // If rendered frame is already valid, there is no need to render frame. 1704 if doc.rendered_frame_is_valid { 1705 render_frame = false; 1706 } else if render_frame { 1707 doc.rendered_frame_is_valid = true; 1708 } 1709 let params = api::FrameReadyParams { 1710 present, 1711 render: render_frame, 1712 scrolled: scroll, 1713 tracked, 1714 }; 1715 self.notifier.new_frame_ready(document_id, self.frame_publish_id, ¶ms); 1716 } 1717 1718 if !doc.hit_tester_is_valid { 1719 doc.rebuild_hit_tester(); 1720 } 1721 1722 build_frame 1723 } 1724 1725 fn send_backend_message(&self, msg: SceneBuilderRequest) { 1726 self.scene_tx.send(msg).unwrap(); 1727 } 1728 1729 fn report_memory(&mut self, tx: Sender<Box<MemoryReport>>) { 1730 let mut report = Box::new(MemoryReport::default()); 1731 let ops = self.size_of_ops.as_mut().unwrap(); 1732 let op = ops.size_of_op; 1733 for doc in self.documents.values() { 1734 report.clip_stores += doc.scene.clip_store.size_of(ops); 1735 report.hit_testers += match &doc.hit_tester { 1736 Some(hit_tester) => hit_tester.size_of(ops), 1737 None => 0, 1738 }; 1739 1740 doc.data_stores.report_memory(ops, &mut report) 1741 } 1742 1743 (*report) += self.resource_cache.report_memory(op); 1744 report.texture_cache_structures = self.resource_cache 1745 .texture_cache 1746 .report_memory(ops); 1747 1748 // Send a message to report memory on the scene-builder thread, which 1749 // will add its report to this one and send the result back to the original 1750 // thread waiting on the request. 1751 self.send_backend_message( 1752 SceneBuilderRequest::ReportMemory(report, tx) 1753 ); 1754 } 1755 1756 #[cfg(feature = "capture")] 1757 fn save_capture_sequence(&mut self) { 1758 if let Some(ref mut config) = self.capture_config { 1759 let deferred = self.resource_cache.save_capture_sequence(config); 1760 1761 let backend = PlainRenderBackend { 1762 frame_config: self.frame_config.clone(), 1763 resource_sequence_id: config.resource_id, 1764 documents: self.documents 1765 .iter() 1766 .map(|(id, doc)| (*id, doc.view)) 1767 .collect(), 1768 }; 1769 config.serialize_for_frame(&backend, "backend"); 1770 1771 if !deferred.is_empty() { 1772 let msg = ResultMsg::DebugOutput(DebugOutput::SaveCapture(config.clone(), deferred)); 1773 self.result_tx.send(msg).unwrap(); 1774 } 1775 } 1776 } 1777 } 1778 1779 impl RenderBackend { 1780 #[cfg(feature = "capture")] 1781 // Note: the mutable `self` is only needed here for resolving blob images 1782 fn save_capture( 1783 &mut self, 1784 root: PathBuf, 1785 bits: CaptureBits, 1786 ) -> DebugOutput { 1787 use std::fs; 1788 use crate::render_task_graph::dump_render_tasks_as_svg; 1789 1790 debug!("capture: saving {:?}", root); 1791 if !root.is_dir() { 1792 if let Err(e) = fs::create_dir_all(&root) { 1793 panic!("Unable to create capture dir: {:?}", e); 1794 } 1795 } 1796 let config = CaptureConfig::new(root, bits); 1797 1798 for (&id, doc) in &mut self.documents { 1799 debug!("\tdocument {:?}", id); 1800 if config.bits.contains(CaptureBits::FRAME) { 1801 // Temporarily force invalidation otherwise the render task graph dump is empty. 1802 let force_invalidation = std::mem::replace(&mut doc.scene.config.force_invalidation, true); 1803 let rendered_document = doc.build_frame( 1804 &mut self.resource_cache, 1805 self.debug_flags, 1806 &mut self.tile_caches, 1807 None, 1808 true, 1809 RenderReasons::empty(), 1810 self.chunk_pool.clone(), 1811 ); 1812 1813 doc.scene.config.force_invalidation = force_invalidation; 1814 1815 //TODO: write down doc's pipeline info? 1816 // it has `pipeline_epoch_map`, 1817 // which may capture necessary details for some cases. 1818 let file_name = format!("frame-{}-{}", id.namespace_id.0, id.id); 1819 config.serialize_for_frame(&rendered_document.frame, file_name); 1820 let file_name = format!("spatial-{}-{}", id.namespace_id.0, id.id); 1821 config.serialize_tree_for_frame(&doc.spatial_tree, file_name); 1822 let file_name = format!("built-primitives-{}-{}", id.namespace_id.0, id.id); 1823 config.serialize_for_frame(&doc.scene.prim_store, file_name); 1824 let file_name = format!("built-clips-{}-{}", id.namespace_id.0, id.id); 1825 config.serialize_for_frame(&doc.scene.clip_store, file_name); 1826 let file_name = format!("scratch-{}-{}", id.namespace_id.0, id.id); 1827 config.serialize_for_frame(&doc.scratch.primitive, file_name); 1828 let file_name = format!("render-tasks-{}-{}.svg", id.namespace_id.0, id.id); 1829 let mut render_tasks_file = fs::File::create(&config.file_path_for_frame(file_name, "svg")) 1830 .expect("Failed to open the SVG file."); 1831 dump_render_tasks_as_svg( 1832 &rendered_document.frame.render_tasks, 1833 &mut render_tasks_file 1834 ).unwrap(); 1835 1836 let file_name = format!("texture-cache-color-linear-{}-{}.svg", id.namespace_id.0, id.id); 1837 let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg")) 1838 .expect("Failed to open the SVG file."); 1839 self.resource_cache.texture_cache.dump_color8_linear_as_svg(&mut texture_file).unwrap(); 1840 1841 let file_name = format!("texture-cache-color8-glyphs-{}-{}.svg", id.namespace_id.0, id.id); 1842 let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg")) 1843 .expect("Failed to open the SVG file."); 1844 self.resource_cache.texture_cache.dump_color8_glyphs_as_svg(&mut texture_file).unwrap(); 1845 1846 let file_name = format!("texture-cache-alpha8-glyphs-{}-{}.svg", id.namespace_id.0, id.id); 1847 let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg")) 1848 .expect("Failed to open the SVG file."); 1849 self.resource_cache.texture_cache.dump_alpha8_glyphs_as_svg(&mut texture_file).unwrap(); 1850 1851 let file_name = format!("texture-cache-alpha8-linear-{}-{}.svg", id.namespace_id.0, id.id); 1852 let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg")) 1853 .expect("Failed to open the SVG file."); 1854 self.resource_cache.texture_cache.dump_alpha8_linear_as_svg(&mut texture_file).unwrap(); 1855 } 1856 1857 let data_stores_name = format!("data-stores-{}-{}", id.namespace_id.0, id.id); 1858 config.serialize_for_frame(&doc.data_stores, data_stores_name); 1859 1860 let frame_spatial_tree_name = format!("frame-spatial-tree-{}-{}", id.namespace_id.0, id.id); 1861 config.serialize_for_frame::<SpatialTree, _>(&doc.spatial_tree, frame_spatial_tree_name); 1862 1863 let properties_name = format!("properties-{}-{}", id.namespace_id.0, id.id); 1864 config.serialize_for_frame(&doc.dynamic_properties, properties_name); 1865 } 1866 1867 if config.bits.contains(CaptureBits::FRAME) { 1868 // TODO: there is no guarantee that we won't hit this case, but we want to 1869 // report it here if we do. If we don't, it will simply crash in 1870 // Renderer::render_impl and give us less information about the source. 1871 assert!(!self.requires_frame_build(), "Caches were cleared during a capture."); 1872 } 1873 1874 debug!("\tscene builder"); 1875 self.send_backend_message( 1876 SceneBuilderRequest::SaveScene(config.clone()) 1877 ); 1878 1879 debug!("\tresource cache"); 1880 let (resources, deferred) = self.resource_cache.save_capture(&config.root); 1881 1882 info!("\tbackend"); 1883 let backend = PlainRenderBackend { 1884 frame_config: self.frame_config.clone(), 1885 resource_sequence_id: 0, 1886 documents: self.documents 1887 .iter() 1888 .map(|(id, doc)| (*id, doc.view)) 1889 .collect(), 1890 }; 1891 1892 config.serialize_for_frame(&backend, "backend"); 1893 config.serialize_for_frame(&resources, "plain-resources"); 1894 1895 if config.bits.contains(CaptureBits::FRAME) { 1896 let msg_update_resources = ResultMsg::UpdateResources { 1897 resource_updates: self.resource_cache.pending_updates(), 1898 memory_pressure: false, 1899 }; 1900 self.result_tx.send(msg_update_resources).unwrap(); 1901 // Save the texture/glyph/image caches. 1902 info!("\tresource cache"); 1903 let caches = self.resource_cache.save_caches(&config.root); 1904 config.serialize_for_resource(&caches, "resource_cache"); 1905 } 1906 1907 DebugOutput::SaveCapture(config, deferred) 1908 } 1909 1910 #[cfg(feature = "capture")] 1911 fn start_capture_sequence( 1912 &mut self, 1913 root: PathBuf, 1914 bits: CaptureBits, 1915 ) { 1916 self.send_backend_message( 1917 SceneBuilderRequest::StartCaptureSequence(CaptureConfig::new(root, bits)) 1918 ); 1919 } 1920 1921 #[cfg(feature = "capture")] 1922 fn stop_capture_sequence( 1923 &mut self, 1924 ) { 1925 self.send_backend_message( 1926 SceneBuilderRequest::StopCaptureSequence 1927 ); 1928 } 1929 1930 #[cfg(feature = "replay")] 1931 fn load_capture( 1932 &mut self, 1933 mut config: CaptureConfig, 1934 ) { 1935 debug!("capture: loading {:?}", config.frame_root()); 1936 let backend = config.deserialize_for_frame::<PlainRenderBackend, _>("backend") 1937 .expect("Unable to open backend.ron"); 1938 1939 // If this is a capture sequence, then the ID will be non-zero, and won't 1940 // match what is loaded, but for still captures, the ID will be zero. 1941 let first_load = backend.resource_sequence_id == 0; 1942 if self.loaded_resource_sequence_id != backend.resource_sequence_id || first_load { 1943 // FIXME(aosmond): We clear the documents because when we update the 1944 // resource cache, we actually wipe and reload, because we don't 1945 // know what is the same and what has changed. If we were to keep as 1946 // much of the resource cache state as possible, we could avoid 1947 // flushing the document state (which has its own dependecies on the 1948 // cache). 1949 // 1950 // FIXME(aosmond): If we try to load the next capture in the 1951 // sequence too quickly, we may lose resources we depend on in the 1952 // current frame. This can cause panics. Ideally we would not 1953 // advance to the next frame until the FrameRendered event for all 1954 // of the pipelines. 1955 self.documents.clear(); 1956 1957 config.resource_id = backend.resource_sequence_id; 1958 self.loaded_resource_sequence_id = backend.resource_sequence_id; 1959 1960 let plain_resources = config.deserialize_for_resource::<PlainResources, _>("plain-resources") 1961 .expect("Unable to open plain-resources.ron"); 1962 let caches_maybe = config.deserialize_for_resource::<PlainCacheOwn, _>("resource_cache"); 1963 1964 // Note: it would be great to have `RenderBackend` to be split 1965 // rather explicitly on what's used before and after scene building 1966 // so that, for example, we never miss anything in the code below: 1967 1968 let plain_externals = self.resource_cache.load_capture( 1969 plain_resources, 1970 caches_maybe, 1971 &config, 1972 ); 1973 1974 let msg_load = ResultMsg::DebugOutput( 1975 DebugOutput::LoadCapture(config.clone(), plain_externals) 1976 ); 1977 self.result_tx.send(msg_load).unwrap(); 1978 } 1979 1980 self.frame_config = backend.frame_config; 1981 1982 let mut scenes_to_build = Vec::new(); 1983 1984 for (id, view) in backend.documents { 1985 debug!("\tdocument {:?}", id); 1986 let scene_name = format!("scene-{}-{}", id.namespace_id.0, id.id); 1987 let scene = config.deserialize_for_scene::<Scene, _>(&scene_name) 1988 .expect(&format!("Unable to open {}.ron", scene_name)); 1989 1990 let scene_spatial_tree_name = format!("scene-spatial-tree-{}-{}", id.namespace_id.0, id.id); 1991 let scene_spatial_tree = config.deserialize_for_scene::<SceneSpatialTree, _>(&scene_spatial_tree_name) 1992 .expect(&format!("Unable to open {}.ron", scene_spatial_tree_name)); 1993 1994 let interners_name = format!("interners-{}-{}", id.namespace_id.0, id.id); 1995 let interners = config.deserialize_for_scene::<Interners, _>(&interners_name) 1996 .expect(&format!("Unable to open {}.ron", interners_name)); 1997 1998 let data_stores_name = format!("data-stores-{}-{}", id.namespace_id.0, id.id); 1999 let data_stores = config.deserialize_for_frame::<DataStores, _>(&data_stores_name) 2000 .expect(&format!("Unable to open {}.ron", data_stores_name)); 2001 2002 let properties_name = format!("properties-{}-{}", id.namespace_id.0, id.id); 2003 let properties = config.deserialize_for_frame::<SceneProperties, _>(&properties_name) 2004 .expect(&format!("Unable to open {}.ron", properties_name)); 2005 2006 let frame_spatial_tree_name = format!("frame-spatial-tree-{}-{}", id.namespace_id.0, id.id); 2007 let frame_spatial_tree = config.deserialize_for_frame::<SpatialTree, _>(&frame_spatial_tree_name) 2008 .expect(&format!("Unable to open {}.ron", frame_spatial_tree_name)); 2009 2010 // Update the document if it still exists, rather than replace it entirely. 2011 // This allows us to preserve state information such as the frame stamp, 2012 // which is necessary for cache sanity. 2013 match self.documents.entry(id) { 2014 Occupied(entry) => { 2015 let doc = entry.into_mut(); 2016 doc.view = view; 2017 doc.loaded_scene = scene.clone(); 2018 doc.data_stores = data_stores; 2019 doc.spatial_tree = frame_spatial_tree; 2020 doc.dynamic_properties = properties; 2021 doc.frame_is_valid = false; 2022 doc.rendered_frame_is_valid = false; 2023 doc.has_built_scene = false; 2024 doc.hit_tester_is_valid = false; 2025 } 2026 Vacant(entry) => { 2027 let doc = Document { 2028 id, 2029 scene: BuiltScene::empty(), 2030 removed_pipelines: Vec::new(), 2031 view, 2032 stamp: FrameStamp::first(id), 2033 frame_builder: FrameBuilder::new(), 2034 dynamic_properties: properties, 2035 hit_tester: None, 2036 shared_hit_tester: Arc::new(SharedHitTester::new()), 2037 frame_is_valid: false, 2038 hit_tester_is_valid: false, 2039 rendered_frame_is_valid: false, 2040 has_built_scene: false, 2041 data_stores, 2042 scratch: ScratchBuffer::default(), 2043 spatial_tree: frame_spatial_tree, 2044 minimap_data: FastHashMap::default(), 2045 loaded_scene: scene.clone(), 2046 prev_composite_descriptor: CompositeDescriptor::empty(), 2047 dirty_rects_are_valid: false, 2048 profile: TransactionProfile::new(), 2049 rg_builder: RenderTaskGraphBuilder::new(), 2050 frame_stats: None, 2051 }; 2052 entry.insert(doc); 2053 } 2054 }; 2055 2056 let frame_name = format!("frame-{}-{}", id.namespace_id.0, id.id); 2057 let frame = config.deserialize_for_frame::<Frame, _>(frame_name); 2058 let build_frame = match frame { 2059 Some(frame) => { 2060 info!("\tloaded a built frame with {} passes", frame.passes.len()); 2061 2062 self.frame_publish_id.advance(); 2063 let msg_publish = ResultMsg::PublishDocument( 2064 self.frame_publish_id, 2065 id, 2066 RenderedDocument { 2067 frame, 2068 profile: TransactionProfile::new(), 2069 render_reasons: RenderReasons::empty(), 2070 frame_stats: None, 2071 }, 2072 self.resource_cache.pending_updates(), 2073 ); 2074 self.result_tx.send(msg_publish).unwrap(); 2075 2076 let params = api::FrameReadyParams { 2077 present: true, 2078 render: true, 2079 scrolled: false, 2080 tracked: false, 2081 }; 2082 self.notifier.new_frame_ready(id, self.frame_publish_id, ¶ms); 2083 2084 // We deserialized the state of the frame so we don't want to build 2085 // it (but we do want to update the scene builder's state) 2086 false 2087 } 2088 None => true, 2089 }; 2090 2091 scenes_to_build.push(LoadScene { 2092 document_id: id, 2093 scene, 2094 view: view.scene.clone(), 2095 config: self.frame_config.clone(), 2096 fonts: self.resource_cache.get_fonts(), 2097 build_frame, 2098 interners, 2099 spatial_tree: scene_spatial_tree, 2100 }); 2101 } 2102 2103 if !scenes_to_build.is_empty() { 2104 self.send_backend_message( 2105 SceneBuilderRequest::LoadScenes(scenes_to_build) 2106 ); 2107 } 2108 } 2109 }