Bug 1531170 - Fix WR hit testing breakage caused by stacking context clip changes. r=kvark

Recently, semantics for clips on stacking contexts were changed
such that primitives inherit the clip chain from all enclosing
stacking contexts. However, the hit testing code was not updated
to handle this change.

As each hit testing primitive is added, the current stack of
active stacking contexts is now scanned. Any valid clip chain
roots from the primitive and/or the stacking context stack are
added to the list of clip chain roots for this hit testing
primitive.

This patch also applies some optimizations and other cleanups
of the hit-testing code, specifically:

 - Instead of cloning the hit testing runs Vec every time a
   frame is built, store these in the new HitTestingScene. The
   HitTestingScene is built once per scene, and then shared by
   any hit tester instances via an Arc. This reduces a lot of
   memory allocations and copying during scrolling.
 - When creating a new HitTestingScene, pre-allocate the size
   of the arrays, based on the size of the previous hit testing
   structure. This works similarly to how arrays are sized
   in the PrimitiveStore.
 - Pre-calculate and cache a number of inverse transform matrices
   that were previously being calculated for each hit testing run.
 - Store hit testing primitives in a flat array, instead of runs,
   since there is no longer a single clip chain id per primitive.
 - Fix an apparent (?) bug in the existing hit testing code, where
   clipping out a single hit test primitive would break out of the
   loop for the current run of hit test items.

Differential Revision: https://phabricator.services.mozilla.com/D22635

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Glenn Watson 2019-03-11 16:19:21 +00:00
parent 020a5bff50
commit f2f661a64c
6 changed files with 281 additions and 108 deletions

View File

@ -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<ExternalScrollId, ScrollFrameInfo>;
@ -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);

View File

@ -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<HitTestingRun>,
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.

View File

@ -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<HitTestingRun>,
pub hit_testing_scene: Arc<HitTestingScene>,
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,

View File

@ -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<WorldToLayoutFastTransform>,
/// Cached inverse of the world viewport transform
inv_world_viewport_transform: Option<WorldToLayoutFastTransform>,
}
#[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<HitTestingClipChainIndex>,
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<HitTestingClipChainIndex>,
) -> 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<HitTestingItem>, 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<HitTestClipChainId>,
/// List of hit testing primitives.
pub items: Vec<HitTestingItem>,
}
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<HitTestingRun>,
#[ignore_malloc_size_of = "Arc"]
scene: Arc<HitTestingScene>,
spatial_nodes: Vec<HitTestSpatialNode>,
clip_chains: Vec<HitTestClipChainNode>,
pipeline_root_nodes: FastHashMap<PipelineId, SpatialNodeIndex>,
@ -134,13 +222,13 @@ pub struct HitTester {
impl HitTester {
pub fn new(
runs: &Vec<HitTestingRun>,
scene: Arc<HitTestingScene>,
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<SpatialNodeIndex> {
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<bool> = 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;
}
}
}
}

View File

@ -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(

View File

@ -809,6 +809,7 @@ impl<Src, Dst> From<TypedVector2D<f32, Src>> for FastTransform<Src, Dst> {
pub type LayoutFastTransform = FastTransform<LayoutPixel, LayoutPixel>;
pub type LayoutToWorldFastTransform = FastTransform<LayoutPixel, WorldPixel>;
pub type WorldToLayoutFastTransform = FastTransform<WorldPixel, LayoutPixel>;
pub fn project_rect<F, T>(
transform: &TypedTransform3D<f32, F, T>,