Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
glMemoryManager.cpp
Go to the documentation of this file.
1
2
5
8
9namespace Divide {
10namespace GLUtil {
11
12namespace GLMemory {
13
14namespace
15{
16 namespace detail
17 {
18 constexpr size_t zeroDataBaseSize = TO_MEGABYTES(64u);
19 };
20
21 eastl::vector<Byte> g_zeroData( detail::zeroDataBaseSize, Byte_ZERO );
22
23 FORCE_INLINE Byte* GetZeroData( const size_t bufferSize )
24 {
25 while ( g_zeroData.size() < bufferSize )
26 {
27 g_zeroData.resize( g_zeroData.size() + detail::zeroDataBaseSize, Byte_ZERO );
28 }
29
30 return g_zeroData.data();
31 }
32}
33
34void OnFrameEnd(const U64 frameCount )
35{
36 constexpr U64 memoryCleanInterval = 256u;
37
38 thread_local U64 lastSyncedFrame = 0u;
39
40 if ( frameCount - lastSyncedFrame > memoryCleanInterval )
41 {
42 // This may be quite large at this point, so clear it to claim back some RAM
43 if (g_zeroData.size() >= detail::zeroDataBaseSize * 4 )
44 {
45 g_zeroData.set_capacity( 0 );
46 }
47
48 lastSyncedFrame = frameCount;
49 }
50}
51
52Chunk::Chunk(const bool poolAllocations,
53 const size_t size,
54 const size_t alignment,
55 const gl46core::BufferStorageMask storageMask,
56 const gl46core::BufferAccessMask accessMask,
57 const gl46core::GLenum usage)
58 : _storageMask(storageMask),
59 _accessMask(accessMask),
60 _usage(usage),
61 _alignment(alignment),
62 _poolAllocations(poolAllocations)
63{
64 Block block;
65 block._size = Util::GetAlignmentCorrected(size, alignment);// Code for the worst case?
66
68 {
69 static U32 g_bufferIndex = 0u;
70 _memory = createAndAllocPersistentBuffer(block._size, storageMask, accessMask, block._bufferHandle, { nullptr, 0u }, Util::StringFormat("DVD_BUFFER_CHUNK_{}", g_bufferIndex++).c_str());
71 block._ptr = _memory;
72 }
73
74 _blocks.emplace_back(block);
75}
76
78{
80 {
81 if (_blocks[0]._bufferHandle > 0u)
82 {
83 gl46core::glDeleteBuffers(1, &_blocks[0]._bufferHandle);
84 }
85 } else {
86 for (Block& block : _blocks)
87 {
88 if (block._bufferHandle > 0u)
89 {
90 gl46core::glDeleteBuffers(1, &block._bufferHandle);
91 }
92 }
93 }
94}
95
96void Chunk::deallocate(const Block &block)
97{
99
100 Block* const blockIt = eastl::find(begin(_blocks), end(_blocks), block);
101 assert(blockIt != cend(_blocks));
102 blockIt->_free = true;
103
104 if (!_poolAllocations)
105 {
106 if (blockIt->_bufferHandle > 0u)
107 {
108 gl46core::glDeleteBuffers(1, &blockIt->_bufferHandle);
109 blockIt->_bufferHandle = 0u;
110 }
111 }
112}
113
114bool Chunk::allocate(const size_t size, const char* name, const std::pair<bufferPtr, size_t> initialData, Block &blockOut)
115{
117
118 const size_t requestedSize = Util::GetAlignmentCorrected(size, _alignment);
119
120 if (requestedSize > _blocks.back()._size)
121 {
122 return false;
123 }
124
125 const size_t count = _blocks.size();
126 for (size_t i = 0u; i < count; ++i)
127 {
128 Block& block = _blocks[i];
129 const size_t remainingSize = block._size;
130
131 if (!block._free || remainingSize < requestedSize)
132 {
133 continue;
134 }
135
137 {
138 memcpy(block._ptr, initialData.first, initialData.second);
139 }
140 else
141 {
142 _memory = createAndAllocPersistentBuffer(requestedSize, storageMask(), accessMask(), block._bufferHandle, initialData, name);
143 block._ptr = _memory;
144 }
145
146 block._free = false;
147 block._size = requestedSize;
148 blockOut = block;
149
150 if (remainingSize > requestedSize)
151 {
152 Block nextBlock;
153 nextBlock._bufferHandle = block._bufferHandle;
154
156 {
157 nextBlock._offset = block._offset + requestedSize;
158 }
159 nextBlock._size = remainingSize - requestedSize;
160 nextBlock._ptr = &_memory[nextBlock._offset];
161
162 _blocks.emplace_back(nextBlock);
163 }
164 return true;
165 }
166
167 return false;
168}
169
170bool Chunk::containsBlock(const Block &block) const
171{
172 return eastl::find(begin(_blocks), end(_blocks), block) != cend(_blocks);
173}
174
175ChunkAllocator::ChunkAllocator(const size_t size) noexcept
176 : _size(size)
177{
178 assert(size > 0u && isPowerOfTwo(size));
179}
180
181std::unique_ptr<Chunk> ChunkAllocator::allocate(const bool poolAllocations,
182 const size_t size,
183 const size_t alignment,
184 const gl46core::BufferStorageMask storageMask,
185 const gl46core::BufferAccessMask accessMask,
186 const gl46core::GLenum usage) const
187{
189
190 const size_t overflowSize = to_size(1) << to_size(std::log2(size) + 1);
191 return std::make_unique<Chunk>(poolAllocations, (size > _size ? overflowSize : _size), alignment, storageMask, accessMask, usage);
192}
193
195 : _memoryType(memoryType)
196{
197}
198
199void DeviceAllocator::init(const size_t size)
200{
201 deallocate();
202 _chunkAllocator = std::make_unique<ChunkAllocator>(size);
203}
204
205Block DeviceAllocator::allocate(const bool poolAllocations,
206 const size_t size,
207 const size_t alignment,
208 const gl46core::BufferStorageMask storageMask,
209 const gl46core::BufferAccessMask accessMask,
210 const gl46core::GLenum usage,
211 const char* blockName,
212 const std::pair<bufferPtr, size_t> initialData)
213{
215
217
218 Block block;
219 for (auto& chunk : _chunks)
220 {
221 if (chunk->storageMask() == storageMask &&
222 chunk->accessMask() == accessMask &&
223 chunk->usage() == usage &&
224 chunk->alignment() == alignment &&
225 chunk->poolAllocations() == poolAllocations)
226 {
227 if (chunk->allocate(size, blockName, initialData, block))
228 {
229 return block;
230 }
231 }
232 }
233
234 _chunks.emplace_back(_chunkAllocator->allocate(poolAllocations, size, alignment, storageMask, accessMask, usage));
235 if(!_chunks.back()->allocate(size, blockName, initialData, block))
236 {
238 }
239 return block;
240}
241
242void DeviceAllocator::deallocate(const Block &block) const
243{
245
247
248 for (auto& chunk : _chunks)
249 {
250 if (chunk->containsBlock(block))
251 {
252 chunk->deallocate(block);
253 return;
254 }
255 }
256
257 DIVIDE_UNEXPECTED_CALL_MSG("DeviceAllocator::deallocate error: unable to deallocate the block");
258}
259
261{
263 _chunks.clear();
264}
265
266} // namespace GLMemory
267
268Byte* createAndAllocPersistentBuffer(const size_t bufferSize,
269 const gl46core::BufferStorageMask storageMask,
270 const gl46core::BufferAccessMask accessMask,
271 gl46core::GLuint& bufferIdOut,
272 const std::pair<bufferPtr, size_t> initialData,
273 const char* name)
274{
276
277 gl46core::glCreateBuffers(1, &bufferIdOut);
279 {
280 gl46core::glObjectLabel( gl46core::GL_BUFFER, bufferIdOut, -1,
281 name != nullptr
282 ? name
283 : Util::StringFormat("DVD_PERSISTENT_BUFFER_{}", bufferIdOut).c_str());
284 }
285
286 assert(bufferIdOut != 0 && "GLUtil::allocPersistentBuffer error: buffer creation failed");
287 const bool hasAllSourceData = initialData.second == bufferSize && initialData.first != nullptr;
288
289 gl46core::glNamedBufferStorage(bufferIdOut, bufferSize, hasAllSourceData ? initialData.first : GLMemory::GetZeroData(bufferSize), storageMask);
290 Byte* ptr = (Byte*)gl46core::glMapNamedBufferRange(bufferIdOut, 0, bufferSize, accessMask);
291 assert(ptr != nullptr);
292
293 if (!hasAllSourceData && initialData.second > 0 && initialData.first != nullptr)
294 {
295 memcpy(ptr, initialData.first, initialData.second);
296 }
297
298 return ptr;
299}
300
301void createBuffer( gl46core::GLuint& bufferIdOut, const char* name)
302{
304
305 gl46core::glCreateBuffers(1, &bufferIdOut);
306
308 {
309 gl46core::glObjectLabel(gl46core::GL_BUFFER, bufferIdOut, -1,
310 name != nullptr
311 ? name
312 : Util::StringFormat("DVD_GENERAL_BUFFER_{}", bufferIdOut).c_str());
313 }
314
315}
316
317void createAndAllocBuffer(const size_t bufferSize,
318 const gl46core::GLenum usageMask,
319 gl46core::GLuint& bufferIdOut,
320 const std::pair<bufferPtr, size_t> initialData,
321 const char* name)
322{
324
325 // 0 bufferSize as we will do our own allocation with proper data
326 createBuffer(bufferIdOut, name);
327
328 const bool hasAllSourceData = initialData.second == bufferSize && initialData.first != nullptr;
329
330 assert(bufferIdOut != 0 && "GLUtil::allocBuffer error: buffer creation failed");
331 glNamedBufferData(bufferIdOut, bufferSize, hasAllSourceData ? initialData.first : GLMemory::GetZeroData( bufferSize ), usageMask);
332
333 if (!hasAllSourceData && initialData.second > 0u && initialData.first != nullptr )
334 {
335 const gl46core::BufferAccessMask accessMask = gl46core::GL_MAP_WRITE_BIT | gl46core::GL_MAP_INVALIDATE_BUFFER_BIT;
336 // We don't want undefined, we want zero as a default. Performance considerations be damned!
337 Byte* ptr = (Byte*)glMapNamedBufferRange(bufferIdOut, 0, bufferSize, accessMask);
338 memcpy(ptr, initialData.first, initialData.second);
339 gl46core::glUnmapNamedBuffer(bufferIdOut);
340 }
341}
342
343void freeBuffer(gl46core::GLuint& bufferId, bufferPtr mappedPtr)
344{
346
347 if (bufferId != GL_NULL_HANDLE && bufferId != 0u)
348 {
349 if (mappedPtr != nullptr)
350 {
351 [[maybe_unused]] const gl46core::GLboolean result = gl46core::glUnmapNamedBuffer(bufferId);
352 DIVIDE_ASSERT(result != gl46core::GL_FALSE && "GLUtil::freeBuffer error: buffer unmapping failed");
353 mappedPtr = nullptr;
354 }
355
356 if ( !GL_API::DeleteBuffers(1, &bufferId) )
357 {
359 }
360
361 bufferId = GL_NULL_HANDLE;
362 }
363}
364
365} // namespace GLUtil
366} // namespace Divide
#define TO_MEGABYTES(X)
Definition: MathHelper.h:47
#define DIVIDE_ASSERT(...)
#define DIVIDE_UNEXPECTED_CALL()
#define DIVIDE_UNEXPECTED_CALL_MSG(X)
#define FORCE_INLINE
#define PROFILE_SCOPE_AUTO(CATEGORY)
Definition: Profiler.h:87
static bool DeleteBuffers(gl46core::GLuint count, gl46core::GLuint *buffers)
Definition: GLWrapper.cpp:1879
std::unique_ptr< Chunk > allocate(bool poolAllocations, size_t size, size_t alignment, gl46core::BufferStorageMask storageMask, gl46core::BufferAccessMask accessMask, gl46core::GLenum usage) const
Chunk(bool poolAllocations, size_t size, size_t alignment, gl46core::BufferStorageMask storageMask, gl46core::BufferAccessMask accessMask, gl46core::GLenum usage)
bool containsBlock(const Block &block) const
bool allocate(size_t size, const char *name, std::pair< bufferPtr, size_t > initialData, Block &blockOut)
void deallocate(const Block &block)
vector< std::unique_ptr< Chunk > > _chunks
DeviceAllocator(GLMemoryType memoryType) noexcept
Block allocate(bool poolAllocations, size_t size, size_t alignment, gl46core::BufferStorageMask storageMask, gl46core::BufferAccessMask accessMask, gl46core::GLenum usage, const char *blockName, std::pair< bufferPtr, size_t > initialData)
constexpr bool ENABLE_GPU_VALIDATION
Error callbacks, validations, buffer checks, etc. are controlled by this flag. Heavy performance impa...
Definition: config.h:192
eastl::vector< Byte > g_zeroData(detail::zeroDataBaseSize, Byte_ZERO)
FORCE_INLINE Byte * GetZeroData(const size_t bufferSize)
void OnFrameEnd(const U64 frameCount)
void createBuffer(gl46core::GLuint &bufferIdOut, const char *name)
void freeBuffer(gl46core::GLuint &bufferId, bufferPtr mappedPtr)
Byte * createAndAllocPersistentBuffer(const size_t bufferSize, const gl46core::BufferStorageMask storageMask, const gl46core::BufferAccessMask accessMask, gl46core::GLuint &bufferIdOut, const std::pair< bufferPtr, size_t > initialData, const char *name)
void createAndAllocBuffer(const size_t bufferSize, const gl46core::GLenum usageMask, gl46core::GLuint &bufferIdOut, const std::pair< bufferPtr, size_t > initialData, const char *name)
constexpr Optick::Category::Type Graphics
Definition: Profiler.h:60
Str StringFormat(const char *fmt, Args &&...args)
FORCE_INLINE size_t GetAlignmentCorrected(const size_t value, const size_t alignment) noexcept
Definition: MathHelper.inl:783
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
std::lock_guard< mutex > LockGuard
Definition: SharedMutex.h:55
std::byte Byte
constexpr Byte Byte_ZERO
constexpr gl46core::GLuint GL_NULL_HANDLE
Invalid object value. Used to compare handles and determine if they were properly created.
Definition: glResources.h:105
constexpr bool isPowerOfTwo(const T x) noexcept
constexpr size_t to_size(const T value)
uint32_t U32
void * bufferPtr
uint64_t U64