xref: /aosp_15_r20/external/skia/src/gpu/ganesh/GrMemoryPool.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2012 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 #ifndef GrMemoryPool_DEFINED
9 #define GrMemoryPool_DEFINED
10 
11 #include "include/private/base/SkDebug.h"
12 #include "src/base/SkBlockAllocator.h"
13 
14 #include <cstddef>
15 #include <cstdint>
16 #include <memory>
17 #include <type_traits>
18 
19 #ifdef SK_DEBUG
20 #include "src/core/SkTHash.h"
21 #endif
22 
23 /**
24  * Allocates memory in blocks and parcels out space in the blocks for allocation requests. It is
25  * optimized for allocate / release speed over memory efficiency. The interface is designed to be
26  * used to implement operator new and delete overrides. All allocations are expected to be released
27  * before the pool's destructor is called. Allocations will be aligned to sizeof(std::max_align_t).
28  *
29  * All allocated objects must be released back to the memory pool before it can be destroyed.
30  */
31 class GrMemoryPool {
32 public:
33 #ifdef SK_FORCE_8_BYTE_ALIGNMENT
34     // https://github.com/emscripten-core/emscripten/issues/10072
35     // Since Skia does not use "long double" (16 bytes), we should be ok to force it back to 8 bytes
36     // until emscripten is fixed.
37     static constexpr size_t kAlignment = 8;
38 #else
39     // Guaranteed alignment of pointer returned by allocate().
40     static constexpr size_t kAlignment = alignof(std::max_align_t);
41 #endif
42 
43     // Smallest block size allocated on the heap (not the smallest reservation via allocate()).
44     inline static constexpr size_t kMinAllocationSize = 1 << 10;
45 
46     /**
47      * Prealloc size is the amount of space to allocate at pool creation
48      * time and keep around until pool destruction. The min alloc size is
49      * the smallest allowed size of additional allocations. Both sizes are
50      * adjusted to ensure that they are at least as large as kMinAllocationSize
51      * and less than SkBlockAllocator::kMaxAllocationSize.
52      *
53      * Both sizes are what the pool will end up allocating from the system, and
54      * portions of the allocated memory is used for internal bookkeeping.
55      */
56     static std::unique_ptr<GrMemoryPool> Make(size_t preallocSize, size_t minAllocSize);
57 
58     ~GrMemoryPool();
delete(void * p)59     void operator delete(void* p) { ::operator delete(p); }
60 
61     /**
62      * Allocates memory. The memory must be freed with release() before the GrMemoryPool is deleted.
63      */
64     void* allocate(size_t size);
65     /**
66      * p must have been returned by allocate().
67      */
68     void release(void* p);
69 
70     /**
71      * Returns true if there are no unreleased allocations.
72      */
isEmpty()73     bool isEmpty() const {
74         // If size is the same as preallocSize, there aren't any heap blocks, so currentBlock()
75         // is the inline head block.
76         return fAllocator.currentBlock() == fAllocator.headBlock() &&
77                fAllocator.currentBlock()->metadata() == 0;
78     }
79 
80     /**
81      * In debug mode, this reports the IDs of unfreed nodes via `SkDebugf`. This reporting is also
82      * performed automatically whenever a GrMemoryPool is destroyed.
83      * In release mode, this method is a no-op.
84      */
85     void reportLeaks() const;
86 
87     /**
88      * Returns the total allocated size of the GrMemoryPool minus any preallocated amount
89      */
size()90     size_t size() const { return fAllocator.totalSize() - fAllocator.preallocSize(); }
91 
92     /**
93      * Returns the preallocated size of the GrMemoryPool
94      */
preallocSize()95     size_t preallocSize() const {
96         // Account for the debug-only fields in this count, the offset is 0 for release builds
97         static_assert(std::is_standard_layout<GrMemoryPool>::value, "");
98         return offsetof(GrMemoryPool, fAllocator) + fAllocator.preallocSize();
99     }
100 
101     /**
102      * Frees any scratch blocks that are no longer being used.
103      */
resetScratchSpace()104     void resetScratchSpace() {
105         fAllocator.resetScratchSpace();
106     }
107 
108 #ifdef SK_DEBUG
109     void validate() const;
110 #endif
111 
112 private:
113     // Per-allocation overhead so that GrMemoryPool can always identify the block owning each and
114     // release all occupied bytes, including any resulting from alignment padding.
115     struct Header {
116         int fStart;
117         int fEnd;
118 #if defined(SK_DEBUG)
119         int fID;       // ID that can be used to track down leaks by clients.
120 #endif
121 #if defined(SK_DEBUG) || defined(SK_SANITIZE_ADDRESS)
122         uint32_t fSentinel; // set to a known value to check for memory stomping; poisoned in ASAN mode
123 #endif
124     };
125 
126     GrMemoryPool(size_t preallocSize, size_t minAllocSize);
127 
128 #ifdef SK_DEBUG
129     // Because this exists preallocSize wants to use offsetof, so keep GrMemoryPool standard layout
130     // without depending on THashSet being standard layout. Note that std::unique_ptr may not be
131     // standard layout.
132     struct Debug{
133         skia_private::THashSet<int> fAllocatedIDs;
134         int                         fAllocationCount;
135     };
136     Debug* fDebug{nullptr};
137 #endif
138 
139     SkBlockAllocator fAllocator; // Must be the last field, in order to use extra allocated space
140 };
141 #endif
142