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 "include/core/SkImage.h" |
9 | #include "include/core/SkPixelRef.h" |
10 | #include "include/core/SkRect.h" |
11 | #include "src/core/SkBitmapCache.h" |
12 | #include "src/core/SkMipMap.h" |
13 | #include "src/core/SkResourceCache.h" |
14 | #include "src/image/SkImage_Base.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 "src/core/SkDiscardableMemory.h" |
57 | #include "src/core/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 | SkAutoMutexExclusive 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 | SkAutoMutexExclusive 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 | SkAutoMutexExclusive 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 SkImage_Base* image, SkResourceCache* localCache) { |
288 | SkBitmap src; |
289 | if (!image->getROPixels(&src)) { |
290 | return nullptr; |
291 | } |
292 | |
293 | SkMipMap* mipmap = SkMipMap::Build(src, get_fact(localCache)); |
294 | if (mipmap) { |
295 | MipMapRec* rec = new MipMapRec(SkBitmapCacheDesc::Make(image), mipmap); |
296 | CHECK_LOCAL(localCache, add, Add, rec); |
297 | image->notifyAddedToRasterCache(); |
298 | } |
299 | return mipmap; |
300 | } |
301 | |