From d299d1bc7a09e3cebdeaa909d7d428d02ef7e429 Mon Sep 17 00:00:00 2001 From: Austin Eng <213reeses@gmail.com> Date: Tue, 24 Oct 2017 22:52:58 -0400 Subject: [PATCH] Implement uniform buffers --- samples/sample/camera.cpp | 51 ++++++++ samples/sample/camera.h | 24 ++++ samples/sample/main.cpp | 195 ++++++++++++++++++++++++++++- samples/sample/shaders/shader.vert | 11 +- 4 files changed, 276 insertions(+), 5 deletions(-) create mode 100644 samples/sample/camera.cpp create mode 100644 samples/sample/camera.h diff --git a/samples/sample/camera.cpp b/samples/sample/camera.cpp new file mode 100644 index 0000000..0ca89ad --- /dev/null +++ b/samples/sample/camera.cpp @@ -0,0 +1,51 @@ +#include "camera.h" +#include + +Camera::Camera(glm::mat4* view) + : _azimuth(glm::radians(-90.f)), + _altitude(glm::radians(0.f)), + _radius(5.f), + _center(0, 0, 0), + _dirty(true), + _view(*view) { + recalculate(); +} + +void Camera::rotate(float dAzimuth, float dAltitude) { + _dirty = true; + _azimuth = glm::mod(_azimuth + dAzimuth, glm::radians(360.f)); + _altitude = glm::clamp(_altitude + dAltitude, glm::radians(-89.f), glm::radians(89.f)); +} + +void Camera::pan(float dX, float dY) { + recalculate(); + _dirty = true; + glm::vec3 vX = glm::normalize(glm::cross(-_eyeDir, glm::vec3(0, 1, 0))); + glm::vec3 vY = glm::normalize(glm::cross(_eyeDir, vX)); + _center += vX * dX * _radius + vY * dY * _radius; +} + +void Camera::zoom(float factor) { + _dirty = true; + _radius = _radius * glm::exp(-factor); +} + +const glm::mat4& Camera::view() { + if (_dirty) { + recalculate(); + } + return _view; +} + +void Camera::recalculate() { + if (_dirty) { + glm::vec4 eye4 = glm::vec4(1, 0, 0, 1); + glm::mat4 r1 = glm::rotate(glm::mat4(1.0), _altitude, glm::vec3(0, 0, 1)); + glm::mat4 r2 = glm::rotate(glm::mat4(1.0), _azimuth, glm::vec3(0, 1, 0)); + eye4 = r2 * r1 * eye4; + _eyeDir = glm::vec3(eye4); + + _view = glm::lookAt(_center + _eyeDir * _radius, _center, glm::vec3(0, 1, 0)); + _dirty = false; + } +} \ No newline at end of file diff --git a/samples/sample/camera.h b/samples/sample/camera.h new file mode 100644 index 0000000..5cbc779 --- /dev/null +++ b/samples/sample/camera.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class Camera { +public: + Camera() = delete; + Camera(glm::mat4* view); + void rotate(float dAzimuth, float dAltitude); + void pan(float dX, float dY); + void zoom(float factor); + const glm::mat4& view(); + +private: + void recalculate(); + + float _azimuth; + float _altitude; + float _radius; + glm::vec3 _center; + glm::vec3 _eyeDir; + bool _dirty; + glm::mat4& _view; +}; \ No newline at end of file diff --git a/samples/sample/main.cpp b/samples/sample/main.cpp index 5adad59..e5e976f 100644 --- a/samples/sample/main.cpp +++ b/samples/sample/main.cpp @@ -1,19 +1,66 @@ #include #include +#include +#include #include "window.h" #include "vulkan_instance.h" #include "vulkan_shader_module.h" #include "vulkan_buffer.h" +#include "camera.h" +#include VulkanInstance* instance; VulkanDevice* device; VulkanSwapChain* swapchain; +Camera* camera; +glm::mat4* mappedCameraView; + +namespace { + bool buttons[GLFW_MOUSE_BUTTON_LAST + 1] = { 0 }; + + void mouseButtonCallback(GLFWwindow*, int button, int action, int) { + buttons[button] = (action == GLFW_PRESS); + } + + void cursorPosCallback(GLFWwindow*, double mouseX, double mouseY) { + static double oldX, oldY; + float dX = static_cast(mouseX - oldX); + float dY = static_cast(mouseY - oldY); + oldX = mouseX; + oldY = mouseY; + + if (buttons[2] || (buttons[0] && buttons[1])) { + camera->pan(-dX * 0.002f, dY * -0.002f); + memcpy(mappedCameraView, &camera->view(), sizeof(glm::mat4)); + } else if (buttons[0]) { + camera->rotate(dX * -0.01f, dY * -0.01f); + memcpy(mappedCameraView, &camera->view(), sizeof(glm::mat4)); + } else if (buttons[1]) { + camera->zoom(dY * -0.005f); + memcpy(mappedCameraView, &camera->view(), sizeof(glm::mat4)); + } + } + + void scrollCallback(GLFWwindow*, double, double yoffset) { + camera->zoom(static_cast(yoffset) * 0.04f); + memcpy(mappedCameraView, &camera->view(), sizeof(glm::mat4)); + } +} struct Vertex { float position[3]; float color[3]; }; +struct CameraUBO { + glm::mat4 viewMatrix; + glm::mat4 projectionMatrix; +}; + +struct ModelUBO { + glm::mat4 modelMatrix; +}; + VkRenderPass CreateRenderPass() { // Color buffer attachment represented by one of the images from the swap chain VkAttachmentDescription colorAttachment = {}; @@ -64,11 +111,56 @@ VkRenderPass CreateRenderPass() { return renderPass; } -VkPipelineLayout CreatePipelineLayout() { +VkDescriptorSetLayout CreateDescriptorSetLayout(std::vector layoutBindings) { + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {}; + descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorSetLayoutCreateInfo.pNext = nullptr; + descriptorSetLayoutCreateInfo.bindingCount = static_cast(layoutBindings.size()); + descriptorSetLayoutCreateInfo.pBindings = layoutBindings.data(); + + VkDescriptorSetLayout descriptorSetLayout; + vkCreateDescriptorSetLayout(device->GetVulkanDevice(), &descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayout); + return descriptorSetLayout; +} + +VkDescriptorPool CreateDescriptorPool() { + // Info for the types of descriptors that can be allocated from this pool + VkDescriptorPoolSize poolSizes[2]; + poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSizes[0].descriptorCount = 1; + + poolSizes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSizes[1].descriptorCount = 1; + + VkDescriptorPoolCreateInfo descriptorPoolInfo = {}; + descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptorPoolInfo.pNext = nullptr; + descriptorPoolInfo.poolSizeCount = 2; + descriptorPoolInfo.pPoolSizes = poolSizes; + descriptorPoolInfo.maxSets = 2; + + VkDescriptorPool descriptorPool; + vkCreateDescriptorPool(device->GetVulkanDevice(), &descriptorPoolInfo, nullptr, &descriptorPool); + return descriptorPool; +} + +VkDescriptorSet CreateDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout) { + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &descriptorSetLayout; + + VkDescriptorSet descriptorSet; + vkAllocateDescriptorSets(device->GetVulkanDevice(), &allocInfo, &descriptorSet); + return descriptorSet; +} + +VkPipelineLayout CreatePipelineLayout(std::vector descriptorSetLayouts) { VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 0; - pipelineLayoutInfo.pSetLayouts = nullptr; + pipelineLayoutInfo.setLayoutCount = static_cast(descriptorSetLayouts.size()); + pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data(); pipelineLayoutInfo.pushConstantRangeCount = 0; pipelineLayoutInfo.pPushConstantRanges = 0; @@ -285,6 +377,13 @@ int main(int argc, char** argv) { throw std::runtime_error("Failed to create command pool"); } + CameraUBO cameraTransforms; + camera = new Camera(&cameraTransforms.viewMatrix); + cameraTransforms.projectionMatrix = glm::perspective(static_cast(45 * M_PI / 180), 640.f / 480.f, 0.1f, 1000.f); + + ModelUBO modelTransforms; + modelTransforms.modelMatrix = glm::rotate(glm::mat4(1.f), static_cast(15 * M_PI / 180), glm::vec3(0.f, 0.f, 1.f)); + std::vector vertices = { { { 0.5f, 0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f } }, { { -0.5f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f } }, @@ -314,8 +413,72 @@ int main(int argc, char** argv) { // Bind the memory to the buffers BindMemoryForBuffers(device, vertexBufferMemory, { vertexBuffer, indexBuffer }, vertexBufferOffsets); + // Create uniform buffers + VkBuffer cameraBuffer = CreateBuffer(device, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, sizeof(CameraUBO)); + VkBuffer modelBuffer = CreateBuffer(device, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, sizeof(ModelUBO)); + unsigned int uniformBufferOffsets[2]; + VkDeviceMemory uniformBufferMemory = AllocateMemoryForBuffers(device, { cameraBuffer, modelBuffer }, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBufferOffsets); + + // Copy data to uniform memory + { + char* data; + vkMapMemory(device->GetVulkanDevice(), uniformBufferMemory, 0, uniformBufferOffsets[1] + sizeof(ModelUBO), 0, reinterpret_cast(&data)); + memcpy(data + uniformBufferOffsets[0], &cameraTransforms, sizeof(CameraUBO)); + memcpy(data + uniformBufferOffsets[1], &modelTransforms, sizeof(ModelUBO)); + vkUnmapMemory(device->GetVulkanDevice(), uniformBufferMemory); + } + + // Bind the memory to the buffers + BindMemoryForBuffers(device, uniformBufferMemory, { cameraBuffer, modelBuffer }, uniformBufferOffsets); + + VkDescriptorPool descriptorPool = CreateDescriptorPool(); + + VkDescriptorSetLayout cameraSetLayout = CreateDescriptorSetLayout({ + { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, nullptr }, + }); + + VkDescriptorSetLayout modelSetLayout = CreateDescriptorSetLayout({ + { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, nullptr }, + }); + + VkDescriptorSet cameraSet = CreateDescriptorSet(descriptorPool, cameraSetLayout); + VkDescriptorSet modelSet = CreateDescriptorSet(descriptorPool, modelSetLayout); + + // Initialize descriptor sets + { + VkDescriptorBufferInfo cameraBufferInfo = {}; + cameraBufferInfo.buffer = cameraBuffer; + cameraBufferInfo.offset = 0; + cameraBufferInfo.range = sizeof(CameraUBO); + + VkWriteDescriptorSet writeCameraInfo = {}; + writeCameraInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeCameraInfo.dstSet = cameraSet; + writeCameraInfo.dstBinding = 0; + writeCameraInfo.descriptorCount = 1; + writeCameraInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + writeCameraInfo.pBufferInfo = &cameraBufferInfo; + + VkDescriptorBufferInfo modelBufferInfo = {}; + modelBufferInfo.buffer = modelBuffer; + modelBufferInfo.offset = 0; + modelBufferInfo.range = sizeof(ModelUBO); + + VkWriteDescriptorSet writeModelInfo = {}; + writeModelInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeModelInfo.dstSet = modelSet; + writeModelInfo.dstBinding = 0; + writeModelInfo.descriptorCount = 1; + writeModelInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + writeModelInfo.pBufferInfo = &modelBufferInfo; + + VkWriteDescriptorSet writeDescriptorSets[] = { writeCameraInfo, writeModelInfo }; + + vkUpdateDescriptorSets(device->GetVulkanDevice(), 2, writeDescriptorSets, 0, nullptr); + } + VkRenderPass renderPass = CreateRenderPass(); - VkPipelineLayout pipelineLayout = CreatePipelineLayout(); + VkPipelineLayout pipelineLayout = CreatePipelineLayout({ cameraSetLayout, modelSetLayout }); VkPipeline pipeline = CreatePipeline(pipelineLayout, renderPass, 0); // Create one framebuffer for each frame of the swap chain @@ -357,9 +520,15 @@ int main(int argc, char** argv) { vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + // Bind camera descriptor set + vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &cameraSet, 0, nullptr); + // Bind the graphics pipeline vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + // Bind model descriptor set + vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &modelSet, 0, nullptr); + VkDeviceSize offsets[1] = { 0 }; vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, &vertexBuffer, offsets); @@ -379,6 +548,13 @@ int main(int argc, char** argv) { } } + glfwSetMouseButtonCallback(GetGLFWWindow(), mouseButtonCallback); + glfwSetCursorPosCallback(GetGLFWWindow(), cursorPosCallback); + glfwSetScrollCallback(GetGLFWWindow(), scrollCallback); + + int frameNumber = 0; + // Map the part of the buffer referring the camera view matrix so it can be updated when the camera moves + vkMapMemory(device->GetVulkanDevice(), uniformBufferMemory, uniformBufferOffsets[0] + offsetof(CameraUBO, viewMatrix), sizeof(glm::mat4), 0, reinterpret_cast(&mappedCameraView)); while (!ShouldQuit()) { swapchain->Acquire(); @@ -407,14 +583,25 @@ int main(int argc, char** argv) { glfwPollEvents(); } + vkUnmapMemory(device->GetVulkanDevice(), uniformBufferMemory); // Wait for the device to finish executing before cleanup vkDeviceWaitIdle(device->GetVulkanDevice()); + delete camera; + vkDestroyBuffer(device->GetVulkanDevice(), vertexBuffer, nullptr); vkDestroyBuffer(device->GetVulkanDevice(), indexBuffer, nullptr); vkFreeMemory(device->GetVulkanDevice(), vertexBufferMemory, nullptr); + vkDestroyBuffer(device->GetVulkanDevice(), cameraBuffer, nullptr); + vkDestroyBuffer(device->GetVulkanDevice(), modelBuffer, nullptr); + vkFreeMemory(device->GetVulkanDevice(), uniformBufferMemory, nullptr); + + vkDestroyDescriptorSetLayout(device->GetVulkanDevice(), cameraSetLayout, nullptr); + vkDestroyDescriptorSetLayout(device->GetVulkanDevice(), modelSetLayout, nullptr); + vkDestroyDescriptorPool(device->GetVulkanDevice(), descriptorPool, nullptr); + vkDestroyPipeline(device->GetVulkanDevice(), pipeline, nullptr); vkDestroyPipelineLayout(device->GetVulkanDevice(), pipelineLayout, nullptr); vkDestroyRenderPass(device->GetVulkanDevice(), renderPass, nullptr); diff --git a/samples/sample/shaders/shader.vert b/samples/sample/shaders/shader.vert index 8b98034..a23d7b7 100644 --- a/samples/sample/shaders/shader.vert +++ b/samples/sample/shaders/shader.vert @@ -1,6 +1,15 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable +layout(set = 0, binding = 0) uniform CameraUBO { + mat4 viewMatrix; + mat4 projectionMatrix; +}; + +layout(set = 1, binding = 0) uniform ModelUBO { + mat4 modelMatrix; +}; + layout (location = 0) in vec3 position; layout (location = 1) in vec3 color; @@ -11,6 +20,6 @@ out gl_PerVertex { layout(location = 0) out vec3 fragColor; void main() { - gl_Position = vec4(position, 1.0); + gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0); fragColor = color; } \ No newline at end of file