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