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