Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EXAMPLE 3: Uniform Buffers #3

Open
wants to merge 1 commit into
base: 2-hello-vertex-buffers
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions samples/sample/camera.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "camera.h"
#include <glm/gtc/matrix_transform.hpp>

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;
}
}
24 changes: 24 additions & 0 deletions samples/sample/camera.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <glm/glm.hpp>

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;
};
195 changes: 191 additions & 4 deletions samples/sample/main.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,66 @@
#include <stdexcept>
#include <vulkan/vulkan.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "window.h"
#include "vulkan_instance.h"
#include "vulkan_shader_module.h"
#include "vulkan_buffer.h"
#include "camera.h"
#include <iostream>

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<float>(mouseX - oldX);
float dY = static_cast<float>(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<float>(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 = {};
Expand Down Expand Up @@ -64,11 +111,56 @@ VkRenderPass CreateRenderPass() {
return renderPass;
}

VkPipelineLayout CreatePipelineLayout() {
VkDescriptorSetLayout CreateDescriptorSetLayout(std::vector<VkDescriptorSetLayoutBinding> layoutBindings) {
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {};
descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorSetLayoutCreateInfo.pNext = nullptr;
descriptorSetLayoutCreateInfo.bindingCount = static_cast<uint32_t>(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<VkDescriptorSetLayout> descriptorSetLayouts) {
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0;
pipelineLayoutInfo.pSetLayouts = nullptr;
pipelineLayoutInfo.setLayoutCount = static_cast<uint32_t>(descriptorSetLayouts.size());
pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data();
pipelineLayoutInfo.pushConstantRangeCount = 0;
pipelineLayoutInfo.pPushConstantRanges = 0;

Expand Down Expand Up @@ -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<float>(45 * M_PI / 180), 640.f / 480.f, 0.1f, 1000.f);

ModelUBO modelTransforms;
modelTransforms.modelMatrix = glm::rotate(glm::mat4(1.f), static_cast<float>(15 * M_PI / 180), glm::vec3(0.f, 0.f, 1.f));

std::vector<Vertex> 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 } },
Expand Down Expand Up @@ -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<void**>(&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
Expand Down Expand Up @@ -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);

Expand All @@ -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<void**>(&mappedCameraView));
while (!ShouldQuit()) {
swapchain->Acquire();

Expand Down Expand Up @@ -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);
Expand Down
11 changes: 10 additions & 1 deletion samples/sample/shaders/shader.vert
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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;
}