Commit 5c7cba20 authored by Stefan Zager's avatar Stefan Zager Committed by Commit Bot

[IntersectionObserver] Use GeometryMapper to translate target rect

GeometryMapper is much faster than MapLocalToAncestor. Running a
release build of chrome on my linux workstation, this patch decreases
the average run time of perf_tests/intersection-observer/scroller.html
by ~65%, and perf_tests/intersection-observer/many-objects.html by
~50%.

Change-Id: Ifcc97d02297ce9a5ff205b0178cb524c0570a0ad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1999455Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Reviewed-by: default avatarvmpstr <vmpstr@chromium.org>
Commit-Queue: Stefan Zager <szager@chromium.org>
Cr-Commit-Position: refs/heads/master@{#732438}
parent 5633ce55
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
namespace blink { namespace blink {
...@@ -157,6 +158,14 @@ LayoutObject* GetTargetLayoutObject(const Element& target_element) { ...@@ -157,6 +158,14 @@ LayoutObject* GetTargetLayoutObject(const Element& target_element) {
return target; return target;
} }
bool CanUseGeometryMapper(const LayoutObject* object) {
// This checks for cases where we didn't just complete a successful lifecycle
// update, e.g., if the frame is throttled.
LayoutView* layout_view = object->GetDocument().GetLayoutView();
return layout_view && !layout_view->NeedsPaintPropertyUpdate() &&
!layout_view->DescendantNeedsPaintPropertyUpdate();
}
static const unsigned kConstructorFlagsMask = static const unsigned kConstructorFlagsMask =
IntersectionGeometry::kShouldReportRootBounds | IntersectionGeometry::kShouldReportRootBounds |
IntersectionGeometry::kShouldComputeVisibility | IntersectionGeometry::kShouldComputeVisibility |
...@@ -261,8 +270,29 @@ void IntersectionGeometry::ComputeGeometry(const RootGeometry& root_geometry, ...@@ -261,8 +270,29 @@ void IntersectionGeometry::ComputeGeometry(const RootGeometry& root_geometry,
ClipToRoot(root, target, root_rect_, unclipped_intersection_rect_, ClipToRoot(root, target, root_rect_, unclipped_intersection_rect_,
intersection_rect_); intersection_rect_);
// Map target_rect_ to absolute coordinates for target's document // Map target_rect_ to absolute coordinates for target's document.
target_rect_ = target->LocalToAncestorRect(target_rect_, nullptr); // GeometryMapper is faster, so we use it when possible; otherwise, fall back
// to LocalToAncestorRect.
PropertyTreeState container_properties = PropertyTreeState::Uninitialized();
const LayoutObject* property_container =
CanUseGeometryMapper(target)
? target->GetPropertyContainer(nullptr, &container_properties)
: nullptr;
if (property_container) {
LayoutRect target_layout_rect = target_rect_.ToLayoutRect();
target_layout_rect.Move(
target->FirstFragment().PaintOffset().ToLayoutSize());
GeometryMapper::SourceToDestinationRect(container_properties.Transform(),
target->GetDocument()
.GetLayoutView()
->FirstFragment()
.LocalBorderBoxProperties()
.Transform(),
target_layout_rect);
target_rect_ = PhysicalRect(target_layout_rect);
} else {
target_rect_ = target->LocalToAncestorRect(target_rect_, nullptr);
}
if (does_intersect) { if (does_intersect) {
if (RootIsImplicit()) { if (RootIsImplicit()) {
// Generate matrix to transform from the space of the implicit root to // Generate matrix to transform from the space of the implicit root to
...@@ -367,14 +397,10 @@ bool IntersectionGeometry::ClipToRoot(const LayoutObject* root, ...@@ -367,14 +397,10 @@ bool IntersectionGeometry::ClipToRoot(const LayoutObject* root,
if (!RootIsImplicit() || root->GetDocument().IsInMainFrame()) if (!RootIsImplicit() || root->GetDocument().IsInMainFrame())
local_ancestor = ToLayoutBox(root); local_ancestor = ToLayoutBox(root);
const LayoutView* layout_view = target->GetDocument().GetLayoutView();
unsigned flags = kDefaultVisualRectFlags | kEdgeInclusive | unsigned flags = kDefaultVisualRectFlags | kEdgeInclusive |
kDontApplyMainFrameOverflowClip; kDontApplyMainFrameOverflowClip;
if (!layout_view->NeedsPaintPropertyUpdate() && if (CanUseGeometryMapper(target))
!layout_view->DescendantNeedsPaintPropertyUpdate()) {
flags |= kUseGeometryMapper; flags |= kUseGeometryMapper;
}
bool does_intersect = target->MapToVisualRectInAncestorSpace( bool does_intersect = target->MapToVisualRectInAncestorSpace(
local_ancestor, unclipped_intersection_rect, local_ancestor, unclipped_intersection_rect,
static_cast<VisualRectFlags>(flags)); static_cast<VisualRectFlags>(flags));
......
...@@ -1628,14 +1628,12 @@ bool LayoutObject::MapToVisualRectInAncestorSpaceInternalFastPath( ...@@ -1628,14 +1628,12 @@ bool LayoutObject::MapToVisualRectInAncestorSpaceInternalFastPath(
if (ancestor == this) if (ancestor == this)
return true; return true;
const auto* property_container = this;
AncestorSkipInfo skip_info(ancestor); AncestorSkipInfo skip_info(ancestor);
while (!property_container->FirstFragment().HasLocalBorderBoxProperties()) { PropertyTreeState container_properties = PropertyTreeState::Uninitialized();
property_container = property_container->Container(&skip_info); const LayoutObject* property_container =
if (!property_container || skip_info.AncestorSkipped() || GetPropertyContainer(&skip_info, &container_properties);
property_container->FirstFragment().NextFragment()) if (!property_container)
return false; return false;
}
// This works because it's not possible to have any intervening clips, // This works because it's not possible to have any intervening clips,
// effects, transforms between |this| and |property_container|, and therefore // effects, transforms between |this| and |property_container|, and therefore
...@@ -1645,13 +1643,9 @@ bool LayoutObject::MapToVisualRectInAncestorSpaceInternalFastPath( ...@@ -1645,13 +1643,9 @@ bool LayoutObject::MapToVisualRectInAncestorSpaceInternalFastPath(
rect.Move(FirstFragment().PaintOffset()); rect.Move(FirstFragment().PaintOffset());
if (property_container != ancestor) { if (property_container != ancestor) {
FloatClipRect clip_rect((FloatRect(rect))); FloatClipRect clip_rect((FloatRect(rect)));
const auto& local_state =
property_container == this
? FirstFragment().LocalBorderBoxProperties()
: property_container->FirstFragment().ContentsProperties();
intersects = GeometryMapper::LocalToAncestorVisualRect( intersects = GeometryMapper::LocalToAncestorVisualRect(
local_state, ancestor->FirstFragment().ContentsProperties(), clip_rect, container_properties, ancestor->FirstFragment().ContentsProperties(),
kIgnorePlatformOverlayScrollbarSize, clip_rect, kIgnorePlatformOverlayScrollbarSize,
(visual_rect_flags & kEdgeInclusive) ? kInclusiveIntersect (visual_rect_flags & kEdgeInclusive) ? kInclusiveIntersect
: kNonInclusiveIntersect); : kNonInclusiveIntersect);
rect = PhysicalRect::EnclosingRect(clip_rect.Rect()); rect = PhysicalRect::EnclosingRect(clip_rect.Rect());
...@@ -1709,6 +1703,27 @@ bool LayoutObject::MapToVisualRectInAncestorSpaceInternal( ...@@ -1709,6 +1703,27 @@ bool LayoutObject::MapToVisualRectInAncestorSpaceInternal(
return true; return true;
} }
const LayoutObject* LayoutObject::GetPropertyContainer(
AncestorSkipInfo* skip_info,
PropertyTreeState* container_properties) const {
const LayoutObject* property_container = this;
while (!property_container->FirstFragment().HasLocalBorderBoxProperties()) {
property_container = property_container->Container(skip_info);
if (!property_container || (skip_info && skip_info->AncestorSkipped()) ||
property_container->FirstFragment().NextFragment())
return nullptr;
}
if (container_properties) {
if (property_container == this) {
*container_properties = FirstFragment().LocalBorderBoxProperties();
} else {
*container_properties =
property_container->FirstFragment().ContentsProperties();
}
}
return property_container;
}
HitTestResult LayoutObject::HitTestForOcclusion( HitTestResult LayoutObject::HitTestForOcclusion(
const PhysicalRect& hit_rect) const { const PhysicalRect& hit_rect) const {
LocalFrame* frame = GetDocument().GetFrame(); LocalFrame* frame = GetDocument().GetFrame();
......
...@@ -1868,6 +1868,14 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, ...@@ -1868,6 +1868,14 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver,
TransformState&, TransformState&,
VisualRectFlags = kDefaultVisualRectFlags) const; VisualRectFlags = kDefaultVisualRectFlags) const;
// Returns the nearest ancestor in the containing block chain that
// HasLocalBorderBoxProperties. If AncestorSkipInfo* is non-null and the
// ancestor was skipped, returns nullptr. If PropertyTreeState* is non-null,
// it will be populated with paint property nodes suitable for mapping upward
// from the coordinate system of the property container.
const LayoutObject* GetPropertyContainer(AncestorSkipInfo*,
PropertyTreeState* = nullptr) const;
// Do a rect-based hit test with this object as the stop node. // Do a rect-based hit test with this object as the stop node.
HitTestResult HitTestForOcclusion(const PhysicalRect&) const; HitTestResult HitTestForOcclusion(const PhysicalRect&) const;
HitTestResult HitTestForOcclusion() const { HitTestResult HitTestForOcclusion() const {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment