skia2/tests/PictureShaderTest.cpp
Mike Reed ff83dda8cd Cache image behind picture-shader
PictureShader = picture + tiling + depth/colorspace + filtering [+ scale]

Today we cache the imageshader that is used to rendering. However, the
key for that cache is the pictureshader's ID itself... which means if
we have several, all using the same picture (but maybe diff tiling) we
would create dup cache entries.

Idea:
1. only cache the image (rastered picture), not an imageShader
2. key the cache on the picture's ID, not the shader's

Several implications of this:

1. Should get more cache reuse, since we don't care about the
shader's ID (which is just wrapping a picture+tiling, etc.)

2. We also eliminate the indirection of creating a PictureImage. Instead
we're creating real (pixel) images, and caching those. This removes one
extra layer of "cache".


Idea: when we cache something for pict
Change-Id: I51cf4e9bff3c91ce1872876597d3d565039d8c7a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/377844
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
2021-03-04 20:48:30 +00:00

115 lines
3.6 KiB
C++

/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkCanvas.h"
#include "include/core/SkPicture.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkShader.h"
#include "include/core/SkSurface.h"
#include "src/shaders/SkPictureShader.h"
#include "tests/Test.h"
// Test that the SkPictureShader cache is purged on shader deletion.
DEF_TEST(PictureShader_caching, reporter) {
auto makePicture = [] () {
SkPictureRecorder recorder;
recorder.beginRecording(100, 100)->drawColor(SK_ColorGREEN);
return recorder.finishRecordingAsPicture();
};
sk_sp<SkPicture> picture = makePicture();
REPORTER_ASSERT(reporter, picture->unique());
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(100, 100);
{
SkPaint paint;
paint.setShader(picture->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat));
surface->getCanvas()->drawPaint(paint);
// We should have about 3 refs by now: local + shader + shader cache.
REPORTER_ASSERT(reporter, !picture->unique());
}
// Draw another picture shader to have a chance to purge.
{
SkPaint paint;
paint.setShader(makePicture()->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat));
surface->getCanvas()->drawPaint(paint);
}
// All but the local ref should be gone now.
REPORTER_ASSERT(reporter, picture->unique());
}
#ifndef SK_SUPPORT_LEGACY_PICTURESHADER_MATH
#include "src/core/SkPicturePriv.h"
#include "src/core/SkResourceCache.h"
/*
* Check caching of picture-shaders
* - we do cache the underlying image (i.e. there is a cache entry)
* - there is only 1 entry, even with differing tile modes
* - after deleting the picture, the cache entry is purged
*/
DEF_TEST(PictureShader_caching2, reporter) {
auto picture = []() {
SkPictureRecorder recorder;
recorder.beginRecording(100, 100)->drawColor(SK_ColorGREEN);
return recorder.finishRecordingAsPicture();
}();
REPORTER_ASSERT(reporter, picture->unique());
struct Data {
uint64_t sharedID;
int counter;
} data = {
SkPicturePriv::MakeSharedID(picture->uniqueID()),
0,
};
auto counter = [](const SkResourceCache::Rec& rec, void* dataPtr) {
if (rec.getKey().getSharedID() == ((Data*)dataPtr)->sharedID) {
((Data*)dataPtr)->counter += 1;
}
};
SkResourceCache::VisitAll(counter, &data);
REPORTER_ASSERT(reporter, data.counter == 0);
// Draw with a view variants of picture-shaders that all use the same picture.
// Only expect 1 cache entry for all (since same CTM for all).
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(100, 100);
for (SkTileMode m : {
SkTileMode::kClamp, SkTileMode::kRepeat, SkTileMode::kRepeat, SkTileMode::kDecal
}) {
SkPaint paint;
paint.setShader(picture->makeShader(m, m));
surface->getCanvas()->drawPaint(paint);
}
// Don't expect any additional refs on the picture
REPORTER_ASSERT(reporter, picture->unique());
// Check that we did cache something, but only 1 thing
data.counter = 0;
SkResourceCache::VisitAll(counter, &data);
REPORTER_ASSERT(reporter, data.counter == 1);
// Now delete the picture, and check the we purge the cache entry
picture.reset();
SkResourceCache::CheckMessages();
data.counter = 0;
SkResourceCache::VisitAll(counter, &data);
REPORTER_ASSERT(reporter, data.counter == 0);
}
#endif