diff --git a/gfx/wr/webrender/src/clip_scroll_tree.rs b/gfx/wr/webrender/src/clip_scroll_tree.rs index 5d423ce4814b..84cc369403e4 100644 --- a/gfx/wr/webrender/src/clip_scroll_tree.rs +++ b/gfx/wr/webrender/src/clip_scroll_tree.rs @@ -10,7 +10,7 @@ use internal_types::{FastHashMap, FastHashSet}; use print_tree::{PrintableTree, PrintTree, PrintTreePrinter}; use scene::SceneProperties; use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo, ScrollFrameKind}; -use std::ops; +use std::{ops, u32}; use util::{project_rect, LayoutToWorldFastTransform, MatrixHelpers, ScaleOffset}; pub type ScrollStates = FastHashMap; @@ -50,6 +50,10 @@ impl CoordinateSystem { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct SpatialNodeIndex(pub u32); +impl SpatialNodeIndex { + pub const INVALID: SpatialNodeIndex = SpatialNodeIndex(u32::MAX); +} + //Note: these have to match ROOT_REFERENCE_FRAME_SPATIAL_ID and ROOT_SCROLL_NODE_SPATIAL_ID pub const ROOT_SPATIAL_NODE_INDEX: SpatialNodeIndex = SpatialNodeIndex(0); const TOPMOST_SCROLL_NODE_INDEX: SpatialNodeIndex = SpatialNodeIndex(1); diff --git a/gfx/wr/webrender/src/display_list_flattener.rs b/gfx/wr/webrender/src/display_list_flattener.rs index 2d2a9bf4589f..b09a78a7c3c8 100644 --- a/gfx/wr/webrender/src/display_list_flattener.rs +++ b/gfx/wr/webrender/src/display_list_flattener.rs @@ -17,7 +17,7 @@ use clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore}; use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex}; use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig}; use glyph_rasterizer::FontInstance; -use hit_test::{HitTestingItem, HitTestingRun}; +use hit_test::{HitTestingItem, HitTestingScene}; use image::simplify_repeated_primitive; use intern::Interner; use internal_types::{FastHashMap, FastHashSet}; @@ -25,7 +25,7 @@ use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureO use picture::{BlitReason, OrderedPictureChild, PrimitiveList, TileCache}; use prim_store::{PrimitiveInstance, PrimitiveSceneData}; use prim_store::{PrimitiveInstanceKind, NinePatchDescriptor, PrimitiveStore}; -use prim_store::{PrimitiveStoreStats, ScrollNodeAndClipChain, PictureIndex}; +use prim_store::{ScrollNodeAndClipChain, PictureIndex}; use prim_store::InternablePrimitive; use prim_store::{register_prim_chase_id, get_line_decoration_sizes}; use prim_store::borders::{ImageBorder, NormalBorderPrim}; @@ -37,9 +37,9 @@ use prim_store::text_run::TextRun; use render_backend::{DocumentView}; use resource_cache::{FontInstanceMap, ImageRequest}; use scene::{Scene, StackingContextHelpers}; -use scene_builder::Interners; +use scene_builder::{DocumentStats, Interners}; use spatial_node::{StickyFrameInfo, ScrollFrameKind, SpatialNodeType}; -use std::{f32, mem, usize}; +use std::{f32, mem, usize, ops}; use std::collections::vec_deque::VecDeque; use std::sync::Arc; use tiling::{CompositeOps}; @@ -205,7 +205,7 @@ pub struct DisplayListFlattener<'a> { pub prim_store: PrimitiveStore, /// Information about all primitives involved in hit testing. - pub hit_testing_runs: Vec, + pub hit_testing_scene: HitTestingScene, /// The store which holds all complex clipping information. pub clip_store: ClipStore, @@ -235,7 +235,7 @@ impl<'a> DisplayListFlattener<'a> { frame_builder_config: &FrameBuilderConfig, new_scene: &mut Scene, interners: &mut Interners, - prim_store_stats: &PrimitiveStoreStats, + doc_stats: &DocumentStats, ) -> FrameBuilder { // We checked that the root pipeline is available on the render backend. let root_pipeline_id = scene.root_pipeline_id.unwrap(); @@ -252,11 +252,11 @@ impl<'a> DisplayListFlattener<'a> { config: *frame_builder_config, output_pipelines, id_to_index_mapper: NodeIdToIndexMapper::default(), - hit_testing_runs: Vec::new(), + hit_testing_scene: HitTestingScene::new(&doc_stats.hit_test_stats), pending_shadow_items: VecDeque::new(), sc_stack: Vec::new(), pipeline_clip_chain_stack: vec![ClipChainId::NONE], - prim_store: PrimitiveStore::new(&prim_store_stats), + prim_store: PrimitiveStore::new(&doc_stats.prim_store_stats), clip_store: ClipStore::new(), interners, root_pic_index: PictureIndex(0), @@ -1192,17 +1192,34 @@ impl<'a> DisplayListFlattener<'a> { None => return, }; - let new_item = HitTestingItem::new(tag, info); - match self.hit_testing_runs.last_mut() { - Some(&mut HitTestingRun(ref mut items, prev_clip_and_scroll)) - if prev_clip_and_scroll == clip_and_scroll => { - items.push(new_item); - return; - } - _ => {} + // We want to get a range of clip chain roots that apply to this + // hit testing primitive. + + // Get the start index for the clip chain root range for this primitive. + let start = self.hit_testing_scene.next_clip_chain_index(); + + // Add the clip chain root for the primitive itself. + self.hit_testing_scene.add_clip_chain(clip_and_scroll.clip_chain_id); + + // Append any clip chain roots from enclosing stacking contexts. + for sc in &self.sc_stack { + self.hit_testing_scene.add_clip_chain(sc.clip_chain_id); } - self.hit_testing_runs.push(HitTestingRun(vec![new_item], clip_and_scroll)); + // Construct a clip chain roots range to be stored with the item. + let clip_chain_range = ops::Range { + start, + end: self.hit_testing_scene.next_clip_chain_index(), + }; + + // Create and store the hit testing primitive itself. + let new_item = HitTestingItem::new( + tag, + info, + clip_and_scroll.spatial_node_index, + clip_chain_range, + ); + self.hit_testing_scene.add_item(new_item); } /// Add an already created primitive to the draw lists. diff --git a/gfx/wr/webrender/src/frame_builder.rs b/gfx/wr/webrender/src/frame_builder.rs index 1dbc7ea9c0aa..7cb3e537fab9 100644 --- a/gfx/wr/webrender/src/frame_builder.rs +++ b/gfx/wr/webrender/src/frame_builder.rs @@ -10,7 +10,9 @@ use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex use display_list_flattener::{DisplayListFlattener}; use gpu_cache::{GpuCache, GpuCacheHandle}; use gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator}; -use hit_test::{HitTester, HitTestingRun}; +use hit_test::{HitTester, HitTestingScene}; +#[cfg(feature = "replay")] +use hit_test::HitTestingSceneStats; use internal_types::{FastHashMap, PlaneSplitter}; use picture::{PictureSurface, PictureUpdateState, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex}; use picture::{RetainedTiles, TileCache, DirtyRegion}; @@ -22,6 +24,7 @@ use render_backend::{DataStores, FrameStamp}; use render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree, RenderTaskTreeCounters}; use resource_cache::{ResourceCache}; use scene::{ScenePipeline, SceneProperties}; +use scene_builder::DocumentStats; use segment::SegmentBuilder; use spatial_node::SpatialNode; use std::{f32, mem}; @@ -104,7 +107,7 @@ pub struct FrameBuilder { pub prim_store: PrimitiveStore, pub clip_store: ClipStore, #[cfg_attr(feature = "capture", serde(skip))] //TODO - pub hit_testing_runs: Vec, + pub hit_testing_scene: Arc, pub config: FrameBuilderConfig, pub globals: FrameGlobalResources, } @@ -219,7 +222,7 @@ impl FrameBuilder { #[cfg(feature = "replay")] pub fn empty() -> Self { FrameBuilder { - hit_testing_runs: Vec::new(), + hit_testing_scene: Arc::new(HitTestingScene::new(&HitTestingSceneStats::empty())), prim_store: PrimitiveStore::new(&PrimitiveStoreStats::empty()), clip_store: ClipStore::new(), output_rect: DeviceIntRect::zero(), @@ -258,7 +261,7 @@ impl FrameBuilder { flattener: DisplayListFlattener, ) -> Self { FrameBuilder { - hit_testing_runs: flattener.hit_testing_runs, + hit_testing_scene: Arc::new(flattener.hit_testing_scene), prim_store: flattener.prim_store, clip_store: flattener.clip_store, root_pic_index: flattener.root_pic_index, @@ -270,6 +273,14 @@ impl FrameBuilder { } } + /// Get the memory usage statistics to pre-allocate for the next scene. + pub fn get_stats(&self) -> DocumentStats { + DocumentStats { + prim_store_stats: self.prim_store.get_stats(), + hit_test_stats: self.hit_testing_scene.get_stats(), + } + } + /// Destroy an existing frame builder. This is called just before /// a frame builder is replaced with a newly built scene. pub fn destroy( @@ -676,7 +687,7 @@ impl FrameBuilder { clip_data_store: &ClipDataStore, ) -> HitTester { HitTester::new( - &self.hit_testing_runs, + Arc::clone(&self.hit_testing_scene), clip_scroll_tree, &self.clip_store, clip_data_store, diff --git a/gfx/wr/webrender/src/hit_test.rs b/gfx/wr/webrender/src/hit_test.rs index 42b646c47051..1eafa24494ea 100644 --- a/gfx/wr/webrender/src/hit_test.rs +++ b/gfx/wr/webrender/src/hit_test.rs @@ -4,13 +4,13 @@ use api::{BorderRadius, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag, LayoutPoint}; use api::{LayoutPrimitiveInfo, LayoutRect, PipelineId, WorldPoint}; -use clip::{ClipDataStore, ClipNode, ClipItem, ClipStore}; +use clip::{ClipChainId, ClipDataStore, ClipNode, ClipItem, ClipStore}; use clip::{rounded_rectangle_contains_point}; use clip_scroll_tree::{SpatialNodeIndex, ClipScrollTree}; use internal_types::FastHashMap; -use prim_store::ScrollNodeAndClipChain; -use std::u32; -use util::LayoutToWorldFastTransform; +use std::{ops, u32}; +use std::sync::Arc; +use util::{LayoutToWorldFastTransform, WorldToLayoutFastTransform}; /// A copy of important clip scroll node data to use during hit testing. This a copy of /// data from the ClipScrollTree that will persist as a new frame is under construction, @@ -25,6 +25,12 @@ pub struct HitTestSpatialNode { /// World viewport transform for content transformed by this node. world_viewport_transform: LayoutToWorldFastTransform, + + /// Cached inverse of the world content transform + inv_world_content_transform: Option, + + /// Cached inverse of the world viewport transform + inv_world_viewport_transform: Option, } #[derive(MallocSizeOf)] @@ -58,13 +64,6 @@ impl HitTestClipNode { } } -// A hit testing clip chain node is the same as a -// normal clip chain node, except that the clip -// node is embedded inside the clip chain, rather -// than referenced. This means we don't need to -// copy the complete interned clip data store for -// hit testing. - #[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq, Eq, Hash)] pub struct HitTestClipChainId(u32); @@ -72,6 +71,12 @@ impl HitTestClipChainId { pub const NONE: Self = HitTestClipChainId(u32::MAX); } +/// A hit testing clip chain node is the same as a +/// normal clip chain node, except that the clip +/// node is embedded inside the clip chain, rather +/// than referenced. This means we don't need to +/// copy the complete interned clip data store for +/// hit testing. #[derive(MallocSizeOf)] pub struct HitTestClipChainNode { pub region: HitTestClipNode, @@ -79,27 +84,109 @@ pub struct HitTestClipChainNode { pub parent_clip_chain_id: HitTestClipChainId, } +#[derive(Copy, Clone, Debug, MallocSizeOf)] +pub struct HitTestingClipChainIndex(u32); + #[derive(Clone, MallocSizeOf)] pub struct HitTestingItem { rect: LayoutRect, clip_rect: LayoutRect, tag: ItemTag, is_backface_visible: bool, + #[ignore_malloc_size_of = "simple"] + clip_chain_range: ops::Range, + spatial_node_index: SpatialNodeIndex, } impl HitTestingItem { - pub fn new(tag: ItemTag, info: &LayoutPrimitiveInfo) -> HitTestingItem { + pub fn new( + tag: ItemTag, + info: &LayoutPrimitiveInfo, + spatial_node_index: SpatialNodeIndex, + clip_chain_range: ops::Range, + ) -> HitTestingItem { HitTestingItem { rect: info.rect, clip_rect: info.clip_rect, tag, is_backface_visible: info.is_backface_visible, + spatial_node_index, + clip_chain_range, } } } -#[derive(Clone, MallocSizeOf)] -pub struct HitTestingRun(pub Vec, pub ScrollNodeAndClipChain); +/// Statistics about allocation sizes of current hit tester, +/// used to pre-allocate size of the next hit tester. +pub struct HitTestingSceneStats { + pub clip_chain_roots_count: usize, + pub items_count: usize, +} + +impl HitTestingSceneStats { + pub fn empty() -> Self { + HitTestingSceneStats { + clip_chain_roots_count: 0, + items_count: 0, + } + } +} + +/// Defines the immutable part of a hit tester for a given scene. +/// The hit tester is recreated each time a frame is built, since +/// it relies on the current values of the clip scroll tree. +/// However, the clip chain and item definitions don't change, +/// so they are created once per scene, and shared between +/// hit tester instances via Arc. +#[derive(MallocSizeOf)] +pub struct HitTestingScene { + /// The list of variable clip chain roots referenced by the items. + pub clip_chain_roots: Vec, + + /// List of hit testing primitives. + pub items: Vec, +} + +impl HitTestingScene { + /// Construct a new hit testing scene, pre-allocating to size + /// provided by previous scene stats. + pub fn new(stats: &HitTestingSceneStats) -> Self { + HitTestingScene { + clip_chain_roots: Vec::with_capacity(stats.clip_chain_roots_count), + items: Vec::with_capacity(stats.items_count), + } + } + + /// Get stats about the current scene allocation sizes. + pub fn get_stats(&self) -> HitTestingSceneStats { + HitTestingSceneStats { + clip_chain_roots_count: self.clip_chain_roots.len(), + items_count: self.items.len(), + } + } + + /// Add a hit testing primitive. + pub fn add_item(&mut self, item: HitTestingItem) { + self.items.push(item); + } + + /// Add a clip chain to the clip chain roots list. + pub fn add_clip_chain(&mut self, clip_chain_id: ClipChainId) { + if clip_chain_id != ClipChainId::INVALID { + self.clip_chain_roots.push(HitTestClipChainId(clip_chain_id.0)); + } + } + + /// Get the slice of clip chain roots for a given hit test primitive. + fn get_clip_chains_for_item(&self, item: &HitTestingItem) -> &[HitTestClipChainId] { + &self.clip_chain_roots[item.clip_chain_range.start.0 as usize .. item.clip_chain_range.end.0 as usize] + } + + /// Get the next index of the clip chain roots list. + pub fn next_clip_chain_index(&self) -> HitTestingClipChainIndex { + HitTestingClipChainIndex(self.clip_chain_roots.len() as u32) + } +} #[derive(MallocSizeOf)] enum HitTestRegion { @@ -126,7 +213,8 @@ impl HitTestRegion { #[derive(MallocSizeOf)] pub struct HitTester { - runs: Vec, + #[ignore_malloc_size_of = "Arc"] + scene: Arc, spatial_nodes: Vec, clip_chains: Vec, pipeline_root_nodes: FastHashMap, @@ -134,13 +222,13 @@ pub struct HitTester { impl HitTester { pub fn new( - runs: &Vec, + scene: Arc, clip_scroll_tree: &ClipScrollTree, clip_store: &ClipStore, clip_data_store: &ClipDataStore, ) -> HitTester { let mut hit_tester = HitTester { - runs: runs.clone(), + scene, spatial_nodes: Vec::new(), clip_chains: Vec::new(), pipeline_root_nodes: FastHashMap::default(), @@ -173,6 +261,8 @@ impl HitTester { self.spatial_nodes.push(HitTestSpatialNode { pipeline_id: node.pipeline_id, world_content_transform: node.world_content_transform, + inv_world_content_transform: node.world_content_transform.inverse(), + inv_world_viewport_transform: node.world_viewport_transform.inverse(), world_viewport_transform: node.world_viewport_transform, }); } @@ -267,34 +357,49 @@ impl HitTester { pub fn find_node_under_point(&self, mut test: HitTest) -> Option { let point = test.get_absolute_point(self); + let mut current_spatial_node_index = SpatialNodeIndex::INVALID; + let mut point_in_layer = None; - for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() { - let spatial_node_index = clip_and_scroll.spatial_node_index; - let scroll_node = &self.spatial_nodes[spatial_node_index.0 as usize]; - let transform = scroll_node.world_content_transform; - let point_in_layer = match transform - .inverse() - .and_then(|inverted| inverted.transform_point2d(&point)) - { - Some(point) => point, - None => continue, - }; + // For each hit test primitive + for item in self.scene.items.iter().rev() { + let scroll_node = &self.spatial_nodes[item.spatial_node_index.0 as usize]; - let mut clipped_in = false; - for item in items.iter().rev() { - if !item.rect.contains(&point_in_layer) || - !item.clip_rect.contains(&point_in_layer) { + // Update the cached point in layer space, if the spatial node + // changed since last primitive. + if item.spatial_node_index != current_spatial_node_index { + point_in_layer = scroll_node + .inv_world_content_transform + .and_then(|inverted| inverted.transform_point2d(&point)); + + current_spatial_node_index = item.spatial_node_index; + } + + // Only consider hit tests on transformable layers. + if let Some(point_in_layer) = point_in_layer { + // If the item's rect or clip rect don't contain this point, + // it's not a valid hit. + if !item.rect.contains(&point_in_layer) { + continue; + } + if !item.clip_rect.contains(&point_in_layer) { continue; } - let clip_chain_id = HitTestClipChainId(clip_and_scroll.clip_chain_id.0); - clipped_in |= - self.is_point_clipped_in_for_clip_chain(point, clip_chain_id, &mut test); - if !clipped_in { - break; + // See if any of the clip chain roots for this primitive + // cull out the item. + let clip_chains = self.scene.get_clip_chains_for_item(item); + let mut is_valid = true; + for clip_chain_id in clip_chains { + if !self.is_point_clipped_in_for_clip_chain(point, *clip_chain_id, &mut test) { + is_valid = false; + break; + } } - return Some(spatial_node_index); + // Found a valid hit test result! + if is_valid { + return Some(item.spatial_node_index); + } } } @@ -305,67 +410,83 @@ impl HitTester { let point = test.get_absolute_point(self); let mut result = HitTestResult::default(); - for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() { - let spatial_node_index = clip_and_scroll.spatial_node_index; - let scroll_node = &self.spatial_nodes[spatial_node_index.0 as usize]; + let mut current_spatial_node_index = SpatialNodeIndex::INVALID; + let mut point_in_layer = None; + let mut current_root_spatial_node_index = SpatialNodeIndex::INVALID; + let mut point_in_viewport = None; + + // For each hit test primitive + for item in self.scene.items.iter().rev() { + let scroll_node = &self.spatial_nodes[item.spatial_node_index.0 as usize]; let pipeline_id = scroll_node.pipeline_id; match (test.pipeline_id, pipeline_id) { (Some(id), node_id) if node_id != id => continue, _ => {}, } - let transform = scroll_node.world_content_transform; - let mut facing_backwards: Option = None; // will be computed on first use - let point_in_layer = match transform - .inverse() - .and_then(|inverted| inverted.transform_point2d(&point)) - { - Some(point) => point, - None => continue, - }; + // Update the cached point in layer space, if the spatial node + // changed since last primitive. + if item.spatial_node_index != current_spatial_node_index { + point_in_layer = scroll_node + .inv_world_content_transform + .and_then(|inverted| inverted.transform_point2d(&point)); + current_spatial_node_index = item.spatial_node_index; + } - let mut clipped_in = false; - for item in items.iter().rev() { - if !item.rect.contains(&point_in_layer) || - !item.clip_rect.contains(&point_in_layer) { + // Only consider hit tests on transformable layers. + if let Some(point_in_layer) = point_in_layer { + // If the item's rect or clip rect don't contain this point, + // it's not a valid hit. + if !item.rect.contains(&point_in_layer) { + continue; + } + if !item.clip_rect.contains(&point_in_layer) { continue; } - let clip_chain_id = HitTestClipChainId(clip_and_scroll.clip_chain_id.0); - clipped_in = clipped_in || - self.is_point_clipped_in_for_clip_chain(point, clip_chain_id, &mut test); - if !clipped_in { - break; + // See if any of the clip chain roots for this primitive + // cull out the item. + let clip_chains = self.scene.get_clip_chains_for_item(item); + let mut is_valid = true; + for clip_chain_id in clip_chains { + if !self.is_point_clipped_in_for_clip_chain(point, *clip_chain_id, &mut test) { + is_valid = false; + break; + } + } + if !is_valid { + continue; } // Don't hit items with backface-visibility:hidden if they are facing the back. - if !item.is_backface_visible { - if *facing_backwards.get_or_insert_with(|| transform.is_backface_visible()) { - continue; - } + if !item.is_backface_visible && scroll_node.world_content_transform.is_backface_visible() { + continue; } // We need to calculate the position of the test point relative to the origin of // the pipeline of the hit item. If we cannot get a transformed point, we are // in a situation with an uninvertible transformation so we should just skip this // result. - let root_node = &self.spatial_nodes[self.pipeline_root_nodes[&pipeline_id].0 as usize]; - let point_in_viewport = match root_node.world_viewport_transform - .inverse() - .and_then(|inverted| inverted.transform_point2d(&point)) - { - Some(point) => point, - None => continue, - }; + let root_spatial_node_index = self.pipeline_root_nodes[&pipeline_id]; + if root_spatial_node_index != current_root_spatial_node_index { + let root_node = &self.spatial_nodes[root_spatial_node_index.0 as usize]; + point_in_viewport = root_node + .inv_world_viewport_transform + .and_then(|inverted| inverted.transform_point2d(&point)); + current_root_spatial_node_index = root_spatial_node_index; + } - result.items.push(HitTestItem { - pipeline: pipeline_id, - tag: item.tag, - point_in_viewport, - point_relative_to_item: point_in_layer - item.rect.origin.to_vector(), - }); - if !test.flags.contains(HitTestFlags::FIND_ALL) { - return result; + if let Some(point_in_viewport) = point_in_viewport { + result.items.push(HitTestItem { + pipeline: pipeline_id, + tag: item.tag, + point_in_viewport, + point_relative_to_item: point_in_layer - item.rect.origin.to_vector(), + }); + + if !test.flags.contains(HitTestFlags::FIND_ALL) { + return result; + } } } } diff --git a/gfx/wr/webrender/src/scene_builder.rs b/gfx/wr/webrender/src/scene_builder.rs index 412dcea77f2d..c74ef2bafe25 100644 --- a/gfx/wr/webrender/src/scene_builder.rs +++ b/gfx/wr/webrender/src/scene_builder.rs @@ -12,6 +12,7 @@ use capture::CaptureConfig; use frame_builder::{FrameBuilderConfig, FrameBuilder}; use clip_scroll_tree::ClipScrollTree; use display_list_flattener::DisplayListFlattener; +use hit_test::HitTestingSceneStats; use intern::{Internable, Interner, UpdateList}; use internal_types::{FastHashMap, FastHashSet}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; @@ -224,6 +225,24 @@ macro_rules! declare_interners { enumerate_interners!(declare_interners); +/// Stores the allocation sizes of various arrays in the frame +/// builder. This is retrieved from the current frame builder +/// and used to reserve an approximately correct capacity of +/// the arrays for the next scene that is getting built. +pub struct DocumentStats { + pub prim_store_stats: PrimitiveStoreStats, + pub hit_test_stats: HitTestingSceneStats, +} + +impl DocumentStats { + pub fn empty() -> DocumentStats { + DocumentStats { + prim_store_stats: PrimitiveStoreStats::empty(), + hit_test_stats: HitTestingSceneStats::empty(), + } + } +} + // A document in the scene builder contains the current scene, // as well as a persistent clip interner. This allows clips // to be de-duplicated, and persisted in the GPU cache between @@ -231,7 +250,7 @@ enumerate_interners!(declare_interners); struct Document { scene: Scene, interners: Interners, - prim_store_stats: PrimitiveStoreStats, + doc_stats: DocumentStats, } impl Document { @@ -239,7 +258,7 @@ impl Document { Document { scene, interners: Interners::default(), - prim_store_stats: PrimitiveStoreStats::empty(), + doc_stats: DocumentStats::empty(), } } } @@ -386,7 +405,7 @@ impl SceneBuilder { &self.config, &mut new_scene, &mut item.interners, - &PrimitiveStoreStats::empty(), + &DocumentStats::empty(), ); interner_updates = Some( @@ -405,7 +424,7 @@ impl SceneBuilder { Document { scene: item.scene, interners: item.interners, - prim_store_stats: PrimitiveStoreStats::empty(), + doc_stats: DocumentStats::empty(), }, ); @@ -481,11 +500,11 @@ impl SceneBuilder { &self.config, &mut new_scene, &mut doc.interners, - &doc.prim_store_stats, + &doc.doc_stats, ); // Update the allocation stats for next scene - doc.prim_store_stats = frame_builder.prim_store.get_stats(); + doc.doc_stats = frame_builder.get_stats(); // Retrieve the list of updates from the clip interner. interner_updates = Some( diff --git a/gfx/wr/webrender/src/util.rs b/gfx/wr/webrender/src/util.rs index 8e2adb0f248c..1ae89bf74224 100644 --- a/gfx/wr/webrender/src/util.rs +++ b/gfx/wr/webrender/src/util.rs @@ -809,6 +809,7 @@ impl From> for FastTransform { pub type LayoutFastTransform = FastTransform; pub type LayoutToWorldFastTransform = FastTransform; +pub type WorldToLayoutFastTransform = FastTransform; pub fn project_rect( transform: &TypedTransform3D,