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