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 2: Vertex Buffers #2

Open
wants to merge 1 commit into
base: 1-hello-triangle
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
84 changes: 76 additions & 8 deletions samples/sample/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
#include "window.h"
#include "vulkan_instance.h"
#include "vulkan_shader_module.h"
#include "vulkan_buffer.h"

VulkanInstance* instance;
VulkanDevice* device;
VulkanSwapChain* swapchain;

struct Vertex {
float position[3];
float color[3];
};

VkRenderPass CreateRenderPass() {
// Color buffer attachment represented by one of the images from the swap chain
VkAttachmentDescription colorAttachment = {};
Expand Down Expand Up @@ -95,13 +101,34 @@ VkPipeline CreatePipeline(VkPipelineLayout pipelineLayout, VkRenderPass renderPa

// --- Set up fixed-function stages ---

// Vertex input binding
VkVertexInputBindingDescription vertexInputBinding = {};
vertexInputBinding.binding = 0;
vertexInputBinding.stride = sizeof(Vertex);
vertexInputBinding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;

// Inpute attribute bindings describe shader attribute locations and memory layouts
std::array<VkVertexInputAttributeDescription, 2> vertexInputAttributes;
// Attribute location 0: Position
vertexInputAttributes[0].binding = 0;
vertexInputAttributes[0].location = 0;
// Position attribute is three 32 bit signed (SFLOAT) floats (R32 G32 B32)
vertexInputAttributes[0].format = VK_FORMAT_R32G32B32_SFLOAT;
vertexInputAttributes[0].offset = offsetof(Vertex, position);
// Attribute location 1: Color
vertexInputAttributes[1].binding = 0;
vertexInputAttributes[1].location = 1;
// Color attribute is three 32 bit signed (SFLOAT) floats (R32 G32 B32)
vertexInputAttributes[1].format = VK_FORMAT_R32G32B32_SFLOAT;
vertexInputAttributes[1].offset = offsetof(Vertex, color);

// Vertex input
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0;
vertexInputInfo.pVertexBindingDescriptions = nullptr;
vertexInputInfo.vertexAttributeDescriptionCount = 0;
vertexInputInfo.pVertexAttributeDescriptions = nullptr;
vertexInputInfo.vertexBindingDescriptionCount = 1;
vertexInputInfo.pVertexBindingDescriptions = &vertexInputBinding;
vertexInputInfo.vertexAttributeDescriptionCount = 2;
vertexInputInfo.pVertexAttributeDescriptions = vertexInputAttributes.data();

// Input assembly
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
Expand Down Expand Up @@ -208,7 +235,6 @@ VkPipeline CreatePipeline(VkPipelineLayout pipelineLayout, VkRenderPass renderPa
return pipeline;
}


std::vector<VkFramebuffer> CreateFrameBuffers(VkRenderPass renderPass) {
std::vector<VkFramebuffer> frameBuffers(swapchain->GetCount());
for (uint32_t i = 0; i < swapchain->GetCount(); i++) {
Expand All @@ -231,7 +257,7 @@ std::vector<VkFramebuffer> CreateFrameBuffers(VkRenderPass renderPass) {
}

int main(int argc, char** argv) {
static constexpr char* applicationName = "Hello Triangle";
static constexpr char* applicationName = "Hello Uniform Buffers";
InitializeWindow(640, 480, applicationName);

unsigned int glfwExtensionCount = 0;
Expand All @@ -244,9 +270,9 @@ int main(int argc, char** argv) {
throw std::runtime_error("Failed to create window surface");
}

instance->PickPhysicalDevice({ VK_KHR_SWAPCHAIN_EXTENSION_NAME }, QueueFlagBit::GraphicsBit | QueueFlagBit::PresentBit, surface);
instance->PickPhysicalDevice({ VK_KHR_SWAPCHAIN_EXTENSION_NAME }, QueueFlagBit::GraphicsBit | QueueFlagBit::TransferBit | QueueFlagBit::PresentBit, surface);

device = instance->CreateDevice(QueueFlagBit::GraphicsBit | QueueFlagBit::PresentBit);
device = instance->CreateDevice(QueueFlagBit::GraphicsBit | QueueFlagBit::TransferBit | QueueFlagBit::PresentBit);
swapchain = device->CreateSwapChain(surface);

VkCommandPoolCreateInfo poolInfo = {};
Expand All @@ -259,6 +285,35 @@ int main(int argc, char** argv) {
throw std::runtime_error("Failed to create command pool");
}

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 } },
{ { 0.0f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f } }
};

std::vector<unsigned int> indices = { 0, 1, 2 };

unsigned int vertexBufferSize = static_cast<uint32_t>(vertices.size() * sizeof(vertices[0]));
unsigned int indexBufferSize = static_cast<uint32_t>(indices.size() * sizeof(indices[0]));

// Create vertex and index buffers
VkBuffer vertexBuffer = CreateBuffer(device, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, vertexBufferSize);
VkBuffer indexBuffer = CreateBuffer(device, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, indexBufferSize);
unsigned int vertexBufferOffsets[2];
VkDeviceMemory vertexBufferMemory = AllocateMemoryForBuffers(device, { vertexBuffer, indexBuffer }, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, vertexBufferOffsets);

// Copy data to buffer memory
{
char* data;
vkMapMemory(device->GetVulkanDevice(), vertexBufferMemory, 0, vertexBufferOffsets[1] + indexBufferSize, 0, reinterpret_cast<void**>(&data));
memcpy(data + vertexBufferOffsets[0], vertices.data(), vertexBufferSize);
memcpy(data + vertexBufferOffsets[1], indices.data(), indexBufferSize);
vkUnmapMemory(device->GetVulkanDevice(), vertexBufferMemory);
}

// Bind the memory to the buffers
BindMemoryForBuffers(device, vertexBufferMemory, { vertexBuffer, indexBuffer }, vertexBufferOffsets);

VkRenderPass renderPass = CreateRenderPass();
VkPipelineLayout pipelineLayout = CreatePipelineLayout();
VkPipeline pipeline = CreatePipeline(pipelineLayout, renderPass, 0);
Expand Down Expand Up @@ -305,6 +360,15 @@ int main(int argc, char** argv) {
// Bind the graphics pipeline
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);

VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, &vertexBuffer, offsets);

// Bind triangle index buffer
vkCmdBindIndexBuffer(commandBuffers[i], indexBuffer, 0, VK_INDEX_TYPE_UINT32);

// Draw indexed triangle
vkCmdDrawIndexed(commandBuffers[i], 3, 1, 0, 0, 1);

// Draw
vkCmdDraw(commandBuffers[i], 3, 1, 0, 0);

Expand Down Expand Up @@ -347,6 +411,10 @@ int main(int argc, char** argv) {
// Wait for the device to finish executing before cleanup
vkDeviceWaitIdle(device->GetVulkanDevice());

vkDestroyBuffer(device->GetVulkanDevice(), vertexBuffer, nullptr);
vkDestroyBuffer(device->GetVulkanDevice(), indexBuffer, nullptr);
vkFreeMemory(device->GetVulkanDevice(), vertexBufferMemory, nullptr);

vkDestroyPipeline(device->GetVulkanDevice(), pipeline, nullptr);
vkDestroyPipelineLayout(device->GetVulkanDevice(), pipelineLayout, nullptr);
vkDestroyRenderPass(device->GetVulkanDevice(), renderPass, nullptr);
Expand Down
19 changes: 5 additions & 14 deletions samples/sample/shaders/shader.vert
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable

layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;

out gl_PerVertex {
vec4 gl_Position;
};

layout(location = 0) out vec3 fragColor;

vec2 positions[3] = vec2[](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);

vec3 colors[3] = vec3[](
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0)
);

void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
fragColor = colors[gl_VertexIndex];
gl_Position = vec4(position, 1.0);
fragColor = color;
}
50 changes: 50 additions & 0 deletions samples/sample/vulkan_buffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

