Commit 80d31ee1 authored by Xianzhu Wang's avatar Xianzhu Wang Committed by Commit Bot

[SPv2] Properly handle infinite cull rects through scroll translations

- Allow infinite cull rect to be properly clipped and expanded through
  scroll translation. This can prevent SPv2 performance regression when
  painting scrolling contents when some ancestor uses infinite cull
  rects. This achieves the similar results of SPv1 painting composited
  scrolling contents.

- Copy and adapt the interest rect tests from composited_layer_mapping_test.cc
  into paint_layer_painter_test.cc

- Add infinite cull rect unit tests

Bug: 792577
Change-Id: Ia784416557e10a3193cd677ad8d31101fd077bc6
Reviewed-on: https://chromium-review.googlesource.com/c/1347354Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611271}
parent e5b4016f
...@@ -202,11 +202,9 @@ static bool ShouldRepaintSubsequence( ...@@ -202,11 +202,9 @@ static bool ShouldRepaintSubsequence(
PaintLayer& paint_layer, PaintLayer& paint_layer,
const PaintLayerPaintingInfo& painting_info, const PaintLayerPaintingInfo& painting_info,
ShouldRespectOverflowClipType respect_overflow_clip) { ShouldRespectOverflowClipType respect_overflow_clip) {
bool needs_repaint = false;
// Repaint subsequence if the layer is marked for needing repaint. // Repaint subsequence if the layer is marked for needing repaint.
if (paint_layer.NeedsRepaint()) if (paint_layer.NeedsRepaint())
needs_repaint = true; return true;
// Repaint if previously the layer may be clipped by cull rect, and cull rect // Repaint if previously the layer may be clipped by cull rect, and cull rect
// changes. // changes.
...@@ -216,12 +214,10 @@ static bool ShouldRepaintSubsequence( ...@@ -216,12 +214,10 @@ static bool ShouldRepaintSubsequence(
// new and cached subsequences. Normally we can reuse the cached fully // new and cached subsequences. Normally we can reuse the cached fully
// painted subsequence even if we would partially paint this time. // painted subsequence even if we would partially paint this time.
RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) && RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) &&
paint_layer.PreviousCullRect() != painting_info.cull_rect) { paint_layer.PreviousCullRect() != painting_info.cull_rect)
needs_repaint = true; return true;
}
paint_layer.SetPreviousCullRect(painting_info.cull_rect);
return needs_repaint; return false;
} }
static bool ShouldUseInfiniteCullRect(const GraphicsContext& context, static bool ShouldUseInfiniteCullRect(const GraphicsContext& context,
...@@ -259,30 +255,21 @@ void PaintLayerPainter::AdjustForPaintProperties( ...@@ -259,30 +255,21 @@ void PaintLayerPainter::AdjustForPaintProperties(
PaintLayerFlags& paint_flags) { PaintLayerFlags& paint_flags) {
const auto& first_fragment = paint_layer_.GetLayoutObject().FirstFragment(); const auto& first_fragment = paint_layer_.GetLayoutObject().FirstFragment();
bool is_using_infinite_cull_rect = painting_info.cull_rect.IsInfinite();
bool should_use_infinite_cull_rect = bool should_use_infinite_cull_rect =
ShouldUseInfiniteCullRect(context, paint_layer_, painting_info); ShouldUseInfiniteCullRect(context, paint_layer_, painting_info);
if (!is_using_infinite_cull_rect && should_use_infinite_cull_rect) { if (should_use_infinite_cull_rect)
painting_info.cull_rect = CullRect::Infinite(); painting_info.cull_rect = CullRect::Infinite();
is_using_infinite_cull_rect = true;
}
if (painting_info.root_layer == &paint_layer_) if (painting_info.root_layer == &paint_layer_)
return; return;
if (!should_use_infinite_cull_rect) {
const auto& first_root_fragment = const auto& first_root_fragment =
painting_info.root_layer->GetLayoutObject().FirstFragment(); painting_info.root_layer->GetLayoutObject().FirstFragment();
bool transform_changed = if (first_root_fragment.LocalBorderBoxProperties().Transform() ==
first_root_fragment.LocalBorderBoxProperties().Transform() != first_fragment.LocalBorderBoxProperties().Transform())
first_fragment.LocalBorderBoxProperties().Transform();
// Will use the current layer as the new root layer if the layer requires
// infinite dirty rect or has different transform space from the current
// root layer.
if (!should_use_infinite_cull_rect && !transform_changed)
return; return;
if (!is_using_infinite_cull_rect && transform_changed) {
// painting_info.cull_rect is currently in |painting_info.root_layer|'s // painting_info.cull_rect is currently in |painting_info.root_layer|'s
// pixel-snapped border box space. We need to adjust it into // pixel-snapped border box space. We need to adjust it into
// |paint_layer_|'s space. This handles the following cases: // |paint_layer_|'s space. This handles the following cases:
...@@ -307,14 +294,16 @@ void PaintLayerPainter::AdjustForPaintProperties( ...@@ -307,14 +294,16 @@ void PaintLayerPainter::AdjustForPaintProperties(
// Convert cull_rect from the layer's transform space to the layer's local // Convert cull_rect from the layer's transform space to the layer's local
// space. // space.
cull_rect.MoveBy(-RoundedIntPoint(first_fragment.PaintOffset())); cull_rect.MoveBy(-RoundedIntPoint(first_fragment.PaintOffset()));
} else { } else if (!painting_info.cull_rect.IsInfinite()) {
auto rect = painting_info.cull_rect.Rect(); auto rect = painting_info.cull_rect.Rect();
first_root_fragment.MapRectToFragment(first_fragment, rect); first_root_fragment.MapRectToFragment(first_fragment, rect);
painting_info.cull_rect = CullRect(rect); painting_info.cull_rect = CullRect(rect);
} }
} }
// Make the current layer the new root layer. // We reach here if the layer requires infinite cull rect or has different
// transform space from the current root layer. Use the current layer as
// the new root layer.
painting_info.root_layer = &paint_layer_; painting_info.root_layer = &paint_layer_;
// These flags no longer apply for the new root layer. // These flags no longer apply for the new root layer.
paint_flags &= ~kPaintLayerPaintingSkipRootBackground; paint_flags &= ~kPaintLayerPaintingSkipRootBackground;
...@@ -645,8 +634,8 @@ PaintResult PaintLayerPainter::PaintLayerContents( ...@@ -645,8 +634,8 @@ PaintResult PaintLayerPainter::PaintLayerContents(
} }
} }
if (subsequence_recorder)
paint_layer_.SetPreviousPaintResult(result); paint_layer_.SetPreviousPaintResult(result);
paint_layer_.SetPreviousCullRect(local_painting_info.cull_rect);
return result; return result;
} }
......
...@@ -60,7 +60,6 @@ class CORE_EXPORT PaintLayerPainter { ...@@ -60,7 +60,6 @@ class CORE_EXPORT PaintLayerPainter {
private: private:
friend class PaintLayerPainterTest; friend class PaintLayerPainterTest;
friend class PaintLayerPainterTestSPv2;
PaintResult PaintChildren(unsigned children_to_visit, PaintResult PaintChildren(unsigned children_to_visit,
GraphicsContext&, GraphicsContext&,
......
...@@ -47,9 +47,6 @@ void CullRect::Move(const IntSize& offset) { ...@@ -47,9 +47,6 @@ void CullRect::Move(const IntSize& offset) {
CullRect::ApplyTransformResult CullRect::ApplyTransformInternal( CullRect::ApplyTransformResult CullRect::ApplyTransformInternal(
const TransformPaintPropertyNode* transform) { const TransformPaintPropertyNode* transform) {
if (IsInfinite())
return kNotExpanded;
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
if (const auto* scroll = transform->ScrollNode()) { if (const auto* scroll = transform->ScrollNode()) {
rect_.Intersect(scroll->ContainerRect()); rect_.Intersect(scroll->ContainerRect());
...@@ -59,8 +56,6 @@ CullRect::ApplyTransformResult CullRect::ApplyTransformInternal( ...@@ -59,8 +56,6 @@ CullRect::ApplyTransformResult CullRect::ApplyTransformInternal(
// Expand the cull rect for scrolling contents in case of composited // Expand the cull rect for scrolling contents in case of composited
// scrolling. // scrolling.
// TODO(wangxianzhu): the expansion distance needs to be scaled to
// screen pixels.
// TODO(wangxianzhu): options for non-composited-scrolling contents: // TODO(wangxianzhu): options for non-composited-scrolling contents:
// 1. to use non-composted-scrolling heuristics to avoid expansion; // 1. to use non-composted-scrolling heuristics to avoid expansion;
// 2. to reduce the 4000px distance, no matter if the contents with be // 2. to reduce the 4000px distance, no matter if the contents with be
...@@ -77,37 +72,42 @@ CullRect::ApplyTransformResult CullRect::ApplyTransformInternal( ...@@ -77,37 +72,42 @@ CullRect::ApplyTransformResult CullRect::ApplyTransformInternal(
} }
} }
if (!IsInfinite())
rect_ = transform->Matrix().Inverse().MapRect(rect_); rect_ = transform->Matrix().Inverse().MapRect(rect_);
return kNotExpanded; return kNotExpanded;
} }
void CullRect::ApplyTransforms(const TransformPaintPropertyNode* from, void CullRect::ApplyTransforms(const TransformPaintPropertyNode* source,
const TransformPaintPropertyNode* to, const TransformPaintPropertyNode* destination,
const base::Optional<CullRect>& old_cull_rect) { const base::Optional<CullRect>& old_cull_rect) {
DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled()); DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
Vector<const TransformPaintPropertyNode*> scroll_translations; Vector<const TransformPaintPropertyNode*> scroll_translations;
for (const auto* t = to; t != from; t = t->Parent()) { for (const auto* t = destination; t != source; t = t->Parent()) {
if (!t) { if (!t) {
// |from| is not an ancestor of |to|. Simply map. // |source| is not an ancestor of |destination|. Simply map.
GeometryMapper::SourceToDestinationRect(from, to, rect_); GeometryMapper::SourceToDestinationRect(source, destination, rect_);
return; return;
} }
if (t->ScrollNode()) if (t->ScrollNode())
scroll_translations.push_back(t); scroll_translations.push_back(t);
} }
const auto* last_transform = from; const auto* last_transform = source;
ApplyTransformResult last_scroll_translation_result = kNotExpanded; ApplyTransformResult last_scroll_translation_result = kNotExpanded;
for (auto it = scroll_translations.rbegin(); it != scroll_translations.rend(); for (auto it = scroll_translations.rbegin(); it != scroll_translations.rend();
++it) { ++it) {
const auto* scroll_translation = *it; const auto* scroll_translation = *it;
if (!IsInfinite()) {
GeometryMapper::SourceToDestinationRect( GeometryMapper::SourceToDestinationRect(
last_transform, scroll_translation->Parent(), rect_); last_transform, scroll_translation->Parent(), rect_);
}
last_scroll_translation_result = ApplyTransformInternal(scroll_translation); last_scroll_translation_result = ApplyTransformInternal(scroll_translation);
last_transform = scroll_translation; last_transform = scroll_translation;
} }
GeometryMapper::SourceToDestinationRect(last_transform, to, rect_);
if (!IsInfinite())
GeometryMapper::SourceToDestinationRect(last_transform, destination, rect_);
if (last_scroll_translation_result == kExpandedForPartialScrollingContents && if (last_scroll_translation_result == kExpandedForPartialScrollingContents &&
old_cull_rect && !ChangedEnough(*old_cull_rect)) old_cull_rect && !ChangedEnough(*old_cull_rect))
......
...@@ -57,6 +57,44 @@ TEST_F(CullRectTest, IntersectsTransformed) { ...@@ -57,6 +57,44 @@ TEST_F(CullRectTest, IntersectsTransformed) {
EXPECT_FALSE(cull_rect.Intersects(IntRect(52, 52, 1, 1))); EXPECT_FALSE(cull_rect.Intersects(IntRect(52, 52, 1, 1)));
} }
TEST_F(CullRectTest, Infinite) {
EXPECT_TRUE(CullRect::Infinite().IsInfinite());
EXPECT_TRUE(CullRect(LayoutRect::InfiniteIntRect()).IsInfinite());
EXPECT_FALSE(CullRect(IntRect(0, 0, 100, 100)).IsInfinite());
}
TEST_F(CullRectTest, Move) {
CullRect cull_rect(IntRect(0, 0, 50, 50));
cull_rect.Move(IntSize());
EXPECT_EQ(IntRect(0, 0, 50, 50), cull_rect.Rect());
cull_rect.Move(IntSize(10, 20));
EXPECT_EQ(IntRect(10, 20, 50, 50), cull_rect.Rect());
}
TEST_F(CullRectTest, MoveInfinite) {
CullRect cull_rect = CullRect::Infinite();
cull_rect.Move(IntSize());
EXPECT_TRUE(cull_rect.IsInfinite());
cull_rect.Move(IntSize(10, 20));
EXPECT_TRUE(cull_rect.IsInfinite());
}
TEST_F(CullRectTest, MoveBy) {
CullRect cull_rect(IntRect(0, 0, 50, 50));
cull_rect.MoveBy(IntPoint());
EXPECT_EQ(IntRect(0, 0, 50, 50), cull_rect.Rect());
cull_rect.MoveBy(IntPoint(10, 20));
EXPECT_EQ(IntRect(10, 20, 50, 50), cull_rect.Rect());
}
TEST_F(CullRectTest, MoveByInfinite) {
CullRect cull_rect = CullRect::Infinite();
cull_rect.MoveBy(IntPoint());
EXPECT_TRUE(cull_rect.IsInfinite());
cull_rect.MoveBy(IntPoint(10, 20));
EXPECT_TRUE(cull_rect.IsInfinite());
}
TEST_F(CullRectTest, ApplyTransform) { TEST_F(CullRectTest, ApplyTransform) {
CullRect cull_rect(IntRect(1, 1, 50, 50)); CullRect cull_rect(IntRect(1, 1, 50, 50));
auto transform = auto transform =
...@@ -66,6 +104,15 @@ TEST_F(CullRectTest, ApplyTransform) { ...@@ -66,6 +104,15 @@ TEST_F(CullRectTest, ApplyTransform) {
EXPECT_EQ(IntRect(0, 0, 50, 50), cull_rect.Rect()); EXPECT_EQ(IntRect(0, 0, 50, 50), cull_rect.Rect());
} }
TEST_F(CullRectTest, ApplyTransformInfinite) {
CullRect cull_rect = CullRect::Infinite();
auto transform =
CreateTransform(t0(), TransformationMatrix().Translate(1, 1));
EXPECT_EQ(kNotExpanded, ApplyTransform(cull_rect, transform.get()));
EXPECT_TRUE(cull_rect.IsInfinite());
}
TEST_F(CullRectTest, ApplyScrollTranslationPartialScrollingContents) { TEST_F(CullRectTest, ApplyScrollTranslationPartialScrollingContents) {
ScopedSlimmingPaintV2ForTest spv2(true); ScopedSlimmingPaintV2ForTest spv2(true);
...@@ -85,6 +132,13 @@ TEST_F(CullRectTest, ApplyScrollTranslationPartialScrollingContents) { ...@@ -85,6 +132,13 @@ TEST_F(CullRectTest, ApplyScrollTranslationPartialScrollingContents) {
// Inverse transformed: (3020, 5010, 30, 50) // Inverse transformed: (3020, 5010, 30, 50)
// Expanded: (-980, 1010, 8030, 8050) // Expanded: (-980, 1010, 8030, 8050)
EXPECT_EQ(IntRect(-980, 1010, 8030, 8050), cull_rect.Rect()); EXPECT_EQ(IntRect(-980, 1010, 8030, 8050), cull_rect.Rect());
cull_rect = CullRect::Infinite();
EXPECT_EQ(kExpandedForPartialScrollingContents,
ApplyTransform(cull_rect, scroll_translation.get()));
// This result differs from the above result in height (8040 vs 8030)
// because it's not clipped by the infinite input cull rect.
EXPECT_EQ(IntRect(-980, 1010, 8040, 8050), cull_rect.Rect());
} }
TEST_F(CullRectTest, ApplyScrollTranslationNoIntersectionWithContainerRect) { TEST_F(CullRectTest, ApplyScrollTranslationNoIntersectionWithContainerRect) {
...@@ -120,6 +174,13 @@ TEST_F(CullRectTest, ApplyScrollTranslationWholeScrollingContents) { ...@@ -120,6 +174,13 @@ TEST_F(CullRectTest, ApplyScrollTranslationWholeScrollingContents) {
// Inverse transformed: (30, 25, 30, 50) // Inverse transformed: (30, 25, 30, 50)
// Expanded: (-3970, -3975, 8030, 8050) // Expanded: (-3970, -3975, 8030, 8050)
EXPECT_EQ(IntRect(-3970, -3975, 8030, 8050), cull_rect.Rect()); EXPECT_EQ(IntRect(-3970, -3975, 8030, 8050), cull_rect.Rect());
cull_rect = CullRect::Infinite();
EXPECT_EQ(kExpandedForWholeScrollingContents,
ApplyTransform(cull_rect, scroll_translation.get()));
// This result differs from the above result in height (8040 vs 8030)
// because it's not clipped by the infinite input cull rect.
EXPECT_EQ(IntRect(-3970, -3975, 8040, 8050), cull_rect.Rect());
} }
TEST_F(CullRectTest, ChangedEnoughEmpty) { TEST_F(CullRectTest, ChangedEnoughEmpty) {
...@@ -167,6 +228,10 @@ TEST_F(CullRectTest, ApplyTransformsSameTransform) { ...@@ -167,6 +228,10 @@ TEST_F(CullRectTest, ApplyTransformsSameTransform) {
// Should ignore old_cull_rect. // Should ignore old_cull_rect.
cull_rect2.ApplyTransforms(transform.get(), transform.get(), old_cull_rect); cull_rect2.ApplyTransforms(transform.get(), transform.get(), old_cull_rect);
EXPECT_EQ(cull_rect1, cull_rect2); EXPECT_EQ(cull_rect1, cull_rect2);
CullRect infinite = CullRect::Infinite();
infinite.ApplyTransforms(transform.get(), transform.get(), base::nullopt);
EXPECT_TRUE(infinite.IsInfinite());
} }
TEST_F(CullRectTest, ApplyTransformsWithoutScroll) { TEST_F(CullRectTest, ApplyTransformsWithoutScroll) {
...@@ -188,6 +253,10 @@ TEST_F(CullRectTest, ApplyTransformsWithoutScroll) { ...@@ -188,6 +253,10 @@ TEST_F(CullRectTest, ApplyTransformsWithoutScroll) {
// Should ignore old_cull_rect. // Should ignore old_cull_rect.
cull_rect3.ApplyTransforms(&t0(), t2.get(), old_cull_rect); cull_rect3.ApplyTransforms(&t0(), t2.get(), old_cull_rect);
EXPECT_EQ(cull_rect2, cull_rect3); EXPECT_EQ(cull_rect2, cull_rect3);
CullRect infinite = CullRect::Infinite();
infinite.ApplyTransforms(&t0(), t2.get(), base::nullopt);
EXPECT_TRUE(infinite.IsInfinite());
} }
TEST_F(CullRectTest, ApplyTransformsSingleScrollWholeScrollingContents) { TEST_F(CullRectTest, ApplyTransformsSingleScrollWholeScrollingContents) {
...@@ -212,6 +281,12 @@ TEST_F(CullRectTest, ApplyTransformsSingleScrollWholeScrollingContents) { ...@@ -212,6 +281,12 @@ TEST_F(CullRectTest, ApplyTransformsSingleScrollWholeScrollingContents) {
// Should ignore old_cull_rect. // Should ignore old_cull_rect.
cull_rect2.ApplyTransforms(t1.get(), scroll_translation.get(), old_cull_rect); cull_rect2.ApplyTransforms(t1.get(), scroll_translation.get(), old_cull_rect);
EXPECT_EQ(cull_rect1, cull_rect2); EXPECT_EQ(cull_rect1, cull_rect2);
CullRect cull_rect3 = CullRect::Infinite();
cull_rect3.ApplyTransforms(t1.get(), scroll_translation.get(), base::nullopt);
// This result differs from the first result in height (8040 vs 8030)
// because it's not clipped by the infinite input cull rect.
EXPECT_EQ(IntRect(-3970, -3975, 8040, 8050), cull_rect3.Rect());
} }
TEST_F(CullRectTest, ApplyTransformsSingleScrollPartialScrollingContents) { TEST_F(CullRectTest, ApplyTransformsSingleScrollPartialScrollingContents) {
...@@ -242,6 +317,12 @@ TEST_F(CullRectTest, ApplyTransformsSingleScrollPartialScrollingContents) { ...@@ -242,6 +317,12 @@ TEST_F(CullRectTest, ApplyTransformsSingleScrollPartialScrollingContents) {
// Use the new cull rect if it changed enough. // Use the new cull rect if it changed enough.
cull_rect3.ApplyTransforms(t1.get(), scroll_translation.get(), old_cull_rect); cull_rect3.ApplyTransforms(t1.get(), scroll_translation.get(), old_cull_rect);
EXPECT_EQ(cull_rect1, cull_rect3); EXPECT_EQ(cull_rect1, cull_rect3);
CullRect cull_rect4 = CullRect::Infinite();
cull_rect4.ApplyTransforms(t1.get(), scroll_translation.get(), base::nullopt);
// This result differs from the first result in height (8040 vs 8030)
// because it's not clipped by the infinite input cull rect.
EXPECT_EQ(IntRect(-980, 1010, 8040, 8050), cull_rect4.Rect());
} }
TEST_F(CullRectTest, ApplyTransformsEscapingScroll) { TEST_F(CullRectTest, ApplyTransformsEscapingScroll) {
......
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