From 2e808a5dd68e40732440be6855d9f39acffafe70 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 25 Nov 2025 21:31:32 +0100 Subject: [PATCH 1/2] Add uniform buffer support to the shader --- assets/shaders/default.vert | 8 +- src/contrib/cppcore | 2 +- src/runtime/renderer/RHIVulkan.cpp | 139 ++++++++++++++++++++++++++++- src/runtime/renderer/rendercore.h | 2 +- 4 files changed, 147 insertions(+), 4 deletions(-) diff --git a/assets/shaders/default.vert b/assets/shaders/default.vert index 28bbc67..b68f601 100644 --- a/assets/shaders/default.vert +++ b/assets/shaders/default.vert @@ -1,11 +1,17 @@ #version 450 +layout(binding = 0) uniform UniformBufferObject { + mat4 model; + mat4 view; + mat4 proj; +} ubo; + layout(location = 0) in vec2 inPosition; layout(location = 1) in vec3 inColor; layout(location = 0) out vec3 fragColor; void main() { - gl_Position = vec4(inPosition, 0.0, 1.0); + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); fragColor = inColor; } diff --git a/src/contrib/cppcore b/src/contrib/cppcore index 3cbea39..cef37da 160000 --- a/src/contrib/cppcore +++ b/src/contrib/cppcore @@ -1 +1 @@ -Subproject commit 3cbea39a6b70da9a9735a993882270cd5e176460 +Subproject commit cef37da2b2d8d216fb876a4d134f24ab63466b6d diff --git a/src/runtime/renderer/RHIVulkan.cpp b/src/runtime/renderer/RHIVulkan.cpp index b438fb2..1bf88c6 100644 --- a/src/runtime/renderer/RHIVulkan.cpp +++ b/src/runtime/renderer/RHIVulkan.cpp @@ -1,6 +1,11 @@ #include "RHI.h" +#include "rendercore.h" + #include "volk.h" #include "SDL_vulkan.h" +#define GLM_FORCE_RADIANS +#include +#include #include #include @@ -11,6 +16,7 @@ #include #include #include +#include namespace segfault::renderer { @@ -99,6 +105,10 @@ namespace segfault::renderer { VkShaderModule vertShaderModule{}; VkShaderModule fragShaderModule{}; VkRenderPass renderPass{}; + VkDescriptorSetLayout descriptorSetLayout{}; + VkDescriptorPool descriptorPool{}; + std::vector descriptorSets{}; + VkPipelineLayout pipelineLayout{}; std::vector swapChainFramebuffers{}; VkCommandPool commandPool{}; @@ -110,6 +120,10 @@ namespace segfault::renderer { VkPipeline graphicsPipeline{}; bool framebufferResized{false}; VkBuffer vertexBuffer{}; + + std::vector uniformBuffers{}; + std::vector uniformBuffersMemory{}; + std::vector uniformBuffersMapped{}; VkDeviceMemory vertexBufferMemory{}; VkBuffer indexBuffer{}; VkDeviceMemory indexBufferMemory{}; @@ -137,18 +151,23 @@ namespace segfault::renderer { void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory); void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size); void createRenderPass(); + void createDescriptorSetLayout(); void createGraphicsPipeline(); void createFramebuffers(); void createCommandPool(QueueFamilyIndices& indices); void createCommandBuffer(); void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex); void createSyncObjects(); + void updateUniformBuffer(uint32_t currentImage); void drawFrame(); void cleanupSwapChain(); void recreateSwapChain(); uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); void createVertexBuffer(); void createIndexBuffer(); + void createUniformBuffers(); + void createDescriptorPool(); + void createDescriptorSets(); }; static std::vector readFile(const std::string& filename) { @@ -561,6 +580,30 @@ namespace segfault::renderer { } } + void RHIImpl::createDescriptorSetLayout() { + VkDescriptorSetLayoutBinding uboLayoutBinding{}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + uboLayoutBinding.pImmutableSamplers = nullptr; // Optional + + VkDescriptorSetLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 1; + layoutInfo.pBindings = &uboLayoutBinding; + + if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("failed to create descriptor set layout!"); + } + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; + } + void RHIImpl::createGraphicsPipeline() { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); @@ -639,13 +682,14 @@ namespace segfault::renderer { rasterizer.lineWidth = 1.0f; rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; - rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rasterizer.depthBiasEnable = VK_FALSE; rasterizer.depthBiasConstantFactor = 0.0f; // Optional rasterizer.depthBiasClamp = 0.0f; // Optional rasterizer.depthBiasSlopeFactor = 0.0f; // Optional + VkPipelineMultisampleStateCreateInfo multisampling{}; multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.sampleShadingEnable = VK_FALSE; @@ -876,6 +920,8 @@ namespace segfault::renderer { vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT16); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[currentFrame], 0, nullptr); + //vkCmdDraw(commandBuffer, 3, 1, 0, 0); vkCmdDrawIndexed(commandBuffer, static_cast(indices.size()), 1, 0, 0, 0); @@ -909,6 +955,18 @@ namespace segfault::renderer { } } } + void RHIImpl::updateUniformBuffer(uint32_t currentImage) { + static auto startTime = std::chrono::high_resolution_clock::now(); + + auto currentTime = std::chrono::high_resolution_clock::now(); + float time = std::chrono::duration(currentTime - startTime).count(); + UniformBufferObject ubo{}; + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float)swapChainExtent.height, 0.1f, 10.0f); + ubo.proj[1][1] *= -1; + memcpy(uniformBuffersMapped[currentImage], &ubo, sizeof(ubo)); + } void RHIImpl::drawFrame() { vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); @@ -924,6 +982,8 @@ namespace segfault::renderer { core::logMessage(core::LogType::Error, "failed to acquire swap chain image!"); throw std::runtime_error("failed to acquire swap chain image!"); } + + updateUniformBuffer(currentFrame); vkResetFences(device, 1, &inFlightFences[currentFrame]); vkResetCommandBuffer(commandBuffers[currentFrame], 0); @@ -1050,6 +1110,73 @@ namespace segfault::renderer { vkFreeMemory(device, stagingBufferMemory, nullptr); } + void RHIImpl::createUniformBuffers() { + VkDeviceSize bufferSize = sizeof(UniformBufferObject); + + uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT); + uniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT); + uniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffers[i], uniformBuffersMemory[i]); + + vkMapMemory(device, uniformBuffersMemory[i], 0, bufferSize, 0, &uniformBuffersMapped[i]); + } + } + + void RHIImpl::createDescriptorPool() { + VkDescriptorPoolSize poolSize{}; + poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSize.descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); + + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 1; + poolInfo.pPoolSizes = &poolSize; + + poolInfo.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); + + if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { + throw std::runtime_error("failed to create descriptor pool!"); + } + } + + void RHIImpl::createDescriptorSets() { + std::vector layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); + VkDescriptorSetAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); + allocInfo.pSetLayouts = layouts.data(); + + descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); + if (vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate descriptor sets!"); + } + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + VkDescriptorBufferInfo bufferInfo{}; + bufferInfo.buffer = uniformBuffers[i]; + bufferInfo.offset = 0; + bufferInfo.range = sizeof(UniformBufferObject); + + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = descriptorSets[i]; + descriptorWrite.dstBinding = 0; + descriptorWrite.dstArrayElement = 0; + + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorCount = 1; + + descriptorWrite.pBufferInfo = &bufferInfo; + descriptorWrite.pImageInfo = nullptr; // Optional + descriptorWrite.pTexelBufferView = nullptr; // Optional + + vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr); + } + } + RHI::RHI() : mImpl(nullptr) { // empty } @@ -1127,6 +1254,10 @@ namespace segfault::renderer { mImpl->createSwapChain(); mImpl->createImageViews(); mImpl->createRenderPass(); + mImpl->createDescriptorSetLayout(); + mImpl->createUniformBuffers(); + mImpl->createDescriptorPool(); + mImpl->createDescriptorSets(); mImpl->createGraphicsPipeline(); mImpl->createFramebuffers(); mImpl->createCommandPool(mImpl->queueFamilyIndices); @@ -1140,6 +1271,12 @@ namespace segfault::renderer { bool RHI::shutdown() { mImpl->cleanupSwapChain(); + for (size_t i = 0; i < RHIImpl::MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroyBuffer(mImpl->device, mImpl->uniformBuffers[i], nullptr); + vkFreeMemory(mImpl->device, mImpl->uniformBuffersMemory[i], nullptr); + } + vkDestroyDescriptorPool(mImpl->device, mImpl->descriptorPool, nullptr); + vkDestroyDescriptorSetLayout(mImpl->device, mImpl->descriptorSetLayout, nullptr); vkDestroyBuffer(mImpl->device, mImpl->vertexBuffer, nullptr); vkFreeMemory(mImpl->device, mImpl->vertexBufferMemory, nullptr); diff --git a/src/runtime/renderer/rendercore.h b/src/runtime/renderer/rendercore.h index c6d1cbe..dc1df0e 100644 --- a/src/runtime/renderer/rendercore.h +++ b/src/runtime/renderer/rendercore.h @@ -22,7 +22,7 @@ namespace segfault::renderer { }; struct DrawCommand { - RenderPipelineState rpState; + //RenderPipelineState rpState; uint32_t numVertices = 0; uint32_t numInstances = 0; From cafaa2ba6e053e1a24f3f3fcd1ae2a03c1b19fab Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 25 Nov 2025 21:54:06 +0100 Subject: [PATCH 2/2] Fix setup of pipeline layout --- src/runtime/renderer/RHIVulkan.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/runtime/renderer/RHIVulkan.cpp b/src/runtime/renderer/RHIVulkan.cpp index 1bf88c6..4583177 100644 --- a/src/runtime/renderer/RHIVulkan.cpp +++ b/src/runtime/renderer/RHIVulkan.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -597,11 +596,6 @@ namespace segfault::renderer { if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor set layout!"); } - - VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; } void RHIImpl::createGraphicsPipeline() { @@ -720,12 +714,13 @@ namespace segfault::renderer { colorBlending.blendConstants[2] = 0.0f; // Optional colorBlending.blendConstants[3] = 0.0f; // Optional - VkPipelineLayout pipelineLayout; + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 0; // Optional - pipelineLayoutInfo.pSetLayouts = nullptr; // Optional - pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; + + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.pPushConstantRanges = nullptr; // Optional if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { @@ -922,7 +917,6 @@ namespace segfault::renderer { vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[currentFrame], 0, nullptr); - //vkCmdDraw(commandBuffer, 3, 1, 0, 0); vkCmdDrawIndexed(commandBuffer, static_cast(indices.size()), 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffer);