2020-06-18 14:18:07 +00:00
|
|
|
// Copyright 2016 Dolphin Emulator Project
|
|
|
|
// Copyright 2020 DuckStation Emulator Project
|
|
|
|
// Licensed under GPLv2+
|
|
|
|
// Refer to the LICENSE file included.
|
|
|
|
|
|
|
|
#include "util.h"
|
|
|
|
#include "../assert.h"
|
|
|
|
#include "../log.h"
|
|
|
|
#include "../string_util.h"
|
|
|
|
#include "context.h"
|
|
|
|
#include "shader_compiler.h"
|
|
|
|
|
|
|
|
namespace Vulkan {
|
|
|
|
namespace Util {
|
|
|
|
bool IsDepthFormat(VkFormat format)
|
|
|
|
{
|
|
|
|
switch (format)
|
|
|
|
{
|
|
|
|
case VK_FORMAT_D16_UNORM:
|
|
|
|
case VK_FORMAT_D16_UNORM_S8_UINT:
|
|
|
|
case VK_FORMAT_D24_UNORM_S8_UINT:
|
|
|
|
case VK_FORMAT_D32_SFLOAT:
|
|
|
|
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsCompressedFormat(VkFormat format)
|
|
|
|
{
|
|
|
|
switch (format)
|
|
|
|
{
|
|
|
|
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
|
|
|
|
case VK_FORMAT_BC2_UNORM_BLOCK:
|
|
|
|
case VK_FORMAT_BC3_UNORM_BLOCK:
|
|
|
|
case VK_FORMAT_BC7_UNORM_BLOCK:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VkFormat GetLinearFormat(VkFormat format)
|
|
|
|
{
|
|
|
|
switch (format)
|
|
|
|
{
|
|
|
|
case VK_FORMAT_R8_SRGB:
|
|
|
|
return VK_FORMAT_R8_UNORM;
|
|
|
|
case VK_FORMAT_R8G8_SRGB:
|
|
|
|
return VK_FORMAT_R8G8_UNORM;
|
|
|
|
case VK_FORMAT_R8G8B8_SRGB:
|
|
|
|
return VK_FORMAT_R8G8B8_UNORM;
|
|
|
|
case VK_FORMAT_R8G8B8A8_SRGB:
|
|
|
|
return VK_FORMAT_R8G8B8A8_UNORM;
|
|
|
|
case VK_FORMAT_B8G8R8_SRGB:
|
|
|
|
return VK_FORMAT_B8G8R8_UNORM;
|
|
|
|
case VK_FORMAT_B8G8R8A8_SRGB:
|
|
|
|
return VK_FORMAT_B8G8R8A8_UNORM;
|
|
|
|
default:
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 GetTexelSize(VkFormat format)
|
|
|
|
{
|
|
|
|
// Only contains pixel formats we use.
|
|
|
|
switch (format)
|
|
|
|
{
|
|
|
|
case VK_FORMAT_R32_SFLOAT:
|
|
|
|
return 4;
|
|
|
|
|
|
|
|
case VK_FORMAT_D32_SFLOAT:
|
|
|
|
return 4;
|
|
|
|
|
|
|
|
case VK_FORMAT_R8G8B8A8_UNORM:
|
|
|
|
return 4;
|
|
|
|
|
|
|
|
case VK_FORMAT_B8G8R8A8_UNORM:
|
|
|
|
return 4;
|
|
|
|
|
|
|
|
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
|
|
|
|
return 8;
|
|
|
|
|
|
|
|
case VK_FORMAT_BC2_UNORM_BLOCK:
|
|
|
|
case VK_FORMAT_BC3_UNORM_BLOCK:
|
|
|
|
case VK_FORMAT_BC7_UNORM_BLOCK:
|
|
|
|
return 16;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Panic("Unhandled pixel format");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 GetBlockSize(VkFormat format)
|
|
|
|
{
|
|
|
|
switch (format)
|
|
|
|
{
|
|
|
|
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
|
|
|
|
case VK_FORMAT_BC2_UNORM_BLOCK:
|
|
|
|
case VK_FORMAT_BC3_UNORM_BLOCK:
|
|
|
|
case VK_FORMAT_BC7_UNORM_BLOCK:
|
|
|
|
return 4;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VkRect2D ClampRect2D(const VkRect2D& rect, u32 width, u32 height)
|
|
|
|
{
|
|
|
|
VkRect2D out;
|
|
|
|
out.offset.x = std::clamp(rect.offset.x, 0, static_cast<int>(width - 1));
|
|
|
|
out.offset.y = std::clamp(rect.offset.y, 0, static_cast<int>(height - 1));
|
|
|
|
out.extent.width = std::min(rect.extent.width, width - static_cast<int>(rect.offset.x));
|
|
|
|
out.extent.height = std::min(rect.extent.height, height - static_cast<int>(rect.offset.y));
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkBlendFactor GetAlphaBlendFactor(VkBlendFactor factor)
|
|
|
|
{
|
|
|
|
switch (factor)
|
|
|
|
{
|
|
|
|
case VK_BLEND_FACTOR_SRC_COLOR:
|
|
|
|
return VK_BLEND_FACTOR_SRC_ALPHA;
|
|
|
|
case VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR:
|
|
|
|
return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
|
|
case VK_BLEND_FACTOR_DST_COLOR:
|
|
|
|
return VK_BLEND_FACTOR_DST_ALPHA;
|
|
|
|
case VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR:
|
|
|
|
return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
|
|
|
default:
|
|
|
|
return factor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetViewport(VkCommandBuffer command_buffer, int x, int y, int width, int height, float min_depth /*= 0.0f*/,
|
|
|
|
float max_depth /*= 1.0f*/)
|
|
|
|
{
|
|
|
|
const VkViewport vp{static_cast<float>(x),
|
|
|
|
static_cast<float>(y),
|
|
|
|
static_cast<float>(width),
|
|
|
|
static_cast<float>(height),
|
|
|
|
min_depth,
|
|
|
|
max_depth};
|
|
|
|
vkCmdSetViewport(command_buffer, 0, 1, &vp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetScissor(VkCommandBuffer command_buffer, int x, int y, int width, int height)
|
|
|
|
{
|
|
|
|
const VkRect2D scissor{{x, y}, {static_cast<u32>(width), static_cast<u32>(height)}};
|
|
|
|
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetViewportAndScissor(VkCommandBuffer command_buffer, int x, int y, int width, int height,
|
|
|
|
float min_depth /* = 0.0f */, float max_depth /* = 1.0f */)
|
|
|
|
{
|
|
|
|
const VkViewport vp{static_cast<float>(x),
|
|
|
|
static_cast<float>(y),
|
|
|
|
static_cast<float>(width),
|
|
|
|
static_cast<float>(height),
|
|
|
|
min_depth,
|
|
|
|
max_depth};
|
|
|
|
const VkRect2D scissor{{x, y}, {static_cast<u32>(width), static_cast<u32>(height)}};
|
|
|
|
vkCmdSetViewport(command_buffer, 0, 1, &vp);
|
|
|
|
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SafeDestroyFramebuffer(VkFramebuffer& fb)
|
|
|
|
{
|
|
|
|
if (fb != VK_NULL_HANDLE)
|
|
|
|
{
|
|
|
|
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), fb, nullptr);
|
|
|
|
fb = VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-20 15:33:08 +00:00
|
|
|
void SafeDestroyShaderModule(VkShaderModule& sm)
|
|
|
|
{
|
|
|
|
if (sm != VK_NULL_HANDLE)
|
|
|
|
{
|
|
|
|
vkDestroyShaderModule(g_vulkan_context->GetDevice(), sm, nullptr);
|
|
|
|
sm = VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-18 14:18:07 +00:00
|
|
|
void SafeDestroyPipeline(VkPipeline& p)
|
|
|
|
{
|
|
|
|
if (p != VK_NULL_HANDLE)
|
|
|
|
{
|
|
|
|
vkDestroyPipeline(g_vulkan_context->GetDevice(), p, nullptr);
|
|
|
|
p = VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SafeDestroyPipelineLayout(VkPipelineLayout& pl)
|
|
|
|
{
|
|
|
|
if (pl != VK_NULL_HANDLE)
|
|
|
|
{
|
|
|
|
vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), pl, nullptr);
|
|
|
|
pl = VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SafeDestroyDescriptorSetLayout(VkDescriptorSetLayout& dsl)
|
|
|
|
{
|
|
|
|
if (dsl != VK_NULL_HANDLE)
|
|
|
|
{
|
|
|
|
vkDestroyDescriptorSetLayout(g_vulkan_context->GetDevice(), dsl, nullptr);
|
|
|
|
dsl = VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SafeDestroyBufferView(VkBufferView& bv)
|
|
|
|
{
|
|
|
|
if (bv != VK_NULL_HANDLE)
|
|
|
|
{
|
|
|
|
vkDestroyBufferView(g_vulkan_context->GetDevice(), bv, nullptr);
|
|
|
|
bv = VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SafeDestroySampler(VkSampler& samp)
|
|
|
|
{
|
|
|
|
if (samp != VK_NULL_HANDLE)
|
|
|
|
{
|
|
|
|
vkDestroySampler(g_vulkan_context->GetDevice(), samp, nullptr);
|
|
|
|
samp = VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-04 10:06:04 +00:00
|
|
|
void SafeDestroySemaphore(VkSemaphore& sem)
|
|
|
|
{
|
|
|
|
if (sem != VK_NULL_HANDLE)
|
|
|
|
{
|
|
|
|
vkDestroySemaphore(g_vulkan_context->GetDevice(), sem, nullptr);
|
|
|
|
sem = VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-18 14:18:07 +00:00
|
|
|
void SafeFreeGlobalDescriptorSet(VkDescriptorSet& ds)
|
|
|
|
{
|
|
|
|
if (ds != VK_NULL_HANDLE)
|
|
|
|
{
|
|
|
|
g_vulkan_context->FreeGlobalDescriptorSet(ds);
|
|
|
|
ds = VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BufferMemoryBarrier(VkCommandBuffer command_buffer, VkBuffer buffer, VkAccessFlags src_access_mask,
|
|
|
|
VkAccessFlags dst_access_mask, VkDeviceSize offset, VkDeviceSize size,
|
|
|
|
VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask)
|
|
|
|
{
|
|
|
|
VkBufferMemoryBarrier buffer_info = {
|
|
|
|
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType
|
|
|
|
nullptr, // const void* pNext
|
|
|
|
src_access_mask, // VkAccessFlags srcAccessMask
|
|
|
|
dst_access_mask, // VkAccessFlags dstAccessMask
|
|
|
|
VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex
|
|
|
|
VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex
|
|
|
|
buffer, // VkBuffer buffer
|
|
|
|
offset, // VkDeviceSize offset
|
|
|
|
size // VkDeviceSize size
|
|
|
|
};
|
|
|
|
|
|
|
|
vkCmdPipelineBarrier(command_buffer, src_stage_mask, dst_stage_mask, 0, 0, nullptr, 1, &buffer_info, 0, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
VkShaderModule CreateShaderModule(const u32* spv, size_t spv_word_count)
|
|
|
|
{
|
|
|
|
VkShaderModuleCreateInfo info = {};
|
|
|
|
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
|
|
|
info.codeSize = spv_word_count * sizeof(u32);
|
|
|
|
info.pCode = spv;
|
|
|
|
|
|
|
|
VkShaderModule module;
|
|
|
|
VkResult res = vkCreateShaderModule(g_vulkan_context->GetDevice(), &info, nullptr, &module);
|
|
|
|
if (res != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
LOG_VULKAN_ERROR(res, "vkCreateShaderModule failed: ");
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return module;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkShaderModule CompileAndCreateVertexShader(std::string_view source_code)
|
|
|
|
{
|
|
|
|
std::optional<ShaderCompiler::SPIRVCodeVector> code = ShaderCompiler::CompileVertexShader(source_code);
|
|
|
|
if (!code)
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
return CreateShaderModule(code->data(), code->size());
|
|
|
|
}
|
|
|
|
|
|
|
|
VkShaderModule CompileAndCreateGeometryShader(std::string_view source_code)
|
|
|
|
{
|
|
|
|
std::optional<ShaderCompiler::SPIRVCodeVector> code = ShaderCompiler::CompileGeometryShader(source_code);
|
|
|
|
if (!code)
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
return CreateShaderModule(code->data(), code->size());
|
|
|
|
}
|
|
|
|
|
|
|
|
VkShaderModule CompileAndCreateFragmentShader(std::string_view source_code)
|
|
|
|
{
|
|
|
|
std::optional<ShaderCompiler::SPIRVCodeVector> code = ShaderCompiler::CompileFragmentShader(source_code);
|
|
|
|
if (!code)
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
return CreateShaderModule(code->data(), code->size());
|
|
|
|
}
|
|
|
|
|
|
|
|
VkShaderModule CompileAndCreateComputeShader(std::string_view source_code)
|
|
|
|
{
|
|
|
|
std::optional<ShaderCompiler::SPIRVCodeVector> code = ShaderCompiler::CompileComputeShader(source_code);
|
|
|
|
if (!code)
|
|
|
|
return VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
return CreateShaderModule(code->data(), code->size());
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* VkResultToString(VkResult res)
|
|
|
|
{
|
|
|
|
switch (res)
|
|
|
|
{
|
|
|
|
case VK_SUCCESS:
|
|
|
|
return "VK_SUCCESS";
|
|
|
|
|
|
|
|
case VK_NOT_READY:
|
|
|
|
return "VK_NOT_READY";
|
|
|
|
|
|
|
|
case VK_TIMEOUT:
|
|
|
|
return "VK_TIMEOUT";
|
|
|
|
|
|
|
|
case VK_EVENT_SET:
|
|
|
|
return "VK_EVENT_SET";
|
|
|
|
|
|
|
|
case VK_EVENT_RESET:
|
|
|
|
return "VK_EVENT_RESET";
|
|
|
|
|
|
|
|
case VK_INCOMPLETE:
|
|
|
|
return "VK_INCOMPLETE";
|
|
|
|
|
|
|
|
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
|
|
|
return "VK_ERROR_OUT_OF_HOST_MEMORY";
|
|
|
|
|
|
|
|
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
|
|
|
|
return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
|
|
|
|
|
|
|
|
case VK_ERROR_INITIALIZATION_FAILED:
|
|
|
|
return "VK_ERROR_INITIALIZATION_FAILED";
|
|
|
|
|
|
|
|
case VK_ERROR_DEVICE_LOST:
|
|
|
|
return "VK_ERROR_DEVICE_LOST";
|
|
|
|
|
|
|
|
case VK_ERROR_MEMORY_MAP_FAILED:
|
|
|
|
return "VK_ERROR_MEMORY_MAP_FAILED";
|
|
|
|
|
|
|
|
case VK_ERROR_LAYER_NOT_PRESENT:
|
|
|
|
return "VK_ERROR_LAYER_NOT_PRESENT";
|
|
|
|
|
|
|
|
case VK_ERROR_EXTENSION_NOT_PRESENT:
|
|
|
|
return "VK_ERROR_EXTENSION_NOT_PRESENT";
|
|
|
|
|
|
|
|
case VK_ERROR_FEATURE_NOT_PRESENT:
|
|
|
|
return "VK_ERROR_FEATURE_NOT_PRESENT";
|
|
|
|
|
|
|
|
case VK_ERROR_INCOMPATIBLE_DRIVER:
|
|
|
|
return "VK_ERROR_INCOMPATIBLE_DRIVER";
|
|
|
|
|
|
|
|
case VK_ERROR_TOO_MANY_OBJECTS:
|
|
|
|
return "VK_ERROR_TOO_MANY_OBJECTS";
|
|
|
|
|
|
|
|
case VK_ERROR_FORMAT_NOT_SUPPORTED:
|
|
|
|
return "VK_ERROR_FORMAT_NOT_SUPPORTED";
|
|
|
|
|
|
|
|
case VK_ERROR_SURFACE_LOST_KHR:
|
|
|
|
return "VK_ERROR_SURFACE_LOST_KHR";
|
|
|
|
|
|
|
|
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
|
|
|
|
return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
|
|
|
|
|
|
|
|
case VK_SUBOPTIMAL_KHR:
|
|
|
|
return "VK_SUBOPTIMAL_KHR";
|
|
|
|
|
|
|
|
case VK_ERROR_OUT_OF_DATE_KHR:
|
|
|
|
return "VK_ERROR_OUT_OF_DATE_KHR";
|
|
|
|
|
|
|
|
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
|
|
|
|
return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
|
|
|
|
|
|
|
|
case VK_ERROR_VALIDATION_FAILED_EXT:
|
|
|
|
return "VK_ERROR_VALIDATION_FAILED_EXT";
|
|
|
|
|
|
|
|
case VK_ERROR_INVALID_SHADER_NV:
|
|
|
|
return "VK_ERROR_INVALID_SHADER_NV";
|
|
|
|
|
|
|
|
default:
|
|
|
|
return "UNKNOWN_VK_RESULT";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LogVulkanResult(int level, const char* func_name, VkResult res, const char* msg, ...)
|
|
|
|
{
|
|
|
|
std::va_list ap;
|
|
|
|
va_start(ap, msg);
|
|
|
|
std::string real_msg = StringUtil::StdStringFromFormatV(msg, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
Log::Writef("Vulkan", func_name, static_cast<LOGLEVEL>(level), "(%s) %s (%d: %s)", func_name, real_msg.c_str(),
|
|
|
|
static_cast<int>(res), VkResultToString(res));
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Util
|
|
|
|
|
|
|
|
} // namespace Vulkan
|