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