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(
PaintLayer& paint_layer,
const PaintLayerPaintingInfo& painting_info,
ShouldRespectOverflowClipType respect_overflow_clip) {
bool needs_repaint = false;
// Repaint subsequence if the layer is marked for needing repaint.
if (paint_layer.NeedsRepaint())
needs_repaint = true;
return true;
// Repaint if previously the layer may be clipped by cull rect, and cull rect
// changes.
......@@ -216,12 +214,10 @@ static bool ShouldRepaintSubsequence(
// new and cached subsequences. Normally we can reuse the cached fully
// painted subsequence even if we would partially paint this time.
RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) &&
paint_layer.PreviousCullRect() != painting_info.cull_rect) {
needs_repaint = true;
}
paint_layer.SetPreviousCullRect(painting_info.cull_rect);
paint_layer.PreviousCullRect() != painting_info.cull_rect)
return true;
return needs_repaint;
return false;
}
static bool ShouldUseInfiniteCullRect(const GraphicsContext& context,
......@@ -259,30 +255,21 @@ void PaintLayerPainter::AdjustForPaintProperties(
PaintLayerFlags& paint_flags) {
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 =
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();
is_using_infinite_cull_rect = true;
}
if (painting_info.root_layer == &paint_layer_)
return;
const auto& first_root_fragment =
painting_info.root_layer->GetLayoutObject().FirstFragment();
bool transform_changed =
first_root_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;
if (!should_use_infinite_cull_rect) {
const auto& first_root_fragment =
painting_info.root_layer->GetLayoutObject().FirstFragment();
if (first_root_fragment.LocalBorderBoxProperties().Transform() ==
first_fragment.LocalBorderBoxProperties().Transform())
return;
if (!is_using_infinite_cull_rect && transform_changed) {
// painting_info.cull_rect is currently in |painting_info.root_layer|'s
// pixel-snapped border box space. We need to adjust it into
// |paint_layer_|'s space. This handles the following cases:
......@@ -307,14 +294,16 @@ void PaintLayerPainter::AdjustForPaintProperties(
// Convert cull_rect from the layer's transform space to the layer's local
// space.
cull_rect.MoveBy(-RoundedIntPoint(first_fragment.PaintOffset()));
} else {
} else if (!painting_info.cull_rect.IsInfinite()) {
auto rect = painting_info.cull_rect.Rect();
first_root_fragment.MapRectToFragment(first_fragment, 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_;
// These flags no longer apply for the new root layer.
paint_flags &= ~kPaintLayerPaintingSkipRootBackground;
......@@ -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;
}
......
......@@ -60,7 +60,6 @@ class CORE_EXPORT PaintLayerPainter {
private:
friend class PaintLayerPainterTest;
friend class PaintLayerPainterTestSPv2;
PaintResult PaintChildren(unsigned children_to_visit,
GraphicsContext&,
......
......@@ -170,12 +170,7 @@ TEST_P(PaintLayerPainterTest, CachedSubsequence) {
check_chunks();
}
TEST_P(PaintLayerPainterTest, CachedSubsequenceOnInterestRectChange) {
// TODO(wangxianzhu): SPv2 deals with interest rect differently, so disable
// this test for SPv2 temporarily.
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
TEST_P(PaintLayerPainterTest, CachedSubsequenceOnCullRectChange) {
SetBodyInnerHTML(R"HTML(
<div id='container1' style='position: relative; z-index: 1;
width: 200px; height: 200px; background-color: blue'>
......@@ -256,7 +251,7 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceOnInterestRectChange) {
}
TEST_P(PaintLayerPainterTest,
CachedSubsequenceOnInterestRectChangeUnderInvalidationChecking) {
CachedSubsequenceOnCullRectChangeUnderInvalidationChecking) {
ScopedPaintUnderInvalidationCheckingForTest under_invalidation_checking(true);
SetBodyInnerHTML(R"HTML(
......@@ -280,7 +275,7 @@ TEST_P(PaintLayerPainterTest,
}
TEST_P(PaintLayerPainterTest,
CachedSubsequenceOnStyleChangeWithInterestRectClipping) {
CachedSubsequenceOnStyleChangeWithCullRectClipping) {
SetBodyInnerHTML(R"HTML(
<div id='container1' style='position: relative; z-index: 1;
width: 200px; height: 200px; background-color: blue'>
......@@ -334,11 +329,6 @@ TEST_P(PaintLayerPainterTest,
}
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(
<style>
html, body { height: 100%; margin: 0 }
......@@ -357,16 +347,31 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) {
const auto* content1 = GetLayoutObjectByElementId("content1");
const auto* content2 = GetLayoutObjectByElementId("content2");
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.
EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult());
EXPECT_EQ(CullRect(IntRect(0, 0, 800, 4600)),
target_layer->PreviousCullRect());
// |target| created subsequence.
EXPECT_SUBSEQUENCE(*target_layer, 1, 2);
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
// SPv2 doesn't clip the cull rect by the scrolling contents rect, which
// doesn't affect painted results.
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
// subsequence.
......@@ -378,16 +383,29 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) {
EXPECT_EQ(2, NumCachedNewItems());
CommitAndFinishCycle();
// Painted result is the same.
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&view_client, kDocumentBackgroundType),
IsSameId(content1, kBackgroundType)));
// |target| is still partially painted.
EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult());
EXPECT_EQ(CullRect(IntRect(0, 0, 800, 4600)),
target_layer->PreviousCullRect());
// |target| still created subsequence (cached).
EXPECT_SUBSEQUENCE(*target_layer, 1, 2);
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
// SPv2 doens't clip the cull rect by the scrolling contents rect, which
// doesn't affect painted results.
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
// rect.
......@@ -401,17 +419,33 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) {
EXPECT_EQ(2, NumCachedNewItems());
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.
EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult());
EXPECT_EQ(CullRect(IntRect(0, 0, 800, 7600)),
target_layer->PreviousCullRect());
// |target| still created subsequence (repainted).
EXPECT_SUBSEQUENCE(*target_layer, 1, 3);
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
// SPv2 doens't clip the cull rect by the scrolling contents rect, which
// doesn't affect painted results.
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) {
......@@ -939,4 +973,247 @@ TEST_P(PaintLayerPainterTest,
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
......@@ -47,9 +47,6 @@ void CullRect::Move(const IntSize& offset) {
CullRect::ApplyTransformResult CullRect::ApplyTransformInternal(
const TransformPaintPropertyNode* transform) {
if (IsInfinite())
return kNotExpanded;
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
if (const auto* scroll = transform->ScrollNode()) {
rect_.Intersect(scroll->ContainerRect());
......@@ -59,8 +56,6 @@ CullRect::ApplyTransformResult CullRect::ApplyTransformInternal(
// Expand the cull rect for scrolling contents in case of composited
// scrolling.
// TODO(wangxianzhu): the expansion distance needs to be scaled to
// screen pixels.
// TODO(wangxianzhu): options for non-composited-scrolling contents:
// 1. to use non-composted-scrolling heuristics to avoid expansion;
// 2. to reduce the 4000px distance, no matter if the contents with be
......@@ -77,37 +72,42 @@ CullRect::ApplyTransformResult CullRect::ApplyTransformInternal(
}
}
rect_ = transform->Matrix().Inverse().MapRect(rect_);
if (!IsInfinite())
rect_ = transform->Matrix().Inverse().MapRect(rect_);
return kNotExpanded;
}
void CullRect::ApplyTransforms(const TransformPaintPropertyNode* from,
const TransformPaintPropertyNode* to,
void CullRect::ApplyTransforms(const TransformPaintPropertyNode* source,
const TransformPaintPropertyNode* destination,
const base::Optional<CullRect>& old_cull_rect) {
DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
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) {
// |from| is not an ancestor of |to|. Simply map.
GeometryMapper::SourceToDestinationRect(from, to, rect_);
// |source| is not an ancestor of |destination|. Simply map.
GeometryMapper::SourceToDestinationRect(source, destination, rect_);
return;
}
if (t->ScrollNode())
scroll_translations.push_back(t);
}
const auto* last_transform = from;
const auto* last_transform = source;
ApplyTransformResult last_scroll_translation_result = kNotExpanded;
for (auto it = scroll_translations.rbegin(); it != scroll_translations.rend();
++it) {
const auto* scroll_translation = *it;
GeometryMapper::SourceToDestinationRect(
last_transform, scroll_translation->Parent(), rect_);
if (!IsInfinite()) {
GeometryMapper::SourceToDestinationRect(
last_transform, scroll_translation->Parent(), rect_);
}
last_scroll_translation_result = ApplyTransformInternal(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 &&
old_cull_rect && !ChangedEnough(*old_cull_rect))
......
......@@ -57,6 +57,44 @@ TEST_F(CullRectTest, IntersectsTransformed) {
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) {
CullRect cull_rect(IntRect(1, 1, 50, 50));
auto transform =
......@@ -66,6 +104,15 @@ TEST_F(CullRectTest, ApplyTransform) {
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) {
ScopedSlimmingPaintV2ForTest spv2(true);
......@@ -85,6 +132,13 @@ TEST_F(CullRectTest, ApplyScrollTranslationPartialScrollingContents) {
// Inverse transformed: (3020, 5010, 30, 50)
// Expanded: (-980, 1010, 8030, 8050)
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) {
......@@ -120,6 +174,13 @@ TEST_F(CullRectTest, ApplyScrollTranslationWholeScrollingContents) {
// Inverse transformed: (30, 25, 30, 50)
// Expanded: (-3970, -3975, 8030, 8050)
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) {
......@@ -167,6 +228,10 @@ TEST_F(CullRectTest, ApplyTransformsSameTransform) {
// Should ignore old_cull_rect.
cull_rect2.ApplyTransforms(transform.get(), transform.get(), old_cull_rect);
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) {
......@@ -188,6 +253,10 @@ TEST_F(CullRectTest, ApplyTransformsWithoutScroll) {
// Should ignore old_cull_rect.
cull_rect3.ApplyTransforms(&t0(), t2.get(), old_cull_rect);
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) {
......@@ -212,6 +281,12 @@ TEST_F(CullRectTest, ApplyTransformsSingleScrollWholeScrollingContents) {
// Should ignore old_cull_rect.
cull_rect2.ApplyTransforms(t1.get(), scroll_translation.get(), old_cull_rect);
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) {
......@@ -242,6 +317,12 @@ TEST_F(CullRectTest, ApplyTransformsSingleScrollPartialScrollingContents) {
// Use the new cull rect if it changed enough.
cull_rect3.ApplyTransforms(t1.get(), scroll_translation.get(), old_cull_rect);
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) {
......
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