Switch to using WebRender hit testing

This trades quite a bit of complicated code in Servo for few more
messages and a significant performance improvement. In particular,
WebRender can search the entire display list at once instead of
ping-ponging down the pipeline tree. This allows us to send mouse
events to the correct pipeline immediately.
This commit is contained in:
Martin Robinson 2017-09-30 15:50:47 +02:00
parent 00e2a1c62a
commit b5d51dd263
20 changed files with 381 additions and 555 deletions

View file

@ -206,79 +206,6 @@ impl DisplayList {
}
}
// Return all nodes containing the point of interest, bottommost first, and
// respecting the `pointer-events` CSS property.
pub fn hit_test(&self,
point: &Point2D<Au>,
scroll_offsets: &ScrollOffsetMap)
-> Vec<DisplayItemMetadata> {
let mut result = Vec::new();
let mut traversal = DisplayListTraversal::new(self);
self.hit_test_contents(&mut traversal,
point,
&mut ScrollOffsetLookup::new(&mut HashMap::new(), scroll_offsets),
&mut result);
result
}
fn hit_test_contents<'a>(&self,
traversal: &mut DisplayListTraversal<'a>,
point: &Point2D<Au>,
offset_lookup: &mut ScrollOffsetLookup,
result: &mut Vec<DisplayItemMetadata>) {
while let Some(item) = traversal.next() {
match item {
&DisplayItem::PushStackingContext(ref context_item) => {
self.hit_test_stacking_context(&context_item.stacking_context,
item.scroll_node_id(),
traversal,
point,
offset_lookup,
result);
}
&DisplayItem::PopStackingContext(_) => return,
&DisplayItem::DefineClipScrollNode(ref item) => {
offset_lookup.add_clip_scroll_node(&item.node);
}
_ => {
if let Some(meta) = item.hit_test(*point, offset_lookup) {
result.push(meta);
}
}
}
}
}
fn hit_test_stacking_context<'a>(&self,
stacking_context: &StackingContext,
clip_id: ClipId,
traversal: &mut DisplayListTraversal<'a>,
point: &Point2D<Au>,
offset_lookup: &mut ScrollOffsetLookup,
result: &mut Vec<DisplayItemMetadata>) {
debug_assert!(stacking_context.context_type == StackingContextType::Real);
let mut point = *point - stacking_context.bounds.origin.to_vector();
if stacking_context.scroll_policy == ScrollPolicy::Fixed {
let old_offset = offset_lookup.calculated_total_offsets.get(&clip_id).cloned();
offset_lookup.calculated_total_offsets.insert(clip_id, Vector2D::zero());
self.hit_test_contents(traversal, &point, offset_lookup, result);
match old_offset {
Some(offset) => offset_lookup.calculated_total_offsets.insert(clip_id, offset),
None => offset_lookup.calculated_total_offsets.remove(&clip_id),
};
} else if let Some(transform) = stacking_context.transform {
if let Some(ref mut sublookup) =
offset_lookup.new_for_reference_frame(clip_id, &transform, &mut point) {
self.hit_test_contents(traversal, &point, sublookup, result);
}
} else {
self.hit_test_contents(traversal, &point, offset_lookup, result);
}
}
pub fn print(&self) {
let mut print_tree = PrintTree::new("Display List".to_owned());
self.print_with_tree(&mut print_tree);
@ -1283,60 +1210,6 @@ impl DisplayItem {
}
println!("{}+ {:?}", indent, self);
}
fn hit_test(&self,
point: Point2D<Au>,
offset_lookup: &mut ScrollOffsetLookup)
-> Option<DisplayItemMetadata> {
// TODO(pcwalton): Use a precise algorithm here. This will allow us to properly hit
// test elements with `border-radius`, for example.
let base_item = self.base();
let scroll_offset = offset_lookup.full_offset_for_clip_scroll_node(&self.scroll_node_id());
let point = Point2D::new(point.x - Au::from_f32_px(scroll_offset.x),
point.y - Au::from_f32_px(scroll_offset.y));
if !base_item.local_clip.clip_rect().contains(&point.to_pointf()) {
// Clipped out.
return None;
}
if !self.bounds().contains(&point) {
// Can't possibly hit.
return None;
}
if base_item.metadata.pointing.is_none() {
// `pointer-events` is `none`. Ignore this item.
return None;
}
match *self {
DisplayItem::Border(ref border) => {
// If the point is inside the border, it didn't hit the border!
let interior_rect =
Rect::new(
Point2D::new(border.base.bounds.origin.x +
border.border_widths.left,
border.base.bounds.origin.y +
border.border_widths.top),
Size2D::new(border.base.bounds.size.width -
(border.border_widths.left +
border.border_widths.right),
border.base.bounds.size.height -
(border.border_widths.top +
border.border_widths.bottom)));
if interior_rect.contains(&point) {
return None;
}
}
DisplayItem::BoxShadow(_) => {
// Box shadows can never be hit.
return None;
}
_ => {}
}
Some(base_item.metadata)
}
}
impl fmt::Debug for DisplayItem {