1 /* 2 * Copyright 2014 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 "SkBitmapCache.h" 9 #include "SkImage.h" 10 #include "SkResourceCache.h" 11 #include "SkMipMap.h" 12 #include "SkPixelRef.h" 13 #include "SkRect.h" 14 15 /** 16 * Use this for bitmapcache and mipmapcache entries. 17 */ 18 uint64_t SkMakeResourceCacheSharedIDForBitmap(uint32_t bitmapGenID) { 19 uint64_t sharedID = SkSetFourByteTag('b', 'm', 'a', 'p'); 20 return (sharedID << 32) | bitmapGenID; 21 } 22 23 void SkNotifyBitmapGenIDIsStale(uint32_t bitmapGenID) { 24 SkResourceCache::PostPurgeSharedID(SkMakeResourceCacheSharedIDForBitmap(bitmapGenID)); 25 } 26 27 /////////////////////////////////////////////////////////////////////////////////////////////////// 28 29 /** 30 This function finds the bounds of the bitmap *within its pixelRef*. 31 If the bitmap lacks a pixelRef, it will return an empty rect, since 32 that doesn't make sense. This may be a useful enough function that 33 it should be somewhere else (in SkBitmap?). 34 */ 35 static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) { 36 if (!(bm.pixelRef())) { 37 return SkIRect::MakeEmpty(); 38 } 39 SkIPoint origin = bm.pixelRefOrigin(); 40 return SkIRect::MakeXYWH(origin.fX, origin.fY, bm.width(), bm.height()); 41 } 42 43 /** 44 * This function finds the bounds of the image. Today this is just the entire bounds, 45 * but in the future we may support subsets within an image, in which case this should 46 * return that subset (see get_bounds_from_bitmap). 47 */ 48 static SkIRect get_bounds_from_image(const SkImage* image) { 49 SkASSERT(image->width() > 0 && image->height() > 0); 50 return SkIRect::MakeWH(image->width(), image->height()); 51 } 52 53 SkBitmapCacheDesc SkBitmapCacheDesc::Make(uint32_t imageID, int origWidth, int origHeight) { 54 SkASSERT(imageID); 55 SkASSERT(origWidth > 0 && origHeight > 0); 56 return { imageID, 0, 0, {0, 0, origWidth, origHeight} }; 57 } 58 59 SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkBitmap& bm, int scaledWidth, int scaledHeight) { 60 SkASSERT(bm.width() > 0 && bm.height() > 0); 61 SkASSERT(scaledWidth > 0 && scaledHeight > 0); 62 SkASSERT(scaledWidth != bm.width() || scaledHeight != bm.height()); 63 64 return { bm.getGenerationID(), scaledWidth, scaledHeight, get_bounds_from_bitmap(bm) }; 65 } 66 67 SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkBitmap& bm) { 68 SkASSERT(bm.width() > 0 && bm.height() > 0); 69 SkASSERT(bm.pixelRefOrigin() == SkIPoint::Make(0, 0)); 70 71 return { bm.getGenerationID(), 0, 0, get_bounds_from_bitmap(bm) }; 72 } 73 74 SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image, int scaledWidth, int scaledHeight) { 75 SkASSERT(image->width() > 0 && image->height() > 0); 76 SkASSERT(scaledWidth > 0 && scaledHeight > 0); 77 78 // If the dimensions are the same, should we set them to 0,0? 79 //SkASSERT(scaledWidth != image->width() || scaledHeight != image->height()); 80 81 return { image->uniqueID(), scaledWidth, scaledHeight, get_bounds_from_image(image) }; 82 } 83 84 SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image) { 85 SkASSERT(image->width() > 0 && image->height() > 0); 86 87 return { image->uniqueID(), 0, 0, get_bounds_from_image(image) }; 88 } 89 90 namespace { 91 static unsigned gBitmapKeyNamespaceLabel; 92 93 struct BitmapKey : public SkResourceCache::Key { 94 public: 95 BitmapKey(const SkBitmapCacheDesc& desc) : fDesc(desc) { 96 this->init(&gBitmapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fDesc.fImageID), 97 sizeof(fDesc)); 98 } 99 100 void dump() const { 101 SkDebugf("-- add [%d %d] %d [%d %d %d %d]\n", 102 fDesc.fScaledWidth, fDesc.fScaledHeight, fDesc.fImageID, 103 fDesc.fSubset.x(), fDesc.fSubset.y(), fDesc.fSubset.width(), fDesc.fSubset.height()); 104 } 105 106 const SkBitmapCacheDesc fDesc; 107 }; 108 } 109 110 ////////////////////// 111 #include "SkDiscardableMemory.h" 112 #include "SkNextID.h" 113 114 void SkBitmapCache_setImmutableWithID(SkPixelRef* pr, uint32_t id) { 115 pr->setImmutableWithID(id); 116 } 117 118 //#define REC_TRACE SkDebugf 119 static void REC_TRACE(const char format[], ...) {} 120 121 // for diagnostics 122 static int32_t gRecCounter; 123 124 class SkBitmapCache::Rec : public SkResourceCache::Rec { 125 public: 126 Rec(const SkBitmapCacheDesc& desc, const SkImageInfo& info, size_t rowBytes, 127 std::unique_ptr<SkDiscardableMemory> dm, void* block) 128 : fKey(desc) 129 , fDM(std::move(dm)) 130 , fMalloc(block) 131 , fInfo(info) 132 , fRowBytes(rowBytes) 133 , fExternalCounter(kBeforeFirstInstall_ExternalCounter) 134 { 135 SkASSERT(!(fDM && fMalloc)); // can't have both 136 137 // We need an ID to return with the bitmap/pixelref. 138 // If they are not scaling, we can return the same ID as the key/desc 139 // If they are scaling, we need a new ID 140 if (desc.fScaledWidth == 0 && desc.fScaledHeight == 0) { 141 fPrUniqueID = desc.fImageID; 142 } else { 143 fPrUniqueID = SkNextID::ImageID(); 144 } 145 REC_TRACE(" Rec(%d): [%d %d] %d\n", 146 sk_atomic_inc(&gRecCounter), fInfo.width(), fInfo.height(), fPrUniqueID); 147 } 148 149 ~Rec() override { 150 SkASSERT(0 == fExternalCounter || kBeforeFirstInstall_ExternalCounter == fExternalCounter); 151 if (fDM && kBeforeFirstInstall_ExternalCounter == fExternalCounter) { 152 // we never installed, so we need to unlock before we destroy the DM 153 SkASSERT(fDM->data()); 154 fDM->unlock(); 155 } 156 REC_TRACE("~Rec(%d): [%d %d] %d\n", 157 sk_atomic_dec(&gRecCounter) - 1, fInfo.width(), fInfo.height(), fPrUniqueID); 158 sk_free(fMalloc); // may be null 159 } 160 161 const Key& getKey() const override { return fKey; } 162 size_t bytesUsed() const override { 163 return sizeof(fKey) + fInfo.getSafeSize(fRowBytes); 164 } 165 bool canBePurged() override { 166 SkAutoMutexAcquire ama(fMutex); 167 return fExternalCounter == 0; 168 } 169 void postAddInstall(void* payload) override { 170 SkAssertResult(this->install(static_cast<SkBitmap*>(payload))); 171 } 172 173 const char* getCategory() const override { return "bitmap"; } 174 SkDiscardableMemory* diagnostic_only_getDiscardable() const override { 175 return fDM.get(); 176 } 177 178 static void ReleaseProc(void* addr, void* ctx) { 179 Rec* rec = static_cast<Rec*>(ctx); 180 SkAutoMutexAcquire ama(rec->fMutex); 181 182 REC_TRACE(" Rec: [%d] releaseproc\n", rec->fPrUniqueID); 183 184 SkASSERT(rec->fExternalCounter > 0); 185 rec->fExternalCounter -= 1; 186 if (rec->fDM) { 187 SkASSERT(rec->fMalloc == nullptr); 188 if (rec->fExternalCounter == 0) { 189 REC_TRACE(" Rec [%d] unlock\n", rec->fPrUniqueID); 190 rec->fDM->unlock(); 191 } 192 } else { 193 SkASSERT(rec->fMalloc != nullptr); 194 } 195 } 196 197 bool install(SkBitmap* bitmap) { 198 SkAutoMutexAcquire ama(fMutex); 199 200 // are we still valid 201 if (!fDM && !fMalloc) { 202 REC_TRACE(" Rec: [%d] invalid\n", fPrUniqueID); 203 return false; 204 } 205 206 /* 207 constructor fExternalCount < 0 fDM->data() 208 after install fExternalCount > 0 fDM->data() 209 after Release fExternalCount == 0 !fDM->data() 210 */ 211 if (fDM) { 212 if (kBeforeFirstInstall_ExternalCounter == fExternalCounter) { 213 SkASSERT(fDM->data()); 214 } else if (fExternalCounter > 0) { 215 SkASSERT(fDM->data()); 216 } else { 217 SkASSERT(fExternalCounter == 0); 218 if (!fDM->lock()) { 219 REC_TRACE(" Rec [%d] re-lock failed\n", fPrUniqueID); 220 fDM.reset(nullptr); 221 return false; 222 } 223 REC_TRACE(" Rec [%d] re-lock succeeded\n", fPrUniqueID); 224 } 225 SkASSERT(fDM->data()); 226 } 227 228 bitmap->installPixels(fInfo, fDM ? fDM->data() : fMalloc, fRowBytes, ReleaseProc, this); 229 SkBitmapCache_setImmutableWithID(bitmap->pixelRef(), fPrUniqueID); 230 231 REC_TRACE(" Rec: [%d] install new pr\n", fPrUniqueID); 232 233 if (kBeforeFirstInstall_ExternalCounter == fExternalCounter) { 234 fExternalCounter = 1; 235 } else { 236 fExternalCounter += 1; 237 } 238 SkASSERT(fExternalCounter > 0); 239 return true; 240 } 241 242 static bool Finder(const SkResourceCache::Rec& baseRec, void* contextBitmap) { 243 Rec* rec = (Rec*)&baseRec; 244 SkBitmap* result = (SkBitmap*)contextBitmap; 245 REC_TRACE(" Rec: [%d] found\n", rec->fPrUniqueID); 246 return rec->install(result); 247 } 248 249 private: 250 BitmapKey fKey; 251 252 SkMutex fMutex; 253 254 // either fDM or fMalloc can be non-null, but not both 255 std::unique_ptr<SkDiscardableMemory> fDM; 256 void* fMalloc; 257 258 SkImageInfo fInfo; 259 size_t fRowBytes; 260 uint32_t fPrUniqueID; 261 262 // This field counts the number of external pixelrefs we have created. They notify us when 263 // they are destroyed so we can decrement this. 264 // 265 // > 0 we have outstanding pixelrefs 266 // == 0 we have no outstanding pixelrefs, and can be safely purged 267 // < 0 we have been created, but not yet "installed" the first time. 268 // 269 int fExternalCounter; 270 271 enum { 272 kBeforeFirstInstall_ExternalCounter = -1 273 }; 274 }; 275 276 void SkBitmapCache::PrivateDeleteRec(Rec* rec) { delete rec; } 277 278 SkBitmapCache::RecPtr SkBitmapCache::Alloc(const SkBitmapCacheDesc& desc, const SkImageInfo& info, 279 SkPixmap* pmap) { 280 // Ensure that the caller is self-consistent: 281 // - if they are scaling, the info matches the scaled size 282 // - if they are not, the info matches the subset (i.e. the subset is the entire image) 283 if (desc.fScaledWidth == 0 && desc.fScaledHeight == 0) { 284 SkASSERT(info.width() == desc.fSubset.width()); 285 SkASSERT(info.height() == desc.fSubset.height()); 286 } else { 287 SkASSERT(info.width() == desc.fScaledWidth); 288 SkASSERT(info.height() == desc.fScaledHeight); 289 } 290 291 const size_t rb = info.minRowBytes(); 292 size_t size = info.getSafeSize(rb); 293 if (0 == size) { 294 return nullptr; 295 } 296 297 std::unique_ptr<SkDiscardableMemory> dm; 298 void* block = nullptr; 299 300 auto factory = SkResourceCache::GetDiscardableFactory(); 301 if (factory) { 302 dm.reset(factory(size)); 303 } else { 304 block = sk_malloc_flags(size, 0); 305 } 306 if (!dm && !block) { 307 return nullptr; 308 } 309 *pmap = SkPixmap(info, dm ? dm->data() : block, rb); 310 return RecPtr(new Rec(desc, info, rb, std::move(dm), block)); 311 } 312 313 void SkBitmapCache::Add(RecPtr rec, SkBitmap* bitmap) { 314 SkResourceCache::Add(rec.release(), bitmap); 315 } 316 317 bool SkBitmapCache::Find(const SkBitmapCacheDesc& desc, SkBitmap* result) { 318 desc.validate(); 319 return SkResourceCache::Find(BitmapKey(desc), SkBitmapCache::Rec::Finder, result); 320 } 321 322 ////////////////////////////////////////////////////////////////////////////////////////// 323 ////////////////////////////////////////////////////////////////////////////////////////// 324 325 #define CHECK_LOCAL(localCache, localName, globalName, ...) \ 326 ((localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__)) 327 328 namespace { 329 static unsigned gMipMapKeyNamespaceLabel; 330 331 struct MipMapKey : public SkResourceCache::Key { 332 public: 333 MipMapKey(uint32_t imageID, const SkIRect& subset, SkDestinationSurfaceColorMode colorMode) 334 : fImageID(imageID) 335 , fColorMode(static_cast<uint32_t>(colorMode)) 336 , fSubset(subset) 337 { 338 SkASSERT(fImageID); 339 SkASSERT(!subset.isEmpty()); 340 this->init(&gMipMapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fImageID), 341 sizeof(fImageID) + sizeof(fColorMode) + sizeof(fSubset)); 342 } 343 344 uint32_t fImageID; 345 uint32_t fColorMode; 346 SkIRect fSubset; 347 }; 348 349 struct MipMapRec : public SkResourceCache::Rec { 350 MipMapRec(uint32_t imageID, const SkIRect& subset, SkDestinationSurfaceColorMode colorMode, 351 const SkMipMap* result) 352 : fKey(imageID, subset, colorMode) 353 , fMipMap(result) 354 { 355 fMipMap->attachToCacheAndRef(); 356 } 357 358 ~MipMapRec() override { 359 fMipMap->detachFromCacheAndUnref(); 360 } 361 362 const Key& getKey() const override { return fKey; } 363 size_t bytesUsed() const override { return sizeof(fKey) + fMipMap->size(); } 364 const char* getCategory() const override { return "mipmap"; } 365 SkDiscardableMemory* diagnostic_only_getDiscardable() const override { 366 return fMipMap->diagnostic_only_getDiscardable(); 367 } 368 369 static bool Finder(const SkResourceCache::Rec& baseRec, void* contextMip) { 370 const MipMapRec& rec = static_cast<const MipMapRec&>(baseRec); 371 const SkMipMap* mm = SkRef(rec.fMipMap); 372 // the call to ref() above triggers a "lock" in the case of discardable memory, 373 // which means we can now check for null (in case the lock failed). 374 if (nullptr == mm->data()) { 375 mm->unref(); // balance our call to ref() 376 return false; 377 } 378 // the call must call unref() when they are done. 379 *(const SkMipMap**)contextMip = mm; 380 return true; 381 } 382 383 private: 384 MipMapKey fKey; 385 const SkMipMap* fMipMap; 386 }; 387 } 388 389 const SkMipMap* SkMipMapCache::FindAndRef(const SkBitmapCacheDesc& desc, 390 SkDestinationSurfaceColorMode colorMode, 391 SkResourceCache* localCache) { 392 SkASSERT(desc.fScaledWidth == 0); 393 SkASSERT(desc.fScaledHeight == 0); 394 MipMapKey key(desc.fImageID, desc.fSubset, colorMode); 395 const SkMipMap* result; 396 397 if (!CHECK_LOCAL(localCache, find, Find, key, MipMapRec::Finder, &result)) { 398 result = nullptr; 399 } 400 return result; 401 } 402 403 static SkResourceCache::DiscardableFactory get_fact(SkResourceCache* localCache) { 404 return localCache ? localCache->GetDiscardableFactory() 405 : SkResourceCache::GetDiscardableFactory(); 406 } 407 408 const SkMipMap* SkMipMapCache::AddAndRef(const SkBitmap& src, 409 SkDestinationSurfaceColorMode colorMode, 410 SkResourceCache* localCache) { 411 SkMipMap* mipmap = SkMipMap::Build(src, colorMode, get_fact(localCache)); 412 if (mipmap) { 413 MipMapRec* rec = new MipMapRec(src.getGenerationID(), get_bounds_from_bitmap(src), 414 colorMode, mipmap); 415 CHECK_LOCAL(localCache, add, Add, rec); 416 src.pixelRef()->notifyAddedToCache(); 417 } 418 return mipmap; 419 } 420