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