webrender/
scene.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
5use api::{BuiltDisplayList, DisplayListWithCache, ColorF, DynamicProperties, Epoch, FontRenderMode};
6use api::{PipelineId, PropertyBinding, PropertyBindingId, PropertyValue, MixBlendMode, StackingContext};
7use api::units::*;
8use api::channel::Sender;
9use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
10use crate::render_api::MemoryReport;
11use crate::composite::CompositorKind;
12use crate::clip::{ClipStore, ClipTree};
13use crate::spatial_tree::SpatialTree;
14use crate::frame_builder::FrameBuilderConfig;
15use crate::hit_test::{HitTester, HitTestingScene, HitTestingSceneStats};
16use crate::internal_types::FastHashMap;
17use crate::picture::SurfaceInfo;
18use crate::picture_graph::PictureGraph;
19use crate::prim_store::{PrimitiveStore, PrimitiveStoreStats, PictureIndex, PrimitiveInstance};
20use crate::tile_cache::TileCacheConfig;
21use std::sync::Arc;
22
23/// Stores a map of the animated property bindings for the current display list. These
24/// can be used to animate the transform and/or opacity of a display list without
25/// re-submitting the display list itself.
26#[cfg_attr(feature = "capture", derive(Serialize))]
27#[cfg_attr(feature = "replay", derive(Deserialize))]
28pub struct SceneProperties {
29    transform_properties: FastHashMap<PropertyBindingId, LayoutTransform>,
30    float_properties: FastHashMap<PropertyBindingId, f32>,
31    color_properties: FastHashMap<PropertyBindingId, ColorF>,
32    current_properties: DynamicProperties,
33    pending_properties: Option<DynamicProperties>,
34}
35
36impl SceneProperties {
37    pub fn new() -> Self {
38        SceneProperties {
39            transform_properties: FastHashMap::default(),
40            float_properties: FastHashMap::default(),
41            color_properties: FastHashMap::default(),
42            current_properties: DynamicProperties::default(),
43            pending_properties: None,
44        }
45    }
46
47    /// Reset the pending properties without flush.
48    pub fn reset_properties(&mut self) {
49        self.pending_properties = None;
50    }
51
52    /// Add to the current property list for this display list.
53    pub fn add_properties(&mut self, properties: DynamicProperties) {
54        let mut pending_properties = self.pending_properties
55            .take()
56            .unwrap_or_default();
57
58        pending_properties.extend(properties);
59
60        self.pending_properties = Some(pending_properties);
61    }
62
63    /// Add to the current transform property list for this display list.
64    pub fn add_transforms(&mut self, transforms: Vec<PropertyValue<LayoutTransform>>) {
65        let mut pending_properties = self.pending_properties
66            .take()
67            .unwrap_or_default();
68
69        pending_properties.transforms.extend(transforms);
70
71        self.pending_properties = Some(pending_properties);
72    }
73
74    /// Flush any pending updates to the scene properties. Returns
75    /// true if the properties have changed since the last flush
76    /// was called. This code allows properties to be changed by
77    /// multiple reset_properties, add_properties and add_transforms calls
78    /// during a single transaction, and still correctly determine if any
79    /// properties have changed. This can have significant power
80    /// saving implications, allowing a frame build to be skipped
81    /// if the properties haven't changed in many cases.
82    pub fn flush_pending_updates(&mut self) -> bool {
83        let mut properties_changed = false;
84
85        if let Some(ref pending_properties) = self.pending_properties {
86            if *pending_properties != self.current_properties {
87                self.transform_properties.clear();
88                self.float_properties.clear();
89                self.color_properties.clear();
90
91                for property in &pending_properties.transforms {
92                    self.transform_properties
93                        .insert(property.key.id, property.value);
94                }
95
96                for property in &pending_properties.floats {
97                    self.float_properties
98                        .insert(property.key.id, property.value);
99                }
100
101                for property in &pending_properties.colors {
102                    self.color_properties
103                        .insert(property.key.id, property.value);
104                }
105
106                self.current_properties = pending_properties.clone();
107                properties_changed = true;
108            }
109        }
110
111        properties_changed
112    }
113
114    /// Get the current value for a transform property.
115    pub fn resolve_layout_transform(
116        &self,
117        property: &PropertyBinding<LayoutTransform>,
118    ) -> LayoutTransform {
119        match *property {
120            PropertyBinding::Value(value) => value,
121            PropertyBinding::Binding(ref key, v) => {
122                self.transform_properties
123                    .get(&key.id)
124                    .cloned()
125                    .unwrap_or(v)
126            }
127        }
128    }
129
130    /// Get the current value for a float property.
131    pub fn resolve_float(
132        &self,
133        property: &PropertyBinding<f32>
134    ) -> f32 {
135        match *property {
136            PropertyBinding::Value(value) => value,
137            PropertyBinding::Binding(ref key, v) => {
138                self.float_properties
139                    .get(&key.id)
140                    .cloned()
141                    .unwrap_or(v)
142            }
143        }
144    }
145
146    pub fn float_properties(&self) -> &FastHashMap<PropertyBindingId, f32> {
147        &self.float_properties
148    }
149
150    /// Get the current value for a color property.
151    pub fn resolve_color(
152        &self,
153        property: &PropertyBinding<ColorF>
154    ) -> ColorF {
155        match *property {
156            PropertyBinding::Value(value) => value,
157            PropertyBinding::Binding(ref key, v) => {
158                self.color_properties
159                    .get(&key.id)
160                    .cloned()
161                    .unwrap_or(v)
162            }
163        }
164    }
165
166    pub fn color_properties(&self) -> &FastHashMap<PropertyBindingId, ColorF> {
167        &self.color_properties
168    }
169
170}
171
172/// A representation of the layout within the display port for a given document or iframe.
173#[cfg_attr(feature = "capture", derive(Serialize))]
174#[cfg_attr(feature = "replay", derive(Deserialize))]
175#[derive(Clone)]
176pub struct ScenePipeline {
177    pub display_list: DisplayListWithCache,
178}
179
180/// A complete representation of the layout bundling visible pipelines together.
181#[cfg_attr(feature = "capture", derive(Serialize))]
182#[cfg_attr(feature = "replay", derive(Deserialize))]
183#[derive(Clone)]
184pub struct Scene {
185    pub root_pipeline_id: Option<PipelineId>,
186    pub pipelines: FastHashMap<PipelineId, ScenePipeline>,
187    pub pipeline_epochs: FastHashMap<PipelineId, Epoch>,
188}
189
190impl Scene {
191    pub fn new() -> Self {
192        Scene {
193            root_pipeline_id: None,
194            pipelines: FastHashMap::default(),
195            pipeline_epochs: FastHashMap::default(),
196        }
197    }
198
199    pub fn set_root_pipeline_id(&mut self, pipeline_id: PipelineId) {
200        self.root_pipeline_id = Some(pipeline_id);
201    }
202
203    pub fn set_display_list(
204        &mut self,
205        pipeline_id: PipelineId,
206        epoch: Epoch,
207        display_list: BuiltDisplayList,
208    ) {
209        // Adds a cache to the given display list. If this pipeline already had
210        // a display list before, that display list is updated and used instead.
211        let display_list = match self.pipelines.remove(&pipeline_id) {
212            Some(mut pipeline) => {
213                pipeline.display_list.update(display_list);
214                pipeline.display_list
215            }
216            None => DisplayListWithCache::new_from_list(display_list)
217        };
218
219        let new_pipeline = ScenePipeline {
220            display_list,
221        };
222
223        self.pipelines.insert(pipeline_id, new_pipeline);
224        self.pipeline_epochs.insert(pipeline_id, epoch);
225    }
226
227    pub fn remove_pipeline(&mut self, pipeline_id: PipelineId) {
228        if self.root_pipeline_id == Some(pipeline_id) {
229            self.root_pipeline_id = None;
230        }
231        self.pipelines.remove(&pipeline_id);
232        self.pipeline_epochs.remove(&pipeline_id);
233    }
234
235    pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) {
236        self.pipeline_epochs.insert(pipeline_id, epoch);
237    }
238
239    pub fn has_root_pipeline(&self) -> bool {
240        if let Some(ref root_id) = self.root_pipeline_id {
241            return self.pipelines.contains_key(root_id);
242        }
243
244        false
245    }
246
247    pub fn report_memory(
248        &self,
249        ops: &mut MallocSizeOfOps,
250        report: &mut MemoryReport
251    ) {
252        for (_, pipeline) in &self.pipelines {
253            report.display_list += pipeline.display_list.size_of(ops)
254        }
255    }
256}
257
258pub trait StackingContextHelpers {
259    fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode>;
260}
261
262impl StackingContextHelpers for StackingContext {
263    fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode> {
264        match self.mix_blend_mode {
265            MixBlendMode::Normal => None,
266            _ => Some(self.mix_blend_mode),
267        }
268    }
269}
270
271
272/// WebRender's internal representation of the scene.
273pub struct BuiltScene {
274    pub has_root_pipeline: bool,
275    pub pipeline_epochs: FastHashMap<PipelineId, Epoch>,
276    pub output_rect: DeviceIntRect,
277    pub prim_store: PrimitiveStore,
278    pub clip_store: ClipStore,
279    pub config: FrameBuilderConfig,
280    pub hit_testing_scene: Arc<HitTestingScene>,
281    pub tile_cache_config: TileCacheConfig,
282    pub snapshot_pictures: Vec<PictureIndex>,
283    pub tile_cache_pictures: Vec<PictureIndex>,
284    pub picture_graph: PictureGraph,
285    pub num_plane_splitters: usize,
286    pub prim_instances: Vec<PrimitiveInstance>,
287    pub surfaces: Vec<SurfaceInfo>,
288    pub clip_tree: ClipTree,
289
290    /// Deallocating memory outside of the thread that allocated it causes lock
291    /// contention in jemalloc. To avoid this we send the built scene back to
292    /// the scene builder thread when we don't need it anymore, and in the process,
293    /// also reuse some allocations.
294    pub recycler_tx: Option<Sender<BuiltScene>>,
295}
296
297impl BuiltScene {
298    pub fn empty() -> Self {
299        BuiltScene {
300            has_root_pipeline: false,
301            pipeline_epochs: FastHashMap::default(),
302            output_rect: DeviceIntRect::zero(),
303            prim_store: PrimitiveStore::new(&PrimitiveStoreStats::empty()),
304            clip_store: ClipStore::new(),
305            hit_testing_scene: Arc::new(HitTestingScene::new(&HitTestingSceneStats::empty())),
306            tile_cache_config: TileCacheConfig::new(0),
307            snapshot_pictures: Vec::new(),
308            tile_cache_pictures: Vec::new(),
309            picture_graph: PictureGraph::new(),
310            num_plane_splitters: 0,
311            prim_instances: Vec::new(),
312            surfaces: Vec::new(),
313            clip_tree: ClipTree::new(),
314            recycler_tx: None,
315            config: FrameBuilderConfig {
316                default_font_render_mode: FontRenderMode::Mono,
317                dual_source_blending_is_supported: false,
318                testing: false,
319                gpu_supports_fast_clears: false,
320                gpu_supports_advanced_blend: false,
321                advanced_blend_is_coherent: false,
322                gpu_supports_render_target_partial_update: true,
323                external_images_require_copy: false,
324                batch_lookback_count: 0,
325                background_color: None,
326                compositor_kind: CompositorKind::default(),
327                tile_size_override: None,
328                max_surface_override: None,
329                max_depth_ids: 0,
330                max_target_size: 0,
331                force_invalidation: false,
332                is_software: false,
333                low_quality_pinch_zoom: false,
334                max_shared_surface_size: 2048,
335            },
336        }
337    }
338
339    /// Send the scene back to the scene builder thread so that recycling/deallocations
340    /// can happen there.
341    pub fn recycle(mut self) {
342        if let Some(tx) = self.recycler_tx.take() {
343            let _ = tx.send(self);
344        }
345    }
346
347    /// Get the memory usage statistics to pre-allocate for the next scene.
348    pub fn get_stats(&self) -> SceneStats {
349        SceneStats {
350            prim_store_stats: self.prim_store.get_stats(),
351            hit_test_stats: self.hit_testing_scene.get_stats(),
352        }
353    }
354
355    pub fn create_hit_tester(
356        &mut self,
357        spatial_tree: &SpatialTree,
358    ) -> HitTester {
359        HitTester::new(
360            Arc::clone(&self.hit_testing_scene),
361            spatial_tree,
362        )
363    }
364}
365
366/// Stores the allocation sizes of various arrays in the built
367/// scene. This is retrieved from the current frame builder
368/// and used to reserve an approximately correct capacity of
369/// the arrays for the next scene that is getting built.
370pub struct SceneStats {
371    pub prim_store_stats: PrimitiveStoreStats,
372    pub hit_test_stats: HitTestingSceneStats,
373}
374
375impl SceneStats {
376    pub fn empty() -> Self {
377        SceneStats {
378            prim_store_stats: PrimitiveStoreStats::empty(),
379            hit_test_stats: HitTestingSceneStats::empty(),
380        }
381    }
382}