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