Home | History | Annotate | Download | only in core
      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