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 "include/core/SkImage.h" 9#include "include/core/SkPixelRef.h" 10#include "include/core/SkRect.h" 11#include "src/core/SkBitmapCache.h" 12#include "src/core/SkMipmap.h" 13#include "src/core/SkResourceCache.h" 14#include "src/image/SkImage_Base.h" 15 16/** 17 * Use this for bitmapcache and mipmapcache entries. 18 */ 19uint64_t SkMakeResourceCacheSharedIDForBitmap(uint32_t bitmapGenID) { 20 uint64_t sharedID = SkSetFourByteTag('b', 'm', 'a', 'p'); 21 return (sharedID << 32) | bitmapGenID; 22} 23 24void SkNotifyBitmapGenIDIsStale(uint32_t bitmapGenID) { 25 SkResourceCache::PostPurgeSharedID(SkMakeResourceCacheSharedIDForBitmap(bitmapGenID)); 26} 27 28/////////////////////////////////////////////////////////////////////////////////////////////////// 29 30SkBitmapCacheDesc 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 36SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image) { 37 SkIRect bounds = SkIRect::MakeWH(image->width(), image->height()); 38 return Make(image->uniqueID(), bounds); 39} 40 41namespace { 42static unsigned gBitmapKeyNamespaceLabel; 43 44struct BitmapKey : public SkResourceCache::Key { 45public: 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} // namespace 54 55////////////////////// 56#include "src/core/SkDiscardableMemory.h" 57#include "src/core/SkNextID.h" 58 59void SkBitmapCache_setImmutableWithID(SkPixelRef* pr, uint32_t id) { 60 pr->setImmutableWithID(id); 61} 62 63class SkBitmapCache::Rec : public SkResourceCache::Rec { 64public: 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 SkAutoMutexExclusive 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 SkAutoMutexExclusive 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 SkAutoMutexExclusive 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 155private: 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 174void SkBitmapCache::PrivateDeleteRec(Rec* rec) { delete rec; } 175 176SkBitmapCache::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 204void SkBitmapCache::Add(RecPtr rec, SkBitmap* bitmap) { 205 SkResourceCache::Add(rec.release(), bitmap); 206} 207 208bool 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 219namespace { 220static unsigned gMipMapKeyNamespaceLabel; 221 222struct MipMapKey : public SkResourceCache::Key { 223public: 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 232struct 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 265private: 266 MipMapKey fKey; 267 const SkMipmap* fMipMap; 268}; 269} // namespace 270 271const 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 282static SkResourceCache::DiscardableFactory get_fact(SkResourceCache* localCache) { 283 return localCache ? localCache->discardableFactory() 284 : SkResourceCache::GetDiscardableFactory(); 285} 286 287const SkMipmap* SkMipmapCache::AddAndRef(const SkImage_Base* image, SkResourceCache* localCache) { 288 SkBitmap src; 289 if (!image->getROPixels(nullptr, &src)) { 290 return nullptr; 291 } 292 293 SkMipmap* mipmap = SkMipmap::Build(src, get_fact(localCache)); 294 if (mipmap) { 295 MipMapRec* rec = new MipMapRec(SkBitmapCacheDesc::Make(image), mipmap); 296 CHECK_LOCAL(localCache, add, Add, rec); 297 image->notifyAddedToRasterCache(); 298 } 299 return mipmap; 300} 301