blob: 6ae3c0a00367fc14e73c0c78f5489c25a71401d0 [file] [log] [blame]
// Copyright 2018 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 <memory>
#include <utility>
#include "cc/tiles/paint_worklet_image_cache.h"
#include "cc/paint/draw_image.h"
#include "cc/raster/paint_worklet_image_provider.h"
#include "cc/test/skia_common.h"
#include "cc/test/test_paint_worklet_input.h"
#include "cc/test/test_paint_worklet_layer_painter.h"
#include "cc/test/test_tile_task_runner.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
namespace {
class TestPaintWorkletImageCache : public PaintWorkletImageCache {
public:
TestPaintWorkletImageCache() {}
};
SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) {
SkMatrix matrix;
matrix.setScale(scale.width(), scale.height());
if (!is_decomposable) {
// Perspective is not decomposable, add it.
matrix[SkMatrix::kMPersp0] = 0.1f;
}
return matrix;
}
PaintImage CreatePaintImage(int width, int height) {
scoped_refptr<TestPaintWorkletInput> input =
base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(width, height));
return CreatePaintWorkletPaintImage(input);
}
scoped_refptr<TileTask> GetTaskForPaintWorkletImage(
const PaintImage& paint_image,
TestPaintWorkletImageCache* cache) {
DrawImage draw_image(
paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
kNone_SkFilterQuality, CreateMatrix(SkSize::Make(1.f, 1.f), true),
PaintImage::kDefaultFrameIndex);
return cache->GetTaskForPaintWorkletImage(draw_image);
}
void TestPaintRecord(const PaintRecord* record) {
EXPECT_EQ(record->total_op_count(), 1u);
// GetOpAtForTesting check whether the type is the same as DrawImageOp or not.
// If not, it returns a nullptr.
auto* paint_op = record->GetOpAtForTesting<DrawImageOp>(0);
EXPECT_TRUE(paint_op);
}
TEST(PaintWorkletImageCacheTest, GetTaskForImage) {
TestPaintWorkletImageCache cache;
std::unique_ptr<TestPaintWorkletLayerPainter> painter =
std::make_unique<TestPaintWorkletLayerPainter>();
cache.SetPaintWorkletLayerPainter(std::move(painter));
PaintImage paint_image = CreatePaintImage(100, 100);
scoped_refptr<TileTask> task =
GetTaskForPaintWorkletImage(paint_image, &cache);
EXPECT_TRUE(task);
PaintWorkletImageProvider provider(&cache);
TestTileTaskRunner::ProcessTask(task.get());
{
ImageProvider::ScopedResult result =
provider.GetPaintRecordResult(paint_image.paint_worklet_input());
EXPECT_TRUE(result.paint_record());
TestPaintRecord(result.paint_record());
base::flat_map<PaintWorkletInput*,
PaintWorkletImageCache::PaintWorkletImageCacheValue>
records = cache.GetRecordsForTest();
// Test the ref count.
EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u);
}
base::flat_map<PaintWorkletInput*,
PaintWorkletImageCache::PaintWorkletImageCacheValue>
records = cache.GetRecordsForTest();
// Test the ref count, which should have been decremented when the result
// goes out of the scope.
EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 0u);
{
ImageProvider::ScopedResult result =
provider.GetPaintRecordResult(paint_image.paint_worklet_input());
base::flat_map<PaintWorkletInput*,
PaintWorkletImageCache::PaintWorkletImageCacheValue>
records = cache.GetRecordsForTest();
// Test the ref count.
EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u);
ImageProvider::ScopedResult moved_result = std::move(result);
EXPECT_FALSE(result);
EXPECT_TRUE(moved_result.paint_record());
TestPaintRecord(moved_result.paint_record());
// Once moved, the ref count from |result| should have been transferred to
// |moved_result|, so there should be only one un-ref when they both go out
// of scope.
EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u);
}
EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 0u);
}
TEST(PaintWorkletImageCacheTest, EntryWithNonZeroRefCountNotPurged) {
TestPaintWorkletImageCache cache;
std::unique_ptr<TestPaintWorkletLayerPainter> painter =
std::make_unique<TestPaintWorkletLayerPainter>();
cache.SetPaintWorkletLayerPainter(std::move(painter));
PaintImage paint_image = CreatePaintImage(100, 100);
scoped_refptr<TileTask> task =
GetTaskForPaintWorkletImage(paint_image, &cache);
EXPECT_TRUE(task);
TestTileTaskRunner::ProcessTask(task.get());
PaintWorkletImageProvider provider(&cache);
ImageProvider::ScopedResult result =
provider.GetPaintRecordResult(paint_image.paint_worklet_input());
base::flat_map<PaintWorkletInput*,
PaintWorkletImageCache::PaintWorkletImageCacheValue>
records = cache.GetRecordsForTest();
EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u);
cache.NotifyDidPrepareTiles();
cache.NotifyDidPrepareTiles();
cache.NotifyDidPrepareTiles();
records = cache.GetRecordsForTest();
EXPECT_EQ(records.size(), 1u);
}
TEST(PaintWorkletImageCacheTest, MultipleRecordsInCache) {
TestPaintWorkletImageCache cache;
std::unique_ptr<TestPaintWorkletLayerPainter> painter =
std::make_unique<TestPaintWorkletLayerPainter>();
cache.SetPaintWorkletLayerPainter(std::move(painter));
PaintImage paint_image1 = CreatePaintImage(100, 100);
scoped_refptr<TileTask> task1 =
GetTaskForPaintWorkletImage(paint_image1, &cache);
EXPECT_TRUE(task1);
PaintImage paint_image2 = CreatePaintImage(200, 200);
scoped_refptr<TileTask> task2 =
GetTaskForPaintWorkletImage(paint_image2, &cache);
EXPECT_TRUE(task2);
TestTileTaskRunner::ProcessTask(task1.get());
TestTileTaskRunner::ProcessTask(task2.get());
base::flat_map<PaintWorkletInput*,
PaintWorkletImageCache::PaintWorkletImageCacheValue>
records = cache.GetRecordsForTest();
EXPECT_EQ(records.size(), 2u);
cache.SetNumOfFramesToPurgeCacheEntryForTest(2u);
PaintRecord* record1 =
records[paint_image1.paint_worklet_input()].record.get();
EXPECT_TRUE(record1);
// Test the |num_of_frames_not_accessed| for this cache entry.
EXPECT_EQ(
records[paint_image1.paint_worklet_input()].num_of_frames_not_accessed,
0u);
TestPaintRecord(record1);
PaintRecord* record2 =
records[paint_image2.paint_worklet_input()].record.get();
EXPECT_TRUE(record2);
// Test the |num_of_frames_not_accessed| for this cache entry.
EXPECT_EQ(
records[paint_image2.paint_worklet_input()].num_of_frames_not_accessed,
0u);
TestPaintRecord(record2);
// NotifyDidPrepareTiles is called by TileManager::PrepareTiles() which is
// called at each new impl frame. Here we test that a paint record with
// |num_of_frames_not_accessed| >= 2 is purged from the cache.
cache.NotifyDidPrepareTiles();
records = cache.GetRecordsForTest();
EXPECT_EQ(
records[paint_image1.paint_worklet_input()].num_of_frames_not_accessed,
1u);
EXPECT_EQ(
records[paint_image2.paint_worklet_input()].num_of_frames_not_accessed,
1u);
std::pair<sk_sp<PaintRecord>, base::OnceCallback<void()>> pair =
cache.GetPaintRecordAndRef(paint_image1.paint_worklet_input());
// Run the callback to decrement the ref count.
std::move(pair.second).Run();
records = cache.GetRecordsForTest();
EXPECT_EQ(
records[paint_image1.paint_worklet_input()].num_of_frames_not_accessed,
0u);
cache.NotifyDidPrepareTiles();
cache.NotifyDidPrepareTiles();
records = cache.GetRecordsForTest();
// The cache entry for paint_image2 should have been purged because it was
// never accessed/updated in the last 2 frames.
EXPECT_EQ(records.size(), 1u);
EXPECT_EQ(
records[paint_image1.paint_worklet_input()].num_of_frames_not_accessed,
2u);
}
// This test ensures that if an entry already exist, then the PaintImageInTask
// will not replace it with a new entry and reset its ref count.
TEST(PaintWorkletImageCacheTest, CacheEntryLookup) {
TestPaintWorkletImageCache cache;
std::unique_ptr<TestPaintWorkletLayerPainter> painter =
std::make_unique<TestPaintWorkletLayerPainter>();
cache.SetPaintWorkletLayerPainter(std::move(painter));
PaintImage paint_image = CreatePaintImage(100, 100);
scoped_refptr<TileTask> task =
GetTaskForPaintWorkletImage(paint_image, &cache);
EXPECT_TRUE(task);
PaintWorkletImageProvider provider(&cache);
TestTileTaskRunner::ProcessTask(task.get());
{
ImageProvider::ScopedResult result =
provider.GetPaintRecordResult(paint_image.paint_worklet_input());
EXPECT_TRUE(result.paint_record());
TestPaintRecord(result.paint_record());
base::flat_map<PaintWorkletInput*,
PaintWorkletImageCache::PaintWorkletImageCacheValue>
records = cache.GetRecordsForTest();
// Test the ref count.
EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u);
// Create a new task with the same PaintWorkletInput as the previous task.
// Then ProcessTask will invoke PaintWorkletImageCache::PaintImageInTask,
// and it should early exit, without replacing the existing PaintRecord and
// resetting the ref count.
scoped_refptr<TileTask> task_with_the_same_input =
GetTaskForPaintWorkletImage(paint_image, &cache);
EXPECT_TRUE(task);
TestTileTaskRunner::ProcessTask(task_with_the_same_input.get());
EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u);
}
}
TEST(PaintWorkletImageCacheTest, TaskIsNullWhenPainterIsNull) {
TestPaintWorkletImageCache cache;
PaintImage paint_image = CreatePaintImage(100, 100);
scoped_refptr<TileTask> task =
GetTaskForPaintWorkletImage(paint_image, &cache);
EXPECT_EQ(task, nullptr);
}
TEST(PaintWorkletImageCacheTest,
RecordAndCallbackAreEmptyWhenInputWasntPainted) {
TestPaintWorkletImageCache cache;
std::unique_ptr<TestPaintWorkletLayerPainter> painter =
std::make_unique<TestPaintWorkletLayerPainter>();
cache.SetPaintWorkletLayerPainter(std::move(painter));
// We request a record and callback without ever painting the input.
PaintImage paint_image = CreatePaintImage(100, 100);
std::pair<sk_sp<PaintRecord>, base::OnceCallback<void()>> result =
cache.GetPaintRecordAndRef(paint_image.paint_worklet_input());
EXPECT_EQ(result.first->total_op_count(), 0u);
// This is an empty callback, running it should not crash.
std::move(result.second).Run();
}
} // namespace
} // namespace cc