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
8 #include "include/core/SkAlphaType.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorFilter.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkColorType.h"
15 #include "include/core/SkGraphics.h"
16 #include "include/core/SkImage.h"
17 #include "include/core/SkImageFilter.h"
18 #include "include/core/SkImageInfo.h"
19 #include "include/core/SkMatrix.h"
20 #include "include/core/SkPoint.h"
21 #include "include/core/SkRect.h"
22 #include "include/core/SkRefCnt.h"
23 #include "include/core/SkSurfaceProps.h"
24 #include "include/core/SkTypes.h"
25 #include "include/effects/SkImageFilters.h"
26 #include "include/gpu/ganesh/GrBackendSurface.h"
27 #include "include/gpu/ganesh/GrDirectContext.h"
28 #include "include/gpu/ganesh/GrTypes.h"
29 #include "include/gpu/ganesh/SkImageGanesh.h"
30 #include "include/private/base/SkDebug.h"
31 #include "include/private/gpu/ganesh/GrTypesPriv.h"
32 #include "src/core/SkImageFilterCache.h"
33 #include "src/core/SkImageFilterTypes.h"
34 #include "src/core/SkSpecialImage.h"
35 #include "src/gpu/ganesh/GrColorInfo.h" // IWYU pragma: keep
36 #include "src/gpu/ganesh/GrDirectContextPriv.h"
37 #include "src/gpu/ganesh/GrSurfaceProxy.h"
38 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
39 #include "src/gpu/ganesh/GrTexture.h"
40 #include "src/gpu/ganesh/SkGr.h"
41 #include "src/gpu/ganesh/image/SkSpecialImage_Ganesh.h"
42 #include "tests/CtsEnforcement.h"
43 #include "tests/Test.h"
44
45 #include <cstddef>
46 #include <tuple>
47 #include <utility>
48
49 class GrRecordingContext;
50 struct GrContextOptions;
51
52 static const int kSmallerSize = 10;
53 static const int kPad = 3;
54 static const int kFullSize = kSmallerSize + 2 * kPad;
55
create_bm()56 static SkBitmap create_bm() {
57 SkImageInfo ii = SkImageInfo::Make(kFullSize, kFullSize, kRGBA_8888_SkColorType,
58 kPremul_SkAlphaType);
59
60 SkBitmap bm;
61 bm.allocPixels(ii);
62 bm.eraseColor(SK_ColorTRANSPARENT);
63 bm.setImmutable();
64 return bm;
65 }
66
make_filter()67 static sk_sp<SkImageFilter> make_filter() {
68 sk_sp<SkColorFilter> filter(SkColorFilters::Blend(SK_ColorBLUE, SkBlendMode::kSrcIn));
69 return SkImageFilters::ColorFilter(std::move(filter), nullptr, nullptr);
70 }
71
72 // Ensure the cache can return a cached image
test_find_existing(skiatest::Reporter * reporter,const sk_sp<SkSpecialImage> & image,const sk_sp<SkSpecialImage> & subset)73 static void test_find_existing(skiatest::Reporter* reporter,
74 const sk_sp<SkSpecialImage>& image,
75 const sk_sp<SkSpecialImage>& subset) {
76 static const size_t kCacheSize = 1000000;
77 sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
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
83 SkIPoint offset = SkIPoint::Make(3, 4);
84 auto filter = make_filter();
85 cache->set(key1, filter.get(), skif::FilterResult(image, skif::LayerSpace<SkIPoint>(offset)));
86
87 skif::FilterResult foundImage;
88 REPORTER_ASSERT(reporter, cache->get(key1, &foundImage));
89 REPORTER_ASSERT(reporter,
90 SkIRect::MakeXYWH(offset.fX, offset.fY, image->width(), image->height()) ==
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.
test_dont_find_if_diff_key(skiatest::Reporter * reporter,const sk_sp<SkSpecialImage> & image,const sk_sp<SkSpecialImage> & subset)98 static void test_dont_find_if_diff_key(skiatest::Reporter* reporter,
99 const sk_sp<SkSpecialImage>& image,
100 const sk_sp<SkSpecialImage>& subset) {
101 static const size_t kCacheSize = 1000000;
102 sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
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
113 SkIPoint offset = SkIPoint::Make(3, 4);
114 auto filter = make_filter();
115 cache->set(key0, filter.get(), skif::FilterResult(image, skif::LayerSpace<SkIPoint>(offset)));
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
test_internal_purge(skiatest::Reporter * reporter,const sk_sp<SkSpecialImage> & image)125 static void test_internal_purge(skiatest::Reporter* reporter, const sk_sp<SkSpecialImage>& image) {
126 SkASSERT(image->getSize());
127 const size_t kCacheSize = image->getSize() + 10;
128 sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
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
134 SkIPoint offset = SkIPoint::Make(3, 4);
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(),
144 skif::FilterResult(image, skif::LayerSpace<SkIPoint>(offset)));
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
test_explicit_purging(skiatest::Reporter * reporter,const sk_sp<SkSpecialImage> & image,const sk_sp<SkSpecialImage> & subset)151 static void test_explicit_purging(skiatest::Reporter* reporter,
152 const sk_sp<SkSpecialImage>& image,
153 const sk_sp<SkSpecialImage>& subset) {
154 static const size_t kCacheSize = 1000000;
155 sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
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
161 SkIPoint offset = SkIPoint::Make(3, 4);
162 auto filter1 = make_filter();
163 auto filter2 = make_filter();
164 cache->set(key1, filter1.get(),
165 skif::FilterResult(image, skif::LayerSpace<SkIPoint>(offset)));
166 cache->set(key2, filter2.get(),
167 skif::FilterResult(image, skif::LayerSpace<SkIPoint>(offset)));
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
DEF_TEST(ImageFilterCache_RasterBacked,reporter)187 DEF_TEST(ImageFilterCache_RasterBacked, reporter) {
188 SkBitmap srcBM = create_bm();
189
190 const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
191
192 sk_sp<SkSpecialImage> fullImg(SkSpecialImages::MakeFromRaster(full, srcBM, SkSurfaceProps()));
193
194 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
195
196 sk_sp<SkSpecialImage> subsetImg(
197 SkSpecialImages::MakeFromRaster(subset, srcBM, SkSurfaceProps()));
198
199 test_find_existing(reporter, fullImg, subsetImg);
200 test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
201 test_internal_purge(reporter, fullImg);
202 test_explicit_purging(reporter, fullImg, subsetImg);
203 }
204
205
206 // Shared test code for both the raster and gpu-backed image cases
test_image_backed(skiatest::Reporter * reporter,GrRecordingContext * rContext,const sk_sp<SkImage> & srcImage)207 static void test_image_backed(skiatest::Reporter* reporter,
208 GrRecordingContext* rContext,
209 const sk_sp<SkImage>& srcImage) {
210 const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
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
219 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
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);
230 test_internal_purge(reporter, fullImg);
231 test_explicit_purging(reporter, fullImg, subsetImg);
232 }
233
DEF_TEST(ImageFilterCache_ImageBackedRaster,reporter)234 DEF_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
create_proxy_view(GrRecordingContext * rContext)242 static GrSurfaceProxyView create_proxy_view(GrRecordingContext* rContext) {
243 SkBitmap srcBM = create_bm();
244 return std::get<0>(GrMakeUncachedBitmapProxyView(rContext, srcBM));
245 }
246
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU,reporter,ctxInfo,CtsEnforcement::kNever)247 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU,
248 reporter,
249 ctxInfo,
250 CtsEnforcement::kNever) {
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
265 GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
266 sk_sp<SkImage> srcImage(SkImages::BorrowTextureFrom(dContext,
267 backendTex,
268 texOrigin,
269 kRGBA_8888_SkColorType,
270 kPremul_SkAlphaType,
271 nullptr,
272 nullptr,
273 nullptr));
274 if (!srcImage) {
275 return;
276 }
277
278 GrSurfaceOrigin readBackOrigin;
279 GrBackendTexture readBackBackendTex;
280 bool ok = SkImages::GetBackendTextureFromImage(
281 srcImage, &readBackBackendTex, false, &readBackOrigin);
282 REPORTER_ASSERT(reporter, ok);
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
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_GPUBacked,reporter,ctxInfo,CtsEnforcement::kNever)296 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_GPUBacked,
297 reporter,
298 ctxInfo,
299 CtsEnforcement::kNever) {
300 auto dContext = ctxInfo.directContext();
301
302 GrSurfaceProxyView srcView = create_proxy_view(dContext);
303 if (!srcView.proxy()) {
304 return;
305 }
306
307 const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
308
309 sk_sp<SkSpecialImage> fullImg(SkSpecialImages::MakeDeferredFromGpu(
310 dContext,
311 full,
312 kNeedNewImageUniqueID_SpecialImage,
313 srcView,
314 {GrColorType::kRGBA_8888, kPremul_SkAlphaType, nullptr},
315 SkSurfaceProps()));
316
317 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
318
319 sk_sp<SkSpecialImage> subsetImg(SkSpecialImages::MakeDeferredFromGpu(
320 dContext,
321 subset,
322 kNeedNewImageUniqueID_SpecialImage,
323 std::move(srcView),
324 {GrColorType::kRGBA_8888, kPremul_SkAlphaType, nullptr},
325 SkSurfaceProps()));
326
327 test_find_existing(reporter, fullImg, subsetImg);
328 test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
329 test_internal_purge(reporter, fullImg);
330 test_explicit_purging(reporter, fullImg, subsetImg);
331 }
332
DEF_SERIAL_TEST(PurgeImageFilterCache,r)333 DEF_SERIAL_TEST(PurgeImageFilterCache, r) {
334 auto cache = SkImageFilterCache::Get(SkImageFilterCache::CreateIfNecessary::kNo);
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.
342 SkGraphics::PurgeResourceCache();
343 cache = SkImageFilterCache::Get(SkImageFilterCache::CreateIfNecessary::kNo);
344
345 // PurgeCache should not have created it.
346 REPORTER_ASSERT(r, !cache);
347
348 {
349 cache = SkImageFilterCache::Get(SkImageFilterCache::CreateIfNecessary::kYes);
350 REPORTER_ASSERT(r, cache);
351 }
352 }
353