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