Skip to content

Commit

Permalink
use nested op counts to determine picture complexity for raster cache (
Browse files Browse the repository at this point in the history
  • Loading branch information
flar authored Oct 28, 2021
1 parent f0a07ab commit 529f08d
Show file tree
Hide file tree
Showing 4 changed files with 289 additions and 9 deletions.
16 changes: 14 additions & 2 deletions flow/display_list.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1427,14 +1427,26 @@ void DisplayListBuilder::drawPicture(const sk_sp<SkPicture> picture,
? Push<DrawSkPictureMatrixOp>(0, 1, std::move(picture), *matrix,
render_with_attributes)
: Push<DrawSkPictureOp>(0, 1, std::move(picture), render_with_attributes);
// The non-nested op count accumulated in the |Push| method will include
// this call to |drawPicture| for non-nested op count metrics.
// But, for nested op count metrics we want the |drawPicture| call itself
// to be transparent. So we subtract 1 from our accumulated nested count to
// balance out against the 1 that was accumulated into the regular count.
// This behavior is identical to the way SkPicture computes nested op counts.
nested_op_count_ += picture->approximateOpCount(true) - 1;
nested_bytes_ += picture->approximateBytesUsed();
nested_op_count_ += picture->approximateOpCount(true);
}
void DisplayListBuilder::drawDisplayList(
const sk_sp<DisplayList> display_list) {
Push<DrawDisplayListOp>(0, 1, std::move(display_list));
// The non-nested op count accumulated in the |Push| method will include
// this call to |drawDisplayList| for non-nested op count metrics.
// But, for nested op count metrics we want the |drawDisplayList| call itself
// to be transparent. So we subtract 1 from our accumulated nested count to
// balance out against the 1 that was accumulated into the regular count.
// This behavior is identical to the way SkPicture computes nested op counts.
nested_op_count_ += display_list->op_count(true) - 1;
nested_bytes_ += display_list->bytes(true);
nested_op_count_ += display_list->op_count(true);
}
void DisplayListBuilder::drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
Expand Down
45 changes: 45 additions & 0 deletions flow/display_list_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1072,5 +1072,50 @@ TEST(DisplayList, DisplayListSaveLayerBoundsWithAlphaFilter) {
}
}

TEST(DisplayList, NestedOpCountMetricsSameAsSkPicture) {
SkPictureRecorder recorder;
recorder.beginRecording(SkRect::MakeWH(150, 100));
SkCanvas* canvas = recorder.getRecordingCanvas();
SkPaint paint;
for (int y = 10; y <= 60; y += 10) {
for (int x = 10; x <= 60; x += 10) {
paint.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE);
canvas->drawRect(SkRect::MakeXYWH(x, y, 80, 80), paint);
}
}
SkPictureRecorder outer_recorder;
outer_recorder.beginRecording(SkRect::MakeWH(150, 100));
canvas = outer_recorder.getRecordingCanvas();
canvas->drawPicture(recorder.finishRecordingAsPicture());

auto picture = outer_recorder.finishRecordingAsPicture();
ASSERT_EQ(picture->approximateOpCount(), 1);
ASSERT_EQ(picture->approximateOpCount(true), 36);

DisplayListBuilder builder(SkRect::MakeWH(150, 100));
for (int y = 10; y <= 60; y += 10) {
for (int x = 10; x <= 60; x += 10) {
builder.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE);
builder.drawRect(SkRect::MakeXYWH(x, y, 80, 80));
}
}
DisplayListBuilder outer_builder(SkRect::MakeWH(150, 100));
outer_builder.drawDisplayList(builder.Build());

auto display_list = outer_builder.Build();
ASSERT_EQ(display_list->op_count(), 1);
ASSERT_EQ(display_list->op_count(true), 36);

ASSERT_EQ(picture->approximateOpCount(), display_list->op_count());
ASSERT_EQ(picture->approximateOpCount(true), display_list->op_count(true));

DisplayListCanvasRecorder dl_recorder(SkRect::MakeWH(150, 100));
picture->playback(&dl_recorder);

auto sk_display_list = dl_recorder.Build();
ASSERT_EQ(display_list->op_count(), 1);
ASSERT_EQ(display_list->op_count(true), 36);
}

} // namespace testing
} // namespace flutter
4 changes: 2 additions & 2 deletions flow/raster_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ static bool IsPictureWorthRasterizing(SkPicture* picture,

// TODO(abarth): We should find a better heuristic here that lets us avoid
// wasting memory on trivial layers that are easy to re-rasterize every frame.
return picture->approximateOpCount() > 5;
return picture->approximateOpCount(true) > 5;
}

