scene.rs (13585B)
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 use api::{BuiltDisplayList, DisplayListWithCache, ColorF, DynamicProperties, Epoch, FontRenderMode}; 6 use api::{PipelineId, PropertyBinding, PropertyBindingId, PropertyValue, MixBlendMode, StackingContext}; 7 use api::units::*; 8 use api::channel::Sender; 9 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; 10 use crate::render_api::MemoryReport; 11 use crate::composite::CompositorKind; 12 use crate::clip::{ClipStore, ClipTree}; 13 use crate::spatial_tree::SpatialTree; 14 use crate::frame_builder::FrameBuilderConfig; 15 use crate::hit_test::{HitTester, HitTestingScene, HitTestingSceneStats}; 16 use crate::internal_types::FastHashMap; 17 use crate::picture::SurfaceInfo; 18 use crate::picture_graph::PictureGraph; 19 use crate::prim_store::{PrimitiveStore, PrimitiveStoreStats, PictureIndex, PrimitiveInstance}; 20 use crate::tile_cache::TileCacheConfig; 21 use 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))] 28 pub 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 36 impl 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)] 176 pub 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)] 184 pub struct Scene { 185 pub root_pipeline_id: Option<PipelineId>, 186 pub pipelines: FastHashMap<PipelineId, ScenePipeline>, 187 pub pipeline_epochs: FastHashMap<PipelineId, Epoch>, 188 } 189 190 impl 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 258 pub trait StackingContextHelpers { 259 fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode>; 260 } 261 262 impl 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. 273 pub 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 297 impl 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 enable_dithering: false, 336 precise_linear_gradients: false, 337 precise_radial_gradients: false, 338 precise_conic_gradients: false, 339 }, 340 } 341 } 342 343 /// Send the scene back to the scene builder thread so that recycling/deallocations 344 /// can happen there. 345 pub fn recycle(mut self) { 346 if let Some(tx) = self.recycler_tx.take() { 347 let _ = tx.send(self); 348 } 349 } 350 351 /// Get the memory usage statistics to pre-allocate for the next scene. 352 pub fn get_stats(&self) -> SceneStats { 353 SceneStats { 354 prim_store_stats: self.prim_store.get_stats(), 355 hit_test_stats: self.hit_testing_scene.get_stats(), 356 } 357 } 358 359 pub fn create_hit_tester( 360 &mut self, 361 spatial_tree: &SpatialTree, 362 ) -> HitTester { 363 HitTester::new( 364 Arc::clone(&self.hit_testing_scene), 365 spatial_tree, 366 ) 367 } 368 } 369 370 /// Stores the allocation sizes of various arrays in the built 371 /// scene. This is retrieved from the current frame builder 372 /// and used to reserve an approximately correct capacity of 373 /// the arrays for the next scene that is getting built. 374 pub struct SceneStats { 375 pub prim_store_stats: PrimitiveStoreStats, 376 pub hit_test_stats: HitTestingSceneStats, 377 } 378 379 impl SceneStats { 380 pub fn empty() -> Self { 381 SceneStats { 382 prim_store_stats: PrimitiveStoreStats::empty(), 383 hit_test_stats: HitTestingSceneStats::empty(), 384 } 385 } 386 }