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