SleakEngine 1.0.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
VulkanBuffer.hpp
Go to the documentation of this file.
1#ifndef VULKANBUFFER_HPP_
2#define VULKANBUFFER_HPP_
3
5#include <vulkan/vulkan.h>
6#include <vma/vk_mem_alloc.h>
7#include <vector>
8
9namespace Sleak {
10namespace RenderEngine {
11
12/// VMA-backed Vulkan buffer with staging uploads, batched copies, and a size-bucketed recycling pool.
13class ENGINE_API VulkanBuffer : public BufferBase {
14public:
15 VulkanBuffer(VkDevice device, VkPhysicalDevice physicalDevice,
16 uint32_t size, BufferType type,
17 VkCommandPool commandPool, VkQueue graphicsQueue);
18 ~VulkanBuffer() override;
19
20 /// Allocates the buffer (recycling a pooled one if a match exists) and uploads initial data, if any.
21 bool Initialize(void* data) override;
22 void Update() override;
23 /// Overwrites buffer contents, staging through a temporary buffer for device-local memory.
24 void Update(void* data, size_t size) override;
25 void Cleanup() override;
26
27 /// Maps host-visible memory for direct CPU writes.
28 bool Map() override;
29 void Unmap() override;
30
31 void* GetData() override;
32
33 VkBuffer GetVkBuffer() const { return m_buffer; }
34
35 // VMA allocator lifecycle (owned by the renderer; created after the
36 // logical device, destroyed after all buffers are drained).
37 static void InitAllocator(VkInstance instance,
38 VkPhysicalDevice physicalDevice, VkDevice device);
39 static void DestroyAllocator();
40 static VmaAllocator GetAllocator() { return s_allocator; }
41
42 // Flush all pending buffer copies in a single batched submission (synchronous).
43 static void FlushPendingCopies();
44
45 // Async flush: submit pending copies signaling the given semaphore.
46 // No CPU wait — the caller must wait on the semaphore before using data.
47 /// A staging buffer awaiting release once its async copy has completed.
49 VkBuffer buffer;
50 VmaAllocation memory;
51 VkDeviceSize allocSize = 0;
52 uint32_t memoryTypeIndex = 0;
53 };
54 /// Result of an async batched flush: the recorded command buffer plus staging resources to free later.
56 bool submitted = false;
57 std::vector<PendingStagingCleanup> stagingBuffers;
58 VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
59 VkCommandPool commandPool = VK_NULL_HANDLE;
60 VkDevice device = VK_NULL_HANDLE;
61 };
62 static AsyncFlushResult FlushPendingCopiesAsync(VkSemaphore signalSemaphore);
63
64 // Enable/disable batching mode. When disabled, CopyBuffer uses
65 // immediate per-buffer submissions (safe during init/scene transitions).
66 static void SetBatchingEnabled(bool enabled) { s_batchingEnabled = enabled; }
67
68private:
69 /// Allocates a VMA-backed VkBuffer with the given usage/memory properties.
70 void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
71 VkMemoryPropertyFlags properties,
72 VkBuffer& buffer, VmaAllocation& memory,
73 VkDeviceSize* outAllocSize = nullptr,
74 uint32_t* outMemTypeIdx = nullptr);
75
76 /// Records a GPU-side copy between buffers, batched or submitted immediately per SetBatchingEnabled.
77 void CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer,
78 VkDeviceSize size);
79
80 /// Finds a physical device memory type matching the filter and required properties.
81 uint32_t FindMemoryType(uint32_t typeFilter,
82 VkMemoryPropertyFlags properties);
83
84 VkDevice m_device = VK_NULL_HANDLE;
85 VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
86 VkCommandPool m_commandPool = VK_NULL_HANDLE;
87 VkQueue m_graphicsQueue = VK_NULL_HANDLE;
88
89 VkBuffer m_buffer = VK_NULL_HANDLE;
90 VmaAllocation m_memory = VK_NULL_HANDLE;
91 VkDeviceSize m_allocSize = 0; // backing memory size (memReq)
92 VkDeviceSize m_bufferSize = 0; // logical VkBuffer size (bucketed)
93 VkBufferUsageFlags m_usage = 0;
94 uint32_t m_memoryTypeIndex = 0;
95
96 VkBuffer m_stagingBuffer = VK_NULL_HANDLE;
97 VmaAllocation m_stagingMemory = VK_NULL_HANDLE;
98
99 void* m_mappedData = nullptr;
100 bool m_pendingInBatch = false;
101
102 // --- Batched transfer state ---
103 static bool s_batchingEnabled;
104 static bool s_batchActive;
105 static VkCommandBuffer s_batchCommandBuffer;
106 static VkDevice s_batchDevice;
107 static VkCommandPool s_batchCommandPool;
108 static VkQueue s_batchQueue;
109 static std::vector<PendingStagingCleanup> s_pendingCleanup;
110
111 /// Opens the shared batch command buffer if one isn't already recording.
112 static void EnsureBatchStarted(VkDevice device, VkCommandPool pool,
113 VkQueue queue);
114
115 // --- Deferred buffer deletion ---
116 /// A buffer queued for destruction once the GPU is guaranteed done referencing it.
117 struct DeferredBufferDelete {
118 VkBuffer buffer;
119 VmaAllocation memory;
120 VkDevice device;
121 VkDeviceSize allocSize;
122 VkBufferUsageFlags usage;
123 uint32_t memoryTypeIndex;
124 uint64_t frameNumber;
125 VkDeviceSize bufferSize;
126 };
127 static std::vector<DeferredBufferDelete> s_deferredDeletions;
128 static uint64_t s_frameNumber;
129
130 // --- Buffer recycling pool ---
131 /// An idle buffer available for reuse by a future same-size, same-usage allocation.
132 struct PooledBuffer {
133 VkBuffer buffer;
134 VmaAllocation memory;
135 VkDevice device;
136 VkDeviceSize allocSize;
137 VkBufferUsageFlags usage;
138 uint32_t memoryTypeIndex;
139 VkDeviceSize bufferSize; // logical VkBuffer size (bucketed)
140 uint64_t insertFrame; // for oldest-first eviction
141 };
142 static std::vector<PooledBuffer> s_bufferPool;
143 static VkDeviceSize s_poolBytes; // total bytes currently in pool
144 // 64 MB budget — enough to recycle a few dozen column meshes without
145 // hoarding hundreds of MB of dead VRAM on a 6 GB card.
146 static constexpr VkDeviceSize MAX_POOL_BYTES = 64 * 1024 * 1024;
147 static constexpr size_t MAX_POOL_SIZE = 128; // hard cap on entry count too
148
149 /// Pulls a same-size, same-usage buffer out of the recycling pool if one is available.
150 bool TryRecycleBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
151 VkMemoryPropertyFlags properties,
152 VkBuffer& buffer, VmaAllocation& memory,
153 VkDeviceSize* outAllocSize = nullptr);
154
155 // Evict entries from the pool until it fits within the byte budget.
156 static void EvictPoolOverBudget();
157
158public:
159 // Called by the renderer each frame after fence wait to safely
160 // destroy buffers that are no longer referenced by the GPU.
161 static void ProcessDeferredDeletions(uint32_t maxFramesInFlight);
162 static void FlushAllDeferredDeletions();
163 static void AdvanceDeletionFrame() { s_frameNumber++; }
164
165 // VRAM tracking
166 static VkDeviceSize GetTotalAllocatedBytes();
167 static VkDeviceSize GetDeviceLocalHeapSize();
168 static VkDeviceSize GetDeviceLocalAllocatedBytes();
169 static void SetPhysicalDevice(VkPhysicalDevice device);
170 static void UntrackAllocation(VkDeviceSize size) { s_totalAllocatedBytes -= size; }
171 static void UntrackAllocation(VkDeviceSize size, uint32_t memTypeIdx) {
172 s_totalAllocatedBytes -= size;
173 if (memTypeIdx < s_memTypeCount)
174 s_perTypeBytes[memTypeIdx] -= size;
175 }
176 static void DumpPerFrameAllocStats();
177
178private:
179 static VmaAllocator s_allocator;
180 static VkDeviceSize s_totalAllocatedBytes;
181 static VkPhysicalDevice s_physicalDeviceGlobal;
182 static VkDeviceSize s_perTypeBytes[VK_MAX_MEMORY_TYPES];
183 static bool s_memTypeIsDeviceLocal[VK_MAX_MEMORY_TYPES];
184 static uint32_t s_memTypeCount;
185 /// Evicts pooled buffers backed by a specific memory type (used when that heap is under pressure).
186 static void EvictPoolForMemType(uint32_t memTypeIdx);
187};
188
189} // namespace RenderEngine
190} // namespace Sleak
191
192#endif // VULKANBUFFER_HPP_
Backend-agnostic GPU buffer: vertex, index, constant, or resource view target.
bool Map() override
Maps host-visible memory for direct CPU writes.
static void UntrackAllocation(VkDeviceSize size, uint32_t memTypeIdx)
static void UntrackAllocation(VkDeviceSize size)
bool Initialize(void *data) override
Allocates the buffer (recycling a pooled one if a match exists) and uploads initial data,...
VulkanBuffer(VkDevice device, VkPhysicalDevice physicalDevice, uint32_t size, BufferType type, VkCommandPool commandPool, VkQueue graphicsQueue)
static AsyncFlushResult FlushPendingCopiesAsync(VkSemaphore signalSemaphore)
static VmaAllocator GetAllocator()
static void SetBatchingEnabled(bool enabled)
Backend-facing rendering layer shared by the four graphics backends.
BufferType
GPU buffer usage kind, drives backend binding flags and layout.
Root namespace for everything the engine exposes.
Definition Camera.hpp:10
Result of an async batched flush: the recorded command buffer plus staging resources to free later.
std::vector< PendingStagingCleanup > stagingBuffers
A staging buffer awaiting release once its async copy has completed.