mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-05-30 08:27:39 +00:00
8416 lines
303 KiB
C
8416 lines
303 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"
|
|
|
|
#ifdef SDL_GPU_D3D12
|
|
|
|
#include "../../core/windows/SDL_windows.h"
|
|
#include "../../video/directx/SDL_d3d12.h"
|
|
#include "../SDL_sysgpu.h"
|
|
#include "SDL_hashtable.h"
|
|
|
|
#ifdef __IDXGIInfoQueue_INTERFACE_DEFINED__
|
|
#define HAVE_IDXGIINFOQUEUE
|
|
#endif
|
|
|
|
// Built-in shaders, compiled with compile_shaders.bat
|
|
|
|
#define g_FullscreenVert D3D12_FullscreenVert
|
|
#define g_BlitFrom2D D3D12_BlitFrom2D
|
|
#define g_BlitFrom2DArray D3D12_BlitFrom2DArray
|
|
#define g_BlitFrom3D D3D12_BlitFrom3D
|
|
#define g_BlitFromCube D3D12_BlitFromCube
|
|
#define g_BlitFromCubeArray D3D12_BlitFromCubeArray
|
|
#if defined(SDL_PLATFORM_XBOXSERIES)
|
|
#include "D3D12_Blit_Series.h"
|
|
#elif defined(SDL_PLATFORM_XBOXONE)
|
|
#include "D3D12_Blit_One.h"
|
|
#else
|
|
#include "D3D12_Blit.h"
|
|
#endif
|
|
#undef g_FullscreenVert
|
|
#undef g_BlitFrom2D
|
|
#undef g_BlitFrom2DArray
|
|
#undef g_BlitFrom3D
|
|
#undef g_BlitFromCube
|
|
#undef g_BlitFromCubeArray
|
|
|
|
// Macros
|
|
|
|
#define ERROR_CHECK(msg) \
|
|
if (FAILED(res)) { \
|
|
D3D12_INTERNAL_LogError(renderer->device, msg, res); \
|
|
}
|
|
|
|
#define ERROR_CHECK_RETURN(msg, ret) \
|
|
if (FAILED(res)) { \
|
|
D3D12_INTERNAL_LogError(renderer->device, msg, res); \
|
|
return ret; \
|
|
}
|
|
|
|
// Defines
|
|
#if defined(_WIN32)
|
|
#if defined(SDL_PLATFORM_XBOXSERIES)
|
|
#define D3D12_DLL "d3d12_xs.dll"
|
|
#elif defined(SDL_PLATFORM_XBOXONE)
|
|
#define D3D12_DLL "d3d12_x.dll"
|
|
#else
|
|
#define D3D12_DLL "d3d12.dll"
|
|
#endif
|
|
#define DXGI_DLL "dxgi.dll"
|
|
#define DXGIDEBUG_DLL "dxgidebug.dll"
|
|
#elif defined(__APPLE__)
|
|
#define D3D12_DLL "libdxvk_d3d12.dylib"
|
|
#define DXGI_DLL "libdxvk_dxgi.dylib"
|
|
#define DXGIDEBUG_DLL "libdxvk_dxgidebug.dylib"
|
|
#else
|
|
#define D3D12_DLL "libdxvk_d3d12.so"
|
|
#define DXGI_DLL "libdxvk_dxgi.so"
|
|
#define DXGIDEBUG_DLL "libdxvk_dxgidebug.so"
|
|
#endif
|
|
|
|
#define D3D12_CREATE_DEVICE_FUNC "D3D12CreateDevice"
|
|
#define D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC "D3D12SerializeRootSignature"
|
|
#define CREATE_DXGI_FACTORY1_FUNC "CreateDXGIFactory1"
|
|
#define DXGI_GET_DEBUG_INTERFACE_FUNC "DXGIGetDebugInterface"
|
|
#define D3D12_GET_DEBUG_INTERFACE_FUNC "D3D12GetDebugInterface"
|
|
#define WINDOW_PROPERTY_DATA "SDL_GPUD3D12WindowPropertyData"
|
|
#define D3D_FEATURE_LEVEL_CHOICE D3D_FEATURE_LEVEL_11_1
|
|
#define D3D_FEATURE_LEVEL_CHOICE_STR "11_1"
|
|
// FIXME: just use sysgpu.h defines
|
|
#define MAX_ROOT_SIGNATURE_PARAMETERS 64
|
|
#define VIEW_GPU_DESCRIPTOR_COUNT 65536
|
|
#define SAMPLER_GPU_DESCRIPTOR_COUNT 2048
|
|
#define VIEW_SAMPLER_STAGING_DESCRIPTOR_COUNT 1000000
|
|
#define TARGET_STAGING_DESCRIPTOR_COUNT 1000000
|
|
#define D3D12_FENCE_UNSIGNALED_VALUE 0
|
|
#define D3D12_FENCE_SIGNAL_VALUE 1
|
|
|
|
#define SDL_GPU_SHADERSTAGE_COMPUTE (SDL_GPUShaderStage)2
|
|
|
|
#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)); \
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
#define HRESULT_FMT "(0x%08lX)"
|
|
#else
|
|
#define HRESULT_FMT "(0x%08X)"
|
|
#endif
|
|
|
|
// Function Pointer Signatures
|
|
typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY1)(const GUID *riid, void **ppFactory);
|
|
typedef HRESULT(WINAPI *PFN_DXGI_GET_DEBUG_INTERFACE)(const GUID *riid, void **ppDebug);
|
|
|
|
// IIDs (from https://www.magnumdb.com/)
|
|
static const IID D3D_IID_IDXGIFactory1 = { 0x770aae78, 0xf26f, 0x4dba, { 0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87 } };
|
|
static const IID D3D_IID_IDXGIFactory4 = { 0x1bc6ea02, 0xef36, 0x464f, { 0xbf, 0x0c, 0x21, 0xca, 0x39, 0xe5, 0x16, 0x8a } };
|
|
static const IID D3D_IID_IDXGIFactory5 = { 0x7632e1f5, 0xee65, 0x4dca, { 0x87, 0xfd, 0x84, 0xcd, 0x75, 0xf8, 0x83, 0x8d } };
|
|
static const IID D3D_IID_IDXGIFactory6 = { 0xc1b6694f, 0xff09, 0x44a9, { 0xb0, 0x3c, 0x77, 0x90, 0x0a, 0x0a, 0x1d, 0x17 } };
|
|
static const IID D3D_IID_IDXGIAdapter1 = { 0x29038f61, 0x3839, 0x4626, { 0x91, 0xfd, 0x08, 0x68, 0x79, 0x01, 0x1a, 0x05 } };
|
|
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
static const IID D3D_IID_IDXGIDevice1 = { 0x77db970f, 0x6276, 0x48ba, { 0xba, 0x28, 0x07, 0x01, 0x43, 0xb4, 0x39, 0x2c } };
|
|
#endif
|
|
static const IID D3D_IID_IDXGISwapChain3 = { 0x94d99bdb, 0xf1f8, 0x4ab0, { 0xb2, 0x36, 0x7d, 0xa0, 0x17, 0x0e, 0xda, 0xb1 } };
|
|
#ifdef HAVE_IDXGIINFOQUEUE
|
|
static const IID D3D_IID_IDXGIDebug = { 0x119e7452, 0xde9e, 0x40fe, { 0x88, 0x06, 0x88, 0xf9, 0x0c, 0x12, 0xb4, 0x41 } };
|
|
static const IID D3D_IID_IDXGIInfoQueue = { 0xd67441c7, 0x672a, 0x476f, { 0x9e, 0x82, 0xcd, 0x55, 0xb4, 0x49, 0x49, 0xce } };
|
|
#endif
|
|
static const GUID D3D_IID_DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, { 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x08 } };
|
|
static const GUID D3D_IID_D3DDebugObjectName = { 0x429b8c22, 0x9188, 0x4b0c, { 0x87, 0x42, 0xac, 0xb0, 0xbf, 0x85, 0xc2, 0x00 } };
|
|
|
|
static const IID D3D_IID_ID3D12Device = { 0x189819f1, 0x1db6, 0x4b57, { 0xbe, 0x54, 0x18, 0x21, 0x33, 0x9b, 0x85, 0xf7 } };
|
|
static const IID D3D_IID_ID3D12CommandQueue = { 0x0ec870a6, 0x5d7e, 0x4c22, { 0x8c, 0xfc, 0x5b, 0xaa, 0xe0, 0x76, 0x16, 0xed } };
|
|
static const IID D3D_IID_ID3D12DescriptorHeap = { 0x8efb471d, 0x616c, 0x4f49, { 0x90, 0xf7, 0x12, 0x7b, 0xb7, 0x63, 0xfa, 0x51 } };
|
|
static const IID D3D_IID_ID3D12Resource = { 0x696442be, 0xa72e, 0x4059, { 0xbc, 0x79, 0x5b, 0x5c, 0x98, 0x04, 0x0f, 0xad } };
|
|
static const IID D3D_IID_ID3D12CommandAllocator = { 0x6102dee4, 0xaf59, 0x4b09, { 0xb9, 0x99, 0xb4, 0x4d, 0x73, 0xf0, 0x9b, 0x24 } };
|
|
static const IID D3D_IID_ID3D12CommandList = { 0x7116d91c, 0xe7e4, 0x47ce, { 0xb8, 0xc6, 0xec, 0x81, 0x68, 0xf4, 0x37, 0xe5 } };
|
|
static const IID D3D_IID_ID3D12GraphicsCommandList = { 0x5b160d0f, 0xac1b, 0x4185, { 0x8b, 0xa8, 0xb3, 0xae, 0x42, 0xa5, 0xa4, 0x55 } };
|
|
static const IID D3D_IID_ID3D12Fence = { 0x0a753dcf, 0xc4d8, 0x4b91, { 0xad, 0xf6, 0xbe, 0x5a, 0x60, 0xd9, 0x5a, 0x76 } };
|
|
static const IID D3D_IID_ID3D12RootSignature = { 0xc54a6b66, 0x72df, 0x4ee8, { 0x8b, 0xe5, 0xa9, 0x46, 0xa1, 0x42, 0x92, 0x14 } };
|
|
static const IID D3D_IID_ID3D12CommandSignature = { 0xc36a797c, 0xec80, 0x4f0a, { 0x89, 0x85, 0xa7, 0xb2, 0x47, 0x50, 0x82, 0xd1 } };
|
|
static const IID D3D_IID_ID3D12PipelineState = { 0x765a30f3, 0xf624, 0x4c6f, { 0xa8, 0x28, 0xac, 0xe9, 0x48, 0x62, 0x24, 0x45 } };
|
|
static const IID D3D_IID_ID3D12Debug = { 0x344488b7, 0x6846, 0x474b, { 0xb9, 0x89, 0xf0, 0x27, 0x44, 0x82, 0x45, 0xe0 } };
|
|
static const IID D3D_IID_ID3D12InfoQueue = { 0x0742a90b, 0xc387, 0x483f, { 0xb9, 0x46, 0x30, 0xa7, 0xe4, 0xe6, 0x14, 0x58 } };
|
|
|
|
// Enums
|
|
|
|
typedef enum D3D12BufferType
|
|
{
|
|
D3D12_BUFFER_TYPE_GPU,
|
|
D3D12_BUFFER_TYPE_UNIFORM,
|
|
D3D12_BUFFER_TYPE_UPLOAD,
|
|
D3D12_BUFFER_TYPE_DOWNLOAD
|
|
} D3D12BufferType;
|
|
|
|
// Conversions
|
|
|
|
static SDL_GPUTextureFormat SwapchainCompositionToSDLTextureFormat[] = {
|
|
SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, // SDR
|
|
SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB, // SDR_SRGB
|
|
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, // HDR
|
|
SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM, // HDR_ADVANCED
|
|
};
|
|
|
|
static DXGI_FORMAT SwapchainCompositionToTextureFormat[] = {
|
|
DXGI_FORMAT_B8G8R8A8_UNORM, // SDR
|
|
DXGI_FORMAT_B8G8R8A8_UNORM, /* SDR_SRGB */ // NOTE: The RTV uses the sRGB format
|
|
DXGI_FORMAT_R16G16B16A16_FLOAT, // HDR
|
|
DXGI_FORMAT_R10G10B10A2_UNORM, // HDR_ADVANCED
|
|
};
|
|
|
|
static DXGI_COLOR_SPACE_TYPE SwapchainCompositionToColorSpace[] = {
|
|
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, // SDR
|
|
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, // SDR_SRGB
|
|
DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, // HDR
|
|
DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 // HDR_ADVANCED
|
|
};
|
|
|
|
static D3D12_BLEND SDLToD3D12_BlendFactor[] = {
|
|
D3D12_BLEND_ZERO, // INVALID
|
|
D3D12_BLEND_ZERO, // ZERO
|
|
D3D12_BLEND_ONE, // ONE
|
|
D3D12_BLEND_SRC_COLOR, // SRC_COLOR
|
|
D3D12_BLEND_INV_SRC_COLOR, // ONE_MINUS_SRC_COLOR
|
|
D3D12_BLEND_DEST_COLOR, // DST_COLOR
|
|
D3D12_BLEND_INV_DEST_COLOR, // ONE_MINUS_DST_COLOR
|
|
D3D12_BLEND_SRC_ALPHA, // SRC_ALPHA
|
|
D3D12_BLEND_INV_SRC_ALPHA, // ONE_MINUS_SRC_ALPHA
|
|
D3D12_BLEND_DEST_ALPHA, // DST_ALPHA
|
|
D3D12_BLEND_INV_DEST_ALPHA, // ONE_MINUS_DST_ALPHA
|
|
D3D12_BLEND_BLEND_FACTOR, // CONSTANT_COLOR
|
|
D3D12_BLEND_INV_BLEND_FACTOR, // ONE_MINUS_CONSTANT_COLOR
|
|
D3D12_BLEND_SRC_ALPHA_SAT, // SRC_ALPHA_SATURATE
|
|
};
|
|
SDL_COMPILE_TIME_ASSERT(SDLToD3D12_BlendFactor, SDL_arraysize(SDLToD3D12_BlendFactor) == SDL_GPU_BLENDFACTOR_MAX_ENUM_VALUE);
|
|
|
|
static D3D12_BLEND SDLToD3D12_BlendFactorAlpha[] = {
|
|
D3D12_BLEND_ZERO, // INVALID
|
|
D3D12_BLEND_ZERO, // ZERO
|
|
D3D12_BLEND_ONE, // ONE
|
|
D3D12_BLEND_SRC_ALPHA, // SRC_COLOR
|
|
D3D12_BLEND_INV_SRC_ALPHA, // ONE_MINUS_SRC_COLOR
|
|
D3D12_BLEND_DEST_ALPHA, // DST_COLOR
|
|
D3D12_BLEND_INV_DEST_ALPHA, // ONE_MINUS_DST_COLOR
|
|
D3D12_BLEND_SRC_ALPHA, // SRC_ALPHA
|
|
D3D12_BLEND_INV_SRC_ALPHA, // ONE_MINUS_SRC_ALPHA
|
|
D3D12_BLEND_DEST_ALPHA, // DST_ALPHA
|
|
D3D12_BLEND_INV_DEST_ALPHA, // ONE_MINUS_DST_ALPHA
|
|
D3D12_BLEND_BLEND_FACTOR, // CONSTANT_COLOR
|
|
D3D12_BLEND_INV_BLEND_FACTOR, // ONE_MINUS_CONSTANT_COLOR
|
|
D3D12_BLEND_SRC_ALPHA_SAT, // SRC_ALPHA_SATURATE
|
|
};
|
|
SDL_COMPILE_TIME_ASSERT(SDLToD3D12_BlendFactorAlpha, SDL_arraysize(SDLToD3D12_BlendFactorAlpha) == SDL_GPU_BLENDFACTOR_MAX_ENUM_VALUE);
|
|
|
|
static D3D12_BLEND_OP SDLToD3D12_BlendOp[] = {
|
|
D3D12_BLEND_OP_ADD, // INVALID
|
|
D3D12_BLEND_OP_ADD, // ADD
|
|
D3D12_BLEND_OP_SUBTRACT, // SUBTRACT
|
|
D3D12_BLEND_OP_REV_SUBTRACT, // REVERSE_SUBTRACT
|
|
D3D12_BLEND_OP_MIN, // MIN
|
|
D3D12_BLEND_OP_MAX // MAX
|
|
};
|
|
SDL_COMPILE_TIME_ASSERT(SDLToD3D12_BlendOp, SDL_arraysize(SDLToD3D12_BlendOp) == SDL_GPU_BLENDOP_MAX_ENUM_VALUE);
|
|
|
|
static DXGI_FORMAT SDLToD3D12_TextureFormat[] = {
|
|
DXGI_FORMAT_UNKNOWN, // INVALID
|
|
DXGI_FORMAT_A8_UNORM, // A8_UNORM
|
|
DXGI_FORMAT_R8_UNORM, // R8_UNORM
|
|
DXGI_FORMAT_R8G8_UNORM, // R8G8_UNORM
|
|
DXGI_FORMAT_R8G8B8A8_UNORM, // R8G8B8A8_UNORM
|
|
DXGI_FORMAT_R16_UNORM, // R16_UNORM
|
|
DXGI_FORMAT_R16G16_UNORM, // R16G16_UNORM
|
|
DXGI_FORMAT_R16G16B16A16_UNORM, // R16G16B16A16_UNORM
|
|
DXGI_FORMAT_R10G10B10A2_UNORM, // R10G10B10A2_UNORM
|
|
DXGI_FORMAT_B5G6R5_UNORM, // B5G6R5_UNORM
|
|
DXGI_FORMAT_B5G5R5A1_UNORM, // B5G5R5A1_UNORM
|
|
DXGI_FORMAT_B4G4R4A4_UNORM, // B4G4R4A4_UNORM
|
|
DXGI_FORMAT_B8G8R8A8_UNORM, // B8G8R8A8_UNORM
|
|
DXGI_FORMAT_BC1_UNORM, // BC1_UNORM
|
|
DXGI_FORMAT_BC2_UNORM, // BC2_UNORM
|
|
DXGI_FORMAT_BC3_UNORM, // BC3_UNORM
|
|
DXGI_FORMAT_BC4_UNORM, // BC4_UNORM
|
|
DXGI_FORMAT_BC5_UNORM, // BC5_UNORM
|
|
DXGI_FORMAT_BC7_UNORM, // BC7_UNORM
|
|
DXGI_FORMAT_BC6H_SF16, // BC6H_FLOAT
|
|
DXGI_FORMAT_BC6H_UF16, // BC6H_UFLOAT
|
|
DXGI_FORMAT_R8_SNORM, // R8_SNORM
|
|
DXGI_FORMAT_R8G8_SNORM, // R8G8_SNORM
|
|
DXGI_FORMAT_R8G8B8A8_SNORM, // R8G8B8A8_SNORM
|
|
DXGI_FORMAT_R16_SNORM, // R16_SNORM
|
|
DXGI_FORMAT_R16G16_SNORM, // R16G16_SNORM
|
|
DXGI_FORMAT_R16G16B16A16_SNORM, // R16G16B16A16_SNORM
|
|
DXGI_FORMAT_R16_FLOAT, // R16_FLOAT
|
|
DXGI_FORMAT_R16G16_FLOAT, // R16G16_FLOAT
|
|
DXGI_FORMAT_R16G16B16A16_FLOAT, // R16G16B16A16_FLOAT
|
|
DXGI_FORMAT_R32_FLOAT, // R32_FLOAT
|
|
DXGI_FORMAT_R32G32_FLOAT, // R32G32_FLOAT
|
|
DXGI_FORMAT_R32G32B32A32_FLOAT, // R32G32B32A32_FLOAT
|
|
DXGI_FORMAT_R11G11B10_FLOAT, // R11G11B10_UFLOAT
|
|
DXGI_FORMAT_R8_UINT, // R8_UINT
|
|
DXGI_FORMAT_R8G8_UINT, // R8G8_UINT
|
|
DXGI_FORMAT_R8G8B8A8_UINT, // R8G8B8A8_UINT
|
|
DXGI_FORMAT_R16_UINT, // R16_UINT
|
|
DXGI_FORMAT_R16G16_UINT, // R16G16_UINT
|
|
DXGI_FORMAT_R16G16B16A16_UINT, // R16G16B16A16_UINT
|
|
DXGI_FORMAT_R8_SINT, // R8_INT
|
|
DXGI_FORMAT_R8G8_SINT, // R8G8_INT
|
|
DXGI_FORMAT_R8G8B8A8_SINT, // R8G8B8A8_INT
|
|
DXGI_FORMAT_R16_SINT, // R16_INT
|
|
DXGI_FORMAT_R16G16_SINT, // R16G16_INT
|
|
DXGI_FORMAT_R16G16B16A16_SINT, // R16G16B16A16_INT
|
|
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, // R8G8B8A8_UNORM_SRGB
|
|
DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, // B8G8R8A8_UNORM_SRGB
|
|
DXGI_FORMAT_BC1_UNORM_SRGB, // BC1_UNORM_SRGB
|
|
DXGI_FORMAT_BC2_UNORM_SRGB, // BC2_UNORM_SRGB
|
|
DXGI_FORMAT_BC3_UNORM_SRGB, // BC3_UNORM_SRGB
|
|
DXGI_FORMAT_BC7_UNORM_SRGB, // BC7_UNORM_SRGB
|
|
DXGI_FORMAT_D16_UNORM, // D16_UNORM
|
|
DXGI_FORMAT_D24_UNORM_S8_UINT, // D24_UNORM
|
|
DXGI_FORMAT_D32_FLOAT, // D32_FLOAT
|
|
DXGI_FORMAT_D24_UNORM_S8_UINT, // D24_UNORM_S8_UINT
|
|
DXGI_FORMAT_D32_FLOAT_S8X24_UINT, // D32_FLOAT_S8_UINT
|
|
};
|
|
SDL_COMPILE_TIME_ASSERT(SDLToD3D12_TextureFormat, SDL_arraysize(SDLToD3D12_TextureFormat) == SDL_GPU_TEXTUREFORMAT_MAX_ENUM_VALUE);
|
|
|
|
static D3D12_COMPARISON_FUNC SDLToD3D12_CompareOp[] = {
|
|
D3D12_COMPARISON_FUNC_NEVER, // INVALID
|
|
D3D12_COMPARISON_FUNC_NEVER, // NEVER
|
|
D3D12_COMPARISON_FUNC_LESS, // LESS
|
|
D3D12_COMPARISON_FUNC_EQUAL, // EQUAL
|
|
D3D12_COMPARISON_FUNC_LESS_EQUAL, // LESS_OR_EQUAL
|
|
D3D12_COMPARISON_FUNC_GREATER, // GREATER
|
|
D3D12_COMPARISON_FUNC_NOT_EQUAL, // NOT_EQUAL
|
|
D3D12_COMPARISON_FUNC_GREATER_EQUAL, // GREATER_OR_EQUAL
|
|
D3D12_COMPARISON_FUNC_ALWAYS // ALWAYS
|
|
};
|
|
SDL_COMPILE_TIME_ASSERT(SDLToD3D12_CompareOp, SDL_arraysize(SDLToD3D12_CompareOp) == SDL_GPU_COMPAREOP_MAX_ENUM_VALUE);
|
|
|
|
static D3D12_STENCIL_OP SDLToD3D12_StencilOp[] = {
|
|
D3D12_STENCIL_OP_KEEP, // INVALID
|
|
D3D12_STENCIL_OP_KEEP, // KEEP
|
|
D3D12_STENCIL_OP_ZERO, // ZERO
|
|
D3D12_STENCIL_OP_REPLACE, // REPLACE
|
|
D3D12_STENCIL_OP_INCR_SAT, // INCREMENT_AND_CLAMP
|
|
D3D12_STENCIL_OP_DECR_SAT, // DECREMENT_AND_CLAMP
|
|
D3D12_STENCIL_OP_INVERT, // INVERT
|
|
D3D12_STENCIL_OP_INCR, // INCREMENT_AND_WRAP
|
|
D3D12_STENCIL_OP_DECR // DECREMENT_AND_WRAP
|
|
};
|
|
SDL_COMPILE_TIME_ASSERT(SDLToD3D12_StencilOp, SDL_arraysize(SDLToD3D12_StencilOp) == SDL_GPU_STENCILOP_MAX_ENUM_VALUE);
|
|
|
|
static D3D12_CULL_MODE SDLToD3D12_CullMode[] = {
|
|
D3D12_CULL_MODE_NONE, // NONE
|
|
D3D12_CULL_MODE_FRONT, // FRONT
|
|
D3D12_CULL_MODE_BACK // BACK
|
|
};
|
|
|
|
static D3D12_FILL_MODE SDLToD3D12_FillMode[] = {
|
|
D3D12_FILL_MODE_SOLID, // FILL
|
|
D3D12_FILL_MODE_WIREFRAME // LINE
|
|
};
|
|
|
|
static D3D12_INPUT_CLASSIFICATION SDLToD3D12_InputRate[] = {
|
|
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, // VERTEX
|
|
D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA // INSTANCE
|
|
};
|
|
|
|
static DXGI_FORMAT SDLToD3D12_VertexFormat[] = {
|
|
DXGI_FORMAT_UNKNOWN, // UNKNOWN
|
|
DXGI_FORMAT_R32_SINT, // INT
|
|
DXGI_FORMAT_R32G32_SINT, // INT2
|
|
DXGI_FORMAT_R32G32B32_SINT, // INT3
|
|
DXGI_FORMAT_R32G32B32A32_SINT, // INT4
|
|
DXGI_FORMAT_R32_UINT, // UINT
|
|
DXGI_FORMAT_R32G32_UINT, // UINT2
|
|
DXGI_FORMAT_R32G32B32_UINT, // UINT3
|
|
DXGI_FORMAT_R32G32B32A32_UINT, // UINT4
|
|
DXGI_FORMAT_R32_FLOAT, // FLOAT
|
|
DXGI_FORMAT_R32G32_FLOAT, // FLOAT2
|
|
DXGI_FORMAT_R32G32B32_FLOAT, // FLOAT3
|
|
DXGI_FORMAT_R32G32B32A32_FLOAT, // FLOAT4
|
|
DXGI_FORMAT_R8G8_SINT, // BYTE2
|
|
DXGI_FORMAT_R8G8B8A8_SINT, // BYTE4
|
|
DXGI_FORMAT_R8G8_UINT, // UBYTE2
|
|
DXGI_FORMAT_R8G8B8A8_UINT, // UBYTE4
|
|
DXGI_FORMAT_R8G8_SNORM, // BYTE2_NORM
|
|
DXGI_FORMAT_R8G8B8A8_SNORM, // BYTE4_NORM
|
|
DXGI_FORMAT_R8G8_UNORM, // UBYTE2_NORM
|
|
DXGI_FORMAT_R8G8B8A8_UNORM, // UBYTE4_NORM
|
|
DXGI_FORMAT_R16G16_SINT, // SHORT2
|
|
DXGI_FORMAT_R16G16B16A16_SINT, // SHORT4
|
|
DXGI_FORMAT_R16G16_UINT, // USHORT2
|
|
DXGI_FORMAT_R16G16B16A16_UINT, // USHORT4
|
|
DXGI_FORMAT_R16G16_SNORM, // SHORT2_NORM
|
|
DXGI_FORMAT_R16G16B16A16_SNORM, // SHORT4_NORM
|
|
DXGI_FORMAT_R16G16_UNORM, // USHORT2_NORM
|
|
DXGI_FORMAT_R16G16B16A16_UNORM, // USHORT4_NORM
|
|
DXGI_FORMAT_R16G16_FLOAT, // HALF2
|
|
DXGI_FORMAT_R16G16B16A16_FLOAT // HALF4
|
|
};
|
|
SDL_COMPILE_TIME_ASSERT(SDLToD3D12_VertexFormat, SDL_arraysize(SDLToD3D12_VertexFormat) == SDL_GPU_VERTEXELEMENTFORMAT_MAX_ENUM_VALUE);
|
|
|
|
static Uint32 SDLToD3D12_SampleCount[] = {
|
|
1, // SDL_GPU_SAMPLECOUNT_1
|
|
2, // SDL_GPU_SAMPLECOUNT_2
|
|
4, // SDL_GPU_SAMPLECOUNT_4
|
|
8, // SDL_GPU_SAMPLECOUNT_8
|
|
};
|
|
|
|
static D3D12_PRIMITIVE_TOPOLOGY SDLToD3D12_PrimitiveType[] = {
|
|
D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, // TRIANGLELIST
|
|
D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, // TRIANGLESTRIP
|
|
D3D_PRIMITIVE_TOPOLOGY_LINELIST, // LINELIST
|
|
D3D_PRIMITIVE_TOPOLOGY_LINESTRIP, // LINESTRIP
|
|
D3D_PRIMITIVE_TOPOLOGY_POINTLIST // POINTLIST
|
|
};
|
|
|
|
static D3D12_TEXTURE_ADDRESS_MODE SDLToD3D12_SamplerAddressMode[] = {
|
|
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // REPEAT
|
|
D3D12_TEXTURE_ADDRESS_MODE_MIRROR, // MIRRORED_REPEAT
|
|
D3D12_TEXTURE_ADDRESS_MODE_CLAMP // CLAMP_TO_EDGE
|
|
};
|
|
|
|
static D3D12_FILTER SDLToD3D12_Filter(
|
|
SDL_GPUFilter minFilter,
|
|
SDL_GPUFilter magFilter,
|
|
SDL_GPUSamplerMipmapMode mipmapMode,
|
|
bool comparisonEnabled,
|
|
bool anisotropyEnabled)
|
|
{
|
|
D3D12_FILTER result = D3D12_ENCODE_BASIC_FILTER(
|
|
(minFilter == SDL_GPU_FILTER_LINEAR) ? 1 : 0,
|
|
(magFilter == SDL_GPU_FILTER_LINEAR) ? 1 : 0,
|
|
(mipmapMode == SDL_GPU_SAMPLERMIPMAPMODE_LINEAR) ? 1 : 0,
|
|
comparisonEnabled ? 1 : 0);
|
|
|
|
if (anisotropyEnabled) {
|
|
result = (D3D12_FILTER)(result | D3D12_ANISOTROPIC_FILTERING_BIT);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Structures
|
|
typedef struct D3D12Renderer D3D12Renderer;
|
|
typedef struct D3D12CommandBufferPool D3D12CommandBufferPool;
|
|
typedef struct D3D12CommandBuffer D3D12CommandBuffer;
|
|
typedef struct D3D12Texture D3D12Texture;
|
|
typedef struct D3D12Shader D3D12Shader;
|
|
typedef struct D3D12GraphicsPipeline D3D12GraphicsPipeline;
|
|
typedef struct D3D12ComputePipeline D3D12ComputePipeline;
|
|
typedef struct D3D12Buffer D3D12Buffer;
|
|
typedef struct D3D12BufferContainer D3D12BufferContainer;
|
|
typedef struct D3D12UniformBuffer D3D12UniformBuffer;
|
|
typedef struct D3D12DescriptorHeap D3D12DescriptorHeap;
|
|
typedef struct D3D12TextureDownload D3D12TextureDownload;
|
|
|
|
typedef struct D3D12Fence
|
|
{
|
|
ID3D12Fence *handle;
|
|
HANDLE event; // used for blocking
|
|
SDL_AtomicInt referenceCount;
|
|
} D3D12Fence;
|
|
|
|
struct D3D12DescriptorHeap
|
|
{
|
|
ID3D12DescriptorHeap *handle;
|
|
D3D12_DESCRIPTOR_HEAP_TYPE heapType;
|
|
D3D12_CPU_DESCRIPTOR_HANDLE descriptorHeapCPUStart;
|
|
D3D12_GPU_DESCRIPTOR_HANDLE descriptorHeapGPUStart; // only exists if staging is true
|
|
Uint32 maxDescriptors;
|
|
Uint32 descriptorSize;
|
|
bool staging;
|
|
|
|
Uint32 currentDescriptorIndex;
|
|
|
|
Uint32 *inactiveDescriptorIndices; // only exists if staging is true
|
|
Uint32 inactiveDescriptorCount;
|
|
};
|
|
|
|
typedef struct D3D12DescriptorHeapPool
|
|
{
|
|
Uint32 capacity;
|
|
Uint32 count;
|
|
D3D12DescriptorHeap **heaps;
|
|
SDL_Mutex *lock;
|
|
} D3D12DescriptorHeapPool;
|
|
|
|
typedef struct D3D12CPUDescriptor
|
|
{
|
|
D3D12DescriptorHeap *heap;
|
|
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle;
|
|
Uint32 cpuHandleIndex;
|
|
} D3D12CPUDescriptor;
|
|
|
|
typedef struct D3D12TextureContainer
|
|
{
|
|
TextureCommonHeader header;
|
|
|
|
D3D12Texture *activeTexture;
|
|
|
|
D3D12Texture **textures;
|
|
Uint32 textureCapacity;
|
|
Uint32 textureCount;
|
|
|
|
// Swapchain images cannot be cycled
|
|
bool canBeCycled;
|
|
|
|
char *debugName;
|
|
} D3D12TextureContainer;
|
|
|
|
// Null views represent by heap = NULL
|
|
typedef struct D3D12TextureSubresource
|
|
{
|
|
D3D12Texture *parent;
|
|
Uint32 layer;
|
|
Uint32 level;
|
|
Uint32 depth;
|
|
Uint32 index;
|
|
|
|
// One per depth slice
|
|
D3D12CPUDescriptor *rtvHandles; // NULL if not a color target
|
|
|
|
D3D12CPUDescriptor uavHandle; // NULL if not a compute storage write texture
|
|
D3D12CPUDescriptor dsvHandle; // NULL if not a depth stencil target
|
|
} D3D12TextureSubresource;
|
|
|
|
struct D3D12Texture
|
|
{
|
|
D3D12TextureContainer *container;
|
|
Uint32 containerIndex;
|
|
|
|
D3D12TextureSubresource *subresources;
|
|
Uint32 subresourceCount; /* layerCount * num_levels */
|
|
|
|
ID3D12Resource *resource;
|
|
D3D12CPUDescriptor srvHandle;
|
|
|
|
SDL_AtomicInt referenceCount;
|
|
};
|
|
|
|
typedef struct D3D12Sampler
|
|
{
|
|
SDL_GPUSamplerCreateInfo createInfo;
|
|
D3D12CPUDescriptor handle;
|
|
SDL_AtomicInt referenceCount;
|
|
} D3D12Sampler;
|
|
|
|
typedef struct D3D12WindowData
|
|
{
|
|
SDL_Window *window;
|
|
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
D3D12XBOX_FRAME_PIPELINE_TOKEN frameToken;
|
|
Uint32 swapchainWidth, swapchainHeight;
|
|
#else
|
|
IDXGISwapChain3 *swapchain;
|
|
#endif
|
|
SDL_GPUPresentMode present_mode;
|
|
SDL_GPUSwapchainComposition swapchainComposition;
|
|
DXGI_COLOR_SPACE_TYPE swapchainColorSpace;
|
|
Uint32 frameCounter;
|
|
|
|
D3D12TextureContainer textureContainers[MAX_FRAMES_IN_FLIGHT];
|
|
SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
|
|
} D3D12WindowData;
|
|
|
|
typedef struct D3D12PresentData
|
|
{
|
|
D3D12WindowData *windowData;
|
|
Uint32 swapchainImageIndex;
|
|
} D3D12PresentData;
|
|
|
|
struct D3D12Renderer
|
|
{
|
|
// Reference to the parent device
|
|
SDL_GPUDevice *sdlGPUDevice;
|
|
|
|
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
IDXGIDebug *dxgiDebug;
|
|
IDXGIFactory4 *factory;
|
|
#ifdef HAVE_IDXGIINFOQUEUE
|
|
IDXGIInfoQueue *dxgiInfoQueue;
|
|
#endif
|
|
IDXGIAdapter1 *adapter;
|
|
void *dxgi_dll;
|
|
void *dxgidebug_dll;
|
|
#endif
|
|
ID3D12Debug *d3d12Debug;
|
|
bool supportsTearing;
|
|
void *d3d12_dll;
|
|
ID3D12Device *device;
|
|
PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignature_func;
|
|
const char *semantic;
|
|
SDL_iconv_t iconv;
|
|
|
|
ID3D12CommandQueue *commandQueue;
|
|
|
|
bool debug_mode;
|
|
bool GPUUploadHeapSupported;
|
|
// FIXME: these might not be necessary since we're not using custom heaps
|
|
bool UMA;
|
|
bool UMACacheCoherent;
|
|
|
|
// Indirect command signatures
|
|
ID3D12CommandSignature *indirectDrawCommandSignature;
|
|
ID3D12CommandSignature *indirectIndexedDrawCommandSignature;
|
|
ID3D12CommandSignature *indirectDispatchCommandSignature;
|
|
|
|
// Blit
|
|
SDL_GPUShader *blitVertexShader;
|
|
SDL_GPUShader *blitFrom2DShader;
|
|
SDL_GPUShader *blitFrom2DArrayShader;
|
|
SDL_GPUShader *blitFrom3DShader;
|
|
SDL_GPUShader *blitFromCubeShader;
|
|
SDL_GPUShader *blitFromCubeArrayShader;
|
|
|
|
SDL_GPUSampler *blitNearestSampler;
|
|
SDL_GPUSampler *blitLinearSampler;
|
|
|
|
BlitPipelineCacheEntry *blitPipelines;
|
|
Uint32 blitPipelineCount;
|
|
Uint32 blitPipelineCapacity;
|
|
|
|
// Resources
|
|
|
|
D3D12CommandBuffer **availableCommandBuffers;
|
|
Uint32 availableCommandBufferCount;
|
|
Uint32 availableCommandBufferCapacity;
|
|
|
|
D3D12CommandBuffer **submittedCommandBuffers;
|
|
Uint32 submittedCommandBufferCount;
|
|
Uint32 submittedCommandBufferCapacity;
|
|
|
|
D3D12UniformBuffer **uniformBufferPool;
|
|
Uint32 uniformBufferPoolCount;
|
|
Uint32 uniformBufferPoolCapacity;
|
|
|
|
D3D12WindowData **claimedWindows;
|
|
Uint32 claimedWindowCount;
|
|
Uint32 claimedWindowCapacity;
|
|
|
|
D3D12Fence **availableFences;
|
|
Uint32 availableFenceCount;
|
|
Uint32 availableFenceCapacity;
|
|
|
|
D3D12DescriptorHeap *stagingDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES];
|
|
D3D12DescriptorHeapPool descriptorHeapPools[2];
|
|
|
|
// Deferred resource releasing
|
|
|
|
D3D12Buffer **buffersToDestroy;
|
|
Uint32 buffersToDestroyCount;
|
|
Uint32 buffersToDestroyCapacity;
|
|
|
|
D3D12Texture **texturesToDestroy;
|
|
Uint32 texturesToDestroyCount;
|
|
Uint32 texturesToDestroyCapacity;
|
|
|
|
D3D12Sampler **samplersToDestroy;
|
|
Uint32 samplersToDestroyCount;
|
|
Uint32 samplersToDestroyCapacity;
|
|
|
|
D3D12GraphicsPipeline **graphicsPipelinesToDestroy;
|
|
Uint32 graphicsPipelinesToDestroyCount;
|
|
Uint32 graphicsPipelinesToDestroyCapacity;
|
|
|
|
D3D12ComputePipeline **computePipelinesToDestroy;
|
|
Uint32 computePipelinesToDestroyCount;
|
|
Uint32 computePipelinesToDestroyCapacity;
|
|
|
|
// Locks
|
|
SDL_Mutex *stagingDescriptorHeapLock;
|
|
SDL_Mutex *acquireCommandBufferLock;
|
|
SDL_Mutex *acquireUniformBufferLock;
|
|
SDL_Mutex *submitLock;
|
|
SDL_Mutex *windowLock;
|
|
SDL_Mutex *fenceLock;
|
|
SDL_Mutex *disposeLock;
|
|
};
|
|
|
|
struct D3D12CommandBuffer
|
|
{
|
|
// reserved for SDL_gpu
|
|
CommandBufferCommonHeader common;
|
|
|
|
// non owning parent reference
|
|
D3D12Renderer *renderer;
|
|
|
|
ID3D12CommandAllocator *commandAllocator;
|
|
ID3D12GraphicsCommandList *graphicsCommandList;
|
|
D3D12Fence *inFlightFence;
|
|
bool autoReleaseFence;
|
|
|
|
// Presentation data
|
|
D3D12PresentData *presentDatas;
|
|
Uint32 presentDataCount;
|
|
Uint32 presentDataCapacity;
|
|
|
|
Uint32 colorTargetTextureSubresourceCount;
|
|
D3D12TextureSubresource *colorTargetTextureSubresources[MAX_COLOR_TARGET_BINDINGS];
|
|
D3D12TextureSubresource *depthStencilTextureSubresource;
|
|
D3D12GraphicsPipeline *currentGraphicsPipeline;
|
|
D3D12ComputePipeline *currentComputePipeline;
|
|
|
|
// Set at acquire time
|
|
D3D12DescriptorHeap *gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER + 1];
|
|
|
|
D3D12UniformBuffer **usedUniformBuffers;
|
|
Uint32 usedUniformBufferCount;
|
|
Uint32 usedUniformBufferCapacity;
|
|
|
|
// Resource slot state
|
|
bool needVertexBufferBind;
|
|
bool needVertexSamplerBind;
|
|
bool needVertexStorageTextureBind;
|
|
bool needVertexStorageBufferBind;
|
|
bool needVertexUniformBufferBind[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
bool needFragmentSamplerBind;
|
|
bool needFragmentStorageTextureBind;
|
|
bool needFragmentStorageBufferBind;
|
|
bool needFragmentUniformBufferBind[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
|
|
bool needComputeSamplerBind;
|
|
bool needComputeReadOnlyStorageTextureBind;
|
|
bool needComputeReadOnlyStorageBufferBind;
|
|
bool needComputeUniformBufferBind[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
|
|
D3D12Buffer *vertexBuffers[MAX_BUFFER_BINDINGS];
|
|
Uint32 vertexBufferOffsets[MAX_BUFFER_BINDINGS];
|
|
Uint32 vertexBufferCount;
|
|
|
|
D3D12Texture *vertexSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
D3D12Sampler *vertexSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
D3D12Texture *vertexStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
|
|
D3D12Buffer *vertexStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
|
|
D3D12UniformBuffer *vertexUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
|
|
D3D12Texture *fragmentSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
D3D12Sampler *fragmentSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
D3D12Texture *fragmentStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
|
|
D3D12Buffer *fragmentStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
|
|
D3D12UniformBuffer *fragmentUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
|
|
D3D12Texture *computeSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
D3D12Sampler *computeSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
D3D12Texture *computeReadOnlyStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
|
|
D3D12Buffer *computeReadOnlyStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
|
|
D3D12TextureSubresource *computeWriteOnlyStorageTextureSubresources[MAX_COMPUTE_WRITE_TEXTURES];
|
|
Uint32 computeWriteOnlyStorageTextureSubresourceCount;
|
|
D3D12Buffer *computeWriteOnlyStorageBuffers[MAX_COMPUTE_WRITE_BUFFERS];
|
|
Uint32 computeWriteOnlyStorageBufferCount;
|
|
D3D12UniformBuffer *computeUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
|
|
// Resource tracking
|
|
D3D12Texture **usedTextures;
|
|
Uint32 usedTextureCount;
|
|
Uint32 usedTextureCapacity;
|
|
|
|
D3D12Buffer **usedBuffers;
|
|
Uint32 usedBufferCount;
|
|
Uint32 usedBufferCapacity;
|
|
|
|
D3D12Sampler **usedSamplers;
|
|
Uint32 usedSamplerCount;
|
|
Uint32 usedSamplerCapacity;
|
|
|
|
D3D12GraphicsPipeline **usedGraphicsPipelines;
|
|
Uint32 usedGraphicsPipelineCount;
|
|
Uint32 usedGraphicsPipelineCapacity;
|
|
|
|
D3D12ComputePipeline **usedComputePipelines;
|
|
Uint32 usedComputePipelineCount;
|
|
Uint32 usedComputePipelineCapacity;
|
|
|
|
// Used for texture pitch hack
|
|
D3D12TextureDownload **textureDownloads;
|
|
Uint32 textureDownloadCount;
|
|
Uint32 textureDownloadCapacity;
|
|
};
|
|
|
|
struct D3D12Shader
|
|
{
|
|
// todo cleanup
|
|
void *bytecode;
|
|
size_t bytecodeSize;
|
|
|
|
Uint32 num_samplers;
|
|
Uint32 numUniformBuffers;
|
|
Uint32 numStorageBuffers;
|
|
Uint32 numStorageTextures;
|
|
};
|
|
|
|
typedef struct D3D12GraphicsRootSignature
|
|
{
|
|
ID3D12RootSignature *handle;
|
|
|
|
Sint32 vertexSamplerRootIndex;
|
|
Sint32 vertexSamplerTextureRootIndex;
|
|
Sint32 vertexStorageTextureRootIndex;
|
|
Sint32 vertexStorageBufferRootIndex;
|
|
|
|
Sint32 vertexUniformBufferRootIndex[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
|
|
Sint32 fragmentSamplerRootIndex;
|
|
Sint32 fragmentSamplerTextureRootIndex;
|
|
Sint32 fragmentStorageTextureRootIndex;
|
|
Sint32 fragmentStorageBufferRootIndex;
|
|
|
|
Sint32 fragmentUniformBufferRootIndex[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
} D3D12GraphicsRootSignature;
|
|
|
|
struct D3D12GraphicsPipeline
|
|
{
|
|
ID3D12PipelineState *pipelineState;
|
|
D3D12GraphicsRootSignature *rootSignature;
|
|
SDL_GPUPrimitiveType primitiveType;
|
|
|
|
Uint32 vertexStrides[MAX_BUFFER_BINDINGS];
|
|
|
|
Uint32 vertexSamplerCount;
|
|
Uint32 vertexUniformBufferCount;
|
|
Uint32 vertexStorageBufferCount;
|
|
Uint32 vertexStorageTextureCount;
|
|
|
|
Uint32 fragmentSamplerCount;
|
|
Uint32 fragmentUniformBufferCount;
|
|
Uint32 fragmentStorageBufferCount;
|
|
Uint32 fragmentStorageTextureCount;
|
|
|
|
SDL_AtomicInt referenceCount;
|
|
};
|
|
|
|
typedef struct D3D12ComputeRootSignature
|
|
{
|
|
ID3D12RootSignature *handle;
|
|
|
|
Sint32 samplerRootIndex;
|
|
Sint32 samplerTextureRootIndex;
|
|
Sint32 readOnlyStorageTextureRootIndex;
|
|
Sint32 readOnlyStorageBufferRootIndex;
|
|
Sint32 writeOnlyStorageTextureRootIndex;
|
|
Sint32 writeOnlyStorageBufferRootIndex;
|
|
Sint32 uniformBufferRootIndex[MAX_UNIFORM_BUFFERS_PER_STAGE];
|
|
} D3D12ComputeRootSignature;
|
|
|
|
struct D3D12ComputePipeline
|
|
{
|
|
ID3D12PipelineState *pipelineState;
|
|
D3D12ComputeRootSignature *rootSignature;
|
|
|
|
Uint32 numSamplers;
|
|
Uint32 numReadOnlyStorageTextures;
|
|
Uint32 numReadOnlyStorageBuffers;
|
|
Uint32 numWriteOnlyStorageTextures;
|
|
Uint32 numWriteOnlyStorageBuffers;
|
|
Uint32 numUniformBuffers;
|
|
|
|
SDL_AtomicInt referenceCount;
|
|
};
|
|
|
|
struct D3D12TextureDownload
|
|
{
|
|
D3D12Buffer *destinationBuffer;
|
|
D3D12Buffer *temporaryBuffer;
|
|
Uint32 width;
|
|
Uint32 height;
|
|
Uint32 depth;
|
|
Uint32 bufferOffset;
|
|
Uint32 bytesPerRow;
|
|
Uint32 bytesPerDepthSlice;
|
|
Uint32 alignedBytesPerRow;
|
|
};
|
|
|
|
struct D3D12Buffer
|
|
{
|
|
D3D12BufferContainer *container;
|
|
Uint32 containerIndex;
|
|
|
|
ID3D12Resource *handle;
|
|
D3D12CPUDescriptor uavDescriptor;
|
|
D3D12CPUDescriptor srvDescriptor;
|
|
D3D12CPUDescriptor cbvDescriptor;
|
|
D3D12_GPU_VIRTUAL_ADDRESS virtualAddress;
|
|
Uint8 *mapPointer; // NULL except for upload buffers and fast uniform buffers
|
|
SDL_AtomicInt referenceCount;
|
|
bool transitioned; // used for initial resource barrier
|
|
};
|
|
|
|
struct D3D12BufferContainer
|
|
{
|
|
SDL_GPUBufferUsageFlags usage;
|
|
Uint32 size;
|
|
D3D12BufferType type;
|
|
|
|
D3D12Buffer *activeBuffer;
|
|
|
|
D3D12Buffer **buffers;
|
|
Uint32 bufferCapacity;
|
|
Uint32 bufferCount;
|
|
|
|
D3D12_RESOURCE_DESC bufferDesc;
|
|
|
|
char *debugName;
|
|
};
|
|
|
|
struct D3D12UniformBuffer
|
|
{
|
|
D3D12Buffer *buffer;
|
|
Uint32 writeOffset;
|
|
Uint32 drawOffset;
|
|
Uint32 currentBlockSize;
|
|
};
|
|
|
|
// Foward function declarations
|
|
|
|
static void D3D12_ReleaseWindow(SDL_GPURenderer *driverData, SDL_Window *window);
|
|
static void D3D12_Wait(SDL_GPURenderer *driverData);
|
|
static void D3D12_WaitForFences(SDL_GPURenderer *driverData, bool waitAll, SDL_GPUFence *const *fences, Uint32 numFences);
|
|
static void D3D12_INTERNAL_ReleaseBlitPipelines(SDL_GPURenderer *driverData);
|
|
|
|
// Helpers
|
|
|
|
static Uint32 D3D12_INTERNAL_Align(Uint32 location, Uint32 alignment)
|
|
{
|
|
return (location + (alignment - 1)) & ~(alignment - 1);
|
|
}
|
|
|
|
// Xbox Hack
|
|
|
|
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
// FIXME: This is purely to work around a presentation bug when recreating the device/command queue.
|
|
static ID3D12Device *s_Device;
|
|
static ID3D12CommandQueue *s_CommandQueue;
|
|
#endif
|
|
|
|
#if defined(SDL_PLATFORM_XBOXONE)
|
|
// This is not defined in d3d12_x.h.
|
|
typedef HRESULT (D3DAPI* PFN_D3D12_XBOX_CREATE_DEVICE)(_In_opt_ IGraphicsUnknown*, _In_ const D3D12XBOX_CREATE_DEVICE_PARAMETERS*, _In_ REFIID, _Outptr_opt_ void**);
|
|
#endif
|
|
|
|
// Logging
|
|
|
|
static void
|
|
D3D12_INTERNAL_LogError(
|
|
ID3D12Device *device,
|
|
const char *msg,
|
|
HRESULT res)
|
|
{
|
|
#define MAX_ERROR_LEN 1024 // FIXME: Arbitrary!
|
|
|
|
// Buffer for text, ensure space for \0 terminator after buffer
|
|
char wszMsgBuff[MAX_ERROR_LEN + 1];
|
|
DWORD dwChars; // Number of chars returned.
|
|
|
|
if (res == DXGI_ERROR_DEVICE_REMOVED) {
|
|
if (device) {
|
|
res = ID3D12Device_GetDeviceRemovedReason(device);
|
|
}
|
|
}
|
|
|
|
// Try to get the message from the system errors.
|
|
dwChars = FormatMessageA(
|
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
res,
|
|
0,
|
|
wszMsgBuff,
|
|
MAX_ERROR_LEN,
|
|
NULL);
|
|
|
|
// No message? Screw it, just post the code.
|
|
if (dwChars == 0) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s! Error Code: " HRESULT_FMT, msg, res);
|
|
return;
|
|
}
|
|
|
|
// Ensure valid range
|
|
dwChars = SDL_min(dwChars, MAX_ERROR_LEN);
|
|
|
|
// Trim whitespace from tail of message
|
|
while (dwChars > 0) {
|
|
if (wszMsgBuff[dwChars - 1] <= ' ') {
|
|
dwChars--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Ensure null-terminated string
|
|
wszMsgBuff[dwChars] = '\0';
|
|
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s! Error Code: %s " HRESULT_FMT, msg, wszMsgBuff, res);
|
|
}
|
|
|
|
// Debug Naming
|
|
|
|
static void D3D12_INTERNAL_SetResourceName(
|
|
D3D12Renderer *renderer,
|
|
ID3D12Resource *resource,
|
|
const char *text)
|
|
{
|
|
if (renderer->debug_mode) {
|
|
ID3D12DeviceChild_SetPrivateData(
|
|
resource,
|
|
D3D_GUID(D3D_IID_D3DDebugObjectName),
|
|
(UINT)SDL_strlen(text),
|
|
text);
|
|
}
|
|
}
|
|
|
|
// Release / Cleanup
|
|
|
|
// TODO: call this when releasing resources
|
|
static void D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
D3D12Renderer *renderer,
|
|
D3D12CPUDescriptor *cpuDescriptor)
|
|
{
|
|
D3D12DescriptorHeap *heap = cpuDescriptor->heap;
|
|
|
|
if (heap != NULL) {
|
|
SDL_LockMutex(renderer->stagingDescriptorHeapLock);
|
|
heap->inactiveDescriptorIndices[heap->inactiveDescriptorCount] = cpuDescriptor->cpuHandleIndex;
|
|
heap->inactiveDescriptorCount += 1;
|
|
SDL_UnlockMutex(renderer->stagingDescriptorHeapLock);
|
|
}
|
|
|
|
cpuDescriptor->heap = NULL;
|
|
cpuDescriptor->cpuHandle.ptr = 0;
|
|
cpuDescriptor->cpuHandleIndex = SDL_MAX_UINT32;
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroyBuffer(
|
|
D3D12Renderer *renderer,
|
|
D3D12Buffer *buffer)
|
|
{
|
|
if (!buffer) {
|
|
return;
|
|
}
|
|
|
|
if (buffer->mapPointer != NULL) {
|
|
ID3D12Resource_Unmap(
|
|
buffer->handle,
|
|
0,
|
|
NULL);
|
|
}
|
|
D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
renderer,
|
|
&buffer->srvDescriptor);
|
|
D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
renderer,
|
|
&buffer->uavDescriptor);
|
|
D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
renderer,
|
|
&buffer->cbvDescriptor);
|
|
|
|
if (buffer->handle) {
|
|
ID3D12Resource_Release(buffer->handle);
|
|
}
|
|
SDL_free(buffer);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_ReleaseBuffer(
|
|
D3D12Renderer *renderer,
|
|
D3D12Buffer *buffer)
|
|
{
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->buffersToDestroy,
|
|
D3D12Buffer *,
|
|
renderer->buffersToDestroyCount + 1,
|
|
renderer->buffersToDestroyCapacity,
|
|
renderer->buffersToDestroyCapacity * 2)
|
|
|
|
renderer->buffersToDestroy[renderer->buffersToDestroyCount] = buffer;
|
|
renderer->buffersToDestroyCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_ReleaseBufferContainer(
|
|
D3D12Renderer *renderer,
|
|
D3D12BufferContainer *container)
|
|
{
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
for (Uint32 i = 0; i < container->bufferCount; i += 1) {
|
|
D3D12_INTERNAL_ReleaseBuffer(
|
|
renderer,
|
|
container->buffers[i]);
|
|
}
|
|
|
|
// Containers are just client handles, so we can free immediately
|
|
if (container->debugName) {
|
|
SDL_free(container->debugName);
|
|
}
|
|
SDL_free(container->buffers);
|
|
SDL_free(container);
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroyTexture(
|
|
D3D12Renderer *renderer,
|
|
D3D12Texture *texture)
|
|
{
|
|
if (!texture) {
|
|
return;
|
|
}
|
|
for (Uint32 i = 0; i < texture->subresourceCount; i += 1) {
|
|
D3D12TextureSubresource *subresource = &texture->subresources[i];
|
|
if (subresource->rtvHandles) {
|
|
for (Uint32 depthIndex = 0; depthIndex < subresource->depth; depthIndex += 1) {
|
|
D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
renderer,
|
|
&subresource->rtvHandles[depthIndex]);
|
|
}
|
|
SDL_free(subresource->rtvHandles);
|
|
}
|
|
|
|
D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
renderer,
|
|
&subresource->uavHandle);
|
|
|
|
D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
renderer,
|
|
&subresource->dsvHandle);
|
|
}
|
|
SDL_free(texture->subresources);
|
|
|
|
D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
renderer,
|
|
&texture->srvHandle);
|
|
|
|
if (texture->resource) {
|
|
ID3D12Resource_Release(texture->resource);
|
|
}
|
|
|
|
SDL_free(texture);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_ReleaseTexture(
|
|
D3D12Renderer *renderer,
|
|
D3D12Texture *texture)
|
|
{
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->texturesToDestroy,
|
|
D3D12Texture *,
|
|
renderer->texturesToDestroyCount + 1,
|
|
renderer->texturesToDestroyCapacity,
|
|
renderer->texturesToDestroyCapacity * 2)
|
|
|
|
renderer->texturesToDestroy[renderer->texturesToDestroyCount] = texture;
|
|
renderer->texturesToDestroyCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_ReleaseTextureContainer(
|
|
D3D12Renderer *renderer,
|
|
D3D12TextureContainer *container)
|
|
{
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
for (Uint32 i = 0; i < container->textureCount; i += 1) {
|
|
D3D12_INTERNAL_ReleaseTexture(
|
|
renderer,
|
|
container->textures[i]);
|
|
}
|
|
|
|
// Containers are just client handles, so we can destroy immediately
|
|
if (container->debugName) {
|
|
SDL_free(container->debugName);
|
|
}
|
|
SDL_free(container->textures);
|
|
SDL_free(container);
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroySampler(
|
|
D3D12Renderer *renderer,
|
|
D3D12Sampler *sampler)
|
|
{
|
|
D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
renderer,
|
|
&sampler->handle);
|
|
|
|
SDL_free(sampler);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroyGraphicsRootSignature(
|
|
D3D12GraphicsRootSignature *rootSignature)
|
|
{
|
|
if (!rootSignature) {
|
|
return;
|
|
}
|
|
if (rootSignature->handle) {
|
|
ID3D12RootSignature_Release(rootSignature->handle);
|
|
}
|
|
SDL_free(rootSignature);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroyGraphicsPipeline(
|
|
D3D12GraphicsPipeline *graphicsPipeline)
|
|
{
|
|
if (graphicsPipeline->pipelineState) {
|
|
ID3D12PipelineState_Release(graphicsPipeline->pipelineState);
|
|
}
|
|
D3D12_INTERNAL_DestroyGraphicsRootSignature(graphicsPipeline->rootSignature);
|
|
SDL_free(graphicsPipeline);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroyComputeRootSignature(
|
|
D3D12ComputeRootSignature *rootSignature)
|
|
{
|
|
if (!rootSignature) {
|
|
return;
|
|
}
|
|
if (rootSignature->handle) {
|
|
ID3D12RootSignature_Release(rootSignature->handle);
|
|
}
|
|
SDL_free(rootSignature);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroyComputePipeline(
|
|
D3D12ComputePipeline *computePipeline)
|
|
{
|
|
if (computePipeline->pipelineState) {
|
|
ID3D12PipelineState_Release(computePipeline->pipelineState);
|
|
}
|
|
D3D12_INTERNAL_DestroyComputeRootSignature(computePipeline->rootSignature);
|
|
SDL_free(computePipeline);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_ReleaseFenceToPool(
|
|
D3D12Renderer *renderer,
|
|
D3D12Fence *fence)
|
|
{
|
|
SDL_LockMutex(renderer->fenceLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->availableFences,
|
|
D3D12Fence *,
|
|
renderer->availableFenceCount + 1,
|
|
renderer->availableFenceCapacity,
|
|
renderer->availableFenceCapacity * 2);
|
|
|
|
renderer->availableFences[renderer->availableFenceCount] = fence;
|
|
renderer->availableFenceCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->fenceLock);
|
|
}
|
|
|
|
static void D3D12_ReleaseFence(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUFence *fence)
|
|
{
|
|
D3D12Fence *d3d12Fence = (D3D12Fence *)fence;
|
|
|
|
if (SDL_AtomicDecRef(&d3d12Fence->referenceCount)) {
|
|
D3D12_INTERNAL_ReleaseFenceToPool(
|
|
(D3D12Renderer *)driverData,
|
|
d3d12Fence);
|
|
}
|
|
}
|
|
|
|
static bool D3D12_QueryFence(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUFence *fence)
|
|
{
|
|
D3D12Fence *d3d12Fence = (D3D12Fence *)fence;
|
|
return ID3D12Fence_GetCompletedValue(d3d12Fence->handle) == D3D12_FENCE_SIGNAL_VALUE;
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroyDescriptorHeap(D3D12DescriptorHeap *descriptorHeap)
|
|
{
|
|
if (!descriptorHeap) {
|
|
return;
|
|
}
|
|
SDL_free(descriptorHeap->inactiveDescriptorIndices);
|
|
if (descriptorHeap->handle) {
|
|
ID3D12DescriptorHeap_Release(descriptorHeap->handle);
|
|
}
|
|
SDL_free(descriptorHeap);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroyCommandBuffer(D3D12CommandBuffer *commandBuffer)
|
|
{
|
|
if (!commandBuffer) {
|
|
return;
|
|
}
|
|
if (commandBuffer->graphicsCommandList) {
|
|
ID3D12GraphicsCommandList_Release(commandBuffer->graphicsCommandList);
|
|
}
|
|
if (commandBuffer->commandAllocator) {
|
|
ID3D12CommandAllocator_Release(commandBuffer->commandAllocator);
|
|
}
|
|
SDL_free(commandBuffer->presentDatas);
|
|
SDL_free(commandBuffer->usedTextures);
|
|
SDL_free(commandBuffer->usedBuffers);
|
|
SDL_free(commandBuffer->usedSamplers);
|
|
SDL_free(commandBuffer->usedGraphicsPipelines);
|
|
SDL_free(commandBuffer->usedComputePipelines);
|
|
SDL_free(commandBuffer->usedUniformBuffers);
|
|
SDL_free(commandBuffer->textureDownloads);
|
|
SDL_free(commandBuffer);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroyFence(D3D12Fence *fence)
|
|
{
|
|
if (!fence) {
|
|
return;
|
|
}
|
|
if (fence->handle) {
|
|
ID3D12Fence_Release(fence->handle);
|
|
}
|
|
if (fence->event) {
|
|
CloseHandle(fence->event);
|
|
}
|
|
SDL_free(fence);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroyRenderer(D3D12Renderer *renderer)
|
|
{
|
|
// Release uniform buffers
|
|
for (Uint32 i = 0; i < renderer->uniformBufferPoolCount; i += 1) {
|
|
D3D12_INTERNAL_DestroyBuffer(
|
|
renderer,
|
|
renderer->uniformBufferPool[i]->buffer);
|
|
SDL_free(renderer->uniformBufferPool[i]);
|
|
}
|
|
|
|
// Clean up descriptor heaps
|
|
for (Uint32 i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; i += 1) {
|
|
if (renderer->stagingDescriptorHeaps[i]) {
|
|
D3D12_INTERNAL_DestroyDescriptorHeap(renderer->stagingDescriptorHeaps[i]);
|
|
renderer->stagingDescriptorHeaps[i] = NULL;
|
|
}
|
|
}
|
|
|
|
for (Uint32 i = 0; i < 2; i += 1) {
|
|
if (renderer->descriptorHeapPools[i].heaps) {
|
|
for (Uint32 j = 0; j < renderer->descriptorHeapPools[i].count; j += 1) {
|
|
if (renderer->descriptorHeapPools[i].heaps[j]) {
|
|
D3D12_INTERNAL_DestroyDescriptorHeap(renderer->descriptorHeapPools[i].heaps[j]);
|
|
renderer->descriptorHeapPools[i].heaps[j] = NULL;
|
|
}
|
|
}
|
|
SDL_free(renderer->descriptorHeapPools[i].heaps);
|
|
}
|
|
if (renderer->descriptorHeapPools[i].lock) {
|
|
SDL_DestroyMutex(renderer->descriptorHeapPools[i].lock);
|
|
renderer->descriptorHeapPools[i].lock = NULL;
|
|
}
|
|
}
|
|
|
|
// Release command buffers
|
|
for (Uint32 i = 0; i < renderer->availableCommandBufferCount; i += 1) {
|
|
if (renderer->availableCommandBuffers[i]) {
|
|
D3D12_INTERNAL_DestroyCommandBuffer(renderer->availableCommandBuffers[i]);
|
|
renderer->availableCommandBuffers[i] = NULL;
|
|
}
|
|
}
|
|
|
|
// Release fences
|
|
for (Uint32 i = 0; i < renderer->availableFenceCount; i += 1) {
|
|
if (renderer->availableFences[i]) {
|
|
D3D12_INTERNAL_DestroyFence(renderer->availableFences[i]);
|
|
renderer->availableFences[i] = NULL;
|
|
}
|
|
}
|
|
|
|
// Clean up allocations
|
|
SDL_free(renderer->availableCommandBuffers);
|
|
SDL_free(renderer->submittedCommandBuffers);
|
|
SDL_free(renderer->uniformBufferPool);
|
|
SDL_free(renderer->claimedWindows);
|
|
SDL_free(renderer->availableFences);
|
|
SDL_free(renderer->buffersToDestroy);
|
|
SDL_free(renderer->texturesToDestroy);
|
|
SDL_free(renderer->samplersToDestroy);
|
|
SDL_free(renderer->graphicsPipelinesToDestroy);
|
|
SDL_free(renderer->computePipelinesToDestroy);
|
|
|
|
// Tear down D3D12 objects
|
|
if (renderer->indirectDrawCommandSignature) {
|
|
ID3D12CommandSignature_Release(renderer->indirectDrawCommandSignature);
|
|
renderer->indirectDrawCommandSignature = NULL;
|
|
}
|
|
if (renderer->indirectIndexedDrawCommandSignature) {
|
|
ID3D12CommandSignature_Release(renderer->indirectIndexedDrawCommandSignature);
|
|
renderer->indirectIndexedDrawCommandSignature = NULL;
|
|
}
|
|
if (renderer->indirectDispatchCommandSignature) {
|
|
ID3D12CommandSignature_Release(renderer->indirectDispatchCommandSignature);
|
|
renderer->indirectDispatchCommandSignature = NULL;
|
|
}
|
|
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
if (renderer->commandQueue) {
|
|
ID3D12CommandQueue_Release(renderer->commandQueue);
|
|
renderer->commandQueue = NULL;
|
|
}
|
|
if (renderer->device) {
|
|
ID3D12Device_Release(renderer->device);
|
|
renderer->device = NULL;
|
|
}
|
|
if (renderer->adapter) {
|
|
IDXGIAdapter1_Release(renderer->adapter);
|
|
renderer->adapter = NULL;
|
|
}
|
|
if (renderer->factory) {
|
|
IDXGIFactory4_Release(renderer->factory);
|
|
renderer->factory = NULL;
|
|
}
|
|
if (renderer->dxgiDebug) {
|
|
IDXGIDebug_ReportLiveObjects(
|
|
renderer->dxgiDebug,
|
|
D3D_IID_DXGI_DEBUG_ALL,
|
|
(DXGI_DEBUG_RLO_FLAGS)(DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_DETAIL));
|
|
IDXGIDebug_Release(renderer->dxgiDebug);
|
|
renderer->dxgiDebug = NULL;
|
|
}
|
|
#endif
|
|
if (renderer->d3d12_dll) {
|
|
SDL_UnloadObject(renderer->d3d12_dll);
|
|
renderer->d3d12_dll = NULL;
|
|
}
|
|
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
if (renderer->dxgi_dll) {
|
|
SDL_UnloadObject(renderer->dxgi_dll);
|
|
renderer->dxgi_dll = NULL;
|
|
}
|
|
if (renderer->dxgidebug_dll) {
|
|
SDL_UnloadObject(renderer->dxgidebug_dll);
|
|
renderer->dxgidebug_dll = NULL;
|
|
}
|
|
#endif
|
|
renderer->D3D12SerializeRootSignature_func = NULL;
|
|
|
|
if (renderer->iconv) {
|
|
SDL_iconv_close(renderer->iconv);
|
|
}
|
|
|
|
SDL_DestroyMutex(renderer->stagingDescriptorHeapLock);
|
|
SDL_DestroyMutex(renderer->acquireCommandBufferLock);
|
|
SDL_DestroyMutex(renderer->acquireUniformBufferLock);
|
|
SDL_DestroyMutex(renderer->submitLock);
|
|
SDL_DestroyMutex(renderer->windowLock);
|
|
SDL_DestroyMutex(renderer->fenceLock);
|
|
SDL_DestroyMutex(renderer->disposeLock);
|
|
SDL_free(renderer);
|
|
}
|
|
|
|
static void D3D12_DestroyDevice(SDL_GPUDevice *device)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)device->driverData;
|
|
|
|
// Release blit pipeline structures
|
|
D3D12_INTERNAL_ReleaseBlitPipelines((SDL_GPURenderer *)renderer);
|
|
|
|
// Flush any remaining GPU work...
|
|
D3D12_Wait((SDL_GPURenderer *)renderer);
|
|
|
|
// Release window data
|
|
for (Sint32 i = renderer->claimedWindowCount - 1; i >= 0; i -= 1) {
|
|
D3D12_ReleaseWindow((SDL_GPURenderer *)renderer, renderer->claimedWindows[i]->window);
|
|
}
|
|
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
SDL_free(device);
|
|
}
|
|
|
|
// Barriers
|
|
|
|
static inline Uint32 D3D12_INTERNAL_CalcSubresource(
|
|
Uint32 mipLevel,
|
|
Uint32 layer,
|
|
Uint32 numLevels)
|
|
{
|
|
return mipLevel + (layer * numLevels);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_ResourceBarrier(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12_RESOURCE_STATES sourceState,
|
|
D3D12_RESOURCE_STATES destinationState,
|
|
ID3D12Resource *resource,
|
|
Uint32 subresourceIndex,
|
|
bool needsUavBarrier)
|
|
{
|
|
D3D12_RESOURCE_BARRIER barrierDesc[2];
|
|
Uint32 numBarriers = 0;
|
|
|
|
// No transition barrier is needed if the state is not changing.
|
|
if (sourceState != destinationState) {
|
|
barrierDesc[numBarriers].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
|
barrierDesc[numBarriers].Flags = (D3D12_RESOURCE_BARRIER_FLAGS)0;
|
|
barrierDesc[numBarriers].Transition.StateBefore = sourceState;
|
|
barrierDesc[numBarriers].Transition.StateAfter = destinationState;
|
|
barrierDesc[numBarriers].Transition.pResource = resource;
|
|
barrierDesc[numBarriers].Transition.Subresource = subresourceIndex;
|
|
|
|
numBarriers += 1;
|
|
}
|
|
|
|
if (needsUavBarrier) {
|
|
barrierDesc[numBarriers].Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
|
|
barrierDesc[numBarriers].Flags = (D3D12_RESOURCE_BARRIER_FLAGS)0;
|
|
barrierDesc[numBarriers].UAV.pResource = resource;
|
|
|
|
numBarriers += 1;
|
|
}
|
|
|
|
if (numBarriers > 0) {
|
|
ID3D12GraphicsCommandList_ResourceBarrier(
|
|
commandBuffer->graphicsCommandList,
|
|
numBarriers,
|
|
barrierDesc);
|
|
}
|
|
}
|
|
|
|
static void D3D12_INTERNAL_TextureSubresourceBarrier(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12_RESOURCE_STATES sourceState,
|
|
D3D12_RESOURCE_STATES destinationState,
|
|
D3D12TextureSubresource *textureSubresource)
|
|
{
|
|
D3D12_INTERNAL_ResourceBarrier(
|
|
commandBuffer,
|
|
sourceState,
|
|
destinationState,
|
|
textureSubresource->parent->resource,
|
|
textureSubresource->index,
|
|
textureSubresource->parent->container->header.info.usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE);
|
|
}
|
|
|
|
static D3D12_RESOURCE_STATES D3D12_INTERNAL_DefaultTextureResourceState(
|
|
SDL_GPUTextureUsageFlags usageFlags)
|
|
{
|
|
// NOTE: order matters here!
|
|
|
|
if (usageFlags & SDL_GPU_TEXTUREUSAGE_SAMPLER) {
|
|
return D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
|
|
} else if (usageFlags & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ) {
|
|
return D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
|
|
} else if (usageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
|
|
return D3D12_RESOURCE_STATE_RENDER_TARGET;
|
|
} else if (usageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
|
|
return D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
|
} else if (usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ) {
|
|
return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
|
} else if (usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
|
|
return D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Texture has no default usage mode!");
|
|
return D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
|
|
}
|
|
}
|
|
|
|
static void D3D12_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12_RESOURCE_STATES destinationUsageMode,
|
|
D3D12TextureSubresource *textureSubresource)
|
|
{
|
|
D3D12_INTERNAL_TextureSubresourceBarrier(
|
|
commandBuffer,
|
|
D3D12_INTERNAL_DefaultTextureResourceState(textureSubresource->parent->container->header.info.usage),
|
|
destinationUsageMode,
|
|
textureSubresource);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_TextureTransitionFromDefaultUsage(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12_RESOURCE_STATES destinationUsageMode,
|
|
D3D12Texture *texture)
|
|
{
|
|
for (Uint32 i = 0; i < texture->subresourceCount; i += 1) {
|
|
D3D12_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
commandBuffer,
|
|
destinationUsageMode,
|
|
&texture->subresources[i]);
|
|
}
|
|
}
|
|
|
|
static void D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12_RESOURCE_STATES sourceUsageMode,
|
|
D3D12TextureSubresource *textureSubresource)
|
|
{
|
|
D3D12_INTERNAL_TextureSubresourceBarrier(
|
|
commandBuffer,
|
|
sourceUsageMode,
|
|
D3D12_INTERNAL_DefaultTextureResourceState(textureSubresource->parent->container->header.info.usage),
|
|
textureSubresource);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_TextureTransitionToDefaultUsage(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12_RESOURCE_STATES sourceUsageMode,
|
|
D3D12Texture *texture)
|
|
{
|
|
for (Uint32 i = 0; i < texture->subresourceCount; i += 1) {
|
|
D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
commandBuffer,
|
|
sourceUsageMode,
|
|
&texture->subresources[i]);
|
|
}
|
|
}
|
|
|
|
static D3D12_RESOURCE_STATES D3D12_INTERNAL_DefaultBufferResourceState(
|
|
D3D12Buffer *buffer)
|
|
{
|
|
if (buffer->container->usage & SDL_GPU_BUFFERUSAGE_VERTEX) {
|
|
return D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
|
|
} else if (buffer->container->usage & SDL_GPU_BUFFERUSAGE_INDEX) {
|
|
return D3D12_RESOURCE_STATE_INDEX_BUFFER;
|
|
} else if (buffer->container->usage & SDL_GPU_BUFFERUSAGE_INDIRECT) {
|
|
return D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
|
|
} else if (buffer->container->usage & SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ) {
|
|
return D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
|
|
} else if (buffer->container->usage & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ) {
|
|
return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
|
} else if (buffer->container->usage & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE) {
|
|
return D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Buffer has no default usage mode!");
|
|
return D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
|
|
}
|
|
}
|
|
|
|
static void D3D12_INTERNAL_BufferBarrier(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12_RESOURCE_STATES sourceState,
|
|
D3D12_RESOURCE_STATES destinationState,
|
|
D3D12Buffer *buffer)
|
|
{
|
|
D3D12_INTERNAL_ResourceBarrier(
|
|
commandBuffer,
|
|
buffer->transitioned ? sourceState : D3D12_RESOURCE_STATE_COMMON,
|
|
destinationState,
|
|
buffer->handle,
|
|
0,
|
|
buffer->container->usage & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE);
|
|
|
|
buffer->transitioned = true;
|
|
}
|
|
|
|
static void D3D12_INTERNAL_BufferTransitionFromDefaultUsage(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12_RESOURCE_STATES destinationState,
|
|
D3D12Buffer *buffer)
|
|
{
|
|
D3D12_INTERNAL_BufferBarrier(
|
|
commandBuffer,
|
|
D3D12_INTERNAL_DefaultBufferResourceState(buffer),
|
|
destinationState,
|
|
buffer);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_BufferTransitionToDefaultUsage(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12_RESOURCE_STATES sourceState,
|
|
D3D12Buffer *buffer)
|
|
{
|
|
D3D12_INTERNAL_BufferBarrier(
|
|
commandBuffer,
|
|
sourceState,
|
|
D3D12_INTERNAL_DefaultBufferResourceState(buffer),
|
|
buffer);
|
|
}
|
|
|
|
// Resource tracking
|
|
|
|
#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 = (type *)SDL_realloc( \
|
|
commandBuffer->array, \
|
|
commandBuffer->capacity * sizeof(type)); \
|
|
} \
|
|
commandBuffer->array[commandBuffer->count] = resource; \
|
|
commandBuffer->count += 1; \
|
|
SDL_AtomicIncRef(&resource->referenceCount);
|
|
|
|
static void D3D12_INTERNAL_TrackTexture(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12Texture *texture)
|
|
{
|
|
TRACK_RESOURCE(
|
|
texture,
|
|
D3D12Texture *,
|
|
usedTextures,
|
|
usedTextureCount,
|
|
usedTextureCapacity)
|
|
}
|
|
|
|
static void D3D12_INTERNAL_TrackBuffer(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12Buffer *buffer)
|
|
{
|
|
TRACK_RESOURCE(
|
|
buffer,
|
|
D3D12Buffer *,
|
|
usedBuffers,
|
|
usedBufferCount,
|
|
usedBufferCapacity)
|
|
}
|
|
|
|
static void D3D12_INTERNAL_TrackSampler(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12Sampler *sampler)
|
|
{
|
|
TRACK_RESOURCE(
|
|
sampler,
|
|
D3D12Sampler *,
|
|
usedSamplers,
|
|
usedSamplerCount,
|
|
usedSamplerCapacity)
|
|
}
|
|
|
|
static void D3D12_INTERNAL_TrackGraphicsPipeline(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12GraphicsPipeline *graphicsPipeline)
|
|
{
|
|
TRACK_RESOURCE(
|
|
graphicsPipeline,
|
|
D3D12GraphicsPipeline *,
|
|
usedGraphicsPipelines,
|
|
usedGraphicsPipelineCount,
|
|
usedGraphicsPipelineCapacity)
|
|
}
|
|
|
|
static void D3D12_INTERNAL_TrackComputePipeline(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12ComputePipeline *computePipeline)
|
|
{
|
|
TRACK_RESOURCE(
|
|
computePipeline,
|
|
D3D12ComputePipeline *,
|
|
usedComputePipelines,
|
|
usedComputePipelineCount,
|
|
usedComputePipelineCapacity)
|
|
}
|
|
|
|
#undef TRACK_RESOURCE
|
|
|
|
// State Creation
|
|
|
|
static D3D12DescriptorHeap *D3D12_INTERNAL_CreateDescriptorHeap(
|
|
D3D12Renderer *renderer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE type,
|
|
Uint32 descriptorCount,
|
|
bool staging)
|
|
{
|
|
D3D12DescriptorHeap *heap;
|
|
ID3D12DescriptorHeap *handle;
|
|
D3D12_DESCRIPTOR_HEAP_DESC heapDesc;
|
|
HRESULT res;
|
|
|
|
heap = (D3D12DescriptorHeap *)SDL_calloc(1, sizeof(D3D12DescriptorHeap));
|
|
if (!heap) {
|
|
return NULL;
|
|
}
|
|
|
|
heap->currentDescriptorIndex = 0;
|
|
heap->inactiveDescriptorCount = 0;
|
|
heap->inactiveDescriptorIndices = NULL;
|
|
|
|
if (staging) {
|
|
heap->inactiveDescriptorIndices = (Uint32 *)SDL_calloc(descriptorCount, sizeof(Uint32));
|
|
if (!heap->inactiveDescriptorIndices) {
|
|
D3D12_INTERNAL_DestroyDescriptorHeap(heap);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
heapDesc.NumDescriptors = descriptorCount;
|
|
heapDesc.Type = type;
|
|
heapDesc.Flags = staging ? D3D12_DESCRIPTOR_HEAP_FLAG_NONE : D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
|
heapDesc.NodeMask = 0;
|
|
|
|
res = ID3D12Device_CreateDescriptorHeap(
|
|
renderer->device,
|
|
&heapDesc,
|
|
D3D_GUID(D3D_IID_ID3D12DescriptorHeap),
|
|
(void **)&handle);
|
|
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_LogError(renderer->device, "Failed to create descriptor heap!", res);
|
|
D3D12_INTERNAL_DestroyDescriptorHeap(heap);
|
|
return NULL;
|
|
}
|
|
|
|
heap->handle = handle;
|
|
heap->heapType = type;
|
|
heap->maxDescriptors = descriptorCount;
|
|
heap->staging = staging;
|
|
heap->descriptorSize = ID3D12Device_GetDescriptorHandleIncrementSize(renderer->device, type);
|
|
D3D_CALL_RET(handle, GetCPUDescriptorHandleForHeapStart, &heap->descriptorHeapCPUStart);
|
|
if (!staging) {
|
|
D3D_CALL_RET(handle, GetGPUDescriptorHandleForHeapStart, &heap->descriptorHeapGPUStart);
|
|
}
|
|
|
|
return heap;
|
|
}
|
|
|
|
static D3D12DescriptorHeap *D3D12_INTERNAL_AcquireDescriptorHeapFromPool(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType)
|
|
{
|
|
D3D12DescriptorHeap *result;
|
|
D3D12Renderer *renderer = commandBuffer->renderer;
|
|
D3D12DescriptorHeapPool *pool = &renderer->descriptorHeapPools[descriptorHeapType];
|
|
|
|
SDL_LockMutex(pool->lock);
|
|
if (pool->count > 0) {
|
|
result = pool->heaps[pool->count - 1];
|
|
pool->count -= 1;
|
|
} else {
|
|
result = D3D12_INTERNAL_CreateDescriptorHeap(
|
|
renderer,
|
|
descriptorHeapType,
|
|
descriptorHeapType == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ? VIEW_GPU_DESCRIPTOR_COUNT : SAMPLER_GPU_DESCRIPTOR_COUNT,
|
|
false);
|
|
}
|
|
SDL_UnlockMutex(pool->lock);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void D3D12_INTERNAL_ReturnDescriptorHeapToPool(
|
|
D3D12Renderer *renderer,
|
|
D3D12DescriptorHeap *heap)
|
|
{
|
|
D3D12DescriptorHeapPool *pool = &renderer->descriptorHeapPools[heap->heapType];
|
|
|
|
heap->currentDescriptorIndex = 0;
|
|
|
|
SDL_LockMutex(pool->lock);
|
|
if (pool->count >= pool->capacity) {
|
|
pool->capacity *= 2;
|
|
pool->heaps = (D3D12DescriptorHeap **)SDL_realloc(
|
|
pool->heaps,
|
|
pool->capacity * sizeof(D3D12DescriptorHeap *));
|
|
}
|
|
|
|
pool->heaps[pool->count] = heap;
|
|
pool->count += 1;
|
|
SDL_UnlockMutex(pool->lock);
|
|
}
|
|
|
|
/*
|
|
* The root signature lets us define "root parameters" which are essentially bind points for resources.
|
|
* These let us define the register ranges as well as the register "space".
|
|
* The register space is akin to the descriptor set index in Vulkan, which allows us to group resources
|
|
* by stage so that the registers from the vertex and fragment shaders don't clobber each other.
|
|
*
|
|
* Most of our root parameters are implemented as "descriptor tables" so we can
|
|
* copy and then point to contiguous descriptor regions.
|
|
* Uniform buffers are the exception - these have to be implemented as raw "root descriptors" so
|
|
* that we can dynamically update the address that the constant buffer view points to.
|
|
*
|
|
* The root signature has a maximum size of 64 DWORDs.
|
|
* A descriptor table uses 1 DWORD.
|
|
* A root descriptor uses 2 DWORDS.
|
|
* This means our biggest root signature uses 24 DWORDs total, well under the limit.
|
|
*
|
|
* The root parameter indices are created dynamically and stored in the D3D12GraphicsRootSignature struct.
|
|
*/
|
|
static D3D12GraphicsRootSignature *D3D12_INTERNAL_CreateGraphicsRootSignature(
|
|
D3D12Renderer *renderer,
|
|
D3D12Shader *vertexShader,
|
|
D3D12Shader *fragmentShader)
|
|
{
|
|
// FIXME: I think the max can be smaller...
|
|
D3D12_ROOT_PARAMETER rootParameters[MAX_ROOT_SIGNATURE_PARAMETERS];
|
|
D3D12_DESCRIPTOR_RANGE descriptorRanges[MAX_ROOT_SIGNATURE_PARAMETERS];
|
|
Uint32 parameterCount = 0;
|
|
Uint32 rangeCount = 0;
|
|
D3D12_DESCRIPTOR_RANGE descriptorRange;
|
|
D3D12_ROOT_PARAMETER rootParameter;
|
|
D3D12GraphicsRootSignature *d3d12GraphicsRootSignature =
|
|
(D3D12GraphicsRootSignature *)SDL_calloc(1, sizeof(D3D12GraphicsRootSignature));
|
|
if (!d3d12GraphicsRootSignature) {
|
|
return NULL;
|
|
}
|
|
|
|
SDL_zeroa(rootParameters);
|
|
SDL_zeroa(descriptorRanges);
|
|
SDL_zero(rootParameter);
|
|
|
|
d3d12GraphicsRootSignature->vertexSamplerRootIndex = -1;
|
|
d3d12GraphicsRootSignature->vertexSamplerTextureRootIndex = -1;
|
|
d3d12GraphicsRootSignature->vertexStorageTextureRootIndex = -1;
|
|
d3d12GraphicsRootSignature->vertexStorageBufferRootIndex = -1;
|
|
|
|
d3d12GraphicsRootSignature->fragmentSamplerRootIndex = -1;
|
|
d3d12GraphicsRootSignature->fragmentSamplerTextureRootIndex = -1;
|
|
d3d12GraphicsRootSignature->fragmentStorageTextureRootIndex = -1;
|
|
d3d12GraphicsRootSignature->fragmentStorageBufferRootIndex = -1;
|
|
|
|
for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
|
|
d3d12GraphicsRootSignature->vertexUniformBufferRootIndex[i] = -1;
|
|
d3d12GraphicsRootSignature->fragmentUniformBufferRootIndex[i] = -1;
|
|
}
|
|
|
|
if (vertexShader->num_samplers > 0) {
|
|
// Vertex Samplers
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
|
|
descriptorRange.NumDescriptors = vertexShader->num_samplers;
|
|
descriptorRange.BaseShaderRegister = 0;
|
|
descriptorRange.RegisterSpace = 0;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12GraphicsRootSignature->vertexSamplerRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
|
descriptorRange.NumDescriptors = vertexShader->num_samplers;
|
|
descriptorRange.BaseShaderRegister = 0;
|
|
descriptorRange.RegisterSpace = 0;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12GraphicsRootSignature->vertexSamplerTextureRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
if (vertexShader->numStorageTextures) {
|
|
// Vertex storage textures
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
|
descriptorRange.NumDescriptors = vertexShader->numStorageTextures;
|
|
descriptorRange.BaseShaderRegister = vertexShader->num_samplers;
|
|
descriptorRange.RegisterSpace = 0;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12GraphicsRootSignature->vertexStorageTextureRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
if (vertexShader->numStorageBuffers) {
|
|
|
|
// Vertex storage buffers
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
|
descriptorRange.NumDescriptors = vertexShader->numStorageBuffers;
|
|
descriptorRange.BaseShaderRegister = vertexShader->num_samplers + vertexShader->numStorageTextures;
|
|
descriptorRange.RegisterSpace = 0;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12GraphicsRootSignature->vertexStorageBufferRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
// Vertex Uniforms
|
|
for (Uint32 i = 0; i < vertexShader->numUniformBuffers; i += 1) {
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
|
rootParameter.Descriptor.ShaderRegister = i;
|
|
rootParameter.Descriptor.RegisterSpace = 1;
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12GraphicsRootSignature->vertexUniformBufferRootIndex[i] = parameterCount;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
if (fragmentShader->num_samplers) {
|
|
// Fragment Samplers
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
|
|
descriptorRange.NumDescriptors = fragmentShader->num_samplers;
|
|
descriptorRange.BaseShaderRegister = 0;
|
|
descriptorRange.RegisterSpace = 2;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12GraphicsRootSignature->fragmentSamplerRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
|
descriptorRange.NumDescriptors = fragmentShader->num_samplers;
|
|
descriptorRange.BaseShaderRegister = 0;
|
|
descriptorRange.RegisterSpace = 2;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12GraphicsRootSignature->fragmentSamplerTextureRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
if (fragmentShader->numStorageTextures) {
|
|
// Fragment Storage Textures
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
|
descriptorRange.NumDescriptors = fragmentShader->numStorageTextures;
|
|
descriptorRange.BaseShaderRegister = fragmentShader->num_samplers;
|
|
descriptorRange.RegisterSpace = 2;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12GraphicsRootSignature->fragmentStorageTextureRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
if (fragmentShader->numStorageBuffers) {
|
|
// Fragment Storage Buffers
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
|
descriptorRange.NumDescriptors = fragmentShader->numStorageBuffers;
|
|
descriptorRange.BaseShaderRegister = fragmentShader->num_samplers + fragmentShader->numStorageTextures;
|
|
descriptorRange.RegisterSpace = 2;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12GraphicsRootSignature->fragmentStorageBufferRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
// Fragment Uniforms
|
|
for (Uint32 i = 0; i < fragmentShader->numUniformBuffers; i += 1) {
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
|
rootParameter.Descriptor.ShaderRegister = i;
|
|
rootParameter.Descriptor.RegisterSpace = 3;
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12GraphicsRootSignature->fragmentUniformBufferRootIndex[i] = parameterCount;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
// FIXME: shouldn't have to assert here
|
|
SDL_assert(parameterCount <= MAX_ROOT_SIGNATURE_PARAMETERS);
|
|
SDL_assert(rangeCount <= MAX_ROOT_SIGNATURE_PARAMETERS);
|
|
|
|
// Create the root signature description
|
|
D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc;
|
|
rootSignatureDesc.NumParameters = parameterCount;
|
|
rootSignatureDesc.pParameters = rootParameters;
|
|
rootSignatureDesc.NumStaticSamplers = 0;
|
|
rootSignatureDesc.pStaticSamplers = NULL;
|
|
rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
|
|
|
// Serialize the root signature
|
|
ID3DBlob *serializedRootSignature;
|
|
ID3DBlob *errorBlob;
|
|
HRESULT res = renderer->D3D12SerializeRootSignature_func(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &serializedRootSignature, &errorBlob);
|
|
|
|
if (FAILED(res)) {
|
|
if (errorBlob) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to serialize RootSignature: %s", (const char *)ID3D10Blob_GetBufferPointer(errorBlob));
|
|
ID3D10Blob_Release(errorBlob);
|
|
}
|
|
D3D12_INTERNAL_DestroyGraphicsRootSignature(d3d12GraphicsRootSignature);
|
|
return NULL;
|
|
}
|
|
|
|
// Create the root signature
|
|
ID3D12RootSignature *rootSignature;
|
|
|
|
res = ID3D12Device_CreateRootSignature(
|
|
renderer->device,
|
|
0,
|
|
ID3D10Blob_GetBufferPointer(serializedRootSignature),
|
|
ID3D10Blob_GetBufferSize(serializedRootSignature),
|
|
D3D_GUID(D3D_IID_ID3D12RootSignature),
|
|
(void **)&rootSignature);
|
|
|
|
if (FAILED(res)) {
|
|
if (errorBlob) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create RootSignature");
|
|
ID3D10Blob_Release(errorBlob);
|
|
}
|
|
D3D12_INTERNAL_DestroyGraphicsRootSignature(d3d12GraphicsRootSignature);
|
|
return NULL;
|
|
}
|
|
|
|
d3d12GraphicsRootSignature->handle = rootSignature;
|
|
return d3d12GraphicsRootSignature;
|
|
}
|
|
|
|
static bool D3D12_INTERNAL_CreateShaderBytecode(
|
|
D3D12Renderer *renderer,
|
|
Uint32 stage,
|
|
SDL_GPUShaderFormat format,
|
|
const Uint8 *code,
|
|
size_t codeSize,
|
|
const char *entrypointName,
|
|
void **pBytecode,
|
|
size_t *pBytecodeSize)
|
|
{
|
|
if (pBytecode != NULL) {
|
|
*pBytecode = SDL_malloc(codeSize);
|
|
if (!*pBytecode) {
|
|
return false;
|
|
}
|
|
SDL_memcpy(*pBytecode, code, codeSize);
|
|
*pBytecodeSize = codeSize;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static D3D12ComputeRootSignature *D3D12_INTERNAL_CreateComputeRootSignature(
|
|
D3D12Renderer *renderer,
|
|
const SDL_GPUComputePipelineCreateInfo *createInfo)
|
|
{
|
|
// FIXME: I think the max can be smaller...
|
|
D3D12_ROOT_PARAMETER rootParameters[MAX_ROOT_SIGNATURE_PARAMETERS];
|
|
D3D12_DESCRIPTOR_RANGE descriptorRanges[MAX_ROOT_SIGNATURE_PARAMETERS];
|
|
Uint32 parameterCount = 0;
|
|
Uint32 rangeCount = 0;
|
|
D3D12_DESCRIPTOR_RANGE descriptorRange;
|
|
D3D12_ROOT_PARAMETER rootParameter;
|
|
D3D12ComputeRootSignature *d3d12ComputeRootSignature =
|
|
(D3D12ComputeRootSignature *)SDL_calloc(1, sizeof(D3D12ComputeRootSignature));
|
|
if (!d3d12ComputeRootSignature) {
|
|
return NULL;
|
|
}
|
|
|
|
SDL_zeroa(rootParameters);
|
|
SDL_zeroa(descriptorRanges);
|
|
SDL_zero(rootParameter);
|
|
|
|
d3d12ComputeRootSignature->samplerRootIndex = -1;
|
|
d3d12ComputeRootSignature->samplerTextureRootIndex = -1;
|
|
d3d12ComputeRootSignature->readOnlyStorageTextureRootIndex = -1;
|
|
d3d12ComputeRootSignature->readOnlyStorageBufferRootIndex = -1;
|
|
d3d12ComputeRootSignature->writeOnlyStorageTextureRootIndex = -1;
|
|
d3d12ComputeRootSignature->writeOnlyStorageBufferRootIndex = -1;
|
|
|
|
for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
|
|
d3d12ComputeRootSignature->uniformBufferRootIndex[i] = -1;
|
|
}
|
|
|
|
if (createInfo->num_samplers) {
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
|
|
descriptorRange.NumDescriptors = createInfo->num_samplers;
|
|
descriptorRange.BaseShaderRegister = 0;
|
|
descriptorRange.RegisterSpace = 0;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // ALL is used for compute
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12ComputeRootSignature->samplerRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
|
descriptorRange.NumDescriptors = createInfo->num_samplers;
|
|
descriptorRange.BaseShaderRegister = 0;
|
|
descriptorRange.RegisterSpace = 0;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // ALL is used for compute
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12ComputeRootSignature->samplerTextureRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
if (createInfo->num_readonly_storage_textures) {
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
|
descriptorRange.NumDescriptors = createInfo->num_readonly_storage_textures;
|
|
descriptorRange.BaseShaderRegister = createInfo->num_samplers;
|
|
descriptorRange.RegisterSpace = 0;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // ALL is used for compute
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12ComputeRootSignature->readOnlyStorageTextureRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
if (createInfo->num_readonly_storage_buffers) {
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
|
descriptorRange.NumDescriptors = createInfo->num_readonly_storage_buffers;
|
|
descriptorRange.BaseShaderRegister = createInfo->num_samplers + createInfo->num_readonly_storage_textures;
|
|
descriptorRange.RegisterSpace = 0;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // ALL is used for compute
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12ComputeRootSignature->readOnlyStorageBufferRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
if (createInfo->num_writeonly_storage_textures) {
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
|
descriptorRange.NumDescriptors = createInfo->num_writeonly_storage_textures;
|
|
descriptorRange.BaseShaderRegister = 0;
|
|
descriptorRange.RegisterSpace = 1;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // ALL is used for compute
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12ComputeRootSignature->writeOnlyStorageTextureRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
if (createInfo->num_writeonly_storage_buffers) {
|
|
descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
|
descriptorRange.NumDescriptors = createInfo->num_writeonly_storage_buffers;
|
|
descriptorRange.BaseShaderRegister = createInfo->num_writeonly_storage_textures;
|
|
descriptorRange.RegisterSpace = 1;
|
|
descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
|
descriptorRanges[rangeCount] = descriptorRange;
|
|
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
|
rootParameter.DescriptorTable.NumDescriptorRanges = 1;
|
|
rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // ALL is used for compute
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12ComputeRootSignature->writeOnlyStorageBufferRootIndex = parameterCount;
|
|
rangeCount += 1;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
for (Uint32 i = 0; i < createInfo->num_uniform_buffers; i += 1) {
|
|
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
|
rootParameter.Descriptor.ShaderRegister = i;
|
|
rootParameter.Descriptor.RegisterSpace = 2;
|
|
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // ALL is used for compute
|
|
rootParameters[parameterCount] = rootParameter;
|
|
d3d12ComputeRootSignature->uniformBufferRootIndex[i] = parameterCount;
|
|
parameterCount += 1;
|
|
}
|
|
|
|
D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc;
|
|
rootSignatureDesc.NumParameters = parameterCount;
|
|
rootSignatureDesc.pParameters = rootParameters;
|
|
rootSignatureDesc.NumStaticSamplers = 0;
|
|
rootSignatureDesc.pStaticSamplers = NULL;
|
|
rootSignatureDesc.Flags = (D3D12_ROOT_SIGNATURE_FLAGS)0;
|
|
|
|
ID3DBlob *serializedRootSignature;
|
|
ID3DBlob *errorBlob;
|
|
HRESULT res = renderer->D3D12SerializeRootSignature_func(
|
|
&rootSignatureDesc,
|
|
D3D_ROOT_SIGNATURE_VERSION_1,
|
|
&serializedRootSignature,
|
|
&errorBlob);
|
|
|
|
if (FAILED(res)) {
|
|
if (errorBlob) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to serialize RootSignature: %s", (const char *)ID3D10Blob_GetBufferPointer(errorBlob));
|
|
ID3D10Blob_Release(errorBlob);
|
|
}
|
|
D3D12_INTERNAL_DestroyComputeRootSignature(d3d12ComputeRootSignature);
|
|
return NULL;
|
|
}
|
|
|
|
ID3D12RootSignature *rootSignature;
|
|
|
|
res = ID3D12Device_CreateRootSignature(
|
|
renderer->device,
|
|
0,
|
|
ID3D10Blob_GetBufferPointer(serializedRootSignature),
|
|
ID3D10Blob_GetBufferSize(serializedRootSignature),
|
|
D3D_GUID(D3D_IID_ID3D12RootSignature),
|
|
(void **)&rootSignature);
|
|
|
|
if (FAILED(res)) {
|
|
if (errorBlob) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create RootSignature");
|
|
ID3D10Blob_Release(errorBlob);
|
|
}
|
|
D3D12_INTERNAL_DestroyComputeRootSignature(d3d12ComputeRootSignature);
|
|
return NULL;
|
|
}
|
|
|
|
d3d12ComputeRootSignature->handle = rootSignature;
|
|
return d3d12ComputeRootSignature;
|
|
}
|
|
|
|
static SDL_GPUComputePipeline *D3D12_CreateComputePipeline(
|
|
SDL_GPURenderer *driverData,
|
|
const SDL_GPUComputePipelineCreateInfo *createinfo)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
void *bytecode;
|
|
size_t bytecodeSize;
|
|
ID3D12PipelineState *pipelineState;
|
|
|
|
if (!D3D12_INTERNAL_CreateShaderBytecode(
|
|
renderer,
|
|
SDL_GPU_SHADERSTAGE_COMPUTE,
|
|
createinfo->format,
|
|
createinfo->code,
|
|
createinfo->code_size,
|
|
createinfo->entrypoint,
|
|
&bytecode,
|
|
&bytecodeSize)) {
|
|
return NULL;
|
|
}
|
|
|
|
D3D12ComputeRootSignature *rootSignature = D3D12_INTERNAL_CreateComputeRootSignature(
|
|
renderer,
|
|
createinfo);
|
|
|
|
if (rootSignature == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not create root signature!");
|
|
SDL_free(bytecode);
|
|
return NULL;
|
|
}
|
|
|
|
D3D12_COMPUTE_PIPELINE_STATE_DESC pipelineDesc;
|
|
pipelineDesc.CS.pShaderBytecode = bytecode;
|
|
pipelineDesc.CS.BytecodeLength = bytecodeSize;
|
|
pipelineDesc.pRootSignature = rootSignature->handle;
|
|
pipelineDesc.CachedPSO.CachedBlobSizeInBytes = 0;
|
|
pipelineDesc.CachedPSO.pCachedBlob = NULL;
|
|
pipelineDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
|
|
pipelineDesc.NodeMask = 0;
|
|
|
|
HRESULT res = ID3D12Device_CreateComputePipelineState(
|
|
renderer->device,
|
|
&pipelineDesc,
|
|
D3D_GUID(D3D_IID_ID3D12PipelineState),
|
|
(void **)&pipelineState);
|
|
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_LogError(renderer->device, "Could not create compute pipeline state", res);
|
|
SDL_free(bytecode);
|
|
return NULL;
|
|
}
|
|
|
|
D3D12ComputePipeline *computePipeline =
|
|
(D3D12ComputePipeline *)SDL_calloc(1, sizeof(D3D12ComputePipeline));
|
|
|
|
if (!computePipeline) {
|
|
ID3D12PipelineState_Release(pipelineState);
|
|
SDL_free(bytecode);
|
|
return NULL;
|
|
}
|
|
|
|
computePipeline->pipelineState = pipelineState;
|
|
computePipeline->rootSignature = rootSignature;
|
|
computePipeline->numSamplers = createinfo->num_samplers;
|
|
computePipeline->numReadOnlyStorageTextures = createinfo->num_readonly_storage_textures;
|
|
computePipeline->numReadOnlyStorageBuffers = createinfo->num_readonly_storage_buffers;
|
|
computePipeline->numWriteOnlyStorageTextures = createinfo->num_writeonly_storage_textures;
|
|
computePipeline->numWriteOnlyStorageBuffers = createinfo->num_writeonly_storage_buffers;
|
|
computePipeline->numUniformBuffers = createinfo->num_uniform_buffers;
|
|
SDL_AtomicSet(&computePipeline->referenceCount, 0);
|
|
|
|
return (SDL_GPUComputePipeline *)computePipeline;
|
|
}
|
|
|
|
static bool D3D12_INTERNAL_ConvertRasterizerState(SDL_GPURasterizerState rasterizerState, D3D12_RASTERIZER_DESC *desc)
|
|
{
|
|
if (!desc) {
|
|
return false;
|
|
}
|
|
|
|
desc->FillMode = SDLToD3D12_FillMode[rasterizerState.fill_mode];
|
|
desc->CullMode = SDLToD3D12_CullMode[rasterizerState.cull_mode];
|
|
|
|
switch (rasterizerState.front_face) {
|
|
case SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE:
|
|
desc->FrontCounterClockwise = TRUE;
|
|
break;
|
|
case SDL_GPU_FRONTFACE_CLOCKWISE:
|
|
desc->FrontCounterClockwise = FALSE;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (rasterizerState.enable_depth_bias) {
|
|
desc->DepthBias = SDL_lroundf(rasterizerState.depth_bias_constant_factor);
|
|
desc->DepthBiasClamp = rasterizerState.depth_bias_clamp;
|
|
desc->SlopeScaledDepthBias = rasterizerState.depth_bias_slope_factor;
|
|
} else {
|
|
desc->DepthBias = 0;
|
|
desc->DepthBiasClamp = 0.0f;
|
|
desc->SlopeScaledDepthBias = 0.0f;
|
|
}
|
|
|
|
desc->DepthClipEnable = TRUE;
|
|
desc->MultisampleEnable = FALSE;
|
|
desc->AntialiasedLineEnable = FALSE;
|
|
desc->ForcedSampleCount = 0;
|
|
desc->ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool D3D12_INTERNAL_ConvertBlendState(
|
|
const SDL_GPUGraphicsPipelineCreateInfo *pipelineInfo,
|
|
D3D12_BLEND_DESC *blendDesc)
|
|
{
|
|
if (!blendDesc) {
|
|
return false;
|
|
}
|
|
|
|
SDL_zerop(blendDesc);
|
|
blendDesc->AlphaToCoverageEnable = FALSE;
|
|
blendDesc->IndependentBlendEnable = FALSE;
|
|
|
|
for (UINT i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) {
|
|
D3D12_RENDER_TARGET_BLEND_DESC rtBlendDesc;
|
|
rtBlendDesc.BlendEnable = FALSE;
|
|
rtBlendDesc.LogicOpEnable = FALSE;
|
|
rtBlendDesc.SrcBlend = D3D12_BLEND_ONE;
|
|
rtBlendDesc.DestBlend = D3D12_BLEND_ZERO;
|
|
rtBlendDesc.BlendOp = D3D12_BLEND_OP_ADD;
|
|
rtBlendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;
|
|
rtBlendDesc.DestBlendAlpha = D3D12_BLEND_ZERO;
|
|
rtBlendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
|
|
rtBlendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
|
|
rtBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
|
|
|
|
// If target_info has more blend states, you can set IndependentBlendEnable to TRUE and assign different blend states to each render target slot
|
|
if (i < pipelineInfo->target_info.num_color_targets) {
|
|
SDL_GPUColorTargetBlendState sdlBlendState = pipelineInfo->target_info.color_target_descriptions[i].blend_state;
|
|
SDL_GPUColorComponentFlags colorWriteMask = sdlBlendState.enable_color_write_mask ?
|
|
sdlBlendState.color_write_mask :
|
|
0xF;
|
|
|
|
rtBlendDesc.BlendEnable = sdlBlendState.enable_blend;
|
|
rtBlendDesc.SrcBlend = SDLToD3D12_BlendFactor[sdlBlendState.src_color_blendfactor];
|
|
rtBlendDesc.DestBlend = SDLToD3D12_BlendFactor[sdlBlendState.dst_color_blendfactor];
|
|
rtBlendDesc.BlendOp = SDLToD3D12_BlendOp[sdlBlendState.color_blend_op];
|
|
rtBlendDesc.SrcBlendAlpha = SDLToD3D12_BlendFactorAlpha[sdlBlendState.src_alpha_blendfactor];
|
|
rtBlendDesc.DestBlendAlpha = SDLToD3D12_BlendFactorAlpha[sdlBlendState.dst_alpha_blendfactor];
|
|
rtBlendDesc.BlendOpAlpha = SDLToD3D12_BlendOp[sdlBlendState.alpha_blend_op];
|
|
rtBlendDesc.RenderTargetWriteMask = colorWriteMask;
|
|
|
|
if (i > 0) {
|
|
blendDesc->IndependentBlendEnable = TRUE;
|
|
}
|
|
}
|
|
|
|
blendDesc->RenderTarget[i] = rtBlendDesc;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool D3D12_INTERNAL_ConvertDepthStencilState(SDL_GPUDepthStencilState depthStencilState, D3D12_DEPTH_STENCIL_DESC *desc)
|
|
{
|
|
if (desc == NULL) {
|
|
return false;
|
|
}
|
|
|
|
desc->DepthEnable = depthStencilState.enable_depth_test == true ? TRUE : FALSE;
|
|
desc->DepthWriteMask = depthStencilState.enable_depth_write == true ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
|
|
desc->DepthFunc = SDLToD3D12_CompareOp[depthStencilState.compare_op];
|
|
desc->StencilEnable = depthStencilState.enable_stencil_test == true ? TRUE : FALSE;
|
|
desc->StencilReadMask = depthStencilState.compare_mask;
|
|
desc->StencilWriteMask = depthStencilState.write_mask;
|
|
|
|
desc->FrontFace.StencilFailOp = SDLToD3D12_StencilOp[depthStencilState.front_stencil_state.fail_op];
|
|
desc->FrontFace.StencilDepthFailOp = SDLToD3D12_StencilOp[depthStencilState.front_stencil_state.depth_fail_op];
|
|
desc->FrontFace.StencilPassOp = SDLToD3D12_StencilOp[depthStencilState.front_stencil_state.pass_op];
|
|
desc->FrontFace.StencilFunc = SDLToD3D12_CompareOp[depthStencilState.front_stencil_state.compare_op];
|
|
|
|
desc->BackFace.StencilFailOp = SDLToD3D12_StencilOp[depthStencilState.back_stencil_state.fail_op];
|
|
desc->BackFace.StencilDepthFailOp = SDLToD3D12_StencilOp[depthStencilState.back_stencil_state.depth_fail_op];
|
|
desc->BackFace.StencilPassOp = SDLToD3D12_StencilOp[depthStencilState.back_stencil_state.pass_op];
|
|
desc->BackFace.StencilFunc = SDLToD3D12_CompareOp[depthStencilState.back_stencil_state.compare_op];
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool D3D12_INTERNAL_ConvertVertexInputState(SDL_GPUVertexInputState vertexInputState, D3D12_INPUT_ELEMENT_DESC *desc, const char *semantic)
|
|
{
|
|
if (desc == NULL || vertexInputState.num_vertex_attributes == 0) {
|
|
return false;
|
|
}
|
|
|
|
for (Uint32 i = 0; i < vertexInputState.num_vertex_attributes; i += 1) {
|
|
SDL_GPUVertexAttribute attribute = vertexInputState.vertex_attributes[i];
|
|
|
|
desc[i].SemanticName = semantic;
|
|
desc[i].SemanticIndex = attribute.location;
|
|
desc[i].Format = SDLToD3D12_VertexFormat[attribute.format];
|
|
desc[i].InputSlot = attribute.binding_index;
|
|
desc[i].AlignedByteOffset = attribute.offset;
|
|
desc[i].InputSlotClass = SDLToD3D12_InputRate[vertexInputState.vertex_bindings[attribute.binding_index].input_rate];
|
|
desc[i].InstanceDataStepRate = (vertexInputState.vertex_bindings[attribute.binding_index].input_rate == SDL_GPU_VERTEXINPUTRATE_INSTANCE) ? vertexInputState.vertex_bindings[attribute.binding_index].instance_step_rate : 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void D3D12_INTERNAL_AssignCpuDescriptorHandle(
|
|
D3D12Renderer *renderer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE heapType,
|
|
D3D12CPUDescriptor *cpuDescriptor)
|
|
{
|
|
D3D12DescriptorHeap *heap = renderer->stagingDescriptorHeaps[heapType];
|
|
Uint32 descriptorIndex;
|
|
|
|
cpuDescriptor->heap = heap;
|
|
|
|
SDL_LockMutex(renderer->stagingDescriptorHeapLock);
|
|
|
|
if (heap->inactiveDescriptorCount > 0) {
|
|
descriptorIndex = heap->inactiveDescriptorIndices[heap->inactiveDescriptorCount - 1];
|
|
heap->inactiveDescriptorCount -= 1;
|
|
} else if (heap->currentDescriptorIndex < heap->maxDescriptors) {
|
|
descriptorIndex = heap->currentDescriptorIndex;
|
|
heap->currentDescriptorIndex += 1;
|
|
} else {
|
|
cpuDescriptor->cpuHandleIndex = SDL_MAX_UINT32;
|
|
cpuDescriptor->cpuHandle.ptr = 0;
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Out of CPU descriptor handles, many bad things are going to happen!");
|
|
SDL_UnlockMutex(renderer->stagingDescriptorHeapLock);
|
|
return;
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->stagingDescriptorHeapLock);
|
|
|
|
cpuDescriptor->cpuHandleIndex = descriptorIndex;
|
|
cpuDescriptor->cpuHandle.ptr = heap->descriptorHeapCPUStart.ptr + (descriptorIndex * heap->descriptorSize);
|
|
}
|
|
|
|
static SDL_GPUGraphicsPipeline *D3D12_CreateGraphicsPipeline(
|
|
SDL_GPURenderer *driverData,
|
|
const SDL_GPUGraphicsPipelineCreateInfo *createinfo)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12Shader *vertShader = (D3D12Shader *)createinfo->vertex_shader;
|
|
D3D12Shader *fragShader = (D3D12Shader *)createinfo->fragment_shader;
|
|
|
|
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
|
|
SDL_zero(psoDesc);
|
|
psoDesc.VS.pShaderBytecode = vertShader->bytecode;
|
|
psoDesc.VS.BytecodeLength = vertShader->bytecodeSize;
|
|
psoDesc.PS.pShaderBytecode = fragShader->bytecode;
|
|
psoDesc.PS.BytecodeLength = fragShader->bytecodeSize;
|
|
|
|
D3D12_INPUT_ELEMENT_DESC inputElementDescs[D3D12_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT];
|
|
if (createinfo->vertex_input_state.num_vertex_attributes > 0) {
|
|
psoDesc.InputLayout.pInputElementDescs = inputElementDescs;
|
|
psoDesc.InputLayout.NumElements = createinfo->vertex_input_state.num_vertex_attributes;
|
|
D3D12_INTERNAL_ConvertVertexInputState(createinfo->vertex_input_state, inputElementDescs, renderer->semantic);
|
|
}
|
|
|
|
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
|
|
|
|
if (!D3D12_INTERNAL_ConvertRasterizerState(createinfo->rasterizer_state, &psoDesc.RasterizerState)) {
|
|
return NULL;
|
|
}
|
|
if (!D3D12_INTERNAL_ConvertBlendState(createinfo, &psoDesc.BlendState)) {
|
|
return NULL;
|
|
}
|
|
if (!D3D12_INTERNAL_ConvertDepthStencilState(createinfo->depth_stencil_state, &psoDesc.DepthStencilState)) {
|
|
return NULL;
|
|
}
|
|
|
|
D3D12GraphicsPipeline *pipeline = (D3D12GraphicsPipeline *)SDL_calloc(1, sizeof(D3D12GraphicsPipeline));
|
|
if (!pipeline) {
|
|
return NULL;
|
|
}
|
|
|
|
Uint32 sampleMask = createinfo->multisample_state.enable_mask ?
|
|
createinfo->multisample_state.sample_mask :
|
|
0xFFFFFFFF;
|
|
|
|
psoDesc.SampleMask = sampleMask;
|
|
psoDesc.SampleDesc.Count = SDLToD3D12_SampleCount[createinfo->multisample_state.sample_count];
|
|
psoDesc.SampleDesc.Quality = 0;
|
|
|
|
psoDesc.DSVFormat = SDLToD3D12_TextureFormat[createinfo->target_info.depth_stencil_format];
|
|
psoDesc.NumRenderTargets = createinfo->target_info.num_color_targets;
|
|
for (uint32_t i = 0; i < createinfo->target_info.num_color_targets; i += 1) {
|
|
psoDesc.RTVFormats[i] = SDLToD3D12_TextureFormat[createinfo->target_info.color_target_descriptions[i].format];
|
|
}
|
|
|
|
// Assuming some default values or further initialization
|
|
psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
|
|
psoDesc.CachedPSO.CachedBlobSizeInBytes = 0;
|
|
psoDesc.CachedPSO.pCachedBlob = NULL;
|
|
|
|
psoDesc.NodeMask = 0;
|
|
|
|
D3D12GraphicsRootSignature *rootSignature = D3D12_INTERNAL_CreateGraphicsRootSignature(
|
|
renderer,
|
|
vertShader,
|
|
fragShader);
|
|
|
|
if (rootSignature == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not create root signature!");
|
|
D3D12_INTERNAL_DestroyGraphicsPipeline(pipeline);
|
|
return NULL;
|
|
}
|
|
pipeline->rootSignature = rootSignature;
|
|
|
|
psoDesc.pRootSignature = rootSignature->handle;
|
|
ID3D12PipelineState *pipelineState;
|
|
|
|
HRESULT res = ID3D12Device_CreateGraphicsPipelineState(
|
|
renderer->device,
|
|
&psoDesc,
|
|
D3D_GUID(D3D_IID_ID3D12PipelineState),
|
|
(void **)&pipelineState);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_LogError(renderer->device, "Could not create graphics pipeline state", res);
|
|
D3D12_INTERNAL_DestroyGraphicsPipeline(pipeline);
|
|
return NULL;
|
|
}
|
|
|
|
pipeline->pipelineState = pipelineState;
|
|
|
|
for (Uint32 i = 0; i < createinfo->vertex_input_state.num_vertex_bindings; i += 1) {
|
|
pipeline->vertexStrides[i] = createinfo->vertex_input_state.vertex_bindings[i].pitch;
|
|
}
|
|
|
|
pipeline->primitiveType = createinfo->primitive_type;
|
|
|
|
pipeline->vertexSamplerCount = vertShader->num_samplers;
|
|
pipeline->vertexStorageTextureCount = vertShader->numStorageTextures;
|
|
pipeline->vertexStorageBufferCount = vertShader->numStorageBuffers;
|
|
pipeline->vertexUniformBufferCount = vertShader->numUniformBuffers;
|
|
|
|
pipeline->fragmentSamplerCount = fragShader->num_samplers;
|
|
pipeline->fragmentStorageTextureCount = fragShader->numStorageTextures;
|
|
pipeline->fragmentStorageBufferCount = fragShader->numStorageBuffers;
|
|
pipeline->fragmentUniformBufferCount = fragShader->numUniformBuffers;
|
|
|
|
SDL_AtomicSet(&pipeline->referenceCount, 0);
|
|
return (SDL_GPUGraphicsPipeline *)pipeline;
|
|
}
|
|
|
|
static SDL_GPUSampler *D3D12_CreateSampler(
|
|
SDL_GPURenderer *driverData,
|
|
const SDL_GPUSamplerCreateInfo *createinfo)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12Sampler *sampler = (D3D12Sampler *)SDL_calloc(1, sizeof(D3D12Sampler));
|
|
if (!sampler) {
|
|
return NULL;
|
|
}
|
|
D3D12_SAMPLER_DESC samplerDesc;
|
|
|
|
samplerDesc.Filter = SDLToD3D12_Filter(
|
|
createinfo->min_filter,
|
|
createinfo->mag_filter,
|
|
createinfo->mipmap_mode,
|
|
createinfo->enable_compare,
|
|
createinfo->enable_anisotropy);
|
|
samplerDesc.AddressU = SDLToD3D12_SamplerAddressMode[createinfo->address_mode_u];
|
|
samplerDesc.AddressV = SDLToD3D12_SamplerAddressMode[createinfo->address_mode_v];
|
|
samplerDesc.AddressW = SDLToD3D12_SamplerAddressMode[createinfo->address_mode_w];
|
|
samplerDesc.MaxAnisotropy = (Uint32)createinfo->max_anisotropy;
|
|
samplerDesc.ComparisonFunc = SDLToD3D12_CompareOp[createinfo->compare_op];
|
|
samplerDesc.MinLOD = createinfo->min_lod;
|
|
samplerDesc.MaxLOD = createinfo->max_lod;
|
|
samplerDesc.MipLODBias = createinfo->mip_lod_bias;
|
|
samplerDesc.BorderColor[0] = 0;
|
|
samplerDesc.BorderColor[1] = 0;
|
|
samplerDesc.BorderColor[2] = 0;
|
|
samplerDesc.BorderColor[3] = 0;
|
|
|
|
D3D12_INTERNAL_AssignCpuDescriptorHandle(
|
|
renderer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
|
|
&sampler->handle);
|
|
|
|
ID3D12Device_CreateSampler(
|
|
renderer->device,
|
|
&samplerDesc,
|
|
sampler->handle.cpuHandle);
|
|
|
|
sampler->createInfo = *createinfo;
|
|
SDL_AtomicSet(&sampler->referenceCount, 0);
|
|
return (SDL_GPUSampler *)sampler;
|
|
}
|
|
|
|
static SDL_GPUShader *D3D12_CreateShader(
|
|
SDL_GPURenderer *driverData,
|
|
const SDL_GPUShaderCreateInfo *createinfo)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
void *bytecode;
|
|
size_t bytecodeSize;
|
|
D3D12Shader *shader;
|
|
|
|
if (!D3D12_INTERNAL_CreateShaderBytecode(
|
|
renderer,
|
|
createinfo->stage,
|
|
createinfo->format,
|
|
createinfo->code,
|
|
createinfo->code_size,
|
|
createinfo->entrypoint,
|
|
&bytecode,
|
|
&bytecodeSize)) {
|
|
return NULL;
|
|
}
|
|
shader = (D3D12Shader *)SDL_calloc(1, sizeof(D3D12Shader));
|
|
if (!shader) {
|
|
SDL_free(bytecode);
|
|
return NULL;
|
|
}
|
|
shader->num_samplers = createinfo->num_samplers;
|
|
shader->numStorageBuffers = createinfo->num_storage_buffers;
|
|
shader->numStorageTextures = createinfo->num_storage_textures;
|
|
shader->numUniformBuffers = createinfo->num_uniform_buffers;
|
|
|
|
shader->bytecode = bytecode;
|
|
shader->bytecodeSize = bytecodeSize;
|
|
|
|
return (SDL_GPUShader *)shader;
|
|
}
|
|
|
|
static D3D12Texture *D3D12_INTERNAL_CreateTexture(
|
|
D3D12Renderer *renderer,
|
|
const SDL_GPUTextureCreateInfo *createinfo,
|
|
bool isSwapchainTexture)
|
|
{
|
|
D3D12Texture *texture;
|
|
ID3D12Resource *handle;
|
|
D3D12_HEAP_PROPERTIES heapProperties;
|
|
D3D12_HEAP_FLAGS heapFlags = (D3D12_HEAP_FLAGS)0;
|
|
D3D12_RESOURCE_DESC desc;
|
|
D3D12_RESOURCE_FLAGS resourceFlags = (D3D12_RESOURCE_FLAGS)0;
|
|
D3D12_RESOURCE_STATES initialState = (D3D12_RESOURCE_STATES)0;
|
|
D3D12_CLEAR_VALUE clearValue;
|
|
bool useClearValue = false;
|
|
HRESULT res;
|
|
|
|
texture = (D3D12Texture *)SDL_calloc(1, sizeof(D3D12Texture));
|
|
if (!texture) {
|
|
return NULL;
|
|
}
|
|
|
|
Uint32 layerCount = createinfo->type == SDL_GPU_TEXTURETYPE_3D ? 1 : createinfo->layer_count_or_depth;
|
|
Uint32 depth = createinfo->type == SDL_GPU_TEXTURETYPE_3D ? createinfo->layer_count_or_depth : 1;
|
|
|
|
if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
|
|
resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
|
|
useClearValue = true;
|
|
clearValue.Color[0] = SDL_GetFloatProperty(createinfo->props, SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_R_FLOAT, 0);
|
|
clearValue.Color[1] = SDL_GetFloatProperty(createinfo->props, SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_G_FLOAT, 0);
|
|
clearValue.Color[2] = SDL_GetFloatProperty(createinfo->props, SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_B_FLOAT, 0);
|
|
clearValue.Color[3] = SDL_GetFloatProperty(createinfo->props, SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_A_FLOAT, 0);
|
|
}
|
|
|
|
if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
|
|
resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
|
useClearValue = true;
|
|
clearValue.DepthStencil.Depth = SDL_GetFloatProperty(createinfo->props, SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_DEPTH_FLOAT, 0);
|
|
clearValue.DepthStencil.Stencil = (UINT8)SDL_GetNumberProperty(createinfo->props, SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_STENCIL_UINT8, 0);
|
|
}
|
|
|
|
if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
|
|
resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
|
}
|
|
|
|
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
heapProperties.CreationNodeMask = 0; // We don't do multi-adapter operation
|
|
heapProperties.VisibleNodeMask = 0; // We don't do multi-adapter operation
|
|
|
|
heapFlags = isSwapchainTexture ? D3D12_HEAP_FLAG_ALLOW_DISPLAY : D3D12_HEAP_FLAG_NONE;
|
|
|
|
if (createinfo->type != SDL_GPU_TEXTURETYPE_3D) {
|
|
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
|
desc.Alignment = isSwapchainTexture ? 0 : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
|
|
desc.Width = createinfo->width;
|
|
desc.Height = createinfo->height;
|
|
desc.DepthOrArraySize = (UINT16)createinfo->layer_count_or_depth;
|
|
desc.MipLevels = (UINT16)createinfo->num_levels;
|
|
desc.Format = SDLToD3D12_TextureFormat[createinfo->format];
|
|
desc.SampleDesc.Count = 1;
|
|
desc.SampleDesc.Quality = 0;
|
|
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; // Apparently this is the most efficient choice
|
|
desc.Flags = resourceFlags;
|
|
} else {
|
|
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D;
|
|
desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
|
|
desc.Width = createinfo->width;
|
|
desc.Height = createinfo->height;
|
|
desc.DepthOrArraySize = (UINT16)createinfo->layer_count_or_depth;
|
|
desc.MipLevels = (UINT16)createinfo->num_levels;
|
|
desc.Format = SDLToD3D12_TextureFormat[createinfo->format];
|
|
desc.SampleDesc.Count = 1;
|
|
desc.SampleDesc.Quality = 0;
|
|
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
|
desc.Flags = resourceFlags;
|
|
}
|
|
|
|
initialState = isSwapchainTexture ? D3D12_RESOURCE_STATE_PRESENT : D3D12_INTERNAL_DefaultTextureResourceState(createinfo->usage);
|
|
clearValue.Format = desc.Format;
|
|
|
|
res = ID3D12Device_CreateCommittedResource(
|
|
renderer->device,
|
|
&heapProperties,
|
|
heapFlags,
|
|
&desc,
|
|
initialState,
|
|
useClearValue ? &clearValue : NULL,
|
|
D3D_GUID(D3D_IID_ID3D12Resource),
|
|
(void **)&handle);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_LogError(renderer->device, "Failed to create texture!", res);
|
|
D3D12_INTERNAL_DestroyTexture(renderer, texture);
|
|
return NULL;
|
|
}
|
|
|
|
texture->resource = handle;
|
|
|
|
// Create the SRV if applicable
|
|
if ((createinfo->usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) ||
|
|
(createinfo->usage & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ) ||
|
|
(createinfo->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ)) {
|
|
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
|
|
|
D3D12_INTERNAL_AssignCpuDescriptorHandle(
|
|
renderer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
&texture->srvHandle);
|
|
|
|
srvDesc.Format = SDLToD3D12_TextureFormat[createinfo->format];
|
|
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
|
|
|
if (createinfo->type == SDL_GPU_TEXTURETYPE_CUBE) {
|
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
|
|
srvDesc.TextureCube.MipLevels = createinfo->num_levels;
|
|
srvDesc.TextureCube.MostDetailedMip = 0;
|
|
srvDesc.TextureCube.ResourceMinLODClamp = 0;
|
|
} else if (createinfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
|
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
|
|
srvDesc.TextureCubeArray.MipLevels = createinfo->num_levels;
|
|
srvDesc.TextureCubeArray.MostDetailedMip = 0;
|
|
srvDesc.TextureCubeArray.First2DArrayFace = 0;
|
|
srvDesc.TextureCubeArray.NumCubes = createinfo->layer_count_or_depth / 6;
|
|
srvDesc.TextureCubeArray.ResourceMinLODClamp = 0;
|
|
} else if (createinfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY) {
|
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
|
|
srvDesc.Texture2DArray.MipLevels = createinfo->num_levels;
|
|
srvDesc.Texture2DArray.MostDetailedMip = 0;
|
|
srvDesc.Texture2DArray.FirstArraySlice = 0;
|
|
srvDesc.Texture2DArray.ArraySize = layerCount;
|
|
srvDesc.Texture2DArray.ResourceMinLODClamp = 0;
|
|
srvDesc.Texture2DArray.PlaneSlice = 0;
|
|
} else if (createinfo->type == SDL_GPU_TEXTURETYPE_3D) {
|
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
|
|
srvDesc.Texture3D.MipLevels = createinfo->num_levels;
|
|
srvDesc.Texture3D.MostDetailedMip = 0;
|
|
srvDesc.Texture3D.ResourceMinLODClamp = 0; // default behavior
|
|
} else {
|
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
|
srvDesc.Texture2D.MipLevels = createinfo->num_levels;
|
|
srvDesc.Texture2D.MostDetailedMip = 0;
|
|
srvDesc.Texture2D.PlaneSlice = 0;
|
|
srvDesc.Texture2D.ResourceMinLODClamp = 0; // default behavior
|
|
}
|
|
|
|
ID3D12Device_CreateShaderResourceView(
|
|
renderer->device,
|
|
handle,
|
|
&srvDesc,
|
|
texture->srvHandle.cpuHandle);
|
|
}
|
|
|
|
SDL_AtomicSet(&texture->referenceCount, 0);
|
|
|
|
texture->subresourceCount = createinfo->num_levels * layerCount;
|
|
texture->subresources = (D3D12TextureSubresource *)SDL_calloc(
|
|
texture->subresourceCount, sizeof(D3D12TextureSubresource));
|
|
if (!texture->subresources) {
|
|
D3D12_INTERNAL_DestroyTexture(renderer, texture);
|
|
return NULL;
|
|
}
|
|
for (Uint32 layerIndex = 0; layerIndex < layerCount; layerIndex += 1) {
|
|
for (Uint32 levelIndex = 0; levelIndex < createinfo->num_levels; levelIndex += 1) {
|
|
Uint32 subresourceIndex = D3D12_INTERNAL_CalcSubresource(
|
|
levelIndex,
|
|
layerIndex,
|
|
createinfo->num_levels);
|
|
|
|
texture->subresources[subresourceIndex].parent = texture;
|
|
texture->subresources[subresourceIndex].layer = layerIndex;
|
|
texture->subresources[subresourceIndex].level = levelIndex;
|
|
texture->subresources[subresourceIndex].depth = depth;
|
|
texture->subresources[subresourceIndex].index = subresourceIndex;
|
|
|
|
texture->subresources[subresourceIndex].rtvHandles = NULL;
|
|
texture->subresources[subresourceIndex].uavHandle.heap = NULL;
|
|
texture->subresources[subresourceIndex].dsvHandle.heap = NULL;
|
|
|
|
// Create RTV if needed
|
|
if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
|
|
texture->subresources[subresourceIndex].rtvHandles = (D3D12CPUDescriptor *)SDL_calloc(depth, sizeof(D3D12CPUDescriptor));
|
|
|
|
for (Uint32 depthIndex = 0; depthIndex < depth; depthIndex += 1) {
|
|
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc;
|
|
|
|
D3D12_INTERNAL_AssignCpuDescriptorHandle(
|
|
renderer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
|
|
&texture->subresources[subresourceIndex].rtvHandles[depthIndex]);
|
|
|
|
rtvDesc.Format = SDLToD3D12_TextureFormat[createinfo->format];
|
|
|
|
if (createinfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY || createinfo->type == SDL_GPU_TEXTURETYPE_CUBE || createinfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
|
|
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
|
|
rtvDesc.Texture2DArray.MipSlice = levelIndex;
|
|
rtvDesc.Texture2DArray.FirstArraySlice = layerIndex;
|
|
rtvDesc.Texture2DArray.ArraySize = 1;
|
|
rtvDesc.Texture2DArray.PlaneSlice = 0;
|
|
} else if (createinfo->type == SDL_GPU_TEXTURETYPE_3D) {
|
|
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
|
|
rtvDesc.Texture3D.MipSlice = levelIndex;
|
|
rtvDesc.Texture3D.FirstWSlice = depthIndex;
|
|
rtvDesc.Texture3D.WSize = 1;
|
|
} else {
|
|
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
|
|
rtvDesc.Texture2D.MipSlice = levelIndex;
|
|
rtvDesc.Texture2D.PlaneSlice = 0;
|
|
}
|
|
|
|
ID3D12Device_CreateRenderTargetView(
|
|
renderer->device,
|
|
texture->resource,
|
|
&rtvDesc,
|
|
texture->subresources[subresourceIndex].rtvHandles[depthIndex].cpuHandle);
|
|
}
|
|
}
|
|
|
|
// Create DSV if needed
|
|
if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
|
|
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
|
|
|
|
D3D12_INTERNAL_AssignCpuDescriptorHandle(
|
|
renderer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_DSV,
|
|
&texture->subresources[subresourceIndex].dsvHandle);
|
|
|
|
dsvDesc.Format = SDLToD3D12_TextureFormat[createinfo->format];
|
|
dsvDesc.Flags = (D3D12_DSV_FLAGS)0;
|
|
dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
|
dsvDesc.Texture2D.MipSlice = levelIndex;
|
|
|
|
ID3D12Device_CreateDepthStencilView(
|
|
renderer->device,
|
|
texture->resource,
|
|
&dsvDesc,
|
|
texture->subresources[subresourceIndex].dsvHandle.cpuHandle);
|
|
}
|
|
|
|
// Create subresource UAV if necessary
|
|
if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
|
|
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc;
|
|
|
|
D3D12_INTERNAL_AssignCpuDescriptorHandle(
|
|
renderer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
&texture->subresources[subresourceIndex].uavHandle);
|
|
|
|
uavDesc.Format = SDLToD3D12_TextureFormat[createinfo->format];
|
|
|
|
if (createinfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY || createinfo->type == SDL_GPU_TEXTURETYPE_CUBE || createinfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
|
|
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
|
|
uavDesc.Texture2DArray.MipSlice = levelIndex;
|
|
uavDesc.Texture2DArray.FirstArraySlice = layerIndex;
|
|
uavDesc.Texture2DArray.ArraySize = 1;
|
|
} else if (createinfo->type == SDL_GPU_TEXTURETYPE_3D) {
|
|
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
|
|
uavDesc.Texture3D.MipSlice = levelIndex;
|
|
uavDesc.Texture3D.FirstWSlice = 0;
|
|
uavDesc.Texture3D.WSize = depth;
|
|
} else {
|
|
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
|
|
uavDesc.Texture2D.MipSlice = levelIndex;
|
|
uavDesc.Texture2D.PlaneSlice = 0;
|
|
}
|
|
|
|
ID3D12Device_CreateUnorderedAccessView(
|
|
renderer->device,
|
|
texture->resource,
|
|
NULL,
|
|
&uavDesc,
|
|
texture->subresources[subresourceIndex].uavHandle.cpuHandle);
|
|
}
|
|
}
|
|
}
|
|
|
|
return texture;
|
|
}
|
|
|
|
static SDL_GPUTexture *D3D12_CreateTexture(
|
|
SDL_GPURenderer *driverData,
|
|
const SDL_GPUTextureCreateInfo *createinfo)
|
|
{
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)SDL_calloc(1, sizeof(D3D12TextureContainer));
|
|
if (!container) {
|
|
return NULL;
|
|
}
|
|
|
|
container->header.info = *createinfo;
|
|
container->textureCapacity = 1;
|
|
container->textureCount = 1;
|
|
container->textures = (D3D12Texture **)SDL_calloc(
|
|
container->textureCapacity, sizeof(D3D12Texture *));
|
|
|
|
if (!container->textures) {
|
|
SDL_free(container);
|
|
return NULL;
|
|
}
|
|
|
|
container->debugName = NULL;
|
|
container->canBeCycled = true;
|
|
|
|
D3D12Texture *texture = D3D12_INTERNAL_CreateTexture(
|
|
(D3D12Renderer *)driverData,
|
|
createinfo,
|
|
false);
|
|
|
|
if (!texture) {
|
|
SDL_free(container->textures);
|
|
SDL_free(container);
|
|
return NULL;
|
|
}
|
|
|
|
container->textures[0] = texture;
|
|
container->activeTexture = texture;
|
|
|
|
texture->container = container;
|
|
texture->containerIndex = 0;
|
|
|
|
return (SDL_GPUTexture *)container;
|
|
}
|
|
|
|
static D3D12Buffer *D3D12_INTERNAL_CreateBuffer(
|
|
D3D12Renderer *renderer,
|
|
SDL_GPUBufferUsageFlags usageFlags,
|
|
Uint32 size,
|
|
D3D12BufferType type)
|
|
{
|
|
D3D12Buffer *buffer;
|
|
ID3D12Resource *handle;
|
|
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc;
|
|
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
|
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
|
|
D3D12_HEAP_PROPERTIES heapProperties;
|
|
D3D12_RESOURCE_DESC desc;
|
|
D3D12_HEAP_FLAGS heapFlags = (D3D12_HEAP_FLAGS)0;
|
|
D3D12_RESOURCE_FLAGS resourceFlags = (D3D12_RESOURCE_FLAGS)0;
|
|
D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON;
|
|
HRESULT res;
|
|
|
|
buffer = (D3D12Buffer *)SDL_calloc(1, sizeof(D3D12Buffer));
|
|
|
|
if (!buffer) {
|
|
return NULL;
|
|
}
|
|
|
|
if (usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE) {
|
|
resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
|
}
|
|
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
if (usageFlags & SDL_GPU_BUFFERUSAGE_INDIRECT) {
|
|
resourceFlags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_INDIRECT_BUFFER;
|
|
}
|
|
#endif
|
|
|
|
heapProperties.CreationNodeMask = 0; // We don't do multi-adapter operation
|
|
heapProperties.VisibleNodeMask = 0; // We don't do multi-adapter operation
|
|
|
|
if (type == D3D12_BUFFER_TYPE_GPU) {
|
|
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
heapFlags = D3D12_HEAP_FLAG_NONE;
|
|
} else if (type == D3D12_BUFFER_TYPE_UPLOAD) {
|
|
heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
heapFlags = D3D12_HEAP_FLAG_NONE;
|
|
initialState = D3D12_RESOURCE_STATE_GENERIC_READ;
|
|
} else if (type == D3D12_BUFFER_TYPE_DOWNLOAD) {
|
|
heapProperties.Type = D3D12_HEAP_TYPE_READBACK;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
heapFlags = D3D12_HEAP_FLAG_NONE;
|
|
initialState = D3D12_RESOURCE_STATE_COPY_DEST;
|
|
} else if (type == D3D12_BUFFER_TYPE_UNIFORM) {
|
|
// D3D12 is badly designed, so we have to check if the fast path for uniform buffers is enabled
|
|
if (renderer->GPUUploadHeapSupported) {
|
|
heapProperties.Type = D3D12_HEAP_TYPE_GPU_UPLOAD;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
} else {
|
|
heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
|
|
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
|
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
|
initialState = D3D12_RESOURCE_STATE_GENERIC_READ;
|
|
}
|
|
heapFlags = D3D12_HEAP_FLAG_NONE;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized buffer type!");
|
|
return NULL;
|
|
}
|
|
|
|
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
|
|
desc.Width = size;
|
|
desc.Height = 1;
|
|
desc.DepthOrArraySize = 1;
|
|
desc.MipLevels = 1;
|
|
desc.Format = DXGI_FORMAT_UNKNOWN;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.SampleDesc.Quality = 0;
|
|
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
desc.Flags = resourceFlags;
|
|
|
|
res = ID3D12Device_CreateCommittedResource(
|
|
renderer->device,
|
|
&heapProperties,
|
|
heapFlags,
|
|
&desc,
|
|
initialState,
|
|
NULL,
|
|
D3D_GUID(D3D_IID_ID3D12Resource),
|
|
(void **)&handle);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_LogError(renderer->device, "Could not create buffer!", res);
|
|
D3D12_INTERNAL_DestroyBuffer(renderer, buffer);
|
|
return NULL;
|
|
}
|
|
|
|
buffer->handle = handle;
|
|
SDL_AtomicSet(&buffer->referenceCount, 0);
|
|
|
|
buffer->uavDescriptor.heap = NULL;
|
|
buffer->srvDescriptor.heap = NULL;
|
|
buffer->cbvDescriptor.heap = NULL;
|
|
|
|
if (usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE) {
|
|
D3D12_INTERNAL_AssignCpuDescriptorHandle(
|
|
renderer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
&buffer->uavDescriptor);
|
|
|
|
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
|
|
uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
|
|
uavDesc.Buffer.FirstElement = 0;
|
|
uavDesc.Buffer.NumElements = size / sizeof(Uint32);
|
|
uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
|
|
uavDesc.Buffer.CounterOffsetInBytes = 0; // TODO: support counters?
|
|
uavDesc.Buffer.StructureByteStride = 0;
|
|
|
|
// Create UAV
|
|
ID3D12Device_CreateUnorderedAccessView(
|
|
renderer->device,
|
|
handle,
|
|
NULL, // TODO: support counters?
|
|
&uavDesc,
|
|
buffer->uavDescriptor.cpuHandle);
|
|
}
|
|
|
|
if (
|
|
(usageFlags & SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ) ||
|
|
(usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ)) {
|
|
D3D12_INTERNAL_AssignCpuDescriptorHandle(
|
|
renderer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
&buffer->srvDescriptor);
|
|
|
|
srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
|
|
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
|
|
srvDesc.Buffer.FirstElement = 0;
|
|
srvDesc.Buffer.NumElements = size / sizeof(Uint32);
|
|
srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
|
|
srvDesc.Buffer.StructureByteStride = 0;
|
|
|
|
// Create SRV
|
|
ID3D12Device_CreateShaderResourceView(
|
|
renderer->device,
|
|
handle,
|
|
&srvDesc,
|
|
buffer->srvDescriptor.cpuHandle);
|
|
}
|
|
|
|
// FIXME: we may not need a CBV since we use root descriptors
|
|
if (type == D3D12_BUFFER_TYPE_UNIFORM) {
|
|
D3D12_INTERNAL_AssignCpuDescriptorHandle(
|
|
renderer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
&buffer->cbvDescriptor);
|
|
|
|
cbvDesc.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(handle);
|
|
cbvDesc.SizeInBytes = size;
|
|
|
|
// Create CBV
|
|
ID3D12Device_CreateConstantBufferView(
|
|
renderer->device,
|
|
&cbvDesc,
|
|
buffer->cbvDescriptor.cpuHandle);
|
|
}
|
|
|
|
buffer->virtualAddress = 0;
|
|
if (type == D3D12_BUFFER_TYPE_GPU || type == D3D12_BUFFER_TYPE_UNIFORM) {
|
|
buffer->virtualAddress = ID3D12Resource_GetGPUVirtualAddress(buffer->handle);
|
|
}
|
|
|
|
buffer->mapPointer = NULL;
|
|
// Persistently map upload buffers
|
|
if (type == D3D12_BUFFER_TYPE_UPLOAD) {
|
|
res = ID3D12Resource_Map(
|
|
buffer->handle,
|
|
0,
|
|
NULL,
|
|
(void **)&buffer->mapPointer);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_LogError(renderer->device, "Failed to map upload buffer!", res);
|
|
D3D12_INTERNAL_DestroyBuffer(renderer, buffer);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
buffer->container = NULL;
|
|
buffer->containerIndex = 0;
|
|
|
|
buffer->transitioned = initialState != D3D12_RESOURCE_STATE_COMMON;
|
|
SDL_AtomicSet(&buffer->referenceCount, 0);
|
|
return buffer;
|
|
}
|
|
|
|
static D3D12BufferContainer *D3D12_INTERNAL_CreateBufferContainer(
|
|
D3D12Renderer *renderer,
|
|
SDL_GPUBufferUsageFlags usageFlags,
|
|
Uint32 size,
|
|
D3D12BufferType type)
|
|
{
|
|
D3D12BufferContainer *container;
|
|
D3D12Buffer *buffer;
|
|
|
|
container = (D3D12BufferContainer *)SDL_calloc(1, sizeof(D3D12BufferContainer));
|
|
if (!container) {
|
|
return NULL;
|
|
}
|
|
|
|
container->usage = usageFlags;
|
|
container->size = size;
|
|
container->type = type;
|
|
|
|
container->bufferCapacity = 1;
|
|
container->bufferCount = 1;
|
|
container->buffers = (D3D12Buffer **)SDL_calloc(
|
|
container->bufferCapacity, sizeof(D3D12Buffer *));
|
|
if (!container->buffers) {
|
|
SDL_free(container);
|
|
return NULL;
|
|
}
|
|
container->debugName = NULL;
|
|
|
|
buffer = D3D12_INTERNAL_CreateBuffer(
|
|
renderer,
|
|
usageFlags,
|
|
size,
|
|
type);
|
|
|
|
if (buffer == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create buffer!");
|
|
SDL_free(container->buffers);
|
|
SDL_free(container);
|
|
return NULL;
|
|
}
|
|
|
|
container->activeBuffer = buffer;
|
|
container->buffers[0] = buffer;
|
|
buffer->container = container;
|
|
buffer->containerIndex = 0;
|
|
|
|
return container;
|
|
}
|
|
|
|
static SDL_GPUBuffer *D3D12_CreateBuffer(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUBufferUsageFlags usageFlags,
|
|
Uint32 size)
|
|
{
|
|
return (SDL_GPUBuffer *)D3D12_INTERNAL_CreateBufferContainer(
|
|
(D3D12Renderer *)driverData,
|
|
usageFlags,
|
|
size,
|
|
D3D12_BUFFER_TYPE_GPU);
|
|
}
|
|
|
|
static SDL_GPUTransferBuffer *D3D12_CreateTransferBuffer(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTransferBufferUsage usage,
|
|
Uint32 size)
|
|
{
|
|
return (SDL_GPUTransferBuffer *)D3D12_INTERNAL_CreateBufferContainer(
|
|
(D3D12Renderer *)driverData,
|
|
0,
|
|
size,
|
|
usage == SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD ? D3D12_BUFFER_TYPE_UPLOAD : D3D12_BUFFER_TYPE_DOWNLOAD);
|
|
}
|
|
|
|
// Debug Naming
|
|
|
|
static void D3D12_SetBufferName(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUBuffer *buffer,
|
|
const char *text)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12BufferContainer *container = (D3D12BufferContainer *)buffer;
|
|
size_t textLength = SDL_strlen(text) + 1;
|
|
|
|
if (renderer->debug_mode) {
|
|
container->debugName = (char *)SDL_realloc(
|
|
container->debugName,
|
|
textLength);
|
|
|
|
SDL_utf8strlcpy(
|
|
container->debugName,
|
|
text,
|
|
textLength);
|
|
|
|
for (Uint32 i = 0; i < container->bufferCount; i += 1) {
|
|
D3D12_INTERNAL_SetResourceName(
|
|
renderer,
|
|
container->buffers[i]->handle,
|
|
text);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void D3D12_SetTextureName(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTexture *texture,
|
|
const char *text)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)texture;
|
|
size_t textLength = SDL_strlen(text) + 1;
|
|
|
|
if (renderer->debug_mode) {
|
|
container->debugName = (char *)SDL_realloc(
|
|
container->debugName,
|
|
textLength);
|
|
|
|
SDL_utf8strlcpy(
|
|
container->debugName,
|
|
text,
|
|
textLength);
|
|
|
|
for (Uint32 i = 0; i < container->textureCount; i += 1) {
|
|
D3D12_INTERNAL_SetResourceName(
|
|
renderer,
|
|
container->textures[i]->resource,
|
|
text);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* These debug functions are all marked as "for internal usage only"
|
|
* on D3D12... works on renderdoc!
|
|
*/
|
|
|
|
static bool D3D12_INTERNAL_StrToWStr(
|
|
D3D12Renderer *renderer,
|
|
const char *str,
|
|
wchar_t *wstr,
|
|
size_t wstrSize,
|
|
Uint32 *outSize)
|
|
{
|
|
size_t inlen, result;
|
|
size_t outlen = wstrSize;
|
|
|
|
if (renderer->iconv == NULL) {
|
|
renderer->iconv = SDL_iconv_open("WCHAR_T", "UTF-8");
|
|
SDL_assert(renderer->iconv);
|
|
}
|
|
|
|
// Convert...
|
|
inlen = SDL_strlen(str) + 1;
|
|
result = SDL_iconv(
|
|
renderer->iconv,
|
|
&str,
|
|
&inlen,
|
|
(char **)&wstr,
|
|
&outlen);
|
|
|
|
*outSize = (Uint32)outlen;
|
|
|
|
// Check...
|
|
switch (result) {
|
|
case SDL_ICONV_ERROR:
|
|
case SDL_ICONV_E2BIG:
|
|
case SDL_ICONV_EILSEQ:
|
|
case SDL_ICONV_EINVAL:
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Failed to convert string to wchar_t!");
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void D3D12_InsertDebugLabel(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const char *text)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
wchar_t wstr[256];
|
|
Uint32 convSize;
|
|
|
|
if (!D3D12_INTERNAL_StrToWStr(
|
|
d3d12CommandBuffer->renderer,
|
|
text,
|
|
wstr,
|
|
sizeof(wstr),
|
|
&convSize)) {
|
|
return;
|
|
}
|
|
|
|
ID3D12GraphicsCommandList_SetMarker(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
0,
|
|
wstr,
|
|
convSize);
|
|
}
|
|
|
|
static void D3D12_PushDebugGroup(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const char *name)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
wchar_t wstr[256];
|
|
Uint32 convSize;
|
|
|
|
if (!D3D12_INTERNAL_StrToWStr(
|
|
d3d12CommandBuffer->renderer,
|
|
name,
|
|
wstr,
|
|
sizeof(wstr),
|
|
&convSize)) {
|
|
return;
|
|
}
|
|
|
|
ID3D12GraphicsCommandList_BeginEvent(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
0,
|
|
wstr,
|
|
convSize);
|
|
}
|
|
|
|
static void D3D12_PopDebugGroup(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
ID3D12GraphicsCommandList_EndEvent(d3d12CommandBuffer->graphicsCommandList);
|
|
}
|
|
|
|
// Disposal
|
|
|
|
static void D3D12_ReleaseTexture(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTexture *texture)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)texture;
|
|
|
|
D3D12_INTERNAL_ReleaseTextureContainer(
|
|
renderer,
|
|
container);
|
|
}
|
|
|
|
static void D3D12_ReleaseSampler(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUSampler *sampler)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12Sampler *d3d12Sampler = (D3D12Sampler *)sampler;
|
|
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->samplersToDestroy,
|
|
D3D12Sampler *,
|
|
renderer->samplersToDestroyCount + 1,
|
|
renderer->samplersToDestroyCapacity,
|
|
renderer->samplersToDestroyCapacity * 2)
|
|
|
|
renderer->samplersToDestroy[renderer->samplersToDestroyCount] = d3d12Sampler;
|
|
renderer->samplersToDestroyCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void D3D12_ReleaseBuffer(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUBuffer *buffer)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12BufferContainer *bufferContainer = (D3D12BufferContainer *)buffer;
|
|
|
|
D3D12_INTERNAL_ReleaseBufferContainer(
|
|
renderer,
|
|
bufferContainer);
|
|
}
|
|
|
|
static void D3D12_ReleaseTransferBuffer(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTransferBuffer *transferBuffer)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12BufferContainer *transferBufferContainer = (D3D12BufferContainer *)transferBuffer;
|
|
|
|
D3D12_INTERNAL_ReleaseBufferContainer(
|
|
renderer,
|
|
transferBufferContainer);
|
|
}
|
|
|
|
static void D3D12_ReleaseShader(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUShader *shader)
|
|
{
|
|
/* D3D12Renderer *renderer = (D3D12Renderer *)driverData; */
|
|
D3D12Shader *d3d12shader = (D3D12Shader *)shader;
|
|
|
|
if (d3d12shader->bytecode) {
|
|
SDL_free(d3d12shader->bytecode);
|
|
d3d12shader->bytecode = NULL;
|
|
}
|
|
SDL_free(d3d12shader);
|
|
}
|
|
|
|
static void D3D12_ReleaseComputePipeline(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUComputePipeline *computePipeline)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12ComputePipeline *d3d12ComputePipeline = (D3D12ComputePipeline *)computePipeline;
|
|
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->computePipelinesToDestroy,
|
|
D3D12ComputePipeline *,
|
|
renderer->computePipelinesToDestroyCount + 1,
|
|
renderer->computePipelinesToDestroyCapacity,
|
|
renderer->computePipelinesToDestroyCapacity * 2)
|
|
|
|
renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount] = d3d12ComputePipeline;
|
|
renderer->computePipelinesToDestroyCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void D3D12_ReleaseGraphicsPipeline(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUGraphicsPipeline *graphicsPipeline)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12GraphicsPipeline *d3d12GraphicsPipeline = (D3D12GraphicsPipeline *)graphicsPipeline;
|
|
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
renderer->graphicsPipelinesToDestroy,
|
|
D3D12GraphicsPipeline *,
|
|
renderer->graphicsPipelinesToDestroyCount + 1,
|
|
renderer->graphicsPipelinesToDestroyCapacity,
|
|
renderer->graphicsPipelinesToDestroyCapacity * 2)
|
|
|
|
renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount] = d3d12GraphicsPipeline;
|
|
renderer->graphicsPipelinesToDestroyCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_ReleaseBlitPipelines(SDL_GPURenderer *driverData)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12_ReleaseSampler(driverData, renderer->blitLinearSampler);
|
|
D3D12_ReleaseSampler(driverData, renderer->blitNearestSampler);
|
|
D3D12_ReleaseShader(driverData, renderer->blitVertexShader);
|
|
D3D12_ReleaseShader(driverData, renderer->blitFrom2DShader);
|
|
D3D12_ReleaseShader(driverData, renderer->blitFrom2DArrayShader);
|
|
D3D12_ReleaseShader(driverData, renderer->blitFrom3DShader);
|
|
D3D12_ReleaseShader(driverData, renderer->blitFromCubeShader);
|
|
D3D12_ReleaseShader(driverData, renderer->blitFromCubeArrayShader);
|
|
|
|
for (Uint32 i = 0; i < renderer->blitPipelineCount; i += 1) {
|
|
D3D12_ReleaseGraphicsPipeline(driverData, renderer->blitPipelines[i].pipeline);
|
|
}
|
|
SDL_free(renderer->blitPipelines);
|
|
}
|
|
|
|
// Render Pass
|
|
|
|
static void D3D12_SetViewport(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const SDL_GPUViewport *viewport)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12_VIEWPORT d3d12Viewport;
|
|
d3d12Viewport.TopLeftX = viewport->x;
|
|
d3d12Viewport.TopLeftY = viewport->y;
|
|
d3d12Viewport.Width = viewport->w;
|
|
d3d12Viewport.Height = viewport->h;
|
|
d3d12Viewport.MinDepth = viewport->min_depth;
|
|
d3d12Viewport.MaxDepth = viewport->max_depth;
|
|
ID3D12GraphicsCommandList_RSSetViewports(d3d12CommandBuffer->graphicsCommandList, 1, &d3d12Viewport);
|
|
}
|
|
|
|
static void D3D12_SetScissor(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const SDL_Rect *scissor)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12_RECT scissorRect;
|
|
scissorRect.left = scissor->x;
|
|
scissorRect.top = scissor->y;
|
|
scissorRect.right = scissor->x + scissor->w;
|
|
scissorRect.bottom = scissor->y + scissor->h;
|
|
ID3D12GraphicsCommandList_RSSetScissorRects(d3d12CommandBuffer->graphicsCommandList, 1, &scissorRect);
|
|
}
|
|
|
|
static void D3D12_SetBlendConstants(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_FColor blendConstants)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
FLOAT blendFactor[4] = { blendConstants.r, blendConstants.g, blendConstants.b, blendConstants.a };
|
|
ID3D12GraphicsCommandList_OMSetBlendFactor(d3d12CommandBuffer->graphicsCommandList, blendFactor);
|
|
}
|
|
|
|
static void D3D12_SetStencilReference(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint8 reference
|
|
) {
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
ID3D12GraphicsCommandList_OMSetStencilRef(d3d12CommandBuffer->graphicsCommandList, reference);
|
|
}
|
|
|
|
static D3D12TextureSubresource *D3D12_INTERNAL_FetchTextureSubresource(
|
|
D3D12TextureContainer *container,
|
|
Uint32 layer,
|
|
Uint32 level)
|
|
{
|
|
Uint32 index = D3D12_INTERNAL_CalcSubresource(
|
|
level,
|
|
layer,
|
|
container->header.info.num_levels);
|
|
return &container->activeTexture->subresources[index];
|
|
}
|
|
|
|
static void D3D12_INTERNAL_CycleActiveTexture(
|
|
D3D12Renderer *renderer,
|
|
D3D12TextureContainer *container)
|
|
{
|
|
D3D12Texture *texture;
|
|
|
|
// If a previously-cycled texture is available, we can use that.
|
|
for (Uint32 i = 0; i < container->textureCount; i += 1) {
|
|
texture = container->textures[i];
|
|
|
|
if (SDL_AtomicGet(&texture->referenceCount) == 0) {
|
|
container->activeTexture = texture;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// No texture is available, generate a new one.
|
|
texture = D3D12_INTERNAL_CreateTexture(
|
|
renderer,
|
|
&container->header.info,
|
|
false);
|
|
|
|
if (!texture) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to cycle active texture!");
|
|
return;
|
|
}
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
container->textures,
|
|
D3D12Texture *,
|
|
container->textureCount + 1,
|
|
container->textureCapacity,
|
|
container->textureCapacity * 2);
|
|
|
|
container->textures[container->textureCount] = texture;
|
|
texture->container = container;
|
|
texture->containerIndex = container->textureCount;
|
|
container->textureCount += 1;
|
|
|
|
container->activeTexture = texture;
|
|
|
|
if (renderer->debug_mode && container->debugName != NULL) {
|
|
D3D12_INTERNAL_SetResourceName(
|
|
renderer,
|
|
container->activeTexture->resource,
|
|
container->debugName);
|
|
}
|
|
}
|
|
|
|
static D3D12TextureSubresource *D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12TextureContainer *container,
|
|
Uint32 layer,
|
|
Uint32 level,
|
|
bool cycle,
|
|
D3D12_RESOURCE_STATES destinationUsageMode)
|
|
{
|
|
D3D12TextureSubresource *subresource = D3D12_INTERNAL_FetchTextureSubresource(
|
|
container,
|
|
layer,
|
|
level);
|
|
|
|
if (
|
|
container->canBeCycled &&
|
|
cycle &&
|
|
SDL_AtomicGet(&subresource->parent->referenceCount) > 0) {
|
|
D3D12_INTERNAL_CycleActiveTexture(
|
|
commandBuffer->renderer,
|
|
container);
|
|
|
|
subresource = D3D12_INTERNAL_FetchTextureSubresource(
|
|
container,
|
|
layer,
|
|
level);
|
|
}
|
|
|
|
D3D12_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
commandBuffer,
|
|
destinationUsageMode,
|
|
subresource);
|
|
|
|
return subresource;
|
|
}
|
|
|
|
static void D3D12_INTERNAL_CycleActiveBuffer(
|
|
D3D12Renderer *renderer,
|
|
D3D12BufferContainer *container)
|
|
{
|
|
// If a previously-cycled buffer is available, we can use that.
|
|
for (Uint32 i = 0; i < container->bufferCount; i += 1) {
|
|
D3D12Buffer *buffer = container->buffers[i];
|
|
if (SDL_AtomicGet(&buffer->referenceCount) == 0) {
|
|
container->activeBuffer = buffer;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// No buffer handle is available, create a new one.
|
|
D3D12Buffer *buffer = D3D12_INTERNAL_CreateBuffer(
|
|
renderer,
|
|
container->usage,
|
|
container->size,
|
|
container->type);
|
|
|
|
if (!buffer) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to cycle active buffer!");
|
|
return;
|
|
}
|
|
|
|
EXPAND_ARRAY_IF_NEEDED(
|
|
container->buffers,
|
|
D3D12Buffer *,
|
|
container->bufferCount + 1,
|
|
container->bufferCapacity,
|
|
container->bufferCapacity * 2);
|
|
|
|
container->buffers[container->bufferCount] = buffer;
|
|
buffer->container = container;
|
|
buffer->containerIndex = container->bufferCount;
|
|
container->bufferCount += 1;
|
|
|
|
container->activeBuffer = buffer;
|
|
|
|
if (renderer->debug_mode && container->debugName != NULL) {
|
|
D3D12_INTERNAL_SetResourceName(
|
|
renderer,
|
|
container->activeBuffer->handle,
|
|
container->debugName);
|
|
}
|
|
}
|
|
|
|
static D3D12Buffer *D3D12_INTERNAL_PrepareBufferForWrite(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12BufferContainer *container,
|
|
bool cycle,
|
|
D3D12_RESOURCE_STATES destinationState)
|
|
{
|
|
if (
|
|
cycle &&
|
|
SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0) {
|
|
D3D12_INTERNAL_CycleActiveBuffer(
|
|
commandBuffer->renderer,
|
|
container);
|
|
}
|
|
|
|
D3D12_INTERNAL_BufferTransitionFromDefaultUsage(
|
|
commandBuffer,
|
|
destinationState,
|
|
container->activeBuffer);
|
|
|
|
return container->activeBuffer;
|
|
}
|
|
|
|
static void D3D12_BeginRenderPass(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const SDL_GPUColorTargetInfo *colorTargetInfos,
|
|
Uint32 numColorTargets,
|
|
const SDL_GPUDepthStencilTargetInfo *depthStencilTargetInfo)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
Uint32 framebufferWidth = SDL_MAX_UINT32;
|
|
Uint32 framebufferHeight = SDL_MAX_UINT32;
|
|
|
|
for (Uint32 i = 0; i < numColorTargets; i += 1) {
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)colorTargetInfos[i].texture;
|
|
Uint32 h = container->header.info.height >> colorTargetInfos[i].mip_level;
|
|
Uint32 w = container->header.info.width >> colorTargetInfos[i].mip_level;
|
|
|
|
// The framebuffer cannot be larger than the smallest target.
|
|
|
|
if (w < framebufferWidth) {
|
|
framebufferWidth = w;
|
|
}
|
|
|
|
if (h < framebufferHeight) {
|
|
framebufferHeight = h;
|
|
}
|
|
|
|
if (!(container->header.info.usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Color target texture was not designated as a color target!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (depthStencilTargetInfo != NULL) {
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)depthStencilTargetInfo->texture;
|
|
|
|
Uint32 h = container->header.info.height;
|
|
Uint32 w = container->header.info.width;
|
|
|
|
// The framebuffer cannot be larger than the smallest target.
|
|
|
|
if (w < framebufferWidth) {
|
|
framebufferWidth = w;
|
|
}
|
|
|
|
if (h < framebufferHeight) {
|
|
framebufferHeight = h;
|
|
}
|
|
|
|
// Fixme:
|
|
if (!(container->header.info.usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Depth stencil target texture was not designated as a depth target!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
D3D12_CPU_DESCRIPTOR_HANDLE rtvs[MAX_COLOR_TARGET_BINDINGS];
|
|
|
|
for (Uint32 i = 0; i < numColorTargets; i += 1) {
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)colorTargetInfos[i].texture;
|
|
D3D12TextureSubresource *subresource = D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
d3d12CommandBuffer,
|
|
container,
|
|
container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : colorTargetInfos[i].layer_or_depth_plane,
|
|
colorTargetInfos[i].mip_level,
|
|
colorTargetInfos[i].cycle,
|
|
D3D12_RESOURCE_STATE_RENDER_TARGET);
|
|
|
|
Uint32 rtvIndex = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? colorTargetInfos[i].layer_or_depth_plane : 0;
|
|
D3D12_CPU_DESCRIPTOR_HANDLE rtv =
|
|
subresource->rtvHandles[rtvIndex].cpuHandle;
|
|
|
|
if (colorTargetInfos[i].load_op == SDL_GPU_LOADOP_CLEAR) {
|
|
float clearColor[4];
|
|
clearColor[0] = colorTargetInfos[i].clear_color.r;
|
|
clearColor[1] = colorTargetInfos[i].clear_color.g;
|
|
clearColor[2] = colorTargetInfos[i].clear_color.b;
|
|
clearColor[3] = colorTargetInfos[i].clear_color.a;
|
|
|
|
ID3D12GraphicsCommandList_ClearRenderTargetView(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
rtv,
|
|
clearColor,
|
|
0,
|
|
NULL);
|
|
}
|
|
|
|
rtvs[i] = rtv;
|
|
d3d12CommandBuffer->colorTargetTextureSubresources[i] = subresource;
|
|
|
|
D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, subresource->parent);
|
|
}
|
|
|
|
d3d12CommandBuffer->colorTargetTextureSubresourceCount = numColorTargets;
|
|
|
|
D3D12_CPU_DESCRIPTOR_HANDLE dsv;
|
|
if (depthStencilTargetInfo != NULL) {
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)depthStencilTargetInfo->texture;
|
|
D3D12TextureSubresource *subresource = D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
d3d12CommandBuffer,
|
|
container,
|
|
0,
|
|
0,
|
|
depthStencilTargetInfo->cycle,
|
|
D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
|
|
|
if (
|
|
depthStencilTargetInfo->load_op == SDL_GPU_LOADOP_CLEAR ||
|
|
depthStencilTargetInfo->stencil_load_op == SDL_GPU_LOADOP_CLEAR) {
|
|
D3D12_CLEAR_FLAGS clearFlags = (D3D12_CLEAR_FLAGS)0;
|
|
if (depthStencilTargetInfo->load_op == SDL_GPU_LOADOP_CLEAR) {
|
|
clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
|
|
}
|
|
if (depthStencilTargetInfo->stencil_load_op == SDL_GPU_LOADOP_CLEAR) {
|
|
clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
|
|
}
|
|
|
|
ID3D12GraphicsCommandList_ClearDepthStencilView(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
subresource->dsvHandle.cpuHandle,
|
|
clearFlags,
|
|
depthStencilTargetInfo->clear_depth,
|
|
depthStencilTargetInfo->clear_stencil,
|
|
0,
|
|
NULL);
|
|
}
|
|
|
|
dsv = subresource->dsvHandle.cpuHandle;
|
|
d3d12CommandBuffer->depthStencilTextureSubresource = subresource;
|
|
D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, subresource->parent);
|
|
}
|
|
|
|
ID3D12GraphicsCommandList_OMSetRenderTargets(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
numColorTargets,
|
|
rtvs,
|
|
false,
|
|
(depthStencilTargetInfo == NULL) ? NULL : &dsv);
|
|
|
|
// Set sensible default states
|
|
SDL_GPUViewport defaultViewport;
|
|
defaultViewport.x = 0;
|
|
defaultViewport.y = 0;
|
|
defaultViewport.w = (float)framebufferWidth;
|
|
defaultViewport.h = (float)framebufferHeight;
|
|
defaultViewport.min_depth = 0;
|
|
defaultViewport.max_depth = 1;
|
|
|
|
D3D12_SetViewport(
|
|
commandBuffer,
|
|
&defaultViewport);
|
|
|
|
SDL_Rect defaultScissor;
|
|
defaultScissor.x = 0;
|
|
defaultScissor.y = 0;
|
|
defaultScissor.w = (Sint32)framebufferWidth;
|
|
defaultScissor.h = (Sint32)framebufferHeight;
|
|
|
|
D3D12_SetScissor(
|
|
commandBuffer,
|
|
&defaultScissor);
|
|
|
|
D3D12_SetStencilReference(
|
|
commandBuffer,
|
|
0);
|
|
|
|
SDL_FColor blendConstants;
|
|
blendConstants.r = 1.0f;
|
|
blendConstants.g = 1.0f;
|
|
blendConstants.b = 1.0f;
|
|
blendConstants.a = 1.0f;
|
|
|
|
D3D12_SetBlendConstants(
|
|
commandBuffer,
|
|
blendConstants);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_TrackUniformBuffer(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12UniformBuffer *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 = (D3D12UniformBuffer **)SDL_realloc(
|
|
commandBuffer->usedUniformBuffers,
|
|
commandBuffer->usedUniformBufferCapacity * sizeof(D3D12UniformBuffer *));
|
|
}
|
|
|
|
commandBuffer->usedUniformBuffers[commandBuffer->usedUniformBufferCount] = uniformBuffer;
|
|
commandBuffer->usedUniformBufferCount += 1;
|
|
|
|
D3D12_INTERNAL_TrackBuffer(
|
|
commandBuffer,
|
|
uniformBuffer->buffer);
|
|
}
|
|
|
|
static D3D12UniformBuffer *D3D12_INTERNAL_AcquireUniformBufferFromPool(
|
|
D3D12CommandBuffer *commandBuffer)
|
|
{
|
|
D3D12Renderer *renderer = commandBuffer->renderer;
|
|
D3D12UniformBuffer *uniformBuffer;
|
|
|
|
SDL_LockMutex(renderer->acquireUniformBufferLock);
|
|
|
|
if (renderer->uniformBufferPoolCount > 0) {
|
|
uniformBuffer = renderer->uniformBufferPool[renderer->uniformBufferPoolCount - 1];
|
|
renderer->uniformBufferPoolCount -= 1;
|
|
} else {
|
|
uniformBuffer = (D3D12UniformBuffer *)SDL_calloc(1, sizeof(D3D12UniformBuffer));
|
|
if (!uniformBuffer) {
|
|
SDL_UnlockMutex(renderer->acquireUniformBufferLock);
|
|
return NULL;
|
|
}
|
|
|
|
uniformBuffer->buffer = D3D12_INTERNAL_CreateBuffer(
|
|
renderer,
|
|
0,
|
|
UNIFORM_BUFFER_SIZE,
|
|
D3D12_BUFFER_TYPE_UNIFORM);
|
|
if (!uniformBuffer->buffer) {
|
|
SDL_UnlockMutex(renderer->acquireUniformBufferLock);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->acquireUniformBufferLock);
|
|
|
|
uniformBuffer->currentBlockSize = 0;
|
|
uniformBuffer->drawOffset = 0;
|
|
uniformBuffer->writeOffset = 0;
|
|
|
|
HRESULT res = ID3D12Resource_Map(
|
|
uniformBuffer->buffer->handle,
|
|
0,
|
|
NULL,
|
|
(void **)&uniformBuffer->buffer->mapPointer);
|
|
ERROR_CHECK_RETURN("Failed to map buffer pool!", NULL);
|
|
|
|
D3D12_INTERNAL_TrackUniformBuffer(commandBuffer, uniformBuffer);
|
|
|
|
return uniformBuffer;
|
|
}
|
|
|
|
static void D3D12_INTERNAL_ReturnUniformBufferToPool(
|
|
D3D12Renderer *renderer,
|
|
D3D12UniformBuffer *uniformBuffer)
|
|
{
|
|
if (renderer->uniformBufferPoolCount >= renderer->uniformBufferPoolCapacity) {
|
|
renderer->uniformBufferPoolCapacity *= 2;
|
|
renderer->uniformBufferPool = (D3D12UniformBuffer **)SDL_realloc(
|
|
renderer->uniformBufferPool,
|
|
renderer->uniformBufferPoolCapacity * sizeof(D3D12UniformBuffer *));
|
|
}
|
|
|
|
renderer->uniformBufferPool[renderer->uniformBufferPoolCount] = uniformBuffer;
|
|
renderer->uniformBufferPoolCount += 1;
|
|
}
|
|
|
|
static void D3D12_INTERNAL_PushUniformData(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
SDL_GPUShaderStage shaderStage,
|
|
Uint32 slotIndex,
|
|
const void *data,
|
|
Uint32 length)
|
|
{
|
|
D3D12UniformBuffer *uniformBuffer;
|
|
|
|
if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) {
|
|
if (commandBuffer->vertexUniformBuffers[slotIndex] == NULL) {
|
|
commandBuffer->vertexUniformBuffers[slotIndex] = D3D12_INTERNAL_AcquireUniformBufferFromPool(
|
|
commandBuffer);
|
|
}
|
|
uniformBuffer = commandBuffer->vertexUniformBuffers[slotIndex];
|
|
} else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) {
|
|
if (commandBuffer->fragmentUniformBuffers[slotIndex] == NULL) {
|
|
commandBuffer->fragmentUniformBuffers[slotIndex] = D3D12_INTERNAL_AcquireUniformBufferFromPool(
|
|
commandBuffer);
|
|
}
|
|
uniformBuffer = commandBuffer->fragmentUniformBuffers[slotIndex];
|
|
} else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) {
|
|
if (commandBuffer->computeUniformBuffers[slotIndex] == NULL) {
|
|
commandBuffer->computeUniformBuffers[slotIndex] = D3D12_INTERNAL_AcquireUniformBufferFromPool(
|
|
commandBuffer);
|
|
}
|
|
uniformBuffer = commandBuffer->computeUniformBuffers[slotIndex];
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
|
|
return;
|
|
}
|
|
|
|
uniformBuffer->currentBlockSize =
|
|
D3D12_INTERNAL_Align(
|
|
length,
|
|
256);
|
|
|
|
// If there is no more room, acquire a new uniform buffer
|
|
if (uniformBuffer->writeOffset + uniformBuffer->currentBlockSize >= UNIFORM_BUFFER_SIZE) {
|
|
ID3D12Resource_Unmap(
|
|
uniformBuffer->buffer->handle,
|
|
0,
|
|
NULL);
|
|
uniformBuffer->buffer->mapPointer = NULL;
|
|
|
|
uniformBuffer = D3D12_INTERNAL_AcquireUniformBufferFromPool(commandBuffer);
|
|
|
|
uniformBuffer->drawOffset = 0;
|
|
uniformBuffer->writeOffset = 0;
|
|
|
|
if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) {
|
|
commandBuffer->vertexUniformBuffers[slotIndex] = uniformBuffer;
|
|
} else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) {
|
|
commandBuffer->fragmentUniformBuffers[slotIndex] = uniformBuffer;
|
|
} else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) {
|
|
commandBuffer->computeUniformBuffers[slotIndex] = uniformBuffer;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
|
|
}
|
|
}
|
|
|
|
uniformBuffer->drawOffset = uniformBuffer->writeOffset;
|
|
|
|
SDL_memcpy(
|
|
(Uint8 *)uniformBuffer->buffer->mapPointer + uniformBuffer->writeOffset,
|
|
data,
|
|
length);
|
|
|
|
uniformBuffer->writeOffset += uniformBuffer->currentBlockSize;
|
|
|
|
if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) {
|
|
commandBuffer->needVertexUniformBufferBind[slotIndex] = true;
|
|
} else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) {
|
|
commandBuffer->needFragmentUniformBufferBind[slotIndex] = true;
|
|
} else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) {
|
|
commandBuffer->needComputeUniformBufferBind[slotIndex] = true;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
|
|
}
|
|
}
|
|
|
|
static void D3D12_BindGraphicsPipeline(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUGraphicsPipeline *graphicsPipeline)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12GraphicsPipeline *pipeline = (D3D12GraphicsPipeline *)graphicsPipeline;
|
|
Uint32 i;
|
|
|
|
d3d12CommandBuffer->currentGraphicsPipeline = pipeline;
|
|
|
|
// Set the pipeline state
|
|
ID3D12GraphicsCommandList_SetPipelineState(d3d12CommandBuffer->graphicsCommandList, pipeline->pipelineState);
|
|
ID3D12GraphicsCommandList_SetGraphicsRootSignature(d3d12CommandBuffer->graphicsCommandList, pipeline->rootSignature->handle);
|
|
ID3D12GraphicsCommandList_IASetPrimitiveTopology(d3d12CommandBuffer->graphicsCommandList, SDLToD3D12_PrimitiveType[pipeline->primitiveType]);
|
|
|
|
// Mark that bindings are needed
|
|
d3d12CommandBuffer->needVertexSamplerBind = true;
|
|
d3d12CommandBuffer->needVertexStorageTextureBind = true;
|
|
d3d12CommandBuffer->needVertexStorageBufferBind = true;
|
|
d3d12CommandBuffer->needFragmentSamplerBind = true;
|
|
d3d12CommandBuffer->needFragmentStorageTextureBind = true;
|
|
d3d12CommandBuffer->needFragmentStorageBufferBind = true;
|
|
|
|
for (i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
|
|
d3d12CommandBuffer->needVertexUniformBufferBind[i] = true;
|
|
d3d12CommandBuffer->needFragmentUniformBufferBind[i] = true;
|
|
}
|
|
|
|
for (i = 0; i < pipeline->vertexUniformBufferCount; i += 1) {
|
|
if (d3d12CommandBuffer->vertexUniformBuffers[i] == NULL) {
|
|
d3d12CommandBuffer->vertexUniformBuffers[i] = D3D12_INTERNAL_AcquireUniformBufferFromPool(
|
|
d3d12CommandBuffer);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < pipeline->fragmentUniformBufferCount; i += 1) {
|
|
if (d3d12CommandBuffer->fragmentUniformBuffers[i] == NULL) {
|
|
d3d12CommandBuffer->fragmentUniformBuffers[i] = D3D12_INTERNAL_AcquireUniformBufferFromPool(
|
|
d3d12CommandBuffer);
|
|
}
|
|
}
|
|
|
|
D3D12_INTERNAL_TrackGraphicsPipeline(d3d12CommandBuffer, pipeline);
|
|
}
|
|
|
|
static void D3D12_BindVertexBuffers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstBinding,
|
|
const SDL_GPUBufferBinding *bindings,
|
|
Uint32 numBindings)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < numBindings; i += 1) {
|
|
D3D12Buffer *currentBuffer = ((D3D12BufferContainer *)bindings[i].buffer)->activeBuffer;
|
|
d3d12CommandBuffer->vertexBuffers[firstBinding + i] = currentBuffer;
|
|
d3d12CommandBuffer->vertexBufferOffsets[firstBinding + i] = bindings[i].offset;
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, currentBuffer);
|
|
}
|
|
|
|
d3d12CommandBuffer->vertexBufferCount =
|
|
SDL_max(d3d12CommandBuffer->vertexBufferCount, firstBinding + numBindings);
|
|
|
|
d3d12CommandBuffer->needVertexBufferBind = true;
|
|
}
|
|
|
|
static void D3D12_BindIndexBuffer(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const SDL_GPUBufferBinding *binding,
|
|
SDL_GPUIndexElementSize indexElementSize)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12Buffer *buffer = ((D3D12BufferContainer *)binding->buffer)->activeBuffer;
|
|
D3D12_INDEX_BUFFER_VIEW view;
|
|
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, buffer);
|
|
|
|
view.BufferLocation = buffer->virtualAddress + binding->offset;
|
|
view.SizeInBytes = buffer->container->size - binding->offset;
|
|
view.Format =
|
|
indexElementSize == SDL_GPU_INDEXELEMENTSIZE_16BIT ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
|
|
|
|
ID3D12GraphicsCommandList_IASetIndexBuffer(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
&view);
|
|
}
|
|
|
|
static void D3D12_BindVertexSamplers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
const SDL_GPUTextureSamplerBinding *textureSamplerBindings,
|
|
Uint32 numBindings)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < numBindings; i += 1) {
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)textureSamplerBindings[i].texture;
|
|
D3D12Sampler *sampler = (D3D12Sampler *)textureSamplerBindings[i].sampler;
|
|
|
|
D3D12_INTERNAL_TrackTexture(
|
|
d3d12CommandBuffer,
|
|
container->activeTexture);
|
|
|
|
D3D12_INTERNAL_TrackSampler(
|
|
d3d12CommandBuffer,
|
|
sampler);
|
|
|
|
d3d12CommandBuffer->vertexSamplers[firstSlot + i] = sampler;
|
|
d3d12CommandBuffer->vertexSamplerTextures[firstSlot + i] = container->activeTexture;
|
|
}
|
|
|
|
d3d12CommandBuffer->needVertexSamplerBind = true;
|
|
}
|
|
|
|
static void D3D12_BindVertexStorageTextures(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUTexture *const *storageTextures,
|
|
Uint32 numBindings)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < numBindings; i += 1) {
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)storageTextures[i];
|
|
D3D12Texture *texture = container->activeTexture;
|
|
|
|
D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, texture);
|
|
|
|
d3d12CommandBuffer->vertexStorageTextures[firstSlot + i] = texture;
|
|
}
|
|
|
|
d3d12CommandBuffer->needVertexStorageTextureBind = true;
|
|
}
|
|
|
|
static void D3D12_BindVertexStorageBuffers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUBuffer *const *storageBuffers,
|
|
Uint32 numBindings)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < numBindings; i += 1) {
|
|
D3D12BufferContainer *container = (D3D12BufferContainer *)storageBuffers[i];
|
|
|
|
D3D12_INTERNAL_TrackBuffer(
|
|
d3d12CommandBuffer,
|
|
container->activeBuffer);
|
|
|
|
d3d12CommandBuffer->vertexStorageBuffers[firstSlot + i] = container->activeBuffer;
|
|
}
|
|
|
|
d3d12CommandBuffer->needVertexStorageBufferBind = true;
|
|
}
|
|
|
|
static void D3D12_BindFragmentSamplers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
const SDL_GPUTextureSamplerBinding *textureSamplerBindings,
|
|
Uint32 numBindings)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < numBindings; i += 1) {
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)textureSamplerBindings[i].texture;
|
|
D3D12Sampler *sampler = (D3D12Sampler *)textureSamplerBindings[i].sampler;
|
|
|
|
D3D12_INTERNAL_TrackTexture(
|
|
d3d12CommandBuffer,
|
|
container->activeTexture);
|
|
|
|
D3D12_INTERNAL_TrackSampler(
|
|
d3d12CommandBuffer,
|
|
sampler);
|
|
|
|
d3d12CommandBuffer->fragmentSamplers[firstSlot + i] = sampler;
|
|
d3d12CommandBuffer->fragmentSamplerTextures[firstSlot + i] = container->activeTexture;
|
|
}
|
|
|
|
d3d12CommandBuffer->needFragmentSamplerBind = true;
|
|
}
|
|
|
|
static void D3D12_BindFragmentStorageTextures(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUTexture *const *storageTextures,
|
|
Uint32 numBindings)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < numBindings; i += 1) {
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)storageTextures[i];
|
|
D3D12Texture *texture = container->activeTexture;
|
|
|
|
D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, texture);
|
|
|
|
d3d12CommandBuffer->fragmentStorageTextures[firstSlot + i] = texture;
|
|
}
|
|
|
|
d3d12CommandBuffer->needFragmentStorageTextureBind = true;
|
|
}
|
|
|
|
static void D3D12_BindFragmentStorageBuffers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUBuffer *const *storageBuffers,
|
|
Uint32 numBindings)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < numBindings; i += 1) {
|
|
D3D12BufferContainer *container = (D3D12BufferContainer *)storageBuffers[i];
|
|
|
|
D3D12_INTERNAL_TrackBuffer(
|
|
d3d12CommandBuffer,
|
|
container->activeBuffer);
|
|
|
|
d3d12CommandBuffer->fragmentStorageBuffers[firstSlot + i] = container->activeBuffer;
|
|
}
|
|
|
|
d3d12CommandBuffer->needFragmentStorageBufferBind = true;
|
|
}
|
|
|
|
static void D3D12_PushVertexUniformData(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 slotIndex,
|
|
const void *data,
|
|
Uint32 length)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
D3D12_INTERNAL_PushUniformData(
|
|
d3d12CommandBuffer,
|
|
SDL_GPU_SHADERSTAGE_VERTEX,
|
|
slotIndex,
|
|
data,
|
|
length);
|
|
}
|
|
|
|
static void D3D12_PushFragmentUniformData(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 slotIndex,
|
|
const void *data,
|
|
Uint32 length)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
D3D12_INTERNAL_PushUniformData(
|
|
d3d12CommandBuffer,
|
|
SDL_GPU_SHADERSTAGE_FRAGMENT,
|
|
slotIndex,
|
|
data,
|
|
length);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_WriteGPUDescriptors(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE heapType,
|
|
D3D12_CPU_DESCRIPTOR_HANDLE *resourceDescriptorHandles,
|
|
Uint32 resourceHandleCount,
|
|
D3D12_GPU_DESCRIPTOR_HANDLE *gpuBaseDescriptor)
|
|
{
|
|
D3D12DescriptorHeap *heap = commandBuffer->gpuDescriptorHeaps[heapType];
|
|
D3D12_CPU_DESCRIPTOR_HANDLE gpuHeapCpuHandle;
|
|
|
|
// FIXME: need to error on overflow
|
|
gpuHeapCpuHandle.ptr = heap->descriptorHeapCPUStart.ptr + (heap->currentDescriptorIndex * heap->descriptorSize);
|
|
gpuBaseDescriptor->ptr = heap->descriptorHeapGPUStart.ptr + (heap->currentDescriptorIndex * heap->descriptorSize);
|
|
|
|
for (Uint32 i = 0; i < resourceHandleCount; i += 1) {
|
|
ID3D12Device_CopyDescriptorsSimple(
|
|
commandBuffer->renderer->device,
|
|
1,
|
|
gpuHeapCpuHandle,
|
|
resourceDescriptorHandles[i],
|
|
heapType);
|
|
|
|
heap->currentDescriptorIndex += 1;
|
|
gpuHeapCpuHandle.ptr += heap->descriptorSize;
|
|
}
|
|
}
|
|
|
|
static void D3D12_INTERNAL_BindGraphicsResources(
|
|
D3D12CommandBuffer *commandBuffer)
|
|
{
|
|
D3D12GraphicsPipeline *graphicsPipeline = commandBuffer->currentGraphicsPipeline;
|
|
|
|
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandles[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorHandle;
|
|
D3D12_VERTEX_BUFFER_VIEW vertexBufferViews[MAX_BUFFER_BINDINGS];
|
|
|
|
if (commandBuffer->needVertexBufferBind) {
|
|
for (Uint32 i = 0; i < commandBuffer->vertexBufferCount; i += 1) {
|
|
vertexBufferViews[i].BufferLocation = commandBuffer->vertexBuffers[i]->virtualAddress + commandBuffer->vertexBufferOffsets[i];
|
|
vertexBufferViews[i].SizeInBytes = commandBuffer->vertexBuffers[i]->container->size - commandBuffer->vertexBufferOffsets[i];
|
|
vertexBufferViews[i].StrideInBytes = graphicsPipeline->vertexStrides[i];
|
|
}
|
|
|
|
ID3D12GraphicsCommandList_IASetVertexBuffers(
|
|
commandBuffer->graphicsCommandList,
|
|
0,
|
|
commandBuffer->vertexBufferCount,
|
|
vertexBufferViews);
|
|
}
|
|
|
|
if (commandBuffer->needVertexSamplerBind) {
|
|
if (graphicsPipeline->vertexSamplerCount > 0) {
|
|
for (Uint32 i = 0; i < graphicsPipeline->vertexSamplerCount; i += 1) {
|
|
cpuHandles[i] = commandBuffer->vertexSamplers[i]->handle.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
|
|
cpuHandles,
|
|
graphicsPipeline->vertexSamplerCount,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
|
|
commandBuffer->graphicsCommandList,
|
|
graphicsPipeline->rootSignature->vertexSamplerRootIndex,
|
|
gpuDescriptorHandle);
|
|
|
|
for (Uint32 i = 0; i < graphicsPipeline->vertexSamplerCount; i += 1) {
|
|
cpuHandles[i] = commandBuffer->vertexSamplerTextures[i]->srvHandle.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
cpuHandles,
|
|
graphicsPipeline->vertexSamplerCount,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
|
|
commandBuffer->graphicsCommandList,
|
|
graphicsPipeline->rootSignature->vertexSamplerTextureRootIndex,
|
|
gpuDescriptorHandle);
|
|
}
|
|
commandBuffer->needVertexSamplerBind = false;
|
|
}
|
|
|
|
if (commandBuffer->needVertexStorageTextureBind) {
|
|
if (graphicsPipeline->vertexStorageTextureCount > 0) {
|
|
for (Uint32 i = 0; i < graphicsPipeline->vertexStorageTextureCount; i += 1) {
|
|
cpuHandles[i] = commandBuffer->vertexStorageTextures[i]->srvHandle.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
cpuHandles,
|
|
graphicsPipeline->vertexStorageTextureCount,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
|
|
commandBuffer->graphicsCommandList,
|
|
graphicsPipeline->rootSignature->vertexStorageTextureRootIndex,
|
|
gpuDescriptorHandle);
|
|
}
|
|
commandBuffer->needVertexStorageTextureBind = false;
|
|
}
|
|
|
|
if (commandBuffer->needVertexStorageBufferBind) {
|
|
if (graphicsPipeline->vertexStorageBufferCount > 0) {
|
|
for (Uint32 i = 0; i < graphicsPipeline->vertexStorageBufferCount; i += 1) {
|
|
cpuHandles[i] = commandBuffer->vertexStorageBuffers[i]->srvDescriptor.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
cpuHandles,
|
|
graphicsPipeline->vertexStorageBufferCount,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
|
|
commandBuffer->graphicsCommandList,
|
|
graphicsPipeline->rootSignature->vertexStorageBufferRootIndex,
|
|
gpuDescriptorHandle);
|
|
}
|
|
commandBuffer->needVertexStorageBufferBind = false;
|
|
}
|
|
|
|
for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
|
|
if (commandBuffer->needVertexUniformBufferBind[i]) {
|
|
if (graphicsPipeline->vertexUniformBufferCount > i) {
|
|
ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(
|
|
commandBuffer->graphicsCommandList,
|
|
graphicsPipeline->rootSignature->vertexUniformBufferRootIndex[i],
|
|
commandBuffer->vertexUniformBuffers[i]->buffer->virtualAddress + commandBuffer->vertexUniformBuffers[i]->drawOffset);
|
|
}
|
|
commandBuffer->needVertexUniformBufferBind[i] = false;
|
|
}
|
|
}
|
|
|
|
if (commandBuffer->needFragmentSamplerBind) {
|
|
if (graphicsPipeline->fragmentSamplerCount > 0) {
|
|
for (Uint32 i = 0; i < graphicsPipeline->fragmentSamplerCount; i += 1) {
|
|
cpuHandles[i] = commandBuffer->fragmentSamplers[i]->handle.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
|
|
cpuHandles,
|
|
graphicsPipeline->fragmentSamplerCount,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
|
|
commandBuffer->graphicsCommandList,
|
|
graphicsPipeline->rootSignature->fragmentSamplerRootIndex,
|
|
gpuDescriptorHandle);
|
|
|
|
for (Uint32 i = 0; i < graphicsPipeline->fragmentSamplerCount; i += 1) {
|
|
cpuHandles[i] = commandBuffer->fragmentSamplerTextures[i]->srvHandle.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
cpuHandles,
|
|
graphicsPipeline->fragmentSamplerCount,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
|
|
commandBuffer->graphicsCommandList,
|
|
graphicsPipeline->rootSignature->fragmentSamplerTextureRootIndex,
|
|
gpuDescriptorHandle);
|
|
}
|
|
commandBuffer->needFragmentSamplerBind = false;
|
|
}
|
|
|
|
if (commandBuffer->needFragmentStorageTextureBind) {
|
|
if (graphicsPipeline->fragmentStorageTextureCount > 0) {
|
|
for (Uint32 i = 0; i < graphicsPipeline->fragmentStorageTextureCount; i += 1) {
|
|
cpuHandles[i] = commandBuffer->fragmentStorageTextures[i]->srvHandle.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
cpuHandles,
|
|
graphicsPipeline->fragmentStorageTextureCount,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
|
|
commandBuffer->graphicsCommandList,
|
|
graphicsPipeline->rootSignature->fragmentStorageTextureRootIndex,
|
|
gpuDescriptorHandle);
|
|
}
|
|
commandBuffer->needFragmentStorageTextureBind = false;
|
|
}
|
|
|
|
if (commandBuffer->needFragmentStorageBufferBind) {
|
|
if (graphicsPipeline->fragmentStorageBufferCount > 0) {
|
|
for (Uint32 i = 0; i < graphicsPipeline->fragmentStorageBufferCount; i += 1) {
|
|
cpuHandles[i] = commandBuffer->fragmentStorageBuffers[i]->srvDescriptor.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
cpuHandles,
|
|
graphicsPipeline->fragmentStorageBufferCount,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
|
|
commandBuffer->graphicsCommandList,
|
|
graphicsPipeline->rootSignature->fragmentStorageBufferRootIndex,
|
|
gpuDescriptorHandle);
|
|
}
|
|
commandBuffer->needFragmentStorageBufferBind = false;
|
|
}
|
|
|
|
for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
|
|
if (commandBuffer->needFragmentUniformBufferBind[i]) {
|
|
if (graphicsPipeline->fragmentUniformBufferCount > i) {
|
|
ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(
|
|
commandBuffer->graphicsCommandList,
|
|
graphicsPipeline->rootSignature->fragmentUniformBufferRootIndex[i],
|
|
commandBuffer->fragmentUniformBuffers[i]->buffer->virtualAddress + commandBuffer->fragmentUniformBuffers[i]->drawOffset);
|
|
}
|
|
commandBuffer->needFragmentUniformBufferBind[i] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void D3D12_DrawIndexedPrimitives(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 numIndices,
|
|
Uint32 numInstances,
|
|
Uint32 firstIndex,
|
|
Sint32 vertexOffset,
|
|
Uint32 firstInstance)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12_INTERNAL_BindGraphicsResources(d3d12CommandBuffer);
|
|
|
|
ID3D12GraphicsCommandList_DrawIndexedInstanced(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
numIndices,
|
|
numInstances,
|
|
firstIndex,
|
|
vertexOffset,
|
|
firstInstance);
|
|
}
|
|
|
|
static void D3D12_DrawPrimitives(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 numVertices,
|
|
Uint32 numInstances,
|
|
Uint32 firstVertex,
|
|
Uint32 firstInstance)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12_INTERNAL_BindGraphicsResources(d3d12CommandBuffer);
|
|
|
|
ID3D12GraphicsCommandList_DrawInstanced(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
numVertices,
|
|
numInstances,
|
|
firstVertex,
|
|
firstInstance);
|
|
}
|
|
|
|
static void D3D12_DrawPrimitivesIndirect(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUBuffer *buffer,
|
|
Uint32 offset,
|
|
Uint32 drawCount)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12Buffer *d3d12Buffer = ((D3D12BufferContainer *)buffer)->activeBuffer;
|
|
|
|
D3D12_INTERNAL_BindGraphicsResources(d3d12CommandBuffer);
|
|
|
|
ID3D12GraphicsCommandList_ExecuteIndirect(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
d3d12CommandBuffer->renderer->indirectDrawCommandSignature,
|
|
drawCount,
|
|
d3d12Buffer->handle,
|
|
offset,
|
|
NULL,
|
|
0);
|
|
}
|
|
|
|
static void D3D12_DrawIndexedPrimitivesIndirect(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUBuffer *buffer,
|
|
Uint32 offset,
|
|
Uint32 drawCount)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12Buffer *d3d12Buffer = ((D3D12BufferContainer *)buffer)->activeBuffer;
|
|
|
|
D3D12_INTERNAL_BindGraphicsResources(d3d12CommandBuffer);
|
|
|
|
ID3D12GraphicsCommandList_ExecuteIndirect(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
d3d12CommandBuffer->renderer->indirectIndexedDrawCommandSignature,
|
|
drawCount,
|
|
d3d12Buffer->handle,
|
|
offset,
|
|
NULL,
|
|
0);
|
|
}
|
|
|
|
static void D3D12_EndRenderPass(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
Uint32 i;
|
|
|
|
for (i = 0; i < d3d12CommandBuffer->colorTargetTextureSubresourceCount; i += 1) {
|
|
D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_RENDER_TARGET,
|
|
d3d12CommandBuffer->colorTargetTextureSubresources[i]);
|
|
|
|
d3d12CommandBuffer->colorTargetTextureSubresources[i] = NULL;
|
|
}
|
|
d3d12CommandBuffer->colorTargetTextureSubresourceCount = 0;
|
|
|
|
if (d3d12CommandBuffer->depthStencilTextureSubresource != NULL) {
|
|
D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_DEPTH_WRITE,
|
|
d3d12CommandBuffer->depthStencilTextureSubresource);
|
|
|
|
d3d12CommandBuffer->depthStencilTextureSubresource = NULL;
|
|
}
|
|
|
|
d3d12CommandBuffer->currentGraphicsPipeline = NULL;
|
|
|
|
ID3D12GraphicsCommandList_OMSetRenderTargets(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
0,
|
|
NULL,
|
|
false,
|
|
NULL);
|
|
|
|
// Reset bind state
|
|
SDL_zeroa(d3d12CommandBuffer->colorTargetTextureSubresources);
|
|
d3d12CommandBuffer->depthStencilTextureSubresource = NULL;
|
|
|
|
SDL_zeroa(d3d12CommandBuffer->vertexBuffers);
|
|
SDL_zeroa(d3d12CommandBuffer->vertexBufferOffsets);
|
|
d3d12CommandBuffer->vertexBufferCount = 0;
|
|
|
|
SDL_zeroa(d3d12CommandBuffer->vertexSamplerTextures);
|
|
SDL_zeroa(d3d12CommandBuffer->vertexSamplers);
|
|
SDL_zeroa(d3d12CommandBuffer->vertexStorageTextures);
|
|
SDL_zeroa(d3d12CommandBuffer->vertexStorageBuffers);
|
|
|
|
SDL_zeroa(d3d12CommandBuffer->fragmentSamplerTextures);
|
|
SDL_zeroa(d3d12CommandBuffer->fragmentSamplers);
|
|
SDL_zeroa(d3d12CommandBuffer->fragmentStorageTextures);
|
|
SDL_zeroa(d3d12CommandBuffer->fragmentStorageBuffers);
|
|
}
|
|
|
|
// Compute Pass
|
|
|
|
static void D3D12_BeginComputePass(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const SDL_GPUStorageTextureWriteOnlyBinding *storageTextureBindings,
|
|
Uint32 numStorageTextureBindings,
|
|
const SDL_GPUStorageBufferWriteOnlyBinding *storageBufferBindings,
|
|
Uint32 numStorageBufferBindings)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresourceCount = numStorageTextureBindings;
|
|
d3d12CommandBuffer->computeWriteOnlyStorageBufferCount = numStorageBufferBindings;
|
|
|
|
/* Write-only resources will be actually bound in BindComputePipeline
|
|
* after the root signature is set.
|
|
* We also have to scan to see which barriers we actually need because depth slices aren't separate subresources
|
|
*/
|
|
if (numStorageTextureBindings > 0) {
|
|
for (Uint32 i = 0; i < numStorageTextureBindings; i += 1) {
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)storageTextureBindings[i].texture;
|
|
if (!(container->header.info.usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Attempted to bind read-only texture as compute write texture");
|
|
}
|
|
|
|
D3D12TextureSubresource *subresource = D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
d3d12CommandBuffer,
|
|
container,
|
|
storageTextureBindings[i].layer,
|
|
storageTextureBindings[i].mip_level,
|
|
storageTextureBindings[i].cycle,
|
|
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
|
|
|
d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresources[i] = subresource;
|
|
|
|
D3D12_INTERNAL_TrackTexture(
|
|
d3d12CommandBuffer,
|
|
subresource->parent);
|
|
}
|
|
}
|
|
|
|
if (numStorageBufferBindings > 0) {
|
|
for (Uint32 i = 0; i < numStorageBufferBindings; i += 1) {
|
|
D3D12BufferContainer *container = (D3D12BufferContainer *)storageBufferBindings[i].buffer;
|
|
if (!(container->usage & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Attempted to bind read-only texture as compute write texture");
|
|
}
|
|
D3D12Buffer *buffer = D3D12_INTERNAL_PrepareBufferForWrite(
|
|
d3d12CommandBuffer,
|
|
container,
|
|
storageBufferBindings[i].cycle,
|
|
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
|
|
|
d3d12CommandBuffer->computeWriteOnlyStorageBuffers[i] = buffer;
|
|
|
|
D3D12_INTERNAL_TrackBuffer(
|
|
d3d12CommandBuffer,
|
|
buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void D3D12_BindComputePipeline(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUComputePipeline *computePipeline)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12ComputePipeline *pipeline = (D3D12ComputePipeline *)computePipeline;
|
|
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandles[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorHandle;
|
|
|
|
ID3D12GraphicsCommandList_SetPipelineState(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
pipeline->pipelineState);
|
|
|
|
ID3D12GraphicsCommandList_SetComputeRootSignature(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
pipeline->rootSignature->handle);
|
|
|
|
d3d12CommandBuffer->currentComputePipeline = pipeline;
|
|
|
|
d3d12CommandBuffer->needComputeSamplerBind = true;
|
|
d3d12CommandBuffer->needComputeReadOnlyStorageTextureBind = true;
|
|
d3d12CommandBuffer->needComputeReadOnlyStorageBufferBind = true;
|
|
|
|
for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
|
|
d3d12CommandBuffer->needComputeUniformBufferBind[i] = true;
|
|
}
|
|
|
|
for (Uint32 i = 0; i < pipeline->numUniformBuffers; i += 1) {
|
|
if (d3d12CommandBuffer->computeUniformBuffers[i] == NULL) {
|
|
d3d12CommandBuffer->computeUniformBuffers[i] = D3D12_INTERNAL_AcquireUniformBufferFromPool(
|
|
d3d12CommandBuffer);
|
|
}
|
|
}
|
|
|
|
D3D12_INTERNAL_TrackComputePipeline(d3d12CommandBuffer, pipeline);
|
|
|
|
// Bind write-only resources after setting root signature
|
|
if (pipeline->numWriteOnlyStorageTextures > 0) {
|
|
for (Uint32 i = 0; i < pipeline->numWriteOnlyStorageTextures; i += 1) {
|
|
cpuHandles[i] = d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresources[i]->uavHandle.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
d3d12CommandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
cpuHandles,
|
|
d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresourceCount,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
d3d12CommandBuffer->currentComputePipeline->rootSignature->writeOnlyStorageTextureRootIndex,
|
|
gpuDescriptorHandle);
|
|
}
|
|
|
|
if (pipeline->numWriteOnlyStorageBuffers > 0) {
|
|
for (Uint32 i = 0; i < pipeline->numWriteOnlyStorageBuffers; i += 1) {
|
|
cpuHandles[i] = d3d12CommandBuffer->computeWriteOnlyStorageBuffers[i]->uavDescriptor.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
d3d12CommandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
cpuHandles,
|
|
d3d12CommandBuffer->computeWriteOnlyStorageBufferCount,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
d3d12CommandBuffer->currentComputePipeline->rootSignature->writeOnlyStorageBufferRootIndex,
|
|
gpuDescriptorHandle);
|
|
}
|
|
}
|
|
|
|
static void D3D12_BindComputeSamplers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
const SDL_GPUTextureSamplerBinding *textureSamplerBindings,
|
|
Uint32 numBindings)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < numBindings; i += 1) {
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)textureSamplerBindings[i].texture;
|
|
|
|
D3D12_INTERNAL_TrackSampler(
|
|
d3d12CommandBuffer,
|
|
(D3D12Sampler *)textureSamplerBindings[i].sampler);
|
|
|
|
D3D12_INTERNAL_TrackTexture(
|
|
d3d12CommandBuffer,
|
|
container->activeTexture);
|
|
|
|
d3d12CommandBuffer->computeSamplerTextures[firstSlot + i] = container->activeTexture;
|
|
d3d12CommandBuffer->computeSamplers[firstSlot + i] = (D3D12Sampler *)textureSamplerBindings[i].sampler;
|
|
}
|
|
|
|
d3d12CommandBuffer->needComputeSamplerBind = true;
|
|
}
|
|
|
|
static void D3D12_BindComputeStorageTextures(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUTexture *const *storageTextures,
|
|
Uint32 numBindings)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < numBindings; i += 1) {
|
|
if (d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i] != NULL) {
|
|
D3D12_INTERNAL_TextureTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
|
|
d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i]);
|
|
}
|
|
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)storageTextures[i];
|
|
d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i] = container->activeTexture;
|
|
|
|
D3D12_INTERNAL_TextureTransitionFromDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
|
|
container->activeTexture);
|
|
|
|
D3D12_INTERNAL_TrackTexture(
|
|
d3d12CommandBuffer,
|
|
container->activeTexture);
|
|
}
|
|
|
|
d3d12CommandBuffer->needComputeReadOnlyStorageTextureBind = true;
|
|
}
|
|
|
|
static void D3D12_BindComputeStorageBuffers(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 firstSlot,
|
|
SDL_GPUBuffer *const *storageBuffers,
|
|
Uint32 numBindings)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < numBindings; i += 1) {
|
|
if (d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i] != NULL) {
|
|
D3D12_INTERNAL_BufferTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
|
|
d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i]);
|
|
}
|
|
|
|
D3D12BufferContainer *container = (D3D12BufferContainer *)storageBuffers[i];
|
|
D3D12Buffer *buffer = container->activeBuffer;
|
|
|
|
d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i] = buffer;
|
|
|
|
D3D12_INTERNAL_BufferTransitionFromDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
|
|
buffer);
|
|
|
|
D3D12_INTERNAL_TrackBuffer(
|
|
d3d12CommandBuffer,
|
|
buffer);
|
|
}
|
|
|
|
d3d12CommandBuffer->needComputeReadOnlyStorageBufferBind = true;
|
|
}
|
|
|
|
static void D3D12_PushComputeUniformData(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 slotIndex,
|
|
const void *data,
|
|
Uint32 length)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
D3D12_INTERNAL_PushUniformData(
|
|
d3d12CommandBuffer,
|
|
SDL_GPU_SHADERSTAGE_COMPUTE,
|
|
slotIndex,
|
|
data,
|
|
length);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_BindComputeResources(
|
|
D3D12CommandBuffer *commandBuffer)
|
|
{
|
|
D3D12ComputePipeline *computePipeline = commandBuffer->currentComputePipeline;
|
|
|
|
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandles[MAX_TEXTURE_SAMPLERS_PER_STAGE];
|
|
D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorHandle;
|
|
|
|
if (commandBuffer->needComputeSamplerBind) {
|
|
if (computePipeline->numSamplers > 0) {
|
|
for (Uint32 i = 0; i < computePipeline->numSamplers; i += 1) {
|
|
cpuHandles[i] = commandBuffer->computeSamplers[i]->handle.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
|
|
cpuHandles,
|
|
computePipeline->numSamplers,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(
|
|
commandBuffer->graphicsCommandList,
|
|
computePipeline->rootSignature->samplerRootIndex,
|
|
gpuDescriptorHandle);
|
|
|
|
for (Uint32 i = 0; i < computePipeline->numSamplers; i += 1) {
|
|
cpuHandles[i] = commandBuffer->computeSamplerTextures[i]->srvHandle.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
cpuHandles,
|
|
computePipeline->numSamplers,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(
|
|
commandBuffer->graphicsCommandList,
|
|
computePipeline->rootSignature->samplerTextureRootIndex,
|
|
gpuDescriptorHandle);
|
|
}
|
|
commandBuffer->needComputeSamplerBind = false;
|
|
}
|
|
|
|
if (commandBuffer->needComputeReadOnlyStorageTextureBind) {
|
|
if (computePipeline->numReadOnlyStorageTextures > 0) {
|
|
for (Uint32 i = 0; i < computePipeline->numReadOnlyStorageTextures; i += 1) {
|
|
cpuHandles[i] = commandBuffer->computeReadOnlyStorageTextures[i]->srvHandle.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
cpuHandles,
|
|
computePipeline->numReadOnlyStorageTextures,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(
|
|
commandBuffer->graphicsCommandList,
|
|
computePipeline->rootSignature->readOnlyStorageTextureRootIndex,
|
|
gpuDescriptorHandle);
|
|
}
|
|
commandBuffer->needComputeReadOnlyStorageTextureBind = false;
|
|
}
|
|
|
|
if (commandBuffer->needComputeReadOnlyStorageBufferBind) {
|
|
if (computePipeline->numReadOnlyStorageBuffers > 0) {
|
|
for (Uint32 i = 0; i < computePipeline->numReadOnlyStorageBuffers; i += 1) {
|
|
cpuHandles[i] = commandBuffer->computeReadOnlyStorageBuffers[i]->srvDescriptor.cpuHandle;
|
|
}
|
|
|
|
D3D12_INTERNAL_WriteGPUDescriptors(
|
|
commandBuffer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
cpuHandles,
|
|
computePipeline->numReadOnlyStorageBuffers,
|
|
&gpuDescriptorHandle);
|
|
|
|
ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(
|
|
commandBuffer->graphicsCommandList,
|
|
computePipeline->rootSignature->readOnlyStorageBufferRootIndex,
|
|
gpuDescriptorHandle);
|
|
}
|
|
commandBuffer->needComputeReadOnlyStorageBufferBind = false;
|
|
}
|
|
|
|
for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
|
|
if (commandBuffer->needComputeUniformBufferBind[i]) {
|
|
if (computePipeline->numUniformBuffers > i) {
|
|
ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(
|
|
commandBuffer->graphicsCommandList,
|
|
computePipeline->rootSignature->uniformBufferRootIndex[i],
|
|
commandBuffer->computeUniformBuffers[i]->buffer->virtualAddress + commandBuffer->computeUniformBuffers[i]->drawOffset);
|
|
}
|
|
}
|
|
commandBuffer->needComputeUniformBufferBind[i] = false;
|
|
}
|
|
}
|
|
|
|
static void D3D12_DispatchCompute(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
Uint32 groupcountX,
|
|
Uint32 groupcountY,
|
|
Uint32 groupcountZ)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
D3D12_INTERNAL_BindComputeResources(d3d12CommandBuffer);
|
|
ID3D12GraphicsCommandList_Dispatch(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
groupcountX,
|
|
groupcountY,
|
|
groupcountZ);
|
|
}
|
|
|
|
static void D3D12_DispatchComputeIndirect(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUBuffer *buffer,
|
|
Uint32 offset)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12Buffer *d3d12Buffer = ((D3D12BufferContainer *)buffer)->activeBuffer;
|
|
|
|
D3D12_INTERNAL_BindComputeResources(d3d12CommandBuffer);
|
|
ID3D12GraphicsCommandList_ExecuteIndirect(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
d3d12CommandBuffer->renderer->indirectDispatchCommandSignature,
|
|
1,
|
|
d3d12Buffer->handle,
|
|
offset,
|
|
NULL,
|
|
0);
|
|
}
|
|
|
|
static void D3D12_EndComputePass(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
|
|
for (Uint32 i = 0; i < d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresourceCount; i += 1) {
|
|
if (d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresources[i]) {
|
|
D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
|
d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresources[i]);
|
|
|
|
d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresources[i] = NULL;
|
|
}
|
|
}
|
|
d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresourceCount = 0;
|
|
|
|
for (Uint32 i = 0; i < d3d12CommandBuffer->computeWriteOnlyStorageBufferCount; i += 1) {
|
|
if (d3d12CommandBuffer->computeWriteOnlyStorageBuffers[i]) {
|
|
D3D12_INTERNAL_BufferTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
|
d3d12CommandBuffer->computeWriteOnlyStorageBuffers[i]);
|
|
|
|
d3d12CommandBuffer->computeWriteOnlyStorageBuffers[i] = NULL;
|
|
}
|
|
}
|
|
d3d12CommandBuffer->computeWriteOnlyStorageBufferCount = 0;
|
|
|
|
for (Uint32 i = 0; i < MAX_STORAGE_TEXTURES_PER_STAGE; i += 1) {
|
|
if (d3d12CommandBuffer->computeReadOnlyStorageTextures[i]) {
|
|
D3D12_INTERNAL_TextureTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
|
|
d3d12CommandBuffer->computeReadOnlyStorageTextures[i]);
|
|
|
|
d3d12CommandBuffer->computeReadOnlyStorageTextures[i] = NULL;
|
|
}
|
|
}
|
|
|
|
for (Uint32 i = 0; i < MAX_STORAGE_BUFFERS_PER_STAGE; i += 1) {
|
|
if (d3d12CommandBuffer->computeReadOnlyStorageBuffers[i]) {
|
|
D3D12_INTERNAL_BufferTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
|
|
d3d12CommandBuffer->computeReadOnlyStorageBuffers[i]);
|
|
|
|
d3d12CommandBuffer->computeReadOnlyStorageBuffers[i] = NULL;
|
|
}
|
|
}
|
|
|
|
SDL_zeroa(d3d12CommandBuffer->computeSamplerTextures);
|
|
SDL_zeroa(d3d12CommandBuffer->computeSamplers);
|
|
|
|
d3d12CommandBuffer->currentComputePipeline = NULL;
|
|
}
|
|
|
|
// TransferBuffer Data
|
|
|
|
static void *D3D12_MapTransferBuffer(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTransferBuffer *transferBuffer,
|
|
bool cycle)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12BufferContainer *container = (D3D12BufferContainer *)transferBuffer;
|
|
void *dataPointer;
|
|
|
|
if (
|
|
cycle &&
|
|
SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0) {
|
|
D3D12_INTERNAL_CycleActiveBuffer(
|
|
renderer,
|
|
container);
|
|
}
|
|
|
|
// Upload buffers are persistently mapped, download buffers are not
|
|
if (container->type == D3D12_BUFFER_TYPE_UPLOAD) {
|
|
dataPointer = container->activeBuffer->mapPointer;
|
|
} else {
|
|
ID3D12Resource_Map(
|
|
container->activeBuffer->handle,
|
|
0,
|
|
NULL,
|
|
(void **)&dataPointer);
|
|
}
|
|
|
|
return dataPointer;
|
|
}
|
|
|
|
static void D3D12_UnmapTransferBuffer(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTransferBuffer *transferBuffer)
|
|
{
|
|
(void)driverData;
|
|
D3D12BufferContainer *container = (D3D12BufferContainer *)transferBuffer;
|
|
|
|
// Upload buffers are persistently mapped, download buffers are not
|
|
if (container->type == D3D12_BUFFER_TYPE_DOWNLOAD) {
|
|
ID3D12Resource_Unmap(
|
|
container->activeBuffer->handle,
|
|
0,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
// Copy Pass
|
|
|
|
static void D3D12_BeginCopyPass(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
// no-op
|
|
(void)commandBuffer;
|
|
}
|
|
|
|
static void D3D12_UploadToTexture(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const SDL_GPUTextureTransferInfo *source,
|
|
const SDL_GPUTextureRegion *destination,
|
|
bool cycle)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12BufferContainer *transferBufferContainer = (D3D12BufferContainer *)source->transfer_buffer;
|
|
D3D12Buffer *temporaryBuffer = NULL;
|
|
D3D12_TEXTURE_COPY_LOCATION sourceLocation;
|
|
D3D12_TEXTURE_COPY_LOCATION destinationLocation;
|
|
Uint32 pixelsPerRow = source->pixels_per_row;
|
|
Uint32 rowPitch;
|
|
Uint32 alignedRowPitch;
|
|
Uint32 rowsPerSlice = source->rows_per_layer;
|
|
Uint32 bytesPerSlice;
|
|
bool needsRealignment;
|
|
bool needsPlacementCopy;
|
|
|
|
// Note that the transfer buffer does not need a barrier, as it is synced by the client.
|
|
|
|
D3D12TextureContainer *textureContainer = (D3D12TextureContainer *)destination->texture;
|
|
D3D12TextureSubresource *textureSubresource = D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
d3d12CommandBuffer,
|
|
textureContainer,
|
|
destination->layer,
|
|
destination->mip_level,
|
|
cycle,
|
|
D3D12_RESOURCE_STATE_COPY_DEST);
|
|
|
|
/* D3D12 requires texture data row pitch to be 256 byte aligned, which is obviously insane.
|
|
* Instead of exposing that restriction to the client, which is a huge rake to step on,
|
|
* and a restriction that no other backend requires, we're going to copy data to a temporary buffer,
|
|
* copy THAT data to the texture, and then get rid of the temporary buffer ASAP.
|
|
* If we're lucky and the row pitch and depth pitch are already aligned, we can skip all of that.
|
|
*
|
|
* D3D12 also requires offsets to be 512 byte aligned. We'll fix that for the client and warn them as well.
|
|
*
|
|
* And just for some extra fun, D3D12 doesn't actually support depth pitch, so we have to realign that too!
|
|
*/
|
|
|
|
if (pixelsPerRow == 0) {
|
|
pixelsPerRow = destination->w;
|
|
}
|
|
|
|
rowPitch = BytesPerRow(pixelsPerRow, textureContainer->header.info.format);
|
|
|
|
if (rowsPerSlice == 0) {
|
|
rowsPerSlice = destination->h;
|
|
}
|
|
|
|
bytesPerSlice = rowsPerSlice * rowPitch;
|
|
|
|
alignedRowPitch = D3D12_INTERNAL_Align(rowPitch, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
|
needsRealignment = rowsPerSlice != destination->h || rowPitch != alignedRowPitch;
|
|
needsPlacementCopy = source->offset % D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT != 0;
|
|
|
|
sourceLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
|
sourceLocation.PlacedFootprint.Footprint.Format = SDLToD3D12_TextureFormat[textureContainer->header.info.format];
|
|
sourceLocation.PlacedFootprint.Footprint.RowPitch = alignedRowPitch;
|
|
|
|
destinationLocation.pResource = textureContainer->activeTexture->resource;
|
|
destinationLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
|
destinationLocation.SubresourceIndex = textureSubresource->index;
|
|
|
|
if (needsRealignment) {
|
|
temporaryBuffer = D3D12_INTERNAL_CreateBuffer(
|
|
d3d12CommandBuffer->renderer,
|
|
0,
|
|
alignedRowPitch * destination->h * destination->d,
|
|
D3D12_BUFFER_TYPE_UPLOAD);
|
|
|
|
if (!temporaryBuffer) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create temporary upload buffer.");
|
|
return;
|
|
}
|
|
|
|
sourceLocation.pResource = temporaryBuffer->handle;
|
|
|
|
for (Uint32 sliceIndex = 0; sliceIndex < destination->d; sliceIndex += 1) {
|
|
// copy row count minus one to avoid overread
|
|
for (Uint32 rowIndex = 0; rowIndex < rowsPerSlice - 1; rowIndex += 1) {
|
|
SDL_memcpy(
|
|
temporaryBuffer->mapPointer + (sliceIndex * rowsPerSlice) + (rowIndex * alignedRowPitch),
|
|
transferBufferContainer->activeBuffer->mapPointer + source->offset + (sliceIndex * bytesPerSlice) + (rowIndex * rowPitch),
|
|
alignedRowPitch);
|
|
}
|
|
Uint32 offset = source->offset + (sliceIndex * bytesPerSlice) + ((rowsPerSlice - 1) * rowPitch);
|
|
SDL_memcpy(
|
|
temporaryBuffer->mapPointer + (sliceIndex * rowsPerSlice) + ((rowsPerSlice - 1) * alignedRowPitch),
|
|
transferBufferContainer->activeBuffer->mapPointer + offset,
|
|
SDL_min(alignedRowPitch, transferBufferContainer->size - offset));
|
|
|
|
sourceLocation.PlacedFootprint.Footprint.Width = destination->w;
|
|
sourceLocation.PlacedFootprint.Footprint.Height = rowsPerSlice;
|
|
sourceLocation.PlacedFootprint.Footprint.Depth = 1;
|
|
sourceLocation.PlacedFootprint.Offset = (sliceIndex * bytesPerSlice);
|
|
|
|
ID3D12GraphicsCommandList_CopyTextureRegion(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
&destinationLocation,
|
|
destination->x,
|
|
destination->y,
|
|
sliceIndex,
|
|
&sourceLocation,
|
|
NULL);
|
|
}
|
|
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, temporaryBuffer);
|
|
D3D12_INTERNAL_ReleaseBuffer(
|
|
d3d12CommandBuffer->renderer,
|
|
temporaryBuffer);
|
|
} else if (needsPlacementCopy) {
|
|
temporaryBuffer = D3D12_INTERNAL_CreateBuffer(
|
|
d3d12CommandBuffer->renderer,
|
|
0,
|
|
alignedRowPitch * destination->h * destination->d,
|
|
D3D12_BUFFER_TYPE_UPLOAD);
|
|
|
|
if (!temporaryBuffer) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create temporary upload buffer.");
|
|
return;
|
|
}
|
|
|
|
SDL_memcpy(
|
|
temporaryBuffer->mapPointer,
|
|
transferBufferContainer->activeBuffer->mapPointer + source->offset,
|
|
alignedRowPitch * destination->h * destination->d);
|
|
|
|
sourceLocation.pResource = temporaryBuffer->handle;
|
|
sourceLocation.PlacedFootprint.Offset = 0;
|
|
sourceLocation.PlacedFootprint.Footprint.Width = destination->w;
|
|
sourceLocation.PlacedFootprint.Footprint.Height = destination->h;
|
|
sourceLocation.PlacedFootprint.Footprint.Depth = 1;
|
|
|
|
ID3D12GraphicsCommandList_CopyTextureRegion(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
&destinationLocation,
|
|
destination->x,
|
|
destination->y,
|
|
destination->z,
|
|
&sourceLocation,
|
|
NULL);
|
|
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, temporaryBuffer);
|
|
D3D12_INTERNAL_ReleaseBuffer(
|
|
d3d12CommandBuffer->renderer,
|
|
temporaryBuffer);
|
|
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Texture upload offset not aligned to 512 bytes! This is suboptimal on D3D12!");
|
|
} else {
|
|
sourceLocation.pResource = transferBufferContainer->activeBuffer->handle;
|
|
sourceLocation.PlacedFootprint.Offset = source->offset;
|
|
sourceLocation.PlacedFootprint.Footprint.Width = destination->w;
|
|
sourceLocation.PlacedFootprint.Footprint.Height = destination->h;
|
|
sourceLocation.PlacedFootprint.Footprint.Depth = destination->d;
|
|
|
|
ID3D12GraphicsCommandList_CopyTextureRegion(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
&destinationLocation,
|
|
destination->x,
|
|
destination->y,
|
|
destination->z,
|
|
&sourceLocation,
|
|
NULL);
|
|
}
|
|
|
|
D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
|
textureSubresource);
|
|
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, transferBufferContainer->activeBuffer);
|
|
D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, textureSubresource->parent);
|
|
}
|
|
|
|
static void D3D12_UploadToBuffer(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const SDL_GPUTransferBufferLocation *source,
|
|
const SDL_GPUBufferRegion *destination,
|
|
bool cycle)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12BufferContainer *transferBufferContainer = (D3D12BufferContainer *)source->transfer_buffer;
|
|
D3D12BufferContainer *bufferContainer = (D3D12BufferContainer *)destination->buffer;
|
|
|
|
// The transfer buffer does not need a barrier, it is synced by the client.
|
|
|
|
D3D12Buffer *buffer = D3D12_INTERNAL_PrepareBufferForWrite(
|
|
d3d12CommandBuffer,
|
|
bufferContainer,
|
|
cycle,
|
|
D3D12_RESOURCE_STATE_COPY_DEST);
|
|
|
|
ID3D12GraphicsCommandList_CopyBufferRegion(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
buffer->handle,
|
|
destination->offset,
|
|
transferBufferContainer->activeBuffer->handle,
|
|
source->offset,
|
|
destination->size);
|
|
|
|
D3D12_INTERNAL_BufferTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
|
buffer);
|
|
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, transferBufferContainer->activeBuffer);
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, buffer);
|
|
}
|
|
|
|
static void D3D12_CopyTextureToTexture(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const SDL_GPUTextureLocation *source,
|
|
const SDL_GPUTextureLocation *destination,
|
|
Uint32 w,
|
|
Uint32 h,
|
|
Uint32 d,
|
|
bool cycle)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12_TEXTURE_COPY_LOCATION sourceLocation;
|
|
D3D12_TEXTURE_COPY_LOCATION destinationLocation;
|
|
|
|
D3D12TextureSubresource *sourceSubresource = D3D12_INTERNAL_FetchTextureSubresource(
|
|
(D3D12TextureContainer *)source->texture,
|
|
source->layer,
|
|
source->mip_level);
|
|
|
|
D3D12TextureSubresource *destinationSubresource = D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
|
|
d3d12CommandBuffer,
|
|
(D3D12TextureContainer *)destination->texture,
|
|
destination->layer,
|
|
destination->mip_level,
|
|
cycle,
|
|
D3D12_RESOURCE_STATE_COPY_DEST);
|
|
|
|
D3D12_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE,
|
|
sourceSubresource);
|
|
|
|
sourceLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
|
sourceLocation.SubresourceIndex = sourceSubresource->index;
|
|
sourceLocation.pResource = sourceSubresource->parent->resource;
|
|
|
|
destinationLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
|
destinationLocation.SubresourceIndex = destinationSubresource->index;
|
|
destinationLocation.pResource = destinationSubresource->parent->resource;
|
|
|
|
D3D12_BOX sourceBox = { source->x, source->y, source->z, source->x + w, source->y + h, source->z + d };
|
|
|
|
ID3D12GraphicsCommandList_CopyTextureRegion(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
&destinationLocation,
|
|
destination->x,
|
|
destination->y,
|
|
destination->z,
|
|
&sourceLocation,
|
|
&sourceBox);
|
|
|
|
D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE,
|
|
sourceSubresource);
|
|
|
|
D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
|
destinationSubresource);
|
|
|
|
D3D12_INTERNAL_TrackTexture(
|
|
d3d12CommandBuffer,
|
|
sourceSubresource->parent);
|
|
|
|
D3D12_INTERNAL_TrackTexture(
|
|
d3d12CommandBuffer,
|
|
destinationSubresource->parent);
|
|
}
|
|
|
|
static void D3D12_CopyBufferToBuffer(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const SDL_GPUBufferLocation *source,
|
|
const SDL_GPUBufferLocation *destination,
|
|
Uint32 size,
|
|
bool cycle)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12BufferContainer *sourceContainer = (D3D12BufferContainer *)source->buffer;
|
|
D3D12BufferContainer *destinationContainer = (D3D12BufferContainer *)destination->buffer;
|
|
|
|
D3D12Buffer *sourceBuffer = sourceContainer->activeBuffer;
|
|
D3D12Buffer *destinationBuffer = D3D12_INTERNAL_PrepareBufferForWrite(
|
|
d3d12CommandBuffer,
|
|
destinationContainer,
|
|
cycle,
|
|
D3D12_RESOURCE_STATE_COPY_DEST);
|
|
|
|
D3D12_INTERNAL_BufferTransitionFromDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE,
|
|
sourceBuffer);
|
|
|
|
ID3D12GraphicsCommandList_CopyBufferRegion(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
destinationBuffer->handle,
|
|
destination->offset,
|
|
sourceBuffer->handle,
|
|
source->offset,
|
|
size);
|
|
|
|
D3D12_INTERNAL_BufferTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE,
|
|
sourceBuffer);
|
|
|
|
D3D12_INTERNAL_BufferTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
|
destinationBuffer);
|
|
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, sourceBuffer);
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, destinationBuffer);
|
|
}
|
|
|
|
static void D3D12_DownloadFromTexture(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const SDL_GPUTextureRegion *source,
|
|
const SDL_GPUTextureTransferInfo *destination)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12_TEXTURE_COPY_LOCATION sourceLocation;
|
|
D3D12_TEXTURE_COPY_LOCATION destinationLocation;
|
|
Uint32 pixelsPerRow = destination->pixels_per_row;
|
|
Uint32 rowPitch;
|
|
Uint32 alignedRowPitch;
|
|
Uint32 rowsPerSlice = destination->rows_per_layer;
|
|
bool needsRealignment;
|
|
bool needsPlacementCopy;
|
|
D3D12TextureDownload *textureDownload = NULL;
|
|
D3D12TextureContainer *sourceContainer = (D3D12TextureContainer *)source->texture;
|
|
D3D12TextureSubresource *sourceSubresource = D3D12_INTERNAL_FetchTextureSubresource(
|
|
sourceContainer,
|
|
source->layer,
|
|
source->mip_level);
|
|
D3D12BufferContainer *destinationContainer = (D3D12BufferContainer *)destination->transfer_buffer;
|
|
D3D12Buffer *destinationBuffer = destinationContainer->activeBuffer;
|
|
|
|
/* D3D12 requires texture data row pitch to be 256 byte aligned, which is obviously insane.
|
|
* Instead of exposing that restriction to the client, which is a huge rake to step on,
|
|
* and a restriction that no other backend requires, we're going to copy data to a temporary buffer,
|
|
* copy THAT data to the texture, and then get rid of the temporary buffer ASAP.
|
|
* If we're lucky and the row pitch and depth pitch are already aligned, we can skip all of that.
|
|
*
|
|
* D3D12 also requires offsets to be 512 byte aligned. We'll fix that for the client and warn them as well.
|
|
*
|
|
* And just for some extra fun, D3D12 doesn't actually support depth pitch, so we have to realign that too!
|
|
*
|
|
* Since this is an async download we have to do all these fixups after the command is finished,
|
|
* so we'll cache the metadata similar to D3D11 and map and copy it when the command buffer is cleaned.
|
|
*/
|
|
|
|
if (pixelsPerRow == 0) {
|
|
pixelsPerRow = source->w;
|
|
}
|
|
|
|
rowPitch = BytesPerRow(pixelsPerRow, sourceContainer->header.info.format);
|
|
|
|
if (rowsPerSlice == 0) {
|
|
rowsPerSlice = source->h;
|
|
}
|
|
|
|
alignedRowPitch = D3D12_INTERNAL_Align(rowPitch, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
|
needsRealignment = rowsPerSlice != source->h || rowPitch != alignedRowPitch;
|
|
needsPlacementCopy = destination->offset % D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT != 0;
|
|
|
|
sourceLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
|
sourceLocation.SubresourceIndex = sourceSubresource->index;
|
|
sourceLocation.pResource = sourceSubresource->parent->resource;
|
|
|
|
D3D12_BOX sourceBox = { source->x, source->y, source->z, source->x + source->w, source->y + rowsPerSlice, source->z + source->d };
|
|
|
|
destinationLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
|
destinationLocation.PlacedFootprint.Footprint.Format = SDLToD3D12_TextureFormat[sourceContainer->header.info.format];
|
|
destinationLocation.PlacedFootprint.Footprint.Width = source->w;
|
|
destinationLocation.PlacedFootprint.Footprint.Height = rowsPerSlice;
|
|
destinationLocation.PlacedFootprint.Footprint.Depth = source->d;
|
|
destinationLocation.PlacedFootprint.Footprint.RowPitch = alignedRowPitch;
|
|
|
|
if (needsRealignment || needsPlacementCopy) {
|
|
textureDownload = (D3D12TextureDownload *)SDL_malloc(sizeof(D3D12TextureDownload));
|
|
|
|
if (!textureDownload) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create texture download structure!");
|
|
return;
|
|
}
|
|
|
|
textureDownload->temporaryBuffer = D3D12_INTERNAL_CreateBuffer(
|
|
d3d12CommandBuffer->renderer,
|
|
0,
|
|
alignedRowPitch * rowsPerSlice * source->d,
|
|
D3D12_BUFFER_TYPE_DOWNLOAD);
|
|
|
|
if (!textureDownload->temporaryBuffer) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create temporary download buffer!");
|
|
SDL_free(textureDownload);
|
|
return;
|
|
}
|
|
|
|
textureDownload->destinationBuffer = destinationBuffer;
|
|
textureDownload->bufferOffset = destination->offset;
|
|
textureDownload->width = source->w;
|
|
textureDownload->height = rowsPerSlice;
|
|
textureDownload->depth = source->d;
|
|
textureDownload->bytesPerRow = rowPitch;
|
|
textureDownload->bytesPerDepthSlice = rowPitch * rowsPerSlice;
|
|
textureDownload->alignedBytesPerRow = alignedRowPitch;
|
|
|
|
destinationLocation.pResource = textureDownload->temporaryBuffer->handle;
|
|
destinationLocation.PlacedFootprint.Offset = 0;
|
|
} else {
|
|
destinationLocation.pResource = destinationBuffer->handle;
|
|
destinationLocation.PlacedFootprint.Offset = destination->offset;
|
|
}
|
|
|
|
D3D12_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE,
|
|
sourceSubresource);
|
|
|
|
ID3D12GraphicsCommandList_CopyTextureRegion(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
&destinationLocation,
|
|
0,
|
|
0,
|
|
0,
|
|
&sourceLocation,
|
|
&sourceBox);
|
|
|
|
D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE,
|
|
sourceSubresource);
|
|
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, destinationBuffer);
|
|
D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, sourceSubresource->parent);
|
|
|
|
if (textureDownload != NULL) {
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, textureDownload->temporaryBuffer);
|
|
|
|
if (d3d12CommandBuffer->textureDownloadCount >= d3d12CommandBuffer->textureDownloadCapacity) {
|
|
d3d12CommandBuffer->textureDownloadCapacity *= 2;
|
|
d3d12CommandBuffer->textureDownloads = (D3D12TextureDownload **)SDL_realloc(
|
|
d3d12CommandBuffer->textureDownloads,
|
|
d3d12CommandBuffer->textureDownloadCapacity * sizeof(D3D12TextureDownload *));
|
|
}
|
|
|
|
d3d12CommandBuffer->textureDownloads[d3d12CommandBuffer->textureDownloadCount] = textureDownload;
|
|
d3d12CommandBuffer->textureDownloadCount += 1;
|
|
|
|
D3D12_INTERNAL_ReleaseBuffer(d3d12CommandBuffer->renderer, textureDownload->temporaryBuffer);
|
|
}
|
|
}
|
|
|
|
static void D3D12_DownloadFromBuffer(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const SDL_GPUBufferRegion *source,
|
|
const SDL_GPUTransferBufferLocation *destination)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12BufferContainer *sourceContainer = (D3D12BufferContainer *)source->buffer;
|
|
D3D12BufferContainer *destinationContainer = (D3D12BufferContainer *)destination->transfer_buffer;
|
|
|
|
D3D12Buffer *sourceBuffer = sourceContainer->activeBuffer;
|
|
D3D12_INTERNAL_BufferTransitionFromDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE,
|
|
sourceBuffer);
|
|
|
|
D3D12Buffer *destinationBuffer = destinationContainer->activeBuffer;
|
|
|
|
ID3D12GraphicsCommandList_CopyBufferRegion(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
destinationBuffer->handle,
|
|
destination->offset,
|
|
sourceBuffer->handle,
|
|
source->offset,
|
|
source->size);
|
|
|
|
D3D12_INTERNAL_BufferTransitionToDefaultUsage(
|
|
d3d12CommandBuffer,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE,
|
|
sourceBuffer);
|
|
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, sourceBuffer);
|
|
D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, destinationBuffer);
|
|
}
|
|
|
|
static void D3D12_EndCopyPass(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
// no-op
|
|
(void)commandBuffer;
|
|
}
|
|
|
|
static void D3D12_GenerateMipmaps(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_GPUTexture *texture)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12Renderer *renderer = d3d12CommandBuffer->renderer;
|
|
D3D12TextureContainer *container = (D3D12TextureContainer *)texture;
|
|
SDL_GPUGraphicsPipeline *blitPipeline;
|
|
|
|
blitPipeline = SDL_GPU_FetchBlitPipeline(
|
|
renderer->sdlGPUDevice,
|
|
container->header.info.type,
|
|
container->header.info.format,
|
|
renderer->blitVertexShader,
|
|
renderer->blitFrom2DShader,
|
|
renderer->blitFrom2DArrayShader,
|
|
renderer->blitFrom3DShader,
|
|
renderer->blitFromCubeShader,
|
|
renderer->blitFromCubeArrayShader,
|
|
&renderer->blitPipelines,
|
|
&renderer->blitPipelineCount,
|
|
&renderer->blitPipelineCapacity);
|
|
|
|
if (blitPipeline == NULL) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not fetch blit pipeline");
|
|
return;
|
|
}
|
|
|
|
// We have to do this one subresource at a time
|
|
for (Uint32 layerOrDepthIndex = 0; layerOrDepthIndex < container->header.info.layer_count_or_depth; layerOrDepthIndex += 1) {
|
|
for (Uint32 levelIndex = 1; levelIndex < container->header.info.num_levels; levelIndex += 1) {
|
|
SDL_GPUBlitInfo blitInfo;
|
|
SDL_zero(blitInfo);
|
|
|
|
blitInfo.source.texture = texture;
|
|
blitInfo.source.mip_level = levelIndex - 1;
|
|
blitInfo.source.layer_or_depth_plane = layerOrDepthIndex;
|
|
blitInfo.source.x = 0;
|
|
blitInfo.source.y = 0;
|
|
blitInfo.source.w = container->header.info.width >> (levelIndex - 1);
|
|
blitInfo.source.h = container->header.info.height >> (levelIndex - 1);
|
|
|
|
blitInfo.destination.texture = texture;
|
|
blitInfo.destination.mip_level = levelIndex;
|
|
blitInfo.destination.layer_or_depth_plane = layerOrDepthIndex;
|
|
blitInfo.destination.x = 0;
|
|
blitInfo.destination.y = 0;
|
|
blitInfo.destination.w = container->header.info.width >> levelIndex;
|
|
blitInfo.destination.h = container->header.info.height >> levelIndex;
|
|
|
|
blitInfo.load_op = SDL_GPU_LOADOP_DONT_CARE;
|
|
blitInfo.filter = SDL_GPU_FILTER_LINEAR;
|
|
|
|
SDL_BlitGPUTexture(
|
|
commandBuffer,
|
|
&blitInfo);
|
|
}
|
|
}
|
|
|
|
D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, container->activeTexture);
|
|
}
|
|
|
|
static void D3D12_Blit(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
const SDL_GPUBlitInfo *info)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12Renderer *renderer = (D3D12Renderer *)d3d12CommandBuffer->renderer;
|
|
|
|
SDL_GPU_BlitCommon(
|
|
commandBuffer,
|
|
info,
|
|
renderer->blitLinearSampler,
|
|
renderer->blitNearestSampler,
|
|
renderer->blitVertexShader,
|
|
renderer->blitFrom2DShader,
|
|
renderer->blitFrom2DArrayShader,
|
|
renderer->blitFrom3DShader,
|
|
renderer->blitFromCubeShader,
|
|
renderer->blitFromCubeArrayShader,
|
|
&renderer->blitPipelines,
|
|
&renderer->blitPipelineCount,
|
|
&renderer->blitPipelineCapacity);
|
|
}
|
|
|
|
// Submission/Presentation
|
|
|
|
static D3D12WindowData *D3D12_INTERNAL_FetchWindowData(
|
|
SDL_Window *window)
|
|
{
|
|
SDL_PropertiesID properties = SDL_GetWindowProperties(window);
|
|
return (D3D12WindowData *)SDL_GetPointerProperty(properties, WINDOW_PROPERTY_DATA, NULL);
|
|
}
|
|
|
|
static bool D3D12_SupportsSwapchainComposition(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_Window *window,
|
|
SDL_GPUSwapchainComposition swapchainComposition)
|
|
{
|
|
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|
|
// FIXME: HDR support would be nice to add, but it seems complicated...
|
|
return swapchainComposition == SDL_GPU_SWAPCHAINCOMPOSITION_SDR ||
|
|
swapchainComposition == SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR;
|
|
#else
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
DXGI_FORMAT format;
|
|
D3D12_FEATURE_DATA_FORMAT_SUPPORT formatSupport;
|
|
Uint32 colorSpaceSupport;
|
|
HRESULT res;
|
|
|
|
format = SwapchainCompositionToTextureFormat[swapchainComposition];
|
|
|
|
formatSupport.Format = format;
|
|
res = ID3D12Device_CheckFeatureSupport(
|
|
renderer->device,
|
|
D3D12_FEATURE_FORMAT_SUPPORT,
|
|
&formatSupport,
|
|
sizeof(formatSupport));
|
|
if (FAILED(res)) {
|
|
// Format is apparently unknown
|
|
return false;
|
|
}
|
|
|
|
if (!(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_DISPLAY)) {
|
|
return false;
|
|
}
|
|
|
|
D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window);
|
|
if (windowData == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Must claim window before querying swapchain composition support!");
|
|
return false;
|
|
}
|
|
|
|
// Check the color space support if necessary
|
|
if (swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_SDR) {
|
|
IDXGISwapChain3_CheckColorSpaceSupport(
|
|
windowData->swapchain,
|
|
SwapchainCompositionToColorSpace[swapchainComposition],
|
|
&colorSpaceSupport);
|
|
|
|
if (!(colorSpaceSupport & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) {
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool D3D12_SupportsPresentMode(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_Window *window,
|
|
SDL_GPUPresentMode presentMode)
|
|
{
|
|
(void)driverData;
|
|
(void)window;
|
|
|
|
switch (presentMode) {
|
|
case SDL_GPU_PRESENTMODE_IMMEDIATE:
|
|
case SDL_GPU_PRESENTMODE_VSYNC:
|
|
return true;
|
|
case SDL_GPU_PRESENTMODE_MAILBOX:
|
|
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
return false;
|
|
#else
|
|
return true;
|
|
#endif
|
|
default:
|
|
SDL_assert(!"Unrecognized present mode");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|
|
static bool D3D12_INTERNAL_CreateSwapchain(
|
|
D3D12Renderer *renderer,
|
|
D3D12WindowData *windowData,
|
|
SDL_GPUSwapchainComposition swapchain_composition,
|
|
SDL_GPUPresentMode present_mode)
|
|
{
|
|
int width, height;
|
|
SDL_GPUTextureCreateInfo createInfo;
|
|
D3D12Texture *texture;
|
|
|
|
// Get the swapchain size
|
|
SDL_GetWindowSize(windowData->window, &width, &height);
|
|
|
|
// Create the swapchain textures
|
|
SDL_zero(createInfo);
|
|
createInfo.type = SDL_GPU_TEXTURETYPE_2D;
|
|
createInfo.width = width;
|
|
createInfo.height = height;
|
|
createInfo.format = SwapchainCompositionToSDLTextureFormat[swapchain_composition];
|
|
createInfo.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
|
|
createInfo.layer_count_or_depth = 1;
|
|
createInfo.num_levels = 1;
|
|
|
|
for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
|
|
texture = D3D12_INTERNAL_CreateTexture(renderer, &createInfo, true);
|
|
texture->container = &windowData->textureContainers[i];
|
|
windowData->textureContainers[i].activeTexture = texture;
|
|
windowData->textureContainers[i].canBeCycled = false;
|
|
windowData->textureContainers[i].header.info = createInfo;
|
|
windowData->textureContainers[i].textureCapacity = 1;
|
|
windowData->textureContainers[i].textureCount = 1;
|
|
windowData->textureContainers[i].textures = &windowData->textureContainers[i].activeTexture;
|
|
}
|
|
|
|
// Initialize the swapchain data
|
|
windowData->present_mode = present_mode;
|
|
windowData->swapchainComposition = swapchain_composition;
|
|
windowData->swapchainColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
|
|
windowData->frameCounter = 0;
|
|
windowData->swapchainWidth = width;
|
|
windowData->swapchainHeight = height;
|
|
|
|
// Precache blit pipelines for the swapchain format
|
|
for (Uint32 i = 0; i < 5; i += 1) {
|
|
SDL_GPU_FetchBlitPipeline(
|
|
renderer->sdlGPUDevice,
|
|
(SDL_GPUTextureType)i,
|
|
createInfo.format,
|
|
renderer->blitVertexShader,
|
|
renderer->blitFrom2DShader,
|
|
renderer->blitFrom2DArrayShader,
|
|
renderer->blitFrom3DShader,
|
|
renderer->blitFromCubeShader,
|
|
&renderer->blitPipelines,
|
|
&renderer->blitPipelineCount,
|
|
&renderer->blitPipelineCapacity);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroySwapchain(
|
|
D3D12Renderer *renderer,
|
|
D3D12WindowData *windowData)
|
|
{
|
|
renderer->commandQueue->PresentX(0, NULL, NULL);
|
|
for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
|
|
D3D12_INTERNAL_DestroyTexture(
|
|
renderer,
|
|
windowData->textureContainers[i].activeTexture);
|
|
}
|
|
}
|
|
|
|
static bool D3D12_INTERNAL_ResizeSwapchainIfNeeded(
|
|
D3D12Renderer *renderer,
|
|
D3D12WindowData *windowData)
|
|
{
|
|
int w, h;
|
|
SDL_GetWindowSize(windowData->window, &w, &h);
|
|
|
|
if (w != windowData->swapchainWidth || h != windowData->swapchainHeight) {
|
|
// Wait so we don't release in-flight views
|
|
D3D12_Wait((SDL_GPURenderer *)renderer);
|
|
|
|
// Present a black screen
|
|
renderer->commandQueue->PresentX(0, NULL, NULL);
|
|
|
|
// Clean up the previous swapchain textures
|
|
for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
|
|
D3D12_INTERNAL_DestroyTexture(
|
|
renderer,
|
|
windowData->textureContainers[i].activeTexture);
|
|
}
|
|
|
|
// Create a new swapchain
|
|
D3D12_INTERNAL_CreateSwapchain(
|
|
renderer,
|
|
windowData,
|
|
windowData->swapchainComposition,
|
|
windowData->present_mode);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#else
|
|
static bool D3D12_INTERNAL_InitializeSwapchainTexture(
|
|
D3D12Renderer *renderer,
|
|
IDXGISwapChain3 *swapchain,
|
|
SDL_GPUSwapchainComposition composition,
|
|
Uint32 index,
|
|
D3D12TextureContainer *pTextureContainer)
|
|
{
|
|
D3D12Texture *pTexture;
|
|
ID3D12Resource *swapchainTexture;
|
|
D3D12_RESOURCE_DESC textureDesc;
|
|
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
|
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc;
|
|
DXGI_FORMAT swapchainFormat = SwapchainCompositionToTextureFormat[composition];
|
|
HRESULT res;
|
|
|
|
res = IDXGISwapChain_GetBuffer(
|
|
swapchain,
|
|
index,
|
|
D3D_GUID(D3D_IID_ID3D12Resource),
|
|
(void **)&swapchainTexture);
|
|
ERROR_CHECK_RETURN("Could not get buffer from swapchain!", 0);
|
|
|
|
pTexture = (D3D12Texture *)SDL_calloc(1, sizeof(D3D12Texture));
|
|
if (!pTexture) {
|
|
ID3D12Resource_Release(swapchainTexture);
|
|
return false;
|
|
}
|
|
pTexture->resource = NULL; // This will be set in AcquireSwapchainTexture
|
|
SDL_AtomicSet(&pTexture->referenceCount, 0);
|
|
pTexture->subresourceCount = 1;
|
|
pTexture->subresources = (D3D12TextureSubresource *)SDL_calloc(1, sizeof(D3D12TextureSubresource));
|
|
if (!pTexture->subresources) {
|
|
SDL_free(pTexture);
|
|
ID3D12Resource_Release(swapchainTexture);
|
|
return false;
|
|
}
|
|
pTexture->subresources[0].rtvHandles = SDL_calloc(1, sizeof(D3D12CPUDescriptor));
|
|
pTexture->subresources[0].uavHandle.heap = NULL;
|
|
pTexture->subresources[0].dsvHandle.heap = NULL;
|
|
pTexture->subresources[0].parent = pTexture;
|
|
pTexture->subresources[0].index = 0;
|
|
pTexture->subresources[0].layer = 0;
|
|
pTexture->subresources[0].depth = 1;
|
|
pTexture->subresources[0].level = 0;
|
|
|
|
ID3D12Resource_GetDesc(swapchainTexture, &textureDesc);
|
|
pTextureContainer->header.info.width = (Uint32)textureDesc.Width;
|
|
pTextureContainer->header.info.height = (Uint32)textureDesc.Height;
|
|
pTextureContainer->header.info.layer_count_or_depth = 1;
|
|
pTextureContainer->header.info.num_levels = 1;
|
|
pTextureContainer->header.info.type = SDL_GPU_TEXTURETYPE_2D;
|
|
pTextureContainer->header.info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
|
|
pTextureContainer->header.info.sample_count = SDL_GPU_SAMPLECOUNT_1;
|
|
pTextureContainer->header.info.format = SwapchainCompositionToSDLTextureFormat[composition];
|
|
|
|
pTextureContainer->debugName = NULL;
|
|
pTextureContainer->textures = (D3D12Texture **)SDL_calloc(1, sizeof(D3D12Texture *));
|
|
if (!pTextureContainer->textures) {
|
|
SDL_free(pTexture->subresources);
|
|
SDL_free(pTexture);
|
|
ID3D12Resource_Release(swapchainTexture);
|
|
return false;
|
|
}
|
|
|
|
pTextureContainer->textureCapacity = 1;
|
|
pTextureContainer->textureCount = 1;
|
|
pTextureContainer->textures[0] = pTexture;
|
|
pTextureContainer->activeTexture = pTexture;
|
|
pTextureContainer->canBeCycled = false;
|
|
|
|
pTexture->container = pTextureContainer;
|
|
pTexture->containerIndex = 0;
|
|
|
|
// Create the SRV for the swapchain
|
|
D3D12_INTERNAL_AssignCpuDescriptorHandle(
|
|
renderer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
&pTexture->srvHandle);
|
|
|
|
srvDesc.Format = SwapchainCompositionToTextureFormat[composition];
|
|
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
|
srvDesc.Texture2D.MipLevels = 1;
|
|
srvDesc.Texture2D.MostDetailedMip = 0;
|
|
srvDesc.Texture2D.ResourceMinLODClamp = 0;
|
|
srvDesc.Texture2D.PlaneSlice = 0;
|
|
|
|
ID3D12Device_CreateShaderResourceView(
|
|
renderer->device,
|
|
swapchainTexture,
|
|
&srvDesc,
|
|
pTexture->srvHandle.cpuHandle);
|
|
|
|
// Create the RTV for the swapchain
|
|
D3D12_INTERNAL_AssignCpuDescriptorHandle(
|
|
renderer,
|
|
D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
|
|
&pTexture->subresources[0].rtvHandles[0]);
|
|
|
|
rtvDesc.Format = (composition == SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR) ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : swapchainFormat;
|
|
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
|
|
rtvDesc.Texture2D.MipSlice = 0;
|
|
rtvDesc.Texture2D.PlaneSlice = 0;
|
|
|
|
ID3D12Device_CreateRenderTargetView(
|
|
renderer->device,
|
|
swapchainTexture,
|
|
&rtvDesc,
|
|
pTexture->subresources[0].rtvHandles[0].cpuHandle);
|
|
|
|
ID3D12Resource_Release(swapchainTexture);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool D3D12_INTERNAL_ResizeSwapchainIfNeeded(
|
|
D3D12Renderer *renderer,
|
|
D3D12WindowData *windowData)
|
|
{
|
|
DXGI_SWAP_CHAIN_DESC swapchainDesc;
|
|
int w, h;
|
|
|
|
IDXGISwapChain_GetDesc(windowData->swapchain, &swapchainDesc);
|
|
SDL_GetWindowSize(windowData->window, &w, &h);
|
|
|
|
if ((UINT)w != swapchainDesc.BufferDesc.Width || (UINT)h != swapchainDesc.BufferDesc.Height) {
|
|
// Wait so we don't release in-flight views
|
|
D3D12_Wait((SDL_GPURenderer *)renderer);
|
|
|
|
// Release views and clean up
|
|
for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
|
|
D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
renderer,
|
|
&windowData->textureContainers[i].activeTexture->srvHandle);
|
|
D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
renderer,
|
|
&windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles[0]);
|
|
|
|
SDL_free(windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles);
|
|
SDL_free(windowData->textureContainers[i].activeTexture->subresources);
|
|
SDL_free(windowData->textureContainers[i].activeTexture);
|
|
SDL_free(windowData->textureContainers[i].textures);
|
|
}
|
|
|
|
// Resize the swapchain
|
|
HRESULT res = IDXGISwapChain_ResizeBuffers(
|
|
windowData->swapchain,
|
|
0, // Keep buffer count the same
|
|
w,
|
|
h,
|
|
DXGI_FORMAT_UNKNOWN, // Keep the old format
|
|
renderer->supportsTearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
|
ERROR_CHECK_RETURN("Could not resize swapchain buffers", 0)
|
|
|
|
// Create texture object for the swapchain
|
|
for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
|
|
if (!D3D12_INTERNAL_InitializeSwapchainTexture(
|
|
renderer,
|
|
windowData->swapchain,
|
|
windowData->swapchainComposition,
|
|
i,
|
|
&windowData->textureContainers[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void D3D12_INTERNAL_DestroySwapchain(
|
|
D3D12Renderer *renderer,
|
|
D3D12WindowData *windowData)
|
|
{
|
|
// Release views and clean up
|
|
for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
|
|
D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
renderer,
|
|
&windowData->textureContainers[i].activeTexture->srvHandle);
|
|
D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
|
|
renderer,
|
|
&windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles[0]);
|
|
|
|
SDL_free(windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles);
|
|
SDL_free(windowData->textureContainers[i].activeTexture->subresources);
|
|
SDL_free(windowData->textureContainers[i].activeTexture);
|
|
SDL_free(windowData->textureContainers[i].textures);
|
|
}
|
|
|
|
IDXGISwapChain_Release(windowData->swapchain);
|
|
windowData->swapchain = NULL;
|
|
}
|
|
|
|
static bool D3D12_INTERNAL_CreateSwapchain(
|
|
D3D12Renderer *renderer,
|
|
D3D12WindowData *windowData,
|
|
SDL_GPUSwapchainComposition swapchainComposition,
|
|
SDL_GPUPresentMode presentMode)
|
|
{
|
|
HWND dxgiHandle;
|
|
int width, height;
|
|
DXGI_SWAP_CHAIN_DESC1 swapchainDesc;
|
|
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullscreenDesc;
|
|
DXGI_FORMAT swapchainFormat;
|
|
IDXGIFactory1 *pParent;
|
|
IDXGISwapChain1 *swapchain;
|
|
IDXGISwapChain3 *swapchain3;
|
|
HRESULT res;
|
|
|
|
// Get the DXGI handle
|
|
#ifdef _WIN32
|
|
dxgiHandle = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(windowData->window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
|
|
#else
|
|
dxgiHandle = (HWND)windowData->window;
|
|
#endif
|
|
|
|
// Get the window size
|
|
SDL_GetWindowSize(windowData->window, &width, &height);
|
|
|
|
swapchainFormat = SwapchainCompositionToTextureFormat[swapchainComposition];
|
|
|
|
// Initialize the swapchain buffer descriptor
|
|
swapchainDesc.Width = 0;
|
|
swapchainDesc.Height = 0;
|
|
swapchainDesc.Format = swapchainFormat;
|
|
swapchainDesc.SampleDesc.Count = 1;
|
|
swapchainDesc.SampleDesc.Quality = 0;
|
|
swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
swapchainDesc.BufferCount = MAX_FRAMES_IN_FLIGHT;
|
|
swapchainDesc.Scaling = DXGI_SCALING_STRETCH;
|
|
swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
|
swapchainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
|
|
swapchainDesc.Flags = 0;
|
|
swapchainDesc.Stereo = 0;
|
|
|
|
// Initialize the fullscreen descriptor (if needed)
|
|
fullscreenDesc.RefreshRate.Numerator = 0;
|
|
fullscreenDesc.RefreshRate.Denominator = 0;
|
|
fullscreenDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
|
|
fullscreenDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
|
fullscreenDesc.Windowed = true;
|
|
|
|
if (renderer->supportsTearing) {
|
|
swapchainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
|
} else {
|
|
swapchainDesc.Flags = 0;
|
|
}
|
|
|
|
if (!IsWindow(dxgiHandle)) {
|
|
return false;
|
|
}
|
|
|
|
// Create the swapchain!
|
|
res = IDXGIFactory4_CreateSwapChainForHwnd(
|
|
renderer->factory,
|
|
(IUnknown *)renderer->commandQueue,
|
|
dxgiHandle,
|
|
&swapchainDesc,
|
|
&fullscreenDesc,
|
|
NULL,
|
|
&swapchain);
|
|
ERROR_CHECK_RETURN("Could not create swapchain", 0);
|
|
|
|
res = IDXGISwapChain1_QueryInterface(
|
|
swapchain,
|
|
D3D_GUID(D3D_IID_IDXGISwapChain3),
|
|
(void **)&swapchain3);
|
|
IDXGISwapChain1_Release(swapchain);
|
|
ERROR_CHECK_RETURN("Could not create IDXGISwapChain3", 0);
|
|
|
|
if (swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_SDR) {
|
|
// Support already verified if we hit this block
|
|
IDXGISwapChain3_SetColorSpace1(
|
|
swapchain3,
|
|
SwapchainCompositionToColorSpace[swapchainComposition]);
|
|
}
|
|
|
|
/*
|
|
* The swapchain's parent is a separate factory from the factory that
|
|
* we used to create the swapchain, and only that parent can be used to
|
|
* set the window association. Trying to set an association on our factory
|
|
* will silently fail and doesn't even verify arguments or return errors.
|
|
* See https://gamedev.net/forums/topic/634235-dxgidisabling-altenter/4999955/
|
|
*/
|
|
res = IDXGISwapChain3_GetParent(
|
|
swapchain3,
|
|
D3D_GUID(D3D_IID_IDXGIFactory1),
|
|
(void **)&pParent);
|
|
if (FAILED(res)) {
|
|
SDL_LogWarn(
|
|
SDL_LOG_CATEGORY_APPLICATION,
|
|
"Could not get swapchain parent! Error Code: " HRESULT_FMT,
|
|
res);
|
|
} else {
|
|
// Disable DXGI window crap
|
|
res = IDXGIFactory1_MakeWindowAssociation(
|
|
pParent,
|
|
dxgiHandle,
|
|
DXGI_MWA_NO_WINDOW_CHANGES);
|
|
if (FAILED(res)) {
|
|
SDL_LogWarn(
|
|
SDL_LOG_CATEGORY_APPLICATION,
|
|
"MakeWindowAssociation failed! Error Code: " HRESULT_FMT,
|
|
res);
|
|
}
|
|
|
|
// We're done with the parent now
|
|
IDXGIFactory1_Release(pParent);
|
|
}
|
|
|
|
// Initialize the swapchain data
|
|
windowData->swapchain = swapchain3;
|
|
windowData->present_mode = presentMode;
|
|
windowData->swapchainComposition = swapchainComposition;
|
|
windowData->swapchainColorSpace = SwapchainCompositionToColorSpace[swapchainComposition];
|
|
windowData->frameCounter = 0;
|
|
|
|
// Precache blit pipelines for the swapchain format
|
|
for (Uint32 i = 0; i < 5; i += 1) {
|
|
SDL_GPU_FetchBlitPipeline(
|
|
renderer->sdlGPUDevice,
|
|
(SDL_GPUTextureType)i,
|
|
SwapchainCompositionToSDLTextureFormat[swapchainComposition],
|
|
renderer->blitVertexShader,
|
|
renderer->blitFrom2DShader,
|
|
renderer->blitFrom2DArrayShader,
|
|
renderer->blitFrom3DShader,
|
|
renderer->blitFromCubeShader,
|
|
renderer->blitFromCubeArrayShader,
|
|
&renderer->blitPipelines,
|
|
&renderer->blitPipelineCount,
|
|
&renderer->blitPipelineCapacity);
|
|
}
|
|
|
|
/* If a you are using a FLIP model format you can't create the swapchain as DXGI_FORMAT_B8G8R8A8_UNORM_SRGB.
|
|
* You have to create the swapchain as DXGI_FORMAT_B8G8R8A8_UNORM and then set the render target view's format to DXGI_FORMAT_B8G8R8A8_UNORM_SRGB
|
|
*/
|
|
for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
|
|
if (!D3D12_INTERNAL_InitializeSwapchainTexture(
|
|
renderer,
|
|
swapchain3,
|
|
swapchainComposition,
|
|
i,
|
|
&windowData->textureContainers[i])) {
|
|
IDXGISwapChain3_Release(swapchain3);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
static bool D3D12_ClaimWindow(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_Window *window)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window);
|
|
|
|
if (windowData == NULL) {
|
|
windowData = (D3D12WindowData *)SDL_calloc(1, sizeof(D3D12WindowData));
|
|
if (!windowData) {
|
|
return false;
|
|
}
|
|
windowData->window = window;
|
|
|
|
if (D3D12_INTERNAL_CreateSwapchain(renderer, windowData, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC)) {
|
|
SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData);
|
|
|
|
SDL_LockMutex(renderer->windowLock);
|
|
|
|
if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity) {
|
|
renderer->claimedWindowCapacity *= 2;
|
|
renderer->claimedWindows = (D3D12WindowData **)SDL_realloc(
|
|
renderer->claimedWindows,
|
|
renderer->claimedWindowCapacity * sizeof(D3D12WindowData *));
|
|
}
|
|
renderer->claimedWindows[renderer->claimedWindowCount] = windowData;
|
|
renderer->claimedWindowCount += 1;
|
|
|
|
SDL_UnlockMutex(renderer->windowLock);
|
|
|
|
return true;
|
|
} else {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not create swapchain, failed to claim window!");
|
|
SDL_free(windowData);
|
|
return false;
|
|
}
|
|
} else {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Window already claimed!");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void D3D12_ReleaseWindow(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_Window *window)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window);
|
|
|
|
if (windowData == NULL) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Window already unclaimed!");
|
|
return;
|
|
}
|
|
|
|
D3D12_Wait(driverData);
|
|
|
|
for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
|
|
if (windowData->inFlightFences[i] != NULL) {
|
|
D3D12_ReleaseFence(
|
|
driverData,
|
|
windowData->inFlightFences[i]);
|
|
windowData->inFlightFences[i] = NULL;
|
|
}
|
|
}
|
|
|
|
D3D12_INTERNAL_DestroySwapchain(renderer, windowData);
|
|
|
|
SDL_LockMutex(renderer->windowLock);
|
|
for (Uint32 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_UnlockMutex(renderer->windowLock);
|
|
|
|
SDL_free(windowData);
|
|
SDL_ClearProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA);
|
|
}
|
|
|
|
static bool D3D12_SetSwapchainParameters(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_Window *window,
|
|
SDL_GPUSwapchainComposition swapchainComposition,
|
|
SDL_GPUPresentMode presentMode)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window);
|
|
|
|
if (windowData == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Cannot set swapchain parameters on unclaimed window!");
|
|
return false;
|
|
}
|
|
|
|
if (!D3D12_SupportsSwapchainComposition(driverData, window, swapchainComposition)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Swapchain composition not supported!");
|
|
return false;
|
|
}
|
|
|
|
if (!D3D12_SupportsPresentMode(driverData, window, presentMode)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Present mode not supported!");
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
swapchainComposition != windowData->swapchainComposition ||
|
|
presentMode != windowData->present_mode) {
|
|
D3D12_Wait(driverData);
|
|
|
|
// Recreate the swapchain
|
|
D3D12_INTERNAL_DestroySwapchain(
|
|
renderer,
|
|
windowData);
|
|
|
|
return D3D12_INTERNAL_CreateSwapchain(
|
|
renderer,
|
|
windowData,
|
|
swapchainComposition,
|
|
presentMode);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static SDL_GPUTextureFormat D3D12_GetSwapchainTextureFormat(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_Window *window)
|
|
{
|
|
D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window);
|
|
|
|
if (windowData == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Cannot get swapchain format, window has not been claimed!");
|
|
return SDL_GPU_TEXTUREFORMAT_INVALID;
|
|
}
|
|
|
|
return windowData->textureContainers[windowData->frameCounter].header.info.format;
|
|
}
|
|
|
|
static D3D12Fence *D3D12_INTERNAL_AcquireFence(
|
|
D3D12Renderer *renderer)
|
|
{
|
|
D3D12Fence *fence;
|
|
ID3D12Fence *handle;
|
|
HRESULT res;
|
|
|
|
SDL_LockMutex(renderer->fenceLock);
|
|
|
|
if (renderer->availableFenceCount == 0) {
|
|
res = ID3D12Device_CreateFence(
|
|
renderer->device,
|
|
D3D12_FENCE_UNSIGNALED_VALUE,
|
|
D3D12_FENCE_FLAG_NONE,
|
|
D3D_GUID(D3D_IID_ID3D12Fence),
|
|
(void **)&handle);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_LogError(renderer->device, "Failed to create fence!", res);
|
|
SDL_UnlockMutex(renderer->fenceLock);
|
|
return NULL;
|
|
}
|
|
|
|
fence = (D3D12Fence *)SDL_calloc(1, sizeof(D3D12Fence));
|
|
if (!fence) {
|
|
ID3D12Fence_Release(handle);
|
|
SDL_UnlockMutex(renderer->fenceLock);
|
|
return NULL;
|
|
}
|
|
fence->handle = handle;
|
|
fence->event = CreateEventEx(NULL, 0, 0, EVENT_ALL_ACCESS);
|
|
SDL_AtomicSet(&fence->referenceCount, 0);
|
|
} else {
|
|
fence = renderer->availableFences[renderer->availableFenceCount - 1];
|
|
renderer->availableFenceCount -= 1;
|
|
ID3D12Fence_Signal(fence->handle, D3D12_FENCE_UNSIGNALED_VALUE);
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->fenceLock);
|
|
|
|
(void)SDL_AtomicIncRef(&fence->referenceCount);
|
|
return fence;
|
|
}
|
|
|
|
static void D3D12_INTERNAL_AllocateCommandBuffer(
|
|
D3D12Renderer *renderer)
|
|
{
|
|
D3D12CommandBuffer *commandBuffer;
|
|
HRESULT res;
|
|
ID3D12CommandAllocator *commandAllocator;
|
|
ID3D12GraphicsCommandList *commandList;
|
|
|
|
commandBuffer = (D3D12CommandBuffer *)SDL_calloc(1, sizeof(D3D12CommandBuffer));
|
|
if (!commandBuffer) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create ID3D12CommandList. Out of Memory");
|
|
return;
|
|
}
|
|
|
|
res = ID3D12Device_CreateCommandAllocator(
|
|
renderer->device,
|
|
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
D3D_GUID(D3D_IID_ID3D12CommandAllocator),
|
|
(void **)&commandAllocator);
|
|
if (FAILED(res)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create ID3D12CommandAllocator");
|
|
D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer);
|
|
return;
|
|
}
|
|
commandBuffer->commandAllocator = commandAllocator;
|
|
|
|
res = ID3D12Device_CreateCommandList(
|
|
renderer->device,
|
|
0,
|
|
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
commandAllocator,
|
|
NULL,
|
|
D3D_GUID(D3D_IID_ID3D12GraphicsCommandList),
|
|
(void **)&commandList);
|
|
|
|
if (FAILED(res)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create ID3D12CommandList");
|
|
D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer);
|
|
return;
|
|
}
|
|
commandBuffer->graphicsCommandList = commandList;
|
|
|
|
commandBuffer->renderer = renderer;
|
|
commandBuffer->inFlightFence = NULL;
|
|
|
|
// Window handling
|
|
commandBuffer->presentDataCapacity = 1;
|
|
commandBuffer->presentDataCount = 0;
|
|
commandBuffer->presentDatas = (D3D12PresentData *)SDL_calloc(
|
|
commandBuffer->presentDataCapacity, sizeof(D3D12PresentData));
|
|
|
|
// Resource tracking
|
|
commandBuffer->usedTextureCapacity = 4;
|
|
commandBuffer->usedTextureCount = 0;
|
|
commandBuffer->usedTextures = (D3D12Texture **)SDL_calloc(
|
|
commandBuffer->usedTextureCapacity, sizeof(D3D12Texture *));
|
|
|
|
commandBuffer->usedBufferCapacity = 4;
|
|
commandBuffer->usedBufferCount = 0;
|
|
commandBuffer->usedBuffers = (D3D12Buffer **)SDL_calloc(
|
|
commandBuffer->usedBufferCapacity, sizeof(D3D12Buffer *));
|
|
|
|
commandBuffer->usedSamplerCapacity = 4;
|
|
commandBuffer->usedSamplerCount = 0;
|
|
commandBuffer->usedSamplers = (D3D12Sampler **)SDL_calloc(
|
|
commandBuffer->usedSamplerCapacity, sizeof(D3D12Sampler *));
|
|
|
|
commandBuffer->usedGraphicsPipelineCapacity = 4;
|
|
commandBuffer->usedGraphicsPipelineCount = 0;
|
|
commandBuffer->usedGraphicsPipelines = (D3D12GraphicsPipeline **)SDL_calloc(
|
|
commandBuffer->usedGraphicsPipelineCapacity, sizeof(D3D12GraphicsPipeline *));
|
|
|
|
commandBuffer->usedComputePipelineCapacity = 4;
|
|
commandBuffer->usedComputePipelineCount = 0;
|
|
commandBuffer->usedComputePipelines = (D3D12ComputePipeline **)SDL_calloc(
|
|
commandBuffer->usedComputePipelineCapacity, sizeof(D3D12ComputePipeline *));
|
|
|
|
commandBuffer->usedUniformBufferCapacity = 4;
|
|
commandBuffer->usedUniformBufferCount = 0;
|
|
commandBuffer->usedUniformBuffers = (D3D12UniformBuffer **)SDL_calloc(
|
|
commandBuffer->usedUniformBufferCapacity, sizeof(D3D12UniformBuffer *));
|
|
|
|
commandBuffer->textureDownloadCapacity = 4;
|
|
commandBuffer->textureDownloadCount = 0;
|
|
commandBuffer->textureDownloads = (D3D12TextureDownload **)SDL_calloc(
|
|
commandBuffer->textureDownloadCapacity, sizeof(D3D12TextureDownload *));
|
|
|
|
if (
|
|
(!commandBuffer->presentDatas) ||
|
|
(!commandBuffer->usedTextures) ||
|
|
(!commandBuffer->usedBuffers) ||
|
|
(!commandBuffer->usedSamplers) ||
|
|
(!commandBuffer->usedGraphicsPipelines) ||
|
|
(!commandBuffer->usedComputePipelines) ||
|
|
(!commandBuffer->usedUniformBuffers) ||
|
|
(!commandBuffer->textureDownloads)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create ID3D12CommandList. Out of Memory");
|
|
D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer);
|
|
return;
|
|
}
|
|
|
|
D3D12CommandBuffer **resizedAvailableCommandBuffers = (D3D12CommandBuffer **)SDL_realloc(
|
|
renderer->availableCommandBuffers,
|
|
sizeof(D3D12CommandBuffer *) * (renderer->availableCommandBufferCapacity + 1));
|
|
|
|
if (!resizedAvailableCommandBuffers) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create ID3D12CommandList. Out of Memory");
|
|
D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer);
|
|
return;
|
|
}
|
|
// Add to inactive command buffer array
|
|
renderer->availableCommandBufferCapacity += 1;
|
|
renderer->availableCommandBuffers = resizedAvailableCommandBuffers;
|
|
|
|
renderer->availableCommandBuffers[renderer->availableCommandBufferCount] = commandBuffer;
|
|
renderer->availableCommandBufferCount += 1;
|
|
}
|
|
|
|
static D3D12CommandBuffer *D3D12_INTERNAL_AcquireCommandBufferFromPool(
|
|
D3D12Renderer *renderer)
|
|
{
|
|
D3D12CommandBuffer *commandBuffer;
|
|
|
|
if (renderer->availableCommandBufferCount == 0) {
|
|
D3D12_INTERNAL_AllocateCommandBuffer(renderer);
|
|
}
|
|
|
|
commandBuffer = renderer->availableCommandBuffers[renderer->availableCommandBufferCount - 1];
|
|
renderer->availableCommandBufferCount -= 1;
|
|
|
|
return commandBuffer;
|
|
}
|
|
|
|
static SDL_GPUCommandBuffer *D3D12_AcquireCommandBuffer(
|
|
SDL_GPURenderer *driverData)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12CommandBuffer *commandBuffer;
|
|
ID3D12DescriptorHeap *heaps[2];
|
|
SDL_zeroa(heaps);
|
|
|
|
SDL_LockMutex(renderer->acquireCommandBufferLock);
|
|
commandBuffer = D3D12_INTERNAL_AcquireCommandBufferFromPool(renderer);
|
|
SDL_UnlockMutex(renderer->acquireCommandBufferLock);
|
|
|
|
if (commandBuffer == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to acquire command buffer!");
|
|
return NULL;
|
|
}
|
|
|
|
// Set the descriptor heaps!
|
|
commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] =
|
|
D3D12_INTERNAL_AcquireDescriptorHeapFromPool(commandBuffer, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
|
|
|
if (!commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV]) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to acquire descriptor heap!");
|
|
D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer);
|
|
return NULL;
|
|
}
|
|
|
|
commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER] =
|
|
D3D12_INTERNAL_AcquireDescriptorHeapFromPool(commandBuffer, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
|
|
|
|
if (!commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER]) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to acquire descriptor heap!");
|
|
D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer);
|
|
return NULL;
|
|
}
|
|
|
|
heaps[0] = commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV]->handle;
|
|
heaps[1] = commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER]->handle;
|
|
|
|
ID3D12GraphicsCommandList_SetDescriptorHeaps(
|
|
commandBuffer->graphicsCommandList,
|
|
2,
|
|
heaps);
|
|
|
|
// Set the bind state
|
|
commandBuffer->currentGraphicsPipeline = NULL;
|
|
|
|
SDL_zeroa(commandBuffer->colorTargetTextureSubresources);
|
|
commandBuffer->colorTargetTextureSubresourceCount = 0;
|
|
commandBuffer->depthStencilTextureSubresource = NULL;
|
|
|
|
SDL_zeroa(commandBuffer->vertexBuffers);
|
|
SDL_zeroa(commandBuffer->vertexBufferOffsets);
|
|
commandBuffer->vertexBufferCount = 0;
|
|
|
|
SDL_zeroa(commandBuffer->vertexSamplerTextures);
|
|
SDL_zeroa(commandBuffer->vertexSamplers);
|
|
SDL_zeroa(commandBuffer->vertexStorageTextures);
|
|
SDL_zeroa(commandBuffer->vertexStorageBuffers);
|
|
SDL_zeroa(commandBuffer->vertexUniformBuffers);
|
|
|
|
SDL_zeroa(commandBuffer->fragmentSamplerTextures);
|
|
SDL_zeroa(commandBuffer->fragmentSamplers);
|
|
SDL_zeroa(commandBuffer->fragmentStorageTextures);
|
|
SDL_zeroa(commandBuffer->fragmentStorageBuffers);
|
|
SDL_zeroa(commandBuffer->fragmentUniformBuffers);
|
|
|
|
SDL_zeroa(commandBuffer->computeSamplerTextures);
|
|
SDL_zeroa(commandBuffer->computeSamplers);
|
|
SDL_zeroa(commandBuffer->computeReadOnlyStorageTextures);
|
|
SDL_zeroa(commandBuffer->computeReadOnlyStorageBuffers);
|
|
SDL_zeroa(commandBuffer->computeWriteOnlyStorageTextureSubresources);
|
|
SDL_zeroa(commandBuffer->computeWriteOnlyStorageBuffers);
|
|
SDL_zeroa(commandBuffer->computeUniformBuffers);
|
|
|
|
commandBuffer->autoReleaseFence = true;
|
|
|
|
return (SDL_GPUCommandBuffer *)commandBuffer;
|
|
}
|
|
|
|
static SDL_GPUTexture *D3D12_AcquireSwapchainTexture(
|
|
SDL_GPUCommandBuffer *commandBuffer,
|
|
SDL_Window *window,
|
|
Uint32 *w,
|
|
Uint32 *h)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12Renderer *renderer = d3d12CommandBuffer->renderer;
|
|
D3D12WindowData *windowData;
|
|
Uint32 swapchainIndex;
|
|
HRESULT res;
|
|
|
|
windowData = D3D12_INTERNAL_FetchWindowData(window);
|
|
if (windowData == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
res = D3D12_INTERNAL_ResizeSwapchainIfNeeded(
|
|
renderer,
|
|
windowData);
|
|
ERROR_CHECK_RETURN("Could not resize swapchain", NULL);
|
|
|
|
if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
|
|
if (windowData->present_mode == SDL_GPU_PRESENTMODE_VSYNC) {
|
|
// In VSYNC mode, block until the least recent presented frame is done
|
|
D3D12_WaitForFences(
|
|
(SDL_GPURenderer *)renderer,
|
|
true,
|
|
&windowData->inFlightFences[windowData->frameCounter],
|
|
1);
|
|
} else {
|
|
if (!D3D12_QueryFence(
|
|
(SDL_GPURenderer *)renderer,
|
|
windowData->inFlightFences[windowData->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;
|
|
}
|
|
}
|
|
|
|
D3D12_ReleaseFence(
|
|
(SDL_GPURenderer *)renderer,
|
|
windowData->inFlightFences[windowData->frameCounter]);
|
|
|
|
windowData->inFlightFences[windowData->frameCounter] = NULL;
|
|
}
|
|
|
|
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
// FIXME: Should this happen before the inFlightFences stuff above?
|
|
windowData->frameToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL;
|
|
renderer->device->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, NULL, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &windowData->frameToken);
|
|
swapchainIndex = windowData->frameCounter;
|
|
#else
|
|
swapchainIndex = IDXGISwapChain3_GetCurrentBackBufferIndex(windowData->swapchain);
|
|
|
|
// Set the handle on the windowData texture data.
|
|
res = IDXGISwapChain_GetBuffer(
|
|
windowData->swapchain,
|
|
swapchainIndex,
|
|
D3D_GUID(D3D_IID_ID3D12Resource),
|
|
(void **)&windowData->textureContainers[swapchainIndex].activeTexture->resource);
|
|
ERROR_CHECK_RETURN("Could not acquire swapchain!", NULL);
|
|
#endif
|
|
|
|
// Send the dimensions to the out parameters.
|
|
*w = windowData->textureContainers[swapchainIndex].header.info.width;
|
|
*h = windowData->textureContainers[swapchainIndex].header.info.height;
|
|
|
|
// Set up presentation
|
|
if (d3d12CommandBuffer->presentDataCount == d3d12CommandBuffer->presentDataCapacity) {
|
|
d3d12CommandBuffer->presentDataCapacity += 1;
|
|
d3d12CommandBuffer->presentDatas = (D3D12PresentData *)SDL_realloc(
|
|
d3d12CommandBuffer->presentDatas,
|
|
d3d12CommandBuffer->presentDataCapacity * sizeof(D3D12PresentData));
|
|
}
|
|
d3d12CommandBuffer->presentDatas[d3d12CommandBuffer->presentDataCount].windowData = windowData;
|
|
d3d12CommandBuffer->presentDatas[d3d12CommandBuffer->presentDataCount].swapchainImageIndex = swapchainIndex;
|
|
d3d12CommandBuffer->presentDataCount += 1;
|
|
|
|
// Set up resource barrier
|
|
D3D12_RESOURCE_BARRIER barrierDesc;
|
|
barrierDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
|
barrierDesc.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
|
barrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
|
barrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
|
barrierDesc.Transition.pResource = windowData->textureContainers[swapchainIndex].activeTexture->resource;
|
|
barrierDesc.Transition.Subresource = 0;
|
|
|
|
ID3D12GraphicsCommandList_ResourceBarrier(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
1,
|
|
&barrierDesc);
|
|
|
|
return (SDL_GPUTexture *)&windowData->textureContainers[swapchainIndex];
|
|
}
|
|
|
|
static void D3D12_INTERNAL_PerformPendingDestroys(D3D12Renderer *renderer)
|
|
{
|
|
SDL_LockMutex(renderer->disposeLock);
|
|
|
|
for (Sint32 i = renderer->buffersToDestroyCount - 1; i >= 0; i -= 1) {
|
|
if (SDL_AtomicGet(&renderer->buffersToDestroy[i]->referenceCount) == 0) {
|
|
D3D12_INTERNAL_DestroyBuffer(
|
|
renderer,
|
|
renderer->buffersToDestroy[i]);
|
|
|
|
renderer->buffersToDestroy[i] = renderer->buffersToDestroy[renderer->buffersToDestroyCount - 1];
|
|
renderer->buffersToDestroyCount -= 1;
|
|
}
|
|
}
|
|
|
|
for (Sint32 i = renderer->texturesToDestroyCount - 1; i >= 0; i -= 1) {
|
|
if (SDL_AtomicGet(&renderer->texturesToDestroy[i]->referenceCount) == 0) {
|
|
D3D12_INTERNAL_DestroyTexture(
|
|
renderer,
|
|
renderer->texturesToDestroy[i]);
|
|
|
|
renderer->texturesToDestroy[i] = renderer->texturesToDestroy[renderer->texturesToDestroyCount - 1];
|
|
renderer->texturesToDestroyCount -= 1;
|
|
}
|
|
}
|
|
|
|
for (Sint32 i = renderer->samplersToDestroyCount - 1; i >= 0; i -= 1) {
|
|
if (SDL_AtomicGet(&renderer->samplersToDestroy[i]->referenceCount) == 0) {
|
|
D3D12_INTERNAL_DestroySampler(
|
|
renderer,
|
|
renderer->samplersToDestroy[i]);
|
|
|
|
renderer->samplersToDestroy[i] = renderer->samplersToDestroy[renderer->samplersToDestroyCount - 1];
|
|
renderer->samplersToDestroyCount -= 1;
|
|
}
|
|
}
|
|
|
|
for (Sint32 i = renderer->graphicsPipelinesToDestroyCount - 1; i >= 0; i -= 1) {
|
|
if (SDL_AtomicGet(&renderer->graphicsPipelinesToDestroy[i]->referenceCount) == 0) {
|
|
D3D12_INTERNAL_DestroyGraphicsPipeline(
|
|
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) {
|
|
D3D12_INTERNAL_DestroyComputePipeline(
|
|
renderer->computePipelinesToDestroy[i]);
|
|
|
|
renderer->computePipelinesToDestroy[i] = renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount - 1];
|
|
renderer->computePipelinesToDestroyCount -= 1;
|
|
}
|
|
}
|
|
|
|
SDL_UnlockMutex(renderer->disposeLock);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_CopyTextureDownload(
|
|
D3D12CommandBuffer *commandBuffer,
|
|
D3D12TextureDownload *download)
|
|
{
|
|
Uint8 *sourcePtr;
|
|
Uint8 *destPtr;
|
|
HRESULT res;
|
|
|
|
res = ID3D12Resource_Map(
|
|
download->temporaryBuffer->handle,
|
|
0,
|
|
NULL,
|
|
(void **)&sourcePtr);
|
|
|
|
if (FAILED(res)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to map temporary buffer!");
|
|
return;
|
|
}
|
|
|
|
res = ID3D12Resource_Map(
|
|
download->destinationBuffer->handle,
|
|
0,
|
|
NULL,
|
|
(void **)&destPtr);
|
|
|
|
if (FAILED(res)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to map destination buffer!");
|
|
return;
|
|
}
|
|
|
|
for (Uint32 sliceIndex = 0; sliceIndex < download->depth; sliceIndex += 1) {
|
|
for (Uint32 rowIndex = 0; rowIndex < download->height; rowIndex += 1) {
|
|
SDL_memcpy(
|
|
destPtr + download->bufferOffset + (sliceIndex * download->bytesPerDepthSlice) + (rowIndex * download->bytesPerRow),
|
|
sourcePtr + (sliceIndex * download->height) + (rowIndex * download->alignedBytesPerRow),
|
|
download->bytesPerRow);
|
|
}
|
|
}
|
|
|
|
ID3D12Resource_Unmap(
|
|
download->temporaryBuffer->handle,
|
|
0,
|
|
NULL);
|
|
|
|
ID3D12Resource_Unmap(
|
|
download->destinationBuffer->handle,
|
|
0,
|
|
NULL);
|
|
}
|
|
|
|
static void D3D12_INTERNAL_CleanCommandBuffer(
|
|
D3D12Renderer *renderer,
|
|
D3D12CommandBuffer *commandBuffer)
|
|
{
|
|
Uint32 i;
|
|
HRESULT res;
|
|
|
|
// Perform deferred texture data copies
|
|
|
|
for (i = 0; i < commandBuffer->textureDownloadCount; i += 1) {
|
|
D3D12_INTERNAL_CopyTextureDownload(
|
|
commandBuffer,
|
|
commandBuffer->textureDownloads[i]);
|
|
SDL_free(commandBuffer->textureDownloads[i]);
|
|
}
|
|
commandBuffer->textureDownloadCount = 0;
|
|
|
|
res = ID3D12CommandAllocator_Reset(commandBuffer->commandAllocator);
|
|
ERROR_CHECK("Could not reset command allocator")
|
|
|
|
res = ID3D12GraphicsCommandList_Reset(
|
|
commandBuffer->graphicsCommandList,
|
|
commandBuffer->commandAllocator,
|
|
NULL);
|
|
ERROR_CHECK("Could not reset graphicsCommandList")
|
|
|
|
// Return descriptor heaps to pool
|
|
D3D12_INTERNAL_ReturnDescriptorHeapToPool(
|
|
renderer,
|
|
commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV]);
|
|
D3D12_INTERNAL_ReturnDescriptorHeapToPool(
|
|
renderer,
|
|
commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER]);
|
|
|
|
// Uniform buffers are now available
|
|
SDL_LockMutex(renderer->acquireUniformBufferLock);
|
|
|
|
for (i = 0; i < commandBuffer->usedUniformBufferCount; i += 1) {
|
|
D3D12_INTERNAL_ReturnUniformBufferToPool(
|
|
renderer,
|
|
commandBuffer->usedUniformBuffers[i]);
|
|
}
|
|
commandBuffer->usedUniformBufferCount = 0;
|
|
|
|
SDL_UnlockMutex(renderer->acquireUniformBufferLock);
|
|
|
|
// TODO: More reference counting
|
|
|
|
for (i = 0; i < commandBuffer->usedTextureCount; i += 1) {
|
|
(void)SDL_AtomicDecRef(&commandBuffer->usedTextures[i]->referenceCount);
|
|
}
|
|
commandBuffer->usedTextureCount = 0;
|
|
|
|
for (i = 0; i < commandBuffer->usedBufferCount; i += 1) {
|
|
(void)SDL_AtomicDecRef(&commandBuffer->usedBuffers[i]->referenceCount);
|
|
}
|
|
commandBuffer->usedBufferCount = 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;
|
|
|
|
// Reset presentation
|
|
commandBuffer->presentDataCount = 0;
|
|
|
|
// The fence is now available (unless SubmitAndAcquireFence was called)
|
|
if (commandBuffer->autoReleaseFence) {
|
|
D3D12_ReleaseFence(
|
|
(SDL_GPURenderer *)renderer,
|
|
(SDL_GPUFence *)commandBuffer->inFlightFence);
|
|
|
|
commandBuffer->inFlightFence = NULL;
|
|
}
|
|
|
|
// Return command buffer to pool
|
|
SDL_LockMutex(renderer->acquireCommandBufferLock);
|
|
|
|
if (renderer->availableCommandBufferCount == renderer->availableCommandBufferCapacity) {
|
|
renderer->availableCommandBufferCapacity += 1;
|
|
renderer->availableCommandBuffers = (D3D12CommandBuffer **)SDL_realloc(
|
|
renderer->availableCommandBuffers,
|
|
renderer->availableCommandBufferCapacity * sizeof(D3D12CommandBuffer *));
|
|
}
|
|
|
|
renderer->availableCommandBuffers[renderer->availableCommandBufferCount] = commandBuffer;
|
|
renderer->availableCommandBufferCount += 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 D3D12_Submit(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
D3D12Renderer *renderer = d3d12CommandBuffer->renderer;
|
|
ID3D12CommandList *commandLists[1];
|
|
HRESULT res;
|
|
|
|
SDL_LockMutex(renderer->submitLock);
|
|
|
|
// Unmap uniform buffers
|
|
for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
|
|
if (d3d12CommandBuffer->vertexUniformBuffers[i] != NULL) {
|
|
ID3D12Resource_Unmap(
|
|
d3d12CommandBuffer->vertexUniformBuffers[i]->buffer->handle,
|
|
0,
|
|
NULL);
|
|
d3d12CommandBuffer->vertexUniformBuffers[i]->buffer->mapPointer = NULL;
|
|
}
|
|
|
|
if (d3d12CommandBuffer->fragmentUniformBuffers[i] != NULL) {
|
|
ID3D12Resource_Unmap(
|
|
d3d12CommandBuffer->fragmentUniformBuffers[i]->buffer->handle,
|
|
0,
|
|
NULL);
|
|
d3d12CommandBuffer->fragmentUniformBuffers[i]->buffer->mapPointer = NULL;
|
|
}
|
|
|
|
// TODO: compute uniforms
|
|
}
|
|
|
|
// Transition present textures to present mode
|
|
for (Uint32 i = 0; i < d3d12CommandBuffer->presentDataCount; i += 1) {
|
|
Uint32 swapchainIndex = d3d12CommandBuffer->presentDatas[i].swapchainImageIndex;
|
|
D3D12TextureContainer *container = &d3d12CommandBuffer->presentDatas[i].windowData->textureContainers[swapchainIndex];
|
|
D3D12TextureSubresource *subresource = D3D12_INTERNAL_FetchTextureSubresource(container, 0, 0);
|
|
|
|
D3D12_RESOURCE_BARRIER barrierDesc;
|
|
barrierDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
|
barrierDesc.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
|
barrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
|
barrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
|
barrierDesc.Transition.pResource = subresource->parent->resource;
|
|
barrierDesc.Transition.Subresource = subresource->index;
|
|
|
|
ID3D12GraphicsCommandList_ResourceBarrier(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
1,
|
|
&barrierDesc);
|
|
}
|
|
|
|
// Notify the command buffer that we have completed recording
|
|
res = ID3D12GraphicsCommandList_Close(d3d12CommandBuffer->graphicsCommandList);
|
|
ERROR_CHECK("Failed to close command list!");
|
|
|
|
res = ID3D12GraphicsCommandList_QueryInterface(
|
|
d3d12CommandBuffer->graphicsCommandList,
|
|
D3D_GUID(D3D_IID_ID3D12CommandList),
|
|
(void **)&commandLists[0]);
|
|
ERROR_CHECK("Failed to convert command list!")
|
|
|
|
// Submit the command list to the queue
|
|
ID3D12CommandQueue_ExecuteCommandLists(
|
|
renderer->commandQueue,
|
|
1,
|
|
commandLists);
|
|
|
|
ID3D12CommandList_Release(commandLists[0]);
|
|
|
|
// Acquire a fence and set it to the in-flight fence
|
|
d3d12CommandBuffer->inFlightFence = D3D12_INTERNAL_AcquireFence(renderer);
|
|
if (!d3d12CommandBuffer->inFlightFence) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to acquire fence.");
|
|
}
|
|
|
|
// Mark that a fence should be signaled after command list execution
|
|
res = ID3D12CommandQueue_Signal(
|
|
renderer->commandQueue,
|
|
d3d12CommandBuffer->inFlightFence->handle,
|
|
D3D12_FENCE_SIGNAL_VALUE);
|
|
ERROR_CHECK("Failed to enqueue fence signal!");
|
|
|
|
// Mark the command buffer as submitted
|
|
if (renderer->submittedCommandBufferCount + 1 >= renderer->submittedCommandBufferCapacity) {
|
|
renderer->submittedCommandBufferCapacity = renderer->submittedCommandBufferCount + 1;
|
|
|
|
renderer->submittedCommandBuffers = (D3D12CommandBuffer **)SDL_realloc(
|
|
renderer->submittedCommandBuffers,
|
|
sizeof(D3D12CommandBuffer *) * renderer->submittedCommandBufferCapacity);
|
|
}
|
|
|
|
renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount] = d3d12CommandBuffer;
|
|
renderer->submittedCommandBufferCount += 1;
|
|
|
|
// Present, if applicable
|
|
for (Uint32 i = 0; i < d3d12CommandBuffer->presentDataCount; i += 1) {
|
|
D3D12PresentData *presentData = &d3d12CommandBuffer->presentDatas[i];
|
|
D3D12WindowData *windowData = presentData->windowData;
|
|
|
|
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|
|
D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParams;
|
|
SDL_zero(planeParams);
|
|
planeParams.Token = windowData->frameToken;
|
|
planeParams.ResourceCount = 1;
|
|
planeParams.ppResources = &windowData->textureContainers[windowData->frameCounter].activeTexture->resource;
|
|
planeParams.ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // FIXME
|
|
|
|
D3D12XBOX_PRESENT_PARAMETERS presentParams;
|
|
SDL_zero(presentParams);
|
|
presentParams.Flags = (windowData->present_mode == SDL_GPU_PRESENTMODE_IMMEDIATE) ? D3D12XBOX_PRESENT_FLAG_IMMEDIATE : D3D12XBOX_PRESENT_FLAG_NONE;
|
|
|
|
renderer->commandQueue->PresentX(1, &planeParams, &presentParams);
|
|
#else
|
|
// NOTE: flip discard always supported since DXGI 1.4 is required
|
|
Uint32 syncInterval = 1;
|
|
if (windowData->present_mode == SDL_GPU_PRESENTMODE_IMMEDIATE ||
|
|
windowData->present_mode == SDL_GPU_PRESENTMODE_MAILBOX) {
|
|
syncInterval = 0;
|
|
}
|
|
|
|
Uint32 presentFlags = 0;
|
|
if (renderer->supportsTearing &&
|
|
windowData->present_mode == SDL_GPU_PRESENTMODE_IMMEDIATE) {
|
|
presentFlags = DXGI_PRESENT_ALLOW_TEARING;
|
|
}
|
|
|
|
IDXGISwapChain_Present(
|
|
windowData->swapchain,
|
|
syncInterval,
|
|
presentFlags);
|
|
|
|
ID3D12Resource_Release(windowData->textureContainers[presentData->swapchainImageIndex].activeTexture->resource);
|
|
#endif
|
|
|
|
windowData->inFlightFences[windowData->frameCounter] = (SDL_GPUFence*)d3d12CommandBuffer->inFlightFence;
|
|
(void)SDL_AtomicIncRef(&d3d12CommandBuffer->inFlightFence->referenceCount);
|
|
windowData->frameCounter = (windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT;
|
|
}
|
|
|
|
// Check for cleanups
|
|
for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
|
|
Uint64 fenceValue = ID3D12Fence_GetCompletedValue(
|
|
renderer->submittedCommandBuffers[i]->inFlightFence->handle);
|
|
|
|
if (fenceValue == D3D12_FENCE_SIGNAL_VALUE) {
|
|
D3D12_INTERNAL_CleanCommandBuffer(
|
|
renderer,
|
|
renderer->submittedCommandBuffers[i]);
|
|
}
|
|
}
|
|
|
|
D3D12_INTERNAL_PerformPendingDestroys(renderer);
|
|
|
|
SDL_UnlockMutex(renderer->submitLock);
|
|
}
|
|
|
|
static SDL_GPUFence *D3D12_SubmitAndAcquireFence(
|
|
SDL_GPUCommandBuffer *commandBuffer)
|
|
{
|
|
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
|
|
d3d12CommandBuffer->autoReleaseFence = false;
|
|
D3D12_Submit(commandBuffer);
|
|
return (SDL_GPUFence *)d3d12CommandBuffer->inFlightFence;
|
|
}
|
|
|
|
static void D3D12_Wait(
|
|
SDL_GPURenderer *driverData)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12Fence *fence = D3D12_INTERNAL_AcquireFence(renderer);
|
|
if (!fence) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to acquire fence.");
|
|
return;
|
|
}
|
|
HRESULT res;
|
|
|
|
SDL_LockMutex(renderer->submitLock);
|
|
|
|
if (renderer->commandQueue) {
|
|
// Insert a signal into the end of the command queue...
|
|
ID3D12CommandQueue_Signal(
|
|
renderer->commandQueue,
|
|
fence->handle,
|
|
D3D12_FENCE_SIGNAL_VALUE);
|
|
|
|
// ...and then block on it.
|
|
if (ID3D12Fence_GetCompletedValue(fence->handle) != D3D12_FENCE_SIGNAL_VALUE) {
|
|
res = ID3D12Fence_SetEventOnCompletion(
|
|
fence->handle,
|
|
D3D12_FENCE_SIGNAL_VALUE,
|
|
fence->event);
|
|
ERROR_CHECK_RETURN("Setting fence event failed", )
|
|
|
|
WaitForSingleObject(fence->event, INFINITE);
|
|
}
|
|
}
|
|
|
|
D3D12_ReleaseFence(
|
|
(SDL_GPURenderer *)renderer,
|
|
(SDL_GPUFence *)fence);
|
|
|
|
// Clean up
|
|
for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
|
|
D3D12_INTERNAL_CleanCommandBuffer(renderer, renderer->submittedCommandBuffers[i]);
|
|
}
|
|
|
|
D3D12_INTERNAL_PerformPendingDestroys(renderer);
|
|
|
|
SDL_UnlockMutex(renderer->submitLock);
|
|
}
|
|
|
|
static void D3D12_WaitForFences(
|
|
SDL_GPURenderer *driverData,
|
|
bool waitAll,
|
|
SDL_GPUFence *const *fences,
|
|
Uint32 numFences)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12Fence *fence;
|
|
HANDLE *events = SDL_stack_alloc(HANDLE, numFences);
|
|
HRESULT res;
|
|
|
|
SDL_LockMutex(renderer->submitLock);
|
|
|
|
for (Uint32 i = 0; i < numFences; i += 1) {
|
|
fence = (D3D12Fence *)fences[i];
|
|
|
|
res = ID3D12Fence_SetEventOnCompletion(
|
|
fence->handle,
|
|
D3D12_FENCE_SIGNAL_VALUE,
|
|
fence->event);
|
|
ERROR_CHECK_RETURN("Setting fence event failed", )
|
|
|
|
events[i] = fence->event;
|
|
}
|
|
|
|
WaitForMultipleObjects(
|
|
numFences,
|
|
events,
|
|
waitAll,
|
|
INFINITE);
|
|
|
|
// Check for cleanups
|
|
for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
|
|
Uint64 fenceValue = ID3D12Fence_GetCompletedValue(
|
|
renderer->submittedCommandBuffers[i]->inFlightFence->handle);
|
|
|
|
if (fenceValue == D3D12_FENCE_SIGNAL_VALUE) {
|
|
D3D12_INTERNAL_CleanCommandBuffer(
|
|
renderer,
|
|
renderer->submittedCommandBuffers[i]);
|
|
}
|
|
}
|
|
|
|
D3D12_INTERNAL_PerformPendingDestroys(renderer);
|
|
|
|
SDL_stack_free(events);
|
|
|
|
SDL_UnlockMutex(renderer->submitLock);
|
|
}
|
|
|
|
// Feature Queries
|
|
|
|
static bool D3D12_SupportsTextureFormat(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTextureFormat format,
|
|
SDL_GPUTextureType type,
|
|
SDL_GPUTextureUsageFlags usage)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
DXGI_FORMAT dxgiFormat = SDLToD3D12_TextureFormat[format];
|
|
D3D12_FEATURE_DATA_FORMAT_SUPPORT formatSupport = { dxgiFormat, D3D12_FORMAT_SUPPORT1_NONE, D3D12_FORMAT_SUPPORT2_NONE };
|
|
HRESULT res;
|
|
|
|
res = ID3D12Device_CheckFeatureSupport(
|
|
renderer->device,
|
|
D3D12_FEATURE_FORMAT_SUPPORT,
|
|
&formatSupport,
|
|
sizeof(formatSupport));
|
|
if (FAILED(res)) {
|
|
// Format is apparently unknown
|
|
return false;
|
|
}
|
|
|
|
// Is the texture type supported?
|
|
if (type == SDL_GPU_TEXTURETYPE_2D && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D)) {
|
|
return false;
|
|
}
|
|
if (type == SDL_GPU_TEXTURETYPE_2D_ARRAY && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D)) {
|
|
return false;
|
|
}
|
|
if (type == SDL_GPU_TEXTURETYPE_3D && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE3D)) {
|
|
return false;
|
|
}
|
|
if (type == SDL_GPU_TEXTURETYPE_CUBE && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURECUBE)) {
|
|
return false;
|
|
}
|
|
if (type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURECUBE)) {
|
|
return false;
|
|
}
|
|
|
|
// Are the usage flags supported?
|
|
if ((usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE)) {
|
|
return false;
|
|
}
|
|
if ((usage & (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ)) && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_SHADER_LOAD)) {
|
|
return false;
|
|
}
|
|
if ((usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) && !(formatSupport.Support2 & D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE)) {
|
|
return false;
|
|
}
|
|
if ((usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_RENDER_TARGET)) {
|
|
return false;
|
|
}
|
|
if ((usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool D3D12_SupportsSampleCount(
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPUTextureFormat format,
|
|
SDL_GPUSampleCount sampleCount)
|
|
{
|
|
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
|
|
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS featureData;
|
|
HRESULT res;
|
|
|
|
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|
|
featureData.Flags = (D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG)0;
|
|
#else
|
|
featureData.Flags = (D3D12_MULTISAMPLE_QUALITY_LEVEL_FLAGS)0;
|
|
#endif
|
|
featureData.Format = SDLToD3D12_TextureFormat[format];
|
|
featureData.SampleCount = SDLToD3D12_SampleCount[sampleCount];
|
|
res = ID3D12Device_CheckFeatureSupport(
|
|
renderer->device,
|
|
D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
|
|
&featureData,
|
|
sizeof(featureData));
|
|
|
|
return SUCCEEDED(res) && featureData.NumQualityLevels > 0;
|
|
}
|
|
|
|
static void D3D12_INTERNAL_InitBlitResources(
|
|
D3D12Renderer *renderer)
|
|
{
|
|
SDL_GPUShaderCreateInfo shaderCreateInfo;
|
|
SDL_GPUSamplerCreateInfo samplerCreateInfo;
|
|
|
|
renderer->blitPipelineCapacity = 2;
|
|
renderer->blitPipelineCount = 0;
|
|
renderer->blitPipelines = (BlitPipelineCacheEntry *)SDL_malloc(
|
|
renderer->blitPipelineCapacity * sizeof(BlitPipelineCacheEntry));
|
|
|
|
// Fullscreen vertex shader
|
|
SDL_zero(shaderCreateInfo);
|
|
shaderCreateInfo.code = (Uint8 *)D3D12_FullscreenVert;
|
|
shaderCreateInfo.code_size = sizeof(D3D12_FullscreenVert);
|
|
shaderCreateInfo.stage = SDL_GPU_SHADERSTAGE_VERTEX;
|
|
shaderCreateInfo.format = SDL_GPU_SHADERFORMAT_DXBC;
|
|
shaderCreateInfo.entrypoint = "main";
|
|
|
|
renderer->blitVertexShader = D3D12_CreateShader(
|
|
(SDL_GPURenderer *)renderer,
|
|
&shaderCreateInfo);
|
|
|
|
if (renderer->blitVertexShader == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile vertex shader for blit!");
|
|
}
|
|
|
|
// BlitFrom2D pixel shader
|
|
shaderCreateInfo.code = (Uint8 *)D3D12_BlitFrom2D;
|
|
shaderCreateInfo.code_size = sizeof(D3D12_BlitFrom2D);
|
|
shaderCreateInfo.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
|
|
shaderCreateInfo.num_samplers = 1;
|
|
shaderCreateInfo.num_uniform_buffers = 1;
|
|
|
|
renderer->blitFrom2DShader = D3D12_CreateShader(
|
|
(SDL_GPURenderer *)renderer,
|
|
&shaderCreateInfo);
|
|
|
|
if (renderer->blitFrom2DShader == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFrom2D pixel shader!");
|
|
}
|
|
|
|
// BlitFrom2DArray pixel shader
|
|
shaderCreateInfo.code = (Uint8 *)D3D12_BlitFrom2DArray;
|
|
shaderCreateInfo.code_size = sizeof(D3D12_BlitFrom2DArray);
|
|
|
|
renderer->blitFrom2DArrayShader = D3D12_CreateShader(
|
|
(SDL_GPURenderer *)renderer,
|
|
&shaderCreateInfo);
|
|
|
|
if (renderer->blitFrom2DArrayShader == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFrom2DArray pixel shader!");
|
|
}
|
|
|
|
// BlitFrom3D pixel shader
|
|
shaderCreateInfo.code = (Uint8 *)D3D12_BlitFrom3D;
|
|
shaderCreateInfo.code_size = sizeof(D3D12_BlitFrom3D);
|
|
|
|
renderer->blitFrom3DShader = D3D12_CreateShader(
|
|
(SDL_GPURenderer *)renderer,
|
|
&shaderCreateInfo);
|
|
|
|
if (renderer->blitFrom3DShader == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFrom3D pixel shader!");
|
|
}
|
|
|
|
// BlitFromCube pixel shader
|
|
shaderCreateInfo.code = (Uint8 *)D3D12_BlitFromCube;
|
|
shaderCreateInfo.code_size = sizeof(D3D12_BlitFromCube);
|
|
|
|
renderer->blitFromCubeShader = D3D12_CreateShader(
|
|
(SDL_GPURenderer *)renderer,
|
|
&shaderCreateInfo);
|
|
|
|
if (renderer->blitFromCubeShader == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFromCube pixel shader!");
|
|
}
|
|
|
|
// BlitFromCubeArray pixel shader
|
|
shaderCreateInfo.code = (Uint8 *)D3D12_BlitFromCubeArray;
|
|
shaderCreateInfo.code_size = sizeof(D3D12_BlitFromCubeArray);
|
|
|
|
renderer->blitFromCubeArrayShader = D3D12_CreateShader(
|
|
(SDL_GPURenderer *)renderer,
|
|
&shaderCreateInfo);
|
|
|
|
if (renderer->blitFromCubeArrayShader == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFromCubeArray pixel shader!");
|
|
}
|
|
|
|
// Create samplers
|
|
samplerCreateInfo.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
|
samplerCreateInfo.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
|
samplerCreateInfo.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
|
samplerCreateInfo.enable_anisotropy = 0;
|
|
samplerCreateInfo.enable_compare = 0;
|
|
samplerCreateInfo.mag_filter = SDL_GPU_FILTER_NEAREST;
|
|
samplerCreateInfo.min_filter = SDL_GPU_FILTER_NEAREST;
|
|
samplerCreateInfo.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST;
|
|
samplerCreateInfo.mip_lod_bias = 0.0f;
|
|
samplerCreateInfo.min_lod = 0;
|
|
samplerCreateInfo.max_lod = 1000;
|
|
samplerCreateInfo.max_anisotropy = 1.0f;
|
|
samplerCreateInfo.compare_op = SDL_GPU_COMPAREOP_ALWAYS;
|
|
|
|
renderer->blitNearestSampler = D3D12_CreateSampler(
|
|
(SDL_GPURenderer *)renderer,
|
|
&samplerCreateInfo);
|
|
|
|
if (renderer->blitNearestSampler == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create blit nearest sampler!");
|
|
}
|
|
|
|
samplerCreateInfo.mag_filter = SDL_GPU_FILTER_LINEAR;
|
|
samplerCreateInfo.min_filter = SDL_GPU_FILTER_LINEAR;
|
|
samplerCreateInfo.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR;
|
|
|
|
renderer->blitLinearSampler = D3D12_CreateSampler(
|
|
(SDL_GPURenderer *)renderer,
|
|
&samplerCreateInfo);
|
|
|
|
if (renderer->blitLinearSampler == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create blit linear sampler!");
|
|
}
|
|
}
|
|
|
|
static bool D3D12_PrepareDriver(SDL_VideoDevice *_this)
|
|
{
|
|
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|
|
return true;
|
|
#else
|
|
void *d3d12Dll;
|
|
void *dxgiDll;
|
|
PFN_D3D12_CREATE_DEVICE D3D12CreateDeviceFunc;
|
|
PFN_CREATE_DXGI_FACTORY1 CreateDXGIFactoryFunc;
|
|
HRESULT res;
|
|
ID3D12Device *device;
|
|
IDXGIFactory1 *factory;
|
|
IDXGIFactory4 *factory4;
|
|
IDXGIFactory6 *factory6;
|
|
IDXGIAdapter1 *adapter;
|
|
|
|
// Can we load D3D12?
|
|
|
|
d3d12Dll = SDL_LoadObject(D3D12_DLL);
|
|
if (d3d12Dll == NULL) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not find " D3D12_DLL);
|
|
return false;
|
|
}
|
|
|
|
D3D12CreateDeviceFunc = (PFN_D3D12_CREATE_DEVICE)SDL_LoadFunction(
|
|
d3d12Dll,
|
|
D3D12_CREATE_DEVICE_FUNC);
|
|
if (D3D12CreateDeviceFunc == NULL) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not find function " D3D12_CREATE_DEVICE_FUNC " in " D3D12_DLL);
|
|
SDL_UnloadObject(d3d12Dll);
|
|
return false;
|
|
}
|
|
|
|
// Can we load DXGI?
|
|
|
|
dxgiDll = SDL_LoadObject(DXGI_DLL);
|
|
if (dxgiDll == NULL) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not find " DXGI_DLL);
|
|
return false;
|
|
}
|
|
|
|
CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY1)SDL_LoadFunction(
|
|
dxgiDll,
|
|
CREATE_DXGI_FACTORY1_FUNC);
|
|
if (CreateDXGIFactoryFunc == NULL) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not find function " CREATE_DXGI_FACTORY1_FUNC " in " DXGI_DLL);
|
|
SDL_UnloadObject(dxgiDll);
|
|
return false;
|
|
}
|
|
|
|
// Can we create a device?
|
|
|
|
// Create the DXGI factory
|
|
res = CreateDXGIFactoryFunc(
|
|
&D3D_IID_IDXGIFactory1,
|
|
(void **)&factory);
|
|
if (FAILED(res)) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not create DXGIFactory");
|
|
SDL_UnloadObject(d3d12Dll);
|
|
SDL_UnloadObject(dxgiDll);
|
|
return false;
|
|
}
|
|
|
|
// Check for DXGI 1.4 support
|
|
res = IDXGIFactory1_QueryInterface(
|
|
factory,
|
|
D3D_GUID(D3D_IID_IDXGIFactory4),
|
|
(void **)&factory4);
|
|
if (FAILED(res)) {
|
|
IDXGIFactory1_Release(factory);
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Failed to find DXGI1.4 support, required for DX12");
|
|
SDL_UnloadObject(d3d12Dll);
|
|
SDL_UnloadObject(dxgiDll);
|
|
return false;
|
|
}
|
|
IDXGIFactory4_Release(factory4);
|
|
|
|
res = IDXGIFactory1_QueryInterface(
|
|
factory,
|
|
D3D_GUID(D3D_IID_IDXGIFactory6),
|
|
(void **)&factory6);
|
|
if (SUCCEEDED(res)) {
|
|
res = IDXGIFactory6_EnumAdapterByGpuPreference(
|
|
factory6,
|
|
0,
|
|
DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
|
|
D3D_GUID(D3D_IID_IDXGIAdapter1),
|
|
(void **)&adapter);
|
|
IDXGIFactory6_Release(factory6);
|
|
} else {
|
|
res = IDXGIFactory1_EnumAdapters1(
|
|
factory,
|
|
0,
|
|
&adapter);
|
|
}
|
|
if (FAILED(res)) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Failed to find adapter for D3D12Device");
|
|
IDXGIFactory1_Release(factory);
|
|
SDL_UnloadObject(d3d12Dll);
|
|
SDL_UnloadObject(dxgiDll);
|
|
return false;
|
|
}
|
|
|
|
res = D3D12CreateDeviceFunc(
|
|
(IUnknown *)adapter,
|
|
D3D_FEATURE_LEVEL_CHOICE,
|
|
D3D_GUID(D3D_IID_ID3D12Device),
|
|
(void **)&device);
|
|
|
|
if (SUCCEEDED(res)) {
|
|
ID3D12Device_Release(device);
|
|
}
|
|
IDXGIAdapter1_Release(adapter);
|
|
IDXGIFactory1_Release(factory);
|
|
|
|
SDL_UnloadObject(d3d12Dll);
|
|
SDL_UnloadObject(dxgiDll);
|
|
|
|
if (FAILED(res)) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not create D3D12Device with feature level " D3D_FEATURE_LEVEL_CHOICE_STR);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)) && defined(HAVE_IDXGIINFOQUEUE)
|
|
static void D3D12_INTERNAL_TryInitializeDXGIDebug(D3D12Renderer *renderer)
|
|
{
|
|
PFN_DXGI_GET_DEBUG_INTERFACE DXGIGetDebugInterfaceFunc;
|
|
HRESULT res;
|
|
|
|
renderer->dxgidebug_dll = SDL_LoadObject(DXGIDEBUG_DLL);
|
|
if (renderer->dxgidebug_dll == NULL) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not find " DXGIDEBUG_DLL);
|
|
return;
|
|
}
|
|
|
|
DXGIGetDebugInterfaceFunc = (PFN_DXGI_GET_DEBUG_INTERFACE)SDL_LoadFunction(
|
|
renderer->dxgidebug_dll,
|
|
DXGI_GET_DEBUG_INTERFACE_FUNC);
|
|
if (DXGIGetDebugInterfaceFunc == NULL) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " DXGI_GET_DEBUG_INTERFACE_FUNC);
|
|
return;
|
|
}
|
|
|
|
res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIDebug, (void **)&renderer->dxgiDebug);
|
|
if (FAILED(res)) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get IDXGIDebug interface");
|
|
}
|
|
|
|
res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIInfoQueue, (void **)&renderer->dxgiInfoQueue);
|
|
if (FAILED(res)) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get IDXGIInfoQueue interface");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void D3D12_INTERNAL_TryInitializeD3D12Debug(D3D12Renderer *renderer)
|
|
{
|
|
PFN_D3D12_GET_DEBUG_INTERFACE D3D12GetDebugInterfaceFunc;
|
|
HRESULT res;
|
|
|
|
D3D12GetDebugInterfaceFunc = (PFN_D3D12_GET_DEBUG_INTERFACE)SDL_LoadFunction(
|
|
renderer->d3d12_dll,
|
|
D3D12_GET_DEBUG_INTERFACE_FUNC);
|
|
if (D3D12GetDebugInterfaceFunc == NULL) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not load function: " D3D12_GET_DEBUG_INTERFACE_FUNC);
|
|
return;
|
|
}
|
|
|
|
res = D3D12GetDebugInterfaceFunc(D3D_GUID(D3D_IID_ID3D12Debug), (void **)&renderer->d3d12Debug);
|
|
if (FAILED(res)) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get ID3D12Debug interface");
|
|
return;
|
|
}
|
|
|
|
ID3D12Debug_EnableDebugLayer(renderer->d3d12Debug);
|
|
}
|
|
|
|
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
static void D3D12_INTERNAL_TryInitializeD3D12DebugInfoQueue(D3D12Renderer *renderer)
|
|
{
|
|
ID3D12InfoQueue *infoQueue = NULL;
|
|
D3D12_MESSAGE_SEVERITY severities[] = { D3D12_MESSAGE_SEVERITY_INFO };
|
|
D3D12_INFO_QUEUE_FILTER filter;
|
|
HRESULT res;
|
|
|
|
res = ID3D12Device_QueryInterface(
|
|
renderer->device,
|
|
D3D_GUID(D3D_IID_ID3D12InfoQueue),
|
|
(void **)&infoQueue);
|
|
if (FAILED(res)) {
|
|
ERROR_CHECK_RETURN("Failed to convert ID3D12Device to ID3D12InfoQueue", );
|
|
}
|
|
|
|
SDL_zero(filter);
|
|
filter.DenyList.NumSeverities = 1;
|
|
filter.DenyList.pSeverityList = severities;
|
|
ID3D12InfoQueue_PushStorageFilter(
|
|
infoQueue,
|
|
&filter);
|
|
|
|
ID3D12InfoQueue_SetBreakOnSeverity(
|
|
infoQueue,
|
|
D3D12_MESSAGE_SEVERITY_ERROR,
|
|
true);
|
|
|
|
ID3D12InfoQueue_SetBreakOnSeverity(
|
|
infoQueue,
|
|
D3D12_MESSAGE_SEVERITY_CORRUPTION,
|
|
true);
|
|
|
|
ID3D12InfoQueue_Release(infoQueue);
|
|
}
|
|
#endif
|
|
|
|
static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SDL_PropertiesID props)
|
|
{
|
|
SDL_GPUDevice *result;
|
|
D3D12Renderer *renderer;
|
|
HRESULT res;
|
|
|
|
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
PFN_D3D12_XBOX_CREATE_DEVICE D3D12XboxCreateDeviceFunc;
|
|
D3D12XBOX_CREATE_DEVICE_PARAMETERS createDeviceParams;
|
|
#else
|
|
PFN_CREATE_DXGI_FACTORY1 CreateDXGIFactoryFunc;
|
|
IDXGIFactory1 *factory1;
|
|
IDXGIFactory5 *factory5;
|
|
IDXGIFactory6 *factory6;
|
|
DXGI_ADAPTER_DESC1 adapterDesc;
|
|
PFN_D3D12_CREATE_DEVICE D3D12CreateDeviceFunc;
|
|
#endif
|
|
D3D12_FEATURE_DATA_ARCHITECTURE architecture;
|
|
D3D12_COMMAND_QUEUE_DESC queueDesc;
|
|
|
|
renderer = (D3D12Renderer *)SDL_calloc(1, sizeof(D3D12Renderer));
|
|
|
|
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
// Load the DXGI library
|
|
renderer->dxgi_dll = SDL_LoadObject(DXGI_DLL);
|
|
if (renderer->dxgi_dll == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not find " DXGI_DLL);
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef HAVE_IDXGIINFOQUEUE
|
|
// Initialize the DXGI debug layer, if applicable
|
|
if (debugMode) {
|
|
D3D12_INTERNAL_TryInitializeDXGIDebug(renderer);
|
|
}
|
|
#endif
|
|
|
|
// Load the CreateDXGIFactory1 function
|
|
CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY1)SDL_LoadFunction(
|
|
renderer->dxgi_dll,
|
|
CREATE_DXGI_FACTORY1_FUNC);
|
|
if (CreateDXGIFactoryFunc == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " CREATE_DXGI_FACTORY1_FUNC);
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
// Create the DXGI factory
|
|
res = CreateDXGIFactoryFunc(
|
|
&D3D_IID_IDXGIFactory1,
|
|
(void **)&factory1);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("Could not create DXGIFactory", NULL);
|
|
}
|
|
|
|
// Check for DXGI 1.4 support
|
|
res = IDXGIFactory1_QueryInterface(
|
|
factory1,
|
|
D3D_GUID(D3D_IID_IDXGIFactory4),
|
|
(void **)&renderer->factory);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("DXGI1.4 support not found, required for DX12", NULL);
|
|
}
|
|
IDXGIFactory1_Release(factory1);
|
|
|
|
// Check for explicit tearing support
|
|
res = IDXGIFactory4_QueryInterface(
|
|
renderer->factory,
|
|
D3D_GUID(D3D_IID_IDXGIFactory5),
|
|
(void **)&factory5);
|
|
if (SUCCEEDED(res)) {
|
|
res = IDXGIFactory5_CheckFeatureSupport(
|
|
factory5,
|
|
DXGI_FEATURE_PRESENT_ALLOW_TEARING,
|
|
&renderer->supportsTearing,
|
|
sizeof(renderer->supportsTearing));
|
|
if (FAILED(res)) {
|
|
renderer->supportsTearing = false;
|
|
}
|
|
IDXGIFactory5_Release(factory5);
|
|
}
|
|
|
|
// Select the appropriate device for rendering
|
|
res = IDXGIFactory4_QueryInterface(
|
|
renderer->factory,
|
|
D3D_GUID(D3D_IID_IDXGIFactory6),
|
|
(void **)&factory6);
|
|
if (SUCCEEDED(res)) {
|
|
res = IDXGIFactory6_EnumAdapterByGpuPreference(
|
|
factory6,
|
|
0,
|
|
preferLowPower ? DXGI_GPU_PREFERENCE_MINIMUM_POWER : DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
|
|
D3D_GUID(D3D_IID_IDXGIAdapter1),
|
|
(void **)&renderer->adapter);
|
|
IDXGIFactory6_Release(factory6);
|
|
} else {
|
|
res = IDXGIFactory4_EnumAdapters1(
|
|
renderer->factory,
|
|
0,
|
|
&renderer->adapter);
|
|
}
|
|
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("Could not find adapter for D3D12Device", NULL);
|
|
}
|
|
|
|
// Get information about the selected adapter. Used for logging info.
|
|
res = IDXGIAdapter1_GetDesc1(renderer->adapter, &adapterDesc);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("Could not get adapter description", NULL);
|
|
}
|
|
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: D3D12");
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "D3D12 Adapter: %S", adapterDesc.Description);
|
|
#endif
|
|
|
|
// Load the D3D library
|
|
renderer->d3d12_dll = SDL_LoadObject(D3D12_DLL);
|
|
if (renderer->d3d12_dll == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not find " D3D12_DLL);
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
// Load the CreateDevice function
|
|
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
D3D12XboxCreateDeviceFunc = (PFN_D3D12_XBOX_CREATE_DEVICE)SDL_LoadFunction(
|
|
renderer->d3d12_dll,
|
|
"D3D12XboxCreateDevice");
|
|
if (D3D12XboxCreateDeviceFunc == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: D3D12XboxCreateDevice");
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
#else
|
|
D3D12CreateDeviceFunc = (PFN_D3D12_CREATE_DEVICE)SDL_LoadFunction(
|
|
renderer->d3d12_dll,
|
|
D3D12_CREATE_DEVICE_FUNC);
|
|
if (D3D12CreateDeviceFunc == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " D3D12_CREATE_DEVICE_FUNC);
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
renderer->D3D12SerializeRootSignature_func = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)SDL_LoadFunction(
|
|
renderer->d3d12_dll,
|
|
D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC);
|
|
if (renderer->D3D12SerializeRootSignature_func == NULL) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC);
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
// Initialize the D3D12 debug layer, if applicable
|
|
if (debugMode) {
|
|
D3D12_INTERNAL_TryInitializeD3D12Debug(renderer);
|
|
}
|
|
|
|
// Create the D3D12Device
|
|
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
if (s_Device != NULL) {
|
|
renderer->device = s_Device;
|
|
} else {
|
|
SDL_zero(createDeviceParams);
|
|
createDeviceParams.Version = D3D12_SDK_VERSION;
|
|
createDeviceParams.GraphicsCommandQueueRingSizeBytes = D3D12XBOX_DEFAULT_SIZE_BYTES;
|
|
createDeviceParams.GraphicsScratchMemorySizeBytes = D3D12XBOX_DEFAULT_SIZE_BYTES;
|
|
createDeviceParams.ComputeScratchMemorySizeBytes = D3D12XBOX_DEFAULT_SIZE_BYTES;
|
|
createDeviceParams.DisableGeometryShaderAllocations = TRUE;
|
|
createDeviceParams.DisableTessellationShaderAllocations = TRUE;
|
|
#if defined(SDL_PLATFORM_XBOXSERIES)
|
|
createDeviceParams.DisableDXR = TRUE;
|
|
#endif
|
|
if (debugMode) {
|
|
createDeviceParams.ProcessDebugFlags = D3D12XBOX_PROCESS_DEBUG_FLAG_DEBUG;
|
|
}
|
|
|
|
res = D3D12XboxCreateDeviceFunc(
|
|
NULL,
|
|
&createDeviceParams,
|
|
IID_GRAPHICS_PPV_ARGS(&renderer->device));
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("Could not create D3D12Device", NULL);
|
|
}
|
|
|
|
res = renderer->device->SetFrameIntervalX(
|
|
NULL,
|
|
D3D12XBOX_FRAME_INTERVAL_60_HZ,
|
|
MAX_FRAMES_IN_FLIGHT - 1,
|
|
D3D12XBOX_FRAME_INTERVAL_FLAG_NONE);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("Could not get set frame interval", NULL);
|
|
}
|
|
|
|
res = renderer->device->ScheduleFrameEventX(
|
|
D3D12XBOX_FRAME_EVENT_ORIGIN,
|
|
0,
|
|
NULL,
|
|
D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("Could not schedule frame events", NULL);
|
|
}
|
|
|
|
s_Device = renderer->device;
|
|
}
|
|
#else
|
|
res = D3D12CreateDeviceFunc(
|
|
(IUnknown *)renderer->adapter,
|
|
D3D_FEATURE_LEVEL_CHOICE,
|
|
D3D_GUID(D3D_IID_ID3D12Device),
|
|
(void **)&renderer->device);
|
|
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("Could not create D3D12Device", NULL);
|
|
}
|
|
|
|
// Initialize the D3D12 debug info queue, if applicable
|
|
if (debugMode) {
|
|
D3D12_INTERNAL_TryInitializeD3D12DebugInfoQueue(renderer);
|
|
}
|
|
#endif
|
|
|
|
// Check UMA
|
|
architecture.NodeIndex = 0;
|
|
res = ID3D12Device_CheckFeatureSupport(
|
|
renderer->device,
|
|
D3D12_FEATURE_ARCHITECTURE,
|
|
&architecture,
|
|
sizeof(D3D12_FEATURE_DATA_ARCHITECTURE));
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("Could not get device architecture", NULL);
|
|
}
|
|
|
|
renderer->UMA = (bool)architecture.UMA;
|
|
renderer->UMACacheCoherent = (bool)architecture.CacheCoherentUMA;
|
|
|
|
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
renderer->GPUUploadHeapSupported = false;
|
|
#else
|
|
// Check "GPU Upload Heap" support (for fast uniform buffers)
|
|
D3D12_FEATURE_DATA_D3D12_OPTIONS16 options16; // 15 wasn't enough, huh?
|
|
renderer->GPUUploadHeapSupported = false;
|
|
res = ID3D12Device_CheckFeatureSupport(
|
|
renderer->device,
|
|
D3D12_FEATURE_D3D12_OPTIONS16,
|
|
&options16,
|
|
sizeof(options16));
|
|
|
|
if (SUCCEEDED(res)) {
|
|
renderer->GPUUploadHeapSupported = options16.GPUUploadHeapSupported;
|
|
}
|
|
#endif
|
|
|
|
// Create command queue
|
|
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
if (s_CommandQueue != NULL) {
|
|
renderer->commandQueue = s_CommandQueue;
|
|
} else {
|
|
#endif
|
|
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
|
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
|
queueDesc.NodeMask = 0;
|
|
queueDesc.Priority = 0;
|
|
|
|
res = ID3D12Device_CreateCommandQueue(
|
|
renderer->device,
|
|
&queueDesc,
|
|
D3D_GUID(D3D_IID_ID3D12CommandQueue),
|
|
(void **)&renderer->commandQueue);
|
|
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("Could not create D3D12CommandQueue", NULL);
|
|
}
|
|
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
s_CommandQueue = renderer->commandQueue;
|
|
}
|
|
#endif
|
|
|
|
// Create indirect command signatures
|
|
|
|
D3D12_COMMAND_SIGNATURE_DESC commandSignatureDesc;
|
|
D3D12_INDIRECT_ARGUMENT_DESC indirectArgumentDesc;
|
|
SDL_zero(indirectArgumentDesc);
|
|
|
|
indirectArgumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
|
|
commandSignatureDesc.NodeMask = 0;
|
|
commandSignatureDesc.ByteStride = sizeof(SDL_GPUIndirectDrawCommand);
|
|
commandSignatureDesc.NumArgumentDescs = 1;
|
|
commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc;
|
|
|
|
res = ID3D12Device_CreateCommandSignature(
|
|
renderer->device,
|
|
&commandSignatureDesc,
|
|
NULL,
|
|
D3D_GUID(D3D_IID_ID3D12CommandSignature),
|
|
(void **)&renderer->indirectDrawCommandSignature);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("Could not create indirect draw command signature", NULL)
|
|
}
|
|
|
|
indirectArgumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
|
|
commandSignatureDesc.ByteStride = sizeof(SDL_GPUIndexedIndirectDrawCommand);
|
|
commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc;
|
|
|
|
res = ID3D12Device_CreateCommandSignature(
|
|
renderer->device,
|
|
&commandSignatureDesc,
|
|
NULL,
|
|
D3D_GUID(D3D_IID_ID3D12CommandSignature),
|
|
(void **)&renderer->indirectIndexedDrawCommandSignature);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("Could not create indirect indexed draw command signature", NULL)
|
|
}
|
|
|
|
indirectArgumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
|
|
commandSignatureDesc.ByteStride = sizeof(SDL_GPUIndirectDispatchCommand);
|
|
commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc;
|
|
|
|
res = ID3D12Device_CreateCommandSignature(
|
|
renderer->device,
|
|
&commandSignatureDesc,
|
|
NULL,
|
|
D3D_GUID(D3D_IID_ID3D12CommandSignature),
|
|
(void **)&renderer->indirectDispatchCommandSignature);
|
|
if (FAILED(res)) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
ERROR_CHECK_RETURN("Could not create indirect dispatch command signature", NULL)
|
|
}
|
|
|
|
// Initialize pools
|
|
|
|
renderer->submittedCommandBufferCapacity = 4;
|
|
renderer->submittedCommandBufferCount = 0;
|
|
renderer->submittedCommandBuffers = (D3D12CommandBuffer **)SDL_calloc(
|
|
renderer->submittedCommandBufferCapacity, sizeof(D3D12CommandBuffer *));
|
|
if (!renderer->submittedCommandBuffers) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
renderer->uniformBufferPoolCapacity = 4;
|
|
renderer->uniformBufferPoolCount = 0;
|
|
renderer->uniformBufferPool = (D3D12UniformBuffer **)SDL_calloc(
|
|
renderer->uniformBufferPoolCapacity, sizeof(D3D12UniformBuffer *));
|
|
if (!renderer->uniformBufferPool) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
renderer->claimedWindowCapacity = 4;
|
|
renderer->claimedWindowCount = 0;
|
|
renderer->claimedWindows = (D3D12WindowData **)SDL_calloc(
|
|
renderer->claimedWindowCapacity, sizeof(D3D12WindowData *));
|
|
if (!renderer->claimedWindows) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
renderer->availableFenceCapacity = 4;
|
|
renderer->availableFenceCount = 0;
|
|
renderer->availableFences = (D3D12Fence **)SDL_calloc(
|
|
renderer->availableFenceCapacity, sizeof(D3D12Fence *));
|
|
if (!renderer->availableFences) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
// Initialize CPU descriptor heaps
|
|
for (Uint32 i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; i += 1) {
|
|
renderer->stagingDescriptorHeaps[i] = D3D12_INTERNAL_CreateDescriptorHeap(
|
|
renderer,
|
|
(D3D12_DESCRIPTOR_HEAP_TYPE)i,
|
|
(i <= D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) ? VIEW_SAMPLER_STAGING_DESCRIPTOR_COUNT : TARGET_STAGING_DESCRIPTOR_COUNT,
|
|
true);
|
|
}
|
|
|
|
// Initialize GPU descriptor heaps
|
|
for (Uint32 i = 0; i < 2; i += 1) {
|
|
renderer->descriptorHeapPools[i].lock = SDL_CreateMutex();
|
|
renderer->descriptorHeapPools[i].capacity = 4;
|
|
renderer->descriptorHeapPools[i].count = 4;
|
|
renderer->descriptorHeapPools[i].heaps = (D3D12DescriptorHeap **)SDL_calloc(
|
|
renderer->descriptorHeapPools[i].capacity, sizeof(D3D12DescriptorHeap *));
|
|
|
|
for (Uint32 j = 0; j < renderer->descriptorHeapPools[i].capacity; j += 1) {
|
|
renderer->descriptorHeapPools[i].heaps[j] = D3D12_INTERNAL_CreateDescriptorHeap(
|
|
renderer,
|
|
(D3D12_DESCRIPTOR_HEAP_TYPE)i,
|
|
i == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ? VIEW_GPU_DESCRIPTOR_COUNT : SAMPLER_GPU_DESCRIPTOR_COUNT,
|
|
false);
|
|
}
|
|
}
|
|
|
|
// Deferred resource releasing
|
|
|
|
renderer->buffersToDestroyCapacity = 4;
|
|
renderer->buffersToDestroyCount = 0;
|
|
renderer->buffersToDestroy = (D3D12Buffer **)SDL_calloc(
|
|
renderer->buffersToDestroyCapacity, sizeof(D3D12Buffer *));
|
|
if (!renderer->buffersToDestroy) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
renderer->texturesToDestroyCapacity = 4;
|
|
renderer->texturesToDestroyCount = 0;
|
|
renderer->texturesToDestroy = (D3D12Texture **)SDL_calloc(
|
|
renderer->texturesToDestroyCapacity, sizeof(D3D12Texture *));
|
|
if (!renderer->texturesToDestroy) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
renderer->samplersToDestroyCapacity = 4;
|
|
renderer->samplersToDestroyCount = 0;
|
|
renderer->samplersToDestroy = (D3D12Sampler **)SDL_calloc(
|
|
renderer->samplersToDestroyCapacity, sizeof(D3D12Sampler *));
|
|
if (!renderer->samplersToDestroy) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
renderer->graphicsPipelinesToDestroyCapacity = 4;
|
|
renderer->graphicsPipelinesToDestroyCount = 0;
|
|
renderer->graphicsPipelinesToDestroy = (D3D12GraphicsPipeline **)SDL_calloc(
|
|
renderer->graphicsPipelinesToDestroyCapacity, sizeof(D3D12GraphicsPipeline *));
|
|
if (!renderer->graphicsPipelinesToDestroy) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
renderer->computePipelinesToDestroyCapacity = 4;
|
|
renderer->computePipelinesToDestroyCount = 0;
|
|
renderer->computePipelinesToDestroy = (D3D12ComputePipeline **)SDL_calloc(
|
|
renderer->computePipelinesToDestroyCapacity, sizeof(D3D12ComputePipeline *));
|
|
if (!renderer->computePipelinesToDestroy) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
// Locks
|
|
renderer->stagingDescriptorHeapLock = SDL_CreateMutex();
|
|
renderer->acquireCommandBufferLock = SDL_CreateMutex();
|
|
renderer->acquireUniformBufferLock = SDL_CreateMutex();
|
|
renderer->submitLock = SDL_CreateMutex();
|
|
renderer->windowLock = SDL_CreateMutex();
|
|
renderer->fenceLock = SDL_CreateMutex();
|
|
renderer->disposeLock = SDL_CreateMutex();
|
|
|
|
renderer->debug_mode = debugMode;
|
|
|
|
renderer->semantic = SDL_GetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING, "TEXCOORD");
|
|
|
|
// Blit resources
|
|
D3D12_INTERNAL_InitBlitResources(renderer);
|
|
|
|
// Create the SDL_GPU Device
|
|
result = (SDL_GPUDevice *)SDL_calloc(1, sizeof(SDL_GPUDevice));
|
|
|
|
if (!result) {
|
|
D3D12_INTERNAL_DestroyRenderer(renderer);
|
|
return NULL;
|
|
}
|
|
|
|
ASSIGN_DRIVER(D3D12)
|
|
result->driverData = (SDL_GPURenderer *)renderer;
|
|
result->debug_mode = debugMode;
|
|
renderer->sdlGPUDevice = result;
|
|
|
|
return result;
|
|
}
|
|
|
|
SDL_GPUBootstrap D3D12Driver = {
|
|
"direct3d12",
|
|
SDL_GPU_DRIVER_D3D12,
|
|
SDL_GPU_SHADERFORMAT_DXIL,
|
|
D3D12_PrepareDriver,
|
|
D3D12_CreateDevice
|
|
};
|
|
|
|
#endif // SDL_GPU_D3D12
|
|
|
|
// GDK-specific APIs
|
|
|
|
#ifdef SDL_PLATFORM_GDK
|
|
|
|
void SDL_GDKSuspendGPU(SDL_GPUDevice *device)
|
|
{
|
|
#if defined(SDL_GPU_D3D12) && (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
D3D12Renderer *renderer = (D3D12Renderer *)device->driverData;
|
|
HRESULT res;
|
|
if (device == NULL) {
|
|
SDL_SetError("Invalid GPU device");
|
|
return;
|
|
}
|
|
|
|
SDL_LockMutex(renderer->submitLock);
|
|
res = renderer->commandQueue->SuspendX(0);
|
|
if (FAILED(res)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "SuspendX failed: %X", res);
|
|
}
|
|
SDL_UnlockMutex(renderer->submitLock);
|
|
#endif
|
|
}
|
|
|
|
void SDL_GDKResumeGPU(SDL_GPUDevice *device)
|
|
{
|
|
#if defined(SDL_GPU_D3D12) && (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
|
|
D3D12Renderer *renderer = (D3D12Renderer *)device->driverData;
|
|
HRESULT res;
|
|
if (device == NULL) {
|
|
SDL_SetError("Invalid GPU device");
|
|
return;
|
|
}
|
|
|
|
SDL_LockMutex(renderer->submitLock);
|
|
res = renderer->commandQueue->ResumeX();
|
|
if (FAILED(res)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "ResumeX failed: %X", res);
|
|
}
|
|
SDL_UnlockMutex(renderer->submitLock);
|
|
|
|
res = renderer->device->SetFrameIntervalX(
|
|
NULL,
|
|
D3D12XBOX_FRAME_INTERVAL_60_HZ,
|
|
MAX_FRAMES_IN_FLIGHT - 1,
|
|
D3D12XBOX_FRAME_INTERVAL_FLAG_NONE);
|
|
if (FAILED(res)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not set frame interval: %X", res);
|
|
}
|
|
|
|
res = renderer->device->ScheduleFrameEventX(
|
|
D3D12XBOX_FRAME_EVENT_ORIGIN,
|
|
0,
|
|
NULL,
|
|
D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE);
|
|
if (FAILED(res)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not schedule frame events: %X", res);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endif // SDL_PLATFORM_GDK
|