Skip to main content

webrender/
render_backend.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5//! The high-level module responsible for managing the pipeline and preparing
6//! commands to be issued by the `Renderer`.
7//!
8//! See the comment at the top of the `renderer` module for a description of
9//! how these two pieces interact.
10
11use api::{DebugFlags, Parameter, BoolParameter, PrimitiveFlags, MinimapData};
12use api::{DocumentId, ExternalScrollId, HitTestResult};
13use api::{IdNamespace, PipelineId, RenderNotifier, SampledScrollOffset};
14use api::{NotificationRequest, Checkpoint, QualitySettings};
15use api::{FramePublishId, RenderReasons};
16use api::units::*;
17use api::channel::{single_msg_channel, Sender, Receiver};
18use crate::bump_allocator::ChunkPool;
19use crate::AsyncPropertySampler;
20use crate::box_shadow::BoxShadow;
21use crate::prim_store::rectangle::RectanglePrim;
22#[cfg(any(feature = "capture", feature = "replay"))]
23use crate::render_api::CaptureBits;
24#[cfg(feature = "replay")]
25use crate::render_api::CapturedDocument;
26use crate::render_api::{MemoryReport, TransactionMsg, ResourceUpdate, ApiMsg, FrameMsg, ClearCache, DebugCommand};
27use crate::clip::{ClipIntern, PolygonIntern, ClipStoreScratchBuffer};
28use crate::filterdata::FilterDataIntern;
29#[cfg(any(feature = "capture", feature = "replay"))]
30use crate::capture::CaptureConfig;
31use crate::composite::{CompositorKind, CompositeDescriptor};
32use crate::frame_builder::{FrameBuilder, FrameBuilderConfig, FrameScratchBuffer};
33use glyph_rasterizer::FontInstance;
34use crate::hit_test::{HitTest, HitTester, SharedHitTester};
35use crate::intern::DataStore;
36#[cfg(any(feature = "capture", feature = "replay"))]
37use crate::internal_types::DebugOutput;
38use crate::internal_types::{FastHashMap, FrameId, FrameStamp, RenderedDocument, ResultMsg};
39use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
40use crate::picture::{PictureScratchBuffer, SurfaceInfo, RasterConfig};
41use crate::tile_cache::{SliceId, TileCacheInstance, TileCacheParams};
42use crate::picture::PictureInstance;
43use crate::prim_store::{PrimitiveScratchBuffer, PrimitiveInstance};
44use crate::prim_store::{PrimitiveKind, PrimTemplateCommonData};
45use crate::prim_store::interned::*;
46use crate::profiler::{self, TransactionProfile};
47use crate::render_task_graph::RenderTaskGraphBuilder;
48use crate::renderer::{FullFrameStats, PipelineInfo};
49use crate::resource_cache::ResourceCache;
50#[cfg(feature = "replay")]
51use crate::resource_cache::PlainCacheOwn;
52#[cfg(feature = "replay")]
53use crate::resource_cache::PlainResources;
54#[cfg(feature = "replay")]
55use crate::scene::Scene;
56use crate::scene::{BuiltScene, SceneProperties};
57use crate::scene_builder_thread::*;
58use crate::spatial_tree::SpatialTree;
59#[cfg(feature = "replay")]
60use crate::spatial_tree::SceneSpatialTree;
61use crate::telemetry::Telemetry;
62#[cfg(feature = "capture")]
63use serde::Serialize;
64#[cfg(feature = "replay")]
65use serde::Deserialize;
66#[cfg(feature = "replay")]
67use std::collections::hash_map::Entry::{Occupied, Vacant};
68use std::sync::Arc;
69use std::sync::atomic::{AtomicUsize, Ordering};
70use std::{mem, u32};
71#[cfg(feature = "capture")]
72use std::path::PathBuf;
73#[cfg(feature = "replay")]
74use crate::frame_builder::Frame;
75use core::time::Duration;
76use crate::util::{Recycler, VecHelper, drain_filter};
77#[cfg(feature = "debugger")]
78use crate::debugger::DebugQueryKind;
79
80#[cfg_attr(feature = "capture", derive(Serialize))]
81#[cfg_attr(feature = "replay", derive(Deserialize))]
82#[derive(Copy, Clone)]
83pub struct DocumentView {
84    scene: SceneView,
85}
86
87/// Some rendering parameters applying at the scene level.
88#[cfg_attr(feature = "capture", derive(Serialize))]
89#[cfg_attr(feature = "replay", derive(Deserialize))]
90#[derive(Copy, Clone)]
91pub struct SceneView {
92    pub device_rect: DeviceIntRect,
93    pub quality_settings: QualitySettings,
94}
95
96enum RenderBackendStatus {
97    Continue,
98    StopRenderBackend,
99    ShutDown(Option<Sender<()>>),
100}
101
102macro_rules! declare_data_stores {
103    ( $( $name:ident : $ty:ty, )+ ) => {
104        /// A collection of resources that are shared by clips, primitives
105        /// between display lists.
106        #[cfg_attr(feature = "capture", derive(Serialize))]
107        #[cfg_attr(feature = "replay", derive(Deserialize))]
108        #[derive(Default)]
109        pub struct DataStores {
110            $(
111                pub $name: DataStore<$ty>,
112            )+
113        }
114
115        impl DataStores {
116            /// Reports CPU heap usage.
117            fn report_memory(&self, ops: &mut MallocSizeOfOps, r: &mut MemoryReport) {
118                $(
119                    r.interning.data_stores.$name += self.$name.size_of(ops);
120                )+
121            }
122
123            fn apply_updates(
124                &mut self,
125                updates: InternerUpdates,
126                profile: &mut TransactionProfile,
127            ) {
128                $(
129                    self.$name.apply_updates(
130                        updates.$name,
131                        profile,
132                    );
133                )+
134            }
135        }
136    }
137}
138
139crate::enumerate_interners!(declare_data_stores);
140
141impl DataStores {
142    /// Returns the local rect for a primitive. For most primitives, this is
143    /// the device-snapped local rect carried on the per-draw header. For
144    /// pictures, the rect is reconstructed from the picture's raster surface
145    /// since it's only known during frame building.
146    pub fn get_local_prim_rect(
147        &self,
148        prim_instance: &PrimitiveInstance,
149        snapped_local_rect: LayoutRect,
150        pictures: &[PictureInstance],
151        surfaces: &[SurfaceInfo],
152    ) -> LayoutRect {
153        match prim_instance.kind {
154            PrimitiveKind::Picture { pic_index, .. } => {
155                let pic = &pictures[pic_index.0];
156
157                match pic.raster_config {
158                    Some(RasterConfig { surface_index, ref composite_mode, .. }) => {
159                        let surface = &surfaces[surface_index.0];
160
161                        composite_mode.get_rect(surface, None)
162                    }
163                    None => {
164                        panic!("bug: get_local_prim_rect should not be called for pass-through pictures");
165                    }
166                }
167            }
168            _ => snapped_local_rect,
169        }
170    }
171
172    /// Returns the local coverage (space occupied) for a primitive. For most
173    /// primitives, this is the device-snapped local rect carried on the
174    /// per-draw header. For pictures, the coverage is reconstructed from the
175    /// picture's raster surface since it's only known during frame building.
176    pub fn get_local_prim_coverage_rect(
177        &self,
178        prim_instance: &PrimitiveInstance,
179        snapped_local_rect: LayoutRect,
180        pictures: &[PictureInstance],
181        surfaces: &[SurfaceInfo],
182    ) -> LayoutRect {
183        match prim_instance.kind {
184            PrimitiveKind::Picture { pic_index, .. } => {
185                let pic = &pictures[pic_index.0];
186
187                match pic.raster_config {
188                    Some(RasterConfig { surface_index, ref composite_mode, .. }) => {
189                        let surface = &surfaces[surface_index.0];
190
191                        composite_mode.get_coverage(surface, None)
192                    }
193                    None => {
194                        panic!("bug: get_local_prim_coverage_rect should not be called for pass-through pictures");
195                    }
196                }
197            }
198            _ => snapped_local_rect,
199        }
200    }
201
202    /// Returns true if this primitive has anti-aliasing enabled.
203    pub fn prim_has_anti_aliasing(
204        &self,
205        prim_instance: &PrimitiveInstance,
206    ) -> bool {
207        match prim_instance.kind {
208            PrimitiveKind::Picture { .. } => {
209                false
210            }
211            _ => {
212                self.as_common_data(prim_instance).flags.contains(PrimitiveFlags::ANTIALISED)
213            }
214        }
215    }
216
217    pub fn as_common_data(
218        &self,
219        prim_inst: &PrimitiveInstance
220    ) -> &PrimTemplateCommonData {
221        match prim_inst.kind {
222            PrimitiveKind::Rectangle { data_handle, .. } => {
223                let prim_data = &self.prim[data_handle];
224                &prim_data.common
225            }
226            PrimitiveKind::Image { data_handle, .. } => {
227                let prim_data = &self.image[data_handle];
228                &prim_data.common
229            }
230            PrimitiveKind::ImageBorder { data_handle, .. } => {
231                let prim_data = &self.image_border[data_handle];
232                &prim_data.common
233            }
234            PrimitiveKind::LineDecoration { data_handle, .. } => {
235                let prim_data = &self.line_decoration[data_handle];
236                &prim_data.common
237            }
238            PrimitiveKind::LinearGradient { data_handle, .. } => {
239                let prim_data = &self.linear_grad[data_handle];
240                &prim_data.common
241            }
242            PrimitiveKind::NormalBorder { data_handle, .. } => {
243                let prim_data = &self.normal_border[data_handle];
244                &prim_data.common
245            }
246            PrimitiveKind::Picture { .. } => {
247                panic!("BUG: picture prims don't have common data!");
248            }
249            PrimitiveKind::RadialGradient { data_handle, .. } => {
250                let prim_data = &self.radial_grad[data_handle];
251                &prim_data.common
252            }
253            PrimitiveKind::ConicGradient { data_handle, .. } => {
254                let prim_data = &self.conic_grad[data_handle];
255                &prim_data.common
256            }
257            PrimitiveKind::TextRun { data_handle, .. }  => {
258                let prim_data = &self.text_run[data_handle];
259                &prim_data.common
260            }
261            PrimitiveKind::YuvImage { data_handle, .. } => {
262                let prim_data = &self.yuv_image[data_handle];
263                &prim_data.common
264            }
265            PrimitiveKind::BackdropCapture { data_handle, .. } => {
266                let prim_data = &self.backdrop_capture[data_handle];
267                &prim_data.common
268            }
269            PrimitiveKind::BackdropRender { data_handle, .. } => {
270                let prim_data = &self.backdrop_render[data_handle];
271                &prim_data.common
272            }
273            PrimitiveKind::BoxShadow { data_handle, .. } => {
274                let prim_data = &self.box_shadow[data_handle];
275                &prim_data.common
276            }
277        }
278    }
279}
280
281#[derive(Default)]
282pub struct ScratchBuffer {
283    pub primitive: PrimitiveScratchBuffer,
284    pub picture: PictureScratchBuffer,
285    pub frame: FrameScratchBuffer,
286    pub clip_store: ClipStoreScratchBuffer,
287}
288
289impl ScratchBuffer {
290    pub fn begin_frame(&mut self) {
291        self.primitive.begin_frame();
292        self.picture.begin_frame();
293        self.frame.begin_frame();
294    }
295
296    pub fn end_frame(&mut self) {
297        self.primitive.end_frame();
298    }
299
300    pub fn recycle(&mut self, recycler: &mut Recycler) {
301        self.primitive.recycle(recycler);
302        self.picture.recycle(recycler);
303    }
304
305    pub fn memory_pressure(&mut self) {
306        // TODO: causes browser chrome test crashes on windows.
307        //self.primitive = Default::default();
308        self.picture = Default::default();
309        self.frame = Default::default();
310        self.clip_store = Default::default();
311    }
312}
313
314struct Document {
315    /// The id of this document
316    id: DocumentId,
317
318    /// Temporary list of removed pipelines received from the scene builder
319    /// thread and forwarded to the renderer.
320    removed_pipelines: Vec<(PipelineId, DocumentId)>,
321
322    view: DocumentView,
323
324    /// The id and time of the current frame.
325    stamp: FrameStamp,
326
327    /// The latest built scene, usable to build frames.
328    /// received from the scene builder thread.
329    scene: BuiltScene,
330
331    /// The builder object that prodces frames, kept around to preserve some retained state.
332    frame_builder: FrameBuilder,
333
334    /// Allows graphs of render tasks to be created, and then built into an immutable graph output.
335    rg_builder: RenderTaskGraphBuilder,
336
337    /// A data structure to allow hit testing against rendered frames. This is updated
338    /// every time we produce a fully rendered frame.
339    hit_tester: Option<Arc<HitTester>>,
340    /// To avoid synchronous messaging we update a shared hit-tester that other threads
341    /// can query.
342    shared_hit_tester: Arc<SharedHitTester>,
343
344    /// Properties that are resolved during frame building and can be changed at any time
345    /// without requiring the scene to be re-built.
346    dynamic_properties: SceneProperties,
347
348    /// Track whether the last built frame is up to date or if it will need to be re-built
349    /// before rendering again.
350    frame_is_valid: bool,
351    hit_tester_is_valid: bool,
352    rendered_frame_is_valid: bool,
353    /// We track this information to be able to display debugging information from the
354    /// renderer.
355    has_built_scene: bool,
356
357    data_stores: DataStores,
358
359    /// Retained frame-building version of the spatial tree
360    spatial_tree: SpatialTree,
361
362    minimap_data: FastHashMap<ExternalScrollId, MinimapData>,
363
364    /// Contains various vecs of data that is used only during frame building,
365    /// where we want to recycle the memory each new display list, to avoid constantly
366    /// re-allocating and moving memory around.
367    scratch: ScratchBuffer,
368
369    #[cfg(feature = "replay")]
370    loaded_scene: Scene,
371
372    /// Tracks the state of the picture cache tiles that were composited on the previous frame.
373    prev_composite_descriptor: CompositeDescriptor,
374
375    /// Tracks if we need to invalidate dirty rects for this document, due to the picture
376    /// cache slice configuration having changed when a new scene is swapped in.
377    dirty_rects_are_valid: bool,
378
379    profile: TransactionProfile,
380    frame_stats: Option<FullFrameStats>,
381}
382
383impl Document {
384    pub fn new(
385        id: DocumentId,
386        size: DeviceIntSize,
387    ) -> Self {
388        Document {
389            id,
390            removed_pipelines: Vec::new(),
391            view: DocumentView {
392                scene: SceneView {
393                    device_rect: size.into(),
394                    quality_settings: QualitySettings::default(),
395                },
396            },
397            stamp: FrameStamp::first(id),
398            scene: BuiltScene::empty(),
399            frame_builder: FrameBuilder::new(),
400            hit_tester: None,
401            shared_hit_tester: Arc::new(SharedHitTester::new()),
402            dynamic_properties: SceneProperties::new(),
403            frame_is_valid: false,
404            hit_tester_is_valid: false,
405            rendered_frame_is_valid: false,
406            has_built_scene: false,
407            data_stores: DataStores::default(),
408            spatial_tree: SpatialTree::new(),
409            minimap_data: FastHashMap::default(),
410            scratch: ScratchBuffer::default(),
411            #[cfg(feature = "replay")]
412            loaded_scene: Scene::new(),
413            prev_composite_descriptor: CompositeDescriptor::empty(),
414            dirty_rects_are_valid: true,
415            profile: TransactionProfile::new(),
416            rg_builder: RenderTaskGraphBuilder::new(),
417            frame_stats: None,
418        }
419    }
420
421    fn can_render(&self) -> bool {
422        self.scene.has_root_pipeline
423    }
424
425    fn has_pixels(&self) -> bool {
426        !self.view.scene.device_rect.is_empty()
427    }
428
429    fn process_frame_msg(
430        &mut self,
431        message: FrameMsg,
432    ) -> DocumentOps {
433        match message {
434            FrameMsg::UpdateEpoch(pipeline_id, epoch) => {
435                self.scene.pipeline_epochs.insert(pipeline_id, epoch);
436            }
437            FrameMsg::HitTest(point, tx) => {
438                if !self.hit_tester_is_valid {
439                    self.rebuild_hit_tester();
440                }
441
442                let result = match self.hit_tester {
443                    Some(ref hit_tester) => {
444                        hit_tester.hit_test(HitTest::new(point))
445                    }
446                    None => HitTestResult { items: Vec::new() },
447                };
448
449                tx.send(result).unwrap();
450            }
451            FrameMsg::RequestHitTester(tx) => {
452                tx.send(self.shared_hit_tester.clone()).unwrap();
453            }
454            FrameMsg::SetScrollOffsets(id, offset) => {
455                profile_scope!("SetScrollOffset");
456
457                if self.set_scroll_offsets(id, offset) {
458                    self.hit_tester_is_valid = false;
459                    self.frame_is_valid = false;
460                }
461
462                return DocumentOps {
463                    scroll: true,
464                    ..DocumentOps::nop()
465                };
466            }
467            FrameMsg::ResetDynamicProperties => {
468                self.dynamic_properties.reset_properties();
469            }
470            FrameMsg::AppendDynamicProperties(property_bindings) => {
471                self.dynamic_properties.add_properties(property_bindings);
472            }
473            FrameMsg::AppendDynamicTransformProperties(property_bindings) => {
474                self.dynamic_properties.add_transforms(property_bindings);
475            }
476            FrameMsg::SetIsTransformAsyncZooming(is_zooming, animation_id) => {
477                if let Some(node_index) = self.spatial_tree.find_spatial_node_by_anim_id(animation_id) {
478                    let node = self.spatial_tree.get_spatial_node_mut(node_index);
479
480                    if node.is_async_zooming != is_zooming {
481                        node.is_async_zooming = is_zooming;
482                        self.frame_is_valid = false;
483                    }
484                }
485            }
486            FrameMsg::SetMinimapData(id, minimap_data) => {
487              self.minimap_data.insert(id, minimap_data);
488            }
489        }
490
491        DocumentOps::nop()
492    }
493
494    fn build_frame(
495        &mut self,
496        resource_cache: &mut ResourceCache,
497        debug_flags: DebugFlags,
498        tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
499        frame_stats: Option<FullFrameStats>,
500        present: bool,
501        render_reasons: RenderReasons,
502        chunk_pool: Arc<ChunkPool>,
503    ) -> RenderedDocument {
504        let frame_build_start_time = zeitstempel::now();
505
506        // Advance to the next frame.
507        self.stamp.advance();
508
509        assert!(self.stamp.frame_id() != FrameId::INVALID,
510                "First frame increment must happen before build_frame()");
511
512        let frame = {
513            let frame = self.frame_builder.build(
514                &mut self.scene,
515                present,
516                resource_cache,
517                &mut self.rg_builder,
518                self.stamp,
519                self.view.scene.device_rect.min,
520                &self.dynamic_properties,
521                &mut self.data_stores,
522                &mut self.scratch,
523                debug_flags,
524                tile_caches,
525                &mut self.spatial_tree,
526                self.dirty_rects_are_valid,
527                &mut self.profile,
528                // Consume the minimap data. If APZ wants a minimap rendered
529                // on the next frame, it will add new entries to the minimap
530                // data during sampling.
531                mem::take(&mut self.minimap_data),
532                chunk_pool,
533            );
534
535            frame
536        };
537
538        self.frame_is_valid = true;
539        self.dirty_rects_are_valid = true;
540
541        self.has_built_scene = false;
542
543        let frame_build_time_ms =
544            profiler::ns_to_ms(zeitstempel::now() - frame_build_start_time);
545        self.profile.set(profiler::FRAME_BUILDING_TIME, frame_build_time_ms);
546        self.profile.start_time(profiler::FRAME_SEND_TIME);
547
548        let frame_stats = frame_stats.map(|mut stats| {
549            stats.frame_build_time += frame_build_time_ms;
550            stats
551        });
552
553        RenderedDocument {
554            frame,
555            profile: self.profile.take_and_reset(),
556            frame_stats: frame_stats,
557            render_reasons,
558        }
559    }
560
561    /// Build a frame without changing the state of the current scene.
562    ///
563    /// This is useful to render arbitrary content into to images in
564    /// the resource cache for later use without affecting what is
565    /// currently being displayed.
566    fn process_offscreen_scene(
567        &mut self,
568        mut txn: OffscreenBuiltScene,
569        resource_cache: &mut ResourceCache,
570        chunk_pool: Arc<ChunkPool>,
571        debug_flags: DebugFlags,
572    ) -> RenderedDocument {
573        let mut profile = TransactionProfile::new();
574        self.stamp.advance();
575
576        let mut data_stores = DataStores::default();
577        data_stores.apply_updates(txn.interner_updates, &mut profile);
578
579        let mut spatial_tree = SpatialTree::new();
580        spatial_tree.apply_updates(txn.spatial_tree_updates);
581
582        let mut tile_caches = FastHashMap::default();
583        self.update_tile_caches_for_new_scene(
584            mem::take(&mut txn.scene.tile_cache_config.tile_caches),
585            &mut tile_caches,
586            resource_cache,
587        );
588
589        let present = false;
590
591        let frame = self.frame_builder.build(
592            &mut txn.scene,
593            present,
594            resource_cache,
595            &mut self.rg_builder,
596            self.stamp, // TODO(nical)
597            self.view.scene.device_rect.min,
598            &self.dynamic_properties,
599            &mut data_stores,
600            &mut self.scratch,
601            debug_flags,
602            &mut tile_caches,
603            &mut spatial_tree,
604            self.dirty_rects_are_valid,
605            &mut profile,
606            // Consume the minimap data. If APZ wants a minimap rendered
607            // on the next frame, it will add new entries to the minimap
608            // data during sampling.
609            mem::take(&mut self.minimap_data),
610            chunk_pool,
611        );
612
613        RenderedDocument {
614            frame,
615            profile,
616            render_reasons: RenderReasons::SNAPSHOT,
617            frame_stats: None,
618        }
619    }
620
621
622    fn rebuild_hit_tester(&mut self) {
623        self.spatial_tree.update_tree(&self.dynamic_properties);
624
625        let hit_tester = Arc::new(self.scene.create_hit_tester(&self.spatial_tree));
626        self.hit_tester = Some(Arc::clone(&hit_tester));
627        self.shared_hit_tester.update(hit_tester);
628        self.hit_tester_is_valid = true;
629    }
630
631    pub fn updated_pipeline_info(&mut self) -> PipelineInfo {
632        let removed_pipelines = self.removed_pipelines.take_and_preallocate();
633        PipelineInfo {
634            epochs: self.scene.pipeline_epochs.iter()
635                .map(|(&pipeline_id, &epoch)| ((pipeline_id, self.id), epoch)).collect(),
636            removed_pipelines,
637        }
638    }
639
640    /// Returns true if the node actually changed position or false otherwise.
641    pub fn set_scroll_offsets(
642        &mut self,
643        id: ExternalScrollId,
644        offsets: Vec<SampledScrollOffset>,
645    ) -> bool {
646        self.spatial_tree.set_scroll_offsets(id, offsets)
647    }
648
649    /// Update the state of tile caches when a new scene is being swapped in to
650    /// the render backend. Retain / reuse existing caches if possible, and
651    /// destroy any now unused caches.
652    fn update_tile_caches_for_new_scene(
653        &mut self,
654        mut requested_tile_caches: FastHashMap<SliceId, TileCacheParams>,
655        tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
656        resource_cache: &mut ResourceCache,
657    ) {
658        let mut new_tile_caches = FastHashMap::default();
659        new_tile_caches.reserve(requested_tile_caches.len());
660
661        // Step through the tile caches that are needed for the new scene, and see
662        // if we have an existing cache that can be reused.
663        for (slice_id, params) in requested_tile_caches.drain() {
664            let tile_cache = match tile_caches.remove(&slice_id) {
665                Some(mut existing_tile_cache) => {
666                    // Found an existing cache - update the cache params and reuse it
667                    existing_tile_cache.prepare_for_new_scene(
668                        params,
669                        resource_cache,
670                    );
671                    existing_tile_cache
672                }
673                None => {
674                    // No cache exists so create a new one
675                    Box::new(TileCacheInstance::new(params))
676                }
677            };
678
679            new_tile_caches.insert(slice_id, tile_cache);
680        }
681
682        // Replace current tile cache map, and return what was left over,
683        // which are now unused.
684        let unused_tile_caches = mem::replace(
685            tile_caches,
686            new_tile_caches,
687        );
688
689        if !unused_tile_caches.is_empty() {
690            // If the slice configuration changed, assume we can't rely on the
691            // current dirty rects for next composite
692            self.dirty_rects_are_valid = false;
693
694            // Destroy any native surfaces allocated by these unused caches
695            for (_, tile_cache) in unused_tile_caches {
696                tile_cache.destroy(resource_cache);
697            }
698        }
699    }
700
701    pub fn new_async_scene_ready(
702        &mut self,
703        mut built_scene: BuiltScene,
704        recycler: &mut Recycler,
705        tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
706        resource_cache: &mut ResourceCache,
707    ) {
708        self.frame_is_valid = false;
709        self.hit_tester_is_valid = false;
710
711        self.update_tile_caches_for_new_scene(
712            mem::replace(&mut built_scene.tile_cache_config.tile_caches, FastHashMap::default()),
713            tile_caches,
714            resource_cache,
715        );
716
717
718        let old_scene = std::mem::replace(&mut self.scene, built_scene);
719        old_scene.recycle();
720
721        self.scratch.recycle(recycler);
722    }
723}
724
725struct DocumentOps {
726    scroll: bool,
727}
728
729impl DocumentOps {
730    fn nop() -> Self {
731        DocumentOps {
732            scroll: false,
733        }
734    }
735}
736
737/// The unique id for WR resource identification.
738/// The namespace_id should start from 1.
739static NEXT_NAMESPACE_ID: AtomicUsize = AtomicUsize::new(1);
740
741#[cfg(any(feature = "capture", feature = "replay"))]
742#[cfg_attr(feature = "capture", derive(Serialize))]
743#[cfg_attr(feature = "replay", derive(Deserialize))]
744struct PlainRenderBackend {
745    frame_config: FrameBuilderConfig,
746    documents: FastHashMap<DocumentId, DocumentView>,
747    resource_sequence_id: u32,
748}
749
750/// The render backend is responsible for transforming high level display lists into
751/// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
752///
753/// The render backend operates on its own thread.
754pub struct RenderBackend {
755    api_rx: Receiver<ApiMsg>,
756    result_tx: Sender<ResultMsg>,
757    scene_tx: Sender<SceneBuilderRequest>,
758
759    resource_cache: ResourceCache,
760    chunk_pool: Arc<ChunkPool>,
761
762    frame_config: FrameBuilderConfig,
763    default_compositor_kind: CompositorKind,
764    documents: FastHashMap<DocumentId, Document>,
765
766    notifier: Box<dyn RenderNotifier>,
767    sampler: Option<Box<dyn AsyncPropertySampler + Send>>,
768    size_of_ops: Option<MallocSizeOfOps>,
769    debug_flags: DebugFlags,
770    namespace_alloc_by_client: bool,
771
772    recycler: Recycler,
773
774    #[cfg(feature = "capture")]
775    /// If `Some`, do 'sequence capture' logging, recording updated documents,
776    /// frames, etc. This is set only through messages from the scene builder,
777    /// so all control of sequence capture goes through there.
778    capture_config: Option<CaptureConfig>,
779
780    #[cfg(feature = "replay")]
781    loaded_resource_sequence_id: u32,
782
783    /// A map of tile caches. These are stored in the backend as they are
784    /// persisted between both frame and scenes.
785    tile_caches: FastHashMap<SliceId, Box<TileCacheInstance>>,
786
787    /// The id of the latest PublishDocument
788    frame_publish_id: FramePublishId,
789}
790
791impl RenderBackend {
792    pub fn new(
793        api_rx: Receiver<ApiMsg>,
794        result_tx: Sender<ResultMsg>,
795        scene_tx: Sender<SceneBuilderRequest>,
796        resource_cache: ResourceCache,
797        chunk_pool: Arc<ChunkPool>,
798        notifier: Box<dyn RenderNotifier>,
799        frame_config: FrameBuilderConfig,
800        sampler: Option<Box<dyn AsyncPropertySampler + Send>>,
801        size_of_ops: Option<MallocSizeOfOps>,
802        debug_flags: DebugFlags,
803        namespace_alloc_by_client: bool,
804    ) -> RenderBackend {
805        RenderBackend {
806            api_rx,
807            result_tx,
808            scene_tx,
809            resource_cache,
810            chunk_pool,
811            frame_config,
812            default_compositor_kind : frame_config.compositor_kind,
813            documents: FastHashMap::default(),
814            notifier,
815            sampler,
816            size_of_ops,
817            debug_flags,
818            namespace_alloc_by_client,
819            recycler: Recycler::new(),
820            #[cfg(feature = "capture")]
821            capture_config: None,
822            #[cfg(feature = "replay")]
823            loaded_resource_sequence_id: 0,
824            tile_caches: FastHashMap::default(),
825            frame_publish_id: FramePublishId::first(),
826        }
827    }
828
829    pub fn next_namespace_id() -> IdNamespace {
830        IdNamespace(NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed) as u32)
831    }
832
833    pub fn run(&mut self) {
834        let mut frame_counter: u32 = 0;
835        let mut status = RenderBackendStatus::Continue;
836
837        if let Some(ref sampler) = self.sampler {
838            sampler.register();
839        }
840
841        while let RenderBackendStatus::Continue = status {
842            status = match self.api_rx.recv() {
843                Ok(msg) => {
844                    self.process_api_msg(msg, &mut frame_counter)
845                }
846                Err(..) => { RenderBackendStatus::ShutDown(None) }
847            };
848        }
849
850        if let RenderBackendStatus::StopRenderBackend = status {
851            while let Ok(msg) = self.api_rx.recv() {
852                match msg {
853                    ApiMsg::SceneBuilderResult(SceneBuilderResult::ExternalEvent(evt)) => {
854                        self.notifier.external_event(evt);
855                    }
856                    ApiMsg::SceneBuilderResult(SceneBuilderResult::FlushComplete(tx)) => {
857                        // If somebody's blocked waiting for a flush, how did they
858                        // trigger the RB thread to shut down? This shouldn't happen
859                        // but handle it gracefully anyway.
860                        debug_assert!(false);
861                        tx.send(()).ok();
862                    }
863                    ApiMsg::SceneBuilderResult(SceneBuilderResult::ShutDown(sender)) => {
864                        info!("Recycling stats: {:?}", self.recycler);
865                        status = RenderBackendStatus::ShutDown(sender);
866                        break;
867                   }
868                    _ => {},
869                }
870            }
871        }
872
873        // Ensure we read everything the scene builder is sending us from
874        // inflight messages, otherwise the scene builder might panic.
875        while let Ok(msg) = self.api_rx.try_recv() {
876            match msg {
877                ApiMsg::SceneBuilderResult(SceneBuilderResult::FlushComplete(tx)) => {
878                    // If somebody's blocked waiting for a flush, how did they
879                    // trigger the RB thread to shut down? This shouldn't happen
880                    // but handle it gracefully anyway.
881                    debug_assert!(false);
882                    tx.send(()).ok();
883                }
884                _ => {},
885            }
886        }
887
888        self.documents.clear();
889
890        self.notifier.shut_down();
891
892        if let Some(ref sampler) = self.sampler {
893            sampler.deregister();
894        }
895
896
897        if let RenderBackendStatus::ShutDown(Some(sender)) = status {
898            let _ = sender.send(());
899        }
900    }
901
902    fn process_transaction(
903        &mut self,
904        mut txns: Vec<Box<BuiltTransaction>>,
905        result_tx: Option<Sender<SceneSwapResult>>,
906        frame_counter: &mut u32,
907    ) -> bool {
908        self.maybe_force_nop_documents(
909            frame_counter,
910            |document_id| txns.iter().any(|txn| txn.document_id == document_id));
911
912        let mut built_frame = false;
913        for mut txn in txns.drain(..) {
914           let has_built_scene = txn.built_scene.is_some();
915
916            if let Some(doc) = self.documents.get_mut(&txn.document_id) {
917                doc.removed_pipelines.append(&mut txn.removed_pipelines);
918                doc.view.scene = txn.view;
919                doc.profile.merge(&mut txn.profile);
920
921                doc.frame_stats = if let Some(stats) = &doc.frame_stats {
922                    Some(stats.merge(&txn.frame_stats))
923                } else {
924                    Some(txn.frame_stats)
925                };
926
927                // Before updating the spatial tree, save the most recently sampled
928                // scroll offsets (which include async deltas).
929                let last_sampled_scroll_offsets = if self.sampler.is_some() {
930                    Some(doc.spatial_tree.get_last_sampled_scroll_offsets())
931                } else {
932                    None
933                };
934
935                if let Some(updates) = txn.spatial_tree_updates.take() {
936                    doc.spatial_tree.apply_updates(updates);
937                }
938
939                if let Some(built_scene) = txn.built_scene.take() {
940                    doc.new_async_scene_ready(
941                        built_scene,
942                        &mut self.recycler,
943                        &mut self.tile_caches,
944                        &mut self.resource_cache,
945                    );
946                }
947
948                // If there are any additions or removals of clip modes
949                // during the scene build, apply them to the data store now.
950                // This needs to happen before we build the hit tester.
951                if let Some(updates) = txn.interner_updates.take() {
952                    doc.data_stores.apply_updates(updates, &mut doc.profile);
953                }
954
955                // Apply the last sampled scroll offsets from the previous scene,
956                // to the current scene. The offsets are identified by scroll ids
957                // which are stable across scenes. This ensures that a hit test,
958                // which could occur in between post-swap hook and the call to
959                // update_document() below, does not observe raw main-thread offsets
960                // from the new scene that don't have async deltas applied to them.
961                if let Some(last_sampled) = last_sampled_scroll_offsets {
962                    doc.spatial_tree
963                        .apply_last_sampled_scroll_offsets(last_sampled);
964                }
965
966                // Build the hit tester while the APZ lock is held so that its content
967                // is in sync with the gecko APZ tree.
968                if !doc.hit_tester_is_valid {
969                    doc.rebuild_hit_tester();
970                }
971
972                if let Some(ref tx) = result_tx {
973                    let (resume_tx, resume_rx) = single_msg_channel();
974                    tx.send(SceneSwapResult::Complete(resume_tx)).unwrap();
975                    // Block until the post-swap hook has completed on
976                    // the scene builder thread. We need to do this before
977                    // we can sample from the sampler hook which might happen
978                    // in the update_document call below.
979                    resume_rx.recv().ok();
980                }
981
982                self.resource_cache.add_rasterized_blob_images(
983                    txn.rasterized_blobs.take(),
984                    &mut doc.profile,
985                );
986
987                for offscreen_scene in txn.offscreen_scenes.drain(..) {
988                    self.resource_cache.post_scene_building_update(
989                        txn.resource_updates.take(),
990                        &mut doc.profile,
991                    );
992
993                    let rendered_document = doc.process_offscreen_scene(
994                        offscreen_scene,
995                        &mut self.resource_cache,
996                        self.chunk_pool.clone(),
997                        self.debug_flags,
998                    );
999
1000                    let pending_update = self.resource_cache.pending_updates();
1001
1002                    let msg = ResultMsg::PublishDocument(
1003                        self.frame_publish_id,
1004                        txn.document_id,
1005                        rendered_document,
1006                        pending_update,
1007                    );
1008                    self.result_tx.send(msg).unwrap();
1009
1010                    let params = api::FrameReadyParams {
1011                        present: false,
1012                        render: true,
1013                        scrolled: false,
1014                        tracked: false,
1015                    };
1016
1017                    self.notifier.new_frame_ready(
1018                        txn.document_id,
1019                        self.frame_publish_id,
1020                        &params
1021                    );
1022                }
1023            } else {
1024                // The document was removed while we were building it, skip it.
1025                // TODO: we might want to just ensure that removed documents are
1026                // always forwarded to the scene builder thread to avoid this case.
1027                if let Some(ref tx) = result_tx {
1028                    tx.send(SceneSwapResult::Aborted).unwrap();
1029                }
1030                continue;
1031            }
1032
1033            built_frame |= self.update_document(
1034                txn.document_id,
1035                txn.resource_updates.take(),
1036                txn.frame_ops.take(),
1037                txn.notifications.take(),
1038                txn.render_frame,
1039                txn.present,
1040                txn.tracked,
1041                RenderReasons::SCENE,
1042                None,
1043                txn.invalidate_rendered_frame,
1044                frame_counter,
1045                has_built_scene,
1046                None,
1047            );
1048
1049            if self.debug_flags.contains(DebugFlags::DUMP_SPATIAL_TREE) {
1050                if let Some(doc) = self.documents.get(&txn.document_id) {
1051                    let spatial_tree = doc.spatial_tree.print_to_string();
1052                    if !spatial_tree.is_empty() {
1053                        eprintln!(
1054                            "-- WebRender spatial tree ({:?}) --\n{}",
1055                            txn.document_id, spatial_tree
1056                        );
1057                    }
1058                }
1059            }
1060        }
1061
1062        built_frame
1063    }
1064
1065    fn process_api_msg(
1066        &mut self,
1067        msg: ApiMsg,
1068        frame_counter: &mut u32,
1069    ) -> RenderBackendStatus {
1070        match msg {
1071            ApiMsg::CloneApi(sender) => {
1072                assert!(!self.namespace_alloc_by_client);
1073                sender.send(Self::next_namespace_id()).unwrap();
1074            }
1075            ApiMsg::CloneApiByClient(namespace_id) => {
1076                assert!(self.namespace_alloc_by_client);
1077                debug_assert!(!self.documents.iter().any(|(did, _doc)| did.namespace_id == namespace_id));
1078            }
1079            ApiMsg::AddDocument(document_id, initial_size) => {
1080                let document = Document::new(
1081                    document_id,
1082                    initial_size,
1083                );
1084                let old = self.documents.insert(document_id, document);
1085                debug_assert!(old.is_none());
1086            }
1087            ApiMsg::MemoryPressure => {
1088                // This is drastic. It will basically flush everything out of the cache,
1089                // and the next frame will have to rebuild all of its resources.
1090                // We may want to look into something less extreme, but on the other hand this
1091                // should only be used in situations where are running low enough on memory
1092                // that we risk crashing if we don't do something about it.
1093                // The advantage of clearing the cache completely is that it gets rid of any
1094                // remaining fragmentation that could have persisted if we kept around the most
1095                // recently used resources.
1096                self.resource_cache.clear(ClearCache::all());
1097
1098                for (_, doc) in &mut self.documents {
1099                    doc.scratch.memory_pressure();
1100                    for tile_cache in self.tile_caches.values_mut() {
1101                        tile_cache.memory_pressure(&mut self.resource_cache);
1102                    }
1103                }
1104
1105                let resource_updates = self.resource_cache.pending_updates();
1106                let msg = ResultMsg::UpdateResources {
1107                    resource_updates,
1108                    memory_pressure: true,
1109                };
1110                self.result_tx.send(msg).unwrap();
1111                self.notifier.wake_up(false);
1112
1113                self.chunk_pool.purge_all_chunks();
1114            }
1115            ApiMsg::ReportMemory(tx) => {
1116                self.report_memory(tx);
1117            }
1118            ApiMsg::DebugCommand(option) => {
1119                let msg = match option {
1120                    DebugCommand::SetPictureTileSize(tile_size) => {
1121                        self.frame_config.tile_size_override = tile_size;
1122                        self.update_frame_builder_config();
1123
1124                        return RenderBackendStatus::Continue;
1125                    }
1126                    DebugCommand::SetMaximumSurfaceSize(surface_size) => {
1127                        self.frame_config.max_surface_override = surface_size;
1128                        self.update_frame_builder_config();
1129
1130                        return RenderBackendStatus::Continue;
1131                    }
1132                    DebugCommand::GenerateFrame => {
1133                        let documents: Vec<DocumentId> = self.documents.keys()
1134                            .cloned()
1135                            .collect();
1136                        for document_id in documents {
1137                            let mut invalidation_config = false;
1138                            if let Some(doc) = self.documents.get_mut(&document_id) {
1139                                doc.frame_is_valid = false;
1140                                invalidation_config = doc.scene.config.force_invalidation;
1141                                doc.scene.config.force_invalidation = true;
1142                            }
1143
1144                            self.update_document(
1145                                document_id,
1146                                Vec::default(),
1147                                Vec::default(),
1148                                Vec::default(),
1149                                true,
1150                                true,
1151                                false,
1152                                RenderReasons::empty(),
1153                                None,
1154                                true,
1155                                frame_counter,
1156                                false,
1157                                None,
1158                            );
1159
1160                            if let Some(doc) = self.documents.get_mut(&document_id) {
1161                                doc.scene.config.force_invalidation = invalidation_config;
1162                            }
1163                        }
1164
1165                        return RenderBackendStatus::Continue;
1166                    }
1167                    #[cfg(feature = "capture")]
1168                    DebugCommand::SaveCapture(root, bits) => {
1169                        let output = self.save_capture(root, bits);
1170                        ResultMsg::DebugOutput(output)
1171                    },
1172                    #[cfg(feature = "capture")]
1173                    DebugCommand::StartCaptureSequence(root, bits) => {
1174                        self.start_capture_sequence(root, bits);
1175                        return RenderBackendStatus::Continue;
1176                    },
1177                    #[cfg(feature = "capture")]
1178                    DebugCommand::StopCaptureSequence => {
1179                        self.stop_capture_sequence();
1180                        return RenderBackendStatus::Continue;
1181                    },
1182                    #[cfg(feature = "replay")]
1183                    DebugCommand::LoadCapture(path, ids, tx) => {
1184                        NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed);
1185                        *frame_counter += 1;
1186
1187                        let mut config = CaptureConfig::new(path, CaptureBits::all());
1188                        if let Some((scene_id, frame_id)) = ids {
1189                            config.scene_id = scene_id;
1190                            config.frame_id = frame_id;
1191                        }
1192
1193                        self.load_capture(config);
1194
1195                        for (id, doc) in &self.documents {
1196                            let captured = CapturedDocument {
1197                                document_id: *id,
1198                                root_pipeline_id: doc.loaded_scene.root_pipeline_id,
1199                            };
1200                            tx.send(captured).unwrap();
1201                        }
1202
1203                        // Note: we can't pass `LoadCapture` here since it needs to arrive
1204                        // before the `PublishDocument` messages sent by `load_capture`.
1205                        return RenderBackendStatus::Continue;
1206                    }
1207                    #[cfg(feature = "debugger")]
1208                    DebugCommand::Query(ref query) => {
1209                        match query.kind {
1210                            DebugQueryKind::SpatialTree { .. } => {
1211                                if let Some(doc) = self.documents.values().next() {
1212                                    let result = doc.spatial_tree.print_to_string();
1213                                    query.result.send(result).ok();
1214                                }
1215                                return RenderBackendStatus::Continue;
1216                            }
1217                            DebugQueryKind::CompositorView { .. } |
1218                            DebugQueryKind::CompositorConfig { .. } |
1219                            DebugQueryKind::Textures { .. } => {
1220                                ResultMsg::DebugCommand(option)
1221                            }
1222                        }
1223                    }
1224                    DebugCommand::ClearCaches(mask) => {
1225                        self.resource_cache.clear(mask);
1226                        return RenderBackendStatus::Continue;
1227                    }
1228                    DebugCommand::EnableNativeCompositor(enable) => {
1229                        // Default CompositorKind should be Native
1230                        if let CompositorKind::Draw { .. } = self.default_compositor_kind {
1231                            unreachable!();
1232                        }
1233
1234                        let compositor_kind = if enable {
1235                            self.default_compositor_kind
1236                        } else {
1237                            CompositorKind::default()
1238                        };
1239
1240                        for (_, doc) in &mut self.documents {
1241                            doc.scene.config.compositor_kind = compositor_kind;
1242                            doc.frame_is_valid = false;
1243                        }
1244
1245                        self.frame_config.compositor_kind = compositor_kind;
1246                        self.update_frame_builder_config();
1247
1248                        // We don't want to forward this message to the renderer.
1249                        return RenderBackendStatus::Continue;
1250                    }
1251                    DebugCommand::SetBatchingLookback(count) => {
1252                        self.frame_config.batch_lookback_count = count as usize;
1253                        self.update_frame_builder_config();
1254
1255                        return RenderBackendStatus::Continue;
1256                    }
1257                    DebugCommand::SimulateLongSceneBuild(time_ms) => {
1258                        let _ = self.scene_tx.send(SceneBuilderRequest::SimulateLongSceneBuild(time_ms));
1259                        return RenderBackendStatus::Continue;
1260                    }
1261                    DebugCommand::SetFlags(flags) => {
1262                        self.resource_cache.set_debug_flags(flags);
1263
1264                        let force_invalidation = flags.contains(DebugFlags::FORCE_PICTURE_INVALIDATION);
1265                        if self.frame_config.force_invalidation != force_invalidation {
1266                            self.frame_config.force_invalidation = force_invalidation;
1267                            for doc in self.documents.values_mut() {
1268                                doc.scene.config.force_invalidation = force_invalidation;
1269                            }
1270                            self.update_frame_builder_config();
1271                        }
1272
1273                        self.debug_flags = flags;
1274
1275                        ResultMsg::DebugCommand(option)
1276                    }
1277                    _ => ResultMsg::DebugCommand(option),
1278                };
1279                self.result_tx.send(msg).unwrap();
1280                self.notifier.wake_up(true);
1281            }
1282            ApiMsg::UpdateDocuments(transaction_msgs) => {
1283                self.prepare_transactions(
1284                    transaction_msgs,
1285                    frame_counter,
1286                );
1287            }
1288            ApiMsg::SceneBuilderResult(msg) => {
1289                return self.process_scene_builder_result(msg, frame_counter);
1290            }
1291        }
1292
1293        // Now that we are likely out of the critical path, purge a few chunks
1294        // from the pool. The underlying deallocation can be expensive, especially
1295        // with build configurations where all of the memory is zeroed, so we
1296        // spread the load over potentially many iterations of the event loop.
1297        self.chunk_pool.purge_chunks(2, 3);
1298
1299        RenderBackendStatus::Continue
1300    }
1301
1302    fn process_scene_builder_result(
1303        &mut self,
1304        msg: SceneBuilderResult,
1305        frame_counter: &mut u32,
1306    ) -> RenderBackendStatus {
1307        profile_scope!("sb_msg");
1308
1309        match msg {
1310            SceneBuilderResult::Transactions(txns, result_tx) => {
1311                self.process_transaction(
1312                    txns,
1313                    result_tx,
1314                    frame_counter,
1315                );
1316            },
1317            #[cfg(feature = "capture")]
1318            SceneBuilderResult::CapturedTransactions(txns, capture_config, result_tx) => {
1319                if let Some(ref mut old_config) = self.capture_config {
1320                    assert!(old_config.scene_id <= capture_config.scene_id);
1321                    if old_config.scene_id < capture_config.scene_id {
1322                        old_config.scene_id = capture_config.scene_id;
1323                        old_config.frame_id = 0;
1324                    }
1325                } else {
1326                    self.capture_config = Some(capture_config);
1327                }
1328
1329                let built_frame = self.process_transaction(
1330                    txns,
1331                    result_tx,
1332                    frame_counter,
1333                );
1334
1335                if built_frame {
1336                    self.save_capture_sequence();
1337                }
1338            },
1339            #[cfg(feature = "capture")]
1340            SceneBuilderResult::StopCaptureSequence => {
1341                self.capture_config = None;
1342            }
1343            SceneBuilderResult::GetGlyphDimensions(request) => {
1344                let mut glyph_dimensions = Vec::with_capacity(request.glyph_indices.len());
1345                let instance_key = self.resource_cache.map_font_instance_key(request.key);
1346                if let Some(base) = self.resource_cache.get_font_instance(instance_key) {
1347                    let font = FontInstance::from_base(Arc::clone(&base));
1348                    for glyph_index in &request.glyph_indices {
1349                        let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, *glyph_index);
1350                        glyph_dimensions.push(glyph_dim);
1351                    }
1352                }
1353                request.sender.send(glyph_dimensions).unwrap();
1354            }
1355            SceneBuilderResult::GetGlyphIndices(request) => {
1356                let mut glyph_indices = Vec::with_capacity(request.text.len());
1357                let font_key = self.resource_cache.map_font_key(request.key);
1358                for ch in request.text.chars() {
1359                    let index = self.resource_cache.get_glyph_index(font_key, ch);
1360                    glyph_indices.push(index);
1361                }
1362                request.sender.send(glyph_indices).unwrap();
1363            }
1364            SceneBuilderResult::FlushComplete(tx) => {
1365                tx.send(()).ok();
1366            }
1367            SceneBuilderResult::ExternalEvent(evt) => {
1368                self.notifier.external_event(evt);
1369            }
1370            SceneBuilderResult::ClearNamespace(id) => {
1371                self.resource_cache.clear_namespace(id);
1372                self.documents.retain(|doc_id, _doc| doc_id.namespace_id != id);
1373            }
1374            SceneBuilderResult::DeleteDocument(document_id) => {
1375                self.documents.remove(&document_id);
1376            }
1377            SceneBuilderResult::SetParameter(param) => {
1378                if let Parameter::Bool(BoolParameter::Multithreading, enabled) = param {
1379                    self.resource_cache.enable_multithreading(enabled);
1380                }
1381                let _ = self.result_tx.send(ResultMsg::SetParameter(param));
1382            }
1383            SceneBuilderResult::StopRenderBackend => {
1384                return RenderBackendStatus::StopRenderBackend;
1385            }
1386            SceneBuilderResult::ShutDown(sender) => {
1387                info!("Recycling stats: {:?}", self.recycler);
1388                return RenderBackendStatus::ShutDown(sender);
1389            }
1390        }
1391
1392        RenderBackendStatus::Continue
1393    }
1394
1395    fn update_frame_builder_config(&self) {
1396        self.send_backend_message(
1397            SceneBuilderRequest::SetFrameBuilderConfig(
1398                self.frame_config.clone()
1399            )
1400        );
1401    }
1402
1403    fn requires_frame_build(&mut self) -> bool {
1404        false // TODO(nical)
1405    }
1406
1407    fn prepare_transactions(
1408        &mut self,
1409        txns: Vec<Box<TransactionMsg>>,
1410        frame_counter: &mut u32,
1411    ) {
1412        self.maybe_force_nop_documents(
1413            frame_counter,
1414            |document_id| txns.iter().any(|txn| txn.document_id == document_id));
1415
1416        let mut built_frame = false;
1417        for mut txn in txns {
1418            if txn.generate_frame.as_bool() {
1419                txn.profile.end_time(profiler::API_SEND_TIME);
1420            }
1421
1422            self.documents.get_mut(&txn.document_id).unwrap().profile.merge(&mut txn.profile);
1423
1424            built_frame |= self.update_document(
1425                txn.document_id,
1426                txn.resource_updates.take(),
1427                txn.frame_ops.take(),
1428                txn.notifications.take(),
1429                txn.generate_frame.as_bool(),
1430                txn.generate_frame.present(),
1431                txn.generate_frame.tracked(),
1432                txn.render_reasons,
1433                txn.generate_frame.id(),
1434                txn.invalidate_rendered_frame,
1435                frame_counter,
1436                false,
1437                txn.creation_time,
1438            );
1439        }
1440        if built_frame {
1441            #[cfg(feature = "capture")]
1442            self.save_capture_sequence();
1443        }
1444    }
1445
1446    /// In certain cases, resources shared by multiple documents have to run
1447    /// maintenance operations, like cleaning up unused cache items. In those
1448    /// cases, we are forced to build frames for all documents, however we
1449    /// may not have a transaction ready for every document - this method
1450    /// calls update_document with the details of a fake, nop transaction just
1451    /// to force a frame build.
1452    fn maybe_force_nop_documents<F>(&mut self,
1453                                    frame_counter: &mut u32,
1454                                    document_already_present: F) where
1455        F: Fn(DocumentId) -> bool {
1456        if self.requires_frame_build() {
1457            let nop_documents : Vec<DocumentId> = self.documents.keys()
1458                .cloned()
1459                .filter(|key| !document_already_present(*key))
1460                .collect();
1461            let mut built_frame = false;
1462            #[cfg_attr(not(feature = "capture"), allow(unused_assignments))]
1463            for &document_id in &nop_documents {
1464                built_frame |= self.update_document(
1465                    document_id,
1466                    Vec::default(),
1467                    Vec::default(),
1468                    Vec::default(),
1469                    false,
1470                    false,
1471                    false,
1472                    RenderReasons::empty(),
1473                    None,
1474                    false,
1475                    frame_counter,
1476                    false,
1477                    None);
1478            }
1479            match built_frame {
1480                true =>
1481                {
1482                    #[cfg(feature = "capture")]
1483                    self.save_capture_sequence()
1484                }
1485                _ => {},
1486            }
1487        }
1488    }
1489
1490    fn update_document(
1491        &mut self,
1492        document_id: DocumentId,
1493        resource_updates: Vec<ResourceUpdate>,
1494        mut frame_ops: Vec<FrameMsg>,
1495        mut notifications: Vec<NotificationRequest>,
1496        mut render_frame: bool,
1497        mut present: bool,
1498        tracked: bool,
1499        render_reasons: RenderReasons,
1500        generated_frame_id: Option<u64>,
1501        invalidate_rendered_frame: bool,
1502        frame_counter: &mut u32,
1503        has_built_scene: bool,
1504        start_time: Option<u64>
1505    ) -> bool {
1506        let update_doc_start = zeitstempel::now();
1507
1508        let requested_frame = render_frame || self.frame_config.force_invalidation;
1509
1510        let requires_frame_build = self.requires_frame_build();
1511        let doc = self.documents.get_mut(&document_id).unwrap();
1512
1513        // If we have a sampler, get more frame ops from it and add them
1514        // to the transaction. This is a hook to allow the WR user code to
1515        // fiddle with things after a potentially long scene build, but just
1516        // before rendering. This is useful for rendering with the latest
1517        // async transforms.
1518        if requested_frame {
1519            if let Some(ref sampler) = self.sampler {
1520                frame_ops.append(&mut sampler.sample(document_id, generated_frame_id));
1521            }
1522        }
1523
1524        doc.has_built_scene |= has_built_scene;
1525
1526        // TODO: this scroll variable doesn't necessarily mean we scrolled. It is only used
1527        // for something wrench specific and we should remove it.
1528        let mut scroll = false;
1529        for frame_msg in frame_ops {
1530            let op = doc.process_frame_msg(frame_msg);
1531            scroll |= op.scroll;
1532        }
1533
1534        for update in &resource_updates {
1535            if let ResourceUpdate::UpdateImage(..) = update {
1536                doc.frame_is_valid = false;
1537            }
1538        }
1539
1540        self.resource_cache.post_scene_building_update(
1541            resource_updates,
1542            &mut doc.profile,
1543        );
1544
1545        if doc.dynamic_properties.flush_pending_updates() {
1546            doc.frame_is_valid = false;
1547            doc.hit_tester_is_valid = false;
1548        }
1549
1550        if !doc.can_render() {
1551            // TODO: this happens if we are building the first scene asynchronously and
1552            // scroll at the same time. we should keep track of the fact that we skipped
1553            // composition here and do it as soon as we receive the scene.
1554            render_frame = false;
1555        }
1556
1557        // Avoid re-building the frame if the current built frame is still valid.
1558        // However, if the resource_cache requires a frame build, _always_ do that, unless
1559        // doc.can_render() is false, as in that case a frame build can't happen anyway.
1560        // We want to ensure we do this because even if the doc doesn't have pixels it
1561        // can still try to access stale texture cache items.
1562        let build_frame = (render_frame && !doc.frame_is_valid && doc.has_pixels()) ||
1563            (requires_frame_build && doc.can_render());
1564
1565        // Request composite is true when we want to composite frame even when
1566        // there is no frame update. This happens when video frame is updated under
1567        // external image with NativeTexture or when platform requested to composite frame.
1568        if invalidate_rendered_frame {
1569            doc.rendered_frame_is_valid = false;
1570            if doc.scene.config.compositor_kind.should_redraw_on_invalidation() {
1571                let msg = ResultMsg::ForceRedraw;
1572                self.result_tx.send(msg).unwrap();
1573            }
1574        }
1575
1576        if build_frame {
1577            if !requested_frame {
1578                // When we don't request a frame, present defaults to false. If for some
1579                // reason we did not request the frame but must render it anyway, set
1580                // present to true (it was false as a byproduct of expecting we wouldn't
1581                // produce the frame but we did not explicitly opt out of it).
1582                present = true;
1583            }
1584
1585            if start_time.is_some() {
1586              Telemetry::record_time_to_frame_build(Duration::from_nanos(zeitstempel::now() - start_time.unwrap()));
1587            }
1588            profile_scope!("generate frame");
1589
1590            *frame_counter += 1;
1591
1592            // borrow ck hack for profile_counters
1593            let (pending_update, mut rendered_document) = {
1594                let timer_id = Telemetry::start_framebuild_time();
1595
1596                let frame_stats = doc.frame_stats.take();
1597
1598                let rendered_document = doc.build_frame(
1599                    &mut self.resource_cache,
1600                    self.debug_flags,
1601                    &mut self.tile_caches,
1602                    frame_stats,
1603                    present,
1604                    render_reasons,
1605                    self.chunk_pool.clone(),
1606                );
1607
1608                debug!("generated frame for document {:?} with {} passes",
1609                    document_id, rendered_document.frame.passes.len());
1610
1611                Telemetry::stop_and_accumulate_framebuild_time(timer_id);
1612
1613                let pending_update = self.resource_cache.pending_updates();
1614                (pending_update, rendered_document)
1615            };
1616
1617            // Invalidate dirty rects if the compositing config has changed significantly
1618            rendered_document
1619                .frame
1620                .composite_state
1621                .update_dirty_rect_validity(&doc.prev_composite_descriptor);
1622
1623            // Build a small struct that represents the state of the tiles to be composited.
1624            let composite_descriptor = rendered_document
1625                .frame
1626                .composite_state
1627                .descriptor
1628                .clone();
1629
1630            // If there are texture cache updates to apply, or if the produced
1631            // frame is not a no-op, or the compositor state has changed,
1632            // then we cannot skip compositing this frame.
1633            if !pending_update.is_nop() ||
1634               !rendered_document.frame.is_nop() ||
1635               composite_descriptor != doc.prev_composite_descriptor {
1636                doc.rendered_frame_is_valid = false;
1637            }
1638            doc.prev_composite_descriptor = composite_descriptor;
1639
1640            #[cfg(feature = "capture")]
1641            match self.capture_config {
1642                Some(ref mut config) => {
1643                    // FIXME(aosmond): document splitting causes multiple prepare frames
1644                    config.prepare_frame();
1645
1646                    if config.bits.contains(CaptureBits::FRAME) {
1647                        let file_name = format!("frame-{}-{}", document_id.namespace_id.0, document_id.id);
1648                        config.serialize_for_frame(&rendered_document.frame, file_name);
1649                    }
1650
1651                    let data_stores_name = format!("data-stores-{}-{}", document_id.namespace_id.0, document_id.id);
1652                    config.serialize_for_frame(&doc.data_stores, data_stores_name);
1653
1654                    let frame_spatial_tree_name = format!("frame-spatial-tree-{}-{}", document_id.namespace_id.0, document_id.id);
1655                    config.serialize_for_frame::<SpatialTree, _>(&doc.spatial_tree, frame_spatial_tree_name);
1656
1657                    let properties_name = format!("properties-{}-{}", document_id.namespace_id.0, document_id.id);
1658                    config.serialize_for_frame(&doc.dynamic_properties, properties_name);
1659                },
1660                None => {},
1661            }
1662
1663            let update_doc_time = profiler::ns_to_ms(zeitstempel::now() - update_doc_start);
1664            rendered_document.profile.set(profiler::UPDATE_DOCUMENT_TIME, update_doc_time);
1665
1666            let msg = ResultMsg::PublishPipelineInfo(doc.updated_pipeline_info());
1667            self.result_tx.send(msg).unwrap();
1668
1669            // Publish the frame
1670            self.frame_publish_id.advance();
1671            let msg = ResultMsg::PublishDocument(
1672                self.frame_publish_id,
1673                document_id,
1674                rendered_document,
1675                pending_update,
1676            );
1677            self.result_tx.send(msg).unwrap();
1678        } else if requested_frame {
1679            // WR-internal optimization to avoid doing a bunch of render work if
1680            // there's no pixels. We still want to pretend to render and request
1681            // a render to make sure that the callbacks (particularly the
1682            // new_frame_ready callback below) has the right flags.
1683            let msg = ResultMsg::PublishPipelineInfo(doc.updated_pipeline_info());
1684            self.result_tx.send(msg).unwrap();
1685        }
1686
1687        drain_filter(
1688            &mut notifications,
1689            |n| { n.when() == Checkpoint::FrameBuilt },
1690            |n| { n.notify(); },
1691        );
1692
1693        if !notifications.is_empty() {
1694            self.result_tx.send(ResultMsg::AppendNotificationRequests(notifications)).unwrap();
1695        }
1696
1697        // Always forward the transaction to the renderer if a frame was requested,
1698        // otherwise gecko can get into a state where it waits (forever) for the
1699        // transaction to complete before sending new work.
1700        if requested_frame {
1701            // If rendered frame is already valid, there is no need to render frame.
1702            if doc.rendered_frame_is_valid {
1703                render_frame = false;
1704            } else if render_frame {
1705                doc.rendered_frame_is_valid = true;
1706            }
1707            let params = api::FrameReadyParams {
1708                present,
1709                render: render_frame,
1710                scrolled: scroll,
1711                tracked,
1712            };
1713            self.notifier.new_frame_ready(document_id, self.frame_publish_id, &params);
1714        }
1715
1716        if !doc.hit_tester_is_valid {
1717            doc.rebuild_hit_tester();
1718        }
1719
1720        build_frame
1721    }
1722
1723    fn send_backend_message(&self, msg: SceneBuilderRequest) {
1724        self.scene_tx.send(msg).unwrap();
1725    }
1726
1727    fn report_memory(&mut self, tx: Sender<Box<MemoryReport>>) {
1728        let mut report = Box::new(MemoryReport::default());
1729        let ops = self.size_of_ops.as_mut().unwrap();
1730        let op = ops.size_of_op;
1731        for doc in self.documents.values() {
1732            report.clip_stores += doc.scene.clip_store.size_of(ops);
1733            report.hit_testers += match &doc.hit_tester {
1734                Some(hit_tester) => hit_tester.size_of(ops),
1735                None => 0,
1736            };
1737
1738            doc.data_stores.report_memory(ops, &mut report)
1739        }
1740
1741        (*report) += self.resource_cache.report_memory(op);
1742        report.texture_cache_structures = self.resource_cache
1743            .texture_cache
1744            .report_memory(ops);
1745
1746        // Send a message to report memory on the scene-builder thread, which
1747        // will add its report to this one and send the result back to the original
1748        // thread waiting on the request.
1749        self.send_backend_message(
1750            SceneBuilderRequest::ReportMemory(report, tx)
1751        );
1752    }
1753
1754    #[cfg(feature = "capture")]
1755    fn save_capture_sequence(&mut self) {
1756        if let Some(ref mut config) = self.capture_config {
1757            let deferred = self.resource_cache.save_capture_sequence(config);
1758
1759            let backend = PlainRenderBackend {
1760                frame_config: self.frame_config.clone(),
1761                resource_sequence_id: config.resource_id,
1762                documents: self.documents
1763                    .iter()
1764                    .map(|(id, doc)| (*id, doc.view))
1765                    .collect(),
1766            };
1767            config.serialize_for_frame(&backend, "backend");
1768
1769            if !deferred.is_empty() {
1770                let msg = ResultMsg::DebugOutput(DebugOutput::SaveCapture(config.clone(), deferred));
1771                self.result_tx.send(msg).unwrap();
1772            }
1773        }
1774    }
1775}
1776
1777impl RenderBackend {
1778    #[cfg(feature = "capture")]
1779    // Note: the mutable `self` is only needed here for resolving blob images
1780    fn save_capture(
1781        &mut self,
1782        root: PathBuf,
1783        bits: CaptureBits,
1784    ) -> DebugOutput {
1785        use std::fs;
1786        use crate::render_task_graph::dump_render_tasks_as_svg;
1787
1788        debug!("capture: saving {:?}", root);
1789        if !root.is_dir() {
1790            if let Err(e) = fs::create_dir_all(&root) {
1791                panic!("Unable to create capture dir: {:?}", e);
1792            }
1793        }
1794        let config = CaptureConfig::new(root, bits);
1795
1796        for (&id, doc) in &mut self.documents {
1797            debug!("\tdocument {:?}", id);
1798            if config.bits.contains(CaptureBits::FRAME) {
1799                // Temporarily force invalidation otherwise the render task graph dump is empty.
1800                let force_invalidation = std::mem::replace(&mut doc.scene.config.force_invalidation, true);
1801                let rendered_document = doc.build_frame(
1802                    &mut self.resource_cache,
1803                    self.debug_flags,
1804                    &mut self.tile_caches,
1805                    None,
1806                    true,
1807                    RenderReasons::empty(),
1808                    self.chunk_pool.clone(),
1809                );
1810
1811                doc.scene.config.force_invalidation = force_invalidation;
1812
1813                //TODO: write down doc's pipeline info?
1814                // it has `pipeline_epoch_map`,
1815                // which may capture necessary details for some cases.
1816                let file_name = format!("frame-{}-{}", id.namespace_id.0, id.id);
1817                config.serialize_for_frame(&rendered_document.frame, file_name);
1818                let file_name = format!("spatial-{}-{}", id.namespace_id.0, id.id);
1819                config.serialize_tree_for_frame(&doc.spatial_tree, file_name);
1820                let file_name = format!("built-primitives-{}-{}", id.namespace_id.0, id.id);
1821                config.serialize_for_frame(&doc.scene.prim_store, file_name);
1822                let file_name = format!("built-clips-{}-{}", id.namespace_id.0, id.id);
1823                config.serialize_for_frame(&doc.scene.clip_store, file_name);
1824                let file_name = format!("scratch-{}-{}", id.namespace_id.0, id.id);
1825                config.serialize_for_frame(&doc.scratch.primitive, file_name);
1826                let file_name = format!("render-tasks-{}-{}.svg", id.namespace_id.0, id.id);
1827                let mut render_tasks_file = fs::File::create(&config.file_path_for_frame(file_name, "svg"))
1828                    .expect("Failed to open the SVG file.");
1829                dump_render_tasks_as_svg(
1830                    &rendered_document.frame.render_tasks,
1831                    &mut render_tasks_file
1832                ).unwrap();
1833
1834                let file_name = format!("texture-cache-color-linear-{}-{}.svg", id.namespace_id.0, id.id);
1835                let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg"))
1836                    .expect("Failed to open the SVG file.");
1837                self.resource_cache.texture_cache.dump_color8_linear_as_svg(&mut texture_file).unwrap();
1838
1839                let file_name = format!("texture-cache-color8-glyphs-{}-{}.svg", id.namespace_id.0, id.id);
1840                let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg"))
1841                    .expect("Failed to open the SVG file.");
1842                self.resource_cache.texture_cache.dump_color8_glyphs_as_svg(&mut texture_file).unwrap();
1843
1844                let file_name = format!("texture-cache-alpha8-glyphs-{}-{}.svg", id.namespace_id.0, id.id);
1845                let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg"))
1846                    .expect("Failed to open the SVG file.");
1847                self.resource_cache.texture_cache.dump_alpha8_glyphs_as_svg(&mut texture_file).unwrap();
1848
1849                let file_name = format!("texture-cache-alpha8-linear-{}-{}.svg", id.namespace_id.0, id.id);
1850                let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg"))
1851                    .expect("Failed to open the SVG file.");
1852                self.resource_cache.texture_cache.dump_alpha8_linear_as_svg(&mut texture_file).unwrap();
1853            }
1854
1855            let data_stores_name = format!("data-stores-{}-{}", id.namespace_id.0, id.id);
1856            config.serialize_for_frame(&doc.data_stores, data_stores_name);
1857
1858            let frame_spatial_tree_name = format!("frame-spatial-tree-{}-{}", id.namespace_id.0, id.id);
1859            config.serialize_for_frame::<SpatialTree, _>(&doc.spatial_tree, frame_spatial_tree_name);
1860
1861            let properties_name = format!("properties-{}-{}", id.namespace_id.0, id.id);
1862            config.serialize_for_frame(&doc.dynamic_properties, properties_name);
1863        }
1864
1865        if config.bits.contains(CaptureBits::FRAME) {
1866            // TODO: there is no guarantee that we won't hit this case, but we want to
1867            // report it here if we do. If we don't, it will simply crash in
1868            // Renderer::render_impl and give us less information about the source.
1869            assert!(!self.requires_frame_build(), "Caches were cleared during a capture.");
1870        }
1871
1872        debug!("\tscene builder");
1873        self.send_backend_message(
1874            SceneBuilderRequest::SaveScene(config.clone())
1875        );
1876
1877        debug!("\tresource cache");
1878        let (resources, deferred) = self.resource_cache.save_capture(&config.root);
1879
1880        info!("\tbackend");
1881        let backend = PlainRenderBackend {
1882            frame_config: self.frame_config.clone(),
1883            resource_sequence_id: 0,
1884            documents: self.documents
1885                .iter()
1886                .map(|(id, doc)| (*id, doc.view))
1887                .collect(),
1888        };
1889
1890        config.serialize_for_frame(&backend, "backend");
1891        config.serialize_for_frame(&resources, "plain-resources");
1892
1893        if config.bits.contains(CaptureBits::FRAME) {
1894            let msg_update_resources = ResultMsg::UpdateResources {
1895                resource_updates: self.resource_cache.pending_updates(),
1896                memory_pressure: false,
1897            };
1898            self.result_tx.send(msg_update_resources).unwrap();
1899            // Save the texture/glyph/image caches.
1900            info!("\tresource cache");
1901            let caches = self.resource_cache.save_caches(&config.root);
1902            config.serialize_for_resource(&caches, "resource_cache");
1903        }
1904
1905        DebugOutput::SaveCapture(config, deferred)
1906    }
1907
1908    #[cfg(feature = "capture")]
1909    fn start_capture_sequence(
1910        &mut self,
1911        root: PathBuf,
1912        bits: CaptureBits,
1913    ) {
1914        self.send_backend_message(
1915            SceneBuilderRequest::StartCaptureSequence(CaptureConfig::new(root, bits))
1916        );
1917    }
1918
1919    #[cfg(feature = "capture")]
1920    fn stop_capture_sequence(
1921        &mut self,
1922    ) {
1923        self.send_backend_message(
1924            SceneBuilderRequest::StopCaptureSequence
1925        );
1926    }
1927
1928    #[cfg(feature = "replay")]
1929    fn load_capture(
1930        &mut self,
1931        mut config: CaptureConfig,
1932    ) {
1933        debug!("capture: loading {:?}", config.frame_root());
1934        let backend = config.deserialize_for_frame::<PlainRenderBackend, _>("backend")
1935            .expect("Unable to open backend.ron");
1936
1937        // If this is a capture sequence, then the ID will be non-zero, and won't
1938        // match what is loaded, but for still captures, the ID will be zero.
1939        let first_load = backend.resource_sequence_id == 0;
1940        if self.loaded_resource_sequence_id != backend.resource_sequence_id || first_load {
1941            // FIXME(aosmond): We clear the documents because when we update the
1942            // resource cache, we actually wipe and reload, because we don't
1943            // know what is the same and what has changed. If we were to keep as
1944            // much of the resource cache state as possible, we could avoid
1945            // flushing the document state (which has its own dependecies on the
1946            // cache).
1947            //
1948            // FIXME(aosmond): If we try to load the next capture in the
1949            // sequence too quickly, we may lose resources we depend on in the
1950            // current frame. This can cause panics. Ideally we would not
1951            // advance to the next frame until the FrameRendered event for all
1952            // of the pipelines.
1953            self.documents.clear();
1954
1955            config.resource_id = backend.resource_sequence_id;
1956            self.loaded_resource_sequence_id = backend.resource_sequence_id;
1957
1958            let plain_resources = config.deserialize_for_resource::<PlainResources, _>("plain-resources")
1959                .expect("Unable to open plain-resources.ron");
1960            let caches_maybe = config.deserialize_for_resource::<PlainCacheOwn, _>("resource_cache");
1961
1962            // Note: it would be great to have `RenderBackend` to be split
1963            // rather explicitly on what's used before and after scene building
1964            // so that, for example, we never miss anything in the code below:
1965
1966            let plain_externals = self.resource_cache.load_capture(
1967                plain_resources,
1968                caches_maybe,
1969                &config,
1970            );
1971
1972            let msg_load = ResultMsg::DebugOutput(
1973                DebugOutput::LoadCapture(config.clone(), plain_externals)
1974            );
1975            self.result_tx.send(msg_load).unwrap();
1976        }
1977
1978        self.frame_config = backend.frame_config;
1979
1980        let mut scenes_to_build = Vec::new();
1981
1982        for (id, view) in backend.documents {
1983            debug!("\tdocument {:?}", id);
1984            let scene_name = format!("scene-{}-{}", id.namespace_id.0, id.id);
1985            let scene = config.deserialize_for_scene::<Scene, _>(&scene_name)
1986                .expect(&format!("Unable to open {}.ron", scene_name));
1987
1988            let scene_spatial_tree_name = format!("scene-spatial-tree-{}-{}", id.namespace_id.0, id.id);
1989            let scene_spatial_tree = config.deserialize_for_scene::<SceneSpatialTree, _>(&scene_spatial_tree_name)
1990                .expect(&format!("Unable to open {}.ron", scene_spatial_tree_name));
1991
1992            let interners_name = format!("interners-{}-{}", id.namespace_id.0, id.id);
1993            let interners = config.deserialize_for_scene::<Interners, _>(&interners_name)
1994                .expect(&format!("Unable to open {}.ron", interners_name));
1995
1996            let data_stores_name = format!("data-stores-{}-{}", id.namespace_id.0, id.id);
1997            let data_stores = config.deserialize_for_frame::<DataStores, _>(&data_stores_name)
1998                .expect(&format!("Unable to open {}.ron", data_stores_name));
1999
2000            let properties_name = format!("properties-{}-{}", id.namespace_id.0, id.id);
2001            let properties = config.deserialize_for_frame::<SceneProperties, _>(&properties_name)
2002                .expect(&format!("Unable to open {}.ron", properties_name));
2003
2004            let frame_spatial_tree_name = format!("frame-spatial-tree-{}-{}", id.namespace_id.0, id.id);
2005            let frame_spatial_tree = config.deserialize_for_frame::<SpatialTree, _>(&frame_spatial_tree_name)
2006                .expect(&format!("Unable to open {}.ron", frame_spatial_tree_name));
2007
2008            // Update the document if it still exists, rather than replace it entirely.
2009            // This allows us to preserve state information such as the frame stamp,
2010            // which is necessary for cache sanity.
2011            match self.documents.entry(id) {
2012                Occupied(entry) => {
2013                    let doc = entry.into_mut();
2014                    doc.view = view;
2015                    doc.loaded_scene = scene.clone();
2016                    doc.data_stores = data_stores;
2017                    doc.spatial_tree = frame_spatial_tree;
2018                    doc.dynamic_properties = properties;
2019                    doc.frame_is_valid = false;
2020                    doc.rendered_frame_is_valid = false;
2021                    doc.has_built_scene = false;
2022                    doc.hit_tester_is_valid = false;
2023                }
2024                Vacant(entry) => {
2025                    let doc = Document {
2026                        id,
2027                        scene: BuiltScene::empty(),
2028                        removed_pipelines: Vec::new(),
2029                        view,
2030                        stamp: FrameStamp::first(id),
2031                        frame_builder: FrameBuilder::new(),
2032                        dynamic_properties: properties,
2033                        hit_tester: None,
2034                        shared_hit_tester: Arc::new(SharedHitTester::new()),
2035                        frame_is_valid: false,
2036                        hit_tester_is_valid: false,
2037                        rendered_frame_is_valid: false,
2038                        has_built_scene: false,
2039                        data_stores,
2040                        scratch: ScratchBuffer::default(),
2041                        spatial_tree: frame_spatial_tree,
2042                        minimap_data: FastHashMap::default(),
2043                        loaded_scene: scene.clone(),
2044                        prev_composite_descriptor: CompositeDescriptor::empty(),
2045                        dirty_rects_are_valid: false,
2046                        profile: TransactionProfile::new(),
2047                        rg_builder: RenderTaskGraphBuilder::new(),
2048                        frame_stats: None,
2049                    };
2050                    entry.insert(doc);
2051                }
2052            };
2053
2054            let frame_name = format!("frame-{}-{}", id.namespace_id.0, id.id);
2055            let frame = config.deserialize_for_frame::<Frame, _>(frame_name);
2056            let build_frame = match frame {
2057                Some(frame) => {
2058                    info!("\tloaded a built frame with {} passes", frame.passes.len());
2059
2060                    self.frame_publish_id.advance();
2061                    let msg_publish = ResultMsg::PublishDocument(
2062                        self.frame_publish_id,
2063                        id,
2064                        RenderedDocument {
2065                            frame,
2066                            profile: TransactionProfile::new(),
2067                            render_reasons: RenderReasons::empty(),
2068                            frame_stats: None,
2069                        },
2070                        self.resource_cache.pending_updates(),
2071                    );
2072                    self.result_tx.send(msg_publish).unwrap();
2073
2074                    let params = api::FrameReadyParams {
2075                        present: true,
2076                        render: true,
2077                        scrolled: false,
2078                        tracked: false,
2079                    };
2080                    self.notifier.new_frame_ready(id, self.frame_publish_id, &params);
2081
2082                    // We deserialized the state of the frame so we don't want to build
2083                    // it (but we do want to update the scene builder's state)
2084                    false
2085                }
2086                None => true,
2087            };
2088
2089            scenes_to_build.push(LoadScene {
2090                document_id: id,
2091                scene,
2092                view: view.scene.clone(),
2093                config: self.frame_config.clone(),
2094                fonts: self.resource_cache.get_fonts(),
2095                build_frame,
2096                interners,
2097                spatial_tree: scene_spatial_tree,
2098            });
2099        }
2100
2101        if !scenes_to_build.is_empty() {
2102            self.send_backend_message(
2103                SceneBuilderRequest::LoadScenes(scenes_to_build)
2104            );
2105        }
2106    }
2107}