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