1/* 2 * Copyright 2014 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#include "SkBitmapCache.h" 10#include "SkCanvas.h" 11#include "SkDiscardableMemoryPool.h" 12#include "SkGraphics.h" 13#include "SkResourceCache.h" 14#include "SkSurface.h" 15 16static const int kCanvasSize = 1; 17static const int kBitmapSize = 16; 18static const int kScale = 8; 19 20static bool is_in_scaled_image_cache(const SkBitmap& orig, 21 SkScalar xScale, 22 SkScalar yScale) { 23 SkBitmap scaled; 24 float roundedImageWidth = SkScalarRoundToScalar(orig.width() * xScale); 25 float roundedImageHeight = SkScalarRoundToScalar(orig.height() * yScale); 26 return SkBitmapCache::Find(orig, roundedImageWidth, roundedImageHeight, &scaled); 27} 28 29// Draw a scaled bitmap, then return true if it has been cached. 30static bool test_scaled_image_cache_usage() { 31 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(kCanvasSize, kCanvasSize)); 32 SkCanvas* canvas = surface->getCanvas(); 33 SkBitmap bitmap; 34 bitmap.allocN32Pixels(kBitmapSize, kBitmapSize); 35 bitmap.eraseColor(0xFFFFFFFF); 36 SkScalar xScale = SkIntToScalar(kScale); 37 SkScalar yScale = xScale / 2; 38 SkScalar xScaledSize = SkIntToScalar(kBitmapSize) * xScale; 39 SkScalar yScaledSize = SkIntToScalar(kBitmapSize) * yScale; 40 canvas->clipRect(SkRect::MakeLTRB(0, 0, xScaledSize, yScaledSize)); 41 SkPaint paint; 42 paint.setFilterQuality(kHigh_SkFilterQuality); 43 44 canvas->drawBitmapRect(bitmap, 45 SkRect::MakeLTRB(0, 0, xScaledSize, yScaledSize), 46 &paint); 47 48 return is_in_scaled_image_cache(bitmap, xScale, yScale); 49} 50 51// http://crbug.com/389439 52DEF_TEST(ResourceCache_SingleAllocationByteLimit, reporter) { 53 size_t originalByteLimit = SkGraphics::GetResourceCacheTotalByteLimit(); 54 size_t originalAllocationLimit = 55 SkGraphics::GetResourceCacheSingleAllocationByteLimit(); 56 57 size_t size = kBitmapSize * kScale * kBitmapSize * kScale 58 * SkColorTypeBytesPerPixel(kN32_SkColorType); 59 60 SkGraphics::SetResourceCacheTotalByteLimit(0); // clear cache 61 SkGraphics::SetResourceCacheTotalByteLimit(2 * size); 62 SkGraphics::SetResourceCacheSingleAllocationByteLimit(0); // No limit 63 64 REPORTER_ASSERT(reporter, test_scaled_image_cache_usage()); 65 66 SkGraphics::SetResourceCacheTotalByteLimit(0); // clear cache 67 SkGraphics::SetResourceCacheTotalByteLimit(2 * size); 68 SkGraphics::SetResourceCacheSingleAllocationByteLimit(size * 2); // big enough 69 70 REPORTER_ASSERT(reporter, test_scaled_image_cache_usage()); 71 72 SkGraphics::SetResourceCacheTotalByteLimit(0); // clear cache 73 SkGraphics::SetResourceCacheTotalByteLimit(2 * size); 74 SkGraphics::SetResourceCacheSingleAllocationByteLimit(size / 2); // too small 75 76 REPORTER_ASSERT(reporter, !test_scaled_image_cache_usage()); 77 78 SkGraphics::SetResourceCacheSingleAllocationByteLimit(originalAllocationLimit); 79 SkGraphics::SetResourceCacheTotalByteLimit(originalByteLimit); 80} 81 82//////////////////////////////////////////////////////////////////////////////////////// 83 84static void make_bitmap(SkBitmap* bitmap, const SkImageInfo& info, SkBitmap::Allocator* allocator) { 85 if (allocator) { 86 bitmap->setInfo(info); 87 allocator->allocPixelRef(bitmap, 0); 88 } else { 89 bitmap->allocPixels(info); 90 } 91} 92 93// http://skbug.com/2894 94DEF_TEST(BitmapCache_add_rect, reporter) { 95 SkResourceCache::DiscardableFactory factory = SkResourceCache::GetDiscardableFactory(); 96 SkBitmap::Allocator* allocator = SkBitmapCache::GetAllocator(); 97 98 SkAutoTDelete<SkResourceCache> cache; 99 if (factory) { 100 cache.reset(SkNEW_ARGS(SkResourceCache, (factory))); 101 } else { 102 const size_t byteLimit = 100 * 1024; 103 cache.reset(SkNEW_ARGS(SkResourceCache, (byteLimit))); 104 } 105 SkBitmap cachedBitmap; 106 make_bitmap(&cachedBitmap, SkImageInfo::MakeN32Premul(5, 5), allocator); 107 cachedBitmap.setImmutable(); 108 109 SkBitmap bm; 110 SkIRect rect = SkIRect::MakeWH(5, 5); 111 uint32_t cachedID = cachedBitmap.getGenerationID(); 112 SkPixelRef* cachedPR = cachedBitmap.pixelRef(); 113 114 // Wrong subset size 115 REPORTER_ASSERT(reporter, !SkBitmapCache::Add(cachedPR, SkIRect::MakeWH(4, 6), cachedBitmap, cache)); 116 REPORTER_ASSERT(reporter, !SkBitmapCache::Find(cachedID, rect, &bm, cache)); 117 // Wrong offset value 118 REPORTER_ASSERT(reporter, !SkBitmapCache::Add(cachedPR, SkIRect::MakeXYWH(-1, 0, 5, 5), cachedBitmap, cache)); 119 REPORTER_ASSERT(reporter, !SkBitmapCache::Find(cachedID, rect, &bm, cache)); 120 121 // Should not be in the cache 122 REPORTER_ASSERT(reporter, !SkBitmapCache::Find(cachedID, rect, &bm, cache)); 123 124 REPORTER_ASSERT(reporter, SkBitmapCache::Add(cachedPR, rect, cachedBitmap, cache)); 125 // Should be in the cache, we just added it 126 REPORTER_ASSERT(reporter, SkBitmapCache::Find(cachedID, rect, &bm, cache)); 127} 128 129#include "SkMipMap.h" 130 131enum LockedState { 132 kNotLocked, 133 kLocked, 134}; 135 136enum CachedState { 137 kNotInCache, 138 kInCache, 139}; 140 141static void check_data(skiatest::Reporter* reporter, const SkCachedData* data, 142 int refcnt, CachedState cacheState, LockedState lockedState) { 143 REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt); 144 REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState)); 145 bool isLocked = (data->data() != NULL); 146 REPORTER_ASSERT(reporter, isLocked == (lockedState == kLocked)); 147} 148 149static void test_mipmapcache(skiatest::Reporter* reporter, SkResourceCache* cache) { 150 cache->purgeAll(); 151 152 SkBitmap src; 153 src.allocN32Pixels(5, 5); 154 src.setImmutable(); 155 156 const SkMipMap* mipmap = SkMipMapCache::FindAndRef(src, cache); 157 REPORTER_ASSERT(reporter, NULL == mipmap); 158 159 mipmap = SkMipMapCache::AddAndRef(src, cache); 160 REPORTER_ASSERT(reporter, mipmap); 161 162 { 163 const SkMipMap* mm = SkMipMapCache::FindAndRef(src, cache); 164 REPORTER_ASSERT(reporter, mm); 165 REPORTER_ASSERT(reporter, mm == mipmap); 166 mm->unref(); 167 } 168 169 check_data(reporter, mipmap, 2, kInCache, kLocked); 170 171 mipmap->unref(); 172 // tricky, since technically after this I'm no longer an owner, but since the cache is 173 // local, I know it won't get purged behind my back 174 check_data(reporter, mipmap, 1, kInCache, kNotLocked); 175 176 // find us again 177 mipmap = SkMipMapCache::FindAndRef(src, cache); 178 check_data(reporter, mipmap, 2, kInCache, kLocked); 179 180 cache->purgeAll(); 181 check_data(reporter, mipmap, 1, kNotInCache, kLocked); 182 183 mipmap->unref(); 184} 185 186static void test_mipmap_notify(skiatest::Reporter* reporter, SkResourceCache* cache) { 187 const int N = 3; 188 SkBitmap src[N]; 189 for (int i = 0; i < N; ++i) { 190 src[i].allocN32Pixels(5, 5); 191 src[i].setImmutable(); 192 SkMipMapCache::AddAndRef(src[i], cache)->unref(); 193 } 194 195 for (int i = 0; i < N; ++i) { 196 const SkMipMap* mipmap = SkMipMapCache::FindAndRef(src[i], cache); 197 if (cache) { 198 // if cache is null, we're working on the global cache, and other threads might purge 199 // it, making this check fragile. 200 REPORTER_ASSERT(reporter, mipmap); 201 } 202 SkSafeUnref(mipmap); 203 204 src[i].reset(); // delete the underlying pixelref, which *should* remove us from the cache 205 206 mipmap = SkMipMapCache::FindAndRef(src[i], cache); 207 REPORTER_ASSERT(reporter, !mipmap); 208 } 209} 210 211static void test_bitmap_notify(skiatest::Reporter* reporter, SkResourceCache* cache) { 212 const SkIRect subset = SkIRect::MakeWH(5, 5); 213 const int N = 3; 214 SkBitmap src[N], dst[N]; 215 for (int i = 0; i < N; ++i) { 216 src[i].allocN32Pixels(5, 5); 217 src[i].setImmutable(); 218 dst[i].allocN32Pixels(5, 5); 219 dst[i].setImmutable(); 220 SkBitmapCache::Add(src[i].pixelRef(), subset, dst[i], cache); 221 } 222 223 for (int i = 0; i < N; ++i) { 224 const uint32_t genID = src[i].getGenerationID(); 225 SkBitmap result; 226 bool found = SkBitmapCache::Find(genID, subset, &result, cache); 227 if (cache) { 228 // if cache is null, we're working on the global cache, and other threads might purge 229 // it, making this check fragile. 230 REPORTER_ASSERT(reporter, found); 231 } 232 233 src[i].reset(); // delete the underlying pixelref, which *should* remove us from the cache 234 235 found = SkBitmapCache::Find(genID, subset, &result, cache); 236 REPORTER_ASSERT(reporter, !found); 237 } 238} 239 240DEF_TEST(BitmapCache_discarded_bitmap, reporter) { 241 SkResourceCache::DiscardableFactory factory = SkResourceCache::GetDiscardableFactory(); 242 SkBitmap::Allocator* allocator = SkBitmapCache::GetAllocator(); 243 244 SkAutoTDelete<SkResourceCache> cache; 245 if (factory) { 246 cache.reset(SkNEW_ARGS(SkResourceCache, (factory))); 247 } else { 248 const size_t byteLimit = 100 * 1024; 249 cache.reset(SkNEW_ARGS(SkResourceCache, (byteLimit))); 250 } 251 SkBitmap cachedBitmap; 252 make_bitmap(&cachedBitmap, SkImageInfo::MakeN32Premul(5, 5), allocator); 253 cachedBitmap.setImmutable(); 254 cachedBitmap.unlockPixels(); 255 256 SkBitmap bm; 257 SkIRect rect = SkIRect::MakeWH(5, 5); 258 259 // Add a bitmap to the cache. 260 REPORTER_ASSERT(reporter, SkBitmapCache::Add(cachedBitmap.pixelRef(), rect, cachedBitmap, cache)); 261 REPORTER_ASSERT(reporter, SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache)); 262 263 // Finding more than once works fine. 264 REPORTER_ASSERT(reporter, SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache)); 265 bm.unlockPixels(); 266 267 // Drop the pixels in the bitmap. 268 if (factory) { 269 REPORTER_ASSERT(reporter, SkGetGlobalDiscardableMemoryPool()->getRAMUsed() > 0); 270 SkGetGlobalDiscardableMemoryPool()->dumpPool(); 271 REPORTER_ASSERT(reporter, SkGetGlobalDiscardableMemoryPool()->getRAMUsed() == 0); 272 273 // The bitmap is not in the cache since it has been dropped. 274 REPORTER_ASSERT(reporter, !SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache)); 275 } 276 277 make_bitmap(&cachedBitmap, SkImageInfo::MakeN32Premul(5, 5), allocator); 278 cachedBitmap.setImmutable(); 279 cachedBitmap.unlockPixels(); 280 281 // We can add the bitmap back to the cache and find it again. 282 REPORTER_ASSERT(reporter, SkBitmapCache::Add(cachedBitmap.pixelRef(), rect, cachedBitmap, cache)); 283 REPORTER_ASSERT(reporter, SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache)); 284 285 test_mipmapcache(reporter, cache); 286 test_bitmap_notify(reporter, cache); 287 test_mipmap_notify(reporter, cache); 288} 289