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