diff --git a/src/main.cpp b/src/main.cpp index ed7c820..9d1649f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,13 @@ +#include #include #include -#include #include #include +#define GLM_FORCE_RADIANS +#include +#include + #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 #define VK_ENABLE_BETA_EXTENSIONS #define VULKAN_HPP_NO_CONSTRUCTORS @@ -21,42 +25,6 @@ constexpr i32 HEIGHT = 600; constexpr i32 MAX_FRAMES_IN_FLIGHT = 2; -struct Vertex { - glm::vec2 pos; - glm::vec3 color; - - static fn getBindingDescription() -> vk::VertexInputBindingDescription { - vk::VertexInputBindingDescription bindingDescription { .binding = 0, - .stride = sizeof(Vertex), - .inputRate = vk::VertexInputRate::eVertex }; - - return bindingDescription; - } - - static fn getAttributeDescriptions() -> std::array { - std::array attributeDescriptions {}; - - attributeDescriptions[0] = { - .location = 0, .binding = 0, .format = vk::Format::eR32G32Sfloat, .offset = offsetof(Vertex, pos) - }; - - attributeDescriptions[1] = { - .location = 1, .binding = 0, .format = vk::Format::eR32G32B32Sfloat, .offset = offsetof(Vertex, color) - }; - - return attributeDescriptions; - } -}; - -constexpr std::array vertices = { - { { { -0.5F, -0.5F }, { 1.0F, 0.0F, 0.0F } }, - { { 0.5F, -0.5F }, { 0.0F, 1.0F, 0.0F } }, - { { 0.5F, 0.5F }, { 0.0F, 0.0F, 1.0F } }, - { { -0.5F, 0.5F }, { 1.0F, 1.0F, 1.0F } } } -}; - -constexpr std::array indices = { 0, 1, 2, 2, 3, 0 }; - constexpr std::array validationLayers = { "VK_LAYER_KHRONOS_validation" }; #ifdef __APPLE__ @@ -72,6 +40,31 @@ constexpr bool enableValidationLayers = false; constexpr bool enableValidationLayers = true; #endif +struct Vertex { + glm::vec2 pos; + glm::vec3 color; + + static fn getBindingDescription() -> vk::VertexInputBindingDescription { + return { .binding = 0, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex }; + } + + static fn getAttributeDescriptions() -> std::array { + return { + vk::VertexInputAttributeDescription { 0, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, pos) }, + vk::VertexInputAttributeDescription { 1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color) } + }; + } +}; + +constexpr std::array vertices = { + { { { -0.5F, -0.5F }, { 1.0F, 0.0F, 0.0F } }, + { { 0.5F, -0.5F }, { 0.0F, 1.0F, 0.0F } }, + { { 0.5F, 0.5F }, { 0.0F, 0.0F, 1.0F } }, + { { -0.5F, 0.5F }, { 1.0F, 1.0F, 1.0F } } } +}; + +constexpr std::array indices = { 0, 1, 2, 2, 3, 0 }; + class VulkanApp { public: fn run() -> void { @@ -102,22 +95,31 @@ class VulkanApp { std::vector mSwapChainImageViews; std::vector mSwapChainFramebuffers; - vk::UniqueRenderPass mRenderPass; - vk::UniquePipelineLayout mPipelineLayout; - vk::UniquePipeline mGraphicsPipeline; + vk::UniqueRenderPass mRenderPass; + vk::UniqueDescriptorSetLayout mDescriptorSetLayout; + vk::UniquePipelineLayout mPipelineLayout; + vk::UniquePipeline mGraphicsPipeline; - vk::UniqueCommandPool mCommandPool; - std::vector mCommandBuffers; - - std::vector mImageAvailableSemaphores; - std::vector mRenderFinishedSemaphores; - std::vector mInFlightFences; + vk::UniqueCommandPool mCommandPool; vk::UniqueBuffer mVertexBuffer; vk::UniqueDeviceMemory mVertexBufferMemory; vk::UniqueBuffer mIndexBuffer; vk::UniqueDeviceMemory mIndexBufferMemory; + std::vector mUniformBuffers; + std::vector mUniformBuffersMemory; + std::vector mUniformBuffersMapped; + + vk::UniqueDescriptorPool mDescriptorPool; + std::vector mDescriptorSets; + + std::vector mCommandBuffers; + + std::vector mImageAvailableSemaphores; + std::vector mRenderFinishedSemaphores; + std::vector mInFlightFences; + bool mFramebufferResized = false; u32 mCurrentFrame = 0; @@ -134,6 +136,12 @@ class VulkanApp { std::vector present_modes; }; + struct UniformBufferObject { + alignas(16) glm::mat4 model; + alignas(16) glm::mat4 view; + alignas(16) glm::mat4 proj; + }; + static fn readFile(const std::string& filename) -> std::vector { std::ifstream file(filename, std::ios::ate | std::ios::binary); @@ -178,11 +186,15 @@ class VulkanApp { createSwapChain(); createImageViews(); createRenderPass(); + createDescriptorSetLayout(); createGraphicsPipeline(); createFramebuffers(); createCommandPool(); createVertexBuffer(); createIndexBuffer(); + createUniformBuffers(); + createDescriptorPool(); + createDescriptorSets(); createCommandBuffers(); createSyncObjects(); } @@ -445,6 +457,23 @@ class VulkanApp { mRenderPass = mDevice->createRenderPassUnique(renderPassInfo); } + fn createDescriptorSetLayout() -> void { + vk::DescriptorSetLayoutBinding uboLayoutBinding { + .binding = 0, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eVertex, + .pImmutableSamplers = nullptr, + }; + + vk::DescriptorSetLayoutCreateInfo layoutInfo { + .bindingCount = 1, + .pBindings = &uboLayoutBinding, + }; + + mDescriptorSetLayout = mDevice->createDescriptorSetLayoutUnique(layoutInfo); + } + fn createGraphicsPipeline() -> void { std::vector vertShaderCode = readFile("src/shaders/vert.spv"); std::vector fragShaderCode = readFile("src/shaders/frag.spv"); @@ -493,7 +522,7 @@ class VulkanApp { .rasterizerDiscardEnable = vk::False, .polygonMode = vk::PolygonMode::eFill, .cullMode = vk::CullModeFlagBits::eBack, - .frontFace = vk::FrontFace::eClockwise, + .frontFace = vk::FrontFace::eCounterClockwise, .depthBiasEnable = vk::False, .lineWidth = 1.0F, }; @@ -516,6 +545,7 @@ class VulkanApp { .pAttachments = &colorBlendAttachment, .blendConstants = std::array { 0.0F, 0.0F, 0.0F, 0.0F }, }; + std::vector dynamicStates = { vk::DynamicState::eViewport, vk::DynamicState::eScissor }; vk::PipelineDynamicStateCreateInfo dynamicState { @@ -524,8 +554,8 @@ class VulkanApp { }; vk::PipelineLayoutCreateInfo pipelineLayoutInfo { - .setLayoutCount = 0, - .pushConstantRangeCount = 0, + .setLayoutCount = 1, + .pSetLayouts = &mDescriptorSetLayout.get(), }; mPipelineLayout = mDevice->createPipelineLayoutUnique(pipelineLayoutInfo); @@ -593,7 +623,7 @@ class VulkanApp { createBuffer( bufferSize, - vk::BufferUsageFlagBits::eVertexBuffer, + vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory @@ -649,6 +679,72 @@ class VulkanApp { stagingBufferMemory.reset(); } + fn createUniformBuffers() -> void { + vk::DeviceSize bufferSize = sizeof(UniformBufferObject); + + mUniformBuffers.resize(MAX_FRAMES_IN_FLIGHT); + mUniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT); + mUniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT); + + for (usize i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + createBuffer( + bufferSize, + vk::BufferUsageFlagBits::eUniformBuffer, + vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, + mUniformBuffers[i], + mUniformBuffersMemory[i] + ); + + mUniformBuffersMapped[i] = mDevice->mapMemory(mUniformBuffersMemory[i].get(), 0, bufferSize); + } + } + + fn createDescriptorPool() -> void { + vk::DescriptorPoolSize poolSize { + .type = vk::DescriptorType::eUniformBuffer, + .descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT), + }; + + vk::DescriptorPoolCreateInfo poolInfo { + .maxSets = static_cast(MAX_FRAMES_IN_FLIGHT), + .poolSizeCount = 1, + .pPoolSizes = &poolSize, + }; + + mDescriptorPool = mDevice->createDescriptorPoolUnique(poolInfo); + } + + fn createDescriptorSets() -> void { + std::vector layouts(MAX_FRAMES_IN_FLIGHT, mDescriptorSetLayout.get()); + + vk::DescriptorSetAllocateInfo allocInfo { + .descriptorPool = mDescriptorPool.get(), + .descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT), + .pSetLayouts = layouts.data(), + }; + + mDescriptorSets = mDevice->allocateDescriptorSets(allocInfo); + + for (usize i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vk::DescriptorBufferInfo bufferInfo { + .buffer = mUniformBuffers[i].get(), + .offset = 0, + .range = sizeof(UniformBufferObject), + }; + + vk::WriteDescriptorSet descriptorWrite { + .dstSet = mDescriptorSets[i], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &bufferInfo, + }; + + mDevice->updateDescriptorSets(1, &descriptorWrite, 0, nullptr); + } + } + fn createBuffer( vk::DeviceSize deviceSize, vk::BufferUsageFlags bufferUsageFlags, @@ -762,6 +858,16 @@ class VulkanApp { commandBuffer.bindIndexBuffer(mIndexBuffer.get(), 0, vk::IndexType::eUint16); + commandBuffer.bindDescriptorSets( + vk::PipelineBindPoint::eGraphics, + mPipelineLayout.get(), + 0, + 1, + &mDescriptorSets[mCurrentFrame], + 0, + nullptr + ); + commandBuffer.drawIndexed(static_cast(indices.size()), 1, 0, 0, 0); commandBuffer.endRenderPass(); @@ -784,6 +890,30 @@ class VulkanApp { } } + fn updateUniformBuffer(u32 currentImage) -> void { + static auto StartTime = std::chrono::high_resolution_clock::now(); + + auto currentTime = std::chrono::high_resolution_clock::now(); + f32 time = std::chrono::duration(currentTime - StartTime).count(); + + UniformBufferObject ubo { + .model = glm::rotate(glm::mat4(1.0F), time * glm::radians(90.0F), glm::vec3(0.0F, 0.0F, 1.0F)), + .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)), + .proj = glm::perspective( + glm::radians(45.0F), + static_cast(mSwapChainExtent.width) / static_cast(mSwapChainExtent.height), + 0.1F, + 10.0F + ) + }; + + // Flip the Y axis, because glm was designed for OpenGL + ubo.proj[1][1] *= -1; + + memcpy(mUniformBuffersMapped[currentImage], &ubo, sizeof(ubo)); + } + fn drawFrame() -> void { try { vk::Result result = @@ -807,6 +937,8 @@ class VulkanApp { if (imageIndexResult != vk::Result::eSuccess && imageIndexResult != vk::Result::eSuboptimalKHR) throw std::runtime_error("Failed to acquire swap chain image!"); + updateUniformBuffer(mCurrentFrame); + mDevice->resetFences(mInFlightFences[mCurrentFrame].get()); mCommandBuffers[mCurrentFrame]->reset(vk::CommandBufferResetFlagBits::eReleaseResources); diff --git a/src/shaders/shader.vert b/src/shaders/shader.vert index 9f27f54..be77714 100644 --- a/src/shaders/shader.vert +++ b/src/shaders/shader.vert @@ -1,5 +1,11 @@ #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; diff --git a/src/shaders/vert.spv b/src/shaders/vert.spv index 1e4ce0c..173ee74 100644 Binary files a/src/shaders/vert.spv and b/src/shaders/vert.spv differ