Home | History | Annotate | Download | only in src
      1 /*
      2     Copyright 2010 Google Inc.
      3 
      4     Licensed under the Apache License, Version 2.0 (the "License");
      5     you may not use this file except in compliance with the License.
      6     You may obtain a copy of the License at
      7 
      8          http://www.apache.org/licenses/LICENSE-2.0
      9 
     10     Unless required by applicable law or agreed to in writing, software
     11     distributed under the License is distributed on an "AS IS" BASIS,
     12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13     See the License for the specific language governing permissions and
     14     limitations under the License.
     15  */
     16 
     17 
     18 #include "GrTextureCache.h"
     19 #include "GrTexture.h"
     20 
     21 GrTextureEntry::GrTextureEntry(const GrTextureKey& key, GrTexture* texture)
     22         : fKey(key), fTexture(texture) {
     23     fLockCount = 0;
     24     fPrev = fNext = NULL;
     25 
     26     // we assume ownership of the texture, and will unref it when we die
     27     GrAssert(texture);
     28 }
     29 
     30 GrTextureEntry::~GrTextureEntry() {
     31     fTexture->unref();
     32 }
     33 
     34 #if GR_DEBUG
     35 void GrTextureEntry::validate() const {
     36     GrAssert(fLockCount >= 0);
     37     GrAssert(fTexture);
     38     fTexture->validate();
     39 }
     40 #endif
     41 
     42 ///////////////////////////////////////////////////////////////////////////////
     43 
     44 GrTextureCache::GrTextureCache(int maxCount, size_t maxBytes) :
     45         fMaxCount(maxCount),
     46         fMaxBytes(maxBytes) {
     47     fEntryCount          = 0;
     48     fEntryBytes          = 0;
     49     fClientDetachedCount = 0;
     50     fClientDetachedBytes = 0;
     51 
     52     fHead = fTail = NULL;
     53 }
     54 
     55 GrTextureCache::~GrTextureCache() {
     56     GrAutoTextureCacheValidate atcv(this);
     57 
     58     this->removeAll();
     59 }
     60 
     61 void GrTextureCache::getLimits(int* maxTextures, size_t* maxTextureBytes) const{
     62     if (maxTextures) {
     63         *maxTextures = fMaxCount;
     64     }
     65     if (maxTextureBytes) {
     66         *maxTextureBytes = fMaxBytes;
     67     }
     68 }
     69 
     70 void GrTextureCache::setLimits(int maxTextures, size_t maxTextureBytes) {
     71     bool smaller = (maxTextures < fMaxCount) || (maxTextureBytes < fMaxBytes);
     72 
     73     fMaxCount = maxTextures;
     74     fMaxBytes = maxTextureBytes;
     75 
     76     if (smaller) {
     77         this->purgeAsNeeded();
     78     }
     79 }
     80 
     81 void GrTextureCache::internalDetach(GrTextureEntry* entry,
     82                                     bool clientDetach) {
     83     GrTextureEntry* prev = entry->fPrev;
     84     GrTextureEntry* next = entry->fNext;
     85 
     86     if (prev) {
     87         prev->fNext = next;
     88     } else {
     89         fHead = next;
     90     }
     91     if (next) {
     92         next->fPrev = prev;
     93     } else {
     94         fTail = prev;
     95     }
     96 
     97     // update our stats
     98     if (clientDetach) {
     99         fClientDetachedCount += 1;
    100         fClientDetachedBytes += entry->texture()->sizeInBytes();
    101     } else {
    102         fEntryCount -= 1;
    103         fEntryBytes -= entry->texture()->sizeInBytes();
    104     }
    105 }
    106 
    107 void GrTextureCache::attachToHead(GrTextureEntry* entry,
    108                                   bool clientReattach) {
    109     entry->fPrev = NULL;
    110     entry->fNext = fHead;
    111     if (fHead) {
    112         fHead->fPrev = entry;
    113     }
    114     fHead = entry;
    115     if (NULL == fTail) {
    116         fTail = entry;
    117     }
    118 
    119     // update our stats
    120     if (clientReattach) {
    121         fClientDetachedCount -= 1;
    122         fClientDetachedBytes -= entry->texture()->sizeInBytes();
    123     } else {
    124         fEntryCount += 1;
    125         fEntryBytes += entry->texture()->sizeInBytes();
    126     }
    127 }
    128 
    129 class GrTextureCache::Key {
    130     typedef GrTextureEntry T;
    131 
    132     const GrTextureKey& fKey;
    133 public:
    134     Key(const GrTextureKey& key) : fKey(key) {}
    135 
    136     uint32_t getHash() const { return fKey.hashIndex(); }
    137 
    138     static bool LT(const T& entry, const Key& key) {
    139         return entry.key() < key.fKey;
    140     }
    141     static bool EQ(const T& entry, const Key& key) {
    142         return entry.key() == key.fKey;
    143     }
    144 #if GR_DEBUG
    145     static uint32_t GetHash(const T& entry) {
    146         return entry.key().hashIndex();
    147     }
    148     static bool LT(const T& a, const T& b) {
    149         return a.key() < b.key();
    150     }
    151     static bool EQ(const T& a, const T& b) {
    152         return a.key() == b.key();
    153     }
    154 #endif
    155 };
    156 
    157 GrTextureEntry* GrTextureCache::findAndLock(const GrTextureKey& key) {
    158     GrAutoTextureCacheValidate atcv(this);
    159 
    160     GrTextureEntry* entry = fCache.find(key);
    161     if (entry) {
    162         this->internalDetach(entry, false);
    163         this->attachToHead(entry, false);
    164         // mark the entry as "busy" so it doesn't get purged
    165         entry->lock();
    166     }
    167     return entry;
    168 }
    169 
    170 GrTextureEntry* GrTextureCache::createAndLock(const GrTextureKey& key,
    171                                               GrTexture* texture) {
    172     GrAutoTextureCacheValidate atcv(this);
    173 
    174     GrTextureEntry* entry = new GrTextureEntry(key, texture);
    175 
    176     this->attachToHead(entry, false);
    177     fCache.insert(key, entry);
    178 
    179 #if GR_DUMP_TEXTURE_UPLOAD
    180     GrPrintf("--- add texture to cache %p, count=%d bytes= %d %d\n",
    181              entry, fEntryCount, texture->sizeInBytes(), fEntryBytes);
    182 #endif
    183 
    184     // mark the entry as "busy" so it doesn't get purged
    185     entry->lock();
    186     this->purgeAsNeeded();
    187     return entry;
    188 }
    189 
    190 void GrTextureCache::detach(GrTextureEntry* entry) {
    191     internalDetach(entry, true);
    192     fCache.remove(entry->fKey, entry);
    193 }
    194 
    195 void GrTextureCache::reattachAndUnlock(GrTextureEntry* entry) {
    196     attachToHead(entry, true);
    197     fCache.insert(entry->key(), entry);
    198     unlock(entry);
    199 }
    200 
    201 void GrTextureCache::unlock(GrTextureEntry* entry) {
    202     GrAutoTextureCacheValidate atcv(this);
    203 
    204     GrAssert(entry);
    205     GrAssert(entry->isLocked());
    206     GrAssert(fCache.find(entry->key()));
    207 
    208     entry->unlock();
    209     this->purgeAsNeeded();
    210 }
    211 
    212 void GrTextureCache::purgeAsNeeded() {
    213     GrAutoTextureCacheValidate atcv(this);
    214 
    215     GrTextureEntry* entry = fTail;
    216     while (entry) {
    217         if (fEntryCount <= fMaxCount && fEntryBytes <= fMaxBytes) {
    218             break;
    219         }
    220 
    221         GrTextureEntry* prev = entry->fPrev;
    222         if (!entry->isLocked()) {
    223             // remove from our cache
    224             fCache.remove(entry->fKey, entry);
    225 
    226             // remove from our llist
    227             this->internalDetach(entry, false);
    228 
    229 #if GR_DUMP_TEXTURE_UPLOAD
    230             GrPrintf("--- ~texture from cache %p [%d %d]\n", entry->texture(),
    231                      entry->texture()->width(),
    232                      entry->texture()->height());
    233 #endif
    234             delete entry;
    235         }
    236         entry = prev;
    237     }
    238 }
    239 
    240 void GrTextureCache::removeAll() {
    241     GrAssert(!fClientDetachedCount);
    242     GrAssert(!fClientDetachedBytes);
    243 
    244     GrTextureEntry* entry = fHead;
    245     while (entry) {
    246         GrAssert(!entry->isLocked());
    247 
    248         GrTextureEntry* next = entry->fNext;
    249         delete entry;
    250         entry = next;
    251     }
    252 
    253     fCache.removeAll();
    254     fHead = fTail = NULL;
    255     fEntryCount = 0;
    256     fEntryBytes = 0;
    257 }
    258 
    259 ///////////////////////////////////////////////////////////////////////////////
    260 
    261 #if GR_DEBUG
    262 static int countMatches(const GrTextureEntry* head, const GrTextureEntry* target) {
    263     const GrTextureEntry* entry = head;
    264     int count = 0;
    265     while (entry) {
    266         if (target == entry) {
    267             count += 1;
    268         }
    269         entry = entry->next();
    270     }
    271     return count;
    272 }
    273 
    274 #if GR_DEBUG
    275 static bool both_zero_or_nonzero(int count, size_t bytes) {
    276     return (count == 0 && bytes == 0) || (count > 0 && bytes > 0);
    277 }
    278 #endif
    279 
    280 void GrTextureCache::validate() const {
    281     GrAssert(!fHead == !fTail);
    282     GrAssert(both_zero_or_nonzero(fEntryCount, fEntryBytes));
    283     GrAssert(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes));
    284     GrAssert(fClientDetachedBytes <= fEntryBytes);
    285     GrAssert(fClientDetachedCount <= fEntryCount);
    286     GrAssert((fEntryCount - fClientDetachedCount) == fCache.count());
    287 
    288     fCache.validate();
    289 
    290     GrTextureEntry* entry = fHead;
    291     int count = 0;
    292     size_t bytes = 0;
    293     while (entry) {
    294         entry->validate();
    295         GrAssert(fCache.find(entry->key()));
    296         count += 1;
    297         bytes += entry->texture()->sizeInBytes();
    298         entry = entry->fNext;
    299     }
    300     GrAssert(count == fEntryCount - fClientDetachedCount);
    301     GrAssert(bytes == fEntryBytes  - fClientDetachedBytes);
    302 
    303     count = 0;
    304     for (entry = fTail; entry; entry = entry->fPrev) {
    305         count += 1;
    306     }
    307     GrAssert(count == fEntryCount - fClientDetachedCount);
    308 
    309     for (int i = 0; i < count; i++) {
    310         int matches = countMatches(fHead, fCache.getArray()[i]);
    311         GrAssert(1 == matches);
    312     }
    313 }
    314 #endif
    315 
    316 
    317