Flutter Engine
The Flutter Engine
ImageFilterCacheTest.cpp
Go to the documentation of this file.
1 /*
2 * Copyright 2016 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
21#include "include/core/SkRect.h"
28#include "include/gpu/GrTypes.h"
35#include "src/gpu/ganesh/GrColorInfo.h" // IWYU pragma: keep
40#include "src/gpu/ganesh/SkGr.h"
43#include "tests/Test.h"
44
45#include <cstddef>
46#include <tuple>
47#include <utility>
48
50struct GrContextOptions;
51
52static const int kSmallerSize = 10;
53static const int kPad = 3;
54static const int kFullSize = kSmallerSize + 2 * kPad;
55
59
60 SkBitmap bm;
61 bm.allocPixels(ii);
63 bm.setImmutable();
64 return bm;
65}
66
69 return SkImageFilters::ColorFilter(std::move(filter), nullptr, nullptr);
70}
71
72// Ensure the cache can return a cached image
75 const sk_sp<SkSpecialImage>& subset) {
76 static const size_t kCacheSize = 1000000;
78
79 SkIRect clip = SkIRect::MakeWH(100, 100);
80 SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
81 SkImageFilterCacheKey key2(0, SkMatrix::I(), clip, subset->uniqueID(), subset->subset());
82
84 auto filter = make_filter();
86
87 skif::FilterResult foundImage;
88 REPORTER_ASSERT(reporter, cache->get(key1, &foundImage));
91 SkIRect(foundImage.layerBounds()));
92
93 REPORTER_ASSERT(reporter, !cache->get(key2, &foundImage));
94}
95
96// If either id is different or the clip or the matrix are different the
97// cached image won't be found. Even if it is caching the same bitmap.
100 const sk_sp<SkSpecialImage>& subset) {
101 static const size_t kCacheSize = 1000000;
103
104 SkIRect clip1 = SkIRect::MakeWH(100, 100);
105 SkIRect clip2 = SkIRect::MakeWH(200, 200);
106 SkImageFilterCacheKey key0(0, SkMatrix::I(), clip1, image->uniqueID(), image->subset());
107 SkImageFilterCacheKey key1(1, SkMatrix::I(), clip1, image->uniqueID(), image->subset());
108 SkImageFilterCacheKey key2(0, SkMatrix::Translate(5, 5), clip1,
109 image->uniqueID(), image->subset());
110 SkImageFilterCacheKey key3(0, SkMatrix::I(), clip2, image->uniqueID(), image->subset());
111 SkImageFilterCacheKey key4(0, SkMatrix::I(), clip1, subset->uniqueID(), subset->subset());
112
114 auto filter = make_filter();
116
117 skif::FilterResult foundImage;
118 REPORTER_ASSERT(reporter, !cache->get(key1, &foundImage));
119 REPORTER_ASSERT(reporter, !cache->get(key2, &foundImage));
120 REPORTER_ASSERT(reporter, !cache->get(key3, &foundImage));
121 REPORTER_ASSERT(reporter, !cache->get(key4, &foundImage));
122}
123
124// Test purging when the max cache size is exceeded
126 SkASSERT(image->getSize());
127 const size_t kCacheSize = image->getSize() + 10;
129
130 SkIRect clip = SkIRect::MakeWH(100, 100);
131 SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
132 SkImageFilterCacheKey key2(1, SkMatrix::I(), clip, image->uniqueID(), image->subset());
133
135 auto filter1 = make_filter();
136 cache->set(key1, filter1.get(), skif::FilterResult(image, skif::LayerSpace<SkIPoint>(offset)));
137
138 skif::FilterResult foundImage;
139 REPORTER_ASSERT(reporter, cache->get(key1, &foundImage));
140
141 // This should knock the first one out of the cache
142 auto filter2 = make_filter();
143 cache->set(key2, filter2.get(),
145
146 REPORTER_ASSERT(reporter, cache->get(key2, &foundImage));
147 REPORTER_ASSERT(reporter, !cache->get(key1, &foundImage));
148}
149
150// Exercise the purgeByKey and purge methods
153 const sk_sp<SkSpecialImage>& subset) {
154 static const size_t kCacheSize = 1000000;
156
157 SkIRect clip = SkIRect::MakeWH(100, 100);
158 SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
159 SkImageFilterCacheKey key2(1, SkMatrix::I(), clip, subset->uniqueID(), image->subset());
160
162 auto filter1 = make_filter();
163 auto filter2 = make_filter();
164 cache->set(key1, filter1.get(),
166 cache->set(key2, filter2.get(),
168 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->count());)
169
170 skif::FilterResult foundImage;
171 REPORTER_ASSERT(reporter, cache->get(key1, &foundImage));
172 REPORTER_ASSERT(reporter, cache->get(key2, &foundImage));
173
174 cache->purgeByImageFilter(filter1.get());
175 SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->count());)
176
177 REPORTER_ASSERT(reporter, !cache->get(key1, &foundImage));
178 REPORTER_ASSERT(reporter, cache->get(key2, &foundImage));
179
180 cache->purge();
181 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->count());)
182
183 REPORTER_ASSERT(reporter, !cache->get(key1, &foundImage));
184 REPORTER_ASSERT(reporter, !cache->get(key2, &foundImage));
185}
186
187DEF_TEST(ImageFilterCache_RasterBacked, reporter) {
188 SkBitmap srcBM = create_bm();
189
191
193
195
196 sk_sp<SkSpecialImage> subsetImg(
198
199 test_find_existing(reporter, fullImg, subsetImg);
200 test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
202 test_explicit_purging(reporter, fullImg, subsetImg);
203}
204
205
206// Shared test code for both the raster and gpu-backed image cases
208 GrRecordingContext* rContext,
209 const sk_sp<SkImage>& srcImage) {
211
212 sk_sp<SkSpecialImage> fullImg;
213 if (rContext) {
214 fullImg = SkSpecialImages::MakeFromTextureImage(rContext, full, srcImage, {});
215 } else {
216 fullImg = SkSpecialImages::MakeFromRaster(full, srcImage, {});
217 }
218
220
221 sk_sp<SkSpecialImage> subsetImg;
222 if (rContext) {
223 subsetImg = SkSpecialImages::MakeFromTextureImage(rContext, subset, srcImage, {});
224 } else {
225 subsetImg = SkSpecialImages::MakeFromRaster(subset, srcImage, {});
226 }
227
228 test_find_existing(reporter, fullImg, subsetImg);
229 test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
231 test_explicit_purging(reporter, fullImg, subsetImg);
232}
233
234DEF_TEST(ImageFilterCache_ImageBackedRaster, reporter) {
235 SkBitmap srcBM = create_bm();
236
237 sk_sp<SkImage> srcImage(srcBM.asImage());
238
239 test_image_backed(reporter, nullptr, srcImage);
240}
241
243 SkBitmap srcBM = create_bm();
244 return std::get<0>(GrMakeUncachedBitmapProxyView(rContext, srcBM));
245}
246
247DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU,
248 reporter,
249 ctxInfo,
251 auto dContext = ctxInfo.directContext();
252
253 GrSurfaceProxyView srcView = create_proxy_view(dContext);
254 if (!srcView.proxy()) {
255 return;
256 }
257
258 if (!srcView.proxy()->instantiate(dContext->priv().resourceProvider())) {
259 return;
260 }
261 GrTexture* tex = srcView.proxy()->peekTexture();
262
263 GrBackendTexture backendTex = tex->getBackendTexture();
264
267 backendTex,
268 texOrigin,
271 nullptr,
272 nullptr,
273 nullptr));
274 if (!srcImage) {
275 return;
276 }
277
278 GrSurfaceOrigin readBackOrigin;
279 GrBackendTexture readBackBackendTex;
281 srcImage, &readBackBackendTex, false, &readBackOrigin);
283 if (!GrBackendTexture::TestingOnly_Equals(readBackBackendTex, backendTex)) {
284 ERRORF(reporter, "backend mismatch\n");
285 }
286 REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(readBackBackendTex, backendTex));
287
288 if (readBackOrigin != texOrigin) {
289 ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, texOrigin);
290 }
291 REPORTER_ASSERT(reporter, readBackOrigin == texOrigin);
292
293 test_image_backed(reporter, dContext, srcImage);
294}
295
296DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_GPUBacked,
297 reporter,
298 ctxInfo,
300 auto dContext = ctxInfo.directContext();
301
302 GrSurfaceProxyView srcView = create_proxy_view(dContext);
303 if (!srcView.proxy()) {
304 return;
305 }
306
308
310 dContext,
311 full,
313 srcView,
315 SkSurfaceProps()));
316
318
320 dContext,
321 subset,
323 std::move(srcView),
325 SkSurfaceProps()));
326
327 test_find_existing(reporter, fullImg, subsetImg);
328 test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
330 test_explicit_purging(reporter, fullImg, subsetImg);
331}
332
333DEF_SERIAL_TEST(PurgeImageFilterCache, r) {
335 if (cache) {
336 // This test verifies that Get(false) does not create the cache, but
337 // another test has already created it, so there is nothing to test.
338 return;
339 }
340
341 // This method calls SkImageFilter_Base::PurgeCache(), which is private.
344
345 // PurgeCache should not have created it.
347
348 {
351 }
352}
reporter
Definition: FontMgrTest.cpp:39
GrSurfaceOrigin
Definition: GrTypes.h:147
@ kTopLeft_GrSurfaceOrigin
Definition: GrTypes.h:148
static const int kFullSize
static const int kSmallerSize
static void test_explicit_purging(skiatest::Reporter *reporter, const sk_sp< SkSpecialImage > &image, const sk_sp< SkSpecialImage > &subset)
static void test_dont_find_if_diff_key(skiatest::Reporter *reporter, const sk_sp< SkSpecialImage > &image, const sk_sp< SkSpecialImage > &subset)
static SkBitmap create_bm()
static const int kPad
static void test_image_backed(skiatest::Reporter *reporter, GrRecordingContext *rContext, const sk_sp< SkImage > &srcImage)
static void test_internal_purge(skiatest::Reporter *reporter, const sk_sp< SkSpecialImage > &image)
static GrSurfaceProxyView create_proxy_view(GrRecordingContext *rContext)
static sk_sp< SkImageFilter > make_filter()
static void test_find_existing(skiatest::Reporter *reporter, const sk_sp< SkSpecialImage > &image, const sk_sp< SkSpecialImage > &subset)
DEF_SERIAL_TEST(PurgeImageFilterCache, r)
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU, reporter, ctxInfo, CtsEnforcement::kNever)
DEF_TEST(ImageFilterCache_RasterBacked, reporter)
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kSrcIn
r = s * da
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
constexpr SkColor SK_ColorTRANSPARENT
Definition: SkColor.h:99
constexpr SkColor SK_ColorBLUE
Definition: SkColor.h:135
std::tuple< GrSurfaceProxyView, GrColorType > GrMakeUncachedBitmapProxyView(GrRecordingContext *rContext, const SkBitmap &bitmap, skgpu::Mipmapped mipmapped, SkBackingFit fit, skgpu::Budgeted budgeted)
Definition: SkGr.cpp:253
static bool ok(int result)
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
@ kNeedNewImageUniqueID_SpecialImage
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define ERRORF(r,...)
Definition: Test.h:293
GrSurfaceProxy * proxy() const
virtual bool instantiate(GrResourceProvider *)=0
GrTexture * peekTexture() const
virtual GrBackendTexture getBackendTexture() const =0
void allocPixels(const SkImageInfo &info, size_t rowBytes)
Definition: SkBitmap.cpp:258
sk_sp< SkImage > asImage() const
Definition: SkBitmap.cpp:645
void setImmutable()
Definition: SkBitmap.cpp:400
void eraseColor(SkColor4f) const
Definition: SkBitmap.cpp:442
static sk_sp< SkColorFilter > Blend(const SkColor4f &c, sk_sp< SkColorSpace >, SkBlendMode mode)
static void PurgeResourceCache()
static sk_sp< SkImageFilterCache > Get(CreateIfNecessary=CreateIfNecessary::kYes)
static sk_sp< SkImageFilterCache > Create(size_t maxBytes)
static sk_sp< SkImageFilter > ColorFilter(sk_sp< SkColorFilter > cf, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
uint32_t uniqueID() const
Definition: SkImage.h:311
int width() const
Definition: SkImage.h:285
int height() const
Definition: SkImage.h:291
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.h:91
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
uint32_t uniqueID() const
const SkIRect & subset() const
LayerSpace< SkIRect > layerBounds() const
SK_API bool GetBackendTextureFromImage(const SkImage *img, GrBackendTexture *outTexture, bool flushPendingGrContextIO, GrSurfaceOrigin *origin=nullptr)
SK_API sk_sp< SkImage > BorrowTextureFrom(GrRecordingContext *context, const GrBackendTexture &backendTexture, GrSurfaceOrigin origin, SkColorType colorType, SkAlphaType alphaType, sk_sp< SkColorSpace > colorSpace, TextureReleaseProc textureReleaseProc=nullptr, ReleaseContext releaseContext=nullptr)
sk_sp< const SkImage > image
Definition: SkRecords.h:269
sk_sp< SkSpecialImage > MakeFromRaster(const SkIRect &subset, const SkBitmap &bm, const SkSurfaceProps &props)
sk_sp< SkSpecialImage > MakeFromTextureImage(GrRecordingContext *rContext, const SkIRect &subset, sk_sp< SkImage > image, const SkSurfaceProps &props)
sk_sp< SkSpecialImage > MakeDeferredFromGpu(GrRecordingContext *context, const SkIRect &subset, uint32_t uniqueID, GrSurfaceProxyView view, const GrColorInfo &colorInfo, const SkSurfaceProps &props)
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
Definition: full.py:1
const myers::Point & get< 0 >(const myers::Segment &s)
Definition: Myers.h:80
SeparatedVector2 offset
static constexpr SkIPoint Make(int32_t x, int32_t y)
Definition: SkPoint_impl.h:38
Definition: SkRect.h:32
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h)
Definition: SkRect.h:104
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)