source: opengl-game/vulkan-buffer.hpp

feature/imgui-sdl
Last change on this file was 90880fb, checked in by Dmitry Portnoy <dportnoy@…>, 3 years ago

Start using the VulkanBuffer class for the non-per-object uniforms (this means the view and projection matrices for all pipelines, as well as the current time for the explosion pipeline)

  • Property mode set to 100644
File size: 6.4 KB
Line 
1#ifndef _VULKAN_BUFFER_H
2#define _VULKAN_BUFFER_H
3
4/*
5* This class is intended to be used with Storage Buffers and Uniform Buffers.
6*/
7
8template<class T>
9class VulkanBuffer {
10
11 public:
12
13 // TODO: Make these private (maybe make a getter for numObjects)
14 // Externally, they are only used in resizeBufferSet
15 size_t capacity;
16
17 // temp field to help with ubo+ssbo resizing until they are added to this class
18 // See if I need a separate field for this or if I can use other fields to check for this
19 // Maybe compare uniform or storage buffer size to the size of the memory allocated here
20 bool resized;
21
22 VulkanBuffer();
23 VulkanBuffer(size_t capacity, size_t range, size_t minOffsetAlignment);
24
25 VulkanBuffer(const VulkanBuffer<T>&) = delete;
26 VulkanBuffer(VulkanBuffer<T>&& other);
27
28 ~VulkanBuffer();
29
30 VulkanBuffer<T>& operator=(const VulkanBuffer<T>&) = delete;
31 VulkanBuffer<T>& operator=(VulkanBuffer<T>&& other) noexcept;
32
33 size_t memorySize();
34
35 T* data(); // Not sure I need to expose this
36
37 T& get(uint32_t index);
38 void add(T obj);
39
40 void* mapped(size_t idx);
41
42 void map(vector<VkDeviceMemory>& deviceMemory, VkDevice device);
43 void unmap(vector<VkDeviceMemory>& deviceMemory, VkDevice device);
44
45 void resize();
46
47 private:
48
49 size_t alignment;
50 size_t range;
51 //size_t capacity;
52 size_t numObjects;
53
54 T* rawData;
55 vector<void*> mappedData;
56
57 size_t memRequirement(size_t capacity);
58 size_t memOffset(uint32_t index);
59};
60
61// Currently, for SSBOs, I store the per-object values (usually just the model matrix), on each object, so they
62// are not in their own array and therefore cannot be pushed to the GPU as one block. The updates must happen
63// separately per object.
64
65// Since Sascha WIllems' dynamic UBO example works the same way (iirc), I can implement dynamic UBOs like that as well
66// for now. Would be nice to plan for potentially storing the ubo data on the CPU in a contiguous block in the future,
67// assuming that would make updates easier. Keep in mind that this only makes sense if all or most of the objects
68// in the ubo get updated every frame.
69
70// ============================= TODO: Also, check when it makes sense to have a staging buffer for copying data to the GPU
71// and see if I actually need to use it everywhere I currently am. I think this is mentioned in Sascha WIllems dubo example
72// or some other Vulkan website I recently bookmarked
73
74template<class T>
75VulkanBuffer<T>::VulkanBuffer()
76 : alignment(0)
77 , range(0)
78 , capacity(0)
79 , numObjects(0)
80 , resized(false)
81 , rawData(nullptr)
82 , mappedData() {
83}
84
85template<class T>
86VulkanBuffer<T>::VulkanBuffer(size_t capacity, size_t range, size_t minOffsetAlignment)
87 : alignment(range)
88 , range(range / sizeof(T))
89 , capacity(capacity)
90 , numObjects(0)
91 , resized(false)
92 , rawData(nullptr)
93 , mappedData() {
94 if (minOffsetAlignment > 0) {
95 alignment = (alignment + minOffsetAlignment - 1) & ~(minOffsetAlignment - 1);
96 }
97
98 rawData = (T*)malloc(memRequirement(capacity));
99}
100
101template<class T>
102VulkanBuffer<T>::VulkanBuffer(VulkanBuffer<T>&& other) {
103 // TODO: Implement
104}
105
106template<class T>
107VulkanBuffer<T>::~VulkanBuffer() {
108 if (rawData != nullptr) {
109 free(rawData);
110 }
111}
112
113template<class T>
114VulkanBuffer<T>& VulkanBuffer<T>::operator=(VulkanBuffer<T>&& other) noexcept {
115 if (this != &other) {
116 capacity = other.capacity;
117 numObjects = other.numObjects;
118 resized = other.resized;
119
120 alignment = other.alignment;
121 range = other.range;
122
123 mappedData = other.mappedData;
124
125 if (rawData != nullptr) {
126 free(rawData);
127 }
128
129 rawData = other.rawData;
130
131 other.capacity = 0;
132 other.numObjects = 0;
133 other.range = 0;
134
135 other.mappedData.clear();
136 other.rawData = nullptr;
137 }
138
139 return *this;
140}
141
142template<class T>
143size_t VulkanBuffer<T>::memorySize() {
144 return memRequirement(capacity);
145}
146
147template<class T>
148T* VulkanBuffer<T>::data() {
149 return rawData;
150}
151
152template<class T>
153T& VulkanBuffer<T>::get(uint32_t index) {
154 // TODO: Check that index < numObjects
155
156 T* obj = (T*)((size_t)rawData + memOffset(index));
157 return *obj;
158}
159
160template<class T>
161void VulkanBuffer<T>::add(T obj) {
162 // TODO: Maybe copy this to the resize() function and call that function here
163 if (numObjects == capacity) {
164 // Once I add Vulkan buffer objects in here, make sure this doesn't overlap with resizeBufferSet
165 resized = true;
166
167 size_t oldMemReq = memRequirement(capacity);
168
169 capacity *= 2;
170
171 size_t newMemReq = memRequirement(capacity);
172
173 T* newData = (T*)malloc(newMemReq);
174 // TODO: Check for failure
175
176 memcpy(newData, rawData, oldMemReq);
177
178 free(rawData);
179 rawData = newData;
180 }
181
182 T* ptr = (T*)((size_t)rawData + memOffset(numObjects));
183 *ptr = obj;
184
185 numObjects++;
186}
187
188template<class T>
189void* VulkanBuffer<T>::mapped(size_t idx) {
190 return mappedData[idx];
191}
192
193template<class T>
194void VulkanBuffer<T>::map(vector<VkDeviceMemory>& deviceMemory, VkDevice device) {
195 // TODO: Make sure that mappedData initally has size 0. If not, it means the memory is already mapped
196 // and I should return some kind of error or warning
197 mappedData.resize(deviceMemory.size());
198
199 for (size_t i = 0; i < deviceMemory.size(); i++) {
200 vkMapMemory(device, deviceMemory[i], 0, memorySize(), 0, &mappedData[i]);
201 }
202}
203
204template<class T>
205void VulkanBuffer<T>::unmap(vector<VkDeviceMemory>& deviceMemory, VkDevice device) {
206 for (size_t i = 0; i < deviceMemory.size(); i++) {
207 vkUnmapMemory(device, deviceMemory[i]);
208 }
209
210 mappedData.clear();
211}
212
213template<class T>
214void VulkanBuffer<T>::resize() {
215 resized = false;
216}
217
218template<class T>
219size_t VulkanBuffer<T>::memRequirement(size_t capacity) {
220 return (capacity / range) * alignment + (capacity % range) * sizeof(T);
221}
222
223template<class T>
224size_t VulkanBuffer<T>::memOffset(uint32_t index) {
225 return (index / range) * alignment + (index % range) * sizeof(T);
226}
227
228#endif // _VULKAN_BUFFER_H
Note: See TracBrowser for help on using the repository browser.