Home | History | Annotate | Download | only in effects
      1 
      2 /*
      3  * Copyright 2012 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 #include "GrTextureStripAtlas.h"
     10 #include "SkPixelRef.h"
     11 #include "SkTSearch.h"
     12 #include "GrTexture.h"
     13 
     14 #ifdef SK_DEBUG
     15     #define VALIDATE this->validate()
     16 #else
     17     #define VALIDATE
     18 #endif
     19 
     20 class GrTextureStripAtlas::Hash : public SkTDynamicHash<GrTextureStripAtlas::AtlasEntry,
     21                                                         GrTextureStripAtlas::AtlasEntry::Key> {};
     22 
     23 int32_t GrTextureStripAtlas::gCacheCount = 0;
     24 
     25 GrTextureStripAtlas::Hash* GrTextureStripAtlas::gAtlasCache = NULL;
     26 
     27 GrTextureStripAtlas::Hash* GrTextureStripAtlas::GetCache() {
     28 
     29     if (NULL == gAtlasCache) {
     30         gAtlasCache = SkNEW(Hash);
     31     }
     32 
     33     return gAtlasCache;
     34 }
     35 
     36 // Remove the specified atlas from the cache
     37 void GrTextureStripAtlas::CleanUp(const GrContext*, void* info) {
     38     SkASSERT(info);
     39 
     40     AtlasEntry* entry = static_cast<AtlasEntry*>(info);
     41 
     42     // remove the cache entry
     43     GetCache()->remove(entry->fKey);
     44 
     45     // remove the actual entry
     46     SkDELETE(entry);
     47 
     48     if (0 == GetCache()->count()) {
     49         SkDELETE(gAtlasCache);
     50         gAtlasCache = NULL;
     51     }
     52 }
     53 
     54 GrTextureStripAtlas* GrTextureStripAtlas::GetAtlas(const GrTextureStripAtlas::Desc& desc) {
     55     AtlasEntry::Key key;
     56     key.setKeyData(desc.asKey());
     57     AtlasEntry* entry = GetCache()->find(key);
     58     if (NULL == entry) {
     59         entry = SkNEW(AtlasEntry);
     60 
     61         entry->fAtlas = SkNEW_ARGS(GrTextureStripAtlas, (desc));
     62         entry->fKey = key;
     63 
     64         desc.fContext->addCleanUp(CleanUp, entry);
     65 
     66         GetCache()->add(entry);
     67     }
     68 
     69     return entry->fAtlas;
     70 }
     71 
     72 GrTextureStripAtlas::GrTextureStripAtlas(GrTextureStripAtlas::Desc desc)
     73     : fCacheKey(sk_atomic_inc(&gCacheCount))
     74     , fLockedRows(0)
     75     , fDesc(desc)
     76     , fNumRows(desc.fHeight / desc.fRowHeight)
     77     , fTexture(NULL)
     78     , fRows(SkNEW_ARRAY(AtlasRow, fNumRows))
     79     , fLRUFront(NULL)
     80     , fLRUBack(NULL) {
     81     SkASSERT(fNumRows * fDesc.fRowHeight == fDesc.fHeight);
     82     this->initLRU();
     83     fNormalizedYHeight = SK_Scalar1 / fDesc.fHeight;
     84     VALIDATE;
     85 }
     86 
     87 GrTextureStripAtlas::~GrTextureStripAtlas() {
     88     SkDELETE_ARRAY(fRows);
     89 }
     90 
     91 int GrTextureStripAtlas::lockRow(const SkBitmap& data) {
     92     VALIDATE;
     93     if (0 == fLockedRows) {
     94         this->lockTexture();
     95         if (!fTexture) {
     96             return -1;
     97         }
     98     }
     99 
    100     int key = data.getGenerationID();
    101     int rowNumber = -1;
    102     int index = this->searchByKey(key);
    103 
    104     if (index >= 0) {
    105         // We already have the data in a row, so we can just return that row
    106         AtlasRow* row = fKeyTable[index];
    107         if (0 == row->fLocks) {
    108             this->removeFromLRU(row);
    109         }
    110         ++row->fLocks;
    111         ++fLockedRows;
    112 
    113         // Since all the rows are always stored in a contiguous array, we can save the memory
    114         // required for storing row numbers and just compute it with some pointer arithmetic
    115         rowNumber = static_cast<int>(row - fRows);
    116     } else {
    117         // ~index is the index where we will insert the new key to keep things sorted
    118         index = ~index;
    119 
    120         // We don't have this data cached, so pick the least recently used row to copy into
    121         AtlasRow* row = this->getLRU();
    122 
    123         ++fLockedRows;
    124 
    125         if (NULL == row) {
    126             // force a flush, which should unlock all the rows; then try again
    127             fDesc.fContext->flush();
    128             row = this->getLRU();
    129             if (NULL == row) {
    130                 --fLockedRows;
    131                 return -1;
    132             }
    133         }
    134 
    135         this->removeFromLRU(row);
    136 
    137         uint32_t oldKey = row->fKey;
    138 
    139         // If we are writing into a row that already held bitmap data, we need to remove the
    140         // reference to that genID which is stored in our sorted table of key values.
    141         if (oldKey != kEmptyAtlasRowKey) {
    142 
    143             // Find the entry in the list; if it's before the index where we plan on adding the new
    144             // entry, we decrement since it will shift elements ahead of it back by one.
    145             int oldIndex = this->searchByKey(oldKey);
    146             if (oldIndex < index) {
    147                 --index;
    148             }
    149 
    150             fKeyTable.remove(oldIndex);
    151         }
    152 
    153         row->fKey = key;
    154         row->fLocks = 1;
    155         fKeyTable.insert(index, 1, &row);
    156         rowNumber = static_cast<int>(row - fRows);
    157 
    158         SkAutoLockPixels lock(data);
    159 
    160         // Pass in the kDontFlush flag, since we know we're writing to a part of this texture
    161         // that is not currently in use
    162         fTexture->writePixels(0,  rowNumber * fDesc.fRowHeight,
    163                               fDesc.fWidth, fDesc.fRowHeight,
    164                               SkImageInfo2GrPixelConfig(data.info()),
    165                               data.getPixels(),
    166                               data.rowBytes(),
    167                               GrContext::kDontFlush_PixelOpsFlag);
    168     }
    169 
    170     SkASSERT(rowNumber >= 0);
    171     VALIDATE;
    172     return rowNumber;
    173 }
    174 
    175 void GrTextureStripAtlas::unlockRow(int row) {
    176     VALIDATE;
    177     --fRows[row].fLocks;
    178     --fLockedRows;
    179     SkASSERT(fRows[row].fLocks >= 0 && fLockedRows >= 0);
    180     if (0 == fRows[row].fLocks) {
    181         this->appendLRU(fRows + row);
    182     }
    183     if (0 == fLockedRows) {
    184         this->unlockTexture();
    185     }
    186     VALIDATE;
    187 }
    188 
    189 GrTextureStripAtlas::AtlasRow* GrTextureStripAtlas::getLRU() {
    190     // Front is least-recently-used
    191     AtlasRow* row = fLRUFront;
    192     return row;
    193 }
    194 
    195 void GrTextureStripAtlas::lockTexture() {
    196     GrSurfaceDesc texDesc;
    197     texDesc.fWidth = fDesc.fWidth;
    198     texDesc.fHeight = fDesc.fHeight;
    199     texDesc.fConfig = fDesc.fConfig;
    200 
    201     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
    202     GrUniqueKey key;
    203     GrUniqueKey::Builder builder(&key, kDomain, 1);
    204     builder[0] = static_cast<uint32_t>(fCacheKey);
    205     builder.finish();
    206 
    207     fTexture = fDesc.fContext->textureProvider()->findAndRefTextureByUniqueKey(key);
    208     if (NULL == fTexture) {
    209         fTexture = fDesc.fContext->textureProvider()->createTexture(texDesc, true, NULL, 0);
    210         if (!fTexture) {
    211             return;
    212         }
    213         fDesc.fContext->textureProvider()->assignUniqueKeyToTexture(key, fTexture);
    214         // This is a new texture, so all of our cache info is now invalid
    215         this->initLRU();
    216         fKeyTable.rewind();
    217     }
    218     SkASSERT(fTexture);
    219 }
    220 
    221 void GrTextureStripAtlas::unlockTexture() {
    222     SkASSERT(fTexture && 0 == fLockedRows);
    223     fTexture->unref();
    224     fTexture = NULL;
    225 }
    226 
    227 void GrTextureStripAtlas::initLRU() {
    228     fLRUFront = NULL;
    229     fLRUBack = NULL;
    230     // Initially all the rows are in the LRU list
    231     for (int i = 0; i < fNumRows; ++i) {
    232         fRows[i].fKey = kEmptyAtlasRowKey;
    233         fRows[i].fNext = NULL;
    234         fRows[i].fPrev = NULL;
    235         this->appendLRU(fRows + i);
    236     }
    237     SkASSERT(NULL == fLRUFront || NULL == fLRUFront->fPrev);
    238     SkASSERT(NULL == fLRUBack || NULL == fLRUBack->fNext);
    239 }
    240 
    241 void GrTextureStripAtlas::appendLRU(AtlasRow* row) {
    242     SkASSERT(NULL == row->fPrev && NULL == row->fNext);
    243     if (NULL == fLRUFront && NULL == fLRUBack) {
    244         fLRUFront = row;
    245         fLRUBack = row;
    246     } else {
    247         row->fPrev = fLRUBack;
    248         fLRUBack->fNext = row;
    249         fLRUBack = row;
    250     }
    251 }
    252 
    253 void GrTextureStripAtlas::removeFromLRU(AtlasRow* row) {
    254     SkASSERT(row);
    255     if (row->fNext && row->fPrev) {
    256         row->fPrev->fNext = row->fNext;
    257         row->fNext->fPrev = row->fPrev;
    258     } else {
    259         if (NULL == row->fNext) {
    260             SkASSERT(row == fLRUBack);
    261             fLRUBack = row->fPrev;
    262             if (fLRUBack) {
    263                 fLRUBack->fNext = NULL;
    264             }
    265         }
    266         if (NULL == row->fPrev) {
    267             SkASSERT(row == fLRUFront);
    268             fLRUFront = row->fNext;
    269             if (fLRUFront) {
    270                 fLRUFront->fPrev = NULL;
    271             }
    272         }
    273     }
    274     row->fNext = NULL;
    275     row->fPrev = NULL;
    276 }
    277 
    278 int GrTextureStripAtlas::searchByKey(uint32_t key) {
    279     AtlasRow target;
    280     target.fKey = key;
    281     return SkTSearch<const AtlasRow,
    282                      GrTextureStripAtlas::KeyLess>((const AtlasRow**)fKeyTable.begin(),
    283                                                    fKeyTable.count(),
    284                                                    &target,
    285                                                    sizeof(AtlasRow*));
    286 }
    287 
    288 #ifdef SK_DEBUG
    289 void GrTextureStripAtlas::validate() {
    290 
    291     // Our key table should be sorted
    292     uint32_t prev = 1 > fKeyTable.count() ? 0 : fKeyTable[0]->fKey;
    293     for (int i = 1; i < fKeyTable.count(); ++i) {
    294         SkASSERT(prev < fKeyTable[i]->fKey);
    295         SkASSERT(fKeyTable[i]->fKey != kEmptyAtlasRowKey);
    296         prev = fKeyTable[i]->fKey;
    297     }
    298 
    299     int lruCount = 0;
    300     // Validate LRU pointers, and count LRU entries
    301     SkASSERT(NULL == fLRUFront || NULL == fLRUFront->fPrev);
    302     SkASSERT(NULL == fLRUBack  || NULL == fLRUBack->fNext);
    303     for (AtlasRow* r = fLRUFront; r != NULL; r = r->fNext) {
    304         if (NULL == r->fNext) {
    305             SkASSERT(r == fLRUBack);
    306         } else {
    307             SkASSERT(r->fNext->fPrev == r);
    308         }
    309         ++lruCount;
    310     }
    311 
    312     int rowLocks = 0;
    313     int freeRows = 0;
    314 
    315     for (int i = 0; i < fNumRows; ++i) {
    316         rowLocks += fRows[i].fLocks;
    317         if (0 == fRows[i].fLocks) {
    318             ++freeRows;
    319             bool inLRU = false;
    320             // Step through the LRU and make sure it's present
    321             for (AtlasRow* r = fLRUFront; r != NULL; r = r->fNext) {
    322                 if (r == &fRows[i]) {
    323                     inLRU = true;
    324                     break;
    325                 }
    326             }
    327             SkASSERT(inLRU);
    328         } else {
    329             // If we are locked, we should have a key
    330             SkASSERT(kEmptyAtlasRowKey != fRows[i].fKey);
    331         }
    332 
    333         // If we have a key != kEmptyAtlasRowKey, it should be in the key table
    334         SkASSERT(fRows[i].fKey == kEmptyAtlasRowKey || this->searchByKey(fRows[i].fKey) >= 0);
    335     }
    336 
    337     // Our count of locks should equal the sum of row locks, unless we ran out of rows and flushed,
    338     // in which case we'll have one more lock than recorded in the rows (to represent the pending
    339     // lock of a row; which ensures we don't unlock the texture prematurely).
    340     SkASSERT(rowLocks == fLockedRows || rowLocks + 1 == fLockedRows);
    341 
    342     // We should have one lru entry for each free row
    343     SkASSERT(freeRows == lruCount);
    344 
    345     // If we have locked rows, we should have a locked texture, otherwise
    346     // it should be unlocked
    347     if (fLockedRows == 0) {
    348         SkASSERT(NULL == fTexture);
    349     } else {
    350         SkASSERT(fTexture);
    351     }
    352 }
    353 #endif
    354