scene_builder_thread.rs (33313B)
1 /* This Source Code Form is subject to the terms of the Mozilla Publi 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::{AsyncBlobImageRasterizer, BlobImageResult, DebugFlags, Parameter}; 6 use api::{DocumentId, PipelineId, ExternalEvent, BlobImageRequest}; 7 use api::{NotificationRequest, Checkpoint, IdNamespace, QualitySettings}; 8 use api::{PrimitiveKeyKind, GlyphDimensionRequest, GlyphIndexRequest}; 9 use api::channel::{unbounded_channel, single_msg_channel, Receiver, Sender}; 10 use api::units::*; 11 use crate::render_api::{ApiMsg, FrameMsg, SceneMsg, ResourceUpdate, TransactionMsg, MemoryReport}; 12 use crate::box_shadow::BoxShadow; 13 #[cfg(feature = "capture")] 14 use crate::capture::CaptureConfig; 15 use crate::frame_builder::FrameBuilderConfig; 16 use crate::scene_building::{SceneBuilder, SceneRecycler}; 17 use crate::clip::{ClipIntern, PolygonIntern}; 18 use crate::filterdata::FilterDataIntern; 19 use glyph_rasterizer::SharedFontResources; 20 use crate::intern::{Internable, Interner, UpdateList}; 21 use crate::internal_types::{FastHashMap, FastHashSet}; 22 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; 23 use crate::prim_store::backdrop::{BackdropCapture, BackdropRender}; 24 use crate::prim_store::borders::{ImageBorder, NormalBorderPrim}; 25 use crate::prim_store::gradient::{LinearGradient, RadialGradient, ConicGradient}; 26 use crate::prim_store::image::{Image, YuvImage}; 27 use crate::prim_store::line_dec::LineDecoration; 28 use crate::prim_store::picture::Picture; 29 use crate::prim_store::text_run::TextRun; 30 use crate::profiler::{self, TransactionProfile}; 31 use crate::render_backend::SceneView; 32 use crate::renderer::{FullFrameStats, PipelineInfo}; 33 use crate::scene::{BuiltScene, Scene, SceneStats}; 34 use crate::spatial_tree::{SceneSpatialTree, SpatialTreeUpdates}; 35 use crate::telemetry::Telemetry; 36 use crate::SceneBuilderHooks; 37 use std::iter; 38 use crate::util::drain_filter; 39 use std::thread; 40 use std::time::Duration; 41 42 fn rasterize_blobs(txn: &mut TransactionMsg, is_low_priority: bool, tile_pool: &mut api::BlobTilePool) { 43 profile_scope!("rasterize_blobs"); 44 45 if let Some(ref mut rasterizer) = txn.blob_rasterizer { 46 let mut rasterized_blobs = rasterizer.rasterize(&txn.blob_requests, is_low_priority, tile_pool); 47 // try using the existing allocation if our current list is empty 48 if txn.rasterized_blobs.is_empty() { 49 txn.rasterized_blobs = rasterized_blobs; 50 } else { 51 txn.rasterized_blobs.append(&mut rasterized_blobs); 52 } 53 } 54 } 55 56 /// Represent the remaining work associated to a transaction after the scene building 57 /// phase as well as the result of scene building itself if applicable. 58 pub struct BuiltTransaction { 59 pub document_id: DocumentId, 60 pub built_scene: Option<BuiltScene>, 61 pub offscreen_scenes: Vec<OffscreenBuiltScene>, 62 pub view: SceneView, 63 pub resource_updates: Vec<ResourceUpdate>, 64 pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>, 65 pub blob_rasterizer: Option<Box<dyn AsyncBlobImageRasterizer>>, 66 pub frame_ops: Vec<FrameMsg>, 67 pub removed_pipelines: Vec<(PipelineId, DocumentId)>, 68 pub notifications: Vec<NotificationRequest>, 69 pub interner_updates: Option<InternerUpdates>, 70 pub spatial_tree_updates: Option<SpatialTreeUpdates>, 71 pub render_frame: bool, 72 pub present: bool, 73 pub tracked: bool, 74 pub invalidate_rendered_frame: bool, 75 pub profile: TransactionProfile, 76 pub frame_stats: FullFrameStats, 77 } 78 79 pub struct OffscreenBuiltScene { 80 pub scene: BuiltScene, 81 pub interner_updates: InternerUpdates, 82 pub spatial_tree_updates: SpatialTreeUpdates, 83 } 84 85 #[cfg(feature = "replay")] 86 pub struct LoadScene { 87 pub document_id: DocumentId, 88 pub scene: Scene, 89 pub fonts: SharedFontResources, 90 pub view: SceneView, 91 pub config: FrameBuilderConfig, 92 pub build_frame: bool, 93 pub interners: Interners, 94 pub spatial_tree: SceneSpatialTree, 95 } 96 97 /// Message to the scene builder thread. 98 pub enum SceneBuilderRequest { 99 Transactions(Vec<Box<TransactionMsg>>), 100 AddDocument(DocumentId, DeviceIntSize), 101 DeleteDocument(DocumentId), 102 GetGlyphDimensions(GlyphDimensionRequest), 103 GetGlyphIndices(GlyphIndexRequest), 104 ClearNamespace(IdNamespace), 105 SimulateLongSceneBuild(u32), 106 ExternalEvent(ExternalEvent), 107 WakeUp, 108 StopRenderBackend, 109 ShutDown(Option<Sender<()>>), 110 Flush(Sender<()>), 111 SetFlags(DebugFlags), 112 SetFrameBuilderConfig(FrameBuilderConfig), 113 SetParameter(Parameter), 114 ReportMemory(Box<MemoryReport>, Sender<Box<MemoryReport>>), 115 #[cfg(feature = "capture")] 116 SaveScene(CaptureConfig), 117 #[cfg(feature = "replay")] 118 LoadScenes(Vec<LoadScene>), 119 #[cfg(feature = "capture")] 120 StartCaptureSequence(CaptureConfig), 121 #[cfg(feature = "capture")] 122 StopCaptureSequence, 123 } 124 125 // Message from scene builder to render backend. 126 pub enum SceneBuilderResult { 127 Transactions(Vec<Box<BuiltTransaction>>, Option<Sender<SceneSwapResult>>), 128 ExternalEvent(ExternalEvent), 129 FlushComplete(Sender<()>), 130 DeleteDocument(DocumentId), 131 ClearNamespace(IdNamespace), 132 GetGlyphDimensions(GlyphDimensionRequest), 133 GetGlyphIndices(GlyphIndexRequest), 134 SetParameter(Parameter), 135 StopRenderBackend, 136 ShutDown(Option<Sender<()>>), 137 138 #[cfg(feature = "capture")] 139 /// The same as `Transactions`, but also supplies a `CaptureConfig` that the 140 /// render backend should use for sequence capture, until the next 141 /// `CapturedTransactions` or `StopCaptureSequence` result. 142 CapturedTransactions(Vec<Box<BuiltTransaction>>, CaptureConfig, Option<Sender<SceneSwapResult>>), 143 144 #[cfg(feature = "capture")] 145 /// The scene builder has stopped sequence capture, so the render backend 146 /// should do the same. 147 StopCaptureSequence, 148 } 149 150 // Message from render backend to scene builder to indicate the 151 // scene swap was completed. We need a separate channel for this 152 // so that they don't get mixed with SceneBuilderRequest messages. 153 pub enum SceneSwapResult { 154 Complete(Sender<()>), 155 Aborted, 156 } 157 158 macro_rules! declare_interners { 159 ( $( $name:ident : $ty:ident, )+ ) => { 160 /// This struct contains all items that can be shared between 161 /// display lists. We want to intern and share the same clips, 162 /// primitives and other things between display lists so that: 163 /// - GPU cache handles remain valid, reducing GPU cache updates. 164 /// - Comparison of primitives and pictures between two 165 /// display lists is (a) fast (b) done during scene building. 166 #[cfg_attr(feature = "capture", derive(Serialize))] 167 #[cfg_attr(feature = "replay", derive(Deserialize))] 168 #[derive(Default)] 169 pub struct Interners { 170 $( 171 pub $name: Interner<$ty>, 172 )+ 173 } 174 175 $( 176 impl AsMut<Interner<$ty>> for Interners { 177 fn as_mut(&mut self) -> &mut Interner<$ty> { 178 &mut self.$name 179 } 180 } 181 )+ 182 183 pub struct InternerUpdates { 184 $( 185 pub $name: UpdateList<<$ty as Internable>::Key>, 186 )+ 187 } 188 189 impl Interners { 190 /// Reports CPU heap memory used by the interners. 191 fn report_memory( 192 &self, 193 ops: &mut MallocSizeOfOps, 194 r: &mut MemoryReport, 195 ) { 196 $( 197 r.interning.interners.$name += self.$name.size_of(ops); 198 )+ 199 } 200 201 fn end_frame_and_get_pending_updates(&mut self) -> InternerUpdates { 202 InternerUpdates { 203 $( 204 $name: self.$name.end_frame_and_get_pending_updates(), 205 )+ 206 } 207 } 208 } 209 } 210 } 211 212 crate::enumerate_interners!(declare_interners); 213 214 // A document in the scene builder contains the current scene, 215 // as well as a persistent clip interner. This allows clips 216 // to be de-duplicated, and persisted in the GPU cache between 217 // display lists. 218 struct Document { 219 scene: Scene, 220 interners: Interners, 221 stats: SceneStats, 222 view: SceneView, 223 spatial_tree: SceneSpatialTree, 224 } 225 226 impl Document { 227 fn new(device_rect: DeviceIntRect) -> Self { 228 Document { 229 scene: Scene::new(), 230 interners: Interners::default(), 231 stats: SceneStats::empty(), 232 spatial_tree: SceneSpatialTree::new(), 233 view: SceneView { 234 device_rect, 235 quality_settings: QualitySettings::default(), 236 }, 237 } 238 } 239 } 240 241 pub struct SceneBuilderThread { 242 documents: FastHashMap<DocumentId, Document>, 243 rx: Receiver<SceneBuilderRequest>, 244 tx: Sender<ApiMsg>, 245 config: FrameBuilderConfig, 246 fonts: SharedFontResources, 247 size_of_ops: Option<MallocSizeOfOps>, 248 hooks: Option<Box<dyn SceneBuilderHooks + Send>>, 249 simulate_slow_ms: u32, 250 removed_pipelines: FastHashSet<PipelineId>, 251 #[cfg(feature = "capture")] 252 capture_config: Option<CaptureConfig>, 253 debug_flags: DebugFlags, 254 recycler: SceneRecycler, 255 tile_pool: api::BlobTilePool, 256 } 257 258 pub struct SceneBuilderThreadChannels { 259 rx: Receiver<SceneBuilderRequest>, 260 tx: Sender<ApiMsg>, 261 } 262 263 impl SceneBuilderThreadChannels { 264 pub fn new( 265 tx: Sender<ApiMsg> 266 ) -> (Self, Sender<SceneBuilderRequest>) { 267 let (in_tx, in_rx) = unbounded_channel(); 268 ( 269 Self { 270 rx: in_rx, 271 tx, 272 }, 273 in_tx, 274 ) 275 } 276 } 277 278 impl SceneBuilderThread { 279 pub fn new( 280 config: FrameBuilderConfig, 281 fonts: SharedFontResources, 282 size_of_ops: Option<MallocSizeOfOps>, 283 hooks: Option<Box<dyn SceneBuilderHooks + Send>>, 284 channels: SceneBuilderThreadChannels, 285 ) -> Self { 286 let SceneBuilderThreadChannels { rx, tx } = channels; 287 288 Self { 289 documents: Default::default(), 290 rx, 291 tx, 292 config, 293 fonts, 294 size_of_ops, 295 hooks, 296 simulate_slow_ms: 0, 297 removed_pipelines: FastHashSet::default(), 298 #[cfg(feature = "capture")] 299 capture_config: None, 300 debug_flags: DebugFlags::default(), 301 recycler: SceneRecycler::new(), 302 // TODO: tile size is hard-coded here. 303 tile_pool: api::BlobTilePool::new(), 304 } 305 } 306 307 /// Send a message to the render backend thread. 308 /// 309 /// We first put something in the result queue and then send a wake-up 310 /// message to the api queue that the render backend is blocking on. 311 pub fn send(&self, msg: SceneBuilderResult) { 312 self.tx.send(ApiMsg::SceneBuilderResult(msg)).unwrap(); 313 } 314 315 /// The scene builder thread's event loop. 316 pub fn run(&mut self) { 317 if let Some(ref hooks) = self.hooks { 318 hooks.register(); 319 } 320 321 loop { 322 tracy_begin_frame!("scene_builder_thread"); 323 324 match self.rx.recv() { 325 Ok(SceneBuilderRequest::WakeUp) => {} 326 Ok(SceneBuilderRequest::Flush(tx)) => { 327 self.send(SceneBuilderResult::FlushComplete(tx)); 328 } 329 Ok(SceneBuilderRequest::SetFlags(debug_flags)) => { 330 self.debug_flags = debug_flags; 331 } 332 Ok(SceneBuilderRequest::Transactions(txns)) => { 333 let built_txns : Vec<Box<BuiltTransaction>> = txns.into_iter() 334 .map(|txn| self.process_transaction(*txn)) 335 .collect(); 336 #[cfg(feature = "capture")] 337 match built_txns.iter().any(|txn| txn.built_scene.is_some()) { 338 true => self.save_capture_sequence(), 339 _ => {}, 340 } 341 self.forward_built_transactions(built_txns); 342 343 // Now that we off the critical path, do some memory bookkeeping. 344 self.recycler.recycle_built_scene(); 345 self.tile_pool.cleanup(); 346 } 347 Ok(SceneBuilderRequest::AddDocument(document_id, initial_size)) => { 348 let old = self.documents.insert(document_id, Document::new( 349 initial_size.into(), 350 )); 351 debug_assert!(old.is_none()); 352 } 353 Ok(SceneBuilderRequest::DeleteDocument(document_id)) => { 354 self.documents.remove(&document_id); 355 self.send(SceneBuilderResult::DeleteDocument(document_id)); 356 } 357 Ok(SceneBuilderRequest::ClearNamespace(id)) => { 358 self.documents.retain(|doc_id, _doc| doc_id.namespace_id != id); 359 self.send(SceneBuilderResult::ClearNamespace(id)); 360 } 361 Ok(SceneBuilderRequest::ExternalEvent(evt)) => { 362 self.send(SceneBuilderResult::ExternalEvent(evt)); 363 } 364 Ok(SceneBuilderRequest::GetGlyphDimensions(request)) => { 365 self.send(SceneBuilderResult::GetGlyphDimensions(request)); 366 } 367 Ok(SceneBuilderRequest::GetGlyphIndices(request)) => { 368 self.send(SceneBuilderResult::GetGlyphIndices(request)); 369 } 370 Ok(SceneBuilderRequest::StopRenderBackend) => { 371 self.send(SceneBuilderResult::StopRenderBackend); 372 } 373 Ok(SceneBuilderRequest::ShutDown(sync)) => { 374 self.send(SceneBuilderResult::ShutDown(sync)); 375 break; 376 } 377 Ok(SceneBuilderRequest::SimulateLongSceneBuild(time_ms)) => { 378 self.simulate_slow_ms = time_ms 379 } 380 Ok(SceneBuilderRequest::ReportMemory(mut report, tx)) => { 381 (*report) += self.report_memory(); 382 tx.send(report).unwrap(); 383 } 384 Ok(SceneBuilderRequest::SetFrameBuilderConfig(cfg)) => { 385 self.config = cfg; 386 } 387 Ok(SceneBuilderRequest::SetParameter(prop)) => { 388 self.send(SceneBuilderResult::SetParameter(prop)); 389 } 390 #[cfg(feature = "replay")] 391 Ok(SceneBuilderRequest::LoadScenes(msg)) => { 392 self.load_scenes(msg); 393 } 394 #[cfg(feature = "capture")] 395 Ok(SceneBuilderRequest::SaveScene(config)) => { 396 self.save_scene(config); 397 } 398 #[cfg(feature = "capture")] 399 Ok(SceneBuilderRequest::StartCaptureSequence(config)) => { 400 self.start_capture_sequence(config); 401 } 402 #[cfg(feature = "capture")] 403 Ok(SceneBuilderRequest::StopCaptureSequence) => { 404 // FIXME(aosmond): clear config for frames and resource cache without scene 405 // rebuild? 406 self.capture_config = None; 407 self.send(SceneBuilderResult::StopCaptureSequence); 408 } 409 Err(_) => { 410 break; 411 } 412 } 413 414 if let Some(ref hooks) = self.hooks { 415 hooks.poke(); 416 } 417 418 tracy_end_frame!("scene_builder_thread"); 419 } 420 421 if let Some(ref hooks) = self.hooks { 422 hooks.deregister(); 423 } 424 } 425 426 #[cfg(feature = "capture")] 427 fn save_scene(&mut self, config: CaptureConfig) { 428 for (id, doc) in &self.documents { 429 let interners_name = format!("interners-{}-{}", id.namespace_id.0, id.id); 430 config.serialize_for_scene(&doc.interners, interners_name); 431 432 let scene_spatial_tree_name = format!("scene-spatial-tree-{}-{}", id.namespace_id.0, id.id); 433 config.serialize_for_scene(&doc.spatial_tree, scene_spatial_tree_name); 434 435 use crate::render_api::CaptureBits; 436 if config.bits.contains(CaptureBits::SCENE) { 437 let file_name = format!("scene-{}-{}", id.namespace_id.0, id.id); 438 config.serialize_for_scene(&doc.scene, file_name); 439 } 440 } 441 } 442 443 #[cfg(feature = "replay")] 444 fn load_scenes(&mut self, scenes: Vec<LoadScene>) { 445 for mut item in scenes { 446 self.config = item.config; 447 448 let mut built_scene = None; 449 let mut interner_updates = None; 450 let mut spatial_tree_updates = None; 451 452 if item.scene.has_root_pipeline() { 453 built_scene = Some(SceneBuilder::build( 454 &item.scene, 455 None, 456 item.fonts, 457 &item.view, 458 &self.config, 459 &mut item.interners, 460 &mut item.spatial_tree, 461 &mut self.recycler, 462 &SceneStats::empty(), 463 self.debug_flags, 464 )); 465 466 interner_updates = Some( 467 item.interners.end_frame_and_get_pending_updates() 468 ); 469 470 spatial_tree_updates = Some( 471 item.spatial_tree.end_frame_and_get_pending_updates() 472 ); 473 } 474 475 self.documents.insert( 476 item.document_id, 477 Document { 478 scene: item.scene, 479 interners: item.interners, 480 stats: SceneStats::empty(), 481 view: item.view.clone(), 482 spatial_tree: item.spatial_tree, 483 }, 484 ); 485 486 let txns = vec![Box::new(BuiltTransaction { 487 document_id: item.document_id, 488 render_frame: item.build_frame, 489 tracked: false, 490 present: true, 491 invalidate_rendered_frame: false, 492 built_scene, 493 view: item.view, 494 resource_updates: Vec::new(), 495 rasterized_blobs: Vec::new(), 496 blob_rasterizer: None, 497 frame_ops: Vec::new(), 498 removed_pipelines: Vec::new(), 499 notifications: Vec::new(), 500 interner_updates, 501 spatial_tree_updates, 502 profile: TransactionProfile::new(), 503 frame_stats: FullFrameStats::default(), 504 offscreen_scenes: Vec::new(), 505 })]; 506 507 self.forward_built_transactions(txns); 508 } 509 } 510 511 #[cfg(feature = "capture")] 512 fn save_capture_sequence( 513 &mut self, 514 ) { 515 if let Some(ref mut config) = self.capture_config { 516 config.prepare_scene(); 517 for (id, doc) in &self.documents { 518 let interners_name = format!("interners-{}-{}", id.namespace_id.0, id.id); 519 config.serialize_for_scene(&doc.interners, interners_name); 520 521 use crate::render_api::CaptureBits; 522 if config.bits.contains(CaptureBits::SCENE) { 523 let file_name = format!("scene-{}-{}", id.namespace_id.0, id.id); 524 config.serialize_for_scene(&doc.scene, file_name); 525 } 526 } 527 } 528 } 529 530 #[cfg(feature = "capture")] 531 fn start_capture_sequence( 532 &mut self, 533 config: CaptureConfig, 534 ) { 535 self.capture_config = Some(config); 536 self.save_capture_sequence(); 537 } 538 539 /// Do the bulk of the work of the scene builder thread. 540 fn process_transaction(&mut self, mut txn: TransactionMsg) -> Box<BuiltTransaction> { 541 profile_scope!("process_transaction"); 542 543 if let Some(ref hooks) = self.hooks { 544 hooks.pre_scene_build(); 545 } 546 547 let doc = self.documents.get_mut(&txn.document_id).unwrap(); 548 let scene = &mut doc.scene; 549 550 let mut profile = txn.profile.take(); 551 552 let scene_build_start = zeitstempel::now(); 553 let mut removed_pipelines = Vec::new(); 554 let mut rebuild_scene = false; 555 let mut frame_stats = FullFrameStats::default(); 556 let mut offscreen_scenes = Vec::new(); 557 558 for message in txn.scene_ops.drain(..) { 559 match message { 560 SceneMsg::UpdateEpoch(pipeline_id, epoch) => { 561 scene.update_epoch(pipeline_id, epoch); 562 } 563 SceneMsg::SetQualitySettings { settings } => { 564 doc.view.quality_settings = settings; 565 } 566 SceneMsg::SetDocumentView { device_rect } => { 567 doc.view.device_rect = device_rect; 568 } 569 SceneMsg::SetDisplayList { 570 epoch, 571 pipeline_id, 572 display_list, 573 } => { 574 let (builder_start_time_ns, builder_end_time_ns, send_time_ns) = 575 display_list.times(); 576 let content_send_time = profiler::ns_to_ms(zeitstempel::now() - send_time_ns); 577 let dl_build_time = profiler::ns_to_ms(builder_end_time_ns - builder_start_time_ns); 578 profile.set(profiler::CONTENT_SEND_TIME, content_send_time); 579 profile.set(profiler::DISPLAY_LIST_BUILD_TIME, dl_build_time); 580 profile.set(profiler::DISPLAY_LIST_MEM, profiler::bytes_to_mb(display_list.size_in_bytes())); 581 582 let (gecko_display_list_time, full_display_list) = display_list.gecko_display_list_stats(); 583 frame_stats.full_display_list = full_display_list; 584 frame_stats.gecko_display_list_time = gecko_display_list_time; 585 frame_stats.wr_display_list_time += dl_build_time; 586 587 if self.removed_pipelines.contains(&pipeline_id) { 588 continue; 589 } 590 591 // Note: We could further reduce the amount of unnecessary scene 592 // building by keeping track of which pipelines are used by the 593 // scene (bug 1490751). 594 rebuild_scene = true; 595 596 scene.set_display_list( 597 pipeline_id, 598 epoch, 599 display_list, 600 ); 601 } 602 SceneMsg::RenderOffscreen(pipeline_id) => { 603 let mut interners = Interners::default(); 604 let mut spatial_tree = SceneSpatialTree::new(); 605 let built = SceneBuilder::build( 606 &scene, 607 Some(pipeline_id), 608 self.fonts.clone(), 609 &doc.view, 610 &self.config, 611 &mut interners, 612 &mut spatial_tree, 613 &mut self.recycler, 614 &doc.stats, 615 self.debug_flags, 616 ); 617 let interner_updates = interners.end_frame_and_get_pending_updates(); 618 let spatial_tree_updates = spatial_tree.end_frame_and_get_pending_updates(); 619 offscreen_scenes.push(OffscreenBuiltScene { 620 scene: built, 621 interner_updates, 622 spatial_tree_updates, 623 }); 624 } 625 SceneMsg::SetRootPipeline(pipeline_id) => { 626 if scene.root_pipeline_id != Some(pipeline_id) { 627 rebuild_scene = true; 628 scene.set_root_pipeline_id(pipeline_id); 629 } 630 } 631 SceneMsg::RemovePipeline(pipeline_id) => { 632 scene.remove_pipeline(pipeline_id); 633 self.removed_pipelines.insert(pipeline_id); 634 removed_pipelines.push((pipeline_id, txn.document_id)); 635 } 636 } 637 } 638 639 self.removed_pipelines.clear(); 640 641 let mut built_scene = None; 642 let mut interner_updates = None; 643 let mut spatial_tree_updates = None; 644 645 if scene.has_root_pipeline() && rebuild_scene { 646 647 let built = SceneBuilder::build( 648 &scene, 649 None, 650 self.fonts.clone(), 651 &doc.view, 652 &self.config, 653 &mut doc.interners, 654 &mut doc.spatial_tree, 655 &mut self.recycler, 656 &doc.stats, 657 self.debug_flags, 658 ); 659 660 // Update the allocation stats for next scene 661 doc.stats = built.get_stats(); 662 663 // Retrieve the list of updates from the clip interner. 664 interner_updates = Some( 665 doc.interners.end_frame_and_get_pending_updates() 666 ); 667 668 spatial_tree_updates = Some( 669 doc.spatial_tree.end_frame_and_get_pending_updates() 670 ); 671 672 built_scene = Some(built); 673 } 674 675 let scene_build_time_ms = 676 profiler::ns_to_ms(zeitstempel::now() - scene_build_start); 677 profile.set(profiler::SCENE_BUILD_TIME, scene_build_time_ms); 678 679 frame_stats.scene_build_time += scene_build_time_ms; 680 681 if !txn.blob_requests.is_empty() { 682 profile.start_time(profiler::BLOB_RASTERIZATION_TIME); 683 684 let is_low_priority = false; 685 rasterize_blobs(&mut txn, is_low_priority, &mut self.tile_pool); 686 687 profile.end_time(profiler::BLOB_RASTERIZATION_TIME); 688 Telemetry::record_rasterize_blobs_time(Duration::from_micros((profile.get(profiler::BLOB_RASTERIZATION_TIME).unwrap() * 1000.00) as u64)); 689 } 690 691 drain_filter( 692 &mut txn.notifications, 693 |n| { n.when() == Checkpoint::SceneBuilt }, 694 |n| { n.notify(); }, 695 ); 696 697 if self.simulate_slow_ms > 0 { 698 thread::sleep(Duration::from_millis(self.simulate_slow_ms as u64)); 699 } 700 701 Box::new(BuiltTransaction { 702 document_id: txn.document_id, 703 render_frame: txn.generate_frame.as_bool(), 704 present: txn.generate_frame.present(), 705 tracked: txn.generate_frame.tracked(), 706 invalidate_rendered_frame: txn.invalidate_rendered_frame, 707 built_scene, 708 offscreen_scenes, 709 view: doc.view, 710 rasterized_blobs: txn.rasterized_blobs, 711 resource_updates: txn.resource_updates, 712 blob_rasterizer: txn.blob_rasterizer, 713 frame_ops: txn.frame_ops, 714 removed_pipelines, 715 notifications: txn.notifications, 716 interner_updates, 717 spatial_tree_updates, 718 profile, 719 frame_stats, 720 }) 721 } 722 723 /// Send the results of process_transaction back to the render backend. 724 fn forward_built_transactions(&mut self, txns: Vec<Box<BuiltTransaction>>) { 725 let (pipeline_info, result_tx, result_rx) = match self.hooks { 726 Some(ref hooks) => { 727 if txns.iter().any(|txn| txn.built_scene.is_some()) { 728 let info = PipelineInfo { 729 epochs: txns.iter() 730 .filter(|txn| txn.built_scene.is_some()) 731 .map(|txn| { 732 txn.built_scene.as_ref().unwrap() 733 .pipeline_epochs.iter() 734 .zip(iter::repeat(txn.document_id)) 735 .map(|((&pipeline_id, &epoch), document_id)| ((pipeline_id, document_id), epoch)) 736 }).flatten().collect(), 737 removed_pipelines: txns.iter() 738 .map(|txn| txn.removed_pipelines.clone()) 739 .flatten().collect(), 740 }; 741 742 let (tx, rx) = single_msg_channel(); 743 let txn = txns.iter().find(|txn| txn.built_scene.is_some()).unwrap(); 744 Telemetry::record_scenebuild_time(Duration::from_millis(txn.profile.get(profiler::SCENE_BUILD_TIME).unwrap() as u64)); 745 hooks.pre_scene_swap(); 746 747 (Some(info), Some(tx), Some(rx)) 748 } else { 749 (None, None, None) 750 } 751 } 752 _ => (None, None, None) 753 }; 754 755 let timer_id = Telemetry::start_sceneswap_time(); 756 let document_ids = txns.iter().map(|txn| txn.document_id).collect(); 757 let have_resources_updates : Vec<DocumentId> = if pipeline_info.is_none() { 758 txns.iter() 759 .filter(|txn| !txn.resource_updates.is_empty() || txn.invalidate_rendered_frame) 760 .map(|txn| txn.document_id) 761 .collect() 762 } else { 763 Vec::new() 764 }; 765 766 // Unless a transaction generates a frame immediately, the compositor should 767 // schedule one whenever appropriate (probably at the next vsync) to present 768 // the changes in the scene. 769 let compositor_should_schedule_a_frame = !txns.iter().any(|txn| { 770 txn.render_frame 771 }); 772 773 #[cfg(feature = "capture")] 774 match self.capture_config { 775 Some(ref config) => self.send(SceneBuilderResult::CapturedTransactions(txns, config.clone(), result_tx)), 776 None => self.send(SceneBuilderResult::Transactions(txns, result_tx)), 777 }; 778 779 #[cfg(not(feature = "capture"))] 780 self.send(SceneBuilderResult::Transactions(txns, result_tx)); 781 782 if let Some(pipeline_info) = pipeline_info { 783 // Block until the swap is done, then invoke the hook. 784 let swap_result = result_rx.unwrap().recv(); 785 Telemetry::stop_and_accumulate_sceneswap_time(timer_id); 786 self.hooks.as_ref().unwrap().post_scene_swap(&document_ids, 787 pipeline_info, 788 compositor_should_schedule_a_frame); 789 // Once the hook is done, allow the RB thread to resume 790 if let Ok(SceneSwapResult::Complete(resume_tx)) = swap_result { 791 resume_tx.send(()).ok(); 792 } 793 } else { 794 Telemetry::cancel_sceneswap_time(timer_id); 795 if !have_resources_updates.is_empty() { 796 if let Some(ref hooks) = self.hooks { 797 hooks.post_resource_update(&have_resources_updates); 798 } 799 } else if let Some(ref hooks) = self.hooks { 800 hooks.post_empty_scene_build(); 801 } 802 } 803 } 804 805 /// Reports CPU heap memory used by the SceneBuilder. 806 fn report_memory(&mut self) -> MemoryReport { 807 let ops = self.size_of_ops.as_mut().unwrap(); 808 let mut report = MemoryReport::default(); 809 for doc in self.documents.values() { 810 doc.interners.report_memory(ops, &mut report); 811 doc.scene.report_memory(ops, &mut report); 812 } 813 814 report 815 } 816 } 817 818 /// A scene builder thread which executes expensive operations such as blob rasterization 819 /// with a lower priority than the normal scene builder thread. 820 /// 821 /// After rasterizing blobs, the secene building request is forwarded to the normal scene 822 /// builder where the FrameBuilder is generated. 823 pub struct LowPrioritySceneBuilderThread { 824 pub rx: Receiver<SceneBuilderRequest>, 825 pub tx: Sender<SceneBuilderRequest>, 826 pub tile_pool: api::BlobTilePool, 827 } 828 829 impl LowPrioritySceneBuilderThread { 830 pub fn run(&mut self) { 831 loop { 832 match self.rx.recv() { 833 Ok(SceneBuilderRequest::Transactions(mut txns)) => { 834 let txns : Vec<Box<TransactionMsg>> = txns.drain(..) 835 .map(|txn| self.process_transaction(txn)) 836 .collect(); 837 self.tx.send(SceneBuilderRequest::Transactions(txns)).unwrap(); 838 self.tile_pool.cleanup(); 839 } 840 Ok(SceneBuilderRequest::ShutDown(sync)) => { 841 self.tx.send(SceneBuilderRequest::ShutDown(sync)).unwrap(); 842 break; 843 } 844 Ok(other) => { 845 self.tx.send(other).unwrap(); 846 } 847 Err(_) => { 848 break; 849 } 850 } 851 } 852 } 853 854 fn process_transaction(&mut self, mut txn: Box<TransactionMsg>) -> Box<TransactionMsg> { 855 let is_low_priority = true; 856 txn.profile.start_time(profiler::BLOB_RASTERIZATION_TIME); 857 rasterize_blobs(&mut txn, is_low_priority, &mut self.tile_pool); 858 txn.profile.end_time(profiler::BLOB_RASTERIZATION_TIME); 859 Telemetry::record_rasterize_blobs_time(Duration::from_micros((txn.profile.get(profiler::BLOB_RASTERIZATION_TIME).unwrap() * 1000.00) as u64)); 860 txn.blob_requests = Vec::new(); 861 862 txn 863 } 864 }