Added source files from demo project as starting point for vulkan development.

This commit is contained in:
Artem Kharytoniuk 2017-03-16 22:32:02 +02:00
parent 10ef302b5f
commit b482e778ff
11 changed files with 2032 additions and 0 deletions

View File

@ -0,0 +1,133 @@
#include "vk_allocator.h"
#include "vk_utils.h"
static Device_Memory_Allocator allocator;
Device_Memory_Allocator* get_allocator() {
return &allocator;
}
static uint32_t find_memory_type(VkPhysicalDevice physical_device, uint32_t memory_type_bits, VkMemoryPropertyFlags properties) {
VkPhysicalDeviceMemoryProperties memory_properties;
vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties);
for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) {
if ((memory_type_bits & (1 << i)) != 0 &&
(memory_properties.memoryTypes[i].propertyFlags & properties) == properties) {
return i;
}
}
error("failed to find matching memory type with requested properties");
return -1;
}
void Shared_Staging_Memory::initialize(VkPhysicalDevice physical_device, VkDevice device) {
this->physical_device = physical_device;
this->device = device;
}
void Shared_Staging_Memory::deallocate_all() {
if (handle != VK_NULL_HANDLE) {
vkFreeMemory(device, handle, nullptr);
}
handle = VK_NULL_HANDLE;
size = 0;
memory_type_index = -1;
}
void Shared_Staging_Memory::ensure_allocation_for_object(VkImage image) {
VkMemoryRequirements memory_requirements;
vkGetImageMemoryRequirements(device, image, &memory_requirements);
ensure_allocation(memory_requirements);
}
void Shared_Staging_Memory::ensure_allocation_for_object(VkBuffer buffer) {
VkMemoryRequirements memory_requirements;
vkGetBufferMemoryRequirements(device, buffer, &memory_requirements);
ensure_allocation(memory_requirements);
}
VkDeviceMemory Shared_Staging_Memory::get_handle() const {
return handle;
}
void Shared_Staging_Memory::ensure_allocation(const VkMemoryRequirements& memory_requirements) {
uint32_t required_memory_type_index = find_memory_type(physical_device, memory_requirements.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
if (size < memory_requirements.size || memory_type_index != required_memory_type_index) {
if (handle != VK_NULL_HANDLE) {
vkFreeMemory(device, handle, nullptr);
}
handle = VK_NULL_HANDLE;
size = 0;
memory_type_index = -1;
VkMemoryAllocateInfo alloc_info;
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.pNext = nullptr;
alloc_info.allocationSize = memory_requirements.size;
alloc_info.memoryTypeIndex = required_memory_type_index;
VkResult result = vkAllocateMemory(device, &alloc_info, nullptr, &handle);
check_vk_result(result, "vkAllocateMemory");
size = memory_requirements.size;
memory_type_index = required_memory_type_index;
}
}
void Device_Memory_Allocator::initialize(VkPhysicalDevice physical_device, VkDevice device) {
this->physical_device = physical_device;
this->device = device;
shared_staging_memory.initialize(physical_device, device);
}
void Device_Memory_Allocator::deallocate_all() {
for (auto chunk : chunks) {
vkFreeMemory(device, chunk, nullptr);
}
chunks.clear();
shared_staging_memory.deallocate_all();
}
VkDeviceMemory Device_Memory_Allocator::allocate_memory(VkImage image) {
VkMemoryRequirements memory_requirements;
vkGetImageMemoryRequirements(device, image, &memory_requirements);
return allocate_memory(memory_requirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
VkDeviceMemory Device_Memory_Allocator::allocate_memory(VkBuffer buffer) {
VkMemoryRequirements memory_requirements;
vkGetBufferMemoryRequirements(device, buffer, &memory_requirements);
return allocate_memory(memory_requirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
VkDeviceMemory Device_Memory_Allocator::allocate_staging_memory(VkImage image) {
VkMemoryRequirements memory_requirements;
vkGetImageMemoryRequirements(device, image, &memory_requirements);
return allocate_memory(memory_requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
}
VkDeviceMemory Device_Memory_Allocator::allocate_staging_memory(VkBuffer buffer) {
VkMemoryRequirements memory_requirements;
vkGetBufferMemoryRequirements(device, buffer, &memory_requirements);
return allocate_memory(memory_requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
}
Shared_Staging_Memory& Device_Memory_Allocator::get_shared_staging_memory() {
return shared_staging_memory;
}
VkDeviceMemory Device_Memory_Allocator::allocate_memory(const VkMemoryRequirements& memory_requirements, VkMemoryPropertyFlags properties) {
VkMemoryAllocateInfo alloc_info;
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.pNext = nullptr;
alloc_info.allocationSize = memory_requirements.size;
alloc_info.memoryTypeIndex = find_memory_type(physical_device, memory_requirements.memoryTypeBits, properties);
VkDeviceMemory chunk;
VkResult result = vkAllocateMemory(device, &alloc_info, nullptr, &chunk);
check_vk_result(result, "vkAllocateMemory");
chunks.push_back(chunk);
return chunk;
}

View File

@ -0,0 +1,52 @@
#pragma once
#include "vk_definitions.h"
#include <vector>
class Shared_Staging_Memory {
public:
void initialize(VkPhysicalDevice physical_device, VkDevice device);
void deallocate_all();
void ensure_allocation_for_object(VkImage image);
void ensure_allocation_for_object(VkBuffer buffer);
VkDeviceMemory get_handle() const;
private:
void ensure_allocation(const VkMemoryRequirements& memory_requirements);
private:
VkPhysicalDevice physical_device = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
VkDeviceMemory handle = VK_NULL_HANDLE;
VkDeviceSize size = 0;
uint32_t memory_type_index = -1;
};
// NOTE: in this implementation I do memory allocation for each allocation request.
// TODO: sub-allocate from larger chunks and return chunk handle plus offset withing corresponding chunk.
class Device_Memory_Allocator {
public:
void initialize(VkPhysicalDevice physical_device, VkDevice device);
void deallocate_all();
VkDeviceMemory allocate_memory(VkImage image);
VkDeviceMemory allocate_memory(VkBuffer buffer);
VkDeviceMemory allocate_staging_memory(VkImage image);
VkDeviceMemory allocate_staging_memory(VkBuffer buffer);
Shared_Staging_Memory& get_shared_staging_memory();
private:
VkDeviceMemory allocate_memory(const VkMemoryRequirements& memory_requirements, VkMemoryPropertyFlags properties);
private:
VkPhysicalDevice physical_device = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
std::vector<VkDeviceMemory> chunks;
Shared_Staging_Memory shared_staging_memory;
};
Device_Memory_Allocator* get_allocator();

View File

@ -0,0 +1,6 @@
#pragma once
#define VK_USE_PLATFORM_WIN32_KHR
#define NOMINMAX
#include "vulkan/vulkan.h"

View File

@ -0,0 +1,817 @@
#include "vk_allocator.h"
#include "vk_resource_manager.h"
#include "vk_demo.h"
#include "vk_init.h"
#include "vk_utils.h"
#include "stb_image.h"
#define TINYOBJLOADER_IMPLEMENTATION
#include "tiny_obj_loader.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtx/hash.hpp"
#include <array>
#include <chrono>
#include <iostream>
#include <functional>
#include <unordered_map>
const std::string model_path = "../../data/model.obj";
const std::string texture_path = "../../data/texture.jpg";
struct Uniform_Buffer_Object {
glm::mat4 model;
glm::mat4 view;
glm::mat4 proj;
};
struct Vertex {
glm::vec3 pos;
glm::vec3 color;
glm::vec2 tex_coord;
bool operator==(const Vertex& other) const {
return pos == other.pos && color == other.color && tex_coord == other.tex_coord;
}
static std::array<VkVertexInputBindingDescription, 1> get_bindings() {
VkVertexInputBindingDescription binding_desc;
binding_desc.binding = 0;
binding_desc.stride = sizeof(Vertex);
binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
return {binding_desc};
}
static std::array<VkVertexInputAttributeDescription, 3> get_attributes() {
VkVertexInputAttributeDescription position_attrib;
position_attrib.location = 0;
position_attrib.binding = 0;
position_attrib.format = VK_FORMAT_R32G32B32_SFLOAT;
position_attrib.offset = offsetof(struct Vertex, pos);
VkVertexInputAttributeDescription color_attrib;
color_attrib.location = 1;
color_attrib.binding = 0;
color_attrib.format = VK_FORMAT_R32G32B32_SFLOAT;
color_attrib.offset = offsetof(struct Vertex, color);
VkVertexInputAttributeDescription tex_coord_attrib;
tex_coord_attrib.location = 2;
tex_coord_attrib.binding = 0;
tex_coord_attrib.format = VK_FORMAT_R32G32_SFLOAT;
tex_coord_attrib.offset = offsetof(struct Vertex, tex_coord);
return {position_attrib, color_attrib, tex_coord_attrib};
}
};
struct Model {
std::vector<Vertex> vertices;
std::vector<uint32_t> indices;
};
namespace std {
template<> struct hash<Vertex> {
size_t operator()(Vertex const& vertex) const {
return ((hash<glm::vec3>()(vertex.pos) ^
(hash<glm::vec3>()(vertex.color) << 1)) >> 1) ^
(hash<glm::vec2>()(vertex.tex_coord) << 1);
}
};
}
static Model load_model() {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, model_path.c_str()))
error("failed to load obj model: " + model_path);
Model model;
std::unordered_map<Vertex, std::size_t> unique_vertices;
for (const auto& shape : shapes) {
for (const auto& index : shape.mesh.indices) {
Vertex vertex;
vertex.pos = {
attrib.vertices[3 * index.vertex_index + 0],
attrib.vertices[3 * index.vertex_index + 1],
attrib.vertices[3 * index.vertex_index + 2]
};
vertex.tex_coord = {
attrib.texcoords[2 * index.texcoord_index + 0],
1.0 - attrib.texcoords[2 * index.texcoord_index + 1]
};
vertex.color = {1.0f, 1.0f, 1.0f};
if (unique_vertices.count(vertex) == 0) {
unique_vertices[vertex] = model.vertices.size();
model.vertices.push_back(vertex);
}
model.indices.push_back((uint32_t)unique_vertices[vertex]);
}
}
return model;
}
static VkFormat find_format_with_features(VkPhysicalDevice physical_device, const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {
for (VkFormat format : candidates) {
VkFormatProperties properties;
vkGetPhysicalDeviceFormatProperties(physical_device, format, &properties);
if (tiling == VK_IMAGE_TILING_LINEAR && (properties.linearTilingFeatures & features) == features)
return format;
if (tiling == VK_IMAGE_TILING_OPTIMAL && (properties.optimalTilingFeatures & features) == features)
return format;
}
error("failed to find format with requested features");
return VK_FORMAT_UNDEFINED; // never get here
}
static VkFormat find_depth_format(VkPhysicalDevice physical_device) {
return find_format_with_features(physical_device, {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT},
VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
}
Vulkan_Demo::Vulkan_Demo(int window_width, int window_height, const SDL_SysWMinfo& window_sys_info)
: window_width(window_width)
, window_height(window_height)
{
initialize_vulkan(window_sys_info);
get_allocator()->initialize(get_physical_device(), get_device());
get_resource_manager()->initialize(get_device());
image_acquired = get_resource_manager()->create_semaphore();
rendering_finished = get_resource_manager()->create_semaphore();
create_command_pool();
create_descriptor_pool();
create_uniform_buffer();
create_texture();
create_texture_sampler();
create_depth_buffer_resources();
create_descriptor_set_layout();
create_descriptor_set();
create_render_pass();
create_framebuffers();
create_pipeline_layout();
create_pipeline();
upload_geometry();
record_render_scene(); // record secondary command buffer before primary ones
record_render_frame();
}
Vulkan_Demo::~Vulkan_Demo() {
VkResult result = vkDeviceWaitIdle(get_device());
if (result < 0)
std::cerr << "vkDeviceWaitIdle returned an error code: " + result;
get_resource_manager()->release_resources();
get_allocator()->deallocate_all();
deinitialize_vulkan();
}
void Vulkan_Demo::create_command_pool() {
VkCommandPoolCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.queueFamilyIndex = get_queue_family_index();
command_pool = get_resource_manager()->create_command_pool(desc);
}
void Vulkan_Demo::create_descriptor_pool() {
std::array<VkDescriptorPoolSize, 2> pool_sizes;
pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
pool_sizes[0].descriptorCount = 1;
pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_sizes[1].descriptorCount = 1;
VkDescriptorPoolCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.maxSets = 1;
desc.poolSizeCount = static_cast<uint32_t>(pool_sizes.size());
desc.pPoolSizes = pool_sizes.data();
descriptor_pool = get_resource_manager()->create_descriptor_pool(desc);
}
void Vulkan_Demo::create_uniform_buffer() {
auto size = static_cast<VkDeviceSize>(sizeof(Uniform_Buffer_Object));
uniform_staging_buffer = create_permanent_staging_buffer(size, uniform_staging_buffer_memory);
uniform_buffer = create_buffer(size, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
}
void Vulkan_Demo::create_texture() {
int image_width, image_height, image_component_count;
auto rgba_pixels = stbi_load(texture_path.c_str(), &image_width, &image_height, &image_component_count, STBI_rgb_alpha);
if (rgba_pixels == nullptr) {
error("failed to load image file");
}
VkImage staging_image = create_staging_texture(image_width, image_height, VK_FORMAT_R8G8B8A8_UNORM, rgba_pixels, 4);
Defer_Action destroy_staging_image([this, &staging_image]() {
vkDestroyImage(get_device(), staging_image, nullptr);
});
stbi_image_free(rgba_pixels);
texture_image = ::create_texture(image_width, image_height, VK_FORMAT_R8G8B8A8_UNORM);
record_and_run_commands(command_pool, get_queue(),
[&staging_image, &image_width, &image_height, this](VkCommandBuffer command_buffer) {
record_image_layout_transition(command_buffer, staging_image, VK_FORMAT_R8G8B8A8_UNORM,
VK_ACCESS_HOST_WRITE_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
record_image_layout_transition(command_buffer, texture_image, VK_FORMAT_R8G8B8A8_UNORM,
0, VK_IMAGE_LAYOUT_UNDEFINED, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
// copy staging image's data to device local image
VkImageSubresourceLayers subresource_layers;
subresource_layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource_layers.mipLevel = 0;
subresource_layers.baseArrayLayer = 0;
subresource_layers.layerCount = 1;
VkImageCopy region;
region.srcSubresource = subresource_layers;
region.srcOffset = {0, 0, 0};
region.dstSubresource = subresource_layers;
region.dstOffset = {0, 0, 0};
region.extent.width = image_width;
region.extent.height = image_height;
vkCmdCopyImage(command_buffer,
staging_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
texture_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &region);
record_image_layout_transition(command_buffer, texture_image, VK_FORMAT_R8G8B8A8_UNORM,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
});
texture_image_view = create_image_view(texture_image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
}
void Vulkan_Demo::create_texture_sampler() {
VkSamplerCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.magFilter = VK_FILTER_LINEAR;
desc.minFilter = VK_FILTER_LINEAR;
desc.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
desc.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
desc.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
desc.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
desc.mipLodBias = 0.0f;
desc.anisotropyEnable = VK_TRUE;
desc.maxAnisotropy = 16;
desc.compareEnable = VK_FALSE;
desc.compareOp = VK_COMPARE_OP_ALWAYS;
desc.minLod = 0.0f;
desc.maxLod = 0.0f;
desc.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
desc.unnormalizedCoordinates = VK_FALSE;
texture_image_sampler = get_resource_manager()->create_sampler(desc);
}
void Vulkan_Demo::create_depth_buffer_resources() {
VkFormat depth_format = find_depth_format(get_physical_device());
depth_image = create_depth_attachment_image(window_width, window_height, depth_format);
depth_image_view = create_image_view(depth_image, depth_format, VK_IMAGE_ASPECT_DEPTH_BIT);
record_and_run_commands(command_pool, get_queue(), [&depth_format, this](VkCommandBuffer command_buffer) {
record_image_layout_transition(command_buffer, depth_image, depth_format, 0, VK_IMAGE_LAYOUT_UNDEFINED,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
});
}
void Vulkan_Demo::create_descriptor_set_layout() {
std::array<VkDescriptorSetLayoutBinding, 2> descriptor_bindings;
descriptor_bindings[0].binding = 0;
descriptor_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptor_bindings[0].descriptorCount = 1;
descriptor_bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
descriptor_bindings[0].pImmutableSamplers = nullptr;
descriptor_bindings[1].binding = 1;
descriptor_bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptor_bindings[1].descriptorCount = 1;
descriptor_bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
descriptor_bindings[1].pImmutableSamplers = nullptr;
VkDescriptorSetLayoutCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.bindingCount = static_cast<uint32_t>(descriptor_bindings.size());
desc.pBindings = descriptor_bindings.data();
descriptor_set_layout = get_resource_manager()->create_descriptor_set_layout(desc);
}
void Vulkan_Demo::create_descriptor_set() {
VkDescriptorSetAllocateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
desc.pNext = nullptr;
desc.descriptorPool = descriptor_pool;
desc.descriptorSetCount = 1;
desc.pSetLayouts = &descriptor_set_layout;
VkResult result = vkAllocateDescriptorSets(get_device(), &desc, &descriptor_set);
check_vk_result(result, "vkAllocateDescriptorSets");
VkDescriptorBufferInfo buffer_info;
buffer_info.buffer = uniform_buffer;
buffer_info.offset = 0;
buffer_info.range = sizeof(Uniform_Buffer_Object);
VkDescriptorImageInfo image_info;
image_info.sampler = texture_image_sampler;
image_info.imageView = texture_image_view;
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
std::array<VkWriteDescriptorSet, 2> descriptor_writes;
descriptor_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_writes[0].pNext = nullptr;
descriptor_writes[0].dstSet = descriptor_set;
descriptor_writes[0].dstBinding = 0;
descriptor_writes[0].dstArrayElement = 0;
descriptor_writes[0].descriptorCount = 1;
descriptor_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptor_writes[0].pImageInfo = nullptr;
descriptor_writes[0].pBufferInfo = &buffer_info;
descriptor_writes[0].pTexelBufferView = nullptr;
descriptor_writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_writes[1].dstSet = descriptor_set;
descriptor_writes[1].dstBinding = 1;
descriptor_writes[1].dstArrayElement = 0;
descriptor_writes[1].descriptorCount = 1;
descriptor_writes[1].pNext = nullptr;
descriptor_writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptor_writes[1].pImageInfo = &image_info;
descriptor_writes[1].pBufferInfo = nullptr;
descriptor_writes[1].pTexelBufferView = nullptr;
vkUpdateDescriptorSets(get_device(), (uint32_t)descriptor_writes.size(), descriptor_writes.data(), 0, nullptr);
}
void Vulkan_Demo::create_render_pass() {
VkAttachmentDescription color_attachment;
color_attachment.flags = 0;
color_attachment.format = get_swapchain_image_format();
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentDescription depth_attachment;
depth_attachment.flags = 0;
depth_attachment.format = find_depth_format(get_physical_device());
depth_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depth_attachment.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depth_attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReference color_attachment_ref;
color_attachment_ref.attachment = 0;
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depth_attachment_ref;
depth_attachment_ref.attachment = 1;
depth_attachment_ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass;
subpass.flags = 0;
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.inputAttachmentCount = 0;
subpass.pInputAttachments = nullptr;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment_ref;
subpass.pResolveAttachments = nullptr;
subpass.pDepthStencilAttachment = &depth_attachment_ref;
subpass.preserveAttachmentCount = 0;
subpass.pPreserveAttachments = nullptr;
std::array<VkAttachmentDescription, 2> attachments{color_attachment, depth_attachment};
VkRenderPassCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.attachmentCount = static_cast<uint32_t>(attachments.size());
desc.pAttachments = attachments.data();
desc.subpassCount = 1;
desc.pSubpasses = &subpass;
desc.dependencyCount = 0;
desc.pDependencies = nullptr;
render_pass = get_resource_manager()->create_render_pass(desc);
}
void Vulkan_Demo::create_framebuffers() {
std::array<VkImageView, 2> attachments = {VK_NULL_HANDLE, depth_image_view};
VkFramebufferCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.renderPass = render_pass;
desc.attachmentCount = static_cast<uint32_t>(attachments.size());
desc.pAttachments = attachments.data();
desc.width = window_width;
desc.height = window_height;
desc.layers = 1;
const auto& swapchain_image_views = get_swapchain_image_views();
framebuffers.resize(swapchain_image_views.size());
for (std::size_t i = 0; i < framebuffers.size(); i++) {
attachments[0] = swapchain_image_views[i]; // set color attachment
framebuffers[i] = get_resource_manager()->create_framebuffer(desc);
}
}
void Vulkan_Demo::create_pipeline_layout() {
VkPipelineLayoutCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.setLayoutCount = 1;
desc.pSetLayouts = &descriptor_set_layout;
desc.pushConstantRangeCount = 0;
desc.pPushConstantRanges = nullptr;
pipeline_layout = get_resource_manager()->create_pipeline_layout(desc);
}
void Vulkan_Demo::create_pipeline() {
Shader_Module vertex_shader("../../shaders/vert.spv");
Shader_Module fragment_shader("../../shaders/frag.spv");
auto get_shader_stage_desc = [](VkShaderStageFlagBits stage, VkShaderModule shader_module, const char* entry) {
VkPipelineShaderStageCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.stage = stage;
desc.module = shader_module;
desc.pName = entry;
desc.pSpecializationInfo = nullptr;
return desc;
};
std::vector<VkPipelineShaderStageCreateInfo> shader_stages_state {
get_shader_stage_desc(VK_SHADER_STAGE_VERTEX_BIT, vertex_shader.handle, "main"),
get_shader_stage_desc(VK_SHADER_STAGE_FRAGMENT_BIT, fragment_shader.handle, "main")
};
VkPipelineVertexInputStateCreateInfo vertex_input_state;
vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_state.pNext = nullptr;
vertex_input_state.flags = 0;
auto bindings = Vertex::get_bindings();
vertex_input_state.vertexBindingDescriptionCount = (uint32_t)bindings.size();
vertex_input_state.pVertexBindingDescriptions = bindings.data();
auto attribs = Vertex::get_attributes();
vertex_input_state.vertexAttributeDescriptionCount = (uint32_t)attribs.size();
vertex_input_state.pVertexAttributeDescriptions = attribs.data();
VkPipelineInputAssemblyStateCreateInfo input_assembly_state;
input_assembly_state.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly_state.pNext = nullptr;
input_assembly_state.flags = 0;
input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
input_assembly_state.primitiveRestartEnable = VK_FALSE;
VkViewport viewport;
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)window_width;
viewport.height = (float)window_height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor;
scissor.offset = {0, 0};
scissor.extent = {(uint32_t)window_width, (uint32_t)window_height};
VkPipelineViewportStateCreateInfo viewport_state;
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state.pNext = nullptr;
viewport_state.flags = 0;
viewport_state.viewportCount = 1;
viewport_state.pViewports = &viewport;
viewport_state.scissorCount = 1;
viewport_state.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterization_state;
rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterization_state.pNext = nullptr;
rasterization_state.flags = 0;
rasterization_state.depthClampEnable = VK_FALSE;
rasterization_state.rasterizerDiscardEnable = VK_FALSE;
rasterization_state.polygonMode = VK_POLYGON_MODE_FILL;
rasterization_state.cullMode = VK_CULL_MODE_BACK_BIT;
rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterization_state.depthBiasEnable = VK_FALSE;
rasterization_state.depthBiasConstantFactor = 0.0f;
rasterization_state.depthBiasClamp = 0.0f;
rasterization_state.depthBiasSlopeFactor = 0.0f;
rasterization_state.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo multisample_state;
multisample_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisample_state.pNext = nullptr;
multisample_state.flags = 0;
multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisample_state.sampleShadingEnable = VK_FALSE;
multisample_state.minSampleShading = 1.0f;
multisample_state.pSampleMask = nullptr;
multisample_state.alphaToCoverageEnable = VK_FALSE;
multisample_state.alphaToOneEnable = VK_FALSE;
VkPipelineDepthStencilStateCreateInfo depth_stencil_state;
depth_stencil_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depth_stencil_state.pNext = nullptr;
depth_stencil_state.flags = 0;
depth_stencil_state.depthTestEnable = VK_TRUE;
depth_stencil_state.depthWriteEnable = VK_TRUE;
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_LESS;
depth_stencil_state.depthBoundsTestEnable = VK_FALSE;
depth_stencil_state.stencilTestEnable = VK_FALSE;
depth_stencil_state.front = {};
depth_stencil_state.back = {};
depth_stencil_state.minDepthBounds = 0.0;
depth_stencil_state.maxDepthBounds = 0.0;
VkPipelineColorBlendAttachmentState attachment_blend_state;
attachment_blend_state.blendEnable = VK_FALSE;
attachment_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
attachment_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
attachment_blend_state.colorBlendOp = VK_BLEND_OP_ADD;
attachment_blend_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
attachment_blend_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
attachment_blend_state.alphaBlendOp = VK_BLEND_OP_ADD;
attachment_blend_state.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
VkPipelineColorBlendStateCreateInfo blend_state;
blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
blend_state.pNext = nullptr;
blend_state.flags = 0;
blend_state.logicOpEnable = VK_FALSE;
blend_state.logicOp = VK_LOGIC_OP_COPY;
blend_state.attachmentCount = 1;
blend_state.pAttachments = &attachment_blend_state;
blend_state.blendConstants[0] = 0.0f;
blend_state.blendConstants[1] = 0.0f;
blend_state.blendConstants[2] = 0.0f;
blend_state.blendConstants[3] = 0.0f;
VkGraphicsPipelineCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.stageCount = static_cast<uint32_t>(shader_stages_state.size());
desc.pStages = shader_stages_state.data();
desc.pVertexInputState = &vertex_input_state;
desc.pInputAssemblyState = &input_assembly_state;
desc.pTessellationState = nullptr;
desc.pViewportState = &viewport_state;
desc.pRasterizationState = &rasterization_state;
desc.pMultisampleState = &multisample_state;
desc.pDepthStencilState = &depth_stencil_state;
desc.pColorBlendState = &blend_state;
desc.pDynamicState = nullptr;
desc.layout = pipeline_layout;
desc.renderPass = render_pass;
desc.subpass = 0;
desc.basePipelineHandle = VK_NULL_HANDLE;
desc.basePipelineIndex = -1;
pipeline = get_resource_manager()->create_graphics_pipeline(desc);
}
void Vulkan_Demo::upload_geometry() {
Model model = load_model();
model_indices_count = static_cast<uint32_t>(model.indices.size());
{
const VkDeviceSize size = model.vertices.size() * sizeof(model.vertices[0]);
vertex_buffer = create_buffer(size, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
VkBuffer staging_buffer = create_staging_buffer(size, model.vertices.data());
Defer_Action destroy_staging_buffer([&staging_buffer, this]() {
vkDestroyBuffer(get_device(), staging_buffer, nullptr);
});
record_and_run_commands(command_pool, get_queue(), [&staging_buffer, &size, this](VkCommandBuffer command_buffer) {
VkBufferCopy region;
region.srcOffset = 0;
region.dstOffset = 0;
region.size = size;
vkCmdCopyBuffer(command_buffer, staging_buffer, vertex_buffer, 1, &region);
});
}
{
const VkDeviceSize size = model.indices.size() * sizeof(model.indices[0]);
index_buffer = create_buffer(size, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
VkBuffer staging_buffer = create_staging_buffer(size, model.indices.data());
Defer_Action destroy_staging_buffer([&staging_buffer, this]() {
vkDestroyBuffer(get_device(), staging_buffer, nullptr);
});
record_and_run_commands(command_pool, get_queue(), [&staging_buffer, &size, this](VkCommandBuffer command_buffer) {
VkBufferCopy region;
region.srcOffset = 0;
region.dstOffset = 0;
region.size = size;
vkCmdCopyBuffer(command_buffer, staging_buffer, index_buffer, 1, &region);
});
}
}
void Vulkan_Demo::record_render_scene() {
VkCommandBufferAllocateInfo alloc_info;
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.pNext = nullptr;
alloc_info.commandPool = command_pool;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
alloc_info.commandBufferCount = 1;
VkResult result = vkAllocateCommandBuffers(get_device(), &alloc_info, &render_scene_command_buffer);
check_vk_result(result, "vkAllocateCommandBuffers");
VkCommandBufferInheritanceInfo inheritance_info;
inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
inheritance_info.pNext = nullptr;
inheritance_info.renderPass = render_pass;
inheritance_info.subpass = 0;
inheritance_info.framebuffer = VK_NULL_HANDLE;
inheritance_info.occlusionQueryEnable = VK_FALSE;
inheritance_info.queryFlags = 0;
inheritance_info.pipelineStatistics = 0;
VkCommandBufferBeginInfo begin_info;
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.pNext = nullptr;
begin_info.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT | VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
begin_info.pInheritanceInfo = &inheritance_info;
result = vkBeginCommandBuffer(render_scene_command_buffer, &begin_info);
check_vk_result(result, "vkBeginCommandBuffer");
const VkDeviceSize offset = 0;
vkCmdBindVertexBuffers(render_scene_command_buffer, 0, 1, &vertex_buffer, &offset);
vkCmdBindIndexBuffer(render_scene_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdBindDescriptorSets(render_scene_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, nullptr);
vkCmdBindPipeline(render_scene_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdDrawIndexed(render_scene_command_buffer, model_indices_count, 1, 0, 0, 0);
result = vkEndCommandBuffer(render_scene_command_buffer);
check_vk_result(result, "vkEndCommandBuffer");
}
void Vulkan_Demo::record_render_frame() {
VkCommandBufferAllocateInfo alloc_info;
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.pNext = nullptr;
alloc_info.commandPool = command_pool;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandBufferCount = static_cast<uint32_t>(get_swapchain_image_views().size());
render_frame_command_buffers.resize(get_swapchain_image_views().size());
VkResult result = vkAllocateCommandBuffers(get_device(), &alloc_info, render_frame_command_buffers.data());
check_vk_result(result, "vkAllocateCommandBuffers");
VkCommandBufferBeginInfo begin_info;
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.pNext = nullptr;
begin_info.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
begin_info.pInheritanceInfo = nullptr;
std::array<VkClearValue, 2> clear_values;
clear_values[0].color = {0.3f, 0.2f, 0.1f, 0.0f};
clear_values[1].depthStencil = {1.0, 0};
VkRenderPassBeginInfo render_pass_begin_info;
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_begin_info.pNext = nullptr;
render_pass_begin_info.renderPass = render_pass;
render_pass_begin_info.framebuffer = VK_NULL_HANDLE; // will be initialized later in the recording loop
render_pass_begin_info.renderArea.offset = {0, 0};
render_pass_begin_info.renderArea.extent = {(uint32_t)window_width, (uint32_t)window_height};
render_pass_begin_info.clearValueCount = static_cast<uint32_t>(clear_values.size());
render_pass_begin_info.pClearValues = clear_values.data();
VkBufferMemoryBarrier barrier;
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
barrier.pNext = nullptr;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.buffer = uniform_buffer;
barrier.offset = 0;
barrier.size = sizeof(Uniform_Buffer_Object);
for (std::size_t i = 0; i < render_frame_command_buffers.size(); i++) {
VkResult result = vkBeginCommandBuffer(render_frame_command_buffers[i], &begin_info);
check_vk_result(result, "vkBeginCommandBuffer");
VkBufferCopy region;
region.srcOffset = 0;
region.dstOffset = 0;
region.size = sizeof(Uniform_Buffer_Object);
vkCmdCopyBuffer(render_frame_command_buffers[i], uniform_staging_buffer, uniform_buffer, 1, &region);
vkCmdPipelineBarrier(render_frame_command_buffers[i], VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0,
0, nullptr, 1, &barrier, 0, nullptr);
render_pass_begin_info.framebuffer = framebuffers[i];
vkCmdBeginRenderPass(render_frame_command_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
vkCmdExecuteCommands(render_frame_command_buffers[i], 1, &render_scene_command_buffer);
vkCmdEndRenderPass(render_frame_command_buffers[i]);
result = vkEndCommandBuffer(render_frame_command_buffers[i]);
check_vk_result(result, "vkEndCommandBuffer");
}
}
void Vulkan_Demo::update_uniform_buffer() {
static auto start_time = std::chrono::high_resolution_clock::now();
auto current_time = std::chrono::high_resolution_clock::now();
float time = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - start_time).count() / 1000.f;
Uniform_Buffer_Object ubo;
ubo.model = glm::rotate(glm::mat4(), time * glm::radians(30.0f), glm::vec3(0, 1, 0)) *
glm::scale(glm::mat4(), glm::vec3(0.015f));
ubo.view = glm::lookAt(glm::vec3(0.5, 1.4, 2.8), glm::vec3(0, 0.7, 0), glm::vec3(0, 1, 0));
// Vulkan clip space has inverted Y and half Z.
const glm::mat4 clip(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.5f, 0.0f,
0.0f, 0.0f, 0.5f, 1.0f);
ubo.proj = clip * glm::perspective(glm::radians(45.0f), window_width / (float)window_height, 0.1f, 50.0f);
void* data;
VkResult result = vkMapMemory(get_device(), uniform_staging_buffer_memory, 0, sizeof(ubo), 0, &data);
check_vk_result(result, "vkMapMemory");
memcpy(data, &ubo, sizeof(ubo));
vkUnmapMemory(get_device(), uniform_staging_buffer_memory);
}
void Vulkan_Demo::run_frame() {
update_uniform_buffer();
uint32_t swapchain_image_index;
VkResult result = vkAcquireNextImageKHR(get_device(), get_swapchain(), UINT64_MAX, image_acquired, VK_NULL_HANDLE, &swapchain_image_index);
check_vk_result(result, "vkAcquireNextImageKHR");
VkPipelineStageFlags wait_dst_stage_mask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo submit_info;
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.pNext = nullptr;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &image_acquired;
submit_info.pWaitDstStageMask = &wait_dst_stage_mask;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &render_frame_command_buffers[swapchain_image_index];
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &rendering_finished;
result = vkQueueSubmit(get_queue(), 1, &submit_info, VK_NULL_HANDLE);
check_vk_result(result, "vkQueueSubmit");
VkSwapchainKHR swapchain = get_swapchain();
VkPresentInfoKHR present_info;
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.pNext = nullptr;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &rendering_finished;
present_info.swapchainCount = 1;
present_info.pSwapchains = &swapchain;
present_info.pImageIndices = &swapchain_image_index;
present_info.pResults = nullptr;
result = vkQueuePresentKHR(get_queue(), &present_info);
check_vk_result(result, "vkQueuePresentKHR");
}

View File

@ -0,0 +1,69 @@
#pragma once
#include <memory>
#include <vector>
#include "vk_definitions.h"
struct SDL_SysWMinfo;
class Vulkan_Demo {
public:
Vulkan_Demo(int window_width, int window_height, const SDL_SysWMinfo& window_sys_info);
~Vulkan_Demo();
void run_frame();
private:
void create_command_pool();
void create_descriptor_pool();
void create_uniform_buffer();
void create_texture();
void create_texture_sampler();
void create_depth_buffer_resources();
void create_descriptor_set_layout();
void create_descriptor_set();
void create_render_pass();
void create_framebuffers();
void create_pipeline_layout();
void create_pipeline();
void upload_geometry();
void record_render_scene();
void record_render_frame();
void update_uniform_buffer();
private:
const int window_width = 0;
const int window_height = 0;
VkSemaphore image_acquired = VK_NULL_HANDLE;
VkSemaphore rendering_finished = VK_NULL_HANDLE;
VkCommandPool command_pool = VK_NULL_HANDLE;
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
VkBuffer uniform_staging_buffer = VK_NULL_HANDLE;
VkDeviceMemory uniform_staging_buffer_memory = VK_NULL_HANDLE;
VkBuffer uniform_buffer = VK_NULL_HANDLE;
VkImage texture_image = VK_NULL_HANDLE;
VkImageView texture_image_view = VK_NULL_HANDLE;
VkSampler texture_image_sampler = VK_NULL_HANDLE;
VkImage depth_image = VK_NULL_HANDLE;
VkImageView depth_image_view = VK_NULL_HANDLE;
VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE;
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
VkRenderPass render_pass = VK_NULL_HANDLE;
std::vector<VkFramebuffer> framebuffers;
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
VkPipeline pipeline = VK_NULL_HANDLE;
VkBuffer vertex_buffer = VK_NULL_HANDLE;
VkBuffer index_buffer = VK_NULL_HANDLE;
uint32_t model_indices_count = 0;
std::vector<VkCommandBuffer> render_frame_command_buffers; // command buffer per swapchain image
VkCommandBuffer render_scene_command_buffer;
};

View File

@ -0,0 +1,333 @@
#include "vk_init.h"
#include "vk_utils.h"
#define SDL_MAIN_HANDLED
#include "sdl/SDL_syswm.h"
#include <algorithm>
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
struct Vulkan_Globals {
VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physical_device = VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE;
uint32_t queue_family_index = 0;
VkDevice device = VK_NULL_HANDLE;
VkQueue queue = VK_NULL_HANDLE;
VkSurfaceFormatKHR surface_format = {};
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
std::vector<VkImage> swapchain_images;
std::vector<VkImageView> swapchain_image_views;
};
static Vulkan_Globals vulkan_globals;
static const std::vector<const char*> instance_extensions = {
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_WIN32_SURFACE_EXTENSION_NAME
};
static const std::vector<const char*> device_extensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
static bool is_extension_available(const std::vector<VkExtensionProperties>& properties, const char* extension_name) {
for (const auto& property : properties) {
if (strcmp(property.extensionName, extension_name) == 0)
return true;
}
return false;
}
static uint32_t select_queue_family(VkPhysicalDevice physical_device, VkSurfaceKHR surface) {
uint32_t queue_family_count;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr);
std::vector<VkQueueFamilyProperties> queue_families(queue_family_count);
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_families.data());
// select queue family with presentation and graphics support
for (uint32_t i = 0; i < queue_family_count; i++) {
VkBool32 presentation_supported;
auto result = vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, i, surface, &presentation_supported);
check_vk_result(result, "vkGetPhysicalDeviceSurfaceSupportKHR");
if (presentation_supported && (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
return i;
}
error("failed to find queue family");
return -1;
}
static VkInstance create_instance() {
uint32_t count = 0;
VkResult result = vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
check_vk_result(result, "vkEnumerateInstanceExtensionProperties");
std::vector<VkExtensionProperties> extension_properties(count);
result = vkEnumerateInstanceExtensionProperties(nullptr, &count, extension_properties.data());
check_vk_result(result, "vkEnumerateInstanceExtensionProperties");
for (auto name : instance_extensions) {
if (!is_extension_available(extension_properties, name))
error(std::string("required instance extension is not available: ") + name);
}
VkInstanceCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.pApplicationInfo = nullptr;
desc.enabledLayerCount = 0;
desc.ppEnabledLayerNames = nullptr;
desc.enabledExtensionCount = static_cast<uint32_t>(instance_extensions.size());
desc.ppEnabledExtensionNames = instance_extensions.data();
VkInstance instance;
result = vkCreateInstance(&desc, nullptr, &instance);
check_vk_result(result, "vkCreateInstance");
return instance;
}
static VkPhysicalDevice select_physical_device(VkInstance instance) {
uint32_t count;
VkResult result = vkEnumeratePhysicalDevices(instance, &count, nullptr);
check_vk_result(result, "vkEnumeratePhysicalDevices");
if (count == 0)
error("no physical device found");
std::vector<VkPhysicalDevice> physical_devices(count);
result = vkEnumeratePhysicalDevices(instance, &count, physical_devices.data());
check_vk_result(result, "vkEnumeratePhysicalDevices");
return physical_devices[0]; // just get the first one
}
static VkSurfaceKHR create_surface(VkInstance instance, const SDL_SysWMinfo& window_sys_info) {
VkWin32SurfaceCreateInfoKHR desc;
desc.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
desc.pNext = nullptr;
desc.flags = 0;
desc.hinstance = ::GetModuleHandle(nullptr);
desc.hwnd = window_sys_info.info.win.window;
VkSurfaceKHR surface;
VkResult result = vkCreateWin32SurfaceKHR(instance, &desc, nullptr, &surface);
check_vk_result(result, "vkCreateWin32SurfaceKHR");
return surface;
}
static VkDevice create_device(VkPhysicalDevice physical_device, uint32_t queue_family_index) {
uint32_t count = 0;
VkResult result = vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &count, nullptr);
check_vk_result(result, "vkEnumerateDeviceExtensionProperties");
std::vector<VkExtensionProperties> extension_properties(count);
result = vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &count, extension_properties.data());
check_vk_result(result, "vkEnumerateDeviceExtensionProperties");
for (auto name : device_extensions) {
if (!is_extension_available(extension_properties, name))
error(std::string("required device extension is not available: ") + name);
}
const float priority = 1.0;
VkDeviceQueueCreateInfo queue_desc;
queue_desc.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_desc.pNext = nullptr;
queue_desc.flags = 0;
queue_desc.queueFamilyIndex = queue_family_index;
queue_desc.queueCount = 1;
queue_desc.pQueuePriorities = &priority;
VkDeviceCreateInfo device_desc;
device_desc.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_desc.pNext = nullptr;
device_desc.flags = 0;
device_desc.queueCreateInfoCount = 1;
device_desc.pQueueCreateInfos = &queue_desc;
device_desc.enabledLayerCount = 0;
device_desc.ppEnabledLayerNames = nullptr;
device_desc.enabledExtensionCount = static_cast<uint32_t>(device_extensions.size());
device_desc.ppEnabledExtensionNames = device_extensions.data();
device_desc.pEnabledFeatures = nullptr;
VkDevice device;
result = vkCreateDevice(physical_device, &device_desc, nullptr, &device);
check_vk_result(result, "vkCreateDevice");
return device;
}
static VkSurfaceFormatKHR select_surface_format(VkPhysicalDevice physical_device, VkSurfaceKHR surface) {
uint32_t format_count;
VkResult result = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &format_count, nullptr);
check_vk_result(result, "vkGetPhysicalDeviceSurfaceFormatsKHR");
assert(format_count > 0);
std::vector<VkSurfaceFormatKHR> candidates(format_count);
result = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &format_count, candidates.data());
check_vk_result(result, "vkGetPhysicalDeviceSurfaceFormatsKHR");
// special case that means we can choose any format
if (candidates.size() == 1 && candidates[0].format == VK_FORMAT_UNDEFINED) {
VkSurfaceFormatKHR surface_format;
surface_format.format = VK_FORMAT_R8G8B8A8_UNORM;
surface_format.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
return surface_format;
}
return candidates[0];
}
static VkSwapchainKHR create_swapchain(VkPhysicalDevice physical_device, VkDevice device, VkSurfaceKHR surface, VkSurfaceFormatKHR surface_format) {
VkSurfaceCapabilitiesKHR surface_caps;
VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surface_caps);
check_vk_result(result, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
VkExtent2D image_extent = surface_caps.currentExtent;
if (image_extent.width == 0xffffffff && image_extent.height == 0xffffffff) {
image_extent.width = std::min(surface_caps.maxImageExtent.width, std::max(surface_caps.minImageExtent.width, 640u));
image_extent.height = std::min(surface_caps.maxImageExtent.height, std::max(surface_caps.minImageExtent.height, 480u));
}
// transfer destination usage is required by image clear operations
if ((surface_caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0)
error("VK_IMAGE_USAGE_TRANSFER_DST_BIT is not supported by the swapchain");
VkImageUsageFlags image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
// determine present mode and swapchain image count
uint32_t present_mode_count;
result = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_mode_count, nullptr);
check_vk_result(result, "vkGetPhysicalDeviceSurfacePresentModesKHR");
std::vector<VkPresentModeKHR> present_modes(present_mode_count);
result = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_mode_count, present_modes.data());
check_vk_result(result, "vkGetPhysicalDeviceSurfacePresentModesKHR");
VkPresentModeKHR present_mode;
uint32_t image_count;
auto it = std::find(present_modes.cbegin(), present_modes.cend(), VK_PRESENT_MODE_MAILBOX_KHR);
if (it != present_modes.cend()) {
present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
image_count = std::max(3u, surface_caps.minImageCount);
if (surface_caps.maxImageCount > 0) {
image_count = std::min(image_count, surface_caps.maxImageCount);
}
}
else {
present_mode = VK_PRESENT_MODE_FIFO_KHR;
image_count = surface_caps.minImageCount;
}
// create swap chain
VkSwapchainCreateInfoKHR desc;
desc.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
desc.pNext = nullptr;
desc.flags = 0;
desc.surface = surface;
desc.minImageCount = image_count;
desc.imageFormat = surface_format.format;
desc.imageColorSpace = surface_format.colorSpace;
desc.imageExtent = image_extent;
desc.imageArrayLayers = 1;
desc.imageUsage = image_usage;
desc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
desc.queueFamilyIndexCount = 0;
desc.pQueueFamilyIndices = nullptr;
desc.preTransform = surface_caps.currentTransform;
desc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
desc.presentMode = present_mode;
desc.clipped = VK_TRUE;
desc.oldSwapchain = VK_NULL_HANDLE;
VkSwapchainKHR swapchain;
result = vkCreateSwapchainKHR(device, &desc, nullptr, &swapchain);
check_vk_result(result, "vkCreateSwapchainKHR");
return swapchain;
}
void initialize_vulkan(const SDL_SysWMinfo& window_sys_info) {
auto& g = vulkan_globals;
g.instance = create_instance();
g.physical_device = select_physical_device(g.instance);
g.surface = create_surface(g.instance, window_sys_info);
g.queue_family_index = select_queue_family(g.physical_device, g.surface);
g.device = create_device(g.physical_device, g.queue_family_index);
vkGetDeviceQueue(g.device, g.queue_family_index, 0, &g.queue);
g.surface_format = select_surface_format(g.physical_device, g.surface);
g.swapchain = create_swapchain(g.physical_device, g.device, g.surface, g.surface_format);
uint32_t image_count;
VkResult result = vkGetSwapchainImagesKHR(g.device, g.swapchain, &image_count, nullptr);
check_vk_result(result, "vkGetSwapchainImagesKHR");
g.swapchain_images.resize(image_count);
result = vkGetSwapchainImagesKHR(g.device, g.swapchain, &image_count, g.swapchain_images.data());
check_vk_result(result, "vkGetSwapchainImagesKHR");
g.swapchain_image_views.resize(image_count);
for (std::size_t i = 0; i < image_count; i++) {
VkImageViewCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.image = g.swapchain_images[i];
desc.viewType = VK_IMAGE_VIEW_TYPE_2D;
desc.format = g.surface_format.format;
desc.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
desc.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
desc.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
desc.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
desc.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
desc.subresourceRange.baseMipLevel = 0;
desc.subresourceRange.levelCount = 1;
desc.subresourceRange.baseArrayLayer = 0;
desc.subresourceRange.layerCount = 1;
result = vkCreateImageView(g.device, &desc, nullptr, &g.swapchain_image_views[i]);
check_vk_result(result, "vkCreateImageView");
}
}
void deinitialize_vulkan() {
auto& g = vulkan_globals;
for (auto image_view : g.swapchain_image_views) {
vkDestroyImageView(g.device, image_view, nullptr);
}
vkDestroySwapchainKHR(g.device, g.swapchain, nullptr);
vkDestroyDevice(g.device, nullptr);
vkDestroySurfaceKHR(g.instance, g.surface, nullptr);
vkDestroyInstance(g.instance, nullptr);
g = Vulkan_Globals();
}
VkPhysicalDevice get_physical_device() {
return vulkan_globals.physical_device;
}
VkDevice get_device() {
return vulkan_globals.device;
}
uint32_t get_queue_family_index() {
return vulkan_globals.queue_family_index;
}
VkQueue get_queue() {
return vulkan_globals.queue;
}
VkSwapchainKHR get_swapchain() {
return vulkan_globals.swapchain;
}
VkFormat get_swapchain_image_format() {
return vulkan_globals.surface_format.format;
}
const std::vector<VkImageView>& get_swapchain_image_views() {
return vulkan_globals.swapchain_image_views;
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "vk_definitions.h"
#include <vector>
struct SDL_SysWMinfo;
void initialize_vulkan(const SDL_SysWMinfo& window_sys_info);
void deinitialize_vulkan();
VkPhysicalDevice get_physical_device();
VkDevice get_device();
uint32_t get_queue_family_index();
VkQueue get_queue();
VkSwapchainKHR get_swapchain();
VkFormat get_swapchain_image_format();
const std::vector<VkImageView>& get_swapchain_image_views();

View File

@ -0,0 +1,174 @@
#include "vk_resource_manager.h"
static Resource_Manager resource_manager;
Resource_Manager* get_resource_manager() {
return &resource_manager;
}
void Resource_Manager::initialize(VkDevice device) {
this->device = device;
}
void Resource_Manager::release_resources() {
for (auto semaphore : semaphores) {
vkDestroySemaphore(device, semaphore, nullptr);
}
semaphores.clear();
for (auto command_pool : command_pools) {
vkDestroyCommandPool(device, command_pool, nullptr);
}
command_pools.clear();
for (auto descriptor_pool : descriptor_pools) {
vkDestroyDescriptorPool(device, descriptor_pool, nullptr);
}
descriptor_pools.clear();
for (auto buffer : buffers) {
vkDestroyBuffer(device, buffer, nullptr);
}
buffers.clear();
for (auto image : images) {
vkDestroyImage(device, image, nullptr);
}
images.clear();
for (auto image_view : image_views) {
vkDestroyImageView(device, image_view, nullptr);
}
image_views.clear();
for (auto sampler : samplers) {
vkDestroySampler(device, sampler, nullptr);
}
samplers.clear();
for (auto render_pass : render_passes) {
vkDestroyRenderPass(device, render_pass, nullptr);
}
render_passes.clear();
for (auto framebuffer : framebuffers) {
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
framebuffers.clear();
for (auto descriptor_set_layout : descriptor_set_layouts) {
vkDestroyDescriptorSetLayout(device, descriptor_set_layout, nullptr);
}
descriptor_set_layouts.clear();
for (auto pipeline_layout : pipeline_layouts) {
vkDestroyPipelineLayout(device, pipeline_layout, nullptr);
}
pipeline_layouts.clear();
for (auto pipeline : graphics_pipelines) {
vkDestroyPipeline(device, pipeline, nullptr);
}
graphics_pipelines.clear();
}
VkSemaphore Resource_Manager::create_semaphore() {
VkSemaphoreCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
VkSemaphore semaphore;
VkResult result = vkCreateSemaphore(device, &desc, nullptr, &semaphore);
check_vk_result(result, "vkCreateSemaphore");
semaphores.push_back(semaphore);
return semaphore;
}
VkCommandPool Resource_Manager::create_command_pool(const VkCommandPoolCreateInfo& desc) {
VkCommandPool command_pool;
VkResult result = vkCreateCommandPool(device, &desc, nullptr, &command_pool);
check_vk_result(result, "vkCreateCommandPool");
command_pools.push_back(command_pool);
return command_pool;
}
VkDescriptorPool Resource_Manager::create_descriptor_pool(const VkDescriptorPoolCreateInfo& desc) {
VkDescriptorPool descriptor_pool;
VkResult result = vkCreateDescriptorPool(device, &desc, nullptr, &descriptor_pool);
check_vk_result(result, "vkCreateDescriptorPool");
descriptor_pools.push_back(descriptor_pool);
return descriptor_pool;
}
VkBuffer Resource_Manager::create_buffer(const VkBufferCreateInfo& desc) {
VkBuffer buffer;
VkResult result = vkCreateBuffer(device, &desc, nullptr, &buffer);
check_vk_result(result, "vkCreateBuffer");
buffers.push_back(buffer);
return buffer;
}
VkImage Resource_Manager::create_image(const VkImageCreateInfo& desc) {
VkImage image;
VkResult result = vkCreateImage(device, &desc, nullptr, &image);
check_vk_result(result, "vkCreateImage");
images.push_back(image);
return image;
}
VkImageView Resource_Manager::create_image_view(const VkImageViewCreateInfo& desc) {
VkImageView image_view;
VkResult result = vkCreateImageView(device, &desc, nullptr, &image_view);
check_vk_result(result, "vkCreateImageView");
image_views.push_back(image_view);
return image_view;
}
VkSampler Resource_Manager::create_sampler(const VkSamplerCreateInfo& desc) {
VkSampler sampler;
VkResult result = vkCreateSampler(device, &desc, nullptr, &sampler);
check_vk_result(result, "vkCreateSampler");
samplers.push_back(sampler);
return sampler;
}
VkRenderPass Resource_Manager::create_render_pass(const VkRenderPassCreateInfo& desc) {
VkRenderPass render_pass;
VkResult result = vkCreateRenderPass(device, &desc, nullptr, &render_pass);
check_vk_result(result, "vkCreateRenderPass");
render_passes.push_back(render_pass);
return render_pass;
}
VkFramebuffer Resource_Manager::create_framebuffer(const VkFramebufferCreateInfo& desc) {
VkFramebuffer framebuffer;
VkResult result = vkCreateFramebuffer(device, &desc, nullptr, &framebuffer);
check_vk_result(result, "vkCreateFramebuffer");
framebuffers.push_back(framebuffer);
return framebuffer;
}
VkDescriptorSetLayout Resource_Manager::create_descriptor_set_layout(const VkDescriptorSetLayoutCreateInfo& desc) {
VkDescriptorSetLayout descriptor_set_layout;
VkResult result = vkCreateDescriptorSetLayout(device, &desc, nullptr, &descriptor_set_layout);
check_vk_result(result, "vkCreateDescriptorSetLayout");
descriptor_set_layouts.push_back(descriptor_set_layout);
return descriptor_set_layout;
}
VkPipelineLayout Resource_Manager::create_pipeline_layout(const VkPipelineLayoutCreateInfo& desc) {
VkPipelineLayout pipeline_layout;
VkResult result = vkCreatePipelineLayout(device, &desc, nullptr, &pipeline_layout);
check_vk_result(result, "vkCreatePipelineLayout");
pipeline_layouts.push_back(pipeline_layout);
return pipeline_layout;
}
VkPipeline Resource_Manager::create_graphics_pipeline(const VkGraphicsPipelineCreateInfo& desc) {
VkPipeline pipeline;
VkResult result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &desc, nullptr, &pipeline);
check_vk_result(result, "vkCreateGraphicsPipelines");
graphics_pipelines.push_back(pipeline);
return pipeline;
}

View File

@ -0,0 +1,40 @@
#pragma once
#include "vk_utils.h"
#include <vector>
class Resource_Manager {
public:
void initialize(VkDevice device);
void release_resources();
VkSemaphore create_semaphore();
VkCommandPool create_command_pool(const VkCommandPoolCreateInfo& desc);
VkDescriptorPool create_descriptor_pool(const VkDescriptorPoolCreateInfo& desc);
VkBuffer create_buffer(const VkBufferCreateInfo& desc);
VkImage create_image(const VkImageCreateInfo& desc);
VkImageView create_image_view(const VkImageViewCreateInfo& desc);
VkSampler create_sampler(const VkSamplerCreateInfo& desc);
VkRenderPass create_render_pass(const VkRenderPassCreateInfo& desc);
VkFramebuffer create_framebuffer(const VkFramebufferCreateInfo& desc);
VkDescriptorSetLayout create_descriptor_set_layout(const VkDescriptorSetLayoutCreateInfo& desc);
VkPipelineLayout create_pipeline_layout(const VkPipelineLayoutCreateInfo& desc);
VkPipeline create_graphics_pipeline(const VkGraphicsPipelineCreateInfo& desc);
private:
VkDevice device = VK_NULL_HANDLE;
std::vector<VkSemaphore> semaphores;
std::vector<VkCommandPool> command_pools;
std::vector<VkDescriptorPool> descriptor_pools;
std::vector<VkBuffer> buffers;
std::vector<VkImage> images;
std::vector<VkImageView> image_views;
std::vector<VkSampler> samplers;
std::vector<VkDescriptorSetLayout> descriptor_set_layouts;
std::vector<VkRenderPass> render_passes;
std::vector<VkFramebuffer> framebuffers;
std::vector<VkPipelineLayout> pipeline_layouts;
std::vector<VkPipeline> graphics_pipelines;
};
Resource_Manager* get_resource_manager();

View File

@ -0,0 +1,348 @@
#include "vk_allocator.h"
#include "vk_resource_manager.h"
#include "vk_init.h"
#include "vk_utils.h"
#include <iostream>
#include <fstream>
void check_vk_result(VkResult result, const std::string& functionName) {
if (result < 0) {
error(functionName + " has returned error code with value " + std::to_string(result));
}
}
void error(const std::string& message) {
std::cout << message << std::endl;
throw std::runtime_error(message);
}
static std::vector<char> read_binary_file_contents(const std::string& file_name) {
std::ifstream stream(file_name, std::ios_base::binary | std::ios_base::ate);
auto file_size = stream.tellg();
stream.seekg(0, std::ios_base::beg);
std::vector<char> buffer(file_size);
stream.read(buffer.data(), file_size);
if (!stream)
throw std::runtime_error("failed to read file contents: " + file_name);
return buffer;
}
Shader_Module::Shader_Module(const std::string& spirv_file_name) {
auto data = read_binary_file_contents(spirv_file_name);
if (data.size() % 4 != 0)
error("SPIR-V binary file size is not multiple of 4: " + spirv_file_name);
VkShaderModuleCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.codeSize = data.size();
desc.pCode = reinterpret_cast<const uint32_t*>(data.data());
VkResult result = vkCreateShaderModule(get_device(), &desc, nullptr, &handle);
check_vk_result(result, "vkCreateShaderModule");
}
Shader_Module::~Shader_Module() {
vkDestroyShaderModule(get_device(), handle, nullptr);
}
void record_and_run_commands(VkCommandPool command_pool, VkQueue queue, std::function<void(VkCommandBuffer)> recorder) {
VkCommandBufferAllocateInfo alloc_info;
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.pNext = nullptr;
alloc_info.commandPool = command_pool;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandBufferCount = 1;
VkCommandBuffer command_buffer;
VkResult result = vkAllocateCommandBuffers(get_device(), &alloc_info, &command_buffer);
check_vk_result(result, "vkAllocateCommandBuffers");
VkCommandBufferBeginInfo begin_info;
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.pNext = nullptr;
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
begin_info.pInheritanceInfo = nullptr;
result = vkBeginCommandBuffer(command_buffer, &begin_info);
check_vk_result(result, "vkBeginCommandBuffer");
recorder(command_buffer);
result = vkEndCommandBuffer(command_buffer);
check_vk_result(result, "vkEndCommandBuffer");
VkSubmitInfo submit_info;
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.pNext = nullptr;
submit_info.waitSemaphoreCount = 0;
submit_info.pWaitSemaphores = nullptr;
submit_info.pWaitDstStageMask = nullptr;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
submit_info.signalSemaphoreCount = 0;
submit_info.pSignalSemaphores = nullptr;
result = vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
check_vk_result(result, "vkQueueSubmit");
result = vkQueueWaitIdle(queue);
check_vk_result(result, "vkQueueWaitIdle");
vkFreeCommandBuffers(get_device(), command_pool, 1, &command_buffer);
}
static bool has_depth_component(VkFormat format) {
switch (format) {
case VK_FORMAT_D16_UNORM:
case VK_FORMAT_X8_D24_UNORM_PACK32:
case VK_FORMAT_D32_SFLOAT:
case VK_FORMAT_D16_UNORM_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return true;
default:
return false;
}
}
static bool has_stencil_component(VkFormat format) {
switch (format) {
case VK_FORMAT_S8_UINT:
case VK_FORMAT_D16_UNORM_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return true;
default:
return false;
}
}
void record_image_layout_transition(VkCommandBuffer command_buffer, VkImage image, VkFormat format,
VkAccessFlags src_access_flags, VkImageLayout old_layout, VkAccessFlags dst_access_flags, VkImageLayout new_layout) {
VkImageMemoryBarrier barrier;
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.pNext = nullptr;
barrier.srcAccessMask = src_access_flags;
barrier.dstAccessMask = dst_access_flags;
barrier.oldLayout = old_layout;
barrier.newLayout = new_layout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
bool depth = has_depth_component(format);
bool stencil = has_stencil_component(format);
if (depth && stencil)
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
else if (depth)
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
else if (stencil)
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
else
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0,
0, nullptr, 0, nullptr, 1, &barrier);
}
VkImage create_texture(int image_width, int image_height, VkFormat format) {
VkImageCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.imageType = VK_IMAGE_TYPE_2D;
desc.format = format;
desc.extent.width = image_width;
desc.extent.height = image_height;
desc.extent.depth = 1;
desc.mipLevels = 1;
desc.arrayLayers = 1;
desc.samples = VK_SAMPLE_COUNT_1_BIT;
desc.tiling = VK_IMAGE_TILING_OPTIMAL;
desc.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
desc.queueFamilyIndexCount = 0;
desc.pQueueFamilyIndices = nullptr;
desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImage image = get_resource_manager()->create_image(desc);
VkDeviceMemory memory = get_allocator()->allocate_memory(image);
VkResult result = vkBindImageMemory(get_device(), image, memory, 0);
check_vk_result(result, "vkBindImageMemory");
return image;
}
VkImage create_staging_texture(int image_width, int image_height, VkFormat format, const uint8_t* pixels, int bytes_per_pixel) {
VkImageCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.imageType = VK_IMAGE_TYPE_2D;
desc.format = format;
desc.extent.width = image_width;
desc.extent.height = image_height;
desc.extent.depth = 1;
desc.mipLevels = 1;
desc.arrayLayers = 1;
desc.samples = VK_SAMPLE_COUNT_1_BIT;
desc.tiling = VK_IMAGE_TILING_LINEAR;
desc.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
desc.queueFamilyIndexCount = 0;
desc.pQueueFamilyIndices = nullptr;
desc.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
VkImage image;
VkResult result = vkCreateImage(get_device(), &desc, nullptr, &image);
check_vk_result(result, "vkCreateImage");
get_allocator()->get_shared_staging_memory().ensure_allocation_for_object(image);
VkDeviceMemory memory = get_allocator()->get_shared_staging_memory().get_handle();
result = vkBindImageMemory(get_device(), image, memory, 0);
check_vk_result(result, "vkBindImageMemory");
VkImageSubresource staging_image_subresource;
staging_image_subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
staging_image_subresource.mipLevel = 0;
staging_image_subresource.arrayLayer = 0;
VkSubresourceLayout staging_image_layout;
vkGetImageSubresourceLayout(get_device(), image, &staging_image_subresource, &staging_image_layout);
void* data;
result = vkMapMemory(get_device(), memory, 0, staging_image_layout.size, 0, &data);
check_vk_result(result, "vkMapMemory");
const int bytes_per_row = image_width * bytes_per_pixel;
if (staging_image_layout.rowPitch == bytes_per_row) {
memcpy(data, pixels, bytes_per_row * image_height);
} else {
auto bytes = static_cast<uint8_t*>(data);
for (int i = 0; i < image_height; i++) {
memcpy(&bytes[i * staging_image_layout.rowPitch], &pixels[i * bytes_per_row], bytes_per_row);
}
}
vkUnmapMemory(get_device(), memory);
return image;
}
VkImage create_depth_attachment_image(int image_width, int image_height, VkFormat format) {
VkImageCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.imageType = VK_IMAGE_TYPE_2D;
desc.format = format;
desc.extent.width = image_width;
desc.extent.height = image_height;
desc.extent.depth = 1;
desc.mipLevels = 1;
desc.arrayLayers = 1;
desc.samples = VK_SAMPLE_COUNT_1_BIT;
desc.tiling = VK_IMAGE_TILING_OPTIMAL;
desc.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
desc.queueFamilyIndexCount = 0;
desc.pQueueFamilyIndices = nullptr;
desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImage image = get_resource_manager()->create_image(desc);
VkDeviceMemory memory = get_allocator()->allocate_memory(image);
VkResult result = vkBindImageMemory(get_device(), image, memory, 0);
check_vk_result(result, "vkBindImageMemory");
return image;
}
VkImageView create_image_view(VkImage image, VkFormat format, VkImageAspectFlags aspect_flags) {
VkImageViewCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.image = image;
desc.viewType = VK_IMAGE_VIEW_TYPE_2D;
desc.format = format;
desc.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
desc.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
desc.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
desc.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
desc.subresourceRange.aspectMask = aspect_flags;
desc.subresourceRange.baseMipLevel = 0;
desc.subresourceRange.levelCount = 1;
desc.subresourceRange.baseArrayLayer = 0;
desc.subresourceRange.layerCount = 1;
return get_resource_manager()->create_image_view(desc);
}
VkBuffer create_buffer(VkDeviceSize size, VkBufferUsageFlags usage) {
VkBufferCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.size = size;
desc.usage = usage;
desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
desc.queueFamilyIndexCount = 0;
desc.pQueueFamilyIndices = nullptr;
VkBuffer buffer = get_resource_manager()->create_buffer(desc);
VkDeviceMemory memory = get_allocator()->allocate_memory(buffer);
VkResult result = vkBindBufferMemory(get_device(), buffer, memory, 0);
check_vk_result(result, "vkBindBufferMemory");
return buffer;
}
VkBuffer create_staging_buffer(VkDeviceSize size, const void* data) {
VkBufferCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.size = size;
desc.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
desc.queueFamilyIndexCount = 0;
desc.pQueueFamilyIndices = nullptr;
VkBuffer buffer;
VkResult result = vkCreateBuffer(get_device(), &desc, nullptr, &buffer);
check_vk_result(result, "vkCreateBuffer");
get_allocator()->get_shared_staging_memory().ensure_allocation_for_object(buffer);
VkDeviceMemory memory = get_allocator()->get_shared_staging_memory().get_handle();
result = vkBindBufferMemory(get_device(), buffer, memory, 0);
check_vk_result(result, "vkBindBufferMemory");
void* buffer_data;
result = vkMapMemory(get_device(), memory, 0, size, 0, &buffer_data);
check_vk_result(result, "vkMapMemory");
memcpy(buffer_data, data, size);
vkUnmapMemory(get_device(), memory);
return buffer;
}
VkBuffer create_permanent_staging_buffer(VkDeviceSize size, VkDeviceMemory& memory) {
VkBufferCreateInfo desc;
desc.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
desc.pNext = nullptr;
desc.flags = 0;
desc.size = size;
desc.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
desc.queueFamilyIndexCount = 0;
desc.pQueueFamilyIndices = nullptr;
VkBuffer buffer = get_resource_manager()->create_buffer(desc);
memory = get_allocator()->allocate_staging_memory(buffer);
VkResult result = vkBindBufferMemory(get_device(), buffer, memory, 0);
check_vk_result(result, "vkBindBufferMemory");
return buffer;
}

View File

@ -0,0 +1,43 @@
#pragma once
#include "vk_definitions.h"
#include <functional>
#include <string>
struct Defer_Action {
Defer_Action(std::function<void()> action)
: action(action) {}
~Defer_Action() {
action();
}
std::function<void()> action;
};
struct Shader_Module {
Shader_Module(const std::string& spriv_file_name);
~Shader_Module();
VkShaderModule handle;
};
// Errors
void check_vk_result(VkResult result, const std::string& function_name);
void error(const std::string& message);
// Command buffers
void record_and_run_commands(VkCommandPool command_pool, VkQueue queue, std::function<void(VkCommandBuffer)> recorder);
void record_image_layout_transition(
VkCommandBuffer command_buffer, VkImage image, VkFormat format,
VkAccessFlags src_access_flags, VkImageLayout old_layout,
VkAccessFlags dst_access_flags, VkImageLayout new_layout);
// Images
VkImage create_texture(int image_width, int image_height, VkFormat format);
VkImage create_staging_texture(int image_width, int image_height, VkFormat format, const uint8_t* pixels, int bytes_per_pixel);
VkImage create_depth_attachment_image(int image_width, int image_height, VkFormat format);
VkImageView create_image_view(VkImage image, VkFormat format, VkImageAspectFlags aspect_flags);
// Buffers
VkBuffer create_buffer(VkDeviceSize size, VkBufferUsageFlags usage);
VkBuffer create_staging_buffer(VkDeviceSize size, const void* data);
VkBuffer create_permanent_staging_buffer(VkDeviceSize size, VkDeviceMemory& memory);