Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkResourceCacheTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
15#include "include/core/SkPicture.h" // IWYU pragma: keep
19#include "include/core/SkSize.h"
25#include "src/core/SkMipmap.h"
29#include "tests/Test.h"
30
31#include <array>
32#include <cstddef>
33#include <cstdint>
34#include <initializer_list>
35#include <memory>
36
37////////////////////////////////////////////////////////////////////////////////////////
38
43
48
50 int refcnt, CachedState cacheState, LockedState lockedState) {
51 REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt);
52 REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState));
53 bool isLocked = (data->data() != nullptr);
54 REPORTER_ASSERT(reporter, isLocked == (lockedState == kLocked));
55}
56
58 cache->purgeAll();
59
60 SkBitmap src;
61 src.allocN32Pixels(5, 5);
62 src.setImmutable();
63 sk_sp<SkImage> img = src.asImage();
64 const auto desc = SkBitmapCacheDesc::Make(img.get());
65
66 const SkMipmap* mipmap = SkMipmapCache::FindAndRef(desc, cache);
67 REPORTER_ASSERT(reporter, nullptr == mipmap);
68
69 mipmap = SkMipmapCache::AddAndRef(as_IB(img.get()), cache);
71
72 {
73 const SkMipmap* mm = SkMipmapCache::FindAndRef(desc, cache);
75 REPORTER_ASSERT(reporter, mm == mipmap);
76 mm->unref();
77 }
78
79 check_data(reporter, mipmap, 2, kInCache, kLocked);
80
81 mipmap->unref();
82 // tricky, since technically after this I'm no longer an owner, but since the cache is
83 // local, I know it won't get purged behind my back
85
86 // find us again
87 mipmap = SkMipmapCache::FindAndRef(desc, cache);
88 check_data(reporter, mipmap, 2, kInCache, kLocked);
89
90 cache->purgeAll();
92
93 mipmap->unref();
94}
95
97 const int N = 3;
98
99 SkBitmap src[N];
100 sk_sp<SkImage> img[N];
101 SkBitmapCacheDesc desc[N];
102 for (int i = 0; i < N; ++i) {
103 src[i].allocN32Pixels(5, 5);
104 src[i].setImmutable();
105 img[i] = src[i].asImage();
106 SkMipmapCache::AddAndRef(as_IB(img[i].get()), cache)->unref();
107 desc[i] = SkBitmapCacheDesc::Make(img[i].get());
108 }
109
110 for (int i = 0; i < N; ++i) {
111 const SkMipmap* mipmap = SkMipmapCache::FindAndRef(desc[i], cache);
112 // We're always using a local cache, so we know we won't be purged by other threads
113 REPORTER_ASSERT(reporter, mipmap);
114 SkSafeUnref(mipmap);
115
116 img[i].reset(); // delete the image, which *should not* remove us from the cache
117 mipmap = SkMipmapCache::FindAndRef(desc[i], cache);
118 REPORTER_ASSERT(reporter, mipmap);
119 SkSafeUnref(mipmap);
120
121 src[i].reset(); // delete the underlying pixelref, which *should* remove us from the cache
122 mipmap = SkMipmapCache::FindAndRef(desc[i], cache);
123 REPORTER_ASSERT(reporter, !mipmap);
124 }
125}
126
128static int gFactoryCalls = 0;
129
130static SkDiscardableMemory* pool_factory(size_t bytes) {
133 return gPool->create(bytes);
134}
135
141
142DEF_TEST(BitmapCache_discarded_bitmap, reporter) {
143 const size_t byteLimit = 100 * 1024;
144 {
145 SkResourceCache cache(byteLimit);
147 }
148 {
150 gPool = pool.get();
152 SkResourceCache cache(factory);
154 }
156}
157
159 sk_sp<SkImage> (*buildImage)()) {
161 SkCanvas* canvas = surface->getCanvas();
162
163 // SkBitmapCache is global, so other threads could be evicting our bitmaps. Loop a few times
164 // to mitigate this risk.
165 const unsigned kRepeatCount = 42;
166 for (unsigned i = 0; i < kRepeatCount; ++i) {
167 SkAutoCanvasRestore acr(canvas, true);
168
169 sk_sp<SkImage> image(buildImage());
170
171 // draw the image (with a transform, to tickle different code paths) to ensure
172 // any associated resources get cached
173 canvas->concat(transform);
174 // always use high quality to ensure caching when scaled
175 canvas->drawImage(image, 0, 0, SkSamplingOptions({1.0f/3, 1.0f/3}));
176
177 const auto desc = SkBitmapCacheDesc::Make(image.get());
178
179 // delete the image
180 image.reset(nullptr);
181
182 // all resources should have been purged
185 }
186}
187
188
189// Verify that associated bitmap cache entries are purged on SkImage destruction.
190DEF_TEST(BitmapCache_discarded_image, reporter) {
191 // Cache entries associated with SkImages fall into two categories:
192 //
193 // 1) generated image bitmaps (managed by the image cacherator)
194 // 2) scaled/resampled bitmaps (cached when HQ filters are used)
195 //
196 // To exercise the first cache type, we use generated/picture-backed SkImages.
197 // To exercise the latter, we draw scaled bitmap images using HQ filters.
198
199 const SkMatrix xforms[] = {
200 SkMatrix::Scale(1, 1),
201 SkMatrix::Scale(1.7f, 0.5f),
202 };
203
204 for (size_t i = 0; i < std::size(xforms); ++i) {
205 test_discarded_image(reporter, xforms[i], []() {
207 surface->getCanvas()->clear(SK_ColorCYAN);
208 return surface->makeImageSnapshot();
209 });
210
211 test_discarded_image(reporter, xforms[i], []() {
212 SkPictureRecorder recorder;
213 SkCanvas* canvas = recorder.beginRecording(10, 10);
214 canvas->clear(SK_ColorCYAN);
216 SkISize::Make(10, 10),
217 nullptr,
218 nullptr,
221 });
222 }
223}
224
225///////////////////////////////////////////////////////////////////////////////////////////////////
226
227static void* gTestNamespace;
228
230 int32_t fData;
231
232 TestKey(int sharedID, int32_t data) : fData(data) {
233 this->init(&gTestNamespace, sharedID, sizeof(fData));
234 }
235};
236
238 enum {
239 kDidInstall = 1 << 0,
240 };
241
243 int* fFlags;
245
246 TestRec(int sharedID, int32_t data, int* flagPtr) : fKey(sharedID, data), fFlags(flagPtr) {
247 fCanBePurged = false;
248 }
249
250 const Key& getKey() const override { return fKey; }
251 size_t bytesUsed() const override { return 1024; /* just need a value */ }
252 bool canBePurged() override { return fCanBePurged; }
253 void postAddInstall(void*) override {
255 }
256 const char* getCategory() const override { return "test-category"; }
257};
258
260 bool purgable) {
261 int sharedID = 1;
262 int data = 0;
263
264 int flags0 = 0, flags1 = 0;
265
266 auto rec0 = std::make_unique<TestRec>(sharedID, data, &flags0);
267 auto rec1 = std::make_unique<TestRec>(sharedID, data, &flags1);
268 SkASSERT(rec0->getKey() == rec1->getKey());
269
270 TestRec* r0 = rec0.get(); // save the bare-pointer since we will release rec0
271 r0->fCanBePurged = purgable;
272
273 REPORTER_ASSERT(reporter, !(flags0 & TestRec::kDidInstall));
274 REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall));
275
276 cache->add(rec0.release(), nullptr);
277 REPORTER_ASSERT(reporter, flags0 & TestRec::kDidInstall);
278 REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall));
279 flags0 = 0; // reset the flag
280
281 cache->add(rec1.release(), nullptr);
282 if (purgable) {
283 // we purged rec0, and did install rec1
284 REPORTER_ASSERT(reporter, !(flags0 & TestRec::kDidInstall));
285 REPORTER_ASSERT(reporter, flags1 & TestRec::kDidInstall);
286 } else {
287 // we re-used rec0 and did not install rec1
288 REPORTER_ASSERT(reporter, flags0 & TestRec::kDidInstall);
289 REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall));
290 r0->fCanBePurged = true; // so we can cleanup the cache
291 }
292}
293
294/*
295 * Test behavior when the same key is added more than once.
296 */
297DEF_TEST(ResourceCache_purge, reporter) {
298 for (bool purgable : { false, true }) {
299 {
300 SkResourceCache cache(1024 * 1024);
301 test_duplicate_add(&cache, reporter, purgable);
302 }
303 {
305 test_duplicate_add(&cache, reporter, purgable);
306 }
307 }
308}
AutoreleasePool pool
CachedState
LockedState
reporter
uint16_t fFlags
#define SkASSERT(cond)
Definition SkAssert.h:116
constexpr SkColor SK_ColorCYAN
Definition SkColor.h:143
static SkImage_Base * as_IB(SkImage *image)
static void SkSafeUnref(T *obj)
Definition SkRefCnt.h:149
static void test_mipmapcache(skiatest::Reporter *reporter, SkResourceCache *cache)
static void test_discarded_image(skiatest::Reporter *reporter, const SkMatrix &transform, sk_sp< SkImage >(*buildImage)())
static void testBitmapCache_discarded_bitmap(skiatest::Reporter *reporter, SkResourceCache *cache, SkResourceCache::DiscardableFactory factory)
static SkDiscardableMemory * pool_factory(size_t bytes)
static void * gTestNamespace
static SkDiscardableMemoryPool * gPool
static void check_data(skiatest::Reporter *reporter, const SkCachedData *data, int refcnt, CachedState cacheState, LockedState lockedState)
static void test_duplicate_add(SkResourceCache *cache, skiatest::Reporter *reporter, bool purgable)
static void test_mipmap_notify(skiatest::Reporter *reporter, SkResourceCache *cache)
static int gFactoryCalls
#define DEF_TEST(name, reporter)
Definition Test.h:312
#define REPORTER_ASSERT(r, cond,...)
Definition Test.h:286
#define N
Definition beziers.cpp:19
static bool Find(const SkBitmapCacheDesc &, SkBitmap *result)
void unref() const
void clear(SkColor color)
Definition SkCanvas.h:1199
void concat(const SkMatrix &matrix)
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition SkCanvas.h:1528
static sk_sp< SkColorSpace > MakeSRGB()
static sk_sp< SkDiscardableMemoryPool > Make(size_t size)
virtual SkDiscardableMemory * create(size_t bytes)=0
static SkDiscardableMemory * Create(size_t bytes)
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition SkMatrix.h:75
static const SkMipmap * AddAndRef(const SkImage_Base *, SkResourceCache *localCache=nullptr)
static const SkMipmap * FindAndRef(const SkBitmapCacheDesc &, SkResourceCache *localCache=nullptr)
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkPicture > finishRecordingAsPicture()
SkDiscardableMemory *(* DiscardableFactory)(size_t bytes)
T * get() const
Definition SkRefCnt.h:303
void reset(T *ptr=nullptr)
Definition SkRefCnt.h:310
VkSurfaceKHR surface
Definition main.cc:49
sk_sp< SkImage > image
Definition examples.cpp:29
GAsyncResult * result
SK_API sk_sp< SkImage > DeferredFromPicture(sk_sp< SkPicture > picture, const SkISize &dimensions, const SkMatrix *matrix, const SkPaint *paint, BitDepth bitDepth, sk_sp< SkColorSpace > colorSpace, SkSurfaceProps props)
@ kU8
uses 8-bit unsigned int per color component
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47
static SkBitmapCacheDesc Make(const SkImage *)
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
static SkImageInfo MakeN32Premul(int width, int height)
void init(void *nameSpace, uint64_t sharedID, size_t dataSize)
TestKey(int sharedID, int32_t data)
size_t bytesUsed() const override
bool canBePurged() override
const char * getCategory() const override
void postAddInstall(void *) override
TestRec(int sharedID, int32_t data, int *flagPtr)
const Key & getKey() const override