GrTextureStripAtlas.cpp revision 6bfef2dfec3253efa48aec0b5ea108e0dc041d15
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
20class GrTextureStripAtlas::Hash : public SkTDynamicHash<GrTextureStripAtlas::AtlasEntry,
21                                                        GrTextureStripAtlas::AtlasEntry::Key> {};
22
23int32_t GrTextureStripAtlas::gCacheCount = 0;
24
25GrTextureStripAtlas::Hash* GrTextureStripAtlas::gAtlasCache = NULL;
26
27GrTextureStripAtlas::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
37void 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
54GrTextureStripAtlas* 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
72GrTextureStripAtlas::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
87GrTextureStripAtlas::~GrTextureStripAtlas() {
88    SkDELETE_ARRAY(fRows);
89}
90
91int GrTextureStripAtlas::lockRow(const SkBitmap& data) {
92    VALIDATE;
93    if (0 == fLockedRows) {
94        this->lockTexture();
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 (NULL == row) {
123            // force a flush, which should unlock all the rows; then try again
124            fDesc.fContext->flush();
125            row = this->getLRU();
126            if (NULL == 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
172void 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
186GrTextureStripAtlas::AtlasRow* GrTextureStripAtlas::getLRU() {
187    // Front is least-recently-used
188    AtlasRow* row = fLRUFront;
189    return row;
190}
191
192void GrTextureStripAtlas::lockTexture() {
193    GrTextureParams params;
194    GrSurfaceDesc texDesc;
195    texDesc.fWidth = fDesc.fWidth;
196    texDesc.fHeight = fDesc.fHeight;
197    texDesc.fConfig = fDesc.fConfig;
198
199    static const GrContentKey::Domain kDomain = GrContentKey::GenerateDomain();
200    GrContentKey key;
201    GrContentKey::Builder builder(&key, kDomain, 1);
202    builder[0] = static_cast<uint32_t>(fCacheKey);
203    builder.finish();
204
205    fTexture = fDesc.fContext->findAndRefTexture(texDesc, key, &params);
206    if (NULL == fTexture) {
207        fTexture = fDesc.fContext->createTexture(&params, texDesc, key, NULL, 0);
208        // This is a new texture, so all of our cache info is now invalid
209        this->initLRU();
210        fKeyTable.rewind();
211    }
212    SkASSERT(fTexture);
213}
214
215void GrTextureStripAtlas::unlockTexture() {
216    SkASSERT(fTexture && 0 == fLockedRows);
217    fTexture->unref();
218    fTexture = NULL;
219}
220
221void GrTextureStripAtlas::initLRU() {
222    fLRUFront = NULL;
223    fLRUBack = NULL;
224    // Initially all the rows are in the LRU list
225    for (int i = 0; i < fNumRows; ++i) {
226        fRows[i].fKey = kEmptyAtlasRowKey;
227        fRows[i].fNext = NULL;
228        fRows[i].fPrev = NULL;
229        this->appendLRU(fRows + i);
230    }
231    SkASSERT(NULL == fLRUFront || NULL == fLRUFront->fPrev);
232    SkASSERT(NULL == fLRUBack || NULL == fLRUBack->fNext);
233}
234
235void GrTextureStripAtlas::appendLRU(AtlasRow* row) {
236    SkASSERT(NULL == row->fPrev && NULL == row->fNext);
237    if (NULL == fLRUFront && NULL == fLRUBack) {
238        fLRUFront = row;
239        fLRUBack = row;
240    } else {
241        row->fPrev = fLRUBack;
242        fLRUBack->fNext = row;
243        fLRUBack = row;
244    }
245}
246
247void GrTextureStripAtlas::removeFromLRU(AtlasRow* row) {
248    SkASSERT(row);
249    if (row->fNext && row->fPrev) {
250        row->fPrev->fNext = row->fNext;
251        row->fNext->fPrev = row->fPrev;
252    } else {
253        if (NULL == row->fNext) {
254            SkASSERT(row == fLRUBack);
255            fLRUBack = row->fPrev;
256            if (fLRUBack) {
257                fLRUBack->fNext = NULL;
258            }
259        }
260        if (NULL == row->fPrev) {
261            SkASSERT(row == fLRUFront);
262            fLRUFront = row->fNext;
263            if (fLRUFront) {
264                fLRUFront->fPrev = NULL;
265            }
266        }
267    }
268    row->fNext = NULL;
269    row->fPrev = NULL;
270}
271
272int GrTextureStripAtlas::searchByKey(uint32_t key) {
273    AtlasRow target;
274    target.fKey = key;
275    return SkTSearch<const AtlasRow,
276                     GrTextureStripAtlas::KeyLess>((const AtlasRow**)fKeyTable.begin(),
277                                                   fKeyTable.count(),
278                                                   &target,
279                                                   sizeof(AtlasRow*));
280}
281
282#ifdef SK_DEBUG
283void GrTextureStripAtlas::validate() {
284
285    // Our key table should be sorted
286    uint32_t prev = 1 > fKeyTable.count() ? 0 : fKeyTable[0]->fKey;
287    for (int i = 1; i < fKeyTable.count(); ++i) {
288        SkASSERT(prev < fKeyTable[i]->fKey);
289        SkASSERT(fKeyTable[i]->fKey != kEmptyAtlasRowKey);
290        prev = fKeyTable[i]->fKey;
291    }
292
293    int lruCount = 0;
294    // Validate LRU pointers, and count LRU entries
295    SkASSERT(NULL == fLRUFront || NULL == fLRUFront->fPrev);
296    SkASSERT(NULL == fLRUBack  || NULL == fLRUBack->fNext);
297    for (AtlasRow* r = fLRUFront; r != NULL; r = r->fNext) {
298        if (NULL == r->fNext) {
299            SkASSERT(r == fLRUBack);
300        } else {
301            SkASSERT(r->fNext->fPrev == r);
302        }
303        ++lruCount;
304    }
305
306    int rowLocks = 0;
307    int freeRows = 0;
308
309    for (int i = 0; i < fNumRows; ++i) {
310        rowLocks += fRows[i].fLocks;
311        if (0 == fRows[i].fLocks) {
312            ++freeRows;
313            bool inLRU = false;
314            // Step through the LRU and make sure it's present
315            for (AtlasRow* r = fLRUFront; r != NULL; r = r->fNext) {
316                if (r == &fRows[i]) {
317                    inLRU = true;
318                    break;
319                }
320            }
321            SkASSERT(inLRU);
322        } else {
323            // If we are locked, we should have a key
324            SkASSERT(kEmptyAtlasRowKey != fRows[i].fKey);
325        }
326
327        // If we have a key != kEmptyAtlasRowKey, it should be in the key table
328        SkASSERT(fRows[i].fKey == kEmptyAtlasRowKey || this->searchByKey(fRows[i].fKey) >= 0);
329    }
330
331    // Our count of locks should equal the sum of row locks, unless we ran out of rows and flushed,
332    // in which case we'll have one more lock than recorded in the rows (to represent the pending
333    // lock of a row; which ensures we don't unlock the texture prematurely).
334    SkASSERT(rowLocks == fLockedRows || rowLocks + 1 == fLockedRows);
335
336    // We should have one lru entry for each free row
337    SkASSERT(freeRows == lruCount);
338
339    // If we have locked rows, we should have a locked texture, otherwise
340    // it should be unlocked
341    if (fLockedRows == 0) {
342        SkASSERT(NULL == fTexture);
343    } else {
344        SkASSERT(fTexture);
345    }
346}
347#endif
348