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 @@
#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/paint/paint_layer.h"
#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
namespace blink {
......@@ -157,6 +158,14 @@ LayoutObject* GetTargetLayoutObject(const Element& target_element) {
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 =
IntersectionGeometry::kShouldReportRootBounds |
IntersectionGeometry::kShouldComputeVisibility |
......@@ -261,8 +270,29 @@ void IntersectionGeometry::ComputeGeometry(const RootGeometry& root_geometry,
ClipToRoot(root, target, root_rect_, unclipped_intersection_rect_,
intersection_rect_);
// Map target_rect_ to absolute coordinates for target's document
target_rect_ = target->LocalToAncestorRect(target_rect_, nullptr);
// Map target_rect_ to absolute coordinates for target's document.
// 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 (RootIsImplicit()) {
// Generate matrix to transform from the space of the implicit root to
......@@ -367,14 +397,10 @@ bool IntersectionGeometry::ClipToRoot(const LayoutObject* root,
if (!RootIsImplicit() || root->GetDocument().IsInMainFrame())
local_ancestor = ToLayoutBox(root);
const LayoutView* layout_view = target->GetDocument().GetLayoutView();
unsigned flags = kDefaultVisualRectFlags | kEdgeInclusive |
kDontApplyMainFrameOverflowClip;
if (!layout_view->NeedsPaintPropertyUpdate() &&
!layout_view->DescendantNeedsPaintPropertyUpdate()) {
if (CanUseGeometryMapper(target))
flags |= kUseGeometryMapper;
}
bool does_intersect = target->MapToVisualRectInAncestorSpace(
local_ancestor, unclipped_intersection_rect,
static_cast<VisualRectFlags>(flags));
......
......@@ -1628,14 +1628,12 @@ bool LayoutObject::MapToVisualRectInAncestorSpaceInternalFastPath(
if (ancestor == this)
return true;
const auto* property_container = this;
AncestorSkipInfo skip_info(ancestor);
while (!property_container->FirstFragment().HasLocalBorderBoxProperties()) {
property_container = property_container->Container(&skip_info);
if (!property_container || skip_info.AncestorSkipped() ||
property_container->FirstFragment().NextFragment())
return false;
}
PropertyTreeState container_properties = PropertyTreeState::Uninitialized();
const LayoutObject* property_container =
GetPropertyContainer(&skip_info, &container_properties);
if (!property_container)
return false;
// This works because it's not possible to have any intervening clips,
// effects, transforms between |this| and |property_container|, and therefore
......@@ -1645,13 +1643,9 @@ bool LayoutObject::MapToVisualRectInAncestorSpaceInternalFastPath(
rect.Move(FirstFragment().PaintOffset());
if (property_container != ancestor) {
FloatClipRect clip_rect((FloatRect(rect)));
const auto& local_state =
property_container == this
? FirstFragment().LocalBorderBoxProperties()
: property_container->FirstFragment().ContentsProperties();
intersects = GeometryMapper::LocalToAncestorVisualRect(
local_state, ancestor->FirstFragment().ContentsProperties(), clip_rect,
kIgnorePlatformOverlayScrollbarSize,
container_properties, ancestor->FirstFragment().ContentsProperties(),
clip_rect, kIgnorePlatformOverlayScrollbarSize,
(visual_rect_flags & kEdgeInclusive) ? kInclusiveIntersect
: kNonInclusiveIntersect);
rect = PhysicalRect::EnclosingRect(clip_rect.Rect());
......@@ -1709,6 +1703,27 @@ bool LayoutObject::MapToVisualRectInAncestorSpaceInternal(
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(
const PhysicalRect& hit_rect) const {
LocalFrame* frame = GetDocument().GetFrame();
......
......@@ -1868,6 +1868,14 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver,
TransformState&,
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.
HitTestResult HitTestForOcclusion(const PhysicalRect&) 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