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

feature/imgui-sdl points-test
Last change on this file since a52ba87 was a52ba87, checked in by Dmitry Portnoy <dmitry.portnoy@…>, 4 years ago

In GraphicsPipeline_Vulkan, change addStorageDescriptor() to take a parameter indicating the shader stage, and add an updateObjectVertices() method

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