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

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

Modify the parameter order of VulkanUtils::copyDataToMemory and add an overload
that takes the data size as a parameter instead of getting it from the template type

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