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