blob: 967f2aec38d09b09e1b9782f6e1445a3f73b8ff5 [file] [log] [blame]
// Copyright 2014 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 "platform/graphics/paint/PaintControllerTest.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/paint/ClipPathDisplayItem.h"
#include "platform/graphics/paint/ClipPathRecorder.h"
#include "platform/graphics/paint/ClipRecorder.h"
#include "platform/graphics/paint/CompositingRecorder.h"
#include "platform/graphics/paint/DisplayItemCacheSkipper.h"
#include "platform/graphics/paint/DrawingDisplayItem.h"
#include "platform/graphics/paint/DrawingRecorder.h"
#include "platform/graphics/paint/SubsequenceRecorder.h"
#include "platform/runtime_enabled_features.h"
#include "platform/testing/PaintTestConfigurations.h"
#include "testing/gmock/include/gmock/gmock.h"
using blink::testing::CreateOpacityOnlyEffect;
using ::testing::UnorderedElementsAre;
namespace blink {
// Tests using this class will be tested with under-invalidation-checking
// enabled and disabled.
class PaintControllerTest : public PaintTestConfigurations,
public PaintControllerTestBase {
};
INSTANTIATE_TEST_CASE_P(
All,
PaintControllerTest,
::testing::Values(0,
kSlimmingPaintV175,
kSlimmingPaintV2,
kUnderInvalidationChecking,
kSlimmingPaintV175 | kUnderInvalidationChecking,
kSlimmingPaintV2 | kUnderInvalidationChecking));
TEST_P(PaintControllerTest, NestedRecorders) {
GraphicsContext context(GetPaintController());
FakeDisplayItemClient client("client", LayoutRect(100, 100, 200, 200));
InitRootChunk();
{
ClipRecorder clip_recorder(context, client, kClipType,
IntRect(100, 100, 50, 50));
DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 200, 200));
}
GetPaintController().CommitNewDisplayItems();
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 1,
TestDisplayItem(client, kBackgroundType));
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
// Raster invalidation for the whole chunk will be issued during
// PaintArtifactCompositor::Update().
EXPECT_TRUE(GetPaintController()
.PaintChunks()[0]
.raster_invalidation_rects.IsEmpty());
} else {
EXPECT_DISPLAY_LIST(
GetPaintController().GetDisplayItemList(), 3,
TestDisplayItem(client, kClipType),
TestDisplayItem(client, kBackgroundType),
TestDisplayItem(client, DisplayItem::ClipTypeToEndClipType(kClipType)));
}
}
TEST_P(PaintControllerTest, UpdateBasic) {
FakeDisplayItemClient first("first", LayoutRect(100, 100, 300, 300));
FakeDisplayItemClient second("second", LayoutRect(100, 100, 200, 200));
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 200, 200));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
EXPECT_EQ(0, NumCachedNewItems());
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 3,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(first, kForegroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
// Raster invalidation for the whole chunk will be issued during
// PaintArtifactCompositor::Update().
EXPECT_TRUE(GetPaintController()
.PaintChunks()[0]
.raster_invalidation_rects.IsEmpty());
InitRootChunk();
}
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
EXPECT_EQ(2, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(2, NumSequentialMatches());
EXPECT_EQ(0, NumOutOfOrderMatches());
EXPECT_EQ(1, NumIndexedItems());
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(first, kForegroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
// |second| disappeared from the chunk.
UnorderedElementsAre(FloatRect(100, 100, 200, 200)));
}
}
TEST_P(PaintControllerTest, UpdateSwapOrder) {
FakeDisplayItemClient first("first", LayoutRect(100, 100, 100, 100));
FakeDisplayItemClient second("second", LayoutRect(100, 100, 50, 200));
FakeDisplayItemClient unaffected("unaffected", LayoutRect(300, 300, 10, 10));
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, second, kForegroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, unaffected, kBackgroundType, FloatRect(300, 300, 10, 10));
DrawRect(context, unaffected, kForegroundType, FloatRect(300, 300, 10, 10));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(first, kForegroundType),
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(second, kForegroundType),
TestDisplayItem(unaffected, kBackgroundType),
TestDisplayItem(unaffected, kForegroundType));
InitRootChunk();
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, second, kForegroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, unaffected, kBackgroundType, FloatRect(300, 300, 10, 10));
DrawRect(context, unaffected, kForegroundType, FloatRect(300, 300, 10, 10));
EXPECT_EQ(6, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(5, NumSequentialMatches()); // second, first foreground, unaffected
EXPECT_EQ(1, NumOutOfOrderMatches()); // first
EXPECT_EQ(2, NumIndexedItems()); // first
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(second, kForegroundType),
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(first, kForegroundType),
TestDisplayItem(unaffected, kBackgroundType),
TestDisplayItem(unaffected, kForegroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
// Bounds of |second| (old and new are the same).
UnorderedElementsAre(FloatRect(100, 100, 50, 200)));
}
}
TEST_P(PaintControllerTest, UpdateSwapOrderWithInvalidation) {
FakeDisplayItemClient first("first", LayoutRect(100, 100, 100, 100));
FakeDisplayItemClient second("second", LayoutRect(100, 100, 50, 200));
FakeDisplayItemClient unaffected("unaffected", LayoutRect(300, 300, 10, 10));
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, second, kForegroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, unaffected, kBackgroundType, FloatRect(300, 300, 10, 10));
DrawRect(context, unaffected, kForegroundType, FloatRect(300, 300, 10, 10));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(first, kForegroundType),
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(second, kForegroundType),
TestDisplayItem(unaffected, kBackgroundType),
TestDisplayItem(unaffected, kForegroundType));
InitRootChunk();
first.SetDisplayItemsUncached();
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, second, kForegroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, unaffected, kBackgroundType, FloatRect(300, 300, 10, 10));
DrawRect(context, unaffected, kForegroundType, FloatRect(300, 300, 10, 10));
EXPECT_EQ(4, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(4, NumSequentialMatches()); // second, unaffected
EXPECT_EQ(0, NumOutOfOrderMatches());
EXPECT_EQ(2, NumIndexedItems());
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(second, kForegroundType),
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(first, kForegroundType),
TestDisplayItem(unaffected, kBackgroundType),
TestDisplayItem(unaffected, kForegroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
// Bounds of |first| (old and new are the same).
UnorderedElementsAre(FloatRect(100, 100, 100, 100)));
// No need to invalidate raster of |second|, because the client (|first|)
// which swapped order with it has been invalidated.
}
}
TEST_P(PaintControllerTest, UpdateNewItemInMiddle) {
FakeDisplayItemClient first("first", LayoutRect(100, 100, 100, 100));
FakeDisplayItemClient second("second", LayoutRect(100, 100, 50, 200));
FakeDisplayItemClient third("third", LayoutRect(125, 100, 200, 50));
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(second, kBackgroundType));
InitRootChunk();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, third, kBackgroundType, FloatRect(125, 100, 200, 50));
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
EXPECT_EQ(2, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(2, NumSequentialMatches()); // first, second
EXPECT_EQ(0, NumOutOfOrderMatches());
EXPECT_EQ(0, NumIndexedItems());
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 3,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(third, kBackgroundType),
TestDisplayItem(second, kBackgroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
// |third| newly appeared in the chunk.
UnorderedElementsAre(FloatRect(125, 100, 200, 50)));
}
}
TEST_P(PaintControllerTest, UpdateInvalidationWithPhases) {
FakeDisplayItemClient first("first", LayoutRect(100, 100, 100, 100));
FakeDisplayItemClient second("second", LayoutRect(100, 100, 50, 200));
FakeDisplayItemClient third("third", LayoutRect(300, 100, 50, 50));
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, third, kBackgroundType, FloatRect(300, 100, 50, 50));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, second, kForegroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, third, kForegroundType, FloatRect(300, 100, 50, 50));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(third, kBackgroundType),
TestDisplayItem(first, kForegroundType),
TestDisplayItem(second, kForegroundType),
TestDisplayItem(third, kForegroundType));
InitRootChunk();
second.SetDisplayItemsUncached();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, third, kBackgroundType, FloatRect(300, 100, 50, 50));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, second, kForegroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, third, kForegroundType, FloatRect(300, 100, 50, 50));
EXPECT_EQ(4, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(4, NumSequentialMatches());
EXPECT_EQ(0, NumOutOfOrderMatches());
EXPECT_EQ(2, NumIndexedItems());
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(third, kBackgroundType),
TestDisplayItem(first, kForegroundType),
TestDisplayItem(second, kForegroundType),
TestDisplayItem(third, kForegroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
// Bounds of |second| (old and new are the same).
UnorderedElementsAre(FloatRect(100, 100, 50, 200)));
}
}
TEST_P(PaintControllerTest, IncrementalRasterInvalidation) {
if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
return;
LayoutRect initial_rect(100, 100, 100, 100);
std::unique_ptr<FakeDisplayItemClient> clients[6];
for (auto& client : clients)
client = std::make_unique<FakeDisplayItemClient>("", initial_rect);
GraphicsContext context(GetPaintController());
InitRootChunk();
for (auto& client : clients)
DrawRect(context, *client, kBackgroundType, FloatRect(initial_rect));
GetPaintController().CommitNewDisplayItems();
InitRootChunk();
clients[0]->SetVisualRect(LayoutRect(100, 100, 150, 100));
clients[1]->SetVisualRect(LayoutRect(100, 100, 100, 150));
clients[2]->SetVisualRect(LayoutRect(100, 100, 150, 80));
clients[3]->SetVisualRect(LayoutRect(100, 100, 80, 150));
clients[4]->SetVisualRect(LayoutRect(100, 100, 150, 150));
clients[5]->SetVisualRect(LayoutRect(100, 100, 80, 80));
for (auto& client : clients) {
client->SetDisplayItemsUncached(PaintInvalidationReason::kIncremental);
DrawRect(context, *client, kBackgroundType,
FloatRect(client->VisualRect()));
}
GetPaintController().CommitNewDisplayItems();
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
UnorderedElementsAre(FloatRect(200, 100, 50, 100), // 0: right
FloatRect(100, 200, 100, 50), // 1: bottom
FloatRect(200, 100, 50, 80), // 2: right
FloatRect(100, 180, 100, 20), // 2: bottom
FloatRect(180, 100, 20, 100), // 3: right
FloatRect(100, 200, 80, 50), // 3: bottom
FloatRect(200, 100, 50, 150), // 4: right
FloatRect(100, 200, 150, 50), // 4: bottom
FloatRect(180, 100, 20, 100), // 5: right
FloatRect(100, 180, 100, 20))); // 5: bottom
InitRootChunk();
}
TEST_P(PaintControllerTest, UpdateAddFirstOverlap) {
FakeDisplayItemClient first("first", LayoutRect(100, 100, 150, 150));
FakeDisplayItemClient second("second", LayoutRect(200, 200, 50, 50));
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, second, kBackgroundType, FloatRect(200, 200, 50, 50));
DrawRect(context, second, kForegroundType, FloatRect(200, 200, 50, 50));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(second, kForegroundType));
InitRootChunk();
first.SetDisplayItemsUncached();
second.SetDisplayItemsUncached();
second.SetVisualRect(LayoutRect(150, 250, 100, 100));
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 150, 150));
DrawRect(context, second, kBackgroundType, FloatRect(150, 250, 100, 100));
DrawRect(context, second, kForegroundType, FloatRect(150, 250, 100, 100));
EXPECT_EQ(0, NumCachedNewItems());
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 4,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(first, kForegroundType),
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(second, kForegroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(
GetPaintController().PaintChunks()[0].raster_invalidation_rects,
UnorderedElementsAre(
// |first| newly appeared in the chunk.
FloatRect(100, 100, 150, 150),
// Old and new bounds of |second|.
FloatRect(200, 200, 50, 50), FloatRect(150, 250, 100, 100)));
InitRootChunk();
}
DrawRect(context, second, kBackgroundType, FloatRect(150, 250, 100, 100));
DrawRect(context, second, kForegroundType, FloatRect(150, 250, 100, 100));
EXPECT_EQ(2, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(2, NumSequentialMatches());
EXPECT_EQ(0, NumOutOfOrderMatches());
EXPECT_EQ(2, NumIndexedItems());
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(second, kForegroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
// |first| disappeared from the chunk.
UnorderedElementsAre(FloatRect(100, 100, 150, 150)));
}
}
TEST_P(PaintControllerTest, UpdateAddLastOverlap) {
FakeDisplayItemClient first("first", LayoutRect(100, 100, 150, 150));
FakeDisplayItemClient second("second", LayoutRect(200, 200, 50, 50));
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 150, 150));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(first, kForegroundType));
InitRootChunk();
first.SetDisplayItemsUncached();
first.SetVisualRect(LayoutRect(150, 150, 100, 100));
second.SetDisplayItemsUncached();
DrawRect(context, first, kBackgroundType, FloatRect(150, 150, 100, 100));
DrawRect(context, first, kForegroundType, FloatRect(150, 150, 100, 100));
DrawRect(context, second, kBackgroundType, FloatRect(200, 200, 50, 50));
DrawRect(context, second, kForegroundType, FloatRect(200, 200, 50, 50));
EXPECT_EQ(0, NumCachedNewItems());
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 4,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(first, kForegroundType),
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(second, kForegroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
UnorderedElementsAre(
// The bigger of old and new bounds of |first|.
FloatRect(100, 100, 150, 150),
// |second| newly appeared in the chunk.
FloatRect(200, 200, 50, 50)));
InitRootChunk();
}
first.SetDisplayItemsUncached();
first.SetVisualRect(LayoutRect(100, 100, 150, 150));
second.SetDisplayItemsUncached();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 150, 150));
EXPECT_EQ(0, NumCachedNewItems());
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(first, kForegroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
UnorderedElementsAre(
// The bigger of old and new bounds of |first|.
FloatRect(100, 100, 150, 150),
// |second| disappeared from the chunk.
FloatRect(200, 200, 50, 50)));
}
}
TEST_P(PaintControllerTest, UpdateClip) {
FakeDisplayItemClient first("first", LayoutRect(100, 100, 150, 150));
FakeDisplayItemClient second("second", LayoutRect(100, 100, 200, 200));
GraphicsContext context(GetPaintController());
scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
nullptr, nullptr, FloatRoundedRect(1, 1, 2, 2));
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(first, kClipType);
PaintChunkProperties properties = testing::DefaultPaintChunkProperties();
properties.property_tree_state.SetClip(clip.get());
GetPaintController().UpdateCurrentPaintChunkProperties(id, properties);
}
ClipRecorder clip_recorder(context, first, kClipType, IntRect(1, 1, 2, 2));
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 200, 200));
}
GetPaintController().CommitNewDisplayItems();
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(second, kBackgroundType));
InitRootChunk();
} else {
EXPECT_DISPLAY_LIST(
GetPaintController().GetDisplayItemList(), 4,
TestDisplayItem(first, kClipType),
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(first, DisplayItem::ClipTypeToEndClipType(kClipType)));
}
first.SetDisplayItemsUncached();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 200, 200));
EXPECT_EQ(1, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(1, NumSequentialMatches());
EXPECT_EQ(0, NumOutOfOrderMatches());
EXPECT_EQ(1, NumIndexedItems());
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(second, kBackgroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
// This is a new chunk. Raster invalidation for the whole chunk will be
// issued during PaintArtifactCompositor::Update().
EXPECT_TRUE(GetPaintController()
.PaintChunks()[0]
.raster_invalidation_rects.IsEmpty());
InitRootChunk();
}
second.SetDisplayItemsUncached();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
scoped_refptr<ClipPaintPropertyNode> clip2 = ClipPaintPropertyNode::Create(
nullptr, nullptr, FloatRoundedRect(1, 1, 2, 2));
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(second, kClipType);
PaintChunkProperties properties = testing::DefaultPaintChunkProperties();
properties.property_tree_state.SetClip(clip2.get());
GetPaintController().UpdateCurrentPaintChunkProperties(id, properties);
}
ClipRecorder clip_recorder(context, second, kClipType, IntRect(1, 1, 2, 2));
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 200, 200));
}
GetPaintController().CommitNewDisplayItems();
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(second, kBackgroundType));
EXPECT_EQ(2u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
// |second| disappeared from the first chunk.
UnorderedElementsAre(FloatRect(100, 100, 200, 200)));
// This is a new chunk. Raster invalidation for the whole chunk will be
// issued during PaintArtifactCompositor::Update().
EXPECT_TRUE(GetPaintController()
.PaintChunks()[1]
.raster_invalidation_rects.IsEmpty());
} else {
EXPECT_DISPLAY_LIST(
GetPaintController().GetDisplayItemList(), 4,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(second, kClipType),
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(second, DisplayItem::ClipTypeToEndClipType(kClipType)));
}
}
TEST_P(PaintControllerTest, CachedDisplayItems) {
FakeDisplayItemClient first("first");
FakeDisplayItemClient second("second");
GraphicsContext context(GetPaintController());
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
InitRootChunk();
}
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 150, 150));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(second, kBackgroundType));
EXPECT_TRUE(GetPaintController().ClientCacheIsValid(first));
EXPECT_TRUE(GetPaintController().ClientCacheIsValid(second));
sk_sp<const PaintRecord> first_paint_record =
static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[0])
.GetPaintRecord();
sk_sp<const PaintRecord> second_paint_record =
static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[1])
.GetPaintRecord();
first.SetDisplayItemsUncached();
EXPECT_FALSE(GetPaintController().ClientCacheIsValid(first));
EXPECT_TRUE(GetPaintController().ClientCacheIsValid(second));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
InitRootChunk();
}
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 150, 150));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(second, kBackgroundType));
// The first display item should be updated.
EXPECT_NE(first_paint_record,
static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[0])
.GetPaintRecord());
// The second display item should be cached.
if (!RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
EXPECT_EQ(second_paint_record,
static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[1])
.GetPaintRecord());
}
EXPECT_TRUE(GetPaintController().ClientCacheIsValid(first));
EXPECT_TRUE(GetPaintController().ClientCacheIsValid(second));
InvalidateAll();
EXPECT_FALSE(GetPaintController().ClientCacheIsValid(first));
EXPECT_FALSE(GetPaintController().ClientCacheIsValid(second));
}
TEST_P(PaintControllerTest, UpdateSwapOrderWithChildren) {
FakeDisplayItemClient container1("container1",
LayoutRect(100, 100, 100, 100));
FakeDisplayItemClient content1("content1", LayoutRect(100, 100, 50, 200));
FakeDisplayItemClient container2("container2",
LayoutRect(100, 200, 100, 100));
FakeDisplayItemClient content2("content2", LayoutRect(100, 200, 50, 200));
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, container1, kForegroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100));
DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
DrawRect(context, container2, kForegroundType, FloatRect(100, 200, 100, 100));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 8,
TestDisplayItem(container1, kBackgroundType),
TestDisplayItem(content1, kBackgroundType),
TestDisplayItem(content1, kForegroundType),
TestDisplayItem(container1, kForegroundType),
TestDisplayItem(container2, kBackgroundType),
TestDisplayItem(content2, kBackgroundType),
TestDisplayItem(content2, kForegroundType),
TestDisplayItem(container2, kForegroundType));
InitRootChunk();
// Simulate the situation when |container1| gets a z-index that is greater
// than that of |container2|.
DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100));
DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
DrawRect(context, container2, kForegroundType, FloatRect(100, 200, 100, 100));
DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, container1, kForegroundType, FloatRect(100, 100, 100, 100));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 8,
TestDisplayItem(container2, kBackgroundType),
TestDisplayItem(content2, kBackgroundType),
TestDisplayItem(content2, kForegroundType),
TestDisplayItem(container2, kForegroundType),
TestDisplayItem(container1, kBackgroundType),
TestDisplayItem(content1, kBackgroundType),
TestDisplayItem(content1, kForegroundType),
TestDisplayItem(container1, kForegroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(
GetPaintController().PaintChunks()[0].raster_invalidation_rects,
UnorderedElementsAre(
// Bounds of |container2| which was moved behind |container1|.
FloatRect(100, 200, 100, 100),
// Bounds of |content2| which was moved along with |container2|.
FloatRect(100, 200, 50, 200)));
}
}
TEST_P(PaintControllerTest, UpdateSwapOrderWithChildrenAndInvalidation) {
FakeDisplayItemClient container1("container1",
LayoutRect(100, 100, 100, 100));
FakeDisplayItemClient content1("content1", LayoutRect(100, 100, 50, 200));
FakeDisplayItemClient container2("container2",
LayoutRect(100, 200, 100, 100));
FakeDisplayItemClient content2("content2", LayoutRect(100, 200, 50, 200));
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, container1, kForegroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100));
DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
DrawRect(context, container2, kForegroundType, FloatRect(100, 200, 100, 100));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 8,
TestDisplayItem(container1, kBackgroundType),
TestDisplayItem(content1, kBackgroundType),
TestDisplayItem(content1, kForegroundType),
TestDisplayItem(container1, kForegroundType),
TestDisplayItem(container2, kBackgroundType),
TestDisplayItem(content2, kBackgroundType),
TestDisplayItem(content2, kForegroundType),
TestDisplayItem(container2, kForegroundType));
InitRootChunk();
// Simulate the situation when |container1| gets a z-index that is greater
// than that of |container2|, and |container1| is invalidated.
container1.SetDisplayItemsUncached();
DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100));
DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
DrawRect(context, container2, kForegroundType, FloatRect(100, 200, 100, 100));
DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, container1, kForegroundType, FloatRect(100, 100, 100, 100));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 8,
TestDisplayItem(container2, kBackgroundType),
TestDisplayItem(content2, kBackgroundType),
TestDisplayItem(content2, kForegroundType),
TestDisplayItem(container2, kForegroundType),
TestDisplayItem(container1, kBackgroundType),
TestDisplayItem(content1, kBackgroundType),
TestDisplayItem(content1, kForegroundType),
TestDisplayItem(container1, kForegroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(
GetPaintController().PaintChunks()[0].raster_invalidation_rects,
UnorderedElementsAre(
// Bounds of |container1| (old and new are the same).
FloatRect(100, 100, 100, 100),
// Bounds of |container2| which was moved behind |container1|.
FloatRect(100, 200, 100, 100),
// Bounds of |content2| which was moved along with |container2|.
FloatRect(100, 200, 50, 200)));
}
}
TEST_P(PaintControllerTest, CachedSubsequenceForcePaintChunk) {
if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled() ||
RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
return;
GraphicsContext context(GetPaintController());
FakeDisplayItemClient root("root");
auto root_properties = testing::DefaultPaintChunkProperties();
PaintChunk::Id root_id(root, DisplayItem::kCaret);
// Record a first chunk with backface_hidden == false
GetPaintController().UpdateCurrentPaintChunkProperties(root_id,
root_properties);
DrawRect(context, root, kBackgroundType, FloatRect(100, 100, 100, 100));
FakeDisplayItemClient container("container");
{
// Record a second chunk with backface_hidden == true
auto container_properties = testing::DefaultPaintChunkProperties();
container_properties.backface_hidden = true;
PaintChunk::Id container_id(container, DisplayItem::kCaret);
SubsequenceRecorder r(context, container);
GetPaintController().UpdateCurrentPaintChunkProperties(
container_id, container_properties);
DrawRect(context, container, kBackgroundType,
FloatRect(100, 100, 100, 100));
DrawRect(context, container, kForegroundType,
FloatRect(100, 100, 100, 100));
}
DrawRect(context, root, kForegroundType, FloatRect(100, 100, 100, 100));
GetPaintController().CommitNewDisplayItems();
root_properties.backface_hidden = true;
// This time, record the fist chunk with backface_hidden == true
GetPaintController().UpdateCurrentPaintChunkProperties(root_id,
root_properties);
DrawRect(context, root, kBackgroundType, FloatRect(100, 100, 100, 100));
EXPECT_TRUE(GetPaintController().UseCachedSubsequenceIfPossible(container));
DrawRect(context, root, kForegroundType, FloatRect(100, 100, 100, 100));
GetPaintController().CommitNewDisplayItems();
// Even though the paint properties match, |container| should receive its
// own PaintChunk because it is a cached subsequence.
EXPECT_EQ(3u, GetPaintController().GetPaintArtifact().PaintChunks().size());
EXPECT_EQ(root,
GetPaintController().GetPaintArtifact().PaintChunks()[0].id.client);
EXPECT_EQ(container,
GetPaintController().GetPaintArtifact().PaintChunks()[1].id.client);
EXPECT_EQ(root,
GetPaintController().GetPaintArtifact().PaintChunks()[2].id.client);
}
TEST_P(PaintControllerTest, CachedSubsequenceSwapOrder) {
FakeDisplayItemClient container1("container1",
LayoutRect(100, 100, 100, 100));
FakeDisplayItemClient content1("content1", LayoutRect(100, 100, 50, 200));
FakeDisplayItemClient container2("container2",
LayoutRect(100, 200, 100, 100));
FakeDisplayItemClient content2("content2", LayoutRect(100, 200, 50, 200));
GraphicsContext context(GetPaintController());
PaintChunkProperties container1_properties =
testing::DefaultPaintChunkProperties();
PaintChunkProperties container2_properties =
testing::DefaultPaintChunkProperties();
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(container1, kBackgroundType);
container1_properties.property_tree_state.SetEffect(
CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5).get());
GetPaintController().UpdateCurrentPaintChunkProperties(
id, container1_properties);
}
SubsequenceRecorder r(context, container1);
DrawRect(context, container1, kBackgroundType,
FloatRect(100, 100, 100, 100));
DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, container1, kForegroundType,
FloatRect(100, 100, 100, 100));
}
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(container2, kBackgroundType);
container2_properties.property_tree_state.SetEffect(
CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5).get());
GetPaintController().UpdateCurrentPaintChunkProperties(
id, container2_properties);
}
SubsequenceRecorder r(context, container2);
DrawRect(context, container2, kBackgroundType,
FloatRect(100, 200, 100, 100));
DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
DrawRect(context, container2, kForegroundType,
FloatRect(100, 200, 100, 100));
}
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 8,
TestDisplayItem(container1, kBackgroundType),
TestDisplayItem(content1, kBackgroundType),
TestDisplayItem(content1, kForegroundType),
TestDisplayItem(container1, kForegroundType),
TestDisplayItem(container2, kBackgroundType),
TestDisplayItem(content2, kBackgroundType),
TestDisplayItem(content2, kForegroundType),
TestDisplayItem(container2, kForegroundType));
auto* markers = GetSubsequenceMarkers(container1);
CHECK(markers);
EXPECT_EQ(0u, markers->start);
EXPECT_EQ(4u, markers->end);
markers = GetSubsequenceMarkers(container2);
CHECK(markers);
EXPECT_EQ(4u, markers->start);
EXPECT_EQ(8u, markers->end);
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(2u, GetPaintController().PaintChunks().size());
EXPECT_EQ(PaintChunk::Id(container1, kBackgroundType),
GetPaintController().PaintChunks()[0].id);
EXPECT_EQ(PaintChunk::Id(container2, kBackgroundType),
GetPaintController().PaintChunks()[1].id);
// Raster invalidation for the whole chunks will be issued during
// PaintArtifactCompositor::Update().
EXPECT_TRUE(GetPaintController()
.PaintChunks()[0]
.raster_invalidation_rects.IsEmpty());
EXPECT_TRUE(GetPaintController()
.PaintChunks()[1]
.raster_invalidation_rects.IsEmpty());
}
// Simulate the situation when |container1| gets a z-index that is greater
// than that of |container2|.
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
// When under-invalidation-checking is enabled,
// useCachedSubsequenceIfPossible is forced off, and the client is expected
// to create the same painting as in the previous paint.
EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container2));
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(container2, kBackgroundType);
GetPaintController().UpdateCurrentPaintChunkProperties(
id, container2_properties);
}
SubsequenceRecorder r(context, container2);
DrawRect(context, container2, kBackgroundType,
FloatRect(100, 200, 100, 100));
DrawRect(context, content2, kBackgroundType,
FloatRect(100, 200, 50, 200));
DrawRect(context, content2, kForegroundType,
FloatRect(100, 200, 50, 200));
DrawRect(context, container2, kForegroundType,
FloatRect(100, 200, 100, 100));
}
EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container1));
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(container1, kBackgroundType);
GetPaintController().UpdateCurrentPaintChunkProperties(
id, container1_properties);
}
SubsequenceRecorder r(context, container1);
DrawRect(context, container1, kBackgroundType,
FloatRect(100, 100, 100, 100));
DrawRect(context, content1, kBackgroundType,
FloatRect(100, 100, 50, 200));
DrawRect(context, content1, kForegroundType,
FloatRect(100, 100, 50, 200));
DrawRect(context, container1, kForegroundType,
FloatRect(100, 100, 100, 100));
}
} else {
EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container2));
EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container1));
}
EXPECT_EQ(8, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(0, NumSequentialMatches());
EXPECT_EQ(0, NumOutOfOrderMatches());
EXPECT_EQ(0, NumIndexedItems());
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 8,
TestDisplayItem(container2, kBackgroundType),
TestDisplayItem(content2, kBackgroundType),
TestDisplayItem(content2, kForegroundType),
TestDisplayItem(container2, kForegroundType),
TestDisplayItem(container1, kBackgroundType),
TestDisplayItem(content1, kBackgroundType),
TestDisplayItem(content1, kForegroundType),
TestDisplayItem(container1, kForegroundType));
markers = GetSubsequenceMarkers(container2);
CHECK(markers);
EXPECT_EQ(0u, markers->start);
EXPECT_EQ(4u, markers->end);
markers = GetSubsequenceMarkers(container1);
CHECK(markers);
EXPECT_EQ(4u, markers->start);
EXPECT_EQ(8u, markers->end);
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(2u, GetPaintController().PaintChunks().size());
EXPECT_EQ(PaintChunk::Id(container2, kBackgroundType),
GetPaintController().PaintChunks()[0].id);
EXPECT_EQ(PaintChunk::Id(container1, kBackgroundType),
GetPaintController().PaintChunks()[1].id);
// Swapping order of chunks should not invalidate anything.
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
UnorderedElementsAre());
EXPECT_THAT(GetPaintController().PaintChunks()[1].raster_invalidation_rects,
UnorderedElementsAre());
}
}
TEST_P(PaintControllerTest, CachedSubsequenceAndDisplayItemsSwapOrder) {
FakeDisplayItemClient root("root", LayoutRect(0, 0, 300, 300));
FakeDisplayItemClient content1("content1", LayoutRect(100, 100, 50, 200));
FakeDisplayItemClient container2("container2",
LayoutRect(100, 200, 100, 100));
FakeDisplayItemClient content2("content2", LayoutRect(100, 200, 50, 200));
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
{
SubsequenceRecorder r(context, container2);
DrawRect(context, container2, kBackgroundType,
FloatRect(100, 200, 100, 100));
DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
DrawRect(context, container2, kForegroundType,
FloatRect(100, 200, 100, 100));
}
DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
TestDisplayItem(content1, kBackgroundType),
TestDisplayItem(container2, kBackgroundType),
TestDisplayItem(content2, kBackgroundType),
TestDisplayItem(content2, kForegroundType),
TestDisplayItem(container2, kForegroundType),
TestDisplayItem(content1, kForegroundType));
auto* markers = GetSubsequenceMarkers(container2);
CHECK(markers);
EXPECT_EQ(1u, markers->start);
EXPECT_EQ(5u, markers->end);
// Simulate the situation when |container2| gets a z-index that is smaller
// than that of |content1|.
InitRootChunk();
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
// When under-invalidation-checking is enabled,
// useCachedSubsequenceIfPossible is forced off, and the client is expected
// to create the same painting as in the previous paint.
EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container2));
{
SubsequenceRecorder r(context, container2);
DrawRect(context, container2, kBackgroundType,
FloatRect(100, 200, 100, 100));
DrawRect(context, content2, kBackgroundType,
FloatRect(100, 200, 50, 200));
DrawRect(context, content2, kForegroundType,
FloatRect(100, 200, 50, 200));
DrawRect(context, container2, kForegroundType,
FloatRect(100, 200, 100, 100));
}
DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
} else {
EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container2));
EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, content1,
kBackgroundType));
EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, content1,
kForegroundType));
}
EXPECT_EQ(6, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(2, NumSequentialMatches());
EXPECT_EQ(0, NumOutOfOrderMatches());
EXPECT_EQ(0, NumIndexedItems());
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
TestDisplayItem(container2, kBackgroundType),
TestDisplayItem(content2, kBackgroundType),
TestDisplayItem(content2, kForegroundType),
TestDisplayItem(container2, kForegroundType),
TestDisplayItem(content1, kBackgroundType),
TestDisplayItem(content1, kForegroundType));
markers = GetSubsequenceMarkers(container2);
CHECK(markers);
EXPECT_EQ(0u, markers->start);
EXPECT_EQ(4u, markers->end);
}
TEST_P(PaintControllerTest, UpdateSwapOrderCrossingChunks) {
FakeDisplayItemClient container1("container1",
LayoutRect(100, 100, 100, 100));
FakeDisplayItemClient content1("content1", LayoutRect(100, 100, 50, 200));
FakeDisplayItemClient container2("container2",
LayoutRect(100, 200, 100, 100));
FakeDisplayItemClient content2("content2", LayoutRect(100, 200, 50, 200));
GraphicsContext context(GetPaintController());
PaintChunkProperties container1_properties =
testing::DefaultPaintChunkProperties();
PaintChunkProperties container2_properties =
testing::DefaultPaintChunkProperties();
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(container1, kBackgroundType);
container1_properties.property_tree_state.SetEffect(
CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5).get());
GetPaintController().UpdateCurrentPaintChunkProperties(
id, container1_properties);
}
DrawRect(context, container1, kBackgroundType,
FloatRect(100, 100, 100, 100));
DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
}
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(container2, kBackgroundType);
container2_properties.property_tree_state.SetEffect(
CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5).get());
GetPaintController().UpdateCurrentPaintChunkProperties(
id, container2_properties);
}
DrawRect(context, container2, kBackgroundType,
FloatRect(100, 200, 100, 100));
DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
}
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 4,
TestDisplayItem(container1, kBackgroundType),
TestDisplayItem(content1, kBackgroundType),
TestDisplayItem(container2, kBackgroundType),
TestDisplayItem(content2, kBackgroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(2u, GetPaintController().PaintChunks().size());
EXPECT_EQ(PaintChunk::Id(container1, kBackgroundType),
GetPaintController().PaintChunks()[0].id);
EXPECT_EQ(PaintChunk::Id(container2, kBackgroundType),
GetPaintController().PaintChunks()[1].id);
// Raster invalidation for the whole chunks will be issued during
// PaintArtifactCompositor::Update().
EXPECT_TRUE(GetPaintController()
.PaintChunks()[0]
.raster_invalidation_rects.IsEmpty());
EXPECT_TRUE(GetPaintController()
.PaintChunks()[1]
.raster_invalidation_rects.IsEmpty());
}
// Move content2 into container1, without invalidation.
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(container1, kBackgroundType);
GetPaintController().UpdateCurrentPaintChunkProperties(
id, container1_properties);
}
DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100));
DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(container2, kBackgroundType);
GetPaintController().UpdateCurrentPaintChunkProperties(
id, container2_properties);
}
DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100));
EXPECT_EQ(4, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(3, NumSequentialMatches());
EXPECT_EQ(1, NumOutOfOrderMatches());
EXPECT_EQ(1, NumIndexedItems());
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 4,
TestDisplayItem(container1, kBackgroundType),
TestDisplayItem(content1, kBackgroundType),
TestDisplayItem(content2, kBackgroundType),
TestDisplayItem(container2, kBackgroundType));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(2u, GetPaintController().PaintChunks().size());
EXPECT_EQ(PaintChunk::Id(container1, kBackgroundType),
GetPaintController().PaintChunks()[0].id);
EXPECT_EQ(PaintChunk::Id(container2, kBackgroundType),
GetPaintController().PaintChunks()[1].id);
// |content2| is invalidated raster on both the old chunk and the new chunk.
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
UnorderedElementsAre(FloatRect(100, 200, 50, 200)));
EXPECT_THAT(GetPaintController().PaintChunks()[1].raster_invalidation_rects,
UnorderedElementsAre(FloatRect(100, 200, 50, 200)));
}
}
TEST_P(PaintControllerTest, OutOfOrderNoCrash) {
FakeDisplayItemClient client("client");
GraphicsContext context(GetPaintController());
const DisplayItem::Type kType1 = DisplayItem::kDrawingFirst;
const DisplayItem::Type kType2 =
static_cast<DisplayItem::Type>(DisplayItem::kDrawingFirst + 1);
const DisplayItem::Type kType3 =
static_cast<DisplayItem::Type>(DisplayItem::kDrawingFirst + 2);
const DisplayItem::Type kType4 =
static_cast<DisplayItem::Type>(DisplayItem::kDrawingFirst + 3);
InitRootChunk();
DrawRect(context, client, kType1, FloatRect(100, 100, 100, 100));
DrawRect(context, client, kType2, FloatRect(100, 100, 50, 200));
DrawRect(context, client, kType3, FloatRect(100, 100, 50, 200));
DrawRect(context, client, kType4, FloatRect(100, 100, 100, 100));
GetPaintController().CommitNewDisplayItems();
InitRootChunk();
DrawRect(context, client, kType2, FloatRect(100, 100, 50, 200));
DrawRect(context, client, kType3, FloatRect(100, 100, 50, 200));
DrawRect(context, client, kType1, FloatRect(100, 100, 100, 100));
DrawRect(context, client, kType4, FloatRect(100, 100, 100, 100));
GetPaintController().CommitNewDisplayItems();
}
TEST_P(PaintControllerTest, CachedNestedSubsequenceUpdate) {
FakeDisplayItemClient container1("container1",
LayoutRect(100, 100, 100, 100));
FakeDisplayItemClient content1("content1", LayoutRect(100, 100, 50, 200));
FakeDisplayItemClient container2("container2",
LayoutRect(100, 200, 100, 100));
FakeDisplayItemClient content2("content2", LayoutRect(100, 200, 50, 200));
GraphicsContext context(GetPaintController());
PaintChunkProperties container1_background_properties =
testing::DefaultPaintChunkProperties();
PaintChunkProperties content1_properties =
testing::DefaultPaintChunkProperties();
PaintChunkProperties container1_foreground_properties =
testing::DefaultPaintChunkProperties();
PaintChunkProperties container2_background_properties =
testing::DefaultPaintChunkProperties();
PaintChunkProperties content2_properties =
testing::DefaultPaintChunkProperties();
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(container1, kBackgroundType);
container1_background_properties.property_tree_state.SetEffect(
CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5).get());
GetPaintController().UpdateCurrentPaintChunkProperties(
id, container1_background_properties);
}
SubsequenceRecorder r(context, container1);
DrawRect(context, container1, kBackgroundType,
FloatRect(100, 100, 100, 100));
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(content1, kBackgroundType);
content1_properties.property_tree_state.SetEffect(
CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.6)
.get());
GetPaintController().UpdateCurrentPaintChunkProperties(
id, content1_properties);
}
SubsequenceRecorder r(context, content1);
DrawRect(context, content1, kBackgroundType,
FloatRect(100, 100, 50, 200));
DrawRect(context, content1, kForegroundType,
FloatRect(100, 100, 50, 200));
}
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(container1, kForegroundType);
container1_foreground_properties.property_tree_state.SetEffect(
CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5).get());
GetPaintController().UpdateCurrentPaintChunkProperties(
id, container1_foreground_properties);
}
DrawRect(context, container1, kForegroundType,
FloatRect(100, 100, 100, 100));
}
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(container2, kBackgroundType);
container2_background_properties.property_tree_state.SetEffect(
CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.7).get());
GetPaintController().UpdateCurrentPaintChunkProperties(
id, container2_background_properties);
}
SubsequenceRecorder r(context, container2);
DrawRect(context, container2, kBackgroundType,
FloatRect(100, 200, 100, 100));
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(content2, kBackgroundType);
content2_properties.property_tree_state.SetEffect(
CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.8)
.get());
GetPaintController().UpdateCurrentPaintChunkProperties(
id, content2_properties);
}
SubsequenceRecorder r(context, content2);
DrawRect(context, content2, kBackgroundType,
FloatRect(100, 200, 50, 200));
}
}
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
TestDisplayItem(container1, kBackgroundType),
TestDisplayItem(content1, kBackgroundType),
TestDisplayItem(content1, kForegroundType),
TestDisplayItem(container1, kForegroundType),
TestDisplayItem(container2, kBackgroundType),
TestDisplayItem(content2, kBackgroundType));
auto* markers = GetSubsequenceMarkers(container1);
CHECK(markers);
EXPECT_EQ(0u, markers->start);
EXPECT_EQ(4u, markers->end);
markers = GetSubsequenceMarkers(content1);
CHECK(markers);
EXPECT_EQ(1u, markers->start);
EXPECT_EQ(3u, markers->end);
markers = GetSubsequenceMarkers(container2);
CHECK(markers);
EXPECT_EQ(4u, markers->start);
EXPECT_EQ(6u, markers->end);
markers = GetSubsequenceMarkers(content2);
CHECK(markers);
EXPECT_EQ(5u, markers->start);
EXPECT_EQ(6u, markers->end);
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(5u, GetPaintController().PaintChunks().size());
EXPECT_EQ(PaintChunk::Id(container1, kBackgroundType),
GetPaintController().PaintChunks()[0].id);
EXPECT_EQ(PaintChunk::Id(content1, kBackgroundType),
GetPaintController().PaintChunks()[1].id);
EXPECT_EQ(PaintChunk::Id(container1, kForegroundType),
GetPaintController().PaintChunks()[2].id);
EXPECT_EQ(PaintChunk::Id(container2, kBackgroundType),
GetPaintController().PaintChunks()[3].id);
EXPECT_EQ(PaintChunk::Id(content2, kBackgroundType),
GetPaintController().PaintChunks()[4].id);
// Raster invalidation for the whole chunks will be issued during
// PaintArtifactCompositor::Update().
EXPECT_TRUE(GetPaintController()
.PaintChunks()[0]
.raster_invalidation_rects.IsEmpty());
EXPECT_TRUE(GetPaintController()
.PaintChunks()[1]
.raster_invalidation_rects.IsEmpty());
EXPECT_TRUE(GetPaintController()
.PaintChunks()[2]
.raster_invalidation_rects.IsEmpty());
EXPECT_TRUE(GetPaintController()
.PaintChunks()[3]
.raster_invalidation_rects.IsEmpty());
EXPECT_TRUE(GetPaintController()
.PaintChunks()[4]
.raster_invalidation_rects.IsEmpty());
}
// Invalidate container1 but not content1.
container1.SetDisplayItemsUncached();
// Container2 itself now becomes empty (but still has the 'content2' child),
// and chooses not to output subsequence info.
container2.SetDisplayItemsUncached();
content2.SetDisplayItemsUncached();
EXPECT_FALSE(
SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, container2));
EXPECT_FALSE(
SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, content2));
// Content2 now outputs foreground only.
{
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(content2, kForegroundType);
GetPaintController().UpdateCurrentPaintChunkProperties(
id, content2_properties);
}
SubsequenceRecorder r(context, content2);
DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
}
// Repaint container1 with foreground only.
{
EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container1));
SubsequenceRecorder r(context, container1);
// Use cached subsequence of content1.
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
// When under-invalidation-checking is enabled,
// useCachedSubsequenceIfPossible is forced off, and the client is
// expected to create the same painting as in the previous paint.
EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, content1));
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(content1, kBackgroundType);
GetPaintController().UpdateCurrentPaintChunkProperties(
id, content1_properties);
}
SubsequenceRecorder r(context, content1);
DrawRect(context, content1, kBackgroundType,
FloatRect(100, 100, 50, 200));
DrawRect(context, content1, kForegroundType,
FloatRect(100, 100, 50, 200));
} else {
EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, content1));
}
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
PaintChunk::Id id(container1, kForegroundType);
GetPaintController().UpdateCurrentPaintChunkProperties(
id, container1_foreground_properties);
}
DrawRect(context, container1, kForegroundType,
FloatRect(100, 100, 100, 100));
}
EXPECT_EQ(2, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(0, NumSequentialMatches());
EXPECT_EQ(0, NumOutOfOrderMatches());
EXPECT_EQ(0, NumIndexedItems());
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 4,
TestDisplayItem(content2, kForegroundType),
TestDisplayItem(content1, kBackgroundType),
TestDisplayItem(content1, kForegroundType),
TestDisplayItem(container1, kForegroundType));
markers = GetSubsequenceMarkers(content2);
CHECK(markers);
EXPECT_EQ(0u, markers->start);
EXPECT_EQ(1u, markers->end);
markers = GetSubsequenceMarkers(container1);
CHECK(markers);
EXPECT_EQ(1u, markers->start);
EXPECT_EQ(4u, markers->end);
markers = GetSubsequenceMarkers(content1);
CHECK(markers);
EXPECT_EQ(1u, markers->start);
EXPECT_EQ(3u, markers->end);
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(3u, GetPaintController().PaintChunks().size());
EXPECT_EQ(PaintChunk::Id(content2, kForegroundType),
GetPaintController().PaintChunks()[0].id);
EXPECT_EQ(PaintChunk::Id(content1, kBackgroundType),
GetPaintController().PaintChunks()[1].id);
EXPECT_EQ(PaintChunk::Id(container1, kForegroundType),
GetPaintController().PaintChunks()[2].id);
// This is a new chunk. Raster invalidation of the whole chunk will be
// issued during PaintArtifactCompositor::Update().
EXPECT_TRUE(GetPaintController()
.PaintChunks()[0]
.raster_invalidation_rects.IsEmpty());
// This chunk didn't change.
EXPECT_TRUE(GetPaintController()
.PaintChunks()[1]
.raster_invalidation_rects.IsEmpty());
// |container1| is invalidated.
EXPECT_THAT(GetPaintController().PaintChunks()[2].raster_invalidation_rects,
UnorderedElementsAre(FloatRect(100, 100, 100, 100)));
}
}
TEST_P(PaintControllerTest, SkipCache) {
FakeDisplayItemClient multicol("multicol", LayoutRect(100, 100, 200, 200));
FakeDisplayItemClient content("content", LayoutRect(100, 100, 100, 100));
GraphicsContext context(GetPaintController());
InitRootChunk();
FloatRect rect1(100, 100, 50, 50);
FloatRect rect2(150, 100, 50, 50);
FloatRect rect3(200, 100, 50, 50);
DrawRect(context, multicol, kBackgroundType, FloatRect(100, 200, 100, 100));
GetPaintController().BeginSkippingCache();
DrawRect(context, content, kForegroundType, rect1);
DrawRect(context, content, kForegroundType, rect2);
GetPaintController().EndSkippingCache();
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 3,
TestDisplayItem(multicol, kBackgroundType),
TestDisplayItem(content, kForegroundType),
TestDisplayItem(content, kForegroundType));
sk_sp<const PaintRecord> record1 =
static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[1])
.GetPaintRecord();
sk_sp<const PaintRecord> record2 =
static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[2])
.GetPaintRecord();
EXPECT_NE(record1, record2);
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
// Raster invalidation for the whole chunk will be issued during
// PaintArtifactCompositor::Update().
EXPECT_TRUE(GetPaintController()
.PaintChunks()[0]
.raster_invalidation_rects.IsEmpty());
InitRootChunk();
}
// Draw again with nothing invalidated.
EXPECT_TRUE(GetPaintController().ClientCacheIsValid(multicol));
DrawRect(context, multicol, kBackgroundType, FloatRect(100, 200, 100, 100));
GetPaintController().BeginSkippingCache();
DrawRect(context, content, kForegroundType, rect1);
DrawRect(context, content, kForegroundType, rect2);
GetPaintController().EndSkippingCache();
EXPECT_EQ(1, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(1, NumSequentialMatches());
EXPECT_EQ(0, NumOutOfOrderMatches());
EXPECT_EQ(0, NumIndexedItems());
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 3,
TestDisplayItem(multicol, kBackgroundType),
TestDisplayItem(content, kForegroundType),
TestDisplayItem(content, kForegroundType));
EXPECT_NE(record1, static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[1])
.GetPaintRecord());
EXPECT_NE(record2, static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[2])
.GetPaintRecord());
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
// Bounds of |content| (old and new are the same);
UnorderedElementsAre(FloatRect(100, 100, 100, 100)));
InitRootChunk();
}
// Now the multicol becomes 3 columns and repaints.
multicol.SetDisplayItemsUncached();
DrawRect(context, multicol, kBackgroundType, FloatRect(100, 100, 100, 100));
GetPaintController().BeginSkippingCache();
DrawRect(context, content, kForegroundType, rect1);
DrawRect(context, content, kForegroundType, rect2);
DrawRect(context, content, kForegroundType, rect3);
GetPaintController().EndSkippingCache();
// We should repaint everything on invalidation of the scope container.
EXPECT_DISPLAY_LIST(GetPaintController().NewDisplayItemList(), 4,
TestDisplayItem(multicol, kBackgroundType),
TestDisplayItem(content, kForegroundType),
TestDisplayItem(content, kForegroundType),
TestDisplayItem(content, kForegroundType));
EXPECT_NE(record1, static_cast<const DrawingDisplayItem&>(
GetPaintController().NewDisplayItemList()[1])
.GetPaintRecord());
EXPECT_NE(record2, static_cast<const DrawingDisplayItem&>(
GetPaintController().NewDisplayItemList()[2])
.GetPaintRecord());
GetPaintController().CommitNewDisplayItems();
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
UnorderedElementsAre(
// Bounds of |multicol| (old and new are the same);
FloatRect(100, 100, 200, 200),
// Bounds of |content| (old and new are the same);
FloatRect(100, 100, 100, 100)));
}
}
TEST_P(PaintControllerTest, PartialSkipCache) {
FakeDisplayItemClient content("content");
GraphicsContext context(GetPaintController());
FloatRect rect1(100, 100, 50, 50);
FloatRect rect2(150, 100, 50, 50);
FloatRect rect3(200, 100, 50, 50);
InitRootChunk();
DrawRect(context, content, kBackgroundType, rect1);
GetPaintController().BeginSkippingCache();
DrawRect(context, content, kForegroundType, rect2);
GetPaintController().EndSkippingCache();
DrawRect(context, content, kForegroundType, rect3);
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 3,
TestDisplayItem(content, kBackgroundType),
TestDisplayItem(content, kForegroundType),
TestDisplayItem(content, kForegroundType));
sk_sp<const PaintRecord> record0 =
static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[0])
.GetPaintRecord();
sk_sp<const PaintRecord> record1 =
static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[1])
.GetPaintRecord();
sk_sp<const PaintRecord> record2 =
static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[2])
.GetPaintRecord();
EXPECT_NE(record1, record2);
// Content's cache is invalid because it has display items skipped cache.
EXPECT_FALSE(GetPaintController().ClientCacheIsValid(content));
EXPECT_EQ(PaintInvalidationReason::kFull,
content.GetPaintInvalidationReason());
InitRootChunk();
// Draw again with nothing invalidated.
DrawRect(context, content, kBackgroundType, rect1);
GetPaintController().BeginSkippingCache();
DrawRect(context, content, kForegroundType, rect2);
GetPaintController().EndSkippingCache();
DrawRect(context, content, kForegroundType, rect3);
EXPECT_EQ(0, NumCachedNewItems());
#if DCHECK_IS_ON()
EXPECT_EQ(0, NumSequentialMatches());
EXPECT_EQ(0, NumOutOfOrderMatches());
EXPECT_EQ(0, NumIndexedItems());
#endif
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 3,
TestDisplayItem(content, kBackgroundType),
TestDisplayItem(content, kForegroundType),
TestDisplayItem(content, kForegroundType));
EXPECT_NE(record0, static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[0])
.GetPaintRecord());
EXPECT_NE(record1, static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[1])
.GetPaintRecord());
EXPECT_NE(record2, static_cast<const DrawingDisplayItem&>(
GetPaintController().GetDisplayItemList()[2])
.GetPaintRecord());
}
TEST_P(PaintControllerTest, OptimizeNoopPairs) {
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
return;
FakeDisplayItemClient first("first");
FakeDisplayItemClient second("second");
FakeDisplayItemClient third("third");
GraphicsContext context(GetPaintController());
DrawRect(context, first, kBackgroundType, FloatRect(0, 0, 100, 100));
{
ClipPathRecorder clip_recorder(context, second, Path());
DrawRect(context, second, kBackgroundType, FloatRect(0, 0, 100, 100));
}
DrawRect(context, third, kBackgroundType, FloatRect(0, 0, 100, 100));
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 5,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(second, DisplayItem::kBeginClipPath),
TestDisplayItem(second, kBackgroundType),
TestDisplayItem(second, DisplayItem::kEndClipPath),
TestDisplayItem(third, kBackgroundType));
DrawRect(context, first, kBackgroundType, FloatRect(0, 0, 100, 100));
{
ClipRecorder clip_recorder(context, second, kClipType, IntRect(1, 1, 2, 2));
// Do not draw anything for second.
}
DrawRect(context, third, kBackgroundType, FloatRect(0, 0, 100, 100));
GetPaintController().CommitNewDisplayItems();
// Empty clips should have been optimized out.
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(third, kBackgroundType));
second.SetDisplayItemsUncached();
DrawRect(context, first, kBackgroundType, FloatRect(0, 0, 100, 100));
{
ClipRecorder clip_recorder(context, second, kClipType, IntRect(1, 1, 2, 2));
{
ClipPathRecorder clip_path_recorder(context, second, Path());
// Do not draw anything for second.
}
}
DrawRect(context, third, kBackgroundType, FloatRect(0, 0, 100, 100));
GetPaintController().CommitNewDisplayItems();
// Empty clips should have been optimized out.
EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
TestDisplayItem(first, kBackgroundType),
TestDisplayItem(third, kBackgroundType));
}
TEST_P(PaintControllerTest, SmallPaintControllerHasOnePaintChunk) {
ScopedSlimmingPaintV2ForTest enable_s_pv2(true);
FakeDisplayItemClient client("test client");
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
GetPaintController().UpdateCurrentPaintChunkProperties(
WTF::nullopt, testing::DefaultPaintChunkProperties());
}
GraphicsContext context(GetPaintController());
DrawRect(context, client, kBackgroundType, FloatRect(0, 0, 100, 100));
GetPaintController().CommitNewDisplayItems();
const auto& paint_chunks = GetPaintController().PaintChunks();
ASSERT_EQ(1u, paint_chunks.size());
EXPECT_EQ(0u, paint_chunks[0].begin_index);
EXPECT_EQ(1u, paint_chunks[0].end_index);
}
void DrawPath(GraphicsContext& context,
DisplayItemClient& client,
DisplayItem::Type type,
unsigned count) {
if (DrawingRecorder::UseCachedDrawingIfPossible(context, client, type))
return;
DrawingRecorder recorder(context, client, type);
SkPath path;
path.moveTo(0, 0);
path.lineTo(0, 100);
path.lineTo(50, 50);
path.lineTo(100, 100);
path.lineTo(100, 0);
path.close();
PaintFlags flags;
flags.setAntiAlias(true);
for (unsigned i = 0; i < count; i++)
context.DrawPath(path, flags);
}
TEST_P(PaintControllerTest, BeginAndEndFrame) {
class FakeFrame {};
// PaintController should have one null frame in the stack since beginning.
GetPaintController().SetFirstPainted();
FrameFirstPaint result = GetPaintController().EndFrame(nullptr);
EXPECT_TRUE(result.first_painted);
EXPECT_FALSE(result.text_painted);
EXPECT_FALSE(result.image_painted);
// Readd the null frame.
GetPaintController().BeginFrame(nullptr);
std::unique_ptr<FakeFrame> frame1(new FakeFrame);
GetPaintController().BeginFrame(frame1.get());
GetPaintController().SetFirstPainted();
GetPaintController().SetTextPainted();
GetPaintController().SetImagePainted();
result = GetPaintController().EndFrame(frame1.get());
EXPECT_TRUE(result.first_painted);
EXPECT_TRUE(result.text_painted);
EXPECT_TRUE(result.image_painted);
std::unique_ptr<FakeFrame> frame2(new FakeFrame);
GetPaintController().BeginFrame(frame2.get());
GetPaintController().SetFirstPainted();
std::unique_ptr<FakeFrame> frame3(new FakeFrame);
GetPaintController().BeginFrame(frame3.get());
GetPaintController().SetTextPainted();
GetPaintController().SetImagePainted();
result = GetPaintController().EndFrame(frame3.get());
EXPECT_FALSE(result.first_painted);
EXPECT_TRUE(result.text_painted);
EXPECT_TRUE(result.image_painted);
result = GetPaintController().EndFrame(frame2.get());
EXPECT_TRUE(result.first_painted);
EXPECT_FALSE(result.text_painted);
EXPECT_FALSE(result.image_painted);
}
TEST_P(PaintControllerTest, PartialInvalidation) {
if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
return;
FakeDisplayItemClient client("client", LayoutRect(100, 100, 300, 300));
GraphicsContext context(GetPaintController());
// Test partial rect invalidation in a new chunk.
InitRootChunk();
client.SetPartialInvalidationRect(LayoutRect(200, 200, 100, 100));
DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 300, 300));
GetPaintController().CommitNewDisplayItems();
ASSERT_EQ(1u, GetPaintController().PaintChunks().size());
// Raster invalidation for the whole new chunk will be issued during
// PaintArtifactCompositor::Update().
EXPECT_TRUE(GetPaintController()
.PaintChunks()[0]
.raster_invalidation_rects.IsEmpty());
EXPECT_EQ(LayoutRect(), client.PartialInvalidationRect());
// Test partial rect invalidation without other invalidations.
InitRootChunk();
client.SetPartialInvalidationRect(LayoutRect(150, 160, 170, 180));
DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 300, 300));
GetPaintController().CommitNewDisplayItems();
ASSERT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
// Partial invalidation.
UnorderedElementsAre(FloatRect(150, 160, 170, 180)));
EXPECT_EQ(LayoutRect(), client.PartialInvalidationRect());
// Test partial rect invalidation with full invalidation.
InitRootChunk();
client.SetPartialInvalidationRect(LayoutRect(150, 160, 170, 180));
client.SetDisplayItemsUncached();
DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 300, 300));
GetPaintController().CommitNewDisplayItems();
ASSERT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
// Partial invalidation is shadowed by full invalidation.
UnorderedElementsAre(FloatRect(100, 100, 300, 300)));
EXPECT_EQ(LayoutRect(), client.PartialInvalidationRect());
// Test partial rect invalidation with incremental invalidation.
InitRootChunk();
client.SetPartialInvalidationRect(LayoutRect(150, 160, 170, 180));
client.SetVisualRect(LayoutRect(100, 100, 300, 400));
DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 300, 400));
GetPaintController().CommitNewDisplayItems();
ASSERT_EQ(1u, GetPaintController().PaintChunks().size());
EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
// Both partial invalidation and incremental invalidation.
UnorderedElementsAre(FloatRect(100, 400, 300, 100),
FloatRect(150, 160, 170, 180)));
EXPECT_EQ(LayoutRect(), client.PartialInvalidationRect());
}
TEST_P(PaintControllerTest, InvalidateAll) {
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
EXPECT_TRUE(GetPaintController().CacheIsAllInvalid());
GetPaintController().CommitNewDisplayItems();
EXPECT_TRUE(GetPaintController().GetPaintArtifact().IsEmpty());
EXPECT_FALSE(GetPaintController().CacheIsAllInvalid());
InvalidateAll();
EXPECT_TRUE(GetPaintController().CacheIsAllInvalid());
GetPaintController().CommitNewDisplayItems();
EXPECT_TRUE(GetPaintController().GetPaintArtifact().IsEmpty());
EXPECT_FALSE(GetPaintController().CacheIsAllInvalid());
FakeDisplayItemClient client("client", LayoutRect(1, 2, 3, 4));
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, client, kBackgroundType, FloatRect(1, 2, 3, 4));
GetPaintController().CommitNewDisplayItems();
EXPECT_FALSE(GetPaintController().GetPaintArtifact().IsEmpty());
EXPECT_FALSE(GetPaintController().CacheIsAllInvalid());
InvalidateAll();
EXPECT_TRUE(GetPaintController().GetPaintArtifact().IsEmpty());
EXPECT_TRUE(GetPaintController().CacheIsAllInvalid());
}
// Death tests don't work properly on Android.
#if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
class PaintControllerUnderInvalidationTest
: public PaintControllerTestBase,
private ScopedPaintUnderInvalidationCheckingForTest {
public:
PaintControllerUnderInvalidationTest()
: PaintControllerTestBase(),
ScopedPaintUnderInvalidationCheckingForTest(true) {}
protected:
void SetUp() override {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
}
void TestChangeDrawing() {
FakeDisplayItemClient first("first");
GraphicsContext context(GetPaintController());
InitRootChunk();
first.SetVisualRect(LayoutRect(100, 100, 300, 300));
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
GetPaintController().CommitNewDisplayItems();
first.SetVisualRect(LayoutRect(200, 200, 300, 300));
DrawRect(context, first, kBackgroundType, FloatRect(200, 200, 300, 300));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
GetPaintController().CommitNewDisplayItems();
}
void TestMoreDrawing() {
FakeDisplayItemClient first("first");
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
GetPaintController().CommitNewDisplayItems();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
GetPaintController().CommitNewDisplayItems();
}
void TestLessDrawing() {
FakeDisplayItemClient first("first");
GraphicsContext context(GetPaintController());
InitRootChunk();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
GetPaintController().CommitNewDisplayItems();
InitRootChunk();
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
GetPaintController().CommitNewDisplayItems();
}
void TestNoopPairsInSubsequence() {
EXPECT_FALSE(GetPaintController().LastDisplayItemIsSubsequenceEnd());
FakeDisplayItemClient container("container");
GraphicsContext context(GetPaintController());
InitRootChunk();
{
SubsequenceRecorder r(context, container);
DrawRect(context, container, kBackgroundType,
FloatRect(100, 100, 100, 100));
}
GetPaintController().CommitNewDisplayItems();
InitRootChunk();
EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container));
{
// Generate some no-op pairs which should not affect under-invalidation
// checking.
ClipRecorder r1(context, container, kClipType, IntRect(1, 1, 9, 9));
ClipRecorder r2(context, container, kClipType, IntRect(1, 1, 2, 2));
ClipRecorder r3(context, container, kClipType, IntRect(1, 1, 3, 3));
ClipPathRecorder r4(context, container, Path());
}
{
EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container));
SubsequenceRecorder r(context, container);
DrawRect(context, container, kBackgroundType,
FloatRect(100, 100, 100, 100));
}
EXPECT_TRUE(GetPaintController().LastDisplayItemIsSubsequenceEnd());
GetPaintController().CommitNewDisplayItems();
}
void TestChangeDrawingInSubsequence() {
FakeDisplayItemClient first("first");
GraphicsContext context(GetPaintController());
{
SubsequenceRecorder r(context, first);
first.SetVisualRect(LayoutRect(100, 100, 300, 300));
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
{
EXPECT_FALSE(
SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, first));
SubsequenceRecorder r(context, first);
first.SetVisualRect(LayoutRect(200, 200, 300, 300));
DrawRect(context, first, kBackgroundType, FloatRect(200, 200, 300, 300));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
}
void TestMoreDrawingInSubsequence() {
FakeDisplayItemClient first("first");
GraphicsContext context(GetPaintController());
{
SubsequenceRecorder r(context, first);
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
{
EXPECT_FALSE(
SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, first));
SubsequenceRecorder r(context, first);
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
}
void TestLessDrawingInSubsequence() {
FakeDisplayItemClient first("first");
GraphicsContext context(GetPaintController());
{
SubsequenceRecorder r(context, first);
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
{
EXPECT_FALSE(
SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, first));
SubsequenceRecorder r(context, first);
DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
}
void TestChangeNonDrawingInSubsequence() {
FakeDisplayItemClient container("container");
FakeDisplayItemClient content("content");
GraphicsContext context(GetPaintController());
{
SubsequenceRecorder r(context, container);
{ ClipPathRecorder clip_path_recorder(context, container, Path()); }
ClipRecorder clip(context, container, kClipType, IntRect(1, 1, 9, 9));
DrawRect(context, content, kBackgroundType,
FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
{
EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container));
SubsequenceRecorder r(context, container);
{ ClipPathRecorder clip_path_recorder(context, container, Path()); }
ClipRecorder clip(context, container, kClipType, IntRect(1, 1, 30, 30));
DrawRect(context, content, kBackgroundType,
FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
}
void TestInvalidationInSubsequence() {
FakeDisplayItemClient container("container");
FakeDisplayItemClient content("content");
GraphicsContext context(GetPaintController());
InitRootChunk();
{
SubsequenceRecorder r(context, container);
DrawRect(context, content, kBackgroundType,
FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
content.SetDisplayItemsUncached();
InitRootChunk();
// Leave container not invalidated.
{
EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container));
SubsequenceRecorder r(context, container);
DrawRect(context, content, kBackgroundType,
FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
}
void TestSubsequenceBecomesEmpty() {
FakeDisplayItemClient target("target");
GraphicsContext context(GetPaintController());
{
SubsequenceRecorder r(context, target);
DrawRect(context, target, kBackgroundType, FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
{
EXPECT_FALSE(
SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, target));
SubsequenceRecorder r(context, target);
}
GetPaintController().CommitNewDisplayItems();
}
};
TEST_F(PaintControllerUnderInvalidationTest, ChangeDrawing) {
EXPECT_DEATH(TestChangeDrawing(), "");
}
TEST_F(PaintControllerUnderInvalidationTest, MoreDrawing) {
EXPECT_DEATH(TestMoreDrawing(), "");
}
TEST_F(PaintControllerUnderInvalidationTest, LessDrawing) {
// We don't detect under-invalidation in this case, and PaintController can
// also handle the case gracefully. However, less drawing at one time often
// means more-drawing at another time, so eventually we'll detect such
// under-invalidations.
TestLessDrawing();
}
TEST_F(PaintControllerUnderInvalidationTest, NoopPairsInSubsequence) {
// This should not die.
TestNoopPairsInSubsequence();
}
TEST_F(PaintControllerUnderInvalidationTest, ChangeDrawingInSubsequence) {
EXPECT_DEATH(TestChangeDrawingInSubsequence(), "");
}
TEST_F(PaintControllerUnderInvalidationTest, MoreDrawingInSubsequence) {
EXPECT_DEATH(TestMoreDrawingInSubsequence(), "");
}
TEST_F(PaintControllerUnderInvalidationTest, LessDrawingInSubsequence) {
// We allow invalidated display item clients as long as they would produce the
// same display items. The cases of changed display items are tested by other
// test cases.
EXPECT_DEATH(TestLessDrawingInSubsequence(), "");
}
TEST_F(PaintControllerUnderInvalidationTest, ChangeNonDrawingInSubsequence) {
if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
EXPECT_DEATH(TestChangeNonDrawingInSubsequence(), "");
}
TEST_F(PaintControllerUnderInvalidationTest, InvalidationInSubsequence) {
// We allow invalidated display item clients as long as they would produce the
// same display items. The cases of changed display items are tested by other
// test cases.
TestInvalidationInSubsequence();
}
TEST_F(PaintControllerUnderInvalidationTest, SubsequenceBecomesEmpty) {
EXPECT_DEATH(TestSubsequenceBecomesEmpty(), "");
}
TEST_F(PaintControllerUnderInvalidationTest, SkipCacheInSubsequence) {
FakeDisplayItemClient container("container");
FakeDisplayItemClient content("content");
GraphicsContext context(GetPaintController());
InitRootChunk();
{
SubsequenceRecorder r(context, container);
{
DisplayItemCacheSkipper cache_skipper(context);
DrawRect(context, content, kBackgroundType,
FloatRect(100, 100, 300, 300));
}
DrawRect(context, content, kForegroundType, FloatRect(200, 200, 400, 400));
}
GetPaintController().CommitNewDisplayItems();
InitRootChunk();
{
EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container));
SubsequenceRecorder r(context, container);
{
DisplayItemCacheSkipper cache_skipper(context);
DrawRect(context, content, kBackgroundType,
FloatRect(200, 200, 400, 400));
}
DrawRect(context, content, kForegroundType, FloatRect(200, 200, 400, 400));
}
GetPaintController().CommitNewDisplayItems();
}
TEST_F(PaintControllerUnderInvalidationTest,
EmptySubsequenceInCachedSubsequence) {
FakeDisplayItemClient container("container");
FakeDisplayItemClient content("content");
GraphicsContext context(GetPaintController());
InitRootChunk();
{
SubsequenceRecorder r(context, container);
DrawRect(context, container, kBackgroundType,
FloatRect(100, 100, 300, 300));
{ SubsequenceRecorder r1(context, content); }
DrawRect(context, container, kForegroundType,
FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
InitRootChunk();
{
EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
context, container));
SubsequenceRecorder r(context, container);
DrawRect(context, container, kBackgroundType,
FloatRect(100, 100, 300, 300));
EXPECT_FALSE(
SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, content));
{ SubsequenceRecorder r1(context, content); }
DrawRect(context, container, kForegroundType,
FloatRect(100, 100, 300, 300));
}
GetPaintController().CommitNewDisplayItems();
}
TEST_F(PaintControllerUnderInvalidationTest,
PairAfterNoopPairInCachedSubsequence) {
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
return;
FakeDisplayItemClient client("client");
GraphicsContext context(GetPaintController());
{
SubsequenceRecorder subsequence_recorder(context, client);
{
ClipRecorder clip_recorder(context, client, kClipType,
IntRect(100, 100, 50, 50));
}
{
ClipRecorder clip_recorder(context, client, kClipType,
IntRect(100, 100, 50, 50));
DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 200, 200));
}
}
GetPaintController().CommitNewDisplayItems();
EXPECT_DISPLAY_LIST(
GetPaintController().GetDisplayItemList(), 3,
TestDisplayItem(client, kClipType),
TestDisplayItem(client, kBackgroundType),
TestDisplayItem(client, DisplayItem::ClipTypeToEndClipType(kClipType)));
{
EXPECT_FALSE(
SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, client));
SubsequenceRecorder subsequence_recorder(context, client);
{
ClipRecorder clip_recorder(context, client, kClipType,
IntRect(100, 100, 50, 50));
}
{
ClipRecorder clip_recorder(context, client, kClipType,
IntRect(100, 100, 50, 50));
DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 200, 200));
}
}
GetPaintController().CommitNewDisplayItems();
}
#endif // defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
} // namespace blink