static bool IsDisplayListWorthRasterizing(DisplayList* display_list,
Expand All @@ -109,7 +109,7 @@ static bool IsDisplayListWorthRasterizing(DisplayList* display_list,

// TODO(abarth): We should find a better heuristic here that lets us avoid
// wasting memory on trivial layers that are easy to re-rasterize every frame.
return display_list->op_count() > 5;
return display_list->op_count(true) > 5;
}

/// @note Procedure doesn't copy all closures.
Expand Down
233 changes: 228 additions & 5 deletions flow/raster_cache_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/flow/display_list.h"
#include "flutter/flow/raster_cache.h"

#include "flutter/flow/testing/mock_raster_cache.h"
Expand All @@ -25,14 +26,52 @@ sk_sp<SkPicture> GetSamplePicture() {
return recorder.finishRecordingAsPicture();
}

sk_sp<DisplayList> GetSampleDisplayList() {
DisplayListBuilder builder(SkRect::MakeWH(150, 100));
builder.setColor(SK_ColorRED);
builder.drawRect(SkRect::MakeXYWH(10, 10, 80, 80));
return builder.Build();
}

sk_sp<SkPicture> GetSampleNestedPicture() {
SkPictureRecorder recorder;
recorder.beginRecording(SkRect::MakeWH(150, 100));
SkCanvas* canvas = recorder.getRecordingCanvas();
SkPaint paint;
for (int y = 10; y <= 60; y += 10) {
for (int x = 10; x <= 60; x += 10) {
paint.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE);
canvas->drawRect(SkRect::MakeXYWH(x, y, 80, 80), paint);
}
}
SkPictureRecorder outer_recorder;
outer_recorder.beginRecording(SkRect::MakeWH(150, 100));
canvas = outer_recorder.getRecordingCanvas();
canvas->drawPicture(recorder.finishRecordingAsPicture());
return outer_recorder.finishRecordingAsPicture();
}

sk_sp<DisplayList> GetSampleNestedDisplayList() {
DisplayListBuilder builder(SkRect::MakeWH(150, 100));
for (int y = 10; y <= 60; y += 10) {
for (int x = 10; x <= 60; x += 10) {
builder.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE);
builder.drawRect(SkRect::MakeXYWH(x, y, 80, 80));
}
}
DisplayListBuilder outer_builder(SkRect::MakeWH(150, 100));
outer_builder.drawDisplayList(builder.Build());
return outer_builder.Build();
}

} // namespace

TEST(RasterCache, SimpleInitialization) {
flutter::RasterCache cache;
ASSERT_TRUE(true);
}

TEST(RasterCache, ThresholdIsRespected) {
TEST(RasterCache, ThresholdIsRespectedForSkPicture) {
size_t threshold = 2;
flutter::RasterCache cache(threshold);

Expand Down Expand Up @@ -65,7 +104,40 @@ TEST(RasterCache, ThresholdIsRespected) {
ASSERT_TRUE(cache.Draw(*picture, dummy_canvas));
}

TEST(RasterCache, AccessThresholdOfZeroDisablesCaching) {
TEST(RasterCache, ThresholdIsRespectedForDisplayList) {
size_t threshold = 2;
flutter::RasterCache cache(threshold);

SkMatrix matrix = SkMatrix::I();

auto display_list = GetSampleDisplayList();

SkCanvas dummy_canvas;

PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder();

ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context,
display_list.get(), true, false, matrix));
// 1st access.
ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas));

cache.SweepAfterFrame();

ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context,
display_list.get(), true, false, matrix));

// 2nd access.
ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas));

cache.SweepAfterFrame();

// Now Prepare should cache it.
ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context,
display_list.get(), true, false, matrix));
ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas));
}

TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForSkPicture) {
size_t threshold = 0;
flutter::RasterCache cache(threshold);

Expand All @@ -83,7 +155,25 @@ TEST(RasterCache, AccessThresholdOfZeroDisablesCaching) {
ASSERT_FALSE(cache.Draw(*picture, dummy_canvas));
}

TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZero) {
TEST(RasterCache, AccessThresholdOfZeroDisablesCachingForDisplayList) {
size_t threshold = 0;
flutter::RasterCache cache(threshold);

SkMatrix matrix = SkMatrix::I();

auto display_list = GetSampleDisplayList();

SkCanvas dummy_canvas;

PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder();

ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context,
display_list.get(), true, false, matrix));

ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas));
}

TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForSkPicture) {
size_t picture_cache_limit_per_frame = 0;
flutter::RasterCache cache(3, picture_cache_limit_per_frame);

Expand All @@ -101,7 +191,25 @@ TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZero) {
ASSERT_FALSE(cache.Draw(*picture, dummy_canvas));
}

TEST(RasterCache, SweepsRemoveUnusedFrames) {
TEST(RasterCache, PictureCacheLimitPerFrameIsRespectedWhenZeroForDisplayList) {
size_t picture_cache_limit_per_frame = 0;
flutter::RasterCache cache(3, picture_cache_limit_per_frame);

SkMatrix matrix = SkMatrix::I();

auto display_list = GetSampleDisplayList();

SkCanvas dummy_canvas;

PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder();

ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context,
display_list.get(), true, false, matrix));

ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas));
}

