0

Move PendingLayer unittests into pending_layer_test.cc

Change-Id: If9e5c77302a1c1f4cb990605e037c8e06a63926f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2987481
Reviewed-by: Philip Rogers <pdr@chromium.org>
Reviewed-by: Stefan Zager <szager@chromium.org>
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#896115}
This commit is contained in:
Xianzhu Wang
2021-06-25 17:53:09 +00:00
committed by Chromium LUCI CQ
parent 26597ae9ad
commit 193ef7d462
7 changed files with 313 additions and 294 deletions

@ -2065,6 +2065,7 @@ source_set("blink_platform_unittests_sources") {
"graphics/compositing/chunk_to_layer_mapper_test.cc",
"graphics/compositing/paint_artifact_compositor_test.cc",
"graphics/compositing/paint_chunks_to_cc_layer_test.cc",
"graphics/compositing/pending_layer_test.cc",
"graphics/compositor_element_id_test.cc",
"graphics/dark_mode_color_classifier_test.cc",
"graphics/dark_mode_filter_test.cc",

@ -433,48 +433,6 @@ static const EffectPaintPropertyNode* StrictUnaliasedChildOfAlongPath(
return nullptr;
}
static const ClipPaintPropertyNode* HighestOutputClipBetween(
const EffectPaintPropertyNode& ancestor,
const EffectPaintPropertyNode& descendant) {
const ClipPaintPropertyNode* result = nullptr;
for (const auto* effect = &descendant; effect != &ancestor;
effect = effect->UnaliasedParent()) {
if (const auto* output_clip = effect->OutputClip())
result = &output_clip->Unalias();
}
return result;
}
bool PaintArtifactCompositor::MightOverlap(const PendingLayer& layer_a,
const PendingLayer& layer_b) {
const PropertyTreeState& layer_a_state = layer_a.GetPropertyTreeState();
const PropertyTreeState& layer_b_state = layer_b.GetPropertyTreeState();
PropertyTreeState common_ancestor_state(
layer_a_state.Transform()
.LowestCommonAncestor(layer_b.GetPropertyTreeState().Transform())
.Unalias(),
layer_a_state.Clip()
.LowestCommonAncestor(layer_b.GetPropertyTreeState().Clip())
.Unalias(),
layer_a_state.Effect()
.LowestCommonAncestor(layer_b.GetPropertyTreeState().Effect())
.Unalias());
// Move the common clip up if some effect nodes have OutputClip escaping the
// common clip.
if (const auto* clip_a = HighestOutputClipBetween(
common_ancestor_state.Effect(), layer_a_state.Effect())) {
common_ancestor_state.SetClip(
clip_a->LowestCommonAncestor(common_ancestor_state.Clip()).Unalias());
}
if (const auto* clip_b = HighestOutputClipBetween(
common_ancestor_state.Effect(), layer_b_state.Effect())) {
common_ancestor_state.SetClip(
clip_b->LowestCommonAncestor(common_ancestor_state.Clip()).Unalias());
}
return layer_a.VisualRectForOverlapTesting(common_ancestor_state)
.Intersects(layer_b.VisualRectForOverlapTesting(common_ancestor_state));
}
bool PaintArtifactCompositor::DecompositeEffect(
const EffectPaintPropertyNode& parent_effect,
wtf_size_t first_layer_in_parent_group_index,
@ -657,7 +615,7 @@ void PaintArtifactCompositor::LayerizeGroup(
pending_layers_.pop_back();
break;
}
if (MightOverlap(new_layer, candidate_layer)) {
if (new_layer.MightOverlap(candidate_layer)) {
new_layer.SetCompositingType(PendingLayer::kOverlap);
break;
}

@ -274,7 +274,6 @@ class PLATFORM_EXPORT PaintArtifactCompositor final
const EffectPaintPropertyNode&,
PaintChunkIterator& chunk_cursor,
bool effectively_invisible);
static bool MightOverlap(const PendingLayer&, const PendingLayer&);
bool DecompositeEffect(const EffectPaintPropertyNode& parent_effect,
wtf_size_t first_layer_in_parent_group_index,
const EffectPaintPropertyNode& effect,

@ -246,23 +246,12 @@ class PaintArtifactCompositorTest : public testing::Test,
Update(artifact.Build());
}
bool MightOverlap(const PendingLayer& a, const PendingLayer& b) {
return PaintArtifactCompositor::MightOverlap(a, b);
}
MockScrollCallbacks& ScrollCallbacks() { return scroll_callbacks_; }
PaintArtifactCompositor& GetPaintArtifactCompositor() {
return *paint_artifact_compositor_;
}
Vector<wtf_size_t> ChunkIndices(const PendingLayer& layer) {
Vector<wtf_size_t> indices;
for (auto it = layer.Chunks().begin(); it != layer.Chunks().end(); ++it)
indices.push_back(it.IndexInPaintArtifact());
return indices;
}
private:
MockScrollCallbacks scroll_callbacks_;
std::unique_ptr<PaintArtifactCompositor> paint_artifact_compositor_;
@ -1659,20 +1648,6 @@ TEST_P(PaintArtifactCompositorTest, MergeNestedWithAlias) {
}
}
TEST_P(PaintArtifactCompositorTest, CanNotMergeAcrossPaintArtifacts) {
TestPaintArtifact test_artifact_a;
test_artifact_a.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite);
PaintChunkSubset chunks_a(test_artifact_a.Build());
PendingLayer layer_a(chunks_a, chunks_a.begin());
TestPaintArtifact test_artifact_b;
test_artifact_b.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kGray);
PaintChunkSubset chunks_b(test_artifact_b.Build());
PendingLayer layer_b(chunks_b, chunks_b.begin());
EXPECT_FALSE(layer_a.CanMerge(layer_b, layer_b.GetPropertyTreeState()));
}
TEST_P(PaintArtifactCompositorTest, ClipPushedUp) {
// Tests merging of an element which has a clipapplied to it,
// but has an ancestor transform of them. This can happen for fixed-
@ -1914,223 +1889,6 @@ TEST_P(PaintArtifactCompositorTest, OverlapTransform) {
ASSERT_EQ(3u, LayerCount());
}
TEST_P(PaintArtifactCompositorTest, MightOverlap) {
TestPaintArtifact artifact;
artifact.Chunk().Bounds(IntRect(0, 0, 100, 100));
artifact.Chunk().Bounds(IntRect(0, 0, 100, 100));
auto t2 = CreateTransform(t0(), TransformationMatrix().Translate(99, 0),
FloatPoint3D(100, 100, 0));
artifact.Chunk(*t2, c0(), e0()).Bounds(IntRect(0, 0, 100, 100));
auto t3 = CreateTransform(t0(), TransformationMatrix().Translate(100, 0),
FloatPoint3D(100, 100, 0));
artifact.Chunk(*t3, c0(), e0()).Bounds(IntRect(0, 0, 100, 100));
auto t4 =
CreateAnimatingTransform(t0(), TransformationMatrix().Translate(100, 0),
FloatPoint3D(100, 100, 0));
artifact.Chunk(*t4, c0(), e0()).Bounds(IntRect(0, 0, 100, 100));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
EXPECT_TRUE(
MightOverlap(pending_layer, PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_TRUE(
MightOverlap(pending_layer, PendingLayer(chunks, chunks.begin() + 2)));
EXPECT_FALSE(
MightOverlap(pending_layer, PendingLayer(chunks, chunks.begin() + 3)));
EXPECT_TRUE(
MightOverlap(pending_layer, PendingLayer(chunks, chunks.begin() + 4)));
}
TEST_P(PaintArtifactCompositorTest, MightOverlapCommonClipAncestor) {
auto common_clip = CreateClip(c0(), t0(), FloatRoundedRect(0, 0, 100, 100));
auto c1 = CreateClip(*common_clip, t0(), FloatRoundedRect(0, 100, 100, 100));
auto c2 = CreateClip(*common_clip, t0(), FloatRoundedRect(50, 100, 100, 100));
auto c3 =
CreateClip(*common_clip, t0(), FloatRoundedRect(100, 100, 100, 100));
TestPaintArtifact artifact;
artifact.Chunk(t0(), *c1, e0())
.Bounds(IntRect(0, 100, 200, 100))
.Chunk(t0(), *c2, e0())
.Bounds(IntRect(0, 100, 200, 100))
.Chunk(t0(), *c3, e0())
.Bounds(IntRect(0, 100, 200, 100));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer1(chunks, chunks.begin());
PendingLayer pending_layer2(chunks, chunks.begin() + 1);
PendingLayer pending_layer3(chunks, chunks.begin() + 2);
EXPECT_FALSE(MightOverlap(pending_layer1, pending_layer3));
EXPECT_TRUE(MightOverlap(pending_layer1, pending_layer2));
EXPECT_TRUE(MightOverlap(pending_layer2, pending_layer3));
}
TEST_P(PaintArtifactCompositorTest, PendingLayer) {
TestPaintArtifact artifact;
artifact.Chunk()
.Bounds(IntRect(0, 0, 30, 40))
.RectKnownToBeOpaque(IntRect(0, 0, 30, 40));
artifact.Chunk()
.Bounds(IntRect(10, 20, 30, 40))
.RectKnownToBeOpaque(IntRect(10, 20, 30, 40));
artifact.Chunk()
.Bounds(IntRect(-5, -25, 20, 20))
.RectKnownToBeOpaque(IntRect(-5, -25, 20, 20));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.Bounds());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0));
EXPECT_EQ(pending_layer.Bounds(), pending_layer.RectKnownToBeOpaque());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
// Bounds not equal to one PaintChunk.
EXPECT_EQ(FloatRect(0, 0, 40, 60), pending_layer.Bounds());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0, 1));
EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.RectKnownToBeOpaque());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 2)));
EXPECT_EQ(FloatRect(-5, -25, 45, 85), pending_layer.Bounds());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0, 1, 2));
EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.RectKnownToBeOpaque());
}
TEST_P(PaintArtifactCompositorTest, PendingLayerMergeWithGuestTransform) {
TestPaintArtifact artifact;
artifact.Chunk().Bounds(IntRect(0, 0, 30, 40));
auto transform = Create2DTranslation(t0(), 20, 25);
artifact.Chunk(*transform, c0(), e0()).Bounds(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(0, 0, 70, 85), pending_layer.Bounds());
EXPECT_EQ(PropertyTreeState::Root(), pending_layer.GetPropertyTreeState());
}
TEST_P(PaintArtifactCompositorTest, PendingLayerMergeWithHomeTransform) {
TestPaintArtifact artifact;
auto transform = Create2DTranslation(t0(), 20, 25);
artifact.Chunk(*transform, c0(), e0()).Bounds(IntRect(0, 0, 30, 40));
artifact.Chunk().Bounds(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(0, 0, 50, 65), pending_layer.Bounds());
EXPECT_EQ(PropertyTreeState::Root(), pending_layer.GetPropertyTreeState());
}
TEST_P(PaintArtifactCompositorTest, PendingLayerMergeWithBothTransforms) {
TestPaintArtifact artifact;
auto t1 = Create2DTranslation(t0(), 20, 25);
artifact.Chunk(*t1, c0(), e0()).Bounds(IntRect(0, 0, 30, 40));
auto t2 = Create2DTranslation(t0(), -20, -25);
artifact.Chunk(*t2, c0(), e0()).Bounds(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(-20, -25, 70, 90), pending_layer.Bounds());
EXPECT_EQ(PropertyTreeState::Root(), pending_layer.GetPropertyTreeState());
}
TEST_P(PaintArtifactCompositorTest, PendingLayerDontMergeSparse) {
TestPaintArtifact artifact;
artifact.Chunk()
.Bounds(IntRect(0, 0, 30, 40))
.RectKnownToBeOpaque(IntRect(0, 0, 30, 40));
artifact.Chunk()
.Bounds(IntRect(200, 200, 30, 40))
.RectKnownToBeOpaque(IntRect(200, 200, 30, 40));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_FALSE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.Bounds());
EXPECT_EQ(chunks.begin()->properties, pending_layer.GetPropertyTreeState());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0));
}
TEST_P(PaintArtifactCompositorTest, PendingLayerDontMergeSparseWithTransforms) {
TestPaintArtifact artifact;
auto t1 = Create2DTranslation(t0(), 20, 25);
artifact.Chunk(*t1, c0(), e0()).Bounds(IntRect(0, 0, 30, 40));
auto t2 = Create2DTranslation(t0(), 1000, 1000);
artifact.Chunk(*t2, c0(), e0()).Bounds(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_FALSE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.Bounds());
EXPECT_EQ(chunks.begin()->properties, pending_layer.GetPropertyTreeState());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0));
}
TEST_P(PaintArtifactCompositorTest,
PendingLayerDontMergeSparseInCompositedEffect) {
TestPaintArtifact artifact;
auto t1 = Create2DTranslation(t0(), 20, 25);
auto e1 =
CreateOpacityEffect(e0(), 1.0f, CompositingReason::kWillChangeOpacity);
artifact.Chunk(*t1, c0(), *e1).Bounds(IntRect(0, 0, 30, 40));
auto t2 = Create2DTranslation(t0(), 1000, 1000);
artifact.Chunk(*t2, c0(), *e1).Bounds(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_FALSE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.Bounds());
EXPECT_EQ(chunks.begin()->properties, pending_layer.GetPropertyTreeState());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0));
}
TEST_P(PaintArtifactCompositorTest,
PendingLayerMergeSparseInNonCompositedEffect) {
TestPaintArtifact artifact;
auto t1 = Create2DTranslation(t0(), 20, 25);
auto t2 = Create2DTranslation(t0(), 1000, 1000);
auto e1 = CreateOpacityEffect(e0(), 1.0f, CompositingReason::kNone);
artifact.Chunk(*t1, c0(), *e1).Bounds(IntRect(0, 0, 30, 40));
artifact.Chunk(*t2, c0(), *e1).Bounds(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(20, 25, 1030, 1035), pending_layer.Bounds());
EXPECT_EQ(PropertyTreeState(t0(), c0(), *e1),
pending_layer.GetPropertyTreeState());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0, 1));
}
TEST_P(PaintArtifactCompositorTest, PendingLayerKnownOpaque) {
TestPaintArtifact artifact;
artifact.Chunk().Bounds(IntRect(0, 0, 30, 40));
artifact.Chunk()
.Bounds(IntRect(0, 0, 25, 35))
.RectKnownToBeOpaque(IntRect(0, 0, 25, 35));
artifact.Chunk()
.Bounds(IntRect(0, 0, 50, 60))
.RectKnownToBeOpaque(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
EXPECT_TRUE(pending_layer.RectKnownToBeOpaque().IsEmpty());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
// Chunk 2 doesn't cover the entire layer, so not opaque.
EXPECT_EQ(FloatRect(0, 0, 25, 35), pending_layer.RectKnownToBeOpaque());
EXPECT_NE(pending_layer.Bounds(), pending_layer.RectKnownToBeOpaque());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 2)));
// Chunk 3 covers the entire layer, so now it's opaque.
EXPECT_EQ(FloatRect(0, 0, 50, 60), pending_layer.Bounds());
EXPECT_EQ(pending_layer.Bounds(), pending_layer.RectKnownToBeOpaque());
}
scoped_refptr<EffectPaintPropertyNode> CreateSampleEffectNodeWithElementId() {
EffectPaintPropertyNode::State state;
state.local_transform_space = &t0();

@ -10,14 +10,22 @@
namespace blink {
bool PendingLayer::PropertyTreeStateChanged() const {
auto change = PaintPropertyChangeType::kChangedOnlyNonRerasterValues;
if (change_of_decomposited_transforms_ >= change)
return true;
namespace {
return property_tree_state_.ChangedToRoot(change);
const ClipPaintPropertyNode* HighestOutputClipBetween(
const EffectPaintPropertyNode& ancestor,
const EffectPaintPropertyNode& descendant) {
const ClipPaintPropertyNode* result = nullptr;
for (const auto* effect = &descendant; effect != &ancestor;
effect = effect->UnaliasedParent()) {
if (const auto* output_clip = effect->OutputClip())
result = &output_clip->Unalias();
}
return result;
}
} // anonymous namespace
PendingLayer::PendingLayer(const PaintChunkSubset& chunks,
const PaintChunkIterator& first_chunk,
CompositingType compositing_type,
@ -197,6 +205,42 @@ PendingLayer::ScrollTranslationForScrollHitTestLayer() const {
return paint_chunk.hit_test_data->scroll_translation;
}
bool PendingLayer::PropertyTreeStateChanged() const {
auto change = PaintPropertyChangeType::kChangedOnlyNonRerasterValues;
if (change_of_decomposited_transforms_ >= change)
return true;
return property_tree_state_.ChangedToRoot(change);
}
bool PendingLayer::MightOverlap(const PendingLayer& other) const {
PropertyTreeState common_ancestor_state(
property_tree_state_.Transform()
.LowestCommonAncestor(other.property_tree_state_.Transform())
.Unalias(),
property_tree_state_.Clip()
.LowestCommonAncestor(other.property_tree_state_.Clip())
.Unalias(),
property_tree_state_.Effect()
.LowestCommonAncestor(other.property_tree_state_.Effect())
.Unalias());
// Move the common clip up if some effect nodes have OutputClip escaping the
// common clip.
if (const auto* clip_a = HighestOutputClipBetween(
common_ancestor_state.Effect(), property_tree_state_.Effect())) {
common_ancestor_state.SetClip(
clip_a->LowestCommonAncestor(common_ancestor_state.Clip()).Unalias());
}
if (const auto* clip_b =
HighestOutputClipBetween(common_ancestor_state.Effect(),
other.property_tree_state_.Effect())) {
common_ancestor_state.SetClip(
clip_b->LowestCommonAncestor(common_ancestor_state.Clip()).Unalias());
}
return VisualRectForOverlapTesting(common_ancestor_state)
.Intersects(other.VisualRectForOverlapTesting(common_ancestor_state));
}
// Walk the pending layer list and build up a table of transform nodes that
// can be de-composited (replaced with offset_to_transform_parent). A
// transform node can be de-composited if:

@ -114,9 +114,6 @@ class PLATFORM_EXPORT PendingLayer {
std::unique_ptr<JSONObject> ToJSON() const;
FloatRect VisualRectForOverlapTesting(
const PropertyTreeState& ancestor_state) const;
bool MayDrawContent() const;
bool RequiresOwnLayer() const {
@ -125,9 +122,13 @@ class PLATFORM_EXPORT PendingLayer {
bool PropertyTreeStateChanged() const;
bool MightOverlap(const PendingLayer& other) const;
static void DecompositeTransforms(Vector<PendingLayer>& pending_layers);
private:
FloatRect VisualRectForOverlapTesting(
const PropertyTreeState& ancestor_state) const;
FloatRect MapRectKnownToBeOpaque(const PropertyTreeState&) const;
bool MergeInternal(const PendingLayer& guest,
const PropertyTreeState& guest_state,

@ -0,0 +1,258 @@
// Copyright 2021 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/platform/graphics/compositing/pending_layer.h"
#include <memory>
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
#include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
#include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/test_paint_artifact.h"
namespace blink {
using testing::ElementsAre;
static Vector<wtf_size_t> ChunkIndices(const PendingLayer& layer) {
Vector<wtf_size_t> indices;
for (auto it = layer.Chunks().begin(); it != layer.Chunks().end(); ++it)
indices.push_back(it.IndexInPaintArtifact());
return indices;
}
TEST(PendingLayerTest, MightOverlap) {
TestPaintArtifact artifact;
artifact.Chunk().Bounds(IntRect(0, 0, 100, 100));
artifact.Chunk().Bounds(IntRect(0, 0, 100, 100));
auto t2 = CreateTransform(t0(), TransformationMatrix().Translate(99, 0),
FloatPoint3D(100, 100, 0));
artifact.Chunk(*t2, c0(), e0()).Bounds(IntRect(0, 0, 100, 100));
auto t3 = CreateTransform(t0(), TransformationMatrix().Translate(100, 0),
FloatPoint3D(100, 100, 0));
artifact.Chunk(*t3, c0(), e0()).Bounds(IntRect(0, 0, 100, 100));
auto t4 =
CreateAnimatingTransform(t0(), TransformationMatrix().Translate(100, 0),
FloatPoint3D(100, 100, 0));
artifact.Chunk(*t4, c0(), e0()).Bounds(IntRect(0, 0, 100, 100));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
EXPECT_TRUE(
pending_layer.MightOverlap(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_TRUE(
pending_layer.MightOverlap(PendingLayer(chunks, chunks.begin() + 2)));
EXPECT_FALSE(
pending_layer.MightOverlap(PendingLayer(chunks, chunks.begin() + 3)));
EXPECT_TRUE(
pending_layer.MightOverlap(PendingLayer(chunks, chunks.begin() + 4)));
}
TEST(PendingLayerTest, MightOverlapCommonClipAncestor) {
auto common_clip = CreateClip(c0(), t0(), FloatRoundedRect(0, 0, 100, 100));
auto c1 = CreateClip(*common_clip, t0(), FloatRoundedRect(0, 100, 100, 100));
auto c2 = CreateClip(*common_clip, t0(), FloatRoundedRect(50, 100, 100, 100));
auto c3 =
CreateClip(*common_clip, t0(), FloatRoundedRect(100, 100, 100, 100));
TestPaintArtifact artifact;
artifact.Chunk(t0(), *c1, e0())
.Bounds(IntRect(0, 100, 200, 100))
.Chunk(t0(), *c2, e0())
.Bounds(IntRect(0, 100, 200, 100))
.Chunk(t0(), *c3, e0())
.Bounds(IntRect(0, 100, 200, 100));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer1(chunks, chunks.begin());
PendingLayer pending_layer2(chunks, chunks.begin() + 1);
PendingLayer pending_layer3(chunks, chunks.begin() + 2);
EXPECT_FALSE(pending_layer1.MightOverlap(pending_layer3));
EXPECT_TRUE(pending_layer1.MightOverlap(pending_layer2));
EXPECT_TRUE(pending_layer2.MightOverlap(pending_layer3));
}
TEST(PendingLayerTest, Merge) {
TestPaintArtifact artifact;
artifact.Chunk()
.Bounds(IntRect(0, 0, 30, 40))
.RectKnownToBeOpaque(IntRect(0, 0, 30, 40));
artifact.Chunk()
.Bounds(IntRect(10, 20, 30, 40))
.RectKnownToBeOpaque(IntRect(10, 20, 30, 40));
artifact.Chunk()
.Bounds(IntRect(-5, -25, 20, 20))
.RectKnownToBeOpaque(IntRect(-5, -25, 20, 20));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.Bounds());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0));
EXPECT_EQ(pending_layer.Bounds(), pending_layer.RectKnownToBeOpaque());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
// Bounds not equal to one PaintChunk.
EXPECT_EQ(FloatRect(0, 0, 40, 60), pending_layer.Bounds());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0, 1));
EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.RectKnownToBeOpaque());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 2)));
EXPECT_EQ(FloatRect(-5, -25, 45, 85), pending_layer.Bounds());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0, 1, 2));
EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.RectKnownToBeOpaque());
}
TEST(PendingLayerTest, MergeWithGuestTransform) {
TestPaintArtifact artifact;
artifact.Chunk().Bounds(IntRect(0, 0, 30, 40));
auto transform = Create2DTranslation(t0(), 20, 25);
artifact.Chunk(*transform, c0(), e0()).Bounds(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(0, 0, 70, 85), pending_layer.Bounds());
EXPECT_EQ(PropertyTreeState::Root(), pending_layer.GetPropertyTreeState());
}
TEST(PendingLayerTest, MergeWithHomeTransform) {
TestPaintArtifact artifact;
auto transform = Create2DTranslation(t0(), 20, 25);
artifact.Chunk(*transform, c0(), e0()).Bounds(IntRect(0, 0, 30, 40));
artifact.Chunk().Bounds(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(0, 0, 50, 65), pending_layer.Bounds());
EXPECT_EQ(PropertyTreeState::Root(), pending_layer.GetPropertyTreeState());
}
TEST(PendingLayerTest, MergeWithBothTransforms) {
TestPaintArtifact artifact;
auto t1 = Create2DTranslation(t0(), 20, 25);
artifact.Chunk(*t1, c0(), e0()).Bounds(IntRect(0, 0, 30, 40));
auto t2 = Create2DTranslation(t0(), -20, -25);
artifact.Chunk(*t2, c0(), e0()).Bounds(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(-20, -25, 70, 90), pending_layer.Bounds());
EXPECT_EQ(PropertyTreeState::Root(), pending_layer.GetPropertyTreeState());
}
TEST(PendingLayerTest, DontMergeSparse) {
TestPaintArtifact artifact;
artifact.Chunk()
.Bounds(IntRect(0, 0, 30, 40))
.RectKnownToBeOpaque(IntRect(0, 0, 30, 40));
artifact.Chunk()
.Bounds(IntRect(200, 200, 30, 40))
.RectKnownToBeOpaque(IntRect(200, 200, 30, 40));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_FALSE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.Bounds());
EXPECT_EQ(chunks.begin()->properties, pending_layer.GetPropertyTreeState());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0));
}
TEST(PendingLayerTest, PendingLayerDontMergeSparseWithTransforms) {
TestPaintArtifact artifact;
auto t1 = Create2DTranslation(t0(), 20, 25);
artifact.Chunk(*t1, c0(), e0()).Bounds(IntRect(0, 0, 30, 40));
auto t2 = Create2DTranslation(t0(), 1000, 1000);
artifact.Chunk(*t2, c0(), e0()).Bounds(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_FALSE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.Bounds());
EXPECT_EQ(chunks.begin()->properties, pending_layer.GetPropertyTreeState());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0));
}
TEST(PendingLayerTest, DontMergeSparseInCompositedEffect) {
TestPaintArtifact artifact;
auto t1 = Create2DTranslation(t0(), 20, 25);
auto e1 =
CreateOpacityEffect(e0(), 1.0f, CompositingReason::kWillChangeOpacity);
artifact.Chunk(*t1, c0(), *e1).Bounds(IntRect(0, 0, 30, 40));
auto t2 = Create2DTranslation(t0(), 1000, 1000);
artifact.Chunk(*t2, c0(), *e1).Bounds(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_FALSE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.Bounds());
EXPECT_EQ(chunks.begin()->properties, pending_layer.GetPropertyTreeState());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0));
}
TEST(PendingLayerTest, MergeSparseInNonCompositedEffect) {
TestPaintArtifact artifact;
auto t1 = Create2DTranslation(t0(), 20, 25);
auto t2 = Create2DTranslation(t0(), 1000, 1000);
auto e1 = CreateOpacityEffect(e0(), 1.0f, CompositingReason::kNone);
artifact.Chunk(*t1, c0(), *e1).Bounds(IntRect(0, 0, 30, 40));
artifact.Chunk(*t2, c0(), *e1).Bounds(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
EXPECT_EQ(FloatRect(20, 25, 1030, 1035), pending_layer.Bounds());
EXPECT_EQ(PropertyTreeState(t0(), c0(), *e1),
pending_layer.GetPropertyTreeState());
EXPECT_THAT(ChunkIndices(pending_layer), ElementsAre(0, 1));
}
TEST(PendingLayerTest, KnownOpaque) {
TestPaintArtifact artifact;
artifact.Chunk().Bounds(IntRect(0, 0, 30, 40));
artifact.Chunk()
.Bounds(IntRect(0, 0, 25, 35))
.RectKnownToBeOpaque(IntRect(0, 0, 25, 35));
artifact.Chunk()
.Bounds(IntRect(0, 0, 50, 60))
.RectKnownToBeOpaque(IntRect(0, 0, 50, 60));
PaintChunkSubset chunks(artifact.Build());
PendingLayer pending_layer(chunks, chunks.begin());
EXPECT_TRUE(pending_layer.RectKnownToBeOpaque().IsEmpty());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 1)));
// Chunk 2 doesn't cover the entire layer, so not opaque.
EXPECT_EQ(FloatRect(0, 0, 25, 35), pending_layer.RectKnownToBeOpaque());
EXPECT_NE(pending_layer.Bounds(), pending_layer.RectKnownToBeOpaque());
ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunks, chunks.begin() + 2)));
// Chunk 3 covers the entire layer, so now it's opaque.
EXPECT_EQ(FloatRect(0, 0, 50, 60), pending_layer.Bounds());
EXPECT_EQ(pending_layer.Bounds(), pending_layer.RectKnownToBeOpaque());
}
TEST(PendingLayerTest, CanNotMergeAcrossPaintArtifacts) {
TestPaintArtifact test_artifact_a;
test_artifact_a.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite);
PaintChunkSubset chunks_a(test_artifact_a.Build());
PendingLayer layer_a(chunks_a, chunks_a.begin());
TestPaintArtifact test_artifact_b;
test_artifact_b.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kGray);
PaintChunkSubset chunks_b(test_artifact_b.Build());
PendingLayer layer_b(chunks_b, chunks_b.begin());
EXPECT_FALSE(layer_a.Merge(layer_b));
}
} // namespace blink