#include "vulkan_buffer.h"

VkDeviceMemory CreateDeviceMemory(VulkanDevice* device, uint32_t size, uint32_t types, VkMemoryPropertyFlags propertyFlags) {
VkMemoryAllocateInfo memoryAllocateInfo = {};
memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memoryAllocateInfo.allocationSize = size;
memoryAllocateInfo.memoryTypeIndex = device->GetInstance()->GetMemoryTypeIndex(types, propertyFlags);
VkDeviceMemory deviceMemory;
vkAllocateMemory(device->GetVulkanDevice(), &memoryAllocateInfo, nullptr, &deviceMemory);
return deviceMemory;
}

VkBuffer CreateBuffer(VulkanDevice* device, VkBufferUsageFlags allowedUsage, uint32_t size, VkDeviceMemory deviceMemory, uint32_t deviceMemoryOffset) {
VkBufferCreateInfo bufferCreateInfo = {};
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferCreateInfo.size = size;
bufferCreateInfo.usage = allowedUsage;
VkBuffer buffer;
vkCreateBuffer(device->GetVulkanDevice(), &bufferCreateInfo, nullptr, &buffer);

if (deviceMemory != VK_NULL_HANDLE) {
vkBindBufferMemory(device->GetVulkanDevice(), buffer, deviceMemory, deviceMemoryOffset);
}

return buffer;
}

