Skip to main content

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