From d37336bd5b6a0a6f4c63d279529ea5378837191f Mon Sep 17 00:00:00 2001 From: Draklaw Date: Tue, 15 Feb 2022 00:41:06 +0100 Subject: [PATCH] Uniform buffers. --- shaders/shader.vert | 12 +- src/Vulkan/Context.cpp | 16 +++ src/Vulkan/Context.h | 2 + src/VulkanTutorial.cpp | 243 +++++++++++++++++++++++++++++++++++++++-- src/VulkanTutorial.h | 21 +++- src/main.cpp | 2 - src/utils.cpp | 36 ++++++ src/utils.h | 9 ++ 8 files changed, 329 insertions(+), 12 deletions(-) diff --git a/shaders/shader.vert b/shaders/shader.vert index 9f27f54..4b6e4e5 100644 --- a/shaders/shader.vert +++ b/shaders/shader.vert @@ -1,11 +1,19 @@ #version 450 -layout(location = 0) in vec2 inPosition; +layout(binding = 0) uniform Uniforms { + mat4 scene_from_model; + mat4 projection_from_scene; +} uniforms; + +layout(location = 0) in vec3 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 = + uniforms.projection_from_scene * + uniforms.scene_from_model * + vec4(inPosition, 1.0); fragColor = inColor; } diff --git a/src/Vulkan/Context.cpp b/src/Vulkan/Context.cpp index 921475f..c84adfc 100644 --- a/src/Vulkan/Context.cpp +++ b/src/Vulkan/Context.cpp @@ -537,6 +537,14 @@ void Context::destroy_command_pool(VkCommandPool& command_pool) { command_pool = VK_NULL_HANDLE; } +void Context::destroy_descriptor_pool(VkDescriptorPool& descriptor_pool) { + if(descriptor_pool == VK_NULL_HANDLE) + return; + + vkDestroyDescriptorPool(m_device, descriptor_pool, nullptr); + descriptor_pool = VK_NULL_HANDLE; +} + void Context::destroy_render_pass(VkRenderPass& render_pass) { if(render_pass == VK_NULL_HANDLE) return; @@ -545,6 +553,14 @@ void Context::destroy_render_pass(VkRenderPass& render_pass) { render_pass = VK_NULL_HANDLE; } +void Context::destroy_descriptor_set_layout(VkDescriptorSetLayout& descriptor_set_layout) { + if(descriptor_set_layout == VK_NULL_HANDLE) + return; + + vkDestroyDescriptorSetLayout(m_device, descriptor_set_layout, nullptr); + descriptor_set_layout = VK_NULL_HANDLE; +} + void Context::destroy_pipeline_layout(VkPipelineLayout& pipeline_layout) { if(pipeline_layout == VK_NULL_HANDLE) return; diff --git a/src/Vulkan/Context.h b/src/Vulkan/Context.h index 69fb317..eafa545 100644 --- a/src/Vulkan/Context.h +++ b/src/Vulkan/Context.h @@ -128,8 +128,10 @@ public: void destroy_buffer(VkBuffer& buffer); void destroy_command_pool(VkCommandPool& command_pool); + void destroy_descriptor_pool(VkDescriptorPool& descriptor_pool); void destroy_render_pass(VkRenderPass& render_pass); + void destroy_descriptor_set_layout(VkDescriptorSetLayout& descriptor_set_layout); void destroy_pipeline_layout(VkPipelineLayout& pipeline_layout); void destroy_pipeline(VkPipeline& pipeline); diff --git a/src/VulkanTutorial.cpp b/src/VulkanTutorial.cpp index 5a29d98..63fd517 100644 --- a/src/VulkanTutorial.cpp +++ b/src/VulkanTutorial.cpp @@ -5,6 +5,8 @@ #include +#include + #include #include #include @@ -26,7 +28,7 @@ std::array Vertex::attributes_description( VkVertexInputAttributeDescription { .location = 0, .binding = 0, - .format = VK_FORMAT_R32G32_SFLOAT, + .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(Vertex, position), }, { @@ -40,15 +42,40 @@ std::array Vertex::attributes_description( const std::vector 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}}, + {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}}, + {{0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}}, + {{-0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}}, + {{0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}}, + + {{-0.5f, -0.5f, 0.5f}, {0.0f, 1.0f, 1.0f}}, + {{0.5f, -0.5f, 0.5f}, {1.0f, 0.0f, 1.0f}}, + {{-0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 0.0f}}, + {{0.5f, 0.5f, 0.5f}, {0.2f, 0.2f, 0.2f}}, }; const std::vector indices = { 0, 1, 2, 2, 1, 3, + + 1, 5, 3, + 3, 5, 7, + + 5, 4, 7, + 7, 4, 6, + + 4, 0, 6, + 6, 0, 2, + + 0, 4, 1, + 1, 4, 5, + + 2, 3, 6, + 6, 3, 7, +}; + +struct Uniforms { + alignas(16) Eigen::Matrix4f scene_from_model; + alignas(16) Eigen::Matrix4f projection_from_scene; }; @@ -73,6 +100,7 @@ void VulkanTutorial::initialize(SDL_Window* window) { m_context.initialize(context_settings); create_command_pool(); + create_descriptor_set_layout(); create_vertex_buffer(); create_index_buffer(); @@ -95,6 +123,7 @@ void VulkanTutorial::shutdown() { m_context.destroy_buffer(m_index_buffer); m_context.free_memory(m_vertex_buffer_memory); m_context.destroy_buffer(m_vertex_buffer); + m_context.destroy_descriptor_set_layout(m_descriptor_set_layout); for(VkSemaphore semaphore: m_render_done) m_context.destroy_semaphore(semaphore); @@ -108,6 +137,57 @@ void VulkanTutorial::draw_frame() { m_swapchain.begin_frame(); auto const image_index = m_swapchain.current_image_index(); + const auto now = Clock::now(); + if(m_last_frame_time.time_since_epoch() != Duration(0)) { + m_time += now - m_last_frame_time; + } + m_last_frame_time = now; + + const float alpha = SecondsD(m_time).count() * (2.0 * M_PI) / 3.0; + const float dist = 2.0f; + const Eigen::Matrix4f view = look_at_matrix( + // Eigen::Vector3f(0.0f, 0.0f, -dist), + Eigen::Vector3f(0.0f, -dist, -dist), + // dist * Eigen::Vector3f(std::cos(alpha), std::sin(alpha), -1.0), + Eigen::Vector3f::Zero(), + -Eigen::Vector3f::UnitY() + ); + + const float fov = M_PI / 3.0f; + const float near = 0.1f; + const float far = 10.0f; + const float hx = near * std::tan(fov / 2.0f); + const float hy = hx * m_swapchain.extent().height / m_swapchain.extent().width; + const Eigen::Matrix4f proj = projection_matrix( + -hx, hx, + -hy, hy, + near, far + ); + + using Transform = Eigen::Transform; + Transform model = Transform::Identity(); + model.rotate(Eigen::AngleAxisf( + alpha, + Eigen::Vector3f::UnitY() + )); + + const Uniforms uniforms = { + .scene_from_model = model.matrix(), + .projection_from_scene = proj * view, + }; + + void* uniform_buffer; + vkMapMemory( + m_context.device(), + m_uniform_buffer_memory, + m_uniform_buffer_offsets[image_index], + sizeof(Uniforms), + 0, + &uniform_buffer + ); + std::memcpy(uniform_buffer, &uniforms, sizeof(Uniforms)); + vkUnmapMemory(m_context.device(), m_uniform_buffer_memory); + VkSemaphore wait_semaphores[] = { m_swapchain.ready_to_render(), }; @@ -142,6 +222,9 @@ void VulkanTutorial::invalidate_swapchain() { void VulkanTutorial::create_swapchain_objects(uint32_t image_count) { create_render_pass(); create_framebuffers(); + create_uniform_buffer(); + create_descriptor_pool(); + create_descriptor_sets(); create_graphic_pipeline(); create_command_buffers(); @@ -174,6 +257,13 @@ void VulkanTutorial::destroy_swapchain_objects() { ); m_command_buffers.clear(); + for(VkBuffer buffer: m_uniform_buffers) + m_context.destroy_buffer(buffer); + m_uniform_buffers.clear(); + m_context.free_memory(m_uniform_buffer_memory); + + m_context.destroy_descriptor_pool(m_descriptor_pool); + for(VkFramebuffer framebuffer: m_framebuffers) m_context.destroy_framebuffer(framebuffer); m_framebuffers.clear(); @@ -234,6 +324,28 @@ void VulkanTutorial::create_render_pass() { throw std::runtime_error("failed to create render pass"); } +void VulkanTutorial::create_descriptor_set_layout() { + VkDescriptorSetLayoutBinding binding { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + }; + + VkDescriptorSetLayoutCreateInfo layout_info { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = 1, + .pBindings = &binding, + }; + if(vkCreateDescriptorSetLayout( + m_context.device(), + &layout_info, + nullptr, + &m_descriptor_set_layout + ) != VK_SUCCESS) + throw std::runtime_error("failed to create descriptor set layout"); +} + void VulkanTutorial::create_graphic_pipeline() { auto const vertex_shader_module = m_context.create_shader_module_from_file("shaders/shader.vert.spv"); @@ -353,8 +465,8 @@ void VulkanTutorial::create_graphic_pipeline() { VkPipelineLayoutCreateInfo layout_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .setLayoutCount = 0, - .pSetLayouts = nullptr, + .setLayoutCount = 1, + .pSetLayouts = &m_descriptor_set_layout, .pushConstantRangeCount = 0, .pPushConstantRanges = nullptr, }; @@ -467,6 +579,115 @@ void VulkanTutorial::create_index_buffer() { ); } +void VulkanTutorial::create_uniform_buffer() { + m_uniform_buffers.assign(m_swapchain.image_count(), VK_NULL_HANDLE); + for(size_t index = 0; index < m_uniform_buffers.size(); index += 1) { + VkBufferCreateInfo buffer_info { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = sizeof(Uniforms), + .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + }; + + if(vkCreateBuffer( + m_context.device(), + &buffer_info, + nullptr, + &m_uniform_buffers[index] + ) != VK_SUCCESS) + throw std::runtime_error("failed to create buffer"); + } + + VkMemoryRequirements memory_requirements; + vkGetBufferMemoryRequirements( + m_context.device(), + m_uniform_buffers[0], + &memory_requirements + ); + + m_uniform_buffer_memory = m_context.allocate_memory( + memory_requirements.size * m_swapchain.image_count(), + memory_requirements.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT + | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + ); + + m_uniform_buffer_offsets.resize(m_uniform_buffers.size()); + for(size_t index = 0; index < m_uniform_buffers.size(); index += 1) { + m_uniform_buffer_offsets[index] = index * memory_requirements.size; + vkBindBufferMemory( + m_context.device(), + m_uniform_buffers[index], + m_uniform_buffer_memory, + m_uniform_buffer_offsets[index] + ); + } +} + +void VulkanTutorial::create_descriptor_pool() { + VkDescriptorPoolSize pool_size { + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = uint32_t(m_swapchain.image_count()), + }; + + VkDescriptorPoolCreateInfo pool_info { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .maxSets = uint32_t(m_swapchain.image_count()), + .poolSizeCount = 1, + .pPoolSizes = &pool_size, + }; + if(vkCreateDescriptorPool( + m_context.device(), + &pool_info, + nullptr, + &m_descriptor_pool + ) != VK_SUCCESS) + throw std::runtime_error("failed to create descriptor pool"); +} + +void VulkanTutorial::create_descriptor_sets() { + const std::vector layouts( + m_swapchain.image_count(), m_descriptor_set_layout); + + VkDescriptorSetAllocateInfo alloc_info { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = m_descriptor_pool, + .descriptorSetCount = uint32_t(layouts.size()), + .pSetLayouts = layouts.data(), + }; + + m_descriptor_sets.resize(m_swapchain.image_count()); + if(vkAllocateDescriptorSets( + m_context.device(), + &alloc_info, + m_descriptor_sets.data() + ) != VK_SUCCESS) + throw std::runtime_error("failed to allocate descriptor sets"); + + for(size_t index = 0; index < m_swapchain.image_count(); index += 1) { + VkDescriptorBufferInfo buffer_info { + .buffer = m_uniform_buffers[index], + .offset = 0, + .range = sizeof(Uniforms), + }; + + VkWriteDescriptorSet write { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = m_descriptor_sets[index], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .pBufferInfo = &buffer_info, + }; + vkUpdateDescriptorSets( + m_context.device(), + 1, &write, + 0, nullptr + ); + } +} + void VulkanTutorial::create_command_buffers() { m_command_buffers.resize(m_framebuffers.size()); @@ -545,6 +766,14 @@ void VulkanTutorial::create_command_buffers() { offsets ); + vkCmdBindDescriptorSets( + command_buffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + m_pipeline_layout, + 0, 1, &m_descriptor_sets[index], + 0, nullptr + ); + vkCmdBindIndexBuffer( command_buffer, m_index_buffer, diff --git a/src/VulkanTutorial.h b/src/VulkanTutorial.h index 9225496..78d3bd5 100644 --- a/src/VulkanTutorial.h +++ b/src/VulkanTutorial.h @@ -7,10 +7,11 @@ #include #include #include +#include struct Vertex { - Eigen::Vector2f position; + Eigen::Vector3f position; Eigen::Vector3f color; static VkVertexInputBindingDescription binding_description(); @@ -24,6 +25,11 @@ public: GRAPHIC_QUEUE, }; + using Clock = std::chrono::high_resolution_clock; + using TimePoint = Clock::time_point; + using Duration = Clock::duration; + using SecondsD = std::chrono::duration; + public: VulkanTutorial(); VulkanTutorial(const VulkanTutorial&) = delete; @@ -42,11 +48,15 @@ private: void destroy_swapchain_objects(); void create_render_pass(); + void create_descriptor_set_layout(); void create_graphic_pipeline(); void create_framebuffers(); void create_command_pool(); void create_vertex_buffer(); void create_index_buffer(); + void create_uniform_buffer(); + void create_descriptor_pool(); + void create_descriptor_sets(); void create_command_buffers(); private: @@ -56,6 +66,7 @@ private: VkQueue m_presentation_queue = nullptr; VkRenderPass m_render_pass = VK_NULL_HANDLE; + VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE; VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE; VkPipeline m_pipeline = VK_NULL_HANDLE; std::vector m_framebuffers; @@ -64,6 +75,14 @@ private: VkDeviceMemory m_vertex_buffer_memory = VK_NULL_HANDLE; VkBuffer m_index_buffer = VK_NULL_HANDLE; VkDeviceMemory m_index_buffer_memory = VK_NULL_HANDLE; + std::vector m_uniform_buffers; + std::vector m_uniform_buffer_offsets; + VkDeviceMemory m_uniform_buffer_memory = VK_NULL_HANDLE; + VkDescriptorPool m_descriptor_pool = VK_NULL_HANDLE; + std::vector m_descriptor_sets; std::vector m_command_buffers; std::vector m_render_done; + + TimePoint m_last_frame_time; + Duration m_time = Duration(0); }; diff --git a/src/main.cpp b/src/main.cpp index d7dd1cd..2a541b1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,6 @@ #include #include -#include - int main(int argc, char** argv) { VkExpe vk_expe(argc, argv); diff --git a/src/utils.cpp b/src/utils.cpp index a5946f9..bfa7670 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -23,3 +23,39 @@ std::vector read_binary_file(const char* path) { return buffer; } + +Eigen::Matrix4f projection_matrix(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max) { + auto const cx = x_max + x_min; + auto const cy = y_max + y_min; + auto const cz = z_max + z_min; + + auto const dx = x_max - x_min; + auto const dy = y_max - y_min; + auto const dz = z_max - z_min; + + auto const pz = z_max * z_min; + + return (Eigen::Matrix4f() << + 2.0f * z_min / dx, 0.0f, -cx / dx, 0.0f, + 0.0f, 2.0f * z_min / dy, -cy / dy, 0.0f, + 0.0f, 0.0f, cz / dz, -2.0f * pz / dz, + 0.0f, 0.0f, 1.0f, 0.0f + ).finished(); +} + +Eigen::Matrix4f look_at_matrix( + const Eigen::Vector3f& cam_pos, + const Eigen::Vector3f& look_at, + const Eigen::Vector3f& up +) { + const Eigen::Vector3f z = (look_at - cam_pos).normalized(); + const Eigen::Vector3f x = z.cross(up).normalized(); + const Eigen::Vector3f y = z.cross(x).normalized(); + + const auto view_linear = (Eigen::Matrix3f() << x, y, z).finished().transpose(); + return (Eigen::Matrix4f() << + view_linear, + view_linear * (-cam_pos), + Eigen::Vector4f::UnitW().transpose() + ).finished(); +} diff --git a/src/utils.h b/src/utils.h index 3e14689..4c00743 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -74,3 +76,10 @@ private: size_t m_size = 0; T* m_data = nullptr; }; + +Eigen::Matrix4f projection_matrix(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max); +Eigen::Matrix4f look_at_matrix( + const Eigen::Vector3f& cam_pos, + const Eigen::Vector3f& look_at, + const Eigen::Vector3f& up +);