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 "Test.h"
9
10#include "SkBitmap.h"
11#include "SkImage.h"
12#include "SkImageFilter.h"
13#include "SkImageFilterCache.h"
14#include "SkMatrix.h"
15#include "SkSpecialImage.h"
16
17static const int kSmallerSize = 10;
18static const int kPad = 3;
19static const int kFullSize = kSmallerSize + 2 * kPad;
20
21static SkBitmap create_bm() {
22    SkBitmap bm;
23    bm.allocN32Pixels(kFullSize, kFullSize, true);
24    bm.eraseColor(SK_ColorTRANSPARENT);
25    return bm;
26}
27
28// Ensure the cache can return a cached image
29static void test_find_existing(skiatest::Reporter* reporter,
30                               const sk_sp<SkSpecialImage>& image,
31                               const sk_sp<SkSpecialImage>& subset) {
32    static const size_t kCacheSize = 1000000;
33    sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
34
35    SkIRect clip = SkIRect::MakeWH(100, 100);
36    SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
37    SkImageFilterCacheKey key2(0, SkMatrix::I(), clip, subset->uniqueID(), subset->subset());
38
39    SkIPoint offset = SkIPoint::Make(3, 4);
40    cache->set(key1, image.get(), offset, nullptr);
41
42    SkIPoint foundOffset;
43
44    sk_sp<SkSpecialImage> foundImage = cache->get(key1, &foundOffset);
45    REPORTER_ASSERT(reporter, foundImage);
46    REPORTER_ASSERT(reporter, offset == foundOffset);
47
48    REPORTER_ASSERT(reporter, !cache->get(key2, &foundOffset));
49}
50
51// If either id is different or the clip or the matrix are different the
52// cached image won't be found. Even if it is caching the same bitmap.
53static void test_dont_find_if_diff_key(skiatest::Reporter* reporter,
54                                       const sk_sp<SkSpecialImage>& image,
55                                       const sk_sp<SkSpecialImage>& subset) {
56    static const size_t kCacheSize = 1000000;
57    sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
58
59    SkIRect clip1 = SkIRect::MakeWH(100, 100);
60    SkIRect clip2 = SkIRect::MakeWH(200, 200);
61    SkImageFilterCacheKey key0(0, SkMatrix::I(), clip1, image->uniqueID(), image->subset());
62    SkImageFilterCacheKey key1(1, SkMatrix::I(), clip1, image->uniqueID(), image->subset());
63    SkImageFilterCacheKey key2(0, SkMatrix::MakeTrans(5, 5), clip1,
64                                   image->uniqueID(), image->subset());
65    SkImageFilterCacheKey key3(0, SkMatrix::I(), clip2, image->uniqueID(), image->subset());
66    SkImageFilterCacheKey key4(0, SkMatrix::I(), clip1, subset->uniqueID(), subset->subset());
67
68    SkIPoint offset = SkIPoint::Make(3, 4);
69    cache->set(key0, image.get(), offset, nullptr);
70
71    SkIPoint foundOffset;
72    REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset));
73    REPORTER_ASSERT(reporter, !cache->get(key2, &foundOffset));
74    REPORTER_ASSERT(reporter, !cache->get(key3, &foundOffset));
75    REPORTER_ASSERT(reporter, !cache->get(key4, &foundOffset));
76}
77
78// Test purging when the max cache size is exceeded
79static void test_internal_purge(skiatest::Reporter* reporter, const sk_sp<SkSpecialImage>& image) {
80    SkASSERT(image->getSize());
81    const size_t kCacheSize = image->getSize() + 10;
82    sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
83
84    SkIRect clip = SkIRect::MakeWH(100, 100);
85    SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
86    SkImageFilterCacheKey key2(1, SkMatrix::I(), clip, image->uniqueID(), image->subset());
87
88    SkIPoint offset = SkIPoint::Make(3, 4);
89    cache->set(key1, image.get(), offset, nullptr);
90
91    SkIPoint foundOffset;
92
93    REPORTER_ASSERT(reporter, cache->get(key1, &foundOffset));
94
95    // This should knock the first one out of the cache
96    cache->set(key2, image.get(), offset, nullptr);
97
98    REPORTER_ASSERT(reporter, cache->get(key2, &foundOffset));
99    REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset));
100}
101
102// Exercise the purgeByKey and purge methods
103static void test_explicit_purging(skiatest::Reporter* reporter,
104                                  const sk_sp<SkSpecialImage>& image,
105                                  const sk_sp<SkSpecialImage>& subset) {
106    static const size_t kCacheSize = 1000000;
107    sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
108
109    SkIRect clip = SkIRect::MakeWH(100, 100);
110    SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
111    SkImageFilterCacheKey key2(1, SkMatrix::I(), clip, subset->uniqueID(), image->subset());
112
113    SkIPoint offset = SkIPoint::Make(3, 4);
114    cache->set(key1, image.get(), offset, nullptr);
115    cache->set(key2, image.get(), offset, nullptr);
116    SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->count());)
117
118    SkIPoint foundOffset;
119
120    REPORTER_ASSERT(reporter, cache->get(key1, &foundOffset));
121    REPORTER_ASSERT(reporter, cache->get(key2, &foundOffset));
122
123    cache->purgeByKeys(&key1, 1);
124    SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->count());)
125
126    REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset));
127    REPORTER_ASSERT(reporter, cache->get(key2, &foundOffset));
128
129    cache->purge();
130    SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->count());)
131
132    REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset));
133    REPORTER_ASSERT(reporter, !cache->get(key2, &foundOffset));
134}
135
136DEF_TEST(ImageFilterCache_RasterBacked, reporter) {
137    SkBitmap srcBM = create_bm();
138
139    const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
140
141    sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeFromRaster(full, srcBM));
142
143    const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
144
145    sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeFromRaster(subset, srcBM));
146
147    test_find_existing(reporter, fullImg, subsetImg);
148    test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
149    test_internal_purge(reporter, fullImg);
150    test_explicit_purging(reporter, fullImg, subsetImg);
151}
152
153
154// Shared test code for both the raster and gpu-backed image cases
155static void test_image_backed(skiatest::Reporter* reporter, const sk_sp<SkImage>& srcImage) {
156    const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
157    SkColorSpace* legacyColorSpace = nullptr;
158
159    sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeFromImage(full, srcImage, legacyColorSpace));
160
161    const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
162
163    sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeFromImage(subset, srcImage,
164                                                                  legacyColorSpace));
165
166    test_find_existing(reporter, fullImg, subsetImg);
167    test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
168    test_internal_purge(reporter, fullImg);
169    test_explicit_purging(reporter, fullImg, subsetImg);
170}
171
172DEF_TEST(ImageFilterCache_ImageBackedRaster, reporter) {
173    SkBitmap srcBM = create_bm();
174
175    sk_sp<SkImage> srcImage(SkImage::MakeFromBitmap(srcBM));
176
177    test_image_backed(reporter, srcImage);
178}
179
180#if SK_SUPPORT_GPU
181#include "GrContext.h"
182#include "GrContextPriv.h"
183#include "GrProxyProvider.h"
184#include "GrResourceProvider.h"
185#include "GrSurfaceProxyPriv.h"
186#include "GrTest.h"
187#include "GrTexture.h"
188#include "GrTextureProxy.h"
189
190static sk_sp<GrTextureProxy> create_proxy(GrProxyProvider* proxyProvider) {
191    SkBitmap srcBM = create_bm();
192
193    GrSurfaceDesc desc;
194    desc.fFlags  = kNone_GrSurfaceFlags;
195    desc.fOrigin = kTopLeft_GrSurfaceOrigin;
196    desc.fWidth  = kFullSize;
197    desc.fHeight = kFullSize;
198    desc.fConfig = kRGBA_8888_GrPixelConfig;
199
200    return proxyProvider->createTextureProxy(desc, SkBudgeted::kYes,
201                                             srcBM.getPixels(), srcBM.rowBytes());
202}
203
204DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU, reporter, ctxInfo) {
205    GrContext* context = ctxInfo.grContext();
206
207    sk_sp<GrTextureProxy> srcProxy(create_proxy(context->contextPriv().proxyProvider()));
208    if (!srcProxy) {
209        return;
210    }
211
212    if (!srcProxy->instantiate(context->contextPriv().resourceProvider())) {
213        return;
214    }
215    GrTexture* tex = srcProxy->priv().peekTexture();
216
217    GrBackendTexture backendTex = tex->getBackendTexture();
218
219    GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
220    sk_sp<SkImage> srcImage(SkImage::MakeFromTexture(context,
221                                                     backendTex,
222                                                     texOrigin,
223                                                     kRGBA_8888_SkColorType,
224                                                     kPremul_SkAlphaType, nullptr,
225                                                     nullptr, nullptr));
226    if (!srcImage) {
227        return;
228    }
229
230    GrSurfaceOrigin readBackOrigin;
231    GrBackendObject readBackHandle = srcImage->getTextureHandle(false, &readBackOrigin);
232    // TODO: Make it so we can check this (see skbug.com/5019)
233#if 0
234    if (readBackHandle != tex->getTextureHandle()) {
235        ERRORF(reporter, "backend mismatch %d %d\n",
236                       (int)readBackHandle, (int)tex->getTextureHandle());
237    }
238    REPORTER_ASSERT(reporter, readBackHandle == tex->getTextureHandle());
239#else
240    REPORTER_ASSERT(reporter, SkToBool(readBackHandle));
241#endif
242    if (readBackOrigin != texOrigin) {
243        ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, texOrigin);
244    }
245    REPORTER_ASSERT(reporter, readBackOrigin == texOrigin);
246
247    test_image_backed(reporter, srcImage);
248}
249
250DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_GPUBacked, reporter, ctxInfo) {
251    GrContext* context = ctxInfo.grContext();
252
253    sk_sp<GrTextureProxy> srcProxy(create_proxy(context->contextPriv().proxyProvider()));
254    if (!srcProxy) {
255        return;
256    }
257
258    const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
259
260    sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeDeferredFromGpu(
261                                                              context, full,
262                                                              kNeedNewImageUniqueID_SpecialImage,
263                                                              srcProxy, nullptr));
264
265    const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
266
267    sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeDeferredFromGpu(
268                                                                context, subset,
269                                                                kNeedNewImageUniqueID_SpecialImage,
270                                                                srcProxy, nullptr));
271
272    test_find_existing(reporter, fullImg, subsetImg);
273    test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
274    test_internal_purge(reporter, fullImg);
275    test_explicit_purging(reporter, fullImg, subsetImg);
276}
277#endif
278