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 
GetCache()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
CleanUp(const GrContext *,void * info)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 
GetAtlas(const GrTextureStripAtlas::Desc & desc)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 
GrTextureStripAtlas(GrTextureStripAtlas::Desc desc)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 
~GrTextureStripAtlas()86 GrTextureStripAtlas::~GrTextureStripAtlas() { delete[] fRows; }
87 
lockRow(const SkBitmap & data)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 
unlockRow(int row)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 
getLRU()186 GrTextureStripAtlas::AtlasRow* GrTextureStripAtlas::getLRU() {
187     // Front is least-recently-used
188     AtlasRow* row = fLRUFront;
189     return row;
190 }
191 
lockTexture()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 
unlockTexture()219 void GrTextureStripAtlas::unlockTexture() {
220     SkASSERT(fTexture && 0 == fLockedRows);
221     fTexture->unref();
222     fTexture = nullptr;
223 }
224 
initLRU()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 
appendLRU(AtlasRow * row)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 
removeFromLRU(AtlasRow * row)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 
searchByKey(uint32_t key)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
validate()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