xref: /aosp_15_r20/external/skia/src/gpu/ganesh/GrMemoryPool.cpp (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 #include "src/gpu/ganesh/GrMemoryPool.h"
9 
10 #include "include/core/SkTypes.h"
11 #include "include/private/base/SkDebug.h"
12 #include "include/private/base/SkTPin.h"
13 
14 #include <cstring>
15 #include <new>
16 
17 #ifdef SK_DEBUG
18     #include <atomic>
19 #endif
20 
21 ///////////////////////////////////////////////////////////////////////////////////////////////////
22 
Make(size_t preallocSize,size_t minAllocSize)23 std::unique_ptr<GrMemoryPool> GrMemoryPool::Make(size_t preallocSize, size_t minAllocSize) {
24     static_assert(sizeof(GrMemoryPool) < GrMemoryPool::kMinAllocationSize);
25 
26     preallocSize = SkTPin(preallocSize, kMinAllocationSize,
27                           (size_t) SkBlockAllocator::kMaxAllocationSize);
28     minAllocSize = SkTPin(minAllocSize, kMinAllocationSize,
29                           (size_t) SkBlockAllocator::kMaxAllocationSize);
30     void* mem = operator new(preallocSize);
31     return std::unique_ptr<GrMemoryPool>(new (mem) GrMemoryPool(preallocSize, minAllocSize));
32 }
33 
GrMemoryPool(size_t preallocSize,size_t minAllocSize)34 GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize)
35         : fAllocator(SkBlockAllocator::GrowthPolicy::kFixed, minAllocSize,
36                      preallocSize - offsetof(GrMemoryPool, fAllocator) - sizeof(SkBlockAllocator)) {
37     SkDEBUGCODE(
38         fDebug = new Debug;
39         fDebug->fAllocationCount = 0;
40     )
41 }
42 
~GrMemoryPool()43 GrMemoryPool::~GrMemoryPool() {
44     this->reportLeaks();
45     SkASSERT(0 == fDebug->fAllocationCount);
46     SkASSERT(this->isEmpty());
47     SkDEBUGCODE(delete fDebug;)
48 }
49 
reportLeaks() const50 void GrMemoryPool::reportLeaks() const {
51 #ifdef SK_DEBUG
52     int i = 0;
53     int n = fDebug->fAllocatedIDs.count();
54     for (int id : fDebug->fAllocatedIDs) {
55         if (++i == 1) {
56             SkDebugf("Leaked %d IDs (in no particular order): %d%s", n, id, (n == i) ? "\n" : "");
57         } else if (i < 11) {
58             SkDebugf(", %d%s", id, (n == i ? "\n" : ""));
59         } else if (i == 11) {
60             SkDebugf(", ...\n");
61             break;
62         }
63     }
64 #endif
65 }
66 
allocate(size_t size)67 void* GrMemoryPool::allocate(size_t size) {
68     static_assert(alignof(Header) <= kAlignment);
69     SkDEBUGCODE(this->validate();)
70 
71     SkBlockAllocator::ByteRange alloc = fAllocator.allocate<kAlignment, sizeof(Header)>(size);
72 
73     // Initialize GrMemoryPool's custom header at the start of the allocation
74     Header* header = static_cast<Header*>(alloc.fBlock->ptr(alloc.fAlignedOffset - sizeof(Header)));
75     header->fStart = alloc.fStart;
76     header->fEnd = alloc.fEnd;
77 
78     // Update live count within the block
79     alloc.fBlock->setMetadata(alloc.fBlock->metadata() + 1);
80 
81 #if defined(SK_SANITIZE_ADDRESS)
82     sk_asan_poison_memory_region(&header->fSentinel, sizeof(header->fSentinel));
83 #elif defined(SK_DEBUG)
84     header->fSentinel = SkBlockAllocator::kAssignedMarker;
85 #endif
86 
87 #if defined(SK_DEBUG)
88     header->fID = []{
89         static std::atomic<int> nextID{1};
90         return nextID.fetch_add(1, std::memory_order_relaxed);
91     }();
92 
93     // You can set a breakpoint here when a leaked ID is allocated to see the stack frame.
94     fDebug->fAllocatedIDs.add(header->fID);
95     fDebug->fAllocationCount++;
96 #endif
97 
98     // User-facing pointer is after the header padding
99     return alloc.fBlock->ptr(alloc.fAlignedOffset);
100 }
101 
release(void * p)102 void GrMemoryPool::release(void* p) {
103     Header* header = reinterpret_cast<Header*>(reinterpret_cast<intptr_t>(p) - sizeof(Header));
104 
105 #if defined(SK_SANITIZE_ADDRESS)
106     sk_asan_unpoison_memory_region(&header->fSentinel, sizeof(header->fSentinel));
107 #elif defined(SK_DEBUG)
108     SkASSERT(SkBlockAllocator::kAssignedMarker == header->fSentinel);
109     header->fSentinel = SkBlockAllocator::kFreedMarker;
110 #endif
111 
112 #if defined(SK_DEBUG)
113     fDebug->fAllocatedIDs.remove(header->fID);
114     fDebug->fAllocationCount--;
115 #endif
116 
117     SkBlockAllocator::Block* block = fAllocator.owningBlock<kAlignment>(header, header->fStart);
118 
119 #if defined(SK_DEBUG)
120     // (p - block) matches the original alignedOffset value from SkBlockAllocator::allocate().
121     intptr_t alignedOffset = (intptr_t)p - (intptr_t)block;
122     SkASSERT(p == block->ptr(alignedOffset));
123 
124     // Scrub the block contents to prevent use-after-free errors.
125     memset(p, 0xDD, header->fEnd - alignedOffset);
126 #endif
127 
128     int alive = block->metadata();
129     if (alive == 1) {
130         // This was last allocation in the block, so remove it
131         fAllocator.releaseBlock(block);
132     } else {
133         // Update count and release storage of the allocation itself
134         block->setMetadata(alive - 1);
135         block->release(header->fStart, header->fEnd);
136     }
137 }
138 
139 #ifdef SK_DEBUG
validate() const140 void GrMemoryPool::validate() const {
141     fAllocator.validate();
142 
143     int allocCount = 0;
144     for (const auto* b : fAllocator.blocks()) {
145         allocCount += b->metadata();
146     }
147     SkASSERT(allocCount == fDebug->fAllocationCount);
148     SkASSERT(fDebug->fAllocationCount == fDebug->fAllocatedIDs.count());
149     SkASSERT(allocCount > 0 || this->isEmpty());
150 }
151 #endif
152