Skip to main content

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, 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: BuiltDisplayList,
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        let new_pipeline = ScenePipeline {
210            display_list,
211        };
212
213        self.pipelines.insert(pipeline_id, new_pipeline);
214        self.pipeline_epochs.insert(pipeline_id, epoch);
215    }
216
217    pub fn remove_pipeline(&mut self, pipeline_id: PipelineId) {
218        if self.root_pipeline_id == Some(pipeline_id) {
219            self.root_pipeline_id = None;
220        }
221        self.pipelines.remove(&pipeline_id);
222        self.pipeline_epochs.remove(&pipeline_id);
223    }
224
225    pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) {
226        self.pipeline_epochs.insert(pipeline_id, epoch);
227    }
228
229    pub fn has_root_pipeline(&self) -> bool {
230        if let Some(ref root_id) = self.root_pipeline_id {
231            return self.pipelines.contains_key(root_id);
232        }
233
234        false
235    }
236
237    pub fn report_memory(
238        &self,
239        ops: &mut MallocSizeOfOps,
240        report: &mut MemoryReport
241    ) {
242        for (_, pipeline) in &self.pipelines {
243            report.display_list += pipeline.display_list.size_of(ops)
244        }
245    }
246}
247
248pub trait StackingContextHelpers {
249    fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode>;
250}
251
252impl StackingContextHelpers for StackingContext {
253    fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode> {
254        match self.mix_blend_mode {
255            MixBlendMode::Normal => None,
256            _ => Some(self.mix_blend_mode),
257        }
258    }
259}
260
261
262/// WebRender's internal representation of the scene.
263pub struct BuiltScene {
264    pub has_root_pipeline: bool,
265    pub pipeline_epochs: FastHashMap<PipelineId, Epoch>,
266    pub output_rect: DeviceIntRect,
267    pub prim_store: PrimitiveStore,
268    pub clip_store: ClipStore,
269    pub config: FrameBuilderConfig,
270    pub hit_testing_scene: Arc<HitTestingScene>,
271    pub tile_cache_config: TileCacheConfig,
272    pub snapshot_pictures: Vec<PictureIndex>,
273    pub tile_cache_pictures: Vec<PictureIndex>,
274    pub picture_graph: PictureGraph,
275    pub num_plane_splitters: usize,
276    pub prim_instances: Vec<PrimitiveInstance>,
277    pub surfaces: Vec<SurfaceInfo>,
278    pub clip_tree: ClipTree,
279
280    /// Deallocating memory outside of the thread that allocated it causes lock
281    /// contention in jemalloc. To avoid this we send the built scene back to
282    /// the scene builder thread when we don't need it anymore, and in the process,
283    /// also reuse some allocations.
284    pub recycler_tx: Option<Sender<BuiltScene>>,
285}
286
287impl BuiltScene {
288    pub fn empty() -> Self {
289        BuiltScene {
290            has_root_pipeline: false,
291            pipeline_epochs: FastHashMap::default(),
292            output_rect: DeviceIntRect::zero(),
293            prim_store: PrimitiveStore::new(&PrimitiveStoreStats::empty()),
294            clip_store: ClipStore::new(),
295            hit_testing_scene: Arc::new(HitTestingScene::new(&HitTestingSceneStats::empty())),
296            tile_cache_config: TileCacheConfig::new(0),
297            snapshot_pictures: Vec::new(),
298            tile_cache_pictures: Vec::new(),
299            picture_graph: PictureGraph::new(),
300            num_plane_splitters: 0,
301            prim_instances: Vec::new(),
302            surfaces: Vec::new(),
303            clip_tree: ClipTree::new(),
304            recycler_tx: None,
305            config: FrameBuilderConfig {
306                default_font_render_mode: FontRenderMode::Mono,
307                dual_source_blending_is_supported: false,
308                testing: false,
309                gpu_supports_fast_clears: false,
310                gpu_supports_advanced_blend: false,
311                advanced_blend_is_coherent: false,
312                gpu_supports_render_target_partial_update: true,
313                external_images_require_copy: false,
314                batch_lookback_count: 0,
315                background_color: None,
316                compositor_kind: CompositorKind::default(),
317                tile_size_override: None,
318                max_surface_override: None,
319                max_depth_ids: 0,
320                max_target_size: 0,
321                force_invalidation: false,
322                is_software: false,
323                low_quality_pinch_zoom: false,
324                max_shared_surface_size: 2048,
325                enable_dithering: false,
326            },
327        }
328    }
329
330    /// Send the scene back to the scene builder thread so that recycling/deallocations
331    /// can happen there.
332    pub fn recycle(mut self) {
333        if let Some(tx) = self.recycler_tx.take() {
334            let _ = tx.send(self);
335        }
336    }
337
338    /// Get the memory usage statistics to pre-allocate for the next scene.
339    pub fn get_stats(&self) -> SceneStats {
340        SceneStats {
341            prim_store_stats: self.prim_store.get_stats(),
342            hit_test_stats: self.hit_testing_scene.get_stats(),
343        }
344    }
345
346    pub fn create_hit_tester(
347        &mut self,
348        spatial_tree: &SpatialTree,
349    ) -> HitTester {
350        HitTester::new(
351            Arc::clone(&self.hit_testing_scene),
352            spatial_tree,
353        )
354    }
355}
356
357/// Stores the allocation sizes of various arrays in the built
358/// scene. This is retrieved from the current frame builder
359/// and used to reserve an approximately correct capacity of
360/// the arrays for the next scene that is getting built.
361pub struct SceneStats {
362    pub prim_store_stats: PrimitiveStoreStats,
363    pub hit_test_stats: HitTestingSceneStats,
364}
365
366impl SceneStats {
367    pub fn empty() -> Self {
368        SceneStats {
369            prim_store_stats: PrimitiveStoreStats::empty(),
370            hit_test_stats: HitTestingSceneStats::empty(),
371        }
372    }
373}