source: opengl-game/graphics-pipeline_vulkan.hpp@ 5049354

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

Change copyDataToMemory to use an arbitrary offset instead of multiplying it by the data size

  • Property mode set to 100644
File size: 29.1 KB
Line 
1#ifndef _GRAPHICS_PIPELINE_VULKAN_H
2#define _GRAPHICS_PIPELINE_VULKAN_H
3
4#include "graphics-pipeline.hpp"
5
6#include <fstream>
7#include <iostream>
8#include <stdexcept>
9#include <vector>
10
11#include <vulkan/vulkan.h>
12
13#define GLM_FORCE_RADIANS
14#define GLM_FORCE_DEPTH_ZERO_TO_ONE // Since, in Vulkan, the depth range is 0 to 1 instead of -1 to 1
15#define GLM_FORCE_RIGHT_HANDED
16
17#include <glm/glm.hpp>
18#include <glm/gtc/matrix_transform.hpp>
19
20#include "vulkan-utils.hpp"
21
22using namespace glm;
23
24// TODO: Maybe change the name of this struct so I can call the list something other than descriptorInfoList
25struct DescriptorInfo {
26 VkDescriptorType type;
27 VkShaderStageFlags stageFlags;
28
29 // Only one of the below properties should be set
30 vector<VkDescriptorBufferInfo>* bufferDataList;
31 VkDescriptorImageInfo* imageData;
32};
33
34// TODO: Use this struct for uniform buffers as well (maybe move it to VulkanUtils)
35struct StorageBufferSet {
36 vector<VkBuffer> buffers;
37 vector<VkDeviceMemory> memory;
38 vector<VkDescriptorBufferInfo> infoSet;
39};
40
41template<class VertexType, class SSBOType>
42class GraphicsPipeline_Vulkan : public GraphicsPipeline {
43 public:
44 string vertShaderFile, fragShaderFile;
45
46 GraphicsPipeline_Vulkan();
47
48 // TODO: swapChainImages is only ever used to get its size. Check how that is determined and,
49 // if it will never change, just pass it in the constructor and save it
50 // If it does change, I could add an updateSwapchainImageCount() function
51 GraphicsPipeline_Vulkan(VkPrimitiveTopology topology, VkPhysicalDevice physicalDevice, VkDevice device,
52 VkRenderPass renderPass, Viewport viewport, vector<VkImage>& swapChainImages,
53 size_t vertexCapacity, size_t indexCapacity, size_t objectCapacity);
54 ~GraphicsPipeline_Vulkan();
55
56 size_t getNumVertices();
57
58 void updateRenderPass(VkRenderPass renderPass);
59
60 // Maybe I should rename these to addVertexAttribute (addVaryingAttribute) and addUniformAttribute
61
62 void addAttribute(VkFormat format, size_t offset);
63
64 void addStorageDescriptor(VkShaderStageFlags stageFlags);
65
66 void addDescriptorInfo(VkDescriptorType type, VkShaderStageFlags stageFlags, vector<VkDescriptorBufferInfo>* bufferData);
67 void addDescriptorInfo(VkDescriptorType type, VkShaderStageFlags stageFlags, VkDescriptorImageInfo* imageData);
68
69 void createPipeline(string vertShaderFile, string fragShaderFile);
70 void createDescriptorSetLayout();
71 void createDescriptorPool(vector<VkImage>& swapChainImages);
72 void createDescriptorSets(vector<VkImage>& swapChainImages);
73
74 void createRenderCommands(VkCommandBuffer& commandBuffer, uint32_t currentImage);
75
76 bool addObject(const vector<VertexType>& vertices, vector<uint16_t> indices, SSBOType& ssbo,
77 VkCommandPool commandPool, VkQueue graphicsQueue);
78
79 void updateObject(size_t objIndex, SSBOType& ssbo);
80
81 void updateObjectVertices(size_t objIndex, const vector<VertexType>& vertices, VkCommandPool commandPool,
82 VkQueue graphicsQueue);
83
84 void cleanup();
85 void cleanupBuffers();
86
87 private:
88 VkPrimitiveTopology topology;
89 VkPhysicalDevice physicalDevice;
90 VkDevice device;
91 VkRenderPass renderPass;
92
93 VkPipeline pipeline;
94 VkPipelineLayout pipelineLayout;
95
96 VkVertexInputBindingDescription bindingDescription;
97
98 vector<VkVertexInputAttributeDescription> attributeDescriptions;
99 vector<DescriptorInfo> descriptorInfoList;
100
101 VkDescriptorSetLayout descriptorSetLayout;
102 VkDescriptorPool descriptorPool;
103 vector<VkDescriptorSet> descriptorSets;
104
105 size_t numVertices;
106 size_t vertexCapacity;
107 VkBuffer vertexBuffer;
108 VkDeviceMemory vertexBufferMemory;
109
110 size_t numIndices;
111 size_t indexCapacity;
112 VkBuffer indexBuffer;
113 VkDeviceMemory indexBufferMemory;
114
115 size_t numObjects;
116 size_t objectCapacity;
117
118 StorageBufferSet storageBufferSet;
119
120 VkShaderModule createShaderModule(const vector<char>& code);
121 vector<char> readFile(const string& filename);
122
123 void resizeVertexBuffer(VkCommandPool commandPool, VkQueue graphicsQueue);
124 void resizeIndexBuffer(VkCommandPool commandPool, VkQueue graphicsQueue);
125 void resizeStorageBufferSet(StorageBufferSet& set, VkCommandPool commandPool, VkQueue graphicsQueue);
126};
127
128/*** PUBLIC METHODS ***/
129
130template<class VertexType, class SSBOType>
131GraphicsPipeline_Vulkan<VertexType, SSBOType>::GraphicsPipeline_Vulkan() {
132}
133
134// TODO: Verify that vertex capacity and index capacity are both > 0
135// TODO: See if it would be feasible to move code in the createPipeline method
136// into the constructor. That way, I can also put relevant cleanup code into the destructor
137template<class VertexType, class SSBOType>
138GraphicsPipeline_Vulkan<VertexType, SSBOType>::GraphicsPipeline_Vulkan(
139 VkPrimitiveTopology topology, VkPhysicalDevice physicalDevice, VkDevice device,
140 VkRenderPass renderPass, Viewport viewport, vector<VkImage>& swapChainImages,
141 size_t vertexCapacity, size_t indexCapacity, size_t objectCapacity) {
142 this->topology = topology;
143 this->physicalDevice = physicalDevice;
144 this->device = device;
145 this->renderPass = renderPass;
146 this->viewport = viewport;
147
148 // Since there is only one array of vertex data, we use binding = 0
149 // I'll probably do that for the foreseeable future
150 // I can calculate the stride myself given info about all the varying attributes
151 this->bindingDescription.binding = 0;
152 this->bindingDescription.stride = sizeof(VertexType);
153 this->bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
154
155 this->numVertices = 0;
156 this->vertexCapacity = vertexCapacity;
157
158 VulkanUtils::createBuffer(device, physicalDevice, vertexCapacity * sizeof(VertexType),
159 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
160 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
161
162 this->numIndices = 0;
163 this->indexCapacity = indexCapacity;
164
165 VulkanUtils::createBuffer(device, physicalDevice, indexCapacity * sizeof(uint16_t),
166 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
167 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);
168
169 this->numObjects = 0;
170 this->objectCapacity = objectCapacity;
171
172 // Hacky way to allow an SSBO to be optional
173 // Specifying void* as the SSBOType will skip allocating the related buffers
174 if (!is_same_v<SSBOType, void*>) {
175 VkDeviceSize bufferSize = objectCapacity * sizeof(SSBOType);
176 cout << "NUM SWAP CHAIN IMAGES: " << swapChainImages.size() << endl;
177
178 storageBufferSet.buffers.resize(swapChainImages.size());
179 storageBufferSet.memory.resize(swapChainImages.size());
180 storageBufferSet.infoSet.resize(swapChainImages.size());
181
182 for (size_t i = 0; i < swapChainImages.size(); i++) {
183 VulkanUtils::createBuffer(this->device, this->physicalDevice, bufferSize,
184 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
185 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
186 storageBufferSet.buffers[i], storageBufferSet.memory[i]);
187
188 storageBufferSet.infoSet[i].buffer = storageBufferSet.buffers[i];
189 storageBufferSet.infoSet[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
190 storageBufferSet.infoSet[i].range = bufferSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
191 }
192 }
193}
194
195// TODO: Move as much cleanup as I can into the destructor
196template<class VertexType, class SSBOType>
197GraphicsPipeline_Vulkan<VertexType, SSBOType>::~GraphicsPipeline_Vulkan() {
198}
199
200template<class VertexType, class SSBOType>
201size_t GraphicsPipeline_Vulkan<VertexType, SSBOType>::getNumVertices() {
202 return numVertices;
203}
204
205template<class VertexType, class SSBOType>
206void GraphicsPipeline_Vulkan<VertexType, SSBOType>::updateRenderPass(VkRenderPass renderPass) {
207 this->renderPass = renderPass;
208}
209
210template<class VertexType, class SSBOType>
211void GraphicsPipeline_Vulkan<VertexType, SSBOType>::addAttribute(VkFormat format, size_t offset) {
212 VkVertexInputAttributeDescription attributeDesc = {};
213
214 attributeDesc.binding = 0;
215 attributeDesc.location = this->attributeDescriptions.size();
216 attributeDesc.format = format;
217 attributeDesc.offset = offset;
218
219 this->attributeDescriptions.push_back(attributeDesc);
220}
221
222// TODO: The SSBOType check isn't really needed since I call this function in VulkanGame explicitly
223template<class VertexType, class SSBOType>
224void GraphicsPipeline_Vulkan<VertexType, SSBOType>::addStorageDescriptor(VkShaderStageFlags stageFlags) {
225 if (!is_same_v<SSBOType, void*>) {
226 addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
227 stageFlags, &storageBufferSet.infoSet);
228 }
229}
230
231template<class VertexType, class SSBOType>
232void GraphicsPipeline_Vulkan<VertexType, SSBOType>::addDescriptorInfo(VkDescriptorType type,
233 VkShaderStageFlags stageFlags, vector<VkDescriptorBufferInfo>* bufferData) {
234 this->descriptorInfoList.push_back({ type, stageFlags, bufferData, nullptr });
235}
236
237template<class VertexType, class SSBOType>
238void GraphicsPipeline_Vulkan<VertexType, SSBOType>::addDescriptorInfo(VkDescriptorType type,
239 VkShaderStageFlags stageFlags, VkDescriptorImageInfo* imageData) {
240 this->descriptorInfoList.push_back({ type, stageFlags, nullptr, imageData });
241}
242
243template<class VertexType, class SSBOType>
244void GraphicsPipeline_Vulkan<VertexType, SSBOType>::createPipeline(string vertShaderFile, string fragShaderFile) {
245 this->vertShaderFile = vertShaderFile;
246 this->fragShaderFile = fragShaderFile;
247
248 vector<char> vertShaderCode = readFile(vertShaderFile);
249 vector<char> fragShaderCode = readFile(fragShaderFile);
250
251 VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
252 VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);
253
254 VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};
255 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
256 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
257 vertShaderStageInfo.module = vertShaderModule;
258 vertShaderStageInfo.pName = "main";
259
260 VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
261 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
262 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
263 fragShaderStageInfo.module = fragShaderModule;
264 fragShaderStageInfo.pName = "main";
265
266 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
267
268 VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
269 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
270
271 vertexInputInfo.vertexBindingDescriptionCount = 1;
272 vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(this->attributeDescriptions.size());
273 vertexInputInfo.pVertexBindingDescriptions = &this->bindingDescription;
274 vertexInputInfo.pVertexAttributeDescriptions = this->attributeDescriptions.data();
275
276 VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
277 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
278 inputAssembly.topology = this->topology;
279 inputAssembly.primitiveRestartEnable = VK_FALSE;
280
281 VkViewport viewport = {};
282 viewport.x = (float)this->viewport.x;
283 viewport.y = (float)this->viewport.y;
284 viewport.width = (float)this->viewport.width;
285 viewport.height = (float)this->viewport.height;
286 viewport.minDepth = 0.0f;
287 viewport.maxDepth = 1.0f;
288
289 VkRect2D scissor = {};
290 scissor.offset = { 0, 0 };
291 scissor.extent = { (uint32_t)this->viewport.width, (uint32_t)this->viewport.height };
292
293 VkPipelineViewportStateCreateInfo viewportState = {};
294 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
295 viewportState.viewportCount = 1;
296 viewportState.pViewports = &viewport;
297 viewportState.scissorCount = 1;
298 viewportState.pScissors = &scissor;
299
300 VkPipelineRasterizationStateCreateInfo rasterizer = {};
301 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
302 rasterizer.depthClampEnable = VK_FALSE;
303 rasterizer.rasterizerDiscardEnable = VK_FALSE;
304 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
305 rasterizer.lineWidth = 1.0f;
306 rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
307 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
308 rasterizer.depthBiasEnable = VK_FALSE;
309
310 VkPipelineMultisampleStateCreateInfo multisampling = {};
311 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
312 multisampling.sampleShadingEnable = VK_FALSE;
313 multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
314
315 VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
316 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
317 colorBlendAttachment.blendEnable = VK_TRUE;
318 colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
319 colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
320 colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
321 colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
322 colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
323 colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
324
325 VkPipelineColorBlendStateCreateInfo colorBlending = {};
326 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
327 colorBlending.logicOpEnable = VK_FALSE;
328 colorBlending.logicOp = VK_LOGIC_OP_COPY;
329 colorBlending.attachmentCount = 1;
330 colorBlending.pAttachments = &colorBlendAttachment;
331 colorBlending.blendConstants[0] = 0.0f;
332 colorBlending.blendConstants[1] = 0.0f;
333 colorBlending.blendConstants[2] = 0.0f;
334 colorBlending.blendConstants[3] = 0.0f;
335
336 VkPipelineDepthStencilStateCreateInfo depthStencil = {};
337 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
338 depthStencil.depthTestEnable = VK_TRUE;
339 depthStencil.depthWriteEnable = VK_TRUE;
340 depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
341 depthStencil.depthBoundsTestEnable = VK_FALSE;
342 depthStencil.minDepthBounds = 0.0f;
343 depthStencil.maxDepthBounds = 1.0f;
344 depthStencil.stencilTestEnable = VK_FALSE;
345 depthStencil.front = {};
346 depthStencil.back = {};
347
348 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
349 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
350 pipelineLayoutInfo.setLayoutCount = 1;
351 pipelineLayoutInfo.pSetLayouts = &this->descriptorSetLayout;
352 pipelineLayoutInfo.pushConstantRangeCount = 0;
353
354 if (vkCreatePipelineLayout(this->device, &pipelineLayoutInfo, nullptr, &this->pipelineLayout) != VK_SUCCESS) {
355 throw runtime_error("failed to create pipeline layout!");
356 }
357
358 VkGraphicsPipelineCreateInfo pipelineInfo = {};
359 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
360 pipelineInfo.stageCount = 2;
361 pipelineInfo.pStages = shaderStages;
362 pipelineInfo.pVertexInputState = &vertexInputInfo;
363 pipelineInfo.pInputAssemblyState = &inputAssembly;
364 pipelineInfo.pViewportState = &viewportState;
365 pipelineInfo.pRasterizationState = &rasterizer;
366 pipelineInfo.pMultisampleState = &multisampling;
367 pipelineInfo.pDepthStencilState = &depthStencil;
368 pipelineInfo.pColorBlendState = &colorBlending;
369 pipelineInfo.pDynamicState = nullptr;
370 pipelineInfo.layout = this->pipelineLayout;
371 pipelineInfo.renderPass = this->renderPass;
372 pipelineInfo.subpass = 0;
373 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
374 pipelineInfo.basePipelineIndex = -1;
375
376 if (vkCreateGraphicsPipelines(this->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &this->pipeline) != VK_SUCCESS) {
377 throw runtime_error("failed to create graphics pipeline!");
378 }
379
380 vkDestroyShaderModule(this->device, vertShaderModule, nullptr);
381 vkDestroyShaderModule(this->device, fragShaderModule, nullptr);
382}
383
384template<class VertexType, class SSBOType>
385void GraphicsPipeline_Vulkan<VertexType, SSBOType>::createDescriptorSetLayout() {
386 vector<VkDescriptorSetLayoutBinding> bindings(this->descriptorInfoList.size());
387
388 for (size_t i = 0; i < bindings.size(); i++) {
389 bindings[i].binding = i;
390 bindings[i].descriptorCount = 1;
391 bindings[i].descriptorType = this->descriptorInfoList[i].type;
392 bindings[i].stageFlags = this->descriptorInfoList[i].stageFlags;
393 bindings[i].pImmutableSamplers = nullptr;
394 }
395
396 VkDescriptorSetLayoutCreateInfo layoutInfo = {};
397 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
398 layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
399 layoutInfo.pBindings = bindings.data();
400
401 if (vkCreateDescriptorSetLayout(this->device, &layoutInfo, nullptr, &this->descriptorSetLayout) != VK_SUCCESS) {
402 throw runtime_error("failed to create descriptor set layout!");
403 }
404}
405
406template<class VertexType, class SSBOType>
407void GraphicsPipeline_Vulkan<VertexType, SSBOType>::createDescriptorPool(vector<VkImage>& swapChainImages) {
408 vector<VkDescriptorPoolSize> poolSizes(this->descriptorInfoList.size());
409
410 for (size_t i = 0; i < poolSizes.size(); i++) {
411 poolSizes[i].type = this->descriptorInfoList[i].type;
412 poolSizes[i].descriptorCount = static_cast<uint32_t>(swapChainImages.size());
413 }
414
415 VkDescriptorPoolCreateInfo poolInfo = {};
416 poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
417 poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
418 poolInfo.pPoolSizes = poolSizes.data();
419 poolInfo.maxSets = static_cast<uint32_t>(swapChainImages.size());
420
421 if (vkCreateDescriptorPool(this->device, &poolInfo, nullptr, &this->descriptorPool) != VK_SUCCESS) {
422 throw runtime_error("failed to create descriptor pool!");
423 }
424}
425
426// TODO: Since I only need the size of the swapChainImages array, I should just pass that in instead of the whole array
427template<class VertexType, class SSBOType>
428void GraphicsPipeline_Vulkan<VertexType, SSBOType>::createDescriptorSets(vector<VkImage>& swapChainImages) {
429 vector<VkDescriptorSetLayout> layouts(swapChainImages.size(), this->descriptorSetLayout);
430
431 VkDescriptorSetAllocateInfo allocInfo = {};
432 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
433 allocInfo.descriptorPool = this->descriptorPool;
434 allocInfo.descriptorSetCount = static_cast<uint32_t>(swapChainImages.size());
435 allocInfo.pSetLayouts = layouts.data();
436
437 this->descriptorSets.resize(swapChainImages.size());
438 if (vkAllocateDescriptorSets(device, &allocInfo, this->descriptorSets.data()) != VK_SUCCESS) {
439 throw runtime_error("failed to allocate descriptor sets!");
440 }
441
442 for (size_t i = 0; i < swapChainImages.size(); i++) {
443 vector<VkWriteDescriptorSet> descriptorWrites(this->descriptorInfoList.size());
444
445 for (size_t j = 0; j < descriptorWrites.size(); j++) {
446 descriptorWrites[j].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
447 descriptorWrites[j].dstSet = this->descriptorSets[i];
448 descriptorWrites[j].dstBinding = j;
449 descriptorWrites[j].dstArrayElement = 0;
450 descriptorWrites[j].descriptorType = this->descriptorInfoList[j].type;
451 descriptorWrites[j].descriptorCount = 1;
452 descriptorWrites[j].pBufferInfo = nullptr;
453 descriptorWrites[j].pImageInfo = nullptr;
454 descriptorWrites[j].pTexelBufferView = nullptr;
455
456 switch (descriptorWrites[j].descriptorType) {
457 case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
458 case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
459 descriptorWrites[j].pBufferInfo = &(*this->descriptorInfoList[j].bufferDataList)[i];
460 break;
461 case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
462 descriptorWrites[j].pImageInfo = this->descriptorInfoList[j].imageData;
463 break;
464 default:
465 throw runtime_error("Unknown descriptor type: " + to_string(descriptorWrites[j].descriptorType));
466 }
467 }
468
469 vkUpdateDescriptorSets(this->device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
470 }
471}
472
473template<class VertexType, class SSBOType>
474void GraphicsPipeline_Vulkan<VertexType, SSBOType>::createRenderCommands(VkCommandBuffer& commandBuffer,
475 uint32_t currentImage) {
476 vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
477 vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1,
478 &descriptorSets[currentImage], 0, nullptr);
479
480 VkBuffer vertexBuffers[] = { vertexBuffer };
481 VkDeviceSize offsets[] = { 0 };
482 vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
483
484 vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT16);
485
486 vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(numIndices), 1, 0, 0, 0);
487}
488
489template<class VertexType, class SSBOType>
490bool GraphicsPipeline_Vulkan<VertexType, SSBOType>::addObject(
491 const vector<VertexType>& vertices, vector<uint16_t> indices,
492 SSBOType& ssbo, VkCommandPool commandPool, VkQueue graphicsQueue) {
493
494 // TODO: When resizing the vertex or index buffer, take deleted objects into account.
495 // Remove their data from the buffer and determine the new size of the bufer based on # of remining objects
496
497 // If # non-deleted objects > currentCapacity / 2
498 // - resize and double capacity
499 // else If # non-deleted objects < currentCapacity / 4
500 // - resize amd halve capacity
501 // else
502 // - don't resize, but rewrite data in the buffer to only have non-deleted objects
503
504 if (this->numVertices + vertices.size() > this->vertexCapacity) {
505 resizeVertexBuffer(commandPool, graphicsQueue);
506 }
507 VulkanUtils::copyDataToBuffer(this->device, this->physicalDevice, commandPool, vertices,
508 this->vertexBuffer, this->numVertices, graphicsQueue);
509 this->numVertices += vertices.size();
510
511 if (this->numIndices + indices.size() > this->indexCapacity) {
512 resizeIndexBuffer(commandPool, graphicsQueue);
513 }
514 VulkanUtils::copyDataToBuffer(this->device, this->physicalDevice, commandPool, indices,
515 this->indexBuffer, this->numIndices, graphicsQueue);
516 this->numIndices += indices.size();
517
518 bool resizedStorageBuffer = false;
519
520 if (!is_same_v<SSBOType, void*>) {
521 if (this->numObjects == this->objectCapacity) {
522 resizeStorageBufferSet(storageBufferSet, commandPool, graphicsQueue);
523 cleanup();
524
525 // Assume the SSBO is always the 2nd binding
526 this->descriptorInfoList[1].bufferDataList = &storageBufferSet.infoSet;
527 resizedStorageBuffer = true;
528
529 cout << "SSBO resized, New object capacity: " << this->objectCapacity << endl;
530
531 // TODO: I'll need to correctly update the descriptor set array instead of appending to it
532 // Then, I'll have to call createDescriptorSets() and finally createCommandBuffers() (from vulkan-game)
533 // This isn't too bad actually, since I have to call createCommandBuffers() every time I add a newobject
534 // anyway. So, in this function, I'll just have to call createDescriptorSets()
535 }
536
537 updateObject(this->numObjects, ssbo);
538 }
539
540 this->numObjects++;
541
542 return resizedStorageBuffer;
543}
544
545template<class VertexType, class SSBOType>
546void GraphicsPipeline_Vulkan<VertexType, SSBOType>::updateObject(size_t objIndex, SSBOType& ssbo) {
547 if (!is_same_v<SSBOType, void*>) {
548 for (size_t i = 0; i < storageBufferSet.memory.size(); i++) {
549 VulkanUtils::copyDataToMemory(this->device, storageBufferSet.memory[i], objIndex * sizeof(SSBOType), ssbo);
550 }
551 }
552}
553
554// Should only be used if the number of vertices has not changed
555template<class VertexType, class SSBOType>
556void GraphicsPipeline_Vulkan<VertexType, SSBOType>::updateObjectVertices(size_t objIndex,
557 const vector<VertexType>& vertices, VkCommandPool commandPool, VkQueue graphicsQueue) {
558 VulkanUtils::copyDataToBuffer(this->device, this->physicalDevice, commandPool, vertices,
559 this->vertexBuffer, objIndex * vertices.size(), graphicsQueue);
560}
561
562template<class VertexType, class SSBOType>
563void GraphicsPipeline_Vulkan<VertexType, SSBOType>::cleanup() {
564 vkDestroyPipeline(device, pipeline, nullptr);
565 vkDestroyDescriptorPool(device, descriptorPool, nullptr);
566
567 // TODO: I read that the pipeline layout does not have to be recreated every time
568 // Try only creating it once
569 vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
570}
571
572template<class VertexType, class SSBOType>
573void GraphicsPipeline_Vulkan<VertexType, SSBOType>::cleanupBuffers() {
574 vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
575
576 vkDestroyBuffer(device, vertexBuffer, nullptr);
577 vkFreeMemory(device, vertexBufferMemory, nullptr);
578 vkDestroyBuffer(device, indexBuffer, nullptr);
579 vkFreeMemory(device, indexBufferMemory, nullptr);
580
581 if (!is_same_v<SSBOType, void*>) {
582 for (size_t i = 0; i < storageBufferSet.buffers.size(); i++) {
583 vkDestroyBuffer(device, storageBufferSet.buffers[i], nullptr);
584 vkFreeMemory(device, storageBufferSet.memory[i], nullptr);
585 }
586 }
587}
588
589/*** PRIVATE METHODS ***/
590
591template<class VertexType, class SSBOType>
592VkShaderModule GraphicsPipeline_Vulkan<VertexType, SSBOType>::createShaderModule(const vector<char>& code) {
593 VkShaderModuleCreateInfo createInfo = {};
594 createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
595 createInfo.codeSize = code.size();
596 createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
597
598 VkShaderModule shaderModule;
599 if (vkCreateShaderModule(this->device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
600 throw runtime_error("failed to create shader module!");
601 }
602
603 return shaderModule;
604}
605
606template<class VertexType, class SSBOType>
607vector<char> GraphicsPipeline_Vulkan<VertexType, SSBOType>::readFile(const string& filename) {
608 ifstream file(filename, ios::ate | ios::binary);
609
610 if (!file.is_open()) {
611 throw runtime_error("failed to open file!");
612 }
613
614 size_t fileSize = (size_t)file.tellg();
615 vector<char> buffer(fileSize);
616
617 file.seekg(0);
618 file.read(buffer.data(), fileSize);
619
620 file.close();
621
622 return buffer;
623}
624
625template<class VertexType, class SSBOType>
626void GraphicsPipeline_Vulkan<VertexType, SSBOType>::resizeVertexBuffer(VkCommandPool commandPool,
627 VkQueue graphicsQueue) {
628 VkBuffer newVertexBuffer;
629 VkDeviceMemory newVertexBufferMemory;
630 this->vertexCapacity *= 2;
631
632 VulkanUtils::createBuffer(this->device, this->physicalDevice, this->vertexCapacity * sizeof(VertexType),
633 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
634 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, newVertexBuffer, newVertexBufferMemory);
635
636 VulkanUtils::copyBuffer(this->device, commandPool, vertexBuffer, newVertexBuffer, 0, 0, numVertices * sizeof(VertexType), graphicsQueue);
637
638 vkDestroyBuffer(this->device, vertexBuffer, nullptr);
639 vkFreeMemory(this->device, vertexBufferMemory, nullptr);
640
641 vertexBuffer = newVertexBuffer;
642 vertexBufferMemory = newVertexBufferMemory;
643}
644
645template<class VertexType, class SSBOType>
646void GraphicsPipeline_Vulkan<VertexType, SSBOType>::resizeIndexBuffer(VkCommandPool commandPool,
647 VkQueue graphicsQueue) {
648 VkBuffer newIndexBuffer;
649 VkDeviceMemory newIndexBufferMemory;
650 this->indexCapacity *= 2;
651
652 VulkanUtils::createBuffer(this->device, this->physicalDevice, this->indexCapacity * sizeof(uint16_t),
653 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
654 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, newIndexBuffer, newIndexBufferMemory);
655
656 VulkanUtils::copyBuffer(this->device, commandPool, indexBuffer, newIndexBuffer, 0, 0, numIndices * sizeof(uint16_t), graphicsQueue);
657
658 vkDestroyBuffer(this->device, indexBuffer, nullptr);
659 vkFreeMemory(this->device, indexBufferMemory, nullptr);
660
661 indexBuffer = newIndexBuffer;
662 indexBufferMemory = newIndexBufferMemory;
663}
664
665template<class VertexType, class SSBOType>
666void GraphicsPipeline_Vulkan<VertexType, SSBOType>::resizeStorageBufferSet(StorageBufferSet& set,
667 VkCommandPool commandPool, VkQueue graphicsQueue) {
668 this->objectCapacity *= 2;
669 VkDeviceSize bufferSize = objectCapacity * sizeof(SSBOType);
670
671 for (size_t i = 0; i < set.buffers.size(); i++) {
672 VkBuffer newStorageBuffer;
673 VkDeviceMemory newStorageBufferMemory;
674
675 VulkanUtils::createBuffer(this->device, this->physicalDevice, bufferSize,
676 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
677 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
678 newStorageBuffer, newStorageBufferMemory);
679
680 VulkanUtils::copyBuffer(this->device, commandPool, set.buffers[i], newStorageBuffer,
681 0, 0, this->numObjects * sizeof(SSBOType), graphicsQueue);
682
683 vkDestroyBuffer(this->device, set.buffers[i], nullptr);
684 vkFreeMemory(this->device, set.memory[i], nullptr);
685
686 set.buffers[i] = newStorageBuffer;
687 set.memory[i] = newStorageBufferMemory;
688
689 set.infoSet[i].buffer = set.buffers[i];
690 set.infoSet[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
691 set.infoSet[i].range = bufferSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
692 }
693}
694
695#endif // _GRAPHICS_PIPELINE_VULKAN_H
Note: See TracBrowser for help on using the repository browser.