webrender/
scene_builder_thread.rs

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