8242 lines
289 KiB
C++
8242 lines
289 KiB
C++
#include "wiGraphicsDevice_Vulkan.h"
|
|
|
|
#ifdef WICKEDENGINE_BUILD_VULKAN
|
|
#include "wiHelper.h"
|
|
#include "wiBacklog.h"
|
|
#include "wiVersion.h"
|
|
#include "wiTimer.h"
|
|
#include "wiUnorderedSet.h"
|
|
|
|
#define VOLK_IMPLEMENTATION
|
|
#include "Utility/volk.h"
|
|
|
|
#include "Utility/spirv_reflect.h"
|
|
|
|
#define VMA_IMPLEMENTATION
|
|
#include "Utility/vk_mem_alloc.h"
|
|
|
|
#ifdef SDL2
|
|
#include <SDL2/SDL_vulkan.h>
|
|
#include "sdl2.h"
|
|
#endif
|
|
|
|
#include <string>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <algorithm>
|
|
|
|
// These shifts are made so that Vulkan resource bindings slots don't interfere with each other across shader stages:
|
|
// These are also defined in wi::shadercompiler.cpp as hard coded compiler arguments for SPIRV, so they need to be the same
|
|
#define VULKAN_BINDING_SHIFT_B 0
|
|
#define VULKAN_BINDING_SHIFT_T 1000
|
|
#define VULKAN_BINDING_SHIFT_U 2000
|
|
#define VULKAN_BINDING_SHIFT_S 3000
|
|
|
|
namespace wi::graphics
|
|
{
|
|
|
|
namespace vulkan_internal
|
|
{
|
|
// Converters:
|
|
constexpr VkFormat _ConvertFormat(Format value)
|
|
{
|
|
switch (value)
|
|
{
|
|
case Format::UNKNOWN:
|
|
return VK_FORMAT_UNDEFINED;
|
|
case Format::R32G32B32A32_FLOAT:
|
|
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
|
case Format::R32G32B32A32_UINT:
|
|
return VK_FORMAT_R32G32B32A32_UINT;
|
|
case Format::R32G32B32A32_SINT:
|
|
return VK_FORMAT_R32G32B32A32_SINT;
|
|
case Format::R32G32B32_FLOAT:
|
|
return VK_FORMAT_R32G32B32_SFLOAT;
|
|
case Format::R32G32B32_UINT:
|
|
return VK_FORMAT_R32G32B32_UINT;
|
|
case Format::R32G32B32_SINT:
|
|
return VK_FORMAT_R32G32B32_SINT;
|
|
case Format::R16G16B16A16_FLOAT:
|
|
return VK_FORMAT_R16G16B16A16_SFLOAT;
|
|
case Format::R16G16B16A16_UNORM:
|
|
return VK_FORMAT_R16G16B16A16_UNORM;
|
|
case Format::R16G16B16A16_UINT:
|
|
return VK_FORMAT_R16G16B16A16_UINT;
|
|
case Format::R16G16B16A16_SNORM:
|
|
return VK_FORMAT_R16G16B16A16_SNORM;
|
|
case Format::R16G16B16A16_SINT:
|
|
return VK_FORMAT_R16G16B16A16_SINT;
|
|
case Format::R32G32_FLOAT:
|
|
return VK_FORMAT_R32G32_SFLOAT;
|
|
case Format::R32G32_UINT:
|
|
return VK_FORMAT_R32G32_UINT;
|
|
case Format::R32G32_SINT:
|
|
return VK_FORMAT_R32G32_SINT;
|
|
case Format::D32_FLOAT_S8X24_UINT:
|
|
return VK_FORMAT_D32_SFLOAT_S8_UINT;
|
|
case Format::R10G10B10A2_UNORM:
|
|
return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
|
|
case Format::R10G10B10A2_UINT:
|
|
return VK_FORMAT_A2B10G10R10_UINT_PACK32;
|
|
case Format::R11G11B10_FLOAT:
|
|
return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
|
|
case Format::R8G8B8A8_UNORM:
|
|
return VK_FORMAT_R8G8B8A8_UNORM;
|
|
case Format::R8G8B8A8_UNORM_SRGB:
|
|
return VK_FORMAT_R8G8B8A8_SRGB;
|
|
case Format::R8G8B8A8_UINT:
|
|
return VK_FORMAT_R8G8B8A8_UINT;
|
|
case Format::R8G8B8A8_SNORM:
|
|
return VK_FORMAT_R8G8B8A8_SNORM;
|
|
case Format::R8G8B8A8_SINT:
|
|
return VK_FORMAT_R8G8B8A8_SINT;
|
|
case Format::R16G16_FLOAT:
|
|
return VK_FORMAT_R16G16_SFLOAT;
|
|
case Format::R16G16_UNORM:
|
|
return VK_FORMAT_R16G16_UNORM;
|
|
case Format::R16G16_UINT:
|
|
return VK_FORMAT_R16G16_UINT;
|
|
case Format::R16G16_SNORM:
|
|
return VK_FORMAT_R16G16_SNORM;
|
|
case Format::R16G16_SINT:
|
|
return VK_FORMAT_R16G16_SINT;
|
|
case Format::D32_FLOAT:
|
|
return VK_FORMAT_D32_SFLOAT;
|
|
case Format::R32_FLOAT:
|
|
return VK_FORMAT_R32_SFLOAT;
|
|
case Format::R32_UINT:
|
|
return VK_FORMAT_R32_UINT;
|
|
case Format::R32_SINT:
|
|
return VK_FORMAT_R32_SINT;
|
|
case Format::D24_UNORM_S8_UINT:
|
|
return VK_FORMAT_D24_UNORM_S8_UINT;
|
|
case Format::R9G9B9E5_SHAREDEXP:
|
|
return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32;
|
|
case Format::R8G8_UNORM:
|
|
return VK_FORMAT_R8G8_UNORM;
|
|
case Format::R8G8_UINT:
|
|
return VK_FORMAT_R8G8_UINT;
|
|
case Format::R8G8_SNORM:
|
|
return VK_FORMAT_R8G8_SNORM;
|
|
case Format::R8G8_SINT:
|
|
return VK_FORMAT_R8G8_SINT;
|
|
case Format::R16_FLOAT:
|
|
return VK_FORMAT_R16_SFLOAT;
|
|
case Format::D16_UNORM:
|
|
return VK_FORMAT_D16_UNORM;
|
|
case Format::R16_UNORM:
|
|
return VK_FORMAT_R16_UNORM;
|
|
case Format::R16_UINT:
|
|
return VK_FORMAT_R16_UINT;
|
|
case Format::R16_SNORM:
|
|
return VK_FORMAT_R16_SNORM;
|
|
case Format::R16_SINT:
|
|
return VK_FORMAT_R16_SINT;
|
|
case Format::R8_UNORM:
|
|
return VK_FORMAT_R8_UNORM;
|
|
case Format::R8_UINT:
|
|
return VK_FORMAT_R8_UINT;
|
|
case Format::R8_SNORM:
|
|
return VK_FORMAT_R8_SNORM;
|
|
case Format::R8_SINT:
|
|
return VK_FORMAT_R8_SINT;
|
|
case Format::BC1_UNORM:
|
|
return VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
|
|
case Format::BC1_UNORM_SRGB:
|
|
return VK_FORMAT_BC1_RGBA_SRGB_BLOCK;
|
|
case Format::BC2_UNORM:
|
|
return VK_FORMAT_BC2_UNORM_BLOCK;
|
|
case Format::BC2_UNORM_SRGB:
|
|
return VK_FORMAT_BC2_SRGB_BLOCK;
|
|
case Format::BC3_UNORM:
|
|
return VK_FORMAT_BC3_UNORM_BLOCK;
|
|
case Format::BC3_UNORM_SRGB:
|
|
return VK_FORMAT_BC3_SRGB_BLOCK;
|
|
case Format::BC4_UNORM:
|
|
return VK_FORMAT_BC4_UNORM_BLOCK;
|
|
case Format::BC4_SNORM:
|
|
return VK_FORMAT_BC4_SNORM_BLOCK;
|
|
case Format::BC5_UNORM:
|
|
return VK_FORMAT_BC5_UNORM_BLOCK;
|
|
case Format::BC5_SNORM:
|
|
return VK_FORMAT_BC5_SNORM_BLOCK;
|
|
case Format::B8G8R8A8_UNORM:
|
|
return VK_FORMAT_B8G8R8A8_UNORM;
|
|
case Format::B8G8R8A8_UNORM_SRGB:
|
|
return VK_FORMAT_B8G8R8A8_SRGB;
|
|
case Format::BC6H_UF16:
|
|
return VK_FORMAT_BC6H_UFLOAT_BLOCK;
|
|
case Format::BC6H_SF16:
|
|
return VK_FORMAT_BC6H_SFLOAT_BLOCK;
|
|
case Format::BC7_UNORM:
|
|
return VK_FORMAT_BC7_UNORM_BLOCK;
|
|
case Format::BC7_UNORM_SRGB:
|
|
return VK_FORMAT_BC7_SRGB_BLOCK;
|
|
}
|
|
return VK_FORMAT_UNDEFINED;
|
|
}
|
|
constexpr VkCompareOp _ConvertComparisonFunc(ComparisonFunc value)
|
|
{
|
|
switch (value)
|
|
{
|
|
case ComparisonFunc::NEVER:
|
|
return VK_COMPARE_OP_NEVER;
|
|
case ComparisonFunc::LESS:
|
|
return VK_COMPARE_OP_LESS;
|
|
case ComparisonFunc::EQUAL:
|
|
return VK_COMPARE_OP_EQUAL;
|
|
case ComparisonFunc::LESS_EQUAL:
|
|
return VK_COMPARE_OP_LESS_OR_EQUAL;
|
|
case ComparisonFunc::GREATER:
|
|
return VK_COMPARE_OP_GREATER;
|
|
case ComparisonFunc::NOT_EQUAL:
|
|
return VK_COMPARE_OP_NOT_EQUAL;
|
|
case ComparisonFunc::GREATER_EQUAL:
|
|
return VK_COMPARE_OP_GREATER_OR_EQUAL;
|
|
case ComparisonFunc::ALWAYS:
|
|
return VK_COMPARE_OP_ALWAYS;
|
|
default:
|
|
return VK_COMPARE_OP_NEVER;
|
|
}
|
|
}
|
|
constexpr VkBlendFactor _ConvertBlend(Blend value)
|
|
{
|
|
switch (value)
|
|
{
|
|
case Blend::ZERO:
|
|
return VK_BLEND_FACTOR_ZERO;
|
|
case Blend::ONE:
|
|
return VK_BLEND_FACTOR_ONE;
|
|
case Blend::SRC_COLOR:
|
|
return VK_BLEND_FACTOR_SRC_COLOR;
|
|
case Blend::INV_SRC_COLOR:
|
|
return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
|
|
case Blend::SRC_ALPHA:
|
|
return VK_BLEND_FACTOR_SRC_ALPHA;
|
|
case Blend::INV_SRC_ALPHA:
|
|
return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
case Blend::DEST_ALPHA:
|
|
return VK_BLEND_FACTOR_DST_ALPHA;
|
|
case Blend::INV_DEST_ALPHA:
|
|
return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
|
case Blend::DEST_COLOR:
|
|
return VK_BLEND_FACTOR_DST_COLOR;
|
|
case Blend::INV_DEST_COLOR:
|
|
return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
|
|
case Blend::SRC_ALPHA_SAT:
|
|
return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
|
|
case Blend::BLEND_FACTOR:
|
|
return VK_BLEND_FACTOR_CONSTANT_COLOR;
|
|
case Blend::INV_BLEND_FACTOR:
|
|
return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
|
|
break;
|
|
case Blend::SRC1_COLOR:
|
|
return VK_BLEND_FACTOR_SRC1_COLOR;
|
|
case Blend::INV_SRC1_COLOR:
|
|
return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
|
|
case Blend::SRC1_ALPHA:
|
|
return VK_BLEND_FACTOR_SRC1_ALPHA;
|
|
case Blend::INV_SRC1_ALPHA:
|
|
return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
|
|
default:
|
|
return VK_BLEND_FACTOR_ZERO;
|
|
}
|
|
}
|
|
constexpr VkBlendOp _ConvertBlendOp(BlendOp value)
|
|
{
|
|
switch (value)
|
|
{
|
|
case BlendOp::ADD:
|
|
return VK_BLEND_OP_ADD;
|
|
case BlendOp::SUBTRACT:
|
|
return VK_BLEND_OP_SUBTRACT;
|
|
case BlendOp::REV_SUBTRACT:
|
|
return VK_BLEND_OP_REVERSE_SUBTRACT;
|
|
case BlendOp::MIN:
|
|
return VK_BLEND_OP_MIN;
|
|
case BlendOp::MAX:
|
|
return VK_BLEND_OP_MAX;
|
|
default:
|
|
return VK_BLEND_OP_ADD;
|
|
}
|
|
}
|
|
constexpr VkSamplerAddressMode _ConvertTextureAddressMode(TextureAddressMode value, const VkPhysicalDeviceVulkan12Features& features_1_2)
|
|
{
|
|
switch (value)
|
|
{
|
|
case TextureAddressMode::WRAP:
|
|
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
|
case TextureAddressMode::MIRROR:
|
|
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
|
case TextureAddressMode::CLAMP:
|
|
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
case TextureAddressMode::BORDER:
|
|
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
|
case TextureAddressMode::MIRROR_ONCE:
|
|
if (features_1_2.samplerMirrorClampToEdge == VK_TRUE)
|
|
{
|
|
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
|
|
}
|
|
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
|
default:
|
|
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
}
|
|
}
|
|
constexpr VkBorderColor _ConvertSamplerBorderColor(SamplerBorderColor value)
|
|
{
|
|
switch (value)
|
|
{
|
|
case SamplerBorderColor::TRANSPARENT_BLACK:
|
|
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
|
|
case SamplerBorderColor::OPAQUE_BLACK:
|
|
return VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
|
|
case SamplerBorderColor::OPAQUE_WHITE:
|
|
return VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
|
|
default:
|
|
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
|
|
}
|
|
}
|
|
constexpr VkStencilOp _ConvertStencilOp(StencilOp value)
|
|
{
|
|
switch (value)
|
|
{
|
|
case wi::graphics::StencilOp::KEEP:
|
|
return VK_STENCIL_OP_KEEP;
|
|
case wi::graphics::StencilOp::ZERO:
|
|
return VK_STENCIL_OP_ZERO;
|
|
case wi::graphics::StencilOp::REPLACE:
|
|
return VK_STENCIL_OP_REPLACE;
|
|
case wi::graphics::StencilOp::INCR_SAT:
|
|
return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
|
|
case wi::graphics::StencilOp::DECR_SAT:
|
|
return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
|
|
case wi::graphics::StencilOp::INVERT:
|
|
return VK_STENCIL_OP_INVERT;
|
|
case wi::graphics::StencilOp::INCR:
|
|
return VK_STENCIL_OP_INCREMENT_AND_WRAP;
|
|
case wi::graphics::StencilOp::DECR:
|
|
return VK_STENCIL_OP_DECREMENT_AND_WRAP;
|
|
default:
|
|
return VK_STENCIL_OP_KEEP;
|
|
}
|
|
}
|
|
constexpr VkImageLayout _ConvertImageLayout(ResourceState value)
|
|
{
|
|
switch (value)
|
|
{
|
|
case ResourceState::UNDEFINED:
|
|
return VK_IMAGE_LAYOUT_UNDEFINED;
|
|
case ResourceState::RENDERTARGET:
|
|
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
case ResourceState::DEPTHSTENCIL:
|
|
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
case ResourceState::DEPTHSTENCIL_READONLY:
|
|
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
|
|
case ResourceState::SHADER_RESOURCE:
|
|
case ResourceState::SHADER_RESOURCE_COMPUTE:
|
|
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
case ResourceState::UNORDERED_ACCESS:
|
|
return VK_IMAGE_LAYOUT_GENERAL;
|
|
case ResourceState::COPY_SRC:
|
|
return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
|
case ResourceState::COPY_DST:
|
|
return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
case ResourceState::SHADING_RATE_SOURCE:
|
|
return VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
|
|
default:
|
|
return VK_IMAGE_LAYOUT_UNDEFINED;
|
|
}
|
|
}
|
|
constexpr VkShaderStageFlags _ConvertStageFlags(ShaderStage value)
|
|
{
|
|
switch (value)
|
|
{
|
|
case ShaderStage::MS:
|
|
return VK_SHADER_STAGE_MESH_BIT_NV;
|
|
case ShaderStage::AS:
|
|
return VK_SHADER_STAGE_TASK_BIT_NV;
|
|
case ShaderStage::VS:
|
|
return VK_SHADER_STAGE_VERTEX_BIT;
|
|
case ShaderStage::HS:
|
|
return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
|
|
case ShaderStage::DS:
|
|
return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
|
|
case ShaderStage::GS:
|
|
return VK_SHADER_STAGE_GEOMETRY_BIT;
|
|
case ShaderStage::PS:
|
|
return VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
case ShaderStage::CS:
|
|
return VK_SHADER_STAGE_COMPUTE_BIT;
|
|
default:
|
|
return VK_SHADER_STAGE_ALL;
|
|
}
|
|
}
|
|
constexpr VkAccessFlags _ParseResourceState(ResourceState value)
|
|
{
|
|
VkAccessFlags flags = 0;
|
|
|
|
if (has_flag(value, ResourceState::SHADER_RESOURCE))
|
|
{
|
|
flags |= VK_ACCESS_SHADER_READ_BIT;
|
|
}
|
|
if (has_flag(value, ResourceState::SHADER_RESOURCE_COMPUTE))
|
|
{
|
|
flags |= VK_ACCESS_SHADER_READ_BIT;
|
|
}
|
|
if (has_flag(value, ResourceState::UNORDERED_ACCESS))
|
|
{
|
|
flags |= VK_ACCESS_SHADER_READ_BIT;
|
|
flags |= VK_ACCESS_SHADER_WRITE_BIT;
|
|
}
|
|
if (has_flag(value, ResourceState::COPY_SRC))
|
|
{
|
|
flags |= VK_ACCESS_TRANSFER_READ_BIT;
|
|
}
|
|
if (has_flag(value, ResourceState::COPY_DST))
|
|
{
|
|
flags |= VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
}
|
|
|
|
if (has_flag(value, ResourceState::RENDERTARGET))
|
|
{
|
|
flags |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
|
|
flags |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
}
|
|
if (has_flag(value, ResourceState::DEPTHSTENCIL))
|
|
{
|
|
flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
|
|
flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
}
|
|
if (has_flag(value, ResourceState::DEPTHSTENCIL_READONLY))
|
|
{
|
|
flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
|
|
}
|
|
|
|
if (has_flag(value, ResourceState::VERTEX_BUFFER))
|
|
{
|
|
flags |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
|
|
}
|
|
if (has_flag(value, ResourceState::INDEX_BUFFER))
|
|
{
|
|
flags |= VK_ACCESS_INDEX_READ_BIT;
|
|
}
|
|
if (has_flag(value, ResourceState::CONSTANT_BUFFER))
|
|
{
|
|
flags |= VK_ACCESS_UNIFORM_READ_BIT;
|
|
}
|
|
if (has_flag(value, ResourceState::INDIRECT_ARGUMENT))
|
|
{
|
|
flags |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
|
|
}
|
|
|
|
if (has_flag(value, ResourceState::PREDICATION))
|
|
{
|
|
flags |= VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT;
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
|
|
bool checkExtensionSupport(const char* checkExtension, const wi::vector<VkExtensionProperties>& available_extensions)
|
|
{
|
|
for (const auto& x : available_extensions)
|
|
{
|
|
if (strcmp(x.extensionName, checkExtension) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ValidateLayers(const wi::vector<const char*>& required,
|
|
const wi::vector<VkLayerProperties>& available)
|
|
{
|
|
for (auto layer : required)
|
|
{
|
|
bool found = false;
|
|
for (auto& available_layer : available)
|
|
{
|
|
if (strcmp(available_layer.layerName, layer) == 0)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsMessengerCallback(
|
|
VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
|
VkDebugUtilsMessageTypeFlagsEXT message_type,
|
|
const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
|
|
void* user_data)
|
|
{
|
|
// Log debug message
|
|
std::string ss;
|
|
|
|
if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
|
|
{
|
|
ss += "[Vulkan Warning]: ";
|
|
ss += callback_data->pMessage;
|
|
wi::backlog::post(ss, wi::backlog::LogLevel::Warning);
|
|
}
|
|
else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
|
|
{
|
|
ss += "[Vulkan Error]: ";
|
|
ss += callback_data->pMessage;
|
|
wi::backlog::post(ss, wi::backlog::LogLevel::Error);
|
|
#ifdef _DEBUG
|
|
assert(0);
|
|
#endif // _DEBUG
|
|
}
|
|
|
|
return VK_FALSE;
|
|
}
|
|
|
|
|
|
struct Buffer_Vulkan
|
|
{
|
|
std::shared_ptr<GraphicsDevice_Vulkan::AllocationHandler> allocationhandler;
|
|
VmaAllocation allocation = nullptr;
|
|
VkBuffer resource = VK_NULL_HANDLE;
|
|
struct BufferSubresource
|
|
{
|
|
bool is_typed = false;
|
|
VkBufferView buffer_view = VK_NULL_HANDLE;
|
|
VkDescriptorBufferInfo buffer_info = {};
|
|
int index = -1; // bindless
|
|
|
|
constexpr bool IsValid() const
|
|
{
|
|
return index >= 0;
|
|
}
|
|
};
|
|
BufferSubresource srv;
|
|
BufferSubresource uav;
|
|
wi::vector<BufferSubresource> subresources_srv;
|
|
wi::vector<BufferSubresource> subresources_uav;
|
|
VkDeviceAddress address = 0;
|
|
|
|
~Buffer_Vulkan()
|
|
{
|
|
if (allocationhandler == nullptr)
|
|
return;
|
|
allocationhandler->destroylocker.lock();
|
|
uint64_t framecount = allocationhandler->framecount;
|
|
if (resource)
|
|
{
|
|
allocationhandler->destroyer_buffers.push_back(std::make_pair(std::make_pair(resource, allocation), framecount));
|
|
}
|
|
else if(allocation)
|
|
{
|
|
allocationhandler->destroyer_allocations.push_back(std::make_pair(allocation, framecount));
|
|
}
|
|
if (srv.IsValid())
|
|
{
|
|
if (srv.is_typed)
|
|
{
|
|
allocationhandler->destroyer_bufferviews.push_back(std::make_pair(srv.buffer_view, framecount));
|
|
allocationhandler->destroyer_bindlessUniformTexelBuffers.push_back(std::make_pair(srv.index, framecount));
|
|
}
|
|
else
|
|
{
|
|
allocationhandler->destroyer_bindlessStorageBuffers.push_back(std::make_pair(srv.index, framecount));
|
|
}
|
|
}
|
|
if (uav.IsValid())
|
|
{
|
|
if (uav.is_typed)
|
|
{
|
|
allocationhandler->destroyer_bufferviews.push_back(std::make_pair(uav.buffer_view, framecount));
|
|
allocationhandler->destroyer_bindlessStorageTexelBuffers.push_back(std::make_pair(uav.index, framecount));
|
|
}
|
|
else
|
|
{
|
|
allocationhandler->destroyer_bindlessStorageBuffers.push_back(std::make_pair(uav.index, framecount));
|
|
}
|
|
}
|
|
for (auto& x : subresources_srv)
|
|
{
|
|
if (x.is_typed)
|
|
{
|
|
allocationhandler->destroyer_bufferviews.push_back(std::make_pair(x.buffer_view, framecount));
|
|
allocationhandler->destroyer_bindlessUniformTexelBuffers.push_back(std::make_pair(x.index, framecount));
|
|
}
|
|
else
|
|
{
|
|
allocationhandler->destroyer_bindlessStorageBuffers.push_back(std::make_pair(x.index, framecount));
|
|
}
|
|
}
|
|
for (auto& x : subresources_uav)
|
|
{
|
|
if (x.is_typed)
|
|
{
|
|
allocationhandler->destroyer_bufferviews.push_back(std::make_pair(x.buffer_view, framecount));
|
|
allocationhandler->destroyer_bindlessStorageTexelBuffers.push_back(std::make_pair(x.index, framecount));
|
|
}
|
|
else
|
|
{
|
|
allocationhandler->destroyer_bindlessStorageBuffers.push_back(std::make_pair(x.index, framecount));
|
|
}
|
|
}
|
|
allocationhandler->destroylocker.unlock();
|
|
}
|
|
};
|
|
struct Texture_Vulkan
|
|
{
|
|
std::shared_ptr<GraphicsDevice_Vulkan::AllocationHandler> allocationhandler;
|
|
VmaAllocation allocation = nullptr;
|
|
VkImage resource = VK_NULL_HANDLE;
|
|
VkBuffer staging_resource = VK_NULL_HANDLE;
|
|
struct TextureSubresource
|
|
{
|
|
VkImageView image_view = VK_NULL_HANDLE;
|
|
int index = -1; // bindless
|
|
|
|
constexpr bool IsValid() const
|
|
{
|
|
return index >= 0;
|
|
}
|
|
};
|
|
TextureSubresource srv;
|
|
TextureSubresource uav;
|
|
VkImageView rtv = VK_NULL_HANDLE;
|
|
VkImageView dsv = VK_NULL_HANDLE;
|
|
uint32_t framebuffer_layercount = 0;
|
|
wi::vector<TextureSubresource> subresources_srv;
|
|
wi::vector<TextureSubresource> subresources_uav;
|
|
wi::vector<VkImageView> subresources_rtv;
|
|
wi::vector<VkImageView> subresources_dsv;
|
|
wi::vector<uint32_t> subresources_framebuffer_layercount;
|
|
|
|
wi::vector<SubresourceData> mapped_subresources;
|
|
SparseTextureProperties sparse_texture_properties;
|
|
|
|
~Texture_Vulkan()
|
|
{
|
|
if (allocationhandler == nullptr)
|
|
return;
|
|
allocationhandler->destroylocker.lock();
|
|
uint64_t framecount = allocationhandler->framecount;
|
|
if (resource)
|
|
{
|
|
allocationhandler->destroyer_images.push_back(std::make_pair(std::make_pair(resource, allocation), framecount));
|
|
}
|
|
else if (staging_resource)
|
|
{
|
|
allocationhandler->destroyer_buffers.push_back(std::make_pair(std::make_pair(staging_resource, allocation), framecount));
|
|
}
|
|
else if (allocation)
|
|
{
|
|
allocationhandler->destroyer_allocations.push_back(std::make_pair(allocation, framecount));
|
|
}
|
|
if (srv.IsValid())
|
|
{
|
|
allocationhandler->destroyer_imageviews.push_back(std::make_pair(srv.image_view, framecount));
|
|
allocationhandler->destroyer_bindlessSampledImages.push_back(std::make_pair(srv.index, framecount));
|
|
}
|
|
if (uav.IsValid())
|
|
{
|
|
allocationhandler->destroyer_imageviews.push_back(std::make_pair(uav.image_view, framecount));
|
|
allocationhandler->destroyer_bindlessStorageImages.push_back(std::make_pair(uav.index, framecount));
|
|
}
|
|
if (rtv != VK_NULL_HANDLE)
|
|
{
|
|
allocationhandler->destroyer_imageviews.push_back(std::make_pair(rtv, framecount));
|
|
}
|
|
if (dsv != VK_NULL_HANDLE)
|
|
{
|
|
allocationhandler->destroyer_imageviews.push_back(std::make_pair(dsv, framecount));
|
|
}
|
|
for (auto x : subresources_srv)
|
|
{
|
|
allocationhandler->destroyer_imageviews.push_back(std::make_pair(x.image_view, framecount));
|
|
allocationhandler->destroyer_bindlessSampledImages.push_back(std::make_pair(x.index, framecount));
|
|
}
|
|
for (auto x : subresources_uav)
|
|
{
|
|
allocationhandler->destroyer_imageviews.push_back(std::make_pair(x.image_view, framecount));
|
|
allocationhandler->destroyer_bindlessStorageImages.push_back(std::make_pair(x.index, framecount));
|
|
}
|
|
for (auto x : subresources_rtv)
|
|
{
|
|
allocationhandler->destroyer_imageviews.push_back(std::make_pair(x, framecount));
|
|
}
|
|
for (auto x : subresources_dsv)
|
|
{
|
|
allocationhandler->destroyer_imageviews.push_back(std::make_pair(x, framecount));
|
|
}
|
|
allocationhandler->destroylocker.unlock();
|
|
}
|
|
};
|
|
struct Sampler_Vulkan
|
|
{
|
|
std::shared_ptr<GraphicsDevice_Vulkan::AllocationHandler> allocationhandler;
|
|
VkSampler resource = VK_NULL_HANDLE;
|
|
int index = -1;
|
|
|
|
~Sampler_Vulkan()
|
|
{
|
|
if (allocationhandler == nullptr)
|
|
return;
|
|
allocationhandler->destroylocker.lock();
|
|
uint64_t framecount = allocationhandler->framecount;
|
|
if (resource) allocationhandler->destroyer_samplers.push_back(std::make_pair(resource, framecount));
|
|
if (index >= 0) allocationhandler->destroyer_bindlessSamplers.push_back(std::make_pair(index, framecount));
|
|
allocationhandler->destroylocker.unlock();
|
|
}
|
|
};
|
|
struct QueryHeap_Vulkan
|
|
{
|
|
std::shared_ptr<GraphicsDevice_Vulkan::AllocationHandler> allocationhandler;
|
|
VkQueryPool pool = VK_NULL_HANDLE;
|
|
|
|
~QueryHeap_Vulkan()
|
|
{
|
|
if (allocationhandler == nullptr)
|
|
return;
|
|
allocationhandler->destroylocker.lock();
|
|
uint64_t framecount = allocationhandler->framecount;
|
|
if (pool) allocationhandler->destroyer_querypools.push_back(std::make_pair(pool, framecount));
|
|
allocationhandler->destroylocker.unlock();
|
|
}
|
|
};
|
|
struct Shader_Vulkan
|
|
{
|
|
std::shared_ptr<GraphicsDevice_Vulkan::AllocationHandler> allocationhandler;
|
|
VkShaderModule shaderModule = VK_NULL_HANDLE;
|
|
VkPipeline pipeline_cs = VK_NULL_HANDLE;
|
|
VkPipelineShaderStageCreateInfo stageInfo = {};
|
|
VkPipelineLayout pipelineLayout_cs = VK_NULL_HANDLE; // no lifetime management here
|
|
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; // no lifetime management here
|
|
wi::vector<VkDescriptorSetLayoutBinding> layoutBindings;
|
|
wi::vector<VkImageViewType> imageViewTypes;
|
|
|
|
wi::vector<VkDescriptorSetLayoutBinding> bindlessBindings;
|
|
wi::vector<VkDescriptorSet> bindlessSets;
|
|
uint32_t bindlessFirstSet = 0;
|
|
|
|
VkPushConstantRange pushconstants = {};
|
|
|
|
VkDeviceSize uniform_buffer_sizes[DESCRIPTORBINDER_CBV_COUNT] = {};
|
|
wi::vector<uint32_t> uniform_buffer_dynamic_slots;
|
|
|
|
size_t binding_hash = 0;
|
|
|
|
~Shader_Vulkan()
|
|
{
|
|
if (allocationhandler == nullptr)
|
|
return;
|
|
allocationhandler->destroylocker.lock();
|
|
uint64_t framecount = allocationhandler->framecount;
|
|
if (shaderModule) allocationhandler->destroyer_shadermodules.push_back(std::make_pair(shaderModule, framecount));
|
|
if (pipeline_cs) allocationhandler->destroyer_pipelines.push_back(std::make_pair(pipeline_cs, framecount));
|
|
allocationhandler->destroylocker.unlock();
|
|
}
|
|
};
|
|
struct PipelineState_Vulkan
|
|
{
|
|
std::shared_ptr<GraphicsDevice_Vulkan::AllocationHandler> allocationhandler;
|
|
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; // no lifetime management here
|
|
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; // no lifetime management here
|
|
wi::vector<VkDescriptorSetLayoutBinding> layoutBindings;
|
|
wi::vector<VkImageViewType> imageViewTypes;
|
|
|
|
wi::vector<VkDescriptorSetLayoutBinding> bindlessBindings;
|
|
wi::vector<VkDescriptorSet> bindlessSets;
|
|
uint32_t bindlessFirstSet = 0;
|
|
|
|
VkPushConstantRange pushconstants = {};
|
|
|
|
VkDeviceSize uniform_buffer_sizes[DESCRIPTORBINDER_CBV_COUNT] = {};
|
|
wi::vector<uint32_t> uniform_buffer_dynamic_slots;
|
|
|
|
size_t binding_hash = 0;
|
|
|
|
VkGraphicsPipelineCreateInfo pipelineInfo = {};
|
|
VkPipelineShaderStageCreateInfo shaderStages[static_cast<size_t>(ShaderStage::Count)] = {};
|
|
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
|
|
VkPipelineRasterizationStateCreateInfo rasterizer = {};
|
|
VkPipelineRasterizationDepthClipStateCreateInfoEXT depthclip = {};
|
|
VkViewport viewport = {};
|
|
VkRect2D scissor = {};
|
|
VkPipelineViewportStateCreateInfo viewportState = {};
|
|
VkPipelineDepthStencilStateCreateInfo depthstencil = {};
|
|
VkSampleMask samplemask = {};
|
|
VkPipelineTessellationStateCreateInfo tessellationInfo = {};
|
|
};
|
|
struct RenderPass_Vulkan
|
|
{
|
|
std::shared_ptr<GraphicsDevice_Vulkan::AllocationHandler> allocationhandler;
|
|
VkRenderPass renderpass = VK_NULL_HANDLE;
|
|
VkFramebuffer framebuffer = VK_NULL_HANDLE;
|
|
VkRenderPassBeginInfo beginInfo = {};
|
|
VkClearValue clearColors[9] = {};
|
|
|
|
~RenderPass_Vulkan()
|
|
{
|
|
if (allocationhandler == nullptr)
|
|
return;
|
|
allocationhandler->destroylocker.lock();
|
|
uint64_t framecount = allocationhandler->framecount;
|
|
if (renderpass) allocationhandler->destroyer_renderpasses.push_back(std::make_pair(renderpass, framecount));
|
|
if (framebuffer) allocationhandler->destroyer_framebuffers.push_back(std::make_pair(framebuffer, framecount));
|
|
allocationhandler->destroylocker.unlock();
|
|
}
|
|
};
|
|
struct BVH_Vulkan
|
|
{
|
|
std::shared_ptr<GraphicsDevice_Vulkan::AllocationHandler> allocationhandler;
|
|
VmaAllocation allocation = nullptr;
|
|
VkBuffer buffer = VK_NULL_HANDLE;
|
|
VkAccelerationStructureKHR resource = VK_NULL_HANDLE;
|
|
int index = -1;
|
|
|
|
VkAccelerationStructureBuildGeometryInfoKHR buildInfo = {};
|
|
VkAccelerationStructureBuildSizesInfoKHR sizeInfo = {};
|
|
VkAccelerationStructureCreateInfoKHR createInfo = {};
|
|
wi::vector<VkAccelerationStructureGeometryKHR> geometries;
|
|
wi::vector<uint32_t> primitiveCounts;
|
|
VkDeviceAddress scratch_address = 0;
|
|
VkDeviceAddress as_address = 0;
|
|
|
|
~BVH_Vulkan()
|
|
{
|
|
if (allocationhandler == nullptr)
|
|
return;
|
|
allocationhandler->destroylocker.lock();
|
|
uint64_t framecount = allocationhandler->framecount;
|
|
if (buffer) allocationhandler->destroyer_buffers.push_back(std::make_pair(std::make_pair(buffer, allocation), framecount));
|
|
if (resource) allocationhandler->destroyer_bvhs.push_back(std::make_pair(resource, framecount));
|
|
if (index >= 0) allocationhandler->destroyer_bindlessAccelerationStructures.push_back(std::make_pair(index, framecount));
|
|
allocationhandler->destroylocker.unlock();
|
|
}
|
|
};
|
|
struct RTPipelineState_Vulkan
|
|
{
|
|
std::shared_ptr<GraphicsDevice_Vulkan::AllocationHandler> allocationhandler;
|
|
VkPipeline pipeline;
|
|
|
|
~RTPipelineState_Vulkan()
|
|
{
|
|
if (allocationhandler == nullptr)
|
|
return;
|
|
allocationhandler->destroylocker.lock();
|
|
uint64_t framecount = allocationhandler->framecount;
|
|
if (pipeline) allocationhandler->destroyer_pipelines.push_back(std::make_pair(pipeline, framecount));
|
|
allocationhandler->destroylocker.unlock();
|
|
}
|
|
};
|
|
struct SwapChain_Vulkan
|
|
{
|
|
std::shared_ptr<GraphicsDevice_Vulkan::AllocationHandler> allocationhandler;
|
|
VkSwapchainKHR swapChain = VK_NULL_HANDLE;
|
|
VkFormat swapChainImageFormat;
|
|
VkExtent2D swapChainExtent;
|
|
wi::vector<VkImage> swapChainImages;
|
|
wi::vector<VkImageView> swapChainImageViews;
|
|
wi::vector<VkFramebuffer> swapChainFramebuffers;
|
|
|
|
Texture dummyTexture;
|
|
RenderPass renderpass;
|
|
|
|
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
|
|
|
uint32_t swapChainImageIndex = 0;
|
|
VkSemaphore swapchainAcquireSemaphore = VK_NULL_HANDLE;
|
|
VkSemaphore swapchainReleaseSemaphore = VK_NULL_HANDLE;
|
|
|
|
ColorSpace colorSpace = ColorSpace::SRGB;
|
|
SwapChainDesc desc;
|
|
std::mutex locker;
|
|
|
|
~SwapChain_Vulkan()
|
|
{
|
|
if (allocationhandler == nullptr)
|
|
return;
|
|
allocationhandler->destroylocker.lock();
|
|
uint64_t framecount = allocationhandler->framecount;
|
|
|
|
for (size_t i = 0; i < swapChainImages.size(); ++i)
|
|
{
|
|
allocationhandler->destroyer_framebuffers.push_back(std::make_pair(swapChainFramebuffers[i], framecount));
|
|
allocationhandler->destroyer_imageviews.push_back(std::make_pair(swapChainImageViews[i], framecount));
|
|
}
|
|
|
|
#ifdef SDL2
|
|
// Checks if the SDL VIDEO System was already destroyed.
|
|
// If so we would delete the swapchain twice, causing a crash on wayland.
|
|
if (SDL_WasInit(SDL_INIT_VIDEO))
|
|
#endif
|
|
{
|
|
allocationhandler->destroyer_swapchains.push_back(std::make_pair(swapChain, framecount));
|
|
allocationhandler->destroyer_surfaces.push_back(std::make_pair(surface, framecount));
|
|
}
|
|
allocationhandler->destroyer_semaphores.push_back(std::make_pair(swapchainAcquireSemaphore, framecount));
|
|
allocationhandler->destroyer_semaphores.push_back(std::make_pair(swapchainReleaseSemaphore, framecount));
|
|
|
|
allocationhandler->destroylocker.unlock();
|
|
|
|
}
|
|
};
|
|
|
|
Buffer_Vulkan* to_internal(const GPUBuffer* param)
|
|
{
|
|
return static_cast<Buffer_Vulkan*>(param->internal_state.get());
|
|
}
|
|
Texture_Vulkan* to_internal(const Texture* param)
|
|
{
|
|
return static_cast<Texture_Vulkan*>(param->internal_state.get());
|
|
}
|
|
Sampler_Vulkan* to_internal(const Sampler* param)
|
|
{
|
|
return static_cast<Sampler_Vulkan*>(param->internal_state.get());
|
|
}
|
|
QueryHeap_Vulkan* to_internal(const GPUQueryHeap* param)
|
|
{
|
|
return static_cast<QueryHeap_Vulkan*>(param->internal_state.get());
|
|
}
|
|
Shader_Vulkan* to_internal(const Shader* param)
|
|
{
|
|
return static_cast<Shader_Vulkan*>(param->internal_state.get());
|
|
}
|
|
PipelineState_Vulkan* to_internal(const PipelineState* param)
|
|
{
|
|
return static_cast<PipelineState_Vulkan*>(param->internal_state.get());
|
|
}
|
|
RenderPass_Vulkan* to_internal(const RenderPass* param)
|
|
{
|
|
return static_cast<RenderPass_Vulkan*>(param->internal_state.get());
|
|
}
|
|
BVH_Vulkan* to_internal(const RaytracingAccelerationStructure* param)
|
|
{
|
|
return static_cast<BVH_Vulkan*>(param->internal_state.get());
|
|
}
|
|
RTPipelineState_Vulkan* to_internal(const RaytracingPipelineState* param)
|
|
{
|
|
return static_cast<RTPipelineState_Vulkan*>(param->internal_state.get());
|
|
}
|
|
SwapChain_Vulkan* to_internal(const SwapChain* param)
|
|
{
|
|
return static_cast<SwapChain_Vulkan*>(param->internal_state.get());
|
|
}
|
|
|
|
inline const std::string GetCachePath()
|
|
{
|
|
return wi::helper::GetTempDirectoryPath() + "WickedVkPipelineCache.data";
|
|
}
|
|
|
|
bool CreateSwapChainInternal(
|
|
SwapChain_Vulkan* internal_state,
|
|
VkPhysicalDevice physicalDevice,
|
|
VkDevice device,
|
|
std::shared_ptr<GraphicsDevice_Vulkan::AllocationHandler> allocationhandler
|
|
)
|
|
{
|
|
// In vulkan, the swapchain recreate can happen whenever it gets outdated, it's not in application's control
|
|
// so we have to be extra careful
|
|
std::scoped_lock lock(internal_state->locker);
|
|
|
|
VkResult res;
|
|
|
|
VkSurfaceCapabilitiesKHR swapchain_capabilities;
|
|
res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, internal_state->surface, &swapchain_capabilities);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
uint32_t formatCount;
|
|
res = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, internal_state->surface, &formatCount, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
wi::vector<VkSurfaceFormatKHR> swapchain_formats(formatCount);
|
|
res = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, internal_state->surface, &formatCount, swapchain_formats.data());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
uint32_t presentModeCount;
|
|
res = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, internal_state->surface, &presentModeCount, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
wi::vector<VkPresentModeKHR> swapchain_presentModes(presentModeCount);
|
|
swapchain_presentModes.resize(presentModeCount);
|
|
res = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, internal_state->surface, &presentModeCount, swapchain_presentModes.data());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
VkSurfaceFormatKHR surfaceFormat = {};
|
|
surfaceFormat.format = _ConvertFormat(internal_state->desc.format);
|
|
surfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
|
bool valid = false;
|
|
|
|
for (const auto& format : swapchain_formats)
|
|
{
|
|
if (!internal_state->desc.allow_hdr && format.colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
|
continue;
|
|
if (format.format == surfaceFormat.format)
|
|
{
|
|
surfaceFormat = format;
|
|
valid = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!valid)
|
|
{
|
|
surfaceFormat.format = VK_FORMAT_B8G8R8A8_UNORM;
|
|
surfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
|
}
|
|
|
|
// For now, we only include the color spaces that were tested successfully:
|
|
ColorSpace prev_colorspace = internal_state->colorSpace;
|
|
switch (surfaceFormat.colorSpace)
|
|
{
|
|
default:
|
|
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
|
|
internal_state->colorSpace = ColorSpace::SRGB;
|
|
break;
|
|
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
|
|
internal_state->colorSpace = ColorSpace::HDR_LINEAR;
|
|
break;
|
|
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
|
|
internal_state->colorSpace = ColorSpace::HDR10_ST2084;
|
|
break;
|
|
}
|
|
|
|
if (prev_colorspace != internal_state->colorSpace)
|
|
{
|
|
if (internal_state->swapChain != VK_NULL_HANDLE)
|
|
{
|
|
// For some reason, if the swapchain gets recreated (via oldSwapChain) with different color space but same image format,
|
|
// the color space change will not be applied
|
|
res = vkDeviceWaitIdle(device);
|
|
assert(res == VK_SUCCESS);
|
|
vkDestroySwapchainKHR(device, internal_state->swapChain, nullptr);
|
|
internal_state->swapChain = nullptr;
|
|
}
|
|
}
|
|
|
|
if (swapchain_capabilities.currentExtent.width != 0xFFFFFFFF && swapchain_capabilities.currentExtent.width != 0xFFFFFFFF)
|
|
{
|
|
internal_state->swapChainExtent = swapchain_capabilities.currentExtent;
|
|
}
|
|
else
|
|
{
|
|
internal_state->swapChainExtent = { internal_state->desc.width, internal_state->desc.height };
|
|
internal_state->swapChainExtent.width = std::max(swapchain_capabilities.minImageExtent.width, std::min(swapchain_capabilities.maxImageExtent.width, internal_state->swapChainExtent.width));
|
|
internal_state->swapChainExtent.height = std::max(swapchain_capabilities.minImageExtent.height, std::min(swapchain_capabilities.maxImageExtent.height, internal_state->swapChainExtent.height));
|
|
}
|
|
|
|
uint32_t imageCount = std::max(internal_state->desc.buffer_count, swapchain_capabilities.minImageCount);
|
|
if ((swapchain_capabilities.maxImageCount > 0) && (imageCount > swapchain_capabilities.maxImageCount))
|
|
{
|
|
imageCount = swapchain_capabilities.maxImageCount;
|
|
}
|
|
|
|
VkSwapchainCreateInfoKHR createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
|
createInfo.surface = internal_state->surface;
|
|
createInfo.minImageCount = imageCount;
|
|
createInfo.imageFormat = surfaceFormat.format;
|
|
createInfo.imageColorSpace = surfaceFormat.colorSpace;
|
|
createInfo.imageExtent = internal_state->swapChainExtent;
|
|
createInfo.imageArrayLayers = 1;
|
|
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
|
createInfo.preTransform = swapchain_capabilities.currentTransform;
|
|
|
|
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; // The only one that is always supported
|
|
if (!internal_state->desc.vsync)
|
|
{
|
|
// The mailbox/immediate present mode is not necessarily supported:
|
|
for (auto& presentMode : swapchain_presentModes)
|
|
{
|
|
if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR)
|
|
{
|
|
createInfo.presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
|
|
break;
|
|
}
|
|
if (presentMode == VK_PRESENT_MODE_IMMEDIATE_KHR)
|
|
{
|
|
createInfo.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
|
}
|
|
}
|
|
}
|
|
createInfo.clipped = VK_TRUE;
|
|
createInfo.oldSwapchain = internal_state->swapChain;
|
|
|
|
res = vkCreateSwapchainKHR(device, &createInfo, nullptr, &internal_state->swapChain);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
if (createInfo.oldSwapchain != VK_NULL_HANDLE)
|
|
{
|
|
vkDestroySwapchainKHR(device, createInfo.oldSwapchain, nullptr);
|
|
}
|
|
|
|
res = vkGetSwapchainImagesKHR(device, internal_state->swapChain, &imageCount, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
internal_state->swapChainImages.resize(imageCount);
|
|
res = vkGetSwapchainImagesKHR(device, internal_state->swapChain, &imageCount, internal_state->swapChainImages.data());
|
|
assert(res == VK_SUCCESS);
|
|
internal_state->swapChainImageFormat = surfaceFormat.format;
|
|
|
|
// Create default render pass:
|
|
{
|
|
VkAttachmentDescription colorAttachment = {};
|
|
colorAttachment.format = internal_state->swapChainImageFormat;
|
|
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
|
|
VkAttachmentReference colorAttachmentRef = {};
|
|
colorAttachmentRef.attachment = 0;
|
|
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
VkSubpassDescription subpass = {};
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
subpass.colorAttachmentCount = 1;
|
|
subpass.pColorAttachments = &colorAttachmentRef;
|
|
|
|
VkRenderPassCreateInfo renderPassInfo = {};
|
|
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
|
renderPassInfo.attachmentCount = 1;
|
|
renderPassInfo.pAttachments = &colorAttachment;
|
|
renderPassInfo.subpassCount = 1;
|
|
renderPassInfo.pSubpasses = &subpass;
|
|
|
|
VkSubpassDependency dependency = {};
|
|
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
|
dependency.dstSubpass = 0;
|
|
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
dependency.srcAccessMask = 0;
|
|
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
|
|
renderPassInfo.dependencyCount = 1;
|
|
renderPassInfo.pDependencies = &dependency;
|
|
|
|
internal_state->dummyTexture.desc.format = internal_state->desc.format;
|
|
internal_state->dummyTexture.desc.width = internal_state->desc.width;
|
|
internal_state->dummyTexture.desc.height = internal_state->desc.height;
|
|
internal_state->renderpass = {};
|
|
wi::helper::hash_combine(internal_state->renderpass.hash, internal_state->swapChainImageFormat);
|
|
auto renderpass_internal = std::make_shared<RenderPass_Vulkan>();
|
|
renderpass_internal->allocationhandler = allocationhandler;
|
|
internal_state->renderpass.internal_state = renderpass_internal;
|
|
internal_state->renderpass.desc.attachments.push_back(RenderPassAttachment::RenderTarget(internal_state->dummyTexture));
|
|
res = vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderpass_internal->renderpass);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
}
|
|
|
|
// Create swap chain render targets:
|
|
internal_state->swapChainImageViews.resize(internal_state->swapChainImages.size());
|
|
internal_state->swapChainFramebuffers.resize(internal_state->swapChainImages.size());
|
|
for (size_t i = 0; i < internal_state->swapChainImages.size(); ++i)
|
|
{
|
|
VkImageViewCreateInfo createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
createInfo.image = internal_state->swapChainImages[i];
|
|
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
createInfo.format = internal_state->swapChainImageFormat;
|
|
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
createInfo.subresourceRange.baseMipLevel = 0;
|
|
createInfo.subresourceRange.levelCount = 1;
|
|
createInfo.subresourceRange.baseArrayLayer = 0;
|
|
createInfo.subresourceRange.layerCount = 1;
|
|
|
|
if (internal_state->swapChainImageViews[i] != VK_NULL_HANDLE)
|
|
{
|
|
allocationhandler->destroylocker.lock();
|
|
allocationhandler->destroyer_imageviews.push_back(std::make_pair(internal_state->swapChainImageViews[i], allocationhandler->framecount));
|
|
allocationhandler->destroylocker.unlock();
|
|
}
|
|
res = vkCreateImageView(device, &createInfo, nullptr, &internal_state->swapChainImageViews[i]);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
VkImageView attachments[] = {
|
|
internal_state->swapChainImageViews[i]
|
|
};
|
|
|
|
VkFramebufferCreateInfo framebufferInfo = {};
|
|
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
|
framebufferInfo.renderPass = to_internal(&internal_state->renderpass)->renderpass;
|
|
framebufferInfo.attachmentCount = 1;
|
|
framebufferInfo.pAttachments = attachments;
|
|
framebufferInfo.width = internal_state->swapChainExtent.width;
|
|
framebufferInfo.height = internal_state->swapChainExtent.height;
|
|
framebufferInfo.layers = 1;
|
|
|
|
if (internal_state->swapChainFramebuffers[i] != VK_NULL_HANDLE)
|
|
{
|
|
allocationhandler->destroylocker.lock();
|
|
allocationhandler->destroyer_framebuffers.push_back(std::make_pair(internal_state->swapChainFramebuffers[i], allocationhandler->framecount));
|
|
allocationhandler->destroylocker.unlock();
|
|
}
|
|
res = vkCreateFramebuffer(device, &framebufferInfo, nullptr, &internal_state->swapChainFramebuffers[i]);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
|
|
|
|
VkSemaphoreCreateInfo semaphoreInfo = {};
|
|
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
|
|
if (internal_state->swapchainAcquireSemaphore == VK_NULL_HANDLE)
|
|
{
|
|
res = vkCreateSemaphore(device, &semaphoreInfo, nullptr, &internal_state->swapchainAcquireSemaphore);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
|
|
if (internal_state->swapchainReleaseSemaphore == VK_NULL_HANDLE)
|
|
{
|
|
res = vkCreateSemaphore(device, &semaphoreInfo, nullptr, &internal_state->swapchainReleaseSemaphore);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
using namespace vulkan_internal;
|
|
|
|
|
|
|
|
void GraphicsDevice_Vulkan::CommandQueue::submit(GraphicsDevice_Vulkan* device, VkFence fence)
|
|
{
|
|
VkSubmitInfo submitInfo = {};
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submitInfo.commandBufferCount = (uint32_t)submit_cmds.size();
|
|
submitInfo.pCommandBuffers = submit_cmds.data();
|
|
|
|
submitInfo.waitSemaphoreCount = (uint32_t)submit_waitSemaphores.size();
|
|
submitInfo.pWaitSemaphores = submit_waitSemaphores.data();
|
|
submitInfo.pWaitDstStageMask = submit_waitStages.data();
|
|
|
|
submitInfo.signalSemaphoreCount = (uint32_t)submit_signalSemaphores.size();
|
|
submitInfo.pSignalSemaphores = submit_signalSemaphores.data();
|
|
|
|
VkTimelineSemaphoreSubmitInfo timelineInfo = {};
|
|
timelineInfo.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO;
|
|
timelineInfo.pNext = nullptr;
|
|
timelineInfo.waitSemaphoreValueCount = (uint32_t)submit_waitValues.size();
|
|
timelineInfo.pWaitSemaphoreValues = submit_waitValues.data();
|
|
timelineInfo.signalSemaphoreValueCount = (uint32_t)submit_signalValues.size();
|
|
timelineInfo.pSignalSemaphoreValues = submit_signalValues.data();
|
|
|
|
submitInfo.pNext = &timelineInfo;
|
|
|
|
VkResult res = vkQueueSubmit(queue, 1, &submitInfo, fence);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
if (!submit_swapchains.empty())
|
|
{
|
|
VkPresentInfoKHR presentInfo = {};
|
|
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
|
presentInfo.waitSemaphoreCount = (uint32_t)submit_signalSemaphores.size();
|
|
presentInfo.pWaitSemaphores = submit_signalSemaphores.data();
|
|
presentInfo.swapchainCount = (uint32_t)submit_swapchains.size();
|
|
presentInfo.pSwapchains = submit_swapchains.data();
|
|
presentInfo.pImageIndices = submit_swapChainImageIndices.data();
|
|
res = vkQueuePresentKHR(queue, &presentInfo);
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
// Handle outdated error in present:
|
|
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
|
{
|
|
for (auto& swapchain : swapchain_updates)
|
|
{
|
|
auto internal_state = to_internal(&swapchain);
|
|
bool success = CreateSwapChainInternal(internal_state, device->physicalDevice, device->device, device->allocationhandler);
|
|
assert(success);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
swapchain_updates.clear();
|
|
submit_swapchains.clear();
|
|
submit_swapChainImageIndices.clear();
|
|
submit_waitStages.clear();
|
|
submit_waitSemaphores.clear();
|
|
submit_waitValues.clear();
|
|
submit_signalSemaphores.clear();
|
|
submit_signalValues.clear();
|
|
submit_cmds.clear();
|
|
}
|
|
|
|
void GraphicsDevice_Vulkan::CopyAllocator::init(GraphicsDevice_Vulkan* device)
|
|
{
|
|
this->device = device;
|
|
|
|
VkSemaphoreTypeCreateInfo timelineCreateInfo = {};
|
|
timelineCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO;
|
|
timelineCreateInfo.pNext = nullptr;
|
|
timelineCreateInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
|
|
timelineCreateInfo.initialValue = 0;
|
|
|
|
VkSemaphoreCreateInfo createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
createInfo.pNext = &timelineCreateInfo;
|
|
createInfo.flags = 0;
|
|
|
|
VkResult res = vkCreateSemaphore(device->device, &createInfo, nullptr, &semaphore);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
void GraphicsDevice_Vulkan::CopyAllocator::destroy()
|
|
{
|
|
vkQueueWaitIdle(device->copyQueue);
|
|
for (auto& x : freelist)
|
|
{
|
|
vkDestroyCommandPool(device->device, x.commandPool, nullptr);
|
|
}
|
|
vkDestroySemaphore(device->device, semaphore, nullptr);
|
|
}
|
|
GraphicsDevice_Vulkan::CopyAllocator::CopyCMD GraphicsDevice_Vulkan::CopyAllocator::allocate(uint64_t staging_size)
|
|
{
|
|
locker.lock();
|
|
|
|
// create a new command list if there are no free ones:
|
|
if (freelist.empty())
|
|
{
|
|
CopyCMD cmd;
|
|
|
|
VkCommandPoolCreateInfo poolInfo = {};
|
|
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
poolInfo.queueFamilyIndex = device->copyFamily;
|
|
poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
|
|
|
VkResult res = vkCreateCommandPool(device->device, &poolInfo, nullptr, &cmd.commandPool);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
VkCommandBufferAllocateInfo commandBufferInfo = {};
|
|
commandBufferInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
commandBufferInfo.commandBufferCount = 1;
|
|
commandBufferInfo.commandPool = cmd.commandPool;
|
|
commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
|
|
res = vkAllocateCommandBuffers(device->device, &commandBufferInfo, &cmd.commandBuffer);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
freelist.push_back(cmd);
|
|
}
|
|
|
|
CopyCMD cmd = freelist.back();
|
|
if (cmd.uploadbuffer.desc.size < staging_size)
|
|
{
|
|
// Try to search for a staging buffer that can fit the request:
|
|
for (size_t i = 0; i < freelist.size(); ++i)
|
|
{
|
|
if (freelist[i].uploadbuffer.desc.size >= staging_size)
|
|
{
|
|
cmd = freelist[i];
|
|
std::swap(freelist[i], freelist.back());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
freelist.pop_back();
|
|
locker.unlock();
|
|
|
|
// If no buffer was found that fits the data, create one:
|
|
if (cmd.uploadbuffer.desc.size < staging_size)
|
|
{
|
|
GPUBufferDesc uploaddesc;
|
|
uploaddesc.size = wi::math::GetNextPowerOfTwo(staging_size);
|
|
uploaddesc.usage = Usage::UPLOAD;
|
|
bool upload_success = device->CreateBuffer(&uploaddesc, nullptr, &cmd.uploadbuffer);
|
|
assert(upload_success);
|
|
device->SetName(&cmd.uploadbuffer, "CopyAllocator::uploadBuffer");
|
|
}
|
|
|
|
// begin command list in valid state:
|
|
VkResult res = vkResetCommandPool(device->device, cmd.commandPool, 0);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
VkCommandBufferBeginInfo beginInfo = {};
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
beginInfo.pInheritanceInfo = nullptr;
|
|
|
|
res = vkBeginCommandBuffer(cmd.commandBuffer, &beginInfo);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
return cmd;
|
|
}
|
|
void GraphicsDevice_Vulkan::CopyAllocator::submit(CopyCMD cmd)
|
|
{
|
|
VkResult res = vkEndCommandBuffer(cmd.commandBuffer);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// It was very slow in Vulkan to submit the copies immediately
|
|
// In Vulkan, the submit is not thread safe, so it had to be locked
|
|
// Instead, the submits are batched and performed in flush() function
|
|
locker.lock();
|
|
cmd.target = ++fenceValue;
|
|
worklist.push_back(cmd);
|
|
submit_cmds.push_back(cmd.commandBuffer);
|
|
submit_wait = std::max(submit_wait, cmd.target);
|
|
locker.unlock();
|
|
}
|
|
uint64_t GraphicsDevice_Vulkan::CopyAllocator::flush()
|
|
{
|
|
locker.lock();
|
|
if (!submit_cmds.empty())
|
|
{
|
|
VkSubmitInfo submitInfo = {};
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submitInfo.commandBufferCount = (uint32_t)submit_cmds.size();
|
|
submitInfo.pCommandBuffers = submit_cmds.data();
|
|
submitInfo.pSignalSemaphores = &semaphore;
|
|
submitInfo.signalSemaphoreCount = 1;
|
|
|
|
VkTimelineSemaphoreSubmitInfo timelineInfo = {};
|
|
timelineInfo.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO;
|
|
timelineInfo.pNext = nullptr;
|
|
timelineInfo.waitSemaphoreValueCount = 0;
|
|
timelineInfo.pWaitSemaphoreValues = nullptr;
|
|
timelineInfo.signalSemaphoreValueCount = 1;
|
|
timelineInfo.pSignalSemaphoreValues = &submit_wait;
|
|
|
|
submitInfo.pNext = &timelineInfo;
|
|
|
|
VkResult res = vkQueueSubmit(device->copyQueue, 1, &submitInfo, VK_NULL_HANDLE);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
submit_cmds.clear();
|
|
}
|
|
|
|
// free up the finished command lists:
|
|
uint64_t completed_fence_value;
|
|
VkResult res = vkGetSemaphoreCounterValue(device->device, semaphore, &completed_fence_value);
|
|
assert(res == VK_SUCCESS);
|
|
for (size_t i = 0; i < worklist.size(); ++i)
|
|
{
|
|
if (worklist[i].target <= completed_fence_value)
|
|
{
|
|
freelist.push_back(worklist[i]);
|
|
worklist[i] = worklist.back();
|
|
worklist.pop_back();
|
|
i--;
|
|
}
|
|
}
|
|
|
|
uint64_t value = submit_wait;
|
|
submit_wait = 0;
|
|
locker.unlock();
|
|
|
|
return value;
|
|
}
|
|
|
|
void GraphicsDevice_Vulkan::DescriptorBinderPool::init(GraphicsDevice_Vulkan* device)
|
|
{
|
|
this->device = device;
|
|
|
|
VkResult res;
|
|
|
|
// Create descriptor pool:
|
|
VkDescriptorPoolSize poolSizes[10] = {};
|
|
uint32_t count = 0;
|
|
|
|
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
poolSizes[0].descriptorCount = DESCRIPTORBINDER_CBV_COUNT * poolSize;
|
|
count++;
|
|
|
|
poolSizes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
poolSizes[1].descriptorCount = DESCRIPTORBINDER_CBV_COUNT * poolSize;
|
|
count++;
|
|
|
|
poolSizes[2].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
|
poolSizes[2].descriptorCount = DESCRIPTORBINDER_SRV_COUNT * poolSize;
|
|
count++;
|
|
|
|
poolSizes[3].type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
|
|
poolSizes[3].descriptorCount = DESCRIPTORBINDER_SRV_COUNT * poolSize;
|
|
count++;
|
|
|
|
poolSizes[4].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
poolSizes[4].descriptorCount = DESCRIPTORBINDER_SRV_COUNT * poolSize;
|
|
count++;
|
|
|
|
poolSizes[5].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
poolSizes[5].descriptorCount = DESCRIPTORBINDER_UAV_COUNT * poolSize;
|
|
count++;
|
|
|
|
poolSizes[6].type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
|
|
poolSizes[6].descriptorCount = DESCRIPTORBINDER_UAV_COUNT * poolSize;
|
|
count++;
|
|
|
|
poolSizes[7].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
poolSizes[7].descriptorCount = DESCRIPTORBINDER_UAV_COUNT * poolSize;
|
|
count++;
|
|
|
|
poolSizes[8].type = VK_DESCRIPTOR_TYPE_SAMPLER;
|
|
poolSizes[8].descriptorCount = DESCRIPTORBINDER_SAMPLER_COUNT * poolSize;
|
|
count++;
|
|
|
|
if (device->CheckCapability(GraphicsDeviceCapability::RAYTRACING))
|
|
{
|
|
poolSizes[9].type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
|
|
poolSizes[9].descriptorCount = DESCRIPTORBINDER_SRV_COUNT * poolSize;
|
|
count++;
|
|
}
|
|
|
|
VkDescriptorPoolCreateInfo poolInfo = {};
|
|
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
|
poolInfo.poolSizeCount = count;
|
|
poolInfo.pPoolSizes = poolSizes;
|
|
poolInfo.maxSets = poolSize;
|
|
|
|
res = vkCreateDescriptorPool(device->device, &poolInfo, nullptr, &descriptorPool);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// WARNING: MUST NOT CALL reset() HERE!
|
|
// This is because init can be called mid-frame when there is allocation error, but the bindings must be retained!
|
|
|
|
}
|
|
void GraphicsDevice_Vulkan::DescriptorBinderPool::destroy()
|
|
{
|
|
if (descriptorPool != VK_NULL_HANDLE)
|
|
{
|
|
device->allocationhandler->destroylocker.lock();
|
|
device->allocationhandler->destroyer_descriptorPools.push_back(std::make_pair(descriptorPool, device->FRAMECOUNT));
|
|
descriptorPool = VK_NULL_HANDLE;
|
|
device->allocationhandler->destroylocker.unlock();
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::DescriptorBinderPool::reset()
|
|
{
|
|
if (descriptorPool != VK_NULL_HANDLE)
|
|
{
|
|
VkResult res = vkResetDescriptorPool(device->device, descriptorPool, 0);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
}
|
|
|
|
void GraphicsDevice_Vulkan::DescriptorBinder::init(GraphicsDevice_Vulkan* device)
|
|
{
|
|
this->device = device;
|
|
|
|
// Important that these don't reallocate themselves during writing descriptors!
|
|
descriptorWrites.reserve(128);
|
|
bufferInfos.reserve(128);
|
|
imageInfos.reserve(128);
|
|
texelBufferViews.reserve(128);
|
|
accelerationStructureViews.reserve(128);
|
|
}
|
|
void GraphicsDevice_Vulkan::DescriptorBinder::reset()
|
|
{
|
|
table = {};
|
|
dirty = true;
|
|
}
|
|
void GraphicsDevice_Vulkan::DescriptorBinder::flush(bool graphics, CommandList cmd)
|
|
{
|
|
if (dirty == DIRTY_NONE)
|
|
return;
|
|
|
|
CommandList_Vulkan& commandlist = device->GetCommandList(cmd);
|
|
auto pso_internal = graphics ? to_internal(commandlist.active_pso) : nullptr;
|
|
auto cs_internal = graphics ? nullptr : to_internal(commandlist.active_cs);
|
|
VkCommandBuffer commandBuffer = commandlist.GetCommandBuffer();
|
|
|
|
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
|
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
|
|
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
|
|
uint32_t uniform_buffer_dynamic_count = 0;
|
|
if (graphics)
|
|
{
|
|
pipelineLayout = pso_internal->pipelineLayout;
|
|
descriptorSetLayout = pso_internal->descriptorSetLayout;
|
|
descriptorSet = descriptorSet_graphics;
|
|
uniform_buffer_dynamic_count = (uint32_t)pso_internal->uniform_buffer_dynamic_slots.size();
|
|
for (size_t i = 0; i < pso_internal->uniform_buffer_dynamic_slots.size(); ++i)
|
|
{
|
|
uniform_buffer_dynamic_offsets[i] = (uint32_t)table.CBV_offset[pso_internal->uniform_buffer_dynamic_slots[i]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pipelineLayout = cs_internal->pipelineLayout_cs;
|
|
descriptorSetLayout = cs_internal->descriptorSetLayout;
|
|
descriptorSet = descriptorSet_compute;
|
|
uniform_buffer_dynamic_count = (uint32_t)cs_internal->uniform_buffer_dynamic_slots.size();
|
|
for (size_t i = 0; i < cs_internal->uniform_buffer_dynamic_slots.size(); ++i)
|
|
{
|
|
uniform_buffer_dynamic_offsets[i] = (uint32_t)table.CBV_offset[cs_internal->uniform_buffer_dynamic_slots[i]];
|
|
}
|
|
}
|
|
|
|
if (dirty & DIRTY_DESCRIPTOR)
|
|
{
|
|
auto& binder_pool = commandlist.binder_pools[device->GetBufferIndex()];
|
|
|
|
VkDescriptorSetAllocateInfo allocInfo = {};
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
allocInfo.descriptorPool = binder_pool.descriptorPool;
|
|
allocInfo.descriptorSetCount = 1;
|
|
allocInfo.pSetLayouts = &descriptorSetLayout;
|
|
|
|
VkResult res = vkAllocateDescriptorSets(device->device, &allocInfo, &descriptorSet);
|
|
while (res == VK_ERROR_OUT_OF_POOL_MEMORY)
|
|
{
|
|
binder_pool.poolSize *= 2;
|
|
binder_pool.destroy();
|
|
binder_pool.init(device);
|
|
allocInfo.descriptorPool = binder_pool.descriptorPool;
|
|
res = vkAllocateDescriptorSets(device->device, &allocInfo, &descriptorSet);
|
|
}
|
|
assert(res == VK_SUCCESS);
|
|
|
|
descriptorWrites.clear();
|
|
bufferInfos.clear();
|
|
imageInfos.clear();
|
|
texelBufferViews.clear();
|
|
accelerationStructureViews.clear();
|
|
|
|
const auto& layoutBindings = graphics ? pso_internal->layoutBindings : cs_internal->layoutBindings;
|
|
const auto& imageViewTypes = graphics ? pso_internal->imageViewTypes : cs_internal->imageViewTypes;
|
|
|
|
|
|
int i = 0;
|
|
for (auto& x : layoutBindings)
|
|
{
|
|
if (x.pImmutableSamplers != nullptr)
|
|
{
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
VkImageViewType viewtype = imageViewTypes[i++];
|
|
|
|
for (uint32_t descriptor_index = 0; descriptor_index < x.descriptorCount; ++descriptor_index)
|
|
{
|
|
uint32_t unrolled_binding = x.binding + descriptor_index;
|
|
|
|
descriptorWrites.emplace_back();
|
|
auto& write = descriptorWrites.back();
|
|
write = {};
|
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
write.dstSet = descriptorSet;
|
|
write.dstArrayElement = descriptor_index;
|
|
write.descriptorType = x.descriptorType;
|
|
write.dstBinding = x.binding;
|
|
write.descriptorCount = 1;
|
|
|
|
switch (x.descriptorType)
|
|
{
|
|
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
|
{
|
|
imageInfos.emplace_back();
|
|
write.pImageInfo = &imageInfos.back();
|
|
imageInfos.back() = {};
|
|
|
|
const uint32_t original_binding = unrolled_binding - VULKAN_BINDING_SHIFT_S;
|
|
const Sampler& sampler = table.SAM[original_binding];
|
|
if (!sampler.IsValid())
|
|
{
|
|
imageInfos.back().sampler = device->nullSampler;
|
|
}
|
|
else
|
|
{
|
|
imageInfos.back().sampler = to_internal(&sampler)->resource;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
|
{
|
|
imageInfos.emplace_back();
|
|
write.pImageInfo = &imageInfos.back();
|
|
imageInfos.back() = {};
|
|
|
|
const uint32_t original_binding = unrolled_binding - VULKAN_BINDING_SHIFT_T;
|
|
const GPUResource& resource = table.SRV[original_binding];
|
|
if (!resource.IsValid() || !resource.IsTexture())
|
|
{
|
|
switch (viewtype)
|
|
{
|
|
case VK_IMAGE_VIEW_TYPE_1D:
|
|
imageInfos.back().imageView = device->nullImageView1D;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_2D:
|
|
imageInfos.back().imageView = device->nullImageView2D;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_3D:
|
|
imageInfos.back().imageView = device->nullImageView3D;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_CUBE:
|
|
imageInfos.back().imageView = device->nullImageViewCube;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
|
|
imageInfos.back().imageView = device->nullImageView1DArray;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
|
|
imageInfos.back().imageView = device->nullImageView2DArray;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
|
|
imageInfos.back().imageView = device->nullImageViewCubeArray;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_MAX_ENUM:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
imageInfos.back().imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
}
|
|
else
|
|
{
|
|
int subresource = table.SRV_index[original_binding];
|
|
auto texture_internal = to_internal((const Texture*)&resource);
|
|
auto& subresource_descriptor = subresource >= 0 ? texture_internal->subresources_srv[subresource] : texture_internal->srv;
|
|
imageInfos.back().imageView = subresource_descriptor.image_view;
|
|
|
|
imageInfos.back().imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
|
{
|
|
imageInfos.emplace_back();
|
|
write.pImageInfo = &imageInfos.back();
|
|
imageInfos.back() = {};
|
|
imageInfos.back().imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
const uint32_t original_binding = unrolled_binding - VULKAN_BINDING_SHIFT_U;
|
|
const GPUResource& resource = table.UAV[original_binding];
|
|
if (!resource.IsValid() || !resource.IsTexture())
|
|
{
|
|
switch (viewtype)
|
|
{
|
|
case VK_IMAGE_VIEW_TYPE_1D:
|
|
imageInfos.back().imageView = device->nullImageView1D;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_2D:
|
|
imageInfos.back().imageView = device->nullImageView2D;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_3D:
|
|
imageInfos.back().imageView = device->nullImageView3D;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_CUBE:
|
|
imageInfos.back().imageView = device->nullImageViewCube;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
|
|
imageInfos.back().imageView = device->nullImageView1DArray;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
|
|
imageInfos.back().imageView = device->nullImageView2DArray;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
|
|
imageInfos.back().imageView = device->nullImageViewCubeArray;
|
|
break;
|
|
case VK_IMAGE_VIEW_TYPE_MAX_ENUM:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int subresource = table.UAV_index[original_binding];
|
|
auto texture_internal = to_internal((const Texture*)&resource);
|
|
auto& subresource_descriptor = subresource >= 0 ? texture_internal->subresources_uav[subresource] : texture_internal->uav;
|
|
imageInfos.back().imageView = subresource_descriptor.image_view;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
|
{
|
|
bufferInfos.emplace_back();
|
|
write.pBufferInfo = &bufferInfos.back();
|
|
bufferInfos.back() = {};
|
|
|
|
const uint32_t original_binding = unrolled_binding - VULKAN_BINDING_SHIFT_B;
|
|
const GPUBuffer& buffer = table.CBV[original_binding];
|
|
uint64_t offset = table.CBV_offset[original_binding];
|
|
|
|
if (!buffer.IsValid())
|
|
{
|
|
bufferInfos.back().buffer = device->nullBuffer;
|
|
bufferInfos.back().range = VK_WHOLE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
auto internal_state = to_internal(&buffer);
|
|
bufferInfos.back().buffer = internal_state->resource;
|
|
bufferInfos.back().offset = offset;
|
|
if (graphics)
|
|
{
|
|
bufferInfos.back().range = pso_internal->uniform_buffer_sizes[original_binding];
|
|
}
|
|
else
|
|
{
|
|
bufferInfos.back().range = cs_internal->uniform_buffer_sizes[original_binding];
|
|
}
|
|
if (bufferInfos.back().range == 0ull)
|
|
{
|
|
bufferInfos.back().range = VK_WHOLE_SIZE;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
|
{
|
|
bufferInfos.emplace_back();
|
|
write.pBufferInfo = &bufferInfos.back();
|
|
bufferInfos.back() = {};
|
|
|
|
const uint32_t original_binding = unrolled_binding - VULKAN_BINDING_SHIFT_B;
|
|
const GPUBuffer& buffer = table.CBV[original_binding];
|
|
|
|
if (!buffer.IsValid())
|
|
{
|
|
bufferInfos.back().buffer = device->nullBuffer;
|
|
bufferInfos.back().range = VK_WHOLE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
auto internal_state = to_internal(&buffer);
|
|
bufferInfos.back().buffer = internal_state->resource;
|
|
if (graphics)
|
|
{
|
|
bufferInfos.back().range = pso_internal->uniform_buffer_sizes[original_binding];
|
|
}
|
|
else
|
|
{
|
|
bufferInfos.back().range = cs_internal->uniform_buffer_sizes[original_binding];
|
|
}
|
|
if (bufferInfos.back().range == 0ull)
|
|
{
|
|
bufferInfos.back().range = VK_WHOLE_SIZE;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
|
{
|
|
texelBufferViews.emplace_back();
|
|
write.pTexelBufferView = &texelBufferViews.back();
|
|
texelBufferViews.back() = {};
|
|
|
|
const uint32_t original_binding = unrolled_binding - VULKAN_BINDING_SHIFT_T;
|
|
const GPUResource& resource = table.SRV[original_binding];
|
|
if (!resource.IsValid() || !resource.IsBuffer())
|
|
{
|
|
texelBufferViews.back() = device->nullBufferView;
|
|
}
|
|
else
|
|
{
|
|
int subresource = table.SRV_index[original_binding];
|
|
auto buffer_internal = to_internal((const GPUBuffer*)&resource);
|
|
auto& subresource_descriptor = subresource >= 0 ? buffer_internal->subresources_srv[subresource] : buffer_internal->srv;
|
|
texelBufferViews.back() = subresource_descriptor.buffer_view;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
|
{
|
|
texelBufferViews.emplace_back();
|
|
write.pTexelBufferView = &texelBufferViews.back();
|
|
texelBufferViews.back() = {};
|
|
|
|
const uint32_t original_binding = unrolled_binding - VULKAN_BINDING_SHIFT_U;
|
|
const GPUResource& resource = table.UAV[original_binding];
|
|
if (!resource.IsValid() || !resource.IsBuffer())
|
|
{
|
|
texelBufferViews.back() = device->nullBufferView;
|
|
}
|
|
else
|
|
{
|
|
int subresource = table.UAV_index[original_binding];
|
|
auto buffer_internal = to_internal((const GPUBuffer*)&resource);
|
|
auto& subresource_descriptor = subresource >= 0 ? buffer_internal->subresources_uav[subresource] : buffer_internal->uav;
|
|
texelBufferViews.back() = subresource_descriptor.buffer_view;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
|
{
|
|
bufferInfos.emplace_back();
|
|
write.pBufferInfo = &bufferInfos.back();
|
|
bufferInfos.back() = {};
|
|
|
|
if (x.binding < VULKAN_BINDING_SHIFT_U)
|
|
{
|
|
// SRV
|
|
const uint32_t original_binding = unrolled_binding - VULKAN_BINDING_SHIFT_T;
|
|
const GPUResource& resource = table.SRV[original_binding];
|
|
if (!resource.IsValid() || !resource.IsBuffer())
|
|
{
|
|
bufferInfos.back().buffer = device->nullBuffer;
|
|
bufferInfos.back().range = VK_WHOLE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
int subresource = table.SRV_index[original_binding];
|
|
auto buffer_internal = to_internal((const GPUBuffer*)&resource);
|
|
auto& subresource_descriptor = subresource >= 0 ? buffer_internal->subresources_srv[subresource] : buffer_internal->srv;
|
|
bufferInfos.back() = subresource_descriptor.buffer_info;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// UAV
|
|
const uint32_t original_binding = unrolled_binding - VULKAN_BINDING_SHIFT_U;
|
|
const GPUResource& resource = table.UAV[original_binding];
|
|
if (!resource.IsValid() || !resource.IsBuffer())
|
|
{
|
|
bufferInfos.back().buffer = device->nullBuffer;
|
|
bufferInfos.back().range = VK_WHOLE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
int subresource = table.UAV_index[original_binding];
|
|
auto buffer_internal = to_internal((const GPUBuffer*)&resource);
|
|
auto& subresource_descriptor = subresource >= 0 ? buffer_internal->subresources_uav[subresource] : buffer_internal->uav;
|
|
bufferInfos.back() = subresource_descriptor.buffer_info;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR:
|
|
{
|
|
accelerationStructureViews.emplace_back();
|
|
write.pNext = &accelerationStructureViews.back();
|
|
accelerationStructureViews.back() = {};
|
|
accelerationStructureViews.back().sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR;
|
|
accelerationStructureViews.back().accelerationStructureCount = 1;
|
|
|
|
const uint32_t original_binding = unrolled_binding - VULKAN_BINDING_SHIFT_T;
|
|
const GPUResource& resource = table.SRV[original_binding];
|
|
if (!resource.IsValid() || !resource.IsAccelerationStructure())
|
|
{
|
|
assert(0); // invalid acceleration structure!
|
|
}
|
|
else
|
|
{
|
|
auto as_internal = to_internal((const RaytracingAccelerationStructure*)&resource);
|
|
accelerationStructureViews.back().pAccelerationStructures = &as_internal->resource;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
vkUpdateDescriptorSets(
|
|
device->device,
|
|
(uint32_t)descriptorWrites.size(),
|
|
descriptorWrites.data(),
|
|
0,
|
|
nullptr
|
|
);
|
|
}
|
|
|
|
VkPipelineBindPoint bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
if (!graphics)
|
|
{
|
|
bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
|
|
|
|
if (commandlist.active_cs->stage == ShaderStage::LIB)
|
|
{
|
|
bindPoint = VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR;
|
|
}
|
|
}
|
|
vkCmdBindDescriptorSets(
|
|
commandBuffer,
|
|
bindPoint,
|
|
pipelineLayout,
|
|
0,
|
|
1,
|
|
&descriptorSet,
|
|
uniform_buffer_dynamic_count,
|
|
uniform_buffer_dynamic_offsets
|
|
);
|
|
|
|
// Save last used descriptor set handles:
|
|
// This is needed to handle the case when descriptorSet is not allocated, but only dynamic offsets are updated
|
|
if (graphics)
|
|
{
|
|
descriptorSet_graphics = descriptorSet;
|
|
}
|
|
else
|
|
{
|
|
descriptorSet_compute = descriptorSet;
|
|
}
|
|
|
|
dirty = DIRTY_NONE;
|
|
}
|
|
|
|
void GraphicsDevice_Vulkan::pso_validate(CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
if (!commandlist.dirty_pso)
|
|
return;
|
|
|
|
const PipelineState* pso = commandlist.active_pso;
|
|
size_t pipeline_hash = commandlist.prev_pipeline_hash;
|
|
wi::helper::hash_combine(pipeline_hash, commandlist.vb_hash);
|
|
auto internal_state = to_internal(pso);
|
|
|
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
|
auto it = pipelines_global.find(pipeline_hash);
|
|
if (it == pipelines_global.end())
|
|
{
|
|
for (auto& x : commandlist.pipelines_worker)
|
|
{
|
|
if (pipeline_hash == x.first)
|
|
{
|
|
pipeline = x.second;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pipeline == VK_NULL_HANDLE)
|
|
{
|
|
VkGraphicsPipelineCreateInfo pipelineInfo = internal_state->pipelineInfo; // make a copy here
|
|
pipelineInfo.renderPass = to_internal(commandlist.active_renderpass)->renderpass;
|
|
pipelineInfo.subpass = 0;
|
|
|
|
// MSAA:
|
|
VkPipelineMultisampleStateCreateInfo multisampling = {};
|
|
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
|
multisampling.sampleShadingEnable = VK_FALSE;
|
|
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
|
if (commandlist.active_renderpass->desc.attachments.size() > 0 && commandlist.active_renderpass->desc.attachments[0].texture.IsValid())
|
|
{
|
|
multisampling.rasterizationSamples = (VkSampleCountFlagBits)commandlist.active_renderpass->desc.attachments[0].texture.desc.sample_count;
|
|
}
|
|
if (pso->desc.rs != nullptr)
|
|
{
|
|
const RasterizerState& desc = *pso->desc.rs;
|
|
if (desc.forced_sample_count > 1)
|
|
{
|
|
multisampling.rasterizationSamples = (VkSampleCountFlagBits)desc.forced_sample_count;
|
|
}
|
|
}
|
|
multisampling.minSampleShading = 1.0f;
|
|
VkSampleMask samplemask = internal_state->samplemask;
|
|
samplemask = pso->desc.sample_mask;
|
|
multisampling.pSampleMask = &samplemask;
|
|
if (pso->desc.bs != nullptr)
|
|
{
|
|
multisampling.alphaToCoverageEnable = pso->desc.bs->alpha_to_coverage_enable ? VK_TRUE : VK_FALSE;
|
|
}
|
|
else
|
|
{
|
|
multisampling.alphaToCoverageEnable = VK_FALSE;
|
|
}
|
|
multisampling.alphaToOneEnable = VK_FALSE;
|
|
|
|
pipelineInfo.pMultisampleState = &multisampling;
|
|
|
|
|
|
// Blending:
|
|
uint32_t numBlendAttachments = 0;
|
|
VkPipelineColorBlendAttachmentState colorBlendAttachments[8] = {};
|
|
const size_t blend_loopCount = commandlist.active_renderpass->desc.attachments.size();
|
|
for (size_t i = 0; i < blend_loopCount; ++i)
|
|
{
|
|
if (commandlist.active_renderpass->desc.attachments[i].type != RenderPassAttachment::Type::RENDERTARGET)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
size_t attachmentIndex = 0;
|
|
if (pso->desc.bs->independent_blend_enable)
|
|
attachmentIndex = i;
|
|
|
|
const auto& desc = pso->desc.bs->render_target[attachmentIndex];
|
|
VkPipelineColorBlendAttachmentState& attachment = colorBlendAttachments[numBlendAttachments];
|
|
numBlendAttachments++;
|
|
|
|
attachment.blendEnable = desc.blend_enable ? VK_TRUE : VK_FALSE;
|
|
|
|
attachment.colorWriteMask = 0;
|
|
if (has_flag(desc.render_target_write_mask, ColorWrite::ENABLE_RED))
|
|
{
|
|
attachment.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT;
|
|
}
|
|
if (has_flag(desc.render_target_write_mask, ColorWrite::ENABLE_GREEN))
|
|
{
|
|
attachment.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT;
|
|
}
|
|
if (has_flag(desc.render_target_write_mask, ColorWrite::ENABLE_BLUE))
|
|
{
|
|
attachment.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT;
|
|
}
|
|
if (has_flag(desc.render_target_write_mask, ColorWrite::ENABLE_ALPHA))
|
|
{
|
|
attachment.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT;
|
|
}
|
|
|
|
attachment.srcColorBlendFactor = _ConvertBlend(desc.src_blend);
|
|
attachment.dstColorBlendFactor = _ConvertBlend(desc.dest_blend);
|
|
attachment.colorBlendOp = _ConvertBlendOp(desc.blend_op);
|
|
attachment.srcAlphaBlendFactor = _ConvertBlend(desc.src_blend_alpha);
|
|
attachment.dstAlphaBlendFactor = _ConvertBlend(desc.dest_blend_alpha);
|
|
attachment.alphaBlendOp = _ConvertBlendOp(desc.blend_op_alpha);
|
|
}
|
|
|
|
VkPipelineColorBlendStateCreateInfo colorBlending = {};
|
|
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
|
colorBlending.logicOpEnable = VK_FALSE;
|
|
colorBlending.logicOp = VK_LOGIC_OP_COPY;
|
|
colorBlending.attachmentCount = numBlendAttachments;
|
|
colorBlending.pAttachments = colorBlendAttachments;
|
|
colorBlending.blendConstants[0] = 1.0f;
|
|
colorBlending.blendConstants[1] = 1.0f;
|
|
colorBlending.blendConstants[2] = 1.0f;
|
|
colorBlending.blendConstants[3] = 1.0f;
|
|
|
|
pipelineInfo.pColorBlendState = &colorBlending;
|
|
|
|
// Input layout:
|
|
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
|
|
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
|
wi::vector<VkVertexInputBindingDescription> bindings;
|
|
wi::vector<VkVertexInputAttributeDescription> attributes;
|
|
if (pso->desc.il != nullptr)
|
|
{
|
|
uint32_t lastBinding = 0xFFFFFFFF;
|
|
for (auto& x : pso->desc.il->elements)
|
|
{
|
|
if (x.input_slot == lastBinding)
|
|
continue;
|
|
lastBinding = x.input_slot;
|
|
VkVertexInputBindingDescription& bind = bindings.emplace_back();
|
|
bind.binding = x.input_slot;
|
|
bind.inputRate = x.input_slot_class == InputClassification::PER_VERTEX_DATA ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
|
|
bind.stride = commandlist.vb_strides[x.input_slot];
|
|
}
|
|
|
|
uint32_t offset = 0;
|
|
uint32_t i = 0;
|
|
lastBinding = 0xFFFFFFFF;
|
|
for (auto& x : pso->desc.il->elements)
|
|
{
|
|
VkVertexInputAttributeDescription attr = {};
|
|
attr.binding = x.input_slot;
|
|
if (attr.binding != lastBinding)
|
|
{
|
|
lastBinding = attr.binding;
|
|
offset = 0;
|
|
}
|
|
attr.format = _ConvertFormat(x.format);
|
|
attr.location = i;
|
|
attr.offset = x.aligned_byte_offset;
|
|
if (attr.offset == InputLayout::APPEND_ALIGNED_ELEMENT)
|
|
{
|
|
// need to manually resolve this from the format spec.
|
|
attr.offset = offset;
|
|
offset += GetFormatStride(x.format);
|
|
}
|
|
|
|
attributes.push_back(attr);
|
|
|
|
i++;
|
|
}
|
|
|
|
vertexInputInfo.vertexBindingDescriptionCount = static_cast<uint32_t>(bindings.size());
|
|
vertexInputInfo.pVertexBindingDescriptions = bindings.data();
|
|
vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributes.size());
|
|
vertexInputInfo.pVertexAttributeDescriptions = attributes.data();
|
|
}
|
|
pipelineInfo.pVertexInputState = &vertexInputInfo;
|
|
|
|
VkResult res = vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineInfo, nullptr, &pipeline);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
commandlist.pipelines_worker.push_back(std::make_pair(pipeline_hash, pipeline));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pipeline = it->second;
|
|
}
|
|
assert(pipeline != VK_NULL_HANDLE);
|
|
|
|
vkCmdBindPipeline(commandlist.GetCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
|
commandlist.dirty_pso = false;
|
|
}
|
|
|
|
void GraphicsDevice_Vulkan::predraw(CommandList cmd)
|
|
{
|
|
pso_validate(cmd);
|
|
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
commandlist.binder.flush(true, cmd);
|
|
}
|
|
void GraphicsDevice_Vulkan::predispatch(CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
commandlist.binder.flush(false, cmd);
|
|
}
|
|
|
|
// Engine functions
|
|
GraphicsDevice_Vulkan::GraphicsDevice_Vulkan(wi::platform::window_type window, ValidationMode validationMode_)
|
|
{
|
|
wi::Timer timer;
|
|
|
|
TOPLEVEL_ACCELERATION_STRUCTURE_INSTANCE_SIZE = sizeof(VkAccelerationStructureInstanceKHR);
|
|
|
|
validationMode = validationMode_;
|
|
|
|
VkResult res;
|
|
|
|
res = volkInitialize();
|
|
assert(res == VK_SUCCESS);
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
wi::helper::messageBox("volkInitialize failed! ERROR: " + std::to_string(res), "Error!");
|
|
wi::platform::Exit();
|
|
}
|
|
|
|
// Fill out application info:
|
|
VkApplicationInfo appInfo = {};
|
|
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
|
appInfo.pApplicationName = "Wicked Engine Application";
|
|
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
|
|
appInfo.pEngineName = "Wicked Engine";
|
|
appInfo.engineVersion = VK_MAKE_VERSION(wi::version::GetMajor(), wi::version::GetMinor(), wi::version::GetRevision());
|
|
appInfo.apiVersion = VK_API_VERSION_1_2;
|
|
|
|
// Enumerate available layers and extensions:
|
|
uint32_t instanceLayerCount;
|
|
res = vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
wi::vector<VkLayerProperties> availableInstanceLayers(instanceLayerCount);
|
|
res = vkEnumerateInstanceLayerProperties(&instanceLayerCount, availableInstanceLayers.data());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
uint32_t extensionCount = 0;
|
|
res = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
wi::vector<VkExtensionProperties> availableInstanceExtensions(extensionCount);
|
|
res = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, availableInstanceExtensions.data());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
wi::vector<const char*> instanceLayers;
|
|
wi::vector<const char*> instanceExtensions;
|
|
|
|
for (auto& availableExtension : availableInstanceExtensions)
|
|
{
|
|
if (strcmp(availableExtension.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0)
|
|
{
|
|
debugUtils = true;
|
|
instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
|
}
|
|
else if (strcmp(availableExtension.extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == 0)
|
|
{
|
|
instanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
}
|
|
else if (strcmp(availableExtension.extensionName, VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME) == 0)
|
|
{
|
|
instanceExtensions.push_back(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
|
|
}
|
|
}
|
|
|
|
instanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
|
|
|
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
|
instanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
|
|
#elif SDL2
|
|
{
|
|
uint32_t extensionCount;
|
|
SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr);
|
|
wi::vector<const char *> extensionNames_sdl(extensionCount);
|
|
SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, extensionNames_sdl.data());
|
|
instanceExtensions.reserve(instanceExtensions.size() + extensionNames_sdl.size());
|
|
for (auto& x : extensionNames_sdl)
|
|
{
|
|
instanceExtensions.push_back(x);
|
|
}
|
|
}
|
|
#endif // _WIN32
|
|
|
|
if (validationMode != ValidationMode::Disabled)
|
|
{
|
|
// Determine the optimal validation layers to enable that are necessary for useful debugging
|
|
static const wi::vector<const char*> validationLayerPriorityList[] =
|
|
{
|
|
// The preferred validation layer is "VK_LAYER_KHRONOS_validation"
|
|
{"VK_LAYER_KHRONOS_validation"},
|
|
|
|
// Otherwise we fallback to using the LunarG meta layer
|
|
{"VK_LAYER_LUNARG_standard_validation"},
|
|
|
|
// Otherwise we attempt to enable the individual layers that compose the LunarG meta layer since it doesn't exist
|
|
{
|
|
"VK_LAYER_GOOGLE_threading",
|
|
"VK_LAYER_LUNARG_parameter_validation",
|
|
"VK_LAYER_LUNARG_object_tracker",
|
|
"VK_LAYER_LUNARG_core_validation",
|
|
"VK_LAYER_GOOGLE_unique_objects",
|
|
},
|
|
|
|
// Otherwise as a last resort we fallback to attempting to enable the LunarG core layer
|
|
{"VK_LAYER_LUNARG_core_validation"}
|
|
};
|
|
|
|
for (auto& validationLayers : validationLayerPriorityList)
|
|
{
|
|
if (ValidateLayers(validationLayers, availableInstanceLayers))
|
|
{
|
|
for (auto& x : validationLayers)
|
|
{
|
|
instanceLayers.push_back(x);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create instance:
|
|
{
|
|
VkInstanceCreateInfo createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
|
createInfo.pApplicationInfo = &appInfo;
|
|
createInfo.enabledLayerCount = static_cast<uint32_t>(instanceLayers.size());
|
|
createInfo.ppEnabledLayerNames = instanceLayers.data();
|
|
createInfo.enabledExtensionCount = static_cast<uint32_t>(instanceExtensions.size());
|
|
createInfo.ppEnabledExtensionNames = instanceExtensions.data();
|
|
|
|
VkDebugUtilsMessengerCreateInfoEXT debugUtilsCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT };
|
|
|
|
if (validationMode != ValidationMode::Disabled && debugUtils)
|
|
{
|
|
debugUtilsCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
|
|
debugUtilsCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
|
|
|
if (validationMode == ValidationMode::Verbose)
|
|
{
|
|
debugUtilsCreateInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
|
|
debugUtilsCreateInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
|
|
}
|
|
|
|
debugUtilsCreateInfo.pfnUserCallback = debugUtilsMessengerCallback;
|
|
createInfo.pNext = &debugUtilsCreateInfo;
|
|
}
|
|
|
|
res = vkCreateInstance(&createInfo, nullptr, &instance);
|
|
assert(res == VK_SUCCESS);
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
wi::helper::messageBox("vkCreateInstance failed! ERROR: " + std::to_string(res), "Error!");
|
|
wi::platform::Exit();
|
|
}
|
|
|
|
volkLoadInstanceOnly(instance);
|
|
|
|
if (validationMode != ValidationMode::Disabled && debugUtils)
|
|
{
|
|
res = vkCreateDebugUtilsMessengerEXT(instance, &debugUtilsCreateInfo, nullptr, &debugUtilsMessenger);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
}
|
|
|
|
// Enumerating and creating devices:
|
|
{
|
|
uint32_t deviceCount = 0;
|
|
VkResult res = vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
if (deviceCount == 0)
|
|
{
|
|
assert(0);
|
|
wi::helper::messageBox("Failed to find GPU with Vulkan support!");
|
|
wi::platform::Exit();
|
|
}
|
|
|
|
wi::vector<VkPhysicalDevice> devices(deviceCount);
|
|
res = vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
const wi::vector<const char*> required_deviceExtensions = {
|
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
|
};
|
|
wi::vector<const char*> enabled_deviceExtensions;
|
|
|
|
for (const auto& dev : devices)
|
|
{
|
|
bool suitable = true;
|
|
|
|
uint32_t extensionCount;
|
|
VkResult res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
wi::vector<VkExtensionProperties> available_deviceExtensions(extensionCount);
|
|
res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, available_deviceExtensions.data());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
for (auto& x : required_deviceExtensions)
|
|
{
|
|
if (!checkExtensionSupport(x, available_deviceExtensions))
|
|
{
|
|
suitable = false;
|
|
}
|
|
}
|
|
if (!suitable)
|
|
continue;
|
|
|
|
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
|
features_1_1.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
|
|
features_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
|
|
features2.pNext = &features_1_1;
|
|
features_1_1.pNext = &features_1_2;
|
|
void** features_chain = &features_1_2.pNext;
|
|
acceleration_structure_features = {};
|
|
raytracing_features = {};
|
|
raytracing_query_features = {};
|
|
fragment_shading_rate_features = {};
|
|
mesh_shader_features = {};
|
|
conditional_rendering_features = {};
|
|
depth_clip_enable_features = {};
|
|
|
|
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
|
properties_1_1.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES;
|
|
properties_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES;
|
|
properties2.pNext = &properties_1_1;
|
|
properties_1_1.pNext = &properties_1_2;
|
|
void** properties_chain = &properties_1_2.pNext;
|
|
sampler_minmax_properties = {};
|
|
acceleration_structure_properties = {};
|
|
raytracing_properties = {};
|
|
fragment_shading_rate_properties = {};
|
|
mesh_shader_properties = {};
|
|
|
|
sampler_minmax_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES;
|
|
*properties_chain = &sampler_minmax_properties;
|
|
properties_chain = &sampler_minmax_properties.pNext;
|
|
|
|
depth_stencil_resolve_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES;
|
|
*properties_chain = &depth_stencil_resolve_properties;
|
|
properties_chain = &depth_stencil_resolve_properties.pNext;
|
|
|
|
enabled_deviceExtensions = required_deviceExtensions;
|
|
|
|
if (checkExtensionSupport(VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, available_deviceExtensions))
|
|
{
|
|
// The shader compiler can still be using this extension, even though it is core in Vulkan 1.2, so enable it for now:
|
|
enabled_deviceExtensions.push_back(VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME);
|
|
}
|
|
if (checkExtensionSupport(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME, available_deviceExtensions))
|
|
{
|
|
enabled_deviceExtensions.push_back(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME);
|
|
depth_clip_enable_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT;
|
|
*features_chain = &depth_clip_enable_features;
|
|
features_chain = &depth_clip_enable_features.pNext;
|
|
}
|
|
if (checkExtensionSupport(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, available_deviceExtensions))
|
|
{
|
|
enabled_deviceExtensions.push_back(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
|
|
assert(checkExtensionSupport(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME, available_deviceExtensions));
|
|
enabled_deviceExtensions.push_back(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME);
|
|
acceleration_structure_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR;
|
|
*features_chain = &acceleration_structure_features;
|
|
features_chain = &acceleration_structure_features.pNext;
|
|
acceleration_structure_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR;
|
|
*properties_chain = &acceleration_structure_properties;
|
|
properties_chain = &acceleration_structure_properties.pNext;
|
|
|
|
if (checkExtensionSupport(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, available_deviceExtensions))
|
|
{
|
|
enabled_deviceExtensions.push_back(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME);
|
|
enabled_deviceExtensions.push_back(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME);
|
|
raytracing_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR;
|
|
*features_chain = &raytracing_features;
|
|
features_chain = &raytracing_features.pNext;
|
|
raytracing_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR;
|
|
*properties_chain = &raytracing_properties;
|
|
properties_chain = &raytracing_properties.pNext;
|
|
}
|
|
|
|
if (checkExtensionSupport(VK_KHR_RAY_QUERY_EXTENSION_NAME, available_deviceExtensions))
|
|
{
|
|
enabled_deviceExtensions.push_back(VK_KHR_RAY_QUERY_EXTENSION_NAME);
|
|
raytracing_query_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR;
|
|
*features_chain = &raytracing_query_features;
|
|
features_chain = &raytracing_query_features.pNext;
|
|
}
|
|
}
|
|
|
|
if (checkExtensionSupport(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, available_deviceExtensions))
|
|
{
|
|
enabled_deviceExtensions.push_back(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
|
|
fragment_shading_rate_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR;
|
|
*features_chain = &fragment_shading_rate_features;
|
|
features_chain = &fragment_shading_rate_features.pNext;
|
|
fragment_shading_rate_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR;
|
|
*properties_chain = &fragment_shading_rate_properties;
|
|
properties_chain = &fragment_shading_rate_properties.pNext;
|
|
}
|
|
|
|
if (checkExtensionSupport(VK_NV_MESH_SHADER_EXTENSION_NAME, available_deviceExtensions))
|
|
{
|
|
enabled_deviceExtensions.push_back(VK_NV_MESH_SHADER_EXTENSION_NAME);
|
|
mesh_shader_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_NV;
|
|
*features_chain = &mesh_shader_features;
|
|
features_chain = &mesh_shader_features.pNext;
|
|
mesh_shader_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_NV;
|
|
*properties_chain = &mesh_shader_properties;
|
|
properties_chain = &mesh_shader_properties.pNext;
|
|
}
|
|
|
|
if (checkExtensionSupport(VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME, available_deviceExtensions))
|
|
{
|
|
enabled_deviceExtensions.push_back(VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME);
|
|
conditional_rendering_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT;
|
|
*features_chain = &conditional_rendering_features;
|
|
features_chain = &conditional_rendering_features.pNext;
|
|
}
|
|
|
|
vkGetPhysicalDeviceProperties2(dev, &properties2);
|
|
|
|
bool discrete = properties2.properties.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
|
|
if (discrete || physicalDevice == VK_NULL_HANDLE)
|
|
{
|
|
physicalDevice = dev;
|
|
if (discrete)
|
|
{
|
|
break; // if this is discrete GPU, look no further (prioritize discrete GPU)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (physicalDevice == VK_NULL_HANDLE)
|
|
{
|
|
assert(0);
|
|
wi::helper::messageBox("Failed to find a suitable GPU!");
|
|
wi::platform::Exit();
|
|
}
|
|
|
|
assert(properties2.properties.limits.timestampComputeAndGraphics == VK_TRUE);
|
|
|
|
vkGetPhysicalDeviceFeatures2(physicalDevice, &features2);
|
|
|
|
assert(features2.features.imageCubeArray == VK_TRUE);
|
|
assert(features2.features.independentBlend == VK_TRUE);
|
|
assert(features2.features.geometryShader == VK_TRUE);
|
|
assert(features2.features.samplerAnisotropy == VK_TRUE);
|
|
assert(features2.features.shaderClipDistance == VK_TRUE);
|
|
assert(features2.features.textureCompressionBC == VK_TRUE);
|
|
assert(features2.features.occlusionQueryPrecise == VK_TRUE);
|
|
assert(features_1_2.descriptorIndexing == VK_TRUE);
|
|
|
|
// Init adapter properties
|
|
vendorId = properties2.properties.vendorID;
|
|
deviceId = properties2.properties.deviceID;
|
|
adapterName = properties2.properties.deviceName;
|
|
|
|
driverDescription = properties_1_2.driverName;
|
|
if (properties_1_2.driverInfo[0] != '\0')
|
|
{
|
|
driverDescription += std::string(": ") + properties_1_2.driverInfo;
|
|
}
|
|
|
|
switch (properties2.properties.deviceType)
|
|
{
|
|
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
|
|
adapterType = AdapterType::IntegratedGpu;
|
|
break;
|
|
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
|
|
adapterType = AdapterType::DiscreteGpu;
|
|
break;
|
|
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
|
|
adapterType = AdapterType::VirtualGpu;
|
|
break;
|
|
case VK_PHYSICAL_DEVICE_TYPE_CPU:
|
|
adapterType = AdapterType::Cpu;
|
|
break;
|
|
default:
|
|
adapterType = AdapterType::Other;
|
|
break;
|
|
}
|
|
|
|
if (features2.features.tessellationShader == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::TESSELLATION;
|
|
}
|
|
if (features2.features.shaderStorageImageExtendedFormats == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::UAV_LOAD_FORMAT_COMMON;
|
|
}
|
|
if (features_1_2.shaderOutputLayer == VK_TRUE && features_1_2.shaderOutputViewportIndex)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::RENDERTARGET_AND_VIEWPORT_ARRAYINDEX_WITHOUT_GS;
|
|
}
|
|
|
|
if (
|
|
raytracing_features.rayTracingPipeline == VK_TRUE &&
|
|
raytracing_query_features.rayQuery == VK_TRUE &&
|
|
acceleration_structure_features.accelerationStructure == VK_TRUE &&
|
|
features_1_2.bufferDeviceAddress == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::RAYTRACING;
|
|
SHADER_IDENTIFIER_SIZE = raytracing_properties.shaderGroupHandleSize;
|
|
}
|
|
if (mesh_shader_features.meshShader == VK_TRUE && mesh_shader_features.taskShader == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::MESH_SHADER;
|
|
}
|
|
if (fragment_shading_rate_features.pipelineFragmentShadingRate == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::VARIABLE_RATE_SHADING;
|
|
}
|
|
if (fragment_shading_rate_features.attachmentFragmentShadingRate == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::VARIABLE_RATE_SHADING_TIER2;
|
|
VARIABLE_RATE_SHADING_TILE_SIZE = std::min(fragment_shading_rate_properties.maxFragmentShadingRateAttachmentTexelSize.width, fragment_shading_rate_properties.maxFragmentShadingRateAttachmentTexelSize.height);
|
|
}
|
|
|
|
VkFormatProperties formatProperties = {};
|
|
vkGetPhysicalDeviceFormatProperties(physicalDevice, _ConvertFormat(Format::R11G11B10_FLOAT), &formatProperties);
|
|
if (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::UAV_LOAD_FORMAT_R11G11B10_FLOAT;
|
|
}
|
|
|
|
if (conditional_rendering_features.conditionalRendering == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::PREDICATION;
|
|
}
|
|
|
|
if (features_1_2.samplerFilterMinmax == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::SAMPLER_MINMAX;
|
|
}
|
|
|
|
if (features2.features.depthBounds == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::DEPTH_BOUNDS_TEST;
|
|
}
|
|
|
|
if (features2.features.sparseBinding == VK_TRUE && features2.features.sparseResidencyAliased == VK_TRUE)
|
|
{
|
|
if (properties2.properties.sparseProperties.residencyNonResidentStrict == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::SPARSE_NULL_MAPPING;
|
|
}
|
|
if (features2.features.sparseResidencyBuffer == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::SPARSE_BUFFER;
|
|
}
|
|
if (features2.features.sparseResidencyImage2D == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::SPARSE_TEXTURE2D;
|
|
}
|
|
if (features2.features.sparseResidencyImage3D == VK_TRUE)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::SPARSE_TEXTURE3D;
|
|
}
|
|
capabilities |= GraphicsDeviceCapability::GENERIC_SPARSE_TILE_POOL;
|
|
}
|
|
|
|
if (depth_stencil_resolve_properties.supportedDepthResolveModes & VK_RESOLVE_MODE_SAMPLE_ZERO_BIT)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::DEPTH_RESOLVE_SAMPLE_ZERO;
|
|
}
|
|
if (depth_stencil_resolve_properties.supportedStencilResolveModes & VK_RESOLVE_MODE_SAMPLE_ZERO_BIT)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::STENCIL_RESOLVE_SAMPLE_ZERO;
|
|
}
|
|
if (
|
|
(depth_stencil_resolve_properties.supportedDepthResolveModes & VK_RESOLVE_MODE_MIN_BIT) &&
|
|
(depth_stencil_resolve_properties.supportedDepthResolveModes & VK_RESOLVE_MODE_MAX_BIT)
|
|
)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::DEPTH_RESOLVE_MIN_MAX;
|
|
}
|
|
if (
|
|
(depth_stencil_resolve_properties.supportedStencilResolveModes & VK_RESOLVE_MODE_MIN_BIT) &&
|
|
(depth_stencil_resolve_properties.supportedStencilResolveModes & VK_RESOLVE_MODE_MAX_BIT)
|
|
)
|
|
{
|
|
capabilities |= GraphicsDeviceCapability::STENCIL_RESOLVE_MIN_MAX;
|
|
}
|
|
|
|
// Find queue families:
|
|
uint32_t queueFamilyCount = 0;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
|
|
|
|
queueFamilies.resize(queueFamilyCount);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());
|
|
|
|
// Query base queue families:
|
|
uint32_t familyIndex = 0;
|
|
for (const auto& queueFamily : queueFamilies)
|
|
{
|
|
if (graphicsFamily == VK_QUEUE_FAMILY_IGNORED && queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
|
|
{
|
|
graphicsFamily = familyIndex;
|
|
if (queueFamily.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT)
|
|
{
|
|
queues[QUEUE_GRAPHICS].sparse_binding_supported = true;
|
|
}
|
|
}
|
|
|
|
if (copyFamily == VK_QUEUE_FAMILY_IGNORED && queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_TRANSFER_BIT)
|
|
{
|
|
copyFamily = familyIndex;
|
|
}
|
|
|
|
if (computeFamily == VK_QUEUE_FAMILY_IGNORED && queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT)
|
|
{
|
|
computeFamily = familyIndex;
|
|
}
|
|
|
|
familyIndex++;
|
|
}
|
|
|
|
// Now try to find dedicated compute and transfer queues:
|
|
familyIndex = 0;
|
|
for (const auto& queueFamily : queueFamilies)
|
|
{
|
|
if (queueFamily.queueCount > 0 &&
|
|
queueFamily.queueFlags & VK_QUEUE_TRANSFER_BIT &&
|
|
!(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) &&
|
|
!(queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT)
|
|
)
|
|
{
|
|
copyFamily = familyIndex;
|
|
|
|
if (queueFamily.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT)
|
|
{
|
|
queues[QUEUE_COPY].sparse_binding_supported = true;
|
|
}
|
|
}
|
|
|
|
if (queueFamily.queueCount > 0 &&
|
|
queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT &&
|
|
!(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
|
|
)
|
|
{
|
|
computeFamily = familyIndex;
|
|
|
|
if (queueFamily.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT)
|
|
{
|
|
queues[QUEUE_COMPUTE].sparse_binding_supported = true;
|
|
}
|
|
}
|
|
|
|
familyIndex++;
|
|
}
|
|
|
|
wi::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
|
|
wi::unordered_set<uint32_t> uniqueQueueFamilies = { graphicsFamily, copyFamily, computeFamily };
|
|
|
|
float queuePriority = 1.0f;
|
|
for (uint32_t queueFamily : uniqueQueueFamilies)
|
|
{
|
|
VkDeviceQueueCreateInfo queueCreateInfo = {};
|
|
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queueCreateInfo.queueFamilyIndex = queueFamily;
|
|
queueCreateInfo.queueCount = 1;
|
|
queueCreateInfo.pQueuePriorities = &queuePriority;
|
|
queueCreateInfos.push_back(queueCreateInfo);
|
|
families.push_back(queueFamily);
|
|
}
|
|
|
|
VkDeviceCreateInfo createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
createInfo.queueCreateInfoCount = (uint32_t)queueCreateInfos.size();
|
|
createInfo.pQueueCreateInfos = queueCreateInfos.data();
|
|
createInfo.pEnabledFeatures = nullptr;
|
|
createInfo.pNext = &features2;
|
|
createInfo.enabledExtensionCount = static_cast<uint32_t>(enabled_deviceExtensions.size());
|
|
createInfo.ppEnabledExtensionNames = enabled_deviceExtensions.data();
|
|
|
|
res = vkCreateDevice(physicalDevice, &createInfo, nullptr, &device);
|
|
assert(res == VK_SUCCESS);
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
wi::helper::messageBox("vkCreateDevice failed! ERROR: " + std::to_string(res), "Error!");
|
|
wi::platform::Exit();
|
|
}
|
|
|
|
volkLoadDevice(device);
|
|
}
|
|
|
|
// queues:
|
|
{
|
|
vkGetDeviceQueue(device, graphicsFamily, 0, &graphicsQueue);
|
|
vkGetDeviceQueue(device, computeFamily, 0, &computeQueue);
|
|
vkGetDeviceQueue(device, copyFamily, 0, ©Queue);
|
|
|
|
queues[QUEUE_GRAPHICS].queue = graphicsQueue;
|
|
queues[QUEUE_COMPUTE].queue = computeQueue;
|
|
queues[QUEUE_COPY].queue = copyQueue;
|
|
|
|
VkSemaphoreTypeCreateInfo timelineCreateInfo = {};
|
|
timelineCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO;
|
|
timelineCreateInfo.pNext = nullptr;
|
|
timelineCreateInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
|
|
timelineCreateInfo.initialValue = 0;
|
|
|
|
VkSemaphoreCreateInfo createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
createInfo.pNext = &timelineCreateInfo;
|
|
createInfo.flags = 0;
|
|
|
|
res = vkCreateSemaphore(device, &createInfo, nullptr, &queues[QUEUE_GRAPHICS].semaphore);
|
|
assert(res == VK_SUCCESS);
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
wi::helper::messageBox("vkCreateSemaphore[QUEUE_GRAPHICS] failed! ERROR: " + std::to_string(res), "Error!");
|
|
wi::platform::Exit();
|
|
}
|
|
res = vkCreateSemaphore(device, &createInfo, nullptr, &queues[QUEUE_COMPUTE].semaphore);
|
|
assert(res == VK_SUCCESS);
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
wi::helper::messageBox("vkCreateSemaphore[QUEUE_COMPUTE] failed! ERROR: " + std::to_string(res), "Error!");
|
|
wi::platform::Exit();
|
|
}
|
|
res = vkCreateSemaphore(device, &createInfo, nullptr, &queues[QUEUE_COPY].semaphore);
|
|
assert(res == VK_SUCCESS);
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
wi::helper::messageBox("vkCreateSemaphore[QUEUE_COPY] failed! ERROR: " + std::to_string(res), "Error!");
|
|
wi::platform::Exit();
|
|
}
|
|
}
|
|
|
|
memory_properties_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
|
|
vkGetPhysicalDeviceMemoryProperties2(physicalDevice, &memory_properties_2);
|
|
|
|
allocationhandler = std::make_shared<AllocationHandler>();
|
|
allocationhandler->device = device;
|
|
allocationhandler->instance = instance;
|
|
|
|
// Initialize Vulkan Memory Allocator helper:
|
|
VmaVulkanFunctions vma_vulkan_func{};
|
|
vma_vulkan_func.vkGetInstanceProcAddr = vkGetInstanceProcAddr;
|
|
vma_vulkan_func.vkGetDeviceProcAddr = vkGetDeviceProcAddr;
|
|
vma_vulkan_func.vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties;
|
|
vma_vulkan_func.vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties;
|
|
vma_vulkan_func.vkAllocateMemory = vkAllocateMemory;
|
|
vma_vulkan_func.vkFreeMemory = vkFreeMemory;
|
|
vma_vulkan_func.vkMapMemory = vkMapMemory;
|
|
vma_vulkan_func.vkUnmapMemory = vkUnmapMemory;
|
|
vma_vulkan_func.vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges;
|
|
vma_vulkan_func.vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges;
|
|
vma_vulkan_func.vkBindBufferMemory = vkBindBufferMemory;
|
|
vma_vulkan_func.vkBindImageMemory = vkBindImageMemory;
|
|
vma_vulkan_func.vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements;
|
|
vma_vulkan_func.vkGetImageMemoryRequirements = vkGetImageMemoryRequirements;
|
|
vma_vulkan_func.vkCreateBuffer = vkCreateBuffer;
|
|
vma_vulkan_func.vkDestroyBuffer = vkDestroyBuffer;
|
|
vma_vulkan_func.vkCreateImage = vkCreateImage;
|
|
vma_vulkan_func.vkDestroyImage = vkDestroyImage;
|
|
vma_vulkan_func.vkCmdCopyBuffer = vkCmdCopyBuffer;
|
|
|
|
VmaAllocatorCreateInfo allocatorInfo = {};
|
|
allocatorInfo.physicalDevice = physicalDevice;
|
|
allocatorInfo.device = device;
|
|
allocatorInfo.instance = instance;
|
|
|
|
// Core in 1.1
|
|
allocatorInfo.flags =
|
|
VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT |
|
|
VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT;
|
|
vma_vulkan_func.vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2;
|
|
vma_vulkan_func.vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2;
|
|
|
|
if (features_1_2.bufferDeviceAddress)
|
|
{
|
|
allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
|
|
vma_vulkan_func.vkBindBufferMemory2KHR = vkBindBufferMemory2;
|
|
vma_vulkan_func.vkBindImageMemory2KHR = vkBindImageMemory2;
|
|
}
|
|
allocatorInfo.pVulkanFunctions = &vma_vulkan_func;
|
|
|
|
res = vmaCreateAllocator(&allocatorInfo, &allocationhandler->allocator);
|
|
assert(res == VK_SUCCESS);
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
wi::helper::messageBox("vmaCreateAllocator failed! ERROR: " + std::to_string(res), "Error!");
|
|
wi::platform::Exit();
|
|
}
|
|
|
|
copyAllocator.init(this);
|
|
|
|
// Create frame resources:
|
|
for (uint32_t fr = 0; fr < BUFFERCOUNT; ++fr)
|
|
{
|
|
for (int queue = 0; queue < QUEUE_COUNT; ++queue)
|
|
{
|
|
VkFenceCreateInfo fenceInfo = {};
|
|
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
//fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
|
VkResult res = vkCreateFence(device, &fenceInfo, nullptr, &frames[fr].fence[queue]);
|
|
assert(res == VK_SUCCESS);
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
wi::helper::messageBox("vkCreateFence[FRAME] failed! ERROR: " + std::to_string(res), "Error!");
|
|
wi::platform::Exit();
|
|
}
|
|
}
|
|
|
|
// Create resources for transition command buffer:
|
|
{
|
|
VkCommandPoolCreateInfo poolInfo = {};
|
|
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
poolInfo.queueFamilyIndex = graphicsFamily;
|
|
poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
|
|
|
res = vkCreateCommandPool(device, &poolInfo, nullptr, &frames[fr].initCommandPool);
|
|
assert(res == VK_SUCCESS);
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
wi::helper::messageBox("vkCreateCommandPool[FRAME_INIT] failed! ERROR: " + std::to_string(res), "Error!");
|
|
wi::platform::Exit();
|
|
}
|
|
|
|
VkCommandBufferAllocateInfo commandBufferInfo = {};
|
|
commandBufferInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
commandBufferInfo.commandBufferCount = 1;
|
|
commandBufferInfo.commandPool = frames[fr].initCommandPool;
|
|
commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
|
|
res = vkAllocateCommandBuffers(device, &commandBufferInfo, &frames[fr].initCommandBuffer);
|
|
assert(res == VK_SUCCESS);
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
wi::helper::messageBox("vkAllocateCommandBuffers[FRAME_INIT] failed! ERROR: " + std::to_string(res), "Error!");
|
|
wi::platform::Exit();
|
|
}
|
|
|
|
VkCommandBufferBeginInfo beginInfo = {};
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
beginInfo.pInheritanceInfo = nullptr; // Optional
|
|
|
|
res = vkBeginCommandBuffer(frames[fr].initCommandBuffer, &beginInfo);
|
|
assert(res == VK_SUCCESS);
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
wi::helper::messageBox("vkBeginCommandBuffer[FRAME_INIT] failed! ERROR: " + std::to_string(res), "Error!");
|
|
wi::platform::Exit();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create default null descriptors:
|
|
{
|
|
VkBufferCreateInfo bufferInfo = {};
|
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bufferInfo.size = 4;
|
|
bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
|
bufferInfo.flags = 0;
|
|
|
|
|
|
VmaAllocationCreateInfo allocInfo = {};
|
|
allocInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
|
|
|
res = vmaCreateBuffer(allocationhandler->allocator, &bufferInfo, &allocInfo, &nullBuffer, &nullBufferAllocation, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
VkBufferViewCreateInfo viewInfo = {};
|
|
viewInfo.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
|
|
viewInfo.format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
|
viewInfo.range = VK_WHOLE_SIZE;
|
|
viewInfo.buffer = nullBuffer;
|
|
res = vkCreateBufferView(device, &viewInfo, nullptr, &nullBufferView);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
{
|
|
VkImageCreateInfo imageInfo = {};
|
|
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
imageInfo.extent.width = 1;
|
|
imageInfo.extent.height = 1;
|
|
imageInfo.extent.depth = 1;
|
|
imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
|
|
imageInfo.arrayLayers = 1;
|
|
imageInfo.mipLevels = 1;
|
|
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
|
|
imageInfo.flags = 0;
|
|
|
|
VmaAllocationCreateInfo allocInfo = {};
|
|
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
|
|
|
imageInfo.imageType = VK_IMAGE_TYPE_1D;
|
|
res = vmaCreateImage(allocationhandler->allocator, &imageInfo, &allocInfo, &nullImage1D, &nullImageAllocation1D, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
|
imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
|
imageInfo.arrayLayers = 6;
|
|
res = vmaCreateImage(allocationhandler->allocator, &imageInfo, &allocInfo, &nullImage2D, &nullImageAllocation2D, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
imageInfo.imageType = VK_IMAGE_TYPE_3D;
|
|
imageInfo.flags = 0;
|
|
imageInfo.arrayLayers = 1;
|
|
res = vmaCreateImage(allocationhandler->allocator, &imageInfo, &allocInfo, &nullImage3D, &nullImageAllocation3D, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// Transitions:
|
|
initLocker.lock();
|
|
{
|
|
VkImageMemoryBarrier barrier = {};
|
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
barrier.oldLayout = imageInfo.initialLayout;
|
|
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
barrier.srcAccessMask = 0;
|
|
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
|
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
barrier.subresourceRange.baseArrayLayer = 0;
|
|
barrier.subresourceRange.baseMipLevel = 0;
|
|
barrier.subresourceRange.levelCount = 1;
|
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.image = nullImage1D;
|
|
barrier.subresourceRange.layerCount = 1;
|
|
vkCmdPipelineBarrier(
|
|
GetFrameResources().initCommandBuffer,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
0,
|
|
0, nullptr,
|
|
0, nullptr,
|
|
1, &barrier
|
|
);
|
|
barrier.image = nullImage2D;
|
|
barrier.subresourceRange.layerCount = 6;
|
|
vkCmdPipelineBarrier(
|
|
GetFrameResources().initCommandBuffer,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
0,
|
|
0, nullptr,
|
|
0, nullptr,
|
|
1, &barrier
|
|
);
|
|
barrier.image = nullImage3D;
|
|
barrier.subresourceRange.layerCount = 1;
|
|
vkCmdPipelineBarrier(
|
|
GetFrameResources().initCommandBuffer,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
0,
|
|
0, nullptr,
|
|
0, nullptr,
|
|
1, &barrier
|
|
);
|
|
}
|
|
submit_inits = true;
|
|
initLocker.unlock();
|
|
|
|
VkImageViewCreateInfo viewInfo = {};
|
|
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
viewInfo.subresourceRange.baseArrayLayer = 0;
|
|
viewInfo.subresourceRange.layerCount = 1;
|
|
viewInfo.subresourceRange.baseMipLevel = 0;
|
|
viewInfo.subresourceRange.levelCount = 1;
|
|
viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
|
|
|
|
viewInfo.image = nullImage1D;
|
|
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;
|
|
res = vkCreateImageView(device, &viewInfo, nullptr, &nullImageView1D);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
viewInfo.image = nullImage1D;
|
|
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
|
|
res = vkCreateImageView(device, &viewInfo, nullptr, &nullImageView1DArray);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
viewInfo.image = nullImage2D;
|
|
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
res = vkCreateImageView(device, &viewInfo, nullptr, &nullImageView2D);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
viewInfo.image = nullImage2D;
|
|
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
|
res = vkCreateImageView(device, &viewInfo, nullptr, &nullImageView2DArray);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
viewInfo.image = nullImage2D;
|
|
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
|
|
viewInfo.subresourceRange.layerCount = 6;
|
|
res = vkCreateImageView(device, &viewInfo, nullptr, &nullImageViewCube);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
viewInfo.image = nullImage2D;
|
|
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
|
|
viewInfo.subresourceRange.layerCount = 6;
|
|
res = vkCreateImageView(device, &viewInfo, nullptr, &nullImageViewCubeArray);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
viewInfo.image = nullImage3D;
|
|
viewInfo.subresourceRange.layerCount = 1;
|
|
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_3D;
|
|
res = vkCreateImageView(device, &viewInfo, nullptr, &nullImageView3D);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
{
|
|
VkSamplerCreateInfo createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
|
|
|
res = vkCreateSampler(device, &createInfo, nullptr, &nullSampler);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
|
|
TIMESTAMP_FREQUENCY = uint64_t(1.0 / double(properties2.properties.limits.timestampPeriod) * 1000 * 1000 * 1000);
|
|
|
|
// Dynamic PSO states:
|
|
pso_dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT);
|
|
pso_dynamicStates.push_back(VK_DYNAMIC_STATE_SCISSOR);
|
|
pso_dynamicStates.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE);
|
|
pso_dynamicStates.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS);
|
|
if (CheckCapability(GraphicsDeviceCapability::DEPTH_BOUNDS_TEST))
|
|
{
|
|
pso_dynamicStates.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS);
|
|
}
|
|
if (CheckCapability(GraphicsDeviceCapability::VARIABLE_RATE_SHADING))
|
|
{
|
|
pso_dynamicStates.push_back(VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR);
|
|
}
|
|
|
|
dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
|
dynamicStateInfo.dynamicStateCount = (uint32_t)pso_dynamicStates.size();
|
|
dynamicStateInfo.pDynamicStates = pso_dynamicStates.data();
|
|
|
|
// Note: limiting descriptors by constant amount is needed, because the bindless sets are bound to multiple slots to match DX12 layout
|
|
// And binding to multiple slot adds up towards limits, so the limits will be quickly reached for some descriptor types
|
|
// But not all descriptor types have this problem, like storage buffers that are not bound for multiple slots usually
|
|
// Ideally, this shouldn't be the case, because Vulkan could have it's own layout in shaders
|
|
const uint32_t limit_bindless_descriptors = 100000u;
|
|
|
|
if (features_1_2.descriptorBindingSampledImageUpdateAfterBind == VK_TRUE)
|
|
{
|
|
allocationhandler->bindlessSampledImages.init(device, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, std::min(limit_bindless_descriptors, properties_1_2.maxDescriptorSetUpdateAfterBindSampledImages / 4));
|
|
}
|
|
if (features_1_2.descriptorBindingUniformTexelBufferUpdateAfterBind == VK_TRUE)
|
|
{
|
|
allocationhandler->bindlessUniformTexelBuffers.init(device, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, std::min(limit_bindless_descriptors, properties_1_2.maxDescriptorSetUpdateAfterBindSampledImages / 4));
|
|
}
|
|
if (features_1_2.descriptorBindingStorageBufferUpdateAfterBind == VK_TRUE)
|
|
{
|
|
allocationhandler->bindlessStorageBuffers.init(device, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, properties_1_2.maxDescriptorSetUpdateAfterBindStorageBuffers / 4);
|
|
}
|
|
if (features_1_2.descriptorBindingStorageImageUpdateAfterBind == VK_TRUE)
|
|
{
|
|
allocationhandler->bindlessStorageImages.init(device, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, std::min(limit_bindless_descriptors, properties_1_2.maxDescriptorSetUpdateAfterBindStorageImages / 4));
|
|
}
|
|
if (features_1_2.descriptorBindingStorageTexelBufferUpdateAfterBind == VK_TRUE)
|
|
{
|
|
allocationhandler->bindlessStorageTexelBuffers.init(device, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, std::min(limit_bindless_descriptors, properties_1_2.maxDescriptorSetUpdateAfterBindStorageImages / 4));
|
|
}
|
|
if (features_1_2.descriptorBindingSampledImageUpdateAfterBind == VK_TRUE)
|
|
{
|
|
allocationhandler->bindlessSamplers.init(device, VK_DESCRIPTOR_TYPE_SAMPLER, 256);
|
|
}
|
|
|
|
if (CheckCapability(GraphicsDeviceCapability::RAYTRACING))
|
|
{
|
|
allocationhandler->bindlessAccelerationStructures.init(device, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 32);
|
|
}
|
|
|
|
// Pipeline Cache
|
|
{
|
|
// Try to read pipeline cache file if exists.
|
|
wi::vector<uint8_t> pipelineData;
|
|
|
|
std::string cachePath = GetCachePath();
|
|
if (!wi::helper::FileRead(cachePath, pipelineData))
|
|
{
|
|
pipelineData.clear();
|
|
}
|
|
|
|
// Verify cache validation.
|
|
if (!pipelineData.empty())
|
|
{
|
|
uint32_t headerLength = 0;
|
|
uint32_t cacheHeaderVersion = 0;
|
|
uint32_t vendorID = 0;
|
|
uint32_t deviceID = 0;
|
|
uint8_t pipelineCacheUUID[VK_UUID_SIZE] = {};
|
|
|
|
std::memcpy(&headerLength, (uint8_t*)pipelineData.data() + 0, 4);
|
|
std::memcpy(&cacheHeaderVersion, (uint8_t*)pipelineData.data() + 4, 4);
|
|
std::memcpy(&vendorID, (uint8_t*)pipelineData.data() + 8, 4);
|
|
std::memcpy(&deviceID, (uint8_t*)pipelineData.data() + 12, 4);
|
|
std::memcpy(pipelineCacheUUID, (uint8_t*)pipelineData.data() + 16, VK_UUID_SIZE);
|
|
|
|
bool badCache = false;
|
|
|
|
if (headerLength <= 0)
|
|
{
|
|
badCache = true;
|
|
}
|
|
|
|
if (cacheHeaderVersion != VK_PIPELINE_CACHE_HEADER_VERSION_ONE)
|
|
{
|
|
badCache = true;
|
|
}
|
|
|
|
if (vendorID != properties2.properties.vendorID)
|
|
{
|
|
badCache = true;
|
|
}
|
|
|
|
if (deviceID != properties2.properties.deviceID)
|
|
{
|
|
badCache = true;
|
|
}
|
|
|
|
if (memcmp(pipelineCacheUUID, properties2.properties.pipelineCacheUUID, sizeof(pipelineCacheUUID)) != 0)
|
|
{
|
|
badCache = true;
|
|
}
|
|
|
|
if (badCache)
|
|
{
|
|
// Don't submit initial cache data if any version info is incorrect
|
|
pipelineData.clear();
|
|
}
|
|
}
|
|
|
|
VkPipelineCacheCreateInfo createInfo{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
|
|
createInfo.initialDataSize = pipelineData.size();
|
|
createInfo.pInitialData = pipelineData.data();
|
|
|
|
// Create Vulkan pipeline cache
|
|
res = vkCreatePipelineCache(device, &createInfo, nullptr, &pipelineCache);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
|
|
// Static samplers:
|
|
{
|
|
VkSamplerCreateInfo createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
|
createInfo.pNext = nullptr;
|
|
createInfo.flags = 0;
|
|
createInfo.compareEnable = false;
|
|
createInfo.compareOp = VK_COMPARE_OP_NEVER;
|
|
createInfo.minLod = 0;
|
|
createInfo.maxLod = FLT_MAX;
|
|
createInfo.mipLodBias = 0;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.maxAnisotropy = 0;
|
|
|
|
// sampler_linear_clamp:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
res = vkCreateSampler(device, &createInfo, nullptr, &immutable_samplers.emplace_back());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// sampler_linear_wrap:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
|
createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
|
createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
|
res = vkCreateSampler(device, &createInfo, nullptr, &immutable_samplers.emplace_back());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
//sampler_linear_mirror:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
|
createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
|
createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
|
res = vkCreateSampler(device, &createInfo, nullptr, &immutable_samplers.emplace_back());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// sampler_point_clamp:
|
|
createInfo.minFilter = VK_FILTER_NEAREST;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
res = vkCreateSampler(device, &createInfo, nullptr, &immutable_samplers.emplace_back());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// sampler_point_wrap:
|
|
createInfo.minFilter = VK_FILTER_NEAREST;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
|
createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
|
createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
|
res = vkCreateSampler(device, &createInfo, nullptr, &immutable_samplers.emplace_back());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// sampler_point_mirror:
|
|
createInfo.minFilter = VK_FILTER_NEAREST;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
|
createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
|
createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
|
res = vkCreateSampler(device, &createInfo, nullptr, &immutable_samplers.emplace_back());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// sampler_aniso_clamp:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
createInfo.anisotropyEnable = true;
|
|
createInfo.maxAnisotropy = 16;
|
|
res = vkCreateSampler(device, &createInfo, nullptr, &immutable_samplers.emplace_back());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// sampler_aniso_wrap:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
|
createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
|
createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
|
createInfo.anisotropyEnable = true;
|
|
createInfo.maxAnisotropy = 16;
|
|
res = vkCreateSampler(device, &createInfo, nullptr, &immutable_samplers.emplace_back());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// sampler_aniso_mirror:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
|
createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
|
createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
|
createInfo.anisotropyEnable = true;
|
|
createInfo.maxAnisotropy = 16;
|
|
res = vkCreateSampler(device, &createInfo, nullptr, &immutable_samplers.emplace_back());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// sampler_cmp_depth:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.maxAnisotropy = 0;
|
|
createInfo.compareEnable = true;
|
|
createInfo.compareOp = VK_COMPARE_OP_GREATER_OR_EQUAL;
|
|
createInfo.minLod = 0;
|
|
createInfo.maxLod = 0;
|
|
res = vkCreateSampler(device, &createInfo, nullptr, &immutable_samplers.emplace_back());
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
|
|
wi::backlog::post("Created GraphicsDevice_Vulkan (" + std::to_string((int)std::round(timer.elapsed())) + " ms)");
|
|
}
|
|
GraphicsDevice_Vulkan::~GraphicsDevice_Vulkan()
|
|
{
|
|
VkResult res = vkDeviceWaitIdle(device);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
for (auto& queue : queues)
|
|
{
|
|
vkDestroySemaphore(device, queue.semaphore, nullptr);
|
|
}
|
|
|
|
for (auto& frame : frames)
|
|
{
|
|
for (int queue = 0; queue < QUEUE_COUNT; ++queue)
|
|
{
|
|
vkDestroyFence(device, frame.fence[queue], nullptr);
|
|
}
|
|
vkDestroyCommandPool(device, frame.initCommandPool, nullptr);
|
|
}
|
|
|
|
copyAllocator.destroy();
|
|
|
|
for (auto& x : pso_layout_cache)
|
|
{
|
|
vkDestroyPipelineLayout(device, x.second.pipelineLayout, nullptr);
|
|
vkDestroyDescriptorSetLayout(device, x.second.descriptorSetLayout, nullptr);
|
|
}
|
|
|
|
for (auto& commandlist : commandlists)
|
|
{
|
|
for (int buffer = 0; buffer < BUFFERCOUNT; ++buffer)
|
|
{
|
|
for (int queue = 0; queue < QUEUE_COUNT; ++queue)
|
|
{
|
|
vkDestroyCommandPool(device, commandlist->commandPools[buffer][queue], nullptr);
|
|
}
|
|
}
|
|
for (auto& x : commandlist->pipelines_worker)
|
|
{
|
|
vkDestroyPipeline(device, x.second, nullptr);
|
|
}
|
|
for (auto& x : commandlist->binder_pools)
|
|
{
|
|
x.destroy();
|
|
}
|
|
}
|
|
for (auto& x : pipelines_global)
|
|
{
|
|
vkDestroyPipeline(device, x.second, nullptr);
|
|
}
|
|
|
|
vmaDestroyBuffer(allocationhandler->allocator, nullBuffer, nullBufferAllocation);
|
|
vkDestroyBufferView(device, nullBufferView, nullptr);
|
|
vmaDestroyImage(allocationhandler->allocator, nullImage1D, nullImageAllocation1D);
|
|
vmaDestroyImage(allocationhandler->allocator, nullImage2D, nullImageAllocation2D);
|
|
vmaDestroyImage(allocationhandler->allocator, nullImage3D, nullImageAllocation3D);
|
|
vkDestroyImageView(device, nullImageView1D, nullptr);
|
|
vkDestroyImageView(device, nullImageView1DArray, nullptr);
|
|
vkDestroyImageView(device, nullImageView2D, nullptr);
|
|
vkDestroyImageView(device, nullImageView2DArray, nullptr);
|
|
vkDestroyImageView(device, nullImageViewCube, nullptr);
|
|
vkDestroyImageView(device, nullImageViewCubeArray, nullptr);
|
|
vkDestroyImageView(device, nullImageView3D, nullptr);
|
|
vkDestroySampler(device, nullSampler, nullptr);
|
|
|
|
if (pipelineCache != VK_NULL_HANDLE)
|
|
{
|
|
// Get size of pipeline cache
|
|
size_t size{};
|
|
res = vkGetPipelineCacheData(device, pipelineCache, &size, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// Get data of pipeline cache
|
|
wi::vector<uint8_t> data(size);
|
|
res = vkGetPipelineCacheData(device, pipelineCache, &size, data.data());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// Write pipeline cache data to a file in binary format
|
|
std::string cachePath = GetCachePath();
|
|
wi::helper::FileWrite(cachePath, data.data(), size);
|
|
|
|
// Destroy Vulkan pipeline cache
|
|
vkDestroyPipelineCache(device, pipelineCache, nullptr);
|
|
pipelineCache = VK_NULL_HANDLE;
|
|
}
|
|
|
|
if (debugUtilsMessenger != VK_NULL_HANDLE)
|
|
{
|
|
vkDestroyDebugUtilsMessengerEXT(instance, debugUtilsMessenger, nullptr);
|
|
}
|
|
}
|
|
|
|
bool GraphicsDevice_Vulkan::CreateSwapChain(const SwapChainDesc* desc, wi::platform::window_type window, SwapChain* swapchain) const
|
|
{
|
|
auto internal_state = std::static_pointer_cast<SwapChain_Vulkan>(swapchain->internal_state);
|
|
if (swapchain->internal_state == nullptr)
|
|
{
|
|
internal_state = std::make_shared<SwapChain_Vulkan>();
|
|
}
|
|
internal_state->allocationhandler = allocationhandler;
|
|
internal_state->desc = *desc;
|
|
swapchain->internal_state = internal_state;
|
|
swapchain->desc = *desc;
|
|
|
|
VkResult res;
|
|
|
|
// Surface creation:
|
|
if(internal_state->surface == VK_NULL_HANDLE)
|
|
{
|
|
#ifdef _WIN32
|
|
VkWin32SurfaceCreateInfoKHR createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
|
|
createInfo.hwnd = window;
|
|
createInfo.hinstance = GetModuleHandle(nullptr);
|
|
|
|
res = vkCreateWin32SurfaceKHR(instance, &createInfo, nullptr, &internal_state->surface);
|
|
assert(res == VK_SUCCESS);
|
|
#elif SDL2
|
|
if (!SDL_Vulkan_CreateSurface(window, instance, &internal_state->surface))
|
|
{
|
|
throw sdl2::SDLError("Error creating a vulkan surface");
|
|
}
|
|
#else
|
|
#error WICKEDENGINE VULKAN DEVICE ERROR: PLATFORM NOT SUPPORTED
|
|
#endif // _WIN32
|
|
}
|
|
|
|
uint32_t presentFamily = VK_QUEUE_FAMILY_IGNORED;
|
|
uint32_t familyIndex = 0;
|
|
for (const auto& queueFamily : queueFamilies)
|
|
{
|
|
VkBool32 presentSupport = false;
|
|
res = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, (uint32_t)familyIndex, internal_state->surface, &presentSupport);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
if (presentFamily == VK_QUEUE_FAMILY_IGNORED && queueFamily.queueCount > 0 && presentSupport)
|
|
{
|
|
presentFamily = familyIndex;
|
|
break;
|
|
}
|
|
|
|
familyIndex++;
|
|
}
|
|
|
|
// Present family not found, we cannot create SwapChain
|
|
if (presentFamily == VK_QUEUE_FAMILY_IGNORED)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return CreateSwapChainInternal(internal_state.get(), physicalDevice, device, allocationhandler);
|
|
}
|
|
bool GraphicsDevice_Vulkan::CreateBuffer(const GPUBufferDesc * desc, const void* initial_data, GPUBuffer* buffer) const
|
|
{
|
|
auto internal_state = std::make_shared<Buffer_Vulkan>();
|
|
internal_state->allocationhandler = allocationhandler;
|
|
buffer->internal_state = internal_state;
|
|
buffer->type = GPUResource::Type::BUFFER;
|
|
buffer->mapped_data = nullptr;
|
|
buffer->mapped_size = 0;
|
|
buffer->desc = *desc;
|
|
|
|
VkBufferCreateInfo bufferInfo = {};
|
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bufferInfo.size = buffer->desc.size;
|
|
bufferInfo.usage = 0;
|
|
if (has_flag(buffer->desc.bind_flags, BindFlag::VERTEX_BUFFER))
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.bind_flags, BindFlag::INDEX_BUFFER))
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.bind_flags, BindFlag::CONSTANT_BUFFER))
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.bind_flags, BindFlag::SHADER_RESOURCE))
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; // read only ByteAddressBuffer is also storage buffer
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.bind_flags, BindFlag::UNORDERED_ACCESS))
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.misc_flags, ResourceMiscFlag::BUFFER_RAW))
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.misc_flags, ResourceMiscFlag::BUFFER_STRUCTURED))
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.misc_flags, ResourceMiscFlag::INDIRECT_ARGS))
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.misc_flags, ResourceMiscFlag::RAY_TRACING))
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR;
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR;
|
|
}
|
|
if (has_flag(buffer->desc.misc_flags, ResourceMiscFlag::PREDICATION))
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
|
|
}
|
|
if (features_1_2.bufferDeviceAddress == VK_TRUE)
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
|
}
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
|
|
bufferInfo.flags = 0;
|
|
|
|
if (families.size() > 1)
|
|
{
|
|
bufferInfo.sharingMode = VK_SHARING_MODE_CONCURRENT;
|
|
bufferInfo.queueFamilyIndexCount = (uint32_t)families.size();
|
|
bufferInfo.pQueueFamilyIndices = families.data();
|
|
}
|
|
else
|
|
{
|
|
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
}
|
|
|
|
VkResult res;
|
|
|
|
if (has_flag(desc->misc_flags, ResourceMiscFlag::SPARSE_TILE_POOL_BUFFER) ||
|
|
has_flag(desc->misc_flags, ResourceMiscFlag::SPARSE_TILE_POOL_TEXTURE_NON_RT_DS) ||
|
|
has_flag(desc->misc_flags, ResourceMiscFlag::SPARSE_TILE_POOL_TEXTURE_RT_DS))
|
|
{
|
|
VkMemoryRequirements memory_requirements = {};
|
|
memory_requirements.alignment = desc->alignment;
|
|
memory_requirements.size = AlignTo(desc->size, memory_requirements.alignment);
|
|
memory_requirements.memoryTypeBits = ~0u;
|
|
|
|
VmaAllocationCreateInfo create_info = {};
|
|
create_info.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
|
|
|
VmaAllocationInfo allocation_info = {};
|
|
|
|
res = vmaAllocateMemory(
|
|
allocationhandler->allocator,
|
|
&memory_requirements,
|
|
&create_info,
|
|
&internal_state->allocation,
|
|
&allocation_info
|
|
);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
return res == VK_SUCCESS;
|
|
}
|
|
|
|
if (has_flag(desc->misc_flags, ResourceMiscFlag::SPARSE))
|
|
{
|
|
bufferInfo.flags |= VK_BUFFER_CREATE_SPARSE_BINDING_BIT;
|
|
bufferInfo.flags |= VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT;
|
|
bufferInfo.flags |= VK_BUFFER_CREATE_SPARSE_ALIASED_BIT;
|
|
|
|
res = vkCreateBuffer(device, &bufferInfo, nullptr, &internal_state->resource);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
VkMemoryRequirements memory_requirements = {};
|
|
vkGetBufferMemoryRequirements(
|
|
device,
|
|
internal_state->resource,
|
|
&memory_requirements
|
|
);
|
|
buffer->sparse_page_size = memory_requirements.alignment;
|
|
}
|
|
else
|
|
{
|
|
VmaAllocationCreateInfo allocInfo = {};
|
|
allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
|
if (desc->usage == Usage::READBACK)
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
|
}
|
|
else if (desc->usage == Usage::UPLOAD)
|
|
{
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
|
allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
|
}
|
|
|
|
res = vmaCreateBuffer(allocationhandler->allocator, &bufferInfo, &allocInfo, &internal_state->resource, &internal_state->allocation, nullptr);
|
|
}
|
|
assert(res == VK_SUCCESS);
|
|
|
|
if (desc->usage == Usage::READBACK || desc->usage == Usage::UPLOAD)
|
|
{
|
|
buffer->mapped_data = internal_state->allocation->GetMappedData();
|
|
buffer->mapped_size = internal_state->allocation->GetSize();
|
|
}
|
|
|
|
if (bufferInfo.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT)
|
|
{
|
|
VkBufferDeviceAddressInfo info = {};
|
|
info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO;
|
|
info.buffer = internal_state->resource;
|
|
internal_state->address = vkGetBufferDeviceAddress(device, &info);
|
|
}
|
|
|
|
// Issue data copy on request:
|
|
if (initial_data != nullptr)
|
|
{
|
|
auto cmd = copyAllocator.allocate(desc->size);
|
|
|
|
std::memcpy(cmd.uploadbuffer.mapped_data, initial_data, buffer->desc.size);
|
|
|
|
VkBufferMemoryBarrier barrier = {};
|
|
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
|
|
barrier.buffer = internal_state->resource;
|
|
barrier.srcAccessMask = 0;
|
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
barrier.size = VK_WHOLE_SIZE;
|
|
|
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
|
|
vkCmdPipelineBarrier(
|
|
cmd.commandBuffer,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
0,
|
|
0, nullptr,
|
|
1, &barrier,
|
|
0, nullptr
|
|
);
|
|
|
|
VkBufferCopy copyRegion = {};
|
|
copyRegion.size = buffer->desc.size;
|
|
copyRegion.srcOffset = 0;
|
|
copyRegion.dstOffset = 0;
|
|
|
|
vkCmdCopyBuffer(
|
|
cmd.commandBuffer,
|
|
to_internal(&cmd.uploadbuffer)->resource,
|
|
internal_state->resource,
|
|
1,
|
|
©Region
|
|
);
|
|
|
|
std::swap(barrier.srcAccessMask, barrier.dstAccessMask);
|
|
|
|
if (has_flag(buffer->desc.bind_flags, BindFlag::CONSTANT_BUFFER))
|
|
{
|
|
barrier.dstAccessMask |= VK_ACCESS_UNIFORM_READ_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.bind_flags, BindFlag::VERTEX_BUFFER))
|
|
{
|
|
barrier.dstAccessMask |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.bind_flags, BindFlag::INDEX_BUFFER))
|
|
{
|
|
barrier.dstAccessMask |= VK_ACCESS_INDEX_READ_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.bind_flags, BindFlag::SHADER_RESOURCE))
|
|
{
|
|
barrier.dstAccessMask |= VK_ACCESS_SHADER_READ_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.bind_flags, BindFlag::UNORDERED_ACCESS))
|
|
{
|
|
barrier.dstAccessMask |= VK_ACCESS_SHADER_WRITE_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.misc_flags, ResourceMiscFlag::INDIRECT_ARGS))
|
|
{
|
|
barrier.dstAccessMask |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
|
|
}
|
|
if (has_flag(buffer->desc.misc_flags, ResourceMiscFlag::RAY_TRACING))
|
|
{
|
|
barrier.dstAccessMask |= VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
|
|
}
|
|
|
|
vkCmdPipelineBarrier(
|
|
cmd.commandBuffer,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
0,
|
|
0, nullptr,
|
|
1, &barrier,
|
|
0, nullptr
|
|
);
|
|
|
|
copyAllocator.submit(cmd);
|
|
}
|
|
|
|
// Create resource views if needed
|
|
if (has_flag(desc->bind_flags, BindFlag::SHADER_RESOURCE))
|
|
{
|
|
CreateSubresource(buffer, SubresourceType::SRV, 0);
|
|
}
|
|
if (has_flag(desc->bind_flags, BindFlag::UNORDERED_ACCESS))
|
|
{
|
|
CreateSubresource(buffer, SubresourceType::UAV, 0);
|
|
}
|
|
|
|
return res == VK_SUCCESS;
|
|
}
|
|
bool GraphicsDevice_Vulkan::CreateTexture(const TextureDesc* desc, const SubresourceData* initial_data, Texture* texture) const
|
|
{
|
|
auto internal_state = std::make_shared<Texture_Vulkan>();
|
|
internal_state->allocationhandler = allocationhandler;
|
|
texture->internal_state = internal_state;
|
|
texture->type = GPUResource::Type::TEXTURE;
|
|
texture->mapped_data = nullptr;
|
|
texture->mapped_size = 0;
|
|
texture->mapped_subresources = nullptr;
|
|
texture->mapped_subresource_count = 0;
|
|
texture->sparse_properties = nullptr;
|
|
texture->desc = *desc;
|
|
|
|
if (texture->desc.mip_levels == 0)
|
|
{
|
|
texture->desc.mip_levels = (uint32_t)log2(std::max(texture->desc.width, texture->desc.height)) + 1;
|
|
}
|
|
|
|
VkImageCreateInfo imageInfo = {};
|
|
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
imageInfo.extent.width = texture->desc.width;
|
|
imageInfo.extent.height = texture->desc.height;
|
|
imageInfo.extent.depth = texture->desc.depth;
|
|
imageInfo.format = _ConvertFormat(texture->desc.format);
|
|
imageInfo.arrayLayers = texture->desc.array_size;
|
|
imageInfo.mipLevels = texture->desc.mip_levels;
|
|
imageInfo.samples = (VkSampleCountFlagBits)texture->desc.sample_count;
|
|
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
imageInfo.usage = 0;
|
|
if (has_flag(texture->desc.bind_flags, BindFlag::SHADER_RESOURCE))
|
|
{
|
|
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
}
|
|
if (has_flag(texture->desc.bind_flags, BindFlag::UNORDERED_ACCESS))
|
|
{
|
|
imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
|
|
|
|
if (IsFormatSRGB(texture->desc.format))
|
|
{
|
|
imageInfo.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
|
|
}
|
|
}
|
|
if (has_flag(texture->desc.bind_flags, BindFlag::RENDER_TARGET))
|
|
{
|
|
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
}
|
|
if (has_flag(texture->desc.bind_flags, BindFlag::DEPTH_STENCIL))
|
|
{
|
|
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
}
|
|
if (has_flag(texture->desc.bind_flags, BindFlag::SHADING_RATE))
|
|
{
|
|
imageInfo.usage |= VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
|
|
}
|
|
if (has_flag(texture->desc.misc_flags, ResourceMiscFlag::TRANSIENT_ATTACHMENT))
|
|
{
|
|
imageInfo.usage |= VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
|
|
}
|
|
else
|
|
{
|
|
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
}
|
|
|
|
imageInfo.flags = 0;
|
|
if (has_flag(texture->desc.misc_flags, ResourceMiscFlag::TEXTURECUBE))
|
|
{
|
|
imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
|
}
|
|
if (has_flag(texture->desc.misc_flags, ResourceMiscFlag::TYPED_FORMAT_CASTING) || has_flag(texture->desc.misc_flags, ResourceMiscFlag::TYPELESS_FORMAT_CASTING))
|
|
{
|
|
imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
|
}
|
|
|
|
if (families.size() > 1)
|
|
{
|
|
imageInfo.sharingMode = VK_SHARING_MODE_CONCURRENT;
|
|
imageInfo.queueFamilyIndexCount = (uint32_t)families.size();
|
|
imageInfo.pQueueFamilyIndices = families.data();
|
|
}
|
|
else
|
|
{
|
|
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
}
|
|
|
|
switch (texture->desc.type)
|
|
{
|
|
case TextureDesc::Type::TEXTURE_1D:
|
|
imageInfo.imageType = VK_IMAGE_TYPE_1D;
|
|
break;
|
|
case TextureDesc::Type::TEXTURE_2D:
|
|
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
|
break;
|
|
case TextureDesc::Type::TEXTURE_3D:
|
|
imageInfo.imageType = VK_IMAGE_TYPE_3D;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
VkResult res;
|
|
|
|
if (has_flag(texture->desc.misc_flags, ResourceMiscFlag::SPARSE))
|
|
{
|
|
imageInfo.flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
|
|
imageInfo.flags |= VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT;
|
|
imageInfo.flags |= VK_IMAGE_CREATE_SPARSE_ALIASED_BIT;
|
|
|
|
res = vkCreateImage(device, &imageInfo, nullptr, &internal_state->resource);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
VkMemoryRequirements memory_requirements = {};
|
|
vkGetImageMemoryRequirements(
|
|
device,
|
|
internal_state->resource,
|
|
&memory_requirements
|
|
);
|
|
texture->sparse_page_size = memory_requirements.alignment;
|
|
|
|
uint32_t sparse_requirement_count = 0;
|
|
|
|
vkGetImageSparseMemoryRequirements(
|
|
device,
|
|
internal_state->resource,
|
|
&sparse_requirement_count,
|
|
nullptr
|
|
);
|
|
|
|
wi::vector<VkSparseImageMemoryRequirements> sparse_requirements(sparse_requirement_count);
|
|
texture->sparse_properties = &internal_state->sparse_texture_properties;
|
|
|
|
vkGetImageSparseMemoryRequirements(
|
|
device,
|
|
internal_state->resource,
|
|
&sparse_requirement_count,
|
|
sparse_requirements.data()
|
|
);
|
|
|
|
SparseTextureProperties& out_sparse = internal_state->sparse_texture_properties;
|
|
out_sparse.total_tile_count = uint32_t(memory_requirements.size / memory_requirements.alignment);
|
|
|
|
for (size_t i = 0; i < sparse_requirements.size(); ++i)
|
|
{
|
|
const VkSparseImageMemoryRequirements& in_sparse = sparse_requirements[i];
|
|
if (i == 0)
|
|
{
|
|
// These should be common for all subresources right? Like in DX12?
|
|
out_sparse.tile_width = in_sparse.formatProperties.imageGranularity.width;
|
|
out_sparse.tile_height = in_sparse.formatProperties.imageGranularity.height;
|
|
out_sparse.tile_depth = in_sparse.formatProperties.imageGranularity.depth;
|
|
out_sparse.packed_mip_start = in_sparse.imageMipTailFirstLod;
|
|
out_sparse.packed_mip_count = texture->desc.mip_levels - in_sparse.imageMipTailFirstLod;
|
|
out_sparse.packed_mip_tile_offset = uint32_t(in_sparse.imageMipTailOffset / memory_requirements.alignment);
|
|
out_sparse.packed_mip_tile_count = uint32_t(in_sparse.imageMipTailSize / memory_requirements.alignment);
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
VmaAllocationCreateInfo allocInfo = {};
|
|
allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
|
|
|
if (texture->desc.usage == Usage::READBACK || texture->desc.usage == Usage::UPLOAD)
|
|
{
|
|
VkBufferCreateInfo bufferInfo = {};
|
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bufferInfo.size = 0;
|
|
const uint32_t data_stride = GetFormatStride(texture->desc.format);
|
|
const uint32_t block_size = GetFormatBlockSize(texture->desc.format);
|
|
const uint32_t num_blocks_x = texture->desc.width / block_size;
|
|
const uint32_t num_blocks_y = texture->desc.height / block_size;
|
|
uint32_t mip_width = num_blocks_x;
|
|
uint32_t mip_height = num_blocks_y;
|
|
uint32_t mip_depth = texture->desc.depth;
|
|
for (uint32_t mip = 0; mip < texture->desc.mip_levels; ++mip)
|
|
{
|
|
bufferInfo.size += mip_width * mip_height * mip_depth;
|
|
mip_width = std::max(1u, mip_width / 2);
|
|
mip_height = std::max(1u, mip_height / 2);
|
|
mip_depth = std::max(1u, mip_depth / 2);
|
|
}
|
|
bufferInfo.size *= imageInfo.arrayLayers;
|
|
bufferInfo.size *= data_stride;
|
|
|
|
allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
|
if (texture->desc.usage == Usage::READBACK)
|
|
{
|
|
allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
|
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
}
|
|
else if (texture->desc.usage == Usage::UPLOAD)
|
|
{
|
|
allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
|
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
|
}
|
|
|
|
res = vmaCreateBuffer(allocationhandler->allocator, &bufferInfo, &allocInfo, &internal_state->staging_resource, &internal_state->allocation, nullptr);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
imageInfo.tiling = VK_IMAGE_TILING_LINEAR;
|
|
imageInfo.mipLevels = 1; // set miplevels to 1 for linear tiling resource (this resource is just temporary)
|
|
VkImage image;
|
|
res = vkCreateImage(device, &imageInfo, nullptr, &image);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
VkSubresourceLayout subresourcelayout = {};
|
|
|
|
if (texture->desc.usage == Usage::UPLOAD)
|
|
{
|
|
const uint32_t block_size = GetFormatBlockSize(desc->format);
|
|
const uint32_t num_blocks_x = std::max(1u, desc->width / block_size);
|
|
const uint32_t num_blocks_y = std::max(1u, desc->height / block_size);
|
|
const uint32_t rowpitch = num_blocks_x * GetFormatStride(desc->format);
|
|
const uint32_t slicepitch = rowpitch * num_blocks_y;
|
|
subresourcelayout.rowPitch = (VkDeviceSize)rowpitch;
|
|
subresourcelayout.depthPitch = (VkDeviceSize)slicepitch;
|
|
subresourcelayout.arrayPitch = (VkDeviceSize)slicepitch;
|
|
}
|
|
else
|
|
{
|
|
VkImageSubresource subresource = {};
|
|
subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
vkGetImageSubresourceLayout(device, image, &subresource, &subresourcelayout);
|
|
}
|
|
|
|
if (texture->desc.usage == Usage::READBACK || texture->desc.usage == Usage::UPLOAD)
|
|
{
|
|
texture->mapped_data = internal_state->allocation->GetMappedData();
|
|
texture->mapped_size = internal_state->allocation->GetSize();
|
|
|
|
internal_state->mapped_subresources.resize(texture->desc.array_size * texture->desc.mip_levels);
|
|
size_t subresourceIndex = 0;
|
|
size_t subresourceDataOffset = 0;
|
|
for (uint32_t layer = 0; layer < texture->desc.array_size; ++layer)
|
|
{
|
|
uint32_t rowpitch = (uint32_t)subresourcelayout.rowPitch;
|
|
uint32_t slicepitch = (uint32_t)subresourcelayout.depthPitch;
|
|
uint32_t mip_width = num_blocks_x;
|
|
for (uint32_t mip = 0; mip < texture->desc.mip_levels; ++mip)
|
|
{
|
|
SubresourceData& subresourcedata = internal_state->mapped_subresources[subresourceIndex++];
|
|
subresourcedata.data_ptr = (uint8_t*)texture->mapped_data + subresourceDataOffset;
|
|
subresourcedata.row_pitch = rowpitch;
|
|
subresourcedata.slice_pitch = slicepitch;
|
|
subresourceDataOffset += std::max(rowpitch * mip_width, slicepitch);
|
|
rowpitch = std::max(data_stride, rowpitch / 2);
|
|
slicepitch = std::max(data_stride, slicepitch / 4); // squared reduction
|
|
mip_width = std::max(1u, mip_width / 2);
|
|
}
|
|
}
|
|
texture->mapped_subresources = internal_state->mapped_subresources.data();
|
|
texture->mapped_subresource_count = internal_state->mapped_subresources.size();
|
|
}
|
|
|
|
vkDestroyImage(device, image, nullptr);
|
|
return res == VK_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
res = vmaCreateImage(allocationhandler->allocator, &imageInfo, &allocInfo, &internal_state->resource, &internal_state->allocation, nullptr);
|
|
}
|
|
}
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// Issue data copy on request:
|
|
if (initial_data != nullptr)
|
|
{
|
|
auto cmd = copyAllocator.allocate(internal_state->allocation->GetSize());
|
|
|
|
wi::vector<VkBufferImageCopy> copyRegions;
|
|
|
|
VkDeviceSize copyOffset = 0;
|
|
uint32_t initDataIdx = 0;
|
|
for (uint32_t layer = 0; layer < desc->array_size; ++layer)
|
|
{
|
|
uint32_t width = imageInfo.extent.width;
|
|
uint32_t height = imageInfo.extent.height;
|
|
uint32_t depth = imageInfo.extent.depth;
|
|
for (uint32_t mip = 0; mip < desc->mip_levels; ++mip)
|
|
{
|
|
const SubresourceData& subresourceData = initial_data[initDataIdx++];
|
|
const uint32_t block_size = GetFormatBlockSize(desc->format);
|
|
const uint32_t num_blocks_x = std::max(1u, width / block_size);
|
|
const uint32_t num_blocks_y = std::max(1u, height / block_size);
|
|
const uint32_t dst_rowpitch = num_blocks_x * GetFormatStride(desc->format);
|
|
const uint32_t dst_slicepitch = dst_rowpitch * num_blocks_y;
|
|
const uint32_t src_rowpitch = subresourceData.row_pitch;
|
|
const uint32_t src_slicepitch = subresourceData.slice_pitch;
|
|
for (uint32_t z = 0; z < depth; ++z)
|
|
{
|
|
uint8_t* dst_slice = (uint8_t*)cmd.uploadbuffer.mapped_data + copyOffset + dst_slicepitch * z;
|
|
uint8_t* src_slice = (uint8_t*)subresourceData.data_ptr + src_slicepitch * z;
|
|
for (uint32_t y = 0; y < num_blocks_y; ++y)
|
|
{
|
|
std::memcpy(
|
|
dst_slice + dst_rowpitch * y,
|
|
src_slice + src_rowpitch * y,
|
|
dst_rowpitch
|
|
);
|
|
}
|
|
}
|
|
|
|
VkBufferImageCopy copyRegion = {};
|
|
copyRegion.bufferOffset = copyOffset;
|
|
copyRegion.bufferRowLength = 0;
|
|
copyRegion.bufferImageHeight = 0;
|
|
|
|
copyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
copyRegion.imageSubresource.mipLevel = mip;
|
|
copyRegion.imageSubresource.baseArrayLayer = layer;
|
|
copyRegion.imageSubresource.layerCount = 1;
|
|
|
|
copyRegion.imageOffset = { 0, 0, 0 };
|
|
copyRegion.imageExtent = {
|
|
width,
|
|
height,
|
|
depth
|
|
};
|
|
|
|
copyRegions.push_back(copyRegion);
|
|
|
|
copyOffset += dst_slicepitch * depth;
|
|
|
|
width = std::max(1u, width / 2);
|
|
height = std::max(1u, height / 2);
|
|
depth = std::max(1u, depth / 2);
|
|
}
|
|
}
|
|
|
|
{
|
|
VkImageMemoryBarrier barrier = {};
|
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
barrier.image = internal_state->resource;
|
|
barrier.oldLayout = imageInfo.initialLayout;
|
|
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
barrier.srcAccessMask = 0;
|
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
barrier.subresourceRange.baseArrayLayer = 0;
|
|
barrier.subresourceRange.layerCount = imageInfo.arrayLayers;
|
|
barrier.subresourceRange.baseMipLevel = 0;
|
|
barrier.subresourceRange.levelCount = imageInfo.mipLevels;
|
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
|
|
vkCmdPipelineBarrier(
|
|
cmd.commandBuffer,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
0,
|
|
0, nullptr,
|
|
0, nullptr,
|
|
1, &barrier
|
|
);
|
|
|
|
vkCmdCopyBufferToImage(cmd.commandBuffer, to_internal(&cmd.uploadbuffer)->resource, internal_state->resource, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (uint32_t)copyRegions.size(), copyRegions.data());
|
|
|
|
copyAllocator.submit(cmd);
|
|
|
|
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
barrier.newLayout = _ConvertImageLayout(texture->desc.layout);
|
|
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
barrier.dstAccessMask = _ParseResourceState(texture->desc.layout);
|
|
|
|
initLocker.lock();
|
|
vkCmdPipelineBarrier(
|
|
GetFrameResources().initCommandBuffer,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
0,
|
|
0, nullptr,
|
|
0, nullptr,
|
|
1, &barrier
|
|
);
|
|
submit_inits = true;
|
|
initLocker.unlock();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VkImageMemoryBarrier barrier = {};
|
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
barrier.image = internal_state->resource;
|
|
barrier.oldLayout = imageInfo.initialLayout;
|
|
barrier.newLayout = _ConvertImageLayout(texture->desc.layout);
|
|
barrier.srcAccessMask = 0;
|
|
barrier.dstAccessMask = _ParseResourceState(texture->desc.layout);
|
|
if (IsFormatDepthSupport(texture->desc.format))
|
|
{
|
|
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
if (IsFormatStencilSupport(texture->desc.format))
|
|
{
|
|
barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
}
|
|
barrier.subresourceRange.baseArrayLayer = 0;
|
|
barrier.subresourceRange.layerCount = imageInfo.arrayLayers;
|
|
barrier.subresourceRange.baseMipLevel = 0;
|
|
barrier.subresourceRange.levelCount = imageInfo.mipLevels;
|
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
|
|
initLocker.lock();
|
|
vkCmdPipelineBarrier(
|
|
GetFrameResources().initCommandBuffer,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
0,
|
|
0, nullptr,
|
|
0, nullptr,
|
|
1, &barrier
|
|
);
|
|
submit_inits = true;
|
|
initLocker.unlock();
|
|
}
|
|
|
|
if (has_flag(texture->desc.bind_flags, BindFlag::RENDER_TARGET))
|
|
{
|
|
CreateSubresource(texture, SubresourceType::RTV, 0, -1, 0, -1);
|
|
}
|
|
if (has_flag(texture->desc.bind_flags, BindFlag::DEPTH_STENCIL))
|
|
{
|
|
CreateSubresource(texture, SubresourceType::DSV, 0, -1, 0, -1);
|
|
}
|
|
if (has_flag(texture->desc.bind_flags, BindFlag::SHADER_RESOURCE))
|
|
{
|
|
CreateSubresource(texture, SubresourceType::SRV, 0, -1, 0, -1);
|
|
}
|
|
if (has_flag(texture->desc.bind_flags, BindFlag::UNORDERED_ACCESS))
|
|
{
|
|
CreateSubresource(texture, SubresourceType::UAV, 0, -1, 0, -1);
|
|
}
|
|
|
|
return res == VK_SUCCESS;
|
|
}
|
|
bool GraphicsDevice_Vulkan::CreateShader(ShaderStage stage, const void* shadercode, size_t shadercode_size, Shader* shader) const
|
|
{
|
|
auto internal_state = std::make_shared<Shader_Vulkan>();
|
|
internal_state->allocationhandler = allocationhandler;
|
|
shader->internal_state = internal_state;
|
|
shader->stage = stage;
|
|
|
|
VkResult res = VK_SUCCESS;
|
|
|
|
VkShaderModuleCreateInfo moduleInfo = {};
|
|
moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
|
moduleInfo.codeSize = shadercode_size;
|
|
moduleInfo.pCode = (const uint32_t*)shadercode;
|
|
res = vkCreateShaderModule(device, &moduleInfo, nullptr, &internal_state->shaderModule);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
internal_state->stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
internal_state->stageInfo.module = internal_state->shaderModule;
|
|
internal_state->stageInfo.pName = "main";
|
|
switch (stage)
|
|
{
|
|
case ShaderStage::MS:
|
|
internal_state->stageInfo.stage = VK_SHADER_STAGE_MESH_BIT_NV;
|
|
break;
|
|
case ShaderStage::AS:
|
|
internal_state->stageInfo.stage = VK_SHADER_STAGE_TASK_BIT_NV;
|
|
break;
|
|
case ShaderStage::VS:
|
|
internal_state->stageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
|
break;
|
|
case ShaderStage::HS:
|
|
internal_state->stageInfo.stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
|
|
break;
|
|
case ShaderStage::DS:
|
|
internal_state->stageInfo.stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
|
|
break;
|
|
case ShaderStage::GS:
|
|
internal_state->stageInfo.stage = VK_SHADER_STAGE_GEOMETRY_BIT;
|
|
break;
|
|
case ShaderStage::PS:
|
|
internal_state->stageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
break;
|
|
case ShaderStage::CS:
|
|
internal_state->stageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
break;
|
|
default:
|
|
// also means library shader (ray tracing)
|
|
internal_state->stageInfo.stage = VK_SHADER_STAGE_ALL;
|
|
break;
|
|
}
|
|
|
|
{
|
|
SpvReflectShaderModule module;
|
|
SpvReflectResult result = spvReflectCreateShaderModule(moduleInfo.codeSize, moduleInfo.pCode, &module);
|
|
assert(result == SPV_REFLECT_RESULT_SUCCESS);
|
|
|
|
uint32_t binding_count = 0;
|
|
result = spvReflectEnumerateDescriptorBindings(
|
|
&module, &binding_count, nullptr
|
|
);
|
|
assert(result == SPV_REFLECT_RESULT_SUCCESS);
|
|
|
|
wi::vector<SpvReflectDescriptorBinding*> bindings(binding_count);
|
|
result = spvReflectEnumerateDescriptorBindings(
|
|
&module, &binding_count, bindings.data()
|
|
);
|
|
assert(result == SPV_REFLECT_RESULT_SUCCESS);
|
|
|
|
uint32_t push_count = 0;
|
|
result = spvReflectEnumeratePushConstantBlocks(&module, &push_count, nullptr);
|
|
assert(result == SPV_REFLECT_RESULT_SUCCESS);
|
|
|
|
wi::vector<SpvReflectBlockVariable*> pushconstants(push_count);
|
|
result = spvReflectEnumeratePushConstantBlocks(&module, &push_count, pushconstants.data());
|
|
assert(result == SPV_REFLECT_RESULT_SUCCESS);
|
|
|
|
for (auto& x : pushconstants)
|
|
{
|
|
auto& push = internal_state->pushconstants;
|
|
push.stageFlags = internal_state->stageInfo.stage;
|
|
push.offset = x->offset;
|
|
push.size = x->size;
|
|
}
|
|
|
|
for (auto& x : bindings)
|
|
{
|
|
const bool bindless = x->set > 0;
|
|
|
|
if (bindless)
|
|
{
|
|
// There can be padding between bindless spaces because sets need to be bound contiguously
|
|
internal_state->bindlessBindings.resize(std::max(internal_state->bindlessBindings.size(), (size_t)x->set));
|
|
}
|
|
|
|
auto& descriptor = bindless ? internal_state->bindlessBindings[x->set - 1] : internal_state->layoutBindings.emplace_back();
|
|
descriptor.stageFlags = internal_state->stageInfo.stage;
|
|
descriptor.binding = x->binding;
|
|
descriptor.descriptorCount = x->count;
|
|
descriptor.descriptorType = (VkDescriptorType)x->descriptor_type;
|
|
|
|
if (bindless)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
auto& imageViewType = internal_state->imageViewTypes.emplace_back();
|
|
imageViewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
|
|
|
|
if (x->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER && x->binding >= VULKAN_BINDING_SHIFT_S + immutable_sampler_slot_begin)
|
|
{
|
|
descriptor.pImmutableSamplers = immutable_samplers.data() + x->binding - VULKAN_BINDING_SHIFT_S - immutable_sampler_slot_begin;
|
|
continue;
|
|
}
|
|
|
|
if (descriptor.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
|
|
{
|
|
// For now, always replace VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER with VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
|
|
// It would be quite messy to track which buffer is dynamic and which is not in the binding code, consider multiple pipeline bind points too
|
|
// But maybe the dynamic uniform buffer is not always best because it occupies more registers (like DX12 root descriptor)?
|
|
descriptor.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
for (uint32_t i = 0; i < descriptor.descriptorCount; ++i)
|
|
{
|
|
internal_state->uniform_buffer_sizes[descriptor.binding + i] = x->block.size;
|
|
internal_state->uniform_buffer_dynamic_slots.push_back(descriptor.binding + i);
|
|
}
|
|
}
|
|
|
|
switch (x->descriptor_type)
|
|
{
|
|
default:
|
|
break;
|
|
case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
|
case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
|
switch (x->image.dim)
|
|
{
|
|
default:
|
|
case SpvDim1D:
|
|
if (x->image.arrayed == 0)
|
|
{
|
|
imageViewType = VK_IMAGE_VIEW_TYPE_1D;
|
|
}
|
|
else
|
|
{
|
|
imageViewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
|
|
}
|
|
break;
|
|
case SpvDim2D:
|
|
if (x->image.arrayed == 0)
|
|
{
|
|
imageViewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
}
|
|
else
|
|
{
|
|
imageViewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
|
}
|
|
break;
|
|
case SpvDim3D:
|
|
imageViewType = VK_IMAGE_VIEW_TYPE_3D;
|
|
break;
|
|
case SpvDimCube:
|
|
if (x->image.arrayed == 0)
|
|
{
|
|
imageViewType = VK_IMAGE_VIEW_TYPE_CUBE;
|
|
}
|
|
else
|
|
{
|
|
imageViewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
spvReflectDestroyShaderModule(&module);
|
|
|
|
if (stage == ShaderStage::CS || stage == ShaderStage::LIB)
|
|
{
|
|
internal_state->binding_hash = 0;
|
|
size_t i = 0;
|
|
for (auto& x : internal_state->layoutBindings)
|
|
{
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.binding);
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.descriptorCount);
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.descriptorType);
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.stageFlags);
|
|
wi::helper::hash_combine(internal_state->binding_hash, internal_state->imageViewTypes[i++]);
|
|
}
|
|
for (auto& x : internal_state->bindlessBindings)
|
|
{
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.binding);
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.descriptorCount);
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.descriptorType);
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.stageFlags);
|
|
}
|
|
wi::helper::hash_combine(internal_state->binding_hash, internal_state->pushconstants.offset);
|
|
wi::helper::hash_combine(internal_state->binding_hash, internal_state->pushconstants.size);
|
|
wi::helper::hash_combine(internal_state->binding_hash, internal_state->pushconstants.stageFlags);
|
|
|
|
pso_layout_cache_mutex.lock();
|
|
if (pso_layout_cache[internal_state->binding_hash].pipelineLayout == VK_NULL_HANDLE)
|
|
{
|
|
wi::vector<VkDescriptorSetLayout> layouts;
|
|
|
|
{
|
|
VkDescriptorSetLayoutCreateInfo descriptorSetlayoutInfo = {};
|
|
descriptorSetlayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
|
descriptorSetlayoutInfo.pBindings = internal_state->layoutBindings.data();
|
|
descriptorSetlayoutInfo.bindingCount = uint32_t(internal_state->layoutBindings.size());
|
|
res = vkCreateDescriptorSetLayout(device, &descriptorSetlayoutInfo, nullptr, &internal_state->descriptorSetLayout);
|
|
assert(res == VK_SUCCESS);
|
|
layouts.push_back(internal_state->descriptorSetLayout);
|
|
}
|
|
|
|
internal_state->bindlessFirstSet = (uint32_t)layouts.size();
|
|
for (auto& x : internal_state->bindlessBindings)
|
|
{
|
|
switch (x.descriptorType)
|
|
{
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
|
assert(0); // not supported, use the raw buffers for same functionality
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
|
layouts.push_back(allocationhandler->bindlessSampledImages.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessSampledImages.descriptorSet);
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
|
layouts.push_back(allocationhandler->bindlessUniformTexelBuffers.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessUniformTexelBuffers.descriptorSet);
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
|
layouts.push_back(allocationhandler->bindlessStorageBuffers.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessStorageBuffers.descriptorSet);
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
|
layouts.push_back(allocationhandler->bindlessStorageImages.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessStorageImages.descriptorSet);
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
|
layouts.push_back(allocationhandler->bindlessStorageTexelBuffers.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessStorageTexelBuffers.descriptorSet);
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
|
layouts.push_back(allocationhandler->bindlessSamplers.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessSamplers.descriptorSet);
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR:
|
|
layouts.push_back(allocationhandler->bindlessAccelerationStructures.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessAccelerationStructures.descriptorSet);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
|
|
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
pipelineLayoutInfo.pSetLayouts = layouts.data();
|
|
pipelineLayoutInfo.setLayoutCount = (uint32_t)layouts.size();
|
|
if (internal_state->pushconstants.size > 0)
|
|
{
|
|
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
|
pipelineLayoutInfo.pPushConstantRanges = &internal_state->pushconstants;
|
|
}
|
|
else
|
|
{
|
|
pipelineLayoutInfo.pushConstantRangeCount = 0;
|
|
pipelineLayoutInfo.pPushConstantRanges = nullptr;
|
|
}
|
|
|
|
res = vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &internal_state->pipelineLayout_cs);
|
|
assert(res == VK_SUCCESS);
|
|
if (res == VK_SUCCESS)
|
|
{
|
|
pso_layout_cache[internal_state->binding_hash].descriptorSetLayout = internal_state->descriptorSetLayout;
|
|
pso_layout_cache[internal_state->binding_hash].pipelineLayout = internal_state->pipelineLayout_cs;
|
|
pso_layout_cache[internal_state->binding_hash].bindlessSets = internal_state->bindlessSets;
|
|
pso_layout_cache[internal_state->binding_hash].bindlessFirstSet = internal_state->bindlessFirstSet;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
internal_state->descriptorSetLayout = pso_layout_cache[internal_state->binding_hash].descriptorSetLayout;
|
|
internal_state->pipelineLayout_cs = pso_layout_cache[internal_state->binding_hash].pipelineLayout;
|
|
internal_state->bindlessSets = pso_layout_cache[internal_state->binding_hash].bindlessSets;
|
|
internal_state->bindlessFirstSet = pso_layout_cache[internal_state->binding_hash].bindlessFirstSet;
|
|
}
|
|
pso_layout_cache_mutex.unlock();
|
|
}
|
|
}
|
|
|
|
if (stage == ShaderStage::CS)
|
|
{
|
|
// sort because dynamic offsets array is tightly packed to match slot numbers:
|
|
std::sort(internal_state->uniform_buffer_dynamic_slots.begin(), internal_state->uniform_buffer_dynamic_slots.end());
|
|
|
|
VkComputePipelineCreateInfo pipelineInfo = {};
|
|
pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
|
pipelineInfo.layout = internal_state->pipelineLayout_cs;
|
|
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
|
|
|
|
// Create compute pipeline state in place:
|
|
pipelineInfo.stage = internal_state->stageInfo;
|
|
|
|
|
|
res = vkCreateComputePipelines(device, pipelineCache, 1, &pipelineInfo, nullptr, &internal_state->pipeline_cs);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
|
|
return res == VK_SUCCESS;
|
|
}
|
|
bool GraphicsDevice_Vulkan::CreateSampler(const SamplerDesc* desc, Sampler* sampler) const
|
|
{
|
|
auto internal_state = std::make_shared<Sampler_Vulkan>();
|
|
internal_state->allocationhandler = allocationhandler;
|
|
sampler->internal_state = internal_state;
|
|
sampler->desc = *desc;
|
|
|
|
VkSamplerCreateInfo createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
|
createInfo.flags = 0;
|
|
createInfo.pNext = nullptr;
|
|
|
|
switch (desc->filter)
|
|
{
|
|
case Filter::MIN_MAG_MIP_POINT:
|
|
case Filter::MINIMUM_MIN_MAG_MIP_POINT:
|
|
case Filter::MAXIMUM_MIN_MAG_MIP_POINT:
|
|
createInfo.minFilter = VK_FILTER_NEAREST;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = false;
|
|
break;
|
|
case Filter::MIN_MAG_POINT_MIP_LINEAR:
|
|
case Filter::MINIMUM_MIN_MAG_POINT_MIP_LINEAR:
|
|
case Filter::MAXIMUM_MIN_MAG_POINT_MIP_LINEAR:
|
|
createInfo.minFilter = VK_FILTER_NEAREST;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = false;
|
|
break;
|
|
case Filter::MIN_POINT_MAG_LINEAR_MIP_POINT:
|
|
case Filter::MINIMUM_MIN_POINT_MAG_LINEAR_MIP_POINT:
|
|
case Filter::MAXIMUM_MIN_POINT_MAG_LINEAR_MIP_POINT:
|
|
createInfo.minFilter = VK_FILTER_NEAREST;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = false;
|
|
break;
|
|
case Filter::MIN_POINT_MAG_MIP_LINEAR:
|
|
case Filter::MINIMUM_MIN_POINT_MAG_MIP_LINEAR:
|
|
case Filter::MAXIMUM_MIN_POINT_MAG_MIP_LINEAR:
|
|
createInfo.minFilter = VK_FILTER_NEAREST;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = false;
|
|
break;
|
|
case Filter::MIN_LINEAR_MAG_MIP_POINT:
|
|
case Filter::MINIMUM_MIN_LINEAR_MAG_MIP_POINT:
|
|
case Filter::MAXIMUM_MIN_LINEAR_MAG_MIP_POINT:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = false;
|
|
break;
|
|
case Filter::MIN_LINEAR_MAG_POINT_MIP_LINEAR:
|
|
case Filter::MINIMUM_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
|
|
case Filter::MAXIMUM_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = false;
|
|
break;
|
|
case Filter::MIN_MAG_LINEAR_MIP_POINT:
|
|
case Filter::MINIMUM_MIN_MAG_LINEAR_MIP_POINT:
|
|
case Filter::MAXIMUM_MIN_MAG_LINEAR_MIP_POINT:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = false;
|
|
break;
|
|
case Filter::MIN_MAG_MIP_LINEAR:
|
|
case Filter::MINIMUM_MIN_MAG_MIP_LINEAR:
|
|
case Filter::MAXIMUM_MIN_MAG_MIP_LINEAR:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = false;
|
|
break;
|
|
case Filter::ANISOTROPIC:
|
|
case Filter::MINIMUM_ANISOTROPIC:
|
|
case Filter::MAXIMUM_ANISOTROPIC:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.anisotropyEnable = true;
|
|
createInfo.compareEnable = false;
|
|
break;
|
|
case Filter::COMPARISON_MIN_MAG_MIP_POINT:
|
|
createInfo.minFilter = VK_FILTER_NEAREST;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = true;
|
|
break;
|
|
case Filter::COMPARISON_MIN_MAG_POINT_MIP_LINEAR:
|
|
createInfo.minFilter = VK_FILTER_NEAREST;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = true;
|
|
break;
|
|
case Filter::COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT:
|
|
createInfo.minFilter = VK_FILTER_NEAREST;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = true;
|
|
break;
|
|
case Filter::COMPARISON_MIN_POINT_MAG_MIP_LINEAR:
|
|
createInfo.minFilter = VK_FILTER_NEAREST;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = true;
|
|
break;
|
|
case Filter::COMPARISON_MIN_LINEAR_MAG_MIP_POINT:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = true;
|
|
break;
|
|
case Filter::COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = true;
|
|
break;
|
|
case Filter::COMPARISON_MIN_MAG_LINEAR_MIP_POINT:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = true;
|
|
break;
|
|
case Filter::COMPARISON_MIN_MAG_MIP_LINEAR:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = true;
|
|
break;
|
|
case Filter::COMPARISON_ANISOTROPIC:
|
|
createInfo.minFilter = VK_FILTER_LINEAR;
|
|
createInfo.magFilter = VK_FILTER_LINEAR;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
createInfo.anisotropyEnable = true;
|
|
createInfo.compareEnable = true;
|
|
break;
|
|
default:
|
|
createInfo.minFilter = VK_FILTER_NEAREST;
|
|
createInfo.magFilter = VK_FILTER_NEAREST;
|
|
createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
createInfo.anisotropyEnable = false;
|
|
createInfo.compareEnable = false;
|
|
break;
|
|
}
|
|
|
|
VkSamplerReductionModeCreateInfo reductionmode = {};
|
|
reductionmode.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO;
|
|
if (CheckCapability(GraphicsDeviceCapability::SAMPLER_MINMAX))
|
|
{
|
|
switch (desc->filter)
|
|
{
|
|
case Filter::MINIMUM_MIN_MAG_MIP_POINT:
|
|
case Filter::MINIMUM_MIN_MAG_POINT_MIP_LINEAR:
|
|
case Filter::MINIMUM_MIN_POINT_MAG_LINEAR_MIP_POINT:
|
|
case Filter::MINIMUM_MIN_POINT_MAG_MIP_LINEAR:
|
|
case Filter::MINIMUM_MIN_LINEAR_MAG_MIP_POINT:
|
|
case Filter::MINIMUM_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
|
|
case Filter::MINIMUM_MIN_MAG_LINEAR_MIP_POINT:
|
|
case Filter::MINIMUM_MIN_MAG_MIP_LINEAR:
|
|
case Filter::MINIMUM_ANISOTROPIC:
|
|
reductionmode.reductionMode = VK_SAMPLER_REDUCTION_MODE_MIN;
|
|
createInfo.pNext = &reductionmode;
|
|
break;
|
|
case Filter::MAXIMUM_MIN_MAG_MIP_POINT:
|
|
case Filter::MAXIMUM_MIN_MAG_POINT_MIP_LINEAR:
|
|
case Filter::MAXIMUM_MIN_POINT_MAG_LINEAR_MIP_POINT:
|
|
case Filter::MAXIMUM_MIN_POINT_MAG_MIP_LINEAR:
|
|
case Filter::MAXIMUM_MIN_LINEAR_MAG_MIP_POINT:
|
|
case Filter::MAXIMUM_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
|
|
case Filter::MAXIMUM_MIN_MAG_LINEAR_MIP_POINT:
|
|
case Filter::MAXIMUM_MIN_MAG_MIP_LINEAR:
|
|
case Filter::MAXIMUM_ANISOTROPIC:
|
|
reductionmode.reductionMode = VK_SAMPLER_REDUCTION_MODE_MAX;
|
|
createInfo.pNext = &reductionmode;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
createInfo.addressModeU = _ConvertTextureAddressMode(desc->address_u, features_1_2);
|
|
createInfo.addressModeV = _ConvertTextureAddressMode(desc->address_v, features_1_2);
|
|
createInfo.addressModeW = _ConvertTextureAddressMode(desc->address_w, features_1_2);
|
|
createInfo.maxAnisotropy = static_cast<float>(desc->max_anisotropy);
|
|
createInfo.compareOp = _ConvertComparisonFunc(desc->comparison_func);
|
|
createInfo.minLod = desc->min_lod;
|
|
createInfo.maxLod = desc->max_lod;
|
|
createInfo.mipLodBias = desc->mip_lod_bias;
|
|
createInfo.borderColor = _ConvertSamplerBorderColor(desc->border_color);
|
|
createInfo.unnormalizedCoordinates = VK_FALSE;
|
|
|
|
VkResult res = vkCreateSampler(device, &createInfo, nullptr, &internal_state->resource);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
internal_state->index = allocationhandler->bindlessSamplers.allocate();
|
|
if (internal_state->index >= 0)
|
|
{
|
|
VkDescriptorImageInfo imageInfo = {};
|
|
imageInfo.sampler = internal_state->resource;
|
|
VkWriteDescriptorSet write = {};
|
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
|
|
write.dstBinding = 0;
|
|
write.dstArrayElement = internal_state->index;
|
|
write.descriptorCount = 1;
|
|
write.dstSet = allocationhandler->bindlessSamplers.descriptorSet;
|
|
write.pImageInfo = &imageInfo;
|
|
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
|
}
|
|
else
|
|
{
|
|
assert(0);
|
|
}
|
|
|
|
return res == VK_SUCCESS;
|
|
}
|
|
bool GraphicsDevice_Vulkan::CreateQueryHeap(const GPUQueryHeapDesc* desc, GPUQueryHeap* queryheap) const
|
|
{
|
|
auto internal_state = std::make_shared<QueryHeap_Vulkan>();
|
|
internal_state->allocationhandler = allocationhandler;
|
|
queryheap->internal_state = internal_state;
|
|
queryheap->desc = *desc;
|
|
|
|
VkQueryPoolCreateInfo poolInfo = {};
|
|
poolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
|
|
poolInfo.queryCount = desc->query_count;
|
|
|
|
switch (desc->type)
|
|
{
|
|
case GpuQueryType::TIMESTAMP:
|
|
poolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;
|
|
break;
|
|
case GpuQueryType::OCCLUSION:
|
|
case GpuQueryType::OCCLUSION_BINARY:
|
|
poolInfo.queryType = VK_QUERY_TYPE_OCCLUSION;
|
|
break;
|
|
}
|
|
|
|
VkResult res = vkCreateQueryPool(device, &poolInfo, nullptr, &internal_state->pool);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
return res == VK_SUCCESS;
|
|
}
|
|
bool GraphicsDevice_Vulkan::CreatePipelineState(const PipelineStateDesc* desc, PipelineState* pso) const
|
|
{
|
|
auto internal_state = std::make_shared<PipelineState_Vulkan>();
|
|
internal_state->allocationhandler = allocationhandler;
|
|
pso->internal_state = internal_state;
|
|
pso->desc = *desc;
|
|
|
|
pso->hash = 0;
|
|
wi::helper::hash_combine(pso->hash, desc->ms);
|
|
wi::helper::hash_combine(pso->hash, desc->as);
|
|
wi::helper::hash_combine(pso->hash, desc->vs);
|
|
wi::helper::hash_combine(pso->hash, desc->ps);
|
|
wi::helper::hash_combine(pso->hash, desc->hs);
|
|
wi::helper::hash_combine(pso->hash, desc->ds);
|
|
wi::helper::hash_combine(pso->hash, desc->gs);
|
|
wi::helper::hash_combine(pso->hash, desc->il);
|
|
wi::helper::hash_combine(pso->hash, desc->rs);
|
|
wi::helper::hash_combine(pso->hash, desc->bs);
|
|
wi::helper::hash_combine(pso->hash, desc->dss);
|
|
wi::helper::hash_combine(pso->hash, desc->pt);
|
|
wi::helper::hash_combine(pso->hash, desc->sample_mask);
|
|
|
|
VkResult res = VK_SUCCESS;
|
|
|
|
{
|
|
auto insert_shader = [&](const Shader* shader) {
|
|
if (shader == nullptr)
|
|
return;
|
|
auto shader_internal = to_internal(shader);
|
|
|
|
uint32_t i = 0;
|
|
size_t check_max = internal_state->layoutBindings.size(); // dont't check for duplicates within self table
|
|
for (auto& x : shader_internal->layoutBindings)
|
|
{
|
|
bool found = false;
|
|
size_t j = 0;
|
|
for (auto& y : internal_state->layoutBindings)
|
|
{
|
|
if (x.binding == y.binding)
|
|
{
|
|
// If the asserts fire, it means there are overlapping bindings between shader stages
|
|
// This is not supported now for performance reasons (less binding management)!
|
|
// (Overlaps between s/b/t bind points are not a problem because those are shifted by the compiler)
|
|
assert(x.descriptorCount == y.descriptorCount);
|
|
assert(x.descriptorType == y.descriptorType);
|
|
found = true;
|
|
y.stageFlags |= x.stageFlags;
|
|
break;
|
|
}
|
|
if (j++ >= check_max)
|
|
break;
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
internal_state->layoutBindings.push_back(x);
|
|
internal_state->imageViewTypes.push_back(shader_internal->imageViewTypes[i]);
|
|
if (x.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || x.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
|
|
{
|
|
for (uint32_t k = 0; k < x.descriptorCount; ++k)
|
|
{
|
|
internal_state->uniform_buffer_sizes[x.binding + k] = shader_internal->uniform_buffer_sizes[x.binding + k];
|
|
}
|
|
}
|
|
if (x.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
|
|
{
|
|
for (uint32_t k = 0; k < x.descriptorCount; ++k)
|
|
{
|
|
internal_state->uniform_buffer_dynamic_slots.push_back(x.binding + k);
|
|
}
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (shader_internal->pushconstants.size > 0)
|
|
{
|
|
internal_state->pushconstants.offset = std::min(internal_state->pushconstants.offset, shader_internal->pushconstants.offset);
|
|
internal_state->pushconstants.size = std::max(internal_state->pushconstants.size, shader_internal->pushconstants.size);
|
|
internal_state->pushconstants.stageFlags |= shader_internal->pushconstants.stageFlags;
|
|
}
|
|
};
|
|
|
|
insert_shader(desc->ms);
|
|
insert_shader(desc->as);
|
|
insert_shader(desc->vs);
|
|
insert_shader(desc->hs);
|
|
insert_shader(desc->ds);
|
|
insert_shader(desc->gs);
|
|
insert_shader(desc->ps);
|
|
|
|
// sort because dynamic offsets array is tightly packed to match slot numbers:
|
|
std::sort(internal_state->uniform_buffer_dynamic_slots.begin(), internal_state->uniform_buffer_dynamic_slots.end());
|
|
|
|
auto insert_shader_bindless = [&](const Shader* shader) {
|
|
if (shader == nullptr)
|
|
return;
|
|
auto shader_internal = to_internal(shader);
|
|
|
|
internal_state->bindlessBindings.resize(std::max(internal_state->bindlessBindings.size(), shader_internal->bindlessBindings.size()));
|
|
|
|
int i = 0;
|
|
for (auto& x : shader_internal->bindlessBindings)
|
|
{
|
|
if (internal_state->bindlessBindings[i].descriptorType != x.descriptorType)
|
|
{
|
|
internal_state->bindlessBindings[i] = x;
|
|
}
|
|
else
|
|
{
|
|
internal_state->bindlessBindings[i].stageFlags |= x.stageFlags;
|
|
}
|
|
i++;
|
|
}
|
|
};
|
|
|
|
insert_shader_bindless(desc->ms);
|
|
insert_shader_bindless(desc->as);
|
|
insert_shader_bindless(desc->vs);
|
|
insert_shader_bindless(desc->hs);
|
|
insert_shader_bindless(desc->ds);
|
|
insert_shader_bindless(desc->gs);
|
|
insert_shader_bindless(desc->ps);
|
|
|
|
internal_state->binding_hash = 0;
|
|
size_t i = 0;
|
|
for (auto& x : internal_state->layoutBindings)
|
|
{
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.binding);
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.descriptorCount);
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.descriptorType);
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.stageFlags);
|
|
wi::helper::hash_combine(internal_state->binding_hash, internal_state->imageViewTypes[i++]);
|
|
}
|
|
for (auto& x : internal_state->bindlessBindings)
|
|
{
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.binding);
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.descriptorCount);
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.descriptorType);
|
|
wi::helper::hash_combine(internal_state->binding_hash, x.stageFlags);
|
|
}
|
|
wi::helper::hash_combine(internal_state->binding_hash, internal_state->pushconstants.offset);
|
|
wi::helper::hash_combine(internal_state->binding_hash, internal_state->pushconstants.size);
|
|
wi::helper::hash_combine(internal_state->binding_hash, internal_state->pushconstants.stageFlags);
|
|
|
|
|
|
pso_layout_cache_mutex.lock();
|
|
if (pso_layout_cache[internal_state->binding_hash].pipelineLayout == VK_NULL_HANDLE)
|
|
{
|
|
wi::vector<VkDescriptorSetLayout> layouts;
|
|
{
|
|
VkDescriptorSetLayoutCreateInfo descriptorSetlayoutInfo = {};
|
|
descriptorSetlayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
|
descriptorSetlayoutInfo.pBindings = internal_state->layoutBindings.data();
|
|
descriptorSetlayoutInfo.bindingCount = static_cast<uint32_t>(internal_state->layoutBindings.size());
|
|
res = vkCreateDescriptorSetLayout(device, &descriptorSetlayoutInfo, nullptr, &internal_state->descriptorSetLayout);
|
|
assert(res == VK_SUCCESS);
|
|
layouts.push_back(internal_state->descriptorSetLayout);
|
|
}
|
|
|
|
internal_state->bindlessFirstSet = (uint32_t)layouts.size();
|
|
for (auto& x : internal_state->bindlessBindings)
|
|
{
|
|
switch (x.descriptorType)
|
|
{
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
|
assert(0); // not supported, use the raw buffers for same functionality
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
|
layouts.push_back(allocationhandler->bindlessSampledImages.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessSampledImages.descriptorSet);
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
|
layouts.push_back(allocationhandler->bindlessUniformTexelBuffers.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessUniformTexelBuffers.descriptorSet);
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
|
layouts.push_back(allocationhandler->bindlessStorageBuffers.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessStorageBuffers.descriptorSet);
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
|
layouts.push_back(allocationhandler->bindlessStorageImages.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessStorageImages.descriptorSet);
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
|
layouts.push_back(allocationhandler->bindlessStorageTexelBuffers.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessStorageTexelBuffers.descriptorSet);
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
|
layouts.push_back(allocationhandler->bindlessSamplers.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessSamplers.descriptorSet);
|
|
break;
|
|
case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR:
|
|
layouts.push_back(allocationhandler->bindlessAccelerationStructures.descriptorSetLayout);
|
|
internal_state->bindlessSets.push_back(allocationhandler->bindlessAccelerationStructures.descriptorSet);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
|
|
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
pipelineLayoutInfo.pSetLayouts = layouts.data();
|
|
pipelineLayoutInfo.setLayoutCount = (uint32_t)layouts.size();
|
|
if (internal_state->pushconstants.size > 0)
|
|
{
|
|
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
|
pipelineLayoutInfo.pPushConstantRanges = &internal_state->pushconstants;
|
|
}
|
|
else
|
|
{
|
|
pipelineLayoutInfo.pushConstantRangeCount = 0;
|
|
pipelineLayoutInfo.pPushConstantRanges = nullptr;
|
|
}
|
|
|
|
res = vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &internal_state->pipelineLayout);
|
|
assert(res == VK_SUCCESS);
|
|
if (res == VK_SUCCESS)
|
|
{
|
|
pso_layout_cache[internal_state->binding_hash].descriptorSetLayout = internal_state->descriptorSetLayout;
|
|
pso_layout_cache[internal_state->binding_hash].pipelineLayout = internal_state->pipelineLayout;
|
|
pso_layout_cache[internal_state->binding_hash].bindlessSets = internal_state->bindlessSets;
|
|
pso_layout_cache[internal_state->binding_hash].bindlessFirstSet = internal_state->bindlessFirstSet;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
internal_state->descriptorSetLayout = pso_layout_cache[internal_state->binding_hash].descriptorSetLayout;
|
|
internal_state->pipelineLayout = pso_layout_cache[internal_state->binding_hash].pipelineLayout;
|
|
internal_state->bindlessSets = pso_layout_cache[internal_state->binding_hash].bindlessSets;
|
|
internal_state->bindlessFirstSet = pso_layout_cache[internal_state->binding_hash].bindlessFirstSet;
|
|
}
|
|
pso_layout_cache_mutex.unlock();
|
|
}
|
|
|
|
|
|
VkGraphicsPipelineCreateInfo& pipelineInfo = internal_state->pipelineInfo;
|
|
//pipelineInfo.flags = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT;
|
|
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
|
pipelineInfo.layout = internal_state->pipelineLayout;
|
|
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
|
|
|
|
// Shaders:
|
|
|
|
uint32_t shaderStageCount = 0;
|
|
auto& shaderStages = internal_state->shaderStages;
|
|
if (pso->desc.ms != nullptr && pso->desc.ms->IsValid())
|
|
{
|
|
shaderStages[shaderStageCount++] = to_internal(pso->desc.ms)->stageInfo;
|
|
}
|
|
if (pso->desc.as != nullptr && pso->desc.as->IsValid())
|
|
{
|
|
shaderStages[shaderStageCount++] = to_internal(pso->desc.as)->stageInfo;
|
|
}
|
|
if (pso->desc.vs != nullptr && pso->desc.vs->IsValid())
|
|
{
|
|
shaderStages[shaderStageCount++] = to_internal(pso->desc.vs)->stageInfo;
|
|
}
|
|
if (pso->desc.hs != nullptr && pso->desc.hs->IsValid())
|
|
{
|
|
shaderStages[shaderStageCount++] = to_internal(pso->desc.hs)->stageInfo;
|
|
}
|
|
if (pso->desc.ds != nullptr && pso->desc.ds->IsValid())
|
|
{
|
|
shaderStages[shaderStageCount++] = to_internal(pso->desc.ds)->stageInfo;
|
|
}
|
|
if (pso->desc.gs != nullptr && pso->desc.gs->IsValid())
|
|
{
|
|
shaderStages[shaderStageCount++] = to_internal(pso->desc.gs)->stageInfo;
|
|
}
|
|
if (pso->desc.ps != nullptr && pso->desc.ps->IsValid())
|
|
{
|
|
shaderStages[shaderStageCount++] = to_internal(pso->desc.ps)->stageInfo;
|
|
}
|
|
pipelineInfo.stageCount = shaderStageCount;
|
|
pipelineInfo.pStages = shaderStages;
|
|
|
|
|
|
// Fixed function states:
|
|
|
|
// Primitive type:
|
|
VkPipelineInputAssemblyStateCreateInfo& inputAssembly = internal_state->inputAssembly;
|
|
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
|
switch (pso->desc.pt)
|
|
{
|
|
case PrimitiveTopology::POINTLIST:
|
|
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
|
break;
|
|
case PrimitiveTopology::LINELIST:
|
|
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
|
break;
|
|
case PrimitiveTopology::LINESTRIP:
|
|
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
|
break;
|
|
case PrimitiveTopology::TRIANGLESTRIP:
|
|
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
|
break;
|
|
case PrimitiveTopology::TRIANGLELIST:
|
|
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
|
break;
|
|
case PrimitiveTopology::PATCHLIST:
|
|
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
inputAssembly.primitiveRestartEnable = VK_FALSE;
|
|
|
|
pipelineInfo.pInputAssemblyState = &inputAssembly;
|
|
|
|
|
|
// Rasterizer:
|
|
VkPipelineRasterizationStateCreateInfo& rasterizer = internal_state->rasterizer;
|
|
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
|
rasterizer.depthClampEnable = VK_TRUE;
|
|
rasterizer.rasterizerDiscardEnable = VK_FALSE;
|
|
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
|
|
rasterizer.lineWidth = 1.0f;
|
|
rasterizer.cullMode = VK_CULL_MODE_NONE;
|
|
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
|
rasterizer.depthBiasEnable = VK_FALSE;
|
|
rasterizer.depthBiasConstantFactor = 0.0f;
|
|
rasterizer.depthBiasClamp = 0.0f;
|
|
rasterizer.depthBiasSlopeFactor = 0.0f;
|
|
|
|
// depth clip will be enabled via Vulkan 1.1 extension VK_EXT_depth_clip_enable:
|
|
VkPipelineRasterizationDepthClipStateCreateInfoEXT& depthclip = internal_state->depthclip;
|
|
depthclip.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT;
|
|
depthclip.depthClipEnable = VK_TRUE;
|
|
if (depth_clip_enable_features.depthClipEnable == VK_TRUE)
|
|
{
|
|
rasterizer.pNext = &depthclip;
|
|
}
|
|
|
|
if (pso->desc.rs != nullptr)
|
|
{
|
|
const RasterizerState& desc = *pso->desc.rs;
|
|
|
|
switch (desc.fill_mode)
|
|
{
|
|
case FillMode::WIREFRAME:
|
|
rasterizer.polygonMode = VK_POLYGON_MODE_LINE;
|
|
break;
|
|
case FillMode::SOLID:
|
|
default:
|
|
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
|
|
break;
|
|
}
|
|
|
|
switch (desc.cull_mode)
|
|
{
|
|
case CullMode::BACK:
|
|
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
|
|
break;
|
|
case CullMode::FRONT:
|
|
rasterizer.cullMode = VK_CULL_MODE_FRONT_BIT;
|
|
break;
|
|
case CullMode::NONE:
|
|
default:
|
|
rasterizer.cullMode = VK_CULL_MODE_NONE;
|
|
break;
|
|
}
|
|
|
|
rasterizer.frontFace = desc.front_counter_clockwise ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE;
|
|
rasterizer.depthBiasEnable = desc.depth_bias != 0 || desc.slope_scaled_depth_bias != 0;
|
|
rasterizer.depthBiasConstantFactor = static_cast<float>(desc.depth_bias);
|
|
rasterizer.depthBiasClamp = desc.depth_bias_clamp;
|
|
rasterizer.depthBiasSlopeFactor = desc.slope_scaled_depth_bias;
|
|
|
|
// depth clip is extension in Vulkan 1.1:
|
|
depthclip.depthClipEnable = desc.depth_clip_enable ? VK_TRUE : VK_FALSE;
|
|
}
|
|
|
|
pipelineInfo.pRasterizationState = &rasterizer;
|
|
|
|
|
|
// Viewport, Scissor:
|
|
VkViewport& viewport = internal_state->viewport;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
viewport.width = 65535;
|
|
viewport.height = 65535;
|
|
viewport.minDepth = 0;
|
|
viewport.maxDepth = 1;
|
|
|
|
VkRect2D& scissor = internal_state->scissor;
|
|
scissor.extent.width = 65535;
|
|
scissor.extent.height = 65535;
|
|
|
|
VkPipelineViewportStateCreateInfo& viewportState = internal_state->viewportState;
|
|
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
|
viewportState.viewportCount = 1;
|
|
viewportState.pViewports = &viewport;
|
|
viewportState.scissorCount = 1;
|
|
viewportState.pScissors = &scissor;
|
|
|
|
pipelineInfo.pViewportState = &viewportState;
|
|
|
|
|
|
// Depth-Stencil:
|
|
VkPipelineDepthStencilStateCreateInfo& depthstencil = internal_state->depthstencil;
|
|
depthstencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
|
if (pso->desc.dss != nullptr)
|
|
{
|
|
depthstencil.depthTestEnable = pso->desc.dss->depth_enable ? VK_TRUE : VK_FALSE;
|
|
depthstencil.depthWriteEnable = pso->desc.dss->depth_write_mask == DepthWriteMask::ZERO ? VK_FALSE : VK_TRUE;
|
|
depthstencil.depthCompareOp = _ConvertComparisonFunc(pso->desc.dss->depth_func);
|
|
|
|
if (pso->desc.dss->stencil_enable)
|
|
{
|
|
depthstencil.stencilTestEnable = VK_TRUE;
|
|
|
|
depthstencil.front.compareMask = pso->desc.dss->stencil_read_mask;
|
|
depthstencil.front.writeMask = pso->desc.dss->stencil_write_mask;
|
|
depthstencil.front.reference = 0; // runtime supplied
|
|
depthstencil.front.compareOp = _ConvertComparisonFunc(pso->desc.dss->front_face.stencil_func);
|
|
depthstencil.front.passOp = _ConvertStencilOp(pso->desc.dss->front_face.stencil_pass_op);
|
|
depthstencil.front.failOp = _ConvertStencilOp(pso->desc.dss->front_face.stencil_fail_op);
|
|
depthstencil.front.depthFailOp = _ConvertStencilOp(pso->desc.dss->front_face.stencil_depth_fail_op);
|
|
|
|
depthstencil.back.compareMask = pso->desc.dss->stencil_read_mask;
|
|
depthstencil.back.writeMask = pso->desc.dss->stencil_write_mask;
|
|
depthstencil.back.reference = 0; // runtime supplied
|
|
depthstencil.back.compareOp = _ConvertComparisonFunc(pso->desc.dss->back_face.stencil_func);
|
|
depthstencil.back.passOp = _ConvertStencilOp(pso->desc.dss->back_face.stencil_pass_op);
|
|
depthstencil.back.failOp = _ConvertStencilOp(pso->desc.dss->back_face.stencil_fail_op);
|
|
depthstencil.back.depthFailOp = _ConvertStencilOp(pso->desc.dss->back_face.stencil_depth_fail_op);
|
|
}
|
|
else
|
|
{
|
|
depthstencil.stencilTestEnable = VK_FALSE;
|
|
|
|
depthstencil.front.compareMask = 0;
|
|
depthstencil.front.writeMask = 0;
|
|
depthstencil.front.reference = 0;
|
|
depthstencil.front.compareOp = VK_COMPARE_OP_NEVER;
|
|
depthstencil.front.passOp = VK_STENCIL_OP_KEEP;
|
|
depthstencil.front.failOp = VK_STENCIL_OP_KEEP;
|
|
depthstencil.front.depthFailOp = VK_STENCIL_OP_KEEP;
|
|
|
|
depthstencil.back.compareMask = 0;
|
|
depthstencil.back.writeMask = 0;
|
|
depthstencil.back.reference = 0; // runtime supplied
|
|
depthstencil.back.compareOp = VK_COMPARE_OP_NEVER;
|
|
depthstencil.back.passOp = VK_STENCIL_OP_KEEP;
|
|
depthstencil.back.failOp = VK_STENCIL_OP_KEEP;
|
|
depthstencil.back.depthFailOp = VK_STENCIL_OP_KEEP;
|
|
}
|
|
|
|
if (CheckCapability(GraphicsDeviceCapability::DEPTH_BOUNDS_TEST))
|
|
{
|
|
depthstencil.depthBoundsTestEnable = pso->desc.dss->depth_bounds_test_enable ? VK_TRUE : VK_FALSE;
|
|
}
|
|
else
|
|
{
|
|
depthstencil.depthBoundsTestEnable = VK_FALSE;
|
|
}
|
|
}
|
|
|
|
pipelineInfo.pDepthStencilState = &depthstencil;
|
|
|
|
|
|
// Tessellation:
|
|
VkPipelineTessellationStateCreateInfo& tessellationInfo = internal_state->tessellationInfo;
|
|
tessellationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
|
|
tessellationInfo.patchControlPoints = desc->patch_control_points;
|
|
|
|
pipelineInfo.pTessellationState = &tessellationInfo;
|
|
|
|
|
|
pipelineInfo.pDynamicState = &dynamicStateInfo;
|
|
|
|
return res == VK_SUCCESS;
|
|
}
|
|
bool GraphicsDevice_Vulkan::CreateRenderPass(const RenderPassDesc* desc, RenderPass* renderpass) const
|
|
{
|
|
auto internal_state = std::make_shared<RenderPass_Vulkan>();
|
|
internal_state->allocationhandler = allocationhandler;
|
|
renderpass->internal_state = internal_state;
|
|
renderpass->desc = *desc;
|
|
|
|
renderpass->hash = 0;
|
|
wi::helper::hash_combine(renderpass->hash, desc->attachments.size());
|
|
for (auto& attachment : desc->attachments)
|
|
{
|
|
if (attachment.type == RenderPassAttachment::Type::RENDERTARGET || attachment.type == RenderPassAttachment::Type::DEPTH_STENCIL)
|
|
{
|
|
wi::helper::hash_combine(renderpass->hash, attachment.texture.desc.format);
|
|
wi::helper::hash_combine(renderpass->hash, attachment.texture.desc.sample_count);
|
|
}
|
|
}
|
|
|
|
VkResult res;
|
|
|
|
VkImageView attachments[18] = {};
|
|
VkAttachmentDescription2 attachmentDescriptions[18] = {};
|
|
VkAttachmentReference2 colorAttachmentRefs[8] = {};
|
|
VkAttachmentReference2 resolveAttachmentRefs[8] = {};
|
|
VkAttachmentReference2 shadingRateAttachmentRef = {};
|
|
VkAttachmentReference2 depthAttachmentRef = {};
|
|
VkSubpassDescriptionDepthStencilResolve depthResolve = {};
|
|
VkAttachmentReference2 depthResolveAttachmentRef = {};
|
|
|
|
VkFragmentShadingRateAttachmentInfoKHR shading_rate_attachment = {};
|
|
shading_rate_attachment.sType = VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR;
|
|
shading_rate_attachment.pFragmentShadingRateAttachment = &shadingRateAttachmentRef;
|
|
shading_rate_attachment.shadingRateAttachmentTexelSize.width = VARIABLE_RATE_SHADING_TILE_SIZE;
|
|
shading_rate_attachment.shadingRateAttachmentTexelSize.height = VARIABLE_RATE_SHADING_TILE_SIZE;
|
|
|
|
int resolvecount = 0;
|
|
|
|
VkSubpassDescription2 subpass = {};
|
|
subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2;
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
const void** subpass_chain = &subpass.pNext;
|
|
|
|
uint32_t validAttachmentCount = 0;
|
|
for (auto& attachment : renderpass->desc.attachments)
|
|
{
|
|
const Texture* texture = &attachment.texture;
|
|
const TextureDesc& texdesc = texture->desc;
|
|
int subresource = attachment.subresource;
|
|
auto texture_internal_state = to_internal(texture);
|
|
|
|
attachmentDescriptions[validAttachmentCount].sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
|
|
attachmentDescriptions[validAttachmentCount].format = _ConvertFormat(texdesc.format);
|
|
attachmentDescriptions[validAttachmentCount].samples = (VkSampleCountFlagBits)texdesc.sample_count;
|
|
|
|
switch (attachment.loadop)
|
|
{
|
|
default:
|
|
case RenderPassAttachment::LoadOp::LOAD:
|
|
attachmentDescriptions[validAttachmentCount].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
|
break;
|
|
case RenderPassAttachment::LoadOp::CLEAR:
|
|
attachmentDescriptions[validAttachmentCount].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
break;
|
|
case RenderPassAttachment::LoadOp::DONTCARE:
|
|
attachmentDescriptions[validAttachmentCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
break;
|
|
}
|
|
|
|
switch (attachment.storeop)
|
|
{
|
|
default:
|
|
case RenderPassAttachment::StoreOp::STORE:
|
|
attachmentDescriptions[validAttachmentCount].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
break;
|
|
case RenderPassAttachment::StoreOp::DONTCARE:
|
|
attachmentDescriptions[validAttachmentCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
break;
|
|
}
|
|
|
|
attachmentDescriptions[validAttachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachmentDescriptions[validAttachmentCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
|
|
attachmentDescriptions[validAttachmentCount].initialLayout = _ConvertImageLayout(attachment.initial_layout);
|
|
attachmentDescriptions[validAttachmentCount].finalLayout = _ConvertImageLayout(attachment.final_layout);
|
|
|
|
if (attachment.type == RenderPassAttachment::Type::RENDERTARGET)
|
|
{
|
|
if (subresource < 0 || texture_internal_state->subresources_rtv.empty())
|
|
{
|
|
attachments[validAttachmentCount] = texture_internal_state->rtv;
|
|
}
|
|
else
|
|
{
|
|
assert(texture_internal_state->subresources_rtv.size() > size_t(subresource) && "Invalid RTV subresource!");
|
|
attachments[validAttachmentCount] = texture_internal_state->subresources_rtv[subresource];
|
|
}
|
|
if (attachments[validAttachmentCount] == VK_NULL_HANDLE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
colorAttachmentRefs[subpass.colorAttachmentCount].sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
|
|
colorAttachmentRefs[subpass.colorAttachmentCount].attachment = validAttachmentCount;
|
|
colorAttachmentRefs[subpass.colorAttachmentCount].layout = _ConvertImageLayout(attachment.subpass_layout);
|
|
colorAttachmentRefs[subpass.colorAttachmentCount].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
subpass.colorAttachmentCount++;
|
|
subpass.pColorAttachments = colorAttachmentRefs;
|
|
}
|
|
else if (attachment.type == RenderPassAttachment::Type::DEPTH_STENCIL)
|
|
{
|
|
if (subresource < 0 || texture_internal_state->subresources_dsv.empty())
|
|
{
|
|
attachments[validAttachmentCount] = texture_internal_state->dsv;
|
|
}
|
|
else
|
|
{
|
|
assert(texture_internal_state->subresources_dsv.size() > size_t(subresource) && "Invalid DSV subresource!");
|
|
attachments[validAttachmentCount] = texture_internal_state->subresources_dsv[subresource];
|
|
}
|
|
if (attachments[validAttachmentCount] == VK_NULL_HANDLE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
depthAttachmentRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
|
|
depthAttachmentRef.attachment = validAttachmentCount;
|
|
depthAttachmentRef.layout = _ConvertImageLayout(attachment.subpass_layout);
|
|
depthAttachmentRef.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
subpass.pDepthStencilAttachment = &depthAttachmentRef;
|
|
|
|
if (IsFormatStencilSupport(texdesc.format))
|
|
{
|
|
depthAttachmentRef.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
switch (attachment.loadop)
|
|
{
|
|
default:
|
|
case RenderPassAttachment::LoadOp::LOAD:
|
|
attachmentDescriptions[validAttachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
|
break;
|
|
case RenderPassAttachment::LoadOp::CLEAR:
|
|
attachmentDescriptions[validAttachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
break;
|
|
case RenderPassAttachment::LoadOp::DONTCARE:
|
|
attachmentDescriptions[validAttachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
break;
|
|
}
|
|
|
|
switch (attachment.storeop)
|
|
{
|
|
default:
|
|
case RenderPassAttachment::StoreOp::STORE:
|
|
attachmentDescriptions[validAttachmentCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
break;
|
|
case RenderPassAttachment::StoreOp::DONTCARE:
|
|
attachmentDescriptions[validAttachmentCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (attachment.type == RenderPassAttachment::Type::RESOLVE)
|
|
{
|
|
resolveAttachmentRefs[resolvecount].sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
|
|
|
|
if (!attachment.texture.IsValid())
|
|
{
|
|
resolveAttachmentRefs[resolvecount].attachment = VK_ATTACHMENT_UNUSED;
|
|
}
|
|
else
|
|
{
|
|
if (subresource < 0 || texture_internal_state->subresources_srv.empty())
|
|
{
|
|
attachments[validAttachmentCount] = texture_internal_state->srv.image_view;
|
|
}
|
|
else
|
|
{
|
|
assert(texture_internal_state->subresources_srv.size() > size_t(subresource) && "Invalid SRV subresource!");
|
|
attachments[validAttachmentCount] = texture_internal_state->subresources_srv[subresource].image_view;
|
|
}
|
|
if (attachments[validAttachmentCount] == VK_NULL_HANDLE)
|
|
{
|
|
continue;
|
|
}
|
|
resolveAttachmentRefs[resolvecount].attachment = validAttachmentCount;
|
|
resolveAttachmentRefs[resolvecount].layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
resolveAttachmentRefs[resolvecount].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
}
|
|
|
|
resolvecount++;
|
|
subpass.pResolveAttachments = resolveAttachmentRefs;
|
|
}
|
|
else if (attachment.type == RenderPassAttachment::Type::RESOLVE_DEPTH)
|
|
{
|
|
|
|
if (!attachment.texture.IsValid())
|
|
{
|
|
resolveAttachmentRefs[resolvecount].attachment = VK_ATTACHMENT_UNUSED;
|
|
}
|
|
else
|
|
{
|
|
if (subresource < 0 || texture_internal_state->subresources_srv.empty())
|
|
{
|
|
attachments[validAttachmentCount] = texture_internal_state->srv.image_view;
|
|
}
|
|
else
|
|
{
|
|
assert(texture_internal_state->subresources_srv.size() > size_t(subresource) && "Invalid SRV subresource!");
|
|
attachments[validAttachmentCount] = texture_internal_state->subresources_srv[subresource].image_view;
|
|
}
|
|
if (attachments[validAttachmentCount] == VK_NULL_HANDLE)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
depthResolve.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE;
|
|
|
|
switch (attachment.depth_resolve_mode)
|
|
{
|
|
default:
|
|
case RenderPassAttachment::DepthResolveMode::Min:
|
|
depthResolve.depthResolveMode = VK_RESOLVE_MODE_MIN_BIT;
|
|
depthResolve.stencilResolveMode = VK_RESOLVE_MODE_MIN_BIT;
|
|
break;
|
|
case RenderPassAttachment::DepthResolveMode::Max:
|
|
depthResolve.depthResolveMode = VK_RESOLVE_MODE_MAX_BIT;
|
|
depthResolve.stencilResolveMode = VK_RESOLVE_MODE_MAX_BIT;
|
|
break;
|
|
}
|
|
|
|
depthResolveAttachmentRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
|
|
depthResolveAttachmentRef.attachment = validAttachmentCount;
|
|
depthResolveAttachmentRef.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
if (IsFormatStencilSupport(attachment.texture.desc.format))
|
|
{
|
|
depthResolveAttachmentRef.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
}
|
|
depthResolveAttachmentRef.layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
depthResolve.pDepthStencilResolveAttachment = &depthResolveAttachmentRef;
|
|
|
|
*subpass_chain = &depthResolve;
|
|
subpass_chain = &depthResolve.pNext;
|
|
}
|
|
else if (attachment.type == RenderPassAttachment::Type::SHADING_RATE_SOURCE && CheckCapability(GraphicsDeviceCapability::VARIABLE_RATE_SHADING_TIER2))
|
|
{
|
|
shadingRateAttachmentRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
|
|
|
|
if (!attachment.texture.IsValid())
|
|
{
|
|
shadingRateAttachmentRef.attachment = VK_ATTACHMENT_UNUSED;
|
|
}
|
|
else
|
|
{
|
|
if (subresource < 0 || texture_internal_state->subresources_uav.empty())
|
|
{
|
|
attachments[validAttachmentCount] = texture_internal_state->uav.image_view;
|
|
}
|
|
else
|
|
{
|
|
assert(texture_internal_state->subresources_uav.size() > size_t(subresource) && "Invalid UAV subresource!");
|
|
attachments[validAttachmentCount] = texture_internal_state->subresources_uav[subresource].image_view;
|
|
}
|
|
if (attachments[validAttachmentCount] == VK_NULL_HANDLE)
|
|
{
|
|
continue;
|
|
}
|
|
shadingRateAttachmentRef.attachment = validAttachmentCount;
|
|
shadingRateAttachmentRef.layout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
|
|
shadingRateAttachmentRef.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
}
|
|
|
|
*subpass_chain = &shading_rate_attachment;
|
|
subpass_chain = &shading_rate_attachment.pNext;
|
|
}
|
|
|
|
validAttachmentCount++;
|
|
}
|
|
assert(renderpass->desc.attachments.size() == validAttachmentCount);
|
|
|
|
VkRenderPassCreateInfo2 renderPassInfo = {};
|
|
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2;
|
|
renderPassInfo.attachmentCount = validAttachmentCount;
|
|
renderPassInfo.pAttachments = attachmentDescriptions;
|
|
renderPassInfo.subpassCount = 1;
|
|
renderPassInfo.pSubpasses = &subpass;
|
|
|
|
res = vkCreateRenderPass2(device, &renderPassInfo, nullptr, &internal_state->renderpass);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
VkFramebufferCreateInfo framebufferInfo = {};
|
|
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
|
framebufferInfo.renderPass = internal_state->renderpass;
|
|
framebufferInfo.attachmentCount = validAttachmentCount;
|
|
|
|
if (validAttachmentCount > 0)
|
|
{
|
|
const TextureDesc& texdesc = renderpass->desc.attachments[0].texture.desc;
|
|
auto texture_internal = to_internal(&renderpass->desc.attachments[0].texture);
|
|
framebufferInfo.pAttachments = attachments;
|
|
framebufferInfo.width = texdesc.width;
|
|
framebufferInfo.height = texdesc.height;
|
|
if (renderpass->desc.attachments[0].subresource >= 0)
|
|
{
|
|
framebufferInfo.layers = texture_internal->subresources_framebuffer_layercount[0];
|
|
}
|
|
else
|
|
{
|
|
framebufferInfo.layers = texture_internal->framebuffer_layercount;
|
|
}
|
|
framebufferInfo.layers = std::min(framebufferInfo.layers, texdesc.array_size);
|
|
}
|
|
else
|
|
{
|
|
framebufferInfo.pAttachments = nullptr;
|
|
framebufferInfo.width = properties2.properties.limits.maxFramebufferWidth;
|
|
framebufferInfo.height = properties2.properties.limits.maxFramebufferHeight;
|
|
framebufferInfo.layers = properties2.properties.limits.maxFramebufferLayers;
|
|
}
|
|
|
|
res = vkCreateFramebuffer(device, &framebufferInfo, nullptr, &internal_state->framebuffer);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
|
|
internal_state->beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
|
internal_state->beginInfo.renderPass = internal_state->renderpass;
|
|
internal_state->beginInfo.framebuffer = internal_state->framebuffer;
|
|
internal_state->beginInfo.renderArea.offset = { 0, 0 };
|
|
internal_state->beginInfo.renderArea.extent.width = framebufferInfo.width;
|
|
internal_state->beginInfo.renderArea.extent.height = framebufferInfo.height;
|
|
|
|
if (validAttachmentCount > 0)
|
|
{
|
|
internal_state->beginInfo.clearValueCount = validAttachmentCount;
|
|
internal_state->beginInfo.pClearValues = internal_state->clearColors;
|
|
|
|
int i = 0;
|
|
for (auto& attachment : renderpass->desc.attachments)
|
|
{
|
|
if (renderpass->desc.attachments[i].type == RenderPassAttachment::Type::RESOLVE ||
|
|
renderpass->desc.attachments[i].type == RenderPassAttachment::Type::SHADING_RATE_SOURCE ||
|
|
!attachment.texture.IsValid())
|
|
continue;
|
|
|
|
const ClearValue& clear = renderpass->desc.attachments[i].texture.desc.clear;
|
|
if (renderpass->desc.attachments[i].type == RenderPassAttachment::Type::RENDERTARGET)
|
|
{
|
|
internal_state->clearColors[i].color.float32[0] = clear.color[0];
|
|
internal_state->clearColors[i].color.float32[1] = clear.color[1];
|
|
internal_state->clearColors[i].color.float32[2] = clear.color[2];
|
|
internal_state->clearColors[i].color.float32[3] = clear.color[3];
|
|
}
|
|
else if (renderpass->desc.attachments[i].type == RenderPassAttachment::Type::DEPTH_STENCIL)
|
|
{
|
|
internal_state->clearColors[i].depthStencil.depth = clear.depth_stencil.depth;
|
|
internal_state->clearColors[i].depthStencil.stencil = clear.depth_stencil.stencil;
|
|
}
|
|
else
|
|
{
|
|
assert(0);
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return res == VK_SUCCESS;
|
|
}
|
|
bool GraphicsDevice_Vulkan::CreateRaytracingAccelerationStructure(const RaytracingAccelerationStructureDesc* desc, RaytracingAccelerationStructure* bvh) const
|
|
{
|
|
auto internal_state = std::make_shared<BVH_Vulkan>();
|
|
internal_state->allocationhandler = allocationhandler;
|
|
bvh->internal_state = internal_state;
|
|
bvh->type = GPUResource::Type::RAYTRACING_ACCELERATION_STRUCTURE;
|
|
bvh->desc = *desc;
|
|
|
|
internal_state->buildInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;
|
|
internal_state->buildInfo.flags = 0;
|
|
if (bvh->desc.flags & RaytracingAccelerationStructureDesc::FLAG_ALLOW_UPDATE)
|
|
{
|
|
internal_state->buildInfo.flags |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR;
|
|
}
|
|
if (bvh->desc.flags & RaytracingAccelerationStructureDesc::FLAG_ALLOW_COMPACTION)
|
|
{
|
|
internal_state->buildInfo.flags |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR;
|
|
}
|
|
if (bvh->desc.flags & RaytracingAccelerationStructureDesc::FLAG_PREFER_FAST_TRACE)
|
|
{
|
|
internal_state->buildInfo.flags |= VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
|
|
}
|
|
if (bvh->desc.flags & RaytracingAccelerationStructureDesc::FLAG_PREFER_FAST_BUILD)
|
|
{
|
|
internal_state->buildInfo.flags |= VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR;
|
|
}
|
|
if (bvh->desc.flags & RaytracingAccelerationStructureDesc::FLAG_MINIMIZE_MEMORY)
|
|
{
|
|
internal_state->buildInfo.flags |= VK_BUILD_ACCELERATION_STRUCTURE_LOW_MEMORY_BIT_KHR;
|
|
}
|
|
|
|
switch (desc->type)
|
|
{
|
|
case RaytracingAccelerationStructureDesc::Type::BOTTOMLEVEL:
|
|
{
|
|
internal_state->buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
|
|
|
|
for (auto& x : desc->bottom_level.geometries)
|
|
{
|
|
internal_state->geometries.emplace_back();
|
|
auto& geometry = internal_state->geometries.back();
|
|
geometry = {};
|
|
geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
|
|
|
|
internal_state->primitiveCounts.emplace_back();
|
|
uint32_t& primitiveCount = internal_state->primitiveCounts.back();
|
|
|
|
if (x.type == RaytracingAccelerationStructureDesc::BottomLevel::Geometry::Type::TRIANGLES)
|
|
{
|
|
geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
|
|
geometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;
|
|
geometry.geometry.triangles.indexType = x.triangles.index_format == IndexBufferFormat::UINT16 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32;
|
|
geometry.geometry.triangles.maxVertex = x.triangles.vertex_count;
|
|
geometry.geometry.triangles.vertexStride = x.triangles.vertex_stride;
|
|
geometry.geometry.triangles.vertexFormat = _ConvertFormat(x.triangles.vertex_format);
|
|
|
|
primitiveCount = x.triangles.index_count / 3;
|
|
}
|
|
else if (x.type == RaytracingAccelerationStructureDesc::BottomLevel::Geometry::Type::PROCEDURAL_AABBS)
|
|
{
|
|
geometry.geometryType = VK_GEOMETRY_TYPE_AABBS_KHR;
|
|
geometry.geometry.aabbs.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR;
|
|
geometry.geometry.aabbs.stride = sizeof(float) * 6; // min - max corners
|
|
|
|
primitiveCount = x.aabbs.count;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
break;
|
|
case RaytracingAccelerationStructureDesc::Type::TOPLEVEL:
|
|
{
|
|
internal_state->buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
|
|
|
|
internal_state->geometries.emplace_back();
|
|
auto& geometry = internal_state->geometries.back();
|
|
geometry = {};
|
|
geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
|
|
geometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
|
|
geometry.geometry.instances.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR;
|
|
geometry.geometry.instances.arrayOfPointers = VK_FALSE;
|
|
|
|
internal_state->primitiveCounts.emplace_back();
|
|
uint32_t& primitiveCount = internal_state->primitiveCounts.back();
|
|
primitiveCount = desc->top_level.count;
|
|
}
|
|
break;
|
|
}
|
|
|
|
internal_state->buildInfo.geometryCount = (uint32_t)internal_state->geometries.size();
|
|
internal_state->buildInfo.pGeometries = internal_state->geometries.data();
|
|
|
|
internal_state->sizeInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR;
|
|
|
|
// Compute memory requirements:
|
|
vkGetAccelerationStructureBuildSizesKHR(
|
|
device,
|
|
VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR,
|
|
&internal_state->buildInfo,
|
|
internal_state->primitiveCounts.data(),
|
|
&internal_state->sizeInfo
|
|
);
|
|
|
|
// Backing memory as buffer:
|
|
VkBufferCreateInfo bufferInfo = {};
|
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bufferInfo.size = internal_state->sizeInfo.accelerationStructureSize +
|
|
std::max(internal_state->sizeInfo.buildScratchSize, internal_state->sizeInfo.updateScratchSize);
|
|
bufferInfo.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR;
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; // scratch
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
|
bufferInfo.flags = 0;
|
|
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
VmaAllocationCreateInfo allocInfo = {};
|
|
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
|
|
|
VkResult res = vmaCreateBuffer(
|
|
allocationhandler->allocator,
|
|
&bufferInfo,
|
|
&allocInfo,
|
|
&internal_state->buffer,
|
|
&internal_state->allocation,
|
|
nullptr
|
|
);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// Create the acceleration structure:
|
|
internal_state->createInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR;
|
|
internal_state->createInfo.type = internal_state->buildInfo.type;
|
|
internal_state->createInfo.buffer = internal_state->buffer;
|
|
internal_state->createInfo.size = internal_state->sizeInfo.accelerationStructureSize;
|
|
|
|
res = vkCreateAccelerationStructureKHR(
|
|
device,
|
|
&internal_state->createInfo,
|
|
nullptr,
|
|
&internal_state->resource
|
|
);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
// Get the device address for the acceleration structure:
|
|
VkAccelerationStructureDeviceAddressInfoKHR addrinfo = {};
|
|
addrinfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR;
|
|
addrinfo.accelerationStructure = internal_state->resource;
|
|
internal_state->as_address = vkGetAccelerationStructureDeviceAddressKHR(device, &addrinfo);
|
|
|
|
// Get scratch address:
|
|
VkBufferDeviceAddressInfo addressinfo = {};
|
|
addressinfo.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO;
|
|
addressinfo.buffer = internal_state->buffer;
|
|
internal_state->scratch_address = vkGetBufferDeviceAddress(device, &addressinfo)
|
|
+ internal_state->sizeInfo.accelerationStructureSize;
|
|
|
|
|
|
if (desc->type == RaytracingAccelerationStructureDesc::Type::TOPLEVEL)
|
|
{
|
|
internal_state->index = allocationhandler->bindlessAccelerationStructures.allocate();
|
|
if (internal_state->index >= 0)
|
|
{
|
|
VkWriteDescriptorSetAccelerationStructureKHR acc = {};
|
|
acc.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR;
|
|
acc.accelerationStructureCount = 1;
|
|
acc.pAccelerationStructures = &internal_state->resource;
|
|
|
|
VkWriteDescriptorSet write = {};
|
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
write.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
|
|
write.dstBinding = 0;
|
|
write.dstArrayElement = internal_state->index;
|
|
write.descriptorCount = 1;
|
|
write.dstSet = allocationhandler->bindlessAccelerationStructures.descriptorSet;
|
|
write.pNext = &acc;
|
|
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
buildAccelerationStructuresKHR(
|
|
device,
|
|
VK_NULL_HANDLE,
|
|
1,
|
|
&info,
|
|
&pRangeInfo
|
|
);
|
|
#endif
|
|
|
|
return res == VK_SUCCESS;
|
|
}
|
|
bool GraphicsDevice_Vulkan::CreateRaytracingPipelineState(const RaytracingPipelineStateDesc* desc, RaytracingPipelineState* rtpso) const
|
|
{
|
|
auto internal_state = std::make_shared<RTPipelineState_Vulkan>();
|
|
internal_state->allocationhandler = allocationhandler;
|
|
rtpso->internal_state = internal_state;
|
|
rtpso->desc = *desc;
|
|
|
|
VkRayTracingPipelineCreateInfoKHR info = {};
|
|
info.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR;
|
|
info.flags = 0;
|
|
|
|
wi::vector<VkPipelineShaderStageCreateInfo> stages;
|
|
for (auto& x : desc->shader_libraries)
|
|
{
|
|
stages.emplace_back();
|
|
auto& stage = stages.back();
|
|
stage = {};
|
|
stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
stage.module = to_internal(x.shader)->shaderModule;
|
|
switch (x.type)
|
|
{
|
|
default:
|
|
case ShaderLibrary::Type::RAYGENERATION:
|
|
stage.stage = VK_SHADER_STAGE_RAYGEN_BIT_KHR;
|
|
break;
|
|
case ShaderLibrary::Type::MISS:
|
|
stage.stage = VK_SHADER_STAGE_MISS_BIT_KHR;
|
|
break;
|
|
case ShaderLibrary::Type::CLOSESTHIT:
|
|
stage.stage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
|
|
break;
|
|
case ShaderLibrary::Type::ANYHIT:
|
|
stage.stage = VK_SHADER_STAGE_ANY_HIT_BIT_KHR;
|
|
break;
|
|
case ShaderLibrary::Type::INTERSECTION:
|
|
stage.stage = VK_SHADER_STAGE_INTERSECTION_BIT_KHR;
|
|
break;
|
|
}
|
|
stage.pName = x.function_name.c_str();
|
|
}
|
|
info.stageCount = (uint32_t)stages.size();
|
|
info.pStages = stages.data();
|
|
|
|
wi::vector<VkRayTracingShaderGroupCreateInfoKHR> groups;
|
|
groups.reserve(desc->hit_groups.size());
|
|
for (auto& x : desc->hit_groups)
|
|
{
|
|
groups.emplace_back();
|
|
auto& group = groups.back();
|
|
group = {};
|
|
group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR;
|
|
switch (x.type)
|
|
{
|
|
default:
|
|
case ShaderHitGroup::Type::GENERAL:
|
|
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
|
|
break;
|
|
case ShaderHitGroup::Type::TRIANGLES:
|
|
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR;
|
|
break;
|
|
case ShaderHitGroup::Type::PROCEDURAL:
|
|
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR;
|
|
break;
|
|
}
|
|
group.generalShader = x.general_shader;
|
|
group.closestHitShader = x.closest_hit_shader;
|
|
group.anyHitShader = x.any_hit_shader;
|
|
group.intersectionShader = x.intersection_shader;
|
|
}
|
|
info.groupCount = (uint32_t)groups.size();
|
|
info.pGroups = groups.data();
|
|
|
|
info.maxPipelineRayRecursionDepth = desc->max_trace_recursion_depth;
|
|
|
|
info.layout = to_internal(desc->shader_libraries.front().shader)->pipelineLayout_cs;
|
|
|
|
//VkRayTracingPipelineInterfaceCreateInfoKHR library_interface = {};
|
|
//library_interface.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_INTERFACE_CREATE_INFO_KHR;
|
|
//library_interface.maxPipelineRayPayloadSize = pDesc->max_payload_size_in_bytes;
|
|
//library_interface.maxPipelineRayHitAttributeSize = pDesc->max_attribute_size_in_bytes;
|
|
//info.pLibraryInterface = &library_interface;
|
|
|
|
info.basePipelineHandle = VK_NULL_HANDLE;
|
|
info.basePipelineIndex = 0;
|
|
|
|
VkResult res = vkCreateRayTracingPipelinesKHR(
|
|
device,
|
|
VK_NULL_HANDLE,
|
|
VK_NULL_HANDLE,
|
|
1,
|
|
&info,
|
|
nullptr,
|
|
&internal_state->pipeline
|
|
);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
return res == VK_SUCCESS;
|
|
}
|
|
|
|
int GraphicsDevice_Vulkan::CreateSubresource(Texture* texture, SubresourceType type, uint32_t firstSlice, uint32_t sliceCount, uint32_t firstMip, uint32_t mipCount, const Format* format_change) const
|
|
{
|
|
auto internal_state = to_internal(texture);
|
|
|
|
Format format = texture->GetDesc().format;
|
|
if (format_change != nullptr)
|
|
{
|
|
format = *format_change;
|
|
}
|
|
|
|
VkImageViewCreateInfo view_desc = {};
|
|
view_desc.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
view_desc.flags = 0;
|
|
view_desc.image = internal_state->resource;
|
|
view_desc.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
view_desc.subresourceRange.baseArrayLayer = firstSlice;
|
|
view_desc.subresourceRange.layerCount = sliceCount;
|
|
view_desc.subresourceRange.baseMipLevel = firstMip;
|
|
view_desc.subresourceRange.levelCount = mipCount;
|
|
view_desc.format = _ConvertFormat(format);
|
|
|
|
if (texture->desc.type == TextureDesc::Type::TEXTURE_1D)
|
|
{
|
|
if (texture->desc.array_size > 1)
|
|
{
|
|
view_desc.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
|
|
}
|
|
else
|
|
{
|
|
view_desc.viewType = VK_IMAGE_VIEW_TYPE_1D;
|
|
}
|
|
}
|
|
else if (texture->desc.type == TextureDesc::Type::TEXTURE_2D)
|
|
{
|
|
if (texture->desc.array_size > 1)
|
|
{
|
|
if (has_flag(texture->desc.misc_flags, ResourceMiscFlag::TEXTURECUBE))
|
|
{
|
|
if (texture->desc.array_size > 6 && sliceCount > 6)
|
|
{
|
|
view_desc.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
|
|
}
|
|
else
|
|
{
|
|
view_desc.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
view_desc.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
view_desc.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
}
|
|
}
|
|
else if (texture->desc.type == TextureDesc::Type::TEXTURE_3D)
|
|
{
|
|
view_desc.viewType = VK_IMAGE_VIEW_TYPE_3D;
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case SubresourceType::SRV:
|
|
{
|
|
if (IsFormatDepthSupport(format))
|
|
{
|
|
view_desc.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
}
|
|
|
|
// This is required in cases where image was created with eg. USAGE_STORAGE, but
|
|
// the view format that we are creating doesn't support USAGE_STORAGE (for examplle: SRGB formats)
|
|
VkImageViewUsageCreateInfo viewUsageInfo = {};
|
|
viewUsageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO;
|
|
viewUsageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
view_desc.pNext = &viewUsageInfo;
|
|
|
|
Texture_Vulkan::TextureSubresource subresource;
|
|
VkResult res = vkCreateImageView(device, &view_desc, nullptr, &subresource.image_view);
|
|
|
|
subresource.index = allocationhandler->bindlessSampledImages.allocate();
|
|
if (subresource.index >= 0)
|
|
{
|
|
VkDescriptorImageInfo imageInfo = {};
|
|
imageInfo.imageView = subresource.image_view;
|
|
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
VkWriteDescriptorSet write = {};
|
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
|
write.dstBinding = 0;
|
|
write.dstArrayElement = subresource.index;
|
|
write.descriptorCount = 1;
|
|
write.dstSet = allocationhandler->bindlessSampledImages.descriptorSet;
|
|
write.pImageInfo = &imageInfo;
|
|
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
|
}
|
|
|
|
if (res == VK_SUCCESS)
|
|
{
|
|
if (!internal_state->srv.IsValid())
|
|
{
|
|
internal_state->srv = subresource;
|
|
return -1;
|
|
}
|
|
internal_state->subresources_srv.push_back(subresource);
|
|
return int(internal_state->subresources_srv.size() - 1);
|
|
}
|
|
else
|
|
{
|
|
assert(0);
|
|
}
|
|
}
|
|
break;
|
|
case SubresourceType::UAV:
|
|
{
|
|
if (view_desc.viewType == VK_IMAGE_VIEW_TYPE_CUBE || view_desc.viewType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
|
|
{
|
|
view_desc.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
|
}
|
|
|
|
Texture_Vulkan::TextureSubresource subresource;
|
|
VkResult res = vkCreateImageView(device, &view_desc, nullptr, &subresource.image_view);
|
|
|
|
subresource.index = allocationhandler->bindlessStorageImages.allocate();
|
|
if (subresource.index >= 0)
|
|
{
|
|
VkDescriptorImageInfo imageInfo = {};
|
|
imageInfo.imageView = subresource.image_view;
|
|
imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
VkWriteDescriptorSet write = {};
|
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
write.dstBinding = 0;
|
|
write.dstArrayElement = subresource.index;
|
|
write.descriptorCount = 1;
|
|
write.dstSet = allocationhandler->bindlessStorageImages.descriptorSet;
|
|
write.pImageInfo = &imageInfo;
|
|
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
|
}
|
|
|
|
if (res == VK_SUCCESS)
|
|
{
|
|
if (!internal_state->uav.IsValid())
|
|
{
|
|
internal_state->uav = subresource;
|
|
return -1;
|
|
}
|
|
internal_state->subresources_uav.push_back(subresource);
|
|
return int(internal_state->subresources_uav.size() - 1);
|
|
}
|
|
else
|
|
{
|
|
assert(0);
|
|
}
|
|
}
|
|
break;
|
|
case SubresourceType::RTV:
|
|
{
|
|
VkImageView rtv;
|
|
view_desc.subresourceRange.levelCount = 1;
|
|
VkResult res = vkCreateImageView(device, &view_desc, nullptr, &rtv);
|
|
|
|
if (res == VK_SUCCESS)
|
|
{
|
|
if (internal_state->rtv == VK_NULL_HANDLE)
|
|
{
|
|
internal_state->rtv = rtv;
|
|
internal_state->framebuffer_layercount = view_desc.subresourceRange.layerCount;
|
|
return -1;
|
|
}
|
|
internal_state->subresources_rtv.push_back(rtv);
|
|
internal_state->subresources_framebuffer_layercount.push_back(view_desc.subresourceRange.layerCount);
|
|
return int(internal_state->subresources_rtv.size() - 1);
|
|
}
|
|
else
|
|
{
|
|
assert(0);
|
|
}
|
|
}
|
|
break;
|
|
case SubresourceType::DSV:
|
|
{
|
|
view_desc.subresourceRange.levelCount = 1;
|
|
view_desc.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
|
|
VkImageView dsv;
|
|
VkResult res = vkCreateImageView(device, &view_desc, nullptr, &dsv);
|
|
|
|
if (res == VK_SUCCESS)
|
|
{
|
|
if (internal_state->dsv == VK_NULL_HANDLE)
|
|
{
|
|
internal_state->dsv = dsv;
|
|
internal_state->framebuffer_layercount = view_desc.subresourceRange.layerCount;
|
|
return -1;
|
|
}
|
|
internal_state->subresources_dsv.push_back(dsv);
|
|
internal_state->subresources_framebuffer_layercount.push_back(view_desc.subresourceRange.layerCount);
|
|
return int(internal_state->subresources_dsv.size() - 1);
|
|
}
|
|
else
|
|
{
|
|
assert(0);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
int GraphicsDevice_Vulkan::CreateSubresource(GPUBuffer* buffer, SubresourceType type, uint64_t offset, uint64_t size, const Format* format_change) const
|
|
{
|
|
auto internal_state = to_internal(buffer);
|
|
const GPUBufferDesc& desc = buffer->GetDesc();
|
|
VkResult res;
|
|
|
|
Buffer_Vulkan::BufferSubresource subresource;
|
|
|
|
Format format = desc.format;
|
|
if (format_change != nullptr)
|
|
{
|
|
format = *format_change;
|
|
}
|
|
if (type == SubresourceType::UAV)
|
|
{
|
|
// RW resource can't be SRGB
|
|
format = GetFormatNonSRGB(format);
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
|
|
case SubresourceType::SRV:
|
|
case SubresourceType::UAV:
|
|
{
|
|
if (format == Format::UNKNOWN)
|
|
{
|
|
// Raw buffer
|
|
subresource.index = allocationhandler->bindlessStorageBuffers.allocate();
|
|
subresource.is_typed = false;
|
|
if (subresource.IsValid())
|
|
{
|
|
subresource.buffer_info.buffer = internal_state->resource;
|
|
subresource.buffer_info.offset = offset;
|
|
subresource.buffer_info.range = size;
|
|
|
|
VkWriteDescriptorSet write = {};
|
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
write.dstBinding = 0;
|
|
write.dstArrayElement = subresource.index;
|
|
write.descriptorCount = 1;
|
|
write.dstSet = allocationhandler->bindlessStorageBuffers.descriptorSet;
|
|
write.pBufferInfo = &subresource.buffer_info;
|
|
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
|
}
|
|
|
|
if (type == SubresourceType::SRV)
|
|
{
|
|
if (!internal_state->srv.IsValid())
|
|
{
|
|
internal_state->srv = subresource;
|
|
return -1;
|
|
}
|
|
internal_state->subresources_srv.push_back(subresource);
|
|
return int(internal_state->subresources_srv.size() - 1);
|
|
}
|
|
else
|
|
{
|
|
if (!internal_state->uav.IsValid())
|
|
{
|
|
internal_state->uav = subresource;
|
|
return -1;
|
|
}
|
|
internal_state->subresources_uav.push_back(subresource);
|
|
return int(internal_state->subresources_uav.size() - 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Typed buffer
|
|
VkBufferViewCreateInfo srv_desc = {};
|
|
srv_desc.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
|
|
srv_desc.buffer = internal_state->resource;
|
|
srv_desc.flags = 0;
|
|
srv_desc.format = _ConvertFormat(format);
|
|
srv_desc.offset = offset;
|
|
srv_desc.range = std::min(size, (uint64_t)desc.size - srv_desc.offset);
|
|
|
|
res = vkCreateBufferView(device, &srv_desc, nullptr, &subresource.buffer_view);
|
|
|
|
if (res == VK_SUCCESS)
|
|
{
|
|
subresource.is_typed = true;
|
|
|
|
if (type == SubresourceType::SRV)
|
|
{
|
|
subresource.index = allocationhandler->bindlessUniformTexelBuffers.allocate();
|
|
if (subresource.IsValid())
|
|
{
|
|
VkWriteDescriptorSet write = {};
|
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
|
|
write.dstBinding = 0;
|
|
write.dstArrayElement = subresource.index;
|
|
write.descriptorCount = 1;
|
|
write.dstSet = allocationhandler->bindlessUniformTexelBuffers.descriptorSet;
|
|
write.pTexelBufferView = &subresource.buffer_view;
|
|
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
|
}
|
|
|
|
if (!internal_state->srv.IsValid())
|
|
{
|
|
internal_state->srv = subresource;
|
|
return -1;
|
|
}
|
|
internal_state->subresources_srv.push_back(subresource);
|
|
return int(internal_state->subresources_srv.size() - 1);
|
|
}
|
|
else
|
|
{
|
|
subresource.index = allocationhandler->bindlessStorageTexelBuffers.allocate();
|
|
if (subresource.IsValid())
|
|
{
|
|
VkWriteDescriptorSet write = {};
|
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
|
|
write.dstBinding = 0;
|
|
write.dstArrayElement = subresource.index;
|
|
write.descriptorCount = 1;
|
|
write.dstSet = allocationhandler->bindlessStorageTexelBuffers.descriptorSet;
|
|
write.pTexelBufferView = &subresource.buffer_view;
|
|
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
|
}
|
|
|
|
if (!internal_state->uav.IsValid())
|
|
{
|
|
internal_state->uav = subresource;
|
|
return -1;
|
|
}
|
|
internal_state->subresources_uav.push_back(subresource);
|
|
return int(internal_state->subresources_uav.size() - 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int GraphicsDevice_Vulkan::GetDescriptorIndex(const GPUResource* resource, SubresourceType type, int subresource) const
|
|
{
|
|
if (resource == nullptr || !resource->IsValid())
|
|
return -1;
|
|
|
|
switch (type)
|
|
{
|
|
default:
|
|
case SubresourceType::SRV:
|
|
if (resource->IsBuffer())
|
|
{
|
|
auto internal_state = to_internal((const GPUBuffer*)resource);
|
|
if (subresource < 0)
|
|
{
|
|
return internal_state->srv.index;
|
|
}
|
|
else
|
|
{
|
|
return internal_state->subresources_srv[subresource].index;
|
|
}
|
|
}
|
|
else if(resource->IsTexture())
|
|
{
|
|
auto internal_state = to_internal((const Texture*)resource);
|
|
if (subresource < 0)
|
|
{
|
|
return internal_state->srv.index;
|
|
}
|
|
else
|
|
{
|
|
return internal_state->subresources_srv[subresource].index;
|
|
}
|
|
}
|
|
else if (resource->IsAccelerationStructure())
|
|
{
|
|
auto internal_state = to_internal((const RaytracingAccelerationStructure*)resource);
|
|
return internal_state->index;
|
|
}
|
|
break;
|
|
case SubresourceType::UAV:
|
|
if (resource->IsBuffer())
|
|
{
|
|
auto internal_state = to_internal((const GPUBuffer*)resource);
|
|
if (subresource < 0)
|
|
{
|
|
return internal_state->uav.index;
|
|
}
|
|
else
|
|
{
|
|
return internal_state->subresources_uav[subresource].index;
|
|
}
|
|
}
|
|
else if (resource->IsTexture())
|
|
{
|
|
auto internal_state = to_internal((const Texture*)resource);
|
|
if (subresource < 0)
|
|
{
|
|
return internal_state->uav.index;
|
|
}
|
|
else
|
|
{
|
|
return internal_state->subresources_uav[subresource].index;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
int GraphicsDevice_Vulkan::GetDescriptorIndex(const Sampler* sampler) const
|
|
{
|
|
if (sampler == nullptr || !sampler->IsValid())
|
|
return -1;
|
|
|
|
auto internal_state = to_internal(sampler);
|
|
return internal_state->index;
|
|
}
|
|
|
|
void GraphicsDevice_Vulkan::WriteShadingRateValue(ShadingRate rate, void* dest) const
|
|
{
|
|
// How to compute shading rate value texel data:
|
|
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#primsrast-fragment-shading-rate-attachment
|
|
|
|
switch (rate)
|
|
{
|
|
default:
|
|
case ShadingRate::RATE_1X1:
|
|
*(uint8_t*)dest = 0;
|
|
break;
|
|
case ShadingRate::RATE_1X2:
|
|
*(uint8_t*)dest = 0x1;
|
|
break;
|
|
case ShadingRate::RATE_2X1:
|
|
*(uint8_t*)dest = 0x4;
|
|
break;
|
|
case ShadingRate::RATE_2X2:
|
|
*(uint8_t*)dest = 0x5;
|
|
break;
|
|
case ShadingRate::RATE_2X4:
|
|
*(uint8_t*)dest = 0x6;
|
|
break;
|
|
case ShadingRate::RATE_4X2:
|
|
*(uint8_t*)dest = 0x9;
|
|
break;
|
|
case ShadingRate::RATE_4X4:
|
|
*(uint8_t*)dest = 0xa;
|
|
break;
|
|
}
|
|
|
|
}
|
|
void GraphicsDevice_Vulkan::WriteTopLevelAccelerationStructureInstance(const RaytracingAccelerationStructureDesc::TopLevel::Instance* instance, void* dest) const
|
|
{
|
|
VkAccelerationStructureInstanceKHR tmp;
|
|
tmp.transform = *(VkTransformMatrixKHR*)&instance->transform;
|
|
tmp.instanceCustomIndex = instance->instance_id;
|
|
tmp.mask = instance->instance_mask;
|
|
tmp.instanceShaderBindingTableRecordOffset = instance->instance_contribution_to_hit_group_index;
|
|
tmp.flags = instance->flags;
|
|
|
|
assert(instance->bottom_level->IsAccelerationStructure());
|
|
auto internal_state = to_internal((RaytracingAccelerationStructure*)instance->bottom_level);
|
|
tmp.accelerationStructureReference = internal_state->as_address;
|
|
|
|
std::memcpy(dest, &tmp, sizeof(VkAccelerationStructureInstanceKHR)); // memcpy whole structure into mapped pointer to avoid read from uncached memory
|
|
}
|
|
void GraphicsDevice_Vulkan::WriteShaderIdentifier(const RaytracingPipelineState* rtpso, uint32_t group_index, void* dest) const
|
|
{
|
|
VkResult res = vkGetRayTracingShaderGroupHandlesKHR(device, to_internal(rtpso)->pipeline, group_index, 1, SHADER_IDENTIFIER_SIZE, dest);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
|
|
void GraphicsDevice_Vulkan::SetName(GPUResource* pResource, const char* name)
|
|
{
|
|
if (debugUtils)
|
|
{
|
|
VkDebugUtilsObjectNameInfoEXT info { VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT };
|
|
info.pObjectName = name;
|
|
if (pResource->IsTexture())
|
|
{
|
|
info.objectType = VK_OBJECT_TYPE_IMAGE;
|
|
info.objectHandle = (uint64_t)to_internal((const Texture*)pResource)->resource;
|
|
}
|
|
else if (pResource->IsBuffer())
|
|
{
|
|
info.objectType = VK_OBJECT_TYPE_BUFFER;
|
|
info.objectHandle = (uint64_t)to_internal((const GPUBuffer*)pResource)->resource;
|
|
}
|
|
else if (pResource->IsAccelerationStructure())
|
|
{
|
|
info.objectType = VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR;
|
|
info.objectHandle = (uint64_t)to_internal((const RaytracingAccelerationStructure*)pResource)->resource;
|
|
}
|
|
|
|
if (info.objectHandle == (uint64_t)VK_NULL_HANDLE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
VkResult res = vkSetDebugUtilsObjectNameEXT(device, &info);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
}
|
|
|
|
CommandList GraphicsDevice_Vulkan::BeginCommandList(QUEUE_TYPE queue)
|
|
{
|
|
VkResult res;
|
|
|
|
cmd_locker.lock();
|
|
uint32_t cmd_current = cmd_count++;
|
|
if (cmd_current >= commandlists.size())
|
|
{
|
|
commandlists.push_back(std::make_unique<CommandList_Vulkan>());
|
|
}
|
|
CommandList cmd;
|
|
cmd.internal_state = commandlists[cmd_current].get();
|
|
cmd_locker.unlock();
|
|
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
commandlist.reset(GetBufferIndex());
|
|
commandlist.queue = queue;
|
|
commandlist.id = cmd_current;
|
|
commandlist.waited_on.store(false);
|
|
|
|
if (commandlist.GetCommandBuffer() == VK_NULL_HANDLE)
|
|
{
|
|
// need to create one more command list:
|
|
|
|
for (uint32_t buffer = 0; buffer < BUFFERCOUNT; ++buffer)
|
|
{
|
|
VkCommandPoolCreateInfo poolInfo = {};
|
|
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
switch (queue)
|
|
{
|
|
case wi::graphics::QUEUE_GRAPHICS:
|
|
poolInfo.queueFamilyIndex = graphicsFamily;
|
|
break;
|
|
case wi::graphics::QUEUE_COMPUTE:
|
|
poolInfo.queueFamilyIndex = computeFamily;
|
|
break;
|
|
case wi::graphics::QUEUE_COPY:
|
|
poolInfo.queueFamilyIndex = copyFamily;
|
|
break;
|
|
default:
|
|
assert(0); // queue type not handled
|
|
break;
|
|
}
|
|
poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
|
|
|
res = vkCreateCommandPool(device, &poolInfo, nullptr, &commandlist.commandPools[buffer][queue]);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
VkCommandBufferAllocateInfo commandBufferInfo = {};
|
|
commandBufferInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
commandBufferInfo.commandBufferCount = 1;
|
|
commandBufferInfo.commandPool = commandlist.commandPools[buffer][queue];
|
|
commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
|
|
res = vkAllocateCommandBuffers(device, &commandBufferInfo, &commandlist.commandBuffers[buffer][queue]);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
commandlist.binder_pools[buffer].init(this);
|
|
}
|
|
|
|
commandlist.binder.init(this);
|
|
}
|
|
|
|
res = vkResetCommandPool(device, commandlist.GetCommandPool(), 0);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
VkCommandBufferBeginInfo beginInfo = {};
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
beginInfo.pInheritanceInfo = nullptr; // Optional
|
|
|
|
res = vkBeginCommandBuffer(commandlist.GetCommandBuffer(), &beginInfo);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
if (queue == QUEUE_GRAPHICS)
|
|
{
|
|
VkRect2D scissors[16];
|
|
for (int i = 0; i < arraysize(scissors); ++i)
|
|
{
|
|
scissors[i].offset.x = 0;
|
|
scissors[i].offset.y = 0;
|
|
scissors[i].extent.width = 65535;
|
|
scissors[i].extent.height = 65535;
|
|
}
|
|
vkCmdSetScissor(commandlist.GetCommandBuffer(), 0, arraysize(scissors), scissors);
|
|
|
|
float blendConstants[] = { 1,1,1,1 };
|
|
vkCmdSetBlendConstants(commandlist.GetCommandBuffer(), blendConstants);
|
|
|
|
vkCmdSetStencilReference(commandlist.GetCommandBuffer(), VK_STENCIL_FRONT_AND_BACK, ~0u);
|
|
|
|
if (features2.features.depthBounds == VK_TRUE)
|
|
{
|
|
vkCmdSetDepthBounds(commandlist.GetCommandBuffer(), 0.0f, 1.0f);
|
|
}
|
|
}
|
|
|
|
return cmd;
|
|
}
|
|
void GraphicsDevice_Vulkan::SubmitCommandLists()
|
|
{
|
|
initLocker.lock();
|
|
VkResult res;
|
|
|
|
// Submit current frame:
|
|
{
|
|
auto& frame = GetFrameResources();
|
|
|
|
// Transitions:
|
|
if(submit_inits)
|
|
{
|
|
submit_inits = false;
|
|
res = vkEndCommandBuffer(frame.initCommandBuffer);
|
|
assert(res == VK_SUCCESS);
|
|
queues[QUEUE_GRAPHICS].submit_cmds.push_back(frame.initCommandBuffer); // can only be submitted on graphics queue
|
|
}
|
|
|
|
uint64_t copy_sync = copyAllocator.flush();
|
|
if (copy_sync > 0) // sync up with copyallocator before first submit
|
|
{
|
|
for (int q = 0; q < QUEUE_COUNT; ++q)
|
|
{
|
|
CommandQueue& queue = queues[q];
|
|
queue.submit_waitStages.push_back(VK_PIPELINE_STAGE_TRANSFER_BIT);
|
|
queue.submit_waitSemaphores.push_back(copyAllocator.semaphore);
|
|
queue.submit_waitValues.push_back(copy_sync);
|
|
}
|
|
copy_sync = 0;
|
|
}
|
|
|
|
uint32_t cmd_last = cmd_count;
|
|
cmd_count = 0;
|
|
for (uint32_t cmd = 0; cmd < cmd_last; ++cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = *commandlists[cmd].get();
|
|
res = vkEndCommandBuffer(commandlist.GetCommandBuffer());
|
|
assert(res == VK_SUCCESS);
|
|
|
|
CommandQueue& queue = queues[commandlist.queue];
|
|
queue.submit_cmds.push_back(commandlist.GetCommandBuffer());
|
|
|
|
queue.swapchain_updates = commandlist.prev_swapchains;
|
|
for (auto& swapchain : commandlist.prev_swapchains)
|
|
{
|
|
auto internal_state = to_internal(&swapchain);
|
|
|
|
queue.submit_swapchains.push_back(internal_state->swapChain);
|
|
queue.submit_swapChainImageIndices.push_back(internal_state->swapChainImageIndex);
|
|
queue.submit_waitStages.push_back(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
|
|
queue.submit_waitSemaphores.push_back(internal_state->swapchainAcquireSemaphore);
|
|
queue.submit_waitValues.push_back(0); // not a timeline semaphore
|
|
queue.submit_signalSemaphores.push_back(internal_state->swapchainReleaseSemaphore);
|
|
queue.submit_signalValues.push_back(0); // not a timeline semaphore
|
|
}
|
|
|
|
if (commandlist.waited_on.load() || !commandlist.waits.empty())
|
|
{
|
|
for (auto& wait : commandlist.waits)
|
|
{
|
|
// Wait for command list dependency:
|
|
CommandList_Vulkan& waitcommandlist = GetCommandList(wait);
|
|
queue.submit_waitStages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
|
|
queue.submit_waitSemaphores.push_back(queues[waitcommandlist.queue].semaphore);
|
|
queue.submit_waitValues.push_back(FRAMECOUNT * commandlists.size() + (uint64_t)waitcommandlist.id);
|
|
}
|
|
|
|
if (commandlist.waited_on.load())
|
|
{
|
|
// Signal this command list's completion:
|
|
queue.submit_signalSemaphores.push_back(queue.semaphore);
|
|
queue.submit_signalValues.push_back(FRAMECOUNT * commandlists.size() + (uint64_t)commandlist.id);
|
|
}
|
|
|
|
queue.submit(this, VK_NULL_HANDLE);
|
|
}
|
|
|
|
for (auto& x : commandlist.pipelines_worker)
|
|
{
|
|
if (pipelines_global.count(x.first) == 0)
|
|
{
|
|
pipelines_global[x.first] = x.second;
|
|
}
|
|
else
|
|
{
|
|
allocationhandler->destroylocker.lock();
|
|
allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, FRAMECOUNT));
|
|
allocationhandler->destroylocker.unlock();
|
|
}
|
|
}
|
|
commandlist.pipelines_worker.clear();
|
|
}
|
|
|
|
// final submits with fences:
|
|
for (int q = 0; q < QUEUE_COUNT; ++q)
|
|
{
|
|
queues[q].submit(this, frame.fence[q]);
|
|
}
|
|
}
|
|
|
|
// From here, we begin a new frame, this affects GetBufferIndex()!
|
|
FRAMECOUNT++;
|
|
|
|
// Begin next frame:
|
|
{
|
|
auto& frame = GetFrameResources();
|
|
|
|
// Initiate stalling CPU when GPU is not yet finished with next frame:
|
|
if (FRAMECOUNT >= BUFFERCOUNT)
|
|
{
|
|
for (int queue = 0; queue < QUEUE_COUNT; ++queue)
|
|
{
|
|
res = vkWaitForFences(device, 1, &frame.fence[queue], true, 0xFFFFFFFFFFFFFFFF);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
res = vkResetFences(device, 1, &frame.fence[queue]);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
}
|
|
|
|
allocationhandler->Update(FRAMECOUNT, BUFFERCOUNT);
|
|
|
|
// Restart transition command buffers:
|
|
{
|
|
res = vkResetCommandPool(device, frame.initCommandPool, 0);
|
|
assert(res == VK_SUCCESS);
|
|
|
|
VkCommandBufferBeginInfo beginInfo = {};
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
beginInfo.pInheritanceInfo = nullptr; // Optional
|
|
|
|
res = vkBeginCommandBuffer(frame.initCommandBuffer, &beginInfo);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
}
|
|
|
|
submit_inits = false;
|
|
initLocker.unlock();
|
|
}
|
|
|
|
void GraphicsDevice_Vulkan::WaitForGPU() const
|
|
{
|
|
VkResult res = vkDeviceWaitIdle(device);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
void GraphicsDevice_Vulkan::ClearPipelineStateCache()
|
|
{
|
|
allocationhandler->destroylocker.lock();
|
|
|
|
pso_layout_cache_mutex.lock();
|
|
for (auto& x : pso_layout_cache)
|
|
{
|
|
if (x.second.pipelineLayout) allocationhandler->destroyer_pipelineLayouts.push_back(std::make_pair(x.second.pipelineLayout, FRAMECOUNT));
|
|
if (x.second.descriptorSetLayout) allocationhandler->destroyer_descriptorSetLayouts.push_back(std::make_pair(x.second.descriptorSetLayout, FRAMECOUNT));
|
|
}
|
|
pso_layout_cache.clear();
|
|
pso_layout_cache_mutex.unlock();
|
|
|
|
for (auto& x : pipelines_global)
|
|
{
|
|
allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, FRAMECOUNT));
|
|
}
|
|
pipelines_global.clear();
|
|
|
|
for (auto& x : commandlists)
|
|
{
|
|
for (auto& y : x->pipelines_worker)
|
|
{
|
|
allocationhandler->destroyer_pipelines.push_back(std::make_pair(y.second, FRAMECOUNT));
|
|
}
|
|
x->pipelines_worker.clear();
|
|
}
|
|
allocationhandler->destroylocker.unlock();
|
|
|
|
// Destroy Vulkan pipeline cache
|
|
vkDestroyPipelineCache(device, pipelineCache, nullptr);
|
|
pipelineCache = VK_NULL_HANDLE;
|
|
|
|
VkPipelineCacheCreateInfo createInfo{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
|
|
createInfo.initialDataSize = 0;
|
|
createInfo.pInitialData = nullptr;
|
|
|
|
// Create Vulkan pipeline cache
|
|
VkResult res = vkCreatePipelineCache(device, &createInfo, nullptr, &pipelineCache);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
|
|
Texture GraphicsDevice_Vulkan::GetBackBuffer(const SwapChain* swapchain) const
|
|
{
|
|
auto swapchain_internal = to_internal(swapchain);
|
|
|
|
auto internal_state = std::make_shared<Texture_Vulkan>();
|
|
internal_state->resource = swapchain_internal->swapChainImages[swapchain_internal->swapChainImageIndex];
|
|
|
|
Texture result;
|
|
result.type = GPUResource::Type::TEXTURE;
|
|
result.internal_state = internal_state;
|
|
result.desc.type = TextureDesc::Type::TEXTURE_2D;
|
|
result.desc.width = swapchain_internal->swapChainExtent.width;
|
|
result.desc.height = swapchain_internal->swapChainExtent.height;
|
|
result.desc.format = swapchain->desc.format;
|
|
return result;
|
|
}
|
|
|
|
ColorSpace GraphicsDevice_Vulkan::GetSwapChainColorSpace(const SwapChain* swapchain) const
|
|
{
|
|
auto internal_state = to_internal(swapchain);
|
|
return internal_state->colorSpace;
|
|
}
|
|
bool GraphicsDevice_Vulkan::IsSwapChainSupportsHDR(const SwapChain* swapchain) const
|
|
{
|
|
auto internal_state = to_internal(swapchain);
|
|
|
|
uint32_t formatCount;
|
|
VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, internal_state->surface, &formatCount, nullptr);
|
|
if (res == VK_SUCCESS)
|
|
{
|
|
wi::vector<VkSurfaceFormatKHR> swapchain_formats(formatCount);
|
|
res = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, internal_state->surface, &formatCount, swapchain_formats.data());
|
|
if (res == VK_SUCCESS)
|
|
{
|
|
for (const auto& format : swapchain_formats)
|
|
{
|
|
if (format.colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void GraphicsDevice_Vulkan::SparseUpdate(QUEUE_TYPE queue, const SparseUpdateCommand* commands, uint32_t command_count)
|
|
{
|
|
thread_local wi::vector<VkBindSparseInfo> sparse_infos;
|
|
struct DataPerBind
|
|
{
|
|
VkSparseBufferMemoryBindInfo buffer_bind_info;
|
|
VkSparseImageOpaqueMemoryBindInfo image_opaque_bind_info;
|
|
VkSparseImageMemoryBindInfo image_bind_info;
|
|
wi::vector<VkSparseMemoryBind> memory_binds;
|
|
wi::vector<VkSparseImageMemoryBind> image_memory_binds;
|
|
};
|
|
thread_local wi::vector<DataPerBind> sparse_binds;
|
|
|
|
sparse_infos.resize(command_count);
|
|
sparse_binds.resize(command_count);
|
|
|
|
for (uint32_t i = 0; i < command_count; ++i)
|
|
{
|
|
const SparseUpdateCommand& in_command = commands[i];
|
|
VkBindSparseInfo& out_info = sparse_infos[i];
|
|
out_info = {};
|
|
out_info.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO;
|
|
|
|
DataPerBind& out_bind = sparse_binds[i];
|
|
|
|
VkDeviceMemory tile_pool_memory = VK_NULL_HANDLE;
|
|
VkDeviceSize tile_pool_offset = 0;
|
|
if (in_command.tile_pool != nullptr)
|
|
{
|
|
auto internal_tile_pool = to_internal(in_command.tile_pool);
|
|
tile_pool_memory = internal_tile_pool->allocation->GetMemory();
|
|
tile_pool_offset = internal_tile_pool->allocation->GetOffset();
|
|
}
|
|
|
|
out_bind.memory_binds.clear();
|
|
out_bind.image_memory_binds.clear();
|
|
|
|
out_bind.memory_binds.reserve(in_command.num_resource_regions);
|
|
out_bind.image_memory_binds.reserve(in_command.num_resource_regions);
|
|
|
|
const VkSparseMemoryBind* memory_bind_ptr = out_bind.memory_binds.data();
|
|
const VkSparseImageMemoryBind* image_memory_bind_ptr = out_bind.image_memory_binds.data();
|
|
|
|
if (in_command.sparse_resource->IsBuffer())
|
|
{
|
|
auto internal_sparse = to_internal((const GPUBuffer*)in_command.sparse_resource);
|
|
|
|
VkSparseBufferMemoryBindInfo& info = out_bind.buffer_bind_info;
|
|
info = {};
|
|
info.buffer = internal_sparse->resource;
|
|
info.pBinds = memory_bind_ptr;
|
|
info.bindCount = in_command.num_resource_regions;
|
|
memory_bind_ptr += in_command.num_resource_regions;
|
|
|
|
for (uint32_t j = 0; j < in_command.num_resource_regions; ++j)
|
|
{
|
|
const SparseResourceCoordinate& in_coordinate = in_command.coordinates[j];
|
|
const SparseRegionSize& in_size = in_command.sizes[j];
|
|
|
|
const TileRangeFlags& in_flags = in_command.range_flags[j];
|
|
uint32_t in_offset = in_command.range_start_offsets[j];
|
|
uint32_t in_tile_count = in_command.range_tile_counts[j];
|
|
VkSparseMemoryBind& out_memory_bind = out_bind.memory_binds.emplace_back();
|
|
out_memory_bind = {};
|
|
out_memory_bind.resourceOffset = in_coordinate.x * in_command.sparse_resource->sparse_page_size;
|
|
out_memory_bind.size = in_tile_count * in_command.sparse_resource->sparse_page_size;
|
|
if (in_flags == TileRangeFlags::Null)
|
|
{
|
|
out_memory_bind.memory = VK_NULL_HANDLE;
|
|
}
|
|
else
|
|
{
|
|
out_memory_bind.memory = tile_pool_memory;
|
|
out_memory_bind.memoryOffset = tile_pool_offset + in_offset * in_command.sparse_resource->sparse_page_size;
|
|
}
|
|
}
|
|
|
|
if (out_info.bufferBindCount > 0)
|
|
{
|
|
out_info.pBufferBinds = &out_bind.buffer_bind_info;
|
|
out_info.bufferBindCount = 1;
|
|
}
|
|
}
|
|
else if (in_command.sparse_resource->IsTexture())
|
|
{
|
|
const Texture* sparse_texture = (const Texture*)in_command.sparse_resource;
|
|
const TextureDesc& texture_desc = sparse_texture->GetDesc();
|
|
auto internal_sparse = to_internal(sparse_texture);
|
|
|
|
VkImageAspectFlags aspectMask = {};
|
|
if (IsFormatDepthSupport(texture_desc.format))
|
|
{
|
|
aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
if (IsFormatStencilSupport(texture_desc.format))
|
|
{
|
|
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
}
|
|
}
|
|
if (has_flag(texture_desc.bind_flags, BindFlag::RENDER_TARGET) ||
|
|
has_flag(texture_desc.bind_flags, BindFlag::SHADER_RESOURCE) ||
|
|
has_flag(texture_desc.bind_flags, BindFlag::UNORDERED_ACCESS))
|
|
{
|
|
aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT;
|
|
}
|
|
|
|
VkSparseImageOpaqueMemoryBindInfo& opaque_info = out_bind.image_opaque_bind_info;
|
|
opaque_info = {};
|
|
opaque_info.image = internal_sparse->resource;
|
|
opaque_info.pBinds = memory_bind_ptr;
|
|
opaque_info.bindCount = 0;
|
|
|
|
VkSparseImageMemoryBindInfo& info = out_bind.image_bind_info;
|
|
info = {};
|
|
info.image = internal_sparse->resource;
|
|
info.pBinds = image_memory_bind_ptr;
|
|
info.bindCount = 0;
|
|
|
|
for (uint32_t j = 0; j < in_command.num_resource_regions; ++j)
|
|
{
|
|
const SparseResourceCoordinate& in_coordinate = in_command.coordinates[j];
|
|
const SparseRegionSize& in_size = in_command.sizes[j];
|
|
const bool is_miptail = in_coordinate.mip >= internal_sparse->sparse_texture_properties.packed_mip_start;
|
|
|
|
if (is_miptail)
|
|
{
|
|
opaque_info.bindCount++;
|
|
memory_bind_ptr++;
|
|
|
|
const TileRangeFlags& in_flags = in_command.range_flags[j];
|
|
uint32_t in_offset = in_command.range_start_offsets[j];
|
|
uint32_t in_tile_count = in_command.range_tile_counts[j];
|
|
VkSparseMemoryBind& out_memory_bind = out_bind.memory_binds.emplace_back();
|
|
out_memory_bind = {};
|
|
out_memory_bind.resourceOffset = internal_sparse->sparse_texture_properties.packed_mip_tile_offset * sparse_texture->sparse_page_size;
|
|
out_memory_bind.size = in_tile_count * in_command.sparse_resource->sparse_page_size;
|
|
if (in_flags == TileRangeFlags::Null)
|
|
{
|
|
out_memory_bind.memory = VK_NULL_HANDLE;
|
|
}
|
|
else
|
|
{
|
|
out_memory_bind.memory = tile_pool_memory;
|
|
out_memory_bind.memoryOffset = tile_pool_offset + in_offset * in_command.sparse_resource->sparse_page_size;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
info.bindCount++;
|
|
image_memory_bind_ptr++;
|
|
|
|
const TileRangeFlags& in_flags = in_command.range_flags[j];
|
|
uint32_t in_offset = in_command.range_start_offsets[j];
|
|
uint32_t in_tile_count = in_command.range_tile_counts[j];
|
|
VkSparseImageMemoryBind& out_image_memory_bind = out_bind.image_memory_binds.emplace_back();
|
|
out_image_memory_bind = {};
|
|
if (in_flags == TileRangeFlags::Null)
|
|
{
|
|
out_image_memory_bind.memory = VK_NULL_HANDLE;
|
|
}
|
|
else
|
|
{
|
|
out_image_memory_bind.memory = tile_pool_memory;
|
|
out_image_memory_bind.memoryOffset = tile_pool_offset + in_offset * in_command.sparse_resource->sparse_page_size;
|
|
}
|
|
out_image_memory_bind.subresource.mipLevel = in_coordinate.mip;
|
|
out_image_memory_bind.subresource.arrayLayer = in_coordinate.slice;
|
|
out_image_memory_bind.subresource.aspectMask = aspectMask;
|
|
out_image_memory_bind.offset.x = in_coordinate.x * internal_sparse->sparse_texture_properties.tile_width;
|
|
out_image_memory_bind.offset.y = in_coordinate.y * internal_sparse->sparse_texture_properties.tile_height;
|
|
out_image_memory_bind.offset.z = in_coordinate.z * internal_sparse->sparse_texture_properties.tile_depth;
|
|
out_image_memory_bind.extent.width = std::min(texture_desc.width, in_size.width * internal_sparse->sparse_texture_properties.tile_width);
|
|
out_image_memory_bind.extent.height = std::min(texture_desc.height, in_size.height * internal_sparse->sparse_texture_properties.tile_height);
|
|
out_image_memory_bind.extent.depth = std::min(texture_desc.depth, in_size.depth * internal_sparse->sparse_texture_properties.tile_depth);
|
|
}
|
|
|
|
}
|
|
|
|
if (opaque_info.bindCount > 0)
|
|
{
|
|
out_info.pImageOpaqueBinds = &out_bind.image_opaque_bind_info;
|
|
out_info.imageOpaqueBindCount = 1;
|
|
}
|
|
if (info.bindCount > 0)
|
|
{
|
|
out_info.pImageBinds = &out_bind.image_bind_info;
|
|
out_info.imageBindCount = 1;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Queue command:
|
|
{
|
|
CommandQueue& q = queues[queue];
|
|
std::scoped_lock lock(q.sparse_mutex);
|
|
assert(q.sparse_binding_supported);
|
|
|
|
VkResult res = vkQueueBindSparse(q.queue, (uint32_t)sparse_infos.size(), sparse_infos.data(), VK_NULL_HANDLE);
|
|
assert(res == VK_SUCCESS);
|
|
}
|
|
}
|
|
|
|
void GraphicsDevice_Vulkan::WaitCommandList(CommandList cmd, CommandList wait_for)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
CommandList_Vulkan& commandlist_wait_for = GetCommandList(wait_for);
|
|
assert(commandlist_wait_for.id < commandlist.id); // can't wait for future command list!
|
|
commandlist.waits.push_back(wait_for);
|
|
commandlist_wait_for.waited_on.store(true);
|
|
}
|
|
void GraphicsDevice_Vulkan::RenderPassBegin(const SwapChain* swapchain, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
|
|
auto internal_state = to_internal(swapchain);
|
|
commandlist.active_renderpass = &internal_state->renderpass;
|
|
commandlist.prev_swapchains.push_back(*swapchain);
|
|
|
|
internal_state->locker.lock();
|
|
VkResult res = vkAcquireNextImageKHR(
|
|
device,
|
|
internal_state->swapChain,
|
|
0xFFFFFFFFFFFFFFFF,
|
|
internal_state->swapchainAcquireSemaphore,
|
|
VK_NULL_HANDLE,
|
|
&internal_state->swapChainImageIndex
|
|
);
|
|
internal_state->locker.unlock();
|
|
|
|
if (res != VK_SUCCESS)
|
|
{
|
|
// Handle outdated error in acquire:
|
|
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
|
{
|
|
if (CreateSwapChainInternal(internal_state, physicalDevice, device, allocationhandler))
|
|
{
|
|
RenderPassBegin(swapchain, cmd);
|
|
return;
|
|
}
|
|
}
|
|
assert(0);
|
|
}
|
|
|
|
VkClearValue clearColor = {
|
|
swapchain->desc.clear_color[0],
|
|
swapchain->desc.clear_color[1],
|
|
swapchain->desc.clear_color[2],
|
|
swapchain->desc.clear_color[3],
|
|
};
|
|
VkRenderPassBeginInfo renderPassInfo = {};
|
|
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
|
renderPassInfo.renderPass = to_internal(&internal_state->renderpass)->renderpass;
|
|
renderPassInfo.framebuffer = internal_state->swapChainFramebuffers[internal_state->swapChainImageIndex];
|
|
renderPassInfo.renderArea.offset = { 0, 0 };
|
|
renderPassInfo.renderArea.extent = internal_state->swapChainExtent;
|
|
renderPassInfo.clearValueCount = 1;
|
|
renderPassInfo.pClearValues = &clearColor;
|
|
vkCmdBeginRenderPass(commandlist.GetCommandBuffer(), &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
|
|
|
}
|
|
void GraphicsDevice_Vulkan::RenderPassBegin(const RenderPass* renderpass, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
commandlist.active_renderpass = renderpass;
|
|
|
|
auto internal_state = to_internal(renderpass);
|
|
vkCmdBeginRenderPass(commandlist.GetCommandBuffer(), &internal_state->beginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
|
}
|
|
void GraphicsDevice_Vulkan::RenderPassEnd(CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdEndRenderPass(commandlist.GetCommandBuffer());
|
|
|
|
commandlist.active_renderpass = nullptr;
|
|
}
|
|
void GraphicsDevice_Vulkan::BindScissorRects(uint32_t numRects, const Rect* rects, CommandList cmd)
|
|
{
|
|
assert(rects != nullptr);
|
|
VkRect2D scissors[16];
|
|
assert(numRects < arraysize(scissors));
|
|
assert(numRects < properties2.properties.limits.maxViewports);
|
|
for(uint32_t i = 0; i < numRects; ++i)
|
|
{
|
|
scissors[i].extent.width = abs(rects[i].right - rects[i].left);
|
|
scissors[i].extent.height = abs(rects[i].top - rects[i].bottom);
|
|
scissors[i].offset.x = std::max(0, rects[i].left);
|
|
scissors[i].offset.y = std::max(0, rects[i].top);
|
|
}
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdSetScissor(commandlist.GetCommandBuffer(), 0, numRects, scissors);
|
|
}
|
|
void GraphicsDevice_Vulkan::BindViewports(uint32_t NumViewports, const Viewport* pViewports, CommandList cmd)
|
|
{
|
|
assert(pViewports != nullptr);
|
|
VkViewport vp[16];
|
|
assert(NumViewports < arraysize(vp));
|
|
assert(NumViewports < properties2.properties.limits.maxViewports);
|
|
for (uint32_t i = 0; i < NumViewports; ++i)
|
|
{
|
|
vp[i].x = pViewports[i].top_left_x;
|
|
vp[i].y = pViewports[i].top_left_y + pViewports[i].height;
|
|
vp[i].width = pViewports[i].width;
|
|
vp[i].height = -pViewports[i].height;
|
|
vp[i].minDepth = pViewports[i].min_depth;
|
|
vp[i].maxDepth = pViewports[i].max_depth;
|
|
}
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdSetViewport(commandlist.GetCommandBuffer(), 0, NumViewports, vp);
|
|
}
|
|
void GraphicsDevice_Vulkan::BindResource(const GPUResource* resource, uint32_t slot, CommandList cmd, int subresource)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
assert(slot < DESCRIPTORBINDER_SRV_COUNT);
|
|
auto& binder = commandlist.binder;
|
|
if (binder.table.SRV[slot].internal_state != resource->internal_state || binder.table.SRV_index[slot] != subresource)
|
|
{
|
|
binder.table.SRV[slot] = *resource;
|
|
binder.table.SRV_index[slot] = subresource;
|
|
binder.dirty |= DescriptorBinder::DIRTY_DESCRIPTOR;
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::BindResources(const GPUResource *const* resources, uint32_t slot, uint32_t count, CommandList cmd)
|
|
{
|
|
if (resources != nullptr)
|
|
{
|
|
for (uint32_t i = 0; i < count; ++i)
|
|
{
|
|
BindResource(resources[i], slot + i, cmd, -1);
|
|
}
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::BindUAV(const GPUResource* resource, uint32_t slot, CommandList cmd, int subresource)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
assert(slot < DESCRIPTORBINDER_UAV_COUNT);
|
|
auto& binder = commandlist.binder;
|
|
if (binder.table.UAV[slot].internal_state != resource->internal_state || binder.table.UAV_index[slot] != subresource)
|
|
{
|
|
binder.table.UAV[slot] = *resource;
|
|
binder.table.UAV_index[slot] = subresource;
|
|
binder.dirty |= DescriptorBinder::DIRTY_DESCRIPTOR;
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::BindUAVs(const GPUResource *const* resources, uint32_t slot, uint32_t count, CommandList cmd)
|
|
{
|
|
if (resources != nullptr)
|
|
{
|
|
for (uint32_t i = 0; i < count; ++i)
|
|
{
|
|
BindUAV(resources[i], slot + i, cmd, -1);
|
|
}
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::BindSampler(const Sampler* sampler, uint32_t slot, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
assert(slot < DESCRIPTORBINDER_SAMPLER_COUNT);
|
|
auto& binder = commandlist.binder;
|
|
if (binder.table.SAM[slot].internal_state != sampler->internal_state)
|
|
{
|
|
binder.table.SAM[slot] = *sampler;
|
|
binder.dirty |= DescriptorBinder::DIRTY_DESCRIPTOR;
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::BindConstantBuffer(const GPUBuffer* buffer, uint32_t slot, CommandList cmd, uint64_t offset)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
assert(slot < DESCRIPTORBINDER_CBV_COUNT);
|
|
auto& binder = commandlist.binder;
|
|
|
|
if (binder.table.CBV[slot].internal_state != buffer->internal_state)
|
|
{
|
|
binder.table.CBV[slot] = *buffer;
|
|
binder.dirty |= DescriptorBinder::DIRTY_DESCRIPTOR;
|
|
}
|
|
|
|
if (binder.table.CBV_offset[slot] != offset)
|
|
{
|
|
binder.table.CBV_offset[slot] = offset;
|
|
binder.dirty |= DescriptorBinder::DIRTY_OFFSET;
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::BindVertexBuffers(const GPUBuffer *const* vertexBuffers, uint32_t slot, uint32_t count, const uint32_t* strides, const uint64_t* offsets, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
size_t hash = 0;
|
|
|
|
VkDeviceSize voffsets[8] = {};
|
|
VkBuffer vbuffers[8] = {};
|
|
assert(count <= 8);
|
|
for (uint32_t i = 0; i < count; ++i)
|
|
{
|
|
wi::helper::hash_combine(hash, strides[i]);
|
|
commandlist.vb_strides[i] = strides[i];
|
|
|
|
if (vertexBuffers[i] == nullptr || !vertexBuffers[i]->IsValid())
|
|
{
|
|
vbuffers[i] = nullBuffer;
|
|
}
|
|
else
|
|
{
|
|
auto internal_state = to_internal(vertexBuffers[i]);
|
|
vbuffers[i] = internal_state->resource;
|
|
if (offsets != nullptr)
|
|
{
|
|
voffsets[i] = offsets[i];
|
|
}
|
|
}
|
|
}
|
|
for (int i = count; i < arraysize(commandlist.vb_strides); ++i)
|
|
{
|
|
commandlist.vb_strides[i] = 0;
|
|
}
|
|
|
|
vkCmdBindVertexBuffers(commandlist.GetCommandBuffer(), static_cast<uint32_t>(slot), static_cast<uint32_t>(count), vbuffers, voffsets);
|
|
|
|
if (hash != commandlist.vb_hash)
|
|
{
|
|
commandlist.vb_hash = hash;
|
|
commandlist.dirty_pso = true;
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::BindIndexBuffer(const GPUBuffer* indexBuffer, const IndexBufferFormat format, uint64_t offset, CommandList cmd)
|
|
{
|
|
if (indexBuffer != nullptr)
|
|
{
|
|
auto internal_state = to_internal(indexBuffer);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdBindIndexBuffer(commandlist.GetCommandBuffer(), internal_state->resource, offset, format == IndexBufferFormat::UINT16 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32);
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::BindStencilRef(uint32_t value, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdSetStencilReference(commandlist.GetCommandBuffer(), VK_STENCIL_FRONT_AND_BACK, value);
|
|
}
|
|
void GraphicsDevice_Vulkan::BindBlendFactor(float r, float g, float b, float a, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
float blendConstants[] = { r, g, b, a };
|
|
vkCmdSetBlendConstants(commandlist.GetCommandBuffer(), blendConstants);
|
|
}
|
|
void GraphicsDevice_Vulkan::BindShadingRate(ShadingRate rate, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
if (CheckCapability(GraphicsDeviceCapability::VARIABLE_RATE_SHADING) && commandlist.prev_shadingrate != rate)
|
|
{
|
|
commandlist.prev_shadingrate = rate;
|
|
|
|
VkExtent2D fragmentSize;
|
|
switch (rate)
|
|
{
|
|
case ShadingRate::RATE_1X1:
|
|
fragmentSize.width = 1;
|
|
fragmentSize.height = 1;
|
|
break;
|
|
case ShadingRate::RATE_1X2:
|
|
fragmentSize.width = 1;
|
|
fragmentSize.height = 2;
|
|
break;
|
|
case ShadingRate::RATE_2X1:
|
|
fragmentSize.width = 2;
|
|
fragmentSize.height = 1;
|
|
break;
|
|
case ShadingRate::RATE_2X2:
|
|
fragmentSize.width = 2;
|
|
fragmentSize.height = 2;
|
|
break;
|
|
case ShadingRate::RATE_2X4:
|
|
fragmentSize.width = 2;
|
|
fragmentSize.height = 4;
|
|
break;
|
|
case ShadingRate::RATE_4X2:
|
|
fragmentSize.width = 4;
|
|
fragmentSize.height = 2;
|
|
break;
|
|
case ShadingRate::RATE_4X4:
|
|
fragmentSize.width = 4;
|
|
fragmentSize.height = 4;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
VkFragmentShadingRateCombinerOpKHR combiner[] = {
|
|
VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR,
|
|
VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR
|
|
};
|
|
|
|
if (fragment_shading_rate_properties.fragmentShadingRateNonTrivialCombinerOps == VK_TRUE)
|
|
{
|
|
if (fragment_shading_rate_features.primitiveFragmentShadingRate == VK_TRUE)
|
|
{
|
|
combiner[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR;
|
|
}
|
|
if (fragment_shading_rate_features.attachmentFragmentShadingRate == VK_TRUE)
|
|
{
|
|
combiner[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fragment_shading_rate_features.primitiveFragmentShadingRate == VK_TRUE)
|
|
{
|
|
combiner[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR;
|
|
}
|
|
if (fragment_shading_rate_features.attachmentFragmentShadingRate == VK_TRUE)
|
|
{
|
|
combiner[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR;
|
|
}
|
|
}
|
|
|
|
vkCmdSetFragmentShadingRateKHR(
|
|
commandlist.GetCommandBuffer(),
|
|
&fragmentSize,
|
|
combiner
|
|
);
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::BindPipelineState(const PipelineState* pso, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
if (commandlist.active_pso == pso)
|
|
{
|
|
return;
|
|
}
|
|
commandlist.active_cs = nullptr;
|
|
commandlist.active_rt = nullptr;
|
|
|
|
size_t pipeline_hash = 0;
|
|
wi::helper::hash_combine(pipeline_hash, pso->hash);
|
|
if (commandlist.active_renderpass != nullptr)
|
|
{
|
|
wi::helper::hash_combine(pipeline_hash, commandlist.active_renderpass->hash);
|
|
}
|
|
if (commandlist.prev_pipeline_hash == pipeline_hash)
|
|
{
|
|
return;
|
|
}
|
|
commandlist.prev_pipeline_hash = pipeline_hash;
|
|
|
|
auto internal_state = to_internal(pso);
|
|
|
|
if (commandlist.active_pso == nullptr)
|
|
{
|
|
commandlist.binder.dirty |= DescriptorBinder::DIRTY_ALL;
|
|
}
|
|
else
|
|
{
|
|
auto active_internal = to_internal(commandlist.active_pso);
|
|
if (internal_state->binding_hash != active_internal->binding_hash)
|
|
{
|
|
commandlist.binder.dirty |= DescriptorBinder::DIRTY_ALL;
|
|
}
|
|
}
|
|
|
|
if (!internal_state->bindlessSets.empty())
|
|
{
|
|
vkCmdBindDescriptorSets(
|
|
commandlist.GetCommandBuffer(),
|
|
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
internal_state->pipelineLayout,
|
|
internal_state->bindlessFirstSet,
|
|
(uint32_t)internal_state->bindlessSets.size(),
|
|
internal_state->bindlessSets.data(),
|
|
0,
|
|
nullptr
|
|
);
|
|
}
|
|
|
|
commandlist.active_pso = pso;
|
|
commandlist.dirty_pso = true;
|
|
}
|
|
void GraphicsDevice_Vulkan::BindComputeShader(const Shader* cs, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
if (commandlist.active_cs == cs)
|
|
{
|
|
return;
|
|
}
|
|
commandlist.active_pso = nullptr;
|
|
commandlist.active_rt = nullptr;
|
|
|
|
assert(cs->stage == ShaderStage::CS || cs->stage == ShaderStage::LIB);
|
|
|
|
if (commandlist.active_cs == nullptr)
|
|
{
|
|
commandlist.binder.dirty |= DescriptorBinder::DIRTY_ALL;
|
|
}
|
|
else
|
|
{
|
|
auto internal_state = to_internal(cs);
|
|
auto active_internal = to_internal(commandlist.active_cs);
|
|
if (internal_state->binding_hash != active_internal->binding_hash)
|
|
{
|
|
commandlist.binder.dirty |= DescriptorBinder::DIRTY_ALL;
|
|
}
|
|
}
|
|
|
|
commandlist.active_cs = cs;
|
|
auto internal_state = to_internal(cs);
|
|
|
|
if (cs->stage == ShaderStage::CS)
|
|
{
|
|
vkCmdBindPipeline(commandlist.GetCommandBuffer(), VK_PIPELINE_BIND_POINT_COMPUTE, internal_state->pipeline_cs);
|
|
|
|
if (!internal_state->bindlessSets.empty())
|
|
{
|
|
vkCmdBindDescriptorSets(
|
|
commandlist.GetCommandBuffer(),
|
|
VK_PIPELINE_BIND_POINT_COMPUTE,
|
|
internal_state->pipelineLayout_cs,
|
|
internal_state->bindlessFirstSet,
|
|
(uint32_t)internal_state->bindlessSets.size(),
|
|
internal_state->bindlessSets.data(),
|
|
0,
|
|
nullptr
|
|
);
|
|
}
|
|
}
|
|
else if (cs->stage == ShaderStage::LIB)
|
|
{
|
|
if (!internal_state->bindlessSets.empty())
|
|
{
|
|
vkCmdBindDescriptorSets(
|
|
commandlist.GetCommandBuffer(),
|
|
VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR,
|
|
internal_state->pipelineLayout_cs,
|
|
internal_state->bindlessFirstSet,
|
|
(uint32_t)internal_state->bindlessSets.size(),
|
|
internal_state->bindlessSets.data(),
|
|
0,
|
|
nullptr
|
|
);
|
|
}
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::BindDepthBounds(float min_bounds, float max_bounds, CommandList cmd)
|
|
{
|
|
if (features2.features.depthBounds == VK_TRUE)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdSetDepthBounds(commandlist.GetCommandBuffer(), min_bounds, max_bounds);
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::Draw(uint32_t vertexCount, uint32_t startVertexLocation, CommandList cmd)
|
|
{
|
|
predraw(cmd);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdDraw(commandlist.GetCommandBuffer(), vertexCount, 1, startVertexLocation, 0);
|
|
}
|
|
void GraphicsDevice_Vulkan::DrawIndexed(uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation, CommandList cmd)
|
|
{
|
|
predraw(cmd);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdDrawIndexed(commandlist.GetCommandBuffer(), indexCount, 1, startIndexLocation, baseVertexLocation, 0);
|
|
}
|
|
void GraphicsDevice_Vulkan::DrawInstanced(uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertexLocation, uint32_t startInstanceLocation, CommandList cmd)
|
|
{
|
|
predraw(cmd);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdDraw(commandlist.GetCommandBuffer(), vertexCount, instanceCount, startVertexLocation, startInstanceLocation);
|
|
}
|
|
void GraphicsDevice_Vulkan::DrawIndexedInstanced(uint32_t indexCount, uint32_t instanceCount, uint32_t startIndexLocation, int32_t baseVertexLocation, uint32_t startInstanceLocation, CommandList cmd)
|
|
{
|
|
predraw(cmd);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdDrawIndexed(commandlist.GetCommandBuffer(), indexCount, instanceCount, startIndexLocation, baseVertexLocation, startInstanceLocation);
|
|
}
|
|
void GraphicsDevice_Vulkan::DrawInstancedIndirect(const GPUBuffer* args, uint64_t args_offset, CommandList cmd)
|
|
{
|
|
predraw(cmd);
|
|
auto internal_state = to_internal(args);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdDrawIndirect(commandlist.GetCommandBuffer(), internal_state->resource, args_offset, 1, (uint32_t)sizeof(IndirectDrawArgsInstanced));
|
|
}
|
|
void GraphicsDevice_Vulkan::DrawIndexedInstancedIndirect(const GPUBuffer* args, uint64_t args_offset, CommandList cmd)
|
|
{
|
|
predraw(cmd);
|
|
auto internal_state = to_internal(args);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdDrawIndexedIndirect(commandlist.GetCommandBuffer(), internal_state->resource, args_offset, 1, sizeof(IndirectDrawArgsIndexedInstanced));
|
|
}
|
|
void GraphicsDevice_Vulkan::DrawInstancedIndirectCount(const GPUBuffer* args, uint64_t args_offset, const GPUBuffer* count, uint64_t count_offset, uint32_t max_count, CommandList cmd)
|
|
{
|
|
predraw(cmd);
|
|
auto args_internal = to_internal(args);
|
|
auto count_internal = to_internal(count);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdDrawIndirectCount(commandlist.GetCommandBuffer(), args_internal->resource, args_offset, count_internal->resource, count_offset, max_count, sizeof(IndirectDrawArgsInstanced));
|
|
}
|
|
void GraphicsDevice_Vulkan::DrawIndexedInstancedIndirectCount(const GPUBuffer* args, uint64_t args_offset, const GPUBuffer* count, uint64_t count_offset, uint32_t max_count, CommandList cmd)
|
|
{
|
|
predraw(cmd);
|
|
auto args_internal = to_internal(args);
|
|
auto count_internal = to_internal(count);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdDrawIndexedIndirectCount(commandlist.GetCommandBuffer(), args_internal->resource, args_offset, count_internal->resource, count_offset, max_count, sizeof(IndirectDrawArgsIndexedInstanced));
|
|
}
|
|
void GraphicsDevice_Vulkan::Dispatch(uint32_t threadGroupCountX, uint32_t threadGroupCountY, uint32_t threadGroupCountZ, CommandList cmd)
|
|
{
|
|
predispatch(cmd);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdDispatch(commandlist.GetCommandBuffer(), threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
|
}
|
|
void GraphicsDevice_Vulkan::DispatchIndirect(const GPUBuffer* args, uint64_t args_offset, CommandList cmd)
|
|
{
|
|
predispatch(cmd);
|
|
auto internal_state = to_internal(args);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdDispatchIndirect(commandlist.GetCommandBuffer(), internal_state->resource, args_offset);
|
|
}
|
|
void GraphicsDevice_Vulkan::DispatchMesh(uint32_t threadGroupCountX, uint32_t threadGroupCountY, uint32_t threadGroupCountZ, CommandList cmd)
|
|
{
|
|
predraw(cmd);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdDrawMeshTasksNV(commandlist.GetCommandBuffer(), threadGroupCountX * threadGroupCountY * threadGroupCountZ, 0);
|
|
}
|
|
void GraphicsDevice_Vulkan::DispatchMeshIndirect(const GPUBuffer* args, uint64_t args_offset, CommandList cmd)
|
|
{
|
|
predraw(cmd);
|
|
auto internal_state = to_internal(args);
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdDrawMeshTasksIndirectNV(commandlist.GetCommandBuffer(), internal_state->resource, args_offset, 1, sizeof(IndirectDispatchArgs));
|
|
}
|
|
void GraphicsDevice_Vulkan::CopyResource(const GPUResource* pDst, const GPUResource* pSrc, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
if (pDst->type == GPUResource::Type::TEXTURE && pSrc->type == GPUResource::Type::TEXTURE)
|
|
{
|
|
auto internal_state_src = to_internal((const Texture*)pSrc);
|
|
auto internal_state_dst = to_internal((const Texture*)pDst);
|
|
|
|
const TextureDesc& src_desc = ((const Texture*)pSrc)->GetDesc();
|
|
const TextureDesc& dst_desc = ((const Texture*)pDst)->GetDesc();
|
|
|
|
if (src_desc.usage == Usage::UPLOAD)
|
|
{
|
|
VkBufferImageCopy copy = {};
|
|
copy.imageSubresource.baseArrayLayer = 0;
|
|
copy.imageSubresource.layerCount = dst_desc.array_size;
|
|
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
const uint32_t data_stride = GetFormatStride(dst_desc.format);
|
|
uint32_t mip_width = dst_desc.width;
|
|
uint32_t mip_height = dst_desc.height;
|
|
uint32_t mip_depth = dst_desc.depth;
|
|
for (uint32_t mip = 0; mip < dst_desc.mip_levels; ++mip)
|
|
{
|
|
copy.imageExtent.width = mip_width;
|
|
copy.imageExtent.height = mip_height;
|
|
copy.imageExtent.depth = mip_depth;
|
|
copy.imageSubresource.mipLevel = mip;
|
|
vkCmdCopyBufferToImage(
|
|
commandlist.GetCommandBuffer(),
|
|
internal_state_src->staging_resource,
|
|
internal_state_dst->resource,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
1,
|
|
©
|
|
);
|
|
|
|
copy.bufferOffset += mip_width * mip_height * mip_depth * data_stride;
|
|
mip_width = std::max(1u, mip_width / 2);
|
|
mip_height = std::max(1u, mip_height / 2);
|
|
mip_depth = std::max(1u, mip_depth / 2);
|
|
}
|
|
|
|
}
|
|
else if (dst_desc.usage == Usage::READBACK)
|
|
{
|
|
VkBufferImageCopy copy = {};
|
|
copy.imageSubresource.baseArrayLayer = 0;
|
|
copy.imageSubresource.layerCount = dst_desc.array_size;
|
|
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
const uint32_t data_stride = GetFormatStride(dst_desc.format);
|
|
uint32_t mip_width = dst_desc.width;
|
|
uint32_t mip_height = dst_desc.height;
|
|
uint32_t mip_depth = dst_desc.depth;
|
|
for (uint32_t mip = 0; mip < dst_desc.mip_levels; ++mip)
|
|
{
|
|
copy.imageExtent.width = mip_width;
|
|
copy.imageExtent.height = mip_height;
|
|
copy.imageExtent.depth = mip_depth;
|
|
copy.imageSubresource.mipLevel = mip;
|
|
vkCmdCopyImageToBuffer(
|
|
commandlist.GetCommandBuffer(),
|
|
internal_state_src->resource,
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
internal_state_dst->staging_resource,
|
|
1,
|
|
©
|
|
);
|
|
|
|
copy.bufferOffset += mip_width * mip_height * mip_depth * data_stride;
|
|
mip_width = std::max(1u, mip_width / 2);
|
|
mip_height = std::max(1u, mip_height / 2);
|
|
mip_depth = std::max(1u, mip_depth / 2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VkImageCopy copy = {};
|
|
copy.extent.width = dst_desc.width;
|
|
copy.extent.height = dst_desc.height;
|
|
copy.extent.depth = std::max(1u, dst_desc.depth);
|
|
|
|
copy.srcOffset.x = 0;
|
|
copy.srcOffset.y = 0;
|
|
copy.srcOffset.z = 0;
|
|
|
|
copy.dstOffset.x = 0;
|
|
copy.dstOffset.y = 0;
|
|
copy.dstOffset.z = 0;
|
|
|
|
if (IsFormatDepthSupport(src_desc.format))
|
|
{
|
|
copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
if (IsFormatStencilSupport(src_desc.format))
|
|
{
|
|
copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
}
|
|
copy.srcSubresource.baseArrayLayer = 0;
|
|
copy.srcSubresource.layerCount = src_desc.array_size;
|
|
copy.srcSubresource.mipLevel = 0;
|
|
|
|
if (has_flag(dst_desc.bind_flags, BindFlag::DEPTH_STENCIL))
|
|
{
|
|
copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
if (IsFormatStencilSupport(dst_desc.format))
|
|
{
|
|
copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
}
|
|
copy.dstSubresource.baseArrayLayer = 0;
|
|
copy.dstSubresource.layerCount = dst_desc.array_size;
|
|
copy.dstSubresource.mipLevel = 0;
|
|
|
|
vkCmdCopyImage(commandlist.GetCommandBuffer(),
|
|
internal_state_src->resource, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
internal_state_dst->resource, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
1, ©
|
|
);
|
|
}
|
|
}
|
|
else if (pDst->type == GPUResource::Type::BUFFER && pSrc->type == GPUResource::Type::BUFFER)
|
|
{
|
|
auto internal_state_src = to_internal((const GPUBuffer*)pSrc);
|
|
auto internal_state_dst = to_internal((const GPUBuffer*)pDst);
|
|
|
|
const GPUBufferDesc& src_desc = ((const GPUBuffer*)pSrc)->GetDesc();
|
|
const GPUBufferDesc& dst_desc = ((const GPUBuffer*)pDst)->GetDesc();
|
|
|
|
VkBufferCopy copy = {};
|
|
copy.srcOffset = 0;
|
|
copy.dstOffset = 0;
|
|
copy.size = std::min(src_desc.size, dst_desc.size);
|
|
|
|
vkCmdCopyBuffer(commandlist.GetCommandBuffer(),
|
|
internal_state_src->resource,
|
|
internal_state_dst->resource,
|
|
1, ©
|
|
);
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::CopyBuffer(const GPUBuffer* pDst, uint64_t dst_offset, const GPUBuffer* pSrc, uint64_t src_offset, uint64_t size, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
auto internal_state_src = to_internal(pSrc);
|
|
auto internal_state_dst = to_internal(pDst);
|
|
|
|
VkBufferCopy copy = {};
|
|
copy.srcOffset = src_offset;
|
|
copy.dstOffset = dst_offset;
|
|
copy.size = size;
|
|
|
|
vkCmdCopyBuffer(commandlist.GetCommandBuffer(),
|
|
internal_state_src->resource,
|
|
internal_state_dst->resource,
|
|
1, ©
|
|
);
|
|
}
|
|
void GraphicsDevice_Vulkan::CopyTexture(const Texture* dst, uint32_t dstX, uint32_t dstY, uint32_t dstZ, uint32_t dstMip, uint32_t dstSlice, const Texture* src, uint32_t srcMip, uint32_t srcSlice, CommandList cmd, const Box* srcbox)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
auto src_internal = to_internal(src);
|
|
auto dst_internal = to_internal(dst);
|
|
|
|
VkImageCopy copy = {};
|
|
copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
copy.dstSubresource.baseArrayLayer = dstSlice;
|
|
copy.dstSubresource.layerCount = 1;
|
|
copy.dstSubresource.mipLevel = dstMip;
|
|
copy.dstOffset.x = dstX;
|
|
copy.dstOffset.y = dstY;
|
|
copy.dstOffset.z = dstZ;
|
|
|
|
copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
copy.srcSubresource.baseArrayLayer = srcSlice;
|
|
copy.srcSubresource.layerCount = 1;
|
|
copy.srcSubresource.mipLevel = srcMip;
|
|
|
|
if (srcbox == nullptr)
|
|
{
|
|
copy.srcOffset.x = 0;
|
|
copy.srcOffset.y = 0;
|
|
copy.srcOffset.z = 0;
|
|
copy.extent.width = std::min(dst->desc.width, src->desc.width);
|
|
copy.extent.height = std::min(dst->desc.height, src->desc.height);
|
|
copy.extent.depth = std::min(dst->desc.depth, src->desc.depth);
|
|
}
|
|
else
|
|
{
|
|
copy.srcOffset.x = srcbox->left;
|
|
copy.srcOffset.y = srcbox->top;
|
|
copy.srcOffset.z = srcbox->front;
|
|
copy.extent.width = srcbox->right - srcbox->left;
|
|
copy.extent.height = srcbox->bottom - srcbox->top;
|
|
copy.extent.depth = srcbox->back - srcbox->front;
|
|
}
|
|
|
|
vkCmdCopyImage(
|
|
commandlist.GetCommandBuffer(),
|
|
src_internal->resource,
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
dst_internal->resource,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
1,
|
|
©
|
|
);
|
|
}
|
|
void GraphicsDevice_Vulkan::QueryBegin(const GPUQueryHeap* heap, uint32_t index, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
auto internal_state = to_internal(heap);
|
|
|
|
switch (heap->desc.type)
|
|
{
|
|
case GpuQueryType::OCCLUSION_BINARY:
|
|
vkCmdBeginQuery(commandlist.GetCommandBuffer(), internal_state->pool, index, 0);
|
|
break;
|
|
case GpuQueryType::OCCLUSION:
|
|
vkCmdBeginQuery(commandlist.GetCommandBuffer(), internal_state->pool, index, VK_QUERY_CONTROL_PRECISE_BIT);
|
|
break;
|
|
case GpuQueryType::TIMESTAMP:
|
|
break;
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::QueryEnd(const GPUQueryHeap* heap, uint32_t index, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
auto internal_state = to_internal(heap);
|
|
|
|
switch (heap->desc.type)
|
|
{
|
|
case GpuQueryType::TIMESTAMP:
|
|
vkCmdWriteTimestamp(commandlist.GetCommandBuffer(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, internal_state->pool, index);
|
|
break;
|
|
case GpuQueryType::OCCLUSION_BINARY:
|
|
case GpuQueryType::OCCLUSION:
|
|
vkCmdEndQuery(commandlist.GetCommandBuffer(), internal_state->pool, index);
|
|
break;
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::QueryResolve(const GPUQueryHeap* heap, uint32_t index, uint32_t count, const GPUBuffer* dest, uint64_t dest_offset, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
assert(commandlist.active_renderpass == nullptr); // Can't resolve inside renderpass!
|
|
|
|
auto internal_state = to_internal(heap);
|
|
auto dst_internal = to_internal(dest);
|
|
|
|
VkQueryResultFlags flags = VK_QUERY_RESULT_64_BIT;
|
|
flags |= VK_QUERY_RESULT_WAIT_BIT;
|
|
|
|
switch (heap->desc.type)
|
|
{
|
|
case GpuQueryType::OCCLUSION_BINARY:
|
|
flags |= VK_QUERY_RESULT_PARTIAL_BIT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
vkCmdCopyQueryPoolResults(
|
|
commandlist.GetCommandBuffer(),
|
|
internal_state->pool,
|
|
index,
|
|
count,
|
|
dst_internal->resource,
|
|
dest_offset,
|
|
sizeof(uint64_t),
|
|
flags
|
|
);
|
|
|
|
}
|
|
void GraphicsDevice_Vulkan::QueryReset(const GPUQueryHeap* heap, uint32_t index, uint32_t count, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
assert(commandlist.active_renderpass == nullptr); // Can't resolve inside renderpass!
|
|
|
|
auto internal_state = to_internal(heap);
|
|
|
|
vkCmdResetQueryPool(
|
|
commandlist.GetCommandBuffer(),
|
|
internal_state->pool,
|
|
index,
|
|
count
|
|
);
|
|
|
|
}
|
|
void GraphicsDevice_Vulkan::Barrier(const GPUBarrier* barriers, uint32_t numBarriers, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
assert(commandlist.active_renderpass == nullptr);
|
|
|
|
VkPipelineStageFlags srcStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
|
VkPipelineStageFlags dstStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
|
|
|
auto& memoryBarriers = commandlist.frame_memoryBarriers;
|
|
auto& imageBarriers = commandlist.frame_imageBarriers;
|
|
auto& bufferBarriers = commandlist.frame_bufferBarriers;
|
|
|
|
for (uint32_t i = 0; i < numBarriers; ++i)
|
|
{
|
|
const GPUBarrier& barrier = barriers[i];
|
|
|
|
if (barrier.type == GPUBarrier::Type::IMAGE && (barrier.image.texture == nullptr || !barrier.image.texture->IsValid()))
|
|
continue;
|
|
if (barrier.type == GPUBarrier::Type::BUFFER && (barrier.buffer.buffer == nullptr || !barrier.buffer.buffer->IsValid()))
|
|
continue;
|
|
|
|
switch (barrier.type)
|
|
{
|
|
default:
|
|
case GPUBarrier::Type::MEMORY:
|
|
{
|
|
VkMemoryBarrier barrierdesc = {};
|
|
barrierdesc.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
|
|
barrierdesc.pNext = nullptr;
|
|
barrierdesc.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
|
|
barrierdesc.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
|
|
|
if (CheckCapability(GraphicsDeviceCapability::RAYTRACING))
|
|
{
|
|
barrierdesc.srcAccessMask |= VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR;
|
|
barrierdesc.dstAccessMask |= VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR;
|
|
srcStage |= VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
|
|
dstStage |= VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
|
|
}
|
|
|
|
if (CheckCapability(GraphicsDeviceCapability::PREDICATION))
|
|
{
|
|
barrierdesc.srcAccessMask |= VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT;
|
|
barrierdesc.dstAccessMask |= VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT;
|
|
srcStage |= VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT;
|
|
dstStage |= VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT;
|
|
}
|
|
|
|
memoryBarriers.push_back(barrierdesc);
|
|
}
|
|
break;
|
|
case GPUBarrier::Type::IMAGE:
|
|
{
|
|
const TextureDesc& desc = barrier.image.texture->desc;
|
|
auto internal_state = to_internal(barrier.image.texture);
|
|
|
|
VkImageMemoryBarrier barrierdesc = {};
|
|
barrierdesc.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
barrierdesc.pNext = nullptr;
|
|
barrierdesc.image = internal_state->resource;
|
|
barrierdesc.oldLayout = _ConvertImageLayout(barrier.image.layout_before);
|
|
barrierdesc.newLayout = _ConvertImageLayout(barrier.image.layout_after);
|
|
barrierdesc.srcAccessMask = _ParseResourceState(barrier.image.layout_before);
|
|
barrierdesc.dstAccessMask = _ParseResourceState(barrier.image.layout_after);
|
|
if (IsFormatDepthSupport(desc.format))
|
|
{
|
|
barrierdesc.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
if (IsFormatStencilSupport(desc.format))
|
|
{
|
|
barrierdesc.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
barrierdesc.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
}
|
|
if (barrier.image.mip >= 0 || barrier.image.slice >= 0)
|
|
{
|
|
barrierdesc.subresourceRange.baseMipLevel = (uint32_t)std::max(0, barrier.image.mip);
|
|
barrierdesc.subresourceRange.levelCount = 1;
|
|
barrierdesc.subresourceRange.baseArrayLayer = (uint32_t)std::max(0, barrier.image.slice);
|
|
barrierdesc.subresourceRange.layerCount = 1;
|
|
}
|
|
else
|
|
{
|
|
barrierdesc.subresourceRange.baseMipLevel = 0;
|
|
barrierdesc.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
|
|
barrierdesc.subresourceRange.baseArrayLayer = 0;
|
|
barrierdesc.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
|
}
|
|
barrierdesc.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrierdesc.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
|
|
imageBarriers.push_back(barrierdesc);
|
|
}
|
|
break;
|
|
case GPUBarrier::Type::BUFFER:
|
|
{
|
|
const GPUBufferDesc& desc = barrier.buffer.buffer->desc;
|
|
auto internal_state = to_internal(barrier.buffer.buffer);
|
|
|
|
VkBufferMemoryBarrier barrierdesc = {};
|
|
barrierdesc.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
|
|
barrierdesc.pNext = nullptr;
|
|
barrierdesc.buffer = internal_state->resource;
|
|
barrierdesc.size = desc.size;
|
|
barrierdesc.offset = 0;
|
|
barrierdesc.srcAccessMask = _ParseResourceState(barrier.buffer.state_before);
|
|
barrierdesc.dstAccessMask = _ParseResourceState(barrier.buffer.state_after);
|
|
barrierdesc.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrierdesc.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
|
|
bufferBarriers.push_back(barrierdesc);
|
|
|
|
if (has_flag(desc.misc_flags, ResourceMiscFlag::RAY_TRACING))
|
|
{
|
|
assert(CheckCapability(GraphicsDeviceCapability::RAYTRACING));
|
|
srcStage |= VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
|
|
dstStage |= VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
|
|
}
|
|
|
|
if (has_flag(desc.misc_flags, ResourceMiscFlag::PREDICATION))
|
|
{
|
|
assert(CheckCapability(GraphicsDeviceCapability::PREDICATION));
|
|
srcStage |= VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT;
|
|
dstStage |= VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!memoryBarriers.empty() ||
|
|
!bufferBarriers.empty() ||
|
|
!imageBarriers.empty()
|
|
)
|
|
{
|
|
vkCmdPipelineBarrier(
|
|
commandlist.GetCommandBuffer(),
|
|
srcStage,
|
|
dstStage,
|
|
0,
|
|
(uint32_t)memoryBarriers.size(), memoryBarriers.data(),
|
|
(uint32_t)bufferBarriers.size(), bufferBarriers.data(),
|
|
(uint32_t)imageBarriers.size(), imageBarriers.data()
|
|
);
|
|
|
|
memoryBarriers.clear();
|
|
imageBarriers.clear();
|
|
bufferBarriers.clear();
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::BuildRaytracingAccelerationStructure(const RaytracingAccelerationStructure* dst, CommandList cmd, const RaytracingAccelerationStructure* src)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
auto dst_internal = to_internal(dst);
|
|
|
|
VkAccelerationStructureBuildGeometryInfoKHR info = dst_internal->buildInfo;
|
|
info.dstAccelerationStructure = dst_internal->resource;
|
|
info.srcAccelerationStructure = VK_NULL_HANDLE;
|
|
info.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
|
|
|
|
info.scratchData.deviceAddress = dst_internal->scratch_address;
|
|
|
|
if (src != nullptr)
|
|
{
|
|
info.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR;
|
|
|
|
auto src_internal = to_internal(src);
|
|
info.srcAccelerationStructure = src_internal->resource;
|
|
}
|
|
|
|
commandlist.accelerationstructure_build_geometries = dst_internal->geometries; // copy!
|
|
commandlist.accelerationstructure_build_ranges.clear();
|
|
|
|
info.type = dst_internal->createInfo.type;
|
|
info.geometryCount = (uint32_t)commandlist.accelerationstructure_build_geometries.size();
|
|
commandlist.accelerationstructure_build_ranges.reserve(info.geometryCount);
|
|
|
|
switch (dst->desc.type)
|
|
{
|
|
case RaytracingAccelerationStructureDesc::Type::BOTTOMLEVEL:
|
|
{
|
|
size_t i = 0;
|
|
for (auto& x : dst->desc.bottom_level.geometries)
|
|
{
|
|
auto& geometry = commandlist.accelerationstructure_build_geometries[i];
|
|
|
|
auto& range = commandlist.accelerationstructure_build_ranges.emplace_back();
|
|
range = {};
|
|
|
|
if (x.flags & RaytracingAccelerationStructureDesc::BottomLevel::Geometry::FLAG_OPAQUE)
|
|
{
|
|
geometry.flags |= VK_GEOMETRY_OPAQUE_BIT_KHR;
|
|
}
|
|
if (x.flags & RaytracingAccelerationStructureDesc::BottomLevel::Geometry::FLAG_NO_DUPLICATE_ANYHIT_INVOCATION)
|
|
{
|
|
geometry.flags |= VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR;
|
|
}
|
|
|
|
if (x.type == RaytracingAccelerationStructureDesc::BottomLevel::Geometry::Type::TRIANGLES)
|
|
{
|
|
geometry.geometry.triangles.vertexData.deviceAddress = to_internal(&x.triangles.vertex_buffer)->address +
|
|
x.triangles.vertex_byte_offset;
|
|
|
|
geometry.geometry.triangles.indexData.deviceAddress = to_internal(&x.triangles.index_buffer)->address +
|
|
x.triangles.index_offset * (x.triangles.index_format == IndexBufferFormat::UINT16 ? sizeof(uint16_t) : sizeof(uint32_t));
|
|
|
|
if (x.flags & RaytracingAccelerationStructureDesc::BottomLevel::Geometry::FLAG_USE_TRANSFORM)
|
|
{
|
|
geometry.geometry.triangles.transformData.deviceAddress = to_internal(&x.triangles.transform_3x4_buffer)->address;
|
|
range.transformOffset = x.triangles.transform_3x4_buffer_offset;
|
|
}
|
|
|
|
range.primitiveCount = x.triangles.index_count / 3;
|
|
range.primitiveOffset = 0;
|
|
}
|
|
else if (x.type == RaytracingAccelerationStructureDesc::BottomLevel::Geometry::Type::PROCEDURAL_AABBS)
|
|
{
|
|
geometry.geometry.aabbs.data.deviceAddress = to_internal(&x.aabbs.aabb_buffer)->address;
|
|
|
|
range.primitiveCount = x.aabbs.count;
|
|
range.primitiveOffset = x.aabbs.offset;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
break;
|
|
case RaytracingAccelerationStructureDesc::Type::TOPLEVEL:
|
|
{
|
|
auto& geometry = commandlist.accelerationstructure_build_geometries.back();
|
|
geometry.geometry.instances.data.deviceAddress = to_internal(&dst->desc.top_level.instance_buffer)->address;
|
|
|
|
auto& range = commandlist.accelerationstructure_build_ranges.emplace_back();
|
|
range = {};
|
|
range.primitiveCount = dst->desc.top_level.count;
|
|
range.primitiveOffset = dst->desc.top_level.offset;
|
|
}
|
|
break;
|
|
}
|
|
|
|
info.pGeometries = commandlist.accelerationstructure_build_geometries.data();
|
|
|
|
VkAccelerationStructureBuildRangeInfoKHR* pRangeInfo = commandlist.accelerationstructure_build_ranges.data();
|
|
|
|
vkCmdBuildAccelerationStructuresKHR(
|
|
commandlist.GetCommandBuffer(),
|
|
1,
|
|
&info,
|
|
&pRangeInfo
|
|
);
|
|
}
|
|
void GraphicsDevice_Vulkan::BindRaytracingPipelineState(const RaytracingPipelineState* rtpso, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
commandlist.prev_pipeline_hash = 0;
|
|
commandlist.active_rt = rtpso;
|
|
|
|
BindComputeShader(rtpso->desc.shader_libraries.front().shader, cmd);
|
|
|
|
vkCmdBindPipeline(commandlist.GetCommandBuffer(), VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, to_internal(rtpso)->pipeline);
|
|
}
|
|
void GraphicsDevice_Vulkan::DispatchRays(const DispatchRaysDesc* desc, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
predispatch(cmd);
|
|
|
|
VkStridedDeviceAddressRegionKHR raygen = {};
|
|
raygen.deviceAddress = desc->ray_generation.buffer ? to_internal(desc->ray_generation.buffer)->address : 0;
|
|
raygen.deviceAddress += desc->ray_generation.offset;
|
|
raygen.size = desc->ray_generation.size;
|
|
raygen.stride = raygen.size; // raygen specifically must be size == stride
|
|
|
|
VkStridedDeviceAddressRegionKHR miss = {};
|
|
miss.deviceAddress = desc->miss.buffer ? to_internal(desc->miss.buffer)->address : 0;
|
|
miss.deviceAddress += desc->miss.offset;
|
|
miss.size = desc->miss.size;
|
|
miss.stride = desc->miss.stride;
|
|
|
|
VkStridedDeviceAddressRegionKHR hitgroup = {};
|
|
hitgroup.deviceAddress = desc->hit_group.buffer ? to_internal(desc->hit_group.buffer)->address : 0;
|
|
hitgroup.deviceAddress += desc->hit_group.offset;
|
|
hitgroup.size = desc->hit_group.size;
|
|
hitgroup.stride = desc->hit_group.stride;
|
|
|
|
VkStridedDeviceAddressRegionKHR callable = {};
|
|
callable.deviceAddress = desc->callable.buffer ? to_internal(desc->callable.buffer)->address : 0;
|
|
callable.deviceAddress += desc->callable.offset;
|
|
callable.size = desc->callable.size;
|
|
callable.stride = desc->callable.stride;
|
|
|
|
vkCmdTraceRaysKHR(
|
|
commandlist.GetCommandBuffer(),
|
|
&raygen,
|
|
&miss,
|
|
&hitgroup,
|
|
&callable,
|
|
desc->width,
|
|
desc->height,
|
|
desc->depth
|
|
);
|
|
}
|
|
void GraphicsDevice_Vulkan::PushConstants(const void* data, uint32_t size, CommandList cmd, uint32_t offset)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
|
|
if (commandlist.active_pso != nullptr)
|
|
{
|
|
auto pso_internal = to_internal(commandlist.active_pso);
|
|
if (pso_internal->pushconstants.size > 0)
|
|
{
|
|
vkCmdPushConstants(
|
|
commandlist.GetCommandBuffer(),
|
|
pso_internal->pipelineLayout,
|
|
pso_internal->pushconstants.stageFlags,
|
|
offset,
|
|
size,
|
|
data
|
|
);
|
|
return;
|
|
}
|
|
assert(0); // there was no push constant block!
|
|
}
|
|
if(commandlist.active_cs != nullptr)
|
|
{
|
|
auto cs_internal = to_internal(commandlist.active_cs);
|
|
if (cs_internal->pushconstants.size > 0)
|
|
{
|
|
vkCmdPushConstants(
|
|
commandlist.GetCommandBuffer(),
|
|
cs_internal->pipelineLayout_cs,
|
|
cs_internal->pushconstants.stageFlags,
|
|
offset,
|
|
size,
|
|
data
|
|
);
|
|
return;
|
|
}
|
|
assert(0); // there was no push constant block!
|
|
}
|
|
assert(0); // there was no active pipeline!
|
|
}
|
|
void GraphicsDevice_Vulkan::PredicationBegin(const GPUBuffer* buffer, uint64_t offset, PredicationOp op, CommandList cmd)
|
|
{
|
|
if (CheckCapability(GraphicsDeviceCapability::PREDICATION))
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
auto internal_state = to_internal(buffer);
|
|
|
|
VkConditionalRenderingBeginInfoEXT info = {};
|
|
info.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
|
|
if (op == PredicationOp::NOT_EQUAL_ZERO)
|
|
{
|
|
info.flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
|
|
}
|
|
info.offset = offset;
|
|
info.buffer = internal_state->resource;
|
|
vkCmdBeginConditionalRenderingEXT(commandlist.GetCommandBuffer(), &info);
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::PredicationEnd(CommandList cmd)
|
|
{
|
|
if (CheckCapability(GraphicsDeviceCapability::PREDICATION))
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
vkCmdEndConditionalRenderingEXT(commandlist.GetCommandBuffer());
|
|
}
|
|
}
|
|
void GraphicsDevice_Vulkan::ClearUAV(const GPUResource* resource, uint32_t value, CommandList cmd)
|
|
{
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
|
|
if (resource->IsBuffer())
|
|
{
|
|
auto internal_state = to_internal((const GPUBuffer*)resource);
|
|
vkCmdFillBuffer(
|
|
commandlist.GetCommandBuffer(),
|
|
internal_state->resource,
|
|
0,
|
|
VK_WHOLE_SIZE,
|
|
value
|
|
);
|
|
}
|
|
else if (resource->IsTexture())
|
|
{
|
|
VkClearColorValue color = {};
|
|
color.uint32[0] = value;
|
|
color.uint32[1] = value;
|
|
color.uint32[2] = value;
|
|
color.uint32[3] = value;
|
|
|
|
VkImageSubresourceRange range = {};
|
|
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
range.baseArrayLayer = 0;
|
|
range.baseMipLevel = 0;
|
|
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
|
range.levelCount = VK_REMAINING_MIP_LEVELS;
|
|
|
|
auto internal_state = to_internal((const Texture*)resource);
|
|
vkCmdClearColorImage(
|
|
commandlist.GetCommandBuffer(),
|
|
internal_state->resource,
|
|
VK_IMAGE_LAYOUT_GENERAL, // "ClearUAV" so must be in UNORDERED_ACCESS state, that's a given
|
|
&color,
|
|
1,
|
|
&range
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
void GraphicsDevice_Vulkan::EventBegin(const char* name, CommandList cmd)
|
|
{
|
|
if (!debugUtils)
|
|
return;
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
|
|
VkDebugUtilsLabelEXT label = { VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT };
|
|
label.pLabelName = name;
|
|
label.color[0] = 0.0f;
|
|
label.color[1] = 0.0f;
|
|
label.color[2] = 0.0f;
|
|
label.color[3] = 1.0f;
|
|
vkCmdBeginDebugUtilsLabelEXT(commandlist.GetCommandBuffer(), &label);
|
|
}
|
|
void GraphicsDevice_Vulkan::EventEnd(CommandList cmd)
|
|
{
|
|
if (!debugUtils)
|
|
return;
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
|
|
vkCmdEndDebugUtilsLabelEXT(commandlist.GetCommandBuffer());
|
|
}
|
|
void GraphicsDevice_Vulkan::SetMarker(const char* name, CommandList cmd)
|
|
{
|
|
if (!debugUtils)
|
|
return;
|
|
CommandList_Vulkan& commandlist = GetCommandList(cmd);
|
|
|
|
VkDebugUtilsLabelEXT label { VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT };
|
|
label.pLabelName = name;
|
|
label.color[0] = 0.0f;
|
|
label.color[1] = 0.0f;
|
|
label.color[2] = 0.0f;
|
|
label.color[3] = 1.0f;
|
|
vkCmdInsertDebugUtilsLabelEXT(commandlist.GetCommandBuffer(), &label);
|
|
}
|
|
|
|
}
|
|
|
|
#endif // WICKEDENGINE_BUILD_VULKAN
|