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