Bug 1775369 - Make hit-test API use clip-chains rather than ClipId r=gfx-reviewers,lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D149938
This commit is contained in:
Glenn Watson 2022-06-22 19:55:26 +00:00
parent c34e0c3270
commit fb6ca66ec6
8 changed files with 134 additions and 49 deletions

View File

@ -3014,22 +3014,27 @@ pub extern "C" fn wr_dp_push_hit_test(
) {
debug_assert!(unsafe { !is_in_render_thread() });
let space_and_clip = parent.to_webrender(state.pipeline_id);
let clip_rect = clip.intersection(&rect);
if clip_rect.is_none() {
return;
}
let tag = (scroll_id, hit_info);
let prim_info = CommonItemProperties {
clip_rect: clip_rect.unwrap(),
clip_id: space_and_clip.clip_id,
spatial_id: space_and_clip.spatial_id,
flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
let spatial_id = parent.space.to_webrender(state.pipeline_id);
let clip_chain_id = if parent.clip_chain == ROOT_CLIP_CHAIN {
ClipChainId::INVALID
} else {
ClipChainId(parent.clip_chain, state.pipeline_id)
};
state.frame_builder.dl_builder.push_hit_test(&prim_info, tag);
state.frame_builder.dl_builder.push_hit_test(
clip_rect.unwrap(),
clip_chain_id,
spatial_id,
prim_flags(is_backface_visible, false),
tag,
);
}
#[no_mangle]

View File

@ -72,12 +72,24 @@ impl Example for App {
// now put some content into it.
// start with a white background
let info = CommonItemProperties::new((0, 0).to(1000, 1000), space_and_clip1);
builder.push_hit_test(&info, (0, 1));
builder.push_hit_test(
info.clip_rect,
ClipChainId::INVALID,
info.spatial_id,
info.flags,
(0, 1)
);
builder.push_rect(&info, info.clip_rect, ColorF::new(1.0, 1.0, 1.0, 1.0));
// let's make a 50x50 blue square as a visual reference
let info = CommonItemProperties::new((0, 0).to(50, 50), space_and_clip1);
builder.push_hit_test(&info, (0, 2));
builder.push_hit_test(
info.clip_rect,
ClipChainId::INVALID,
info.spatial_id,
info.flags,
(0, 2)
);
builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 0.0, 1.0, 1.0));
// and a 50x50 green square next to it with an offset clip
@ -86,7 +98,13 @@ impl Example for App {
(50, 0).to(100, 50).intersection(&(60, 10).to(110, 60)).unwrap(),
space_and_clip1,
);
builder.push_hit_test(&info, (0, 3));
builder.push_hit_test(
info.clip_rect,
ClipChainId::INVALID,
info.spatial_id,
info.flags,
(0, 3)
);
builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 0.0, 1.0));
// Below the above rectangles, set up a nested scrollbox. It's still in
@ -113,13 +131,25 @@ impl Example for App {
(-1000, -1000).to(5000, 5000),
space_and_clip2,
);
builder.push_hit_test(&info, (0, 4));
builder.push_hit_test(
info.clip_rect,
ClipChainId::INVALID,
info.spatial_id,
info.flags,
(0, 4)
);
builder.push_rect(&info, info.clip_rect, ColorF::new(0.5, 0.5, 0.5, 1.0));
// add a teal square to visualize the scrolling/clipping behaviour
// as you scroll the nested scrollbox
let info = CommonItemProperties::new((0, 200).to(50, 250), space_and_clip2);
builder.push_hit_test(&info, (0, 5));
builder.push_hit_test(
info.clip_rect,
ClipChainId::INVALID,
info.spatial_id,
info.flags,
(0, 5)
);
builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 1.0, 1.0));
// Add a sticky frame. It will "stick" twice while scrolling, once
@ -143,7 +173,13 @@ impl Example for App {
clip_id: space_and_clip2.clip_id,
},
);
builder.push_hit_test(&info, (0, 6));
builder.push_hit_test(
info.clip_rect,
ClipChainId::INVALID,
info.spatial_id,
info.flags,
(0, 6)
);
builder.push_rect(
&info,
info.clip_rect,
@ -156,7 +192,13 @@ impl Example for App {
(250, 350).to(300, 400),
space_and_clip2,
);
builder.push_hit_test(&info, (0, 7));
builder.push_hit_test(
info.clip_rect,
ClipChainId::INVALID,
info.spatial_id,
info.flags,
(0, 7)
);
builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 1.0, 1.0));
builder.pop_stacking_context();

View File

