Home | History | Annotate | Download | only in tests
      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 "SkMakeUnique.h"
     14 #include "SkMipMap.h"
     15 #include "SkPicture.h"
     16 #include "SkPictureRecorder.h"
     17 #include "SkResourceCache.h"
     18 #include "SkSurface.h"
     19 
     20 ////////////////////////////////////////////////////////////////////////////////////////
     21 
     22 enum LockedState {
     23     kNotLocked,
     24     kLocked,
     25 };
     26 
     27 enum CachedState {
     28     kNotInCache,
     29     kInCache,
     30 };
     31 
     32 static void check_data(skiatest::Reporter* reporter, const SkCachedData* data,
     33                        int refcnt, CachedState cacheState, LockedState lockedState) {
     34     REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt);
     35     REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState));
     36     bool isLocked = (data->data() != nullptr);
     37     REPORTER_ASSERT(reporter, isLocked == (lockedState == kLocked));
     38 }
     39 
     40 static void test_mipmapcache(skiatest::Reporter* reporter, SkResourceCache* cache) {
     41     cache->purgeAll();
     42 
     43     SkBitmap src;
     44     src.allocN32Pixels(5, 5);
     45     src.setImmutable();
     46 
     47     const SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
     48 
     49     const SkMipMap* mipmap = SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(src), colorMode,
     50                                                        cache);
     51     REPORTER_ASSERT(reporter, nullptr == mipmap);
     52 
     53     mipmap = SkMipMapCache::AddAndRef(src, colorMode, cache);
     54     REPORTER_ASSERT(reporter, mipmap);
     55 
     56     {
     57         const SkMipMap* mm = SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(src), colorMode,
     58                                                        cache);
     59         REPORTER_ASSERT(reporter, mm);
     60         REPORTER_ASSERT(reporter, mm == mipmap);
     61         mm->unref();
     62     }
     63 
     64     check_data(reporter, mipmap, 2, kInCache, kLocked);
     65 
     66     mipmap->unref();
     67     // tricky, since technically after this I'm no longer an owner, but since the cache is
     68     // local, I know it won't get purged behind my back
     69     check_data(reporter, mipmap, 1, kInCache, kNotLocked);
     70 
     71     // find us again
     72     mipmap = SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(src), colorMode, cache);
     73     check_data(reporter, mipmap, 2, kInCache, kLocked);
     74 
     75     cache->purgeAll();
     76     check_data(reporter, mipmap, 1, kNotInCache, kLocked);
     77 
     78     mipmap->unref();
     79 }
     80 
     81 static void test_mipmap_notify(skiatest::Reporter* reporter, SkResourceCache* cache) {
     82     const SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
     83     const int N = 3;
     84 
     85     SkBitmap src[N];
     86     for (int i = 0; i < N; ++i) {
     87         src[i].allocN32Pixels(5, 5);
     88         src[i].setImmutable();
     89         SkMipMapCache::AddAndRef(src[i], colorMode, cache)->unref();
     90     }
     91 
     92     for (int i = 0; i < N; ++i) {
     93         const auto desc = SkBitmapCacheDesc::Make(src[i]);
     94         const SkMipMap* mipmap = SkMipMapCache::FindAndRef(desc, colorMode, cache);
     95         if (cache) {
     96             // if cache is null, we're working on the global cache, and other threads might purge
     97             // it, making this check fragile.
     98             REPORTER_ASSERT(reporter, mipmap);
     99         }
    100         SkSafeUnref(mipmap);
    101 
    102         src[i].reset(); // delete the underlying pixelref, which *should* remove us from the cache
    103 
    104         mipmap = SkMipMapCache::FindAndRef(desc, colorMode, cache);
    105         REPORTER_ASSERT(reporter, !mipmap);
    106     }
    107 }
    108 
    109 #include "SkDiscardableMemoryPool.h"
    110 
    111 static SkDiscardableMemoryPool* gPool = 0;
    112 static SkDiscardableMemory* pool_factory(size_t bytes) {
    113     SkASSERT(gPool);
    114     return gPool->create(bytes);
    115 }
    116 
    117 static void testBitmapCache_discarded_bitmap(skiatest::Reporter* reporter, SkResourceCache* cache,
    118                                              SkResourceCache::DiscardableFactory factory) {
    119     test_mipmapcache(reporter, cache);
    120     test_mipmap_notify(reporter, cache);
    121 }
    122 
    123 DEF_TEST(BitmapCache_discarded_bitmap, reporter) {
    124     const size_t byteLimit = 100 * 1024;
    125     {
    126         SkResourceCache cache(byteLimit);
    127         testBitmapCache_discarded_bitmap(reporter, &cache, nullptr);
    128     }
    129     {
    130         sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(byteLimit));
    131         gPool = pool.get();
    132         SkResourceCache::DiscardableFactory factory = pool_factory;
    133         SkResourceCache cache(factory);
    134         testBitmapCache_discarded_bitmap(reporter, &cache, factory);
    135     }
    136 }
    137 
    138 static void test_discarded_image(skiatest::Reporter* reporter, const SkMatrix& transform,
    139                                  sk_sp<SkImage> (*buildImage)()) {
    140     auto surface(SkSurface::MakeRasterN32Premul(10, 10));
    141     SkCanvas* canvas = surface->getCanvas();
    142 
    143     // SkBitmapCache is global, so other threads could be evicting our bitmaps.  Loop a few times
    144     // to mitigate this risk.
    145     const unsigned kRepeatCount = 42;
    146     for (unsigned i = 0; i < kRepeatCount; ++i) {
    147         SkAutoCanvasRestore acr(canvas, true);
    148 
    149         sk_sp<SkImage> image(buildImage());
    150 
    151         // always use high quality to ensure caching when scaled
    152         SkPaint paint;
    153         paint.setFilterQuality(kHigh_SkFilterQuality);
    154 
    155         // draw the image (with a transform, to tickle different code paths) to ensure
    156         // any associated resources get cached
    157         canvas->concat(transform);
    158         canvas->drawImage(image, 0, 0, &paint);
    159 
    160         const auto desc = SkBitmapCacheDesc::Make(image.get());
    161 
    162         // delete the image
    163         image.reset(nullptr);
    164 
    165         // all resources should have been purged
    166         SkBitmap result;
    167         REPORTER_ASSERT(reporter, !SkBitmapCache::Find(desc, &result));
    168     }
    169 }
    170 
    171 
    172 // Verify that associated bitmap cache entries are purged on SkImage destruction.
    173 DEF_TEST(BitmapCache_discarded_image, reporter) {
    174     // Cache entries associated with SkImages fall into two categories:
    175     //
    176     // 1) generated image bitmaps (managed by the image cacherator)
    177     // 2) scaled/resampled bitmaps (cached when HQ filters are used)
    178     //
    179     // To exercise the first cache type, we use generated/picture-backed SkImages.
    180     // To exercise the latter, we draw scaled bitmap images using HQ filters.
    181 
    182     const SkMatrix xforms[] = {
    183         SkMatrix::MakeScale(1, 1),
    184         SkMatrix::MakeScale(1.7f, 0.5f),
    185     };
    186 
    187     for (size_t i = 0; i < SK_ARRAY_COUNT(xforms); ++i) {
    188         test_discarded_image(reporter, xforms[i], []() {
    189             auto surface(SkSurface::MakeRasterN32Premul(10, 10));
    190             surface->getCanvas()->clear(SK_ColorCYAN);
    191             return surface->makeImageSnapshot();
    192         });
    193 
    194         test_discarded_image(reporter, xforms[i], []() {
    195             SkPictureRecorder recorder;
    196             SkCanvas* canvas = recorder.beginRecording(10, 10);
    197             canvas->clear(SK_ColorCYAN);
    198             return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(),
    199                                             SkISize::Make(10, 10), nullptr, nullptr,
    200                                             SkImage::BitDepth::kU8,
    201                                             SkColorSpace::MakeSRGB());
    202         });
    203     }
    204 }
    205 
    206 ///////////////////////////////////////////////////////////////////////////////////////////////////
    207 
    208 static void* gTestNamespace;
    209 
    210 struct TestKey : SkResourceCache::Key {
    211     int32_t fData;
    212 
    213     TestKey(int sharedID, int32_t data) : fData(data) {
    214         this->init(&gTestNamespace, sharedID, sizeof(fData));
    215     }
    216 };
    217 
    218 struct TestRec : SkResourceCache::Rec {
    219     enum {
    220         kDidInstall = 1 << 0,
    221     };
    222 
    223     TestKey fKey;
    224     int*    fFlags;
    225     bool    fCanBePurged;
    226 
    227     TestRec(int sharedID, int32_t data, int* flagPtr) : fKey(sharedID, data), fFlags(flagPtr) {
    228         fCanBePurged = false;
    229     }
    230 
    231     const Key& getKey() const override { return fKey; }
    232     size_t bytesUsed() const override { return 1024; /* just need a value */ }
    233     bool canBePurged() override { return fCanBePurged; }
    234     void postAddInstall(void*) override {
    235         *fFlags |= kDidInstall;
    236     }
    237     const char* getCategory() const override { return "test-category"; }
    238 };
    239 
    240 static void test_duplicate_add(SkResourceCache* cache, skiatest::Reporter* reporter,
    241                                bool purgable) {
    242     int sharedID = 1;
    243     int data = 0;
    244 
    245     int flags0 = 0, flags1 = 0;
    246 
    247     auto rec0 = skstd::make_unique<TestRec>(sharedID, data, &flags0);
    248     auto rec1 = skstd::make_unique<TestRec>(sharedID, data, &flags1);
    249     SkASSERT(rec0->getKey() == rec1->getKey());
    250 
    251     TestRec* r0 = rec0.get();   // save the bare-pointer since we will release rec0
    252     r0->fCanBePurged = purgable;
    253 
    254     REPORTER_ASSERT(reporter, !(flags0 & TestRec::kDidInstall));
    255     REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall));
    256 
    257     cache->add(rec0.release(), nullptr);
    258     REPORTER_ASSERT(reporter, flags0 & TestRec::kDidInstall);
    259     REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall));
    260     flags0 = 0; // reset the flag
    261 
    262     cache->add(rec1.release(), nullptr);
    263     if (purgable) {
    264         // we purged rec0, and did install rec1
    265         REPORTER_ASSERT(reporter, !(flags0 & TestRec::kDidInstall));
    266         REPORTER_ASSERT(reporter, flags1 & TestRec::kDidInstall);
    267     } else {
    268         // we re-used rec0 and did not install rec1
    269         REPORTER_ASSERT(reporter, flags0 & TestRec::kDidInstall);
    270         REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall));
    271         r0->fCanBePurged = true;  // so we can cleanup the cache
    272     }
    273 }
    274 
    275 /*
    276  *  Test behavior when the same key is added more than once.
    277  */
    278 DEF_TEST(ResourceCache_purge, reporter) {
    279     for (bool purgable : { false, true }) {
    280         {
    281             SkResourceCache cache(1024 * 1024);
    282             test_duplicate_add(&cache, reporter, purgable);
    283         }
    284         {
    285             SkResourceCache cache(SkDiscardableMemory::Create);
    286             test_duplicate_add(&cache, reporter, purgable);
    287         }
    288     }
    289 }
    290