349 lines
13 KiB
C++
349 lines
13 KiB
C++
#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;
|
|
}
|