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