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