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 "tests/Test.h"
9
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkColorFilter.h"
12 #include "include/core/SkImage.h"
13 #include "include/core/SkImageFilter.h"
14 #include "include/core/SkMatrix.h"
15 #include "include/effects/SkImageFilters.h"
16 #include "src/core/SkImageFilterCache.h"
17 #include "src/core/SkSpecialImage.h"
18
19 static const int kSmallerSize = 10;
20 static const int kPad = 3;
21 static const int kFullSize = kSmallerSize + 2 * kPad;
22
create_bm()23 static SkBitmap create_bm() {
24 SkImageInfo ii = SkImageInfo::Make(kFullSize, kFullSize, kRGBA_8888_SkColorType,
25 kPremul_SkAlphaType);
26
27 SkBitmap bm;
28 bm.allocPixels(ii);
29 bm.eraseColor(SK_ColorTRANSPARENT);
30 bm.setImmutable();
31 return bm;
32 }
33
make_filter()34 static sk_sp<SkImageFilter> make_filter() {
35 sk_sp<SkColorFilter> filter(SkColorFilters::Blend(SK_ColorBLUE, SkBlendMode::kSrcIn));
36 return SkImageFilters::ColorFilter(std::move(filter), nullptr, nullptr);
37 }
38
39 // Ensure the cache can return a cached image
test_find_existing(skiatest::Reporter * reporter,const sk_sp<SkSpecialImage> & image,const sk_sp<SkSpecialImage> & subset)40 static void test_find_existing(skiatest::Reporter* reporter,
41 const sk_sp<SkSpecialImage>& image,
42 const sk_sp<SkSpecialImage>& subset) {
43 static const size_t kCacheSize = 1000000;
44 sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
45
46 SkIRect clip = SkIRect::MakeWH(100, 100);
47 SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
48 SkImageFilterCacheKey key2(0, SkMatrix::I(), clip, subset->uniqueID(), subset->subset());
49
50 SkIPoint offset = SkIPoint::Make(3, 4);
51 auto filter = make_filter();
52 cache->set(key1, filter.get(), skif::FilterResult(image, skif::LayerSpace<SkIPoint>(offset)));
53
54 skif::FilterResult foundImage;
55 REPORTER_ASSERT(reporter, cache->get(key1, &foundImage));
56 REPORTER_ASSERT(reporter, offset == SkIPoint(foundImage.layerOrigin()));
57
58 REPORTER_ASSERT(reporter, !cache->get(key2, &foundImage));
59 }
60
61 // If either id is different or the clip or the matrix are different the
62 // 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)63 static void test_dont_find_if_diff_key(skiatest::Reporter* reporter,
64 const sk_sp<SkSpecialImage>& image,
65 const sk_sp<SkSpecialImage>& subset) {
66 static const size_t kCacheSize = 1000000;
67 sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
68
69 SkIRect clip1 = SkIRect::MakeWH(100, 100);
70 SkIRect clip2 = SkIRect::MakeWH(200, 200);
71 SkImageFilterCacheKey key0(0, SkMatrix::I(), clip1, image->uniqueID(), image->subset());
72 SkImageFilterCacheKey key1(1, SkMatrix::I(), clip1, image->uniqueID(), image->subset());
73 SkImageFilterCacheKey key2(0, SkMatrix::Translate(5, 5), clip1,
74 image->uniqueID(), image->subset());
75 SkImageFilterCacheKey key3(0, SkMatrix::I(), clip2, image->uniqueID(), image->subset());
76 SkImageFilterCacheKey key4(0, SkMatrix::I(), clip1, subset->uniqueID(), subset->subset());
77
78 SkIPoint offset = SkIPoint::Make(3, 4);
79 auto filter = make_filter();
80 cache->set(key0, filter.get(), skif::FilterResult(image, skif::LayerSpace<SkIPoint>(offset)));
81
82 skif::FilterResult foundImage;
83 REPORTER_ASSERT(reporter, !cache->get(key1, &foundImage));
84 REPORTER_ASSERT(reporter, !cache->get(key2, &foundImage));
85 REPORTER_ASSERT(reporter, !cache->get(key3, &foundImage));
86 REPORTER_ASSERT(reporter, !cache->get(key4, &foundImage));
87 }
88
89 // Test purging when the max cache size is exceeded
test_internal_purge(skiatest::Reporter * reporter,const sk_sp<SkSpecialImage> & image)90 static void test_internal_purge(skiatest::Reporter* reporter, const sk_sp<SkSpecialImage>& image) {
91 SkASSERT(image->getSize());
92 const size_t kCacheSize = image->getSize() + 10;
93 sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
94
95 SkIRect clip = SkIRect::MakeWH(100, 100);
96 SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
97 SkImageFilterCacheKey key2(1, SkMatrix::I(), clip, image->uniqueID(), image->subset());
98
99 SkIPoint offset = SkIPoint::Make(3, 4);
100 auto filter1 = make_filter();
101 cache->set(key1, filter1.get(), skif::FilterResult(image, skif::LayerSpace<SkIPoint>(offset)));
102
103 skif::FilterResult foundImage;
104 REPORTER_ASSERT(reporter, cache->get(key1, &foundImage));
105
106 // This should knock the first one out of the cache
107 auto filter2 = make_filter();
108 cache->set(key2, filter2.get(),
109 skif::FilterResult(image, skif::LayerSpace<SkIPoint>(offset)));
110
111 REPORTER_ASSERT(reporter, cache->get(key2, &foundImage));
112 REPORTER_ASSERT(reporter, !cache->get(key1, &foundImage));
113 }
114
115 // Exercise the purgeByKey and purge methods
test_explicit_purging(skiatest::Reporter * reporter,const sk_sp<SkSpecialImage> & image,const sk_sp<SkSpecialImage> & subset)116 static void test_explicit_purging(skiatest::Reporter* reporter,
117 const sk_sp<SkSpecialImage>& image,
118 const sk_sp<SkSpecialImage>& subset) {
119 static const size_t kCacheSize = 1000000;
120 sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
121
122 SkIRect clip = SkIRect::MakeWH(100, 100);
123 SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
124 SkImageFilterCacheKey key2(1, SkMatrix::I(), clip, subset->uniqueID(), image->subset());
125
126 SkIPoint offset = SkIPoint::Make(3, 4);
127 auto filter1 = make_filter();
128 auto filter2 = make_filter();
129 cache->set(key1, filter1.get(),
130 skif::FilterResult(image, skif::LayerSpace<SkIPoint>(offset)));
131 cache->set(key2, filter2.get(),
132 skif::FilterResult(image, skif::LayerSpace<SkIPoint>(offset)));
133 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->count());)
134
135 skif::FilterResult foundImage;
136 REPORTER_ASSERT(reporter, cache->get(key1, &foundImage));
137 REPORTER_ASSERT(reporter, cache->get(key2, &foundImage));
138
139 cache->purgeByImageFilter(filter1.get());
140 SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->count());)
141
142 REPORTER_ASSERT(reporter, !cache->get(key1, &foundImage));
143 REPORTER_ASSERT(reporter, cache->get(key2, &foundImage));
144
145 cache->purge();
146 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->count());)
147
148 REPORTER_ASSERT(reporter, !cache->get(key1, &foundImage));
149 REPORTER_ASSERT(reporter, !cache->get(key2, &foundImage));
150 }
151
DEF_TEST(ImageFilterCache_RasterBacked,reporter)152 DEF_TEST(ImageFilterCache_RasterBacked, reporter) {
153 SkBitmap srcBM = create_bm();
154
155 const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
156
157 sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeFromRaster(full, srcBM, SkSurfaceProps()));
158
159 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
160
161 sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeFromRaster(subset, srcBM,
162 SkSurfaceProps()));
163
164 test_find_existing(reporter, fullImg, subsetImg);
165 test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
166 test_internal_purge(reporter, fullImg);
167 test_explicit_purging(reporter, fullImg, subsetImg);
168 }
169
170
171 // 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)172 static void test_image_backed(skiatest::Reporter* reporter,
173 GrRecordingContext* rContext,
174 const sk_sp<SkImage>& srcImage) {
175 const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
176
177 sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeFromImage(rContext, full, srcImage,
178 SkSurfaceProps()));
179
180 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
181
182 sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeFromImage(rContext, subset, srcImage,
183 SkSurfaceProps()));
184
185 test_find_existing(reporter, fullImg, subsetImg);
186 test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
187 test_internal_purge(reporter, fullImg);
188 test_explicit_purging(reporter, fullImg, subsetImg);
189 }
190
DEF_TEST(ImageFilterCache_ImageBackedRaster,reporter)191 DEF_TEST(ImageFilterCache_ImageBackedRaster, reporter) {
192 SkBitmap srcBM = create_bm();
193
194 sk_sp<SkImage> srcImage(srcBM.asImage());
195
196 test_image_backed(reporter, nullptr, srcImage);
197 }
198
199 #include "include/gpu/GrDirectContext.h"
200 #include "src/gpu/GrDirectContextPriv.h"
201 #include "src/gpu/GrProxyProvider.h"
202 #include "src/gpu/GrResourceProvider.h"
203 #include "src/gpu/GrSurfaceProxyPriv.h"
204 #include "src/gpu/GrTexture.h"
205 #include "src/gpu/GrTextureProxy.h"
206 #include "src/gpu/SkGr.h"
207
create_proxy_view(GrRecordingContext * rContext)208 static GrSurfaceProxyView create_proxy_view(GrRecordingContext* rContext) {
209 SkBitmap srcBM = create_bm();
210 return std::get<0>(GrMakeUncachedBitmapProxyView(rContext, srcBM));
211 }
212
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU,reporter,ctxInfo)213 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU, reporter, ctxInfo) {
214 auto dContext = ctxInfo.directContext();
215
216 GrSurfaceProxyView srcView = create_proxy_view(dContext);
217 if (!srcView.proxy()) {
218 return;
219 }
220
221 if (!srcView.proxy()->instantiate(dContext->priv().resourceProvider())) {
222 return;
223 }
224 GrTexture* tex = srcView.proxy()->peekTexture();
225
226 GrBackendTexture backendTex = tex->getBackendTexture();
227
228 GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
229 sk_sp<SkImage> srcImage(SkImage::MakeFromTexture(dContext,
230 backendTex,
231 texOrigin,
232 kRGBA_8888_SkColorType,
233 kPremul_SkAlphaType, nullptr,
234 nullptr, nullptr));
235 if (!srcImage) {
236 return;
237 }
238
239 GrSurfaceOrigin readBackOrigin;
240 GrBackendTexture readBackBackendTex = srcImage->getBackendTexture(false, &readBackOrigin);
241 if (!GrBackendTexture::TestingOnly_Equals(readBackBackendTex, backendTex)) {
242 ERRORF(reporter, "backend mismatch\n");
243 }
244 REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(readBackBackendTex, backendTex));
245
246 if (readBackOrigin != texOrigin) {
247 ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, texOrigin);
248 }
249 REPORTER_ASSERT(reporter, readBackOrigin == texOrigin);
250
251 test_image_backed(reporter, dContext, srcImage);
252 }
253
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_GPUBacked,reporter,ctxInfo)254 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_GPUBacked, reporter, ctxInfo) {
255 auto dContext = ctxInfo.directContext();
256
257 GrSurfaceProxyView srcView = create_proxy_view(dContext);
258 if (!srcView.proxy()) {
259 return;
260 }
261
262 const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
263
264 sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeDeferredFromGpu(
265 dContext, full,
266 kNeedNewImageUniqueID_SpecialImage,
267 srcView,
268 GrColorType::kRGBA_8888, nullptr,
269 SkSurfaceProps()));
270
271 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
272
273 sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeDeferredFromGpu(
274 dContext, subset,
275 kNeedNewImageUniqueID_SpecialImage,
276 std::move(srcView),
277 GrColorType::kRGBA_8888, nullptr,
278 SkSurfaceProps()));
279
280 test_find_existing(reporter, fullImg, subsetImg);
281 test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
282 test_internal_purge(reporter, fullImg);
283 test_explicit_purging(reporter, fullImg, subsetImg);
284 }
285