TEST(RasterCache, SweepsRemoveUnusedSkPictures) {
size_t threshold = 1;
flutter::RasterCache cache(threshold);

Expand Down Expand Up @@ -129,10 +237,38 @@ TEST(RasterCache, SweepsRemoveUnusedFrames) {
ASSERT_FALSE(cache.Draw(*picture, dummy_canvas));
}

TEST(RasterCache, SweepsRemoveUnusedDisplayLists) {
size_t threshold = 1;
flutter::RasterCache cache(threshold);

SkMatrix matrix = SkMatrix::I();

auto display_list = GetSampleDisplayList();

SkCanvas dummy_canvas;

PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder();

ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context,
display_list.get(), true, false, matrix)); // 1
ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas));

cache.SweepAfterFrame();

ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context,
display_list.get(), true, false, matrix)); // 2
ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas));

cache.SweepAfterFrame();
cache.SweepAfterFrame(); // Extra frame without a Get image access.

ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas));
}

// Construct a cache result whose device target rectangle rounds out to be one
// pixel wider than the cached image. Verify that it can be drawn without
// triggering any assertions.
TEST(RasterCache, DeviceRectRoundOut) {
TEST(RasterCache, DeviceRectRoundOutForSkPicture) {
size_t threshold = 1;
flutter::RasterCache cache(threshold);

Expand All @@ -154,7 +290,9 @@ TEST(RasterCache, DeviceRectRoundOut) {
ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context,
picture.get(), true, false, ctm));
ASSERT_FALSE(cache.Draw(*picture, canvas));

cache.SweepAfterFrame();

ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context,
picture.get(), true, false, ctm));
ASSERT_TRUE(cache.Draw(*picture, canvas));
Expand All @@ -163,5 +301,90 @@ TEST(RasterCache, DeviceRectRoundOut) {
ASSERT_TRUE(cache.Draw(*picture, canvas));
}

// Construct a cache result whose device target rectangle rounds out to be one
// pixel wider than the cached image. Verify that it can be drawn without
// triggering any assertions.
TEST(RasterCache, DeviceRectRoundOutForDisplayList) {
size_t threshold = 1;
flutter::RasterCache cache(threshold);

SkRect logical_rect = SkRect::MakeLTRB(28, 0, 354.56731, 310.288);
DisplayListBuilder builder(logical_rect);
builder.setColor(SK_ColorRED);
builder.drawRect(logical_rect);
sk_sp<DisplayList> display_list = builder.Build();

SkMatrix ctm = SkMatrix::MakeAll(1.3312, 0, 233, 0, 1.3312, 206, 0, 0, 1);

SkCanvas canvas(100, 100, nullptr);
canvas.setMatrix(ctm);

PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder();

ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context,
display_list.get(), true, false, ctm));
ASSERT_FALSE(cache.Draw(*display_list, canvas));

cache.SweepAfterFrame();

ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context,
display_list.get(), true, false, ctm));
ASSERT_TRUE(cache.Draw(*display_list, canvas));

canvas.translate(248, 0);
ASSERT_TRUE(cache.Draw(*display_list, canvas));
}

TEST(RasterCache, NestedOpCountMetricUsedForSkPicture) {
size_t threshold = 1;
flutter::RasterCache cache(threshold);

SkMatrix matrix = SkMatrix::I();

auto picture = GetSampleNestedPicture();
ASSERT_EQ(picture->approximateOpCount(), 1);
ASSERT_EQ(picture->approximateOpCount(true), 36);

SkCanvas dummy_canvas;

PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder();

ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context,
picture.get(), false, false, matrix));
ASSERT_FALSE(cache.Draw(*picture, dummy_canvas));

cache.SweepAfterFrame();

ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context,
picture.get(), false, false, matrix));
ASSERT_TRUE(cache.Draw(*picture, dummy_canvas));
}

TEST(RasterCache, NestedOpCountMetricUsedForDisplayList) {
size_t threshold = 1;
flutter::RasterCache cache(threshold);

SkMatrix matrix = SkMatrix::I();

auto display_list = GetSampleNestedDisplayList();
ASSERT_EQ(display_list->op_count(), 1);
ASSERT_EQ(display_list->op_count(true), 36);

SkCanvas dummy_canvas;

PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder();

ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context,
display_list.get(), false, false, matrix));
ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas));

cache.SweepAfterFrame();

ASSERT_TRUE(cache.Prepare(&preroll_context_holder.preroll_context,
display_list.get(), false, false, matrix));
ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas));
}

} // namespace testing

} // namespace flutter

0 comments on commit 529f08d

Please sign in to comment.