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 "SkBitmapProvider.h" 10 #include "SkImage.h" 11 #include "SkResourceCache.h" 12 #include "SkMipMap.h" 13 #include "SkPixelRef.h" 14 #include "SkRect.h" 15 16 /** 17 * Use this for bitmapcache and mipmapcache entries. 18 */ 19 uint64_t SkMakeResourceCacheSharedIDForBitmap(uint32_t bitmapGenID) { 20 uint64_t sharedID = SkSetFourByteTag('b', 'm', 'a', 'p'); 21 return (sharedID << 32) | bitmapGenID; 22 } 23 24 void SkNotifyBitmapGenIDIsStale(uint32_t bitmapGenID) { 25 SkResourceCache::PostPurgeSharedID(SkMakeResourceCacheSharedIDForBitmap(bitmapGenID)); 26 } 27 28 /////////////////////////////////////////////////////////////////////////////////////////////////// 29 30 SkBitmapCacheDesc SkBitmapCacheDesc::Make(uint32_t imageID, const SkIRect& subset) { 31 SkASSERT(imageID); 32 SkASSERT(subset.width() > 0 && subset.height() > 0); 33 return { imageID, subset }; 34 } 35 36 SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image) { 37 SkIRect bounds = SkIRect::MakeWH(image->width(), image->height()); 38 return Make(image->uniqueID(), bounds); 39 } 40 41 namespace { 42 static unsigned gBitmapKeyNamespaceLabel; 43 44 struct BitmapKey : public SkResourceCache::Key { 45 public: 46 BitmapKey(const SkBitmapCacheDesc& desc) : fDesc(desc) { 47 this->init(&gBitmapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fDesc.fImageID), 48 sizeof(fDesc)); 49 } 50 51 const SkBitmapCacheDesc fDesc; 52 }; 53 } 54 55 ////////////////////// 56 #include "SkDiscardableMemory.h" 57 #include "SkNextID.h" 58 59 void SkBitmapCache_setImmutableWithID(SkPixelRef* pr, uint32_t id) { 60 pr->setImmutableWithID(id); 61 } 62 63 class SkBitmapCache::Rec : public SkResourceCache::Rec { 64 public: 65 Rec(const SkBitmapCacheDesc& desc, const SkImageInfo& info, size_t rowBytes, 66 std::unique_ptr<SkDiscardableMemory> dm, void* block) 67 : fKey(desc) 68 , fDM(std::move(dm)) 69 , fMalloc(block) 70 , fInfo(info) 71 , fRowBytes(rowBytes) 72 { 73 SkASSERT(!(fDM && fMalloc)); // can't have both 74 75 // We need an ID to return with the bitmap/pixelref. We can't necessarily use the key/desc 76 // ID - lazy images cache the same ID with multiple keys (in different color types). 77 fPrUniqueID = SkNextID::ImageID(); 78 } 79 80 ~Rec() override { 81 SkASSERT(0 == fExternalCounter); 82 if (fDM && fDiscardableIsLocked) { 83 SkASSERT(fDM->data()); 84 fDM->unlock(); 85 } 86 sk_free(fMalloc); // may be null 87 } 88 89 const Key& getKey() const override { return fKey; } 90 size_t bytesUsed() const override { 91 return sizeof(fKey) + fInfo.computeByteSize(fRowBytes); 92 } 93 bool canBePurged() override { 94 SkAutoMutexAcquire ama(fMutex); 95 return fExternalCounter == 0; 96 } 97 void postAddInstall(void* payload) override { 98 SkAssertResult(this->install(static_cast<SkBitmap*>(payload))); 99 } 100 101 const char* getCategory() const override { return "bitmap"; } 102 SkDiscardableMemory* diagnostic_only_getDiscardable() const override { 103 return fDM.get(); 104 } 105 106 static void ReleaseProc(void* addr, void* ctx) { 107 Rec* rec = static_cast<Rec*>(ctx); 108 SkAutoMutexAcquire ama(rec->fMutex); 109 110 SkASSERT(rec->fExternalCounter > 0); 111 rec->fExternalCounter -= 1; 112 if (rec->fDM) { 113 SkASSERT(rec->fMalloc == nullptr); 114 if (rec->fExternalCounter == 0) { 115 rec->fDM->unlock(); 116 rec->fDiscardableIsLocked = false; 117 } 118 } else { 119 SkASSERT(rec->fMalloc != nullptr); 120 } 121 } 122 123 bool install(SkBitmap* bitmap) { 124 SkAutoMutexAcquire ama(fMutex); 125 126 if (!fDM && !fMalloc) { 127 return false; 128 } 129 130 if (fDM) { 131 if (!fDiscardableIsLocked) { 132 SkASSERT(fExternalCounter == 0); 133 if (!fDM->lock()) { 134 fDM.reset(nullptr); 135 return false; 136 } 137 fDiscardableIsLocked = true; 138 } 139 SkASSERT(fDM->data()); 140 } 141 142 bitmap->installPixels(fInfo, fDM ? fDM->data() : fMalloc, fRowBytes, ReleaseProc, this); 143 SkBitmapCache_setImmutableWithID(bitmap->pixelRef(), fPrUniqueID); 144 fExternalCounter++; 145 146 return true; 147 } 148 149 static bool Finder(const SkResourceCache::Rec& baseRec, void* contextBitmap) { 150 Rec* rec = (Rec*)&baseRec; 151 SkBitmap* result = (SkBitmap*)contextBitmap; 152 return rec->install(result); 153 } 154 155 private: 156 BitmapKey fKey; 157 158 SkMutex fMutex; 159 160 // either fDM or fMalloc can be non-null, but not both 161 std::unique_ptr<SkDiscardableMemory> fDM; 162 void* fMalloc; 163 164 SkImageInfo fInfo; 165 size_t fRowBytes; 166 uint32_t fPrUniqueID; 167 168 // This field counts the number of external pixelrefs we have created. 169 // They notify us when they are destroyed so we can decrement this. 170 int fExternalCounter = 0; 171 bool fDiscardableIsLocked = true; 172 }; 173 174 void SkBitmapCache::PrivateDeleteRec(Rec* rec) { delete rec; } 175 176 SkBitmapCache::RecPtr SkBitmapCache::Alloc(const SkBitmapCacheDesc& desc, const SkImageInfo& info, 177 SkPixmap* pmap) { 178 // Ensure that the info matches the subset (i.e. the subset is the entire image) 179 SkASSERT(info.width() == desc.fSubset.width()); 180 SkASSERT(info.height() == desc.fSubset.height()); 181 182 const size_t rb = info.minRowBytes(); 183 size_t size = info.computeByteSize(rb); 184 if (SkImageInfo::ByteSizeOverflowed(size)) { 185 return nullptr; 186 } 187 188 std::unique_ptr<SkDiscardableMemory> dm; 189 void* block = nullptr; 190 191 auto factory = SkResourceCache::GetDiscardableFactory(); 192 if (factory) { 193 dm.reset(factory(size)); 194 } else { 195 block = sk_malloc_canfail(size); 196 } 197 if (!dm && !block) { 198 return nullptr; 199 } 200 *pmap = SkPixmap(info, dm ? dm->data() : block, rb); 201 return RecPtr(new Rec(desc, info, rb, std::move(dm), block)); 202 } 203 204 void SkBitmapCache::Add(RecPtr rec, SkBitmap* bitmap) { 205 SkResourceCache::Add(rec.release(), bitmap); 206 } 207 208 bool SkBitmapCache::Find(const SkBitmapCacheDesc& desc, SkBitmap* result) { 209 desc.validate(); 210 return SkResourceCache::Find(BitmapKey(desc), SkBitmapCache::Rec::Finder, result); 211 } 212 213 ////////////////////////////////////////////////////////////////////////////////////////// 214 ////////////////////////////////////////////////////////////////////////////////////////// 215 216 #define CHECK_LOCAL(localCache, localName, globalName, ...) \ 217 ((localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__)) 218 219 namespace { 220 static unsigned gMipMapKeyNamespaceLabel; 221 222 struct MipMapKey : public SkResourceCache::Key { 223 public: 224 MipMapKey(const SkBitmapCacheDesc& desc) : fDesc(desc) { 225 this->init(&gMipMapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(fDesc.fImageID), 226 sizeof(fDesc)); 227 } 228 229 const SkBitmapCacheDesc fDesc; 230 }; 231 232 struct MipMapRec : public SkResourceCache::Rec { 233 MipMapRec(const SkBitmapCacheDesc& desc, const SkMipMap* result) 234 : fKey(desc) 235 , fMipMap(result) 236 { 237 fMipMap->attachToCacheAndRef(); 238 } 239 240 ~MipMapRec() override { 241 fMipMap->detachFromCacheAndUnref(); 242 } 243 244 const Key& getKey() const override { return fKey; } 245 size_t bytesUsed() const override { return sizeof(fKey) + fMipMap->size(); } 246 const char* getCategory() const override { return "mipmap"; } 247 SkDiscardableMemory* diagnostic_only_getDiscardable() const override { 248 return fMipMap->diagnostic_only_getDiscardable(); 249 } 250 251 static bool Finder(const SkResourceCache::Rec& baseRec, void* contextMip) { 252 const MipMapRec& rec = static_cast<const MipMapRec&>(baseRec); 253 const SkMipMap* mm = SkRef(rec.fMipMap); 254 // the call to ref() above triggers a "lock" in the case of discardable memory, 255 // which means we can now check for null (in case the lock failed). 256 if (nullptr == mm->data()) { 257 mm->unref(); // balance our call to ref() 258 return false; 259 } 260 // the call must call unref() when they are done. 261 *(const SkMipMap**)contextMip = mm; 262 return true; 263 } 264 265 private: 266 MipMapKey fKey; 267 const SkMipMap* fMipMap; 268 }; 269 } 270 271 const SkMipMap* SkMipMapCache::FindAndRef(const SkBitmapCacheDesc& desc, 272 SkResourceCache* localCache) { 273 MipMapKey key(desc); 274 const SkMipMap* result; 275 276 if (!CHECK_LOCAL(localCache, find, Find, key, MipMapRec::Finder, &result)) { 277 result = nullptr; 278 } 279 return result; 280 } 281 282 static SkResourceCache::DiscardableFactory get_fact(SkResourceCache* localCache) { 283 return localCache ? localCache->GetDiscardableFactory() 284 : SkResourceCache::GetDiscardableFactory(); 285 } 286 287 const SkMipMap* SkMipMapCache::AddAndRef(const SkBitmapProvider& provider, 288 SkResourceCache* localCache) { 289 SkBitmap src; 290 if (!provider.asBitmap(&src)) { 291 return nullptr; 292 } 293 294 SkMipMap* mipmap = SkMipMap::Build(src, get_fact(localCache)); 295 if (mipmap) { 296 MipMapRec* rec = new MipMapRec(provider.makeCacheDesc(), mipmap); 297 CHECK_LOCAL(localCache, add, Add, rec); 298 provider.notifyAddedToCache(); 299 } 300 return mipmap; 301 } 302