@ -762,6 +762,13 @@ impl<'a> SceneBuilder<'a> {
kind: ContextKind<'a>,
}
let invalid_clip_chain_id = ClipId::ClipChain(api::ClipChainId::INVALID);
self.clip_store.register_clip_template(
invalid_clip_chain_id,
invalid_clip_chain_id,
&[],
);
let root_clip_id = ClipId::root(root_pipeline.pipeline_id);
self.clip_store.register_clip_template(root_clip_id, root_clip_id, &[]);
self.clip_store.push_clip_root(Some(root_clip_id), false);
@ -1356,20 +1363,28 @@ impl<'a> SceneBuilder<'a> {
DisplayItem::HitTest(ref info) => {
profile_scope!("hit_test");
// TODO(gw): We could skip building the clip-chain here completely, as it's not used by
// hit-test items.
let (layout, _, spatial_node_index, _) = self.process_common_properties(
&info.common,
None,
let spatial_node_index = self.get_space(info.spatial_id);
let current_offset = self.current_offset(spatial_node_index);
let unsnapped_rect = info.rect.translate(current_offset);
let rect = self.snap_rect(
&unsnapped_rect,
spatial_node_index,
);
// Don't add transparent rectangles to the draw list,
// but do consider them for hit testing. This allows
// specifying invisible hit testing areas.
let layout = LayoutPrimitiveInfo {
rect,
clip_rect: rect,
flags: info.flags,
};
// TODO(gw): Port internal API to be ClipChain based, rather than ClipId once all callers updated
let clip_id = ClipId::ClipChain(info.clip_chain_id);
self.add_primitive_to_hit_testing_list(
&layout,
spatial_node_index,
info.common.clip_id,
clip_id,
info.tag,
);
}

View File

@ -365,7 +365,10 @@ pub struct ClearRectangleDisplayItem {
/// distinct item also makes it easier to inspect/debug display items.
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct HitTestDisplayItem {
pub common: CommonItemProperties,
pub rect: LayoutRect,
pub clip_chain_id: ClipChainId,
pub spatial_id: SpatialId,
pub flags: PrimitiveFlags,
pub tag: ItemTag,
}
@ -1633,6 +1636,10 @@ impl From<FillRule> for u8 {
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
pub struct ClipChainId(pub u64, pub PipelineId);
impl ClipChainId {
pub const INVALID: Self = ClipChainId(!0, PipelineId::INVALID);
}
/// A reference to a clipping node defining how an item is clipped.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
pub enum ClipId {

View File

@ -1350,11 +1350,17 @@ impl DisplayListBuilder {
pub fn push_hit_test(
&mut self,
common: &di::CommonItemProperties,
rect: LayoutRect,
clip_chain_id: di::ClipChainId,
spatial_id: di::SpatialId,
flags: di::PrimitiveFlags,
tag: di::ItemTag,
) {
let item = di::DisplayItem::HitTest(di::HitTestDisplayItem {
common: *common,
rect,
clip_chain_id,
spatial_id,
flags,
tag,
});
self.push_item(&item);

View File

@ -159,6 +159,8 @@ impl PipelineId {
pub fn dummy() -> Self {
PipelineId(!0, !0)
}
pub const INVALID: Self = PipelineId(!0, !0);
}

View File

@ -1331,24 +1331,27 @@ impl<'a> RawtestHarness<'a> {
builder.begin();
// Add a rectangle that covers the entire scene.
let info = self.make_common_properties(LayoutRect::from_size(layout_size));
let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
builder.push_hit_test(
&info,
LayoutRect::from_size(layout_size),
ClipChainId::INVALID,
space_and_clip.spatial_id,
PrimitiveFlags::default(),
(0, 1),
);
// Add a simple 100x100 rectangle at 100,0.
let info = self.make_common_properties(LayoutRect::from_origin_and_size(
LayoutPoint::new(100., 0.),
LayoutSize::new(100., 100.)
));
builder.push_hit_test(
&info,
LayoutRect::from_origin_and_size(
LayoutPoint::new(100., 0.),
LayoutSize::new(100., 100.)
),
ClipChainId::INVALID,
space_and_clip.spatial_id,
PrimitiveFlags::default(),
(0, 2),
);
let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
let make_rounded_complex_clip = |rect: &LayoutRect, radius: f32| -> ComplexClipRegion {
ComplexClipRegion::new(
*rect,
@ -1363,13 +1366,12 @@ impl<'a> RawtestHarness<'a> {
&space_and_clip,
make_rounded_complex_clip(&rect, 20.),
);
let clip_chain_id = builder.define_clip_chain(None, vec![temp_clip_id]);
builder.push_hit_test(
&CommonItemProperties {
clip_rect: rect,
clip_id: temp_clip_id,
spatial_id: space_and_clip.spatial_id,
flags: PrimitiveFlags::default(),
},
rect,
clip_chain_id,
space_and_clip.spatial_id,
PrimitiveFlags::default(),
(0, 4),
);
@ -1381,12 +1383,10 @@ impl<'a> RawtestHarness<'a> {
);
let clip_chain_id = builder.define_clip_chain(None, vec![clip_id]);
builder.push_hit_test(
&CommonItemProperties {
clip_rect: rect,
clip_id: ClipId::ClipChain(clip_chain_id),
spatial_id: space_and_clip.spatial_id,
flags: PrimitiveFlags::default(),
},
rect,
clip_chain_id,
space_and_clip.spatial_id,
PrimitiveFlags::default(),
(0, 5),
);

View File

@ -878,9 +878,17 @@ impl YamlFrameReader {
&info.clip_rect
);
let clip_chain_id = match info.clip_id {
ClipId::Clip(..) => panic!("bug: must be a clip-chain"),
ClipId::ClipChain(id) => id,
};
if let Some(tag) = self.to_hit_testing_tag(&item["hit-testing-tag"]) {
dl.push_hit_test(
info,
info.clip_rect,
clip_chain_id,
info.spatial_id,
info.flags,
tag,
);
}