tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }