1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 //! The high-level module responsible for managing the pipeline and preparing
6 //! commands to be issued by the `Renderer`.
8 //! See the comment at the top of the `renderer` module for a description of
9 //! how these two pieces interact.
11 use api::{DebugFlags, BlobImageHandler};
12 use api::{DocumentId, ExternalScrollId, HitTestResult};
13 use api::{IdNamespace, PipelineId, RenderNotifier, ScrollClamping};
14 use api::{NotificationRequest, Checkpoint, QualitySettings};
15 use api::{PrimitiveKeyKind};
17 use api::channel::{single_msg_channel, Sender, Receiver};
18 #[cfg(any(feature = "capture", feature = "replay"))]
19 use crate::render_api::CaptureBits;
20 #[cfg(feature = "replay")]
21 use crate::render_api::CapturedDocument;
22 use crate::render_api::{MemoryReport, TransactionMsg, ResourceUpdate, ApiMsg, FrameMsg, ClearCache, DebugCommand};
23 use crate::clip::{ClipIntern, PolygonIntern, ClipStoreScratchBuffer};
24 use crate::filterdata::FilterDataIntern;
25 #[cfg(any(feature = "capture", feature = "replay"))]
26 use crate::capture::CaptureConfig;
27 use crate::composite::{CompositorKind, CompositeDescriptor};
28 use crate::frame_builder::{FrameBuilder, FrameBuilderConfig, FrameScratchBuffer};
29 use crate::glyph_rasterizer::{FontInstance};
30 use crate::gpu_cache::GpuCache;
31 use crate::hit_test::{HitTest, HitTester, SharedHitTester};
32 use crate::intern::DataStore;
33 #[cfg(any(feature = "capture", feature = "replay"))]
34 use crate::internal_types::DebugOutput;
35 use crate::internal_types::{FastHashMap, RenderedDocument, ResultMsg};
36 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
37 use crate::picture::{TileCacheLogger, PictureScratchBuffer, SliceId, TileCacheInstance, TileCacheParams};
38 use crate::prim_store::{PrimitiveScratchBuffer, PrimitiveInstance};
39 use crate::prim_store::{PrimitiveInstanceKind, PrimTemplateCommonData, PrimitiveStore};
40 use crate::prim_store::interned::*;
41 use crate::profiler::{self, TransactionProfile};
42 use crate::render_task_graph::RenderTaskGraphBuilder;
43 use crate::renderer::{AsyncPropertySampler, FullFrameStats, PipelineInfo};
44 use crate::resource_cache::ResourceCache;
45 #[cfg(feature = "replay")]
46 use crate::resource_cache::PlainCacheOwn;
47 #[cfg(feature = "replay")]
48 use crate::resource_cache::PlainResources;
49 #[cfg(feature = "replay")]
50 use crate::scene::Scene;
51 use crate::scene::{BuiltScene, SceneProperties};
52 use crate::scene_builder_thread::*;
53 #[cfg(feature = "serialize")]
54 use serde::{Serialize, Deserialize};
55 #[cfg(feature = "replay")]
56 use std::collections::hash_map::Entry::{Occupied, Vacant};
58 use std::sync::atomic::{AtomicUsize, Ordering};
59 use std::time::{UNIX_EPOCH, SystemTime};
61 #[cfg(feature = "capture")]
62 use std::path::PathBuf;
63 #[cfg(feature = "replay")]
64 use crate::frame_builder::Frame;
65 use time::precise_time_ns;
66 use crate::util::{Recycler, VecHelper, drain_filter};
68 #[cfg_attr(feature = "capture", derive(Serialize))]
69 #[cfg_attr(feature = "replay", derive(Deserialize))]
70 #[derive(Copy, Clone)]
71 pub struct DocumentView {
76 /// Some rendering parameters applying at the scene level.
77 #[cfg_attr(feature = "capture", derive(Serialize))]
78 #[cfg_attr(feature = "replay", derive(Deserialize))]
79 #[derive(Copy, Clone)]
80 pub struct SceneView {
81 pub device_rect: DeviceIntRect,
82 pub device_pixel_ratio: f32,
83 pub page_zoom_factor: f32,
84 pub quality_settings: QualitySettings,
88 pub fn accumulated_scale_factor_for_snapping(&self) -> DevicePixelScale {
89 DevicePixelScale::new(
90 self.device_pixel_ratio *
96 /// Some rendering parameters applying at the frame level.
97 #[cfg_attr(feature = "capture", derive(Serialize))]
98 #[cfg_attr(feature = "replay", derive(Deserialize))]
99 #[derive(Copy, Clone)]
100 pub struct FrameView {
102 pinch_zoom_factor: f32,
106 pub fn accumulated_scale_factor(&self) -> DevicePixelScale {
107 DevicePixelScale::new(
108 self.scene.device_pixel_ratio *
109 self.scene.page_zoom_factor *
110 self.frame.pinch_zoom_factor
115 #[derive(Copy, Clone, Hash, MallocSizeOf, PartialEq, PartialOrd, Debug, Eq, Ord)]
116 #[cfg_attr(feature = "capture", derive(Serialize))]
117 #[cfg_attr(feature = "replay", derive(Deserialize))]
118 pub struct FrameId(usize);
121 /// Returns a FrameId corresponding to the first frame.
123 /// Note that we use 0 as the internal id here because the current code
124 /// increments the frame id at the beginning of the frame, rather than
125 /// at the end, and we want the first frame to be 1. It would probably
126 /// be sensible to move the advance() call to after frame-building, and
127 /// then make this method return FrameId(1).
128 pub fn first() -> Self {
132 /// Returns the backing usize for this FrameId.
133 pub fn as_usize(&self) -> usize {
137 /// Advances this FrameId to the next frame.
138 pub fn advance(&mut self) {
142 /// An invalid sentinel FrameId, which will always compare less than
143 /// any valid FrameId.
144 pub const INVALID: FrameId = FrameId(0);
147 impl Default for FrameId {
148 fn default() -> Self {
153 impl ::std::ops::Add<usize> for FrameId {
155 fn add(self, other: usize) -> FrameId {
156 FrameId(self.0 + other)
160 impl ::std::ops::Sub<usize> for FrameId {
162 fn sub(self, other: usize) -> FrameId {
163 assert!(self.0 >= other, "Underflow subtracting FrameIds");
164 FrameId(self.0 - other)
167 enum RenderBackendStatus {
170 ShutDown(Option<Sender<()>>),
173 /// Identifier to track a sequence of frames.
175 /// This is effectively a `FrameId` with a ridealong timestamp corresponding
176 /// to when advance() was called, which allows for more nuanced cache eviction
177 /// decisions. As such, we use the `FrameId` for equality and comparison, since
178 /// we should never have two `FrameStamps` with the same id but different
180 #[derive(Copy, Clone, Debug, MallocSizeOf)]
181 #[cfg_attr(feature = "capture", derive(Serialize))]
182 #[cfg_attr(feature = "replay", derive(Deserialize))]
183 pub struct FrameStamp {
186 document_id: DocumentId,
189 impl Eq for FrameStamp {}
191 impl PartialEq for FrameStamp {
192 fn eq(&self, other: &Self) -> bool {
193 // We should not be checking equality unless the documents are the same
194 debug_assert!(self.document_id == other.document_id);
199 impl PartialOrd for FrameStamp {
200 fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
201 self.id.partial_cmp(&other.id)
206 /// Gets the FrameId in this stamp.
207 pub fn frame_id(&self) -> FrameId {
211 /// Gets the time associated with this FrameStamp.
212 pub fn time(&self) -> SystemTime {
216 /// Gets the DocumentId in this stamp.
217 pub fn document_id(&self) -> DocumentId {
221 pub fn is_valid(&self) -> bool {
222 // If any fields are their default values, the whole struct should equal INVALID
223 debug_assert!((self.time != UNIX_EPOCH && self.id != FrameId(0) && self.document_id != DocumentId::INVALID) ||
224 *self == Self::INVALID);
225 self.document_id != DocumentId::INVALID
228 /// Returns a FrameStamp corresponding to the first frame.
229 pub fn first(document_id: DocumentId) -> Self {
231 id: FrameId::first(),
232 time: SystemTime::now(),
237 /// Advances to a new frame.
238 pub fn advance(&mut self) {
240 self.time = SystemTime::now();
243 /// An invalid sentinel FrameStamp.
244 pub const INVALID: FrameStamp = FrameStamp {
247 document_id: DocumentId::INVALID,
251 macro_rules! declare_data_stores {
252 ( $( $name:ident : $ty:ty, )+ ) => {
253 /// A collection of resources that are shared by clips, primitives
254 /// between display lists.
255 #[cfg_attr(feature = "capture", derive(Serialize))]
256 #[cfg_attr(feature = "replay", derive(Deserialize))]
258 pub struct DataStores {
260 pub $name: DataStore<$ty>,
265 /// Reports CPU heap usage.
266 fn report_memory(&self, ops: &mut MallocSizeOfOps, r: &mut MemoryReport) {
268 r.interning.data_stores.$name += self.$name.size_of(ops);
274 updates: InternerUpdates,
275 profile: &mut TransactionProfile,
278 self.$name.apply_updates(
288 crate::enumerate_interners!(declare_data_stores);
291 /// Returns the local rect for a primitive. For most primitives, this is
292 /// stored in the template. For pictures, this is stored inside the picture
293 /// primitive instance itself, since this is determined during frame building.
294 pub fn get_local_prim_rect(
296 prim_instance: &PrimitiveInstance,
297 prim_store: &PrimitiveStore,
299 match prim_instance.kind {
300 PrimitiveInstanceKind::Picture { pic_index, .. } => {
301 let pic = &prim_store.pictures[pic_index.0];
302 pic.precise_local_rect
305 self.as_common_data(prim_instance).prim_rect
310 /// Returns true if this primitive might need repition.
311 // TODO(gw): This seems like the wrong place for this - maybe this flag should
312 // not be in the common prim template data?
313 pub fn prim_may_need_repetition(
315 prim_instance: &PrimitiveInstance,
317 match prim_instance.kind {
318 PrimitiveInstanceKind::Picture { .. } => {
322 self.as_common_data(prim_instance).may_need_repetition
327 pub fn as_common_data(
329 prim_inst: &PrimitiveInstance
330 ) -> &PrimTemplateCommonData {
331 match prim_inst.kind {
332 PrimitiveInstanceKind::Rectangle { data_handle, .. } |
333 PrimitiveInstanceKind::Clear { data_handle, .. } => {
334 let prim_data = &self.prim[data_handle];
337 PrimitiveInstanceKind::Image { data_handle, .. } => {
338 let prim_data = &self.image[data_handle];
341 PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
342 let prim_data = &self.image_border[data_handle];
345 PrimitiveInstanceKind::LineDecoration { data_handle, .. } => {
346 let prim_data = &self.line_decoration[data_handle];
349 PrimitiveInstanceKind::LinearGradient { data_handle, .. }
350 | PrimitiveInstanceKind::CachedLinearGradient { data_handle, .. } => {
351 let prim_data = &self.linear_grad[data_handle];
354 PrimitiveInstanceKind::NormalBorder { data_handle, .. } => {
355 let prim_data = &self.normal_border[data_handle];
358 PrimitiveInstanceKind::Picture { .. } => {
359 panic!("BUG: picture prims don't have common data!");
361 PrimitiveInstanceKind::RadialGradient { data_handle, .. } => {
362 let prim_data = &self.radial_grad[data_handle];
365 PrimitiveInstanceKind::ConicGradient { data_handle, .. } => {
366 let prim_data = &self.conic_grad[data_handle];
369 PrimitiveInstanceKind::TextRun { data_handle, .. } => {
370 let prim_data = &self.text_run[data_handle];
373 PrimitiveInstanceKind::YuvImage { data_handle, .. } => {
374 let prim_data = &self.yuv_image[data_handle];
377 PrimitiveInstanceKind::Backdrop { data_handle, .. } => {
378 let prim_data = &self.backdrop[data_handle];
386 pub struct ScratchBuffer {
387 pub primitive: PrimitiveScratchBuffer,
388 pub picture: PictureScratchBuffer,
389 pub frame: FrameScratchBuffer,
390 pub clip_store: ClipStoreScratchBuffer,
394 pub fn begin_frame(&mut self) {
395 self.primitive.begin_frame();
396 self.picture.begin_frame();
397 self.frame.begin_frame();
400 pub fn recycle(&mut self, recycler: &mut Recycler) {
401 self.primitive.recycle(recycler);
402 self.picture.recycle(recycler);
403 self.frame.recycle(recycler);
406 pub fn memory_pressure(&mut self) {
407 // TODO: causes browser chrome test crashes on windows.
408 //self.primitive = Default::default();
409 self.picture = Default::default();
410 self.frame = Default::default();
411 self.clip_store = Default::default();
416 /// The id of this document
419 /// Temporary list of removed pipelines received from the scene builder
420 /// thread and forwarded to the renderer.
421 removed_pipelines: Vec<(PipelineId, DocumentId)>,
425 /// The id and time of the current frame.
428 /// The latest built scene, usable to build frames.
429 /// received from the scene builder thread.
432 /// The builder object that prodces frames, kept around to preserve some retained state.
433 frame_builder: FrameBuilder,
435 /// Allows graphs of render tasks to be created, and then built into an immutable graph output.
436 rg_builder: RenderTaskGraphBuilder,
438 /// A data structure to allow hit testing against rendered frames. This is updated
439 /// every time we produce a fully rendered frame.
440 hit_tester: Option<Arc<HitTester>>,
441 /// To avoid synchronous messaging we update a shared hit-tester that other threads
443 shared_hit_tester: Arc<SharedHitTester>,
445 /// Properties that are resolved during frame building and can be changed at any time
446 /// without requiring the scene to be re-built.
447 dynamic_properties: SceneProperties,
449 /// Track whether the last built frame is up to date or if it will need to be re-built
450 /// before rendering again.
451 frame_is_valid: bool,
452 hit_tester_is_valid: bool,
453 rendered_frame_is_valid: bool,
454 /// We track this information to be able to display debugging information from the
456 has_built_scene: bool,
458 data_stores: DataStores,
460 /// Contains various vecs of data that is used only during frame building,
461 /// where we want to recycle the memory each new display list, to avoid constantly
462 /// re-allocating and moving memory around.
463 scratch: ScratchBuffer,
465 #[cfg(feature = "replay")]
468 /// Tracks the state of the picture cache tiles that were composited on the previous frame.
469 prev_composite_descriptor: CompositeDescriptor,
471 /// Tracks if we need to invalidate dirty rects for this document, due to the picture
472 /// cache slice configuration having changed when a new scene is swapped in.
473 dirty_rects_are_valid: bool,
475 profile: TransactionProfile,
476 frame_stats: Option<FullFrameStats>,
483 default_device_pixel_ratio: f32,
487 removed_pipelines: Vec::new(),
490 device_rect: size.into(),
491 page_zoom_factor: 1.0,
492 device_pixel_ratio: default_device_pixel_ratio,
493 quality_settings: QualitySettings::default(),
496 pan: DeviceIntPoint::new(0, 0),
497 pinch_zoom_factor: 1.0,
500 stamp: FrameStamp::first(id),
501 scene: BuiltScene::empty(),
502 frame_builder: FrameBuilder::new(),
504 shared_hit_tester: Arc::new(SharedHitTester::new()),
505 dynamic_properties: SceneProperties::new(),
506 frame_is_valid: false,
507 hit_tester_is_valid: false,
508 rendered_frame_is_valid: false,
509 has_built_scene: false,
510 data_stores: DataStores::default(),
511 scratch: ScratchBuffer::default(),
512 #[cfg(feature = "replay")]
513 loaded_scene: Scene::new(),
514 prev_composite_descriptor: CompositeDescriptor::empty(),
515 dirty_rects_are_valid: true,
516 profile: TransactionProfile::new(),
517 rg_builder: RenderTaskGraphBuilder::new(),
522 fn can_render(&self) -> bool {
523 self.scene.has_root_pipeline
526 fn has_pixels(&self) -> bool {
527 !self.view.scene.device_rect.size.is_empty()
530 fn process_frame_msg(
535 FrameMsg::UpdateEpoch(pipeline_id, epoch) => {
536 self.scene.pipeline_epochs.insert(pipeline_id, epoch);
538 FrameMsg::HitTest(pipeline_id, point, tx) => {
539 if !self.hit_tester_is_valid {
540 self.rebuild_hit_tester();
543 let result = match self.hit_tester {
544 Some(ref hit_tester) => {
545 hit_tester.hit_test(HitTest::new(pipeline_id, point))
547 None => HitTestResult { items: Vec::new() },
550 tx.send(result).unwrap();
552 FrameMsg::RequestHitTester(tx) => {
553 tx.send(self.shared_hit_tester.clone()).unwrap();
555 FrameMsg::SetPan(pan) => {
556 if self.view.frame.pan != pan {
557 self.view.frame.pan = pan;
558 self.hit_tester_is_valid = false;
559 self.frame_is_valid = false;
562 FrameMsg::ScrollNodeWithId(origin, id, clamp) => {
563 profile_scope!("ScrollNodeWithScrollId");
565 if self.scroll_node(origin, id, clamp) {
566 self.hit_tester_is_valid = false;
567 self.frame_is_valid = false;
575 FrameMsg::GetScrollNodeState(tx) => {
576 profile_scope!("GetScrollNodeState");
577 tx.send(self.scene.spatial_tree.get_scroll_node_state()).unwrap();
579 FrameMsg::UpdateDynamicProperties(property_bindings) => {
580 self.dynamic_properties.set_properties(property_bindings);
582 FrameMsg::AppendDynamicTransformProperties(property_bindings) => {
583 self.dynamic_properties.add_transforms(property_bindings);
585 FrameMsg::SetPinchZoom(factor) => {
586 if self.view.frame.pinch_zoom_factor != factor.get() {
587 self.view.frame.pinch_zoom_factor = factor.get();
588 self.frame_is_valid = false;
591 FrameMsg::SetIsTransformAsyncZooming(is_zooming, animation_id) => {
592 let node = self.scene.spatial_tree.spatial_nodes.iter_mut()
593 .find(|node| node.is_transform_bound_to_property(animation_id));
594 if let Some(node) = node {
595 if node.is_async_zooming != is_zooming {
596 node.is_async_zooming = is_zooming;
597 self.frame_is_valid = false;
608 resource_cache: &mut ResourceCache,
609 gpu_cache: &mut GpuCache,
610 debug_flags: DebugFlags,
611 tile_cache_logger: &mut TileCacheLogger,
612 tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
613 frame_stats: Option<FullFrameStats>
614 ) -> RenderedDocument {
615 let frame_build_start_time = precise_time_ns();
617 let accumulated_scale_factor = self.view.accumulated_scale_factor();
618 let pan = self.view.frame.pan.to_f32() / accumulated_scale_factor;
620 // Advance to the next frame.
621 self.stamp.advance();
623 assert!(self.stamp.frame_id() != FrameId::INVALID,
624 "First frame increment must happen before build_frame()");
627 let frame = self.frame_builder.build(
631 &mut self.rg_builder,
633 accumulated_scale_factor,
634 self.view.scene.device_rect.origin,
636 &self.dynamic_properties,
637 &mut self.data_stores,
642 self.dirty_rects_are_valid,
649 self.frame_is_valid = true;
650 self.dirty_rects_are_valid = true;
652 let is_new_scene = self.has_built_scene;
653 self.has_built_scene = false;
655 let frame_build_time_ms =
656 profiler::ns_to_ms(precise_time_ns() - frame_build_start_time);
657 self.profile.set(profiler::FRAME_BUILDING_TIME, frame_build_time_ms);
659 let frame_stats = frame_stats.map(|mut stats| {
660 stats.frame_build_time += frame_build_time_ms;
667 profile: self.profile.take_and_reset(),
668 frame_stats: frame_stats
672 fn rebuild_hit_tester(&mut self) {
673 let accumulated_scale_factor = self.view.accumulated_scale_factor();
674 let pan = self.view.frame.pan.to_f32() / accumulated_scale_factor;
676 self.scene.spatial_tree.update_tree(
678 accumulated_scale_factor,
679 &self.dynamic_properties,
682 let hit_tester = Arc::new(self.scene.create_hit_tester());
683 self.hit_tester = Some(Arc::clone(&hit_tester));
684 self.shared_hit_tester.update(hit_tester);
685 self.hit_tester_is_valid = true;
688 pub fn updated_pipeline_info(&mut self) -> PipelineInfo {
689 let removed_pipelines = self.removed_pipelines.take_and_preallocate();
691 epochs: self.scene.pipeline_epochs.iter()
692 .map(|(&pipeline_id, &epoch)| ((pipeline_id, self.id), epoch)).collect(),
697 /// Returns true if the node actually changed position or false otherwise.
701 id: ExternalScrollId,
702 clamp: ScrollClamping
704 self.scene.spatial_tree.scroll_node(origin, id, clamp)
707 /// Update the state of tile caches when a new scene is being swapped in to
708 /// the render backend. Retain / reuse existing caches if possible, and
709 /// destroy any now unused caches.
710 fn update_tile_caches_for_new_scene(
712 mut requested_tile_caches: FastHashMap<SliceId, TileCacheParams>,
713 tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
714 resource_cache: &mut ResourceCache,
716 let mut new_tile_caches = FastHashMap::default();
717 new_tile_caches.reserve(requested_tile_caches.len());
719 // Step through the tile caches that are needed for the new scene, and see
720 // if we have an existing cache that can be reused.
721 for (slice_id, params) in requested_tile_caches.drain() {
722 let tile_cache = match tile_caches.remove(&slice_id) {
723 Some(mut existing_tile_cache) => {
724 // Found an existing cache - update the cache params and reuse it
725 existing_tile_cache.prepare_for_new_scene(
732 // No cache exists so create a new one
733 Box::new(TileCacheInstance::new(params))
737 new_tile_caches.insert(slice_id, tile_cache);
740 // Replace current tile cache map, and return what was left over,
741 // which are now unused.
742 let unused_tile_caches = mem::replace(
747 if !unused_tile_caches.is_empty() {
748 // If the slice configuration changed, assume we can't rely on the
749 // current dirty rects for next composite
750 self.dirty_rects_are_valid = false;
752 // Destroy any native surfaces allocated by these unused caches
753 for (_, tile_cache) in unused_tile_caches {
754 tile_cache.destroy(resource_cache);
759 pub fn new_async_scene_ready(
761 mut built_scene: BuiltScene,
762 recycler: &mut Recycler,
763 tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
764 resource_cache: &mut ResourceCache,
766 self.frame_is_valid = false;
767 self.hit_tester_is_valid = false;
769 self.update_tile_caches_for_new_scene(
770 mem::replace(&mut built_scene.tile_cache_config.tile_caches, FastHashMap::default()),
775 let old_scrolling_states = self.scene.spatial_tree.drain();
776 self.scene = built_scene;
777 self.scratch.recycle(recycler);
778 self.scene.spatial_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
794 /// The unique id for WR resource identification.
795 /// The namespace_id should start from 1.
796 static NEXT_NAMESPACE_ID: AtomicUsize = AtomicUsize::new(1);
798 #[cfg(any(feature = "capture", feature = "replay"))]
799 #[cfg_attr(feature = "capture", derive(Serialize))]
800 #[cfg_attr(feature = "replay", derive(Deserialize))]
801 struct PlainRenderBackend {
802 default_device_pixel_ratio: f32,
803 frame_config: FrameBuilderConfig,
804 documents: FastHashMap<DocumentId, DocumentView>,
805 resource_sequence_id: u32,
808 /// The render backend is responsible for transforming high level display lists into
809 /// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
811 /// The render backend operates on its own thread.
812 pub struct RenderBackend {
813 api_rx: Receiver<ApiMsg>,
814 result_tx: Sender<ResultMsg>,
815 scene_tx: Sender<SceneBuilderRequest>,
817 default_device_pixel_ratio: f32,
820 resource_cache: ResourceCache,
822 frame_config: FrameBuilderConfig,
823 default_compositor_kind: CompositorKind,
824 documents: FastHashMap<DocumentId, Document>,
826 notifier: Box<dyn RenderNotifier>,
827 tile_cache_logger: TileCacheLogger,
828 sampler: Option<Box<dyn AsyncPropertySampler + Send>>,
829 size_of_ops: Option<MallocSizeOfOps>,
830 debug_flags: DebugFlags,
831 namespace_alloc_by_client: bool,
833 // We keep one around to be able to call clear_namespace
834 // after the api object is deleted. For most purposes the
835 // api object's blob handler should be used instead.
836 blob_image_handler: Option<Box<dyn BlobImageHandler>>,
840 #[cfg(feature = "capture")]
841 /// If `Some`, do 'sequence capture' logging, recording updated documents,
842 /// frames, etc. This is set only through messages from the scene builder,
843 /// so all control of sequence capture goes through there.
844 capture_config: Option<CaptureConfig>,
846 #[cfg(feature = "replay")]
847 loaded_resource_sequence_id: u32,
849 /// A map of tile caches. These are stored in the backend as they are
850 /// persisted between both frame and scenes.
851 tile_caches: FastHashMap<SliceId, Box<TileCacheInstance>>,
856 api_rx: Receiver<ApiMsg>,
857 result_tx: Sender<ResultMsg>,
858 scene_tx: Sender<SceneBuilderRequest>,
859 default_device_pixel_ratio: f32,
860 resource_cache: ResourceCache,
861 notifier: Box<dyn RenderNotifier>,
862 blob_image_handler: Option<Box<dyn BlobImageHandler>>,
863 frame_config: FrameBuilderConfig,
864 sampler: Option<Box<dyn AsyncPropertySampler + Send>>,
865 size_of_ops: Option<MallocSizeOfOps>,
866 debug_flags: DebugFlags,
867 namespace_alloc_by_client: bool,
873 default_device_pixel_ratio,
875 gpu_cache: GpuCache::new(),
877 default_compositor_kind : frame_config.compositor_kind,
878 documents: FastHashMap::default(),
880 tile_cache_logger: TileCacheLogger::new(500usize),
884 namespace_alloc_by_client,
885 recycler: Recycler::new(),
887 #[cfg(feature = "capture")]
888 capture_config: None,
889 #[cfg(feature = "replay")]
890 loaded_resource_sequence_id: 0,
891 tile_caches: FastHashMap::default(),
895 fn next_namespace_id(&self) -> IdNamespace {
896 IdNamespace(NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed) as u32)
899 pub fn run(&mut self) {
900 let mut frame_counter: u32 = 0;
901 let mut status = RenderBackendStatus::Continue;
903 if let Some(ref sampler) = self.sampler {
907 while let RenderBackendStatus::Continue = status {
908 status = match self.api_rx.recv() {
910 self.process_api_msg(msg, &mut frame_counter)
912 Err(..) => { RenderBackendStatus::ShutDown(None) }
916 if let RenderBackendStatus::StopRenderBackend = status {
917 while let Ok(msg) = self.api_rx.recv() {
919 ApiMsg::SceneBuilderResult(SceneBuilderResult::ExternalEvent(evt)) => {
920 self.notifier.external_event(evt);
922 ApiMsg::SceneBuilderResult(SceneBuilderResult::FlushComplete(tx)) => {
923 // If somebody's blocked waiting for a flush, how did they
924 // trigger the RB thread to shut down? This shouldn't happen
925 // but handle it gracefully anyway.
926 debug_assert!(false);
929 ApiMsg::SceneBuilderResult(SceneBuilderResult::ShutDown(sender)) => {
930 info!("Recycling stats: {:?}", self.recycler);
931 status = RenderBackendStatus::ShutDown(sender);
939 // Ensure we read everything the scene builder is sending us from
940 // inflight messages, otherwise the scene builder might panic.
941 while let Ok(msg) = self.api_rx.try_recv() {
943 ApiMsg::SceneBuilderResult(SceneBuilderResult::FlushComplete(tx)) => {
944 // If somebody's blocked waiting for a flush, how did they
945 // trigger the RB thread to shut down? This shouldn't happen
946 // but handle it gracefully anyway.
947 debug_assert!(false);
954 self.documents.clear();
956 self.notifier.shut_down();
958 if let Some(ref sampler) = self.sampler {
959 sampler.deregister();
963 if let RenderBackendStatus::ShutDown(Some(sender)) = status {
964 let _ = sender.send(());
968 fn process_transaction(
970 mut txns: Vec<Box<BuiltTransaction>>,
971 result_tx: Option<Sender<SceneSwapResult>>,
972 frame_counter: &mut u32,
974 self.prepare_for_frames();
975 self.maybe_force_nop_documents(
977 |document_id| txns.iter().any(|txn| txn.document_id == document_id));
979 let mut built_frame = false;
980 for mut txn in txns.drain(..) {
981 let has_built_scene = txn.built_scene.is_some();
983 if let Some(doc) = self.documents.get_mut(&txn.document_id) {
984 doc.removed_pipelines.append(&mut txn.removed_pipelines);
985 doc.view.scene = txn.view;
986 doc.profile.merge(&mut txn.profile);
988 doc.frame_stats = if let Some(stats) = &doc.frame_stats {
989 Some(stats.merge(&txn.frame_stats))
991 Some(txn.frame_stats)
994 if let Some(built_scene) = txn.built_scene.take() {
995 doc.new_async_scene_ready(
998 &mut self.tile_caches,
999 &mut self.resource_cache,
1003 // If there are any additions or removals of clip modes
1004 // during the scene build, apply them to the data store now.
1005 // This needs to happen before we build the hit tester.
1006 if let Some(updates) = txn.interner_updates.take() {
1007 #[cfg(feature = "capture")]
1009 if self.debug_flags.contains(DebugFlags::TILE_CACHE_LOGGING_DBG) {
1010 self.tile_cache_logger.serialize_updates(&updates);
1013 doc.data_stores.apply_updates(updates, &mut doc.profile);
1016 // Build the hit tester while the APZ lock is held so that its content
1017 // is in sync with the gecko APZ tree.
1018 if !doc.hit_tester_is_valid {
1019 doc.rebuild_hit_tester();
1022 if let Some(ref tx) = result_tx {
1023 let (resume_tx, resume_rx) = single_msg_channel();
1024 tx.send(SceneSwapResult::Complete(resume_tx)).unwrap();
1025 // Block until the post-swap hook has completed on
1026 // the scene builder thread. We need to do this before
1027 // we can sample from the sampler hook which might happen
1028 // in the update_document call below.
1029 resume_rx.recv().ok();
1032 for pipeline_id in &txn.discard_frame_state_for_pipelines {
1035 .discard_frame_state_for_pipeline(*pipeline_id);
1038 self.resource_cache.add_rasterized_blob_images(
1039 txn.rasterized_blobs.take(),
1044 // The document was removed while we were building it, skip it.
1045 // TODO: we might want to just ensure that removed documents are
1046 // always forwarded to the scene builder thread to avoid this case.
1047 if let Some(ref tx) = result_tx {
1048 tx.send(SceneSwapResult::Aborted).unwrap();
1053 built_frame |= self.update_document(
1055 txn.resource_updates.take(),
1056 txn.frame_ops.take(),
1057 txn.notifications.take(),
1060 txn.invalidate_rendered_frame,
1072 frame_counter: &mut u32,
1073 ) -> RenderBackendStatus {
1075 ApiMsg::CloneApi(sender) => {
1076 assert!(!self.namespace_alloc_by_client);
1077 sender.send(self.next_namespace_id()).unwrap();
1079 ApiMsg::CloneApiByClient(namespace_id) => {
1080 assert!(self.namespace_alloc_by_client);
1081 debug_assert!(!self.documents.iter().any(|(did, _doc)| did.namespace_id == namespace_id));
1083 ApiMsg::AddDocument(document_id, initial_size) => {
1084 let document = Document::new(
1087 self.default_device_pixel_ratio,
1089 let old = self.documents.insert(document_id, document);
1090 debug_assert!(old.is_none());
1092 ApiMsg::MemoryPressure => {
1093 // This is drastic. It will basically flush everything out of the cache,
1094 // and the next frame will have to rebuild all of its resources.
1095 // We may want to look into something less extreme, but on the other hand this
1096 // should only be used in situations where are running low enough on memory
1097 // that we risk crashing if we don't do something about it.
1098 // The advantage of clearing the cache completely is that it gets rid of any
1099 // remaining fragmentation that could have persisted if we kept around the most
1100 // recently used resources.
1101 self.resource_cache.clear(ClearCache::all());
1103 self.gpu_cache.clear();
1105 for (_, doc) in &mut self.documents {
1106 doc.scratch.memory_pressure();
1109 let resource_updates = self.resource_cache.pending_updates();
1110 let msg = ResultMsg::UpdateResources {
1112 memory_pressure: true,
1114 self.result_tx.send(msg).unwrap();
1115 self.notifier.wake_up(false);
1117 ApiMsg::ReportMemory(tx) => {
1118 self.report_memory(tx);
1120 ApiMsg::DebugCommand(option) => {
1121 let msg = match option {
1122 DebugCommand::EnableDualSourceBlending(enable) => {
1123 // Set in the config used for any future documents
1124 // that are created.
1126 .dual_source_blending_is_enabled = enable;
1127 self.update_frame_builder_config();
1129 // We don't want to forward this message to the renderer.
1130 return RenderBackendStatus::Continue;
1132 DebugCommand::SetPictureTileSize(tile_size) => {
1133 self.frame_config.tile_size_override = tile_size;
1134 self.update_frame_builder_config();
1136 return RenderBackendStatus::Continue;
1138 #[cfg(feature = "capture")]
1139 DebugCommand::SaveCapture(root, bits) => {
1140 let output = self.save_capture(root, bits);
1141 ResultMsg::DebugOutput(output)
1143 #[cfg(feature = "capture")]
1144 DebugCommand::StartCaptureSequence(root, bits) => {
1145 self.start_capture_sequence(root, bits);
1146 return RenderBackendStatus::Continue;
1148 #[cfg(feature = "capture")]
1149 DebugCommand::StopCaptureSequence => {
1150 self.stop_capture_sequence();
1151 return RenderBackendStatus::Continue;
1153 #[cfg(feature = "replay")]
1154 DebugCommand::LoadCapture(path, ids, tx) => {
1155 NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed);
1156 *frame_counter += 1;
1158 let mut config = CaptureConfig::new(path, CaptureBits::all());
1159 if let Some((scene_id, frame_id)) = ids {
1160 config.scene_id = scene_id;
1161 config.frame_id = frame_id;
1164 self.load_capture(config);
1166 for (id, doc) in &self.documents {
1167 let captured = CapturedDocument {
1169 root_pipeline_id: doc.loaded_scene.root_pipeline_id,
1171 tx.send(captured).unwrap();
1174 // Note: we can't pass `LoadCapture` here since it needs to arrive
1175 // before the `PublishDocument` messages sent by `load_capture`.
1176 return RenderBackendStatus::Continue;
1178 DebugCommand::ClearCaches(mask) => {
1179 self.resource_cache.clear(mask);
1180 return RenderBackendStatus::Continue;
1182 DebugCommand::EnableNativeCompositor(enable) => {
1183 // Default CompositorKind should be Native
1184 if let CompositorKind::Draw { .. } = self.default_compositor_kind {
1188 let compositor_kind = if enable {
1189 self.default_compositor_kind
1191 CompositorKind::default()
1194 for (_, doc) in &mut self.documents {
1195 doc.scene.config.compositor_kind = compositor_kind;
1196 doc.frame_is_valid = false;
1199 self.frame_config.compositor_kind = compositor_kind;
1200 self.update_frame_builder_config();
1202 // We don't want to forward this message to the renderer.
1203 return RenderBackendStatus::Continue;
1205 DebugCommand::EnableMultithreading(enable) => {
1206 self.resource_cache.enable_multithreading(enable);
1207 return RenderBackendStatus::Continue;
1209 DebugCommand::SetBatchingLookback(count) => {
1210 self.frame_config.batch_lookback_count = count as usize;
1211 self.update_frame_builder_config();
1213 return RenderBackendStatus::Continue;
1215 DebugCommand::SimulateLongSceneBuild(time_ms) => {
1216 let _ = self.scene_tx.send(SceneBuilderRequest::SimulateLongSceneBuild(time_ms));
1217 return RenderBackendStatus::Continue;
1219 DebugCommand::SetFlags(flags) => {
1220 self.resource_cache.set_debug_flags(flags);
1221 self.gpu_cache.set_debug_flags(flags);
1223 let force_invalidation = flags.contains(DebugFlags::FORCE_PICTURE_INVALIDATION);
1224 if self.frame_config.force_invalidation != force_invalidation {
1225 self.frame_config.force_invalidation = force_invalidation;
1226 self.update_frame_builder_config();
1229 // If we're toggling on the GPU cache debug display, we
1230 // need to blow away the cache. This is because we only
1231 // send allocation/free notifications to the renderer
1232 // thread when the debug display is enabled, and thus
1233 // enabling it when the cache is partially populated will
1234 // give the renderer an incomplete view of the world.
1235 // And since we might as well drop all the debugging state
1236 // from the renderer when we disable the debug display,
1237 // we just clear the cache on toggle.
1238 let changed = self.debug_flags ^ flags;
1239 if changed.contains(DebugFlags::GPU_CACHE_DBG) {
1240 self.gpu_cache.clear();
1242 self.debug_flags = flags;
1244 ResultMsg::DebugCommand(option)
1246 _ => ResultMsg::DebugCommand(option),
1248 self.result_tx.send(msg).unwrap();
1249 self.notifier.wake_up(true);
1251 ApiMsg::UpdateDocuments(transaction_msgs) => {
1252 self.prepare_transactions(
1257 ApiMsg::SceneBuilderResult(msg) => {
1258 return self.process_scene_builder_result(msg, frame_counter);
1262 RenderBackendStatus::Continue
1265 fn process_scene_builder_result(
1267 msg: SceneBuilderResult,
1268 frame_counter: &mut u32,
1269 ) -> RenderBackendStatus {
1270 profile_scope!("sb_msg");
1273 SceneBuilderResult::Transactions(txns, result_tx) => {
1274 self.process_transaction(
1279 self.bookkeep_after_frames();
1281 #[cfg(feature = "capture")]
1282 SceneBuilderResult::CapturedTransactions(txns, capture_config, result_tx) => {
1283 if let Some(ref mut old_config) = self.capture_config {
1284 assert!(old_config.scene_id <= capture_config.scene_id);
1285 if old_config.scene_id < capture_config.scene_id {
1286 old_config.scene_id = capture_config.scene_id;
1287 old_config.frame_id = 0;
1290 self.capture_config = Some(capture_config);
1293 let built_frame = self.process_transaction(
1300 self.save_capture_sequence();
1303 self.bookkeep_after_frames();
1305 #[cfg(feature = "capture")]
1306 SceneBuilderResult::StopCaptureSequence => {
1307 self.capture_config = None;
1309 SceneBuilderResult::GetGlyphDimensions(request) => {
1310 let mut glyph_dimensions = Vec::with_capacity(request.glyph_indices.len());
1311 if let Some(base) = self.resource_cache.get_font_instance(request.key) {
1312 let font = FontInstance::from_base(Arc::clone(&base));
1313 for glyph_index in &request.glyph_indices {
1314 let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, *glyph_index);
1315 glyph_dimensions.push(glyph_dim);
1318 request.sender.send(glyph_dimensions).unwrap();
1320 SceneBuilderResult::GetGlyphIndices(request) => {
1321 let mut glyph_indices = Vec::with_capacity(request.text.len());
1322 for ch in request.text.chars() {
1323 let index = self.resource_cache.get_glyph_index(request.key, ch);
1324 glyph_indices.push(index);
1326 request.sender.send(glyph_indices).unwrap();
1328 SceneBuilderResult::FlushComplete(tx) => {
1331 SceneBuilderResult::ExternalEvent(evt) => {
1332 self.notifier.external_event(evt);
1334 SceneBuilderResult::ClearNamespace(id) => {
1335 self.resource_cache.clear_namespace(id);
1336 self.documents.retain(|doc_id, _doc| doc_id.namespace_id != id);
1337 if let Some(handler) = &mut self.blob_image_handler {
1338 handler.clear_namespace(id);
1341 SceneBuilderResult::DeleteDocument(document_id) => {
1342 self.documents.remove(&document_id);
1344 SceneBuilderResult::StopRenderBackend => {
1345 return RenderBackendStatus::StopRenderBackend;
1347 SceneBuilderResult::ShutDown(sender) => {
1348 info!("Recycling stats: {:?}", self.recycler);
1349 return RenderBackendStatus::ShutDown(sender);
1353 RenderBackendStatus::Continue
1356 fn update_frame_builder_config(&self) {
1357 self.send_backend_message(
1358 SceneBuilderRequest::SetFrameBuilderConfig(
1359 self.frame_config.clone()
1364 fn prepare_for_frames(&mut self) {
1365 self.gpu_cache.prepare_for_frames();
1368 fn bookkeep_after_frames(&mut self) {
1369 self.gpu_cache.bookkeep_after_frames();
1372 fn requires_frame_build(&mut self) -> bool {
1373 self.gpu_cache.requires_frame_build()
1376 fn prepare_transactions(
1378 txns: Vec<Box<TransactionMsg>>,
1379 frame_counter: &mut u32,
1381 self.prepare_for_frames();
1382 self.maybe_force_nop_documents(
1384 |document_id| txns.iter().any(|txn| txn.document_id == document_id));
1386 let mut built_frame = false;
1387 for mut txn in txns {
1388 if txn.generate_frame.as_bool() {
1389 txn.profile.end_time(profiler::API_SEND_TIME);
1392 self.documents.get_mut(&txn.document_id).unwrap().profile.merge(&mut txn.profile);
1394 built_frame |= self.update_document(
1396 txn.resource_updates.take(),
1397 txn.frame_ops.take(),
1398 txn.notifications.take(),
1399 txn.generate_frame.as_bool(),
1400 txn.generate_frame.id(),
1401 txn.invalidate_rendered_frame,
1407 #[cfg(feature = "capture")]
1408 self.save_capture_sequence();
1410 self.bookkeep_after_frames();
1413 /// In certain cases, resources shared by multiple documents have to run
1414 /// maintenance operations, like cleaning up unused cache items. In those
1415 /// cases, we are forced to build frames for all documents, however we
1416 /// may not have a transaction ready for every document - this method
1417 /// calls update_document with the details of a fake, nop transaction just
1418 /// to force a frame build.
1419 fn maybe_force_nop_documents<F>(&mut self,
1420 frame_counter: &mut u32,
1421 document_already_present: F) where
1422 F: Fn(DocumentId) -> bool {
1423 if self.requires_frame_build() {
1424 let nop_documents : Vec<DocumentId> = self.documents.keys()
1426 .filter(|key| !document_already_present(*key))
1428 #[allow(unused_variables)]
1429 let mut built_frame = false;
1430 for &document_id in &nop_documents {
1431 built_frame |= self.update_document(
1442 #[cfg(feature = "capture")]
1444 true => self.save_capture_sequence(),
1452 document_id: DocumentId,
1453 resource_updates: Vec<ResourceUpdate>,
1454 mut frame_ops: Vec<FrameMsg>,
1455 mut notifications: Vec<NotificationRequest>,
1456 mut render_frame: bool,
1457 generated_frame_id: Option<u64>,
1458 invalidate_rendered_frame: bool,
1459 frame_counter: &mut u32,
1460 has_built_scene: bool,
1462 let requested_frame = render_frame;
1464 let requires_frame_build = self.requires_frame_build();
1465 let doc = self.documents.get_mut(&document_id).unwrap();
1467 // If we have a sampler, get more frame ops from it and add them
1468 // to the transaction. This is a hook to allow the WR user code to
1469 // fiddle with things after a potentially long scene build, but just
1470 // before rendering. This is useful for rendering with the latest
1471 // async transforms.
1472 if requested_frame {
1473 if let Some(ref sampler) = self.sampler {
1474 frame_ops.append(&mut sampler.sample(document_id, generated_frame_id));
1478 doc.has_built_scene |= has_built_scene;
1480 // TODO: this scroll variable doesn't necessarily mean we scrolled. It is only used
1481 // for something wrench specific and we should remove it.
1482 let mut scroll = false;
1483 for frame_msg in frame_ops {
1484 let op = doc.process_frame_msg(frame_msg);
1485 scroll |= op.scroll;
1488 for update in &resource_updates {
1489 if let ResourceUpdate::UpdateImage(..) = update {
1490 doc.frame_is_valid = false;
1494 self.resource_cache.post_scene_building_update(
1499 if doc.dynamic_properties.flush_pending_updates() {
1500 doc.frame_is_valid = false;
1501 doc.hit_tester_is_valid = false;
1504 if !doc.can_render() {
1505 // TODO: this happens if we are building the first scene asynchronously and
1506 // scroll at the same time. we should keep track of the fact that we skipped
1507 // composition here and do it as soon as we receive the scene.
1508 render_frame = false;
1511 // Avoid re-building the frame if the current built frame is still valid.
1512 // However, if the resource_cache requires a frame build, _always_ do that, unless
1513 // doc.can_render() is false, as in that case a frame build can't happen anyway.
1514 // We want to ensure we do this because even if the doc doesn't have pixels it
1515 // can still try to access stale texture cache items.
1516 let build_frame = (render_frame && !doc.frame_is_valid && doc.has_pixels()) ||
1517 (requires_frame_build && doc.can_render());
1519 // Request composite is true when we want to composite frame even when
1520 // there is no frame update. This happens when video frame is updated under
1521 // external image with NativeTexture or when platform requested to composite frame.
1522 if invalidate_rendered_frame {
1523 doc.rendered_frame_is_valid = false;
1524 if doc.scene.config.compositor_kind.should_redraw_on_invalidation() {
1525 let msg = ResultMsg::ForceRedraw;
1526 self.result_tx.send(msg).unwrap();
1530 let mut frame_build_time = None;
1532 profile_scope!("generate frame");
1534 *frame_counter += 1;
1536 // borrow ck hack for profile_counters
1537 let (pending_update, rendered_document) = {
1538 let frame_build_start_time = precise_time_ns();
1540 let frame_stats = doc.frame_stats.take();
1542 let rendered_document = doc.build_frame(
1543 &mut self.resource_cache,
1544 &mut self.gpu_cache,
1546 &mut self.tile_cache_logger,
1547 &mut self.tile_caches,
1551 debug!("generated frame for document {:?} with {} passes",
1552 document_id, rendered_document.frame.passes.len());
1554 let msg = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates());
1555 self.result_tx.send(msg).unwrap();
1557 frame_build_time = Some(precise_time_ns() - frame_build_start_time);
1559 let pending_update = self.resource_cache.pending_updates();
1560 (pending_update, rendered_document)
1563 // Build a small struct that represents the state of the tiles to be composited.
1564 let composite_descriptor = rendered_document
1570 // If there are texture cache updates to apply, or if the produced
1571 // frame is not a no-op, or the compositor state has changed,
1572 // then we cannot skip compositing this frame.
1573 if !pending_update.is_nop() ||
1574 !rendered_document.frame.is_nop() ||
1575 composite_descriptor != doc.prev_composite_descriptor {
1576 doc.rendered_frame_is_valid = false;
1578 doc.prev_composite_descriptor = composite_descriptor;
1580 #[cfg(feature = "capture")]
1581 match self.capture_config {
1582 Some(ref mut config) => {
1583 // FIXME(aosmond): document splitting causes multiple prepare frames
1584 config.prepare_frame();
1586 if config.bits.contains(CaptureBits::FRAME) {
1587 let file_name = format!("frame-{}-{}", document_id.namespace_id.0, document_id.id);
1588 config.serialize_for_frame(&rendered_document.frame, file_name);
1591 let data_stores_name = format!("data-stores-{}-{}", document_id.namespace_id.0, document_id.id);
1592 config.serialize_for_frame(&doc.data_stores, data_stores_name);
1594 let properties_name = format!("properties-{}-{}", document_id.namespace_id.0, document_id.id);
1595 config.serialize_for_frame(&doc.dynamic_properties, properties_name);
1600 let msg = ResultMsg::PublishPipelineInfo(doc.updated_pipeline_info());
1601 self.result_tx.send(msg).unwrap();
1603 // Publish the frame
1604 let msg = ResultMsg::PublishDocument(
1609 self.result_tx.send(msg).unwrap();
1610 } else if requested_frame {
1611 // WR-internal optimization to avoid doing a bunch of render work if
1612 // there's no pixels. We still want to pretend to render and request
1613 // a render to make sure that the callbacks (particularly the
1614 // new_frame_ready callback below) has the right flags.
1615 let msg = ResultMsg::PublishPipelineInfo(doc.updated_pipeline_info());
1616 self.result_tx.send(msg).unwrap();
1621 |n| { n.when() == Checkpoint::FrameBuilt },
1622 |n| { n.notify(); },
1625 if !notifications.is_empty() {
1626 self.result_tx.send(ResultMsg::AppendNotificationRequests(notifications)).unwrap();
1629 // Always forward the transaction to the renderer if a frame was requested,
1630 // otherwise gecko can get into a state where it waits (forever) for the
1631 // transaction to complete before sending new work.
1632 if requested_frame {
1633 // If rendered frame is already valid, there is no need to render frame.
1634 if doc.rendered_frame_is_valid {
1635 render_frame = false;
1636 } else if render_frame {
1637 doc.rendered_frame_is_valid = true;
1639 self.notifier.new_frame_ready(document_id, scroll, render_frame, frame_build_time);
1642 if !doc.hit_tester_is_valid {
1643 doc.rebuild_hit_tester();
1649 fn send_backend_message(&self, msg: SceneBuilderRequest) {
1650 self.scene_tx.send(msg).unwrap();
1653 fn report_memory(&mut self, tx: Sender<Box<MemoryReport>>) {
1654 let mut report = Box::new(MemoryReport::default());
1655 let ops = self.size_of_ops.as_mut().unwrap();
1656 let op = ops.size_of_op;
1657 report.gpu_cache_metadata = self.gpu_cache.size_of(ops);
1658 for doc in self.documents.values() {
1659 report.clip_stores += doc.scene.clip_store.size_of(ops);
1660 report.hit_testers += match &doc.hit_tester {
1661 Some(hit_tester) => hit_tester.size_of(ops),
1665 doc.data_stores.report_memory(ops, &mut report)
1668 (*report) += self.resource_cache.report_memory(op);
1669 report.texture_cache_structures = self.resource_cache
1671 .report_memory(ops);
1673 // Send a message to report memory on the scene-builder thread, which
1674 // will add its report to this one and send the result back to the original
1675 // thread waiting on the request.
1676 self.send_backend_message(
1677 SceneBuilderRequest::ReportMemory(report, tx)
1681 #[cfg(feature = "capture")]
1682 fn save_capture_sequence(&mut self) {
1683 if let Some(ref mut config) = self.capture_config {
1684 let deferred = self.resource_cache.save_capture_sequence(config);
1686 let backend = PlainRenderBackend {
1687 default_device_pixel_ratio: self.default_device_pixel_ratio,
1688 frame_config: self.frame_config.clone(),
1689 resource_sequence_id: config.resource_id,
1690 documents: self.documents
1692 .map(|(id, doc)| (*id, doc.view))
1695 config.serialize_for_frame(&backend, "backend");
1697 if !deferred.is_empty() {
1698 let msg = ResultMsg::DebugOutput(DebugOutput::SaveCapture(config.clone(), deferred));
1699 self.result_tx.send(msg).unwrap();
1705 impl RenderBackend {
1706 #[cfg(feature = "capture")]
1707 // Note: the mutable `self` is only needed here for resolving blob images
1714 use crate::render_task_graph::dump_render_tasks_as_svg;
1716 debug!("capture: saving {:?}", root);
1718 if let Err(e) = fs::create_dir_all(&root) {
1719 panic!("Unable to create capture dir: {:?}", e);
1722 let config = CaptureConfig::new(root, bits);
1724 if config.bits.contains(CaptureBits::FRAME) {
1725 self.prepare_for_frames();
1728 for (&id, doc) in &mut self.documents {
1729 debug!("\tdocument {:?}", id);
1730 if config.bits.contains(CaptureBits::FRAME) {
1731 let rendered_document = doc.build_frame(
1732 &mut self.resource_cache,
1733 &mut self.gpu_cache,
1735 &mut self.tile_cache_logger,
1736 &mut self.tile_caches,
1739 // After we rendered the frames, there are pending updates to both
1740 // GPU cache and resources. Instead of serializing them, we are going to make sure
1741 // they are applied on the `Renderer` side.
1742 let msg_update_gpu_cache = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates());
1743 self.result_tx.send(msg_update_gpu_cache).unwrap();
1744 //TODO: write down doc's pipeline info?
1745 // it has `pipeline_epoch_map`,
1746 // which may capture necessary details for some cases.
1747 let file_name = format!("frame-{}-{}", id.namespace_id.0, id.id);
1748 config.serialize_for_frame(&rendered_document.frame, file_name);
1749 let file_name = format!("spatial-{}-{}", id.namespace_id.0, id.id);
1750 config.serialize_tree_for_frame(&doc.scene.spatial_tree, file_name);
1751 let file_name = format!("built-primitives-{}-{}", id.namespace_id.0, id.id);
1752 config.serialize_for_frame(&doc.scene.prim_store, file_name);
1753 let file_name = format!("built-clips-{}-{}", id.namespace_id.0, id.id);
1754 config.serialize_for_frame(&doc.scene.clip_store, file_name);
1755 let file_name = format!("scratch-{}-{}", id.namespace_id.0, id.id);
1756 config.serialize_for_frame(&doc.scratch.primitive, file_name);
1757 let file_name = format!("render-tasks-{}-{}.svg", id.namespace_id.0, id.id);
1758 let mut render_tasks_file = fs::File::create(&config.file_path_for_frame(file_name, "svg"))
1759 .expect("Failed to open the SVG file.");
1760 dump_render_tasks_as_svg(
1761 &rendered_document.frame.render_tasks,
1762 &mut render_tasks_file
1765 let file_name = format!("texture-cache-color-linear-{}-{}.svg", id.namespace_id.0, id.id);
1766 let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg"))
1767 .expect("Failed to open the SVG file.");
1768 self.resource_cache.texture_cache.dump_color8_linear_as_svg(&mut texture_file).unwrap();
1770 let file_name = format!("texture-cache-color8-glyphs-{}-{}.svg", id.namespace_id.0, id.id);
1771 let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg"))
1772 .expect("Failed to open the SVG file.");
1773 self.resource_cache.texture_cache.dump_color8_glyphs_as_svg(&mut texture_file).unwrap();
1775 let file_name = format!("texture-cache-alpha8-glyphs-{}-{}.svg", id.namespace_id.0, id.id);
1776 let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg"))
1777 .expect("Failed to open the SVG file.");
1778 self.resource_cache.texture_cache.dump_alpha8_glyphs_as_svg(&mut texture_file).unwrap();
1780 let file_name = format!("texture-cache-alpha8-linear-{}-{}.svg", id.namespace_id.0, id.id);
1781 let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg"))
1782 .expect("Failed to open the SVG file.");
1783 self.resource_cache.texture_cache.dump_alpha8_linear_as_svg(&mut texture_file).unwrap();
1786 let data_stores_name = format!("data-stores-{}-{}", id.namespace_id.0, id.id);
1787 config.serialize_for_frame(&doc.data_stores, data_stores_name);
1789 let properties_name = format!("properties-{}-{}", id.namespace_id.0, id.id);
1790 config.serialize_for_frame(&doc.dynamic_properties, properties_name);
1793 if config.bits.contains(CaptureBits::FRAME) {
1794 // TODO: there is no guarantee that we won't hit this case, but we want to
1795 // report it here if we do. If we don't, it will simply crash in
1796 // Renderer::render_impl and give us less information about the source.
1797 assert!(!self.requires_frame_build(), "Caches were cleared during a capture.");
1798 self.bookkeep_after_frames();
1801 debug!("\tscene builder");
1802 self.send_backend_message(
1803 SceneBuilderRequest::SaveScene(config.clone())
1806 debug!("\tresource cache");
1807 let (resources, deferred) = self.resource_cache.save_capture(&config.root);
1809 if config.bits.contains(CaptureBits::TILE_CACHE) {
1810 debug!("\ttile cache");
1811 self.tile_cache_logger.save_capture(&config.root);
1815 let backend = PlainRenderBackend {
1816 default_device_pixel_ratio: self.default_device_pixel_ratio,
1817 frame_config: self.frame_config.clone(),
1818 resource_sequence_id: 0,
1819 documents: self.documents
1821 .map(|(id, doc)| (*id, doc.view))
1825 config.serialize_for_frame(&backend, "backend");
1826 config.serialize_for_frame(&resources, "plain-resources");
1828 if config.bits.contains(CaptureBits::FRAME) {
1829 let msg_update_resources = ResultMsg::UpdateResources {
1830 resource_updates: self.resource_cache.pending_updates(),
1831 memory_pressure: false,
1833 self.result_tx.send(msg_update_resources).unwrap();
1834 // Save the texture/glyph/image caches.
1835 info!("\tresource cache");
1836 let caches = self.resource_cache.save_caches(&config.root);
1837 config.serialize_for_resource(&caches, "resource_cache");
1838 info!("\tgpu cache");
1839 config.serialize_for_resource(&self.gpu_cache, "gpu_cache");
1842 DebugOutput::SaveCapture(config, deferred)
1845 #[cfg(feature = "capture")]
1846 fn start_capture_sequence(
1851 self.send_backend_message(
1852 SceneBuilderRequest::StartCaptureSequence(CaptureConfig::new(root, bits))
1856 #[cfg(feature = "capture")]
1857 fn stop_capture_sequence(
1860 self.send_backend_message(
1861 SceneBuilderRequest::StopCaptureSequence
1865 #[cfg(feature = "replay")]
1868 mut config: CaptureConfig,
1870 debug!("capture: loading {:?}", config.frame_root());
1871 let backend = config.deserialize_for_frame::<PlainRenderBackend, _>("backend")
1872 .expect("Unable to open backend.ron");
1874 // If this is a capture sequence, then the ID will be non-zero, and won't
1875 // match what is loaded, but for still captures, the ID will be zero.
1876 let first_load = backend.resource_sequence_id == 0;
1877 if self.loaded_resource_sequence_id != backend.resource_sequence_id || first_load {
1878 // FIXME(aosmond): We clear the documents because when we update the
1879 // resource cache, we actually wipe and reload, because we don't
1880 // know what is the same and what has changed. If we were to keep as
1881 // much of the resource cache state as possible, we could avoid
1882 // flushing the document state (which has its own dependecies on the
1885 // FIXME(aosmond): If we try to load the next capture in the
1886 // sequence too quickly, we may lose resources we depend on in the
1887 // current frame. This can cause panics. Ideally we would not
1888 // advance to the next frame until the FrameRendered event for all
1889 // of the pipelines.
1890 self.documents.clear();
1892 config.resource_id = backend.resource_sequence_id;
1893 self.loaded_resource_sequence_id = backend.resource_sequence_id;
1895 let plain_resources = config.deserialize_for_resource::<PlainResources, _>("plain-resources")
1896 .expect("Unable to open plain-resources.ron");
1897 let caches_maybe = config.deserialize_for_resource::<PlainCacheOwn, _>("resource_cache");
1899 // Note: it would be great to have `RenderBackend` to be split
1900 // rather explicitly on what's used before and after scene building
1901 // so that, for example, we never miss anything in the code below:
1903 let plain_externals = self.resource_cache.load_capture(
1909 let msg_load = ResultMsg::DebugOutput(
1910 DebugOutput::LoadCapture(config.clone(), plain_externals)
1912 self.result_tx.send(msg_load).unwrap();
1914 self.gpu_cache = match config.deserialize_for_resource::<GpuCache, _>("gpu_cache") {
1915 Some(gpu_cache) => gpu_cache,
1916 None => GpuCache::new(),
1920 self.default_device_pixel_ratio = backend.default_device_pixel_ratio;
1921 self.frame_config = backend.frame_config;
1923 let mut scenes_to_build = Vec::new();
1925 for (id, view) in backend.documents {
1926 debug!("\tdocument {:?}", id);
1927 let scene_name = format!("scene-{}-{}", id.namespace_id.0, id.id);
1928 let scene = config.deserialize_for_scene::<Scene, _>(&scene_name)
1929 .expect(&format!("Unable to open {}.ron", scene_name));
1931 let interners_name = format!("interners-{}-{}", id.namespace_id.0, id.id);
1932 let interners = config.deserialize_for_scene::<Interners, _>(&interners_name)
1933 .expect(&format!("Unable to open {}.ron", interners_name));
1935 let data_stores_name = format!("data-stores-{}-{}", id.namespace_id.0, id.id);
1936 let data_stores = config.deserialize_for_frame::<DataStores, _>(&data_stores_name)
1937 .expect(&format!("Unable to open {}.ron", data_stores_name));
1939 let properties_name = format!("properties-{}-{}", id.namespace_id.0, id.id);
1940 let properties = config.deserialize_for_frame::<SceneProperties, _>(&properties_name)
1941 .expect(&format!("Unable to open {}.ron", properties_name));
1943 // Update the document if it still exists, rather than replace it entirely.
1944 // This allows us to preserve state information such as the frame stamp,
1945 // which is necessary for cache sanity.
1946 match self.documents.entry(id) {
1947 Occupied(entry) => {
1948 let doc = entry.into_mut();
1950 doc.loaded_scene = scene.clone();
1951 doc.data_stores = data_stores;
1952 doc.dynamic_properties = properties;
1953 doc.frame_is_valid = false;
1954 doc.rendered_frame_is_valid = false;
1955 doc.has_built_scene = false;
1956 doc.hit_tester_is_valid = false;
1959 let doc = Document {
1961 scene: BuiltScene::empty(),
1962 removed_pipelines: Vec::new(),
1964 stamp: FrameStamp::first(id),
1965 frame_builder: FrameBuilder::new(),
1966 dynamic_properties: properties,
1968 shared_hit_tester: Arc::new(SharedHitTester::new()),
1969 frame_is_valid: false,
1970 hit_tester_is_valid: false,
1971 rendered_frame_is_valid: false,
1972 has_built_scene: false,
1974 scratch: ScratchBuffer::default(),
1975 loaded_scene: scene.clone(),
1976 prev_composite_descriptor: CompositeDescriptor::empty(),
1977 dirty_rects_are_valid: false,
1978 profile: TransactionProfile::new(),
1979 rg_builder: RenderTaskGraphBuilder::new(),
1986 let frame_name = format!("frame-{}-{}", id.namespace_id.0, id.id);
1987 let frame = config.deserialize_for_frame::<Frame, _>(frame_name);
1988 let build_frame = match frame {
1990 info!("\tloaded a built frame with {} passes", frame.passes.len());
1992 let msg_update = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates());
1993 self.result_tx.send(msg_update).unwrap();
1995 let msg_publish = ResultMsg::PublishDocument(
1997 RenderedDocument { frame, is_new_scene: true, profile: TransactionProfile::new(), frame_stats: None },
1998 self.resource_cache.pending_updates(),
2000 self.result_tx.send(msg_publish).unwrap();
2002 self.notifier.new_frame_ready(id, false, true, None);
2004 // We deserialized the state of the frame so we don't want to build
2005 // it (but we do want to update the scene builder's state)
2011 scenes_to_build.push(LoadScene {
2014 view: view.scene.clone(),
2015 config: self.frame_config.clone(),
2016 font_instances: self.resource_cache.get_font_instances(),
2022 if !scenes_to_build.is_empty() {
2023 self.send_backend_message(
2024 SceneBuilderRequest::LoadScenes(scenes_to_build)