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 17 static const int kSmallerSize = 10; 18 static const int kPad = 3; 19 static const int kFullSize = kSmallerSize + 2 * kPad; 20 21 static 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 29 static 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. 53 static 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 79 static 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 103 static 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 136 DEF_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 155 static 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 172 DEF_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 "GrResourceProvider.h" 184 #include "GrSurfaceProxyPriv.h" 185 #include "GrTest.h" 186 #include "GrTexture.h" 187 #include "GrTextureProxy.h" 188 189 static sk_sp<GrTextureProxy> create_proxy(GrResourceProvider* resourceProvider) { 190 SkBitmap srcBM = create_bm(); 191 192 GrSurfaceDesc desc; 193 desc.fConfig = kRGBA_8888_GrPixelConfig; 194 desc.fFlags = kNone_GrSurfaceFlags; 195 desc.fWidth = kFullSize; 196 desc.fHeight = kFullSize; 197 198 return GrSurfaceProxy::MakeDeferred(resourceProvider, 199 desc, SkBudgeted::kYes, 200 srcBM.getPixels(), 201 srcBM.rowBytes()); 202 } 203 204 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU, reporter, ctxInfo) { 205 GrContext* context = ctxInfo.grContext(); 206 207 sk_sp<GrTextureProxy> srcProxy(create_proxy(context->resourceProvider())); 208 if (!srcProxy) { 209 return; 210 } 211 212 if (!srcProxy->instantiate(context->resourceProvider())) { 213 return; 214 } 215 GrTexture* tex = srcProxy->priv().peekTexture(); 216 217 GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(), 218 kFullSize, 219 kFullSize, 220 kRGBA_8888_GrPixelConfig, 221 tex->getTextureHandle()); 222 GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin; 223 sk_sp<SkImage> srcImage(SkImage::MakeFromTexture(context, 224 backendTex, 225 texOrigin, 226 kPremul_SkAlphaType, nullptr)); 227 if (!srcImage) { 228 return; 229 } 230 231 GrSurfaceOrigin readBackOrigin; 232 GrBackendObject readBackHandle = srcImage->getTextureHandle(false, &readBackOrigin); 233 // TODO: Make it so we can check this (see skbug.com/5019) 234 #if 0 235 if (readBackHandle != tex->getTextureHandle()) { 236 ERRORF(reporter, "backend mismatch %d %d\n", 237 (int)readBackHandle, (int)tex->getTextureHandle()); 238 } 239 REPORTER_ASSERT(reporter, readBackHandle == tex->getTextureHandle()); 240 #else 241 REPORTER_ASSERT(reporter, SkToBool(readBackHandle)); 242 #endif 243 if (readBackOrigin != texOrigin) { 244 ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, texOrigin); 245 } 246 REPORTER_ASSERT(reporter, readBackOrigin == texOrigin); 247 248 test_image_backed(reporter, srcImage); 249 } 250 251 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_GPUBacked, reporter, ctxInfo) { 252 GrContext* context = ctxInfo.grContext(); 253 254 sk_sp<GrTextureProxy> srcProxy(create_proxy(context->resourceProvider())); 255 if (!srcProxy) { 256 return; 257 } 258 259 const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize); 260 261 sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeDeferredFromGpu( 262 context, full, 263 kNeedNewImageUniqueID_SpecialImage, 264 srcProxy, nullptr)); 265 266 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); 267 268 sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeDeferredFromGpu( 269 context, subset, 270 kNeedNewImageUniqueID_SpecialImage, 271 srcProxy, nullptr)); 272 273 test_find_existing(reporter, fullImg, subsetImg); 274 test_dont_find_if_diff_key(reporter, fullImg, subsetImg); 275 test_internal_purge(reporter, fullImg); 276 test_explicit_purging(reporter, fullImg, subsetImg); 277 } 278 #endif 279