1 2 /* 3 * Copyright 2012 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 #include "SkBitmapHeap.h" 10 11 #include "SkBitmap.h" 12 #include "SkFlattenableBuffers.h" 13 #include "SkTSearch.h" 14 15 SkBitmapHeapEntry::SkBitmapHeapEntry() 16 : fSlot(-1) 17 , fRefCount(0) 18 , fBytesAllocated(0) { 19 } 20 21 SkBitmapHeapEntry::~SkBitmapHeapEntry() { 22 SkASSERT(0 == fRefCount); 23 } 24 25 void SkBitmapHeapEntry::addReferences(int count) { 26 if (0 == fRefCount) { 27 // If there are no current owners then the heap manager 28 // will be the only one able to modify it, so it does not 29 // need to be an atomic operation. 30 fRefCount = count; 31 } else { 32 sk_atomic_add(&fRefCount, count); 33 } 34 } 35 36 /////////////////////////////////////////////////////////////////////////////// 37 38 bool SkBitmapHeap::LookupEntry::Less(const SkBitmapHeap::LookupEntry& a, 39 const SkBitmapHeap::LookupEntry& b) { 40 if (a.fGenerationId < b.fGenerationId) { 41 return true; 42 } else if (a.fGenerationId > b.fGenerationId) { 43 return false; 44 } else if (a.fPixelOffset < b.fPixelOffset) { 45 return true; 46 } else if (a.fPixelOffset > b.fPixelOffset) { 47 return false; 48 } else if (a.fWidth < b.fWidth) { 49 return true; 50 } else if (a.fWidth > b.fWidth) { 51 return false; 52 } else if (a.fHeight < b.fHeight) { 53 return true; 54 } 55 return false; 56 } 57 58 /////////////////////////////////////////////////////////////////////////////// 59 60 SkBitmapHeap::SkBitmapHeap(int32_t preferredSize, int32_t ownerCount) 61 : INHERITED() 62 , fExternalStorage(NULL) 63 , fMostRecentlyUsed(NULL) 64 , fLeastRecentlyUsed(NULL) 65 , fPreferredCount(preferredSize) 66 , fOwnerCount(ownerCount) 67 , fBytesAllocated(0) 68 , fDeferAddingOwners(false) { 69 } 70 71 SkBitmapHeap::SkBitmapHeap(ExternalStorage* storage, int32_t preferredSize) 72 : INHERITED() 73 , fExternalStorage(storage) 74 , fMostRecentlyUsed(NULL) 75 , fLeastRecentlyUsed(NULL) 76 , fPreferredCount(preferredSize) 77 , fOwnerCount(IGNORE_OWNERS) 78 , fBytesAllocated(0) 79 , fDeferAddingOwners(false) { 80 SkSafeRef(storage); 81 } 82 83 SkBitmapHeap::~SkBitmapHeap() { 84 SkDEBUGCODE( 85 for (int i = 0; i < fStorage.count(); i++) { 86 bool unused = false; 87 for (int j = 0; j < fUnusedSlots.count(); j++) { 88 if (fUnusedSlots[j] == fStorage[i]->fSlot) { 89 unused = true; 90 break; 91 } 92 } 93 if (!unused) { 94 fBytesAllocated -= fStorage[i]->fBytesAllocated; 95 } 96 } 97 fBytesAllocated -= (fStorage.count() * sizeof(SkBitmapHeapEntry)); 98 ) 99 SkASSERT(0 == fBytesAllocated); 100 fStorage.deleteAll(); 101 SkSafeUnref(fExternalStorage); 102 fLookupTable.deleteAll(); 103 } 104 105 SkTRefArray<SkBitmap>* SkBitmapHeap::extractBitmaps() const { 106 const int size = fStorage.count(); 107 SkTRefArray<SkBitmap>* array = NULL; 108 if (size > 0) { 109 array = SkTRefArray<SkBitmap>::Create(size); 110 for (int i = 0; i < size; i++) { 111 // make a shallow copy of the bitmap 112 array->writableAt(i) = fStorage[i]->fBitmap; 113 } 114 } 115 return array; 116 } 117 118 void SkBitmapHeap::removeFromLRU(SkBitmapHeap::LookupEntry* entry) { 119 if (fMostRecentlyUsed == entry) { 120 fMostRecentlyUsed = entry->fLessRecentlyUsed; 121 if (NULL == fMostRecentlyUsed) { 122 SkASSERT(fLeastRecentlyUsed == entry); 123 fLeastRecentlyUsed = NULL; 124 } else { 125 fMostRecentlyUsed->fMoreRecentlyUsed = NULL; 126 } 127 } else { 128 // Remove entry from its prior place, and make sure to cover the hole. 129 if (fLeastRecentlyUsed == entry) { 130 SkASSERT(entry->fMoreRecentlyUsed != NULL); 131 fLeastRecentlyUsed = entry->fMoreRecentlyUsed; 132 } 133 // Since we have already considered the case where entry is the most recently used, it must 134 // have a more recently used at this point. 135 SkASSERT(entry->fMoreRecentlyUsed != NULL); 136 entry->fMoreRecentlyUsed->fLessRecentlyUsed = entry->fLessRecentlyUsed; 137 138 if (entry->fLessRecentlyUsed != NULL) { 139 SkASSERT(fLeastRecentlyUsed != entry); 140 entry->fLessRecentlyUsed->fMoreRecentlyUsed = entry->fMoreRecentlyUsed; 141 } 142 } 143 entry->fMoreRecentlyUsed = NULL; 144 } 145 146 void SkBitmapHeap::appendToLRU(SkBitmapHeap::LookupEntry* entry) { 147 if (fMostRecentlyUsed != NULL) { 148 SkASSERT(NULL == fMostRecentlyUsed->fMoreRecentlyUsed); 149 fMostRecentlyUsed->fMoreRecentlyUsed = entry; 150 entry->fLessRecentlyUsed = fMostRecentlyUsed; 151 } 152 fMostRecentlyUsed = entry; 153 if (NULL == fLeastRecentlyUsed) { 154 fLeastRecentlyUsed = entry; 155 } 156 } 157 158 // iterate through our LRU cache and try to find an entry to evict 159 SkBitmapHeap::LookupEntry* SkBitmapHeap::findEntryToReplace(const SkBitmap& replacement) { 160 SkASSERT(fPreferredCount != UNLIMITED_SIZE); 161 SkASSERT(fStorage.count() >= fPreferredCount); 162 163 SkBitmapHeap::LookupEntry* iter = fLeastRecentlyUsed; 164 while (iter != NULL) { 165 SkBitmapHeapEntry* heapEntry = fStorage[iter->fStorageSlot]; 166 if (heapEntry->fRefCount > 0) { 167 // If the least recently used bitmap has not been unreferenced 168 // by its owner, then according to our LRU specifications a more 169 // recently used one can not have used all its references yet either. 170 return NULL; 171 } 172 if (replacement.getGenerationID() == iter->fGenerationId) { 173 // Do not replace a bitmap with a new one using the same 174 // pixel ref. Instead look for a different one that will 175 // potentially free up more space. 176 iter = iter->fMoreRecentlyUsed; 177 } else { 178 return iter; 179 } 180 } 181 return NULL; 182 } 183 184 size_t SkBitmapHeap::freeMemoryIfPossible(size_t bytesToFree) { 185 if (UNLIMITED_SIZE == fPreferredCount) { 186 return 0; 187 } 188 LookupEntry* iter = fLeastRecentlyUsed; 189 size_t origBytesAllocated = fBytesAllocated; 190 // Purge starting from LRU until a non-evictable bitmap is found or until 191 // everything is evicted. 192 while (iter != NULL) { 193 SkBitmapHeapEntry* heapEntry = fStorage[iter->fStorageSlot]; 194 if (heapEntry->fRefCount > 0) { 195 break; 196 } 197 LookupEntry* next = iter->fMoreRecentlyUsed; 198 this->removeEntryFromLookupTable(iter); 199 // Free the pixel memory. removeEntryFromLookupTable already reduced 200 // fBytesAllocated properly. 201 heapEntry->fBitmap.reset(); 202 // Add to list of unused slots which can be reused in the future. 203 fUnusedSlots.push(heapEntry->fSlot); 204 iter = next; 205 if (origBytesAllocated - fBytesAllocated >= bytesToFree) { 206 break; 207 } 208 } 209 210 if (fLeastRecentlyUsed != iter) { 211 // There was at least one eviction. 212 fLeastRecentlyUsed = iter; 213 if (NULL == fLeastRecentlyUsed) { 214 // Everything was evicted 215 fMostRecentlyUsed = NULL; 216 fBytesAllocated -= (fStorage.count() * sizeof(SkBitmapHeapEntry)); 217 fStorage.deleteAll(); 218 fUnusedSlots.reset(); 219 SkASSERT(0 == fBytesAllocated); 220 } else { 221 fLeastRecentlyUsed->fLessRecentlyUsed = NULL; 222 } 223 } 224 225 return origBytesAllocated - fBytesAllocated; 226 } 227 228 int SkBitmapHeap::findInLookupTable(const LookupEntry& indexEntry, SkBitmapHeapEntry** entry) { 229 int index = SkTSearch<const LookupEntry, LookupEntry::Less>( 230 (const LookupEntry**)fLookupTable.begin(), 231 fLookupTable.count(), 232 &indexEntry, sizeof(void*)); 233 234 if (index < 0) { 235 // insert ourselves into the bitmapIndex 236 index = ~index; 237 *fLookupTable.insert(index) = SkNEW_ARGS(LookupEntry, (indexEntry)); 238 } else if (entry != NULL) { 239 // populate the entry if needed 240 *entry = fStorage[fLookupTable[index]->fStorageSlot]; 241 } 242 243 return index; 244 } 245 246 bool SkBitmapHeap::copyBitmap(const SkBitmap& originalBitmap, SkBitmap& copiedBitmap) { 247 SkASSERT(!fExternalStorage); 248 249 // If the bitmap is mutable, we need to do a deep copy, since the 250 // caller may modify it afterwards. 251 if (originalBitmap.isImmutable()) { 252 copiedBitmap = originalBitmap; 253 // TODO if we have the pixel ref in the heap we could pass it here to avoid a potential deep copy 254 // else if (sharedPixelRef != NULL) { 255 // copiedBitmap = orig; 256 // copiedBitmap.setPixelRef(sharedPixelRef, originalBitmap.pixelRefOffset()); 257 } else if (originalBitmap.empty()) { 258 copiedBitmap.reset(); 259 } else if (!originalBitmap.deepCopyTo(&copiedBitmap, originalBitmap.config())) { 260 return false; 261 } 262 copiedBitmap.setImmutable(); 263 return true; 264 } 265 266 int SkBitmapHeap::removeEntryFromLookupTable(LookupEntry* entry) { 267 // remove the bitmap index for the deleted entry 268 SkDEBUGCODE(int count = fLookupTable.count();) 269 int index = this->findInLookupTable(*entry, NULL); 270 // Verify that findInLookupTable found an existing entry rather than adding 271 // a new entry to the lookup table. 272 SkASSERT(count == fLookupTable.count()); 273 fBytesAllocated -= fStorage[entry->fStorageSlot]->fBytesAllocated; 274 SkDELETE(fLookupTable[index]); 275 fLookupTable.remove(index); 276 return index; 277 } 278 279 int32_t SkBitmapHeap::insert(const SkBitmap& originalBitmap) { 280 SkBitmapHeapEntry* entry = NULL; 281 int searchIndex = this->findInLookupTable(LookupEntry(originalBitmap), &entry); 282 283 if (entry) { 284 // Already had a copy of the bitmap in the heap. 285 if (fOwnerCount != IGNORE_OWNERS) { 286 if (fDeferAddingOwners) { 287 *fDeferredEntries.append() = entry->fSlot; 288 } else { 289 entry->addReferences(fOwnerCount); 290 } 291 } 292 if (fPreferredCount != UNLIMITED_SIZE) { 293 LookupEntry* lookupEntry = fLookupTable[searchIndex]; 294 if (lookupEntry != fMostRecentlyUsed) { 295 this->removeFromLRU(lookupEntry); 296 this->appendToLRU(lookupEntry); 297 } 298 } 299 return entry->fSlot; 300 } 301 302 // decide if we need to evict an existing heap entry or create a new one 303 if (fPreferredCount != UNLIMITED_SIZE && fStorage.count() >= fPreferredCount) { 304 // iterate through our LRU cache and try to find an entry to evict 305 LookupEntry* lookupEntry = this->findEntryToReplace(originalBitmap); 306 if (lookupEntry != NULL) { 307 // we found an entry to evict 308 entry = fStorage[lookupEntry->fStorageSlot]; 309 // Remove it from the LRU. The new entry will be added to the LRU later. 310 this->removeFromLRU(lookupEntry); 311 int index = this->removeEntryFromLookupTable(lookupEntry); 312 313 // update the current search index now that we have removed one 314 if (index < searchIndex) { 315 searchIndex--; 316 } 317 } 318 } 319 320 // if we didn't have an entry yet we need to create one 321 if (!entry) { 322 if (fPreferredCount != UNLIMITED_SIZE && fUnusedSlots.count() > 0) { 323 int slot; 324 fUnusedSlots.pop(&slot); 325 entry = fStorage[slot]; 326 } else { 327 entry = SkNEW(SkBitmapHeapEntry); 328 fStorage.append(1, &entry); 329 entry->fSlot = fStorage.count() - 1; 330 fBytesAllocated += sizeof(SkBitmapHeapEntry); 331 } 332 } 333 334 // create a copy of the bitmap 335 bool copySucceeded; 336 if (fExternalStorage) { 337 copySucceeded = fExternalStorage->insert(originalBitmap, entry->fSlot); 338 } else { 339 copySucceeded = copyBitmap(originalBitmap, entry->fBitmap); 340 } 341 342 // if the copy failed then we must abort 343 if (!copySucceeded) { 344 // delete the index 345 SkDELETE(fLookupTable[searchIndex]); 346 fLookupTable.remove(searchIndex); 347 // If entry is the last slot in storage, it is safe to delete it. 348 if (fStorage.count() - 1 == entry->fSlot) { 349 // free the slot 350 fStorage.remove(entry->fSlot); 351 fBytesAllocated -= sizeof(SkBitmapHeapEntry); 352 SkDELETE(entry); 353 } else { 354 fUnusedSlots.push(entry->fSlot); 355 } 356 return INVALID_SLOT; 357 } 358 359 // update the index with the appropriate slot in the heap 360 fLookupTable[searchIndex]->fStorageSlot = entry->fSlot; 361 362 // compute the space taken by this entry 363 // TODO if there is a shared pixel ref don't count it 364 // If the SkBitmap does not share an SkPixelRef with an SkBitmap already 365 // in the SharedHeap, also include the size of its pixels. 366 entry->fBytesAllocated = originalBitmap.getSize(); 367 368 // add the bytes from this entry to the total count 369 fBytesAllocated += entry->fBytesAllocated; 370 371 if (fOwnerCount != IGNORE_OWNERS) { 372 if (fDeferAddingOwners) { 373 *fDeferredEntries.append() = entry->fSlot; 374 } else { 375 entry->addReferences(fOwnerCount); 376 } 377 } 378 if (fPreferredCount != UNLIMITED_SIZE) { 379 this->appendToLRU(fLookupTable[searchIndex]); 380 } 381 return entry->fSlot; 382 } 383 384 void SkBitmapHeap::deferAddingOwners() { 385 fDeferAddingOwners = true; 386 } 387 388 void SkBitmapHeap::endAddingOwnersDeferral(bool add) { 389 if (add) { 390 for (int i = 0; i < fDeferredEntries.count(); i++) { 391 SkASSERT(fOwnerCount != IGNORE_OWNERS); 392 SkBitmapHeapEntry* heapEntry = this->getEntry(fDeferredEntries[i]); 393 SkASSERT(heapEntry != NULL); 394 heapEntry->addReferences(fOwnerCount); 395 } 396 } 397 fDeferAddingOwners = false; 398 fDeferredEntries.reset(); 399 } 400