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 "GrProxyProvider.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 void GrTextureStripAtlas::lockRow(int row) {
     90     // This should only be called on a row that is already locked.
     91     SkASSERT(fRows[row].fLocks);
     92     fRows[row].fLocks++;
     93     ++fLockedRows;
     94 }
     95 
     96 int GrTextureStripAtlas::lockRow(const SkBitmap& bitmap) {
     97     VALIDATE;
     98 
     99     if (!this->getContext()->contextPriv().resourceProvider()) {
    100         // DDL TODO: For DDL we need to schedule inline & ASAP uploads. However these systems
    101         // currently use the flushState which we can't use for the opList-based DDL phase.
    102         // For the opList-based solution every texture strip will get its own texture proxy.
    103         // We will revisit this for the flushState-based solution.
    104         return -1;
    105     }
    106 
    107     if (0 == fLockedRows) {
    108         this->lockTexture();
    109         if (!fTexContext) {
    110             return -1;
    111         }
    112     }
    113 
    114     int key = bitmap.getGenerationID();
    115     int rowNumber = -1;
    116     int index = this->searchByKey(key);
    117 
    118     if (index >= 0) {
    119         // We already have the data in a row, so we can just return that row
    120         AtlasRow* row = fKeyTable[index];
    121         if (0 == row->fLocks) {
    122             this->removeFromLRU(row);
    123         }
    124         ++row->fLocks;
    125         ++fLockedRows;
    126 
    127         // Since all the rows are always stored in a contiguous array, we can save the memory
    128         // required for storing row numbers and just compute it with some pointer arithmetic
    129         rowNumber = static_cast<int>(row - fRows);
    130     } else {
    131         // ~index is the index where we will insert the new key to keep things sorted
    132         index = ~index;
    133 
    134         // We don't have this data cached, so pick the least recently used row to copy into
    135         AtlasRow* row = this->getLRU();
    136 
    137         ++fLockedRows;
    138 
    139         if (nullptr == row) {
    140             // force a flush, which should unlock all the rows; then try again
    141             fDesc.fContext->contextPriv().flush(nullptr); // tighten this up?
    142             row = this->getLRU();
    143             if (nullptr == row) {
    144                 --fLockedRows;
    145                 return -1;
    146             }
    147         }
    148 
    149         this->removeFromLRU(row);
    150 
    151         uint32_t oldKey = row->fKey;
    152 
    153         // If we are writing into a row that already held bitmap data, we need to remove the
    154         // reference to that genID which is stored in our sorted table of key values.
    155         if (oldKey != kEmptyAtlasRowKey) {
    156 
    157             // Find the entry in the list; if it's before the index where we plan on adding the new
    158             // entry, we decrement since it will shift elements ahead of it back by one.
    159             int oldIndex = this->searchByKey(oldKey);
    160             if (oldIndex < index) {
    161                 --index;
    162             }
    163 
    164             fKeyTable.remove(oldIndex);
    165         }
    166 
    167         row->fKey = key;
    168         row->fLocks = 1;
    169         fKeyTable.insert(index, 1, &row);
    170         rowNumber = static_cast<int>(row - fRows);
    171 
    172         SkASSERT(bitmap.width() == fDesc.fWidth);
    173         SkASSERT(bitmap.height() == fDesc.fRowHeight);
    174 
    175         // Pass in the kDontFlush flag, since we know we're writing to a part of this texture
    176         // that is not currently in use
    177         fTexContext->writePixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(),
    178                                  0, rowNumber * fDesc.fRowHeight,
    179                                  GrContextPriv::kDontFlush_PixelOpsFlag);
    180     }
    181 
    182     SkASSERT(rowNumber >= 0);
    183     VALIDATE;
    184     return rowNumber;
    185 }
    186 
    187 sk_sp<GrTextureProxy> GrTextureStripAtlas::asTextureProxyRef() const {
    188     return fTexContext->asTextureProxyRef();
    189 }
    190 
    191 void GrTextureStripAtlas::unlockRow(int row) {
    192     VALIDATE;
    193     --fRows[row].fLocks;
    194     --fLockedRows;
    195     SkASSERT(fRows[row].fLocks >= 0 && fLockedRows >= 0);
    196     if (0 == fRows[row].fLocks) {
    197         this->appendLRU(fRows + row);
    198     }
    199     if (0 == fLockedRows) {
    200         this->unlockTexture();
    201     }
    202     VALIDATE;
    203 }
    204 
    205 GrTextureStripAtlas::AtlasRow* GrTextureStripAtlas::getLRU() {
    206     // Front is least-recently-used
    207     AtlasRow* row = fLRUFront;
    208     return row;
    209 }
    210 
    211 void GrTextureStripAtlas::lockTexture() {
    212 
    213     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
    214     GrUniqueKey key;
    215     GrUniqueKey::Builder builder(&key, kDomain, 1);
    216     builder[0] = static_cast<uint32_t>(fCacheKey);
    217     builder.finish();
    218 
    219     GrProxyProvider* proxyProvider = fDesc.fContext->contextPriv().proxyProvider();
    220 
    221     sk_sp<GrTextureProxy> proxy = proxyProvider->findOrCreateProxyByUniqueKey(
    222                                                                 key, kTopLeft_GrSurfaceOrigin);
    223     if (!proxy) {
    224         GrSurfaceDesc texDesc;
    225         texDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
    226         texDesc.fWidth  = fDesc.fWidth;
    227         texDesc.fHeight = fDesc.fHeight;
    228         texDesc.fConfig = fDesc.fConfig;
    229 
    230         proxy = proxyProvider->createProxy(texDesc, SkBackingFit::kExact, SkBudgeted::kYes,
    231                                            GrResourceProvider::kNoPendingIO_Flag);
    232         if (!proxy) {
    233             return;
    234         }
    235 
    236         SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin);
    237         proxyProvider->assignUniqueKeyToProxy(key, proxy.get());
    238         // This is a new texture, so all of our cache info is now invalid
    239         this->initLRU();
    240         fKeyTable.rewind();
    241     }
    242     SkASSERT(proxy);
    243     fTexContext = fDesc.fContext->contextPriv().makeWrappedSurfaceContext(std::move(proxy));
    244 }
    245 
    246 void GrTextureStripAtlas::unlockTexture() {
    247     SkASSERT(fTexContext && 0 == fLockedRows);
    248     fTexContext.reset();
    249 }
    250 
    251 void GrTextureStripAtlas::initLRU() {
    252     fLRUFront = nullptr;
    253     fLRUBack = nullptr;
    254     // Initially all the rows are in the LRU list
    255     for (int i = 0; i < fNumRows; ++i) {
    256         fRows[i].fKey = kEmptyAtlasRowKey;
    257         fRows[i].fNext = nullptr;
    258         fRows[i].fPrev = nullptr;
    259         this->appendLRU(fRows + i);
    260     }
    261     SkASSERT(nullptr == fLRUFront || nullptr == fLRUFront->fPrev);
    262     SkASSERT(nullptr == fLRUBack || nullptr == fLRUBack->fNext);
    263 }
    264 
    265 void GrTextureStripAtlas::appendLRU(AtlasRow* row) {
    266     SkASSERT(nullptr == row->fPrev && nullptr == row->fNext);
    267     if (nullptr == fLRUFront && nullptr == fLRUBack) {
    268         fLRUFront = row;
    269         fLRUBack = row;
    270     } else {
    271         row->fPrev = fLRUBack;
    272         fLRUBack->fNext = row;
    273         fLRUBack = row;
    274     }
    275 }
    276 
    277 void GrTextureStripAtlas::removeFromLRU(AtlasRow* row) {
    278     SkASSERT(row);
    279     if (row->fNext && row->fPrev) {
    280         row->fPrev->fNext = row->fNext;
    281         row->fNext->fPrev = row->fPrev;
    282     } else {
    283         if (nullptr == row->fNext) {
    284             SkASSERT(row == fLRUBack);
    285             fLRUBack = row->fPrev;
    286             if (fLRUBack) {
    287                 fLRUBack->fNext = nullptr;
    288             }
    289         }
    290         if (nullptr == row->fPrev) {
    291             SkASSERT(row == fLRUFront);
    292             fLRUFront = row->fNext;
    293             if (fLRUFront) {
    294                 fLRUFront->fPrev = nullptr;
    295             }
    296         }
    297     }
    298     row->fNext = nullptr;
    299     row->fPrev = nullptr;
    300 }
    301 
    302 int GrTextureStripAtlas::searchByKey(uint32_t key) {
    303     AtlasRow target;
    304     target.fKey = key;
    305     return SkTSearch<const AtlasRow,
    306                      GrTextureStripAtlas::KeyLess>((const AtlasRow**)fKeyTable.begin(),
    307                                                    fKeyTable.count(),
    308                                                    &target,
    309                                                    sizeof(AtlasRow*));
    310 }
    311 
    312 #ifdef SK_DEBUG
    313 void GrTextureStripAtlas::validate() {
    314 
    315     // Our key table should be sorted
    316     uint32_t prev = 1 > fKeyTable.count() ? 0 : fKeyTable[0]->fKey;
    317     for (int i = 1; i < fKeyTable.count(); ++i) {
    318         SkASSERT(prev < fKeyTable[i]->fKey);
    319         SkASSERT(fKeyTable[i]->fKey != kEmptyAtlasRowKey);
    320         prev = fKeyTable[i]->fKey;
    321     }
    322 
    323     int lruCount = 0;
    324     // Validate LRU pointers, and count LRU entries
    325     SkASSERT(nullptr == fLRUFront || nullptr == fLRUFront->fPrev);
    326     SkASSERT(nullptr == fLRUBack  || nullptr == fLRUBack->fNext);
    327     for (AtlasRow* r = fLRUFront; r != nullptr; r = r->fNext) {
    328         if (nullptr == r->fNext) {
    329             SkASSERT(r == fLRUBack);
    330         } else {
    331             SkASSERT(r->fNext->fPrev == r);
    332         }
    333         ++lruCount;
    334     }
    335 
    336     int rowLocks = 0;
    337     int freeRows = 0;
    338 
    339     for (int i = 0; i < fNumRows; ++i) {
    340         rowLocks += fRows[i].fLocks;
    341         if (0 == fRows[i].fLocks) {
    342             ++freeRows;
    343             bool inLRU = false;
    344             // Step through the LRU and make sure it's present
    345             for (AtlasRow* r = fLRUFront; r != nullptr; r = r->fNext) {
    346                 if (r == &fRows[i]) {
    347                     inLRU = true;
    348                     break;
    349                 }
    350             }
    351             SkASSERT(inLRU);
    352         } else {
    353             // If we are locked, we should have a key
    354             SkASSERT(kEmptyAtlasRowKey != fRows[i].fKey);
    355         }
    356 
    357         // If we have a key != kEmptyAtlasRowKey, it should be in the key table
    358         SkASSERT(fRows[i].fKey == kEmptyAtlasRowKey || this->searchByKey(fRows[i].fKey) >= 0);
    359     }
    360 
    361     // Our count of locks should equal the sum of row locks, unless we ran out of rows and flushed,
    362     // in which case we'll have one more lock than recorded in the rows (to represent the pending
    363     // lock of a row; which ensures we don't unlock the texture prematurely).
    364     SkASSERT(rowLocks == fLockedRows || rowLocks + 1 == fLockedRows);
    365 
    366     // We should have one lru entry for each free row
    367     SkASSERT(freeRows == lruCount);
    368 
    369     // If we have locked rows, we should have a locked texture, otherwise
    370     // it should be unlocked
    371     if (fLockedRows == 0) {
    372         SkASSERT(!fTexContext);
    373     } else {
    374         SkASSERT(fTexContext);
    375     }
    376 }
    377 #endif
    378