layout: Cache projected point in spatial node when hit testing (#38464)

Implement a simple one element cache for projected points in spatial
nodes. This should reduce the amount matrix math done during hit
testing.

Testing: This should not change test results, but should improve
performance a bit. Thus, tests are not necessary beyond existing
performance tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Martin Robinson 2025-08-05 13:07:42 +02:00 committed by GitHub
parent ec99e7a8b0
commit 0bf8676345
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -28,6 +28,9 @@ pub(crate) struct HitTest<'a> {
flags: ElementsFromPointFlags, flags: ElementsFromPointFlags,
/// The point to test for this hit test, relative to the page. /// The point to test for this hit test, relative to the page.
point_to_test: LayoutPoint, point_to_test: LayoutPoint,
/// A cached version of [`Self::point_to_test`] projected to a spatial node, to avoid
/// doing a lot of matrix math over and over.
projected_point_to_test: Option<(ScrollTreeNodeId, LayoutPoint, LayoutTransform)>,
/// The stacking context tree against which to perform the hit test. /// The stacking context tree against which to perform the hit test.
stacking_context_tree: &'a StackingContextTree, stacking_context_tree: &'a StackingContextTree,
/// The resulting [`HitTestResultItems`] for this hit test. /// The resulting [`HitTestResultItems`] for this hit test.
@ -45,6 +48,7 @@ impl<'a> HitTest<'a> {
let mut hit_test = Self { let mut hit_test = Self {
flags, flags,
point_to_test, point_to_test,
projected_point_to_test: None,
stacking_context_tree, stacking_context_tree,
results: Vec::new(), results: Vec::new(),
clip_hit_test_results: HashMap::new(), clip_hit_test_results: HashMap::new(),
@ -80,9 +84,18 @@ impl<'a> HitTest<'a> {
/// returning `None` if the transformation is uninvertible or the point cannot be /// returning `None` if the transformation is uninvertible or the point cannot be
/// projected into the spatial node. /// projected into the spatial node.
fn location_in_spatial_node( fn location_in_spatial_node(
&self, &mut self,
scroll_tree_node_id: ScrollTreeNodeId, scroll_tree_node_id: ScrollTreeNodeId,
) -> Option<(LayoutPoint, LayoutTransform)> { ) -> Option<(LayoutPoint, LayoutTransform)> {
match self.projected_point_to_test {
Some((cached_scroll_tree_node_id, projected_point, transform))
if cached_scroll_tree_node_id == scroll_tree_node_id =>
{
return Some((projected_point, transform));
},
_ => {},
}
let transform = self let transform = self
.stacking_context_tree .stacking_context_tree
.compositor_info .compositor_info
@ -103,11 +116,10 @@ impl<'a> HitTest<'a> {
let z = let z =
-(point.x * transform.m13 + point.y * transform.m23 + transform.m43) / transform.m33; -(point.x * transform.m13 + point.y * transform.m23 + transform.m43) / transform.m33;
let projected_point = transform.transform_point3d(Point3D::new(point.x, point.y, z))?; let projected_point = transform.transform_point3d(Point3D::new(point.x, point.y, z))?;
let projected_point = Point2D::new(projected_point.x, projected_point.y);
Some(( self.projected_point_to_test = Some((scroll_tree_node_id, projected_point, transform));
Point2D::new(projected_point.x, projected_point.y), Some((projected_point, transform))
transform,
))
} }
} }