blob: 2791577666b8d9b9b8fc73f7d3a84b02df249b97 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#define FML_USED_ON_EMBEDDER
#include "flutter/flow/layers/display_list_layer.h"
#include "flutter/display_list/dl_builder.h"
#include "flutter/flow/layers/layer_tree.h"
#include "flutter/flow/testing/diff_context_test.h"
#include "flutter/fml/macros.h"
// TODO(zanderso): https://github.com/flutter/flutter/issues/127701
// NOLINTBEGIN(bugprone-unchecked-optional-access)
namespace flutter {
namespace testing {
using DisplayListLayerTest = LayerTest;
#ifndef NDEBUG
TEST_F(DisplayListLayerTest, PaintBeforePrerollInvalidDisplayListDies) {
const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
auto layer = std::make_shared<DisplayListLayer>(
layer_offset, sk_sp<DisplayList>(), false, false);
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), "display_list_");
}
TEST_F(DisplayListLayerTest, PaintBeforePrerollDies) {
const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
DisplayListBuilder builder;
builder.DrawRect(picture_bounds, DlPaint());
auto display_list = builder.Build();
auto layer = std::make_shared<DisplayListLayer>(layer_offset, display_list,
false, false);
EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty());
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"needs_painting\\(context\\)");
}
TEST_F(DisplayListLayerTest, PaintingEmptyLayerDies) {
const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
const SkRect picture_bounds = SkRect::MakeEmpty();
DisplayListBuilder builder;
builder.DrawRect(picture_bounds, DlPaint());
auto display_list = builder.Build();
auto layer = std::make_shared<DisplayListLayer>(layer_offset, display_list,
false, false);
layer->Preroll(preroll_context());
EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty());
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"needs_painting\\(context\\)");
}
TEST_F(DisplayListLayerTest, InvalidDisplayListDies) {
const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
auto layer = std::make_shared<DisplayListLayer>(
layer_offset, sk_sp<DisplayList>(), false, false);
// Crashes reading a nullptr.
EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context()), "");
}
#endif
TEST_F(DisplayListLayerTest, SimpleDisplayList) {
const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);
const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
DisplayListBuilder builder;
builder.DrawRect(picture_bounds, DlPaint());
auto display_list = builder.Build();
auto layer = std::make_shared<DisplayListLayer>(layer_offset, display_list,
false, false);
layer->Preroll(preroll_context());
EXPECT_EQ(layer->paint_bounds(),
picture_bounds.makeOffset(layer_offset.fX, layer_offset.fY));
EXPECT_EQ(layer->display_list(), display_list.get());
EXPECT_TRUE(layer->needs_painting(paint_context()));
layer->Paint(display_list_paint_context());
DisplayListBuilder expected_builder;
/* (DisplayList)layer::Paint */ {
expected_builder.Save();
{
expected_builder.Translate(layer_offset.fX, layer_offset.fY);
expected_builder.DrawDisplayList(display_list);
}
expected_builder.Restore();
}
EXPECT_TRUE(
DisplayListsEQ_Verbose(this->display_list(), expected_builder.Build()));
}
TEST_F(DisplayListLayerTest, CachingDoesNotChangeCullRect) {
const SkPoint layer_offset = SkPoint::Make(10, 10);
DisplayListBuilder builder;
builder.DrawRect({10, 10, 20, 20}, DlPaint());
auto display_list = builder.Build();
auto layer = std::make_shared<DisplayListLayer>(layer_offset, display_list,
true, false);
SkRect original_cull_rect = preroll_context()->state_stack.device_cull_rect();
use_mock_raster_cache();
layer->Preroll(preroll_context());
ASSERT_EQ(preroll_context()->state_stack.device_cull_rect(),
original_cull_rect);
}
TEST_F(DisplayListLayerTest, SimpleDisplayListOpacityInheritance) {
const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);
const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
DisplayListBuilder builder;
builder.DrawRect(picture_bounds, DlPaint());
auto display_list = builder.Build();
auto display_list_layer = std::make_shared<DisplayListLayer>(
layer_offset, display_list, false, false);
EXPECT_TRUE(display_list->can_apply_group_opacity());
auto context = preroll_context();
display_list_layer->Preroll(preroll_context());
EXPECT_EQ(context->renderable_state_flags,
LayerStateStack::kCallerCanApplyOpacity);
int opacity_alpha = 0x7F;
SkScalar opacity = opacity_alpha / 255.0;
SkPoint opacity_offset = SkPoint::Make(10, 10);
auto opacity_layer =
std::make_shared<OpacityLayer>(opacity_alpha, opacity_offset);
opacity_layer->Add(display_list_layer);
opacity_layer->Preroll(context);
EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
DisplayListBuilder child_builder;
child_builder.DrawRect(picture_bounds, DlPaint());
auto child_display_list = child_builder.Build();
DisplayListBuilder expected_builder;
/* opacity_layer::Paint() */ {
expected_builder.Save();
{
expected_builder.Translate(opacity_offset.fX, opacity_offset.fY);
/* display_list_layer::Paint() */ {
expected_builder.Save();
{
expected_builder.Translate(layer_offset.fX, layer_offset.fY);
expected_builder.DrawDisplayList(child_display_list, opacity);
}
expected_builder.Restore();
}
}
expected_builder.Restore();
}
opacity_layer->Paint(display_list_paint_context());
EXPECT_TRUE(
DisplayListsEQ_Verbose(this->display_list(), expected_builder.Build()));
}
TEST_F(DisplayListLayerTest, IncompatibleDisplayListOpacityInheritance) {
const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);
const SkRect picture1_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
const SkRect picture2_bounds = SkRect::MakeLTRB(10.0f, 15.0f, 30.0f, 35.0f);
DisplayListBuilder builder;
builder.DrawRect(picture1_bounds, DlPaint());
builder.DrawRect(picture2_bounds, DlPaint());
auto display_list = builder.Build();
auto display_list_layer = std::make_shared<DisplayListLayer>(
layer_offset, display_list, false, false);
EXPECT_FALSE(display_list->can_apply_group_opacity());
auto context = preroll_context();
display_list_layer->Preroll(preroll_context());
EXPECT_EQ(context->renderable_state_flags, 0);
int opacity_alpha = 0x7F;
SkPoint opacity_offset = SkPoint::Make(10, 10);
auto opacity_layer =
std::make_shared<OpacityLayer>(opacity_alpha, opacity_offset);
opacity_layer->Add(display_list_layer);
opacity_layer->Preroll(context);
EXPECT_FALSE(opacity_layer->children_can_accept_opacity());
DisplayListBuilder child_builder;
child_builder.DrawRect(picture1_bounds, DlPaint());
child_builder.DrawRect(picture2_bounds, DlPaint());
auto child_display_list = child_builder.Build();
auto display_list_bounds = picture1_bounds;
display_list_bounds.join(picture2_bounds);
auto save_layer_bounds =
display_list_bounds.makeOffset(layer_offset.fX, layer_offset.fY);
DisplayListBuilder expected_builder;
/* opacity_layer::Paint() */ {
expected_builder.Save();
{
expected_builder.Translate(opacity_offset.fX, opacity_offset.fY);
expected_builder.SaveLayer(&save_layer_bounds,
&DlPaint().setAlpha(opacity_alpha));
{
/* display_list_layer::Paint() */ {
expected_builder.Save();
{
expected_builder.Translate(layer_offset.fX, layer_offset.fY);
expected_builder.DrawDisplayList(child_display_list);
}
expected_builder.Restore();
}
}
expected_builder.Restore();
}
expected_builder.Restore();
}
opacity_layer->Paint(display_list_paint_context());
EXPECT_TRUE(
DisplayListsEQ_Verbose(this->display_list(), expected_builder.Build()));
}
TEST_F(DisplayListLayerTest, CachedIncompatibleDisplayListOpacityInheritance) {
const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);
const SkRect picture1_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
const SkRect picture2_bounds = SkRect::MakeLTRB(10.0f, 15.0f, 30.0f, 35.0f);
DisplayListBuilder builder;
builder.DrawRect(picture1_bounds, DlPaint());
builder.DrawRect(picture2_bounds, DlPaint());
auto display_list = builder.Build();
auto display_list_layer = std::make_shared<DisplayListLayer>(
layer_offset, display_list, true, false);
EXPECT_FALSE(display_list->can_apply_group_opacity());
use_skia_raster_cache();
auto context = preroll_context();
display_list_layer->Preroll(preroll_context());
EXPECT_EQ(context->renderable_state_flags, 0);
// Pump the DisplayListLayer until it is ready to cache its DL
display_list_layer->Preroll(preroll_context());
display_list_layer->Preroll(preroll_context());
display_list_layer->Preroll(preroll_context());
LayerTree::TryToRasterCache(*preroll_context()->raster_cached_entries,
&paint_context(), false);
int opacity_alpha = 0x7F;
SkPoint opacity_offset = SkPoint::Make(10, 10);
auto opacity_layer =
std::make_shared<OpacityLayer>(opacity_alpha, opacity_offset);
opacity_layer->Add(display_list_layer);
opacity_layer->Preroll(context);
EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
auto display_list_bounds = picture1_bounds;
display_list_bounds.join(picture2_bounds);
auto save_layer_bounds =
display_list_bounds.makeOffset(layer_offset.fX, layer_offset.fY);
save_layer_bounds.roundOut(&save_layer_bounds);
auto opacity_integral_matrix =
RasterCacheUtil::GetIntegralTransCTM(SkMatrix::Translate(opacity_offset));
SkMatrix layer_offset_matrix = opacity_integral_matrix;
layer_offset_matrix.postTranslate(layer_offset.fX, layer_offset.fY);
auto layer_offset_integral_matrix =
RasterCacheUtil::GetIntegralTransCTM(layer_offset_matrix);
DisplayListBuilder expected(SkRect::MakeWH(1000, 1000));
/* opacity_layer::Paint() */ {
expected.Save();
{
expected.Translate(opacity_offset.fX, opacity_offset.fY);
expected.TransformReset();
expected.Transform(opacity_integral_matrix);
/* display_list_layer::Paint() */ {
expected.Save();
{
expected.Translate(layer_offset.fX, layer_offset.fY);
expected.TransformReset();
expected.Transform(layer_offset_integral_matrix);
context->raster_cache->Draw(display_list_layer->caching_key_id(),
expected,
&DlPaint().setAlpha(opacity_alpha));
}
expected.Restore();
}
}
expected.Restore();
}
opacity_layer->Paint(display_list_paint_context());
EXPECT_TRUE(DisplayListsEQ_Verbose(expected.Build(), this->display_list()));
}
TEST_F(DisplayListLayerTest, RasterCachePreservesRTree) {
const SkRect picture1_bounds = SkRect::MakeXYWH(10, 10, 10, 10);
const SkRect picture2_bounds = SkRect::MakeXYWH(15, 15, 10, 10);
DisplayListBuilder builder(true);
builder.DrawRect(picture1_bounds, DlPaint());
builder.DrawRect(picture2_bounds, DlPaint());
auto display_list = builder.Build();
auto display_list_layer = std::make_shared<DisplayListLayer>(
SkPoint::Make(3, 3), display_list, true, false);
use_skia_raster_cache();
auto context = preroll_context();
{
auto mutator = context->state_stack.save();
mutator.transform(SkMatrix::Scale(2.0, 2.0));
display_list_layer->Preroll(preroll_context());
EXPECT_EQ(context->renderable_state_flags, 0);
// Pump the DisplayListLayer until it is ready to cache its DL
display_list_layer->Preroll(preroll_context());
display_list_layer->Preroll(preroll_context());
display_list_layer->Preroll(preroll_context());
LayerTree::TryToRasterCache(*preroll_context()->raster_cached_entries,
&paint_context(), false);
}
DisplayListBuilder expected_root_canvas(true);
expected_root_canvas.Scale(2.0, 2.0);
ASSERT_TRUE(context->raster_cache->Draw(display_list_layer->caching_key_id(),
expected_root_canvas, nullptr,
false));
auto root_canvas_dl = expected_root_canvas.Build();
const auto root_canvas_rects =
root_canvas_dl->rtree()->searchAndConsolidateRects(kGiantRect, true);
std::list<SkRect> root_canvas_rects_expected = {
SkRect::MakeLTRB(26, 26, 56, 56),
};
EXPECT_EQ(root_canvas_rects_expected, root_canvas_rects);
DisplayListBuilder expected_overlay_canvas(true);
expected_overlay_canvas.Scale(2.0, 2.0);
ASSERT_TRUE(context->raster_cache->Draw(display_list_layer->caching_key_id(),
expected_overlay_canvas, nullptr,
true));
auto overlay_canvas_dl = expected_overlay_canvas.Build();
const auto overlay_canvas_rects =
overlay_canvas_dl->rtree()->searchAndConsolidateRects(kGiantRect, true);
// Same bounds as root canvas, but preserves individual rects.
std::list<SkRect> overlay_canvas_rects_expected = {
SkRect::MakeLTRB(26, 26, 46, 36),
SkRect::MakeLTRB(26, 36, 56, 46),
SkRect::MakeLTRB(36, 46, 56, 56),
};
EXPECT_EQ(overlay_canvas_rects_expected, overlay_canvas_rects);
};
using DisplayListLayerDiffTest = DiffContextTest;
TEST_F(DisplayListLayerDiffTest, SimpleDisplayList) {
auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60));
MockLayerTree tree1;
tree1.root()->Add(CreateDisplayListLayer(display_list));
auto damage = DiffLayerTree(tree1, MockLayerTree());
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60));
MockLayerTree tree2;
tree2.root()->Add(CreateDisplayListLayer(display_list));
damage = DiffLayerTree(tree2, tree1);
EXPECT_TRUE(damage.frame_damage.isEmpty());
MockLayerTree tree3;
damage = DiffLayerTree(tree3, tree2);
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60));
}
TEST_F(DisplayListLayerDiffTest, FractionalTranslation) {
auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60));
MockLayerTree tree1;
tree1.root()->Add(
CreateDisplayListLayer(display_list, SkPoint::Make(0.5, 0.5)));
auto damage =
DiffLayerTree(tree1, MockLayerTree(), SkIRect::MakeEmpty(), 0, 0,
/*use_raster_cache=*/false);
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 61, 61));
}
TEST_F(DisplayListLayerDiffTest, FractionalTranslationWithRasterCache) {
auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60));
MockLayerTree tree1;
tree1.root()->Add(
CreateDisplayListLayer(display_list, SkPoint::Make(0.5, 0.5)));
auto damage =
DiffLayerTree(tree1, MockLayerTree(), SkIRect::MakeEmpty(), 0, 0,
/*use_raster_cache=*/true);
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(11, 11, 61, 61));
}
TEST_F(DisplayListLayerDiffTest, DisplayListCompare) {
MockLayerTree tree1;
auto display_list1 =
CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), DlColor::kGreen());
tree1.root()->Add(CreateDisplayListLayer(display_list1));
auto damage = DiffLayerTree(tree1, MockLayerTree());
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60));
MockLayerTree tree2;
// same DL, same offset
auto display_list2 =
CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), DlColor::kGreen());
tree2.root()->Add(CreateDisplayListLayer(display_list2));
damage = DiffLayerTree(tree2, tree1);
EXPECT_EQ(damage.frame_damage, SkIRect::MakeEmpty());
MockLayerTree tree3;
auto display_list3 =
CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), DlColor::kGreen());
// add offset
tree3.root()->Add(
CreateDisplayListLayer(display_list3, SkPoint::Make(10, 10)));
damage = DiffLayerTree(tree3, tree2);
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 70, 70));
MockLayerTree tree4;
// different color
auto display_list4 =
CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), DlColor::kRed());
tree4.root()->Add(
CreateDisplayListLayer(display_list4, SkPoint::Make(10, 10)));
damage = DiffLayerTree(tree4, tree3);
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(20, 20, 70, 70));
}
TEST_F(DisplayListLayerTest, LayerTreeSnapshotsWhenEnabled) {
const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);
const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
DisplayListBuilder builder;
builder.DrawRect(picture_bounds, DlPaint());
auto display_list = builder.Build();
auto layer = std::make_shared<DisplayListLayer>(layer_offset, display_list,
false, false);
layer->Preroll(preroll_context());
enable_leaf_layer_tracing();
layer->Paint(paint_context());
disable_leaf_layer_tracing();
auto& snapshot_store = layer_snapshot_store();
EXPECT_EQ(1u, snapshot_store.Size());
}
TEST_F(DisplayListLayerTest, NoLayerTreeSnapshotsWhenDisabledByDefault) {
const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);
const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
DisplayListBuilder builder;
builder.DrawRect(picture_bounds, DlPaint());
auto display_list = builder.Build();
auto layer = std::make_shared<DisplayListLayer>(layer_offset, display_list,
false, false);
layer->Preroll(preroll_context());
layer->Paint(paint_context());
auto& snapshot_store = layer_snapshot_store();
EXPECT_EQ(0u, snapshot_store.Size());
}
TEST_F(DisplayListLayerTest, DisplayListAccessCountDependsOnVisibility) {
const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);
const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
const SkRect missed_cull_rect = SkRect::MakeLTRB(100, 100, 200, 200);
const SkRect hit_cull_rect = SkRect::MakeLTRB(0, 0, 200, 200);
DisplayListBuilder builder;
builder.DrawRect(picture_bounds, DlPaint());
auto display_list = builder.Build();
auto layer = std::make_shared<DisplayListLayer>(layer_offset, display_list,
true, false);
auto raster_cache_item = layer->raster_cache_item();
use_mock_raster_cache();
// First Preroll the DisplayListLayer a few times where it does not intersect
// the cull rect. No caching progress should occur during this time, the
// access_count should remain 0 because the DisplayList was never "visible".
ASSERT_TRUE(preroll_context()->state_stack.is_empty());
preroll_context()->state_stack.set_preroll_delegate(missed_cull_rect);
for (int i = 0; i < 10; i++) {
preroll_context()->raster_cached_entries->clear();
layer->Preroll(preroll_context());
ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kNone);
ASSERT_TRUE(raster_cache_item->GetId().has_value());
ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount(
raster_cache_item->GetId().value(), SkMatrix::I()),
0);
ASSERT_EQ(preroll_context()->raster_cached_entries->size(), size_t(1));
ASSERT_EQ(preroll_context()->raster_cache->EstimatePictureCacheByteSize(),
size_t(0));
ASSERT_FALSE(raster_cache_item->TryToPrepareRasterCache(paint_context()));
ASSERT_FALSE(raster_cache_item->Draw(paint_context(), nullptr));
}
// Next Preroll the DisplayListLayer once where it does intersect
// the cull rect. No caching progress should occur during this time
// since this is the first frame in which it was visible, but the
// count should start incrementing.
ASSERT_TRUE(preroll_context()->state_stack.is_empty());
preroll_context()->state_stack.set_preroll_delegate(hit_cull_rect);
preroll_context()->raster_cached_entries->clear();
layer->Preroll(preroll_context());
ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kNone);
ASSERT_TRUE(raster_cache_item->GetId().has_value());
ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount(
raster_cache_item->GetId().value(), SkMatrix::I()),
1);
ASSERT_EQ(preroll_context()->raster_cached_entries->size(), size_t(1));
ASSERT_EQ(preroll_context()->raster_cache->EstimatePictureCacheByteSize(),
size_t(0));
ASSERT_FALSE(raster_cache_item->TryToPrepareRasterCache(paint_context()));
ASSERT_FALSE(raster_cache_item->Draw(paint_context(), nullptr));
// Now we can Preroll the DisplayListLayer again with a cull rect that
// it does not intersect and it should continue to count these operations
// even though it is not visible. No actual caching should occur yet,
// even though we will surpass its threshold.
ASSERT_TRUE(preroll_context()->state_stack.is_empty());
preroll_context()->state_stack.set_preroll_delegate(missed_cull_rect);
for (int i = 0; i < 10; i++) {
preroll_context()->raster_cached_entries->clear();
layer->Preroll(preroll_context());
ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kNone);
ASSERT_TRUE(raster_cache_item->GetId().has_value());
ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount(
raster_cache_item->GetId().value(), SkMatrix::I()),
i + 2);
ASSERT_EQ(preroll_context()->raster_cached_entries->size(), size_t(1));
ASSERT_EQ(preroll_context()->raster_cache->EstimatePictureCacheByteSize(),
size_t(0));
ASSERT_FALSE(raster_cache_item->TryToPrepareRasterCache(paint_context()));
ASSERT_FALSE(raster_cache_item->Draw(paint_context(), nullptr));
}
// Finally Preroll the DisplayListLayer again where it does intersect
// the cull rect. Since we should have exhausted our access count
// threshold in the loop above, these operations should result in the
// DisplayList being cached.
ASSERT_TRUE(preroll_context()->state_stack.is_empty());
preroll_context()->state_stack.set_preroll_delegate(hit_cull_rect);
preroll_context()->raster_cached_entries->clear();
layer->Preroll(preroll_context());
ASSERT_EQ(raster_cache_item->cache_state(), RasterCacheItem::kCurrent);
ASSERT_TRUE(raster_cache_item->GetId().has_value());
ASSERT_EQ(preroll_context()->raster_cache->GetAccessCount(
raster_cache_item->GetId().value(), SkMatrix::I()),
12);
ASSERT_EQ(preroll_context()->raster_cached_entries->size(), size_t(1));
ASSERT_EQ(preroll_context()->raster_cache->EstimatePictureCacheByteSize(),
size_t(0));
ASSERT_TRUE(raster_cache_item->TryToPrepareRasterCache(paint_context()));
ASSERT_GT(preroll_context()->raster_cache->EstimatePictureCacheByteSize(),
size_t(0));
ASSERT_TRUE(raster_cache_item->Draw(paint_context(), nullptr));
}
TEST_F(DisplayListLayerTest, OverflowCachedDisplayListOpacityInheritance) {
use_mock_raster_cache();
PrerollContext* context = preroll_context();
int per_frame =
RasterCacheUtil::kDefaultPictureAndDisplayListCacheLimitPerFrame;
int layer_count = per_frame + 1;
SkPoint opacity_offset = {10, 10};
auto opacity_layer = std::make_shared<OpacityLayer>(0.5f, opacity_offset);
std::shared_ptr<DisplayListLayer> layers[layer_count];
for (int i = 0; i < layer_count; i++) {
DisplayListBuilder builder(false);
builder.DrawRect({0, 0, 100, 100}, DlPaint());
builder.DrawRect({50, 50, 100, 100}, DlPaint());
auto display_list = builder.Build();
ASSERT_FALSE(display_list->can_apply_group_opacity());
SkPoint offset = {i * 200.0f, 0};
layers[i] =
std::make_shared<DisplayListLayer>(offset, display_list, true, false);
opacity_layer->Add(layers[i]);
}
for (size_t j = 0; j < context->raster_cache->access_threshold(); j++) {
context->raster_cache->BeginFrame();
for (int i = 0; i < layer_count; i++) {
context->renderable_state_flags = 0;
layers[i]->Preroll(context);
ASSERT_EQ(context->renderable_state_flags, 0) << "pass " << (j + 1);
}
}
opacity_layer->Preroll(context);
ASSERT_FALSE(opacity_layer->children_can_accept_opacity());
LayerTree::TryToRasterCache(*context->raster_cached_entries, &paint_context(),
false);
context->raster_cached_entries->clear();
context->raster_cache->BeginFrame();
for (int i = 0; i < per_frame; i++) {
context->renderable_state_flags = 0;
layers[i]->Preroll(context);
ASSERT_EQ(context->renderable_state_flags,
LayerStateStack::kCallerCanApplyOpacity)
<< "layer " << (i + 1);
}
for (int i = per_frame; i < layer_count; i++) {
context->renderable_state_flags = 0;
layers[i]->Preroll(context);
ASSERT_EQ(context->renderable_state_flags, 0) << "layer " << (i + 1);
}
opacity_layer->Preroll(context);
ASSERT_FALSE(opacity_layer->children_can_accept_opacity());
LayerTree::TryToRasterCache(*context->raster_cached_entries, &paint_context(),
false);
context->raster_cached_entries->clear();
context->raster_cache->BeginFrame();
for (int i = 0; i < layer_count; i++) {
context->renderable_state_flags = 0;
layers[i]->Preroll(context);
ASSERT_EQ(context->renderable_state_flags,
LayerStateStack::kCallerCanApplyOpacity)
<< "layer " << (i + 1);
}
opacity_layer->Preroll(context);
ASSERT_TRUE(opacity_layer->children_can_accept_opacity());
}
} // namespace testing
} // namespace flutter
// NOLINTEND(bugprone-unchecked-optional-access)