gfx/skia/trunk/src/gpu/effects/GrTextureStripAtlas.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/gpu/effects/GrTextureStripAtlas.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,348 @@
     1.4 +
     1.5 +/*
     1.6 + * Copyright 2012 Google Inc.
     1.7 + *
     1.8 + * Use of this source code is governed by a BSD-style license that can be
     1.9 + * found in the LICENSE file.
    1.10 + */
    1.11 +
    1.12 +#include "GrTextureStripAtlas.h"
    1.13 +#include "SkPixelRef.h"
    1.14 +#include "SkTSearch.h"
    1.15 +#include "GrTexture.h"
    1.16 +
    1.17 +#ifdef SK_DEBUG
    1.18 +    #define VALIDATE this->validate()
    1.19 +#else
    1.20 +    #define VALIDATE
    1.21 +#endif
    1.22 +
    1.23 +int32_t GrTextureStripAtlas::gCacheCount = 0;
    1.24 +
    1.25 +GrTHashTable<GrTextureStripAtlas::AtlasEntry,
    1.26 +                GrTextureStripAtlas::AtlasHashKey, 8>*
    1.27 +                            GrTextureStripAtlas::gAtlasCache = NULL;
    1.28 +
    1.29 +GrTHashTable<GrTextureStripAtlas::AtlasEntry, GrTextureStripAtlas::AtlasHashKey, 8>*
    1.30 +GrTextureStripAtlas::GetCache() {
    1.31 +
    1.32 +    if (NULL == gAtlasCache) {
    1.33 +        gAtlasCache = SkNEW((GrTHashTable<AtlasEntry, AtlasHashKey, 8>));
    1.34 +    }
    1.35 +
    1.36 +    return gAtlasCache;
    1.37 +}
    1.38 +
    1.39 +// Remove the specified atlas from the cache
    1.40 +void GrTextureStripAtlas::CleanUp(const GrContext*, void* info) {
    1.41 +    SkASSERT(NULL != info);
    1.42 +
    1.43 +    AtlasEntry* entry = static_cast<AtlasEntry*>(info);
    1.44 +
    1.45 +    // remove the cache entry
    1.46 +    GetCache()->remove(entry->fKey, entry);
    1.47 +
    1.48 +    // remove the actual entry
    1.49 +    SkDELETE(entry);
    1.50 +
    1.51 +    if (0 == GetCache()->count()) {
    1.52 +        SkDELETE(gAtlasCache);
    1.53 +        gAtlasCache = NULL;
    1.54 +    }
    1.55 +}
    1.56 +
    1.57 +GrTextureStripAtlas* GrTextureStripAtlas::GetAtlas(const GrTextureStripAtlas::Desc& desc) {
    1.58 +    AtlasHashKey key;
    1.59 +    key.setKeyData(desc.asKey());
    1.60 +    AtlasEntry* entry = GetCache()->find(key);
    1.61 +    if (NULL == entry) {
    1.62 +        entry = SkNEW(AtlasEntry);
    1.63 +
    1.64 +        entry->fAtlas = SkNEW_ARGS(GrTextureStripAtlas, (desc));
    1.65 +        entry->fKey = key;
    1.66 +
    1.67 +        desc.fContext->addCleanUp(CleanUp, entry);
    1.68 +
    1.69 +        GetCache()->insert(key, entry);
    1.70 +    }
    1.71 +
    1.72 +    return entry->fAtlas;
    1.73 +}
    1.74 +
    1.75 +GrTextureStripAtlas::GrTextureStripAtlas(GrTextureStripAtlas::Desc desc)
    1.76 +    : fCacheKey(sk_atomic_inc(&gCacheCount))
    1.77 +    , fLockedRows(0)
    1.78 +    , fDesc(desc)
    1.79 +    , fNumRows(desc.fHeight / desc.fRowHeight)
    1.80 +    , fTexture(NULL)
    1.81 +    , fRows(SkNEW_ARRAY(AtlasRow, fNumRows))
    1.82 +    , fLRUFront(NULL)
    1.83 +    , fLRUBack(NULL) {
    1.84 +    SkASSERT(fNumRows * fDesc.fRowHeight == fDesc.fHeight);
    1.85 +    this->initLRU();
    1.86 +    VALIDATE;
    1.87 +}
    1.88 +
    1.89 +GrTextureStripAtlas::~GrTextureStripAtlas() {
    1.90 +    SkDELETE_ARRAY(fRows);
    1.91 +}
    1.92 +
    1.93 +int GrTextureStripAtlas::lockRow(const SkBitmap& data) {
    1.94 +    VALIDATE;
    1.95 +    if (0 == fLockedRows) {
    1.96 +        this->lockTexture();
    1.97 +    }
    1.98 +
    1.99 +    int key = data.getGenerationID();
   1.100 +    int rowNumber = -1;
   1.101 +    int index = this->searchByKey(key);
   1.102 +
   1.103 +    if (index >= 0) {
   1.104 +        // We already have the data in a row, so we can just return that row
   1.105 +        AtlasRow* row = fKeyTable[index];
   1.106 +        if (0 == row->fLocks) {
   1.107 +            this->removeFromLRU(row);
   1.108 +        }
   1.109 +        ++row->fLocks;
   1.110 +        ++fLockedRows;
   1.111 +
   1.112 +        // Since all the rows are always stored in a contiguous array, we can save the memory
   1.113 +        // required for storing row numbers and just compute it with some pointer arithmetic
   1.114 +        rowNumber = static_cast<int>(row - fRows);
   1.115 +    } else {
   1.116 +        // ~index is the index where we will insert the new key to keep things sorted
   1.117 +        index = ~index;
   1.118 +
   1.119 +        // We don't have this data cached, so pick the least recently used row to copy into
   1.120 +        AtlasRow* row = this->getLRU();
   1.121 +
   1.122 +        ++fLockedRows;
   1.123 +
   1.124 +        if (NULL == row) {
   1.125 +            // force a flush, which should unlock all the rows; then try again
   1.126 +            fDesc.fContext->flush();
   1.127 +            row = this->getLRU();
   1.128 +            if (NULL == row) {
   1.129 +                --fLockedRows;
   1.130 +                return -1;
   1.131 +            }
   1.132 +        }
   1.133 +
   1.134 +        this->removeFromLRU(row);
   1.135 +
   1.136 +        uint32_t oldKey = row->fKey;
   1.137 +
   1.138 +        // If we are writing into a row that already held bitmap data, we need to remove the
   1.139 +        // reference to that genID which is stored in our sorted table of key values.
   1.140 +        if (oldKey != kEmptyAtlasRowKey) {
   1.141 +
   1.142 +            // Find the entry in the list; if it's before the index where we plan on adding the new
   1.143 +            // entry, we decrement since it will shift elements ahead of it back by one.
   1.144 +            int oldIndex = this->searchByKey(oldKey);
   1.145 +            if (oldIndex < index) {
   1.146 +                --index;
   1.147 +            }
   1.148 +
   1.149 +            fKeyTable.remove(oldIndex);
   1.150 +        }
   1.151 +
   1.152 +        row->fKey = key;
   1.153 +        row->fLocks = 1;
   1.154 +        fKeyTable.insert(index, 1, &row);
   1.155 +        rowNumber = static_cast<int>(row - fRows);
   1.156 +
   1.157 +        SkAutoLockPixels lock(data);
   1.158 +
   1.159 +        // Pass in the kDontFlush flag, since we know we're writing to a part of this texture
   1.160 +        // that is not currently in use
   1.161 +        fDesc.fContext->writeTexturePixels(fTexture,
   1.162 +                                           0,  rowNumber * fDesc.fRowHeight,
   1.163 +                                           fDesc.fWidth, fDesc.fRowHeight,
   1.164 +                                           SkBitmapConfig2GrPixelConfig(data.config()),
   1.165 +                                           data.getPixels(),
   1.166 +                                           data.rowBytes(),
   1.167 +                                           GrContext::kDontFlush_PixelOpsFlag);
   1.168 +    }
   1.169 +
   1.170 +    SkASSERT(rowNumber >= 0);
   1.171 +    VALIDATE;
   1.172 +    return rowNumber;
   1.173 +}
   1.174 +
   1.175 +void GrTextureStripAtlas::unlockRow(int row) {
   1.176 +    VALIDATE;
   1.177 +    --fRows[row].fLocks;
   1.178 +    --fLockedRows;
   1.179 +    SkASSERT(fRows[row].fLocks >= 0 && fLockedRows >= 0);
   1.180 +    if (0 == fRows[row].fLocks) {
   1.181 +        this->appendLRU(fRows + row);
   1.182 +    }
   1.183 +    if (0 == fLockedRows) {
   1.184 +        this->unlockTexture();
   1.185 +    }
   1.186 +    VALIDATE;
   1.187 +}
   1.188 +
   1.189 +GrTextureStripAtlas::AtlasRow* GrTextureStripAtlas::getLRU() {
   1.190 +    // Front is least-recently-used
   1.191 +    AtlasRow* row = fLRUFront;
   1.192 +    return row;
   1.193 +}
   1.194 +
   1.195 +void GrTextureStripAtlas::lockTexture() {
   1.196 +    GrTextureParams params;
   1.197 +    GrTextureDesc texDesc;
   1.198 +    texDesc.fWidth = fDesc.fWidth;
   1.199 +    texDesc.fHeight = fDesc.fHeight;
   1.200 +    texDesc.fConfig = fDesc.fConfig;
   1.201 +
   1.202 +    static const GrCacheID::Domain gTextureStripAtlasDomain = GrCacheID::GenerateDomain();
   1.203 +    GrCacheID::Key key;
   1.204 +    *key.fData32 = fCacheKey;
   1.205 +    memset(key.fData32 + 1, 0, sizeof(key) - sizeof(uint32_t));
   1.206 +    GrCacheID cacheID(gTextureStripAtlasDomain, key);
   1.207 +
   1.208 +    fTexture = fDesc.fContext->findAndRefTexture(texDesc, cacheID, &params);
   1.209 +    if (NULL == fTexture) {
   1.210 +        fTexture = fDesc.fContext->createTexture(&params, texDesc, cacheID, NULL, 0);
   1.211 +        // This is a new texture, so all of our cache info is now invalid
   1.212 +        this->initLRU();
   1.213 +        fKeyTable.rewind();
   1.214 +    }
   1.215 +    SkASSERT(NULL != fTexture);
   1.216 +}
   1.217 +
   1.218 +void GrTextureStripAtlas::unlockTexture() {
   1.219 +    SkASSERT(NULL != fTexture && 0 == fLockedRows);
   1.220 +    fTexture->unref();
   1.221 +    fTexture = NULL;
   1.222 +    fDesc.fContext->purgeCache();
   1.223 +}
   1.224 +
   1.225 +void GrTextureStripAtlas::initLRU() {
   1.226 +    fLRUFront = NULL;
   1.227 +    fLRUBack = NULL;
   1.228 +    // Initially all the rows are in the LRU list
   1.229 +    for (int i = 0; i < fNumRows; ++i) {
   1.230 +        fRows[i].fKey = kEmptyAtlasRowKey;
   1.231 +        fRows[i].fNext = NULL;
   1.232 +        fRows[i].fPrev = NULL;
   1.233 +        this->appendLRU(fRows + i);
   1.234 +    }
   1.235 +    SkASSERT(NULL == fLRUFront || NULL == fLRUFront->fPrev);
   1.236 +    SkASSERT(NULL == fLRUBack || NULL == fLRUBack->fNext);
   1.237 +}
   1.238 +
   1.239 +void GrTextureStripAtlas::appendLRU(AtlasRow* row) {
   1.240 +    SkASSERT(NULL == row->fPrev && NULL == row->fNext);
   1.241 +    if (NULL == fLRUFront && NULL == fLRUBack) {
   1.242 +        fLRUFront = row;
   1.243 +        fLRUBack = row;
   1.244 +    } else {
   1.245 +        row->fPrev = fLRUBack;
   1.246 +        fLRUBack->fNext = row;
   1.247 +        fLRUBack = row;
   1.248 +    }
   1.249 +}
   1.250 +
   1.251 +void GrTextureStripAtlas::removeFromLRU(AtlasRow* row) {
   1.252 +    SkASSERT(NULL != row);
   1.253 +    if (NULL != row->fNext && NULL != row->fPrev) {
   1.254 +        row->fPrev->fNext = row->fNext;
   1.255 +        row->fNext->fPrev = row->fPrev;
   1.256 +    } else {
   1.257 +        if (NULL == row->fNext) {
   1.258 +            SkASSERT(row == fLRUBack);
   1.259 +            fLRUBack = row->fPrev;
   1.260 +            if (fLRUBack) {
   1.261 +                fLRUBack->fNext = NULL;
   1.262 +            }
   1.263 +        }
   1.264 +        if (NULL == row->fPrev) {
   1.265 +            SkASSERT(row == fLRUFront);
   1.266 +            fLRUFront = row->fNext;
   1.267 +            if (fLRUFront) {
   1.268 +                fLRUFront->fPrev = NULL;
   1.269 +            }
   1.270 +        }
   1.271 +    }
   1.272 +    row->fNext = NULL;
   1.273 +    row->fPrev = NULL;
   1.274 +}
   1.275 +
   1.276 +int GrTextureStripAtlas::searchByKey(uint32_t key) {
   1.277 +    AtlasRow target;
   1.278 +    target.fKey = key;
   1.279 +    return SkTSearch<const AtlasRow,
   1.280 +                     GrTextureStripAtlas::KeyLess>((const AtlasRow**)fKeyTable.begin(),
   1.281 +                                                   fKeyTable.count(),
   1.282 +                                                   &target,
   1.283 +                                                   sizeof(AtlasRow*));
   1.284 +}
   1.285 +
   1.286 +#ifdef SK_DEBUG
   1.287 +void GrTextureStripAtlas::validate() {
   1.288 +
   1.289 +    // Our key table should be sorted
   1.290 +    uint32_t prev = 1 > fKeyTable.count() ? 0 : fKeyTable[0]->fKey;
   1.291 +    for (int i = 1; i < fKeyTable.count(); ++i) {
   1.292 +        SkASSERT(prev < fKeyTable[i]->fKey);
   1.293 +        SkASSERT(fKeyTable[i]->fKey != kEmptyAtlasRowKey);
   1.294 +        prev = fKeyTable[i]->fKey;
   1.295 +    }
   1.296 +
   1.297 +    int lruCount = 0;
   1.298 +    // Validate LRU pointers, and count LRU entries
   1.299 +    SkASSERT(NULL == fLRUFront || NULL == fLRUFront->fPrev);
   1.300 +    SkASSERT(NULL == fLRUBack  || NULL == fLRUBack->fNext);
   1.301 +    for (AtlasRow* r = fLRUFront; r != NULL; r = r->fNext) {
   1.302 +        if (NULL == r->fNext) {
   1.303 +            SkASSERT(r == fLRUBack);
   1.304 +        } else {
   1.305 +            SkASSERT(r->fNext->fPrev == r);
   1.306 +        }
   1.307 +        ++lruCount;
   1.308 +    }
   1.309 +
   1.310 +    int rowLocks = 0;
   1.311 +    int freeRows = 0;
   1.312 +
   1.313 +    for (int i = 0; i < fNumRows; ++i) {
   1.314 +        rowLocks += fRows[i].fLocks;
   1.315 +        if (0 == fRows[i].fLocks) {
   1.316 +            ++freeRows;
   1.317 +            bool inLRU = false;
   1.318 +            // Step through the LRU and make sure it's present
   1.319 +            for (AtlasRow* r = fLRUFront; r != NULL; r = r->fNext) {
   1.320 +                if (r == &fRows[i]) {
   1.321 +                    inLRU = true;
   1.322 +                    break;
   1.323 +                }
   1.324 +            }
   1.325 +            SkASSERT(inLRU);
   1.326 +        } else {
   1.327 +            // If we are locked, we should have a key
   1.328 +            SkASSERT(kEmptyAtlasRowKey != fRows[i].fKey);
   1.329 +        }
   1.330 +
   1.331 +        // If we have a key != kEmptyAtlasRowKey, it should be in the key table
   1.332 +        SkASSERT(fRows[i].fKey == kEmptyAtlasRowKey || this->searchByKey(fRows[i].fKey) >= 0);
   1.333 +    }
   1.334 +
   1.335 +    // Our count of locks should equal the sum of row locks, unless we ran out of rows and flushed,
   1.336 +    // in which case we'll have one more lock than recorded in the rows (to represent the pending
   1.337 +    // lock of a row; which ensures we don't unlock the texture prematurely).
   1.338 +    SkASSERT(rowLocks == fLockedRows || rowLocks + 1 == fLockedRows);
   1.339 +
   1.340 +    // We should have one lru entry for each free row
   1.341 +    SkASSERT(freeRows == lruCount);
   1.342 +
   1.343 +    // If we have locked rows, we should have a locked texture, otherwise
   1.344 +    // it should be unlocked
   1.345 +    if (fLockedRows == 0) {
   1.346 +        SkASSERT(NULL == fTexture);
   1.347 +    } else {
   1.348 +        SkASSERT(NULL != fTexture);
   1.349 +    }
   1.350 +}
   1.351 +#endif

mercurial