From d3ebdfbd66f40ddfd7a3678434e0372bcc5eb569 Mon Sep 17 00:00:00 2001 From: Benjamin Kraft Date: Sat, 7 Sep 2024 14:28:48 +0200 Subject: [PATCH] basic compute --- shaders/compile.sh | 3 +- shaders/shader.comp | 16 +++++++ src/vulkan/application.cpp | 96 ++++++++++++++++++++++++++----------- src/vulkan/application.hpp | 13 +++-- src/vulkan/command_pool.cpp | 8 ++-- src/vulkan/command_pool.hpp | 3 +- src/vulkan/instance.cpp | 4 ++ src/vulkan/instance.hpp | 14 +++++- src/vulkan/pipeline.cpp | 59 ++++++++++++++++++++--- src/vulkan/pipeline.hpp | 13 +++-- src/vulkan/vertex.hpp | 4 +- 11 files changed, 183 insertions(+), 50 deletions(-) create mode 100644 shaders/shader.comp diff --git a/shaders/compile.sh b/shaders/compile.sh index 645f430..f78dcd3 100755 --- a/shaders/compile.sh +++ b/shaders/compile.sh @@ -1,3 +1,4 @@ #!/usr/bin/env bash glslc shader.vert -o vert.spv -glslc shader.frag -o frag.spv \ No newline at end of file +glslc shader.frag -o frag.spv +glslc shader.comp -o comp.spv \ No newline at end of file diff --git a/shaders/shader.comp b/shaders/shader.comp new file mode 100644 index 0000000..a72a073 --- /dev/null +++ b/shaders/shader.comp @@ -0,0 +1,16 @@ +#version 450 + +layout (local_size_x = 1) in; + +struct Vertex { + vec3 position; + vec3 color; +}; + +layout (std140, binding = 0) buffer VertexBuffer { + Vertex vertices[]; +}; + +void main() { + vertices[4].position.z += 0.001; +} \ No newline at end of file diff --git a/src/vulkan/application.cpp b/src/vulkan/application.cpp index e84b9ee..1f249d3 100644 --- a/src/vulkan/application.cpp +++ b/src/vulkan/application.cpp @@ -10,10 +10,10 @@ Application::Application() { new Instance; swapchain = new Swapchain(); - pipeline = new Pipeline(swapchain->renderPass); + graphicsPipeline = new Pipeline(swapchain->renderPass); auto stagedVertexBuffer = Buffer::createStagedVertexBuffer(); vertexBuffer = new Buffer(stagedVertexBuffer->size, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); auto stagedIndexBuffer = Buffer::createStagedIndexBuffer(); @@ -28,7 +28,8 @@ Application::Application() { VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); vkMapMemory(Instance::instance->device, uniformBuffer->memory, 0, bufferSize, 0, &uniformBufferMapped); - pipeline->createDescriptorSet(uniformBuffer); + graphicsPipeline->createDescriptorSet(uniformBuffer, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); + computePipeline = new ComputePipeline(vertexBuffer); commandPool = new CommandPool(); stagedVertexBuffer->copyTo(vertexBuffer, commandPool); @@ -38,6 +39,8 @@ Application::Application() { delete stagedIndexBuffer; createSyncObjects(); + + recordComputeCommandBuffer(); } void Application::updateUniformBuffer() { @@ -77,7 +80,7 @@ void Application::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t im renderPassInfo.pClearValues = clearValues; vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->handle); + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline->handle); VkViewport viewport {}; viewport.x = 0; @@ -97,7 +100,7 @@ void Application::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t im VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffer, 0, 1, buffers, offsets); vkCmdBindIndexBuffer(commandBuffer, indexBuffer->handle, 0, VK_INDEX_TYPE_UINT32); - vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->layout, 0, 1, &pipeline->descriptorSet, 0, nullptr); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline->layout, 0, 1, &graphicsPipeline->descriptorSet, 0, nullptr); vkCmdDrawIndexed(commandBuffer, indices.size(), 1, 0, 0, 0); @@ -105,8 +108,32 @@ void Application::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t im vkEndCommandBuffer(commandBuffer); } +void Application::createSyncObjects() { + VkSemaphoreCreateInfo semaphoreInfo {}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo {}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + vkCreateSemaphore(Instance::instance->device, &semaphoreInfo, nullptr, &imageAvailableSemaphore); + vkCreateSemaphore(Instance::instance->device, &semaphoreInfo, nullptr, &renderFinishedSemaphore); + vkCreateSemaphore(Instance::instance->device, &semaphoreInfo, nullptr, &computeFinishedSemaphore); + vkCreateFence(Instance::instance->device, &fenceInfo, nullptr, &renderInFlightFence); + vkCreateFence(Instance::instance->device, &fenceInfo, nullptr, &computeInFlightFence); +} + +void Application::mainLoop() { + while (!glfwWindowShouldClose(Instance::instance->window)){ + glfwPollEvents(); + update(); + drawFrame(); + } + vkDeviceWaitIdle(Instance::instance->device); +} + void Application::drawFrame() { - vkWaitForFences(Instance::instance->device, 1, &inFlightFence, VK_TRUE, UINT64_MAX); + vkWaitForFences(Instance::instance->device, 1, &renderInFlightFence, VK_TRUE, UINT64_MAX); uint32_t imageIndex; VkResult result = vkAcquireNextImageKHR(Instance::instance->device, swapchain->handle, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex); @@ -115,10 +142,10 @@ void Application::drawFrame() { return; } - vkResetFences(Instance::instance->device, 1, &inFlightFence); + vkResetFences(Instance::instance->device, 1, &renderInFlightFence); - vkResetCommandBuffer(commandPool->buffer, 0); - recordCommandBuffer(commandPool->buffer, imageIndex); + vkResetCommandBuffer(commandPool->graphicsBuffer, 0); + recordCommandBuffer(commandPool->graphicsBuffer, imageIndex); updateUniformBuffer(); @@ -131,13 +158,15 @@ void Application::drawFrame() { submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandPool->buffer; + submitInfo.pCommandBuffers = &commandPool->graphicsBuffer; VkSemaphore signalSemaphores[] = {renderFinishedSemaphore}; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = &computeFinishedSemaphore; - vkQueueSubmit(Instance::instance->graphicsQueue, 1, &submitInfo, inFlightFence); + vkQueueSubmit(Instance::instance->graphicsQueue, 1, &submitInfo, renderInFlightFence); VkPresentInfoKHR presentInfo {}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; @@ -156,37 +185,48 @@ void Application::drawFrame() { } } -void Application::createSyncObjects() { - VkSemaphoreCreateInfo semaphoreInfo {}; - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; +void Application::update() { + vkWaitForFences(Instance::instance->device, 1, &computeInFlightFence, VK_TRUE, UINT64_MAX); + vkResetFences(Instance::instance->device, 1, &computeInFlightFence); - VkFenceCreateInfo fenceInfo {}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + VkSubmitInfo submit {}; + submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit.commandBufferCount = 1; + submit.pCommandBuffers = &commandPool->computeBuffer; + submit.signalSemaphoreCount = 1; + submit.pSignalSemaphores = &computeFinishedSemaphore; - vkCreateSemaphore(Instance::instance->device, &semaphoreInfo, nullptr, &imageAvailableSemaphore); - vkCreateSemaphore(Instance::instance->device, &semaphoreInfo, nullptr, &renderFinishedSemaphore); - vkCreateFence(Instance::instance->device, &fenceInfo, nullptr, &inFlightFence); + vkQueueSubmit(Instance::instance->computeQueue, 1, &submit, computeInFlightFence); } -void Application::mainLoop() { - while (!glfwWindowShouldClose(Instance::instance->window)){ - glfwPollEvents(); - drawFrame(); - } - vkDeviceWaitIdle(Instance::instance->device); +void Application::recordComputeCommandBuffer() { + VkCommandBuffer buffer = commandPool->computeBuffer; + + VkCommandBufferBeginInfo beginInfo {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + vkBeginCommandBuffer(buffer, &beginInfo); + + vkCmdBindPipeline(buffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline->handle); + vkCmdBindDescriptorSets(buffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline->layout,0, 1, &computePipeline->descriptorSet, 0, nullptr); + vkCmdDispatch(buffer, 1, 1, 1); + + vkEndCommandBuffer(buffer); } Application::~Application() { delete swapchain; vkDestroySemaphore(Instance::instance->device, imageAvailableSemaphore, nullptr); vkDestroySemaphore(Instance::instance->device, renderFinishedSemaphore, nullptr); - vkDestroyFence(Instance::instance->device, inFlightFence, nullptr); + vkDestroySemaphore(Instance::instance->device, computeFinishedSemaphore, nullptr); + vkDestroyFence(Instance::instance->device, renderInFlightFence, nullptr); + vkDestroyFence(Instance::instance->device, computeInFlightFence, nullptr); delete commandPool; delete vertexBuffer; delete indexBuffer; delete uniformBuffer; - delete pipeline; + delete graphicsPipeline; + delete computePipeline; delete Instance::instance; } diff --git a/src/vulkan/application.hpp b/src/vulkan/application.hpp index d94a40f..2a10195 100644 --- a/src/vulkan/application.hpp +++ b/src/vulkan/application.hpp @@ -20,8 +20,7 @@ class Pipeline; class Buffer; class CommandPool; class Image; - -constexpr int MAX_FRAMES_IN_FLIGHT = 1; +class ComputePipeline; class Timer { public: @@ -43,22 +42,28 @@ public: ~Application(); private: Swapchain* swapchain = nullptr; - Pipeline* pipeline = nullptr; + Pipeline* graphicsPipeline = nullptr; Buffer* vertexBuffer = nullptr; Buffer* indexBuffer = nullptr; Buffer* uniformBuffer = nullptr; CommandPool* commandPool = nullptr; + ComputePipeline* computePipeline = nullptr; + void* uniformBufferMapped = nullptr; void updateUniformBuffer(); void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex); + void recordComputeCommandBuffer(); VkSemaphore imageAvailableSemaphore = VK_NULL_HANDLE; VkSemaphore renderFinishedSemaphore = VK_NULL_HANDLE; - VkFence inFlightFence = VK_NULL_HANDLE; + VkSemaphore computeFinishedSemaphore = VK_NULL_HANDLE; + VkFence renderInFlightFence = VK_NULL_HANDLE; + VkFence computeInFlightFence = VK_NULL_HANDLE; void drawFrame(); + void update(); void createSyncObjects(); }; diff --git a/src/vulkan/command_pool.cpp b/src/vulkan/command_pool.cpp index ab8811a..05d6927 100644 --- a/src/vulkan/command_pool.cpp +++ b/src/vulkan/command_pool.cpp @@ -8,7 +8,7 @@ CommandPool::CommandPool() { VkCommandPoolCreateInfo poolInfo {}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - poolInfo.queueFamilyIndex = indices.graphicsFamily.value(); + poolInfo.queueFamilyIndex = indices.graphicsAndComputeFamily.value(); vkCreateCommandPool(Instance::instance->device, &poolInfo, nullptr, &handle); @@ -22,10 +22,12 @@ void CommandPool::createBuffers() { allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocateInfo.commandBufferCount = 1; - vkAllocateCommandBuffers(Instance::instance->device, &allocateInfo, &buffer); + vkAllocateCommandBuffers(Instance::instance->device, &allocateInfo, &graphicsBuffer); + vkAllocateCommandBuffers(Instance::instance->device, &allocateInfo, &computeBuffer); } CommandPool::~CommandPool() { - vkFreeCommandBuffers(Instance::instance->device, handle, 1, &buffer); + vkFreeCommandBuffers(Instance::instance->device, handle, 1, &graphicsBuffer); + vkFreeCommandBuffers(Instance::instance->device, handle, 1, &computeBuffer); vkDestroyCommandPool(Instance::instance->device, handle, nullptr); } diff --git a/src/vulkan/command_pool.hpp b/src/vulkan/command_pool.hpp index 4f494a2..d80ffa7 100644 --- a/src/vulkan/command_pool.hpp +++ b/src/vulkan/command_pool.hpp @@ -9,7 +9,8 @@ class CommandPool { public: explicit CommandPool(); ~CommandPool(); - VkCommandBuffer buffer = VK_NULL_HANDLE; + VkCommandBuffer graphicsBuffer = VK_NULL_HANDLE; + VkCommandBuffer computeBuffer = VK_NULL_HANDLE; VkCommandPool handle = VK_NULL_HANDLE; private: diff --git a/src/vulkan/instance.cpp b/src/vulkan/instance.cpp index d1e47d6..aa9c6ca 100644 --- a/src/vulkan/instance.cpp +++ b/src/vulkan/instance.cpp @@ -163,6 +163,7 @@ void Instance::createLogicalDevice() { vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); + vkGetDeviceQueue(device, indices.graphicsAndComputeFamily.value(), 0, &computeQueue); } Instance::QueueFamilyIndices Instance::findQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface) { @@ -182,6 +183,9 @@ Instance::QueueFamilyIndices Instance::findQueueFamilies(VkPhysicalDevice device if (queueFamilyProperties.queueFlags & VK_QUEUE_COMPUTE_BIT){ indices.computeFamily = i; } + if (indices.graphicsFamily == i && indices.computeFamily == i){ + indices.graphicsAndComputeFamily = i; + } VkBool32 presentSupport = false; vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); if (presentSupport){ diff --git a/src/vulkan/instance.hpp b/src/vulkan/instance.hpp index 7eb1036..9e16bb2 100644 --- a/src/vulkan/instance.hpp +++ b/src/vulkan/instance.hpp @@ -16,16 +16,26 @@ public: VkDevice device = VK_NULL_HANDLE; VkQueue graphicsQueue = VK_NULL_HANDLE; VkQueue presentQueue = VK_NULL_HANDLE; + VkQueue computeQueue = VK_NULL_HANDLE; struct QueueFamilyIndices { std::optional graphicsFamily; std::optional computeFamily; std::optional presentFamily; + std::optional graphicsAndComputeFamily; bool isComplete() const { - return graphicsFamily.has_value() && computeFamily.has_value() && presentFamily.has_value(); + return graphicsFamily.has_value() && + computeFamily.has_value() && + presentFamily.has_value() && + graphicsAndComputeFamily.has_value(); } std::set uniqueQueueFamilies(){ - return {graphicsFamily.value(), presentFamily.value(), computeFamily.value()}; + return { + graphicsFamily.value(), + presentFamily.value(), + computeFamily.value(), + graphicsAndComputeFamily.value() + }; } }; static QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface); diff --git a/src/vulkan/pipeline.cpp b/src/vulkan/pipeline.cpp index c2591a6..5c1bfc0 100644 --- a/src/vulkan/pipeline.cpp +++ b/src/vulkan/pipeline.cpp @@ -22,9 +22,13 @@ std::vector readFile(const std::string& fileName){ return buffer; } +Pipeline::Pipeline() { + +} + Pipeline::Pipeline(VkRenderPass renderPass) { createDescriptorSetLayout(); - createDescriptorPool(); + createDescriptorPool(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); @@ -178,10 +182,10 @@ void Pipeline::createDescriptorSetLayout() { vkCreateDescriptorSetLayout(Instance::instance->device, &layoutCreateInfo, nullptr, &descriptorSetLayout); } -void Pipeline::createDescriptorPool() { +void Pipeline::createDescriptorPool(VkDescriptorType type) { VkDescriptorPoolSize poolSize {}; poolSize.descriptorCount = 1; - poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSize.type = type; VkDescriptorPoolCreateInfo poolInfo {}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; @@ -192,7 +196,7 @@ void Pipeline::createDescriptorPool() { vkCreateDescriptorPool(Instance::instance->device, &poolInfo, nullptr, &descriptorPool); } -void Pipeline::createDescriptorSet(Buffer *buffer) { +void Pipeline::createDescriptorSet(Buffer *buffer, VkDescriptorType type) { VkDescriptorSetAllocateInfo allocateInfo {}; allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocateInfo.descriptorPool = descriptorPool; @@ -204,17 +208,60 @@ void Pipeline::createDescriptorSet(Buffer *buffer) { VkDescriptorBufferInfo bufferInfo {}; bufferInfo.buffer = buffer->handle; bufferInfo.offset = 0; - bufferInfo.range = sizeof(UniformBufferObject); + bufferInfo.range = buffer->size; VkWriteDescriptorSet descriptorWrite {}; descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrite.dstSet = descriptorSet; descriptorWrite.dstBinding = 0; descriptorWrite.dstArrayElement = 0; - descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorType = type; descriptorWrite.descriptorCount = 1; descriptorWrite.pBufferInfo = &bufferInfo; vkUpdateDescriptorSets(Instance::instance->device, 1, &descriptorWrite, 0, nullptr); } +ComputePipeline::ComputePipeline(Buffer* buffer) { + { + VkDescriptorSetLayoutBinding layoutBinding {}; + layoutBinding.binding = 0; + layoutBinding.descriptorCount = 1; + layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + layoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo {}; + descriptorSetLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorSetLayoutInfo.bindingCount = 1; + descriptorSetLayoutInfo.pBindings = &layoutBinding; + + vkCreateDescriptorSetLayout(Instance::instance->device, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout); + + createDescriptorPool(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + createDescriptorSet(buffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + } + + VkShaderModule module = createShaderModule(readFile("shaders/comp.spv")); + + VkPipelineShaderStageCreateInfo stageInfo {}; + stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; + stageInfo.pName = "main"; + stageInfo.module = module; + + VkPipelineLayoutCreateInfo layoutInfo {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + layoutInfo.setLayoutCount = 1; + layoutInfo.pSetLayouts = &descriptorSetLayout; + + vkCreatePipelineLayout(Instance::instance->device, &layoutInfo, nullptr, &layout); + + VkComputePipelineCreateInfo pipelineInfo {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + pipelineInfo.layout = layout; + pipelineInfo.stage = stageInfo; + + vkCreateComputePipelines(Instance::instance->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &handle); + + vkDestroyShaderModule(Instance::instance->device, module, nullptr); +} diff --git a/src/vulkan/pipeline.hpp b/src/vulkan/pipeline.hpp index 8e80778..1324064 100644 --- a/src/vulkan/pipeline.hpp +++ b/src/vulkan/pipeline.hpp @@ -15,19 +15,26 @@ struct UniformBufferObject { class Pipeline { public: + explicit Pipeline(); explicit Pipeline(VkRenderPass renderPass); ~Pipeline(); VkPipeline handle = VK_NULL_HANDLE; VkPipelineLayout layout = VK_NULL_HANDLE; VkDescriptorSet descriptorSet = VK_NULL_HANDLE; - void createDescriptorSet(Buffer* buffer); -private: + void createDescriptorSet(Buffer* buffer, VkDescriptorType type); + +protected: VkShaderModule createShaderModule(const std::vector &code); VkDescriptorPool descriptorPool = VK_NULL_HANDLE; VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; void createDescriptorSetLayout(); - void createDescriptorPool(); + void createDescriptorPool(VkDescriptorType type); + +}; +class ComputePipeline : public Pipeline { +public: + explicit ComputePipeline(Buffer* buffer); }; \ No newline at end of file diff --git a/src/vulkan/vertex.hpp b/src/vulkan/vertex.hpp index ef809d4..fc60cd0 100644 --- a/src/vulkan/vertex.hpp +++ b/src/vulkan/vertex.hpp @@ -5,8 +5,8 @@ #include struct Vertex { - glm::vec3 pos; - glm::vec3 color; + alignas(16) glm::vec3 pos; + alignas(16) glm::vec3 color; static VkVertexInputBindingDescription getBindingDescription();