Commit 17a9c540 authored by Chris Harrelson's avatar Chris Harrelson Committed by Commit Bot

[Hit Testing] Optimize for the case of infinite clip rects

* Don't intersect with the overall dirty rect except at changes of root layer, because
otherwise the location and rect don't change
* Early out intersections when a ClipRect is infinite (since it has no effect)
* Don't adjust infinite ClipRects (since it has no effect)
* Simplify / resolve TODOs in some cases where we previously had to construct
 an "infinite" layout rect

In local testing, this CL yields a 10% improvement on the intersection-observer/deep-layers.html
performance test due to faster hit testing.

Bug: 831762

Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: Ie7a1e2fb5cbeb8f7f9a267489785266ed7b8ca47
Reviewed-on: https://chromium-review.googlesource.com/1104964
Commit-Queue: Chris Harrelson <chrishtr@chromium.org>
Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568631}
parent 099664d6
......@@ -2033,6 +2033,7 @@ jumbo_source_set("unit_tests") {
"paint/block_painter_test.cc",
"paint/box_paint_invalidator_test.cc",
"paint/clip_path_clipper_test.cc",
"paint/clip_rect_test.cc",
"paint/compositing/composited_layer_mapping_test.cc",
"paint/compositing/compositing_inputs_updater_test.cc",
"paint/compositing/compositing_layer_assigner_test.cc",
......
......@@ -701,7 +701,7 @@ void LayoutTreeAsText::WriteLayers(WTF::TextStream& ts,
ClipRect damage_rect, clip_rect_to_apply;
layer->Clipper(PaintLayer::kUseGeometryMapper)
.CalculateRects(ClipRectsContext(root_layer, kUncachedClipRects),
&layer->GetLayoutObject().FirstFragment(), paint_rect,
&layer->GetLayoutObject().FirstFragment(), &paint_rect,
layer_bounds, damage_rect, clip_rect_to_apply);
// Ensure our lists are up to date.
......
......@@ -33,19 +33,31 @@
namespace blink {
ClipRect::ClipRect(const FloatClipRect& rect)
: rect_(rect.Rect()), has_radius_(rect.HasRadius()) {}
: rect_(rect.Rect()),
has_radius_(rect.HasRadius()),
is_infinite_(rect.IsInfinite()) {}
void ClipRect::SetRect(const FloatClipRect& rect) {
rect_ = LayoutRect(rect.Rect());
has_radius_ = rect.HasRadius();
is_infinite_ = rect.IsInfinite();
}
void ClipRect::SetRect(const LayoutRect& rect) {
rect_ = rect;
has_radius_ = false;
is_infinite_ = false;
}
bool ClipRect::Intersects(const HitTestLocation& hit_test_location) const {
if (is_infinite_)
return true;
return hit_test_location.Intersects(rect_);
}
String ClipRect::ToString() const {
return rect_.ToString() + (has_radius_ ? " hasRadius" : " noRadius");
return rect_.ToString() + (has_radius_ ? " hasRadius" : " noRadius") +
(is_infinite_ ? " isInfinite" : " notInfinite");
}
std::ostream& operator<<(std::ostream& ostream, const ClipRect& rect) {
......
......@@ -35,20 +35,27 @@ namespace blink {
class FloatClipRect;
class HitTestLocation;
class ClipRect {
class CORE_EXPORT ClipRect {
USING_FAST_MALLOC(ClipRect);
public:
ClipRect() : has_radius_(false) {}
ClipRect(const LayoutRect& rect) : rect_(rect), has_radius_(false) {}
ClipRect()
: rect_(LayoutRect::InfiniteIntRect()),
has_radius_(false),
is_infinite_(true) {}
ClipRect(const LayoutRect& rect)
: rect_(rect), has_radius_(false), is_infinite_(false) {}
ClipRect(const FloatClipRect& rect);
void SetRect(const LayoutRect& rect);
const LayoutRect& Rect() const { return rect_; }
void SetRect(const FloatClipRect& rect);
bool HasRadius() const { return has_radius_; }
void SetHasRadius(bool has_radius) { has_radius_ = has_radius; }
bool IsInfinite() const { return is_infinite_; }
bool operator==(const ClipRect& other) const {
return Rect() == other.Rect() && HasRadius() == other.HasRadius();
}
......@@ -59,9 +66,19 @@ class ClipRect {
return Rect() != other_rect;
}
void Intersect(const LayoutRect& other) { rect_.Intersect(other); }
void Intersect(const LayoutRect& other) {
if (IsInfinite()) {
rect_ = other;
is_infinite_ = false;
} else {
rect_.Intersect(other);
}
}
void Intersect(const ClipRect& other) {
rect_.Intersect(other.Rect());
if (other.IsInfinite())
return;
Intersect(other.Rect());
if (other.HasRadius())
has_radius_ = true;
}
......@@ -76,7 +93,8 @@ class ClipRect {
private:
LayoutRect rect_;
bool has_radius_;
bool has_radius_ : 1;
bool is_infinite_ : 1;
};
inline ClipRect Intersection(const ClipRect& a, const ClipRect& b) {
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/paint/clip_rect.h"
#include "third_party/blink/renderer/core/layout/hit_test_location.h"
#include "third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
class ClipRectTest : public testing::Test {};
TEST_F(ClipRectTest, IsInfinite) {
ClipRect rect;
EXPECT_TRUE(rect.IsInfinite());
rect.SetRect(FloatClipRect());
EXPECT_TRUE(rect.IsInfinite());
rect.SetRect(LayoutRect());
EXPECT_FALSE(rect.IsInfinite());
}
TEST_F(ClipRectTest, HasRadius) {
ClipRect rect;
EXPECT_FALSE(rect.HasRadius());
rect.SetRect(FloatClipRect());
EXPECT_FALSE(rect.HasRadius());
FloatClipRect float_clip_rect;
float_clip_rect.SetHasRadius();
rect.SetRect(float_clip_rect);
EXPECT_TRUE(rect.HasRadius());
rect.SetRect(LayoutRect());
EXPECT_FALSE(rect.HasRadius());
rect.SetHasRadius(true);
EXPECT_TRUE(rect.HasRadius());
}
TEST_F(ClipRectTest, IntersectClipRect) {
ClipRect rect;
rect.SetRect(LayoutRect(100, 200, 300, 400));
EXPECT_FALSE(rect.HasRadius());
ClipRect rect2;
rect2.SetRect(LayoutRect(100, 100, 200, 300));
rect2.SetHasRadius(true);
rect.Intersect(rect2);
EXPECT_TRUE(rect.HasRadius());
EXPECT_FALSE(rect.IsInfinite());
EXPECT_EQ(LayoutRect(100, 200, 200, 200), rect.Rect());
}
TEST_F(ClipRectTest, IntersectLayoutRect) {
ClipRect rect;
LayoutRect layout_rect;
rect.Intersect(layout_rect);
EXPECT_FALSE(rect.IsInfinite());
}
TEST_F(ClipRectTest, IntersectsInfinite) {
ClipRect rect;
EXPECT_TRUE(rect.Intersects(HitTestLocation(FloatPoint(100000, -3333333))));
}
TEST_F(ClipRectTest, ToString) {
ClipRect rect;
rect.SetRect(LayoutRect(0, 0, 100, 100));
EXPECT_EQ(String("0,0 100x100 noRadius notInfinite"), rect.ToString());
rect.SetHasRadius(true);
EXPECT_EQ(String("0,0 100x100 hasRadius notInfinite"), rect.ToString());
}
} // namespace blink
......@@ -907,7 +907,7 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
void AppendSingleFragmentIgnoringPagination(
PaintLayerFragments&,
const PaintLayer* root_layer,
const LayoutRect& dirty_rect,
const LayoutRect* dirty_rect,
OverlayScrollbarClipBehavior = kIgnorePlatformOverlayScrollbarSize,
ShouldRespectOverflowClipType = kRespectOverflowClip,
const LayoutPoint* offset_from_root = nullptr,
......@@ -916,7 +916,7 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
void CollectFragments(
PaintLayerFragments&,
const PaintLayer* root_layer,
const LayoutRect& dirty_rect,
const LayoutRect* dirty_rect,
OverlayScrollbarClipBehavior = kIgnorePlatformOverlayScrollbarSize,
ShouldRespectOverflowClipType = kRespectOverflowClip,
const LayoutPoint* offset_from_root = nullptr,
......@@ -1091,11 +1091,20 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
void SetLastChild(PaintLayer* last) { last_ = last; }
void UpdateHasSelfPaintingLayerDescendant() const;
struct HitTestRecursionData {
const LayoutRect& rect;
// Whether location.Intersects(rect) returns true.
const HitTestLocation& location;
const bool intersects_location;
HitTestRecursionData(const LayoutRect& rect_arg,
const HitTestLocation& location_arg);
};
PaintLayer* HitTestLayer(PaintLayer* root_layer,
PaintLayer* container_layer,
HitTestResult&,
const LayoutRect& hit_test_rect,
const HitTestLocation&,
const HitTestRecursionData& recursion_data,
bool applied_transform,
const HitTestingTransformState* = nullptr,
double* z_offset = nullptr);
......@@ -1103,8 +1112,7 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
PaintLayer* root_layer,
PaintLayer* container_layer,
HitTestResult&,
const LayoutRect& hit_test_rect,
const HitTestLocation&,
const HitTestRecursionData& recursion_data,
const HitTestingTransformState* = nullptr,
double* z_offset = nullptr,
const LayoutPoint& translation_offset = LayoutPoint());
......@@ -1112,8 +1120,7 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
ChildrenIteration,
PaintLayer* root_layer,
HitTestResult&,
const LayoutRect& hit_test_rect,
const HitTestLocation&,
const HitTestRecursionData& recursion_data,
const HitTestingTransformState*,
double* z_offset_for_descendants,
double* z_offset,
......@@ -1123,8 +1130,7 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
scoped_refptr<HitTestingTransformState> CreateLocalTransformState(
PaintLayer* root_layer,
PaintLayer* container_layer,
const LayoutRect& hit_test_rect,
const HitTestLocation&,
const HitTestRecursionData& recursion_data,
const HitTestingTransformState* container_transform_state,
const LayoutPoint& translation_offset = LayoutPoint()) const;
......@@ -1142,8 +1148,7 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
PaintLayer* root_layer,
PaintLayer* container_layer,
HitTestResult&,
const LayoutRect& hit_test_rect,
const HitTestLocation&,
const HitTestRecursionData&,
const HitTestingTransformState*,
double* z_offset,
ShouldRespectOverflowClipType);
......
......@@ -221,8 +221,11 @@ LayoutRect PaintLayerClipper::LocalClipRect(
CalculateBackgroundClipRectWithGeometryMapper(
context, layer_.GetLayoutObject().FirstFragment(), kRespectOverflowClip,
clip_rect);
LayoutRect premapped_rect = clip_rect.Rect();
if (clip_rect.IsInfinite())
return clip_rect.Rect();
LayoutRect premapped_rect = clip_rect.Rect();
// The rect now needs to be transformed to the local space of this
// PaintLayer.
// TODO(chrishtr): not correct for fragmentation.
......@@ -251,14 +254,13 @@ LayoutRect PaintLayerClipper::LocalClipRect(
LayoutRect layer_bounds;
ClipRect background_rect, foreground_rect;
CalculateRects(context, nullptr, LayoutRect(LayoutRect::InfiniteIntRect()),
layer_bounds, background_rect, foreground_rect);
CalculateRects(context, nullptr, nullptr, layer_bounds, background_rect,
foreground_rect);
LayoutRect clip_rect = background_rect.Rect();
// TODO(chrishtr): avoid converting to IntRect and back.
if (clip_rect == LayoutRect(LayoutRect::InfiniteIntRect()))
return clip_rect;
if (background_rect.IsInfinite())
return background_rect.Rect();
LayoutRect clip_rect = background_rect.Rect();
LayoutPoint clipping_root_offset;
layer_.ConvertToLayerCoords(&clipping_root_layer, clipping_root_offset);
clip_rect.MoveBy(-clipping_root_offset);
......@@ -269,7 +271,7 @@ LayoutRect PaintLayerClipper::LocalClipRect(
void PaintLayerClipper::CalculateRectsWithGeometryMapper(
const ClipRectsContext& context,
const FragmentData& fragment_data,
const LayoutRect& paint_dirty_rect,
const LayoutRect* paint_dirty_rect,
LayoutRect& layer_bounds,
ClipRect& background_rect,
ClipRect& foreground_rect,
......@@ -329,7 +331,9 @@ void PaintLayerClipper::CalculateRectsWithGeometryMapper(
CalculateBackgroundClipRectWithGeometryMapper(
context, fragment_data, kRespectOverflowClip, background_rect);
background_rect.Intersect(paint_dirty_rect);
if (paint_dirty_rect)
background_rect.Intersect(*paint_dirty_rect);
if (ShouldClipOverflow(context)) {
LayoutBoxModelObject& layout_object = layer_.GetLayoutObject();
......@@ -348,7 +352,7 @@ void PaintLayerClipper::CalculateRectsWithGeometryMapper(
void PaintLayerClipper::CalculateRects(
const ClipRectsContext& context,
const FragmentData* fragment_data,
const LayoutRect& paint_dirty_rect,
const LayoutRect* paint_dirty_rect,
LayoutRect& layer_bounds,
ClipRect& background_rect,
ClipRect& foreground_rect,
......@@ -374,10 +378,9 @@ void PaintLayerClipper::CalculateRects(
if (!is_clipping_root && layer_.Parent()) {
CalculateBackgroundClipRect(context, background_rect);
background_rect.Move(context.sub_pixel_accumulation);
background_rect.Intersect(paint_dirty_rect);
} else {
background_rect = paint_dirty_rect;
}
if (paint_dirty_rect)
background_rect.Intersect(*paint_dirty_rect);
foreground_rect = background_rect;
......@@ -530,10 +533,12 @@ void PaintLayerClipper::CalculateBackgroundClipRectWithGeometryMapper(
output.SetRect(clipped_rect_in_root_layer_space);
}
// TODO(chrishtr): generalize to multiple fragments.
output.MoveBy(
-context.root_layer->GetLayoutObject().FirstFragment().PaintOffset());
output.Move(context.sub_pixel_accumulation);
if (!output.IsInfinite()) {
// TODO(chrishtr): generalize to multiple fragments.
output.MoveBy(
-context.root_layer->GetLayoutObject().FirstFragment().PaintOffset());
output.Move(context.sub_pixel_accumulation);
}
}
void PaintLayerClipper::InitializeCommonClipRectState(
......
......@@ -188,9 +188,11 @@ class CORE_EXPORT PaintLayerClipper {
// include subpixel accumualation. Otherwise it is set to the offset from
// |layer_| to |root_layer|, plus |context.sub_pixel_accumuation|.
// |fragment_data| is only used in kUseGeometryMapper mode.
// If |paint_dirty_rect| is provided, intersects |background_rect|
// and |foreground_rect| with it.
void CalculateRects(const ClipRectsContext&,
const FragmentData*,
const LayoutRect& paint_dirty_rect,
const LayoutRect* paint_dirty_rect,
LayoutRect& layer_bounds,
ClipRect& background_rect,
ClipRect& foreground_rect,
......@@ -227,7 +229,7 @@ class CORE_EXPORT PaintLayerClipper {
ALWAYS_INLINE void CalculateRectsWithGeometryMapper(
const ClipRectsContext&,
const FragmentData&,
const LayoutRect& paint_dirty_rect,
const LayoutRect* paint_dirty_rect,
LayoutRect& layer_bounds,
ClipRect& background_rect,
ClipRect& foreground_rect,
......
......@@ -523,7 +523,7 @@ PaintResult PaintLayerPainter::PaintLayerContents(
// PaintLayerWithTransform() path.
paint_layer_for_fragments->AppendSingleFragmentIgnoringPagination(
layer_fragments, local_painting_info.root_layer,
local_painting_info.paint_dirty_rect,
&local_painting_info.paint_dirty_rect,
kIgnorePlatformOverlayScrollbarSize, respect_overflow_clip,
&offset_from_root, local_painting_info.sub_pixel_accumulation);
layer_fragments[0].fragment_data = fragment->fragment_data;
......@@ -533,7 +533,7 @@ PaintResult PaintLayerPainter::PaintLayerContents(
PaintLayerFragments single_fragment;
paint_layer_for_fragments->AppendSingleFragmentIgnoringPagination(
single_fragment, local_painting_info.root_layer,
local_painting_info.paint_dirty_rect,
&local_painting_info.paint_dirty_rect,
kIgnorePlatformOverlayScrollbarSize, respect_overflow_clip,
&offset_from_root, local_painting_info.sub_pixel_accumulation);
RepeatFixedPositionObjectInPages(single_fragment[0], painting_info,
......@@ -566,7 +566,7 @@ PaintResult PaintLayerPainter::PaintLayerContents(
} else {
paint_layer_for_fragments->CollectFragments(
layer_fragments, local_painting_info.root_layer,
local_painting_info.paint_dirty_rect,
&local_painting_info.paint_dirty_rect,
kIgnorePlatformOverlayScrollbarSize, respect_overflow_clip,
&offset_from_root, local_painting_info.sub_pixel_accumulation);
......@@ -611,7 +611,7 @@ PaintResult PaintLayerPainter::PaintLayerContents(
PaintLayerFragments filter_fragments;
paint_layer_.AppendSingleFragmentIgnoringPagination(
filter_fragments, local_painting_info.root_layer,
local_painting_info.paint_dirty_rect,
&local_painting_info.paint_dirty_rect,
kIgnorePlatformOverlayScrollbarSize, respect_overflow_clip,
&offset_from_root, local_painting_info.sub_pixel_accumulation);
......@@ -921,7 +921,7 @@ PaintResult PaintLayerPainter::PaintLayerWithAdjustedRoot(
ShouldRespectOverflowClip(paint_flags, paint_layer_.GetLayoutObject());
paint_layer_.CollectFragments(
layer_fragments, painting_info.root_layer,
painting_info.paint_dirty_rect, kIgnorePlatformOverlayScrollbarSize,
&painting_info.paint_dirty_rect, kIgnorePlatformOverlayScrollbarSize,
respect_overflow_clip, nullptr, painting_info.sub_pixel_accumulation);
// PaintLayer::CollectFragments depends on the paint dirty rect in
// complicated ways. For now, always assume a partially painted output
......
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