xref: /aosp_15_r20/external/skia/src/lazy/SkDiscardableMemoryPool.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2013 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMalloc.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMutex.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/chromium/SkDiscardableMemory.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTInternalLList.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/lazy/SkDiscardableMemoryPool.h"
14*c8dee2aaSAndroid Build Coastguard Worker 
15*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
16*c8dee2aaSAndroid Build Coastguard Worker 
17*c8dee2aaSAndroid Build Coastguard Worker // Note:
18*c8dee2aaSAndroid Build Coastguard Worker // A PoolDiscardableMemory is memory that is counted in a pool.
19*c8dee2aaSAndroid Build Coastguard Worker // A DiscardableMemoryPool is a pool of PoolDiscardableMemorys.
20*c8dee2aaSAndroid Build Coastguard Worker 
21*c8dee2aaSAndroid Build Coastguard Worker namespace {
22*c8dee2aaSAndroid Build Coastguard Worker 
23*c8dee2aaSAndroid Build Coastguard Worker class PoolDiscardableMemory;
24*c8dee2aaSAndroid Build Coastguard Worker 
25*c8dee2aaSAndroid Build Coastguard Worker /**
26*c8dee2aaSAndroid Build Coastguard Worker  *  This non-global pool can be used for unit tests to verify that the
27*c8dee2aaSAndroid Build Coastguard Worker  *  pool works.
28*c8dee2aaSAndroid Build Coastguard Worker  */
29*c8dee2aaSAndroid Build Coastguard Worker class DiscardableMemoryPool : public SkDiscardableMemoryPool {
30*c8dee2aaSAndroid Build Coastguard Worker public:
31*c8dee2aaSAndroid Build Coastguard Worker     DiscardableMemoryPool(size_t budget);
32*c8dee2aaSAndroid Build Coastguard Worker     ~DiscardableMemoryPool() override;
33*c8dee2aaSAndroid Build Coastguard Worker 
34*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkDiscardableMemory> make(size_t bytes);
create(size_t bytes)35*c8dee2aaSAndroid Build Coastguard Worker     SkDiscardableMemory* create(size_t bytes) override {
36*c8dee2aaSAndroid Build Coastguard Worker         return this->make(bytes).release();  // TODO: change API
37*c8dee2aaSAndroid Build Coastguard Worker     }
38*c8dee2aaSAndroid Build Coastguard Worker 
39*c8dee2aaSAndroid Build Coastguard Worker     size_t getRAMUsed() override;
40*c8dee2aaSAndroid Build Coastguard Worker     void setRAMBudget(size_t budget) override;
getRAMBudget()41*c8dee2aaSAndroid Build Coastguard Worker     size_t getRAMBudget() override { return fBudget; }
42*c8dee2aaSAndroid Build Coastguard Worker 
43*c8dee2aaSAndroid Build Coastguard Worker     /** purges all unlocked DMs */
44*c8dee2aaSAndroid Build Coastguard Worker     void dumpPool() override;
45*c8dee2aaSAndroid Build Coastguard Worker 
46*c8dee2aaSAndroid Build Coastguard Worker     #if SK_LAZY_CACHE_STATS  // Defined in SkDiscardableMemoryPool.h
getCacheHits()47*c8dee2aaSAndroid Build Coastguard Worker     int getCacheHits() override { return fCacheHits; }
getCacheMisses()48*c8dee2aaSAndroid Build Coastguard Worker     int getCacheMisses() override { return fCacheMisses; }
resetCacheHitsAndMisses()49*c8dee2aaSAndroid Build Coastguard Worker     void resetCacheHitsAndMisses() override {
50*c8dee2aaSAndroid Build Coastguard Worker         fCacheHits = fCacheMisses = 0;
51*c8dee2aaSAndroid Build Coastguard Worker     }
52*c8dee2aaSAndroid Build Coastguard Worker     int          fCacheHits;
53*c8dee2aaSAndroid Build Coastguard Worker     int          fCacheMisses;
54*c8dee2aaSAndroid Build Coastguard Worker     #endif  // SK_LAZY_CACHE_STATS
55*c8dee2aaSAndroid Build Coastguard Worker 
56*c8dee2aaSAndroid Build Coastguard Worker private:
57*c8dee2aaSAndroid Build Coastguard Worker     SkMutex      fMutex;
58*c8dee2aaSAndroid Build Coastguard Worker     size_t       fBudget;
59*c8dee2aaSAndroid Build Coastguard Worker     size_t       fUsed;
60*c8dee2aaSAndroid Build Coastguard Worker     SkTInternalLList<PoolDiscardableMemory> fList;
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker     /** Function called to free memory if needed */
63*c8dee2aaSAndroid Build Coastguard Worker     void dumpDownTo(size_t budget);
64*c8dee2aaSAndroid Build Coastguard Worker     /** called by DiscardableMemoryPool upon destruction */
65*c8dee2aaSAndroid Build Coastguard Worker     void removeFromPool(PoolDiscardableMemory* dm);
66*c8dee2aaSAndroid Build Coastguard Worker     /** called by DiscardableMemoryPool::lock() */
67*c8dee2aaSAndroid Build Coastguard Worker     bool lock(PoolDiscardableMemory* dm);
68*c8dee2aaSAndroid Build Coastguard Worker     /** called by DiscardableMemoryPool::unlock() */
69*c8dee2aaSAndroid Build Coastguard Worker     void unlock(PoolDiscardableMemory* dm);
70*c8dee2aaSAndroid Build Coastguard Worker 
71*c8dee2aaSAndroid Build Coastguard Worker     friend class PoolDiscardableMemory;
72*c8dee2aaSAndroid Build Coastguard Worker 
73*c8dee2aaSAndroid Build Coastguard Worker     using INHERITED = SkDiscardableMemory::Factory;
74*c8dee2aaSAndroid Build Coastguard Worker };
75*c8dee2aaSAndroid Build Coastguard Worker 
76*c8dee2aaSAndroid Build Coastguard Worker /**
77*c8dee2aaSAndroid Build Coastguard Worker  *  A PoolDiscardableMemory is a SkDiscardableMemory that relies on
78*c8dee2aaSAndroid Build Coastguard Worker  *  a DiscardableMemoryPool object to manage the memory.
79*c8dee2aaSAndroid Build Coastguard Worker  */
80*c8dee2aaSAndroid Build Coastguard Worker class PoolDiscardableMemory : public SkDiscardableMemory {
81*c8dee2aaSAndroid Build Coastguard Worker public:
82*c8dee2aaSAndroid Build Coastguard Worker     PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool, UniqueVoidPtr pointer, size_t bytes);
83*c8dee2aaSAndroid Build Coastguard Worker     ~PoolDiscardableMemory() override;
84*c8dee2aaSAndroid Build Coastguard Worker     bool lock() override;
85*c8dee2aaSAndroid Build Coastguard Worker     void* data() override;
86*c8dee2aaSAndroid Build Coastguard Worker     void unlock() override;
87*c8dee2aaSAndroid Build Coastguard Worker     friend class DiscardableMemoryPool;
88*c8dee2aaSAndroid Build Coastguard Worker private:
89*c8dee2aaSAndroid Build Coastguard Worker     SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory);
90*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<DiscardableMemoryPool> fPool;
91*c8dee2aaSAndroid Build Coastguard Worker     bool                         fLocked;
92*c8dee2aaSAndroid Build Coastguard Worker     UniqueVoidPtr                   fPointer;
93*c8dee2aaSAndroid Build Coastguard Worker     const size_t                 fBytes;
94*c8dee2aaSAndroid Build Coastguard Worker };
95*c8dee2aaSAndroid Build Coastguard Worker 
PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool,UniqueVoidPtr pointer,size_t bytes)96*c8dee2aaSAndroid Build Coastguard Worker PoolDiscardableMemory::PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool,
97*c8dee2aaSAndroid Build Coastguard Worker                                              UniqueVoidPtr pointer,
98*c8dee2aaSAndroid Build Coastguard Worker                                              size_t bytes)
99*c8dee2aaSAndroid Build Coastguard Worker         : fPool(std::move(pool)), fLocked(true), fPointer(std::move(pointer)), fBytes(bytes) {
100*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fPool != nullptr);
101*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fPointer != nullptr);
102*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fBytes > 0);
103*c8dee2aaSAndroid Build Coastguard Worker }
104*c8dee2aaSAndroid Build Coastguard Worker 
~PoolDiscardableMemory()105*c8dee2aaSAndroid Build Coastguard Worker PoolDiscardableMemory::~PoolDiscardableMemory() {
106*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!fLocked); // contract for SkDiscardableMemory
107*c8dee2aaSAndroid Build Coastguard Worker     fPool->removeFromPool(this);
108*c8dee2aaSAndroid Build Coastguard Worker }
109*c8dee2aaSAndroid Build Coastguard Worker 
lock()110*c8dee2aaSAndroid Build Coastguard Worker bool PoolDiscardableMemory::lock() {
111*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!fLocked); // contract for SkDiscardableMemory
112*c8dee2aaSAndroid Build Coastguard Worker     return fPool->lock(this);
113*c8dee2aaSAndroid Build Coastguard Worker }
114*c8dee2aaSAndroid Build Coastguard Worker 
data()115*c8dee2aaSAndroid Build Coastguard Worker void* PoolDiscardableMemory::data() {
116*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fLocked); // contract for SkDiscardableMemory
117*c8dee2aaSAndroid Build Coastguard Worker     return fPointer.get();
118*c8dee2aaSAndroid Build Coastguard Worker }
119*c8dee2aaSAndroid Build Coastguard Worker 
unlock()120*c8dee2aaSAndroid Build Coastguard Worker void PoolDiscardableMemory::unlock() {
121*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fLocked); // contract for SkDiscardableMemory
122*c8dee2aaSAndroid Build Coastguard Worker     fPool->unlock(this);
123*c8dee2aaSAndroid Build Coastguard Worker }
124*c8dee2aaSAndroid Build Coastguard Worker 
125*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
126*c8dee2aaSAndroid Build Coastguard Worker 
DiscardableMemoryPool(size_t budget)127*c8dee2aaSAndroid Build Coastguard Worker DiscardableMemoryPool::DiscardableMemoryPool(size_t budget)
128*c8dee2aaSAndroid Build Coastguard Worker     : fBudget(budget)
129*c8dee2aaSAndroid Build Coastguard Worker     , fUsed(0) {
130*c8dee2aaSAndroid Build Coastguard Worker     #if SK_LAZY_CACHE_STATS
131*c8dee2aaSAndroid Build Coastguard Worker     fCacheHits = 0;
132*c8dee2aaSAndroid Build Coastguard Worker     fCacheMisses = 0;
133*c8dee2aaSAndroid Build Coastguard Worker     #endif  // SK_LAZY_CACHE_STATS
134*c8dee2aaSAndroid Build Coastguard Worker }
~DiscardableMemoryPool()135*c8dee2aaSAndroid Build Coastguard Worker DiscardableMemoryPool::~DiscardableMemoryPool() {
136*c8dee2aaSAndroid Build Coastguard Worker     // PoolDiscardableMemory objects that belong to this pool are
137*c8dee2aaSAndroid Build Coastguard Worker     // always deleted before deleting this pool since each one has a
138*c8dee2aaSAndroid Build Coastguard Worker     // ref to the pool.
139*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fList.isEmpty());
140*c8dee2aaSAndroid Build Coastguard Worker }
141*c8dee2aaSAndroid Build Coastguard Worker 
dumpDownTo(size_t budget)142*c8dee2aaSAndroid Build Coastguard Worker void DiscardableMemoryPool::dumpDownTo(size_t budget) {
143*c8dee2aaSAndroid Build Coastguard Worker     fMutex.assertHeld();
144*c8dee2aaSAndroid Build Coastguard Worker     if (fUsed <= budget) {
145*c8dee2aaSAndroid Build Coastguard Worker         return;
146*c8dee2aaSAndroid Build Coastguard Worker     }
147*c8dee2aaSAndroid Build Coastguard Worker     using Iter = SkTInternalLList<PoolDiscardableMemory>::Iter;
148*c8dee2aaSAndroid Build Coastguard Worker     Iter iter;
149*c8dee2aaSAndroid Build Coastguard Worker     PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
150*c8dee2aaSAndroid Build Coastguard Worker     while ((fUsed > budget) && (cur)) {
151*c8dee2aaSAndroid Build Coastguard Worker         if (!cur->fLocked) {
152*c8dee2aaSAndroid Build Coastguard Worker             PoolDiscardableMemory* dm = cur;
153*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(dm->fPointer != nullptr);
154*c8dee2aaSAndroid Build Coastguard Worker             dm->fPointer = nullptr;
155*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(fUsed >= dm->fBytes);
156*c8dee2aaSAndroid Build Coastguard Worker             fUsed -= dm->fBytes;
157*c8dee2aaSAndroid Build Coastguard Worker             cur = iter.prev();
158*c8dee2aaSAndroid Build Coastguard Worker             // Purged DMs are taken out of the list.  This saves times
159*c8dee2aaSAndroid Build Coastguard Worker             // looking them up.  Purged DMs are NOT deleted.
160*c8dee2aaSAndroid Build Coastguard Worker             fList.remove(dm);
161*c8dee2aaSAndroid Build Coastguard Worker         } else {
162*c8dee2aaSAndroid Build Coastguard Worker             cur = iter.prev();
163*c8dee2aaSAndroid Build Coastguard Worker         }
164*c8dee2aaSAndroid Build Coastguard Worker     }
165*c8dee2aaSAndroid Build Coastguard Worker }
166*c8dee2aaSAndroid Build Coastguard Worker 
make(size_t bytes)167*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkDiscardableMemory> DiscardableMemoryPool::make(size_t bytes) {
168*c8dee2aaSAndroid Build Coastguard Worker     UniqueVoidPtr addr(sk_malloc_canfail(bytes));
169*c8dee2aaSAndroid Build Coastguard Worker     if (nullptr == addr) {
170*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
171*c8dee2aaSAndroid Build Coastguard Worker     }
172*c8dee2aaSAndroid Build Coastguard Worker     auto dm = std::make_unique<PoolDiscardableMemory>(sk_ref_sp(this), std::move(addr), bytes);
173*c8dee2aaSAndroid Build Coastguard Worker     SkAutoMutexExclusive autoMutexAcquire(fMutex);
174*c8dee2aaSAndroid Build Coastguard Worker     fList.addToHead(dm.get());
175*c8dee2aaSAndroid Build Coastguard Worker     fUsed += bytes;
176*c8dee2aaSAndroid Build Coastguard Worker     this->dumpDownTo(fBudget);
177*c8dee2aaSAndroid Build Coastguard Worker     return dm;
178*c8dee2aaSAndroid Build Coastguard Worker }
179*c8dee2aaSAndroid Build Coastguard Worker 
removeFromPool(PoolDiscardableMemory * dm)180*c8dee2aaSAndroid Build Coastguard Worker void DiscardableMemoryPool::removeFromPool(PoolDiscardableMemory* dm) {
181*c8dee2aaSAndroid Build Coastguard Worker     SkAutoMutexExclusive autoMutexAcquire(fMutex);
182*c8dee2aaSAndroid Build Coastguard Worker     // This is called by dm's destructor.
183*c8dee2aaSAndroid Build Coastguard Worker     if (dm->fPointer != nullptr) {
184*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fUsed >= dm->fBytes);
185*c8dee2aaSAndroid Build Coastguard Worker         fUsed -= dm->fBytes;
186*c8dee2aaSAndroid Build Coastguard Worker         fList.remove(dm);
187*c8dee2aaSAndroid Build Coastguard Worker     } else {
188*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fList.isInList(dm));
189*c8dee2aaSAndroid Build Coastguard Worker     }
190*c8dee2aaSAndroid Build Coastguard Worker }
191*c8dee2aaSAndroid Build Coastguard Worker 
lock(PoolDiscardableMemory * dm)192*c8dee2aaSAndroid Build Coastguard Worker bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) {
193*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(dm != nullptr);
194*c8dee2aaSAndroid Build Coastguard Worker     SkAutoMutexExclusive autoMutexAcquire(fMutex);
195*c8dee2aaSAndroid Build Coastguard Worker     if (nullptr == dm->fPointer) {
196*c8dee2aaSAndroid Build Coastguard Worker         // May have been purged while waiting for lock.
197*c8dee2aaSAndroid Build Coastguard Worker         #if SK_LAZY_CACHE_STATS
198*c8dee2aaSAndroid Build Coastguard Worker         ++fCacheMisses;
199*c8dee2aaSAndroid Build Coastguard Worker         #endif  // SK_LAZY_CACHE_STATS
200*c8dee2aaSAndroid Build Coastguard Worker         return false;
201*c8dee2aaSAndroid Build Coastguard Worker     }
202*c8dee2aaSAndroid Build Coastguard Worker     dm->fLocked = true;
203*c8dee2aaSAndroid Build Coastguard Worker     fList.remove(dm);
204*c8dee2aaSAndroid Build Coastguard Worker     fList.addToHead(dm);
205*c8dee2aaSAndroid Build Coastguard Worker     #if SK_LAZY_CACHE_STATS
206*c8dee2aaSAndroid Build Coastguard Worker     ++fCacheHits;
207*c8dee2aaSAndroid Build Coastguard Worker     #endif  // SK_LAZY_CACHE_STATS
208*c8dee2aaSAndroid Build Coastguard Worker     return true;
209*c8dee2aaSAndroid Build Coastguard Worker }
210*c8dee2aaSAndroid Build Coastguard Worker 
unlock(PoolDiscardableMemory * dm)211*c8dee2aaSAndroid Build Coastguard Worker void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
212*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(dm != nullptr);
213*c8dee2aaSAndroid Build Coastguard Worker     SkAutoMutexExclusive autoMutexAcquire(fMutex);
214*c8dee2aaSAndroid Build Coastguard Worker     dm->fLocked = false;
215*c8dee2aaSAndroid Build Coastguard Worker     this->dumpDownTo(fBudget);
216*c8dee2aaSAndroid Build Coastguard Worker }
217*c8dee2aaSAndroid Build Coastguard Worker 
getRAMUsed()218*c8dee2aaSAndroid Build Coastguard Worker size_t DiscardableMemoryPool::getRAMUsed() {
219*c8dee2aaSAndroid Build Coastguard Worker     return fUsed;
220*c8dee2aaSAndroid Build Coastguard Worker }
setRAMBudget(size_t budget)221*c8dee2aaSAndroid Build Coastguard Worker void DiscardableMemoryPool::setRAMBudget(size_t budget) {
222*c8dee2aaSAndroid Build Coastguard Worker     SkAutoMutexExclusive autoMutexAcquire(fMutex);
223*c8dee2aaSAndroid Build Coastguard Worker     fBudget = budget;
224*c8dee2aaSAndroid Build Coastguard Worker     this->dumpDownTo(fBudget);
225*c8dee2aaSAndroid Build Coastguard Worker }
dumpPool()226*c8dee2aaSAndroid Build Coastguard Worker void DiscardableMemoryPool::dumpPool() {
227*c8dee2aaSAndroid Build Coastguard Worker     SkAutoMutexExclusive autoMutexAcquire(fMutex);
228*c8dee2aaSAndroid Build Coastguard Worker     this->dumpDownTo(0);
229*c8dee2aaSAndroid Build Coastguard Worker }
230*c8dee2aaSAndroid Build Coastguard Worker 
231*c8dee2aaSAndroid Build Coastguard Worker }  // namespace
232*c8dee2aaSAndroid Build Coastguard Worker 
Make(size_t size)233*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkDiscardableMemoryPool> SkDiscardableMemoryPool::Make(size_t size) {
234*c8dee2aaSAndroid Build Coastguard Worker     return sk_make_sp<DiscardableMemoryPool>(size);
235*c8dee2aaSAndroid Build Coastguard Worker }
236*c8dee2aaSAndroid Build Coastguard Worker 
SkGetGlobalDiscardableMemoryPool()237*c8dee2aaSAndroid Build Coastguard Worker SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
238*c8dee2aaSAndroid Build Coastguard Worker     // Intentionally leak this global pool.
239*c8dee2aaSAndroid Build Coastguard Worker     static SkDiscardableMemoryPool* global =
240*c8dee2aaSAndroid Build Coastguard Worker             new DiscardableMemoryPool(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE);
241*c8dee2aaSAndroid Build Coastguard Worker     return global;
242*c8dee2aaSAndroid Build Coastguard Worker }
243