VkDeviceMemory AllocateMemoryForBuffers(VulkanDevice* device, std::vector<VkBuffer> buffers, VkMemoryPropertyFlags propertyFlags, unsigned int* bufferOffsets) {
// We will allocate one large chunk of memory for both. Figure out what kind of memory we need
VkMemoryRequirements memoryRequirements;
uint32_t typeBits = 0;
for (unsigned int i = 0; i < buffers.size(); ++i) {
if (i == 0) {
bufferOffsets[i] = 0;
} else {
bufferOffsets[i] = bufferOffsets[i - 1] + memoryRequirements.size;
}
vkGetBufferMemoryRequirements(device->GetVulkanDevice(), buffers[i], &memoryRequirements);
typeBits |= memoryRequirements.memoryTypeBits;
}

return CreateDeviceMemory(device, bufferOffsets[buffers.size() - 1] + memoryRequirements.size, typeBits, propertyFlags);
}

void BindMemoryForBuffers(VulkanDevice* device, VkDeviceMemory bufferMemory, std::vector<VkBuffer> buffers, unsigned int* bufferOffsets) {
for (unsigned int i = 0; i < buffers.size(); ++i) {
vkBindBufferMemory(device->GetVulkanDevice(), buffers[i], bufferMemory, bufferOffsets[i]);
}
}
10 changes: 10 additions & 0 deletions samples/sample/vulkan_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

#include <vulkan/vulkan.h>
#include <vector>
#include "vulkan_device.h"

VkDeviceMemory CreateDeviceMemory(VulkanDevice* device, uint32_t size, uint32_t types, VkMemoryPropertyFlags propertyFlags);
VkBuffer CreateBuffer(VulkanDevice* device, VkBufferUsageFlags allowedUsage, uint32_t size, VkDeviceMemory deviceMemory = VK_NULL_HANDLE, uint32_t deviceMemoryOffset = 0);
VkDeviceMemory AllocateMemoryForBuffers(VulkanDevice* device, std::vector<VkBuffer> buffers, VkMemoryPropertyFlags propertyFlags, unsigned int* bufferOffsets);
void BindMemoryForBuffers(VulkanDevice* device, VkDeviceMemory bufferMemory, std::vector<VkBuffer> buffers, unsigned int* bufferOffsets);