Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2013 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 #if SK_SUPPORT_GPU
      9 
     10 #include "GrContextFactory.h"
     11 #include "GrResourceCache.h"
     12 #include "SkGpuDevice.h"
     13 #include "Test.h"
     14 
     15 static const int gWidth = 640;
     16 static const int gHeight = 480;
     17 
     18 ////////////////////////////////////////////////////////////////////////////////
     19 static void test_cache(skiatest::Reporter* reporter,
     20                        GrContext* context,
     21                        SkCanvas* canvas) {
     22     const SkIRect size = SkIRect::MakeWH(gWidth, gHeight);
     23 
     24     SkBitmap src;
     25     src.allocN32Pixels(size.width(), size.height());
     26     src.eraseColor(SK_ColorBLACK);
     27     size_t srcSize = src.getSize();
     28 
     29     size_t initialCacheSize;
     30     context->getResourceCacheUsage(NULL, &initialCacheSize);
     31 
     32     int oldMaxNum;
     33     size_t oldMaxBytes;
     34     context->getResourceCacheLimits(&oldMaxNum, &oldMaxBytes);
     35 
     36     // Set the cache limits so we can fit 10 "src" images and the
     37     // max number of textures doesn't matter
     38     size_t maxCacheSize = initialCacheSize + 10*srcSize;
     39     context->setResourceCacheLimits(1000, maxCacheSize);
     40 
     41     SkBitmap readback;
     42     readback.allocN32Pixels(size.width(), size.height());
     43 
     44     for (int i = 0; i < 100; ++i) {
     45         canvas->drawBitmap(src, 0, 0);
     46         canvas->readPixels(size, &readback);
     47 
     48         // "modify" the src texture
     49         src.notifyPixelsChanged();
     50 
     51         size_t curCacheSize;
     52         context->getResourceCacheUsage(NULL, &curCacheSize);
     53 
     54         // we should never go over the size limit
     55         REPORTER_ASSERT(reporter, curCacheSize <= maxCacheSize);
     56     }
     57 
     58     context->setResourceCacheLimits(oldMaxNum, oldMaxBytes);
     59 }
     60 
     61 class TestResource : public GrCacheable {
     62     static const size_t kDefaultSize = 100;
     63 
     64 public:
     65     SK_DECLARE_INST_COUNT(TestResource);
     66     TestResource(size_t size = kDefaultSize)
     67         : fCache(NULL)
     68         , fToDelete(NULL)
     69         , fSize(size) {
     70         ++fAlive;
     71     }
     72 
     73     ~TestResource() {
     74         --fAlive;
     75         if (NULL != fToDelete) {
     76             // Breaks our little 2-element cycle below.
     77             fToDelete->setDeleteWhenDestroyed(NULL, NULL);
     78             fCache->deleteResource(fToDelete->getCacheEntry());
     79         }
     80     }
     81 
     82     void setSize(size_t size) {
     83         fSize = size;
     84         this->didChangeGpuMemorySize();
     85     }
     86 
     87     size_t gpuMemorySize() const SK_OVERRIDE { return fSize; }
     88 
     89     bool isValidOnGpu() const SK_OVERRIDE { return true; }
     90 
     91     static int alive() { return fAlive; }
     92 
     93     void setDeleteWhenDestroyed(GrResourceCache* cache, TestResource* resource) {
     94         fCache = cache;
     95         fToDelete = resource;
     96     }
     97 
     98 private:
     99     GrResourceCache* fCache;
    100     TestResource* fToDelete;
    101     size_t fSize;
    102     static int fAlive;
    103 
    104     typedef GrCacheable INHERITED;
    105 };
    106 int TestResource::fAlive = 0;
    107 
    108 static void test_purge_invalidated(skiatest::Reporter* reporter, GrContext* context) {
    109     GrCacheID::Domain domain = GrCacheID::GenerateDomain();
    110     GrCacheID::Key keyData;
    111     keyData.fData64[0] = 5;
    112     keyData.fData64[1] = 18;
    113     GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType();
    114     GrResourceKey key(GrCacheID(domain, keyData), t, 0);
    115 
    116     GrResourceCache cache(5, 30000);
    117 
    118     // Add two resources with the same key that delete each other from the cache when destroyed.
    119     TestResource* a = new TestResource();
    120     TestResource* b = new TestResource();
    121     cache.addResource(key, a);
    122     cache.addResource(key, b);
    123     // Circle back.
    124     a->setDeleteWhenDestroyed(&cache, b);
    125     b->setDeleteWhenDestroyed(&cache, a);
    126     a->unref();
    127     b->unref();
    128 
    129     // Add a third independent resource also with the same key.
    130     GrCacheable* r = new TestResource();
    131     cache.addResource(key, r);
    132     r->unref();
    133 
    134     // Invalidate all three, all three should be purged and destroyed.
    135     REPORTER_ASSERT(reporter, 3 == TestResource::alive());
    136     const GrResourceInvalidatedMessage msg = { key };
    137     SkMessageBus<GrResourceInvalidatedMessage>::Post(msg);
    138     cache.purgeAsNeeded();
    139     REPORTER_ASSERT(reporter, 0 == TestResource::alive());
    140 }
    141 
    142 static void test_cache_delete_on_destruction(skiatest::Reporter* reporter,
    143                                              GrContext* context) {
    144     GrCacheID::Domain domain = GrCacheID::GenerateDomain();
    145     GrCacheID::Key keyData;
    146     keyData.fData64[0] = 5;
    147     keyData.fData64[1] = 0;
    148     GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType();
    149 
    150     GrResourceKey key(GrCacheID(domain, keyData), t, 0);
    151 
    152     {
    153         {
    154             GrResourceCache cache(3, 30000);
    155             TestResource* a = new TestResource();
    156             TestResource* b = new TestResource();
    157             cache.addResource(key, a);
    158             cache.addResource(key, b);
    159 
    160             a->setDeleteWhenDestroyed(&cache, b);
    161             b->setDeleteWhenDestroyed(&cache, a);
    162 
    163             a->unref();
    164             b->unref();
    165             REPORTER_ASSERT(reporter, 2 == TestResource::alive());
    166         }
    167         REPORTER_ASSERT(reporter, 0 == TestResource::alive());
    168     }
    169     {
    170         GrResourceCache cache(3, 30000);
    171         TestResource* a = new TestResource();
    172         TestResource* b = new TestResource();
    173         cache.addResource(key, a);
    174         cache.addResource(key, b);
    175 
    176         a->setDeleteWhenDestroyed(&cache, b);
    177         b->setDeleteWhenDestroyed(&cache, a);
    178 
    179         a->unref();
    180         b->unref();
    181 
    182         cache.deleteResource(a->getCacheEntry());
    183 
    184         REPORTER_ASSERT(reporter, 0 == TestResource::alive());
    185     }
    186 }
    187 
    188 static void test_resource_size_changed(skiatest::Reporter* reporter,
    189                                        GrContext* context) {
    190     GrCacheID::Domain domain = GrCacheID::GenerateDomain();
    191     GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType();
    192 
    193     GrCacheID::Key key1Data;
    194     key1Data.fData64[0] = 0;
    195     key1Data.fData64[1] = 0;
    196     GrResourceKey key1(GrCacheID(domain, key1Data), t, 0);
    197 
    198     GrCacheID::Key key2Data;
    199     key2Data.fData64[0] = 1;
    200     key2Data.fData64[1] = 0;
    201     GrResourceKey key2(GrCacheID(domain, key2Data), t, 0);
    202 
    203     // Test changing resources sizes (both increase & decrease).
    204     {
    205         GrResourceCache cache(2, 300);
    206 
    207         TestResource* a = new TestResource(0);
    208         a->setSize(100); // Test didChangeGpuMemorySize() when not in the cache.
    209         cache.addResource(key1, a);
    210         a->unref();
    211 
    212         TestResource* b = new TestResource(0);
    213         b->setSize(100);
    214         cache.addResource(key2, b);
    215         b->unref();
    216 
    217         REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes());
    218         REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
    219 
    220         static_cast<TestResource*>(cache.find(key2))->setSize(200);
    221         static_cast<TestResource*>(cache.find(key1))->setSize(50);
    222 
    223         REPORTER_ASSERT(reporter, 250 == cache.getCachedResourceBytes());
    224         REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
    225     }
    226 
    227     // Test increasing a resources size beyond the cache budget.
    228     {
    229         GrResourceCache cache(2, 300);
    230 
    231         TestResource* a = new TestResource(100);
    232         cache.addResource(key1, a);
    233         a->unref();
    234 
    235         TestResource* b = new TestResource(100);
    236         cache.addResource(key2, b);
    237         b->unref();
    238 
    239         REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes());
    240         REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
    241 
    242         static_cast<TestResource*>(cache.find(key2))->setSize(201);
    243         REPORTER_ASSERT(reporter, NULL == cache.find(key1));
    244 
    245         REPORTER_ASSERT(reporter, 201 == cache.getCachedResourceBytes());
    246         REPORTER_ASSERT(reporter, 1 == cache.getCachedResourceCount());
    247     }
    248 
    249     // Test changing the size of an exclusively-held resource.
    250     {
    251         GrResourceCache cache(2, 300);
    252 
    253         TestResource* a = new TestResource(100);
    254         cache.addResource(key1, a);
    255         cache.makeExclusive(a->getCacheEntry());
    256 
    257         TestResource* b = new TestResource(100);
    258         cache.addResource(key2, b);
    259         b->unref();
    260 
    261         REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes());
    262         REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
    263         REPORTER_ASSERT(reporter, NULL == cache.find(key1));
    264 
    265         a->setSize(200);
    266 
    267         REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes());
    268         REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
    269         // Internal resource cache validation will test the detached size (debug mode only).
    270 
    271         cache.makeNonExclusive(a->getCacheEntry());
    272         a->unref();
    273 
    274         REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes());
    275         REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
    276         REPORTER_ASSERT(reporter, NULL != cache.find(key1));
    277         // Internal resource cache validation will test the detached size (debug mode only).
    278     }
    279 }
    280 
    281 ////////////////////////////////////////////////////////////////////////////////
    282 DEF_GPUTEST(ResourceCache, reporter, factory) {
    283     for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
    284         GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type);
    285         if (!GrContextFactory::IsRenderingGLContext(glType)) {
    286             continue;
    287         }
    288         GrContext* context = factory->get(glType);
    289         if (NULL == context) {
    290             continue;
    291         }
    292 
    293         GrTextureDesc desc;
    294         desc.fConfig = kSkia8888_GrPixelConfig;
    295         desc.fFlags = kRenderTarget_GrTextureFlagBit;
    296         desc.fWidth = gWidth;
    297         desc.fHeight = gHeight;
    298 
    299         SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0));
    300         SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice, (context, texture.get())));
    301         SkCanvas canvas(device.get());
    302 
    303         test_cache(reporter, context, &canvas);
    304         test_purge_invalidated(reporter, context);
    305         test_cache_delete_on_destruction(reporter, context);
    306         test_resource_size_changed(reporter, context);
    307     }
    308 }
    309 
    310 #endif
    311