Added source files from demo project as starting point for vulkan development.
This commit is contained in:
parent
10ef302b5f
commit
b482e778ff
133
source/engine/renderer/vk_allocator.cpp
Normal file
133
source/engine/renderer/vk_allocator.cpp
Normal 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;
|
||||
}
|
||||
52
source/engine/renderer/vk_allocator.h
Normal file
52
source/engine/renderer/vk_allocator.h
Normal 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();
|
||||
6
source/engine/renderer/vk_definitions.h
Normal file
6
source/engine/renderer/vk_definitions.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
#define NOMINMAX
|
||||
|
||||
#include "vulkan/vulkan.h"
|
||||
817
source/engine/renderer/vk_demo.cpp
Normal file
817
source/engine/renderer/vk_demo.cpp
Normal 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, ®ion);
|
||||
|
||||
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, ®ion);
|
||||
});
|
||||
}
|
||||
{
|
||||
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, ®ion);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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, ®ion);
|
||||
|
||||
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");
|
||||
}
|
||||
69
source/engine/renderer/vk_demo.h
Normal file
69
source/engine/renderer/vk_demo.h
Normal 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;
|
||||
};
|
||||
333
source/engine/renderer/vk_init.cpp
Normal file
333
source/engine/renderer/vk_init.cpp
Normal 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;
|
||||
}
|
||||
17
source/engine/renderer/vk_init.h
Normal file
17
source/engine/renderer/vk_init.h
Normal 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();
|
||||
174
source/engine/renderer/vk_resource_manager.cpp
Normal file
174
source/engine/renderer/vk_resource_manager.cpp
Normal 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;
|
||||
}
|
||||
40
source/engine/renderer/vk_resource_manager.h
Normal file
40
source/engine/renderer/vk_resource_manager.h
Normal 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();
|
||||
348
source/engine/renderer/vk_utils.cpp
Normal file
348
source/engine/renderer/vk_utils.cpp
Normal 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;
|
||||
}
|
||||
43
source/engine/renderer/vk_utils.h
Normal file
43
source/engine/renderer/vk_utils.h
Normal 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);
|
||||
Loading…
Reference in New Issue
Block a user