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::units::*;
11use crate::render_api::{ApiMsg, FrameMsg, SceneMsg, ResourceUpdate, TransactionMsg, MemoryReport};
12use crate::box_shadow::BoxShadow;
13#[cfg(feature = "capture")]
14use crate::capture::CaptureConfig;
15use crate::frame_builder::FrameBuilderConfig;
16use crate::scene_building::{SceneBuilder, SceneRecycler};
17use crate::clip::{ClipIntern, PolygonIntern};
18use crate::filterdata::FilterDataIntern;
19use glyph_rasterizer::SharedFontResources;
20use crate::intern::{Internable, Interner, UpdateList};
21use crate::internal_types::{FastHashMap, FastHashSet};
22use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
23use crate::prim_store::backdrop::{BackdropCapture, BackdropRender};
24use crate::prim_store::borders::{ImageBorder, NormalBorderPrim};
25use crate::prim_store::gradient::{LinearGradient, RadialGradient, ConicGradient};
26use crate::prim_store::image::{Image, YuvImage};
27use crate::prim_store::line_dec::LineDecoration;
28use crate::prim_store::picture::Picture;
29use crate::prim_store::text_run::TextRun;
30use crate::profiler::{self, TransactionProfile};
31use crate::render_backend::SceneView;
32use crate::renderer::{FullFrameStats, PipelineInfo};
33use crate::scene::{BuiltScene, Scene, SceneStats};
34use crate::spatial_tree::{SceneSpatialTree, SpatialTreeUpdates};
35use crate::telemetry::Telemetry;
36use crate::SceneBuilderHooks;
37use std::iter;
38use crate::util::drain_filter;
39use std::thread;
40use std::time::Duration;
41
42fn 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.
58pub 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
79pub struct OffscreenBuiltScene {
80    pub scene: BuiltScene,
81    pub interner_updates: InternerUpdates,
82    pub spatial_tree_updates: SpatialTreeUpdates,
83}
84
85#[cfg(feature = "replay")]
86pub 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.
98pub 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.
126pub 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.
153pub enum SceneSwapResult {
154    Complete(Sender<()>),
155    Aborted,
156}
157
158macro_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
212crate::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.
218struct Document {
219    scene: Scene,
220    interners: Interners,
221    stats: SceneStats,
222    view: SceneView,
223    spatial_tree: SceneSpatialTree,
224}
225
226impl 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
241pub 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
258pub struct SceneBuilderThreadChannels {
259    rx: Receiver<SceneBuilderRequest>,
260    tx: Sender<ApiMsg>,
261}
262
263impl 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
278impl 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.
823pub struct LowPrioritySceneBuilderThread {
824    pub rx: Receiver<SceneBuilderRequest>,
825    pub tx: Sender<SceneBuilderRequest>,
826    pub tile_pool: api::BlobTilePool,
827}
828
829impl 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}