#include "vk_allocator.h" #include "vk_resource_manager.h" #include "vk_init.h" #include "vk_utils.h" #include #include 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 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 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(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 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(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; }