xref: /aosp_15_r20/external/skia/tests/ImageFilterCacheTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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