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;
const auto& first_root_fragment = if (!should_use_infinite_cull_rect) {
painting_info.root_layer->GetLayoutObject().FirstFragment(); const auto& first_root_fragment =
bool transform_changed = painting_info.root_layer->GetLayoutObject().FirstFragment();
first_root_fragment.LocalBorderBoxProperties().Transform() != if (first_root_fragment.LocalBorderBoxProperties().Transform() ==
first_fragment.LocalBorderBoxProperties().Transform(); first_fragment.LocalBorderBoxProperties().Transform())
return;
// 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;
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&,
......
...@@ -170,12 +170,7 @@ TEST_P(PaintLayerPainterTest, CachedSubsequence) { ...@@ -170,12 +170,7 @@ TEST_P(PaintLayerPainterTest, CachedSubsequence) {
check_chunks(); check_chunks();
} }
TEST_P(PaintLayerPainterTest, CachedSubsequenceOnInterestRectChange) { TEST_P(PaintLayerPainterTest, CachedSubsequenceOnCullRectChange) {
// TODO(wangxianzhu): SPv2 deals with interest rect differently, so disable
// this test for SPv2 temporarily.
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
SetBodyInnerHTML(R"HTML( SetBodyInnerHTML(R"HTML(
<div id='container1' style='position: relative; z-index: 1; <div id='container1' style='position: relative; z-index: 1;
width: 200px; height: 200px; background-color: blue'> width: 200px; height: 200px; background-color: blue'>
...@@ -256,7 +251,7 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceOnInterestRectChange) { ...@@ -256,7 +251,7 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceOnInterestRectChange) {
} }
TEST_P(PaintLayerPainterTest, TEST_P(PaintLayerPainterTest,
CachedSubsequenceOnInterestRectChangeUnderInvalidationChecking) { CachedSubsequenceOnCullRectChangeUnderInvalidationChecking) {
ScopedPaintUnderInvalidationCheckingForTest under_invalidation_checking(true); ScopedPaintUnderInvalidationCheckingForTest under_invalidation_checking(true);
SetBodyInnerHTML(R"HTML( SetBodyInnerHTML(R"HTML(
...@@ -280,7 +275,7 @@ TEST_P(PaintLayerPainterTest, ...@@ -280,7 +275,7 @@ TEST_P(PaintLayerPainterTest,
} }
TEST_P(PaintLayerPainterTest, TEST_P(PaintLayerPainterTest,
CachedSubsequenceOnStyleChangeWithInterestRectClipping) { CachedSubsequenceOnStyleChangeWithCullRectClipping) {
SetBodyInnerHTML(R"HTML( SetBodyInnerHTML(R"HTML(
<div id='container1' style='position: relative; z-index: 1; <div id='container1' style='position: relative; z-index: 1;
width: 200px; height: 200px; background-color: blue'> width: 200px; height: 200px; background-color: blue'>
...@@ -334,11 +329,6 @@ TEST_P(PaintLayerPainterTest, ...@@ -334,11 +329,6 @@ TEST_P(PaintLayerPainterTest,
} }
TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) { TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) {
// TODO(wangxianzhu): SPv2 deals with interest rect differently, so disable
// this test for SPv2 temporarily.
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
SetBodyInnerHTML(R"HTML( SetBodyInnerHTML(R"HTML(
<style> <style>
html, body { height: 100%; margin: 0 } html, body { height: 100%; margin: 0 }
...@@ -357,16 +347,31 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) { ...@@ -357,16 +347,31 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) {
const auto* content1 = GetLayoutObjectByElementId("content1"); const auto* content1 = GetLayoutObjectByElementId("content1");
const auto* content2 = GetLayoutObjectByElementId("content2"); const auto* content2 = GetLayoutObjectByElementId("content2");
const auto& view_client = ViewScrollingBackgroundClient(); const auto& view_client = ViewScrollingBackgroundClient();
// |content2| is out of the interest rect.
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&view_client, kDocumentBackgroundType),
IsSameId(content1, kBackgroundType)));
// |target| is partially painted. // |target| is partially painted.
EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult()); EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult());
EXPECT_EQ(CullRect(IntRect(0, 0, 800, 4600)), if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
target_layer->PreviousCullRect()); // SPv2 doesn't clip the cull rect by the scrolling contents rect, which
// |target| created subsequence. // doesn't affect painted results.
EXPECT_SUBSEQUENCE(*target_layer, 1, 2); EXPECT_EQ(CullRect(IntRect(-4000, -4000, 8800, 8600)),
target_layer->PreviousCullRect());
// |content2| is out of the cull rect.
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest),
IsSameId(&view_client, kDocumentBackgroundType),
IsSameId(content1, kBackgroundType)));
// |target| created subsequence.
EXPECT_SUBSEQUENCE(*target_layer, 2, 3);
} else {
EXPECT_EQ(CullRect(IntRect(0, 0, 800, 4600)),
target_layer->PreviousCullRect());
// |content2| is out of the cull rect.
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&view_client, kDocumentBackgroundType),
IsSameId(content1, kBackgroundType)));
// |target| created subsequence.
EXPECT_SUBSEQUENCE(*target_layer, 1, 2);
}
// Change something that triggers a repaint but |target| should use cached // Change something that triggers a repaint but |target| should use cached
// subsequence. // subsequence.
...@@ -378,16 +383,29 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) { ...@@ -378,16 +383,29 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) {
EXPECT_EQ(2, NumCachedNewItems()); EXPECT_EQ(2, NumCachedNewItems());
CommitAndFinishCycle(); CommitAndFinishCycle();
// Painted result is the same.
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&view_client, kDocumentBackgroundType),
IsSameId(content1, kBackgroundType)));
// |target| is still partially painted. // |target| is still partially painted.
EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult()); EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult());
EXPECT_EQ(CullRect(IntRect(0, 0, 800, 4600)), if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
target_layer->PreviousCullRect()); // SPv2 doens't clip the cull rect by the scrolling contents rect, which
// |target| still created subsequence (cached). // doesn't affect painted results.
EXPECT_SUBSEQUENCE(*target_layer, 1, 2); EXPECT_EQ(CullRect(IntRect(-4000, -4000, 8800, 8600)),
target_layer->PreviousCullRect());
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest),
IsSameId(&view_client, kDocumentBackgroundType),
IsSameId(content1, kBackgroundType)));
// |target| still created subsequence (cached).
EXPECT_SUBSEQUENCE(*target_layer, 2, 3);
} else {
EXPECT_EQ(CullRect(IntRect(0, 0, 800, 4600)),
target_layer->PreviousCullRect());
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&view_client, kDocumentBackgroundType),
IsSameId(content1, kBackgroundType)));
// |target| still created subsequence (cached).
EXPECT_SUBSEQUENCE(*target_layer, 1, 2);
}
// Scroll the view so that both |content1| and |content2| are in the interest // Scroll the view so that both |content1| and |content2| are in the interest
// rect. // rect.
...@@ -401,17 +419,33 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) { ...@@ -401,17 +419,33 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) {
EXPECT_EQ(2, NumCachedNewItems()); EXPECT_EQ(2, NumCachedNewItems());
CommitAndFinishCycle(); CommitAndFinishCycle();
// Painted result should include both |content1| and |content2|.
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&view_client, kDocumentBackgroundType),
IsSameId(content1, kBackgroundType),
IsSameId(content2, kBackgroundType)));
// |target| is still partially painted. // |target| is still partially painted.
EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult()); EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult());
EXPECT_EQ(CullRect(IntRect(0, 0, 800, 7600)), if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
target_layer->PreviousCullRect()); // SPv2 doens't clip the cull rect by the scrolling contents rect, which
// |target| still created subsequence (repainted). // doesn't affect painted results.
EXPECT_SUBSEQUENCE(*target_layer, 1, 3); EXPECT_EQ(CullRect(IntRect(-4000, -1000, 8800, 8600)),
target_layer->PreviousCullRect());
// Painted result should include both |content1| and |content2|.
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest),
IsSameId(&view_client, kDocumentBackgroundType),
IsSameId(content1, kBackgroundType),
IsSameId(content2, kBackgroundType)));
// |target| still created subsequence (repainted).
EXPECT_SUBSEQUENCE(*target_layer, 2, 4);
} else {
EXPECT_EQ(CullRect(IntRect(0, 0, 800, 7600)),
target_layer->PreviousCullRect());
// Painted result should include both |content1| and |content2|.
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&view_client, kDocumentBackgroundType),
IsSameId(content1, kBackgroundType),
IsSameId(content2, kBackgroundType)));
// |target| still created subsequence (repainted).
EXPECT_SUBSEQUENCE(*target_layer, 1, 3);
}
} }
TEST_P(PaintLayerPainterTest, PaintPhaseOutline) { TEST_P(PaintLayerPainterTest, PaintPhaseOutline) {
...@@ -939,4 +973,247 @@ TEST_P(PaintLayerPainterTest, ...@@ -939,4 +973,247 @@ TEST_P(PaintLayerPainterTest,
ExpectPaintedOutputInvisible("target", false); ExpectPaintedOutputInvisible("target", false);
} }
using PaintLayerPainterTestSPv2 = PaintLayerPainterTest;
INSTANTIATE_SPV2_TEST_CASE_P(PaintLayerPainterTestSPv2);
TEST_P(PaintLayerPainterTestSPv2, SimpleCullRect) {
SetBodyInnerHTML(R"HTML(
<div id='target'
style='width: 200px; height: 200px; position: relative'>
</div>
)HTML");
EXPECT_EQ(IntRect(0, 0, 800, 600),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, TallLayerCullRect) {
SetBodyInnerHTML(R"HTML(
<div id='target'
style='width: 200px; height: 10000px; position: relative'>
</div>
)HTML");
// Viewport rect (0, 0, 800, 600) expanded by 4000 for scrolling.
EXPECT_EQ(IntRect(-4000, -4000, 8800, 8600),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, WideLayerCullRect) {
SetBodyInnerHTML(R"HTML(
<div id='target'
style='width: 10000px; height: 200px; position: relative'>
</div>
)HTML");
// Same as TallLayerCullRect.
EXPECT_EQ(IntRect(-4000, -4000, 8800, 8600),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, TallScrolledLayerCullRect) {
SetBodyInnerHTML(R"HTML(
<div id='target' style='width: 200px; height: 10000px; position: relative'>
</div>
)HTML");
// Viewport rect (0, 0, 800, 600) expanded by 4000.
EXPECT_EQ(IntRect(-4000, -4000, 8800, 8600),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 6000),
kProgrammaticScroll);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(IntRect(-4000, 2000, 8800, 8600),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 6500),
kProgrammaticScroll);
UpdateAllLifecyclePhasesForTest();
// Used the previous cull rect because the scroll amount is small.
EXPECT_EQ(IntRect(-4000, 2000, 8800, 8600),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 6600),
kProgrammaticScroll);
UpdateAllLifecyclePhasesForTest();
// Used new cull rect.
EXPECT_EQ(IntRect(-4000, 2600, 8800, 8600),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, VerticalRightLeftWritingModeDocument) {
SetBodyInnerHTML(R"HTML(
<style>
html { writing-mode: vertical-rl; }
body { margin: 0; }
</style>
<div id='target' style='width: 10000px; height: 200px; position: relative'>
</div>
)HTML");
GetDocument().View()->LayoutViewport()->SetScrollOffset(
ScrollOffset(-5000, 0), kProgrammaticScroll);
UpdateAllLifecyclePhasesForTest();
// A scroll by -5000px is equivalent to a scroll by (10000 - 5000 - 800)px =
// 4200px in non-RTL mode. Expanding the resulting rect by 4000px in each
// direction yields this result.
EXPECT_EQ(IntRect(200, -4000, 8800, 8600),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, ScaledCullRect) {
SetBodyInnerHTML(R"HTML(
<div style='width: 200px; height: 300px; overflow: scroll;
transform: scaleX(2) scaleY(0.5)'>
<div id='target' style='height: 400px; position: relative'></div>
</div>
)HTML");
// The scale doesn't affect the cull rect.
EXPECT_EQ(IntRect(-4000, -4000, 8200, 8300),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, ScaledAndRotatedCullRect) {
SetBodyInnerHTML(R"HTML(
<div style='width: 200px; height: 300px; overflow: scroll;
transform: scaleX(2) scaleY(0.5) rotateZ(45deg)'>
<div id='target' style='height: 400px; position: relative'></div>
</div>
)HTML");
// The scale and the rotation don't affect the cull rect.
EXPECT_EQ(IntRect(-4000, -4000, 8200, 8300),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, 3DRotated90DegreesCullRect) {
SetBodyInnerHTML(R"HTML(
<div style='width: 200px; height: 300px; overflow: scroll;
transform: rotateY(90deg)'>
<div id='target' style='height: 400px; position: relative'></div>
</div>
)HTML");
// It's rotated 90 degrees about the X axis, which means its visual content
// rect is empty, we fall back to the 4000px cull rect padding amount.
EXPECT_EQ(IntRect(-4000, -4000, 8200, 8300),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, 3DRotatedNear90DegreesCullRect) {
SetBodyInnerHTML(R"HTML(
<div style='width: 200px; height: 300px; overflow: scroll;
transform: rotateY(89.9999deg)'>
<div id='target' style='height: 400px; position: relative'></div>
</div>
)HTML");
// Because the layer is rotated to almost 90 degrees, floating-point error
// leads to a reverse-projected rect that is much much larger than the
// original layer size in certain dimensions. In such cases, we often fall
// back to the 4000px cull rect padding amount.
EXPECT_EQ(IntRect(-4000, -4000, 8200, 8300),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, PerspectiveCullRect) {
SetBodyInnerHTML(R"HTML(
<div id='target'
style='width: 100px; height: 100px; transform: perspective(1000px)'>
</div>
)HTML");
// Use infinite cull rect with perspective.
EXPECT_TRUE(
GetPaintLayerByElementId("target")->PreviousCullRect().IsInfinite());
}
TEST_P(PaintLayerPainterTestSPv2, 3D45DegRotatedTallCullRect) {
SetBodyInnerHTML(R"HTML(
<div id='target'
style='width: 200px; height: 10000px; transform: rotateY(45deg)'>
</div>
)HTML");
// Use infinite cull rect with 3d transform.
EXPECT_TRUE(
GetPaintLayerByElementId("target")->PreviousCullRect().IsInfinite());
}
TEST_P(PaintLayerPainterTestSPv2, FixedPositionCullRect) {
SetBodyInnerHTML(R"HTML(
<div id='target' style='width: 1000px; height: 2000px;
position: fixed; top: 100px; left: 200px;'>
</div>
)HTML");
EXPECT_EQ(IntRect(0, 0, 800, 600),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, LayerOffscreenNearCullRect) {
SetBodyInnerHTML(R"HTML(
<div style='width: 200px; height: 300px; overflow: scroll;
position: absolute; top: 3000px; left: 0px;'>
<div id='target' style='height: 500px; position: relative'></div>
</div>
)HTML");
EXPECT_EQ(IntRect(-4000, -4000, 8200, 8300),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, LayerOffscreenFarCullRect) {
SetBodyInnerHTML(R"HTML(
<div style='width: 200px; height: 300px; overflow: scroll;
position: absolute; top: 9000px'>
<div id='target' style='height: 500px; position: relative'></div>
</div>
)HTML");
// The layer is too far away from the viewport.
EXPECT_EQ(IntRect(),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, ScrollingLayerCullRect) {
SetBodyInnerHTML(R"HTML(
<style>
div::-webkit-scrollbar { width: 5px; }
</style>
<div style='width: 200px; height: 200px; overflow: scroll'>
<div id='target'
style='width: 100px; height: 10000px; position: relative'>
</div>
</div>
)HTML");
// In screen space, the scroller is (8, 8, 195, 193) (because of overflow clip
// of 'target', scrollbar and root margin).
// Applying the viewport clip of the root has no effect because
// the clip is already small. Mapping it down into the graphics layer
// space yields (0, 0, 195, 193). This is then expanded by 4000px.
EXPECT_EQ(IntRect(-4000, -4000, 8195, 8193),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
TEST_P(PaintLayerPainterTestSPv2, ClippedBigLayer) {
SetBodyInnerHTML(R"HTML(
<div style='width: 1px; height: 1px; overflow: hidden'>
<div id='target'
style='width: 10000px; height: 10000px; position: relative'>
</div>
</div>
)HTML");
// The viewport is not scrollable because of the clip, so the cull rect is
// just the viewport rect.
EXPECT_EQ(IntRect(0, 0, 800, 600),
GetPaintLayerByElementId("target")->PreviousCullRect().Rect());
}
} // namespace blink } // namespace blink
...@@ -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(
} }
} }
rect_ = transform->Matrix().Inverse().MapRect(rect_); if (!IsInfinite())
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;
GeometryMapper::SourceToDestinationRect( if (!IsInfinite()) {
last_transform, scroll_translation->Parent(), rect_); GeometryMapper::SourceToDestinationRect(
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