xref: /third_party/skia/src/core/SkBitmapCache.cpp (revision cb93a386)
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 */
19uint64_t SkMakeResourceCacheSharedIDForBitmap(uint32_t bitmapGenID) {
20    uint64_t sharedID = SkSetFourByteTag('b', 'm', 'a', 'p');
21    return (sharedID << 32) | bitmapGenID;
22}
23
24void SkNotifyBitmapGenIDIsStale(uint32_t bitmapGenID) {
25    SkResourceCache::PostPurgeSharedID(SkMakeResourceCacheSharedIDForBitmap(bitmapGenID));
26}
27
28///////////////////////////////////////////////////////////////////////////////////////////////////
29
30SkBitmapCacheDesc 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
36SkBitmapCacheDesc SkBitmapCacheDesc::Make(const SkImage* image) {
37    SkIRect bounds = SkIRect::MakeWH(image->width(), image->height());
38    return Make(image->uniqueID(), bounds);
39}
40
41namespace {
42static unsigned gBitmapKeyNamespaceLabel;
43
44struct BitmapKey : public SkResourceCache::Key {
45public:
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}  // namespace
54
55//////////////////////
56#include "src/core/SkDiscardableMemory.h"
57#include "src/core/SkNextID.h"
58
59void SkBitmapCache_setImmutableWithID(SkPixelRef* pr, uint32_t id) {
60    pr->setImmutableWithID(id);
61}
62
63class SkBitmapCache::Rec : public SkResourceCache::Rec {
64public:
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
155private:
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
174void SkBitmapCache::PrivateDeleteRec(Rec* rec) { delete rec; }
175
176SkBitmapCache::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
204void SkBitmapCache::Add(RecPtr rec, SkBitmap* bitmap) {
205    SkResourceCache::Add(rec.release(), bitmap);
206}
207
208bool 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
219namespace {
220static unsigned gMipMapKeyNamespaceLabel;
221
222struct MipMapKey : public SkResourceCache::Key {
223public:
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
232struct 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
265private:
266    MipMapKey       fKey;
267    const SkMipmap* fMipMap;
268};
269}  // namespace
270
271const 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
282static SkResourceCache::DiscardableFactory get_fact(SkResourceCache* localCache) {
283    return localCache ? localCache->discardableFactory()
284                      : SkResourceCache::GetDiscardableFactory();
285}
286
287const SkMipmap* SkMipmapCache::AddAndRef(const SkImage_Base* image, SkResourceCache* localCache) {
288    SkBitmap src;
289    if (!image->getROPixels(nullptr, &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