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