Flutter Engine
The Flutter Engine
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
42};
43
47};
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
61 src.allocN32Pixels(5, 5);
62 src.setImmutable();
63 sk_sp<SkImage> img = src.asImage();
64 const auto desc = SkBitmapCacheDesc::Make(img.get());
65
67 REPORTER_ASSERT(reporter, nullptr == mipmap);
68
69 mipmap = SkMipmapCache::AddAndRef(as_IB(img.get()), cache);
71
72 {
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
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];
102 for (int i = 0; i < N; ++i) {
103 src[i].allocN32Pixels(5, 5);
104 src[i].setImmutable();
105 img[i] = src[i].asImage();
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
118 REPORTER_ASSERT(reporter, mipmap);
119 SkSafeUnref(mipmap);
120
121 src[i].reset(); // delete the underlying pixelref, which *should* remove us from the 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
140}
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) {
207 surface->getCanvas()->clear(SK_ColorCYAN);
208 return surface->makeImageSnapshot();
209 });
210
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
229struct TestKey : SkResourceCache::Key {
230 int32_t fData;
231
232 TestKey(int sharedID, int32_t data) : fData(data) {
233 this->init(&gTestNamespace, sharedID, sizeof(fData));
234 }
235};
236
237struct TestRec : SkResourceCache::Rec {
238 enum {
239 kDidInstall = 1 << 0,
240 };
241
242 TestKey fKey;
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
Definition: FontMgrTest.cpp:39
uint16_t fFlags
Definition: ShapeLayer.cpp:106
#define SkASSERT(cond)
Definition: SkAssert.h:116
constexpr SkColor SK_ColorCYAN
Definition: SkColor.h:143
static SkImage_Base * as_IB(SkImage *image)
Definition: SkImage_Base.h:201
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)
DEF_TEST(BitmapCache_discarded_bitmap, reporter)
@ kNotInCache
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)
@ kNotLocked
static void test_mipmap_notify(skiatest::Reporter *reporter, SkResourceCache *cache)
static int gFactoryCalls
#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
Definition: SkCachedData.h:31
void clear(SkColor color)
Definition: SkCanvas.h:1199
void concat(const SkMatrix &matrix)
Definition: SkCanvas.cpp:1318
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
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_sp< const SkImage > image
Definition: SkRecords.h:269
PODArray< SkRSXform > xforms
Definition: SkRecords.h:332
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition: switches.h:191
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
const myers::Point & get(const myers::Segment &)
SkSamplingOptions(SkFilterMode::kLinear))
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
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63