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