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