mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-05-29 07:59:11 +00:00
11798 lines
431 KiB
C
11798 lines
431 KiB
C
/*
|
|
Simple DirectMedia Layer
|
|
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
|
|
#include "SDL_internal.h"
|
|
|
|
#if SDL_GPU_VULKAN
|
|
|
|
// Needed for VK_KHR_portability_subset
|
|
#define VK_ENABLE_BETA_EXTENSIONS
|
|
|
|
#define VK_NO_PROTOTYPES
|
|
#include "../../video/khronos/vulkan/vulkan.h"
|
|
|
|
#include "SDL_hashtable.h"
|
|
#include <SDL3/SDL_vulkan.h>
|
|
|
|
#include "../SDL_sysgpu.h"
|
|
|
|
#define VULKAN_INTERNAL_clamp(val, min, max) SDL_max(min, SDL_min(val, max))
|
|
|
|
// Global Vulkan Loader Entry Points
|
|
|
|
static PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
|
|
|
|
#define VULKAN_GLOBAL_FUNCTION(name) \
|
|
static PFN_##name name = NULL;
|
|
#include "SDL_gpu_vulkan_vkfuncs.h"
|
|
|
|
typedef struct VulkanExtensions
|
|
{
|
|
// These extensions are required!
|
|
|
|
// Globally supported
|
|
Uint8 KHR_swapchain;
|
|
// Core since 1.1, needed for negative VkViewport::height
|
|
Uint8 KHR_maintenance1;
|
|
|
|
// These extensions are optional!
|
|
|
|
// Core since 1.2, but requires annoying paperwork to implement
|
|
Uint8 KHR_driver_properties;
|
|
// EXT, probably not going to be Core
|
|
Uint8 EXT_vertex_attribute_divisor;
|
|
// Only required for special implementations (i.e. MoltenVK)
|
|
Uint8 KHR_portability_subset;
|
|
} VulkanExtensions;
|
|
|
|
// Defines
|
|
|
|
#define SMALL_ALLOCATION_THRESHOLD 2097152 // 2 MiB
|
|
#define SMALL_ALLOCATION_SIZE 16777216 // 16 MiB
|
|
#define LARGE_ALLOCATION_INCREMENT 67108864 // 64 MiB
|
|
#define MAX_UBO_SECTION_SIZE 4096 // 4 KiB
|
|
#define DESCRIPTOR_POOL_STARTING_SIZE 128
|
|
#define WINDOW_PROPERTY_DATA "SDL_GPUVulkanWindowPropertyData"
|
|
|
|
#define IDENTITY_SWIZZLE \
|
|
{ \
|
|
VK_COMPONENT_SWIZZLE_IDENTITY, \
|
|
VK_COMPONENT_SWIZZLE_IDENTITY, \
|
|
VK_COMPONENT_SWIZZLE_IDENTITY, \
|
|
VK_COMPONENT_SWIZZLE_IDENTITY \
|
|
}
|
|
|
|
#define NULL_DESC_LAYOUT (VkDescriptorSetLayout)0
|
|
#define NULL_PIPELINE_LAYOUT (VkPipelineLayout)0
|
|
#define NULL_RENDER_PASS (SDL_GPURenderPass *)0
|
|
|
|
#define EXPAND_ELEMENTS_IF_NEEDED(arr, initialValue, type) \
|
|
if (arr->count == arr->capacity) { \
|
|
if (arr->capacity == 0) { \
|
|
arr->capacity = initialValue; \
|
|
} else { \
|
|
arr->capacity *= 2; \
|
|
} \
|
|
arr->elements = (type *)SDL_realloc( \
|
|
arr->elements, \
|
|
arr->capacity * sizeof(type)); \
|
|
}
|
|
|
|
#define EXPAND_ARRAY_IF_NEEDED(arr, elementType, newCount, capacity, newCapacity) \
|
|
if (newCount >= capacity) { \
|
|
capacity = newCapacity; \
|
|
arr = (elementType *)SDL_realloc( \
|
|
arr, \
|
|
sizeof(elementType) * capacity); \
|
|
}
|
|
|
|
#define MOVE_ARRAY_CONTENTS_AND_RESET(i, dstArr, dstCount, srcArr, srcCount) \
|
|
for (i = 0; i < srcCount; i += 1) { \
|
|
dstArr[i] = srcArr[i]; \
|
|
} \
|
|
dstCount = srcCount; \
|
|
srcCount = 0;
|
|
|
|
// Conversions
|
|
|
|
static const Uint8 DEVICE_PRIORITY_HIGHPERFORMANCE[] = {
|
|
0, // VK_PHYSICAL_DEVICE_TYPE_OTHER
|
|
3, // VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU
|
|
4, // VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
|
|
2, // VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU
|
|
1 // VK_PHYSICAL_DEVICE_TYPE_CPU
|
|
};
|
|
|
|
static const Uint8 DEVICE_PRIORITY_LOWPOWER[] = {
|
|
0, // VK_PHYSICAL_DEVICE_TYPE_OTHER
|
|
4, // VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU
|
|
3, // VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
|
|
2, // VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU
|
|
1 // VK_PHYSICAL_DEVICE_TYPE_CPU
|
|
};
|
|
|
|
static VkPresentModeKHR SDLToVK_PresentMode[] = {
|
|
VK_PRESENT_MODE_FIFO_KHR,
|
|
VK_PRESENT_MODE_IMMEDIATE_KHR,
|
|
VK_PRESENT_MODE_MAILBOX_KHR
|
|
};
|
|
|
|
static VkFormat SDLToVK_SurfaceFormat[] = {
|
|
VK_FORMAT_R8G8B8A8_UNORM, // R8G8B8A8_UNORM
|
|
VK_FORMAT_B8G8R8A8_UNORM, // B8G8R8A8_UNORM
|
|
VK_FORMAT_R5G6B5_UNORM_PACK16, // B5G6R5_UNORM
|
|
VK_FORMAT_A1R5G5B5_UNORM_PACK16, // B5G5R5A1_UNORM
|
|
VK_FORMAT_B4G4R4A4_UNORM_PACK16, // B4G4R4A4_UNORM
|
|
VK_FORMAT_A2B10G10R10_UNORM_PACK32, // R10G10B10A2_UNORM
|
|
VK_FORMAT_R16G16_UNORM, // R16G16_UNORM
|
|
VK_FORMAT_R16G16B16A16_UNORM, // R16G16B16A16_UNORM
|
|
VK_FORMAT_R8_UNORM, // R8_UNORM
|
|
VK_FORMAT_R8_UNORM, // A8_UNORM
|
|
VK_FORMAT_BC1_RGBA_UNORM_BLOCK, // BC1_UNORM
|
|
VK_FORMAT_BC2_UNORM_BLOCK, // BC2_UNORM
|
|
VK_FORMAT_BC3_UNORM_BLOCK, // BC3_UNORM
|
|
VK_FORMAT_BC7_UNORM_BLOCK, // BC7_UNORM
|
|
VK_FORMAT_R8G8_SNORM, // R8G8_SNORM
|
|
VK_FORMAT_R8G8B8A8_SNORM, // R8G8B8A8_SNORM
|
|
VK_FORMAT_R16_SFLOAT, // R16_FLOAT
|
|
VK_FORMAT_R16G16_SFLOAT, // R16G16_FLOAT
|
|
VK_FORMAT_R16G16B16A16_SFLOAT, // R16G16B16A16_FLOAT
|
|
VK_FORMAT_R32_SFLOAT, // R32_FLOAT
|
|
VK_FORMAT_R32G32_SFLOAT, // R32G32_FLOAT
|
|
VK_FORMAT_R32G32B32A32_SFLOAT, // R32G32B32A32_FLOAT
|
|
VK_FORMAT_R8_UINT, // R8_UINT
|
|
VK_FORMAT_R8G8_UINT, // R8G8_UINT
|
|
VK_FORMAT_R8G8B8A8_UINT, // R8G8B8A8_UINT
|
|
VK_FORMAT_R16_UINT, // R16_UINT
|
|
VK_FORMAT_R16G16_UINT, // R16G16_UINT
|
|
VK_FORMAT_R16G16B16A16_UINT, // R16G16B16A16_UINT
|
|
VK_FORMAT_R8G8B8A8_SRGB, // R8G8B8A8_UNORM_SRGB
|
|
VK_FORMAT_B8G8R8A8_SRGB, // B8G8R8A8_UNORM_SRGB
|
|
VK_FORMAT_BC3_SRGB_BLOCK, // BC3_UNORM_SRGB
|
|
VK_FORMAT_BC7_SRGB_BLOCK, // BC7_UNORM_SRGB
|
|
VK_FORMAT_D16_UNORM, // D16_UNORM
|
|
VK_FORMAT_X8_D24_UNORM_PACK32, // D24_UNORM
|
|
VK_FORMAT_D32_SFLOAT, // D32_FLOAT
|
|
VK_FORMAT_D24_UNORM_S8_UINT, // D24_UNORM_S8_UINT
|
|
VK_FORMAT_D32_SFLOAT_S8_UINT, // D32_FLOAT_S8_UINT
|
|
};
|
|
SDL_COMPILE_TIME_ASSERT(SDLToVK_SurfaceFormat, SDL_arraysize(SDLToVK_SurfaceFormat) == SDL_GPU_TEXTUREFORMAT_MAX);
|
|
|
|
static VkComponentMapping SDLToVK_SurfaceSwizzle[] = {
|
|
IDENTITY_SWIZZLE, // R8G8B8A8
|
|
IDENTITY_SWIZZLE, // B8G8R8A8
|
|
{
|
|
// B5G6R5
|
|
VK_COMPONENT_SWIZZLE_B,
|
|
VK_COMPONENT_SWIZZLE_G,
|
|
VK_COMPONENT_SWIZZLE_R,
|
|
VK_COMPONENT_SWIZZLE_ONE,
|
|
},
|
|
{
|
|
// B5G5R5A1
|
|
VK_COMPONENT_SWIZZLE_B,
|
|
VK_COMPONENT_SWIZZLE_G,
|
|
VK_COMPONENT_SWIZZLE_R,
|
|
VK_COMPONENT_SWIZZLE_A,
|
|
},
|
|
IDENTITY_SWIZZLE, // B4G4R4A4
|
|
{
|
|
// R10G10B10A2
|
|
VK_COMPONENT_SWIZZLE_R,
|
|
VK_COMPONENT_SWIZZLE_G,
|
|
VK_COMPONENT_SWIZZLE_B,
|
|
VK_COMPONENT_SWIZZLE_A,
|
|
},
|
|
IDENTITY_SWIZZLE, // R16G16
|
|
IDENTITY_SWIZZLE, // R16G16B16A16
|
|
IDENTITY_SWIZZLE, // R8
|
|
{
|
|
// A8
|
|
VK_COMPONENT_SWIZZLE_ZERO,
|
|
VK_COMPONENT_SWIZZLE_ZERO,
|
|
VK_COMPONENT_SWIZZLE_ZERO,
|
|
VK_COMPONENT_SWIZZLE_R,
|
|
},
|
|
IDENTITY_SWIZZLE, // BC1
|
|
IDENTITY_SWIZZLE, // BC2
|
|
IDENTITY_SWIZZLE, // BC3
|
|
IDENTITY_SWIZZLE, // BC7
|
|
IDENTITY_SWIZZLE, // R8G8_SNORM
|
|
IDENTITY_SWIZZLE, // R8G8B8A8_SNORM
|
|
IDENTITY_SWIZZLE, // R16_SFLOAT
|
|
IDENTITY_SWIZZLE, // R16G16_SFLOAT
|
|
IDENTITY_SWIZZLE, // R16G16B16A16_SFLOAT
|
|
IDENTITY_SWIZZLE, // R32_SFLOAT
|
|
IDENTITY_SWIZZLE, // R32G32_SFLOAT
|
|
IDENTITY_SWIZZLE, // R32G32B32A32_SFLOAT
|
|
IDENTITY_SWIZZLE, // R8_UINT
|
|
IDENTITY_SWIZZLE, // R8G8_UINT
|
|
IDENTITY_SWIZZLE, // R8G8B8A8_UINT
|
|
IDENTITY_SWIZZLE, // R16_UINT
|
|
IDENTITY_SWIZZLE, // R16G16_UINT
|
|
IDENTITY_SWIZZLE, // R16G16B16A16_UINT
|
|
IDENTITY_SWIZZLE, // R8G8B8A8_SRGB
|
|
IDENTITY_SWIZZLE, // B8G8R8A8_SRGB
|
|
IDENTITY_SWIZZLE, // BC3_SRGB
|
|
IDENTITY_SWIZZLE, // BC7_SRGB
|
|
IDENTITY_SWIZZLE, // D16_UNORM
|
|
IDENTITY_SWIZZLE, // D24_UNORM
|
|
IDENTITY_SWIZZLE, // D32_SFLOAT
|
|
IDENTITY_SWIZZLE, // D24_UNORM_S8_UINT
|
|
IDENTITY_SWIZZLE, // D32_SFLOAT_S8_UINT
|
|
};
|
|
|
|
static VkFormat SwapchainCompositionToFormat[] = {
|
|
VK_FORMAT_B8G8R8A8_UNORM, // SDR
|
|
VK_FORMAT_B8G8R8A8_SRGB, // SDR_LINEAR
|
|
VK_FORMAT_R16G16B16A16_SFLOAT, // HDR_EXTENDED_LINEAR
|
|
VK_FORMAT_A2B10G10R10_UNORM_PACK32 // HDR10_ST2048
|
|
};
|
|
|
|
static VkFormat SwapchainCompositionToFallbackFormat[] = {
|
|
VK_FORMAT_R8G8B8A8_UNORM,
|
|
VK_FORMAT_R8G8B8A8_SRGB,
|
|
VK_FORMAT_UNDEFINED, // no fallback
|
|
VK_FORMAT_UNDEFINED // no fallback
|
|
};
|
|
|
|
static SDL_GPUTextureFormat SwapchainCompositionToSDLFormat(
|
|
SDL_GPUSwapchainComposition composition,
|
|
bool usingFallback)
|
|
{
|
|
switch (composition) {
|
|
case SDL_GPU_SWAPCHAINCOMPOSITION_SDR:
|
|
return usingFallback ? SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM : SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
|
|
case SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR:
|
|
return usingFallback ? SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB : SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB;
|
|
case SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR:
|
|
return SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT;
|
|
case SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2048:
|
|
return SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM;
|
|
default:
|
|
return SDL_GPU_TEXTUREFORMAT_INVALID;
|
|
}
|
|
}
|
|
|
|
static VkColorSpaceKHR SwapchainCompositionToColorSpace[] = {
|
|
VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
|
VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
|
VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT,
|
|
VK_COLOR_SPACE_HDR10_ST2084_EXT
|
|
};
|
|
|
|
static VkComponentMapping SwapchainCompositionSwizzle[] = {
|
|
IDENTITY_SWIZZLE, // SDR
|
|
IDENTITY_SWIZZLE, // SDR_SRGB
|
|
IDENTITY_SWIZZLE, // HDR
|
|
{
|
|
// HDR_ADVANCED
|
|
VK_COMPONENT_SWIZZLE_R,
|
|
VK_COMPONENT_SWIZZLE_G,
|
|
VK_COMPONENT_SWIZZLE_B,
|
|
VK_COMPONENT_SWIZZLE_A,
|
|
}
|
|
};
|
|
|
|
static VkFormat SDLToVK_VertexFormat[] = {
|
|
VK_FORMAT_R32_SINT, // INT
|
|
VK_FORMAT_R32G32_SINT, // INT2
|
|
VK_FORMAT_R32G32B32_SINT, // INT3
|
|
VK_FORMAT_R32G32B32A32_SINT, // INT4
|
|
VK_FORMAT_R32_UINT, // UINT
|
|
VK_FORMAT_R32G32_UINT, // UINT2
|
|
VK_FORMAT_R32G32B32_UINT, // UINT3
|
|
VK_FORMAT_R32G32B32A32_UINT, // UINT4
|
|
VK_FORMAT_R32_SFLOAT, // FLOAT
|
|
VK_FORMAT_R32G32_SFLOAT, // FLOAT2
|
|
VK_FORMAT_R32G32B32_SFLOAT, // FLOAT3
|
|
VK_FORMAT_R32G32B32A32_SFLOAT, // FLOAT4
|
|
VK_FORMAT_R8G8_SINT, // BYTE2
|
|
VK_FORMAT_R8G8B8A8_SINT, // BYTE4
|
|
VK_FORMAT_R8G8_UINT, // UBYTE2
|
|
VK_FORMAT_R8G8B8A8_UINT, // UBYTE4
|
|
VK_FORMAT_R8G8_SNORM, // BYTE2_NORM
|
|
VK_FORMAT_R8G8B8A8_SNORM, // BYTE4_NORM
|
|
VK_FORMAT_R8G8_UNORM, // UBYTE2_NORM
|
|
VK_FORMAT_R8G8B8A8_UNORM, // UBYTE4_NORM
|
|
VK_FORMAT_R16G16_SINT, // SHORT2
|
|
VK_FORMAT_R16G16B16A16_SINT, // SHORT4
|
|
VK_FORMAT_R16G16_UINT, // USHORT2
|
|
VK_FORMAT_R16G16B16A16_UINT, // USHORT4
|
|
VK_FORMAT_R16G16_SNORM, // SHORT2_NORM
|
|
VK_FORMAT_R16G16B16A16_SNORM, // SHORT4_NORM
|
|
VK_FORMAT_R16G16_UNORM, // USHORT2_NORM
|
|
VK_FORMAT_R16G16B16A16_UNORM, // USHORT4_NORM
|
|
VK_FORMAT_R16G16_SFLOAT, // HALF2
|
|
VK_FORMAT_R16G16B16A16_SFLOAT // HALF4
|
|
};
|
|
|
|
static VkIndexType SDLToVK_IndexType[] = {
|
|
VK_INDEX_TYPE_UINT16,
|
|
VK_INDEX_TYPE_UINT32
|
|
};
|
|
|
|
static VkPrimitiveTopology SDLToVK_PrimitiveType[] = {
|
|
VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
|
|
VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
|
|
VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
|
|
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
|
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP
|
|
};
|
|
|
|
static VkCullModeFlags SDLToVK_CullMode[] = {
|
|
VK_CULL_MODE_NONE,
|
|
VK_CULL_MODE_FRONT_BIT,
|
|
VK_CULL_MODE_BACK_BIT,
|
|
VK_CULL_MODE_FRONT_AND_BACK
|
|
};
|
|
|
|
static VkFrontFace SDLToVK_FrontFace[] = {
|
|
VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
|
VK_FRONT_FACE_CLOCKWISE
|
|
};
|
|
|
|
static VkBlendFactor SDLToVK_BlendFactor[] = {
|
|
VK_BLEND_FACTOR_ZERO,
|
|
VK_BLEND_FACTOR_ONE,
|
|
VK_BLEND_FACTOR_SRC_COLOR,
|
|
VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
|
|
VK_BLEND_FACTOR_DST_COLOR,
|
|
VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,
|
|
VK_BLEND_FACTOR_SRC_ALPHA,
|
|
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
|
VK_BLEND_FACTOR_DST_ALPHA,
|
|
VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
|
|
VK_BLEND_FACTOR_CONSTANT_COLOR,
|
|
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
|
|
VK_BLEND_FACTOR_CONSTANT_ALPHA,
|
|
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
|
|
VK_BLEND_FACTOR_SRC_ALPHA_SATURATE
|
|
};
|
|
|
|
static VkBlendOp SDLToVK_BlendOp[] = {
|
|
VK_BLEND_OP_ADD,
|
|
VK_BLEND_OP_SUBTRACT,
|
|
VK_BLEND_OP_REVERSE_SUBTRACT,
|
|
VK_BLEND_OP_MIN,
|
|
VK_BLEND_OP_MAX
|
|
};
|
|
|
|
static VkCompareOp SDLToVK_CompareOp[] = {
|
|
VK_COMPARE_OP_NEVER,
|
|
VK_COMPARE_OP_LESS,
|
|
VK_COMPARE_OP_EQUAL,
|
|
VK_COMPARE_OP_LESS_OR_EQUAL,
|
|
VK_COMPARE_OP_GREATER,
|
|
VK_COMPARE_OP_NOT_EQUAL,
|
|
VK_COMPARE_OP_GREATER_OR_EQUAL,
|
|
VK_COMPARE_OP_ALWAYS
|
|
};
|
|
|
|
static VkStencilOp SDLToVK_StencilOp[] = {
|
|
VK_STENCIL_OP_KEEP,
|
|
VK_STENCIL_OP_ZERO,
|
|
VK_STENCIL_OP_REPLACE,
|
|
VK_STENCIL_OP_INCREMENT_AND_CLAMP,
|
|
VK_STENCIL_OP_DECREMENT_AND_CLAMP,
|
|
VK_STENCIL_OP_INVERT,
|
|
VK_STENCIL_OP_INCREMENT_AND_WRAP,
|
|
VK_STENCIL_OP_DECREMENT_AND_WRAP
|
|
};
|
|
|
|
static VkAttachmentLoadOp SDLToVK_LoadOp[] = {
|
|
VK_ATTACHMENT_LOAD_OP_LOAD,
|
|
VK_ATTACHMENT_LOAD_OP_CLEAR,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE
|
|
};
|
|
|
|
static VkAttachmentStoreOp SDLToVK_StoreOp[] = {
|
|
VK_ATTACHMENT_STORE_OP_STORE,
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE
|
|
};
|
|
|
|
static VkSampleCountFlagBits SDLToVK_SampleCount[] = {
|
|
VK_SAMPLE_COUNT_1_BIT,
|
|
VK_SAMPLE_COUNT_2_BIT,
|
|
VK_SAMPLE_COUNT_4_BIT,
|
|
VK_SAMPLE_COUNT_8_BIT
|
|
};
|
|
|
|
static VkVertexInputRate SDLToVK_VertexInputRate[] = {
|
|
VK_VERTEX_INPUT_RATE_VERTEX,
|
|
VK_VERTEX_INPUT_RATE_INSTANCE
|
|
};
|
|
|
|
static VkFilter SDLToVK_Filter[] = {
|
|
VK_FILTER_NEAREST,
|
|
VK_FILTER_LINEAR
|
|
};
|
|
|
|
static VkSamplerMipmapMode SDLToVK_SamplerMipmapMode[] = {
|
|
VK_SAMPLER_MIPMAP_MODE_NEAREST,
|
|
VK_SAMPLER_MIPMAP_MODE_LINEAR
|
|
};
|
|
|
|
static VkSamplerAddressMode SDLToVK_SamplerAddressMode[] = {
|
|
VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
|
VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
|
|
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE
|
|
};
|
|
|
|
// Structures
|
|
|
|
typedef struct VulkanMemoryAllocation VulkanMemoryAllocation;
|
|
typedef struct VulkanBuffer VulkanBuffer;
|
|
typedef struct VulkanBufferContainer VulkanBufferContainer;
|
|
typedef struct VulkanTexture VulkanTexture;
|
|
typedef struct VulkanTextureContainer VulkanTextureContainer;
|
|
|
|
typedef struct VulkanFenceHandle
|
|
{
|
|
VkFence fence;
|
|
SDL_AtomicInt referenceCount;
|
|
} VulkanFenceHandle;
|
|
|
|
// Memory Allocation
|
|
|
|
typedef struct VulkanMemoryFreeRegion
|
|
{
|
|
VulkanMemoryAllocation *allocation;
|
|
VkDeviceSize offset;
|
|
VkDeviceSize size;
|
|
Uint32 allocationIndex;
|
|
Uint32 sortedIndex;
|
|
} VulkanMemoryFreeRegion;
|
|
|
|
typedef struct VulkanMemoryUsedRegion
|
|
{
|
|
VulkanMemoryAllocation *allocation;
|
|
VkDeviceSize offset;
|
|
VkDeviceSize size;
|
|
VkDeviceSize resourceOffset; // differs from offset based on alignment
|
|
VkDeviceSize resourceSize; // differs from size based on alignment
|
|
VkDeviceSize alignment;
|
|
Uint8 isBuffer;
|
|
union
|
|
{
|
|
VulkanBuffer *vulkanBuffer;
|
|
VulkanTexture *vulkanTexture;
|
|
};
|
|
} VulkanMemoryUsedRegion;
|
|
|
|
typedef struct VulkanMemorySubAllocator
|
|
{
|
|
Uint32 memoryTypeIndex;
|
|
VulkanMemoryAllocation **allocations;
|
|
Uint32 allocationCount;
|
|
VulkanMemoryFreeRegion **sortedFreeRegions;
|
|
Uint32 sortedFreeRegionCount;
|
|
Uint32 sortedFreeRegionCapacity;
|
|
} VulkanMemorySubAllocator;
|
|
|
|
struct VulkanMemoryAllocation
|
|
{
|
|
VulkanMemorySubAllocator *allocator;
|
|
VkDeviceMemory memory;
|
|
VkDeviceSize size;
|
|
VulkanMemoryUsedRegion **usedRegions;
|
|
Uint32 usedRegionCount;
|
|
Uint32 usedRegionCapacity;
|
|
VulkanMemoryFreeRegion **freeRegions;
|
|
Uint32 freeRegionCount;
|
|
Uint32 freeRegionCapacity;
|
|
Uint8 availableForAllocation;
|
|
VkDeviceSize freeSpace;
|
|
VkDeviceSize usedSpace;
|
|
Uint8 *mapPointer;
|
|
SDL_Mutex *memoryLock;
|
|
};
|
|
|
|
typedef struct VulkanMemoryAllocator
|
|
{
|
|
VulkanMemorySubAllocator subAllocators[VK_MAX_MEMORY_TYPES];
|
|
} VulkanMemoryAllocator;
|
|
|
|
// Memory structures
|
|
|
|
/* We use pointer indirection so that defrag can occur without objects
|
|
* needing to be aware of the backing buffers changing.
|
|
*/
|
|
typedef struct VulkanBufferHandle
|
|
{
|
|
VulkanBuffer *vulkanBuffer;
|
|
VulkanBufferContainer *container;
|
|
} VulkanBufferHandle;
|
|
|
|
typedef enum VulkanBufferType
|
|
{
|
|
VULKAN_BUFFER_TYPE_GPU,
|
|
VULKAN_BUFFER_TYPE_UNIFORM,
|
|
VULKAN_BUFFER_TYPE_TRANSFER
|
|
} VulkanBufferType;
|
|
|
|
struct VulkanBuffer
|
|
{
|
|
VkBuffer buffer;
|
|
VkDeviceSize size;
|
|
VulkanMemoryUsedRegion *usedRegion;
|
|
|
|
VulkanBufferType type;
|
|
SDL_GPUBufferUsageFlags usageFlags;
|
|
|
|
SDL_AtomicInt referenceCount; // Tracks command buffer usage
|
|
|
|
VulkanBufferHandle *handle;
|
|
|
|
bool transitioned;
|
|
Uint8 markedForDestroy; // so that defrag doesn't double-free
|
|
};
|
|
|
|
/* Buffer resources consist of multiple backing buffer handles so that data transfers
|
|
* can occur without blocking or the client having to manage extra resources.
|
|
*
|
|
* Cast from SDL_GPUBuffer or SDL_GPUTransferBuffer.
|
|
*/
|
|
struct VulkanBufferContainer
|
|
{
|
|
VulkanBufferHandle *activeBufferHandle;
|
|
|
|
/* These are all the buffer handles that have been used by this container.
|
|
* If the resource is bound and then updated with a cycle parameter, a new resource
|
|
* will be added to this list.
|
|
* These can be reused after they are submitted and command processing is complete.
|
|
*/
|
|
Uint32 bufferCapacity;
|
|
Uint32 bufferCount;
|
|
VulkanBufferHandle **bufferHandles;
|
|
|
|
char *debugName;
|
|
};
|
|
|
|
// Renderer Structure
|
|
|
|
typedef struct QueueFamilyIndices
|
|
{
|
|
Uint32 graphicsFamily;
|
|
Uint32 presentFamily;
|
|
Uint32 computeFamily;
|
|
Uint32 transferFamily;
|
|
} QueueFamilyIndices;
|
|
|
|
typedef struct VulkanSampler
|
|
{
|
|
VkSampler sampler;
|
|
SDL_AtomicInt referenceCount;
|
|
} VulkanSampler;
|
|
|
|
typedef struct VulkanShader
|
|
{
|
|
VkShaderModule shaderModule;
|
|
const char *entryPointName;
|
|
Uint32 samplerCount;
|
|
Uint32 storageTextureCount;
|
|
Uint32 storageBufferCount;
|
|
Uint32 uniformBufferCount;
|
|
SDL_AtomicInt referenceCount;
|
|
} VulkanShader;
|
|
|
|
typedef struct VulkanTextureHandle
|
|
{
|
|
VulkanTexture *vulkanTexture;
|
|
VulkanTextureContainer *container;
|
|
} VulkanTextureHandle;
|
|
|
|
/* Textures are made up of individual subresources.
|
|
* This helps us barrier the resource efficiently.
|
|
*/
|
|
typedef struct VulkanTextureSubresource
|
|
{
|
|
VulkanTexture *parent;
|
|
Uint32 layer;
|
|
Uint32 level;
|
|
|
|
VkImageView *renderTargetViews; // One render target view per depth slice
|
|
VkImageView computeWriteView;
|
|
VkImageView depthStencilView;
|
|
|
|
VulkanTextureHandle *msaaTexHandle; // NULL if parent sample count is 1 or is depth target
|
|
|
|
bool transitioned; // used for layout tracking
|
|
} VulkanTextureSubresource;
|
|
|
|
struct VulkanTexture
|
|
{
|
|
VulkanMemoryUsedRegion *usedRegion;
|
|
|
|
VkImage image;
|
|
VkImageView fullView; // used for samplers and storage reads
|
|
VkExtent2D dimensions;
|
|
|
|
SDL_GPUTextureType type;
|
|
Uint8 isMSAAColorTarget;
|
|
|
|
Uint32 depth;
|
|
Uint32 layerCount;
|
|
Uint32 levelCount;
|
|
VkSampleCountFlagBits sampleCount; // NOTE: This refers to the sample count of a render target pass using this texture, not the actual sample count of the texture
|
|
VkFormat format;
|
|
VkComponentMapping swizzle;
|
|
SDL_GPUTextureUsageFlags usageFlags;
|
|
VkImageAspectFlags aspectFlags;
|
|
|
|
Uint32 subresourceCount;
|
|
VulkanTextureSubresource *subresources;
|
|
|
|
VulkanTextureHandle *handle;
|
|
|
|
Uint8 markedForDestroy; // so that defrag doesn't double-free
|
|
SDL_AtomicInt referenceCount;
|
|
};
|
|
|
|
/* Texture resources consist of multiple backing texture handles so that data transfers
|
|
* can occur without blocking or the client having to manage extra resources.
|
|
*
|
|
* Cast from SDL_GPUTexture.
|
|
*/
|
|
struct VulkanTextureContainer
|
|
{
|
|
TextureCommonHeader header; // FIXME: Use this instead of passing so many args to CreateTexture
|
|
|
|
VulkanTextureHandle *activeTextureHandle;
|
|
|
|
/* These are all the texture handles that have been used by this container.
|
|
* If the resource is bound and then updated with CYCLE, a new resource
|
|
* will be added to this list.
|
|
* These can be reused after they are submitted and command processing is complete.
|
|
*/
|
|
Uint32 textureCapacity;
|
|
Uint32 textureCount;
|
|
VulkanTextureHandle **textureHandles;
|
|
|
|
// Swapchain images cannot be cycled
|
|
Uint8 canBeCycled;
|
|
|
|
char *debugName;
|
|
};
|
|
|
|
typedef enum VulkanBufferUsageMode
|
|
{
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
|
|
VULKAN_BUFFER_USAGE_MODE_VERTEX_READ,
|
|
VULKAN_BUFFER_USAGE_MODE_INDEX_READ,
|
|
VULKAN_BUFFER_USAGE_MODE_INDIRECT,
|
|
VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ,
|
|
VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ,
|
|
VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE,
|
|
} VulkanBufferUsageMode;
|
|
|
|
typedef enum VulkanTextureUsageMode
|
|
{
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
|
|
VULKAN_TEXTURE_USAGE_MODE_SAMPLER,
|
|
VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ,
|
|
VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
|
|
VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE,
|
|
VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT,
|
|
VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT,
|
|
VULKAN_TEXTURE_USAGE_MODE_PRESENT
|
|
} VulkanTextureUsageMode;
|
|
|
|
typedef enum VulkanUniformBufferStage
|
|
{
|
|
VULKAN_UNIFORM_BUFFER_STAGE_VERTEX,
|
|
VULKAN_UNIFORM_BUFFER_STAGE_FRAGMENT,
|
|
VULKAN_UNIFORM_BUFFER_STAGE_COMPUTE
|
|
} VulkanUniformBufferStage;
|
|
|
|
typedef struct VulkanFramebuffer
|
|
{
|
|
VkFramebuffer framebuffer;
|
|
SDL_AtomicInt referenceCount;
|
|
} VulkanFramebuffer;
|
|
|
|
typedef struct VulkanSwapchainData
|
|
{
|
|
// Window surface
|
|
VkSurfaceKHR surface;
|
|
|
|
// Swapchain for window surface
|
|
VkSwapchainKHR swapchain;
|
|
VkFormat format;
|
|
VkColorSpaceKHR colorSpace;
|
|
VkComponentMapping swapchainSwizzle;
|
|
VkPresentModeKHR presentMode;
|
|
bool usingFallbackFormat;
|
|
|
|
// Swapchain images
|
|
VulkanTextureContainer *textureContainers; // use containers so that swapchain textures can use the same API as other textures
|
|
Uint32 imageCount;
|
|
|
|
// Synchronization primitives
|
|
VkSemaphore imageAvailableSemaphore[MAX_FRAMES_IN_FLIGHT];
|
|
VkSemaphore renderFinishedSemaphore[MAX_FRAMES_IN_FLIGHT];
|
|
VulkanFenceHandle *inFlightFences[MAX_FRAMES_IN_FLIGHT];
|
|
|
|
Uint32 frameCounter;
|
|
} VulkanSwapchainData;
|
|
|
|
typedef struct WindowData
|
|
{
|
|
SDL_Window *window;
|
|
SDL_GPUSwapchainComposition swapchainComposition;
|
|
SDL_GPUPresentMode presentMode;
|
|
VulkanSwapchainData *swapchainData;
|
|
bool needsSwapchainRecreate;
|
|
} WindowData;
|
|
|
|
typedef struct SwapchainSupportDetails
|
|
{
|
|
VkSurfaceCapabilitiesKHR capabilities;
|
|
VkSurfaceFormatKHR *formats;
|
|
Uint32 formatsLength;
|
|
VkPresentModeKHR *presentModes;
|
|
Uint32 presentModesLength;
|
|
} SwapchainSupportDetails;
|
|
|
|
typedef struct VulkanPresentData
|
|
{
|
|
WindowData *windowData;
|
|
Uint32 swapchainImageIndex;
|
|
} VulkanPresentData;
|
|
|
|
typedef struct VulkanUniformBuffer
|
|
{
|
|
VulkanBufferHandle *bufferHandle;
|
|
Uint32 drawOffset;
|
|
Uint32 writeOffset;
|
|
} VulkanUniformBuffer;
|
|
|
|
typedef struct VulkanDescriptorInfo
|
|
{
|
|
VkDescriptorType descriptorType;
|
|
VkShaderStageFlagBits stageFlag;
|
|
} VulkanDescriptorInfo;
|
|
|
|
typedef struct DescriptorSetPool
|
|
{
|
|
SDL_Mutex *lock;
|
|
|
|
VkDescriptorSetLayout descriptorSetLayout;
|
|
|
|
VulkanDescriptorInfo *descriptorInfos;
|
|
Uint32 descriptorInfoCount;
|
|
|
|
// This is actually a descriptor set and descriptor pool simultaneously
|
|
VkDescriptorPool *descriptorPools;
|
|
Uint32 descriptorPoolCount;
|
|
Uint32 nextPoolSize;
|
|
|
|
// We just manage a pool ourselves instead of freeing the sets
|
|
VkDescriptorSet *inactiveDescriptorSets;
|
|
Uint32 inactiveDescriptorSetCount;
|
|
Uint32 inactiveDescriptorSetCapacity;
|
|
} DescriptorSetPool;
|
|
|
|
typedef struct VulkanGraphicsPipelineResourceLayout
|
|
{
|
|
VkPipelineLayout pipelineLayout;
|
|
|
|
/*
|
|
* Descriptor set layout is as follows:
|
|
* 0: vertex resources
|
|
* 1: vertex uniform buffers
|
|
* 2: fragment resources
|
|
* 3: fragment uniform buffers
|
|
*/
|
|
DescriptorSetPool descriptorSetPools[4];
|
|
|
|
Uint32 vertexSamplerCount;
|
|
Uint32 vertexStorageBufferCount;
|
|
Uint32 vertexStorageTextureCount;
|
|
Uint32 vertexUniformBufferCount;
|
|
|
|
Uint32 fragmentSamplerCount;
|
|
Uint32 fragmentStorageBufferCount;
|
|
Uint32 fragmentStorageTextureCount;
|
|
Uint32 fragmentUniformBufferCount;
|
|
} VulkanGraphicsPipelineResourceLayout;
|
|
|
|
typedef struct VulkanGraphicsPipeline
|
|
{
|
|
VkPipeline pipeline;
|
|
SDL_GPUPrimitiveType primitiveType;
|
|
|
|
VulkanGraphicsPipelineResourceLayout resourceLayout;
|
|
|
|
VulkanShader *vertexShader;
|
|
VulkanShader *fragmentShader;
|
|
|
|
SDL_AtomicInt referenceCount;
|
|
} VulkanGraphicsPipeline;
|
|
|
|
typedef struct VulkanComputePipelineResourceLayout
|
|
{
|
|
VkPipelineLayout pipelineLayout;
|
|
|
|
/*
|
|
* Descriptor set layout is as follows:
|
|
* 0: read-only textures, then read-only buffers
|
|
* 1: write-only textures, then write-only buffers
|
|
* 2: uniform buffers
|
|
*/
|
|
DescriptorSetPool descriptorSetPools[3];
|
|
|
|
Uint32 readOnlyStorageTextureCount;
|
|
Uint32 readOnlyStorageBufferCount;
|
|
Uint32 writeOnlyStorageTextureCount;
|
|
Uint32 writeOnlyStorageBufferCount;
|
|
Uint32 uniformBufferCount;
|
|
} VulkanComputePipelineResourceLayout;
|
|
|
|
typedef struct VulkanComputePipeline
|
|
{
|
|
VkShaderModule shaderModule;
|
|
VkPipeline pipeline;
|
|
VulkanComputePipelineResourceLayout resourceLayout;
|
|
SDL_AtomicInt referenceCount;
|
|
} VulkanComputePipeline;
|
|
|
|
typedef struct RenderPassColorTargetDescription
|
|
{
|
|
VkFormat format;
|
|
SDL_GPULoadOp loadOp;
|
|
SDL_GPUStoreOp storeOp;
|
|
} RenderPassColorTargetDescription;
|
|
|
|
typedef struct RenderPassDepthStencilTargetDescription
|
|
{
|
|
VkFormat format;
|
|
SDL_GPULoadOp loadOp;
|
|
SDL_GPUStoreOp storeOp;
|
|
SDL_GPULoadOp stencilLoadOp;
|
|
SDL_GPUStoreOp stencilStoreOp;
|
|
} RenderPassDepthStencilTargetDescription;
|
|
|
|
typedef struct CommandPoolHashTableKey
|
|
{
|
|
SDL_ThreadID threadID;
|
|
} CommandPoolHashTableKey;
|
|
|
|
typedef struct RenderPassHashTableKey
|
|
{
|
|
RenderPassColorTargetDescription colorTargetDescriptions[MAX_COLOR_TARGET_BINDINGS];
|
|
Uint32 colorAttachmentCount;
|
|
RenderPassDepthStencilTargetDescription depthStencilTargetDescription;
|
|
VkSampleCountFlagBits colorAttachmentSampleCount;
|
|
} RenderPassHashTableKey;
|
|
|
|
typedef struct VulkanRenderPassHashTableValue
|
|
{
|
|
VkRenderPass handle;
|
|
} VulkanRenderPassHashTableValue;
|
|
|
|
typedef struct FramebufferHashTableKey
|
|
{
|
|
VkImageView colorAttachmentViews[MAX_COLOR_TARGET_BINDINGS];
|
|
VkImageView colorMultiSampleAttachmentViews[MAX_COLOR_TARGET_BINDINGS];
|
|
Uint32 colorAttachmentCount;
|
|
VkImageView depthStencilAttachmentView;
|
|
Uint32 width;
|
|
Uint32 height;
|
|
} FramebufferHashTableKey;
|
|
|
|
// Command structures
|
|
|
|
typedef struct DescriptorSetData
|
|
{
|
|
DescriptorSetPool *descriptorSetPool;
|
|
VkDescriptorSet descriptorSet;
|
|
} DescriptorSetData;
|
|
|
|
typedef struct VulkanFencePool
|
|
{
|
|
SDL_Mutex *lock;
|
|
|
|
VulkanFenceHandle **availableFences;
|
|
Uint32 availableFenceCount;
|
|
Uint32 availableFenceCapacity;
|
|
} VulkanFencePool;
|
|
|
|
typedef struct VulkanCommandPool VulkanCommandPool;
|
|
|
|
typedef struct VulkanRenderer VulkanRenderer;
|
|
|
|
typedef struct VulkanCommandBuffer
|
|
{
|
|
CommandBufferCommonHeader common;
|
|
VulkanRenderer *renderer;
|
|
|
|
VkCommandBuffer commandBuffer;
|
|
VulkanCommandPool *commandPool;
|
|
|
|
VulkanPresentData *presentDatas;
|
|
Uint32 presentDataCount;
|
|
Uint32 presentDataCapacity;
|
|
|
|
VkSemaphore *waitSemaphores;
|
|
Uint32 waitSemaphoreCount;
|
|
Uint32 waitSemaphoreCapacity;
|
|
|
|
VkSemaphore *signalSemaphores;
|
|
Uint32 signalSemaphoreCount;
|
|
Uint32 signalSemaphoreCapacity;
|
|
|
|
VulkanComputePipeline *currentComputePipeline;
|
|
VulkanGraphicsPipeline *currentGraphicsPipeline;
|
|
|
|
// Keep track of resources transitioned away from their default state to barrier them on pass end
|
|
|
|
VulkanTextureSubresource *colorAttachmentSubresources[MAX_COLOR_TARGET_BINDINGS];
|
|
Uint32 colorAttachmentSubresourceCount;
|
|
|
|
VulkanTextureSubresource *depthStencilAttachmentSubresource; // may be NULL
|
|
|
|
// Viewport/scissor state
|
|
|
|
VkViewport currentViewport;
|
|
VkRect2D currentScissor;
|
|
|
|
// Resource bind state
|
|
|
|
bool needNewVertexResourceDescriptorSet;
|
|
bool needNewVertexUniformDescriptorSet;
|
|
bool needNewVertexUniformOffsets;
|
|
bool needNewFragmentResourceDescriptorSet;
|
|
bool needNewFragmentUniformDescriptorSet;
|
|
bool needNewFragmentUniformOffsets;
|
|
|
|
bool needNewComputeReadOnlyDescriptorSet;
|
|
bool needNewComputeWriteOnlyDescriptorSet;
|
|
bool needNewComputeUniformDescriptorSet;
|
|
bool needNewComputeUniformOffsets;
|
|
|
|
VkDescriptorSet vertexResourceDescriptorSet;
|
|
VkDescriptorSet vertexUniformDescriptorSet;
|
|
VkDescriptorSet fragmentResourceDescriptorSet;
|
|
VkDescriptorSet fragmentUniformDescriptorSet;
|
|
|
|
VkDescriptorSet computeReadOnlyDescriptorSet;
|
|
VkDescriptorSet computeWriteOnlyDescriptorSet;
|
|
VkDescriptorSet computeUniformDescriptorSet;
|
|
|
|
DescriptorSetData *boundDescriptorSetDatas;
|
|
Uint32 boundDescriptorSetDataCount;
|
|
Uint32 boundDescriptorSetDataCapacity;
|
|
|
|
VulkanTexture *vertexSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
VulkanSampler *vertexSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
VulkanTexture *vertexStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
|
|
VulkanBuffer *vertexStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
|
|
|
|
VulkanTexture *fragmentSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
VulkanSampler *fragmentSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
VulkanTexture *fragmentStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
|
|
VulkanBuffer *fragmentStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
|
|
|
|
VulkanTextureSubresource *writeOnlyComputeStorageTextureSubresources[MAX_COMPUTE_WRITE_TEXTURES];
|
|
Uint32 writeOnlyComputeStorageTextureSubresourceCount;
|
|
VulkanBuffer *writeOnlyComputeStorageBuffers[MAX_COMPUTE_WRITE_BUFFERS];
|
|
|
|
VulkanTexture *readOnlyComputeStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
|
|
VulkanBuffer *readOnlyComputeStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
|
|
|
|
// Uniform buffers
|
|
|
|
VulkanUniformBuffer *vertexUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
VulkanUniformBuffer *fragmentUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
VulkanUniformBuffer *computeUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
|
|
// Track used resources
|
|
|
|
VulkanBuffer **usedBuffers;
|
|
Uint32 usedBufferCount;
|
|
Uint32 usedBufferCapacity;
|
|
|
|
VulkanTexture **usedTextures;
|
|
Uint32 usedTextureCount;
|
|
Uint32 usedTextureCapacity;
|
|
|
|
VulkanSampler **usedSamplers;
|
|
Uint32 usedSamplerCount;
|
|
Uint32 usedSamplerCapacity;
|
|
|
|
VulkanGraphicsPipeline **usedGraphicsPipelines;
|
|
Uint32 usedGraphicsPipelineCount;
|
|
Uint32 usedGraphicsPipelineCapacity;
|
|
|
|
VulkanComputePipeline **usedComputePipelines;
|
|
Uint32 usedComputePipelineCount;
|
|
Uint32 usedComputePipelineCapacity;
|
|
|
|
VulkanFramebuffer **usedFramebuffers;
|
|
Uint32 usedFramebufferCount;
|
|
Uint32 usedFramebufferCapacity;
|
|
|
|
VulkanUniformBuffer **usedUniformBuffers;
|
|
Uint32 usedUniformBufferCount;
|
|
Uint32 usedUniformBufferCapacity;
|
|
|
|
VulkanFenceHandle *inFlightFence;
|
|
Uint8 autoReleaseFence;
|
|
|
|
Uint8 isDefrag; // Whether this CB was created for defragging
|
|
} VulkanCommandBuffer;
|
|
|
|
struct VulkanCommandPool
|
|
{
|
|
SDL_ThreadID threadID;
|
|
VkCommandPool commandPool;
|
|
|
|
VulkanCommandBuffer **inactiveCommandBuffers;
|
|
Uint32 inactiveCommandBufferCapacity;
|
|
Uint32 inactiveCommandBufferCount;
|
|
};
|
|
|
|
// Context
|
|
|
|
struct VulkanRenderer
|
|
{
|
|
VkInstance instance;
|
|
VkPhysicalDevice physicalDevice;
|
|
VkPhysicalDeviceProperties2KHR physicalDeviceProperties;
|
|
VkPhysicalDeviceDriverPropertiesKHR physicalDeviceDriverProperties;
|
|
VkDevice logicalDevice;
|
|
Uint8 integratedMemoryNotification;
|
|
Uint8 outOfDeviceLocalMemoryWarning;
|
|
Uint8 outofBARMemoryWarning;
|
|
Uint8 fillModeOnlyWarning;
|
|
|
|
bool debugMode;
|
|
bool preferLowPower;
|
|
VulkanExtensions supports;
|
|
bool supportsDebugUtils;
|
|
bool supportsColorspace;
|
|
bool supportsFillModeNonSolid;
|
|
bool supportsMultiDrawIndirect;
|
|
|
|
VulkanMemoryAllocator *memoryAllocator;
|
|
VkPhysicalDeviceMemoryProperties memoryProperties;
|
|
|
|
WindowData **claimedWindows;
|
|
Uint32 claimedWindowCount;
|
|
Uint32 claimedWindowCapacity;
|
|
|
|
Uint32 queueFamilyIndex;
|
|
VkQueue unifiedQueue;
|
|
|
|
VulkanCommandBuffer **submittedCommandBuffers;
|
|
Uint32 submittedCommandBufferCount;
|
|
Uint32 submittedCommandBufferCapacity;
|
|
|
|
VulkanFencePool fencePool;
|
|
|
|
SDL_HashTable *commandPoolHashTable;
|
|
SDL_HashTable *renderPassHashTable;
|
|
SDL_HashTable *framebufferHashTable;
|
|
|
|
VulkanUniformBuffer **uniformBufferPool;
|
|
Uint32 uniformBufferPoolCount;
|
|
Uint32 uniformBufferPoolCapacity;
|
|
|
|
Uint32 minUBOAlignment;
|
|
|
|
// Some drivers don't support D16 for some reason. Fun!
|
|
VkFormat D16Format;
|
|
VkFormat D16S8Format;
|
|
|
|
// Deferred resource destruction
|
|
|
|
VulkanTexture **texturesToDestroy;
|
|
Uint32 texturesToDestroyCount;
|
|
Uint32 texturesToDestroyCapacity;
|
|
|
|
VulkanBuffer **buffersToDestroy;
|
|
Uint32 buffersToDestroyCount;
|
|
Uint32 buffersToDestroyCapacity;
|
|
|
|
VulkanSampler **samplersToDestroy;
|
|
Uint32 samplersToDestroyCount;
|
|
Uint32 samplersToDestroyCapacity;
|
|
|
|
VulkanGraphicsPipeline **graphicsPipelinesToDestroy;
|
|
Uint32 graphicsPipelinesToDestroyCount;
|
|
Uint32 graphicsPipelinesToDestroyCapacity;
|
|
|
|
VulkanComputePipeline **computePipelinesToDestroy;
|
|
Uint32 computePipelinesToDestroyCount;
|
|
Uint32 computePipelinesToDestroyCapacity;
|
|
|
|
VulkanShader **shadersToDestroy;
|
|
Uint32 shadersToDestroyCount;
|
|
Uint32 shadersToDestroyCapacity;
|
|
|
|
VulkanFramebuffer **framebuffersToDestroy;
|
|
Uint32 framebuffersToDestroyCount;
|
|
Uint32 framebuffersToDestroyCapacity;
|
|
|
|
SDL_Mutex *allocatorLock;
|
|
SDL_Mutex *disposeLock;
|
|
SDL_Mutex *submitLock;
|
|
SDL_Mutex *acquireCommandBufferLock;
|
|
SDL_Mutex *acquireUniformBufferLock;
|
|
SDL_Mutex *renderPassFetchLock;
|
|
SDL_Mutex *framebufferFetchLock;
|
|
|
|
Uint8 defragInProgress;
|
|
|
|
VulkanMemoryAllocation **allocationsToDefrag;
|
|
Uint32 allocationsToDefragCount;
|
|
Uint32 allocationsToDefragCapacity;
|
|
|
|
#define VULKAN_INSTANCE_FUNCTION(func) \
|
|
PFN_##func func;
|
|
#define VULKAN_DEVICE_FUNCTION(func) \
|
|
PFN_##func func;
|
|
#include "SDL_gpu_vulkan_vkfuncs.h"
|
|
};
|
|
|
|
// Forward declarations
|
|
|
|
static Uint8 VULKAN_INTERNAL_DefragmentMemory(VulkanRenderer *renderer);
|
|
static void VULKAN_INTERNAL_BeginCommandBuffer(VulkanRenderer *renderer, VulkanCommandBuffer *commandBuffer);
|
|
static void VULKAN_ReleaseWindow(SDL_GPURenderer *driverData, SDL_Window *window);
|
|
static void VULKAN_Wait(SDL_GPURenderer *driverData);
|
|
static void VULKAN_WaitForFences(SDL_GPURenderer *driverData, bool waitAll, SDL_GPUFence **pFences, Uint32 fenceCount);
|
|
static void VULKAN_Submit(SDL_GPUCommandBuffer *commandBuffer);
|
|
static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
|
|
VulkanRenderer *renderer,
|
|
Uint32 width,
|
|
Uint32 height,
|
|
Uint32 depth,
|
|
SDL_GPUTextureType type,
|
|
Uint32 layerCount,
|
|
Uint32 levelCount,
|
|
VkSampleCountFlagBits sampleCount,
|
|
VkFormat format,
|
|
VkComponentMapping swizzle,
|
|
VkImageAspectFlags aspectMask,
|
|
SDL_GPUTextureUsageFlags textureUsageFlags,
|
|
bool isMSAAColorTarget);
|
|
|
|
// Error Handling
|
|
|
|
static inline const char *VkErrorMessages(VkResult code)
|
|
{
|
|
#define ERR_TO_STR(e) \
|
|
case e: \
|
|
return #e;
|
|
switch (code) {
|
|
ERR_TO_STR(VK_ERROR_OUT_OF_HOST_MEMORY)
|
|
ERR_TO_STR(VK_ERROR_OUT_OF_DEVICE_MEMORY)
|
|
ERR_TO_STR(VK_ERROR_FRAGMENTED_POOL)
|
|
ERR_TO_STR(VK_ERROR_OUT_OF_POOL_MEMORY)
|
|
ERR_TO_STR(VK_ERROR_INITIALIZATION_FAILED)
|
|
ERR_TO_STR(VK_ERROR_LAYER_NOT_PRESENT)
|
|
ERR_TO_STR(VK_ERROR_EXTENSION_NOT_PRESENT)
|
|
ERR_TO_STR(VK_ERROR_FEATURE_NOT_PRESENT)
|
|
ERR_TO_STR(VK_ERROR_TOO_MANY_OBJECTS)
|
|
ERR_TO_STR(VK_ERROR_DEVICE_LOST)
|
|
ERR_TO_STR(VK_ERROR_INCOMPATIBLE_DRIVER)
|
|
ERR_TO_STR(VK_ERROR_OUT_OF_DATE_KHR)
|
|
ERR_TO_STR(VK_ERROR_SURFACE_LOST_KHR)
|
|
ERR_TO_STR(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)
|
|
ERR_TO_STR(VK_SUBOPTIMAL_KHR)
|
|
default:
|
|
return "Unhandled VkResult!";
|
|
}
|
|
#undef ERR_TO_STR
|
|
}
|
|
|
|
static inline void LogVulkanResultAsError(
|
|
const char *vulkanFunctionName,
|
|
VkResult result)
|
|
{
|
|
if (result != VK_SUCCESS) {
|
|
SDL_LogError(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"%s: %s",
|
|
vulkanFunctionName,
|
|
VkErrorMessages(result));
|
|
}
|
|
}
|
|
|
|
#define VULKAN_ERROR_CHECK(res, fn, ret) \
|
|
if (res != VK_SUCCESS) { \
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s %s", #fn, VkErrorMessages(res)); \
|
|
return ret; \
|
|
}
|
|
|
|
// Utility
|
|
|
|
static inline bool VULKAN_INTERNAL_IsVulkanDepthFormat(VkFormat format)
|
|
{
|
|
// FIXME: Can we refactor and use the regular IsDepthFormat for this?
|
|
return (
|
|
format == SDLToVK_SurfaceFormat[SDL_GPU_TEXTUREFORMAT_D16_UNORM] ||
|
|
format == SDLToVK_SurfaceFormat[SDL_GPU_TEXTUREFORMAT_D24_UNORM] ||
|
|
format == SDLToVK_SurfaceFormat[SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT] ||
|
|
format == SDLToVK_SurfaceFormat[SDL_GPU_TEXTUREFORMAT_D32_FLOAT] ||
|
|
format == SDLToVK_SurfaceFormat[SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT]);
|
|
}
|
|
|
|
static inline VkSampleCountFlagBits VULKAN_INTERNAL_GetMaxMultiSampleCount(
|
|
VulkanRenderer *renderer,
|
|
VkSampleCountFlagBits multiSampleCount)
|
|
{
|
|
VkSampleCountFlags flags = renderer->physicalDeviceProperties.properties.limits.framebufferColorSampleCounts;
|
|
VkSampleCountFlagBits maxSupported = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
if (flags & VK_SAMPLE_COUNT_8_BIT) {
|
|
maxSupported = VK_SAMPLE_COUNT_8_BIT;
|
|
} else if (flags & VK_SAMPLE_COUNT_4_BIT) {
|
|
maxSupported = VK_SAMPLE_COUNT_4_BIT;
|
|
} else if (flags & VK_SAMPLE_COUNT_2_BIT) {
|
|
maxSupported = VK_SAMPLE_COUNT_2_BIT;
|
|
}
|
|
|
|
return SDL_min(multiSampleCount, maxSupported);
|
|
}
|
|
|
|
static inline VkPolygonMode SDLToVK_PolygonMode(
|
|
VulkanRenderer *renderer,
|
|
SDL_GPUFillMode mode)
|
|
{
|
|
if (mode == SDL_GPU_FILLMODE_FILL) {
|
|
return VK_POLYGON_MODE_FILL; // always available!
|
|
}
|
|
|
|
if (renderer->supportsFillModeNonSolid && mode == SDL_GPU_FILLMODE_LINE) {
|
|
return VK_POLYGON_MODE_LINE;
|
|
}
|
|
|
|
if (!renderer->fillModeOnlyWarning) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Unsupported fill mode requested, using FILL!");
|
|
renderer->fillModeOnlyWarning = 1;
|
|
}
|
|
return VK_POLYGON_MODE_FILL;
|
|
}
|
|
|
|
// Memory Management
|
|
|
|
// Vulkan: Memory Allocation
|
|
|
|
static inline VkDeviceSize VULKAN_INTERNAL_NextHighestAlignment(
|
|
VkDeviceSize n,
|
|
VkDeviceSize align)
|
|
{
|
|
return align * ((n + align - 1) / align);
|
|
}
|
|
|
|
static inline Uint32 VULKAN_INTERNAL_NextHighestAlignment32(
|
|
Uint32 n,
|
|
Uint32 align)
|
|
{
|
|
return align * ((n + align - 1) / align);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_MakeMemoryUnavailable(
|
|
VulkanRenderer *renderer,
|
|
VulkanMemoryAllocation *allocation)
|
|
{
|
|
Uint32 i, j;
|
|
VulkanMemoryFreeRegion *freeRegion;
|
|
|
|
allocation->availableForAllocation = 0;
|
|
|
|
for (i = 0; i < allocation->freeRegionCount; i += 1) {
|
|
freeRegion = allocation->freeRegions[i];
|
|
|
|
// close the gap in the sorted list
|
|
if (allocation->allocator->sortedFreeRegionCount > 1) {
|
|
for (j = freeRegion->sortedIndex; j < allocation->allocator->sortedFreeRegionCount - 1; j += 1) {
|
|
allocation->allocator->sortedFreeRegions[j] =
|
|
allocation->allocator->sortedFreeRegions[j + 1];
|
|
|
|
allocation->allocator->sortedFreeRegions[j]->sortedIndex = j;
|
|
}
|
|
}
|
|
|
|
allocation->allocator->sortedFreeRegionCount -= 1;
|
|
}
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_MarkAllocationsForDefrag(
|
|
VulkanRenderer *renderer)
|
|
{
|
|
Uint32 memoryType, allocationIndex;
|
|
VulkanMemorySubAllocator *currentAllocator;
|
|
|
|
for (memoryType = 0; memoryType < VK_MAX_MEMORY_TYPES; memoryType += 1) {
|
|
currentAllocator = &renderer->memoryAllocator->subAllocators[memoryType];
|
|
|
|
for (allocationIndex = 0; allocationIndex < currentAllocator->allocationCount; allocationIndex += 1) {
|
|
if (currentAllocator->allocations[allocationIndex]->availableForAllocation == 1) {
|
|
if (currentAllocator->allocations[allocationIndex]->freeRegionCount > 1) {
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->allocationsToDefrag,
|
|
VulkanMemoryAllocation *,
|
|
renderer->allocationsToDefragCount + 1,
|
|
renderer->allocationsToDefragCapacity,
|
|
renderer->allocationsToDefragCapacity * 2);
|
|
|
|
renderer->allocationsToDefrag[renderer->allocationsToDefragCount] =
|
|
currentAllocator->allocations[allocationIndex];
|
|
|
|
renderer->allocationsToDefragCount += 1;
|
|
|
|
VULKAN_INTERNAL_MakeMemoryUnavailable(
|
|
renderer,
|
|
currentAllocator->allocations[allocationIndex]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_RemoveMemoryFreeRegion(
|
|
VulkanRenderer *renderer,
|
|
VulkanMemoryFreeRegion *freeRegion)
|
|
{
|
|
Uint32 i;
|
|
|
|
SDL_LockMutex(renderer->allocatorLock);
|
|
|
|
if (freeRegion->allocation->availableForAllocation) {
|
|
// close the gap in the sorted list
|
|
if (freeRegion->allocation->allocator->sortedFreeRegionCount > 1) {
|
|
for (i = freeRegion->sortedIndex; i < freeRegion->allocation->allocator->sortedFreeRegionCount - 1; i += 1) {
|
|
freeRegion->allocation->allocator->sortedFreeRegions[i] =
|
|
freeRegion->allocation->allocator->sortedFreeRegions[i + 1];
|
|
|
|
freeRegion->allocation->allocator->sortedFreeRegions[i]->sortedIndex = i;
|
|
}
|
|
}
|
|
|
|
freeRegion->allocation->allocator->sortedFreeRegionCount -= 1;
|
|
}
|
|
|
|
// close the gap in the buffer list
|
|
if (freeRegion->allocation->freeRegionCount > 1 && freeRegion->allocationIndex != freeRegion->allocation->freeRegionCount - 1) {
|
|
freeRegion->allocation->freeRegions[freeRegion->allocationIndex] =
|
|
freeRegion->allocation->freeRegions[freeRegion->allocation->freeRegionCount - 1];
|
|
|
|
freeRegion->allocation->freeRegions[freeRegion->allocationIndex]->allocationIndex =
|
|
freeRegion->allocationIndex;
|
|
}
|
|
|
|
freeRegion->allocation->freeRegionCount -= 1;
|
|
|
|
freeRegion->allocation->freeSpace -= freeRegion->size;
|
|
|
|
SDL_free(freeRegion);
|
|
|
|
SDL_UnlockMutex(renderer->allocatorLock);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_NewMemoryFreeRegion(
|
|
VulkanRenderer *renderer,
|
|
VulkanMemoryAllocation *allocation,
|
|
VkDeviceSize offset,
|
|
VkDeviceSize size)
|
|
{
|
|
VulkanMemoryFreeRegion *newFreeRegion;
|
|
VkDeviceSize newOffset, newSize;
|
|
Sint32 insertionIndex = 0;
|
|
|
|
SDL_LockMutex(renderer->allocatorLock);
|
|
|
|
// look for an adjacent region to merge
|
|
for (Sint32 i = allocation->freeRegionCount - 1; i >= 0; i -= 1) {
|
|
// check left side
|
|
if (allocation->freeRegions[i]->offset + allocation->freeRegions[i]->size == offset) {
|
|
newOffset = allocation->freeRegions[i]->offset;
|
|
newSize = allocation->freeRegions[i]->size + size;
|
|
|
|
VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, allocation->freeRegions[i]);
|
|
VULKAN_INTERNAL_NewMemoryFreeRegion(renderer, allocation, newOffset, newSize);
|
|
|
|
SDL_UnlockMutex(renderer->allocatorLock);
|
|
return;
|
|
}
|
|
|
|
// check right side
|
|
if (allocation->freeRegions[i]->offset == offset + size) {
|
|
newOffset = offset;
|
|
newSize = allocation->freeRegions[i]->size + size;
|
|
|
|
VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, allocation->freeRegions[i]);
|
|
VULKAN_INTERNAL_NewMemoryFreeRegion(renderer, allocation, newOffset, newSize);
|
|
|
|
SDL_UnlockMutex(renderer->allocatorLock);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// region is not contiguous with another free region, make a new one
|
|
allocation->freeRegionCount += 1;
|
|
if (allocation->freeRegionCount > allocation->freeRegionCapacity) {
|
|
allocation->freeRegionCapacity *= 2;
|
|
allocation->freeRegions = SDL_realloc(
|
|
allocation->freeRegions,
|
|
sizeof(VulkanMemoryFreeRegion *) * allocation->freeRegionCapacity);
|
|
}
|
|
|
|
newFreeRegion = SDL_malloc(sizeof(VulkanMemoryFreeRegion));
|
|
newFreeRegion->offset = offset;
|
|
newFreeRegion->size = size;
|
|
newFreeRegion->allocation = allocation;
|
|
|
|
allocation->freeSpace += size;
|
|
|
|
allocation->freeRegions[allocation->freeRegionCount - 1] = newFreeRegion;
|
|
newFreeRegion->allocationIndex = allocation->freeRegionCount - 1;
|
|
|
|
if (allocation->availableForAllocation) {
|
|
for (Uint32 i = 0; i < allocation->allocator->sortedFreeRegionCount; i += 1) {
|
|
if (allocation->allocator->sortedFreeRegions[i]->size < size) {
|
|
// this is where the new region should go
|
|
break;
|
|
}
|
|
|
|
insertionIndex += 1;
|
|
}
|
|
|
|
if (allocation->allocator->sortedFreeRegionCount + 1 > allocation->allocator->sortedFreeRegionCapacity) {
|
|
allocation->allocator->sortedFreeRegionCapacity *= 2;
|
|
allocation->allocator->sortedFreeRegions = SDL_realloc(
|
|
allocation->allocator->sortedFreeRegions,
|
|
sizeof(VulkanMemoryFreeRegion *) * allocation->allocator->sortedFreeRegionCapacity);
|
|
}
|
|
|
|
// perform insertion sort
|
|
if (allocation->allocator->sortedFreeRegionCount > 0 && insertionIndex != allocation->allocator->sortedFreeRegionCount) {
|
|
for (Sint32 i = allocation->allocator->sortedFreeRegionCount; i > insertionIndex && i > 0; i -= 1) {
|
|
allocation->allocator->sortedFreeRegions[i] = allocation->allocator->sortedFreeRegions[i - 1];
|
|
allocation->allocator->sortedFreeRegions[i]->sortedIndex = i;
|
|
}
|
|
}
|
|
|
|
allocation->allocator->sortedFreeRegionCount += 1;
|
|
allocation->allocator->sortedFreeRegions[insertionIndex] = newFreeRegion;
|
|
newFreeRegion->sortedIndex = insertionIndex;
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->allocatorLock);
|
|
}
|
|
|
|
static VulkanMemoryUsedRegion *VULKAN_INTERNAL_NewMemoryUsedRegion(
|
|
VulkanRenderer *renderer,
|
|
VulkanMemoryAllocation *allocation,
|
|
VkDeviceSize offset,
|
|
VkDeviceSize size,
|
|
VkDeviceSize resourceOffset,
|
|
VkDeviceSize resourceSize,
|
|
VkDeviceSize alignment)
|
|
{
|
|
VulkanMemoryUsedRegion *memoryUsedRegion;
|
|
|
|
SDL_LockMutex(renderer->allocatorLock);
|
|
|
|
if (allocation->usedRegionCount == allocation->usedRegionCapacity) {
|
|
allocation->usedRegionCapacity *= 2;
|
|
allocation->usedRegions = SDL_realloc(
|
|
allocation->usedRegions,
|
|
allocation->usedRegionCapacity * sizeof(VulkanMemoryUsedRegion *));
|
|
}
|
|
|
|
memoryUsedRegion = SDL_malloc(sizeof(VulkanMemoryUsedRegion));
|
|
memoryUsedRegion->allocation = allocation;
|
|
memoryUsedRegion->offset = offset;
|
|
memoryUsedRegion->size = size;
|
|
memoryUsedRegion->resourceOffset = resourceOffset;
|
|
memoryUsedRegion->resourceSize = resourceSize;
|
|
memoryUsedRegion->alignment = alignment;
|
|
|
|
allocation->usedSpace += size;
|
|
|
|
allocation->usedRegions[allocation->usedRegionCount] = memoryUsedRegion;
|
|
allocation->usedRegionCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->allocatorLock);
|
|
|
|
return memoryUsedRegion;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_RemoveMemoryUsedRegion(
|
|
VulkanRenderer *renderer,
|
|
VulkanMemoryUsedRegion *usedRegion)
|
|
{
|
|
Uint32 i;
|
|
|
|
SDL_LockMutex(renderer->allocatorLock);
|
|
|
|
for (i = 0; i < usedRegion->allocation->usedRegionCount; i += 1) {
|
|
if (usedRegion->allocation->usedRegions[i] == usedRegion) {
|
|
// plug the hole
|
|
if (i != usedRegion->allocation->usedRegionCount - 1) {
|
|
usedRegion->allocation->usedRegions[i] = usedRegion->allocation->usedRegions[usedRegion->allocation->usedRegionCount - 1];
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
usedRegion->allocation->usedSpace -= usedRegion->size;
|
|
|
|
usedRegion->allocation->usedRegionCount -= 1;
|
|
|
|
VULKAN_INTERNAL_NewMemoryFreeRegion(
|
|
renderer,
|
|
usedRegion->allocation,
|
|
usedRegion->offset,
|
|
usedRegion->size);
|
|
|
|
SDL_free(usedRegion);
|
|
|
|
SDL_UnlockMutex(renderer->allocatorLock);
|
|
}
|
|
|
|
static bool VULKAN_INTERNAL_CheckMemoryTypeArrayUnique(
|
|
Uint32 memoryTypeIndex,
|
|
Uint32 *memoryTypeIndexArray,
|
|
Uint32 count)
|
|
{
|
|
Uint32 i = 0;
|
|
|
|
for (i = 0; i < count; i += 1) {
|
|
if (memoryTypeIndexArray[i] == memoryTypeIndex) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Returns an array of memory type indices in order of preference.
|
|
* Memory types are requested with the following three guidelines:
|
|
*
|
|
* Required: Absolutely necessary
|
|
* Preferred: Nice to have, but not necessary
|
|
* Tolerable: Can be allowed if there are no other options
|
|
*
|
|
* We return memory types in this order:
|
|
* 1. Required and preferred. This is the best category.
|
|
* 2. Required only.
|
|
* 3. Required, preferred, and tolerable.
|
|
* 4. Required and tolerable. This is the worst category.
|
|
*/
|
|
static Uint32 *VULKAN_INTERNAL_FindBestMemoryTypes(
|
|
VulkanRenderer *renderer,
|
|
Uint32 typeFilter,
|
|
VkMemoryPropertyFlags requiredProperties,
|
|
VkMemoryPropertyFlags preferredProperties,
|
|
VkMemoryPropertyFlags tolerableProperties,
|
|
Uint32 *pCount)
|
|
{
|
|
Uint32 i;
|
|
Uint32 index = 0;
|
|
Uint32 *result = SDL_malloc(sizeof(Uint32) * renderer->memoryProperties.memoryTypeCount);
|
|
|
|
// required + preferred + !tolerable
|
|
for (i = 0; i < renderer->memoryProperties.memoryTypeCount; i += 1) {
|
|
if ((typeFilter & (1 << i)) &&
|
|
(renderer->memoryProperties.memoryTypes[i].propertyFlags & requiredProperties) == requiredProperties &&
|
|
(renderer->memoryProperties.memoryTypes[i].propertyFlags & preferredProperties) == preferredProperties &&
|
|
(renderer->memoryProperties.memoryTypes[i].propertyFlags & tolerableProperties) == 0) {
|
|
if (VULKAN_INTERNAL_CheckMemoryTypeArrayUnique(
|
|
i,
|
|
result,
|
|
index)) {
|
|
result[index] = i;
|
|
index += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// required + !preferred + !tolerable
|
|
for (i = 0; i < renderer->memoryProperties.memoryTypeCount; i += 1) {
|
|
if ((typeFilter & (1 << i)) &&
|
|
(renderer->memoryProperties.memoryTypes[i].propertyFlags & requiredProperties) == requiredProperties &&
|
|
(renderer->memoryProperties.memoryTypes[i].propertyFlags & preferredProperties) == 0 &&
|
|
(renderer->memoryProperties.memoryTypes[i].propertyFlags & tolerableProperties) == 0) {
|
|
if (VULKAN_INTERNAL_CheckMemoryTypeArrayUnique(
|
|
i,
|
|
result,
|
|
index)) {
|
|
result[index] = i;
|
|
index += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// required + preferred + tolerable
|
|
for (i = 0; i < renderer->memoryProperties.memoryTypeCount; i += 1) {
|
|
if ((typeFilter & (1 << i)) &&
|
|
(renderer->memoryProperties.memoryTypes[i].propertyFlags & requiredProperties) == requiredProperties &&
|
|
(renderer->memoryProperties.memoryTypes[i].propertyFlags & preferredProperties) == preferredProperties &&
|
|
(renderer->memoryProperties.memoryTypes[i].propertyFlags & tolerableProperties) == tolerableProperties) {
|
|
if (VULKAN_INTERNAL_CheckMemoryTypeArrayUnique(
|
|
i,
|
|
result,
|
|
index)) {
|
|
result[index] = i;
|
|
index += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// required + !preferred + tolerable
|
|
for (i = 0; i < renderer->memoryProperties.memoryTypeCount; i += 1) {
|
|
if ((typeFilter & (1 << i)) &&
|
|
(renderer->memoryProperties.memoryTypes[i].propertyFlags & requiredProperties) == requiredProperties &&
|
|
(renderer->memoryProperties.memoryTypes[i].propertyFlags & preferredProperties) == 0 &&
|
|
(renderer->memoryProperties.memoryTypes[i].propertyFlags & tolerableProperties) == tolerableProperties) {
|
|
if (VULKAN_INTERNAL_CheckMemoryTypeArrayUnique(
|
|
i,
|
|
result,
|
|
index)) {
|
|
result[index] = i;
|
|
index += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
*pCount = index;
|
|
return result;
|
|
}
|
|
|
|
static Uint32 *VULKAN_INTERNAL_FindBestBufferMemoryTypes(
|
|
VulkanRenderer *renderer,
|
|
VkBuffer buffer,
|
|
VkMemoryPropertyFlags requiredMemoryProperties,
|
|
VkMemoryPropertyFlags preferredMemoryProperties,
|
|
VkMemoryPropertyFlags tolerableMemoryProperties,
|
|
VkMemoryRequirements *pMemoryRequirements,
|
|
Uint32 *pCount)
|
|
{
|
|
renderer->vkGetBufferMemoryRequirements(
|
|
renderer->logicalDevice,
|
|
buffer,
|
|
pMemoryRequirements);
|
|
|
|
return VULKAN_INTERNAL_FindBestMemoryTypes(
|
|
renderer,
|
|
pMemoryRequirements->memoryTypeBits,
|
|
requiredMemoryProperties,
|
|
preferredMemoryProperties,
|
|
tolerableMemoryProperties,
|
|
pCount);
|
|
}
|
|
|
|
static Uint32 *VULKAN_INTERNAL_FindBestImageMemoryTypes(
|
|
VulkanRenderer *renderer,
|
|
VkImage image,
|
|
VkMemoryPropertyFlags preferredMemoryPropertyFlags,
|
|
VkMemoryRequirements *pMemoryRequirements,
|
|
Uint32 *pCount)
|
|
{
|
|
renderer->vkGetImageMemoryRequirements(
|
|
renderer->logicalDevice,
|
|
image,
|
|
pMemoryRequirements);
|
|
|
|
return VULKAN_INTERNAL_FindBestMemoryTypes(
|
|
renderer,
|
|
pMemoryRequirements->memoryTypeBits,
|
|
0,
|
|
preferredMemoryPropertyFlags,
|
|
0,
|
|
pCount);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_DeallocateMemory(
|
|
VulkanRenderer *renderer,
|
|
VulkanMemorySubAllocator *allocator,
|
|
Uint32 allocationIndex)
|
|
{
|
|
Uint32 i;
|
|
|
|
VulkanMemoryAllocation *allocation = allocator->allocations[allocationIndex];
|
|
|
|
SDL_LockMutex(renderer->allocatorLock);
|
|
|
|
// If this allocation was marked for defrag, cancel that
|
|
for (i = 0; i < renderer->allocationsToDefragCount; i += 1) {
|
|
if (allocation == renderer->allocationsToDefrag[i]) {
|
|
renderer->allocationsToDefrag[i] = renderer->allocationsToDefrag[renderer->allocationsToDefragCount - 1];
|
|
renderer->allocationsToDefragCount -= 1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < allocation->freeRegionCount; i += 1) {
|
|
VULKAN_INTERNAL_RemoveMemoryFreeRegion(
|
|
renderer,
|
|
allocation->freeRegions[i]);
|
|
}
|
|
SDL_free(allocation->freeRegions);
|
|
|
|
/* no need to iterate used regions because deallocate
|
|
* only happens when there are 0 used regions
|
|
*/
|
|
SDL_free(allocation->usedRegions);
|
|
|
|
renderer->vkFreeMemory(
|
|
renderer->logicalDevice,
|
|
allocation->memory,
|
|
NULL);
|
|
|
|
SDL_DestroyMutex(allocation->memoryLock);
|
|
SDL_free(allocation);
|
|
|
|
if (allocationIndex != allocator->allocationCount - 1) {
|
|
allocator->allocations[allocationIndex] = allocator->allocations[allocator->allocationCount - 1];
|
|
}
|
|
|
|
allocator->allocationCount -= 1;
|
|
|
|
SDL_UnlockMutex(renderer->allocatorLock);
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_AllocateMemory(
|
|
VulkanRenderer *renderer,
|
|
VkBuffer buffer,
|
|
VkImage image,
|
|
Uint32 memoryTypeIndex,
|
|
VkDeviceSize allocationSize,
|
|
Uint8 isHostVisible,
|
|
VulkanMemoryAllocation **pMemoryAllocation)
|
|
{
|
|
VulkanMemoryAllocation *allocation;
|
|
VulkanMemorySubAllocator *allocator = &renderer->memoryAllocator->subAllocators[memoryTypeIndex];
|
|
VkMemoryAllocateInfo allocInfo;
|
|
VkResult result;
|
|
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
allocInfo.pNext = NULL;
|
|
allocInfo.memoryTypeIndex = memoryTypeIndex;
|
|
allocInfo.allocationSize = allocationSize;
|
|
|
|
allocation = SDL_malloc(sizeof(VulkanMemoryAllocation));
|
|
allocation->size = allocationSize;
|
|
allocation->freeSpace = 0; // added by FreeRegions
|
|
allocation->usedSpace = 0; // added by UsedRegions
|
|
allocation->memoryLock = SDL_CreateMutex();
|
|
|
|
allocator->allocationCount += 1;
|
|
allocator->allocations = SDL_realloc(
|
|
allocator->allocations,
|
|
sizeof(VulkanMemoryAllocation *) * allocator->allocationCount);
|
|
|
|
allocator->allocations[allocator->allocationCount - 1] = allocation;
|
|
|
|
allocInfo.pNext = NULL;
|
|
allocation->availableForAllocation = 1;
|
|
|
|
allocation->usedRegions = SDL_malloc(sizeof(VulkanMemoryUsedRegion *));
|
|
allocation->usedRegionCount = 0;
|
|
allocation->usedRegionCapacity = 1;
|
|
|
|
allocation->freeRegions = SDL_malloc(sizeof(VulkanMemoryFreeRegion *));
|
|
allocation->freeRegionCount = 0;
|
|
allocation->freeRegionCapacity = 1;
|
|
|
|
allocation->allocator = allocator;
|
|
|
|
result = renderer->vkAllocateMemory(
|
|
renderer->logicalDevice,
|
|
&allocInfo,
|
|
NULL,
|
|
&allocation->memory);
|
|
|
|
if (result != VK_SUCCESS) {
|
|
// Uh oh, we couldn't allocate, time to clean up
|
|
SDL_free(allocation->freeRegions);
|
|
|
|
allocator->allocationCount -= 1;
|
|
allocator->allocations = SDL_realloc(
|
|
allocator->allocations,
|
|
sizeof(VulkanMemoryAllocation *) * allocator->allocationCount);
|
|
|
|
SDL_free(allocation);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Persistent mapping for host-visible memory
|
|
if (isHostVisible) {
|
|
result = renderer->vkMapMemory(
|
|
renderer->logicalDevice,
|
|
allocation->memory,
|
|
0,
|
|
VK_WHOLE_SIZE,
|
|
0,
|
|
(void **)&allocation->mapPointer);
|
|
VULKAN_ERROR_CHECK(result, vkMapMemory, 0)
|
|
} else {
|
|
allocation->mapPointer = NULL;
|
|
}
|
|
|
|
VULKAN_INTERNAL_NewMemoryFreeRegion(
|
|
renderer,
|
|
allocation,
|
|
0,
|
|
allocation->size);
|
|
|
|
*pMemoryAllocation = allocation;
|
|
return 1;
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_BindBufferMemory(
|
|
VulkanRenderer *renderer,
|
|
VulkanMemoryUsedRegion *usedRegion,
|
|
VkDeviceSize alignedOffset,
|
|
VkBuffer buffer)
|
|
{
|
|
VkResult vulkanResult;
|
|
|
|
SDL_LockMutex(usedRegion->allocation->memoryLock);
|
|
|
|
vulkanResult = renderer->vkBindBufferMemory(
|
|
renderer->logicalDevice,
|
|
buffer,
|
|
usedRegion->allocation->memory,
|
|
alignedOffset);
|
|
|
|
SDL_UnlockMutex(usedRegion->allocation->memoryLock);
|
|
|
|
VULKAN_ERROR_CHECK(vulkanResult, vkBindBufferMemory, 0)
|
|
|
|
return 1;
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_BindImageMemory(
|
|
VulkanRenderer *renderer,
|
|
VulkanMemoryUsedRegion *usedRegion,
|
|
VkDeviceSize alignedOffset,
|
|
VkImage image)
|
|
{
|
|
VkResult vulkanResult;
|
|
|
|
SDL_LockMutex(usedRegion->allocation->memoryLock);
|
|
|
|
vulkanResult = renderer->vkBindImageMemory(
|
|
renderer->logicalDevice,
|
|
image,
|
|
usedRegion->allocation->memory,
|
|
alignedOffset);
|
|
|
|
SDL_UnlockMutex(usedRegion->allocation->memoryLock);
|
|
|
|
VULKAN_ERROR_CHECK(vulkanResult, vkBindBufferMemory, 0)
|
|
|
|
return 1;
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_BindResourceMemory(
|
|
VulkanRenderer *renderer,
|
|
Uint32 memoryTypeIndex,
|
|
VkMemoryRequirements *memoryRequirements,
|
|
VkDeviceSize resourceSize, // may be different from requirements size!
|
|
VkBuffer buffer, // may be VK_NULL_HANDLE
|
|
VkImage image, // may be VK_NULL_HANDLE
|
|
VulkanMemoryUsedRegion **pMemoryUsedRegion)
|
|
{
|
|
VulkanMemoryAllocation *allocation;
|
|
VulkanMemorySubAllocator *allocator;
|
|
VulkanMemoryFreeRegion *region;
|
|
VulkanMemoryFreeRegion *selectedRegion;
|
|
VulkanMemoryUsedRegion *usedRegion;
|
|
|
|
VkDeviceSize requiredSize, allocationSize;
|
|
VkDeviceSize alignedOffset;
|
|
VkDeviceSize newRegionSize, newRegionOffset;
|
|
Uint8 isHostVisible, smallAllocation, allocationResult;
|
|
Sint32 i;
|
|
|
|
isHostVisible =
|
|
(renderer->memoryProperties.memoryTypes[memoryTypeIndex].propertyFlags &
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
|
|
|
|
allocator = &renderer->memoryAllocator->subAllocators[memoryTypeIndex];
|
|
requiredSize = memoryRequirements->size;
|
|
smallAllocation = requiredSize <= SMALL_ALLOCATION_THRESHOLD;
|
|
|
|
if ((buffer == VK_NULL_HANDLE && image == VK_NULL_HANDLE) ||
|
|
(buffer != VK_NULL_HANDLE && image != VK_NULL_HANDLE)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "BindResourceMemory must be given either a VulkanBuffer or a VulkanTexture");
|
|
return 0;
|
|
}
|
|
|
|
SDL_LockMutex(renderer->allocatorLock);
|
|
|
|
selectedRegion = NULL;
|
|
|
|
for (i = allocator->sortedFreeRegionCount - 1; i >= 0; i -= 1) {
|
|
region = allocator->sortedFreeRegions[i];
|
|
|
|
if (smallAllocation && region->allocation->size != SMALL_ALLOCATION_SIZE) {
|
|
// region is not in a small allocation
|
|
continue;
|
|
}
|
|
|
|
if (!smallAllocation && region->allocation->size == SMALL_ALLOCATION_SIZE) {
|
|
// allocation is not small and current region is in a small allocation
|
|
continue;
|
|
}
|
|
|
|
alignedOffset = VULKAN_INTERNAL_NextHighestAlignment(
|
|
region->offset,
|
|
memoryRequirements->alignment);
|
|
|
|
if (alignedOffset + requiredSize <= region->offset + region->size) {
|
|
selectedRegion = region;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (selectedRegion != NULL) {
|
|
region = selectedRegion;
|
|
allocation = region->allocation;
|
|
|
|
usedRegion = VULKAN_INTERNAL_NewMemoryUsedRegion(
|
|
renderer,
|
|
allocation,
|
|
region->offset,
|
|
requiredSize + (alignedOffset - region->offset),
|
|
alignedOffset,
|
|
resourceSize,
|
|
memoryRequirements->alignment);
|
|
|
|
usedRegion->isBuffer = buffer != VK_NULL_HANDLE;
|
|
|
|
newRegionSize = region->size - ((alignedOffset - region->offset) + requiredSize);
|
|
newRegionOffset = alignedOffset + requiredSize;
|
|
|
|
// remove and add modified region to re-sort
|
|
VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, region);
|
|
|
|
// if size is 0, no need to re-insert
|
|
if (newRegionSize != 0) {
|
|
VULKAN_INTERNAL_NewMemoryFreeRegion(
|
|
renderer,
|
|
allocation,
|
|
newRegionOffset,
|
|
newRegionSize);
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->allocatorLock);
|
|
|
|
if (buffer != VK_NULL_HANDLE) {
|
|
if (!VULKAN_INTERNAL_BindBufferMemory(
|
|
renderer,
|
|
usedRegion,
|
|
alignedOffset,
|
|
buffer)) {
|
|
VULKAN_INTERNAL_RemoveMemoryUsedRegion(
|
|
renderer,
|
|
usedRegion);
|
|
|
|
return 0;
|
|
}
|
|
} else if (image != VK_NULL_HANDLE) {
|
|
if (!VULKAN_INTERNAL_BindImageMemory(
|
|
renderer,
|
|
usedRegion,
|
|
alignedOffset,
|
|
image)) {
|
|
VULKAN_INTERNAL_RemoveMemoryUsedRegion(
|
|
renderer,
|
|
usedRegion);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
*pMemoryUsedRegion = usedRegion;
|
|
return 1;
|
|
}
|
|
|
|
// No suitable free regions exist, allocate a new memory region
|
|
if (
|
|
renderer->allocationsToDefragCount == 0 &&
|
|
!renderer->defragInProgress) {
|
|
// Mark currently fragmented allocations for defrag
|
|
VULKAN_INTERNAL_MarkAllocationsForDefrag(renderer);
|
|
}
|
|
|
|
if (requiredSize > SMALL_ALLOCATION_THRESHOLD) {
|
|
// allocate a page of required size aligned to LARGE_ALLOCATION_INCREMENT increments
|
|
allocationSize =
|
|
VULKAN_INTERNAL_NextHighestAlignment(requiredSize, LARGE_ALLOCATION_INCREMENT);
|
|
} else {
|
|
allocationSize = SMALL_ALLOCATION_SIZE;
|
|
}
|
|
|
|
allocationResult = VULKAN_INTERNAL_AllocateMemory(
|
|
renderer,
|
|
buffer,
|
|
image,
|
|
memoryTypeIndex,
|
|
allocationSize,
|
|
isHostVisible,
|
|
&allocation);
|
|
|
|
// Uh oh, we're out of memory
|
|
if (allocationResult == 0) {
|
|
SDL_UnlockMutex(renderer->allocatorLock);
|
|
|
|
// Responsibility of the caller to handle being out of memory
|
|
return 2;
|
|
}
|
|
|
|
usedRegion = VULKAN_INTERNAL_NewMemoryUsedRegion(
|
|
renderer,
|
|
allocation,
|
|
0,
|
|
requiredSize,
|
|
0,
|
|
resourceSize,
|
|
memoryRequirements->alignment);
|
|
|
|
usedRegion->isBuffer = buffer != VK_NULL_HANDLE;
|
|
|
|
region = allocation->freeRegions[0];
|
|
|
|
newRegionOffset = region->offset + requiredSize;
|
|
newRegionSize = region->size - requiredSize;
|
|
|
|
VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, region);
|
|
|
|
if (newRegionSize != 0) {
|
|
VULKAN_INTERNAL_NewMemoryFreeRegion(
|
|
renderer,
|
|
allocation,
|
|
newRegionOffset,
|
|
newRegionSize);
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->allocatorLock);
|
|
|
|
if (buffer != VK_NULL_HANDLE) {
|
|
if (!VULKAN_INTERNAL_BindBufferMemory(
|
|
renderer,
|
|
usedRegion,
|
|
0,
|
|
buffer)) {
|
|
VULKAN_INTERNAL_RemoveMemoryUsedRegion(
|
|
renderer,
|
|
usedRegion);
|
|
|
|
return 0;
|
|
}
|
|
} else if (image != VK_NULL_HANDLE) {
|
|
if (!VULKAN_INTERNAL_BindImageMemory(
|
|
renderer,
|
|
usedRegion,
|
|
0,
|
|
image)) {
|
|
VULKAN_INTERNAL_RemoveMemoryUsedRegion(
|
|
renderer,
|
|
usedRegion);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
*pMemoryUsedRegion = usedRegion;
|
|
return 1;
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_BindMemoryForImage(
|
|
VulkanRenderer *renderer,
|
|
VkImage image,
|
|
VulkanMemoryUsedRegion **usedRegion)
|
|
{
|
|
Uint8 bindResult = 0;
|
|
Uint32 memoryTypeCount = 0;
|
|
Uint32 *memoryTypesToTry = NULL;
|
|
Uint32 selectedMemoryTypeIndex = 0;
|
|
Uint32 i;
|
|
VkMemoryPropertyFlags preferredMemoryPropertyFlags;
|
|
VkMemoryRequirements memoryRequirements;
|
|
|
|
/* Vulkan memory types have several memory properties.
|
|
*
|
|
* Unlike buffers, images are always optimally stored device-local,
|
|
* so that is the only property we prefer here.
|
|
*
|
|
* If memory is constrained, it is fine for the texture to not
|
|
* be device-local.
|
|
*/
|
|
preferredMemoryPropertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
|
|
|
memoryTypesToTry = VULKAN_INTERNAL_FindBestImageMemoryTypes(
|
|
renderer,
|
|
image,
|
|
preferredMemoryPropertyFlags,
|
|
&memoryRequirements,
|
|
&memoryTypeCount);
|
|
|
|
for (i = 0; i < memoryTypeCount; i += 1) {
|
|
bindResult = VULKAN_INTERNAL_BindResourceMemory(
|
|
renderer,
|
|
memoryTypesToTry[i],
|
|
&memoryRequirements,
|
|
memoryRequirements.size,
|
|
VK_NULL_HANDLE,
|
|
image,
|
|
usedRegion);
|
|
|
|
if (bindResult == 1) {
|
|
selectedMemoryTypeIndex = memoryTypesToTry[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDL_free(memoryTypesToTry);
|
|
|
|
// Check for warnings on success
|
|
if (bindResult == 1) {
|
|
if (!renderer->outOfDeviceLocalMemoryWarning) {
|
|
if ((renderer->memoryProperties.memoryTypes[selectedMemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Out of device-local memory, allocating textures on host-local memory!");
|
|
renderer->outOfDeviceLocalMemoryWarning = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bindResult;
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_BindMemoryForBuffer(
|
|
VulkanRenderer *renderer,
|
|
VkBuffer buffer,
|
|
VkDeviceSize size,
|
|
VulkanBufferType type,
|
|
VulkanMemoryUsedRegion **usedRegion)
|
|
{
|
|
Uint8 bindResult = 0;
|
|
Uint32 memoryTypeCount = 0;
|
|
Uint32 *memoryTypesToTry = NULL;
|
|
Uint32 selectedMemoryTypeIndex = 0;
|
|
Uint32 i;
|
|
VkMemoryPropertyFlags requiredMemoryPropertyFlags = 0;
|
|
VkMemoryPropertyFlags preferredMemoryPropertyFlags = 0;
|
|
VkMemoryPropertyFlags tolerableMemoryPropertyFlags = 0;
|
|
VkMemoryRequirements memoryRequirements;
|
|
|
|
/* Buffers need to be optimally bound to a memory type
|
|
* based on their use case and the architecture of the system.
|
|
*
|
|
* It is important to understand the distinction between device and host.
|
|
*
|
|
* On a traditional high-performance desktop computer,
|
|
* the "device" would be the GPU, and the "host" would be the CPU.
|
|
* Memory being copied between these two must cross the PCI bus.
|
|
* On these systems we have to be concerned about bandwidth limitations
|
|
* and causing memory stalls, so we have taken a great deal of care
|
|
* to structure this API to guide the client towards optimal usage.
|
|
*
|
|
* Other kinds of devices do not necessarily have this distinction.
|
|
* On an iPhone or Nintendo Switch, all memory is accessible both to the
|
|
* GPU and the CPU at all times. These kinds of systems are known as
|
|
* UMA, or Unified Memory Architecture. A desktop computer using the
|
|
* CPU's integrated graphics can also be thought of as UMA.
|
|
*
|
|
* Vulkan memory types have several memory properties.
|
|
* The relevant memory properties are as follows:
|
|
*
|
|
* DEVICE_LOCAL:
|
|
* This memory is on-device and most efficient for device access.
|
|
* On UMA systems all memory is device-local.
|
|
* If memory is not device-local, then it is host-local.
|
|
*
|
|
* HOST_VISIBLE:
|
|
* This memory can be mapped for host access, meaning we can obtain
|
|
* a pointer to directly access the memory.
|
|
*
|
|
* HOST_COHERENT:
|
|
* Host-coherent memory does not require cache management operations
|
|
* when mapped, so we always set this alongside HOST_VISIBLE
|
|
* to avoid extra record keeping.
|
|
*
|
|
* HOST_CACHED:
|
|
* Host-cached memory is faster to access than uncached memory
|
|
* but memory of this type might not always be available.
|
|
*
|
|
* GPU buffers, like vertex buffers, indirect buffers, etc
|
|
* are optimally stored in device-local memory.
|
|
* However, if device-local memory is low, these buffers
|
|
* can be accessed from host-local memory with a performance penalty.
|
|
*
|
|
* Uniform buffers must be host-visible and coherent because
|
|
* the client uses them to quickly push small amounts of data.
|
|
* We prefer uniform buffers to also be device-local because
|
|
* they are accessed by shaders, but the amount of memory
|
|
* that is both device-local and host-visible
|
|
* is often constrained, particularly on low-end devices.
|
|
*
|
|
* Transfer buffers must be host-visible and coherent because
|
|
* the client uses them to stage data to be transferred
|
|
* to device-local memory, or to read back data transferred
|
|
* from the device. We prefer the cache bit for performance
|
|
* but it isn't strictly necessary. We tolerate device-local
|
|
* memory in this situation because, as mentioned above,
|
|
* on certain devices all memory is device-local, and even
|
|
* though the transfer isn't strictly necessary it is still
|
|
* useful for correctly timelining data.
|
|
*/
|
|
if (type == VULKAN_BUFFER_TYPE_GPU) {
|
|
preferredMemoryPropertyFlags |=
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
|
} else if (type == VULKAN_BUFFER_TYPE_UNIFORM) {
|
|
requiredMemoryPropertyFlags |=
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
|
|
|
preferredMemoryPropertyFlags |=
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
|
} else if (type == VULKAN_BUFFER_TYPE_TRANSFER) {
|
|
requiredMemoryPropertyFlags |=
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
|
|
|
preferredMemoryPropertyFlags |=
|
|
VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
|
|
|
|
tolerableMemoryPropertyFlags |=
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized buffer type!");
|
|
return 0;
|
|
}
|
|
|
|
memoryTypesToTry = VULKAN_INTERNAL_FindBestBufferMemoryTypes(
|
|
renderer,
|
|
buffer,
|
|
requiredMemoryPropertyFlags,
|
|
preferredMemoryPropertyFlags,
|
|
tolerableMemoryPropertyFlags,
|
|
&memoryRequirements,
|
|
&memoryTypeCount);
|
|
|
|
for (i = 0; i < memoryTypeCount; i += 1) {
|
|
bindResult = VULKAN_INTERNAL_BindResourceMemory(
|
|
renderer,
|
|
memoryTypesToTry[i],
|
|
&memoryRequirements,
|
|
size,
|
|
buffer,
|
|
VK_NULL_HANDLE,
|
|
usedRegion);
|
|
|
|
if (bindResult == 1) {
|
|
selectedMemoryTypeIndex = memoryTypesToTry[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDL_free(memoryTypesToTry);
|
|
|
|
// Check for warnings on success
|
|
if (bindResult == 1) {
|
|
if (type == VULKAN_BUFFER_TYPE_GPU) {
|
|
if (!renderer->outOfDeviceLocalMemoryWarning) {
|
|
if ((renderer->memoryProperties.memoryTypes[selectedMemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Out of device-local memory, allocating buffers on host-local memory, expect degraded performance!");
|
|
renderer->outOfDeviceLocalMemoryWarning = 1;
|
|
}
|
|
}
|
|
} else if (type == VULKAN_BUFFER_TYPE_UNIFORM) {
|
|
if (!renderer->outofBARMemoryWarning) {
|
|
if ((renderer->memoryProperties.memoryTypes[selectedMemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Out of BAR memory, allocating uniform buffers on host-local memory, expect degraded performance!");
|
|
renderer->outofBARMemoryWarning = 1;
|
|
}
|
|
}
|
|
} else if (type == VULKAN_BUFFER_TYPE_TRANSFER) {
|
|
if (!renderer->integratedMemoryNotification) {
|
|
if ((renderer->memoryProperties.memoryTypes[selectedMemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Integrated memory detected, allocating TransferBuffers on device-local memory!");
|
|
renderer->integratedMemoryNotification = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bindResult;
|
|
}
|
|
|
|
// Resource tracking
|
|
|
|
#define ADD_TO_ARRAY_UNIQUE(resource, type, array, count, capacity) \
|
|
Uint32 i; \
|
|
\
|
|
for (i = 0; i < commandBuffer->count; i += 1) { \
|
|
if (commandBuffer->array[i] == resource) { \
|
|
return; \
|
|
} \
|
|
} \
|
|
\
|
|
if (commandBuffer->count == commandBuffer->capacity) { \
|
|
commandBuffer->capacity += 1; \
|
|
commandBuffer->array = SDL_realloc( \
|
|
commandBuffer->array, \
|
|
commandBuffer->capacity * sizeof(type)); \
|
|
} \
|
|
commandBuffer->array[commandBuffer->count] = resource; \
|
|
commandBuffer->count += 1;
|
|
|
|
#define TRACK_RESOURCE(resource, type, array, count, capacity) \
|
|
Uint32 i; \
|
|
\
|
|
for (i = 0; i < commandBuffer->count; i += 1) { \
|
|
if (commandBuffer->array[i] == resource) { \
|
|
return; \
|
|
} \
|
|
} \
|
|
\
|
|
if (commandBuffer->count == commandBuffer->capacity) { \
|
|
commandBuffer->capacity += 1; \
|
|
commandBuffer->array = SDL_realloc( \
|
|
commandBuffer->array, \
|
|
commandBuffer->capacity * sizeof(type)); \
|
|
} \
|
|
commandBuffer->array[commandBuffer->count] = resource; \
|
|
commandBuffer->count += 1; \
|
|
SDL_AtomicIncRef(&resource->referenceCount);
|
|
|
|
static void VULKAN_INTERNAL_TrackBuffer(
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanBuffer *buffer)
|
|
{
|
|
TRACK_RESOURCE(
|
|
buffer,
|
|
VulkanBuffer *,
|
|
usedBuffers,
|
|
usedBufferCount,
|
|
usedBufferCapacity)
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_TrackTexture(
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanTexture *texture)
|
|
{
|
|
TRACK_RESOURCE(
|
|
texture,
|
|
VulkanTexture *,
|
|
usedTextures,
|
|
usedTextureCount,
|
|
usedTextureCapacity)
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_TrackSampler(
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanSampler *sampler)
|
|
{
|
|
TRACK_RESOURCE(
|
|
sampler,
|
|
VulkanSampler *,
|
|
usedSamplers,
|
|
usedSamplerCount,
|
|
usedSamplerCapacity)
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_TrackGraphicsPipeline(
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanGraphicsPipeline *graphicsPipeline)
|
|
{
|
|
TRACK_RESOURCE(
|
|
graphicsPipeline,
|
|
VulkanGraphicsPipeline *,
|
|
usedGraphicsPipelines,
|
|
usedGraphicsPipelineCount,
|
|
usedGraphicsPipelineCapacity)
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_TrackComputePipeline(
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanComputePipeline *computePipeline)
|
|
{
|
|
TRACK_RESOURCE(
|
|
computePipeline,
|
|
VulkanComputePipeline *,
|
|
usedComputePipelines,
|
|
usedComputePipelineCount,
|
|
usedComputePipelineCapacity)
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_TrackFramebuffer(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanFramebuffer *framebuffer)
|
|
{
|
|
TRACK_RESOURCE(
|
|
framebuffer,
|
|
VulkanFramebuffer *,
|
|
usedFramebuffers,
|
|
usedFramebufferCount,
|
|
usedFramebufferCapacity);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_TrackUniformBuffer(
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanUniformBuffer *uniformBuffer)
|
|
{
|
|
Uint32 i;
|
|
for (i = 0; i < commandBuffer->usedUniformBufferCount; i += 1) {
|
|
if (commandBuffer->usedUniformBuffers[i] == uniformBuffer) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (commandBuffer->usedUniformBufferCount == commandBuffer->usedUniformBufferCapacity) {
|
|
commandBuffer->usedUniformBufferCapacity += 1;
|
|
commandBuffer->usedUniformBuffers = SDL_realloc(
|
|
commandBuffer->usedUniformBuffers,
|
|
commandBuffer->usedUniformBufferCapacity * sizeof(VulkanUniformBuffer *));
|
|
}
|
|
commandBuffer->usedUniformBuffers[commandBuffer->usedUniformBufferCount] = uniformBuffer;
|
|
commandBuffer->usedUniformBufferCount += 1;
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(
|
|
commandBuffer,
|
|
uniformBuffer->bufferHandle->vulkanBuffer);
|
|
}
|
|
|
|
#undef TRACK_RESOURCE
|
|
|
|
// Memory Barriers
|
|
|
|
/*
|
|
* In Vulkan, we must manually synchronize operations that write to resources on the GPU
|
|
* so that read-after-write, write-after-read, and write-after-write hazards do not occur.
|
|
* Additionally, textures are required to be in specific layouts for specific use cases.
|
|
* Both of these tasks are accomplished with vkCmdPipelineBarrier.
|
|
*
|
|
* To insert the correct barriers, we keep track of "usage modes" for buffers and textures.
|
|
* These indicate the current usage of that resource on the command buffer.
|
|
* The transition from one usage mode to another indicates how the barrier should be constructed.
|
|
*
|
|
* Pipeline barriers cannot be inserted during a render pass, but they can be inserted
|
|
* during a compute or copy pass.
|
|
*
|
|
* This means that the "default" usage mode of any given resource should be that it should be
|
|
* ready for a graphics-read operation, because we cannot barrier during a render pass.
|
|
* In the case where a resource is only used in compute, its default usage mode can be compute-read.
|
|
* This strategy allows us to avoid expensive record keeping of command buffer/resource usage mode pairs,
|
|
* and it fully covers synchronization between all combinations of stages.
|
|
*
|
|
* In Upload and Copy functions, we transition the resource immediately before and after the copy command.
|
|
*
|
|
* When binding a resource for compute, we transition when the Bind functions are called.
|
|
* If a bind slot containing a resource is overwritten, we transition the resource in that slot back to its default.
|
|
* When EndComputePass is called we transition all bound resources back to their default state.
|
|
*
|
|
* When binding a texture as a render pass attachment, we transition the resource on BeginRenderPass
|
|
* and transition it back to its default on EndRenderPass.
|
|
*
|
|
* This strategy imposes certain limitations on resource usage flags.
|
|
* For example, a texture cannot have both the SAMPLER and GRAPHICS_STORAGE usage flags,
|
|
* because then it is imposible for the backend to infer which default usage mode the texture should use.
|
|
*
|
|
* Sync hazards can be detected by setting VK_KHRONOS_VALIDATION_VALIDATE_SYNC=1 when using validation layers.
|
|
*/
|
|
|
|
static void VULKAN_INTERNAL_BufferMemoryBarrier(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanBufferUsageMode sourceUsageMode,
|
|
VulkanBufferUsageMode destinationUsageMode,
|
|
VulkanBuffer *buffer)
|
|
{
|
|
VkPipelineStageFlags srcStages = 0;
|
|
VkPipelineStageFlags dstStages = 0;
|
|
VkBufferMemoryBarrier memoryBarrier;
|
|
|
|
memoryBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
|
|
memoryBarrier.pNext = NULL;
|
|
memoryBarrier.srcAccessMask = 0;
|
|
memoryBarrier.dstAccessMask = 0;
|
|
memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
memoryBarrier.buffer = buffer->buffer;
|
|
memoryBarrier.offset = 0;
|
|
memoryBarrier.size = buffer->size;
|
|
|
|
if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE) {
|
|
srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
} else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION) {
|
|
srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
} else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_VERTEX_READ) {
|
|
srcStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
|
|
} else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_INDEX_READ) {
|
|
srcStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_INDEX_READ_BIT;
|
|
} else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_INDIRECT) {
|
|
srcStages = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
|
|
} else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ) {
|
|
srcStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
} else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ) {
|
|
srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
} else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE) {
|
|
srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized buffer source barrier type!");
|
|
return;
|
|
}
|
|
|
|
if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE) {
|
|
dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
} else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION) {
|
|
dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
} else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_VERTEX_READ) {
|
|
dstStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
|
|
} else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_INDEX_READ) {
|
|
dstStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_INDEX_READ_BIT;
|
|
} else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_INDIRECT) {
|
|
dstStages = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
|
|
} else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ) {
|
|
dstStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
} else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ) {
|
|
dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
} else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE) {
|
|
dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized buffer destination barrier type!");
|
|
return;
|
|
}
|
|
|
|
renderer->vkCmdPipelineBarrier(
|
|
commandBuffer->commandBuffer,
|
|
srcStages,
|
|
dstStages,
|
|
0,
|
|
0,
|
|
NULL,
|
|
1,
|
|
&memoryBarrier,
|
|
0,
|
|
NULL);
|
|
|
|
buffer->transitioned = true;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_TextureSubresourceMemoryBarrier(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanTextureUsageMode sourceUsageMode,
|
|
VulkanTextureUsageMode destinationUsageMode,
|
|
VulkanTextureSubresource *textureSubresource)
|
|
{
|
|
VkPipelineStageFlags srcStages = 0;
|
|
VkPipelineStageFlags dstStages = 0;
|
|
VkImageMemoryBarrier memoryBarrier;
|
|
|
|
memoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
memoryBarrier.pNext = NULL;
|
|
memoryBarrier.srcAccessMask = 0;
|
|
memoryBarrier.dstAccessMask = 0;
|
|
memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
memoryBarrier.image = textureSubresource->parent->image;
|
|
memoryBarrier.subresourceRange.aspectMask = textureSubresource->parent->aspectFlags;
|
|
memoryBarrier.subresourceRange.baseArrayLayer = textureSubresource->layer;
|
|
memoryBarrier.subresourceRange.layerCount = 1;
|
|
memoryBarrier.subresourceRange.baseMipLevel = textureSubresource->level;
|
|
memoryBarrier.subresourceRange.levelCount = 1;
|
|
|
|
if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE) {
|
|
srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
|
} else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION) {
|
|
srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
} else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_SAMPLER) {
|
|
srcStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
} else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ) {
|
|
srcStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
} else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ) {
|
|
srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
} else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE) {
|
|
srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
|
memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
} else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT) {
|
|
srcStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
} else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT) {
|
|
srcStages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized texture source barrier type!");
|
|
return;
|
|
}
|
|
|
|
if (!textureSubresource->transitioned) {
|
|
memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
}
|
|
|
|
if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE) {
|
|
dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
|
} else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION) {
|
|
dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
} else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_SAMPLER) {
|
|
dstStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
} else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ) {
|
|
dstStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
} else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ) {
|
|
dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
} else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE) {
|
|
dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
|
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
} else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT) {
|
|
dstStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
} else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT) {
|
|
dstStages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
|
memoryBarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
} else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_PRESENT) {
|
|
dstStages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
|
memoryBarrier.dstAccessMask = 0;
|
|
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized texture destination barrier type!");
|
|
return;
|
|
}
|
|
|
|
renderer->vkCmdPipelineBarrier(
|
|
commandBuffer->commandBuffer,
|
|
srcStages,
|
|
dstStages,
|
|
0,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
1,
|
|
&memoryBarrier);
|
|
|
|
textureSubresource->transitioned = true;
|
|
}
|
|
|
|
static VulkanBufferUsageMode VULKAN_INTERNAL_DefaultBufferUsageMode(
|
|
VulkanBuffer *buffer)
|
|
{
|
|
// NOTE: order matters here!
|
|
|
|
if (buffer->usageFlags & SDL_GPU_BUFFERUSAGE_VERTEX_BIT) {
|
|
return VULKAN_BUFFER_USAGE_MODE_VERTEX_READ;
|
|
} else if (buffer->usageFlags & SDL_GPU_BUFFERUSAGE_INDEX_BIT) {
|
|
return VULKAN_BUFFER_USAGE_MODE_INDEX_READ;
|
|
} else if (buffer->usageFlags & SDL_GPU_BUFFERUSAGE_INDIRECT_BIT) {
|
|
return VULKAN_BUFFER_USAGE_MODE_INDIRECT;
|
|
} else if (buffer->usageFlags & SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ_BIT) {
|
|
return VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ;
|
|
} else if (buffer->usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ_BIT) {
|
|
return VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ;
|
|
} else if (buffer->usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE_BIT) {
|
|
return VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Buffer has no default usage mode!");
|
|
return VULKAN_BUFFER_USAGE_MODE_VERTEX_READ;
|
|
}
|
|
}
|
|
|
|
static VulkanTextureUsageMode VULKAN_INTERNAL_DefaultTextureUsageMode(
|
|
VulkanTexture *texture)
|
|
{
|
|
// NOTE: order matters here!
|
|
// NOTE: graphics storage bits and sampler bit are mutually exclusive!
|
|
|
|
if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_SAMPLER_BIT) {
|
|
return VULKAN_TEXTURE_USAGE_MODE_SAMPLER;
|
|
} else if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT) {
|
|
return VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ;
|
|
} else if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT) {
|
|
return VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT;
|
|
} else if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) {
|
|
return VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT;
|
|
} else if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT) {
|
|
return VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ;
|
|
} else if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT) {
|
|
return VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Texture has no default usage mode!");
|
|
return VULKAN_TEXTURE_USAGE_MODE_SAMPLER;
|
|
}
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanBufferUsageMode destinationUsageMode,
|
|
VulkanBuffer *buffer)
|
|
{
|
|
VULKAN_INTERNAL_BufferMemoryBarrier(
|
|
renderer,
|
|
commandBuffer,
|
|
VULKAN_INTERNAL_DefaultBufferUsageMode(buffer),
|
|
destinationUsageMode,
|
|
buffer);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanBufferUsageMode sourceUsageMode,
|
|
VulkanBuffer *buffer)
|
|
{
|
|
VULKAN_INTERNAL_BufferMemoryBarrier(
|
|
renderer,
|
|
commandBuffer,
|
|
sourceUsageMode,
|
|
VULKAN_INTERNAL_DefaultBufferUsageMode(buffer),
|
|
buffer);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanTextureUsageMode destinationUsageMode,
|
|
VulkanTextureSubresource *textureSubresource)
|
|
{
|
|
VULKAN_INTERNAL_TextureSubresourceMemoryBarrier(
|
|
renderer,
|
|
commandBuffer,
|
|
VULKAN_INTERNAL_DefaultTextureUsageMode(textureSubresource->parent),
|
|
destinationUsageMode,
|
|
textureSubresource);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_TextureTransitionFromDefaultUsage(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanTextureUsageMode destinationUsageMode,
|
|
VulkanTexture *texture)
|
|
{
|
|
for (Uint32 i = 0; i < texture->subresourceCount; i += 1) {
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
renderer,
|
|
commandBuffer,
|
|
destinationUsageMode,
|
|
&texture->subresources[i]);
|
|
}
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanTextureUsageMode sourceUsageMode,
|
|
VulkanTextureSubresource *textureSubresource)
|
|
{
|
|
VULKAN_INTERNAL_TextureSubresourceMemoryBarrier(
|
|
renderer,
|
|
commandBuffer,
|
|
sourceUsageMode,
|
|
VULKAN_INTERNAL_DefaultTextureUsageMode(textureSubresource->parent),
|
|
textureSubresource);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanTextureUsageMode sourceUsageMode,
|
|
VulkanTexture *texture)
|
|
{
|
|
// FIXME: could optimize this barrier
|
|
for (Uint32 i = 0; i < texture->subresourceCount; i += 1) {
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
renderer,
|
|
commandBuffer,
|
|
sourceUsageMode,
|
|
&texture->subresources[i]);
|
|
}
|
|
}
|
|
|
|
// Resource Disposal
|
|
|
|
static void VULKAN_INTERNAL_ReleaseFramebuffer(
|
|
VulkanRenderer *renderer,
|
|
VulkanFramebuffer *framebuffer)
|
|
{
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->framebuffersToDestroy,
|
|
VulkanFramebuffer *,
|
|
renderer->framebuffersToDestroyCount + 1,
|
|
renderer->framebuffersToDestroyCapacity,
|
|
renderer->framebuffersToDestroyCapacity * 2)
|
|
|
|
renderer->framebuffersToDestroy[renderer->framebuffersToDestroyCount] = framebuffer;
|
|
renderer->framebuffersToDestroyCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_DestroyFramebuffer(
|
|
VulkanRenderer *renderer,
|
|
VulkanFramebuffer *framebuffer)
|
|
{
|
|
renderer->vkDestroyFramebuffer(
|
|
renderer->logicalDevice,
|
|
framebuffer->framebuffer,
|
|
NULL);
|
|
|
|
SDL_free(framebuffer);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_RemoveFramebuffersContainingView(
|
|
VulkanRenderer *renderer,
|
|
VkImageView view)
|
|
{
|
|
FramebufferHashTableKey *key;
|
|
VulkanFramebuffer *value;
|
|
void *iter = NULL;
|
|
|
|
// Can't remove while iterating!
|
|
Uint32 keysToRemoveCapacity = 8;
|
|
Uint32 keysToRemoveCount = 0;
|
|
FramebufferHashTableKey **keysToRemove = SDL_malloc(keysToRemoveCapacity * sizeof(FramebufferHashTableKey *));
|
|
|
|
SDL_LockMutex(renderer->framebufferFetchLock);
|
|
|
|
while (SDL_IterateHashTable(renderer->framebufferHashTable, (const void **)&key, (const void **)&value, &iter)) {
|
|
bool remove = false;
|
|
for (Uint32 i = 0; i < key->colorAttachmentCount; i += 1) {
|
|
if (key->colorAttachmentViews[i] == view) {
|
|
remove = true;
|
|
}
|
|
}
|
|
if (key->depthStencilAttachmentView == view) {
|
|
remove = true;
|
|
}
|
|
|
|
if (remove) {
|
|
if (keysToRemoveCount == keysToRemoveCapacity) {
|
|
keysToRemoveCapacity *= 2;
|
|
keysToRemove = SDL_realloc(
|
|
keysToRemove,
|
|
keysToRemoveCapacity * sizeof(FramebufferHashTableKey *));
|
|
}
|
|
|
|
keysToRemove[keysToRemoveCount] = key;
|
|
keysToRemoveCount += 1;
|
|
}
|
|
}
|
|
|
|
for (Uint32 i = 0; i < keysToRemoveCount; i += 1) {
|
|
SDL_RemoveFromHashTable(renderer->framebufferHashTable, (void *)keysToRemove[i]);
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->framebufferFetchLock);
|
|
|
|
SDL_free(keysToRemove);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_DestroyTexture(
|
|
VulkanRenderer *renderer,
|
|
VulkanTexture *texture)
|
|
{
|
|
// Clean up subresources
|
|
for (Uint32 subresourceIndex = 0; subresourceIndex < texture->subresourceCount; subresourceIndex += 1) {
|
|
if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT) {
|
|
for (Uint32 depthIndex = 0; depthIndex < texture->depth; depthIndex += 1) {
|
|
VULKAN_INTERNAL_RemoveFramebuffersContainingView(
|
|
renderer,
|
|
texture->subresources[subresourceIndex].renderTargetViews[depthIndex]);
|
|
}
|
|
|
|
if (texture->subresources[subresourceIndex].msaaTexHandle != NULL) {
|
|
VULKAN_INTERNAL_DestroyTexture(
|
|
renderer,
|
|
texture->subresources[subresourceIndex].msaaTexHandle->vulkanTexture);
|
|
SDL_free(texture->subresources[subresourceIndex].msaaTexHandle);
|
|
}
|
|
|
|
for (Uint32 depthIndex = 0; depthIndex < texture->depth; depthIndex += 1) {
|
|
renderer->vkDestroyImageView(
|
|
renderer->logicalDevice,
|
|
texture->subresources[subresourceIndex].renderTargetViews[depthIndex],
|
|
NULL);
|
|
}
|
|
SDL_free(texture->subresources[subresourceIndex].renderTargetViews);
|
|
}
|
|
|
|
if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT) {
|
|
renderer->vkDestroyImageView(
|
|
renderer->logicalDevice,
|
|
texture->subresources[subresourceIndex].computeWriteView,
|
|
NULL);
|
|
}
|
|
|
|
if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) {
|
|
renderer->vkDestroyImageView(
|
|
renderer->logicalDevice,
|
|
texture->subresources[subresourceIndex].depthStencilView,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
SDL_free(texture->subresources);
|
|
|
|
renderer->vkDestroyImageView(
|
|
renderer->logicalDevice,
|
|
texture->fullView,
|
|
NULL);
|
|
|
|
renderer->vkDestroyImage(
|
|
renderer->logicalDevice,
|
|
texture->image,
|
|
NULL);
|
|
|
|
VULKAN_INTERNAL_RemoveMemoryUsedRegion(
|
|
renderer,
|
|
texture->usedRegion);
|
|
|
|
SDL_free(texture);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_DestroyBuffer(
|
|
VulkanRenderer *renderer,
|
|
VulkanBuffer *buffer)
|
|
{
|
|
renderer->vkDestroyBuffer(
|
|
renderer->logicalDevice,
|
|
buffer->buffer,
|
|
NULL);
|
|
|
|
VULKAN_INTERNAL_RemoveMemoryUsedRegion(
|
|
renderer,
|
|
buffer->usedRegion);
|
|
|
|
SDL_free(buffer);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_DestroyCommandPool(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandPool *commandPool)
|
|
{
|
|
Uint32 i;
|
|
VulkanCommandBuffer *commandBuffer;
|
|
|
|
renderer->vkDestroyCommandPool(
|
|
renderer->logicalDevice,
|
|
commandPool->commandPool,
|
|
NULL);
|
|
|
|
for (i = 0; i < commandPool->inactiveCommandBufferCount; i += 1) {
|
|
commandBuffer = commandPool->inactiveCommandBuffers[i];
|
|
|
|
SDL_free(commandBuffer->presentDatas);
|
|
SDL_free(commandBuffer->waitSemaphores);
|
|
SDL_free(commandBuffer->signalSemaphores);
|
|
SDL_free(commandBuffer->boundDescriptorSetDatas);
|
|
SDL_free(commandBuffer->usedBuffers);
|
|
SDL_free(commandBuffer->usedTextures);
|
|
SDL_free(commandBuffer->usedSamplers);
|
|
SDL_free(commandBuffer->usedGraphicsPipelines);
|
|
SDL_free(commandBuffer->usedComputePipelines);
|
|
SDL_free(commandBuffer->usedFramebuffers);
|
|
SDL_free(commandBuffer->usedUniformBuffers);
|
|
|
|
SDL_free(commandBuffer);
|
|
}
|
|
|
|
SDL_free(commandPool->inactiveCommandBuffers);
|
|
SDL_free(commandPool);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_DestroyDescriptorSetPool(
|
|
VulkanRenderer *renderer,
|
|
DescriptorSetPool *pool)
|
|
{
|
|
Uint32 i;
|
|
|
|
if (pool == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < pool->descriptorPoolCount; i += 1) {
|
|
renderer->vkDestroyDescriptorPool(
|
|
renderer->logicalDevice,
|
|
pool->descriptorPools[i],
|
|
NULL);
|
|
}
|
|
|
|
renderer->vkDestroyDescriptorSetLayout(
|
|
renderer->logicalDevice,
|
|
pool->descriptorSetLayout,
|
|
NULL);
|
|
|
|
SDL_free(pool->descriptorInfos);
|
|
SDL_free(pool->descriptorPools);
|
|
SDL_free(pool->inactiveDescriptorSets);
|
|
SDL_DestroyMutex(pool->lock);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_DestroyGraphicsPipeline(
|
|
VulkanRenderer *renderer,
|
|
VulkanGraphicsPipeline *graphicsPipeline)
|
|
{
|
|
Uint32 i;
|
|
|
|
renderer->vkDestroyPipeline(
|
|
renderer->logicalDevice,
|
|
graphicsPipeline->pipeline,
|
|
NULL);
|
|
|
|
renderer->vkDestroyPipelineLayout(
|
|
renderer->logicalDevice,
|
|
graphicsPipeline->resourceLayout.pipelineLayout,
|
|
NULL);
|
|
|
|
for (i = 0; i < 4; i += 1) {
|
|
VULKAN_INTERNAL_DestroyDescriptorSetPool(
|
|
renderer,
|
|
&graphicsPipeline->resourceLayout.descriptorSetPools[i]);
|
|
}
|
|
|
|
(void)SDL_AtomicDecRef(&graphicsPipeline->vertexShader->referenceCount);
|
|
(void)SDL_AtomicDecRef(&graphicsPipeline->fragmentShader->referenceCount);
|
|
|
|
SDL_free(graphicsPipeline);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_DestroyComputePipeline(
|
|
VulkanRenderer *renderer,
|
|
VulkanComputePipeline *computePipeline)
|
|
{
|
|
Uint32 i;
|
|
|
|
renderer->vkDestroyPipeline(
|
|
renderer->logicalDevice,
|
|
computePipeline->pipeline,
|
|
NULL);
|
|
|
|
renderer->vkDestroyPipelineLayout(
|
|
renderer->logicalDevice,
|
|
computePipeline->resourceLayout.pipelineLayout,
|
|
NULL);
|
|
|
|
for (i = 0; i < 3; i += 1) {
|
|
VULKAN_INTERNAL_DestroyDescriptorSetPool(
|
|
renderer,
|
|
&computePipeline->resourceLayout.descriptorSetPools[i]);
|
|
}
|
|
|
|
renderer->vkDestroyShaderModule(
|
|
renderer->logicalDevice,
|
|
computePipeline->shaderModule,
|
|
NULL);
|
|
|
|
SDL_free(computePipeline);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_DestroyShader(
|
|
VulkanRenderer *renderer,
|
|
VulkanShader *vulkanShader)
|
|
{
|
|
renderer->vkDestroyShaderModule(
|
|
renderer->logicalDevice,
|
|
vulkanShader->shaderModule,
|
|
NULL);
|
|
|
|
SDL_free((void *)vulkanShader->entryPointName);
|
|
SDL_free(vulkanShader);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_DestroySampler(
|
|
VulkanRenderer *renderer,
|
|
VulkanSampler *vulkanSampler)
|
|
{
|
|
renderer->vkDestroySampler(
|
|
renderer->logicalDevice,
|
|
vulkanSampler->sampler,
|
|
NULL);
|
|
|
|
SDL_free(vulkanSampler);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_DestroySwapchain(
|
|
VulkanRenderer *renderer,
|
|
WindowData *windowData)
|
|
{
|
|
Uint32 i;
|
|
VulkanSwapchainData *swapchainData;
|
|
|
|
if (windowData == NULL) {
|
|
return;
|
|
}
|
|
|
|
swapchainData = windowData->swapchainData;
|
|
|
|
if (swapchainData == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < swapchainData->imageCount; i += 1) {
|
|
VULKAN_INTERNAL_RemoveFramebuffersContainingView(
|
|
renderer,
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].renderTargetViews[0]);
|
|
renderer->vkDestroyImageView(
|
|
renderer->logicalDevice,
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].renderTargetViews[0],
|
|
NULL);
|
|
SDL_free(swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].renderTargetViews);
|
|
SDL_free(swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources);
|
|
|
|
SDL_free(swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture);
|
|
SDL_free(swapchainData->textureContainers[i].activeTextureHandle);
|
|
}
|
|
|
|
SDL_free(swapchainData->textureContainers);
|
|
|
|
renderer->vkDestroySwapchainKHR(
|
|
renderer->logicalDevice,
|
|
swapchainData->swapchain,
|
|
NULL);
|
|
|
|
renderer->vkDestroySurfaceKHR(
|
|
renderer->instance,
|
|
swapchainData->surface,
|
|
NULL);
|
|
|
|
for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
|
|
renderer->vkDestroySemaphore(
|
|
renderer->logicalDevice,
|
|
swapchainData->imageAvailableSemaphore[i],
|
|
NULL);
|
|
|
|
renderer->vkDestroySemaphore(
|
|
renderer->logicalDevice,
|
|
swapchainData->renderFinishedSemaphore[i],
|
|
NULL);
|
|
}
|
|
|
|
windowData->swapchainData = NULL;
|
|
SDL_free(swapchainData);
|
|
}
|
|
|
|
// Hashtable functions
|
|
|
|
static Uint32 VULKAN_INTERNAL_CommandPoolHashFunction(const void *key, void *data)
|
|
{
|
|
return (Uint32)((CommandPoolHashTableKey *)key)->threadID;
|
|
}
|
|
|
|
static bool VULKAN_INTERNAL_CommandPoolHashKeyMatch(const void *aKey, const void *bKey, void *data)
|
|
{
|
|
CommandPoolHashTableKey *a = (CommandPoolHashTableKey *)aKey;
|
|
CommandPoolHashTableKey *b = (CommandPoolHashTableKey *)bKey;
|
|
return a->threadID == b->threadID;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_CommandPoolHashNuke(const void *key, const void *value, void *data)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)data;
|
|
VulkanCommandPool *pool = (VulkanCommandPool *)value;
|
|
VULKAN_INTERNAL_DestroyCommandPool(renderer, pool);
|
|
SDL_free((void *)key);
|
|
}
|
|
|
|
static Uint32 VULKAN_INTERNAL_RenderPassHashFunction(
|
|
const void *key,
|
|
void *data)
|
|
{
|
|
RenderPassHashTableKey *hashTableKey = (RenderPassHashTableKey *)key;
|
|
|
|
/* The algorithm for this hashing function
|
|
* is taken from Josh Bloch's "Effective Java".
|
|
* (https://stackoverflow.com/a/113600/12492383)
|
|
*/
|
|
const Uint32 HASH_FACTOR = 31;
|
|
Uint32 result = 1;
|
|
|
|
for (Uint32 i = 0; i < hashTableKey->colorAttachmentCount; i += 1) {
|
|
result = result * HASH_FACTOR + hashTableKey->colorTargetDescriptions[i].loadOp;
|
|
result = result * HASH_FACTOR + hashTableKey->colorTargetDescriptions[i].storeOp;
|
|
result = result * HASH_FACTOR + hashTableKey->colorTargetDescriptions[i].format;
|
|
}
|
|
|
|
result = result * HASH_FACTOR + hashTableKey->depthStencilTargetDescription.loadOp;
|
|
result = result * HASH_FACTOR + hashTableKey->depthStencilTargetDescription.storeOp;
|
|
result = result * HASH_FACTOR + hashTableKey->depthStencilTargetDescription.stencilLoadOp;
|
|
result = result * HASH_FACTOR + hashTableKey->depthStencilTargetDescription.stencilStoreOp;
|
|
result = result * HASH_FACTOR + hashTableKey->depthStencilTargetDescription.format;
|
|
|
|
result = result * HASH_FACTOR + hashTableKey->colorAttachmentSampleCount;
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool VULKAN_INTERNAL_RenderPassHashKeyMatch(
|
|
const void *aKey,
|
|
const void *bKey,
|
|
void *data)
|
|
{
|
|
RenderPassHashTableKey *a = (RenderPassHashTableKey *)aKey;
|
|
RenderPassHashTableKey *b = (RenderPassHashTableKey *)bKey;
|
|
|
|
if (a->colorAttachmentCount != b->colorAttachmentCount) {
|
|
return 0;
|
|
}
|
|
|
|
if (a->colorAttachmentSampleCount != b->colorAttachmentSampleCount) {
|
|
return 0;
|
|
}
|
|
|
|
for (Uint32 i = 0; i < a->colorAttachmentCount; i += 1) {
|
|
if (a->colorTargetDescriptions[i].format != b->colorTargetDescriptions[i].format) {
|
|
return 0;
|
|
}
|
|
|
|
if (a->colorTargetDescriptions[i].loadOp != b->colorTargetDescriptions[i].loadOp) {
|
|
return 0;
|
|
}
|
|
|
|
if (a->colorTargetDescriptions[i].storeOp != b->colorTargetDescriptions[i].storeOp) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (a->depthStencilTargetDescription.format != b->depthStencilTargetDescription.format) {
|
|
return 0;
|
|
}
|
|
|
|
if (a->depthStencilTargetDescription.loadOp != b->depthStencilTargetDescription.loadOp) {
|
|
return 0;
|
|
}
|
|
|
|
if (a->depthStencilTargetDescription.storeOp != b->depthStencilTargetDescription.storeOp) {
|
|
return 0;
|
|
}
|
|
|
|
if (a->depthStencilTargetDescription.stencilLoadOp != b->depthStencilTargetDescription.stencilLoadOp) {
|
|
return 0;
|
|
}
|
|
|
|
if (a->depthStencilTargetDescription.stencilStoreOp != b->depthStencilTargetDescription.stencilStoreOp) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_RenderPassHashNuke(const void *key, const void *value, void *data)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)data;
|
|
VulkanRenderPassHashTableValue *renderPassWrapper = (VulkanRenderPassHashTableValue *)value;
|
|
renderer->vkDestroyRenderPass(
|
|
renderer->logicalDevice,
|
|
renderPassWrapper->handle,
|
|
NULL);
|
|
SDL_free(renderPassWrapper);
|
|
SDL_free((void *)key);
|
|
}
|
|
|
|
static Uint32 VULKAN_INTERNAL_FramebufferHashFunction(
|
|
const void *key,
|
|
void *data)
|
|
{
|
|
FramebufferHashTableKey *hashTableKey = (FramebufferHashTableKey *)key;
|
|
|
|
/* The algorithm for this hashing function
|
|
* is taken from Josh Bloch's "Effective Java".
|
|
* (https://stackoverflow.com/a/113600/12492383)
|
|
*/
|
|
const Uint32 HASH_FACTOR = 31;
|
|
Uint32 result = 1;
|
|
|
|
for (Uint32 i = 0; i < hashTableKey->colorAttachmentCount; i += 1) {
|
|
result = result * HASH_FACTOR + (Uint32)(uintptr_t)hashTableKey->colorAttachmentViews[i];
|
|
result = result * HASH_FACTOR + (Uint32)(uintptr_t)hashTableKey->colorMultiSampleAttachmentViews[i];
|
|
}
|
|
|
|
result = result * HASH_FACTOR + (Uint32)(uintptr_t)hashTableKey->depthStencilAttachmentView;
|
|
result = result * HASH_FACTOR + hashTableKey->width;
|
|
result = result * HASH_FACTOR + hashTableKey->height;
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool VULKAN_INTERNAL_FramebufferHashKeyMatch(
|
|
const void *aKey,
|
|
const void *bKey,
|
|
void *data)
|
|
{
|
|
FramebufferHashTableKey *a = (FramebufferHashTableKey *)aKey;
|
|
FramebufferHashTableKey *b = (FramebufferHashTableKey *)bKey;
|
|
|
|
if (a->colorAttachmentCount != b->colorAttachmentCount) {
|
|
return 0;
|
|
}
|
|
|
|
for (Uint32 i = 0; i < a->colorAttachmentCount; i += 1) {
|
|
if (a->colorAttachmentViews[i] != b->colorAttachmentViews[i]) {
|
|
return 0;
|
|
}
|
|
|
|
if (a->colorMultiSampleAttachmentViews[i] != b->colorMultiSampleAttachmentViews[i]) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (a->depthStencilAttachmentView != b->depthStencilAttachmentView) {
|
|
return 0;
|
|
}
|
|
|
|
if (a->width != b->width) {
|
|
return 0;
|
|
}
|
|
|
|
if (a->height != b->height) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_FramebufferHashNuke(const void *key, const void *value, void *data)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)data;
|
|
VulkanFramebuffer *framebuffer = (VulkanFramebuffer *)value;
|
|
VULKAN_INTERNAL_ReleaseFramebuffer(renderer, framebuffer);
|
|
SDL_free((void *)key);
|
|
}
|
|
|
|
// Descriptor pool stuff
|
|
|
|
static bool VULKAN_INTERNAL_CreateDescriptorPool(
|
|
VulkanRenderer *renderer,
|
|
VulkanDescriptorInfo *descriptorInfos,
|
|
Uint32 descriptorInfoCount,
|
|
Uint32 descriptorSetPoolSize,
|
|
VkDescriptorPool *pDescriptorPool)
|
|
{
|
|
VkDescriptorPoolSize *descriptorPoolSizes;
|
|
VkDescriptorPoolCreateInfo descriptorPoolInfo;
|
|
VkResult vulkanResult;
|
|
Uint32 i;
|
|
|
|
descriptorPoolSizes = NULL;
|
|
|
|
if (descriptorInfoCount > 0) {
|
|
descriptorPoolSizes = SDL_stack_alloc(VkDescriptorPoolSize, descriptorInfoCount);
|
|
|
|
for (i = 0; i < descriptorInfoCount; i += 1) {
|
|
descriptorPoolSizes[i].type = descriptorInfos[i].descriptorType;
|
|
descriptorPoolSizes[i].descriptorCount = descriptorSetPoolSize;
|
|
}
|
|
}
|
|
|
|
descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
|
descriptorPoolInfo.pNext = NULL;
|
|
descriptorPoolInfo.flags = 0;
|
|
descriptorPoolInfo.maxSets = descriptorSetPoolSize;
|
|
descriptorPoolInfo.poolSizeCount = descriptorInfoCount;
|
|
descriptorPoolInfo.pPoolSizes = descriptorPoolSizes;
|
|
|
|
vulkanResult = renderer->vkCreateDescriptorPool(
|
|
renderer->logicalDevice,
|
|
&descriptorPoolInfo,
|
|
NULL,
|
|
pDescriptorPool);
|
|
|
|
SDL_stack_free(descriptorPoolSizes);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkCreateDescriptorPool", vulkanResult);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool VULKAN_INTERNAL_AllocateDescriptorSets(
|
|
VulkanRenderer *renderer,
|
|
VkDescriptorPool descriptorPool,
|
|
VkDescriptorSetLayout descriptorSetLayout,
|
|
Uint32 descriptorSetCount,
|
|
VkDescriptorSet *descriptorSetArray)
|
|
{
|
|
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo;
|
|
VkDescriptorSetLayout *descriptorSetLayouts = SDL_stack_alloc(VkDescriptorSetLayout, descriptorSetCount);
|
|
VkResult vulkanResult;
|
|
Uint32 i;
|
|
|
|
for (i = 0; i < descriptorSetCount; i += 1) {
|
|
descriptorSetLayouts[i] = descriptorSetLayout;
|
|
}
|
|
|
|
descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
descriptorSetAllocateInfo.pNext = NULL;
|
|
descriptorSetAllocateInfo.descriptorPool = descriptorPool;
|
|
descriptorSetAllocateInfo.descriptorSetCount = descriptorSetCount;
|
|
descriptorSetAllocateInfo.pSetLayouts = descriptorSetLayouts;
|
|
|
|
vulkanResult = renderer->vkAllocateDescriptorSets(
|
|
renderer->logicalDevice,
|
|
&descriptorSetAllocateInfo,
|
|
descriptorSetArray);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkAllocateDescriptorSets", vulkanResult);
|
|
SDL_stack_free(descriptorSetLayouts);
|
|
return false;
|
|
}
|
|
|
|
SDL_stack_free(descriptorSetLayouts);
|
|
return true;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_InitializeDescriptorSetPool(
|
|
VulkanRenderer *renderer,
|
|
DescriptorSetPool *descriptorSetPool)
|
|
{
|
|
descriptorSetPool->lock = SDL_CreateMutex();
|
|
|
|
// Descriptor set layout and descriptor infos are already set when this function is called
|
|
|
|
descriptorSetPool->descriptorPoolCount = 1;
|
|
descriptorSetPool->descriptorPools = SDL_malloc(sizeof(VkDescriptorPool));
|
|
descriptorSetPool->nextPoolSize = DESCRIPTOR_POOL_STARTING_SIZE * 2;
|
|
|
|
VULKAN_INTERNAL_CreateDescriptorPool(
|
|
renderer,
|
|
descriptorSetPool->descriptorInfos,
|
|
descriptorSetPool->descriptorInfoCount,
|
|
DESCRIPTOR_POOL_STARTING_SIZE,
|
|
&descriptorSetPool->descriptorPools[0]);
|
|
|
|
descriptorSetPool->inactiveDescriptorSetCapacity = DESCRIPTOR_POOL_STARTING_SIZE;
|
|
descriptorSetPool->inactiveDescriptorSetCount = DESCRIPTOR_POOL_STARTING_SIZE;
|
|
descriptorSetPool->inactiveDescriptorSets = SDL_malloc(
|
|
sizeof(VkDescriptorSet) * DESCRIPTOR_POOL_STARTING_SIZE);
|
|
|
|
VULKAN_INTERNAL_AllocateDescriptorSets(
|
|
renderer,
|
|
descriptorSetPool->descriptorPools[0],
|
|
descriptorSetPool->descriptorSetLayout,
|
|
DESCRIPTOR_POOL_STARTING_SIZE,
|
|
descriptorSetPool->inactiveDescriptorSets);
|
|
}
|
|
|
|
static bool VULKAN_INTERNAL_InitializeGraphicsPipelineResourceLayout(
|
|
VulkanRenderer *renderer,
|
|
VulkanShader *vertexShader,
|
|
VulkanShader *fragmentShader,
|
|
VulkanGraphicsPipelineResourceLayout *pipelineResourceLayout)
|
|
{
|
|
VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[MAX_TEXTURE_SAMPLERS_PER_STAGE + MAX_STORAGE_TEXTURES_PER_STAGE + MAX_STORAGE_BUFFERS_PER_STAGE];
|
|
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo;
|
|
VkDescriptorSetLayout descriptorSetLayouts[4];
|
|
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo;
|
|
DescriptorSetPool *descriptorSetPool;
|
|
VkResult vulkanResult;
|
|
Uint32 i;
|
|
|
|
pipelineResourceLayout->vertexSamplerCount = vertexShader->samplerCount;
|
|
pipelineResourceLayout->vertexStorageTextureCount = vertexShader->storageTextureCount;
|
|
pipelineResourceLayout->vertexStorageBufferCount = vertexShader->storageBufferCount;
|
|
pipelineResourceLayout->vertexUniformBufferCount = vertexShader->uniformBufferCount;
|
|
|
|
pipelineResourceLayout->fragmentSamplerCount = fragmentShader->samplerCount;
|
|
pipelineResourceLayout->fragmentStorageTextureCount = fragmentShader->storageTextureCount;
|
|
pipelineResourceLayout->fragmentStorageBufferCount = fragmentShader->storageBufferCount;
|
|
pipelineResourceLayout->fragmentUniformBufferCount = fragmentShader->uniformBufferCount;
|
|
|
|
// Vertex Resources
|
|
|
|
descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
|
descriptorSetLayoutCreateInfo.pNext = NULL;
|
|
descriptorSetLayoutCreateInfo.flags = 0;
|
|
descriptorSetLayoutCreateInfo.pBindings = NULL;
|
|
descriptorSetLayoutCreateInfo.bindingCount =
|
|
vertexShader->samplerCount +
|
|
vertexShader->storageTextureCount +
|
|
vertexShader->storageBufferCount;
|
|
|
|
descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[0];
|
|
|
|
descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount;
|
|
descriptorSetPool->descriptorInfos = NULL;
|
|
|
|
if (descriptorSetLayoutCreateInfo.bindingCount > 0) {
|
|
descriptorSetPool->descriptorInfos = SDL_malloc(
|
|
descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo));
|
|
|
|
for (i = 0; i < vertexShader->samplerCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_VERTEX_BIT;
|
|
}
|
|
|
|
for (i = vertexShader->samplerCount; i < vertexShader->samplerCount + vertexShader->storageTextureCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_VERTEX_BIT;
|
|
}
|
|
|
|
for (i = vertexShader->samplerCount + vertexShader->storageTextureCount; i < descriptorSetLayoutCreateInfo.bindingCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_VERTEX_BIT;
|
|
}
|
|
|
|
descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings;
|
|
}
|
|
|
|
vulkanResult = renderer->vkCreateDescriptorSetLayout(
|
|
renderer->logicalDevice,
|
|
&descriptorSetLayoutCreateInfo,
|
|
NULL,
|
|
&descriptorSetPool->descriptorSetLayout);
|
|
|
|
descriptorSetLayouts[0] = descriptorSetPool->descriptorSetLayout;
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult);
|
|
return false;
|
|
}
|
|
|
|
// Vertex UBOs
|
|
|
|
descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[1];
|
|
|
|
descriptorSetLayoutCreateInfo.bindingCount = pipelineResourceLayout->vertexUniformBufferCount;
|
|
descriptorSetLayoutCreateInfo.pBindings = NULL;
|
|
|
|
descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount;
|
|
descriptorSetPool->descriptorInfos = NULL;
|
|
|
|
if (descriptorSetLayoutCreateInfo.bindingCount > 0) {
|
|
descriptorSetPool->descriptorInfos = SDL_malloc(
|
|
descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo));
|
|
|
|
for (i = 0; i < vertexShader->uniformBufferCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_VERTEX_BIT;
|
|
}
|
|
|
|
descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings;
|
|
}
|
|
|
|
vulkanResult = renderer->vkCreateDescriptorSetLayout(
|
|
renderer->logicalDevice,
|
|
&descriptorSetLayoutCreateInfo,
|
|
NULL,
|
|
&descriptorSetPool->descriptorSetLayout);
|
|
|
|
descriptorSetLayouts[1] = descriptorSetPool->descriptorSetLayout;
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult);
|
|
return false;
|
|
}
|
|
|
|
// Fragment resources
|
|
|
|
descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[2];
|
|
|
|
descriptorSetLayoutCreateInfo.bindingCount =
|
|
fragmentShader->samplerCount +
|
|
fragmentShader->storageTextureCount +
|
|
fragmentShader->storageBufferCount;
|
|
|
|
descriptorSetLayoutCreateInfo.pBindings = NULL;
|
|
|
|
descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount;
|
|
descriptorSetPool->descriptorInfos = NULL;
|
|
|
|
if (descriptorSetLayoutCreateInfo.bindingCount > 0) {
|
|
descriptorSetPool->descriptorInfos = SDL_malloc(
|
|
descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo));
|
|
|
|
for (i = 0; i < fragmentShader->samplerCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
}
|
|
|
|
for (i = fragmentShader->samplerCount; i < fragmentShader->samplerCount + fragmentShader->storageTextureCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
}
|
|
|
|
for (i = fragmentShader->samplerCount + fragmentShader->storageTextureCount; i < descriptorSetLayoutCreateInfo.bindingCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
}
|
|
|
|
descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings;
|
|
}
|
|
|
|
vulkanResult = renderer->vkCreateDescriptorSetLayout(
|
|
renderer->logicalDevice,
|
|
&descriptorSetLayoutCreateInfo,
|
|
NULL,
|
|
&descriptorSetPool->descriptorSetLayout);
|
|
|
|
descriptorSetLayouts[2] = descriptorSetPool->descriptorSetLayout;
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult);
|
|
return false;
|
|
}
|
|
|
|
// Fragment UBOs
|
|
|
|
descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[3];
|
|
|
|
descriptorSetLayoutCreateInfo.bindingCount =
|
|
pipelineResourceLayout->fragmentUniformBufferCount;
|
|
|
|
descriptorSetLayoutCreateInfo.pBindings = NULL;
|
|
|
|
descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount;
|
|
descriptorSetPool->descriptorInfos = NULL;
|
|
|
|
if (descriptorSetLayoutCreateInfo.bindingCount > 0) {
|
|
descriptorSetPool->descriptorInfos = SDL_malloc(
|
|
descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo));
|
|
|
|
for (i = 0; i < fragmentShader->uniformBufferCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
}
|
|
|
|
descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings;
|
|
}
|
|
|
|
vulkanResult = renderer->vkCreateDescriptorSetLayout(
|
|
renderer->logicalDevice,
|
|
&descriptorSetLayoutCreateInfo,
|
|
NULL,
|
|
&descriptorSetPool->descriptorSetLayout);
|
|
|
|
descriptorSetLayouts[3] = descriptorSetPool->descriptorSetLayout;
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult);
|
|
return false;
|
|
}
|
|
|
|
// Create the pipeline layout
|
|
|
|
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
pipelineLayoutCreateInfo.pNext = NULL;
|
|
pipelineLayoutCreateInfo.flags = 0;
|
|
pipelineLayoutCreateInfo.setLayoutCount = 4;
|
|
pipelineLayoutCreateInfo.pSetLayouts = descriptorSetLayouts;
|
|
pipelineLayoutCreateInfo.pushConstantRangeCount = 0;
|
|
pipelineLayoutCreateInfo.pPushConstantRanges = NULL;
|
|
|
|
vulkanResult = renderer->vkCreatePipelineLayout(
|
|
renderer->logicalDevice,
|
|
&pipelineLayoutCreateInfo,
|
|
NULL,
|
|
&pipelineResourceLayout->pipelineLayout);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkCreatePipelineLayout", vulkanResult);
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < 4; i += 1) {
|
|
VULKAN_INTERNAL_InitializeDescriptorSetPool(
|
|
renderer,
|
|
&pipelineResourceLayout->descriptorSetPools[i]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool VULKAN_INTERNAL_InitializeComputePipelineResourceLayout(
|
|
VulkanRenderer *renderer,
|
|
SDL_GPUComputePipelineCreateInfo *pipelineCreateInfo,
|
|
VulkanComputePipelineResourceLayout *pipelineResourceLayout)
|
|
{
|
|
VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo;
|
|
VkDescriptorSetLayout descriptorSetLayouts[3];
|
|
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo;
|
|
DescriptorSetPool *descriptorSetPool;
|
|
VkResult vulkanResult;
|
|
Uint32 i;
|
|
|
|
pipelineResourceLayout->readOnlyStorageTextureCount = pipelineCreateInfo->readOnlyStorageTextureCount;
|
|
pipelineResourceLayout->readOnlyStorageBufferCount = pipelineCreateInfo->readOnlyStorageBufferCount;
|
|
pipelineResourceLayout->writeOnlyStorageTextureCount = pipelineCreateInfo->writeOnlyStorageTextureCount;
|
|
pipelineResourceLayout->writeOnlyStorageBufferCount = pipelineCreateInfo->writeOnlyStorageBufferCount;
|
|
pipelineResourceLayout->uniformBufferCount = pipelineCreateInfo->uniformBufferCount;
|
|
|
|
// Read-only resources
|
|
|
|
descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
|
descriptorSetLayoutCreateInfo.pNext = NULL;
|
|
descriptorSetLayoutCreateInfo.flags = 0;
|
|
descriptorSetLayoutCreateInfo.pBindings = NULL;
|
|
descriptorSetLayoutCreateInfo.bindingCount =
|
|
pipelineCreateInfo->readOnlyStorageTextureCount +
|
|
pipelineCreateInfo->readOnlyStorageBufferCount;
|
|
|
|
descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[0];
|
|
|
|
descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount;
|
|
descriptorSetPool->descriptorInfos = NULL;
|
|
|
|
if (descriptorSetLayoutCreateInfo.bindingCount > 0) {
|
|
descriptorSetPool->descriptorInfos = SDL_malloc(
|
|
descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo));
|
|
|
|
for (i = 0; i < pipelineCreateInfo->readOnlyStorageTextureCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
}
|
|
|
|
for (i = pipelineCreateInfo->readOnlyStorageTextureCount; i < descriptorSetLayoutCreateInfo.bindingCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
}
|
|
|
|
descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings;
|
|
}
|
|
|
|
vulkanResult = renderer->vkCreateDescriptorSetLayout(
|
|
renderer->logicalDevice,
|
|
&descriptorSetLayoutCreateInfo,
|
|
NULL,
|
|
&descriptorSetPool->descriptorSetLayout);
|
|
|
|
descriptorSetLayouts[0] = descriptorSetPool->descriptorSetLayout;
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult);
|
|
return false;
|
|
}
|
|
|
|
// Write-only resources
|
|
|
|
descriptorSetLayoutCreateInfo.bindingCount =
|
|
pipelineCreateInfo->writeOnlyStorageTextureCount +
|
|
pipelineCreateInfo->writeOnlyStorageBufferCount;
|
|
|
|
descriptorSetLayoutCreateInfo.pBindings = NULL;
|
|
|
|
descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[1];
|
|
|
|
descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount;
|
|
descriptorSetPool->descriptorInfos = NULL;
|
|
|
|
if (descriptorSetLayoutCreateInfo.bindingCount > 0) {
|
|
descriptorSetPool->descriptorInfos = SDL_malloc(
|
|
descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo));
|
|
|
|
for (i = 0; i < pipelineCreateInfo->writeOnlyStorageTextureCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
}
|
|
|
|
for (i = pipelineCreateInfo->writeOnlyStorageTextureCount; i < descriptorSetLayoutCreateInfo.bindingCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
}
|
|
|
|
descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings;
|
|
}
|
|
|
|
vulkanResult = renderer->vkCreateDescriptorSetLayout(
|
|
renderer->logicalDevice,
|
|
&descriptorSetLayoutCreateInfo,
|
|
NULL,
|
|
&descriptorSetPool->descriptorSetLayout);
|
|
|
|
descriptorSetLayouts[1] = descriptorSetPool->descriptorSetLayout;
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult);
|
|
return false;
|
|
}
|
|
|
|
// Uniform buffers
|
|
|
|
descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[2];
|
|
|
|
descriptorSetLayoutCreateInfo.bindingCount = pipelineCreateInfo->uniformBufferCount;
|
|
descriptorSetLayoutCreateInfo.pBindings = NULL;
|
|
|
|
descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount;
|
|
descriptorSetPool->descriptorInfos = NULL;
|
|
|
|
if (descriptorSetLayoutCreateInfo.bindingCount > 0) {
|
|
descriptorSetPool->descriptorInfos = SDL_malloc(
|
|
descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo));
|
|
|
|
for (i = 0; i < pipelineCreateInfo->uniformBufferCount; i += 1) {
|
|
descriptorSetLayoutBindings[i].binding = i;
|
|
descriptorSetLayoutBindings[i].descriptorCount = 1;
|
|
descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
|
|
|
|
descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
}
|
|
|
|
descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings;
|
|
}
|
|
|
|
vulkanResult = renderer->vkCreateDescriptorSetLayout(
|
|
renderer->logicalDevice,
|
|
&descriptorSetLayoutCreateInfo,
|
|
NULL,
|
|
&descriptorSetPool->descriptorSetLayout);
|
|
|
|
descriptorSetLayouts[2] = descriptorSetPool->descriptorSetLayout;
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult);
|
|
return false;
|
|
}
|
|
|
|
// Create the pipeline layout
|
|
|
|
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
pipelineLayoutCreateInfo.pNext = NULL;
|
|
pipelineLayoutCreateInfo.flags = 0;
|
|
pipelineLayoutCreateInfo.setLayoutCount = 3;
|
|
pipelineLayoutCreateInfo.pSetLayouts = descriptorSetLayouts;
|
|
pipelineLayoutCreateInfo.pushConstantRangeCount = 0;
|
|
pipelineLayoutCreateInfo.pPushConstantRanges = NULL;
|
|
|
|
vulkanResult = renderer->vkCreatePipelineLayout(
|
|
renderer->logicalDevice,
|
|
&pipelineLayoutCreateInfo,
|
|
NULL,
|
|
&pipelineResourceLayout->pipelineLayout);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkCreatePipelineLayout", vulkanResult);
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < 3; i += 1) {
|
|
VULKAN_INTERNAL_InitializeDescriptorSetPool(
|
|
renderer,
|
|
&pipelineResourceLayout->descriptorSetPools[i]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Data Buffer
|
|
|
|
static VulkanBuffer *VULKAN_INTERNAL_CreateBuffer(
|
|
VulkanRenderer *renderer,
|
|
VkDeviceSize size,
|
|
SDL_GPUBufferUsageFlags usageFlags,
|
|
VulkanBufferType type)
|
|
{
|
|
VulkanBuffer *buffer;
|
|
VkResult vulkanResult;
|
|
VkBufferCreateInfo bufferCreateInfo;
|
|
VkBufferUsageFlags vulkanUsageFlags = 0;
|
|
Uint8 bindResult;
|
|
|
|
if (usageFlags & SDL_GPU_BUFFERUSAGE_VERTEX_BIT) {
|
|
vulkanUsageFlags |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
|
}
|
|
|
|
if (usageFlags & SDL_GPU_BUFFERUSAGE_INDEX_BIT) {
|
|
vulkanUsageFlags |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
|
|
}
|
|
|
|
if (usageFlags & (SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ_BIT |
|
|
SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ_BIT |
|
|
SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE_BIT)) {
|
|
vulkanUsageFlags |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
}
|
|
|
|
if (usageFlags & SDL_GPU_BUFFERUSAGE_INDIRECT_BIT) {
|
|
vulkanUsageFlags |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
|
|
}
|
|
|
|
if (type == VULKAN_BUFFER_TYPE_UNIFORM) {
|
|
vulkanUsageFlags |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
|
} else {
|
|
// GPU buffers need transfer bits for defrag, transfer buffers need them for transfers
|
|
vulkanUsageFlags |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
}
|
|
|
|
buffer = SDL_malloc(sizeof(VulkanBuffer));
|
|
|
|
buffer->size = size;
|
|
buffer->usageFlags = usageFlags;
|
|
buffer->type = type;
|
|
buffer->markedForDestroy = 0;
|
|
buffer->transitioned = false;
|
|
|
|
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bufferCreateInfo.pNext = NULL;
|
|
bufferCreateInfo.flags = 0;
|
|
bufferCreateInfo.size = size;
|
|
bufferCreateInfo.usage = vulkanUsageFlags;
|
|
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
bufferCreateInfo.queueFamilyIndexCount = 1;
|
|
bufferCreateInfo.pQueueFamilyIndices = &renderer->queueFamilyIndex;
|
|
|
|
// Set transfer bits so we can defrag
|
|
bufferCreateInfo.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
|
|
vulkanResult = renderer->vkCreateBuffer(
|
|
renderer->logicalDevice,
|
|
&bufferCreateInfo,
|
|
NULL,
|
|
&buffer->buffer);
|
|
VULKAN_ERROR_CHECK(vulkanResult, vkCreateBuffer, 0)
|
|
|
|
bindResult = VULKAN_INTERNAL_BindMemoryForBuffer(
|
|
renderer,
|
|
buffer->buffer,
|
|
buffer->size,
|
|
buffer->type,
|
|
&buffer->usedRegion);
|
|
|
|
if (bindResult != 1) {
|
|
renderer->vkDestroyBuffer(
|
|
renderer->logicalDevice,
|
|
buffer->buffer,
|
|
NULL);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
buffer->usedRegion->vulkanBuffer = buffer; // lol
|
|
buffer->handle = NULL;
|
|
|
|
SDL_AtomicSet(&buffer->referenceCount, 0);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
// Indirection so we can cleanly defrag buffers
|
|
static VulkanBufferHandle *VULKAN_INTERNAL_CreateBufferHandle(
|
|
VulkanRenderer *renderer,
|
|
VkDeviceSize sizeInBytes,
|
|
SDL_GPUBufferUsageFlags usageFlags,
|
|
VulkanBufferType type)
|
|
{
|
|
VulkanBufferHandle *bufferHandle;
|
|
VulkanBuffer *buffer;
|
|
|
|
buffer = VULKAN_INTERNAL_CreateBuffer(
|
|
renderer,
|
|
sizeInBytes,
|
|
usageFlags,
|
|
type);
|
|
|
|
if (buffer == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create buffer!");
|
|
return NULL;
|
|
}
|
|
|
|
bufferHandle = SDL_malloc(sizeof(VulkanBufferHandle));
|
|
bufferHandle->vulkanBuffer = buffer;
|
|
bufferHandle->container = NULL;
|
|
|
|
buffer->handle = bufferHandle;
|
|
|
|
return bufferHandle;
|
|
}
|
|
|
|
static VulkanBufferContainer *VULKAN_INTERNAL_CreateBufferContainer(
|
|
VulkanRenderer *renderer,
|
|
VkDeviceSize sizeInBytes,
|
|
SDL_GPUBufferUsageFlags usageFlags,
|
|
VulkanBufferType type)
|
|
{
|
|
VulkanBufferContainer *bufferContainer;
|
|
VulkanBufferHandle *bufferHandle;
|
|
|
|
bufferHandle = VULKAN_INTERNAL_CreateBufferHandle(
|
|
renderer,
|
|
sizeInBytes,
|
|
usageFlags,
|
|
type);
|
|
|
|
if (bufferHandle == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create buffer container!");
|
|
return NULL;
|
|
}
|
|
|
|
bufferContainer = SDL_malloc(sizeof(VulkanBufferContainer));
|
|
|
|
bufferContainer->activeBufferHandle = bufferHandle;
|
|
bufferHandle->container = bufferContainer;
|
|
|
|
bufferContainer->bufferCapacity = 1;
|
|
bufferContainer->bufferCount = 1;
|
|
bufferContainer->bufferHandles = SDL_malloc(
|
|
bufferContainer->bufferCapacity * sizeof(VulkanBufferHandle *));
|
|
bufferContainer->bufferHandles[0] = bufferContainer->activeBufferHandle;
|
|
bufferContainer->debugName = NULL;
|
|
|
|
return bufferContainer;
|
|
}
|
|
|
|
// Texture Subresource Utilities
|
|
|
|
static Uint32 VULKAN_INTERNAL_GetTextureSubresourceIndex(
|
|
Uint32 mipLevel,
|
|
Uint32 layer,
|
|
Uint32 numLevels)
|
|
{
|
|
return mipLevel + (layer * numLevels);
|
|
}
|
|
|
|
static VulkanTextureSubresource *VULKAN_INTERNAL_FetchTextureSubresource(
|
|
VulkanTextureContainer *textureContainer,
|
|
Uint32 layer,
|
|
Uint32 level)
|
|
{
|
|
Uint32 index = VULKAN_INTERNAL_GetTextureSubresourceIndex(
|
|
level,
|
|
layer,
|
|
textureContainer->header.info.levelCount);
|
|
|
|
return &textureContainer->activeTextureHandle->vulkanTexture->subresources[index];
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_CreateRenderTargetView(
|
|
VulkanRenderer *renderer,
|
|
VulkanTexture *texture,
|
|
Uint32 layerOrDepth,
|
|
Uint32 level,
|
|
VkComponentMapping swizzle,
|
|
VkImageView *pView)
|
|
{
|
|
VkResult vulkanResult;
|
|
VkImageViewCreateInfo imageViewCreateInfo;
|
|
|
|
// create framebuffer compatible views for RenderTarget
|
|
imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
imageViewCreateInfo.pNext = NULL;
|
|
imageViewCreateInfo.flags = 0;
|
|
imageViewCreateInfo.image = texture->image;
|
|
imageViewCreateInfo.format = texture->format;
|
|
imageViewCreateInfo.components = swizzle;
|
|
imageViewCreateInfo.subresourceRange.aspectMask = texture->aspectFlags;
|
|
imageViewCreateInfo.subresourceRange.baseMipLevel = level;
|
|
imageViewCreateInfo.subresourceRange.levelCount = 1;
|
|
imageViewCreateInfo.subresourceRange.baseArrayLayer = layerOrDepth;
|
|
imageViewCreateInfo.subresourceRange.layerCount = 1;
|
|
imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
vulkanResult = renderer->vkCreateImageView(
|
|
renderer->logicalDevice,
|
|
&imageViewCreateInfo,
|
|
NULL,
|
|
pView);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError(
|
|
"vkCreateImageView",
|
|
vulkanResult);
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create color attachment image view");
|
|
*pView = (VkImageView)VK_NULL_HANDLE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_CreateSubresourceView(
|
|
VulkanRenderer *renderer,
|
|
VulkanTexture *texture,
|
|
Uint32 layer,
|
|
Uint32 level,
|
|
VkComponentMapping swizzle,
|
|
VkImageView *pView)
|
|
{
|
|
VkResult vulkanResult;
|
|
VkImageViewCreateInfo imageViewCreateInfo;
|
|
|
|
// create framebuffer compatible views for RenderTarget
|
|
imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
imageViewCreateInfo.pNext = NULL;
|
|
imageViewCreateInfo.flags = 0;
|
|
imageViewCreateInfo.image = texture->image;
|
|
imageViewCreateInfo.format = texture->format;
|
|
imageViewCreateInfo.components = swizzle;
|
|
imageViewCreateInfo.subresourceRange.aspectMask = texture->aspectFlags;
|
|
imageViewCreateInfo.subresourceRange.baseMipLevel = level;
|
|
imageViewCreateInfo.subresourceRange.levelCount = 1;
|
|
imageViewCreateInfo.subresourceRange.baseArrayLayer = layer;
|
|
imageViewCreateInfo.subresourceRange.layerCount = 1;
|
|
imageViewCreateInfo.viewType = texture->depth > 1 ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
vulkanResult = renderer->vkCreateImageView(
|
|
renderer->logicalDevice,
|
|
&imageViewCreateInfo,
|
|
NULL,
|
|
pView);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError(
|
|
"vkCreateImageView",
|
|
vulkanResult);
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create color attachment image view");
|
|
*pView = (VkImageView)VK_NULL_HANDLE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Swapchain
|
|
|
|
static Uint8 VULKAN_INTERNAL_QuerySwapchainSupport(
|
|
VulkanRenderer *renderer,
|
|
VkPhysicalDevice physicalDevice,
|
|
VkSurfaceKHR surface,
|
|
SwapchainSupportDetails *outputDetails)
|
|
{
|
|
VkResult result;
|
|
VkBool32 supportsPresent;
|
|
|
|
renderer->vkGetPhysicalDeviceSurfaceSupportKHR(
|
|
physicalDevice,
|
|
renderer->queueFamilyIndex,
|
|
surface,
|
|
&supportsPresent);
|
|
|
|
// Initialize these in case anything fails
|
|
outputDetails->formatsLength = 0;
|
|
outputDetails->presentModesLength = 0;
|
|
|
|
if (!supportsPresent) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "This surface does not support presenting!");
|
|
return 0;
|
|
}
|
|
|
|
// Run the device surface queries
|
|
result = renderer->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
|
|
physicalDevice,
|
|
surface,
|
|
&outputDetails->capabilities);
|
|
VULKAN_ERROR_CHECK(result, vkGetPhysicalDeviceSurfaceCapabilitiesKHR, 0)
|
|
|
|
if (!(outputDetails->capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Opaque presentation unsupported! Expect weird transparency bugs!");
|
|
}
|
|
|
|
result = renderer->vkGetPhysicalDeviceSurfaceFormatsKHR(
|
|
physicalDevice,
|
|
surface,
|
|
&outputDetails->formatsLength,
|
|
NULL);
|
|
VULKAN_ERROR_CHECK(result, vkGetPhysicalDeviceSurfaceFormatsKHR, 0)
|
|
result = renderer->vkGetPhysicalDeviceSurfacePresentModesKHR(
|
|
physicalDevice,
|
|
surface,
|
|
&outputDetails->presentModesLength,
|
|
NULL);
|
|
VULKAN_ERROR_CHECK(result, vkGetPhysicalDeviceSurfacePresentModesKHR, 0)
|
|
|
|
// Generate the arrays, if applicable
|
|
|
|
outputDetails->formats = NULL;
|
|
if (outputDetails->formatsLength != 0) {
|
|
outputDetails->formats = (VkSurfaceFormatKHR *)SDL_malloc(
|
|
sizeof(VkSurfaceFormatKHR) * outputDetails->formatsLength);
|
|
|
|
if (!outputDetails->formats) {
|
|
return 0;
|
|
}
|
|
|
|
result = renderer->vkGetPhysicalDeviceSurfaceFormatsKHR(
|
|
physicalDevice,
|
|
surface,
|
|
&outputDetails->formatsLength,
|
|
outputDetails->formats);
|
|
if (result != VK_SUCCESS) {
|
|
SDL_LogError(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"vkGetPhysicalDeviceSurfaceFormatsKHR: %s",
|
|
VkErrorMessages(result));
|
|
|
|
SDL_free(outputDetails->formats);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
outputDetails->presentModes = NULL;
|
|
if (outputDetails->presentModesLength != 0) {
|
|
outputDetails->presentModes = (VkPresentModeKHR *)SDL_malloc(
|
|
sizeof(VkPresentModeKHR) * outputDetails->presentModesLength);
|
|
|
|
if (!outputDetails->presentModes) {
|
|
SDL_free(outputDetails->formats);
|
|
return 0;
|
|
}
|
|
|
|
result = renderer->vkGetPhysicalDeviceSurfacePresentModesKHR(
|
|
physicalDevice,
|
|
surface,
|
|
&outputDetails->presentModesLength,
|
|
outputDetails->presentModes);
|
|
if (result != VK_SUCCESS) {
|
|
SDL_LogError(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"vkGetPhysicalDeviceSurfacePresentModesKHR: %s",
|
|
VkErrorMessages(result));
|
|
|
|
SDL_free(outputDetails->formats);
|
|
SDL_free(outputDetails->presentModes);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* If we made it here, all the queries were successful. This does NOT
|
|
* necessarily mean there are any supported formats or present modes!
|
|
*/
|
|
return 1;
|
|
}
|
|
|
|
static bool VULKAN_INTERNAL_VerifySwapSurfaceFormat(
|
|
VkFormat desiredFormat,
|
|
VkColorSpaceKHR desiredColorSpace,
|
|
VkSurfaceFormatKHR *availableFormats,
|
|
Uint32 availableFormatsLength)
|
|
{
|
|
Uint32 i;
|
|
for (i = 0; i < availableFormatsLength; i += 1) {
|
|
if (availableFormats[i].format == desiredFormat &&
|
|
availableFormats[i].colorSpace == desiredColorSpace) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool VULKAN_INTERNAL_VerifySwapPresentMode(
|
|
VkPresentModeKHR presentMode,
|
|
VkPresentModeKHR *availablePresentModes,
|
|
Uint32 availablePresentModesLength)
|
|
{
|
|
Uint32 i;
|
|
for (i = 0; i < availablePresentModesLength; i += 1) {
|
|
if (availablePresentModes[i] == presentMode) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool VULKAN_INTERNAL_CreateSwapchain(
|
|
VulkanRenderer *renderer,
|
|
WindowData *windowData)
|
|
{
|
|
VkResult vulkanResult;
|
|
VulkanSwapchainData *swapchainData;
|
|
VkSwapchainCreateInfoKHR swapchainCreateInfo;
|
|
VkImage *swapchainImages;
|
|
VkSemaphoreCreateInfo semaphoreCreateInfo;
|
|
SwapchainSupportDetails swapchainSupportDetails;
|
|
bool hasValidSwapchainComposition, hasValidPresentMode;
|
|
Sint32 drawableWidth, drawableHeight;
|
|
Uint32 i;
|
|
SDL_VideoDevice *_this = SDL_GetVideoDevice();
|
|
|
|
SDL_assert(_this && _this->Vulkan_CreateSurface);
|
|
|
|
swapchainData = SDL_malloc(sizeof(VulkanSwapchainData));
|
|
swapchainData->frameCounter = 0;
|
|
|
|
// Each swapchain must have its own surface.
|
|
|
|
if (!_this->Vulkan_CreateSurface(
|
|
_this,
|
|
windowData->window,
|
|
renderer->instance,
|
|
NULL, // FIXME: VAllocationCallbacks
|
|
&swapchainData->surface)) {
|
|
SDL_free(swapchainData);
|
|
SDL_LogError(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"Vulkan_CreateSurface failed: %s",
|
|
SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
if (!VULKAN_INTERNAL_QuerySwapchainSupport(
|
|
renderer,
|
|
renderer->physicalDevice,
|
|
swapchainData->surface,
|
|
&swapchainSupportDetails)) {
|
|
renderer->vkDestroySurfaceKHR(
|
|
renderer->instance,
|
|
swapchainData->surface,
|
|
NULL);
|
|
if (swapchainSupportDetails.formatsLength > 0) {
|
|
SDL_free(swapchainSupportDetails.formats);
|
|
}
|
|
if (swapchainSupportDetails.presentModesLength > 0) {
|
|
SDL_free(swapchainSupportDetails.presentModes);
|
|
}
|
|
SDL_free(swapchainData);
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Device does not support swap chain creation");
|
|
return false;
|
|
}
|
|
|
|
if (swapchainSupportDetails.capabilities.currentExtent.width == 0 ||
|
|
swapchainSupportDetails.capabilities.currentExtent.height == 0) {
|
|
// Not an error, just minimize behavior!
|
|
renderer->vkDestroySurfaceKHR(
|
|
renderer->instance,
|
|
swapchainData->surface,
|
|
NULL);
|
|
if (swapchainSupportDetails.formatsLength > 0) {
|
|
SDL_free(swapchainSupportDetails.formats);
|
|
}
|
|
if (swapchainSupportDetails.presentModesLength > 0) {
|
|
SDL_free(swapchainSupportDetails.presentModes);
|
|
}
|
|
SDL_free(swapchainData);
|
|
return false;
|
|
}
|
|
|
|
// Verify that we can use the requested composition and present mode
|
|
|
|
swapchainData->format = SwapchainCompositionToFormat[windowData->swapchainComposition];
|
|
swapchainData->colorSpace = SwapchainCompositionToColorSpace[windowData->swapchainComposition];
|
|
swapchainData->swapchainSwizzle = SwapchainCompositionSwizzle[windowData->swapchainComposition];
|
|
swapchainData->usingFallbackFormat = false;
|
|
|
|
hasValidSwapchainComposition = VULKAN_INTERNAL_VerifySwapSurfaceFormat(
|
|
swapchainData->format,
|
|
swapchainData->colorSpace,
|
|
swapchainSupportDetails.formats,
|
|
swapchainSupportDetails.formatsLength);
|
|
|
|
if (!hasValidSwapchainComposition) {
|
|
// Let's try again with the fallback format...
|
|
swapchainData->format = SwapchainCompositionToFallbackFormat[windowData->swapchainComposition];
|
|
swapchainData->usingFallbackFormat = true;
|
|
hasValidSwapchainComposition = VULKAN_INTERNAL_VerifySwapSurfaceFormat(
|
|
swapchainData->format,
|
|
swapchainData->colorSpace,
|
|
swapchainSupportDetails.formats,
|
|
swapchainSupportDetails.formatsLength);
|
|
}
|
|
|
|
swapchainData->presentMode = SDLToVK_PresentMode[windowData->presentMode];
|
|
hasValidPresentMode = VULKAN_INTERNAL_VerifySwapPresentMode(
|
|
swapchainData->presentMode,
|
|
swapchainSupportDetails.presentModes,
|
|
swapchainSupportDetails.presentModesLength);
|
|
|
|
if (!hasValidSwapchainComposition || !hasValidPresentMode) {
|
|
renderer->vkDestroySurfaceKHR(
|
|
renderer->instance,
|
|
swapchainData->surface,
|
|
NULL);
|
|
|
|
if (swapchainSupportDetails.formatsLength > 0) {
|
|
SDL_free(swapchainSupportDetails.formats);
|
|
}
|
|
|
|
if (swapchainSupportDetails.presentModesLength > 0) {
|
|
SDL_free(swapchainSupportDetails.presentModes);
|
|
}
|
|
|
|
SDL_free(swapchainData);
|
|
|
|
if (!hasValidSwapchainComposition) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Device does not support requested swapchain composition!");
|
|
}
|
|
if (!hasValidPresentMode) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Device does not support requested presentMode!");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Sync now to be sure that our swapchain size is correct
|
|
SDL_SyncWindow(windowData->window);
|
|
SDL_GetWindowSizeInPixels(
|
|
windowData->window,
|
|
&drawableWidth,
|
|
&drawableHeight);
|
|
|
|
if (drawableWidth < (Sint32)swapchainSupportDetails.capabilities.minImageExtent.width ||
|
|
drawableWidth > (Sint32)swapchainSupportDetails.capabilities.maxImageExtent.width ||
|
|
drawableHeight < (Sint32)swapchainSupportDetails.capabilities.minImageExtent.height ||
|
|
drawableHeight > (Sint32)swapchainSupportDetails.capabilities.maxImageExtent.height) {
|
|
if (swapchainSupportDetails.capabilities.currentExtent.width != UINT32_MAX) {
|
|
drawableWidth = VULKAN_INTERNAL_clamp(
|
|
drawableWidth,
|
|
(Sint32)swapchainSupportDetails.capabilities.minImageExtent.width,
|
|
(Sint32)swapchainSupportDetails.capabilities.maxImageExtent.width);
|
|
drawableHeight = VULKAN_INTERNAL_clamp(
|
|
drawableHeight,
|
|
(Sint32)swapchainSupportDetails.capabilities.minImageExtent.height,
|
|
(Sint32)swapchainSupportDetails.capabilities.maxImageExtent.height);
|
|
} else {
|
|
renderer->vkDestroySurfaceKHR(
|
|
renderer->instance,
|
|
swapchainData->surface,
|
|
NULL);
|
|
if (swapchainSupportDetails.formatsLength > 0) {
|
|
SDL_free(swapchainSupportDetails.formats);
|
|
}
|
|
if (swapchainSupportDetails.presentModesLength > 0) {
|
|
SDL_free(swapchainSupportDetails.presentModes);
|
|
}
|
|
SDL_free(swapchainData);
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "No fallback swapchain size available!");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
swapchainData->imageCount = MAX_FRAMES_IN_FLIGHT;
|
|
|
|
if (swapchainSupportDetails.capabilities.maxImageCount > 0 &&
|
|
swapchainData->imageCount > swapchainSupportDetails.capabilities.maxImageCount) {
|
|
swapchainData->imageCount = swapchainSupportDetails.capabilities.maxImageCount;
|
|
}
|
|
|
|
if (swapchainData->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
|
|
/* Required for proper triple-buffering.
|
|
* Note that this is below the above maxImageCount check!
|
|
* If the driver advertises MAILBOX but does not support 3 swap
|
|
* images, it's not real mailbox support, so let it fail hard.
|
|
* -flibit
|
|
*/
|
|
swapchainData->imageCount = SDL_max(swapchainData->imageCount, 3);
|
|
}
|
|
|
|
swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
|
swapchainCreateInfo.pNext = NULL;
|
|
swapchainCreateInfo.flags = 0;
|
|
swapchainCreateInfo.surface = swapchainData->surface;
|
|
swapchainCreateInfo.minImageCount = swapchainData->imageCount;
|
|
swapchainCreateInfo.imageFormat = swapchainData->format;
|
|
swapchainCreateInfo.imageColorSpace = swapchainData->colorSpace;
|
|
swapchainCreateInfo.imageExtent.width = drawableWidth;
|
|
swapchainCreateInfo.imageExtent.height = drawableHeight;
|
|
swapchainCreateInfo.imageArrayLayers = 1;
|
|
swapchainCreateInfo.imageUsage =
|
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
|
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
swapchainCreateInfo.queueFamilyIndexCount = 0;
|
|
swapchainCreateInfo.pQueueFamilyIndices = NULL;
|
|
swapchainCreateInfo.preTransform = swapchainSupportDetails.capabilities.currentTransform;
|
|
swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
|
swapchainCreateInfo.presentMode = swapchainData->presentMode;
|
|
swapchainCreateInfo.clipped = VK_TRUE;
|
|
swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE;
|
|
|
|
vulkanResult = renderer->vkCreateSwapchainKHR(
|
|
renderer->logicalDevice,
|
|
&swapchainCreateInfo,
|
|
NULL,
|
|
&swapchainData->swapchain);
|
|
|
|
if (swapchainSupportDetails.formatsLength > 0) {
|
|
SDL_free(swapchainSupportDetails.formats);
|
|
}
|
|
if (swapchainSupportDetails.presentModesLength > 0) {
|
|
SDL_free(swapchainSupportDetails.presentModes);
|
|
}
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
renderer->vkDestroySurfaceKHR(
|
|
renderer->instance,
|
|
swapchainData->surface,
|
|
NULL);
|
|
SDL_free(swapchainData);
|
|
LogVulkanResultAsError("vkCreateSwapchainKHR", vulkanResult);
|
|
return false;
|
|
}
|
|
|
|
renderer->vkGetSwapchainImagesKHR(
|
|
renderer->logicalDevice,
|
|
swapchainData->swapchain,
|
|
&swapchainData->imageCount,
|
|
NULL);
|
|
|
|
swapchainData->textureContainers = SDL_malloc(
|
|
sizeof(VulkanTextureContainer) * swapchainData->imageCount);
|
|
|
|
if (!swapchainData->textureContainers) {
|
|
renderer->vkDestroySurfaceKHR(
|
|
renderer->instance,
|
|
swapchainData->surface,
|
|
NULL);
|
|
SDL_free(swapchainData);
|
|
return false;
|
|
}
|
|
|
|
swapchainImages = SDL_stack_alloc(VkImage, swapchainData->imageCount);
|
|
|
|
renderer->vkGetSwapchainImagesKHR(
|
|
renderer->logicalDevice,
|
|
swapchainData->swapchain,
|
|
&swapchainData->imageCount,
|
|
swapchainImages);
|
|
|
|
for (i = 0; i < swapchainData->imageCount; i += 1) {
|
|
|
|
// Initialize dummy container
|
|
SDL_zero(swapchainData->textureContainers[i]);
|
|
swapchainData->textureContainers[i].canBeCycled = false;
|
|
swapchainData->textureContainers[i].header.info.width = drawableWidth;
|
|
swapchainData->textureContainers[i].header.info.height = drawableHeight;
|
|
swapchainData->textureContainers[i].header.info.layerCountOrDepth = 1;
|
|
swapchainData->textureContainers[i].header.info.format = SwapchainCompositionToSDLFormat(
|
|
windowData->swapchainComposition,
|
|
swapchainData->usingFallbackFormat);
|
|
swapchainData->textureContainers[i].header.info.type = SDL_GPU_TEXTURETYPE_2D;
|
|
swapchainData->textureContainers[i].header.info.levelCount = 1;
|
|
swapchainData->textureContainers[i].header.info.sampleCount = SDL_GPU_SAMPLECOUNT_1;
|
|
swapchainData->textureContainers[i].header.info.usageFlags = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT;
|
|
|
|
swapchainData->textureContainers[i].activeTextureHandle = SDL_malloc(sizeof(VulkanTextureHandle));
|
|
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture = SDL_malloc(sizeof(VulkanTexture));
|
|
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->image = swapchainImages[i];
|
|
|
|
// Swapchain memory is managed by the driver
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->usedRegion = NULL;
|
|
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->dimensions.width = drawableWidth;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->dimensions.height = drawableHeight;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->format = swapchainData->format;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->swizzle = swapchainData->swapchainSwizzle;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->type = SDL_GPU_TEXTURETYPE_2D;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->depth = 1;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->layerCount = 1;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->levelCount = 1;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->usageFlags =
|
|
SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
SDL_AtomicSet(&swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->referenceCount, 0);
|
|
|
|
swapchainData->textureContainers[i].activeTextureHandle->container = NULL;
|
|
|
|
// Create slice
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresourceCount = 1;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources = SDL_malloc(sizeof(VulkanTextureSubresource));
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].parent = swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].layer = 0;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].level = 0;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].transitioned = true;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].msaaTexHandle = NULL;
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].renderTargetViews = SDL_malloc(sizeof(VkImageView));
|
|
VULKAN_INTERNAL_CreateRenderTargetView(
|
|
renderer,
|
|
swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture,
|
|
0,
|
|
0,
|
|
swapchainData->swapchainSwizzle,
|
|
&swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].renderTargetViews[0]);
|
|
}
|
|
|
|
SDL_stack_free(swapchainImages);
|
|
|
|
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
semaphoreCreateInfo.pNext = NULL;
|
|
semaphoreCreateInfo.flags = 0;
|
|
|
|
for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
|
|
renderer->vkCreateSemaphore(
|
|
renderer->logicalDevice,
|
|
&semaphoreCreateInfo,
|
|
NULL,
|
|
&swapchainData->imageAvailableSemaphore[i]);
|
|
|
|
renderer->vkCreateSemaphore(
|
|
renderer->logicalDevice,
|
|
&semaphoreCreateInfo,
|
|
NULL,
|
|
&swapchainData->renderFinishedSemaphore[i]);
|
|
|
|
swapchainData->inFlightFences[i] = NULL;
|
|
}
|
|
|
|
windowData->swapchainData = swapchainData;
|
|
windowData->needsSwapchainRecreate = false;
|
|
return true;
|
|
}
|
|
|
|
// Command Buffers
|
|
|
|
static void VULKAN_INTERNAL_BeginCommandBuffer(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer)
|
|
{
|
|
VkCommandBufferBeginInfo beginInfo;
|
|
VkResult result;
|
|
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
beginInfo.pNext = NULL;
|
|
beginInfo.flags = 0;
|
|
beginInfo.pInheritanceInfo = NULL;
|
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
|
|
result = renderer->vkBeginCommandBuffer(
|
|
commandBuffer->commandBuffer,
|
|
&beginInfo);
|
|
|
|
if (result != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkBeginCommandBuffer", result);
|
|
}
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_EndCommandBuffer(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer)
|
|
{
|
|
VkResult result;
|
|
|
|
result = renderer->vkEndCommandBuffer(
|
|
commandBuffer->commandBuffer);
|
|
|
|
if (result != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkEndCommandBuffer", result);
|
|
}
|
|
}
|
|
|
|
static void VULKAN_DestroyDevice(
|
|
SDL_GPUDevice *device)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)device->driverData;
|
|
VulkanMemorySubAllocator *allocator;
|
|
|
|
VULKAN_Wait(device->driverData);
|
|
|
|
for (Sint32 i = renderer->claimedWindowCount - 1; i >= 0; i -= 1) {
|
|
VULKAN_ReleaseWindow(device->driverData, renderer->claimedWindows[i]->window);
|
|
}
|
|
|
|
SDL_free(renderer->claimedWindows);
|
|
|
|
VULKAN_Wait(device->driverData);
|
|
|
|
SDL_free(renderer->submittedCommandBuffers);
|
|
|
|
for (Uint32 i = 0; i < renderer->uniformBufferPoolCount; i += 1) {
|
|
VULKAN_INTERNAL_DestroyBuffer(
|
|
renderer,
|
|
renderer->uniformBufferPool[i]->bufferHandle->vulkanBuffer);
|
|
SDL_free(renderer->uniformBufferPool[i]->bufferHandle);
|
|
SDL_free(renderer->uniformBufferPool[i]);
|
|
}
|
|
SDL_free(renderer->uniformBufferPool);
|
|
|
|
for (Uint32 i = 0; i < renderer->fencePool.availableFenceCount; i += 1) {
|
|
renderer->vkDestroyFence(
|
|
renderer->logicalDevice,
|
|
renderer->fencePool.availableFences[i]->fence,
|
|
NULL);
|
|
|
|
SDL_free(renderer->fencePool.availableFences[i]);
|
|
}
|
|
|
|
SDL_free(renderer->fencePool.availableFences);
|
|
SDL_DestroyMutex(renderer->fencePool.lock);
|
|
|
|
SDL_DestroyHashTable(renderer->commandPoolHashTable);
|
|
SDL_DestroyHashTable(renderer->renderPassHashTable);
|
|
SDL_DestroyHashTable(renderer->framebufferHashTable);
|
|
|
|
for (Uint32 i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) {
|
|
allocator = &renderer->memoryAllocator->subAllocators[i];
|
|
|
|
for (Sint32 j = allocator->allocationCount - 1; j >= 0; j -= 1) {
|
|
for (Sint32 k = allocator->allocations[j]->usedRegionCount - 1; k >= 0; k -= 1) {
|
|
VULKAN_INTERNAL_RemoveMemoryUsedRegion(
|
|
renderer,
|
|
allocator->allocations[j]->usedRegions[k]);
|
|
}
|
|
|
|
VULKAN_INTERNAL_DeallocateMemory(
|
|
renderer,
|
|
allocator,
|
|
j);
|
|
}
|
|
|
|
if (renderer->memoryAllocator->subAllocators[i].allocations != NULL) {
|
|
SDL_free(renderer->memoryAllocator->subAllocators[i].allocations);
|
|
}
|
|
|
|
SDL_free(renderer->memoryAllocator->subAllocators[i].sortedFreeRegions);
|
|
}
|
|
|
|
SDL_free(renderer->memoryAllocator);
|
|
|
|
SDL_free(renderer->texturesToDestroy);
|
|
SDL_free(renderer->buffersToDestroy);
|
|
SDL_free(renderer->graphicsPipelinesToDestroy);
|
|
SDL_free(renderer->computePipelinesToDestroy);
|
|
SDL_free(renderer->shadersToDestroy);
|
|
SDL_free(renderer->samplersToDestroy);
|
|
SDL_free(renderer->framebuffersToDestroy);
|
|
SDL_free(renderer->allocationsToDefrag);
|
|
|
|
SDL_DestroyMutex(renderer->allocatorLock);
|
|
SDL_DestroyMutex(renderer->disposeLock);
|
|
SDL_DestroyMutex(renderer->submitLock);
|
|
SDL_DestroyMutex(renderer->acquireCommandBufferLock);
|
|
SDL_DestroyMutex(renderer->acquireUniformBufferLock);
|
|
SDL_DestroyMutex(renderer->renderPassFetchLock);
|
|
SDL_DestroyMutex(renderer->framebufferFetchLock);
|
|
|
|
renderer->vkDestroyDevice(renderer->logicalDevice, NULL);
|
|
renderer->vkDestroyInstance(renderer->instance, NULL);
|
|
|
|
SDL_free(renderer);
|
|
SDL_free(device);
|
|
SDL_Vulkan_UnloadLibrary();
|
|
}
|
|
|
|
static VkDescriptorSet VULKAN_INTERNAL_FetchDescriptorSet(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *vulkanCommandBuffer,
|
|
DescriptorSetPool *descriptorSetPool)
|
|
{
|
|
VkDescriptorSet descriptorSet;
|
|
|
|
SDL_LockMutex(descriptorSetPool->lock);
|
|
|
|
// If no inactive descriptor sets remain, create a new pool and allocate new inactive sets
|
|
|
|
if (descriptorSetPool->inactiveDescriptorSetCount == 0) {
|
|
descriptorSetPool->descriptorPoolCount += 1;
|
|
descriptorSetPool->descriptorPools = SDL_realloc(
|
|
descriptorSetPool->descriptorPools,
|
|
sizeof(VkDescriptorPool) * descriptorSetPool->descriptorPoolCount);
|
|
|
|
if (!VULKAN_INTERNAL_CreateDescriptorPool(
|
|
renderer,
|
|
descriptorSetPool->descriptorInfos,
|
|
descriptorSetPool->descriptorInfoCount,
|
|
descriptorSetPool->nextPoolSize,
|
|
&descriptorSetPool->descriptorPools[descriptorSetPool->descriptorPoolCount - 1])) {
|
|
SDL_UnlockMutex(descriptorSetPool->lock);
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create descriptor pool!");
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
descriptorSetPool->inactiveDescriptorSetCapacity += descriptorSetPool->nextPoolSize;
|
|
|
|
descriptorSetPool->inactiveDescriptorSets = SDL_realloc(
|
|
descriptorSetPool->inactiveDescriptorSets,
|
|
sizeof(VkDescriptorSet) * descriptorSetPool->inactiveDescriptorSetCapacity);
|
|
|
|
if (!VULKAN_INTERNAL_AllocateDescriptorSets(
|
|
renderer,
|
|
descriptorSetPool->descriptorPools[descriptorSetPool->descriptorPoolCount - 1],
|
|
descriptorSetPool->descriptorSetLayout,
|
|
descriptorSetPool->nextPoolSize,
|
|
descriptorSetPool->inactiveDescriptorSets)) {
|
|
SDL_UnlockMutex(descriptorSetPool->lock);
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to allocate descriptor sets!");
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
descriptorSetPool->inactiveDescriptorSetCount = descriptorSetPool->nextPoolSize;
|
|
|
|
descriptorSetPool->nextPoolSize *= 2;
|
|
}
|
|
|
|
descriptorSet = descriptorSetPool->inactiveDescriptorSets[descriptorSetPool->inactiveDescriptorSetCount - 1];
|
|
descriptorSetPool->inactiveDescriptorSetCount -= 1;
|
|
|
|
SDL_UnlockMutex(descriptorSetPool->lock);
|
|
|
|
if (vulkanCommandBuffer->boundDescriptorSetDataCount == vulkanCommandBuffer->boundDescriptorSetDataCapacity) {
|
|
vulkanCommandBuffer->boundDescriptorSetDataCapacity *= 2;
|
|
vulkanCommandBuffer->boundDescriptorSetDatas = SDL_realloc(
|
|
vulkanCommandBuffer->boundDescriptorSetDatas,
|
|
vulkanCommandBuffer->boundDescriptorSetDataCapacity * sizeof(DescriptorSetData));
|
|
}
|
|
|
|
vulkanCommandBuffer->boundDescriptorSetDatas[vulkanCommandBuffer->boundDescriptorSetDataCount].descriptorSet = descriptorSet;
|
|
vulkanCommandBuffer->boundDescriptorSetDatas[vulkanCommandBuffer->boundDescriptorSetDataCount].descriptorSetPool = descriptorSetPool;
|
|
vulkanCommandBuffer->boundDescriptorSetDataCount += 1;
|
|
|
|
return descriptorSet;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_BindGraphicsDescriptorSets(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer)
|
|
{
|
|
VulkanGraphicsPipelineResourceLayout *resourceLayout;
|
|
VkWriteDescriptorSet *writeDescriptorSets;
|
|
VkWriteDescriptorSet *currentWriteDescriptorSet;
|
|
DescriptorSetPool *descriptorSetPool;
|
|
VkDescriptorBufferInfo bufferInfos[MAX_STORAGE_BUFFERS_PER_STAGE];
|
|
VkDescriptorImageInfo imageInfos[MAX_TEXTURE_SAMPLERS_PER_STAGE + MAX_STORAGE_TEXTURES_PER_STAGE];
|
|
Uint32 dynamicOffsets[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
Uint32 bufferInfoCount = 0;
|
|
Uint32 imageInfoCount = 0;
|
|
Uint32 i;
|
|
|
|
resourceLayout = &commandBuffer->currentGraphicsPipeline->resourceLayout;
|
|
|
|
if (commandBuffer->needNewVertexResourceDescriptorSet) {
|
|
descriptorSetPool = &resourceLayout->descriptorSetPools[0];
|
|
|
|
commandBuffer->vertexResourceDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
|
|
renderer,
|
|
commandBuffer,
|
|
descriptorSetPool);
|
|
|
|
writeDescriptorSets = SDL_stack_alloc(
|
|
VkWriteDescriptorSet,
|
|
resourceLayout->vertexSamplerCount +
|
|
resourceLayout->vertexStorageTextureCount +
|
|
resourceLayout->vertexStorageBufferCount);
|
|
|
|
for (i = 0; i < resourceLayout->vertexSamplerCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[i];
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->vertexResourceDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pBufferInfo = NULL;
|
|
|
|
imageInfos[imageInfoCount].sampler = commandBuffer->vertexSamplers[i]->sampler;
|
|
imageInfos[imageInfoCount].imageView = commandBuffer->vertexSamplerTextures[i]->fullView;
|
|
imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
|
|
|
|
imageInfoCount += 1;
|
|
}
|
|
|
|
for (i = 0; i < resourceLayout->vertexStorageTextureCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[resourceLayout->vertexSamplerCount + i];
|
|
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = resourceLayout->vertexSamplerCount + i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->vertexResourceDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pBufferInfo = NULL;
|
|
|
|
imageInfos[imageInfoCount].sampler = VK_NULL_HANDLE;
|
|
imageInfos[imageInfoCount].imageView = commandBuffer->vertexStorageTextures[i]->fullView;
|
|
imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
|
|
|
|
imageInfoCount += 1;
|
|
}
|
|
|
|
for (i = 0; i < resourceLayout->vertexStorageBufferCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[resourceLayout->vertexSamplerCount + resourceLayout->vertexStorageTextureCount + i];
|
|
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = resourceLayout->vertexSamplerCount + resourceLayout->vertexStorageTextureCount + i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->vertexResourceDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pImageInfo = NULL;
|
|
|
|
bufferInfos[bufferInfoCount].buffer = commandBuffer->vertexStorageBuffers[i]->buffer;
|
|
bufferInfos[bufferInfoCount].offset = 0;
|
|
bufferInfos[bufferInfoCount].range = VK_WHOLE_SIZE;
|
|
|
|
currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
|
|
|
|
bufferInfoCount += 1;
|
|
}
|
|
|
|
renderer->vkUpdateDescriptorSets(
|
|
renderer->logicalDevice,
|
|
resourceLayout->vertexSamplerCount + resourceLayout->vertexStorageTextureCount + resourceLayout->vertexStorageBufferCount,
|
|
writeDescriptorSets,
|
|
0,
|
|
NULL);
|
|
|
|
renderer->vkCmdBindDescriptorSets(
|
|
commandBuffer->commandBuffer,
|
|
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
resourceLayout->pipelineLayout,
|
|
0,
|
|
1,
|
|
&commandBuffer->vertexResourceDescriptorSet,
|
|
0,
|
|
NULL);
|
|
|
|
SDL_stack_free(writeDescriptorSets);
|
|
bufferInfoCount = 0;
|
|
imageInfoCount = 0;
|
|
|
|
commandBuffer->needNewVertexResourceDescriptorSet = false;
|
|
}
|
|
|
|
if (commandBuffer->needNewVertexUniformDescriptorSet) {
|
|
descriptorSetPool = &resourceLayout->descriptorSetPools[1];
|
|
|
|
commandBuffer->vertexUniformDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
|
|
renderer,
|
|
commandBuffer,
|
|
descriptorSetPool);
|
|
|
|
writeDescriptorSets = SDL_stack_alloc(
|
|
VkWriteDescriptorSet,
|
|
resourceLayout->vertexUniformBufferCount);
|
|
|
|
for (i = 0; i < resourceLayout->vertexUniformBufferCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[i];
|
|
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->vertexUniformDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pImageInfo = NULL;
|
|
|
|
bufferInfos[bufferInfoCount].buffer = commandBuffer->vertexUniformBuffers[i]->bufferHandle->vulkanBuffer->buffer;
|
|
bufferInfos[bufferInfoCount].offset = 0;
|
|
bufferInfos[bufferInfoCount].range = MAX_UBO_SECTION_SIZE;
|
|
|
|
currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
|
|
|
|
bufferInfoCount += 1;
|
|
}
|
|
|
|
renderer->vkUpdateDescriptorSets(
|
|
renderer->logicalDevice,
|
|
resourceLayout->vertexUniformBufferCount,
|
|
writeDescriptorSets,
|
|
0,
|
|
NULL);
|
|
|
|
SDL_stack_free(writeDescriptorSets);
|
|
bufferInfoCount = 0;
|
|
imageInfoCount = 0;
|
|
|
|
commandBuffer->needNewVertexUniformDescriptorSet = false;
|
|
commandBuffer->needNewVertexUniformOffsets = true;
|
|
}
|
|
|
|
if (commandBuffer->needNewVertexUniformOffsets) {
|
|
for (i = 0; i < resourceLayout->vertexUniformBufferCount; i += 1) {
|
|
dynamicOffsets[i] = commandBuffer->vertexUniformBuffers[i]->drawOffset;
|
|
}
|
|
|
|
renderer->vkCmdBindDescriptorSets(
|
|
commandBuffer->commandBuffer,
|
|
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
resourceLayout->pipelineLayout,
|
|
1,
|
|
1,
|
|
&commandBuffer->vertexUniformDescriptorSet,
|
|
resourceLayout->vertexUniformBufferCount,
|
|
dynamicOffsets);
|
|
|
|
commandBuffer->needNewVertexUniformOffsets = false;
|
|
}
|
|
|
|
if (commandBuffer->needNewFragmentResourceDescriptorSet) {
|
|
descriptorSetPool = &resourceLayout->descriptorSetPools[2];
|
|
|
|
commandBuffer->fragmentResourceDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
|
|
renderer,
|
|
commandBuffer,
|
|
descriptorSetPool);
|
|
|
|
writeDescriptorSets = SDL_stack_alloc(
|
|
VkWriteDescriptorSet,
|
|
resourceLayout->fragmentSamplerCount +
|
|
resourceLayout->fragmentStorageTextureCount +
|
|
resourceLayout->fragmentStorageBufferCount);
|
|
|
|
for (i = 0; i < resourceLayout->fragmentSamplerCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[i];
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->fragmentResourceDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pBufferInfo = NULL;
|
|
|
|
imageInfos[imageInfoCount].sampler = commandBuffer->fragmentSamplers[i]->sampler;
|
|
imageInfos[imageInfoCount].imageView = commandBuffer->fragmentSamplerTextures[i]->fullView;
|
|
imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
|
|
|
|
imageInfoCount += 1;
|
|
}
|
|
|
|
for (i = 0; i < resourceLayout->fragmentStorageTextureCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[resourceLayout->fragmentSamplerCount + i];
|
|
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = resourceLayout->fragmentSamplerCount + i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->fragmentResourceDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pBufferInfo = NULL;
|
|
|
|
imageInfos[imageInfoCount].sampler = VK_NULL_HANDLE;
|
|
imageInfos[imageInfoCount].imageView = commandBuffer->fragmentStorageTextures[i]->fullView;
|
|
imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
|
|
|
|
imageInfoCount += 1;
|
|
}
|
|
|
|
for (i = 0; i < resourceLayout->fragmentStorageBufferCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[resourceLayout->fragmentSamplerCount + resourceLayout->fragmentStorageTextureCount + i];
|
|
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = resourceLayout->fragmentSamplerCount + resourceLayout->fragmentStorageTextureCount + i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->fragmentResourceDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pImageInfo = NULL;
|
|
|
|
bufferInfos[bufferInfoCount].buffer = commandBuffer->fragmentStorageBuffers[i]->buffer;
|
|
bufferInfos[bufferInfoCount].offset = 0;
|
|
bufferInfos[bufferInfoCount].range = VK_WHOLE_SIZE;
|
|
|
|
currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
|
|
|
|
bufferInfoCount += 1;
|
|
}
|
|
|
|
renderer->vkUpdateDescriptorSets(
|
|
renderer->logicalDevice,
|
|
resourceLayout->fragmentSamplerCount + resourceLayout->fragmentStorageTextureCount + resourceLayout->fragmentStorageBufferCount,
|
|
writeDescriptorSets,
|
|
0,
|
|
NULL);
|
|
|
|
renderer->vkCmdBindDescriptorSets(
|
|
commandBuffer->commandBuffer,
|
|
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
resourceLayout->pipelineLayout,
|
|
2,
|
|
1,
|
|
&commandBuffer->fragmentResourceDescriptorSet,
|
|
0,
|
|
NULL);
|
|
|
|
SDL_stack_free(writeDescriptorSets);
|
|
bufferInfoCount = 0;
|
|
imageInfoCount = 0;
|
|
|
|
commandBuffer->needNewFragmentResourceDescriptorSet = false;
|
|
}
|
|
|
|
if (commandBuffer->needNewFragmentUniformDescriptorSet) {
|
|
descriptorSetPool = &resourceLayout->descriptorSetPools[3];
|
|
|
|
commandBuffer->fragmentUniformDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
|
|
renderer,
|
|
commandBuffer,
|
|
descriptorSetPool);
|
|
|
|
writeDescriptorSets = SDL_stack_alloc(
|
|
VkWriteDescriptorSet,
|
|
resourceLayout->fragmentUniformBufferCount);
|
|
|
|
for (i = 0; i < resourceLayout->fragmentUniformBufferCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[i];
|
|
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->fragmentUniformDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pImageInfo = NULL;
|
|
|
|
bufferInfos[bufferInfoCount].buffer = commandBuffer->fragmentUniformBuffers[i]->bufferHandle->vulkanBuffer->buffer;
|
|
bufferInfos[bufferInfoCount].offset = 0;
|
|
bufferInfos[bufferInfoCount].range = MAX_UBO_SECTION_SIZE;
|
|
|
|
currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
|
|
|
|
bufferInfoCount += 1;
|
|
}
|
|
|
|
renderer->vkUpdateDescriptorSets(
|
|
renderer->logicalDevice,
|
|
resourceLayout->fragmentUniformBufferCount,
|
|
writeDescriptorSets,
|
|
0,
|
|
NULL);
|
|
|
|
SDL_stack_free(writeDescriptorSets);
|
|
bufferInfoCount = 0;
|
|
imageInfoCount = 0;
|
|
|
|
commandBuffer->needNewFragmentUniformDescriptorSet = false;
|
|
commandBuffer->needNewFragmentUniformOffsets = true;
|
|
}
|
|
|
|
if (commandBuffer->needNewFragmentUniformOffsets) {
|
|
for (i = 0; i < resourceLayout->fragmentUniformBufferCount; i += 1) {
|
|
dynamicOffsets[i] = commandBuffer->fragmentUniformBuffers[i]->drawOffset;
|
|
}
|
|
|
|
renderer->vkCmdBindDescriptorSets(
|
|
commandBuffer->commandBuffer,
|
|
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
resourceLayout->pipelineLayout,
|
|
3,
|
|
1,
|
|
&commandBuffer->fragmentUniformDescriptorSet,
|
|
resourceLayout->fragmentUniformBufferCount,
|
|
dynamicOffsets);
|
|
|
|
commandBuffer->needNewFragmentUniformOffsets = false;
|
|
}
|
|
}
|
|
|
|
static void VULKAN_DrawIndexedPrimitives(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 indexCount,
|
|
Uint32 instanceCount,
|
|
Uint32 firstIndex,
|
|
Sint32 vertexOffset,
|
|
Uint32 firstInstance)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
|
|
VULKAN_INTERNAL_BindGraphicsDescriptorSets(renderer, vulkanCommandBuffer);
|
|
|
|
renderer->vkCmdDrawIndexed(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
indexCount,
|
|
instanceCount,
|
|
firstIndex,
|
|
vertexOffset,
|
|
firstInstance);
|
|
}
|
|
|
|
static void VULKAN_DrawPrimitives(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 vertexCount,
|
|
Uint32 instanceCount,
|
|
Uint32 firstVertex,
|
|
Uint32 firstInstance)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
|
|
VULKAN_INTERNAL_BindGraphicsDescriptorSets(renderer, vulkanCommandBuffer);
|
|
|
|
renderer->vkCmdDraw(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
vertexCount,
|
|
instanceCount,
|
|
firstVertex,
|
|
firstInstance);
|
|
}
|
|
|
|
static void VULKAN_DrawPrimitivesIndirect(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUBuffer *buffer,
|
|
Uint32 offsetInBytes,
|
|
Uint32 drawCount,
|
|
Uint32 stride)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)buffer)->activeBufferHandle->vulkanBuffer;
|
|
Uint32 i;
|
|
|
|
VULKAN_INTERNAL_BindGraphicsDescriptorSets(renderer, vulkanCommandBuffer);
|
|
|
|
if (renderer->supportsMultiDrawIndirect) {
|
|
// Real multi-draw!
|
|
renderer->vkCmdDrawIndirect(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
vulkanBuffer->buffer,
|
|
offsetInBytes,
|
|
drawCount,
|
|
stride);
|
|
} else {
|
|
// Fake multi-draw...
|
|
for (i = 0; i < drawCount; i += 1) {
|
|
renderer->vkCmdDrawIndirect(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
vulkanBuffer->buffer,
|
|
offsetInBytes + (stride * i),
|
|
1,
|
|
stride);
|
|
}
|
|
}
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
|
|
}
|
|
|
|
static void VULKAN_DrawIndexedPrimitivesIndirect(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUBuffer *buffer,
|
|
Uint32 offsetInBytes,
|
|
Uint32 drawCount,
|
|
Uint32 stride)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)buffer)->activeBufferHandle->vulkanBuffer;
|
|
Uint32 i;
|
|
|
|
VULKAN_INTERNAL_BindGraphicsDescriptorSets(renderer, vulkanCommandBuffer);
|
|
|
|
if (renderer->supportsMultiDrawIndirect) {
|
|
// Real multi-draw!
|
|
renderer->vkCmdDrawIndexedIndirect(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
vulkanBuffer->buffer,
|
|
offsetInBytes,
|
|
drawCount,
|
|
stride);
|
|
} else {
|
|
// Fake multi-draw...
|
|
for (i = 0; i < drawCount; i += 1) {
|
|
renderer->vkCmdDrawIndexedIndirect(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
vulkanBuffer->buffer,
|
|
offsetInBytes + (stride * i),
|
|
1,
|
|
stride);
|
|
}
|
|
}
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
|
|
}
|
|
|
|
// Debug Naming
|
|
|
|
static void VULKAN_INTERNAL_SetBufferName(
|
|
VulkanRenderer *renderer,
|
|
VulkanBuffer *buffer,
|
|
const char *text)
|
|
{
|
|
VkDebugUtilsObjectNameInfoEXT nameInfo;
|
|
|
|
if (renderer->debugMode && renderer->supportsDebugUtils) {
|
|
nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
|
nameInfo.pNext = NULL;
|
|
nameInfo.pObjectName = text;
|
|
nameInfo.objectType = VK_OBJECT_TYPE_BUFFER;
|
|
nameInfo.objectHandle = (uint64_t)buffer->buffer;
|
|
|
|
renderer->vkSetDebugUtilsObjectNameEXT(
|
|
renderer->logicalDevice,
|
|
&nameInfo);
|
|
}
|
|
}
|
|
|
|
static void VULKAN_SetBufferName(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUBuffer *buffer,
|
|
const char *text)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanBufferContainer *container = (VulkanBufferContainer *)buffer;
|
|
size_t textLength = SDL_strlen(text) + 1;
|
|
|
|
if (renderer->debugMode && renderer->supportsDebugUtils) {
|
|
container->debugName = SDL_realloc(
|
|
container->debugName,
|
|
textLength);
|
|
|
|
SDL_utf8strlcpy(
|
|
container->debugName,
|
|
text,
|
|
textLength);
|
|
|
|
for (Uint32 i = 0; i < container->bufferCount; i += 1) {
|
|
VULKAN_INTERNAL_SetBufferName(
|
|
renderer,
|
|
container->bufferHandles[i]->vulkanBuffer,
|
|
text);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_SetTextureName(
|
|
VulkanRenderer *renderer,
|
|
VulkanTexture *texture,
|
|
const char *text)
|
|
{
|
|
VkDebugUtilsObjectNameInfoEXT nameInfo;
|
|
|
|
if (renderer->debugMode && renderer->supportsDebugUtils) {
|
|
nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
|
nameInfo.pNext = NULL;
|
|
nameInfo.pObjectName = text;
|
|
nameInfo.objectType = VK_OBJECT_TYPE_IMAGE;
|
|
nameInfo.objectHandle = (uint64_t)texture->image;
|
|
|
|
renderer->vkSetDebugUtilsObjectNameEXT(
|
|
renderer->logicalDevice,
|
|
&nameInfo);
|
|
}
|
|
}
|
|
|
|
static void VULKAN_SetTextureName(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTexture *texture,
|
|
const char *text)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanTextureContainer *container = (VulkanTextureContainer *)texture;
|
|
size_t textLength = SDL_strlen(text) + 1;
|
|
|
|
if (renderer->debugMode && renderer->supportsDebugUtils) {
|
|
container->debugName = SDL_realloc(
|
|
container->debugName,
|
|
textLength);
|
|
|
|
SDL_utf8strlcpy(
|
|
container->debugName,
|
|
text,
|
|
textLength);
|
|
|
|
for (Uint32 i = 0; i < container->textureCount; i += 1) {
|
|
VULKAN_INTERNAL_SetTextureName(
|
|
renderer,
|
|
container->textureHandles[i]->vulkanTexture,
|
|
text);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void VULKAN_InsertDebugLabel(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const char *text)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VkDebugUtilsLabelEXT labelInfo;
|
|
|
|
if (renderer->supportsDebugUtils) {
|
|
labelInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
|
labelInfo.pNext = NULL;
|
|
labelInfo.pLabelName = text;
|
|
|
|
renderer->vkCmdInsertDebugUtilsLabelEXT(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
&labelInfo);
|
|
}
|
|
}
|
|
|
|
static void VULKAN_PushDebugGroup(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const char *name)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VkDebugUtilsLabelEXT labelInfo;
|
|
|
|
if (renderer->supportsDebugUtils) {
|
|
labelInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
|
labelInfo.pNext = NULL;
|
|
labelInfo.pLabelName = name;
|
|
|
|
renderer->vkCmdBeginDebugUtilsLabelEXT(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
&labelInfo);
|
|
}
|
|
}
|
|
|
|
static void VULKAN_PopDebugGroup(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
|
|
if (renderer->supportsDebugUtils) {
|
|
renderer->vkCmdEndDebugUtilsLabelEXT(vulkanCommandBuffer->commandBuffer);
|
|
}
|
|
}
|
|
|
|
static VulkanTextureHandle *VULKAN_INTERNAL_CreateTextureHandle(
|
|
VulkanRenderer *renderer,
|
|
Uint32 width,
|
|
Uint32 height,
|
|
Uint32 depth,
|
|
SDL_GPUTextureType type,
|
|
Uint32 layerCount,
|
|
Uint32 levelCount,
|
|
VkSampleCountFlagBits sampleCount,
|
|
VkFormat format,
|
|
VkComponentMapping swizzle,
|
|
VkImageAspectFlags aspectMask,
|
|
SDL_GPUTextureUsageFlags textureUsageFlags,
|
|
bool isMSAAColorTarget)
|
|
{
|
|
VulkanTextureHandle *textureHandle;
|
|
VulkanTexture *texture;
|
|
|
|
texture = VULKAN_INTERNAL_CreateTexture(
|
|
renderer,
|
|
width,
|
|
height,
|
|
depth,
|
|
type,
|
|
layerCount,
|
|
levelCount,
|
|
sampleCount,
|
|
format,
|
|
swizzle,
|
|
aspectMask,
|
|
textureUsageFlags,
|
|
isMSAAColorTarget);
|
|
|
|
if (texture == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create texture!");
|
|
return NULL;
|
|
}
|
|
|
|
textureHandle = SDL_malloc(sizeof(VulkanTextureHandle));
|
|
textureHandle->vulkanTexture = texture;
|
|
textureHandle->container = NULL;
|
|
|
|
texture->handle = textureHandle;
|
|
|
|
return textureHandle;
|
|
}
|
|
|
|
static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
|
|
VulkanRenderer *renderer,
|
|
Uint32 width,
|
|
Uint32 height,
|
|
Uint32 depth,
|
|
SDL_GPUTextureType type,
|
|
Uint32 layerCount,
|
|
Uint32 levelCount,
|
|
VkSampleCountFlagBits sampleCount,
|
|
VkFormat format,
|
|
VkComponentMapping swizzle,
|
|
VkImageAspectFlags aspectMask,
|
|
SDL_GPUTextureUsageFlags textureUsageFlags,
|
|
bool isMSAAColorTarget)
|
|
{
|
|
VkResult vulkanResult;
|
|
VkImageCreateInfo imageCreateInfo;
|
|
VkImageCreateFlags imageCreateFlags = 0;
|
|
VkImageViewCreateInfo imageViewCreateInfo;
|
|
Uint8 bindResult;
|
|
Uint8 isRenderTarget =
|
|
((textureUsageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT) != 0) ||
|
|
((textureUsageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) != 0);
|
|
VkImageUsageFlags vkUsageFlags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
VulkanTexture *texture = SDL_malloc(sizeof(VulkanTexture));
|
|
|
|
texture->type = type;
|
|
texture->isMSAAColorTarget = isMSAAColorTarget;
|
|
texture->markedForDestroy = 0;
|
|
|
|
if (type == SDL_GPU_TEXTURETYPE_CUBE) {
|
|
imageCreateFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
|
} else if (type == SDL_GPU_TEXTURETYPE_3D) {
|
|
imageCreateFlags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
|
|
}
|
|
|
|
if (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_SAMPLER_BIT) {
|
|
vkUsageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
}
|
|
if (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT) {
|
|
vkUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
}
|
|
if (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) {
|
|
vkUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
}
|
|
if (textureUsageFlags & (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT |
|
|
SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT |
|
|
SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT)) {
|
|
vkUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
|
|
}
|
|
|
|
imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
imageCreateInfo.pNext = NULL;
|
|
imageCreateInfo.flags = imageCreateFlags;
|
|
imageCreateInfo.imageType = type == SDL_GPU_TEXTURETYPE_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
|
|
imageCreateInfo.format = format;
|
|
imageCreateInfo.extent.width = width;
|
|
imageCreateInfo.extent.height = height;
|
|
imageCreateInfo.extent.depth = depth;
|
|
imageCreateInfo.mipLevels = levelCount;
|
|
imageCreateInfo.arrayLayers = layerCount;
|
|
imageCreateInfo.samples = isMSAAColorTarget || VULKAN_INTERNAL_IsVulkanDepthFormat(format) ? sampleCount : VK_SAMPLE_COUNT_1_BIT;
|
|
imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
imageCreateInfo.usage = vkUsageFlags;
|
|
imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
imageCreateInfo.queueFamilyIndexCount = 0;
|
|
imageCreateInfo.pQueueFamilyIndices = NULL;
|
|
imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
vulkanResult = renderer->vkCreateImage(
|
|
renderer->logicalDevice,
|
|
&imageCreateInfo,
|
|
NULL,
|
|
&texture->image);
|
|
VULKAN_ERROR_CHECK(vulkanResult, vkCreateImage, 0)
|
|
|
|
bindResult = VULKAN_INTERNAL_BindMemoryForImage(
|
|
renderer,
|
|
texture->image,
|
|
&texture->usedRegion);
|
|
|
|
if (bindResult != 1) {
|
|
renderer->vkDestroyImage(
|
|
renderer->logicalDevice,
|
|
texture->image,
|
|
NULL);
|
|
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unable to bind memory for texture!");
|
|
return NULL;
|
|
}
|
|
|
|
texture->usedRegion->vulkanTexture = texture; // lol
|
|
|
|
texture->fullView = VK_NULL_HANDLE;
|
|
|
|
if (
|
|
(textureUsageFlags & SDL_GPU_TEXTUREUSAGE_SAMPLER_BIT) ||
|
|
(textureUsageFlags & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT) ||
|
|
(textureUsageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT)) {
|
|
|
|
imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
imageViewCreateInfo.pNext = NULL;
|
|
imageViewCreateInfo.flags = 0;
|
|
imageViewCreateInfo.image = texture->image;
|
|
imageViewCreateInfo.format = format;
|
|
imageViewCreateInfo.components = swizzle;
|
|
imageViewCreateInfo.subresourceRange.aspectMask = aspectMask;
|
|
imageViewCreateInfo.subresourceRange.baseMipLevel = 0;
|
|
imageViewCreateInfo.subresourceRange.levelCount = levelCount;
|
|
imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
|
|
imageViewCreateInfo.subresourceRange.layerCount = layerCount;
|
|
|
|
if (type == SDL_GPU_TEXTURETYPE_CUBE) {
|
|
imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
|
|
} else if (type == SDL_GPU_TEXTURETYPE_3D) {
|
|
imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_3D;
|
|
} else if (type == SDL_GPU_TEXTURETYPE_2D_ARRAY) {
|
|
imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
|
} else {
|
|
imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
}
|
|
|
|
vulkanResult = renderer->vkCreateImageView(
|
|
renderer->logicalDevice,
|
|
&imageViewCreateInfo,
|
|
NULL,
|
|
&texture->fullView);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkCreateImageView", vulkanResult);
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create texture image view");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
texture->dimensions.width = width;
|
|
texture->dimensions.height = height;
|
|
texture->depth = depth;
|
|
texture->format = format;
|
|
texture->swizzle = swizzle;
|
|
texture->levelCount = levelCount;
|
|
texture->layerCount = layerCount;
|
|
texture->sampleCount = sampleCount;
|
|
texture->usageFlags = textureUsageFlags;
|
|
texture->aspectFlags = aspectMask;
|
|
SDL_AtomicSet(&texture->referenceCount, 0);
|
|
|
|
// Define slices
|
|
texture->subresourceCount =
|
|
texture->layerCount *
|
|
texture->levelCount;
|
|
|
|
texture->subresources = SDL_malloc(
|
|
texture->subresourceCount * sizeof(VulkanTextureSubresource));
|
|
|
|
for (Uint32 i = 0; i < texture->layerCount; i += 1) {
|
|
for (Uint32 j = 0; j < texture->levelCount; j += 1) {
|
|
Uint32 subresourceIndex = VULKAN_INTERNAL_GetTextureSubresourceIndex(
|
|
j,
|
|
i,
|
|
texture->levelCount);
|
|
|
|
texture->subresources[subresourceIndex].renderTargetViews = NULL;
|
|
texture->subresources[subresourceIndex].computeWriteView = VK_NULL_HANDLE;
|
|
texture->subresources[subresourceIndex].depthStencilView = VK_NULL_HANDLE;
|
|
|
|
if (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT) {
|
|
texture->subresources[subresourceIndex].renderTargetViews = SDL_malloc(
|
|
texture->depth * sizeof(VkImageView));
|
|
|
|
if (texture->depth > 1) {
|
|
for (Uint32 k = 0; k < texture->depth; k += 1) {
|
|
VULKAN_INTERNAL_CreateRenderTargetView(
|
|
renderer,
|
|
texture,
|
|
k,
|
|
j,
|
|
swizzle,
|
|
&texture->subresources[subresourceIndex].renderTargetViews[k]);
|
|
}
|
|
} else {
|
|
VULKAN_INTERNAL_CreateRenderTargetView(
|
|
renderer,
|
|
texture,
|
|
i,
|
|
j,
|
|
swizzle,
|
|
&texture->subresources[subresourceIndex].renderTargetViews[0]);
|
|
}
|
|
}
|
|
|
|
if (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT) {
|
|
VULKAN_INTERNAL_CreateSubresourceView(
|
|
renderer,
|
|
texture,
|
|
i,
|
|
j,
|
|
swizzle,
|
|
&texture->subresources[subresourceIndex].computeWriteView);
|
|
}
|
|
|
|
if (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) {
|
|
VULKAN_INTERNAL_CreateSubresourceView(
|
|
renderer,
|
|
texture,
|
|
i,
|
|
j,
|
|
swizzle,
|
|
&texture->subresources[subresourceIndex].depthStencilView);
|
|
}
|
|
|
|
texture->subresources[subresourceIndex].parent = texture;
|
|
texture->subresources[subresourceIndex].layer = i;
|
|
texture->subresources[subresourceIndex].level = j;
|
|
texture->subresources[subresourceIndex].msaaTexHandle = NULL;
|
|
texture->subresources[subresourceIndex].transitioned = false;
|
|
|
|
if (
|
|
sampleCount > VK_SAMPLE_COUNT_1_BIT &&
|
|
isRenderTarget &&
|
|
!isMSAAColorTarget &&
|
|
!VULKAN_INTERNAL_IsVulkanDepthFormat(texture->format)) {
|
|
texture->subresources[subresourceIndex].msaaTexHandle = VULKAN_INTERNAL_CreateTextureHandle(
|
|
renderer,
|
|
texture->dimensions.width >> j,
|
|
texture->dimensions.height >> j,
|
|
1,
|
|
0,
|
|
1,
|
|
1,
|
|
sampleCount,
|
|
texture->format,
|
|
texture->swizzle,
|
|
aspectMask,
|
|
SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT,
|
|
true);
|
|
}
|
|
}
|
|
}
|
|
|
|
return texture;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_CycleActiveBuffer(
|
|
VulkanRenderer *renderer,
|
|
VulkanBufferContainer *bufferContainer)
|
|
{
|
|
VulkanBufferHandle *bufferHandle;
|
|
Uint32 i;
|
|
|
|
// If a previously-cycled buffer is available, we can use that.
|
|
for (i = 0; i < bufferContainer->bufferCount; i += 1) {
|
|
bufferHandle = bufferContainer->bufferHandles[i];
|
|
if (SDL_AtomicGet(&bufferHandle->vulkanBuffer->referenceCount) == 0) {
|
|
bufferContainer->activeBufferHandle = bufferHandle;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// No buffer handle is available, generate a new one.
|
|
bufferContainer->activeBufferHandle = VULKAN_INTERNAL_CreateBufferHandle(
|
|
renderer,
|
|
bufferContainer->activeBufferHandle->vulkanBuffer->size,
|
|
bufferContainer->activeBufferHandle->vulkanBuffer->usageFlags,
|
|
bufferContainer->activeBufferHandle->vulkanBuffer->type);
|
|
|
|
bufferContainer->activeBufferHandle->container = bufferContainer;
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
bufferContainer->bufferHandles,
|
|
VulkanBufferHandle *,
|
|
bufferContainer->bufferCount + 1,
|
|
bufferContainer->bufferCapacity,
|
|
bufferContainer->bufferCapacity * 2);
|
|
|
|
bufferContainer->bufferHandles[bufferContainer->bufferCount] = bufferContainer->activeBufferHandle;
|
|
bufferContainer->bufferCount += 1;
|
|
|
|
if (
|
|
renderer->debugMode &&
|
|
renderer->supportsDebugUtils &&
|
|
bufferContainer->debugName != NULL) {
|
|
VULKAN_INTERNAL_SetBufferName(
|
|
renderer,
|
|
bufferContainer->activeBufferHandle->vulkanBuffer,
|
|
bufferContainer->debugName);
|
|
}
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_CycleActiveTexture(
|
|
VulkanRenderer *renderer,
|
|
VulkanTextureContainer *textureContainer)
|
|
{
|
|
// If a previously-cycled texture is available, we can use that.
|
|
for (Uint32 i = 0; i < textureContainer->textureCount; i += 1) {
|
|
VulkanTextureHandle *textureHandle = textureContainer->textureHandles[i];
|
|
|
|
if (SDL_AtomicGet(&textureHandle->vulkanTexture->referenceCount) == 0) {
|
|
textureContainer->activeTextureHandle = textureHandle;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// No texture handle is available, generate a new one.
|
|
textureContainer->activeTextureHandle = VULKAN_INTERNAL_CreateTextureHandle(
|
|
renderer,
|
|
textureContainer->activeTextureHandle->vulkanTexture->dimensions.width,
|
|
textureContainer->activeTextureHandle->vulkanTexture->dimensions.height,
|
|
textureContainer->activeTextureHandle->vulkanTexture->depth,
|
|
textureContainer->activeTextureHandle->vulkanTexture->type,
|
|
textureContainer->activeTextureHandle->vulkanTexture->layerCount,
|
|
textureContainer->activeTextureHandle->vulkanTexture->levelCount,
|
|
textureContainer->activeTextureHandle->vulkanTexture->sampleCount,
|
|
textureContainer->activeTextureHandle->vulkanTexture->format,
|
|
textureContainer->activeTextureHandle->vulkanTexture->swizzle,
|
|
textureContainer->activeTextureHandle->vulkanTexture->aspectFlags,
|
|
textureContainer->activeTextureHandle->vulkanTexture->usageFlags,
|
|
false);
|
|
|
|
textureContainer->activeTextureHandle->container = textureContainer;
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
textureContainer->textureHandles,
|
|
VulkanTextureHandle *,
|
|
textureContainer->textureCount + 1,
|
|
textureContainer->textureCapacity,
|
|
textureContainer->textureCapacity * 2);
|
|
|
|
textureContainer->textureHandles[textureContainer->textureCount] = textureContainer->activeTextureHandle;
|
|
textureContainer->textureCount += 1;
|
|
|
|
if (
|
|
renderer->debugMode &&
|
|
renderer->supportsDebugUtils &&
|
|
textureContainer->debugName != NULL) {
|
|
VULKAN_INTERNAL_SetTextureName(
|
|
renderer,
|
|
textureContainer->activeTextureHandle->vulkanTexture,
|
|
textureContainer->debugName);
|
|
}
|
|
}
|
|
|
|
static VulkanBuffer *VULKAN_INTERNAL_PrepareBufferForWrite(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanBufferContainer *bufferContainer,
|
|
bool cycle,
|
|
VulkanBufferUsageMode destinationUsageMode)
|
|
{
|
|
if (
|
|
cycle &&
|
|
SDL_AtomicGet(&bufferContainer->activeBufferHandle->vulkanBuffer->referenceCount) > 0) {
|
|
VULKAN_INTERNAL_CycleActiveBuffer(
|
|
renderer,
|
|
bufferContainer);
|
|
}
|
|
|
|
VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
|
|
renderer,
|
|
commandBuffer,
|
|
destinationUsageMode,
|
|
bufferContainer->activeBufferHandle->vulkanBuffer);
|
|
|
|
return bufferContainer->activeBufferHandle->vulkanBuffer;
|
|
}
|
|
|
|
static VulkanTextureSubresource *VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanTextureContainer *textureContainer,
|
|
Uint32 layer,
|
|
Uint32 level,
|
|
bool cycle,
|
|
VulkanTextureUsageMode destinationUsageMode)
|
|
{
|
|
VulkanTextureSubresource *textureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
|
|
textureContainer,
|
|
layer,
|
|
level);
|
|
|
|
if (
|
|
cycle &&
|
|
textureContainer->canBeCycled &&
|
|
SDL_AtomicGet(&textureContainer->activeTextureHandle->vulkanTexture->referenceCount) > 0) {
|
|
VULKAN_INTERNAL_CycleActiveTexture(
|
|
renderer,
|
|
textureContainer);
|
|
|
|
textureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
|
|
textureContainer,
|
|
layer,
|
|
level);
|
|
}
|
|
|
|
// always do barrier because of layout transitions
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
renderer,
|
|
commandBuffer,
|
|
destinationUsageMode,
|
|
textureSubresource);
|
|
|
|
return textureSubresource;
|
|
}
|
|
|
|
static VkRenderPass VULKAN_INTERNAL_CreateRenderPass(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
SDL_GPUColorAttachmentInfo *colorAttachmentInfos,
|
|
Uint32 colorAttachmentCount,
|
|
SDL_GPUDepthStencilAttachmentInfo *depthStencilAttachmentInfo)
|
|
{
|
|
VkResult vulkanResult;
|
|
VkAttachmentDescription attachmentDescriptions[2 * MAX_COLOR_TARGET_BINDINGS + 1];
|
|
VkAttachmentReference colorAttachmentReferences[MAX_COLOR_TARGET_BINDINGS];
|
|
VkAttachmentReference resolveReferences[MAX_COLOR_TARGET_BINDINGS + 1];
|
|
VkAttachmentReference depthStencilAttachmentReference;
|
|
VkRenderPassCreateInfo renderPassCreateInfo;
|
|
VkSubpassDescription subpass;
|
|
VkRenderPass renderPass;
|
|
Uint32 i;
|
|
|
|
Uint32 attachmentDescriptionCount = 0;
|
|
Uint32 colorAttachmentReferenceCount = 0;
|
|
Uint32 resolveReferenceCount = 0;
|
|
|
|
VulkanTexture *texture = NULL;
|
|
|
|
for (i = 0; i < colorAttachmentCount; i += 1) {
|
|
texture = ((VulkanTextureContainer *)colorAttachmentInfos[i].texture)->activeTextureHandle->vulkanTexture;
|
|
|
|
if (texture->sampleCount > VK_SAMPLE_COUNT_1_BIT) {
|
|
// Resolve attachment and multisample attachment
|
|
|
|
attachmentDescriptions[attachmentDescriptionCount].flags = 0;
|
|
attachmentDescriptions[attachmentDescriptionCount].format = texture->format;
|
|
attachmentDescriptions[attachmentDescriptionCount].samples =
|
|
VK_SAMPLE_COUNT_1_BIT;
|
|
attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[colorAttachmentInfos[i].loadOp];
|
|
attachmentDescriptions[attachmentDescriptionCount].storeOp =
|
|
VK_ATTACHMENT_STORE_OP_STORE; // Always store the resolve texture
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp =
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp =
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].initialLayout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
attachmentDescriptions[attachmentDescriptionCount].finalLayout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
resolveReferences[resolveReferenceCount].attachment =
|
|
attachmentDescriptionCount;
|
|
resolveReferences[resolveReferenceCount].layout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
attachmentDescriptionCount += 1;
|
|
resolveReferenceCount += 1;
|
|
|
|
attachmentDescriptions[attachmentDescriptionCount].flags = 0;
|
|
attachmentDescriptions[attachmentDescriptionCount].format = texture->format;
|
|
attachmentDescriptions[attachmentDescriptionCount].samples = texture->sampleCount;
|
|
attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[colorAttachmentInfos[i].loadOp];
|
|
attachmentDescriptions[attachmentDescriptionCount].storeOp = SDLToVK_StoreOp[colorAttachmentInfos[i].storeOp];
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp =
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp =
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].initialLayout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
attachmentDescriptions[attachmentDescriptionCount].finalLayout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
colorAttachmentReferences[colorAttachmentReferenceCount].attachment =
|
|
attachmentDescriptionCount;
|
|
colorAttachmentReferences[colorAttachmentReferenceCount].layout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
attachmentDescriptionCount += 1;
|
|
colorAttachmentReferenceCount += 1;
|
|
} else {
|
|
attachmentDescriptions[attachmentDescriptionCount].flags = 0;
|
|
attachmentDescriptions[attachmentDescriptionCount].format = texture->format;
|
|
attachmentDescriptions[attachmentDescriptionCount].samples =
|
|
VK_SAMPLE_COUNT_1_BIT;
|
|
attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[colorAttachmentInfos[i].loadOp];
|
|
attachmentDescriptions[attachmentDescriptionCount].storeOp =
|
|
VK_ATTACHMENT_STORE_OP_STORE; // Always store non-MSAA textures
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp =
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp =
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].initialLayout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
attachmentDescriptions[attachmentDescriptionCount].finalLayout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
colorAttachmentReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount;
|
|
colorAttachmentReferences[colorAttachmentReferenceCount].layout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
attachmentDescriptionCount += 1;
|
|
colorAttachmentReferenceCount += 1;
|
|
}
|
|
}
|
|
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
subpass.flags = 0;
|
|
subpass.inputAttachmentCount = 0;
|
|
subpass.pInputAttachments = NULL;
|
|
subpass.colorAttachmentCount = colorAttachmentCount;
|
|
subpass.pColorAttachments = colorAttachmentReferences;
|
|
subpass.preserveAttachmentCount = 0;
|
|
subpass.pPreserveAttachments = NULL;
|
|
|
|
if (depthStencilAttachmentInfo == NULL) {
|
|
subpass.pDepthStencilAttachment = NULL;
|
|
} else {
|
|
texture = ((VulkanTextureContainer *)depthStencilAttachmentInfo->texture)->activeTextureHandle->vulkanTexture;
|
|
|
|
attachmentDescriptions[attachmentDescriptionCount].flags = 0;
|
|
attachmentDescriptions[attachmentDescriptionCount].format = texture->format;
|
|
attachmentDescriptions[attachmentDescriptionCount].samples = texture->sampleCount;
|
|
|
|
attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[depthStencilAttachmentInfo->loadOp];
|
|
attachmentDescriptions[attachmentDescriptionCount].storeOp = SDLToVK_StoreOp[depthStencilAttachmentInfo->storeOp];
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = SDLToVK_LoadOp[depthStencilAttachmentInfo->stencilLoadOp];
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = SDLToVK_StoreOp[depthStencilAttachmentInfo->stencilStoreOp];
|
|
attachmentDescriptions[attachmentDescriptionCount].initialLayout =
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
attachmentDescriptions[attachmentDescriptionCount].finalLayout =
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
|
|
depthStencilAttachmentReference.attachment =
|
|
attachmentDescriptionCount;
|
|
depthStencilAttachmentReference.layout =
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
|
|
subpass.pDepthStencilAttachment =
|
|
&depthStencilAttachmentReference;
|
|
|
|
attachmentDescriptionCount += 1;
|
|
}
|
|
|
|
if (texture != NULL && texture->sampleCount > VK_SAMPLE_COUNT_1_BIT) {
|
|
subpass.pResolveAttachments = resolveReferences;
|
|
} else {
|
|
subpass.pResolveAttachments = NULL;
|
|
}
|
|
|
|
renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
|
renderPassCreateInfo.pNext = NULL;
|
|
renderPassCreateInfo.flags = 0;
|
|
renderPassCreateInfo.pAttachments = attachmentDescriptions;
|
|
renderPassCreateInfo.attachmentCount = attachmentDescriptionCount;
|
|
renderPassCreateInfo.subpassCount = 1;
|
|
renderPassCreateInfo.pSubpasses = &subpass;
|
|
renderPassCreateInfo.dependencyCount = 0;
|
|
renderPassCreateInfo.pDependencies = NULL;
|
|
|
|
vulkanResult = renderer->vkCreateRenderPass(
|
|
renderer->logicalDevice,
|
|
&renderPassCreateInfo,
|
|
NULL,
|
|
&renderPass);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
renderPass = VK_NULL_HANDLE;
|
|
LogVulkanResultAsError("vkCreateRenderPass", vulkanResult);
|
|
}
|
|
|
|
return renderPass;
|
|
}
|
|
|
|
static VkRenderPass VULKAN_INTERNAL_CreateTransientRenderPass(
|
|
VulkanRenderer *renderer,
|
|
SDL_GPUGraphicsPipelineAttachmentInfo attachmentInfo,
|
|
VkSampleCountFlagBits sampleCount)
|
|
{
|
|
VkAttachmentDescription attachmentDescriptions[2 * MAX_COLOR_TARGET_BINDINGS + 1];
|
|
VkAttachmentReference colorAttachmentReferences[MAX_COLOR_TARGET_BINDINGS];
|
|
VkAttachmentReference resolveReferences[MAX_COLOR_TARGET_BINDINGS + 1];
|
|
VkAttachmentReference depthStencilAttachmentReference;
|
|
SDL_GPUColorAttachmentDescription attachmentDescription;
|
|
VkSubpassDescription subpass;
|
|
VkRenderPassCreateInfo renderPassCreateInfo;
|
|
VkRenderPass renderPass;
|
|
VkResult result;
|
|
|
|
Uint32 multisampling = 0;
|
|
Uint32 attachmentDescriptionCount = 0;
|
|
Uint32 colorAttachmentReferenceCount = 0;
|
|
Uint32 resolveReferenceCount = 0;
|
|
Uint32 i;
|
|
|
|
for (i = 0; i < attachmentInfo.colorAttachmentCount; i += 1) {
|
|
attachmentDescription = attachmentInfo.colorAttachmentDescriptions[i];
|
|
|
|
if (sampleCount > VK_SAMPLE_COUNT_1_BIT) {
|
|
multisampling = 1;
|
|
|
|
// Resolve attachment and multisample attachment
|
|
|
|
attachmentDescriptions[attachmentDescriptionCount].flags = 0;
|
|
attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_SurfaceFormat[attachmentDescription.format];
|
|
attachmentDescriptions[attachmentDescriptionCount].samples = VK_SAMPLE_COUNT_1_BIT;
|
|
attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
resolveReferences[resolveReferenceCount].attachment = attachmentDescriptionCount;
|
|
resolveReferences[resolveReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
attachmentDescriptionCount += 1;
|
|
resolveReferenceCount += 1;
|
|
|
|
attachmentDescriptions[attachmentDescriptionCount].flags = 0;
|
|
attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_SurfaceFormat[attachmentDescription.format];
|
|
attachmentDescriptions[attachmentDescriptionCount].samples = sampleCount;
|
|
|
|
attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
colorAttachmentReferences[colorAttachmentReferenceCount].attachment =
|
|
attachmentDescriptionCount;
|
|
colorAttachmentReferences[colorAttachmentReferenceCount].layout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
attachmentDescriptionCount += 1;
|
|
colorAttachmentReferenceCount += 1;
|
|
} else {
|
|
attachmentDescriptions[attachmentDescriptionCount].flags = 0;
|
|
attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_SurfaceFormat[attachmentDescription.format];
|
|
attachmentDescriptions[attachmentDescriptionCount].samples =
|
|
VK_SAMPLE_COUNT_1_BIT;
|
|
attachmentDescriptions[attachmentDescriptionCount].loadOp =
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].storeOp =
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp =
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp =
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].initialLayout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
attachmentDescriptions[attachmentDescriptionCount].finalLayout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
colorAttachmentReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount;
|
|
colorAttachmentReferences[colorAttachmentReferenceCount].layout =
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
attachmentDescriptionCount += 1;
|
|
colorAttachmentReferenceCount += 1;
|
|
}
|
|
}
|
|
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
subpass.flags = 0;
|
|
subpass.inputAttachmentCount = 0;
|
|
subpass.pInputAttachments = NULL;
|
|
subpass.colorAttachmentCount = attachmentInfo.colorAttachmentCount;
|
|
subpass.pColorAttachments = colorAttachmentReferences;
|
|
subpass.preserveAttachmentCount = 0;
|
|
subpass.pPreserveAttachments = NULL;
|
|
|
|
if (attachmentInfo.hasDepthStencilAttachment) {
|
|
attachmentDescriptions[attachmentDescriptionCount].flags = 0;
|
|
attachmentDescriptions[attachmentDescriptionCount].format =
|
|
SDLToVK_SurfaceFormat[attachmentInfo.depthStencilFormat];
|
|
attachmentDescriptions[attachmentDescriptionCount].samples = sampleCount;
|
|
|
|
attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachmentDescriptions[attachmentDescriptionCount].initialLayout =
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
attachmentDescriptions[attachmentDescriptionCount].finalLayout =
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
|
|
depthStencilAttachmentReference.attachment =
|
|
attachmentDescriptionCount;
|
|
depthStencilAttachmentReference.layout =
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
|
|
subpass.pDepthStencilAttachment =
|
|
&depthStencilAttachmentReference;
|
|
|
|
attachmentDescriptionCount += 1;
|
|
} else {
|
|
subpass.pDepthStencilAttachment = NULL;
|
|
}
|
|
|
|
if (multisampling) {
|
|
subpass.pResolveAttachments = resolveReferences;
|
|
} else {
|
|
subpass.pResolveAttachments = NULL;
|
|
}
|
|
|
|
renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
|
renderPassCreateInfo.pNext = NULL;
|
|
renderPassCreateInfo.flags = 0;
|
|
renderPassCreateInfo.pAttachments = attachmentDescriptions;
|
|
renderPassCreateInfo.attachmentCount = attachmentDescriptionCount;
|
|
renderPassCreateInfo.subpassCount = 1;
|
|
renderPassCreateInfo.pSubpasses = &subpass;
|
|
renderPassCreateInfo.dependencyCount = 0;
|
|
renderPassCreateInfo.pDependencies = NULL;
|
|
|
|
result = renderer->vkCreateRenderPass(
|
|
renderer->logicalDevice,
|
|
&renderPassCreateInfo,
|
|
NULL,
|
|
&renderPass);
|
|
|
|
if (result != VK_SUCCESS) {
|
|
renderPass = VK_NULL_HANDLE;
|
|
LogVulkanResultAsError("vkCreateRenderPass", result);
|
|
}
|
|
|
|
return renderPass;
|
|
}
|
|
|
|
static SDL_GPUGraphicsPipeline *VULKAN_CreateGraphicsPipeline(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUGraphicsPipelineCreateInfo *pipelineCreateInfo)
|
|
{
|
|
VkResult vulkanResult;
|
|
Uint32 i;
|
|
VkSampleCountFlagBits actualSampleCount;
|
|
|
|
VulkanGraphicsPipeline *graphicsPipeline = (VulkanGraphicsPipeline *)SDL_malloc(sizeof(VulkanGraphicsPipeline));
|
|
VkGraphicsPipelineCreateInfo vkPipelineCreateInfo;
|
|
|
|
VkPipelineShaderStageCreateInfo shaderStageCreateInfos[2];
|
|
|
|
VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo;
|
|
VkPipelineVertexInputDivisorStateCreateInfoEXT divisorStateCreateInfo;
|
|
VkVertexInputBindingDescription *vertexInputBindingDescriptions = SDL_stack_alloc(VkVertexInputBindingDescription, pipelineCreateInfo->vertexInputState.vertexBindingCount);
|
|
VkVertexInputAttributeDescription *vertexInputAttributeDescriptions = SDL_stack_alloc(VkVertexInputAttributeDescription, pipelineCreateInfo->vertexInputState.vertexAttributeCount);
|
|
VkVertexInputBindingDivisorDescriptionEXT *divisorDescriptions = SDL_stack_alloc(VkVertexInputBindingDivisorDescriptionEXT, pipelineCreateInfo->vertexInputState.vertexBindingCount);
|
|
Uint32 divisorDescriptionCount = 0;
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo;
|
|
|
|
VkPipelineViewportStateCreateInfo viewportStateCreateInfo;
|
|
|
|
VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo;
|
|
|
|
VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo;
|
|
|
|
VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo;
|
|
VkStencilOpState frontStencilState;
|
|
VkStencilOpState backStencilState;
|
|
|
|
VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo;
|
|
VkPipelineColorBlendAttachmentState *colorBlendAttachmentStates = SDL_stack_alloc(
|
|
VkPipelineColorBlendAttachmentState,
|
|
pipelineCreateInfo->attachmentInfo.colorAttachmentCount);
|
|
|
|
static const VkDynamicState dynamicStates[] = {
|
|
VK_DYNAMIC_STATE_VIEWPORT,
|
|
VK_DYNAMIC_STATE_SCISSOR
|
|
};
|
|
VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo;
|
|
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
|
|
// Find a compatible sample count to use
|
|
|
|
actualSampleCount = VULKAN_INTERNAL_GetMaxMultiSampleCount(
|
|
renderer,
|
|
SDLToVK_SampleCount[pipelineCreateInfo->multisampleState.sampleCount]);
|
|
|
|
// Create a "compatible" render pass
|
|
|
|
VkRenderPass transientRenderPass = VULKAN_INTERNAL_CreateTransientRenderPass(
|
|
renderer,
|
|
pipelineCreateInfo->attachmentInfo,
|
|
actualSampleCount);
|
|
|
|
// Dynamic state
|
|
|
|
dynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
|
dynamicStateCreateInfo.pNext = NULL;
|
|
dynamicStateCreateInfo.flags = 0;
|
|
dynamicStateCreateInfo.dynamicStateCount = SDL_arraysize(dynamicStates);
|
|
dynamicStateCreateInfo.pDynamicStates = dynamicStates;
|
|
|
|
// Shader stages
|
|
|
|
graphicsPipeline->vertexShader = (VulkanShader *)pipelineCreateInfo->vertexShader;
|
|
SDL_AtomicIncRef(&graphicsPipeline->vertexShader->referenceCount);
|
|
|
|
shaderStageCreateInfos[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
shaderStageCreateInfos[0].pNext = NULL;
|
|
shaderStageCreateInfos[0].flags = 0;
|
|
shaderStageCreateInfos[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
|
shaderStageCreateInfos[0].module = graphicsPipeline->vertexShader->shaderModule;
|
|
shaderStageCreateInfos[0].pName = graphicsPipeline->vertexShader->entryPointName;
|
|
shaderStageCreateInfos[0].pSpecializationInfo = NULL;
|
|
|
|
graphicsPipeline->fragmentShader = (VulkanShader *)pipelineCreateInfo->fragmentShader;
|
|
SDL_AtomicIncRef(&graphicsPipeline->fragmentShader->referenceCount);
|
|
|
|
shaderStageCreateInfos[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
shaderStageCreateInfos[1].pNext = NULL;
|
|
shaderStageCreateInfos[1].flags = 0;
|
|
shaderStageCreateInfos[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
shaderStageCreateInfos[1].module = graphicsPipeline->fragmentShader->shaderModule;
|
|
shaderStageCreateInfos[1].pName = graphicsPipeline->fragmentShader->entryPointName;
|
|
shaderStageCreateInfos[1].pSpecializationInfo = NULL;
|
|
|
|
// Vertex input
|
|
|
|
for (i = 0; i < pipelineCreateInfo->vertexInputState.vertexBindingCount; i += 1) {
|
|
vertexInputBindingDescriptions[i].binding = pipelineCreateInfo->vertexInputState.vertexBindings[i].binding;
|
|
vertexInputBindingDescriptions[i].inputRate = SDLToVK_VertexInputRate[pipelineCreateInfo->vertexInputState.vertexBindings[i].inputRate];
|
|
vertexInputBindingDescriptions[i].stride = pipelineCreateInfo->vertexInputState.vertexBindings[i].stride;
|
|
|
|
if (pipelineCreateInfo->vertexInputState.vertexBindings[i].inputRate == SDL_GPU_VERTEXINPUTRATE_INSTANCE) {
|
|
divisorDescriptionCount += 1;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < pipelineCreateInfo->vertexInputState.vertexAttributeCount; i += 1) {
|
|
vertexInputAttributeDescriptions[i].binding = pipelineCreateInfo->vertexInputState.vertexAttributes[i].binding;
|
|
vertexInputAttributeDescriptions[i].format = SDLToVK_VertexFormat[pipelineCreateInfo->vertexInputState.vertexAttributes[i].format];
|
|
vertexInputAttributeDescriptions[i].location = pipelineCreateInfo->vertexInputState.vertexAttributes[i].location;
|
|
vertexInputAttributeDescriptions[i].offset = pipelineCreateInfo->vertexInputState.vertexAttributes[i].offset;
|
|
}
|
|
|
|
vertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
|
vertexInputStateCreateInfo.pNext = NULL;
|
|
vertexInputStateCreateInfo.flags = 0;
|
|
vertexInputStateCreateInfo.vertexBindingDescriptionCount = pipelineCreateInfo->vertexInputState.vertexBindingCount;
|
|
vertexInputStateCreateInfo.pVertexBindingDescriptions = vertexInputBindingDescriptions;
|
|
vertexInputStateCreateInfo.vertexAttributeDescriptionCount = pipelineCreateInfo->vertexInputState.vertexAttributeCount;
|
|
vertexInputStateCreateInfo.pVertexAttributeDescriptions = vertexInputAttributeDescriptions;
|
|
|
|
if (divisorDescriptionCount > 0) {
|
|
divisorDescriptionCount = 0;
|
|
|
|
for (i = 0; i < pipelineCreateInfo->vertexInputState.vertexBindingCount; i += 1) {
|
|
if (pipelineCreateInfo->vertexInputState.vertexBindings[i].inputRate == SDL_GPU_VERTEXINPUTRATE_INSTANCE) {
|
|
divisorDescriptions[divisorDescriptionCount].binding = pipelineCreateInfo->vertexInputState.vertexBindings[i].binding;
|
|
divisorDescriptions[divisorDescriptionCount].divisor = pipelineCreateInfo->vertexInputState.vertexBindings[i].instanceStepRate;
|
|
|
|
divisorDescriptionCount += 1;
|
|
}
|
|
}
|
|
|
|
divisorStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
|
|
divisorStateCreateInfo.pNext = NULL;
|
|
divisorStateCreateInfo.vertexBindingDivisorCount = divisorDescriptionCount;
|
|
divisorStateCreateInfo.pVertexBindingDivisors = divisorDescriptions;
|
|
|
|
vertexInputStateCreateInfo.pNext = &divisorStateCreateInfo;
|
|
}
|
|
|
|
// Topology
|
|
|
|
inputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
|
inputAssemblyStateCreateInfo.pNext = NULL;
|
|
inputAssemblyStateCreateInfo.flags = 0;
|
|
inputAssemblyStateCreateInfo.primitiveRestartEnable = VK_FALSE;
|
|
inputAssemblyStateCreateInfo.topology = SDLToVK_PrimitiveType[pipelineCreateInfo->primitiveType];
|
|
|
|
graphicsPipeline->primitiveType = pipelineCreateInfo->primitiveType;
|
|
|
|
// Viewport
|
|
|
|
// NOTE: viewport and scissor are dynamic, and must be set using the command buffer
|
|
|
|
viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
|
viewportStateCreateInfo.pNext = NULL;
|
|
viewportStateCreateInfo.flags = 0;
|
|
viewportStateCreateInfo.viewportCount = 1;
|
|
viewportStateCreateInfo.pViewports = NULL;
|
|
viewportStateCreateInfo.scissorCount = 1;
|
|
viewportStateCreateInfo.pScissors = NULL;
|
|
|
|
// Rasterization
|
|
|
|
rasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
|
rasterizationStateCreateInfo.pNext = NULL;
|
|
rasterizationStateCreateInfo.flags = 0;
|
|
rasterizationStateCreateInfo.depthClampEnable = VK_FALSE;
|
|
rasterizationStateCreateInfo.rasterizerDiscardEnable = VK_FALSE;
|
|
rasterizationStateCreateInfo.polygonMode = SDLToVK_PolygonMode(
|
|
renderer,
|
|
pipelineCreateInfo->rasterizerState.fillMode);
|
|
rasterizationStateCreateInfo.cullMode = SDLToVK_CullMode[pipelineCreateInfo->rasterizerState.cullMode];
|
|
rasterizationStateCreateInfo.frontFace = SDLToVK_FrontFace[pipelineCreateInfo->rasterizerState.frontFace];
|
|
rasterizationStateCreateInfo.depthBiasEnable =
|
|
pipelineCreateInfo->rasterizerState.depthBiasEnable;
|
|
rasterizationStateCreateInfo.depthBiasConstantFactor =
|
|
pipelineCreateInfo->rasterizerState.depthBiasConstantFactor;
|
|
rasterizationStateCreateInfo.depthBiasClamp =
|
|
pipelineCreateInfo->rasterizerState.depthBiasClamp;
|
|
rasterizationStateCreateInfo.depthBiasSlopeFactor =
|
|
pipelineCreateInfo->rasterizerState.depthBiasSlopeFactor;
|
|
rasterizationStateCreateInfo.lineWidth = 1.0f;
|
|
|
|
// Multisample
|
|
|
|
multisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
|
multisampleStateCreateInfo.pNext = NULL;
|
|
multisampleStateCreateInfo.flags = 0;
|
|
multisampleStateCreateInfo.rasterizationSamples = actualSampleCount;
|
|
multisampleStateCreateInfo.sampleShadingEnable = VK_FALSE;
|
|
multisampleStateCreateInfo.minSampleShading = 1.0f;
|
|
multisampleStateCreateInfo.pSampleMask =
|
|
&pipelineCreateInfo->multisampleState.sampleMask;
|
|
multisampleStateCreateInfo.alphaToCoverageEnable = VK_FALSE;
|
|
multisampleStateCreateInfo.alphaToOneEnable = VK_FALSE;
|
|
|
|
// Depth Stencil State
|
|
|
|
frontStencilState.failOp = SDLToVK_StencilOp[pipelineCreateInfo->depthStencilState.frontStencilState.failOp];
|
|
frontStencilState.passOp = SDLToVK_StencilOp[pipelineCreateInfo->depthStencilState.frontStencilState.passOp];
|
|
frontStencilState.depthFailOp = SDLToVK_StencilOp[pipelineCreateInfo->depthStencilState.frontStencilState.depthFailOp];
|
|
frontStencilState.compareOp = SDLToVK_CompareOp[pipelineCreateInfo->depthStencilState.frontStencilState.compareOp];
|
|
frontStencilState.compareMask =
|
|
pipelineCreateInfo->depthStencilState.compareMask;
|
|
frontStencilState.writeMask =
|
|
pipelineCreateInfo->depthStencilState.writeMask;
|
|
frontStencilState.reference =
|
|
pipelineCreateInfo->depthStencilState.reference;
|
|
|
|
backStencilState.failOp = SDLToVK_StencilOp[pipelineCreateInfo->depthStencilState.backStencilState.failOp];
|
|
backStencilState.passOp = SDLToVK_StencilOp[pipelineCreateInfo->depthStencilState.backStencilState.passOp];
|
|
backStencilState.depthFailOp = SDLToVK_StencilOp[pipelineCreateInfo->depthStencilState.backStencilState.depthFailOp];
|
|
backStencilState.compareOp = SDLToVK_CompareOp[pipelineCreateInfo->depthStencilState.backStencilState.compareOp];
|
|
backStencilState.compareMask =
|
|
pipelineCreateInfo->depthStencilState.compareMask;
|
|
backStencilState.writeMask =
|
|
pipelineCreateInfo->depthStencilState.writeMask;
|
|
backStencilState.reference =
|
|
pipelineCreateInfo->depthStencilState.reference;
|
|
|
|
depthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
|
depthStencilStateCreateInfo.pNext = NULL;
|
|
depthStencilStateCreateInfo.flags = 0;
|
|
depthStencilStateCreateInfo.depthTestEnable =
|
|
pipelineCreateInfo->depthStencilState.depthTestEnable;
|
|
depthStencilStateCreateInfo.depthWriteEnable =
|
|
pipelineCreateInfo->depthStencilState.depthWriteEnable;
|
|
depthStencilStateCreateInfo.depthCompareOp = SDLToVK_CompareOp[pipelineCreateInfo->depthStencilState.compareOp];
|
|
depthStencilStateCreateInfo.depthBoundsTestEnable = VK_FALSE;
|
|
depthStencilStateCreateInfo.stencilTestEnable =
|
|
pipelineCreateInfo->depthStencilState.stencilTestEnable;
|
|
depthStencilStateCreateInfo.front = frontStencilState;
|
|
depthStencilStateCreateInfo.back = backStencilState;
|
|
depthStencilStateCreateInfo.minDepthBounds = 0; // unused
|
|
depthStencilStateCreateInfo.maxDepthBounds = 0; // unused
|
|
|
|
// Color Blend
|
|
|
|
for (i = 0; i < pipelineCreateInfo->attachmentInfo.colorAttachmentCount; i += 1) {
|
|
SDL_GPUColorAttachmentBlendState blendState = pipelineCreateInfo->attachmentInfo.colorAttachmentDescriptions[i].blendState;
|
|
|
|
colorBlendAttachmentStates[i].blendEnable =
|
|
blendState.blendEnable;
|
|
colorBlendAttachmentStates[i].srcColorBlendFactor = SDLToVK_BlendFactor[blendState.srcColorBlendFactor];
|
|
colorBlendAttachmentStates[i].dstColorBlendFactor = SDLToVK_BlendFactor[blendState.dstColorBlendFactor];
|
|
colorBlendAttachmentStates[i].colorBlendOp = SDLToVK_BlendOp[blendState.colorBlendOp];
|
|
colorBlendAttachmentStates[i].srcAlphaBlendFactor = SDLToVK_BlendFactor[blendState.srcAlphaBlendFactor];
|
|
colorBlendAttachmentStates[i].dstAlphaBlendFactor = SDLToVK_BlendFactor[blendState.dstAlphaBlendFactor];
|
|
colorBlendAttachmentStates[i].alphaBlendOp = SDLToVK_BlendOp[blendState.alphaBlendOp];
|
|
colorBlendAttachmentStates[i].colorWriteMask =
|
|
blendState.colorWriteMask;
|
|
}
|
|
|
|
colorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
|
colorBlendStateCreateInfo.pNext = NULL;
|
|
colorBlendStateCreateInfo.flags = 0;
|
|
colorBlendStateCreateInfo.attachmentCount =
|
|
pipelineCreateInfo->attachmentInfo.colorAttachmentCount;
|
|
colorBlendStateCreateInfo.pAttachments =
|
|
colorBlendAttachmentStates;
|
|
colorBlendStateCreateInfo.blendConstants[0] =
|
|
pipelineCreateInfo->blendConstants[0];
|
|
colorBlendStateCreateInfo.blendConstants[1] =
|
|
pipelineCreateInfo->blendConstants[1];
|
|
colorBlendStateCreateInfo.blendConstants[2] =
|
|
pipelineCreateInfo->blendConstants[2];
|
|
colorBlendStateCreateInfo.blendConstants[3] =
|
|
pipelineCreateInfo->blendConstants[3];
|
|
|
|
// We don't support LogicOp, so this is easy.
|
|
colorBlendStateCreateInfo.logicOpEnable = VK_FALSE;
|
|
colorBlendStateCreateInfo.logicOp = 0;
|
|
|
|
// Pipeline Layout
|
|
|
|
if (!VULKAN_INTERNAL_InitializeGraphicsPipelineResourceLayout(
|
|
renderer,
|
|
graphicsPipeline->vertexShader,
|
|
graphicsPipeline->fragmentShader,
|
|
&graphicsPipeline->resourceLayout)) {
|
|
SDL_stack_free(vertexInputBindingDescriptions);
|
|
SDL_stack_free(vertexInputAttributeDescriptions);
|
|
SDL_stack_free(colorBlendAttachmentStates);
|
|
SDL_stack_free(divisorDescriptions);
|
|
SDL_free(graphicsPipeline);
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to initialize pipeline resource layout!");
|
|
return NULL;
|
|
}
|
|
|
|
// Pipeline
|
|
|
|
vkPipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
|
vkPipelineCreateInfo.pNext = NULL;
|
|
vkPipelineCreateInfo.flags = 0;
|
|
vkPipelineCreateInfo.stageCount = 2;
|
|
vkPipelineCreateInfo.pStages = shaderStageCreateInfos;
|
|
vkPipelineCreateInfo.pVertexInputState = &vertexInputStateCreateInfo;
|
|
vkPipelineCreateInfo.pInputAssemblyState = &inputAssemblyStateCreateInfo;
|
|
vkPipelineCreateInfo.pTessellationState = VK_NULL_HANDLE;
|
|
vkPipelineCreateInfo.pViewportState = &viewportStateCreateInfo;
|
|
vkPipelineCreateInfo.pRasterizationState = &rasterizationStateCreateInfo;
|
|
vkPipelineCreateInfo.pMultisampleState = &multisampleStateCreateInfo;
|
|
vkPipelineCreateInfo.pDepthStencilState = &depthStencilStateCreateInfo;
|
|
vkPipelineCreateInfo.pColorBlendState = &colorBlendStateCreateInfo;
|
|
vkPipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo;
|
|
vkPipelineCreateInfo.layout = graphicsPipeline->resourceLayout.pipelineLayout;
|
|
vkPipelineCreateInfo.renderPass = transientRenderPass;
|
|
vkPipelineCreateInfo.subpass = 0;
|
|
vkPipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
|
|
vkPipelineCreateInfo.basePipelineIndex = 0;
|
|
|
|
// TODO: enable pipeline caching
|
|
vulkanResult = renderer->vkCreateGraphicsPipelines(
|
|
renderer->logicalDevice,
|
|
VK_NULL_HANDLE,
|
|
1,
|
|
&vkPipelineCreateInfo,
|
|
NULL,
|
|
&graphicsPipeline->pipeline);
|
|
|
|
SDL_stack_free(vertexInputBindingDescriptions);
|
|
SDL_stack_free(vertexInputAttributeDescriptions);
|
|
SDL_stack_free(colorBlendAttachmentStates);
|
|
SDL_stack_free(divisorDescriptions);
|
|
|
|
renderer->vkDestroyRenderPass(
|
|
renderer->logicalDevice,
|
|
transientRenderPass,
|
|
NULL);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
SDL_free(graphicsPipeline);
|
|
LogVulkanResultAsError("vkCreateGraphicsPipelines", vulkanResult);
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create graphics pipeline!");
|
|
return NULL;
|
|
}
|
|
|
|
SDL_AtomicSet(&graphicsPipeline->referenceCount, 0);
|
|
|
|
return (SDL_GPUGraphicsPipeline *)graphicsPipeline;
|
|
}
|
|
|
|
static SDL_GPUComputePipeline *VULKAN_CreateComputePipeline(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUComputePipelineCreateInfo *pipelineCreateInfo)
|
|
{
|
|
VkShaderModuleCreateInfo shaderModuleCreateInfo;
|
|
VkComputePipelineCreateInfo computePipelineCreateInfo;
|
|
VkPipelineShaderStageCreateInfo pipelineShaderStageCreateInfo;
|
|
VkResult vulkanResult;
|
|
Uint32 i;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanComputePipeline *vulkanComputePipeline;
|
|
|
|
if (pipelineCreateInfo->format != SDL_GPU_SHADERFORMAT_SPIRV) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Incompatible shader format for Vulkan!");
|
|
return NULL;
|
|
}
|
|
|
|
vulkanComputePipeline = SDL_malloc(sizeof(VulkanComputePipeline));
|
|
shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
|
shaderModuleCreateInfo.pNext = NULL;
|
|
shaderModuleCreateInfo.flags = 0;
|
|
shaderModuleCreateInfo.codeSize = pipelineCreateInfo->codeSize;
|
|
shaderModuleCreateInfo.pCode = (Uint32 *)pipelineCreateInfo->code;
|
|
|
|
vulkanResult = renderer->vkCreateShaderModule(
|
|
renderer->logicalDevice,
|
|
&shaderModuleCreateInfo,
|
|
NULL,
|
|
&vulkanComputePipeline->shaderModule);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
SDL_free(vulkanComputePipeline);
|
|
LogVulkanResultAsError("vkCreateShaderModule", vulkanResult);
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create compute pipeline!");
|
|
return NULL;
|
|
}
|
|
|
|
pipelineShaderStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
pipelineShaderStageCreateInfo.pNext = NULL;
|
|
pipelineShaderStageCreateInfo.flags = 0;
|
|
pipelineShaderStageCreateInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
pipelineShaderStageCreateInfo.module = vulkanComputePipeline->shaderModule;
|
|
pipelineShaderStageCreateInfo.pName = pipelineCreateInfo->entryPointName;
|
|
pipelineShaderStageCreateInfo.pSpecializationInfo = NULL;
|
|
|
|
if (!VULKAN_INTERNAL_InitializeComputePipelineResourceLayout(
|
|
renderer,
|
|
pipelineCreateInfo,
|
|
&vulkanComputePipeline->resourceLayout)) {
|
|
renderer->vkDestroyShaderModule(
|
|
renderer->logicalDevice,
|
|
vulkanComputePipeline->shaderModule,
|
|
NULL);
|
|
SDL_free(vulkanComputePipeline);
|
|
return NULL;
|
|
}
|
|
|
|
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
|
computePipelineCreateInfo.pNext = NULL;
|
|
computePipelineCreateInfo.flags = 0;
|
|
computePipelineCreateInfo.stage = pipelineShaderStageCreateInfo;
|
|
computePipelineCreateInfo.layout =
|
|
vulkanComputePipeline->resourceLayout.pipelineLayout;
|
|
computePipelineCreateInfo.basePipelineHandle = (VkPipeline)VK_NULL_HANDLE;
|
|
computePipelineCreateInfo.basePipelineIndex = 0;
|
|
|
|
vulkanResult = renderer->vkCreateComputePipelines(
|
|
renderer->logicalDevice,
|
|
(VkPipelineCache)VK_NULL_HANDLE,
|
|
1,
|
|
&computePipelineCreateInfo,
|
|
NULL,
|
|
&vulkanComputePipeline->pipeline);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
renderer->vkDestroyPipelineLayout(
|
|
renderer->logicalDevice,
|
|
vulkanComputePipeline->resourceLayout.pipelineLayout,
|
|
NULL);
|
|
|
|
for (i = 0; i < 3; i += 1) {
|
|
VULKAN_INTERNAL_DestroyDescriptorSetPool(
|
|
renderer,
|
|
&vulkanComputePipeline->resourceLayout.descriptorSetPools[i]);
|
|
}
|
|
|
|
renderer->vkDestroyShaderModule(
|
|
renderer->logicalDevice,
|
|
vulkanComputePipeline->shaderModule,
|
|
NULL);
|
|
|
|
LogVulkanResultAsError("vkCreateComputePipeline", vulkanResult);
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create compute pipeline!");
|
|
return NULL;
|
|
}
|
|
|
|
SDL_AtomicSet(&vulkanComputePipeline->referenceCount, 0);
|
|
|
|
return (SDL_GPUComputePipeline *)vulkanComputePipeline;
|
|
}
|
|
|
|
static SDL_GPUSampler *VULKAN_CreateSampler(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUSamplerCreateInfo *samplerCreateInfo)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanSampler *vulkanSampler = SDL_malloc(sizeof(VulkanSampler));
|
|
VkResult vulkanResult;
|
|
|
|
VkSamplerCreateInfo vkSamplerCreateInfo;
|
|
vkSamplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
|
vkSamplerCreateInfo.pNext = NULL;
|
|
vkSamplerCreateInfo.flags = 0;
|
|
vkSamplerCreateInfo.magFilter = SDLToVK_Filter[samplerCreateInfo->magFilter];
|
|
vkSamplerCreateInfo.minFilter = SDLToVK_Filter[samplerCreateInfo->minFilter];
|
|
vkSamplerCreateInfo.mipmapMode = SDLToVK_SamplerMipmapMode[samplerCreateInfo->mipmapMode];
|
|
vkSamplerCreateInfo.addressModeU = SDLToVK_SamplerAddressMode[samplerCreateInfo->addressModeU];
|
|
vkSamplerCreateInfo.addressModeV = SDLToVK_SamplerAddressMode[samplerCreateInfo->addressModeV];
|
|
vkSamplerCreateInfo.addressModeW = SDLToVK_SamplerAddressMode[samplerCreateInfo->addressModeW];
|
|
vkSamplerCreateInfo.mipLodBias = samplerCreateInfo->mipLodBias;
|
|
vkSamplerCreateInfo.anisotropyEnable = samplerCreateInfo->anisotropyEnable;
|
|
vkSamplerCreateInfo.maxAnisotropy = samplerCreateInfo->maxAnisotropy;
|
|
vkSamplerCreateInfo.compareEnable = samplerCreateInfo->compareEnable;
|
|
vkSamplerCreateInfo.compareOp = SDLToVK_CompareOp[samplerCreateInfo->compareOp];
|
|
vkSamplerCreateInfo.minLod = samplerCreateInfo->minLod;
|
|
vkSamplerCreateInfo.maxLod = samplerCreateInfo->maxLod;
|
|
vkSamplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; // arbitrary, unused
|
|
vkSamplerCreateInfo.unnormalizedCoordinates = VK_FALSE;
|
|
|
|
vulkanResult = renderer->vkCreateSampler(
|
|
renderer->logicalDevice,
|
|
&vkSamplerCreateInfo,
|
|
NULL,
|
|
&vulkanSampler->sampler);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
SDL_free(vulkanSampler);
|
|
LogVulkanResultAsError("vkCreateSampler", vulkanResult);
|
|
return NULL;
|
|
}
|
|
|
|
SDL_AtomicSet(&vulkanSampler->referenceCount, 0);
|
|
|
|
return (SDL_GPUSampler *)vulkanSampler;
|
|
}
|
|
|
|
static SDL_GPUShader *VULKAN_CreateShader(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUShaderCreateInfo *shaderCreateInfo)
|
|
{
|
|
VulkanShader *vulkanShader;
|
|
VkResult vulkanResult;
|
|
VkShaderModuleCreateInfo vkShaderModuleCreateInfo;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
size_t entryPointNameLength;
|
|
|
|
vulkanShader = SDL_malloc(sizeof(VulkanShader));
|
|
vkShaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
|
vkShaderModuleCreateInfo.pNext = NULL;
|
|
vkShaderModuleCreateInfo.flags = 0;
|
|
vkShaderModuleCreateInfo.codeSize = shaderCreateInfo->codeSize;
|
|
vkShaderModuleCreateInfo.pCode = (Uint32 *)shaderCreateInfo->code;
|
|
|
|
vulkanResult = renderer->vkCreateShaderModule(
|
|
renderer->logicalDevice,
|
|
&vkShaderModuleCreateInfo,
|
|
NULL,
|
|
&vulkanShader->shaderModule);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
SDL_free(vulkanShader);
|
|
LogVulkanResultAsError("vkCreateShaderModule", vulkanResult);
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create shader module!");
|
|
return NULL;
|
|
}
|
|
|
|
entryPointNameLength = SDL_strlen(shaderCreateInfo->entryPointName) + 1;
|
|
vulkanShader->entryPointName = SDL_malloc(entryPointNameLength);
|
|
SDL_utf8strlcpy((char *)vulkanShader->entryPointName, shaderCreateInfo->entryPointName, entryPointNameLength);
|
|
|
|
vulkanShader->samplerCount = shaderCreateInfo->samplerCount;
|
|
vulkanShader->storageTextureCount = shaderCreateInfo->storageTextureCount;
|
|
vulkanShader->storageBufferCount = shaderCreateInfo->storageBufferCount;
|
|
vulkanShader->uniformBufferCount = shaderCreateInfo->uniformBufferCount;
|
|
|
|
SDL_AtomicSet(&vulkanShader->referenceCount, 0);
|
|
|
|
return (SDL_GPUShader *)vulkanShader;
|
|
}
|
|
|
|
static bool VULKAN_SupportsSampleCount(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTextureFormat format,
|
|
SDL_GPUSampleCount sampleCount)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VkSampleCountFlags bits = IsDepthFormat(format) ? renderer->physicalDeviceProperties.properties.limits.framebufferDepthSampleCounts : renderer->physicalDeviceProperties.properties.limits.framebufferColorSampleCounts;
|
|
VkSampleCountFlagBits vkSampleCount = SDLToVK_SampleCount[sampleCount];
|
|
return !!(bits & vkSampleCount);
|
|
}
|
|
|
|
static SDL_GPUTexture *VULKAN_CreateTexture(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTextureCreateInfo *textureCreateInfo)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VkImageAspectFlags imageAspectFlags;
|
|
Uint8 isDepthFormat = IsDepthFormat(textureCreateInfo->format);
|
|
VkFormat format;
|
|
VkComponentMapping swizzle;
|
|
VulkanTextureContainer *container;
|
|
VulkanTextureHandle *textureHandle;
|
|
|
|
format = SDLToVK_SurfaceFormat[textureCreateInfo->format];
|
|
swizzle = SDLToVK_SurfaceSwizzle[textureCreateInfo->format];
|
|
|
|
if (isDepthFormat) {
|
|
imageAspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
|
|
if (IsStencilFormat(textureCreateInfo->format)) {
|
|
imageAspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
}
|
|
} else {
|
|
imageAspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
}
|
|
|
|
textureHandle = VULKAN_INTERNAL_CreateTextureHandle(
|
|
renderer,
|
|
textureCreateInfo->width,
|
|
textureCreateInfo->height,
|
|
textureCreateInfo->type == SDL_GPU_TEXTURETYPE_3D ? textureCreateInfo->layerCountOrDepth : 1,
|
|
textureCreateInfo->type,
|
|
textureCreateInfo->type == SDL_GPU_TEXTURETYPE_3D ? 1 : textureCreateInfo->layerCountOrDepth,
|
|
textureCreateInfo->levelCount,
|
|
SDLToVK_SampleCount[textureCreateInfo->sampleCount],
|
|
format,
|
|
swizzle,
|
|
imageAspectFlags,
|
|
textureCreateInfo->usageFlags,
|
|
false);
|
|
|
|
if (textureHandle == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create texture container!");
|
|
return NULL;
|
|
}
|
|
|
|
container = SDL_malloc(sizeof(VulkanTextureContainer));
|
|
container->header.info = *textureCreateInfo;
|
|
container->canBeCycled = 1;
|
|
container->activeTextureHandle = textureHandle;
|
|
container->textureCapacity = 1;
|
|
container->textureCount = 1;
|
|
container->textureHandles = SDL_malloc(
|
|
container->textureCapacity * sizeof(VulkanTextureHandle *));
|
|
container->textureHandles[0] = container->activeTextureHandle;
|
|
container->debugName = NULL;
|
|
|
|
textureHandle->container = container;
|
|
|
|
return (SDL_GPUTexture *)container;
|
|
}
|
|
|
|
static SDL_GPUBuffer *VULKAN_CreateBuffer(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUBufferUsageFlags usageFlags,
|
|
Uint32 sizeInBytes)
|
|
{
|
|
return (SDL_GPUBuffer *)VULKAN_INTERNAL_CreateBufferContainer(
|
|
(VulkanRenderer *)driverData,
|
|
(VkDeviceSize)sizeInBytes,
|
|
usageFlags,
|
|
VULKAN_BUFFER_TYPE_GPU);
|
|
}
|
|
|
|
static VulkanUniformBuffer *VULKAN_INTERNAL_CreateUniformBuffer(
|
|
VulkanRenderer *renderer,
|
|
Uint32 sizeInBytes)
|
|
{
|
|
VulkanUniformBuffer *uniformBuffer = SDL_malloc(sizeof(VulkanUniformBuffer));
|
|
|
|
uniformBuffer->bufferHandle = VULKAN_INTERNAL_CreateBufferHandle(
|
|
renderer,
|
|
(VkDeviceSize)sizeInBytes,
|
|
0,
|
|
VULKAN_BUFFER_TYPE_UNIFORM);
|
|
|
|
uniformBuffer->drawOffset = 0;
|
|
uniformBuffer->writeOffset = 0;
|
|
|
|
return uniformBuffer;
|
|
}
|
|
|
|
static SDL_GPUTransferBuffer *VULKAN_CreateTransferBuffer(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTransferBufferUsage usage, // ignored on Vulkan
|
|
Uint32 sizeInBytes)
|
|
{
|
|
return (SDL_GPUTransferBuffer *)VULKAN_INTERNAL_CreateBufferContainer(
|
|
(VulkanRenderer *)driverData,
|
|
(VkDeviceSize)sizeInBytes,
|
|
0,
|
|
VULKAN_BUFFER_TYPE_TRANSFER);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_ReleaseTexture(
|
|
VulkanRenderer *renderer,
|
|
VulkanTexture *vulkanTexture)
|
|
{
|
|
if (vulkanTexture->markedForDestroy) {
|
|
return;
|
|
}
|
|
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->texturesToDestroy,
|
|
VulkanTexture *,
|
|
renderer->texturesToDestroyCount + 1,
|
|
renderer->texturesToDestroyCapacity,
|
|
renderer->texturesToDestroyCapacity * 2)
|
|
|
|
renderer->texturesToDestroy[renderer->texturesToDestroyCount] = vulkanTexture;
|
|
renderer->texturesToDestroyCount += 1;
|
|
|
|
vulkanTexture->markedForDestroy = 1;
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void VULKAN_ReleaseTexture(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTexture *texture)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanTextureContainer *vulkanTextureContainer = (VulkanTextureContainer *)texture;
|
|
Uint32 i;
|
|
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
for (i = 0; i < vulkanTextureContainer->textureCount; i += 1) {
|
|
VULKAN_INTERNAL_ReleaseTexture(renderer, vulkanTextureContainer->textureHandles[i]->vulkanTexture);
|
|
SDL_free(vulkanTextureContainer->textureHandles[i]);
|
|
}
|
|
|
|
// Containers are just client handles, so we can destroy immediately
|
|
if (vulkanTextureContainer->debugName != NULL) {
|
|
SDL_free(vulkanTextureContainer->debugName);
|
|
}
|
|
SDL_free(vulkanTextureContainer->textureHandles);
|
|
SDL_free(vulkanTextureContainer);
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void VULKAN_ReleaseSampler(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUSampler *sampler)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanSampler *vulkanSampler = (VulkanSampler *)sampler;
|
|
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->samplersToDestroy,
|
|
VulkanSampler *,
|
|
renderer->samplersToDestroyCount + 1,
|
|
renderer->samplersToDestroyCapacity,
|
|
renderer->samplersToDestroyCapacity * 2)
|
|
|
|
renderer->samplersToDestroy[renderer->samplersToDestroyCount] = vulkanSampler;
|
|
renderer->samplersToDestroyCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_ReleaseBuffer(
|
|
VulkanRenderer *renderer,
|
|
VulkanBuffer *vulkanBuffer)
|
|
{
|
|
if (vulkanBuffer->markedForDestroy) {
|
|
return;
|
|
}
|
|
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->buffersToDestroy,
|
|
VulkanBuffer *,
|
|
renderer->buffersToDestroyCount + 1,
|
|
renderer->buffersToDestroyCapacity,
|
|
renderer->buffersToDestroyCapacity * 2)
|
|
|
|
renderer->buffersToDestroy[renderer->buffersToDestroyCount] = vulkanBuffer;
|
|
renderer->buffersToDestroyCount += 1;
|
|
|
|
vulkanBuffer->markedForDestroy = 1;
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_ReleaseBufferContainer(
|
|
VulkanRenderer *renderer,
|
|
VulkanBufferContainer *bufferContainer)
|
|
{
|
|
Uint32 i;
|
|
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
for (i = 0; i < bufferContainer->bufferCount; i += 1) {
|
|
VULKAN_INTERNAL_ReleaseBuffer(renderer, bufferContainer->bufferHandles[i]->vulkanBuffer);
|
|
SDL_free(bufferContainer->bufferHandles[i]);
|
|
}
|
|
|
|
// Containers are just client handles, so we can free immediately
|
|
if (bufferContainer->debugName != NULL) {
|
|
SDL_free(bufferContainer->debugName);
|
|
}
|
|
SDL_free(bufferContainer->bufferHandles);
|
|
SDL_free(bufferContainer);
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void VULKAN_ReleaseBuffer(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUBuffer *buffer)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanBufferContainer *vulkanBufferContainer = (VulkanBufferContainer *)buffer;
|
|
|
|
VULKAN_INTERNAL_ReleaseBufferContainer(
|
|
renderer,
|
|
vulkanBufferContainer);
|
|
}
|
|
|
|
static void VULKAN_ReleaseTransferBuffer(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTransferBuffer *transferBuffer)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)transferBuffer;
|
|
|
|
VULKAN_INTERNAL_ReleaseBufferContainer(
|
|
renderer,
|
|
transferBufferContainer);
|
|
}
|
|
|
|
static void VULKAN_ReleaseShader(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUShader *shader)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanShader *vulkanShader = (VulkanShader *)shader;
|
|
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->shadersToDestroy,
|
|
VulkanShader *,
|
|
renderer->shadersToDestroyCount + 1,
|
|
renderer->shadersToDestroyCapacity,
|
|
renderer->shadersToDestroyCapacity * 2)
|
|
|
|
renderer->shadersToDestroy[renderer->shadersToDestroyCount] = vulkanShader;
|
|
renderer->shadersToDestroyCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void VULKAN_ReleaseComputePipeline(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUComputePipeline *computePipeline)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanComputePipeline *vulkanComputePipeline = (VulkanComputePipeline *)computePipeline;
|
|
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->computePipelinesToDestroy,
|
|
VulkanComputePipeline *,
|
|
renderer->computePipelinesToDestroyCount + 1,
|
|
renderer->computePipelinesToDestroyCapacity,
|
|
renderer->computePipelinesToDestroyCapacity * 2)
|
|
|
|
renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount] = vulkanComputePipeline;
|
|
renderer->computePipelinesToDestroyCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void VULKAN_ReleaseGraphicsPipeline(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUGraphicsPipeline *graphicsPipeline)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanGraphicsPipeline *vulkanGraphicsPipeline = (VulkanGraphicsPipeline *)graphicsPipeline;
|
|
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->graphicsPipelinesToDestroy,
|
|
VulkanGraphicsPipeline *,
|
|
renderer->graphicsPipelinesToDestroyCount + 1,
|
|
renderer->graphicsPipelinesToDestroyCapacity,
|
|
renderer->graphicsPipelinesToDestroyCapacity * 2)
|
|
|
|
renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount] = vulkanGraphicsPipeline;
|
|
renderer->graphicsPipelinesToDestroyCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
// Command Buffer render state
|
|
|
|
static VkRenderPass VULKAN_INTERNAL_FetchRenderPass(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer,
|
|
SDL_GPUColorAttachmentInfo *colorAttachmentInfos,
|
|
Uint32 colorAttachmentCount,
|
|
SDL_GPUDepthStencilAttachmentInfo *depthStencilAttachmentInfo)
|
|
{
|
|
VulkanRenderPassHashTableValue *renderPassWrapper = NULL;
|
|
VkRenderPass renderPassHandle;
|
|
RenderPassHashTableKey key;
|
|
Uint32 i;
|
|
|
|
for (i = 0; i < colorAttachmentCount; i += 1) {
|
|
key.colorTargetDescriptions[i].format = ((VulkanTextureContainer *)colorAttachmentInfos[i].texture)->activeTextureHandle->vulkanTexture->format;
|
|
key.colorTargetDescriptions[i].loadOp = colorAttachmentInfos[i].loadOp;
|
|
key.colorTargetDescriptions[i].storeOp = colorAttachmentInfos[i].storeOp;
|
|
}
|
|
|
|
key.colorAttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT;
|
|
if (colorAttachmentCount > 0) {
|
|
key.colorAttachmentSampleCount = ((VulkanTextureContainer *)colorAttachmentInfos[0].texture)->activeTextureHandle->vulkanTexture->sampleCount;
|
|
}
|
|
|
|
key.colorAttachmentCount = colorAttachmentCount;
|
|
|
|
if (depthStencilAttachmentInfo == NULL) {
|
|
key.depthStencilTargetDescription.format = 0;
|
|
key.depthStencilTargetDescription.loadOp = SDL_GPU_LOADOP_DONT_CARE;
|
|
key.depthStencilTargetDescription.storeOp = SDL_GPU_STOREOP_DONT_CARE;
|
|
key.depthStencilTargetDescription.stencilLoadOp = SDL_GPU_LOADOP_DONT_CARE;
|
|
key.depthStencilTargetDescription.stencilStoreOp = SDL_GPU_STOREOP_DONT_CARE;
|
|
} else {
|
|
key.depthStencilTargetDescription.format = ((VulkanTextureContainer *)depthStencilAttachmentInfo->texture)->activeTextureHandle->vulkanTexture->format;
|
|
key.depthStencilTargetDescription.loadOp = depthStencilAttachmentInfo->loadOp;
|
|
key.depthStencilTargetDescription.storeOp = depthStencilAttachmentInfo->storeOp;
|
|
key.depthStencilTargetDescription.stencilLoadOp = depthStencilAttachmentInfo->stencilLoadOp;
|
|
key.depthStencilTargetDescription.stencilStoreOp = depthStencilAttachmentInfo->stencilStoreOp;
|
|
}
|
|
|
|
SDL_LockMutex(renderer->renderPassFetchLock);
|
|
|
|
bool result = SDL_FindInHashTable(
|
|
renderer->renderPassHashTable,
|
|
(const void *)&key,
|
|
(const void **)&renderPassWrapper);
|
|
|
|
SDL_UnlockMutex(renderer->renderPassFetchLock);
|
|
|
|
if (result) {
|
|
return renderPassWrapper->handle;
|
|
}
|
|
|
|
renderPassHandle = VULKAN_INTERNAL_CreateRenderPass(
|
|
renderer,
|
|
commandBuffer,
|
|
colorAttachmentInfos,
|
|
colorAttachmentCount,
|
|
depthStencilAttachmentInfo);
|
|
|
|
if (renderPassHandle == VK_NULL_HANDLE) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create VkRenderPass!");
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
// Have to malloc the key to store it in the hashtable
|
|
RenderPassHashTableKey *allocedKey = SDL_malloc(sizeof(RenderPassHashTableKey));
|
|
SDL_memcpy(allocedKey, &key, sizeof(RenderPassHashTableKey));
|
|
|
|
renderPassWrapper = SDL_malloc(sizeof(VulkanRenderPassHashTableValue));
|
|
renderPassWrapper->handle = renderPassHandle;
|
|
|
|
SDL_LockMutex(renderer->renderPassFetchLock);
|
|
|
|
SDL_InsertIntoHashTable(
|
|
renderer->renderPassHashTable,
|
|
(const void *)allocedKey,
|
|
(const void *)renderPassWrapper);
|
|
|
|
SDL_UnlockMutex(renderer->renderPassFetchLock);
|
|
return renderPassHandle;
|
|
}
|
|
|
|
static VulkanFramebuffer *VULKAN_INTERNAL_FetchFramebuffer(
|
|
VulkanRenderer *renderer,
|
|
VkRenderPass renderPass,
|
|
SDL_GPUColorAttachmentInfo *colorAttachmentInfos,
|
|
Uint32 colorAttachmentCount,
|
|
SDL_GPUDepthStencilAttachmentInfo *depthStencilAttachmentInfo,
|
|
Uint32 width,
|
|
Uint32 height)
|
|
{
|
|
VulkanFramebuffer *vulkanFramebuffer = NULL;
|
|
VkFramebufferCreateInfo framebufferInfo;
|
|
VkResult result;
|
|
VkImageView imageViewAttachments[2 * MAX_COLOR_TARGET_BINDINGS + 1];
|
|
FramebufferHashTableKey key;
|
|
Uint32 attachmentCount = 0;
|
|
Uint32 i;
|
|
|
|
for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) {
|
|
key.colorAttachmentViews[i] = VK_NULL_HANDLE;
|
|
key.colorMultiSampleAttachmentViews[i] = VK_NULL_HANDLE;
|
|
}
|
|
|
|
key.colorAttachmentCount = colorAttachmentCount;
|
|
|
|
for (i = 0; i < colorAttachmentCount; i += 1) {
|
|
VulkanTextureContainer *container = (VulkanTextureContainer *)colorAttachmentInfos[i].texture;
|
|
VulkanTextureSubresource *subresource = VULKAN_INTERNAL_FetchTextureSubresource(
|
|
container,
|
|
container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : colorAttachmentInfos[i].layerOrDepthPlane,
|
|
colorAttachmentInfos[i].mipLevel);
|
|
|
|
Uint32 rtvIndex =
|
|
container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? colorAttachmentInfos[i].layerOrDepthPlane : 0;
|
|
key.colorAttachmentViews[i] = subresource->renderTargetViews[rtvIndex];
|
|
|
|
if (subresource->msaaTexHandle != NULL) {
|
|
key.colorMultiSampleAttachmentViews[i] = subresource->msaaTexHandle->vulkanTexture->subresources[0].renderTargetViews[0];
|
|
}
|
|
}
|
|
|
|
if (depthStencilAttachmentInfo == NULL) {
|
|
key.depthStencilAttachmentView = VK_NULL_HANDLE;
|
|
} else {
|
|
VulkanTextureSubresource *subresource = VULKAN_INTERNAL_FetchTextureSubresource(
|
|
(VulkanTextureContainer *)depthStencilAttachmentInfo->texture,
|
|
0,
|
|
0);
|
|
key.depthStencilAttachmentView = subresource->depthStencilView;
|
|
}
|
|
|
|
key.width = width;
|
|
key.height = height;
|
|
|
|
SDL_LockMutex(renderer->framebufferFetchLock);
|
|
|
|
bool findResult = SDL_FindInHashTable(
|
|
renderer->framebufferHashTable,
|
|
(const void *)&key,
|
|
(const void **)&vulkanFramebuffer);
|
|
|
|
SDL_UnlockMutex(renderer->framebufferFetchLock);
|
|
|
|
if (findResult) {
|
|
return vulkanFramebuffer;
|
|
}
|
|
|
|
vulkanFramebuffer = SDL_malloc(sizeof(VulkanFramebuffer));
|
|
|
|
SDL_AtomicSet(&vulkanFramebuffer->referenceCount, 0);
|
|
|
|
// Create a new framebuffer
|
|
|
|
for (i = 0; i < colorAttachmentCount; i += 1) {
|
|
VulkanTextureContainer *container = (VulkanTextureContainer *)colorAttachmentInfos[i].texture;
|
|
VulkanTextureSubresource *subresource = VULKAN_INTERNAL_FetchTextureSubresource(
|
|
container,
|
|
container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : colorAttachmentInfos[i].layerOrDepthPlane,
|
|
colorAttachmentInfos[i].mipLevel);
|
|
|
|
Uint32 rtvIndex =
|
|
container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? colorAttachmentInfos[i].layerOrDepthPlane : 0;
|
|
|
|
imageViewAttachments[attachmentCount] =
|
|
subresource->renderTargetViews[rtvIndex];
|
|
|
|
attachmentCount += 1;
|
|
|
|
if (subresource->msaaTexHandle != NULL) {
|
|
imageViewAttachments[attachmentCount] =
|
|
subresource->msaaTexHandle->vulkanTexture->subresources[0].renderTargetViews[0];
|
|
|
|
attachmentCount += 1;
|
|
}
|
|
}
|
|
|
|
if (depthStencilAttachmentInfo != NULL) {
|
|
VulkanTextureSubresource *subresource = VULKAN_INTERNAL_FetchTextureSubresource(
|
|
(VulkanTextureContainer *)depthStencilAttachmentInfo->texture,
|
|
0,
|
|
0);
|
|
imageViewAttachments[attachmentCount] = subresource->depthStencilView;
|
|
|
|
attachmentCount += 1;
|
|
}
|
|
|
|
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
|
framebufferInfo.pNext = NULL;
|
|
framebufferInfo.flags = 0;
|
|
framebufferInfo.renderPass = renderPass;
|
|
framebufferInfo.attachmentCount = attachmentCount;
|
|
framebufferInfo.pAttachments = imageViewAttachments;
|
|
framebufferInfo.width = key.width;
|
|
framebufferInfo.height = key.height;
|
|
framebufferInfo.layers = 1;
|
|
|
|
result = renderer->vkCreateFramebuffer(
|
|
renderer->logicalDevice,
|
|
&framebufferInfo,
|
|
NULL,
|
|
&vulkanFramebuffer->framebuffer);
|
|
|
|
if (result == VK_SUCCESS) {
|
|
// Have to malloc the key to store it in the hashtable
|
|
FramebufferHashTableKey *allocedKey = SDL_malloc(sizeof(FramebufferHashTableKey));
|
|
SDL_memcpy(allocedKey, &key, sizeof(FramebufferHashTableKey));
|
|
|
|
SDL_LockMutex(renderer->framebufferFetchLock);
|
|
|
|
SDL_InsertIntoHashTable(
|
|
renderer->framebufferHashTable,
|
|
(const void *)allocedKey,
|
|
(const void *)vulkanFramebuffer);
|
|
|
|
SDL_UnlockMutex(renderer->framebufferFetchLock);
|
|
} else {
|
|
LogVulkanResultAsError("vkCreateFramebuffer", result);
|
|
SDL_free(vulkanFramebuffer);
|
|
vulkanFramebuffer = NULL;
|
|
}
|
|
|
|
return vulkanFramebuffer;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_SetCurrentViewport(
|
|
VulkanCommandBuffer *commandBuffer,
|
|
SDL_GPUViewport *viewport)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
|
|
vulkanCommandBuffer->currentViewport.x = viewport->x;
|
|
vulkanCommandBuffer->currentViewport.width = viewport->w;
|
|
vulkanCommandBuffer->currentViewport.minDepth = viewport->minDepth;
|
|
vulkanCommandBuffer->currentViewport.maxDepth = viewport->maxDepth;
|
|
|
|
// Viewport flip for consistency with other backends
|
|
// FIXME: need moltenVK hack
|
|
vulkanCommandBuffer->currentViewport.y = viewport->y + viewport->h;
|
|
vulkanCommandBuffer->currentViewport.height = -viewport->h;
|
|
}
|
|
|
|
static void VULKAN_SetViewport(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUViewport *viewport)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
|
|
VULKAN_INTERNAL_SetCurrentViewport(
|
|
vulkanCommandBuffer,
|
|
viewport);
|
|
|
|
renderer->vkCmdSetViewport(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
0,
|
|
1,
|
|
&vulkanCommandBuffer->currentViewport);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_SetCurrentScissor(
|
|
VulkanCommandBuffer *vulkanCommandBuffer,
|
|
SDL_Rect *scissor)
|
|
{
|
|
vulkanCommandBuffer->currentScissor.offset.x = scissor->x;
|
|
vulkanCommandBuffer->currentScissor.offset.y = scissor->y;
|
|
vulkanCommandBuffer->currentScissor.extent.width = scissor->w;
|
|
vulkanCommandBuffer->currentScissor.extent.height = scissor->h;
|
|
}
|
|
|
|
static void VULKAN_SetScissor(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_Rect *scissor)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
|
|
VULKAN_INTERNAL_SetCurrentScissor(
|
|
vulkanCommandBuffer,
|
|
scissor);
|
|
|
|
renderer->vkCmdSetScissor(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
0,
|
|
1,
|
|
&vulkanCommandBuffer->currentScissor);
|
|
}
|
|
|
|
static void VULKAN_BindVertexSamplers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUTextureSamplerBinding *textureSamplerBindings,
|
|
Uint32 bindingCount)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < bindingCount; i += 1) {
|
|
VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)textureSamplerBindings[i].texture;
|
|
vulkanCommandBuffer->vertexSamplerTextures[firstSlot + i] = textureContainer->activeTextureHandle->vulkanTexture;
|
|
vulkanCommandBuffer->vertexSamplers[firstSlot + i] = (VulkanSampler *)textureSamplerBindings[i].sampler;
|
|
|
|
VULKAN_INTERNAL_TrackSampler(
|
|
vulkanCommandBuffer,
|
|
(VulkanSampler *)textureSamplerBindings[i].sampler);
|
|
|
|
VULKAN_INTERNAL_TrackTexture(
|
|
vulkanCommandBuffer,
|
|
textureContainer->activeTextureHandle->vulkanTexture);
|
|
}
|
|
|
|
vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
|
|
}
|
|
|
|
static void VULKAN_BindVertexStorageTextures(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUTexture **storageTextures,
|
|
Uint32 bindingCount)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < bindingCount; i += 1) {
|
|
VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
|
|
|
|
vulkanCommandBuffer->vertexStorageTextures[firstSlot + i] = textureContainer->activeTextureHandle->vulkanTexture;
|
|
|
|
VULKAN_INTERNAL_TrackTexture(
|
|
vulkanCommandBuffer,
|
|
textureContainer->activeTextureHandle->vulkanTexture);
|
|
}
|
|
|
|
vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
|
|
}
|
|
|
|
static void VULKAN_BindVertexStorageBuffers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUBuffer **storageBuffers,
|
|
Uint32 bindingCount)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanBufferContainer *bufferContainer;
|
|
Uint32 i;
|
|
|
|
for (i = 0; i < bindingCount; i += 1) {
|
|
bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
|
|
|
|
vulkanCommandBuffer->vertexStorageBuffers[firstSlot + i] = bufferContainer->activeBufferHandle->vulkanBuffer;
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(
|
|
vulkanCommandBuffer,
|
|
bufferContainer->activeBufferHandle->vulkanBuffer);
|
|
}
|
|
|
|
vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
|
|
}
|
|
|
|
static void VULKAN_BindFragmentSamplers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUTextureSamplerBinding *textureSamplerBindings,
|
|
Uint32 bindingCount)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < bindingCount; i += 1) {
|
|
VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)textureSamplerBindings[i].texture;
|
|
vulkanCommandBuffer->fragmentSamplerTextures[firstSlot + i] = textureContainer->activeTextureHandle->vulkanTexture;
|
|
vulkanCommandBuffer->fragmentSamplers[firstSlot + i] = (VulkanSampler *)textureSamplerBindings[i].sampler;
|
|
|
|
VULKAN_INTERNAL_TrackSampler(
|
|
vulkanCommandBuffer,
|
|
(VulkanSampler *)textureSamplerBindings[i].sampler);
|
|
|
|
VULKAN_INTERNAL_TrackTexture(
|
|
vulkanCommandBuffer,
|
|
textureContainer->activeTextureHandle->vulkanTexture);
|
|
}
|
|
|
|
vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
|
|
}
|
|
|
|
static void VULKAN_BindFragmentStorageTextures(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUTexture **storageTextures,
|
|
Uint32 bindingCount)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < bindingCount; i += 1) {
|
|
VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
|
|
|
|
vulkanCommandBuffer->fragmentStorageTextures[firstSlot + i] =
|
|
textureContainer->activeTextureHandle->vulkanTexture;
|
|
|
|
VULKAN_INTERNAL_TrackTexture(
|
|
vulkanCommandBuffer,
|
|
textureContainer->activeTextureHandle->vulkanTexture);
|
|
}
|
|
|
|
vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
|
|
}
|
|
|
|
static void VULKAN_BindFragmentStorageBuffers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUBuffer **storageBuffers,
|
|
Uint32 bindingCount)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanBufferContainer *bufferContainer;
|
|
Uint32 i;
|
|
|
|
for (i = 0; i < bindingCount; i += 1) {
|
|
bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
|
|
|
|
vulkanCommandBuffer->fragmentStorageBuffers[firstSlot + i] = bufferContainer->activeBufferHandle->vulkanBuffer;
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(
|
|
vulkanCommandBuffer,
|
|
bufferContainer->activeBufferHandle->vulkanBuffer);
|
|
}
|
|
|
|
vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
|
|
}
|
|
|
|
static VulkanUniformBuffer *VULKAN_INTERNAL_AcquireUniformBufferFromPool(
|
|
VulkanCommandBuffer *commandBuffer)
|
|
{
|
|
VulkanRenderer *renderer = commandBuffer->renderer;
|
|
VulkanUniformBuffer *uniformBuffer;
|
|
|
|
SDL_LockMutex(renderer->acquireUniformBufferLock);
|
|
|
|
if (renderer->uniformBufferPoolCount > 0) {
|
|
uniformBuffer = renderer->uniformBufferPool[renderer->uniformBufferPoolCount - 1];
|
|
renderer->uniformBufferPoolCount -= 1;
|
|
} else {
|
|
uniformBuffer = VULKAN_INTERNAL_CreateUniformBuffer(
|
|
renderer,
|
|
UNIFORM_BUFFER_SIZE);
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->acquireUniformBufferLock);
|
|
|
|
VULKAN_INTERNAL_TrackUniformBuffer(commandBuffer, uniformBuffer);
|
|
|
|
return uniformBuffer;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_ReturnUniformBufferToPool(
|
|
VulkanRenderer *renderer,
|
|
VulkanUniformBuffer *uniformBuffer)
|
|
{
|
|
if (renderer->uniformBufferPoolCount >= renderer->uniformBufferPoolCapacity) {
|
|
renderer->uniformBufferPoolCapacity *= 2;
|
|
renderer->uniformBufferPool = SDL_realloc(
|
|
renderer->uniformBufferPool,
|
|
renderer->uniformBufferPoolCapacity * sizeof(VulkanUniformBuffer *));
|
|
}
|
|
|
|
renderer->uniformBufferPool[renderer->uniformBufferPoolCount] = uniformBuffer;
|
|
renderer->uniformBufferPoolCount += 1;
|
|
|
|
uniformBuffer->writeOffset = 0;
|
|
uniformBuffer->drawOffset = 0;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_PushUniformData(
|
|
VulkanCommandBuffer *commandBuffer,
|
|
VulkanUniformBufferStage uniformBufferStage,
|
|
Uint32 slotIndex,
|
|
const void *data,
|
|
Uint32 dataLengthInBytes)
|
|
{
|
|
Uint32 blockSize =
|
|
VULKAN_INTERNAL_NextHighestAlignment32(
|
|
dataLengthInBytes,
|
|
commandBuffer->renderer->minUBOAlignment);
|
|
|
|
VulkanUniformBuffer *uniformBuffer;
|
|
|
|
if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_VERTEX) {
|
|
if (commandBuffer->vertexUniformBuffers[slotIndex] == NULL) {
|
|
commandBuffer->vertexUniformBuffers[slotIndex] = VULKAN_INTERNAL_AcquireUniformBufferFromPool(
|
|
commandBuffer);
|
|
}
|
|
uniformBuffer = commandBuffer->vertexUniformBuffers[slotIndex];
|
|
} else if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_FRAGMENT) {
|
|
if (commandBuffer->fragmentUniformBuffers[slotIndex] == NULL) {
|
|
commandBuffer->fragmentUniformBuffers[slotIndex] = VULKAN_INTERNAL_AcquireUniformBufferFromPool(
|
|
commandBuffer);
|
|
}
|
|
uniformBuffer = commandBuffer->fragmentUniformBuffers[slotIndex];
|
|
} else if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_COMPUTE) {
|
|
if (commandBuffer->computeUniformBuffers[slotIndex] == NULL) {
|
|
commandBuffer->computeUniformBuffers[slotIndex] = VULKAN_INTERNAL_AcquireUniformBufferFromPool(
|
|
commandBuffer);
|
|
}
|
|
uniformBuffer = commandBuffer->computeUniformBuffers[slotIndex];
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
|
|
return;
|
|
}
|
|
|
|
// If there is no more room, acquire a new uniform buffer
|
|
if (uniformBuffer->writeOffset + blockSize + MAX_UBO_SECTION_SIZE >= uniformBuffer->bufferHandle->vulkanBuffer->size) {
|
|
uniformBuffer = VULKAN_INTERNAL_AcquireUniformBufferFromPool(commandBuffer);
|
|
|
|
uniformBuffer->drawOffset = 0;
|
|
uniformBuffer->writeOffset = 0;
|
|
|
|
if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_VERTEX) {
|
|
commandBuffer->vertexUniformBuffers[slotIndex] = uniformBuffer;
|
|
commandBuffer->needNewVertexUniformDescriptorSet = true;
|
|
} else if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_FRAGMENT) {
|
|
commandBuffer->fragmentUniformBuffers[slotIndex] = uniformBuffer;
|
|
commandBuffer->needNewFragmentUniformDescriptorSet = true;
|
|
} else if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_COMPUTE) {
|
|
commandBuffer->computeUniformBuffers[slotIndex] = uniformBuffer;
|
|
commandBuffer->needNewComputeUniformDescriptorSet = true;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
uniformBuffer->drawOffset = uniformBuffer->writeOffset;
|
|
|
|
Uint8 *dst =
|
|
uniformBuffer->bufferHandle->vulkanBuffer->usedRegion->allocation->mapPointer +
|
|
uniformBuffer->bufferHandle->vulkanBuffer->usedRegion->resourceOffset +
|
|
uniformBuffer->writeOffset;
|
|
|
|
SDL_memcpy(
|
|
dst,
|
|
data,
|
|
dataLengthInBytes);
|
|
|
|
uniformBuffer->writeOffset += blockSize;
|
|
|
|
if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_VERTEX) {
|
|
commandBuffer->needNewVertexUniformOffsets = true;
|
|
} else if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_FRAGMENT) {
|
|
commandBuffer->needNewFragmentUniformOffsets = true;
|
|
} else if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_COMPUTE) {
|
|
commandBuffer->needNewComputeUniformOffsets = true;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void VULKAN_BeginRenderPass(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUColorAttachmentInfo *colorAttachmentInfos,
|
|
Uint32 colorAttachmentCount,
|
|
SDL_GPUDepthStencilAttachmentInfo *depthStencilAttachmentInfo)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VkRenderPass renderPass;
|
|
VulkanFramebuffer *framebuffer;
|
|
|
|
Uint32 w, h;
|
|
VkClearValue *clearValues;
|
|
Uint32 clearCount = colorAttachmentCount;
|
|
Uint32 multisampleAttachmentCount = 0;
|
|
Uint32 totalColorAttachmentCount = 0;
|
|
Uint32 i;
|
|
SDL_GPUViewport defaultViewport;
|
|
SDL_Rect defaultScissor;
|
|
Uint32 framebufferWidth = UINT32_MAX;
|
|
Uint32 framebufferHeight = UINT32_MAX;
|
|
|
|
for (i = 0; i < colorAttachmentCount; i += 1) {
|
|
VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)colorAttachmentInfos[i].texture;
|
|
|
|
w = textureContainer->activeTextureHandle->vulkanTexture->dimensions.width >> colorAttachmentInfos[i].mipLevel;
|
|
h = textureContainer->activeTextureHandle->vulkanTexture->dimensions.height >> colorAttachmentInfos[i].mipLevel;
|
|
|
|
// The framebuffer cannot be larger than the smallest attachment.
|
|
|
|
if (w < framebufferWidth) {
|
|
framebufferWidth = w;
|
|
}
|
|
|
|
if (h < framebufferHeight) {
|
|
framebufferHeight = h;
|
|
}
|
|
|
|
// FIXME: validate this in gpu.c
|
|
if (!(textureContainer->header.info.usageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Color attachment texture was not designated as a target!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (depthStencilAttachmentInfo != NULL) {
|
|
VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)depthStencilAttachmentInfo->texture;
|
|
|
|
w = textureContainer->activeTextureHandle->vulkanTexture->dimensions.width;
|
|
h = textureContainer->activeTextureHandle->vulkanTexture->dimensions.height;
|
|
|
|
// The framebuffer cannot be larger than the smallest attachment.
|
|
|
|
if (w < framebufferWidth) {
|
|
framebufferWidth = w;
|
|
}
|
|
|
|
if (h < framebufferHeight) {
|
|
framebufferHeight = h;
|
|
}
|
|
|
|
// FIXME: validate this in gpu.c
|
|
if (!(textureContainer->header.info.usageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Depth stencil attachment texture was not designated as a target!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < colorAttachmentCount; i += 1) {
|
|
VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)colorAttachmentInfos[i].texture;
|
|
VulkanTextureSubresource *subresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
textureContainer,
|
|
textureContainer->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : colorAttachmentInfos[i].layerOrDepthPlane,
|
|
colorAttachmentInfos[i].mipLevel,
|
|
colorAttachmentInfos[i].cycle,
|
|
VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT);
|
|
|
|
if (subresource->msaaTexHandle != NULL) {
|
|
// Transition the multisample attachment
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT,
|
|
&subresource->msaaTexHandle->vulkanTexture->subresources[0]);
|
|
|
|
clearCount += 1;
|
|
multisampleAttachmentCount += 1;
|
|
}
|
|
|
|
vulkanCommandBuffer->colorAttachmentSubresources[i] = subresource;
|
|
|
|
VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, subresource->parent);
|
|
// TODO: do we need to track the msaa texture? or is it implicitly only used when the regular texture is used?
|
|
}
|
|
|
|
vulkanCommandBuffer->colorAttachmentSubresourceCount = colorAttachmentCount;
|
|
|
|
if (depthStencilAttachmentInfo != NULL) {
|
|
VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)depthStencilAttachmentInfo->texture;
|
|
VulkanTextureSubresource *subresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
textureContainer,
|
|
0,
|
|
0,
|
|
depthStencilAttachmentInfo->cycle,
|
|
VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT);
|
|
|
|
clearCount += 1;
|
|
|
|
vulkanCommandBuffer->depthStencilAttachmentSubresource = subresource;
|
|
|
|
VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, subresource->parent);
|
|
}
|
|
|
|
// Fetch required render objects
|
|
|
|
renderPass = VULKAN_INTERNAL_FetchRenderPass(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
colorAttachmentInfos,
|
|
colorAttachmentCount,
|
|
depthStencilAttachmentInfo);
|
|
|
|
framebuffer = VULKAN_INTERNAL_FetchFramebuffer(
|
|
renderer,
|
|
renderPass,
|
|
colorAttachmentInfos,
|
|
colorAttachmentCount,
|
|
depthStencilAttachmentInfo,
|
|
framebufferWidth,
|
|
framebufferHeight);
|
|
|
|
VULKAN_INTERNAL_TrackFramebuffer(renderer, vulkanCommandBuffer, framebuffer);
|
|
|
|
// Set clear values
|
|
|
|
clearValues = SDL_stack_alloc(VkClearValue, clearCount);
|
|
|
|
totalColorAttachmentCount = colorAttachmentCount + multisampleAttachmentCount;
|
|
|
|
for (i = 0; i < totalColorAttachmentCount; i += 1) {
|
|
clearValues[i].color.float32[0] = colorAttachmentInfos[i].clearColor.r;
|
|
clearValues[i].color.float32[1] = colorAttachmentInfos[i].clearColor.g;
|
|
clearValues[i].color.float32[2] = colorAttachmentInfos[i].clearColor.b;
|
|
clearValues[i].color.float32[3] = colorAttachmentInfos[i].clearColor.a;
|
|
|
|
VulkanTextureContainer *container = (VulkanTextureContainer *)colorAttachmentInfos[i].texture;
|
|
VulkanTextureSubresource *subresource = VULKAN_INTERNAL_FetchTextureSubresource(
|
|
container,
|
|
container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : colorAttachmentInfos[i].layerOrDepthPlane,
|
|
colorAttachmentInfos[i].mipLevel);
|
|
|
|
if (subresource->parent->sampleCount > VK_SAMPLE_COUNT_1_BIT) {
|
|
clearValues[i + 1].color.float32[0] = colorAttachmentInfos[i].clearColor.r;
|
|
clearValues[i + 1].color.float32[1] = colorAttachmentInfos[i].clearColor.g;
|
|
clearValues[i + 1].color.float32[2] = colorAttachmentInfos[i].clearColor.b;
|
|
clearValues[i + 1].color.float32[3] = colorAttachmentInfos[i].clearColor.a;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
if (depthStencilAttachmentInfo != NULL) {
|
|
clearValues[totalColorAttachmentCount].depthStencil.depth =
|
|
depthStencilAttachmentInfo->depthStencilClearValue.depth;
|
|
clearValues[totalColorAttachmentCount].depthStencil.stencil =
|
|
depthStencilAttachmentInfo->depthStencilClearValue.stencil;
|
|
}
|
|
|
|
VkRenderPassBeginInfo renderPassBeginInfo;
|
|
renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
|
renderPassBeginInfo.pNext = NULL;
|
|
renderPassBeginInfo.renderPass = renderPass;
|
|
renderPassBeginInfo.framebuffer = framebuffer->framebuffer;
|
|
renderPassBeginInfo.pClearValues = clearValues;
|
|
renderPassBeginInfo.clearValueCount = clearCount;
|
|
renderPassBeginInfo.renderArea.extent.width = framebufferWidth;
|
|
renderPassBeginInfo.renderArea.extent.height = framebufferHeight;
|
|
renderPassBeginInfo.renderArea.offset.x = 0;
|
|
renderPassBeginInfo.renderArea.offset.y = 0;
|
|
|
|
renderer->vkCmdBeginRenderPass(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
&renderPassBeginInfo,
|
|
VK_SUBPASS_CONTENTS_INLINE);
|
|
|
|
SDL_stack_free(clearValues);
|
|
|
|
// Set sensible default viewport state
|
|
|
|
defaultViewport.x = 0;
|
|
defaultViewport.y = 0;
|
|
defaultViewport.w = (float)framebufferWidth;
|
|
defaultViewport.h = (float)framebufferHeight;
|
|
defaultViewport.minDepth = 0;
|
|
defaultViewport.maxDepth = 1;
|
|
|
|
VULKAN_INTERNAL_SetCurrentViewport(
|
|
vulkanCommandBuffer,
|
|
&defaultViewport);
|
|
|
|
defaultScissor.x = 0;
|
|
defaultScissor.y = 0;
|
|
defaultScissor.w = (Sint32)framebufferWidth;
|
|
defaultScissor.h = (Sint32)framebufferHeight;
|
|
|
|
VULKAN_INTERNAL_SetCurrentScissor(
|
|
vulkanCommandBuffer,
|
|
&defaultScissor);
|
|
}
|
|
|
|
static void VULKAN_BindGraphicsPipeline(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUGraphicsPipeline *graphicsPipeline)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VulkanGraphicsPipeline *pipeline = (VulkanGraphicsPipeline *)graphicsPipeline;
|
|
|
|
renderer->vkCmdBindPipeline(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
pipeline->pipeline);
|
|
|
|
vulkanCommandBuffer->currentGraphicsPipeline = pipeline;
|
|
|
|
VULKAN_INTERNAL_TrackGraphicsPipeline(vulkanCommandBuffer, pipeline);
|
|
|
|
renderer->vkCmdSetViewport(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
0,
|
|
1,
|
|
&vulkanCommandBuffer->currentViewport);
|
|
|
|
renderer->vkCmdSetScissor(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
0,
|
|
1,
|
|
&vulkanCommandBuffer->currentScissor);
|
|
|
|
// Acquire uniform buffers if necessary
|
|
for (Uint32 i = 0; i < pipeline->resourceLayout.vertexUniformBufferCount; i += 1) {
|
|
if (vulkanCommandBuffer->vertexUniformBuffers[i] == NULL) {
|
|
vulkanCommandBuffer->vertexUniformBuffers[i] = VULKAN_INTERNAL_AcquireUniformBufferFromPool(
|
|
vulkanCommandBuffer);
|
|
}
|
|
}
|
|
|
|
for (Uint32 i = 0; i < pipeline->resourceLayout.fragmentUniformBufferCount; i += 1) {
|
|
if (vulkanCommandBuffer->fragmentUniformBuffers[i] == NULL) {
|
|
vulkanCommandBuffer->fragmentUniformBuffers[i] = VULKAN_INTERNAL_AcquireUniformBufferFromPool(
|
|
vulkanCommandBuffer);
|
|
}
|
|
}
|
|
|
|
// Mark bindings as needed
|
|
vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
|
|
vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
|
|
vulkanCommandBuffer->needNewVertexUniformDescriptorSet = true;
|
|
vulkanCommandBuffer->needNewFragmentUniformDescriptorSet = true;
|
|
vulkanCommandBuffer->needNewVertexUniformOffsets = true;
|
|
vulkanCommandBuffer->needNewFragmentUniformOffsets = true;
|
|
}
|
|
|
|
static void VULKAN_BindVertexBuffers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstBinding,
|
|
SDL_GPUBufferBinding *pBindings,
|
|
Uint32 bindingCount)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VulkanBuffer *currentVulkanBuffer;
|
|
VkBuffer *buffers = SDL_stack_alloc(VkBuffer, bindingCount);
|
|
VkDeviceSize *offsets = SDL_stack_alloc(VkDeviceSize, bindingCount);
|
|
Uint32 i;
|
|
|
|
for (i = 0; i < bindingCount; i += 1) {
|
|
currentVulkanBuffer = ((VulkanBufferContainer *)pBindings[i].buffer)->activeBufferHandle->vulkanBuffer;
|
|
buffers[i] = currentVulkanBuffer->buffer;
|
|
offsets[i] = (VkDeviceSize)pBindings[i].offset;
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, currentVulkanBuffer);
|
|
}
|
|
|
|
renderer->vkCmdBindVertexBuffers(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
firstBinding,
|
|
bindingCount,
|
|
buffers,
|
|
offsets);
|
|
|
|
SDL_stack_free(buffers);
|
|
SDL_stack_free(offsets);
|
|
}
|
|
|
|
static void VULKAN_BindIndexBuffer(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUBufferBinding *pBinding,
|
|
SDL_GPUIndexElementSize indexElementSize)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)pBinding->buffer)->activeBufferHandle->vulkanBuffer;
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
|
|
|
|
renderer->vkCmdBindIndexBuffer(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
vulkanBuffer->buffer,
|
|
(VkDeviceSize)pBinding->offset,
|
|
SDLToVK_IndexType[indexElementSize]);
|
|
}
|
|
|
|
static void VULKAN_PushVertexUniformData(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 slotIndex,
|
|
const void *data,
|
|
Uint32 dataLengthInBytes)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
|
|
VULKAN_INTERNAL_PushUniformData(
|
|
vulkanCommandBuffer,
|
|
VULKAN_UNIFORM_BUFFER_STAGE_VERTEX,
|
|
slotIndex,
|
|
data,
|
|
dataLengthInBytes);
|
|
}
|
|
|
|
static void VULKAN_PushFragmentUniformData(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 slotIndex,
|
|
const void *data,
|
|
Uint32 dataLengthInBytes)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
|
|
VULKAN_INTERNAL_PushUniformData(
|
|
vulkanCommandBuffer,
|
|
VULKAN_UNIFORM_BUFFER_STAGE_FRAGMENT,
|
|
slotIndex,
|
|
data,
|
|
dataLengthInBytes);
|
|
}
|
|
|
|
static void VULKAN_EndRenderPass(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
Uint32 i;
|
|
|
|
renderer->vkCmdEndRenderPass(
|
|
vulkanCommandBuffer->commandBuffer);
|
|
|
|
for (i = 0; i < vulkanCommandBuffer->colorAttachmentSubresourceCount; i += 1) {
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT,
|
|
vulkanCommandBuffer->colorAttachmentSubresources[i]);
|
|
}
|
|
vulkanCommandBuffer->colorAttachmentSubresourceCount = 0;
|
|
|
|
if (vulkanCommandBuffer->depthStencilAttachmentSubresource != NULL) {
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT,
|
|
vulkanCommandBuffer->depthStencilAttachmentSubresource);
|
|
vulkanCommandBuffer->depthStencilAttachmentSubresource = NULL;
|
|
}
|
|
|
|
vulkanCommandBuffer->currentGraphicsPipeline = NULL;
|
|
|
|
vulkanCommandBuffer->vertexResourceDescriptorSet = VK_NULL_HANDLE;
|
|
vulkanCommandBuffer->vertexUniformDescriptorSet = VK_NULL_HANDLE;
|
|
vulkanCommandBuffer->fragmentResourceDescriptorSet = VK_NULL_HANDLE;
|
|
vulkanCommandBuffer->fragmentUniformDescriptorSet = VK_NULL_HANDLE;
|
|
|
|
// Reset bind state
|
|
SDL_zeroa(vulkanCommandBuffer->colorAttachmentSubresources);
|
|
vulkanCommandBuffer->depthStencilAttachmentSubresource = NULL;
|
|
|
|
SDL_zeroa(vulkanCommandBuffer->vertexSamplers);
|
|
SDL_zeroa(vulkanCommandBuffer->vertexSamplerTextures);
|
|
SDL_zeroa(vulkanCommandBuffer->vertexStorageTextures);
|
|
SDL_zeroa(vulkanCommandBuffer->vertexStorageBuffers);
|
|
|
|
SDL_zeroa(vulkanCommandBuffer->fragmentSamplers);
|
|
SDL_zeroa(vulkanCommandBuffer->fragmentSamplerTextures);
|
|
SDL_zeroa(vulkanCommandBuffer->fragmentStorageTextures);
|
|
SDL_zeroa(vulkanCommandBuffer->fragmentStorageBuffers);
|
|
}
|
|
|
|
static void VULKAN_BeginComputePass(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUStorageTextureWriteOnlyBinding *storageTextureBindings,
|
|
Uint32 storageTextureBindingCount,
|
|
SDL_GPUStorageBufferWriteOnlyBinding *storageBufferBindings,
|
|
Uint32 storageBufferBindingCount)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
|
|
VulkanBufferContainer *bufferContainer;
|
|
VulkanBuffer *buffer;
|
|
Uint32 i;
|
|
|
|
vulkanCommandBuffer->writeOnlyComputeStorageTextureSubresourceCount = storageTextureBindingCount;
|
|
|
|
for (i = 0; i < storageTextureBindingCount; i += 1) {
|
|
VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextureBindings[i].texture;
|
|
if (!(textureContainer->activeTextureHandle->vulkanTexture->usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Attempted to bind read-only texture as compute write texture");
|
|
}
|
|
|
|
VulkanTextureSubresource *subresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
textureContainer,
|
|
storageTextureBindings[i].layer,
|
|
storageTextureBindings[i].mipLevel,
|
|
storageTextureBindings[i].cycle,
|
|
VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE);
|
|
|
|
vulkanCommandBuffer->writeOnlyComputeStorageTextureSubresources[i] = subresource;
|
|
|
|
VULKAN_INTERNAL_TrackTexture(
|
|
vulkanCommandBuffer,
|
|
subresource->parent);
|
|
}
|
|
|
|
for (i = 0; i < storageBufferBindingCount; i += 1) {
|
|
bufferContainer = (VulkanBufferContainer *)storageBufferBindings[i].buffer;
|
|
buffer = VULKAN_INTERNAL_PrepareBufferForWrite(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
bufferContainer,
|
|
storageBufferBindings[i].cycle,
|
|
VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ);
|
|
|
|
vulkanCommandBuffer->writeOnlyComputeStorageBuffers[i] = buffer;
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(
|
|
vulkanCommandBuffer,
|
|
buffer);
|
|
}
|
|
}
|
|
|
|
static void VULKAN_BindComputePipeline(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUComputePipeline *computePipeline)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VulkanComputePipeline *vulkanComputePipeline = (VulkanComputePipeline *)computePipeline;
|
|
|
|
renderer->vkCmdBindPipeline(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
VK_PIPELINE_BIND_POINT_COMPUTE,
|
|
vulkanComputePipeline->pipeline);
|
|
|
|
vulkanCommandBuffer->currentComputePipeline = vulkanComputePipeline;
|
|
|
|
VULKAN_INTERNAL_TrackComputePipeline(vulkanCommandBuffer, vulkanComputePipeline);
|
|
|
|
// Acquire uniform buffers if necessary
|
|
for (Uint32 i = 0; i < vulkanComputePipeline->resourceLayout.uniformBufferCount; i += 1) {
|
|
if (vulkanCommandBuffer->computeUniformBuffers[i] == NULL) {
|
|
vulkanCommandBuffer->computeUniformBuffers[i] = VULKAN_INTERNAL_AcquireUniformBufferFromPool(
|
|
vulkanCommandBuffer);
|
|
}
|
|
}
|
|
|
|
// Mark binding as needed
|
|
vulkanCommandBuffer->needNewComputeWriteOnlyDescriptorSet = true;
|
|
vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
|
|
vulkanCommandBuffer->needNewComputeUniformDescriptorSet = true;
|
|
vulkanCommandBuffer->needNewComputeUniformOffsets = true;
|
|
}
|
|
|
|
static void VULKAN_BindComputeStorageTextures(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUTexture **storageTextures,
|
|
Uint32 bindingCount)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
|
|
|
|
for (Uint32 i = 0; i < bindingCount; i += 1) {
|
|
if (vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i] != NULL) {
|
|
VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
|
|
vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i]);
|
|
}
|
|
|
|
VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
|
|
|
|
vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i] =
|
|
textureContainer->activeTextureHandle->vulkanTexture;
|
|
|
|
VULKAN_INTERNAL_TextureTransitionFromDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
|
|
textureContainer->activeTextureHandle->vulkanTexture);
|
|
|
|
VULKAN_INTERNAL_TrackTexture(
|
|
vulkanCommandBuffer,
|
|
textureContainer->activeTextureHandle->vulkanTexture);
|
|
}
|
|
|
|
vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
|
|
}
|
|
|
|
static void VULKAN_BindComputeStorageBuffers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUBuffer **storageBuffers,
|
|
Uint32 bindingCount)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
|
|
VulkanBufferContainer *bufferContainer;
|
|
Uint32 i;
|
|
|
|
for (i = 0; i < bindingCount; i += 1) {
|
|
if (vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] != NULL) {
|
|
VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ,
|
|
vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i]);
|
|
}
|
|
|
|
bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
|
|
|
|
vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] = bufferContainer->activeBufferHandle->vulkanBuffer;
|
|
|
|
VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ,
|
|
bufferContainer->activeBufferHandle->vulkanBuffer);
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(
|
|
vulkanCommandBuffer,
|
|
bufferContainer->activeBufferHandle->vulkanBuffer);
|
|
}
|
|
|
|
vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
|
|
}
|
|
|
|
static void VULKAN_PushComputeUniformData(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 slotIndex,
|
|
const void *data,
|
|
Uint32 dataLengthInBytes)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
|
|
VULKAN_INTERNAL_PushUniformData(
|
|
vulkanCommandBuffer,
|
|
VULKAN_UNIFORM_BUFFER_STAGE_COMPUTE,
|
|
slotIndex,
|
|
data,
|
|
dataLengthInBytes);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_BindComputeDescriptorSets(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer)
|
|
{
|
|
VulkanComputePipelineResourceLayout *resourceLayout;
|
|
VkWriteDescriptorSet *writeDescriptorSets;
|
|
VkWriteDescriptorSet *currentWriteDescriptorSet;
|
|
DescriptorSetPool *descriptorSetPool;
|
|
VkDescriptorBufferInfo bufferInfos[MAX_STORAGE_BUFFERS_PER_STAGE]; // 8 is max for both read and write
|
|
VkDescriptorImageInfo imageInfos[MAX_STORAGE_TEXTURES_PER_STAGE]; // 8 is max for both read and write
|
|
Uint32 dynamicOffsets[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
Uint32 bufferInfoCount = 0;
|
|
Uint32 imageInfoCount = 0;
|
|
Uint32 i;
|
|
|
|
resourceLayout = &commandBuffer->currentComputePipeline->resourceLayout;
|
|
|
|
if (commandBuffer->needNewComputeReadOnlyDescriptorSet) {
|
|
descriptorSetPool = &resourceLayout->descriptorSetPools[0];
|
|
|
|
commandBuffer->computeReadOnlyDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
|
|
renderer,
|
|
commandBuffer,
|
|
descriptorSetPool);
|
|
|
|
writeDescriptorSets = SDL_stack_alloc(
|
|
VkWriteDescriptorSet,
|
|
resourceLayout->readOnlyStorageTextureCount +
|
|
resourceLayout->readOnlyStorageBufferCount);
|
|
|
|
for (i = 0; i < resourceLayout->readOnlyStorageTextureCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[i];
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->computeReadOnlyDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pBufferInfo = NULL;
|
|
|
|
imageInfos[imageInfoCount].sampler = VK_NULL_HANDLE;
|
|
imageInfos[imageInfoCount].imageView = commandBuffer->readOnlyComputeStorageTextures[i]->fullView;
|
|
imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
|
|
|
|
imageInfoCount += 1;
|
|
}
|
|
|
|
for (i = 0; i < resourceLayout->readOnlyStorageBufferCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[resourceLayout->readOnlyStorageTextureCount + i];
|
|
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = resourceLayout->readOnlyStorageTextureCount + i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->computeReadOnlyDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pImageInfo = NULL;
|
|
|
|
bufferInfos[bufferInfoCount].buffer = commandBuffer->readOnlyComputeStorageBuffers[i]->buffer;
|
|
bufferInfos[bufferInfoCount].offset = 0;
|
|
bufferInfos[bufferInfoCount].range = VK_WHOLE_SIZE;
|
|
|
|
currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
|
|
|
|
bufferInfoCount += 1;
|
|
}
|
|
|
|
renderer->vkUpdateDescriptorSets(
|
|
renderer->logicalDevice,
|
|
resourceLayout->readOnlyStorageTextureCount + resourceLayout->readOnlyStorageBufferCount,
|
|
writeDescriptorSets,
|
|
0,
|
|
NULL);
|
|
|
|
renderer->vkCmdBindDescriptorSets(
|
|
commandBuffer->commandBuffer,
|
|
VK_PIPELINE_BIND_POINT_COMPUTE,
|
|
resourceLayout->pipelineLayout,
|
|
0,
|
|
1,
|
|
&commandBuffer->computeReadOnlyDescriptorSet,
|
|
0,
|
|
NULL);
|
|
|
|
SDL_stack_free(writeDescriptorSets);
|
|
bufferInfoCount = 0;
|
|
imageInfoCount = 0;
|
|
|
|
commandBuffer->needNewComputeReadOnlyDescriptorSet = false;
|
|
}
|
|
|
|
if (commandBuffer->needNewComputeWriteOnlyDescriptorSet) {
|
|
descriptorSetPool = &resourceLayout->descriptorSetPools[1];
|
|
|
|
commandBuffer->computeWriteOnlyDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
|
|
renderer,
|
|
commandBuffer,
|
|
descriptorSetPool);
|
|
|
|
writeDescriptorSets = SDL_stack_alloc(
|
|
VkWriteDescriptorSet,
|
|
resourceLayout->writeOnlyStorageTextureCount +
|
|
resourceLayout->writeOnlyStorageBufferCount);
|
|
|
|
for (i = 0; i < resourceLayout->writeOnlyStorageTextureCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[i];
|
|
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->computeWriteOnlyDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pBufferInfo = NULL;
|
|
|
|
imageInfos[imageInfoCount].sampler = VK_NULL_HANDLE;
|
|
imageInfos[imageInfoCount].imageView = commandBuffer->writeOnlyComputeStorageTextureSubresources[i]->computeWriteView;
|
|
imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
|
|
|
|
imageInfoCount += 1;
|
|
}
|
|
|
|
for (i = 0; i < resourceLayout->writeOnlyStorageBufferCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[resourceLayout->writeOnlyStorageTextureCount + i];
|
|
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = resourceLayout->writeOnlyStorageTextureCount + i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->computeWriteOnlyDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pImageInfo = NULL;
|
|
|
|
bufferInfos[bufferInfoCount].buffer = commandBuffer->writeOnlyComputeStorageBuffers[i]->buffer;
|
|
bufferInfos[bufferInfoCount].offset = 0;
|
|
bufferInfos[bufferInfoCount].range = VK_WHOLE_SIZE;
|
|
|
|
currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
|
|
|
|
bufferInfoCount += 1;
|
|
}
|
|
|
|
renderer->vkUpdateDescriptorSets(
|
|
renderer->logicalDevice,
|
|
resourceLayout->writeOnlyStorageTextureCount + resourceLayout->writeOnlyStorageBufferCount,
|
|
writeDescriptorSets,
|
|
0,
|
|
NULL);
|
|
|
|
renderer->vkCmdBindDescriptorSets(
|
|
commandBuffer->commandBuffer,
|
|
VK_PIPELINE_BIND_POINT_COMPUTE,
|
|
resourceLayout->pipelineLayout,
|
|
1,
|
|
1,
|
|
&commandBuffer->computeWriteOnlyDescriptorSet,
|
|
0,
|
|
NULL);
|
|
|
|
SDL_stack_free(writeDescriptorSets);
|
|
bufferInfoCount = 0;
|
|
imageInfoCount = 0;
|
|
|
|
commandBuffer->needNewComputeWriteOnlyDescriptorSet = false;
|
|
}
|
|
|
|
if (commandBuffer->needNewComputeUniformDescriptorSet) {
|
|
descriptorSetPool = &resourceLayout->descriptorSetPools[2];
|
|
|
|
commandBuffer->computeUniformDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
|
|
renderer,
|
|
commandBuffer,
|
|
descriptorSetPool);
|
|
|
|
writeDescriptorSets = SDL_stack_alloc(
|
|
VkWriteDescriptorSet,
|
|
resourceLayout->uniformBufferCount);
|
|
|
|
for (i = 0; i < resourceLayout->uniformBufferCount; i += 1) {
|
|
currentWriteDescriptorSet = &writeDescriptorSets[i];
|
|
|
|
currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
currentWriteDescriptorSet->pNext = NULL;
|
|
currentWriteDescriptorSet->descriptorCount = 1;
|
|
currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
currentWriteDescriptorSet->dstArrayElement = 0;
|
|
currentWriteDescriptorSet->dstBinding = i;
|
|
currentWriteDescriptorSet->dstSet = commandBuffer->computeUniformDescriptorSet;
|
|
currentWriteDescriptorSet->pTexelBufferView = NULL;
|
|
currentWriteDescriptorSet->pImageInfo = NULL;
|
|
|
|
bufferInfos[bufferInfoCount].buffer = commandBuffer->computeUniformBuffers[i]->bufferHandle->vulkanBuffer->buffer;
|
|
bufferInfos[bufferInfoCount].offset = 0;
|
|
bufferInfos[bufferInfoCount].range = MAX_UBO_SECTION_SIZE;
|
|
|
|
currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
|
|
|
|
bufferInfoCount += 1;
|
|
}
|
|
|
|
renderer->vkUpdateDescriptorSets(
|
|
renderer->logicalDevice,
|
|
resourceLayout->uniformBufferCount,
|
|
writeDescriptorSets,
|
|
0,
|
|
NULL);
|
|
|
|
SDL_stack_free(writeDescriptorSets);
|
|
bufferInfoCount = 0;
|
|
imageInfoCount = 0;
|
|
|
|
commandBuffer->needNewComputeUniformDescriptorSet = false;
|
|
commandBuffer->needNewComputeUniformOffsets = true;
|
|
}
|
|
|
|
if (commandBuffer->needNewComputeUniformOffsets) {
|
|
for (i = 0; i < resourceLayout->uniformBufferCount; i += 1) {
|
|
dynamicOffsets[i] = commandBuffer->computeUniformBuffers[i]->drawOffset;
|
|
}
|
|
|
|
renderer->vkCmdBindDescriptorSets(
|
|
commandBuffer->commandBuffer,
|
|
VK_PIPELINE_BIND_POINT_COMPUTE,
|
|
resourceLayout->pipelineLayout,
|
|
2,
|
|
1,
|
|
&commandBuffer->computeUniformDescriptorSet,
|
|
resourceLayout->uniformBufferCount,
|
|
dynamicOffsets);
|
|
|
|
commandBuffer->needNewComputeUniformOffsets = false;
|
|
}
|
|
}
|
|
|
|
static void VULKAN_DispatchCompute(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 groupCountX,
|
|
Uint32 groupCountY,
|
|
Uint32 groupCountZ)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
|
|
VULKAN_INTERNAL_BindComputeDescriptorSets(renderer, vulkanCommandBuffer);
|
|
|
|
renderer->vkCmdDispatch(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
groupCountX,
|
|
groupCountY,
|
|
groupCountZ);
|
|
}
|
|
|
|
static void VULKAN_DispatchComputeIndirect(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUBuffer *buffer,
|
|
Uint32 offsetInBytes)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)buffer)->activeBufferHandle->vulkanBuffer;
|
|
|
|
VULKAN_INTERNAL_BindComputeDescriptorSets(renderer, vulkanCommandBuffer);
|
|
|
|
renderer->vkCmdDispatchIndirect(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
vulkanBuffer->buffer,
|
|
offsetInBytes);
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
|
|
}
|
|
|
|
static void VULKAN_EndComputePass(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
Uint32 i;
|
|
|
|
for (i = 0; i < vulkanCommandBuffer->writeOnlyComputeStorageTextureSubresourceCount; i += 1) {
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
vulkanCommandBuffer->renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE,
|
|
vulkanCommandBuffer->writeOnlyComputeStorageTextureSubresources[i]);
|
|
vulkanCommandBuffer->writeOnlyComputeStorageTextureSubresources[i] = NULL;
|
|
}
|
|
vulkanCommandBuffer->writeOnlyComputeStorageTextureSubresourceCount = 0;
|
|
|
|
for (i = 0; i < MAX_COMPUTE_WRITE_BUFFERS; i += 1) {
|
|
if (vulkanCommandBuffer->writeOnlyComputeStorageBuffers[i] != NULL) {
|
|
VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
|
|
vulkanCommandBuffer->renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE,
|
|
vulkanCommandBuffer->writeOnlyComputeStorageBuffers[i]);
|
|
|
|
vulkanCommandBuffer->writeOnlyComputeStorageBuffers[i] = NULL;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_STORAGE_TEXTURES_PER_STAGE; i += 1) {
|
|
if (vulkanCommandBuffer->readOnlyComputeStorageTextures[i] != NULL) {
|
|
VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
|
|
vulkanCommandBuffer->renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
|
|
vulkanCommandBuffer->readOnlyComputeStorageTextures[i]);
|
|
|
|
vulkanCommandBuffer->readOnlyComputeStorageTextures[i] = NULL;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_STORAGE_BUFFERS_PER_STAGE; i += 1) {
|
|
if (vulkanCommandBuffer->readOnlyComputeStorageBuffers[i] != NULL) {
|
|
VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
|
|
vulkanCommandBuffer->renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ,
|
|
vulkanCommandBuffer->readOnlyComputeStorageBuffers[i]);
|
|
|
|
vulkanCommandBuffer->readOnlyComputeStorageBuffers[i] = NULL;
|
|
}
|
|
}
|
|
|
|
vulkanCommandBuffer->currentComputePipeline = NULL;
|
|
|
|
vulkanCommandBuffer->computeReadOnlyDescriptorSet = VK_NULL_HANDLE;
|
|
vulkanCommandBuffer->computeWriteOnlyDescriptorSet = VK_NULL_HANDLE;
|
|
vulkanCommandBuffer->computeUniformDescriptorSet = VK_NULL_HANDLE;
|
|
}
|
|
|
|
static void *VULKAN_MapTransferBuffer(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTransferBuffer *transferBuffer,
|
|
bool cycle)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)transferBuffer;
|
|
|
|
if (
|
|
cycle &&
|
|
SDL_AtomicGet(&transferBufferContainer->activeBufferHandle->vulkanBuffer->referenceCount) > 0) {
|
|
VULKAN_INTERNAL_CycleActiveBuffer(
|
|
renderer,
|
|
transferBufferContainer);
|
|
}
|
|
|
|
Uint8 *bufferPointer =
|
|
transferBufferContainer->activeBufferHandle->vulkanBuffer->usedRegion->allocation->mapPointer +
|
|
transferBufferContainer->activeBufferHandle->vulkanBuffer->usedRegion->resourceOffset;
|
|
|
|
return bufferPointer;
|
|
}
|
|
|
|
static void VULKAN_UnmapTransferBuffer(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTransferBuffer *transferBuffer)
|
|
{
|
|
// no-op because transfer buffers are persistently mapped
|
|
(void)driverData;
|
|
(void)transferBuffer;
|
|
}
|
|
|
|
static void VULKAN_BeginCopyPass(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
// no-op
|
|
(void)commandBuffer;
|
|
}
|
|
|
|
static void VULKAN_UploadToTexture(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUTextureTransferInfo *source,
|
|
SDL_GPUTextureRegion *destination,
|
|
bool cycle)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)source->transferBuffer;
|
|
VulkanTextureContainer *vulkanTextureContainer = (VulkanTextureContainer *)destination->texture;
|
|
VulkanTextureSubresource *vulkanTextureSubresource;
|
|
VkBufferImageCopy imageCopy;
|
|
|
|
// Note that the transfer buffer does not need a barrier, as it is synced by the client
|
|
|
|
vulkanTextureSubresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
vulkanTextureContainer,
|
|
destination->layer,
|
|
destination->mipLevel,
|
|
cycle,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION);
|
|
|
|
imageCopy.imageExtent.width = destination->w;
|
|
imageCopy.imageExtent.height = destination->h;
|
|
imageCopy.imageExtent.depth = destination->d;
|
|
imageCopy.imageOffset.x = destination->x;
|
|
imageCopy.imageOffset.y = destination->y;
|
|
imageCopy.imageOffset.z = destination->z;
|
|
imageCopy.imageSubresource.aspectMask = vulkanTextureSubresource->parent->aspectFlags;
|
|
imageCopy.imageSubresource.baseArrayLayer = destination->layer;
|
|
imageCopy.imageSubresource.layerCount = 1;
|
|
imageCopy.imageSubresource.mipLevel = destination->mipLevel;
|
|
imageCopy.bufferOffset = source->offset;
|
|
imageCopy.bufferRowLength = source->imagePitch;
|
|
imageCopy.bufferImageHeight = source->imageHeight;
|
|
|
|
renderer->vkCmdCopyBufferToImage(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer,
|
|
vulkanTextureSubresource->parent->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
1,
|
|
&imageCopy);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
|
|
vulkanTextureSubresource);
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer);
|
|
VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, vulkanTextureSubresource->parent);
|
|
}
|
|
|
|
static void VULKAN_UploadToBuffer(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUTransferBufferLocation *source,
|
|
SDL_GPUBufferRegion *destination,
|
|
bool cycle)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)source->transferBuffer;
|
|
VulkanBufferContainer *bufferContainer = (VulkanBufferContainer *)destination->buffer;
|
|
VkBufferCopy bufferCopy;
|
|
|
|
// Note that the transfer buffer does not need a barrier, as it is synced by the client
|
|
|
|
VulkanBuffer *vulkanBuffer = VULKAN_INTERNAL_PrepareBufferForWrite(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
bufferContainer,
|
|
cycle,
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION);
|
|
|
|
bufferCopy.srcOffset = source->offset;
|
|
bufferCopy.dstOffset = destination->offset;
|
|
bufferCopy.size = destination->size;
|
|
|
|
renderer->vkCmdCopyBuffer(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer,
|
|
vulkanBuffer->buffer,
|
|
1,
|
|
&bufferCopy);
|
|
|
|
VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
|
|
vulkanBuffer);
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer);
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
|
|
}
|
|
|
|
// Readback
|
|
|
|
static void VULKAN_DownloadFromTexture(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUTextureRegion *source,
|
|
SDL_GPUTextureTransferInfo *destination)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
|
|
VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)source->texture;
|
|
VulkanTextureSubresource *vulkanTextureSubresource;
|
|
VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)destination->transferBuffer;
|
|
VkBufferImageCopy imageCopy;
|
|
vulkanTextureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
|
|
textureContainer,
|
|
source->layer,
|
|
source->mipLevel);
|
|
|
|
// Note that the transfer buffer does not need a barrier, as it is synced by the client
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
|
|
vulkanTextureSubresource);
|
|
|
|
imageCopy.imageExtent.width = source->w;
|
|
imageCopy.imageExtent.height = source->h;
|
|
imageCopy.imageExtent.depth = source->d;
|
|
imageCopy.imageOffset.x = source->x;
|
|
imageCopy.imageOffset.y = source->y;
|
|
imageCopy.imageOffset.z = source->z;
|
|
imageCopy.imageSubresource.aspectMask = vulkanTextureSubresource->parent->aspectFlags;
|
|
imageCopy.imageSubresource.baseArrayLayer = source->layer;
|
|
imageCopy.imageSubresource.layerCount = 1;
|
|
imageCopy.imageSubresource.mipLevel = source->mipLevel;
|
|
imageCopy.bufferOffset = destination->offset;
|
|
imageCopy.bufferRowLength = destination->imagePitch;
|
|
imageCopy.bufferImageHeight = destination->imageHeight;
|
|
|
|
renderer->vkCmdCopyImageToBuffer(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
vulkanTextureSubresource->parent->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer,
|
|
1,
|
|
&imageCopy);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
|
|
vulkanTextureSubresource);
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer);
|
|
VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, vulkanTextureSubresource->parent);
|
|
}
|
|
|
|
static void VULKAN_DownloadFromBuffer(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUBufferRegion *source,
|
|
SDL_GPUTransferBufferLocation *destination)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
|
|
VulkanBufferContainer *bufferContainer = (VulkanBufferContainer *)source->buffer;
|
|
VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)destination->transferBuffer;
|
|
VkBufferCopy bufferCopy;
|
|
|
|
// Note that transfer buffer does not need a barrier, as it is synced by the client
|
|
|
|
VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
|
|
bufferContainer->activeBufferHandle->vulkanBuffer);
|
|
|
|
bufferCopy.srcOffset = source->offset;
|
|
bufferCopy.dstOffset = destination->offset;
|
|
bufferCopy.size = source->size;
|
|
|
|
renderer->vkCmdCopyBuffer(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
bufferContainer->activeBufferHandle->vulkanBuffer->buffer,
|
|
transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer,
|
|
1,
|
|
&bufferCopy);
|
|
|
|
VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
|
|
bufferContainer->activeBufferHandle->vulkanBuffer);
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer);
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, bufferContainer->activeBufferHandle->vulkanBuffer);
|
|
}
|
|
|
|
static void VULKAN_CopyTextureToTexture(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUTextureLocation *source,
|
|
SDL_GPUTextureLocation *destination,
|
|
Uint32 w,
|
|
Uint32 h,
|
|
Uint32 d,
|
|
bool cycle)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VulkanTextureSubresource *srcSubresource;
|
|
VulkanTextureSubresource *dstSubresource;
|
|
VkImageCopy imageCopy;
|
|
|
|
srcSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
|
|
(VulkanTextureContainer *)source->texture,
|
|
source->layer,
|
|
source->mipLevel);
|
|
|
|
dstSubresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
(VulkanTextureContainer *)destination->texture,
|
|
destination->layer,
|
|
destination->mipLevel,
|
|
cycle,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
|
|
srcSubresource);
|
|
|
|
imageCopy.srcOffset.x = source->x;
|
|
imageCopy.srcOffset.y = source->y;
|
|
imageCopy.srcOffset.z = source->z;
|
|
imageCopy.srcSubresource.aspectMask = srcSubresource->parent->aspectFlags;
|
|
imageCopy.srcSubresource.baseArrayLayer = source->layer;
|
|
imageCopy.srcSubresource.layerCount = 1;
|
|
imageCopy.srcSubresource.mipLevel = source->mipLevel;
|
|
imageCopy.dstOffset.x = destination->x;
|
|
imageCopy.dstOffset.y = destination->y;
|
|
imageCopy.dstOffset.z = destination->z;
|
|
imageCopy.dstSubresource.aspectMask = dstSubresource->parent->aspectFlags;
|
|
imageCopy.dstSubresource.baseArrayLayer = destination->layer;
|
|
imageCopy.dstSubresource.layerCount = 1;
|
|
imageCopy.dstSubresource.mipLevel = destination->mipLevel;
|
|
imageCopy.extent.width = w;
|
|
imageCopy.extent.height = h;
|
|
imageCopy.extent.depth = d;
|
|
|
|
renderer->vkCmdCopyImage(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
srcSubresource->parent->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
dstSubresource->parent->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
1,
|
|
&imageCopy);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
|
|
srcSubresource);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
|
|
dstSubresource);
|
|
|
|
VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, srcSubresource->parent);
|
|
VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, dstSubresource->parent);
|
|
}
|
|
|
|
static void VULKAN_CopyBufferToBuffer(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUBufferLocation *source,
|
|
SDL_GPUBufferLocation *destination,
|
|
Uint32 size,
|
|
bool cycle)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VulkanBufferContainer *srcContainer = (VulkanBufferContainer *)source->buffer;
|
|
VulkanBufferContainer *dstContainer = (VulkanBufferContainer *)destination->buffer;
|
|
VkBufferCopy bufferCopy;
|
|
|
|
VulkanBuffer *dstBuffer = VULKAN_INTERNAL_PrepareBufferForWrite(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
dstContainer,
|
|
cycle,
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION);
|
|
|
|
VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
|
|
srcContainer->activeBufferHandle->vulkanBuffer);
|
|
|
|
bufferCopy.srcOffset = source->offset;
|
|
bufferCopy.dstOffset = destination->offset;
|
|
bufferCopy.size = size;
|
|
|
|
renderer->vkCmdCopyBuffer(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
srcContainer->activeBufferHandle->vulkanBuffer->buffer,
|
|
dstBuffer->buffer,
|
|
1,
|
|
&bufferCopy);
|
|
|
|
VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
|
|
srcContainer->activeBufferHandle->vulkanBuffer);
|
|
|
|
VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
|
|
dstBuffer);
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, srcContainer->activeBufferHandle->vulkanBuffer);
|
|
VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, dstBuffer);
|
|
}
|
|
|
|
static void VULKAN_GenerateMipmaps(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUTexture *texture)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VulkanTexture *vulkanTexture = ((VulkanTextureContainer *)texture)->activeTextureHandle->vulkanTexture;
|
|
VulkanTextureSubresource *srcTextureSubresource;
|
|
VulkanTextureSubresource *dstTextureSubresource;
|
|
VkImageBlit blit;
|
|
|
|
// Blit each slice sequentially. Barriers, barriers everywhere!
|
|
for (Uint32 layerOrDepthIndex = 0; layerOrDepthIndex < vulkanTexture->layerCount; layerOrDepthIndex += 1)
|
|
for (Uint32 level = 1; level < vulkanTexture->levelCount; level += 1) {
|
|
Uint32 layer = vulkanTexture->type == SDL_GPU_TEXTURETYPE_3D ? 0 : layerOrDepthIndex;
|
|
Uint32 depth = vulkanTexture->type == SDL_GPU_TEXTURETYPE_3D ? layerOrDepthIndex : 0;
|
|
|
|
Uint32 srcSubresourceIndex = VULKAN_INTERNAL_GetTextureSubresourceIndex(
|
|
level - 1,
|
|
layer,
|
|
vulkanTexture->levelCount);
|
|
Uint32 dstSubresourceIndex = VULKAN_INTERNAL_GetTextureSubresourceIndex(
|
|
level,
|
|
layer,
|
|
vulkanTexture->levelCount);
|
|
|
|
srcTextureSubresource = &vulkanTexture->subresources[srcSubresourceIndex];
|
|
dstTextureSubresource = &vulkanTexture->subresources[dstSubresourceIndex];
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
|
|
srcTextureSubresource);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
|
|
dstTextureSubresource);
|
|
|
|
blit.srcOffsets[0].x = 0;
|
|
blit.srcOffsets[0].y = 0;
|
|
blit.srcOffsets[0].z = depth;
|
|
|
|
blit.srcOffsets[1].x = vulkanTexture->dimensions.width >> (level - 1);
|
|
blit.srcOffsets[1].y = vulkanTexture->dimensions.height >> (level - 1);
|
|
blit.srcOffsets[1].z = depth + 1;
|
|
|
|
blit.dstOffsets[0].x = 0;
|
|
blit.dstOffsets[0].y = 0;
|
|
blit.dstOffsets[0].z = depth;
|
|
|
|
blit.dstOffsets[1].x = vulkanTexture->dimensions.width >> level;
|
|
blit.dstOffsets[1].y = vulkanTexture->dimensions.height >> level;
|
|
blit.dstOffsets[1].z = depth + 1;
|
|
|
|
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
blit.srcSubresource.baseArrayLayer = layer;
|
|
blit.srcSubresource.layerCount = 1;
|
|
blit.srcSubresource.mipLevel = level - 1;
|
|
|
|
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
blit.dstSubresource.baseArrayLayer = layer;
|
|
blit.dstSubresource.layerCount = 1;
|
|
blit.dstSubresource.mipLevel = level;
|
|
|
|
renderer->vkCmdBlitImage(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
vulkanTexture->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
vulkanTexture->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
1,
|
|
&blit,
|
|
VK_FILTER_LINEAR);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
|
|
srcTextureSubresource);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
|
|
dstTextureSubresource);
|
|
|
|
VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, srcTextureSubresource->parent);
|
|
VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, dstTextureSubresource->parent);
|
|
}
|
|
}
|
|
|
|
static void VULKAN_EndCopyPass(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
// no-op
|
|
(void)commandBuffer;
|
|
}
|
|
|
|
static void VULKAN_Blit(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUBlitRegion *source,
|
|
SDL_GPUBlitRegion *destination,
|
|
SDL_FlipMode flipMode,
|
|
SDL_GPUFilter filterMode,
|
|
bool cycle)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
TextureCommonHeader *srcHeader = (TextureCommonHeader *)source->texture;
|
|
TextureCommonHeader *dstHeader = (TextureCommonHeader *)destination->texture;
|
|
VkImageBlit region;
|
|
Uint32 srcLayer = srcHeader->info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : source->layerOrDepthPlane;
|
|
Uint32 srcDepth = srcHeader->info.type == SDL_GPU_TEXTURETYPE_3D ? source->layerOrDepthPlane : 0;
|
|
Uint32 dstLayer = dstHeader->info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : destination->layerOrDepthPlane;
|
|
Uint32 dstDepth = dstHeader->info.type == SDL_GPU_TEXTURETYPE_3D ? destination->layerOrDepthPlane : 0;
|
|
int32_t swap;
|
|
|
|
VulkanTextureSubresource *srcSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
|
|
(VulkanTextureContainer *)source->texture,
|
|
srcLayer,
|
|
source->mipLevel);
|
|
|
|
VulkanTextureSubresource *dstSubresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
(VulkanTextureContainer *)destination->texture,
|
|
dstLayer,
|
|
destination->mipLevel,
|
|
cycle,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
|
|
srcSubresource);
|
|
|
|
region.srcSubresource.aspectMask = srcSubresource->parent->aspectFlags;
|
|
region.srcSubresource.baseArrayLayer = srcSubresource->layer;
|
|
region.srcSubresource.layerCount = 1;
|
|
region.srcSubresource.mipLevel = srcSubresource->level;
|
|
region.srcOffsets[0].x = source->x;
|
|
region.srcOffsets[0].y = source->y;
|
|
region.srcOffsets[0].z = srcDepth;
|
|
region.srcOffsets[1].x = source->x + source->w;
|
|
region.srcOffsets[1].y = source->y + source->h;
|
|
region.srcOffsets[1].z = srcDepth + 1;
|
|
|
|
if (flipMode & SDL_FLIP_HORIZONTAL) {
|
|
// flip the x positions
|
|
swap = region.srcOffsets[0].x;
|
|
region.srcOffsets[0].x = region.srcOffsets[1].x;
|
|
region.srcOffsets[1].x = swap;
|
|
}
|
|
|
|
if (flipMode & SDL_FLIP_VERTICAL) {
|
|
// flip the y positions
|
|
swap = region.srcOffsets[0].y;
|
|
region.srcOffsets[0].y = region.srcOffsets[1].y;
|
|
region.srcOffsets[1].y = swap;
|
|
}
|
|
|
|
region.dstSubresource.aspectMask = dstSubresource->parent->aspectFlags;
|
|
region.dstSubresource.baseArrayLayer = dstSubresource->layer;
|
|
region.dstSubresource.layerCount = 1;
|
|
region.dstSubresource.mipLevel = dstSubresource->level;
|
|
region.dstOffsets[0].x = destination->x;
|
|
region.dstOffsets[0].y = destination->y;
|
|
region.dstOffsets[0].z = dstDepth;
|
|
region.dstOffsets[1].x = destination->x + destination->w;
|
|
region.dstOffsets[1].y = destination->y + destination->h;
|
|
region.dstOffsets[1].z = dstDepth + 1;
|
|
|
|
renderer->vkCmdBlitImage(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
srcSubresource->parent->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
dstSubresource->parent->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
1,
|
|
®ion,
|
|
SDLToVK_Filter[filterMode]);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
|
|
srcSubresource);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
|
|
dstSubresource);
|
|
|
|
VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, srcSubresource->parent);
|
|
VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, dstSubresource->parent);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_AllocateCommandBuffers(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandPool *vulkanCommandPool,
|
|
Uint32 allocateCount)
|
|
{
|
|
VkCommandBufferAllocateInfo allocateInfo;
|
|
VkResult vulkanResult;
|
|
Uint32 i;
|
|
VkCommandBuffer *commandBuffers = SDL_stack_alloc(VkCommandBuffer, allocateCount);
|
|
VulkanCommandBuffer *commandBuffer;
|
|
|
|
vulkanCommandPool->inactiveCommandBufferCapacity += allocateCount;
|
|
|
|
vulkanCommandPool->inactiveCommandBuffers = SDL_realloc(
|
|
vulkanCommandPool->inactiveCommandBuffers,
|
|
sizeof(VulkanCommandBuffer *) *
|
|
vulkanCommandPool->inactiveCommandBufferCapacity);
|
|
|
|
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
allocateInfo.pNext = NULL;
|
|
allocateInfo.commandPool = vulkanCommandPool->commandPool;
|
|
allocateInfo.commandBufferCount = allocateCount;
|
|
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
|
|
vulkanResult = renderer->vkAllocateCommandBuffers(
|
|
renderer->logicalDevice,
|
|
&allocateInfo,
|
|
commandBuffers);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkAllocateCommandBuffers", vulkanResult);
|
|
SDL_stack_free(commandBuffers);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < allocateCount; i += 1) {
|
|
commandBuffer = SDL_malloc(sizeof(VulkanCommandBuffer));
|
|
commandBuffer->renderer = renderer;
|
|
commandBuffer->commandPool = vulkanCommandPool;
|
|
commandBuffer->commandBuffer = commandBuffers[i];
|
|
|
|
commandBuffer->inFlightFence = VK_NULL_HANDLE;
|
|
|
|
// Presentation tracking
|
|
|
|
commandBuffer->presentDataCapacity = 1;
|
|
commandBuffer->presentDataCount = 0;
|
|
commandBuffer->presentDatas = SDL_malloc(
|
|
commandBuffer->presentDataCapacity * sizeof(VulkanPresentData));
|
|
|
|
commandBuffer->waitSemaphoreCapacity = 1;
|
|
commandBuffer->waitSemaphoreCount = 0;
|
|
commandBuffer->waitSemaphores = SDL_malloc(
|
|
commandBuffer->waitSemaphoreCapacity * sizeof(VkSemaphore));
|
|
|
|
commandBuffer->signalSemaphoreCapacity = 1;
|
|
commandBuffer->signalSemaphoreCount = 0;
|
|
commandBuffer->signalSemaphores = SDL_malloc(
|
|
commandBuffer->signalSemaphoreCapacity * sizeof(VkSemaphore));
|
|
|
|
// Descriptor set tracking
|
|
|
|
commandBuffer->boundDescriptorSetDataCapacity = 16;
|
|
commandBuffer->boundDescriptorSetDataCount = 0;
|
|
commandBuffer->boundDescriptorSetDatas = SDL_malloc(
|
|
commandBuffer->boundDescriptorSetDataCapacity * sizeof(DescriptorSetData));
|
|
|
|
// Resource bind tracking
|
|
|
|
commandBuffer->needNewVertexResourceDescriptorSet = true;
|
|
commandBuffer->needNewVertexUniformDescriptorSet = true;
|
|
commandBuffer->needNewVertexUniformOffsets = true;
|
|
commandBuffer->needNewFragmentResourceDescriptorSet = true;
|
|
commandBuffer->needNewFragmentUniformDescriptorSet = true;
|
|
commandBuffer->needNewFragmentUniformOffsets = true;
|
|
|
|
commandBuffer->needNewComputeWriteOnlyDescriptorSet = true;
|
|
commandBuffer->needNewComputeReadOnlyDescriptorSet = true;
|
|
commandBuffer->needNewComputeUniformDescriptorSet = true;
|
|
commandBuffer->needNewComputeUniformOffsets = true;
|
|
|
|
commandBuffer->vertexResourceDescriptorSet = VK_NULL_HANDLE;
|
|
commandBuffer->vertexUniformDescriptorSet = VK_NULL_HANDLE;
|
|
commandBuffer->fragmentResourceDescriptorSet = VK_NULL_HANDLE;
|
|
commandBuffer->fragmentUniformDescriptorSet = VK_NULL_HANDLE;
|
|
|
|
commandBuffer->computeReadOnlyDescriptorSet = VK_NULL_HANDLE;
|
|
commandBuffer->computeWriteOnlyDescriptorSet = VK_NULL_HANDLE;
|
|
commandBuffer->computeUniformDescriptorSet = VK_NULL_HANDLE;
|
|
|
|
// Resource tracking
|
|
|
|
commandBuffer->usedBufferCapacity = 4;
|
|
commandBuffer->usedBufferCount = 0;
|
|
commandBuffer->usedBuffers = SDL_malloc(
|
|
commandBuffer->usedBufferCapacity * sizeof(VulkanBuffer *));
|
|
|
|
commandBuffer->usedTextureCapacity = 4;
|
|
commandBuffer->usedTextureCount = 0;
|
|
commandBuffer->usedTextures = SDL_malloc(
|
|
commandBuffer->usedTextureCapacity * sizeof(VulkanTexture *));
|
|
|
|
commandBuffer->usedSamplerCapacity = 4;
|
|
commandBuffer->usedSamplerCount = 0;
|
|
commandBuffer->usedSamplers = SDL_malloc(
|
|
commandBuffer->usedSamplerCapacity * sizeof(VulkanSampler *));
|
|
|
|
commandBuffer->usedGraphicsPipelineCapacity = 4;
|
|
commandBuffer->usedGraphicsPipelineCount = 0;
|
|
commandBuffer->usedGraphicsPipelines = SDL_malloc(
|
|
commandBuffer->usedGraphicsPipelineCapacity * sizeof(VulkanGraphicsPipeline *));
|
|
|
|
commandBuffer->usedComputePipelineCapacity = 4;
|
|
commandBuffer->usedComputePipelineCount = 0;
|
|
commandBuffer->usedComputePipelines = SDL_malloc(
|
|
commandBuffer->usedComputePipelineCapacity * sizeof(VulkanComputePipeline *));
|
|
|
|
commandBuffer->usedFramebufferCapacity = 4;
|
|
commandBuffer->usedFramebufferCount = 0;
|
|
commandBuffer->usedFramebuffers = SDL_malloc(
|
|
commandBuffer->usedFramebufferCapacity * sizeof(VulkanFramebuffer *));
|
|
|
|
commandBuffer->usedUniformBufferCapacity = 4;
|
|
commandBuffer->usedUniformBufferCount = 0;
|
|
commandBuffer->usedUniformBuffers = SDL_malloc(
|
|
commandBuffer->usedUniformBufferCapacity * sizeof(VulkanUniformBuffer *));
|
|
|
|
// Pool it!
|
|
|
|
vulkanCommandPool->inactiveCommandBuffers[vulkanCommandPool->inactiveCommandBufferCount] = commandBuffer;
|
|
vulkanCommandPool->inactiveCommandBufferCount += 1;
|
|
}
|
|
|
|
SDL_stack_free(commandBuffers);
|
|
}
|
|
|
|
static VulkanCommandPool *VULKAN_INTERNAL_FetchCommandPool(
|
|
VulkanRenderer *renderer,
|
|
SDL_ThreadID threadID)
|
|
{
|
|
VulkanCommandPool *vulkanCommandPool = NULL;
|
|
VkCommandPoolCreateInfo commandPoolCreateInfo;
|
|
VkResult vulkanResult;
|
|
CommandPoolHashTableKey key;
|
|
key.threadID = threadID;
|
|
|
|
bool result = SDL_FindInHashTable(
|
|
renderer->commandPoolHashTable,
|
|
(const void *)&key,
|
|
(const void **)&vulkanCommandPool);
|
|
|
|
if (result) {
|
|
return vulkanCommandPool;
|
|
}
|
|
|
|
vulkanCommandPool = (VulkanCommandPool *)SDL_malloc(sizeof(VulkanCommandPool));
|
|
|
|
commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
commandPoolCreateInfo.pNext = NULL;
|
|
commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
commandPoolCreateInfo.queueFamilyIndex = renderer->queueFamilyIndex;
|
|
|
|
vulkanResult = renderer->vkCreateCommandPool(
|
|
renderer->logicalDevice,
|
|
&commandPoolCreateInfo,
|
|
NULL,
|
|
&vulkanCommandPool->commandPool);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create command pool!");
|
|
LogVulkanResultAsError("vkCreateCommandPool", vulkanResult);
|
|
return NULL;
|
|
}
|
|
|
|
vulkanCommandPool->threadID = threadID;
|
|
|
|
vulkanCommandPool->inactiveCommandBufferCapacity = 0;
|
|
vulkanCommandPool->inactiveCommandBufferCount = 0;
|
|
vulkanCommandPool->inactiveCommandBuffers = NULL;
|
|
|
|
VULKAN_INTERNAL_AllocateCommandBuffers(
|
|
renderer,
|
|
vulkanCommandPool,
|
|
2);
|
|
|
|
CommandPoolHashTableKey *allocedKey = SDL_malloc(sizeof(CommandPoolHashTableKey));
|
|
allocedKey->threadID = threadID;
|
|
|
|
SDL_InsertIntoHashTable(
|
|
renderer->commandPoolHashTable,
|
|
(const void *)allocedKey,
|
|
(const void *)vulkanCommandPool);
|
|
|
|
return vulkanCommandPool;
|
|
}
|
|
|
|
static VulkanCommandBuffer *VULKAN_INTERNAL_GetInactiveCommandBufferFromPool(
|
|
VulkanRenderer *renderer,
|
|
SDL_ThreadID threadID)
|
|
{
|
|
VulkanCommandPool *commandPool =
|
|
VULKAN_INTERNAL_FetchCommandPool(renderer, threadID);
|
|
VulkanCommandBuffer *commandBuffer;
|
|
|
|
if (commandPool == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to fetch command pool!");
|
|
return NULL;
|
|
}
|
|
|
|
if (commandPool->inactiveCommandBufferCount == 0) {
|
|
VULKAN_INTERNAL_AllocateCommandBuffers(
|
|
renderer,
|
|
commandPool,
|
|
commandPool->inactiveCommandBufferCapacity);
|
|
}
|
|
|
|
commandBuffer = commandPool->inactiveCommandBuffers[commandPool->inactiveCommandBufferCount - 1];
|
|
commandPool->inactiveCommandBufferCount -= 1;
|
|
|
|
return commandBuffer;
|
|
}
|
|
|
|
static SDL_GPUCommandBuffer *VULKAN_AcquireCommandBuffer(
|
|
SDL_GPURenderer *driverData)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VkResult result;
|
|
Uint32 i;
|
|
|
|
SDL_ThreadID threadID = SDL_GetCurrentThreadID();
|
|
|
|
SDL_LockMutex(renderer->acquireCommandBufferLock);
|
|
|
|
VulkanCommandBuffer *commandBuffer =
|
|
VULKAN_INTERNAL_GetInactiveCommandBufferFromPool(renderer, threadID);
|
|
|
|
SDL_UnlockMutex(renderer->acquireCommandBufferLock);
|
|
|
|
if (commandBuffer == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to acquire command buffer!");
|
|
return NULL;
|
|
}
|
|
|
|
// Reset state
|
|
|
|
commandBuffer->currentComputePipeline = NULL;
|
|
commandBuffer->currentGraphicsPipeline = NULL;
|
|
|
|
for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) {
|
|
commandBuffer->colorAttachmentSubresources[i] = NULL;
|
|
}
|
|
|
|
for (i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
|
|
commandBuffer->vertexUniformBuffers[i] = NULL;
|
|
commandBuffer->fragmentUniformBuffers[i] = NULL;
|
|
commandBuffer->computeUniformBuffers[i] = NULL;
|
|
}
|
|
|
|
commandBuffer->depthStencilAttachmentSubresource = NULL;
|
|
|
|
commandBuffer->needNewVertexResourceDescriptorSet = true;
|
|
commandBuffer->needNewVertexUniformDescriptorSet = true;
|
|
commandBuffer->needNewVertexUniformOffsets = true;
|
|
commandBuffer->needNewFragmentResourceDescriptorSet = true;
|
|
commandBuffer->needNewFragmentUniformDescriptorSet = true;
|
|
commandBuffer->needNewFragmentUniformOffsets = true;
|
|
|
|
commandBuffer->needNewComputeReadOnlyDescriptorSet = true;
|
|
commandBuffer->needNewComputeUniformDescriptorSet = true;
|
|
commandBuffer->needNewComputeUniformOffsets = true;
|
|
|
|
commandBuffer->vertexResourceDescriptorSet = VK_NULL_HANDLE;
|
|
commandBuffer->vertexUniformDescriptorSet = VK_NULL_HANDLE;
|
|
commandBuffer->fragmentResourceDescriptorSet = VK_NULL_HANDLE;
|
|
commandBuffer->fragmentUniformDescriptorSet = VK_NULL_HANDLE;
|
|
|
|
commandBuffer->computeReadOnlyDescriptorSet = VK_NULL_HANDLE;
|
|
commandBuffer->computeWriteOnlyDescriptorSet = VK_NULL_HANDLE;
|
|
commandBuffer->computeUniformDescriptorSet = VK_NULL_HANDLE;
|
|
|
|
SDL_zeroa(commandBuffer->vertexSamplerTextures);
|
|
SDL_zeroa(commandBuffer->vertexSamplers);
|
|
SDL_zeroa(commandBuffer->vertexStorageTextures);
|
|
SDL_zeroa(commandBuffer->vertexStorageBuffers);
|
|
|
|
SDL_zeroa(commandBuffer->fragmentSamplerTextures);
|
|
SDL_zeroa(commandBuffer->fragmentSamplers);
|
|
SDL_zeroa(commandBuffer->fragmentStorageTextures);
|
|
SDL_zeroa(commandBuffer->fragmentStorageBuffers);
|
|
|
|
SDL_zeroa(commandBuffer->writeOnlyComputeStorageTextureSubresources);
|
|
commandBuffer->writeOnlyComputeStorageTextureSubresourceCount = 0;
|
|
SDL_zeroa(commandBuffer->writeOnlyComputeStorageBuffers);
|
|
SDL_zeroa(commandBuffer->readOnlyComputeStorageTextures);
|
|
SDL_zeroa(commandBuffer->readOnlyComputeStorageBuffers);
|
|
|
|
commandBuffer->autoReleaseFence = 1;
|
|
|
|
commandBuffer->isDefrag = 0;
|
|
|
|
/* Reset the command buffer here to avoid resets being called
|
|
* from a separate thread than where the command buffer was acquired
|
|
*/
|
|
result = renderer->vkResetCommandBuffer(
|
|
commandBuffer->commandBuffer,
|
|
VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
|
|
|
|
if (result != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkResetCommandBuffer", result);
|
|
}
|
|
|
|
VULKAN_INTERNAL_BeginCommandBuffer(renderer, commandBuffer);
|
|
|
|
return (SDL_GPUCommandBuffer *)commandBuffer;
|
|
}
|
|
|
|
static bool VULKAN_QueryFence(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUFence *fence)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VkResult result;
|
|
|
|
result = renderer->vkGetFenceStatus(
|
|
renderer->logicalDevice,
|
|
((VulkanFenceHandle *)fence)->fence);
|
|
|
|
if (result == VK_SUCCESS) {
|
|
return 1;
|
|
} else if (result == VK_NOT_READY) {
|
|
return 0;
|
|
} else {
|
|
LogVulkanResultAsError("vkGetFenceStatus", result);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_ReturnFenceToPool(
|
|
VulkanRenderer *renderer,
|
|
VulkanFenceHandle *fenceHandle)
|
|
{
|
|
SDL_LockMutex(renderer->fencePool.lock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->fencePool.availableFences,
|
|
VulkanFenceHandle *,
|
|
renderer->fencePool.availableFenceCount + 1,
|
|
renderer->fencePool.availableFenceCapacity,
|
|
renderer->fencePool.availableFenceCapacity * 2);
|
|
|
|
renderer->fencePool.availableFences[renderer->fencePool.availableFenceCount] = fenceHandle;
|
|
renderer->fencePool.availableFenceCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->fencePool.lock);
|
|
}
|
|
|
|
static void VULKAN_ReleaseFence(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUFence *fence)
|
|
{
|
|
VulkanFenceHandle *handle = (VulkanFenceHandle *)fence;
|
|
|
|
if (SDL_AtomicDecRef(&handle->referenceCount)) {
|
|
VULKAN_INTERNAL_ReturnFenceToPool((VulkanRenderer *)driverData, handle);
|
|
}
|
|
}
|
|
|
|
static WindowData *VULKAN_INTERNAL_FetchWindowData(
|
|
SDL_Window *window)
|
|
{
|
|
SDL_PropertiesID properties = SDL_GetWindowProperties(window);
|
|
return (WindowData *)SDL_GetPointerProperty(properties, WINDOW_PROPERTY_DATA, NULL);
|
|
}
|
|
|
|
static SDL_bool VULKAN_INTERNAL_OnWindowResize(void *userdata, SDL_Event *e)
|
|
{
|
|
SDL_Window *w = (SDL_Window *)userdata;
|
|
WindowData *data;
|
|
if (e->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
|
|
data = VULKAN_INTERNAL_FetchWindowData(w);
|
|
data->needsSwapchainRecreate = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool VULKAN_SupportsSwapchainComposition(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_Window *window,
|
|
SDL_GPUSwapchainComposition swapchainComposition)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
|
|
VkSurfaceKHR surface;
|
|
SwapchainSupportDetails supportDetails;
|
|
bool result = false;
|
|
|
|
if (windowData == NULL || windowData->swapchainData == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Must claim window before querying swapchain composition support!");
|
|
return false;
|
|
}
|
|
|
|
surface = windowData->swapchainData->surface;
|
|
|
|
if (VULKAN_INTERNAL_QuerySwapchainSupport(
|
|
renderer,
|
|
renderer->physicalDevice,
|
|
surface,
|
|
&supportDetails)) {
|
|
|
|
result = VULKAN_INTERNAL_VerifySwapSurfaceFormat(
|
|
SwapchainCompositionToFormat[swapchainComposition],
|
|
SwapchainCompositionToColorSpace[swapchainComposition],
|
|
supportDetails.formats,
|
|
supportDetails.formatsLength);
|
|
|
|
if (!result) {
|
|
// Let's try again with the fallback format...
|
|
result = VULKAN_INTERNAL_VerifySwapSurfaceFormat(
|
|
SwapchainCompositionToFallbackFormat[swapchainComposition],
|
|
SwapchainCompositionToColorSpace[swapchainComposition],
|
|
supportDetails.formats,
|
|
supportDetails.formatsLength);
|
|
}
|
|
|
|
SDL_free(supportDetails.formats);
|
|
SDL_free(supportDetails.presentModes);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool VULKAN_SupportsPresentMode(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_Window *window,
|
|
SDL_GPUPresentMode presentMode)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
|
|
VkSurfaceKHR surface;
|
|
SwapchainSupportDetails supportDetails;
|
|
bool result = false;
|
|
|
|
if (windowData == NULL || windowData->swapchainData == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Must claim window before querying present mode support!");
|
|
return false;
|
|
}
|
|
|
|
surface = windowData->swapchainData->surface;
|
|
|
|
if (VULKAN_INTERNAL_QuerySwapchainSupport(
|
|
renderer,
|
|
renderer->physicalDevice,
|
|
surface,
|
|
&supportDetails)) {
|
|
|
|
result = VULKAN_INTERNAL_VerifySwapPresentMode(
|
|
SDLToVK_PresentMode[presentMode],
|
|
supportDetails.presentModes,
|
|
supportDetails.presentModesLength);
|
|
|
|
SDL_free(supportDetails.formats);
|
|
SDL_free(supportDetails.presentModes);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool VULKAN_ClaimWindow(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_Window *window)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
|
|
|
|
if (windowData == NULL) {
|
|
windowData = SDL_malloc(sizeof(WindowData));
|
|
windowData->window = window;
|
|
windowData->presentMode = SDL_GPU_PRESENTMODE_VSYNC;
|
|
windowData->swapchainComposition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR;
|
|
|
|
if (VULKAN_INTERNAL_CreateSwapchain(renderer, windowData)) {
|
|
SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData);
|
|
|
|
if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity) {
|
|
renderer->claimedWindowCapacity *= 2;
|
|
renderer->claimedWindows = SDL_realloc(
|
|
renderer->claimedWindows,
|
|
renderer->claimedWindowCapacity * sizeof(WindowData *));
|
|
}
|
|
|
|
renderer->claimedWindows[renderer->claimedWindowCount] = windowData;
|
|
renderer->claimedWindowCount += 1;
|
|
|
|
SDL_AddEventWatch(VULKAN_INTERNAL_OnWindowResize, window);
|
|
|
|
return 1;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not create swapchain, failed to claim window!");
|
|
SDL_free(windowData);
|
|
return 0;
|
|
}
|
|
} else {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Window already claimed!");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void VULKAN_ReleaseWindow(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_Window *window)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
|
|
Uint32 i;
|
|
|
|
if (windowData == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (windowData->swapchainData != NULL) {
|
|
VULKAN_Wait(driverData);
|
|
|
|
for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
|
|
if (windowData->swapchainData->inFlightFences[i] != NULL) {
|
|
VULKAN_ReleaseFence(
|
|
driverData,
|
|
(SDL_GPUFence *)windowData->swapchainData->inFlightFences[i]);
|
|
}
|
|
}
|
|
|
|
VULKAN_INTERNAL_DestroySwapchain(
|
|
(VulkanRenderer *)driverData,
|
|
windowData);
|
|
}
|
|
|
|
for (i = 0; i < renderer->claimedWindowCount; i += 1) {
|
|
if (renderer->claimedWindows[i]->window == window) {
|
|
renderer->claimedWindows[i] = renderer->claimedWindows[renderer->claimedWindowCount - 1];
|
|
renderer->claimedWindowCount -= 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDL_free(windowData);
|
|
|
|
SDL_ClearProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA);
|
|
SDL_DelEventWatch(VULKAN_INTERNAL_OnWindowResize, window);
|
|
}
|
|
|
|
static bool VULKAN_INTERNAL_RecreateSwapchain(
|
|
VulkanRenderer *renderer,
|
|
WindowData *windowData)
|
|
{
|
|
Uint32 i;
|
|
|
|
if (windowData->swapchainData != NULL) {
|
|
VULKAN_Wait((SDL_GPURenderer *)renderer);
|
|
|
|
for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
|
|
if (windowData->swapchainData->inFlightFences[i] != NULL) {
|
|
VULKAN_ReleaseFence(
|
|
(SDL_GPURenderer *)renderer,
|
|
(SDL_GPUFence *)windowData->swapchainData->inFlightFences[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
VULKAN_INTERNAL_DestroySwapchain(renderer, windowData);
|
|
return VULKAN_INTERNAL_CreateSwapchain(renderer, windowData);
|
|
}
|
|
|
|
static SDL_GPUTexture *VULKAN_AcquireSwapchainTexture(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_Window *window,
|
|
Uint32 *pWidth,
|
|
Uint32 *pHeight)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
Uint32 swapchainImageIndex;
|
|
WindowData *windowData;
|
|
VulkanSwapchainData *swapchainData;
|
|
VkResult acquireResult = VK_SUCCESS;
|
|
VulkanTextureContainer *swapchainTextureContainer = NULL;
|
|
VulkanPresentData *presentData;
|
|
|
|
windowData = VULKAN_INTERNAL_FetchWindowData(window);
|
|
if (windowData == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
swapchainData = windowData->swapchainData;
|
|
|
|
// Window is claimed but swapchain is invalid!
|
|
if (swapchainData == NULL) {
|
|
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) {
|
|
// Window is minimized, don't bother
|
|
return NULL;
|
|
}
|
|
|
|
// Let's try to recreate
|
|
VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
|
|
swapchainData = windowData->swapchainData;
|
|
|
|
if (swapchainData == NULL) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Failed to recreate swapchain!");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (swapchainData->inFlightFences[swapchainData->frameCounter] != NULL) {
|
|
if (swapchainData->presentMode == VK_PRESENT_MODE_FIFO_KHR) {
|
|
// In VSYNC mode, block until the least recent presented frame is done
|
|
VULKAN_WaitForFences(
|
|
(SDL_GPURenderer *)renderer,
|
|
true,
|
|
(SDL_GPUFence **)&swapchainData->inFlightFences[swapchainData->frameCounter],
|
|
1);
|
|
} else {
|
|
if (!VULKAN_QueryFence(
|
|
(SDL_GPURenderer *)renderer,
|
|
(SDL_GPUFence *)swapchainData->inFlightFences[swapchainData->frameCounter])) {
|
|
/*
|
|
* In MAILBOX or IMMEDIATE mode, if the least recent fence is not signaled,
|
|
* return NULL to indicate that rendering should be skipped
|
|
*/
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
VULKAN_ReleaseFence(
|
|
(SDL_GPURenderer *)renderer,
|
|
(SDL_GPUFence *)swapchainData->inFlightFences[swapchainData->frameCounter]);
|
|
|
|
swapchainData->inFlightFences[swapchainData->frameCounter] = NULL;
|
|
}
|
|
|
|
// If window data marked as needing swapchain recreate, try to recreate
|
|
if (windowData->needsSwapchainRecreate) {
|
|
VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
|
|
swapchainData = windowData->swapchainData;
|
|
|
|
if (swapchainData == NULL) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Failed to recreate swapchain!");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// Finally, try to acquire!
|
|
acquireResult = renderer->vkAcquireNextImageKHR(
|
|
renderer->logicalDevice,
|
|
swapchainData->swapchain,
|
|
UINT64_MAX,
|
|
swapchainData->imageAvailableSemaphore[swapchainData->frameCounter],
|
|
VK_NULL_HANDLE,
|
|
&swapchainImageIndex);
|
|
|
|
// Acquisition is invalid, let's try to recreate
|
|
if (acquireResult != VK_SUCCESS && acquireResult != VK_SUBOPTIMAL_KHR) {
|
|
VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
|
|
swapchainData = windowData->swapchainData;
|
|
|
|
if (swapchainData == NULL) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Failed to recreate swapchain!");
|
|
return NULL;
|
|
}
|
|
|
|
acquireResult = renderer->vkAcquireNextImageKHR(
|
|
renderer->logicalDevice,
|
|
swapchainData->swapchain,
|
|
UINT64_MAX,
|
|
swapchainData->imageAvailableSemaphore[swapchainData->frameCounter],
|
|
VK_NULL_HANDLE,
|
|
&swapchainImageIndex);
|
|
|
|
if (acquireResult != VK_SUCCESS && acquireResult != VK_SUBOPTIMAL_KHR) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Failed to acquire swapchain texture!");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
swapchainTextureContainer = &swapchainData->textureContainers[swapchainImageIndex];
|
|
|
|
// We need a special execution dependency with pWaitDstStageMask or image transition can start before acquire finishes
|
|
|
|
VkImageMemoryBarrier imageBarrier;
|
|
imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
imageBarrier.pNext = NULL;
|
|
imageBarrier.srcAccessMask = 0;
|
|
imageBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
imageBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
imageBarrier.image = swapchainTextureContainer->activeTextureHandle->vulkanTexture->image;
|
|
imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
imageBarrier.subresourceRange.baseMipLevel = 0;
|
|
imageBarrier.subresourceRange.levelCount = 1;
|
|
imageBarrier.subresourceRange.baseArrayLayer = 0;
|
|
imageBarrier.subresourceRange.layerCount = 1;
|
|
|
|
renderer->vkCmdPipelineBarrier(
|
|
vulkanCommandBuffer->commandBuffer,
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
0,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
1,
|
|
&imageBarrier);
|
|
|
|
// Set up present struct
|
|
|
|
if (vulkanCommandBuffer->presentDataCount == vulkanCommandBuffer->presentDataCapacity) {
|
|
vulkanCommandBuffer->presentDataCapacity += 1;
|
|
vulkanCommandBuffer->presentDatas = SDL_realloc(
|
|
vulkanCommandBuffer->presentDatas,
|
|
vulkanCommandBuffer->presentDataCapacity * sizeof(VulkanPresentData));
|
|
}
|
|
|
|
presentData = &vulkanCommandBuffer->presentDatas[vulkanCommandBuffer->presentDataCount];
|
|
vulkanCommandBuffer->presentDataCount += 1;
|
|
|
|
presentData->windowData = windowData;
|
|
presentData->swapchainImageIndex = swapchainImageIndex;
|
|
|
|
// Set up present semaphores
|
|
|
|
if (vulkanCommandBuffer->waitSemaphoreCount == vulkanCommandBuffer->waitSemaphoreCapacity) {
|
|
vulkanCommandBuffer->waitSemaphoreCapacity += 1;
|
|
vulkanCommandBuffer->waitSemaphores = SDL_realloc(
|
|
vulkanCommandBuffer->waitSemaphores,
|
|
vulkanCommandBuffer->waitSemaphoreCapacity * sizeof(VkSemaphore));
|
|
}
|
|
|
|
vulkanCommandBuffer->waitSemaphores[vulkanCommandBuffer->waitSemaphoreCount] =
|
|
swapchainData->imageAvailableSemaphore[swapchainData->frameCounter];
|
|
vulkanCommandBuffer->waitSemaphoreCount += 1;
|
|
|
|
if (vulkanCommandBuffer->signalSemaphoreCount == vulkanCommandBuffer->signalSemaphoreCapacity) {
|
|
vulkanCommandBuffer->signalSemaphoreCapacity += 1;
|
|
vulkanCommandBuffer->signalSemaphores = SDL_realloc(
|
|
vulkanCommandBuffer->signalSemaphores,
|
|
vulkanCommandBuffer->signalSemaphoreCapacity * sizeof(VkSemaphore));
|
|
}
|
|
|
|
vulkanCommandBuffer->signalSemaphores[vulkanCommandBuffer->signalSemaphoreCount] =
|
|
swapchainData->renderFinishedSemaphore[swapchainData->frameCounter];
|
|
vulkanCommandBuffer->signalSemaphoreCount += 1;
|
|
|
|
*pWidth = swapchainData->textureContainers[swapchainData->frameCounter].header.info.width;
|
|
*pHeight = swapchainData->textureContainers[swapchainData->frameCounter].header.info.height;
|
|
|
|
return (SDL_GPUTexture *)swapchainTextureContainer;
|
|
}
|
|
|
|
static SDL_GPUTextureFormat VULKAN_GetSwapchainTextureFormat(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_Window *window)
|
|
{
|
|
WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
|
|
|
|
if (windowData == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Cannot get swapchain format, window has not been claimed!");
|
|
return 0;
|
|
}
|
|
|
|
if (windowData->swapchainData == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Cannot get swapchain format, swapchain is currently invalid!");
|
|
return 0;
|
|
}
|
|
|
|
return SwapchainCompositionToSDLFormat(
|
|
windowData->swapchainComposition,
|
|
windowData->swapchainData->usingFallbackFormat);
|
|
}
|
|
|
|
static bool VULKAN_SetSwapchainParameters(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_Window *window,
|
|
SDL_GPUSwapchainComposition swapchainComposition,
|
|
SDL_GPUPresentMode presentMode)
|
|
{
|
|
WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
|
|
|
|
if (windowData == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Cannot set swapchain parameters on unclaimed window!");
|
|
return false;
|
|
}
|
|
|
|
if (!VULKAN_SupportsSwapchainComposition(driverData, window, swapchainComposition)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Swapchain composition not supported!");
|
|
return false;
|
|
}
|
|
|
|
if (!VULKAN_SupportsPresentMode(driverData, window, presentMode)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Present mode not supported!");
|
|
return false;
|
|
}
|
|
|
|
windowData->presentMode = presentMode;
|
|
windowData->swapchainComposition = swapchainComposition;
|
|
|
|
return VULKAN_INTERNAL_RecreateSwapchain(
|
|
(VulkanRenderer *)driverData,
|
|
windowData);
|
|
}
|
|
|
|
// Submission structure
|
|
|
|
static VulkanFenceHandle *VULKAN_INTERNAL_AcquireFenceFromPool(
|
|
VulkanRenderer *renderer)
|
|
{
|
|
VulkanFenceHandle *handle;
|
|
VkFenceCreateInfo fenceCreateInfo;
|
|
VkFence fence;
|
|
VkResult vulkanResult;
|
|
|
|
if (renderer->fencePool.availableFenceCount == 0) {
|
|
// Create fence
|
|
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
fenceCreateInfo.pNext = NULL;
|
|
fenceCreateInfo.flags = 0;
|
|
|
|
vulkanResult = renderer->vkCreateFence(
|
|
renderer->logicalDevice,
|
|
&fenceCreateInfo,
|
|
NULL,
|
|
&fence);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkCreateFence", vulkanResult);
|
|
return NULL;
|
|
}
|
|
|
|
handle = SDL_malloc(sizeof(VulkanFenceHandle));
|
|
handle->fence = fence;
|
|
SDL_AtomicSet(&handle->referenceCount, 0);
|
|
return handle;
|
|
}
|
|
|
|
SDL_LockMutex(renderer->fencePool.lock);
|
|
|
|
handle = renderer->fencePool.availableFences[renderer->fencePool.availableFenceCount - 1];
|
|
renderer->fencePool.availableFenceCount -= 1;
|
|
|
|
vulkanResult = renderer->vkResetFences(
|
|
renderer->logicalDevice,
|
|
1,
|
|
&handle->fence);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkResetFences", vulkanResult);
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->fencePool.lock);
|
|
|
|
return handle;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_PerformPendingDestroys(
|
|
VulkanRenderer *renderer)
|
|
{
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
for (Sint32 i = renderer->texturesToDestroyCount - 1; i >= 0; i -= 1) {
|
|
if (SDL_AtomicGet(&renderer->texturesToDestroy[i]->referenceCount) == 0) {
|
|
VULKAN_INTERNAL_DestroyTexture(
|
|
renderer,
|
|
renderer->texturesToDestroy[i]);
|
|
|
|
renderer->texturesToDestroy[i] = renderer->texturesToDestroy[renderer->texturesToDestroyCount - 1];
|
|
renderer->texturesToDestroyCount -= 1;
|
|
}
|
|
}
|
|
|
|
for (Sint32 i = renderer->buffersToDestroyCount - 1; i >= 0; i -= 1) {
|
|
if (SDL_AtomicGet(&renderer->buffersToDestroy[i]->referenceCount) == 0) {
|
|
VULKAN_INTERNAL_DestroyBuffer(
|
|
renderer,
|
|
renderer->buffersToDestroy[i]);
|
|
|
|
renderer->buffersToDestroy[i] = renderer->buffersToDestroy[renderer->buffersToDestroyCount - 1];
|
|
renderer->buffersToDestroyCount -= 1;
|
|
}
|
|
}
|
|
|
|
for (Sint32 i = renderer->graphicsPipelinesToDestroyCount - 1; i >= 0; i -= 1) {
|
|
if (SDL_AtomicGet(&renderer->graphicsPipelinesToDestroy[i]->referenceCount) == 0) {
|
|
VULKAN_INTERNAL_DestroyGraphicsPipeline(
|
|
renderer,
|
|
renderer->graphicsPipelinesToDestroy[i]);
|
|
|
|
renderer->graphicsPipelinesToDestroy[i] = renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount - 1];
|
|
renderer->graphicsPipelinesToDestroyCount -= 1;
|
|
}
|
|
}
|
|
|
|
for (Sint32 i = renderer->computePipelinesToDestroyCount - 1; i >= 0; i -= 1) {
|
|
if (SDL_AtomicGet(&renderer->computePipelinesToDestroy[i]->referenceCount) == 0) {
|
|
VULKAN_INTERNAL_DestroyComputePipeline(
|
|
renderer,
|
|
renderer->computePipelinesToDestroy[i]);
|
|
|
|
renderer->computePipelinesToDestroy[i] = renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount - 1];
|
|
renderer->computePipelinesToDestroyCount -= 1;
|
|
}
|
|
}
|
|
|
|
for (Sint32 i = renderer->shadersToDestroyCount - 1; i >= 0; i -= 1) {
|
|
if (SDL_AtomicGet(&renderer->shadersToDestroy[i]->referenceCount) == 0) {
|
|
VULKAN_INTERNAL_DestroyShader(
|
|
renderer,
|
|
renderer->shadersToDestroy[i]);
|
|
|
|
renderer->shadersToDestroy[i] = renderer->shadersToDestroy[renderer->shadersToDestroyCount - 1];
|
|
renderer->shadersToDestroyCount -= 1;
|
|
}
|
|
}
|
|
|
|
for (Sint32 i = renderer->samplersToDestroyCount - 1; i >= 0; i -= 1) {
|
|
if (SDL_AtomicGet(&renderer->samplersToDestroy[i]->referenceCount) == 0) {
|
|
VULKAN_INTERNAL_DestroySampler(
|
|
renderer,
|
|
renderer->samplersToDestroy[i]);
|
|
|
|
renderer->samplersToDestroy[i] = renderer->samplersToDestroy[renderer->samplersToDestroyCount - 1];
|
|
renderer->samplersToDestroyCount -= 1;
|
|
}
|
|
}
|
|
|
|
for (Sint32 i = renderer->framebuffersToDestroyCount - 1; i >= 0; i -= 1) {
|
|
if (SDL_AtomicGet(&renderer->framebuffersToDestroy[i]->referenceCount) == 0) {
|
|
VULKAN_INTERNAL_DestroyFramebuffer(
|
|
renderer,
|
|
renderer->framebuffersToDestroy[i]);
|
|
|
|
renderer->framebuffersToDestroy[i] = renderer->framebuffersToDestroy[renderer->framebuffersToDestroyCount - 1];
|
|
renderer->framebuffersToDestroyCount -= 1;
|
|
}
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_CleanCommandBuffer(
|
|
VulkanRenderer *renderer,
|
|
VulkanCommandBuffer *commandBuffer)
|
|
{
|
|
Uint32 i;
|
|
DescriptorSetData *descriptorSetData;
|
|
|
|
if (commandBuffer->autoReleaseFence) {
|
|
VULKAN_ReleaseFence(
|
|
(SDL_GPURenderer *)renderer,
|
|
(SDL_GPUFence *)commandBuffer->inFlightFence);
|
|
|
|
commandBuffer->inFlightFence = NULL;
|
|
}
|
|
|
|
// Bound descriptor sets are now available
|
|
|
|
for (i = 0; i < commandBuffer->boundDescriptorSetDataCount; i += 1) {
|
|
descriptorSetData = &commandBuffer->boundDescriptorSetDatas[i];
|
|
|
|
SDL_LockMutex(descriptorSetData->descriptorSetPool->lock);
|
|
|
|
if (descriptorSetData->descriptorSetPool->inactiveDescriptorSetCount == descriptorSetData->descriptorSetPool->inactiveDescriptorSetCapacity) {
|
|
descriptorSetData->descriptorSetPool->inactiveDescriptorSetCapacity *= 2;
|
|
descriptorSetData->descriptorSetPool->inactiveDescriptorSets = SDL_realloc(
|
|
descriptorSetData->descriptorSetPool->inactiveDescriptorSets,
|
|
descriptorSetData->descriptorSetPool->inactiveDescriptorSetCapacity * sizeof(VkDescriptorSet));
|
|
}
|
|
|
|
descriptorSetData->descriptorSetPool->inactiveDescriptorSets[descriptorSetData->descriptorSetPool->inactiveDescriptorSetCount] = descriptorSetData->descriptorSet;
|
|
descriptorSetData->descriptorSetPool->inactiveDescriptorSetCount += 1;
|
|
|
|
SDL_UnlockMutex(descriptorSetData->descriptorSetPool->lock);
|
|
}
|
|
|
|
commandBuffer->boundDescriptorSetDataCount = 0;
|
|
|
|
// Uniform buffers are now available
|
|
|
|
SDL_LockMutex(renderer->acquireUniformBufferLock);
|
|
|
|
for (i = 0; i < commandBuffer->usedUniformBufferCount; i += 1) {
|
|
VULKAN_INTERNAL_ReturnUniformBufferToPool(
|
|
renderer,
|
|
commandBuffer->usedUniformBuffers[i]);
|
|
}
|
|
commandBuffer->usedUniformBufferCount = 0;
|
|
|
|
SDL_UnlockMutex(renderer->acquireUniformBufferLock);
|
|
|
|
// Decrement reference counts
|
|
|
|
for (i = 0; i < commandBuffer->usedBufferCount; i += 1) {
|
|
(void)SDL_AtomicDecRef(&commandBuffer->usedBuffers[i]->referenceCount);
|
|
}
|
|
commandBuffer->usedBufferCount = 0;
|
|
|
|
for (i = 0; i < commandBuffer->usedTextureCount; i += 1) {
|
|
(void)SDL_AtomicDecRef(&commandBuffer->usedTextures[i]->referenceCount);
|
|
}
|
|
commandBuffer->usedTextureCount = 0;
|
|
|
|
for (i = 0; i < commandBuffer->usedSamplerCount; i += 1) {
|
|
(void)SDL_AtomicDecRef(&commandBuffer->usedSamplers[i]->referenceCount);
|
|
}
|
|
commandBuffer->usedSamplerCount = 0;
|
|
|
|
for (i = 0; i < commandBuffer->usedGraphicsPipelineCount; i += 1) {
|
|
(void)SDL_AtomicDecRef(&commandBuffer->usedGraphicsPipelines[i]->referenceCount);
|
|
}
|
|
commandBuffer->usedGraphicsPipelineCount = 0;
|
|
|
|
for (i = 0; i < commandBuffer->usedComputePipelineCount; i += 1) {
|
|
(void)SDL_AtomicDecRef(&commandBuffer->usedComputePipelines[i]->referenceCount);
|
|
}
|
|
commandBuffer->usedComputePipelineCount = 0;
|
|
|
|
for (i = 0; i < commandBuffer->usedFramebufferCount; i += 1) {
|
|
(void)SDL_AtomicDecRef(&commandBuffer->usedFramebuffers[i]->referenceCount);
|
|
}
|
|
commandBuffer->usedFramebufferCount = 0;
|
|
|
|
// Reset presentation data
|
|
|
|
commandBuffer->presentDataCount = 0;
|
|
commandBuffer->waitSemaphoreCount = 0;
|
|
commandBuffer->signalSemaphoreCount = 0;
|
|
|
|
// Reset defrag state
|
|
|
|
if (commandBuffer->isDefrag) {
|
|
renderer->defragInProgress = 0;
|
|
}
|
|
|
|
// Return command buffer to pool
|
|
|
|
SDL_LockMutex(renderer->acquireCommandBufferLock);
|
|
|
|
if (commandBuffer->commandPool->inactiveCommandBufferCount == commandBuffer->commandPool->inactiveCommandBufferCapacity) {
|
|
commandBuffer->commandPool->inactiveCommandBufferCapacity += 1;
|
|
commandBuffer->commandPool->inactiveCommandBuffers = SDL_realloc(
|
|
commandBuffer->commandPool->inactiveCommandBuffers,
|
|
commandBuffer->commandPool->inactiveCommandBufferCapacity * sizeof(VulkanCommandBuffer *));
|
|
}
|
|
|
|
commandBuffer->commandPool->inactiveCommandBuffers[commandBuffer->commandPool->inactiveCommandBufferCount] = commandBuffer;
|
|
commandBuffer->commandPool->inactiveCommandBufferCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->acquireCommandBufferLock);
|
|
|
|
// Remove this command buffer from the submitted list
|
|
for (i = 0; i < renderer->submittedCommandBufferCount; i += 1) {
|
|
if (renderer->submittedCommandBuffers[i] == commandBuffer) {
|
|
renderer->submittedCommandBuffers[i] = renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount - 1];
|
|
renderer->submittedCommandBufferCount -= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void VULKAN_WaitForFences(
|
|
SDL_GPURenderer *driverData,
|
|
bool waitAll,
|
|
SDL_GPUFence **pFences,
|
|
Uint32 fenceCount)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VkFence *fences = SDL_stack_alloc(VkFence, fenceCount);
|
|
VkResult result;
|
|
|
|
for (Uint32 i = 0; i < fenceCount; i += 1) {
|
|
fences[i] = ((VulkanFenceHandle *)pFences[i])->fence;
|
|
}
|
|
|
|
result = renderer->vkWaitForFences(
|
|
renderer->logicalDevice,
|
|
fenceCount,
|
|
fences,
|
|
waitAll,
|
|
UINT64_MAX);
|
|
|
|
if (result != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkWaitForFences", result);
|
|
}
|
|
|
|
SDL_stack_free(fences);
|
|
|
|
SDL_LockMutex(renderer->submitLock);
|
|
|
|
for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
|
|
result = renderer->vkGetFenceStatus(
|
|
renderer->logicalDevice,
|
|
renderer->submittedCommandBuffers[i]->inFlightFence->fence);
|
|
|
|
if (result == VK_SUCCESS) {
|
|
VULKAN_INTERNAL_CleanCommandBuffer(
|
|
renderer,
|
|
renderer->submittedCommandBuffers[i]);
|
|
}
|
|
}
|
|
|
|
VULKAN_INTERNAL_PerformPendingDestroys(renderer);
|
|
|
|
SDL_UnlockMutex(renderer->submitLock);
|
|
}
|
|
|
|
static void VULKAN_Wait(
|
|
SDL_GPURenderer *driverData)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VulkanCommandBuffer *commandBuffer;
|
|
VkResult result;
|
|
Sint32 i;
|
|
|
|
result = renderer->vkDeviceWaitIdle(renderer->logicalDevice);
|
|
|
|
if (result != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkDeviceWaitIdle", result);
|
|
return;
|
|
}
|
|
|
|
SDL_LockMutex(renderer->submitLock);
|
|
|
|
for (i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
|
|
commandBuffer = renderer->submittedCommandBuffers[i];
|
|
VULKAN_INTERNAL_CleanCommandBuffer(renderer, commandBuffer);
|
|
}
|
|
|
|
VULKAN_INTERNAL_PerformPendingDestroys(renderer);
|
|
|
|
SDL_UnlockMutex(renderer->submitLock);
|
|
}
|
|
|
|
static SDL_GPUFence *VULKAN_SubmitAndAcquireFence(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer;
|
|
|
|
vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
vulkanCommandBuffer->autoReleaseFence = 0;
|
|
|
|
VULKAN_Submit(commandBuffer);
|
|
|
|
return (SDL_GPUFence *)vulkanCommandBuffer->inFlightFence;
|
|
}
|
|
|
|
static void VULKAN_Submit(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
|
|
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
|
|
VkSubmitInfo submitInfo;
|
|
VkPresentInfoKHR presentInfo;
|
|
VulkanPresentData *presentData;
|
|
VkResult vulkanResult, presentResult = VK_SUCCESS;
|
|
VkPipelineStageFlags waitStages[MAX_PRESENT_COUNT];
|
|
Uint32 swapchainImageIndex;
|
|
VulkanTextureSubresource *swapchainTextureSubresource;
|
|
Uint8 commandBufferCleaned = 0;
|
|
VulkanMemorySubAllocator *allocator;
|
|
bool presenting = false;
|
|
|
|
SDL_LockMutex(renderer->submitLock);
|
|
|
|
// FIXME: Can this just be permanent?
|
|
for (Uint32 i = 0; i < MAX_PRESENT_COUNT; i += 1) {
|
|
waitStages[i] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
}
|
|
|
|
for (Uint32 j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) {
|
|
swapchainImageIndex = vulkanCommandBuffer->presentDatas[j].swapchainImageIndex;
|
|
swapchainTextureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
|
|
&vulkanCommandBuffer->presentDatas[j].windowData->swapchainData->textureContainers[swapchainImageIndex],
|
|
0,
|
|
0);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
renderer,
|
|
vulkanCommandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_PRESENT,
|
|
swapchainTextureSubresource);
|
|
}
|
|
|
|
VULKAN_INTERNAL_EndCommandBuffer(renderer, vulkanCommandBuffer);
|
|
|
|
vulkanCommandBuffer->inFlightFence = VULKAN_INTERNAL_AcquireFenceFromPool(renderer);
|
|
|
|
// Command buffer has a reference to the in-flight fence
|
|
(void)SDL_AtomicIncRef(&vulkanCommandBuffer->inFlightFence->referenceCount);
|
|
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submitInfo.pNext = NULL;
|
|
submitInfo.commandBufferCount = 1;
|
|
submitInfo.pCommandBuffers = &vulkanCommandBuffer->commandBuffer;
|
|
|
|
submitInfo.pWaitDstStageMask = waitStages;
|
|
submitInfo.pWaitSemaphores = vulkanCommandBuffer->waitSemaphores;
|
|
submitInfo.waitSemaphoreCount = vulkanCommandBuffer->waitSemaphoreCount;
|
|
submitInfo.pSignalSemaphores = vulkanCommandBuffer->signalSemaphores;
|
|
submitInfo.signalSemaphoreCount = vulkanCommandBuffer->signalSemaphoreCount;
|
|
|
|
vulkanResult = renderer->vkQueueSubmit(
|
|
renderer->unifiedQueue,
|
|
1,
|
|
&submitInfo,
|
|
vulkanCommandBuffer->inFlightFence->fence);
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
LogVulkanResultAsError("vkQueueSubmit", vulkanResult);
|
|
}
|
|
|
|
// Mark command buffers as submitted
|
|
|
|
if (renderer->submittedCommandBufferCount + 1 >= renderer->submittedCommandBufferCapacity) {
|
|
renderer->submittedCommandBufferCapacity = renderer->submittedCommandBufferCount + 1;
|
|
|
|
renderer->submittedCommandBuffers = SDL_realloc(
|
|
renderer->submittedCommandBuffers,
|
|
sizeof(VulkanCommandBuffer *) * renderer->submittedCommandBufferCapacity);
|
|
}
|
|
|
|
renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount] = vulkanCommandBuffer;
|
|
renderer->submittedCommandBufferCount += 1;
|
|
|
|
// Present, if applicable
|
|
|
|
for (Uint32 j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) {
|
|
presenting = true;
|
|
|
|
presentData = &vulkanCommandBuffer->presentDatas[j];
|
|
|
|
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
|
presentInfo.pNext = NULL;
|
|
presentInfo.pWaitSemaphores =
|
|
&presentData->windowData->swapchainData->renderFinishedSemaphore[presentData->windowData->swapchainData->frameCounter];
|
|
presentInfo.waitSemaphoreCount = 1;
|
|
presentInfo.pSwapchains = &presentData->windowData->swapchainData->swapchain;
|
|
presentInfo.swapchainCount = 1;
|
|
presentInfo.pImageIndices = &presentData->swapchainImageIndex;
|
|
presentInfo.pResults = NULL;
|
|
|
|
presentResult = renderer->vkQueuePresentKHR(
|
|
renderer->unifiedQueue,
|
|
&presentInfo);
|
|
|
|
presentData->windowData->swapchainData->frameCounter =
|
|
(presentData->windowData->swapchainData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT;
|
|
|
|
if (presentResult != VK_SUCCESS) {
|
|
VULKAN_INTERNAL_RecreateSwapchain(
|
|
renderer,
|
|
presentData->windowData);
|
|
} else {
|
|
// If presenting, the swapchain is using the in-flight fence
|
|
presentData->windowData->swapchainData->inFlightFences[presentData->windowData->swapchainData->frameCounter] = vulkanCommandBuffer->inFlightFence;
|
|
|
|
(void)SDL_AtomicIncRef(&vulkanCommandBuffer->inFlightFence->referenceCount);
|
|
}
|
|
}
|
|
|
|
// Check if we can perform any cleanups
|
|
|
|
for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
|
|
vulkanResult = renderer->vkGetFenceStatus(
|
|
renderer->logicalDevice,
|
|
renderer->submittedCommandBuffers[i]->inFlightFence->fence);
|
|
|
|
if (vulkanResult == VK_SUCCESS) {
|
|
VULKAN_INTERNAL_CleanCommandBuffer(
|
|
renderer,
|
|
renderer->submittedCommandBuffers[i]);
|
|
|
|
commandBufferCleaned = 1;
|
|
}
|
|
}
|
|
|
|
if (commandBufferCleaned) {
|
|
SDL_LockMutex(renderer->allocatorLock);
|
|
|
|
for (Uint32 i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) {
|
|
allocator = &renderer->memoryAllocator->subAllocators[i];
|
|
|
|
for (Sint32 j = allocator->allocationCount - 1; j >= 0; j -= 1) {
|
|
if (allocator->allocations[j]->usedRegionCount == 0) {
|
|
VULKAN_INTERNAL_DeallocateMemory(
|
|
renderer,
|
|
allocator,
|
|
j);
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->allocatorLock);
|
|
}
|
|
|
|
// Check pending destroys
|
|
VULKAN_INTERNAL_PerformPendingDestroys(renderer);
|
|
|
|
// Defrag!
|
|
if (
|
|
presenting &&
|
|
renderer->allocationsToDefragCount > 0 &&
|
|
!renderer->defragInProgress) {
|
|
VULKAN_INTERNAL_DefragmentMemory(renderer);
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->submitLock);
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_DefragmentMemory(
|
|
VulkanRenderer *renderer)
|
|
{
|
|
VulkanMemoryAllocation *allocation;
|
|
VulkanMemoryUsedRegion *currentRegion;
|
|
VulkanBuffer *newBuffer;
|
|
VulkanTexture *newTexture;
|
|
VkBufferCopy bufferCopy;
|
|
VkImageCopy imageCopy;
|
|
VulkanCommandBuffer *commandBuffer;
|
|
VulkanTextureSubresource *srcSubresource;
|
|
VulkanTextureSubresource *dstSubresource;
|
|
Uint32 i, subresourceIndex;
|
|
|
|
SDL_LockMutex(renderer->allocatorLock);
|
|
|
|
renderer->defragInProgress = 1;
|
|
|
|
commandBuffer = (VulkanCommandBuffer *)VULKAN_AcquireCommandBuffer((SDL_GPURenderer *)renderer);
|
|
if (commandBuffer == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create defrag command buffer!");
|
|
return 0;
|
|
}
|
|
commandBuffer->isDefrag = 1;
|
|
|
|
allocation = renderer->allocationsToDefrag[renderer->allocationsToDefragCount - 1];
|
|
renderer->allocationsToDefragCount -= 1;
|
|
|
|
/* For each used region in the allocation
|
|
* create a new resource, copy the data
|
|
* and re-point the resource containers
|
|
*/
|
|
for (i = 0; i < allocation->usedRegionCount; i += 1) {
|
|
currentRegion = allocation->usedRegions[i];
|
|
|
|
if (currentRegion->isBuffer && !currentRegion->vulkanBuffer->markedForDestroy) {
|
|
currentRegion->vulkanBuffer->usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
|
|
newBuffer = VULKAN_INTERNAL_CreateBuffer(
|
|
renderer,
|
|
currentRegion->vulkanBuffer->size,
|
|
currentRegion->vulkanBuffer->usageFlags,
|
|
currentRegion->vulkanBuffer->type);
|
|
|
|
if (newBuffer == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create defrag buffer!");
|
|
return 0;
|
|
}
|
|
|
|
if (
|
|
renderer->debugMode &&
|
|
renderer->supportsDebugUtils &&
|
|
currentRegion->vulkanBuffer->handle != NULL &&
|
|
currentRegion->vulkanBuffer->handle->container != NULL &&
|
|
currentRegion->vulkanBuffer->handle->container->debugName != NULL) {
|
|
VULKAN_INTERNAL_SetBufferName(
|
|
renderer,
|
|
newBuffer,
|
|
currentRegion->vulkanBuffer->handle->container->debugName);
|
|
}
|
|
|
|
// Copy buffer contents if necessary
|
|
if (
|
|
currentRegion->vulkanBuffer->type == VULKAN_BUFFER_TYPE_GPU && currentRegion->vulkanBuffer->transitioned) {
|
|
VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
|
|
renderer,
|
|
commandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
|
|
currentRegion->vulkanBuffer);
|
|
|
|
VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
|
|
renderer,
|
|
commandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
|
|
newBuffer);
|
|
|
|
bufferCopy.srcOffset = 0;
|
|
bufferCopy.dstOffset = 0;
|
|
bufferCopy.size = currentRegion->resourceSize;
|
|
|
|
renderer->vkCmdCopyBuffer(
|
|
commandBuffer->commandBuffer,
|
|
currentRegion->vulkanBuffer->buffer,
|
|
newBuffer->buffer,
|
|
1,
|
|
&bufferCopy);
|
|
|
|
VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
|
|
renderer,
|
|
commandBuffer,
|
|
VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
|
|
newBuffer);
|
|
|
|
VULKAN_INTERNAL_TrackBuffer(commandBuffer, currentRegion->vulkanBuffer);
|
|
VULKAN_INTERNAL_TrackBuffer(commandBuffer, newBuffer);
|
|
}
|
|
|
|
// re-point original container to new buffer
|
|
if (currentRegion->vulkanBuffer->handle != NULL) {
|
|
newBuffer->handle = currentRegion->vulkanBuffer->handle;
|
|
newBuffer->handle->vulkanBuffer = newBuffer;
|
|
currentRegion->vulkanBuffer->handle = NULL;
|
|
}
|
|
|
|
VULKAN_INTERNAL_ReleaseBuffer(renderer, currentRegion->vulkanBuffer);
|
|
} else if (!currentRegion->isBuffer && !currentRegion->vulkanTexture->markedForDestroy) {
|
|
newTexture = VULKAN_INTERNAL_CreateTexture(
|
|
renderer,
|
|
currentRegion->vulkanTexture->dimensions.width,
|
|
currentRegion->vulkanTexture->dimensions.height,
|
|
currentRegion->vulkanTexture->depth,
|
|
currentRegion->vulkanTexture->type,
|
|
currentRegion->vulkanTexture->layerCount,
|
|
currentRegion->vulkanTexture->levelCount,
|
|
currentRegion->vulkanTexture->sampleCount,
|
|
currentRegion->vulkanTexture->format,
|
|
currentRegion->vulkanTexture->swizzle,
|
|
currentRegion->vulkanTexture->aspectFlags,
|
|
currentRegion->vulkanTexture->usageFlags,
|
|
currentRegion->vulkanTexture->isMSAAColorTarget);
|
|
|
|
if (newTexture == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create defrag texture!");
|
|
return 0;
|
|
}
|
|
|
|
for (subresourceIndex = 0; subresourceIndex < currentRegion->vulkanTexture->subresourceCount; subresourceIndex += 1) {
|
|
// copy subresource if necessary
|
|
srcSubresource = ¤tRegion->vulkanTexture->subresources[subresourceIndex];
|
|
dstSubresource = &newTexture->subresources[subresourceIndex];
|
|
|
|
// Set debug name if it exists
|
|
if (
|
|
renderer->debugMode &&
|
|
renderer->supportsDebugUtils &&
|
|
srcSubresource->parent->handle != NULL &&
|
|
srcSubresource->parent->handle->container != NULL &&
|
|
srcSubresource->parent->handle->container->debugName != NULL) {
|
|
VULKAN_INTERNAL_SetTextureName(
|
|
renderer,
|
|
currentRegion->vulkanTexture,
|
|
srcSubresource->parent->handle->container->debugName);
|
|
}
|
|
|
|
if (srcSubresource->transitioned) {
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
renderer,
|
|
commandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
|
|
srcSubresource);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
renderer,
|
|
commandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
|
|
dstSubresource);
|
|
|
|
imageCopy.srcOffset.x = 0;
|
|
imageCopy.srcOffset.y = 0;
|
|
imageCopy.srcOffset.z = 0;
|
|
imageCopy.srcSubresource.aspectMask = srcSubresource->parent->aspectFlags;
|
|
imageCopy.srcSubresource.baseArrayLayer = srcSubresource->layer;
|
|
imageCopy.srcSubresource.layerCount = 1;
|
|
imageCopy.srcSubresource.mipLevel = srcSubresource->level;
|
|
imageCopy.extent.width = SDL_max(1, srcSubresource->parent->dimensions.width >> srcSubresource->level);
|
|
imageCopy.extent.height = SDL_max(1, srcSubresource->parent->dimensions.height >> srcSubresource->level);
|
|
imageCopy.extent.depth = srcSubresource->parent->depth;
|
|
imageCopy.dstOffset.x = 0;
|
|
imageCopy.dstOffset.y = 0;
|
|
imageCopy.dstOffset.z = 0;
|
|
imageCopy.dstSubresource.aspectMask = dstSubresource->parent->aspectFlags;
|
|
imageCopy.dstSubresource.baseArrayLayer = dstSubresource->layer;
|
|
imageCopy.dstSubresource.layerCount = 1;
|
|
imageCopy.dstSubresource.mipLevel = dstSubresource->level;
|
|
|
|
renderer->vkCmdCopyImage(
|
|
commandBuffer->commandBuffer,
|
|
currentRegion->vulkanTexture->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
newTexture->image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
1,
|
|
&imageCopy);
|
|
|
|
VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
renderer,
|
|
commandBuffer,
|
|
VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
|
|
dstSubresource);
|
|
|
|
VULKAN_INTERNAL_TrackTexture(commandBuffer, srcSubresource->parent);
|
|
VULKAN_INTERNAL_TrackTexture(commandBuffer, dstSubresource->parent);
|
|
}
|
|
}
|
|
|
|
// re-point original container to new texture
|
|
newTexture->handle = currentRegion->vulkanTexture->handle;
|
|
newTexture->handle->vulkanTexture = newTexture;
|
|
currentRegion->vulkanTexture->handle = NULL;
|
|
|
|
VULKAN_INTERNAL_ReleaseTexture(renderer, currentRegion->vulkanTexture);
|
|
}
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->allocatorLock);
|
|
|
|
VULKAN_Submit(
|
|
(SDL_GPUCommandBuffer *)commandBuffer);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Format Info
|
|
|
|
static bool VULKAN_SupportsTextureFormat(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTextureFormat format,
|
|
SDL_GPUTextureType type,
|
|
SDL_GPUTextureUsageFlags usage)
|
|
{
|
|
VulkanRenderer *renderer = (VulkanRenderer *)driverData;
|
|
VkFormat vulkanFormat = SDLToVK_SurfaceFormat[format];
|
|
VkImageUsageFlags vulkanUsage = 0;
|
|
VkImageCreateFlags createFlags = 0;
|
|
VkImageFormatProperties properties;
|
|
VkResult vulkanResult;
|
|
|
|
if (usage & SDL_GPU_TEXTUREUSAGE_SAMPLER_BIT) {
|
|
vulkanUsage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
}
|
|
if (usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT) {
|
|
vulkanUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
}
|
|
if (usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) {
|
|
vulkanUsage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
}
|
|
if (usage & (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT |
|
|
SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT |
|
|
SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT)) {
|
|
vulkanUsage |= VK_IMAGE_USAGE_STORAGE_BIT;
|
|
}
|
|
|
|
if (type == SDL_GPU_TEXTURETYPE_CUBE) {
|
|
createFlags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
|
}
|
|
|
|
vulkanResult = renderer->vkGetPhysicalDeviceImageFormatProperties(
|
|
renderer->physicalDevice,
|
|
vulkanFormat,
|
|
(type == SDL_GPU_TEXTURETYPE_3D) ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
|
|
VK_IMAGE_TILING_OPTIMAL,
|
|
vulkanUsage,
|
|
createFlags,
|
|
&properties);
|
|
|
|
return vulkanResult == VK_SUCCESS;
|
|
}
|
|
|
|
// Device instantiation
|
|
|
|
static inline Uint8 CheckDeviceExtensions(
|
|
VkExtensionProperties *extensions,
|
|
Uint32 numExtensions,
|
|
VulkanExtensions *supports)
|
|
{
|
|
Uint32 i;
|
|
|
|
SDL_memset(supports, '\0', sizeof(VulkanExtensions));
|
|
for (i = 0; i < numExtensions; i += 1) {
|
|
const char *name = extensions[i].extensionName;
|
|
#define CHECK(ext) \
|
|
if (SDL_strcmp(name, "VK_" #ext) == 0) { \
|
|
supports->ext = 1; \
|
|
}
|
|
CHECK(KHR_swapchain)
|
|
else CHECK(KHR_maintenance1) else CHECK(KHR_driver_properties) else CHECK(EXT_vertex_attribute_divisor) else CHECK(KHR_portability_subset)
|
|
#undef CHECK
|
|
}
|
|
|
|
return (supports->KHR_swapchain &&
|
|
supports->KHR_maintenance1);
|
|
}
|
|
|
|
static inline Uint32 GetDeviceExtensionCount(VulkanExtensions *supports)
|
|
{
|
|
return (
|
|
supports->KHR_swapchain +
|
|
supports->KHR_maintenance1 +
|
|
supports->KHR_driver_properties +
|
|
supports->EXT_vertex_attribute_divisor +
|
|
supports->KHR_portability_subset);
|
|
}
|
|
|
|
static inline void CreateDeviceExtensionArray(
|
|
VulkanExtensions *supports,
|
|
const char **extensions)
|
|
{
|
|
Uint8 cur = 0;
|
|
#define CHECK(ext) \
|
|
if (supports->ext) { \
|
|
extensions[cur++] = "VK_" #ext; \
|
|
}
|
|
CHECK(KHR_swapchain)
|
|
CHECK(KHR_maintenance1)
|
|
CHECK(KHR_driver_properties)
|
|
CHECK(EXT_vertex_attribute_divisor)
|
|
CHECK(KHR_portability_subset)
|
|
#undef CHECK
|
|
}
|
|
|
|
static inline Uint8 SupportsInstanceExtension(
|
|
const char *ext,
|
|
VkExtensionProperties *availableExtensions,
|
|
Uint32 numAvailableExtensions)
|
|
{
|
|
Uint32 i;
|
|
for (i = 0; i < numAvailableExtensions; i += 1) {
|
|
if (SDL_strcmp(ext, availableExtensions[i].extensionName) == 0) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_CheckInstanceExtensions(
|
|
const char **requiredExtensions,
|
|
Uint32 requiredExtensionsLength,
|
|
bool *supportsDebugUtils,
|
|
bool *supportsColorspace)
|
|
{
|
|
Uint32 extensionCount, i;
|
|
VkExtensionProperties *availableExtensions;
|
|
Uint8 allExtensionsSupported = 1;
|
|
|
|
vkEnumerateInstanceExtensionProperties(
|
|
NULL,
|
|
&extensionCount,
|
|
NULL);
|
|
availableExtensions = SDL_malloc(
|
|
extensionCount * sizeof(VkExtensionProperties));
|
|
vkEnumerateInstanceExtensionProperties(
|
|
NULL,
|
|
&extensionCount,
|
|
availableExtensions);
|
|
|
|
for (i = 0; i < requiredExtensionsLength; i += 1) {
|
|
if (!SupportsInstanceExtension(
|
|
requiredExtensions[i],
|
|
availableExtensions,
|
|
extensionCount)) {
|
|
allExtensionsSupported = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// This is optional, but nice to have!
|
|
*supportsDebugUtils = SupportsInstanceExtension(
|
|
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
|
|
availableExtensions,
|
|
extensionCount);
|
|
|
|
// Also optional and nice to have!
|
|
*supportsColorspace = SupportsInstanceExtension(
|
|
VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
|
|
availableExtensions,
|
|
extensionCount);
|
|
|
|
SDL_free(availableExtensions);
|
|
return allExtensionsSupported;
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_CheckDeviceExtensions(
|
|
VulkanRenderer *renderer,
|
|
VkPhysicalDevice physicalDevice,
|
|
VulkanExtensions *physicalDeviceExtensions)
|
|
{
|
|
Uint32 extensionCount;
|
|
VkExtensionProperties *availableExtensions;
|
|
Uint8 allExtensionsSupported;
|
|
|
|
renderer->vkEnumerateDeviceExtensionProperties(
|
|
physicalDevice,
|
|
NULL,
|
|
&extensionCount,
|
|
NULL);
|
|
availableExtensions = (VkExtensionProperties *)SDL_malloc(
|
|
extensionCount * sizeof(VkExtensionProperties));
|
|
renderer->vkEnumerateDeviceExtensionProperties(
|
|
physicalDevice,
|
|
NULL,
|
|
&extensionCount,
|
|
availableExtensions);
|
|
|
|
allExtensionsSupported = CheckDeviceExtensions(
|
|
availableExtensions,
|
|
extensionCount,
|
|
physicalDeviceExtensions);
|
|
|
|
SDL_free(availableExtensions);
|
|
return allExtensionsSupported;
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_CheckValidationLayers(
|
|
const char **validationLayers,
|
|
Uint32 validationLayersLength)
|
|
{
|
|
Uint32 layerCount;
|
|
VkLayerProperties *availableLayers;
|
|
Uint32 i, j;
|
|
Uint8 layerFound = 0;
|
|
|
|
vkEnumerateInstanceLayerProperties(&layerCount, NULL);
|
|
availableLayers = (VkLayerProperties *)SDL_malloc(
|
|
layerCount * sizeof(VkLayerProperties));
|
|
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers);
|
|
|
|
for (i = 0; i < validationLayersLength; i += 1) {
|
|
layerFound = 0;
|
|
|
|
for (j = 0; j < layerCount; j += 1) {
|
|
if (SDL_strcmp(validationLayers[i], availableLayers[j].layerName) == 0) {
|
|
layerFound = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!layerFound) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDL_free(availableLayers);
|
|
return layerFound;
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_CreateInstance(VulkanRenderer *renderer)
|
|
{
|
|
VkResult vulkanResult;
|
|
VkApplicationInfo appInfo;
|
|
VkInstanceCreateFlags createFlags;
|
|
const char *const *originalInstanceExtensionNames;
|
|
const char **instanceExtensionNames;
|
|
Uint32 instanceExtensionCount;
|
|
VkInstanceCreateInfo createInfo;
|
|
static const char *layerNames[] = { "VK_LAYER_KHRONOS_validation" };
|
|
|
|
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
|
appInfo.pNext = NULL;
|
|
appInfo.pApplicationName = NULL;
|
|
appInfo.applicationVersion = 0;
|
|
appInfo.pEngineName = "SDLGPU";
|
|
appInfo.engineVersion = SDL_VERSION;
|
|
appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0);
|
|
|
|
createFlags = 0;
|
|
|
|
originalInstanceExtensionNames = SDL_Vulkan_GetInstanceExtensions(&instanceExtensionCount);
|
|
if (!originalInstanceExtensionNames) {
|
|
SDL_LogError(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"SDL_Vulkan_GetInstanceExtensions(): getExtensionCount: %s",
|
|
SDL_GetError());
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Extra space for the following extensions:
|
|
* VK_KHR_get_physical_device_properties2
|
|
* VK_EXT_swapchain_colorspace
|
|
* VK_EXT_debug_utils
|
|
* VK_KHR_portability_enumeration
|
|
*/
|
|
instanceExtensionNames = SDL_stack_alloc(
|
|
const char *,
|
|
instanceExtensionCount + 4);
|
|
SDL_memcpy(instanceExtensionNames, originalInstanceExtensionNames, instanceExtensionCount * sizeof(const char *));
|
|
|
|
// Core since 1.1
|
|
instanceExtensionNames[instanceExtensionCount++] =
|
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
|
|
|
|
#ifdef SDL_PLATFORM_APPLE
|
|
instanceExtensionNames[instanceExtensionCount++] =
|
|
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME;
|
|
createFlags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
|
#endif
|
|
|
|
if (!VULKAN_INTERNAL_CheckInstanceExtensions(
|
|
instanceExtensionNames,
|
|
instanceExtensionCount,
|
|
&renderer->supportsDebugUtils,
|
|
&renderer->supportsColorspace)) {
|
|
SDL_LogError(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"Required Vulkan instance extensions not supported");
|
|
|
|
SDL_stack_free((char *)instanceExtensionNames);
|
|
return 0;
|
|
}
|
|
|
|
if (renderer->supportsDebugUtils) {
|
|
// Append the debug extension
|
|
instanceExtensionNames[instanceExtensionCount++] =
|
|
VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
|
|
} else {
|
|
SDL_LogWarn(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"%s is not supported!",
|
|
VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
|
}
|
|
|
|
if (renderer->supportsColorspace) {
|
|
// Append colorspace extension
|
|
instanceExtensionNames[instanceExtensionCount++] =
|
|
VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME;
|
|
}
|
|
|
|
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
|
createInfo.pNext = NULL;
|
|
createInfo.flags = createFlags;
|
|
createInfo.pApplicationInfo = &appInfo;
|
|
createInfo.ppEnabledLayerNames = layerNames;
|
|
createInfo.enabledExtensionCount = instanceExtensionCount;
|
|
createInfo.ppEnabledExtensionNames = instanceExtensionNames;
|
|
if (renderer->debugMode) {
|
|
createInfo.enabledLayerCount = SDL_arraysize(layerNames);
|
|
if (!VULKAN_INTERNAL_CheckValidationLayers(
|
|
layerNames,
|
|
createInfo.enabledLayerCount)) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Validation layers not found, continuing without validation");
|
|
createInfo.enabledLayerCount = 0;
|
|
} else {
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Validation layers enabled, expect debug level performance!");
|
|
}
|
|
} else {
|
|
createInfo.enabledLayerCount = 0;
|
|
}
|
|
|
|
vulkanResult = vkCreateInstance(&createInfo, NULL, &renderer->instance);
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
SDL_LogError(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"vkCreateInstance failed: %s",
|
|
VkErrorMessages(vulkanResult));
|
|
|
|
SDL_stack_free((char *)instanceExtensionNames);
|
|
return 0;
|
|
}
|
|
|
|
SDL_stack_free((char *)instanceExtensionNames);
|
|
return 1;
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_IsDeviceSuitable(
|
|
VulkanRenderer *renderer,
|
|
VkPhysicalDevice physicalDevice,
|
|
VulkanExtensions *physicalDeviceExtensions,
|
|
Uint32 *queueFamilyIndex,
|
|
Uint8 *deviceRank)
|
|
{
|
|
Uint32 queueFamilyCount, queueFamilyRank, queueFamilyBest;
|
|
VkQueueFamilyProperties *queueProps;
|
|
bool supportsPresent;
|
|
VkPhysicalDeviceProperties deviceProperties;
|
|
Uint32 i;
|
|
|
|
const Uint8 *devicePriority = renderer->preferLowPower ? DEVICE_PRIORITY_LOWPOWER : DEVICE_PRIORITY_HIGHPERFORMANCE;
|
|
|
|
/* Get the device rank before doing any checks, in case one fails.
|
|
* Note: If no dedicated device exists, one that supports our features
|
|
* would be fine
|
|
*/
|
|
renderer->vkGetPhysicalDeviceProperties(
|
|
physicalDevice,
|
|
&deviceProperties);
|
|
if (*deviceRank < devicePriority[deviceProperties.deviceType]) {
|
|
/* This device outranks the best device we've found so far!
|
|
* This includes a dedicated GPU that has less features than an
|
|
* integrated GPU, because this is a freak case that is almost
|
|
* never intentionally desired by the end user
|
|
*/
|
|
*deviceRank = devicePriority[deviceProperties.deviceType];
|
|
} else if (*deviceRank > devicePriority[deviceProperties.deviceType]) {
|
|
/* Device is outranked by a previous device, don't even try to
|
|
* run a query and reset the rank to avoid overwrites
|
|
*/
|
|
*deviceRank = 0;
|
|
return 0;
|
|
}
|
|
|
|
if (!VULKAN_INTERNAL_CheckDeviceExtensions(
|
|
renderer,
|
|
physicalDevice,
|
|
physicalDeviceExtensions)) {
|
|
return 0;
|
|
}
|
|
|
|
renderer->vkGetPhysicalDeviceQueueFamilyProperties(
|
|
physicalDevice,
|
|
&queueFamilyCount,
|
|
NULL);
|
|
|
|
queueProps = (VkQueueFamilyProperties *)SDL_stack_alloc(
|
|
VkQueueFamilyProperties,
|
|
queueFamilyCount);
|
|
renderer->vkGetPhysicalDeviceQueueFamilyProperties(
|
|
physicalDevice,
|
|
&queueFamilyCount,
|
|
queueProps);
|
|
|
|
queueFamilyBest = 0;
|
|
*queueFamilyIndex = UINT32_MAX;
|
|
for (i = 0; i < queueFamilyCount; i += 1) {
|
|
supportsPresent = SDL_Vulkan_GetPresentationSupport(
|
|
renderer->instance,
|
|
physicalDevice,
|
|
i);
|
|
if (!supportsPresent ||
|
|
!(queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) {
|
|
// Not a graphics family, ignore.
|
|
continue;
|
|
}
|
|
|
|
/* The queue family bitflags are kind of annoying.
|
|
*
|
|
* We of course need a graphics family, but we ideally want the
|
|
* _primary_ graphics family. The spec states that at least one
|
|
* graphics family must also be a compute family, so generally
|
|
* drivers make that the first one. But hey, maybe something
|
|
* genuinely can't do compute or something, and FNA doesn't
|
|
* need it, so we'll be open to a non-compute queue family.
|
|
*
|
|
* Additionally, it's common to see the primary queue family
|
|
* have the transfer bit set, which is great! But this is
|
|
* actually optional; it's impossible to NOT have transfers in
|
|
* graphics/compute but it _is_ possible for a graphics/compute
|
|
* family, even the primary one, to just decide not to set the
|
|
* bitflag. Admittedly, a driver may want to isolate transfer
|
|
* queues to a dedicated family so that queues made solely for
|
|
* transfers can have an optimized DMA queue.
|
|
*
|
|
* That, or the driver author got lazy and decided not to set
|
|
* the bit. Looking at you, Android.
|
|
*
|
|
* -flibit
|
|
*/
|
|
if (queueProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT) {
|
|
if (queueProps[i].queueFlags & VK_QUEUE_TRANSFER_BIT) {
|
|
// Has all attribs!
|
|
queueFamilyRank = 3;
|
|
} else {
|
|
// Probably has a DMA transfer queue family
|
|
queueFamilyRank = 2;
|
|
}
|
|
} else {
|
|
// Just a graphics family, probably has something better
|
|
queueFamilyRank = 1;
|
|
}
|
|
if (queueFamilyRank > queueFamilyBest) {
|
|
*queueFamilyIndex = i;
|
|
queueFamilyBest = queueFamilyRank;
|
|
}
|
|
}
|
|
|
|
SDL_stack_free(queueProps);
|
|
|
|
if (*queueFamilyIndex == UINT32_MAX) {
|
|
// Somehow no graphics queues existed. Compute-only device?
|
|
return 0;
|
|
}
|
|
|
|
// FIXME: Need better structure for checking vs storing swapchain support details
|
|
return 1;
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_DeterminePhysicalDevice(VulkanRenderer *renderer)
|
|
{
|
|
VkResult vulkanResult;
|
|
VkPhysicalDevice *physicalDevices;
|
|
VulkanExtensions *physicalDeviceExtensions;
|
|
Uint32 physicalDeviceCount, i, suitableIndex;
|
|
Uint32 queueFamilyIndex, suitableQueueFamilyIndex;
|
|
Uint8 deviceRank, highestRank;
|
|
|
|
vulkanResult = renderer->vkEnumeratePhysicalDevices(
|
|
renderer->instance,
|
|
&physicalDeviceCount,
|
|
NULL);
|
|
VULKAN_ERROR_CHECK(vulkanResult, vkEnumeratePhysicalDevices, 0)
|
|
|
|
if (physicalDeviceCount == 0) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Failed to find any GPUs with Vulkan support");
|
|
return 0;
|
|
}
|
|
|
|
physicalDevices = SDL_stack_alloc(VkPhysicalDevice, physicalDeviceCount);
|
|
physicalDeviceExtensions = SDL_stack_alloc(VulkanExtensions, physicalDeviceCount);
|
|
|
|
vulkanResult = renderer->vkEnumeratePhysicalDevices(
|
|
renderer->instance,
|
|
&physicalDeviceCount,
|
|
physicalDevices);
|
|
|
|
/* This should be impossible to hit, but from what I can tell this can
|
|
* be triggered not because the array is too small, but because there
|
|
* were drivers that turned out to be bogus, so this is the loader's way
|
|
* of telling us that the list is now smaller than expected :shrug:
|
|
*/
|
|
if (vulkanResult == VK_INCOMPLETE) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "vkEnumeratePhysicalDevices returned VK_INCOMPLETE, will keep trying anyway...");
|
|
vulkanResult = VK_SUCCESS;
|
|
}
|
|
|
|
if (vulkanResult != VK_SUCCESS) {
|
|
SDL_LogWarn(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"vkEnumeratePhysicalDevices failed: %s",
|
|
VkErrorMessages(vulkanResult));
|
|
SDL_stack_free(physicalDevices);
|
|
SDL_stack_free(physicalDeviceExtensions);
|
|
return 0;
|
|
}
|
|
|
|
// Any suitable device will do, but we'd like the best
|
|
suitableIndex = -1;
|
|
suitableQueueFamilyIndex = 0;
|
|
highestRank = 0;
|
|
for (i = 0; i < physicalDeviceCount; i += 1) {
|
|
deviceRank = highestRank;
|
|
if (VULKAN_INTERNAL_IsDeviceSuitable(
|
|
renderer,
|
|
physicalDevices[i],
|
|
&physicalDeviceExtensions[i],
|
|
&queueFamilyIndex,
|
|
&deviceRank)) {
|
|
/* Use this for rendering.
|
|
* Note that this may override a previous device that
|
|
* supports rendering, but shares the same device rank.
|
|
*/
|
|
suitableIndex = i;
|
|
suitableQueueFamilyIndex = queueFamilyIndex;
|
|
highestRank = deviceRank;
|
|
} else if (deviceRank > highestRank) {
|
|
/* In this case, we found a... "realer?" GPU,
|
|
* but it doesn't actually support our Vulkan.
|
|
* We should disqualify all devices below as a
|
|
* result, because if we don't we end up
|
|
* ignoring real hardware and risk using
|
|
* something like LLVMpipe instead!
|
|
* -flibit
|
|
*/
|
|
suitableIndex = -1;
|
|
highestRank = deviceRank;
|
|
}
|
|
}
|
|
|
|
if (suitableIndex != -1) {
|
|
renderer->supports = physicalDeviceExtensions[suitableIndex];
|
|
renderer->physicalDevice = physicalDevices[suitableIndex];
|
|
renderer->queueFamilyIndex = suitableQueueFamilyIndex;
|
|
} else {
|
|
SDL_stack_free(physicalDevices);
|
|
SDL_stack_free(physicalDeviceExtensions);
|
|
return 0;
|
|
}
|
|
|
|
renderer->physicalDeviceProperties.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
|
if (renderer->supports.KHR_driver_properties) {
|
|
renderer->physicalDeviceDriverProperties.sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
|
|
renderer->physicalDeviceDriverProperties.pNext = NULL;
|
|
|
|
renderer->physicalDeviceProperties.pNext =
|
|
&renderer->physicalDeviceDriverProperties;
|
|
|
|
renderer->vkGetPhysicalDeviceProperties2KHR(
|
|
renderer->physicalDevice,
|
|
&renderer->physicalDeviceProperties);
|
|
} else {
|
|
renderer->physicalDeviceProperties.pNext = NULL;
|
|
|
|
renderer->vkGetPhysicalDeviceProperties(
|
|
renderer->physicalDevice,
|
|
&renderer->physicalDeviceProperties.properties);
|
|
}
|
|
|
|
renderer->vkGetPhysicalDeviceMemoryProperties(
|
|
renderer->physicalDevice,
|
|
&renderer->memoryProperties);
|
|
|
|
SDL_stack_free(physicalDevices);
|
|
SDL_stack_free(physicalDeviceExtensions);
|
|
return 1;
|
|
}
|
|
|
|
static Uint8 VULKAN_INTERNAL_CreateLogicalDevice(
|
|
VulkanRenderer *renderer)
|
|
{
|
|
VkResult vulkanResult;
|
|
VkDeviceCreateInfo deviceCreateInfo;
|
|
VkPhysicalDeviceFeatures desiredDeviceFeatures;
|
|
VkPhysicalDeviceFeatures haveDeviceFeatures;
|
|
VkPhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures;
|
|
const char **deviceExtensions;
|
|
|
|
VkDeviceQueueCreateInfo queueCreateInfo;
|
|
float queuePriority = 1.0f;
|
|
|
|
queueCreateInfo.sType =
|
|
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queueCreateInfo.pNext = NULL;
|
|
queueCreateInfo.flags = 0;
|
|
queueCreateInfo.queueFamilyIndex = renderer->queueFamilyIndex;
|
|
queueCreateInfo.queueCount = 1;
|
|
queueCreateInfo.pQueuePriorities = &queuePriority;
|
|
|
|
// check feature support
|
|
|
|
renderer->vkGetPhysicalDeviceFeatures(
|
|
renderer->physicalDevice,
|
|
&haveDeviceFeatures);
|
|
|
|
// specifying used device features
|
|
|
|
SDL_zero(desiredDeviceFeatures);
|
|
desiredDeviceFeatures.independentBlend = VK_TRUE;
|
|
desiredDeviceFeatures.samplerAnisotropy = VK_TRUE;
|
|
|
|
if (haveDeviceFeatures.fillModeNonSolid) {
|
|
desiredDeviceFeatures.fillModeNonSolid = VK_TRUE;
|
|
renderer->supportsFillModeNonSolid = true;
|
|
}
|
|
|
|
if (haveDeviceFeatures.multiDrawIndirect) {
|
|
desiredDeviceFeatures.multiDrawIndirect = VK_TRUE;
|
|
renderer->supportsMultiDrawIndirect = true;
|
|
}
|
|
|
|
// creating the logical device
|
|
|
|
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
if (renderer->supports.KHR_portability_subset) {
|
|
portabilityFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR;
|
|
portabilityFeatures.pNext = NULL;
|
|
portabilityFeatures.constantAlphaColorBlendFactors = VK_FALSE;
|
|
portabilityFeatures.events = VK_FALSE;
|
|
portabilityFeatures.imageViewFormatReinterpretation = VK_FALSE;
|
|
portabilityFeatures.imageViewFormatSwizzle = VK_TRUE;
|
|
portabilityFeatures.imageView2DOn3DImage = VK_FALSE;
|
|
portabilityFeatures.multisampleArrayImage = VK_FALSE;
|
|
portabilityFeatures.mutableComparisonSamplers = VK_FALSE;
|
|
portabilityFeatures.pointPolygons = VK_FALSE;
|
|
portabilityFeatures.samplerMipLodBias = VK_FALSE; // Technically should be true, but eh
|
|
portabilityFeatures.separateStencilMaskRef = VK_FALSE;
|
|
portabilityFeatures.shaderSampleRateInterpolationFunctions = VK_FALSE;
|
|
portabilityFeatures.tessellationIsolines = VK_FALSE;
|
|
portabilityFeatures.tessellationPointMode = VK_FALSE;
|
|
portabilityFeatures.triangleFans = VK_FALSE;
|
|
portabilityFeatures.vertexAttributeAccessBeyondStride = VK_FALSE;
|
|
deviceCreateInfo.pNext = &portabilityFeatures;
|
|
} else {
|
|
deviceCreateInfo.pNext = NULL;
|
|
}
|
|
deviceCreateInfo.flags = 0;
|
|
deviceCreateInfo.queueCreateInfoCount = 1;
|
|
deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
|
|
deviceCreateInfo.enabledLayerCount = 0;
|
|
deviceCreateInfo.ppEnabledLayerNames = NULL;
|
|
deviceCreateInfo.enabledExtensionCount = GetDeviceExtensionCount(
|
|
&renderer->supports);
|
|
deviceExtensions = SDL_stack_alloc(
|
|
const char *,
|
|
deviceCreateInfo.enabledExtensionCount);
|
|
CreateDeviceExtensionArray(&renderer->supports, deviceExtensions);
|
|
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions;
|
|
deviceCreateInfo.pEnabledFeatures = &desiredDeviceFeatures;
|
|
|
|
vulkanResult = renderer->vkCreateDevice(
|
|
renderer->physicalDevice,
|
|
&deviceCreateInfo,
|
|
NULL,
|
|
&renderer->logicalDevice);
|
|
SDL_stack_free(deviceExtensions);
|
|
VULKAN_ERROR_CHECK(vulkanResult, vkCreateDevice, 0)
|
|
|
|
// Load vkDevice entry points
|
|
|
|
#define VULKAN_DEVICE_FUNCTION(func) \
|
|
renderer->func = (PFN_##func) \
|
|
renderer->vkGetDeviceProcAddr( \
|
|
renderer->logicalDevice, \
|
|
#func);
|
|
#include "SDL_gpu_vulkan_vkfuncs.h"
|
|
|
|
renderer->vkGetDeviceQueue(
|
|
renderer->logicalDevice,
|
|
renderer->queueFamilyIndex,
|
|
0,
|
|
&renderer->unifiedQueue);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void VULKAN_INTERNAL_LoadEntryPoints(void)
|
|
{
|
|
// Required for MoltenVK support
|
|
SDL_setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
|
|
|
|
// Load Vulkan entry points
|
|
if (!SDL_Vulkan_LoadLibrary(NULL)) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Vulkan: SDL_Vulkan_LoadLibrary failed!");
|
|
return;
|
|
}
|
|
|
|
#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
#endif
|
|
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr();
|
|
#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
if (vkGetInstanceProcAddr == NULL) {
|
|
SDL_LogWarn(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"SDL_Vulkan_GetVkGetInstanceProcAddr(): %s",
|
|
SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
#define VULKAN_GLOBAL_FUNCTION(name) \
|
|
name = (PFN_##name)vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \
|
|
if (name == NULL) { \
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed"); \
|
|
return; \
|
|
}
|
|
#include "SDL_gpu_vulkan_vkfuncs.h"
|
|
}
|
|
|
|
static bool VULKAN_INTERNAL_PrepareVulkan(
|
|
VulkanRenderer *renderer)
|
|
{
|
|
VULKAN_INTERNAL_LoadEntryPoints();
|
|
|
|
if (!VULKAN_INTERNAL_CreateInstance(renderer)) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Vulkan: Could not create Vulkan instance");
|
|
return false;
|
|
}
|
|
|
|
#define VULKAN_INSTANCE_FUNCTION(func) \
|
|
renderer->func = (PFN_##func)vkGetInstanceProcAddr(renderer->instance, #func);
|
|
#include "SDL_gpu_vulkan_vkfuncs.h"
|
|
|
|
if (!VULKAN_INTERNAL_DeterminePhysicalDevice(renderer)) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Vulkan: Failed to determine a suitable physical device");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool VULKAN_PrepareDriver(SDL_VideoDevice *_this)
|
|
{
|
|
// Set up dummy VulkanRenderer
|
|
VulkanRenderer *renderer;
|
|
Uint8 result;
|
|
|
|
if (_this->Vulkan_CreateSurface == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (!SDL_Vulkan_LoadLibrary(NULL)) {
|
|
return false;
|
|
}
|
|
|
|
renderer = (VulkanRenderer *)SDL_malloc(sizeof(VulkanRenderer));
|
|
SDL_memset(renderer, '\0', sizeof(VulkanRenderer));
|
|
|
|
result = VULKAN_INTERNAL_PrepareVulkan(renderer);
|
|
|
|
if (result) {
|
|
renderer->vkDestroyInstance(renderer->instance, NULL);
|
|
}
|
|
SDL_free(renderer);
|
|
SDL_Vulkan_UnloadLibrary();
|
|
return result;
|
|
}
|
|
|
|
static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, SDL_PropertiesID props)
|
|
{
|
|
VulkanRenderer *renderer;
|
|
|
|
SDL_GPUDevice *result;
|
|
VkResult vulkanResult;
|
|
Uint32 i;
|
|
|
|
// Variables: Image Format Detection
|
|
VkImageFormatProperties imageFormatProperties;
|
|
|
|
if (!SDL_Vulkan_LoadLibrary(NULL)) {
|
|
SDL_assert(!"This should have failed in PrepareDevice first!");
|
|
return NULL;
|
|
}
|
|
|
|
renderer = (VulkanRenderer *)SDL_malloc(sizeof(VulkanRenderer));
|
|
SDL_memset(renderer, '\0', sizeof(VulkanRenderer));
|
|
renderer->debugMode = debugMode;
|
|
renderer->preferLowPower = preferLowPower;
|
|
|
|
if (!VULKAN_INTERNAL_PrepareVulkan(renderer)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to initialize Vulkan!");
|
|
SDL_free(renderer);
|
|
SDL_Vulkan_UnloadLibrary();
|
|
return NULL;
|
|
}
|
|
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: Vulkan");
|
|
SDL_LogInfo(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"Vulkan Device: %s",
|
|
renderer->physicalDeviceProperties.properties.deviceName);
|
|
if (renderer->supports.KHR_driver_properties) {
|
|
SDL_LogInfo(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"Vulkan Driver: %s %s",
|
|
renderer->physicalDeviceDriverProperties.driverName,
|
|
renderer->physicalDeviceDriverProperties.driverInfo);
|
|
SDL_LogInfo(
|
|
SDL_LOG_CATEGORY_GPU,
|
|
"Vulkan Conformance: %u.%u.%u",
|
|
renderer->physicalDeviceDriverProperties.conformanceVersion.major,
|
|
renderer->physicalDeviceDriverProperties.conformanceVersion.minor,
|
|
renderer->physicalDeviceDriverProperties.conformanceVersion.patch);
|
|
} else {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "KHR_driver_properties unsupported! Bother your vendor about this!");
|
|
}
|
|
|
|
if (!VULKAN_INTERNAL_CreateLogicalDevice(
|
|
renderer)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create logical device");
|
|
SDL_free(renderer);
|
|
SDL_Vulkan_UnloadLibrary();
|
|
return NULL;
|
|
}
|
|
|
|
// FIXME: just move this into this function
|
|
result = (SDL_GPUDevice *)SDL_malloc(sizeof(SDL_GPUDevice));
|
|
ASSIGN_DRIVER(VULKAN)
|
|
|
|
result->driverData = (SDL_GPURenderer *)renderer;
|
|
|
|
/*
|
|
* Create initial swapchain array
|
|
*/
|
|
|
|
renderer->claimedWindowCapacity = 1;
|
|
renderer->claimedWindowCount = 0;
|
|
renderer->claimedWindows = SDL_malloc(
|
|
renderer->claimedWindowCapacity * sizeof(WindowData *));
|
|
|
|
// Threading
|
|
|
|
renderer->allocatorLock = SDL_CreateMutex();
|
|
renderer->disposeLock = SDL_CreateMutex();
|
|
renderer->submitLock = SDL_CreateMutex();
|
|
renderer->acquireCommandBufferLock = SDL_CreateMutex();
|
|
renderer->acquireUniformBufferLock = SDL_CreateMutex();
|
|
renderer->renderPassFetchLock = SDL_CreateMutex();
|
|
renderer->framebufferFetchLock = SDL_CreateMutex();
|
|
|
|
/*
|
|
* Create submitted command buffer list
|
|
*/
|
|
|
|
renderer->submittedCommandBufferCapacity = 16;
|
|
renderer->submittedCommandBufferCount = 0;
|
|
renderer->submittedCommandBuffers = SDL_malloc(sizeof(VulkanCommandBuffer *) * renderer->submittedCommandBufferCapacity);
|
|
|
|
// Memory Allocator
|
|
|
|
renderer->memoryAllocator = (VulkanMemoryAllocator *)SDL_malloc(
|
|
sizeof(VulkanMemoryAllocator));
|
|
|
|
for (i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) {
|
|
renderer->memoryAllocator->subAllocators[i].memoryTypeIndex = i;
|
|
renderer->memoryAllocator->subAllocators[i].allocations = NULL;
|
|
renderer->memoryAllocator->subAllocators[i].allocationCount = 0;
|
|
renderer->memoryAllocator->subAllocators[i].sortedFreeRegions = SDL_malloc(
|
|
sizeof(VulkanMemoryFreeRegion *) * 4);
|
|
renderer->memoryAllocator->subAllocators[i].sortedFreeRegionCount = 0;
|
|
renderer->memoryAllocator->subAllocators[i].sortedFreeRegionCapacity = 4;
|
|
}
|
|
|
|
// Create uniform buffer pool
|
|
|
|
renderer->uniformBufferPoolCount = 32;
|
|
renderer->uniformBufferPoolCapacity = 32;
|
|
renderer->uniformBufferPool = SDL_malloc(
|
|
renderer->uniformBufferPoolCapacity * sizeof(VulkanUniformBuffer *));
|
|
|
|
for (i = 0; i < renderer->uniformBufferPoolCount; i += 1) {
|
|
renderer->uniformBufferPool[i] = VULKAN_INTERNAL_CreateUniformBuffer(
|
|
renderer,
|
|
UNIFORM_BUFFER_SIZE);
|
|
}
|
|
|
|
// Device limits
|
|
|
|
renderer->minUBOAlignment = (Uint32)renderer->physicalDeviceProperties.properties.limits.minUniformBufferOffsetAlignment;
|
|
|
|
// Initialize caches
|
|
|
|
renderer->commandPoolHashTable = SDL_CreateHashTable(
|
|
(void *)renderer,
|
|
64,
|
|
VULKAN_INTERNAL_CommandPoolHashFunction,
|
|
VULKAN_INTERNAL_CommandPoolHashKeyMatch,
|
|
VULKAN_INTERNAL_CommandPoolHashNuke,
|
|
false);
|
|
|
|
renderer->renderPassHashTable = SDL_CreateHashTable(
|
|
(void *)renderer,
|
|
64,
|
|
VULKAN_INTERNAL_RenderPassHashFunction,
|
|
VULKAN_INTERNAL_RenderPassHashKeyMatch,
|
|
VULKAN_INTERNAL_RenderPassHashNuke,
|
|
false);
|
|
|
|
renderer->framebufferHashTable = SDL_CreateHashTable(
|
|
(void *)renderer,
|
|
64,
|
|
VULKAN_INTERNAL_FramebufferHashFunction,
|
|
VULKAN_INTERNAL_FramebufferHashKeyMatch,
|
|
VULKAN_INTERNAL_FramebufferHashNuke,
|
|
false);
|
|
|
|
// Initialize fence pool
|
|
|
|
renderer->fencePool.lock = SDL_CreateMutex();
|
|
|
|
renderer->fencePool.availableFenceCapacity = 4;
|
|
renderer->fencePool.availableFenceCount = 0;
|
|
renderer->fencePool.availableFences = SDL_malloc(
|
|
renderer->fencePool.availableFenceCapacity * sizeof(VulkanFenceHandle *));
|
|
|
|
// Some drivers don't support D16, so we have to fall back to D32.
|
|
|
|
vulkanResult = renderer->vkGetPhysicalDeviceImageFormatProperties(
|
|
renderer->physicalDevice,
|
|
VK_FORMAT_D16_UNORM,
|
|
VK_IMAGE_TYPE_2D,
|
|
VK_IMAGE_TILING_OPTIMAL,
|
|
VK_IMAGE_ASPECT_DEPTH_BIT,
|
|
0,
|
|
&imageFormatProperties);
|
|
|
|
if (vulkanResult == VK_ERROR_FORMAT_NOT_SUPPORTED) {
|
|
renderer->D16Format = VK_FORMAT_D32_SFLOAT;
|
|
} else {
|
|
renderer->D16Format = VK_FORMAT_D16_UNORM;
|
|
}
|
|
|
|
vulkanResult = renderer->vkGetPhysicalDeviceImageFormatProperties(
|
|
renderer->physicalDevice,
|
|
VK_FORMAT_D16_UNORM_S8_UINT,
|
|
VK_IMAGE_TYPE_2D,
|
|
VK_IMAGE_TILING_OPTIMAL,
|
|
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
|
|
0,
|
|
&imageFormatProperties);
|
|
|
|
if (vulkanResult == VK_ERROR_FORMAT_NOT_SUPPORTED) {
|
|
renderer->D16S8Format = VK_FORMAT_D32_SFLOAT_S8_UINT;
|
|
} else {
|
|
renderer->D16S8Format = VK_FORMAT_D16_UNORM_S8_UINT;
|
|
}
|
|
|
|
// Deferred destroy storage
|
|
|
|
renderer->texturesToDestroyCapacity = 16;
|
|
renderer->texturesToDestroyCount = 0;
|
|
|
|
renderer->texturesToDestroy = (VulkanTexture **)SDL_malloc(
|
|
sizeof(VulkanTexture *) *
|
|
renderer->texturesToDestroyCapacity);
|
|
|
|
renderer->buffersToDestroyCapacity = 16;
|
|
renderer->buffersToDestroyCount = 0;
|
|
|
|
renderer->buffersToDestroy = SDL_malloc(
|
|
sizeof(VulkanBuffer *) *
|
|
renderer->buffersToDestroyCapacity);
|
|
|
|
renderer->samplersToDestroyCapacity = 16;
|
|
renderer->samplersToDestroyCount = 0;
|
|
|
|
renderer->samplersToDestroy = SDL_malloc(
|
|
sizeof(VulkanSampler *) *
|
|
renderer->samplersToDestroyCapacity);
|
|
|
|
renderer->graphicsPipelinesToDestroyCapacity = 16;
|
|
renderer->graphicsPipelinesToDestroyCount = 0;
|
|
|
|
renderer->graphicsPipelinesToDestroy = SDL_malloc(
|
|
sizeof(VulkanGraphicsPipeline *) *
|
|
renderer->graphicsPipelinesToDestroyCapacity);
|
|
|
|
renderer->computePipelinesToDestroyCapacity = 16;
|
|
renderer->computePipelinesToDestroyCount = 0;
|
|
|
|
renderer->computePipelinesToDestroy = SDL_malloc(
|
|
sizeof(VulkanComputePipeline *) *
|
|
renderer->computePipelinesToDestroyCapacity);
|
|
|
|
renderer->shadersToDestroyCapacity = 16;
|
|
renderer->shadersToDestroyCount = 0;
|
|
|
|
renderer->shadersToDestroy = SDL_malloc(
|
|
sizeof(VulkanShader *) *
|
|
renderer->shadersToDestroyCapacity);
|
|
|
|
renderer->framebuffersToDestroyCapacity = 16;
|
|
renderer->framebuffersToDestroyCount = 0;
|
|
renderer->framebuffersToDestroy = SDL_malloc(
|
|
sizeof(VulkanFramebuffer *) *
|
|
renderer->framebuffersToDestroyCapacity);
|
|
|
|
// Defrag state
|
|
|
|
renderer->defragInProgress = 0;
|
|
|
|
renderer->allocationsToDefragCount = 0;
|
|
renderer->allocationsToDefragCapacity = 4;
|
|
renderer->allocationsToDefrag = SDL_malloc(
|
|
renderer->allocationsToDefragCapacity * sizeof(VulkanMemoryAllocation *));
|
|
|
|
return result;
|
|
}
|
|
|
|
SDL_GPUBootstrap VulkanDriver = {
|
|
"Vulkan",
|
|
SDL_GPU_DRIVER_VULKAN,
|
|
SDL_GPU_SHADERFORMAT_SPIRV,
|
|
VULKAN_PrepareDriver,
|
|
VULKAN_CreateDevice
|
|
};
|
|
|
|
#endif // SDL_GPU_VULKAN
|