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 "SkMipMap.h"
     14 #include "SkPicture.h"
     15 #include "SkPictureRecorder.h"
     16 #include "SkResourceCache.h"
     17 #include "SkSurface.h"
     18 
     19 ////////////////////////////////////////////////////////////////////////////////////////
     20 
     21 enum LockedState {
     22     kNotLocked,
     23     kLocked,
     24 };
     25 
     26 enum CachedState {
     27     kNotInCache,
     28     kInCache,
     29 };
     30 
     31 static void check_data(skiatest::Reporter* reporter, const SkCachedData* data,
     32                        int refcnt, CachedState cacheState, LockedState lockedState) {
     33     REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt);
     34     REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState));
     35     bool isLocked = (data->data() != nullptr);
     36     REPORTER_ASSERT(reporter, isLocked == (lockedState == kLocked));
     37 }
     38 
     39 static void test_mipmapcache(skiatest::Reporter* reporter, SkResourceCache* cache) {
     40     cache->purgeAll();
     41 
     42     SkBitmap src;
     43     src.allocN32Pixels(5, 5);
     44     src.setImmutable();
     45 
     46     const SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
     47 
     48     const SkMipMap* mipmap = SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(src), colorMode,
     49                                                        cache);
     50     REPORTER_ASSERT(reporter, nullptr == mipmap);
     51 
     52     mipmap = SkMipMapCache::AddAndRef(src, colorMode, cache);
     53     REPORTER_ASSERT(reporter, mipmap);
     54 
     55     {
     56         const SkMipMap* mm = SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(src), colorMode,
     57                                                        cache);
     58         REPORTER_ASSERT(reporter, mm);
     59         REPORTER_ASSERT(reporter, mm == mipmap);
     60         mm->unref();
     61     }
     62 
     63     check_data(reporter, mipmap, 2, kInCache, kLocked);
     64 
     65     mipmap->unref();
     66     // tricky, since technically after this I'm no longer an owner, but since the cache is
     67     // local, I know it won't get purged behind my back
     68     check_data(reporter, mipmap, 1, kInCache, kNotLocked);
     69 
     70     // find us again
     71     mipmap = SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(src), colorMode, cache);
     72     check_data(reporter, mipmap, 2, kInCache, kLocked);
     73 
     74     cache->purgeAll();
     75     check_data(reporter, mipmap, 1, kNotInCache, kLocked);
     76 
     77     mipmap->unref();
     78 }
     79 
     80 static void test_mipmap_notify(skiatest::Reporter* reporter, SkResourceCache* cache) {
     81     const SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
     82     const int N = 3;
     83 
     84     SkBitmap src[N];
     85     for (int i = 0; i < N; ++i) {
     86         src[i].allocN32Pixels(5, 5);
     87         src[i].setImmutable();
     88         SkMipMapCache::AddAndRef(src[i], colorMode, cache)->unref();
     89     }
     90 
     91     for (int i = 0; i < N; ++i) {
     92         const auto desc = SkBitmapCacheDesc::Make(src[i]);
     93         const SkMipMap* mipmap = SkMipMapCache::FindAndRef(desc, colorMode, cache);
     94         if (cache) {
     95             // if cache is null, we're working on the global cache, and other threads might purge
     96             // it, making this check fragile.
     97             REPORTER_ASSERT(reporter, mipmap);
     98         }
     99         SkSafeUnref(mipmap);
    100 
    101         src[i].reset(); // delete the underlying pixelref, which *should* remove us from the cache
    102 
    103         mipmap = SkMipMapCache::FindAndRef(desc, colorMode, cache);
    104         REPORTER_ASSERT(reporter, !mipmap);
    105     }
    106 }
    107 
    108 #include "SkDiscardableMemoryPool.h"
    109 
    110 static SkDiscardableMemoryPool* gPool = 0;
    111 static SkDiscardableMemory* pool_factory(size_t bytes) {
    112     SkASSERT(gPool);
    113     return gPool->create(bytes);
    114 }
    115 
    116 static void testBitmapCache_discarded_bitmap(skiatest::Reporter* reporter, SkResourceCache* cache,
    117                                              SkResourceCache::DiscardableFactory factory) {
    118     test_mipmapcache(reporter, cache);
    119     test_mipmap_notify(reporter, cache);
    120 }
    121 
    122 DEF_TEST(BitmapCache_discarded_bitmap, reporter) {
    123     const size_t byteLimit = 100 * 1024;
    124     {
    125         SkResourceCache cache(byteLimit);
    126         testBitmapCache_discarded_bitmap(reporter, &cache, nullptr);
    127     }
    128     {
    129         sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Create(byteLimit, nullptr));
    130         gPool = pool.get();
    131         SkResourceCache::DiscardableFactory factory = pool_factory;
    132         SkResourceCache cache(factory);
    133         testBitmapCache_discarded_bitmap(reporter, &cache, factory);
    134     }
    135 }
    136 
    137 static void test_discarded_image(skiatest::Reporter* reporter, const SkMatrix& transform,
    138                                  sk_sp<SkImage> (*buildImage)()) {
    139     auto surface(SkSurface::MakeRasterN32Premul(10, 10));
    140     SkCanvas* canvas = surface->getCanvas();
    141 
    142     // SkBitmapCache is global, so other threads could be evicting our bitmaps.  Loop a few times
    143     // to mitigate this risk.
    144     const unsigned kRepeatCount = 42;
    145     for (unsigned i = 0; i < kRepeatCount; ++i) {
    146         SkAutoCanvasRestore acr(canvas, true);
    147 
    148         sk_sp<SkImage> image(buildImage());
    149 
    150         // always use high quality to ensure caching when scaled
    151         SkPaint paint;
    152         paint.setFilterQuality(kHigh_SkFilterQuality);
    153 
    154         // draw the image (with a transform, to tickle different code paths) to ensure
    155         // any associated resources get cached
    156         canvas->concat(transform);
    157         canvas->drawImage(image, 0, 0, &paint);
    158 
    159         const auto desc = SkBitmapCacheDesc::Make(image.get());
    160 
    161         // delete the image
    162         image.reset(nullptr);
    163 
    164         // all resources should have been purged
    165         SkBitmap result;
    166         REPORTER_ASSERT(reporter, !SkBitmapCache::Find(desc, &result));
    167     }
    168 }
    169 
    170 
    171 // Verify that associated bitmap cache entries are purged on SkImage destruction.
    172 DEF_TEST(BitmapCache_discarded_image, reporter) {
    173     // Cache entries associated with SkImages fall into two categories:
    174     //
    175     // 1) generated image bitmaps (managed by the image cacherator)
    176     // 2) scaled/resampled bitmaps (cached when HQ filters are used)
    177     //
    178     // To exercise the first cache type, we use generated/picture-backed SkImages.
    179     // To exercise the latter, we draw scaled bitmap images using HQ filters.
    180 
    181     const SkMatrix xforms[] = {
    182         SkMatrix::MakeScale(1, 1),
    183         SkMatrix::MakeScale(1.7f, 0.5f),
    184     };
    185 
    186     for (size_t i = 0; i < SK_ARRAY_COUNT(xforms); ++i) {
    187         test_discarded_image(reporter, xforms[i], []() {
    188             auto surface(SkSurface::MakeRasterN32Premul(10, 10));
    189             surface->getCanvas()->clear(SK_ColorCYAN);
    190             return surface->makeImageSnapshot();
    191         });
    192 
    193         test_discarded_image(reporter, xforms[i], []() {
    194             SkPictureRecorder recorder;
    195             SkCanvas* canvas = recorder.beginRecording(10, 10);
    196             canvas->clear(SK_ColorCYAN);
    197             return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(),
    198                                             SkISize::Make(10, 10), nullptr, nullptr,
    199                                             SkImage::BitDepth::kU8,
    200                                             SkColorSpace::MakeSRGB());
    201         });
    202     }
    203 }
    204