SDL

Форк
0
/
SDL_gpu_d3d12.c 
8257 строк · 296.6 Кб
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
4

5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8

9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12

13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21

22
#include "SDL_internal.h"
23

24
#if SDL_GPU_D3D12
25

26
#include "../../video/directx/SDL_d3d12.h"
27
#include "../SDL_sysgpu.h"
28
#include "SDL_hashtable.h"
29

30
// Built-in shaders, compiled with compile_shaders.bat
31

32
#define g_FullscreenVert  D3D12_FullscreenVert
33
#define g_BlitFrom2D      D3D12_BlitFrom2D
34
#define g_BlitFrom2DArray D3D12_BlitFrom2DArray
35
#define g_BlitFrom3D      D3D12_BlitFrom3D
36
#define g_BlitFromCube    D3D12_BlitFromCube
37
#if defined(SDL_PLATFORM_XBOXSERIES)
38
#include "D3D12_Blit_Series.h"
39
#elif defined(SDL_PLATFORM_XBOXONE)
40
#include "D3D12_Blit_One.h"
41
#else
42
#include "D3D12_Blit.h"
43
#endif
44
#undef g_FullscreenVert
45
#undef g_BlitFrom2D
46
#undef g_BlitFrom2DArray
47
#undef g_BlitFrom3D
48
#undef g_BlitFromCube
49

50
// Macros
51

52
#define ERROR_CHECK(msg)                                     \
53
    if (FAILED(res)) {                                       \
54
        D3D12_INTERNAL_LogError(renderer->device, msg, res); \
55
    }
56

57
#define ERROR_CHECK_RETURN(msg, ret)                         \
58
    if (FAILED(res)) {                                       \
59
        D3D12_INTERNAL_LogError(renderer->device, msg, res); \
60
        return ret;                                          \
61
    }
62

63
// Defines
64
#if defined(_WIN32)
65
#if defined(SDL_PLATFORM_XBOXSERIES)
66
#define D3D12_DLL "d3d12_xs.dll"
67
#elif defined(SDL_PLATFORM_XBOXONE)
68
#define D3D12_DLL "d3d12_x.dll"
69
#else
70
#define D3D12_DLL "d3d12.dll"
71
#endif
72
#define DXGI_DLL      "dxgi.dll"
73
#define DXGIDEBUG_DLL "dxgidebug.dll"
74
#elif defined(__APPLE__)
75
#define D3D12_DLL     "libdxvk_d3d12.dylib"
76
#define DXGI_DLL      "libdxvk_dxgi.dylib"
77
#define DXGIDEBUG_DLL "libdxvk_dxgidebug.dylib"
78
#else
79
#define D3D12_DLL     "libdxvk_d3d12.so"
80
#define DXGI_DLL      "libdxvk_dxgi.so"
81
#define DXGIDEBUG_DLL "libdxvk_dxgidebug.so"
82
#endif
83

84
#define D3D12_CREATE_DEVICE_FUNC            "D3D12CreateDevice"
85
#define D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC "D3D12SerializeRootSignature"
86
#define CREATE_DXGI_FACTORY1_FUNC           "CreateDXGIFactory1"
87
#define DXGI_GET_DEBUG_INTERFACE_FUNC       "DXGIGetDebugInterface"
88
#define D3D12_GET_DEBUG_INTERFACE_FUNC      "D3D12GetDebugInterface"
89
#define WINDOW_PROPERTY_DATA                "SDL_GPUD3D12WindowPropertyData"
90
#define D3D_FEATURE_LEVEL_CHOICE            D3D_FEATURE_LEVEL_11_1
91
#define D3D_FEATURE_LEVEL_CHOICE_STR        "11_1"
92
// FIXME: just use sysgpu.h defines
93
#define MAX_ROOT_SIGNATURE_PARAMETERS         64
94
#define VIEW_GPU_DESCRIPTOR_COUNT             65536
95
#define SAMPLER_GPU_DESCRIPTOR_COUNT          2048
96
#define VIEW_SAMPLER_STAGING_DESCRIPTOR_COUNT 1000000
97
#define TARGET_STAGING_DESCRIPTOR_COUNT       1000000
98
#define D3D12_FENCE_UNSIGNALED_VALUE          0
99
#define D3D12_FENCE_SIGNAL_VALUE              1
100

101
#define SDL_GPU_SHADERSTAGE_COMPUTE (SDL_GPUShaderStage)2
102

103
#define EXPAND_ELEMENTS_IF_NEEDED(arr, initialValue, type) \
104
    if (arr->count == arr->capacity) {                     \
105
        if (arr->capacity == 0) {                          \
106
            arr->capacity = initialValue;                  \
107
        } else {                                           \
108
            arr->capacity *= 2;                            \
109
        }                                                  \
110
        arr->elements = (type *)SDL_realloc(               \
111
            arr->elements,                                 \
112
            arr->capacity * sizeof(type));                 \
113
    }
114

115
#ifdef _WIN32
116
#define HRESULT_FMT "(0x%08lX)"
117
#else
118
#define HRESULT_FMT "(0x%08X)"
119
#endif
120

121
// Function Pointer Signatures
122
typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY1)(const GUID *riid, void **ppFactory);
123
typedef HRESULT(WINAPI *PFN_DXGI_GET_DEBUG_INTERFACE)(const GUID *riid, void **ppDebug);
124

125
// IIDs (from https://www.magnumdb.com/)
126
static const IID D3D_IID_IDXGIFactory1 = { 0x770aae78, 0xf26f, 0x4dba, { 0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87 } };
127
static const IID D3D_IID_IDXGIFactory4 = { 0x1bc6ea02, 0xef36, 0x464f, { 0xbf, 0x0c, 0x21, 0xca, 0x39, 0xe5, 0x16, 0x8a } };
128
static const IID D3D_IID_IDXGIFactory5 = { 0x7632e1f5, 0xee65, 0x4dca, { 0x87, 0xfd, 0x84, 0xcd, 0x75, 0xf8, 0x83, 0x8d } };
129
static const IID D3D_IID_IDXGIFactory6 = { 0xc1b6694f, 0xff09, 0x44a9, { 0xb0, 0x3c, 0x77, 0x90, 0x0a, 0x0a, 0x1d, 0x17 } };
130
static const IID D3D_IID_IDXGIAdapter1 = { 0x29038f61, 0x3839, 0x4626, { 0x91, 0xfd, 0x08, 0x68, 0x79, 0x01, 0x1a, 0x05 } };
131
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
132
static const IID D3D_IID_IDXGIDevice1 = { 0x77db970f, 0x6276, 0x48ba, { 0xba, 0x28, 0x07, 0x01, 0x43, 0xb4, 0x39, 0x2c } };
133
#endif
134
static const IID D3D_IID_IDXGISwapChain3 = { 0x94d99bdb, 0xf1f8, 0x4ab0, { 0xb2, 0x36, 0x7d, 0xa0, 0x17, 0x0e, 0xda, 0xb1 } };
135
static const IID D3D_IID_IDXGIDebug = { 0x119e7452, 0xde9e, 0x40fe, { 0x88, 0x06, 0x88, 0xf9, 0x0c, 0x12, 0xb4, 0x41 } };
136
static const IID D3D_IID_IDXGIInfoQueue = { 0xd67441c7, 0x672a, 0x476f, { 0x9e, 0x82, 0xcd, 0x55, 0xb4, 0x49, 0x49, 0xce } };
137
static const GUID D3D_IID_DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, { 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x08 } };
138
static const GUID D3D_IID_D3DDebugObjectName = { 0x429b8c22, 0x9188, 0x4b0c, { 0x87, 0x42, 0xac, 0xb0, 0xbf, 0x85, 0xc2, 0x00 } };
139

140
static const IID D3D_IID_ID3D12Device = { 0x189819f1, 0x1db6, 0x4b57, { 0xbe, 0x54, 0x18, 0x21, 0x33, 0x9b, 0x85, 0xf7 } };
141
static const IID D3D_IID_ID3D12CommandQueue = { 0x0ec870a6, 0x5d7e, 0x4c22, { 0x8c, 0xfc, 0x5b, 0xaa, 0xe0, 0x76, 0x16, 0xed } };
142
static const IID D3D_IID_ID3D12DescriptorHeap = { 0x8efb471d, 0x616c, 0x4f49, { 0x90, 0xf7, 0x12, 0x7b, 0xb7, 0x63, 0xfa, 0x51 } };
143
static const IID D3D_IID_ID3D12Resource = { 0x696442be, 0xa72e, 0x4059, { 0xbc, 0x79, 0x5b, 0x5c, 0x98, 0x04, 0x0f, 0xad } };
144
static const IID D3D_IID_ID3D12CommandAllocator = { 0x6102dee4, 0xaf59, 0x4b09, { 0xb9, 0x99, 0xb4, 0x4d, 0x73, 0xf0, 0x9b, 0x24 } };
145
static const IID D3D_IID_ID3D12CommandList = { 0x7116d91c, 0xe7e4, 0x47ce, { 0xb8, 0xc6, 0xec, 0x81, 0x68, 0xf4, 0x37, 0xe5 } };
146
static const IID D3D_IID_ID3D12GraphicsCommandList = { 0x5b160d0f, 0xac1b, 0x4185, { 0x8b, 0xa8, 0xb3, 0xae, 0x42, 0xa5, 0xa4, 0x55 } };
147
static const IID D3D_IID_ID3D12Fence = { 0x0a753dcf, 0xc4d8, 0x4b91, { 0xad, 0xf6, 0xbe, 0x5a, 0x60, 0xd9, 0x5a, 0x76 } };
148
static const IID D3D_IID_ID3D12RootSignature = { 0xc54a6b66, 0x72df, 0x4ee8, { 0x8b, 0xe5, 0xa9, 0x46, 0xa1, 0x42, 0x92, 0x14 } };
149
static const IID D3D_IID_ID3D12CommandSignature = { 0xc36a797c, 0xec80, 0x4f0a, { 0x89, 0x85, 0xa7, 0xb2, 0x47, 0x50, 0x82, 0xd1 } };
150
static const IID D3D_IID_ID3D12PipelineState = { 0x765a30f3, 0xf624, 0x4c6f, { 0xa8, 0x28, 0xac, 0xe9, 0x48, 0x62, 0x24, 0x45 } };
151
static const IID D3D_IID_ID3D12Debug = { 0x344488b7, 0x6846, 0x474b, { 0xb9, 0x89, 0xf0, 0x27, 0x44, 0x82, 0x45, 0xe0 } };
152
static const IID D3D_IID_ID3D12InfoQueue = { 0x0742a90b, 0xc387, 0x483f, { 0xb9, 0x46, 0x30, 0xa7, 0xe4, 0xe6, 0x14, 0x58 } };
153

154
// Enums
155

156
typedef enum D3D12BufferType
157
{
158
    D3D12_BUFFER_TYPE_GPU,
159
    D3D12_BUFFER_TYPE_UNIFORM,
160
    D3D12_BUFFER_TYPE_UPLOAD,
161
    D3D12_BUFFER_TYPE_DOWNLOAD
162
} D3D12BufferType;
163

164
// Conversions
165

166
static SDL_GPUTextureFormat SwapchainCompositionToSDLTextureFormat[] = {
167
    SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM,      // SDR
168
    SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB, // SDR_SRGB
169
    SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT,  // HDR
170
    SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM,   // HDR_ADVANCED
171
};
172

173
static DXGI_FORMAT SwapchainCompositionToTextureFormat[] = {
174
    DXGI_FORMAT_B8G8R8A8_UNORM,                // SDR
175
    DXGI_FORMAT_B8G8R8A8_UNORM, /* SDR_SRGB */ // NOTE: The RTV uses the sRGB format
176
    DXGI_FORMAT_R16G16B16A16_FLOAT,            // HDR
177
    DXGI_FORMAT_R10G10B10A2_UNORM,             // HDR_ADVANCED
178
};
179

180
static DXGI_COLOR_SPACE_TYPE SwapchainCompositionToColorSpace[] = {
181
    DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709,   // SDR
182
    DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709,   // SDR_SRGB
183
    DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709,   // HDR
184
    DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 // HDR_ADVANCED
185
};
186

187
static D3D12_BLEND SDLToD3D12_BlendFactor[] = {
188
    D3D12_BLEND_ZERO,             // ZERO
189
    D3D12_BLEND_ONE,              // ONE
190
    D3D12_BLEND_SRC_COLOR,        // SRC_COLOR
191
    D3D12_BLEND_INV_SRC_COLOR,    // ONE_MINUS_SRC_COLOR
192
    D3D12_BLEND_DEST_COLOR,       // DST_COLOR
193
    D3D12_BLEND_INV_DEST_COLOR,   // ONE_MINUS_DST_COLOR
194
    D3D12_BLEND_SRC_ALPHA,        // SRC_ALPHA
195
    D3D12_BLEND_INV_SRC_ALPHA,    // ONE_MINUS_SRC_ALPHA
196
    D3D12_BLEND_DEST_ALPHA,       // DST_ALPHA
197
    D3D12_BLEND_INV_DEST_ALPHA,   // ONE_MINUS_DST_ALPHA
198
    D3D12_BLEND_BLEND_FACTOR,     // CONSTANT_COLOR
199
    D3D12_BLEND_INV_BLEND_FACTOR, // ONE_MINUS_CONSTANT_COLOR
200
    D3D12_BLEND_SRC_ALPHA_SAT,    // SRC_ALPHA_SATURATE
201
};
202

203
static D3D12_BLEND SDLToD3D12_BlendFactorAlpha[] = {
204
    D3D12_BLEND_ZERO,             // ZERO
205
    D3D12_BLEND_ONE,              // ONE
206
    D3D12_BLEND_SRC_ALPHA,        // SRC_COLOR
207
    D3D12_BLEND_INV_SRC_ALPHA,    // ONE_MINUS_SRC_COLOR
208
    D3D12_BLEND_DEST_ALPHA,       // DST_COLOR
209
    D3D12_BLEND_INV_DEST_ALPHA,   // ONE_MINUS_DST_COLOR
210
    D3D12_BLEND_SRC_ALPHA,        // SRC_ALPHA
211
    D3D12_BLEND_INV_SRC_ALPHA,    // ONE_MINUS_SRC_ALPHA
212
    D3D12_BLEND_DEST_ALPHA,       // DST_ALPHA
213
    D3D12_BLEND_INV_DEST_ALPHA,   // ONE_MINUS_DST_ALPHA
214
    D3D12_BLEND_BLEND_FACTOR,     // CONSTANT_COLOR
215
    D3D12_BLEND_INV_BLEND_FACTOR, // ONE_MINUS_CONSTANT_COLOR
216
    D3D12_BLEND_SRC_ALPHA_SAT,    // SRC_ALPHA_SATURATE
217
};
218

219
static D3D12_BLEND_OP SDLToD3D12_BlendOp[] = {
220
    D3D12_BLEND_OP_ADD,          // ADD
221
    D3D12_BLEND_OP_SUBTRACT,     // SUBTRACT
222
    D3D12_BLEND_OP_REV_SUBTRACT, // REVERSE_SUBTRACT
223
    D3D12_BLEND_OP_MIN,          // MIN
224
    D3D12_BLEND_OP_MAX           // MAX
225
};
226

227
static DXGI_FORMAT SDLToD3D12_TextureFormat[] = {
228
    DXGI_FORMAT_R8G8B8A8_UNORM,       // R8G8B8A8_UNORM
229
    DXGI_FORMAT_B8G8R8A8_UNORM,       // B8G8R8A8_UNORM
230
    DXGI_FORMAT_B5G6R5_UNORM,         // B5G6R5_UNORM
231
    DXGI_FORMAT_B5G5R5A1_UNORM,       // B5G5R5A1_UNORM
232
    DXGI_FORMAT_B4G4R4A4_UNORM,       // B4G4R4A4_UNORM
233
    DXGI_FORMAT_R10G10B10A2_UNORM,    // R10G10B10A2_UNORM
234
    DXGI_FORMAT_R16G16_UNORM,         // R16G16_UNORM
235
    DXGI_FORMAT_R16G16B16A16_UNORM,   // R16G16B16A16_UNORM
236
    DXGI_FORMAT_R8_UNORM,             // R8_UNORM
237
    DXGI_FORMAT_A8_UNORM,             // A8_UNORM
238
    DXGI_FORMAT_BC1_UNORM,            // BC1_UNORM
239
    DXGI_FORMAT_BC2_UNORM,            // BC2_UNORM
240
    DXGI_FORMAT_BC3_UNORM,            // BC3_UNORM
241
    DXGI_FORMAT_BC7_UNORM,            // BC7_UNORM
242
    DXGI_FORMAT_R8G8_SNORM,           // R8G8_SNORM
243
    DXGI_FORMAT_R8G8B8A8_SNORM,       // R8G8B8A8_SNORM
244
    DXGI_FORMAT_R16_FLOAT,            // R16_FLOAT
245
    DXGI_FORMAT_R16G16_FLOAT,         // R16G16_FLOAT
246
    DXGI_FORMAT_R16G16B16A16_FLOAT,   // R16G16B16A16_FLOAT
247
    DXGI_FORMAT_R32_FLOAT,            // R32_FLOAT
248
    DXGI_FORMAT_R32G32_FLOAT,         // R32G32_FLOAT
249
    DXGI_FORMAT_R32G32B32A32_FLOAT,   // R32G32B32A32_FLOAT
250
    DXGI_FORMAT_R8_UINT,              // R8_UINT
251
    DXGI_FORMAT_R8G8_UINT,            // R8G8_UINT
252
    DXGI_FORMAT_R8G8B8A8_UINT,        // R8G8B8A8_UINT
253
    DXGI_FORMAT_R16_UINT,             // R16_UINT
254
    DXGI_FORMAT_R16G16_UINT,          // R16G16_UINT
255
    DXGI_FORMAT_R16G16B16A16_UINT,    // R16G16B16A16_UINT
256
    DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,  // R8G8B8A8_UNORM_SRGB
257
    DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,  // B8G8R8A8_UNORM_SRGB
258
    DXGI_FORMAT_BC3_UNORM_SRGB,       // BC3_UNORM_SRGB
259
    DXGI_FORMAT_BC7_UNORM_SRGB,       // BC7_UNORM_SRGB
260
    DXGI_FORMAT_D16_UNORM,            // D16_UNORM
261
    DXGI_FORMAT_D24_UNORM_S8_UINT,    // D24_UNORM
262
    DXGI_FORMAT_D32_FLOAT,            // D32_FLOAT
263
    DXGI_FORMAT_D24_UNORM_S8_UINT,    // D24_UNORM_S8_UINT
264
    DXGI_FORMAT_D32_FLOAT_S8X24_UINT, // D32_FLOAT_S8_UINT
265
};
266
SDL_COMPILE_TIME_ASSERT(SDLToD3D12_TextureFormat, SDL_arraysize(SDLToD3D12_TextureFormat) == SDL_GPU_TEXTUREFORMAT_MAX);
267

268
static D3D12_COMPARISON_FUNC SDLToD3D12_CompareOp[] = {
269
    D3D12_COMPARISON_FUNC_NEVER,         // NEVER
270
    D3D12_COMPARISON_FUNC_LESS,          // LESS
271
    D3D12_COMPARISON_FUNC_EQUAL,         // EQUAL
272
    D3D12_COMPARISON_FUNC_LESS_EQUAL,    // LESS_OR_EQUAL
273
    D3D12_COMPARISON_FUNC_GREATER,       // GREATER
274
    D3D12_COMPARISON_FUNC_NOT_EQUAL,     // NOT_EQUAL
275
    D3D12_COMPARISON_FUNC_GREATER_EQUAL, // GREATER_OR_EQUAL
276
    D3D12_COMPARISON_FUNC_ALWAYS         // ALWAYS
277
};
278

279
static D3D12_STENCIL_OP SDLToD3D12_StencilOp[] = {
280
    D3D12_STENCIL_OP_KEEP,     // KEEP
281
    D3D12_STENCIL_OP_ZERO,     // ZERO
282
    D3D12_STENCIL_OP_REPLACE,  // REPLACE
283
    D3D12_STENCIL_OP_INCR_SAT, // INCREMENT_AND_CLAMP
284
    D3D12_STENCIL_OP_DECR_SAT, // DECREMENT_AND_CLAMP
285
    D3D12_STENCIL_OP_INVERT,   // INVERT
286
    D3D12_STENCIL_OP_INCR,     // INCREMENT_AND_WRAP
287
    D3D12_STENCIL_OP_DECR      // DECREMENT_AND_WRAP
288
};
289

290
static D3D12_CULL_MODE SDLToD3D12_CullMode[] = {
291
    D3D12_CULL_MODE_NONE,  // NONE
292
    D3D12_CULL_MODE_FRONT, // FRONT
293
    D3D12_CULL_MODE_BACK   // BACK
294
};
295

296
static D3D12_FILL_MODE SDLToD3D12_FillMode[] = {
297
    D3D12_FILL_MODE_SOLID,    // FILL
298
    D3D12_FILL_MODE_WIREFRAME // LINE
299
};
300

301
static D3D12_INPUT_CLASSIFICATION SDLToD3D12_InputRate[] = {
302
    D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,  // VERTEX
303
    D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA // INSTANCE
304
};
305

306
static DXGI_FORMAT SDLToD3D12_VertexFormat[] = {
307
    DXGI_FORMAT_R32_SINT,           // INT
308
    DXGI_FORMAT_R32G32_SINT,        // INT2
309
    DXGI_FORMAT_R32G32B32_SINT,     // INT3
310
    DXGI_FORMAT_R32G32B32A32_SINT,  // INT4
311
    DXGI_FORMAT_R32_UINT,           // UINT
312
    DXGI_FORMAT_R32G32_UINT,        // UINT2
313
    DXGI_FORMAT_R32G32B32_UINT,     // UINT3
314
    DXGI_FORMAT_R32G32B32A32_UINT,  // UINT4
315
    DXGI_FORMAT_R32_FLOAT,          // FLOAT
316
    DXGI_FORMAT_R32G32_FLOAT,       // FLOAT2
317
    DXGI_FORMAT_R32G32B32_FLOAT,    // FLOAT3
318
    DXGI_FORMAT_R32G32B32A32_FLOAT, // FLOAT4
319
    DXGI_FORMAT_R8G8_SINT,          // BYTE2
320
    DXGI_FORMAT_R8G8B8A8_SINT,      // BYTE4
321
    DXGI_FORMAT_R8G8_UINT,          // UBYTE2
322
    DXGI_FORMAT_R8G8B8A8_UINT,      // UBYTE4
323
    DXGI_FORMAT_R8G8_SNORM,         // BYTE2_NORM
324
    DXGI_FORMAT_R8G8B8A8_SNORM,     // BYTE4_NORM
325
    DXGI_FORMAT_R8G8_UNORM,         // UBYTE2_NORM
326
    DXGI_FORMAT_R8G8B8A8_UNORM,     // UBYTE4_NORM
327
    DXGI_FORMAT_R16G16_SINT,        // SHORT2
328
    DXGI_FORMAT_R16G16B16A16_SINT,  // SHORT4
329
    DXGI_FORMAT_R16G16_UINT,        // USHORT2
330
    DXGI_FORMAT_R16G16B16A16_UINT,  // USHORT4
331
    DXGI_FORMAT_R16G16_SNORM,       // SHORT2_NORM
332
    DXGI_FORMAT_R16G16B16A16_SNORM, // SHORT4_NORM
333
    DXGI_FORMAT_R16G16_UNORM,       // USHORT2_NORM
334
    DXGI_FORMAT_R16G16B16A16_UNORM, // USHORT4_NORM
335
    DXGI_FORMAT_R16G16_FLOAT,       // HALF2
336
    DXGI_FORMAT_R16G16B16A16_FLOAT  // HALF4
337
};
338

339
static Uint32 SDLToD3D12_SampleCount[] = {
340
    1, // SDL_GPU_SAMPLECOUNT_1
341
    2, // SDL_GPU_SAMPLECOUNT_2
342
    4, // SDL_GPU_SAMPLECOUNT_4
343
    8, // SDL_GPU_SAMPLECOUNT_8
344
};
345

346
static D3D12_PRIMITIVE_TOPOLOGY SDLToD3D12_PrimitiveType[] = {
347
    D3D_PRIMITIVE_TOPOLOGY_POINTLIST,    // POINTLIST
348
    D3D_PRIMITIVE_TOPOLOGY_LINELIST,     // LINELIST
349
    D3D_PRIMITIVE_TOPOLOGY_LINESTRIP,    // LINESTRIP
350
    D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, // TRIANGLELIST
351
    D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP // TRIANGLESTRIP
352
};
353

354
static D3D12_TEXTURE_ADDRESS_MODE SDLToD3D12_SamplerAddressMode[] = {
355
    D3D12_TEXTURE_ADDRESS_MODE_WRAP,   // REPEAT
356
    D3D12_TEXTURE_ADDRESS_MODE_MIRROR, // MIRRORED_REPEAT
357
    D3D12_TEXTURE_ADDRESS_MODE_CLAMP   // CLAMP_TO_EDGE
358
};
359

360
static D3D12_FILTER SDLToD3D12_Filter(
361
    SDL_GPUFilter minFilter,
362
    SDL_GPUFilter magFilter,
363
    SDL_GPUSamplerMipmapMode mipmapMode,
364
    bool comparisonEnabled,
365
    bool anisotropyEnabled)
366
{
367
    D3D12_FILTER result = D3D12_ENCODE_BASIC_FILTER(
368
        (minFilter == SDL_GPU_FILTER_LINEAR) ? 1 : 0,
369
        (magFilter == SDL_GPU_FILTER_LINEAR) ? 1 : 0,
370
        (mipmapMode == SDL_GPU_SAMPLERMIPMAPMODE_LINEAR) ? 1 : 0,
371
        comparisonEnabled ? 1 : 0);
372

373
    if (anisotropyEnabled) {
374
        result = (D3D12_FILTER)(result | D3D12_ANISOTROPIC_FILTERING_BIT);
375
    }
376

377
    return result;
378
}
379

380
// Structures
381
typedef struct D3D12Renderer D3D12Renderer;
382
typedef struct D3D12CommandBufferPool D3D12CommandBufferPool;
383
typedef struct D3D12CommandBuffer D3D12CommandBuffer;
384
typedef struct D3D12Texture D3D12Texture;
385
typedef struct D3D12Shader D3D12Shader;
386
typedef struct D3D12GraphicsPipeline D3D12GraphicsPipeline;
387
typedef struct D3D12ComputePipeline D3D12ComputePipeline;
388
typedef struct D3D12Buffer D3D12Buffer;
389
typedef struct D3D12BufferContainer D3D12BufferContainer;
390
typedef struct D3D12UniformBuffer D3D12UniformBuffer;
391
typedef struct D3D12DescriptorHeap D3D12DescriptorHeap;
392
typedef struct D3D12TextureDownload D3D12TextureDownload;
393

394
typedef struct D3D12Fence
395
{
396
    ID3D12Fence *handle;
397
    HANDLE event; // used for blocking
398
    SDL_AtomicInt referenceCount;
399
} D3D12Fence;
400

401
struct D3D12DescriptorHeap
402
{
403
    ID3D12DescriptorHeap *handle;
404
    D3D12_DESCRIPTOR_HEAP_TYPE heapType;
405
    D3D12_CPU_DESCRIPTOR_HANDLE descriptorHeapCPUStart;
406
    D3D12_GPU_DESCRIPTOR_HANDLE descriptorHeapGPUStart; // only exists if staging is true
407
    Uint32 maxDescriptors;
408
    Uint32 descriptorSize;
409
    bool staging;
410

411
    Uint32 currentDescriptorIndex;
412

413
    Uint32 *inactiveDescriptorIndices; // only exists if staging is true
414
    Uint32 inactiveDescriptorCount;
415
};
416

417
typedef struct D3D12DescriptorHeapPool
418
{
419
    Uint32 capacity;
420
    Uint32 count;
421
    D3D12DescriptorHeap **heaps;
422
    SDL_Mutex *lock;
423
} D3D12DescriptorHeapPool;
424

425
typedef struct D3D12CPUDescriptor
426
{
427
    D3D12DescriptorHeap *heap;
428
    D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle;
429
    Uint32 cpuHandleIndex;
430
} D3D12CPUDescriptor;
431

432
typedef struct D3D12TextureContainer
433
{
434
    TextureCommonHeader header;
435

436
    D3D12Texture *activeTexture;
437

438
    D3D12Texture **textures;
439
    Uint32 textureCapacity;
440
    Uint32 textureCount;
441

442
    // Swapchain images cannot be cycled
443
    bool canBeCycled;
444

445
    char *debugName;
446
} D3D12TextureContainer;
447

448
// Null views represent by heap = NULL
449
typedef struct D3D12TextureSubresource
450
{
451
    D3D12Texture *parent;
452
    Uint32 layer;
453
    Uint32 level;
454
    Uint32 depth;
455
    Uint32 index;
456

457
    // One per depth slice
458
    D3D12CPUDescriptor *rtvHandles; // NULL if not a color target
459

460
    D3D12CPUDescriptor uavHandle; // NULL if not a compute storage write texture
461
    D3D12CPUDescriptor dsvHandle; // NULL if not a depth stencil target
462
} D3D12TextureSubresource;
463

464
struct D3D12Texture
465
{
466
    D3D12TextureContainer *container;
467
    Uint32 containerIndex;
468

469
    D3D12TextureSubresource *subresources;
470
    Uint32 subresourceCount; /* layerCount * levelCount */
471

472
    ID3D12Resource *resource;
473
    D3D12CPUDescriptor srvHandle;
474

475
    SDL_AtomicInt referenceCount;
476
};
477

478
typedef struct D3D12Sampler
479
{
480
    SDL_GPUSamplerCreateInfo createInfo;
481
    D3D12CPUDescriptor handle;
482
    SDL_AtomicInt referenceCount;
483
} D3D12Sampler;
484

485
typedef struct D3D12WindowData
486
{
487
    SDL_Window *window;
488
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
489
    D3D12XBOX_FRAME_PIPELINE_TOKEN frameToken;
490
    Uint32 swapchainWidth, swapchainHeight;
491
#else
492
    IDXGISwapChain3 *swapchain;
493
#endif
494
    SDL_GPUPresentMode presentMode;
495
    SDL_GPUSwapchainComposition swapchainComposition;
496
    DXGI_COLOR_SPACE_TYPE swapchainColorSpace;
497
    Uint32 frameCounter;
498

499
    D3D12TextureContainer textureContainers[MAX_FRAMES_IN_FLIGHT];
500
    D3D12Fence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
501
} D3D12WindowData;
502

503
typedef struct D3D12PresentData
504
{
505
    D3D12WindowData *windowData;
506
    Uint32 swapchainImageIndex;
507
} D3D12PresentData;
508

509
struct D3D12Renderer
510
{
511
    // Reference to the parent device
512
    SDL_GPUDevice *sdlGPUDevice;
513

514
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
515
    IDXGIDebug *dxgiDebug;
516
    IDXGIFactory4 *factory;
517
    IDXGIInfoQueue *dxgiInfoQueue;
518
    IDXGIAdapter1 *adapter;
519
    void *dxgi_dll;
520
    void *dxgidebug_dll;
521
#endif
522
    ID3D12Debug *d3d12Debug;
523
    bool supportsTearing;
524
    void *d3d12_dll;
525
    ID3D12Device *device;
526
    PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignature_func;
527
    const char *semantic;
528
    SDL_iconv_t iconv;
529

530
    ID3D12CommandQueue *commandQueue;
531

532
    bool debugMode;
533
    bool GPUUploadHeapSupported;
534
    // FIXME: these might not be necessary since we're not using custom heaps
535
    bool UMA;
536
    bool UMACacheCoherent;
537

538
    // Indirect command signatures
539
    ID3D12CommandSignature *indirectDrawCommandSignature;
540
    ID3D12CommandSignature *indirectIndexedDrawCommandSignature;
541
    ID3D12CommandSignature *indirectDispatchCommandSignature;
542

543
    // Blit
544
    SDL_GPUShader *blitVertexShader;
545
    SDL_GPUShader *blitFrom2DShader;
546
    SDL_GPUShader *blitFrom2DArrayShader;
547
    SDL_GPUShader *blitFrom3DShader;
548
    SDL_GPUShader *blitFromCubeShader;
549

550
    SDL_GPUSampler *blitNearestSampler;
551
    SDL_GPUSampler *blitLinearSampler;
552

553
    BlitPipelineCacheEntry *blitPipelines;
554
    Uint32 blitPipelineCount;
555
    Uint32 blitPipelineCapacity;
556

557
    // Resources
558

559
    D3D12CommandBuffer **availableCommandBuffers;
560
    Uint32 availableCommandBufferCount;
561
    Uint32 availableCommandBufferCapacity;
562

563
    D3D12CommandBuffer **submittedCommandBuffers;
564
    Uint32 submittedCommandBufferCount;
565
    Uint32 submittedCommandBufferCapacity;
566

567
    D3D12UniformBuffer **uniformBufferPool;
568
    Uint32 uniformBufferPoolCount;
569
    Uint32 uniformBufferPoolCapacity;
570

571
    D3D12WindowData **claimedWindows;
572
    Uint32 claimedWindowCount;
573
    Uint32 claimedWindowCapacity;
574

575
    D3D12Fence **availableFences;
576
    Uint32 availableFenceCount;
577
    Uint32 availableFenceCapacity;
578

579
    D3D12DescriptorHeap *stagingDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES];
580
    D3D12DescriptorHeapPool descriptorHeapPools[2];
581

582
    // Deferred resource releasing
583

584
    D3D12Buffer **buffersToDestroy;
585
    Uint32 buffersToDestroyCount;
586
    Uint32 buffersToDestroyCapacity;
587

588
    D3D12Texture **texturesToDestroy;
589
    Uint32 texturesToDestroyCount;
590
    Uint32 texturesToDestroyCapacity;
591

592
    D3D12Sampler **samplersToDestroy;
593
    Uint32 samplersToDestroyCount;
594
    Uint32 samplersToDestroyCapacity;
595

596
    D3D12GraphicsPipeline **graphicsPipelinesToDestroy;
597
    Uint32 graphicsPipelinesToDestroyCount;
598
    Uint32 graphicsPipelinesToDestroyCapacity;
599

600
    D3D12ComputePipeline **computePipelinesToDestroy;
601
    Uint32 computePipelinesToDestroyCount;
602
    Uint32 computePipelinesToDestroyCapacity;
603

604
    // Locks
605
    SDL_Mutex *stagingDescriptorHeapLock;
606
    SDL_Mutex *acquireCommandBufferLock;
607
    SDL_Mutex *acquireUniformBufferLock;
608
    SDL_Mutex *submitLock;
609
    SDL_Mutex *windowLock;
610
    SDL_Mutex *fenceLock;
611
    SDL_Mutex *disposeLock;
612
};
613

614
struct D3D12CommandBuffer
615
{
616
    // reserved for SDL_gpu
617
    CommandBufferCommonHeader common;
618

619
    // non owning parent reference
620
    D3D12Renderer *renderer;
621

622
    ID3D12CommandAllocator *commandAllocator;
623
    ID3D12GraphicsCommandList *graphicsCommandList;
624
    D3D12Fence *inFlightFence;
625
    bool autoReleaseFence;
626

627
    // Presentation data
628
    D3D12PresentData *presentDatas;
629
    Uint32 presentDataCount;
630
    Uint32 presentDataCapacity;
631

632
    Uint32 colorAttachmentTextureSubresourceCount;
633
    D3D12TextureSubresource *colorAttachmentTextureSubresources[MAX_COLOR_TARGET_BINDINGS];
634
    D3D12TextureSubresource *depthStencilTextureSubresource;
635
    D3D12GraphicsPipeline *currentGraphicsPipeline;
636
    D3D12ComputePipeline *currentComputePipeline;
637

638
    // Set at acquire time
639
    D3D12DescriptorHeap *gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER + 1];
640

641
    D3D12UniformBuffer **usedUniformBuffers;
642
    Uint32 usedUniformBufferCount;
643
    Uint32 usedUniformBufferCapacity;
644

645
    // Resource slot state
646
    bool needVertexBufferBind;
647
    bool needVertexSamplerBind;
648
    bool needVertexStorageTextureBind;
649
    bool needVertexStorageBufferBind;
650
    bool needVertexUniformBufferBind[MAX_UNIFORM_BUFFERS_PER_STAGE];
651
    bool needFragmentSamplerBind;
652
    bool needFragmentStorageTextureBind;
653
    bool needFragmentStorageBufferBind;
654
    bool needFragmentUniformBufferBind[MAX_UNIFORM_BUFFERS_PER_STAGE];
655

656
    bool needComputeReadOnlyStorageTextureBind;
657
    bool needComputeReadOnlyStorageBufferBind;
658
    bool needComputeUniformBufferBind[MAX_UNIFORM_BUFFERS_PER_STAGE];
659

660
    D3D12Buffer *vertexBuffers[MAX_BUFFER_BINDINGS];
661
    Uint32 vertexBufferOffsets[MAX_BUFFER_BINDINGS];
662
    Uint32 vertexBufferCount;
663

664
    D3D12Texture *vertexSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
665
    D3D12Sampler *vertexSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
666
    D3D12Texture *vertexStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
667
    D3D12Buffer *vertexStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
668
    D3D12UniformBuffer *vertexUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
669

670
    D3D12Texture *fragmentSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
671
    D3D12Sampler *fragmentSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
672
    D3D12Texture *fragmentStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
673
    D3D12Buffer *fragmentStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
674
    D3D12UniformBuffer *fragmentUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
675

676
    D3D12Texture *computeReadOnlyStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
677
    D3D12Buffer *computeReadOnlyStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
678
    D3D12TextureSubresource *computeWriteOnlyStorageTextureSubresources[MAX_COMPUTE_WRITE_TEXTURES];
679
    Uint32 computeWriteOnlyStorageTextureSubresourceCount;
680
    D3D12Buffer *computeWriteOnlyStorageBuffers[MAX_COMPUTE_WRITE_BUFFERS];
681
    Uint32 computeWriteOnlyStorageBufferCount;
682
    D3D12UniformBuffer *computeUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
683

684
    // Resource tracking
685
    D3D12Texture **usedTextures;
686
    Uint32 usedTextureCount;
687
    Uint32 usedTextureCapacity;
688

689
    D3D12Buffer **usedBuffers;
690
    Uint32 usedBufferCount;
691
    Uint32 usedBufferCapacity;
692

693
    D3D12Sampler **usedSamplers;
694
    Uint32 usedSamplerCount;
695
    Uint32 usedSamplerCapacity;
696

697
    D3D12GraphicsPipeline **usedGraphicsPipelines;
698
    Uint32 usedGraphicsPipelineCount;
699
    Uint32 usedGraphicsPipelineCapacity;
700

701
    D3D12ComputePipeline **usedComputePipelines;
702
    Uint32 usedComputePipelineCount;
703
    Uint32 usedComputePipelineCapacity;
704

705
    // Used for texture pitch hack
706
    D3D12TextureDownload **textureDownloads;
707
    Uint32 textureDownloadCount;
708
    Uint32 textureDownloadCapacity;
709
};
710

711
struct D3D12Shader
712
{
713
    // todo cleanup
714
    void *bytecode;
715
    size_t bytecodeSize;
716

717
    Uint32 samplerCount;
718
    Uint32 uniformBufferCount;
719
    Uint32 storageBufferCount;
720
    Uint32 storageTextureCount;
721
};
722

723
typedef struct D3D12GraphicsRootSignature
724
{
725
    ID3D12RootSignature *handle;
726

727
    Sint32 vertexSamplerRootIndex;
728
    Sint32 vertexSamplerTextureRootIndex;
729
    Sint32 vertexStorageTextureRootIndex;
730
    Sint32 vertexStorageBufferRootIndex;
731

732
    Sint32 vertexUniformBufferRootIndex[MAX_UNIFORM_BUFFERS_PER_STAGE];
733

734
    Sint32 fragmentSamplerRootIndex;
735
    Sint32 fragmentSamplerTextureRootIndex;
736
    Sint32 fragmentStorageTextureRootIndex;
737
    Sint32 fragmentStorageBufferRootIndex;
738

739
    Sint32 fragmentUniformBufferRootIndex[MAX_UNIFORM_BUFFERS_PER_STAGE];
740
} D3D12GraphicsRootSignature;
741

742
struct D3D12GraphicsPipeline
743
{
744
    ID3D12PipelineState *pipelineState;
745
    D3D12GraphicsRootSignature *rootSignature;
746
    SDL_GPUPrimitiveType primitiveType;
747

748
    Uint32 vertexStrides[MAX_BUFFER_BINDINGS];
749

750
    float blendConstants[4];
751
    Uint8 stencilRef;
752

753
    Uint32 vertexSamplerCount;
754
    Uint32 vertexUniformBufferCount;
755
    Uint32 vertexStorageBufferCount;
756
    Uint32 vertexStorageTextureCount;
757

758
    Uint32 fragmentSamplerCount;
759
    Uint32 fragmentUniformBufferCount;
760
    Uint32 fragmentStorageBufferCount;
761
    Uint32 fragmentStorageTextureCount;
762

763
    SDL_AtomicInt referenceCount;
764
};
765

766
typedef struct D3D12ComputeRootSignature
767
{
768
    ID3D12RootSignature *handle;
769

770
    Uint32 readOnlyStorageTextureRootIndex;
771
    Uint32 readOnlyStorageBufferRootIndex;
772
    Uint32 writeOnlyStorageTextureRootIndex;
773
    Uint32 writeOnlyStorageBufferRootIndex;
774
    Uint32 uniformBufferRootIndex[MAX_UNIFORM_BUFFERS_PER_STAGE];
775
} D3D12ComputeRootSignature;
776

777
struct D3D12ComputePipeline
778
{
779
    ID3D12PipelineState *pipelineState;
780
    D3D12ComputeRootSignature *rootSignature;
781

782
    Uint32 readOnlyStorageTextureCount;
783
    Uint32 readOnlyStorageBufferCount;
784
    Uint32 writeOnlyStorageTextureCount;
785
    Uint32 writeOnlyStorageBufferCount;
786
    Uint32 uniformBufferCount;
787

788
    SDL_AtomicInt referenceCount;
789
};
790

791
struct D3D12TextureDownload
792
{
793
    D3D12Buffer *destinationBuffer;
794
    D3D12Buffer *temporaryBuffer;
795
    Uint32 width;
796
    Uint32 height;
797
    Uint32 depth;
798
    Uint32 bufferOffset;
799
    Uint32 bytesPerRow;
800
    Uint32 bytesPerDepthSlice;
801
    Uint32 alignedBytesPerRow;
802
};
803

804
struct D3D12Buffer
805
{
806
    D3D12BufferContainer *container;
807
    Uint32 containerIndex;
808

809
    ID3D12Resource *handle;
810
    D3D12CPUDescriptor uavDescriptor;
811
    D3D12CPUDescriptor srvDescriptor;
812
    D3D12CPUDescriptor cbvDescriptor;
813
    D3D12_GPU_VIRTUAL_ADDRESS virtualAddress;
814
    Uint8 *mapPointer; // NULL except for upload buffers and fast uniform buffers
815
    SDL_AtomicInt referenceCount;
816
    bool transitioned; // used for initial resource barrier
817
};
818

819
struct D3D12BufferContainer
820
{
821
    SDL_GPUBufferUsageFlags usageFlags;
822
    Uint32 size;
823
    D3D12BufferType type;
824

825
    D3D12Buffer *activeBuffer;
826

827
    D3D12Buffer **buffers;
828
    Uint32 bufferCapacity;
829
    Uint32 bufferCount;
830

831
    D3D12_RESOURCE_DESC bufferDesc;
832

833
    char *debugName;
834
};
835

836
struct D3D12UniformBuffer
837
{
838
    D3D12Buffer *buffer;
839
    Uint32 writeOffset;
840
    Uint32 drawOffset;
841
    Uint32 currentBlockSize;
842
};
843

844
// Foward function declarations
845

846
static void D3D12_ReleaseWindow(SDL_GPURenderer *driverData, SDL_Window *window);
847
static void D3D12_Wait(SDL_GPURenderer *driverData);
848
static void D3D12_WaitForFences(SDL_GPURenderer *driverData, bool waitAll, SDL_GPUFence **pFences, Uint32 fenceCount);
849
static void D3D12_INTERNAL_ReleaseBlitPipelines(SDL_GPURenderer *driverData);
850

851
// Helpers
852

853
static Uint32 D3D12_INTERNAL_Align(Uint32 location, Uint32 alignment)
854
{
855
    return (location + (alignment - 1)) & ~(alignment - 1);
856
}
857

858
// Xbox Hack
859

860
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
861
// FIXME: This is purely to work around a presentation bug when recreating the device/command queue.
862
static ID3D12Device *s_Device;
863
static ID3D12CommandQueue *s_CommandQueue;
864
#endif
865

866
// Logging
867

868
static void
869
D3D12_INTERNAL_LogError(
870
    ID3D12Device *device,
871
    const char *msg,
872
    HRESULT res)
873
{
874
#define MAX_ERROR_LEN 1024 // FIXME: Arbitrary!
875

876
    // Buffer for text, ensure space for \0 terminator after buffer
877
    char wszMsgBuff[MAX_ERROR_LEN + 1];
878
    DWORD dwChars; // Number of chars returned.
879

880
    if (res == DXGI_ERROR_DEVICE_REMOVED) {
881
        if (device) {
882
            res = ID3D12Device_GetDeviceRemovedReason(device);
883
        }
884
    }
885

886
    // Try to get the message from the system errors.
887
    dwChars = FormatMessageA(
888
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
889
        NULL,
890
        res,
891
        0,
892
        wszMsgBuff,
893
        MAX_ERROR_LEN,
894
        NULL);
895

896
    // No message? Screw it, just post the code.
897
    if (dwChars == 0) {
898
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s! Error Code: " HRESULT_FMT, msg, res);
899
        return;
900
    }
901

902
    // Ensure valid range
903
    dwChars = SDL_min(dwChars, MAX_ERROR_LEN);
904

905
    // Trim whitespace from tail of message
906
    while (dwChars > 0) {
907
        if (wszMsgBuff[dwChars - 1] <= ' ') {
908
            dwChars--;
909
        } else {
910
            break;
911
        }
912
    }
913

914
    // Ensure null-terminated string
915
    wszMsgBuff[dwChars] = '\0';
916

917
    SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s! Error Code: %s " HRESULT_FMT, msg, wszMsgBuff, res);
918
}
919

920
// Debug Naming
921

922
static void D3D12_INTERNAL_SetResourceName(
923
    D3D12Renderer *renderer,
924
    ID3D12Resource *resource,
925
    const char *text)
926
{
927
    if (renderer->debugMode) {
928
        ID3D12DeviceChild_SetPrivateData(
929
            resource,
930
            D3D_GUID(D3D_IID_D3DDebugObjectName),
931
            (UINT)SDL_strlen(text),
932
            text);
933
    }
934
}
935

936
// Release / Cleanup
937

938
// TODO: call this when releasing resources
939
static void D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
940
    D3D12Renderer *renderer,
941
    D3D12CPUDescriptor *cpuDescriptor)
942
{
943
    D3D12DescriptorHeap *heap = cpuDescriptor->heap;
944

945
    if (heap != NULL) {
946
        SDL_LockMutex(renderer->stagingDescriptorHeapLock);
947
        heap->inactiveDescriptorIndices[heap->inactiveDescriptorCount] = cpuDescriptor->cpuHandleIndex;
948
        heap->inactiveDescriptorCount += 1;
949
        SDL_UnlockMutex(renderer->stagingDescriptorHeapLock);
950
    }
951

952
    cpuDescriptor->heap = NULL;
953
    cpuDescriptor->cpuHandle.ptr = 0;
954
    cpuDescriptor->cpuHandleIndex = SDL_MAX_UINT32;
955
}
956

957
static void D3D12_INTERNAL_DestroyBuffer(
958
    D3D12Renderer *renderer,
959
    D3D12Buffer *buffer)
960
{
961
    if (!buffer) {
962
        return;
963
    }
964

965
    if (buffer->mapPointer != NULL) {
966
        ID3D12Resource_Unmap(
967
            buffer->handle,
968
            0,
969
            NULL);
970
    }
971
    D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
972
        renderer,
973
        &buffer->srvDescriptor);
974
    D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
975
        renderer,
976
        &buffer->uavDescriptor);
977
    D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
978
        renderer,
979
        &buffer->cbvDescriptor);
980

981
    if (buffer->handle) {
982
        ID3D12Resource_Release(buffer->handle);
983
    }
984
    SDL_free(buffer);
985
}
986

987
static void D3D12_INTERNAL_ReleaseBuffer(
988
    D3D12Renderer *renderer,
989
    D3D12Buffer *buffer)
990
{
991
    SDL_LockMutex(renderer->disposeLock);
992

993
    EXPAND_ARRAY_IF_NEEDED(
994
        renderer->buffersToDestroy,
995
        D3D12Buffer *,
996
        renderer->buffersToDestroyCount + 1,
997
        renderer->buffersToDestroyCapacity,
998
        renderer->buffersToDestroyCapacity * 2)
999

1000
    renderer->buffersToDestroy[renderer->buffersToDestroyCount] = buffer;
1001
    renderer->buffersToDestroyCount += 1;
1002

1003
    SDL_UnlockMutex(renderer->disposeLock);
1004
}
1005

1006
static void D3D12_INTERNAL_ReleaseBufferContainer(
1007
    D3D12Renderer *renderer,
1008
    D3D12BufferContainer *container)
1009
{
1010
    SDL_LockMutex(renderer->disposeLock);
1011

1012
    for (Uint32 i = 0; i < container->bufferCount; i += 1) {
1013
        D3D12_INTERNAL_ReleaseBuffer(
1014
            renderer,
1015
            container->buffers[i]);
1016
    }
1017

1018
    // Containers are just client handles, so we can free immediately
1019
    if (container->debugName) {
1020
        SDL_free(container->debugName);
1021
    }
1022
    SDL_free(container->buffers);
1023
    SDL_free(container);
1024

1025
    SDL_UnlockMutex(renderer->disposeLock);
1026
}
1027

1028
static void D3D12_INTERNAL_DestroyTexture(
1029
    D3D12Renderer *renderer,
1030
    D3D12Texture *texture)
1031
{
1032
    if (!texture) {
1033
        return;
1034
    }
1035
    for (Uint32 i = 0; i < texture->subresourceCount; i += 1) {
1036
        D3D12TextureSubresource *subresource = &texture->subresources[i];
1037
        if (subresource->rtvHandles) {
1038
            for (Uint32 depthIndex = 0; depthIndex < subresource->depth; depthIndex += 1) {
1039
                D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
1040
                    renderer,
1041
                    &subresource->rtvHandles[depthIndex]);
1042
            }
1043
            SDL_free(subresource->rtvHandles);
1044
        }
1045

1046
        D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
1047
            renderer,
1048
            &subresource->uavHandle);
1049

1050
        D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
1051
            renderer,
1052
            &subresource->dsvHandle);
1053
    }
1054
    SDL_free(texture->subresources);
1055

1056
    D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
1057
        renderer,
1058
        &texture->srvHandle);
1059

1060
    if (texture->resource) {
1061
        ID3D12Resource_Release(texture->resource);
1062
    }
1063

1064
    SDL_free(texture);
1065
}
1066

1067
static void D3D12_INTERNAL_ReleaseTexture(
1068
    D3D12Renderer *renderer,
1069
    D3D12Texture *texture)
1070
{
1071
    SDL_LockMutex(renderer->disposeLock);
1072

1073
    EXPAND_ARRAY_IF_NEEDED(
1074
        renderer->texturesToDestroy,
1075
        D3D12Texture *,
1076
        renderer->texturesToDestroyCount + 1,
1077
        renderer->texturesToDestroyCapacity,
1078
        renderer->texturesToDestroyCapacity * 2)
1079

1080
    renderer->texturesToDestroy[renderer->texturesToDestroyCount] = texture;
1081
    renderer->texturesToDestroyCount += 1;
1082

1083
    SDL_UnlockMutex(renderer->disposeLock);
1084
}
1085

1086
static void D3D12_INTERNAL_ReleaseTextureContainer(
1087
    D3D12Renderer *renderer,
1088
    D3D12TextureContainer *container)
1089
{
1090
    SDL_LockMutex(renderer->disposeLock);
1091

1092
    for (Uint32 i = 0; i < container->textureCount; i += 1) {
1093
        D3D12_INTERNAL_ReleaseTexture(
1094
            renderer,
1095
            container->textures[i]);
1096
    }
1097

1098
    // Containers are just client handles, so we can destroy immediately
1099
    if (container->debugName) {
1100
        SDL_free(container->debugName);
1101
    }
1102
    SDL_free(container->textures);
1103
    SDL_free(container);
1104

1105
    SDL_UnlockMutex(renderer->disposeLock);
1106
}
1107

1108
static void D3D12_INTERNAL_DestroySampler(
1109
    D3D12Renderer *renderer,
1110
    D3D12Sampler *sampler)
1111
{
1112
    D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
1113
        renderer,
1114
        &sampler->handle);
1115

1116
    SDL_free(sampler);
1117
}
1118

1119
static void D3D12_INTERNAL_DestroyGraphicsRootSignature(
1120
    D3D12GraphicsRootSignature *rootSignature)
1121
{
1122
    if (!rootSignature) {
1123
        return;
1124
    }
1125
    if (rootSignature->handle) {
1126
        ID3D12RootSignature_Release(rootSignature->handle);
1127
    }
1128
    SDL_free(rootSignature);
1129
}
1130

1131
static void D3D12_INTERNAL_DestroyGraphicsPipeline(
1132
    D3D12GraphicsPipeline *graphicsPipeline)
1133
{
1134
    if (graphicsPipeline->pipelineState) {
1135
        ID3D12PipelineState_Release(graphicsPipeline->pipelineState);
1136
    }
1137
    D3D12_INTERNAL_DestroyGraphicsRootSignature(graphicsPipeline->rootSignature);
1138
    SDL_free(graphicsPipeline);
1139
}
1140

1141
static void D3D12_INTERNAL_DestroyComputeRootSignature(
1142
    D3D12ComputeRootSignature *rootSignature)
1143
{
1144
    if (!rootSignature) {
1145
        return;
1146
    }
1147
    if (rootSignature->handle) {
1148
        ID3D12RootSignature_Release(rootSignature->handle);
1149
    }
1150
    SDL_free(rootSignature);
1151
}
1152

1153
static void D3D12_INTERNAL_DestroyComputePipeline(
1154
    D3D12ComputePipeline *computePipeline)
1155
{
1156
    if (computePipeline->pipelineState) {
1157
        ID3D12PipelineState_Release(computePipeline->pipelineState);
1158
    }
1159
    D3D12_INTERNAL_DestroyComputeRootSignature(computePipeline->rootSignature);
1160
    SDL_free(computePipeline);
1161
}
1162

1163
static void D3D12_INTERNAL_ReleaseFenceToPool(
1164
    D3D12Renderer *renderer,
1165
    D3D12Fence *fence)
1166
{
1167
    SDL_LockMutex(renderer->fenceLock);
1168

1169
    EXPAND_ARRAY_IF_NEEDED(
1170
        renderer->availableFences,
1171
        D3D12Fence *,
1172
        renderer->availableFenceCount + 1,
1173
        renderer->availableFenceCapacity,
1174
        renderer->availableFenceCapacity * 2);
1175

1176
    renderer->availableFences[renderer->availableFenceCount] = fence;
1177
    renderer->availableFenceCount += 1;
1178

1179
    SDL_UnlockMutex(renderer->fenceLock);
1180
}
1181

1182
static void D3D12_ReleaseFence(
1183
    SDL_GPURenderer *driverData,
1184
    SDL_GPUFence *fence)
1185
{
1186
    D3D12Fence *d3d12Fence = (D3D12Fence *)fence;
1187

1188
    if (SDL_AtomicDecRef(&d3d12Fence->referenceCount)) {
1189
        D3D12_INTERNAL_ReleaseFenceToPool(
1190
            (D3D12Renderer *)driverData,
1191
            d3d12Fence);
1192
    }
1193
}
1194

1195
static bool D3D12_QueryFence(
1196
    SDL_GPURenderer *driverData,
1197
    SDL_GPUFence *fence)
1198
{
1199
    D3D12Fence *d3d12Fence = (D3D12Fence *)fence;
1200
    return ID3D12Fence_GetCompletedValue(d3d12Fence->handle) == D3D12_FENCE_SIGNAL_VALUE;
1201
}
1202

1203
static void D3D12_INTERNAL_DestroyDescriptorHeap(D3D12DescriptorHeap *descriptorHeap)
1204
{
1205
    if (!descriptorHeap) {
1206
        return;
1207
    }
1208
    SDL_free(descriptorHeap->inactiveDescriptorIndices);
1209
    if (descriptorHeap->handle) {
1210
        ID3D12DescriptorHeap_Release(descriptorHeap->handle);
1211
    }
1212
    SDL_free(descriptorHeap);
1213
}
1214

1215
static void D3D12_INTERNAL_DestroyCommandBuffer(D3D12CommandBuffer *commandBuffer)
1216
{
1217
    if (!commandBuffer) {
1218
        return;
1219
    }
1220
    if (commandBuffer->graphicsCommandList) {
1221
        ID3D12GraphicsCommandList_Release(commandBuffer->graphicsCommandList);
1222
    }
1223
    if (commandBuffer->commandAllocator) {
1224
        ID3D12CommandAllocator_Release(commandBuffer->commandAllocator);
1225
    }
1226
    SDL_free(commandBuffer->presentDatas);
1227
    SDL_free(commandBuffer->usedTextures);
1228
    SDL_free(commandBuffer->usedBuffers);
1229
    SDL_free(commandBuffer->usedSamplers);
1230
    SDL_free(commandBuffer->usedGraphicsPipelines);
1231
    SDL_free(commandBuffer->usedComputePipelines);
1232
    SDL_free(commandBuffer->usedUniformBuffers);
1233
    SDL_free(commandBuffer->textureDownloads);
1234
    SDL_free(commandBuffer);
1235
}
1236

1237
static void D3D12_INTERNAL_DestroyFence(D3D12Fence *fence)
1238
{
1239
    if (!fence) {
1240
        return;
1241
    }
1242
    if (fence->handle) {
1243
        ID3D12Fence_Release(fence->handle);
1244
    }
1245
    if (fence->event) {
1246
        CloseHandle(fence->event);
1247
    }
1248
    SDL_free(fence);
1249
}
1250

1251
static void D3D12_INTERNAL_DestroyRenderer(D3D12Renderer *renderer)
1252
{
1253
    // Release uniform buffers
1254
    for (Uint32 i = 0; i < renderer->uniformBufferPoolCount; i += 1) {
1255
        D3D12_INTERNAL_DestroyBuffer(
1256
            renderer,
1257
            renderer->uniformBufferPool[i]->buffer);
1258
        SDL_free(renderer->uniformBufferPool[i]);
1259
    }
1260

1261
    // Clean up descriptor heaps
1262
    for (Uint32 i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; i += 1) {
1263
        if (renderer->stagingDescriptorHeaps[i]) {
1264
            D3D12_INTERNAL_DestroyDescriptorHeap(renderer->stagingDescriptorHeaps[i]);
1265
            renderer->stagingDescriptorHeaps[i] = NULL;
1266
        }
1267
    }
1268

1269
    for (Uint32 i = 0; i < 2; i += 1) {
1270
        if (renderer->descriptorHeapPools[i].heaps) {
1271
            for (Uint32 j = 0; j < renderer->descriptorHeapPools[i].count; j += 1) {
1272
                if (renderer->descriptorHeapPools[i].heaps[j]) {
1273
                    D3D12_INTERNAL_DestroyDescriptorHeap(renderer->descriptorHeapPools[i].heaps[j]);
1274
                    renderer->descriptorHeapPools[i].heaps[j] = NULL;
1275
                }
1276
            }
1277
            SDL_free(renderer->descriptorHeapPools[i].heaps);
1278
        }
1279
        if (renderer->descriptorHeapPools[i].lock) {
1280
            SDL_DestroyMutex(renderer->descriptorHeapPools[i].lock);
1281
            renderer->descriptorHeapPools[i].lock = NULL;
1282
        }
1283
    }
1284

1285
    // Release command buffers
1286
    for (Uint32 i = 0; i < renderer->availableCommandBufferCount; i += 1) {
1287
        if (renderer->availableCommandBuffers[i]) {
1288
            D3D12_INTERNAL_DestroyCommandBuffer(renderer->availableCommandBuffers[i]);
1289
            renderer->availableCommandBuffers[i] = NULL;
1290
        }
1291
    }
1292

1293
    // Release fences
1294
    for (Uint32 i = 0; i < renderer->availableFenceCount; i += 1) {
1295
        if (renderer->availableFences[i]) {
1296
            D3D12_INTERNAL_DestroyFence(renderer->availableFences[i]);
1297
            renderer->availableFences[i] = NULL;
1298
        }
1299
    }
1300

1301
    // Clean up allocations
1302
    SDL_free(renderer->availableCommandBuffers);
1303
    SDL_free(renderer->submittedCommandBuffers);
1304
    SDL_free(renderer->uniformBufferPool);
1305
    SDL_free(renderer->claimedWindows);
1306
    SDL_free(renderer->availableFences);
1307
    SDL_free(renderer->buffersToDestroy);
1308
    SDL_free(renderer->texturesToDestroy);
1309
    SDL_free(renderer->samplersToDestroy);
1310
    SDL_free(renderer->graphicsPipelinesToDestroy);
1311
    SDL_free(renderer->computePipelinesToDestroy);
1312

1313
    // Tear down D3D12 objects
1314
    if (renderer->indirectDrawCommandSignature) {
1315
        ID3D12CommandSignature_Release(renderer->indirectDrawCommandSignature);
1316
        renderer->indirectDrawCommandSignature = NULL;
1317
    }
1318
    if (renderer->indirectIndexedDrawCommandSignature) {
1319
        ID3D12CommandSignature_Release(renderer->indirectIndexedDrawCommandSignature);
1320
        renderer->indirectIndexedDrawCommandSignature = NULL;
1321
    }
1322
    if (renderer->indirectDispatchCommandSignature) {
1323
        ID3D12CommandSignature_Release(renderer->indirectDispatchCommandSignature);
1324
        renderer->indirectDispatchCommandSignature = NULL;
1325
    }
1326
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
1327
    if (renderer->commandQueue) {
1328
        ID3D12CommandQueue_Release(renderer->commandQueue);
1329
        renderer->commandQueue = NULL;
1330
    }
1331
    if (renderer->device) {
1332
        ID3D12Device_Release(renderer->device);
1333
        renderer->device = NULL;
1334
    }
1335
    if (renderer->adapter) {
1336
        IDXGIAdapter1_Release(renderer->adapter);
1337
        renderer->adapter = NULL;
1338
    }
1339
    if (renderer->factory) {
1340
        IDXGIFactory4_Release(renderer->factory);
1341
        renderer->factory = NULL;
1342
    }
1343
    if (renderer->dxgiDebug) {
1344
        IDXGIDebug_ReportLiveObjects(
1345
            renderer->dxgiDebug,
1346
            D3D_IID_DXGI_DEBUG_ALL,
1347
            (DXGI_DEBUG_RLO_FLAGS)(DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_DETAIL));
1348
        IDXGIDebug_Release(renderer->dxgiDebug);
1349
        renderer->dxgiDebug = NULL;
1350
    }
1351
#endif
1352
    if (renderer->d3d12_dll) {
1353
        SDL_UnloadObject(renderer->d3d12_dll);
1354
        renderer->d3d12_dll = NULL;
1355
    }
1356
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
1357
    if (renderer->dxgi_dll) {
1358
        SDL_UnloadObject(renderer->dxgi_dll);
1359
        renderer->dxgi_dll = NULL;
1360
    }
1361
    if (renderer->dxgidebug_dll) {
1362
        SDL_UnloadObject(renderer->dxgidebug_dll);
1363
        renderer->dxgidebug_dll = NULL;
1364
    }
1365
#endif
1366
    renderer->D3D12SerializeRootSignature_func = NULL;
1367

1368
    if (renderer->iconv) {
1369
        SDL_iconv_close(renderer->iconv);
1370
    }
1371

1372
    SDL_DestroyMutex(renderer->stagingDescriptorHeapLock);
1373
    SDL_DestroyMutex(renderer->acquireCommandBufferLock);
1374
    SDL_DestroyMutex(renderer->acquireUniformBufferLock);
1375
    SDL_DestroyMutex(renderer->submitLock);
1376
    SDL_DestroyMutex(renderer->windowLock);
1377
    SDL_DestroyMutex(renderer->fenceLock);
1378
    SDL_DestroyMutex(renderer->disposeLock);
1379
    SDL_free(renderer);
1380
}
1381

1382
static void D3D12_DestroyDevice(SDL_GPUDevice *device)
1383
{
1384
    D3D12Renderer *renderer = (D3D12Renderer *)device->driverData;
1385

1386
    // Release blit pipeline structures
1387
    D3D12_INTERNAL_ReleaseBlitPipelines((SDL_GPURenderer *)renderer);
1388

1389
    // Flush any remaining GPU work...
1390
    D3D12_Wait((SDL_GPURenderer *)renderer);
1391

1392
    // Release window data
1393
    for (Sint32 i = renderer->claimedWindowCount - 1; i >= 0; i -= 1) {
1394
        D3D12_ReleaseWindow((SDL_GPURenderer *)renderer, renderer->claimedWindows[i]->window);
1395
    }
1396

1397
    D3D12_INTERNAL_DestroyRenderer(renderer);
1398
    SDL_free(device);
1399
}
1400

1401
// Barriers
1402

1403
static inline Uint32 D3D12_INTERNAL_CalcSubresource(
1404
    Uint32 mipLevel,
1405
    Uint32 layer,
1406
    Uint32 numLevels)
1407
{
1408
    return mipLevel + (layer * numLevels);
1409
}
1410

1411
static void D3D12_INTERNAL_ResourceBarrier(
1412
    D3D12CommandBuffer *commandBuffer,
1413
    D3D12_RESOURCE_STATES sourceState,
1414
    D3D12_RESOURCE_STATES destinationState,
1415
    ID3D12Resource *resource,
1416
    Uint32 subresourceIndex,
1417
    bool needsUavBarrier)
1418
{
1419
    D3D12_RESOURCE_BARRIER barrierDesc[2];
1420
    Uint32 numBarriers = 0;
1421

1422
    // No transition barrier is needed if the state is not changing.
1423
    if (sourceState != destinationState) {
1424
        barrierDesc[numBarriers].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
1425
        barrierDesc[numBarriers].Flags = (D3D12_RESOURCE_BARRIER_FLAGS)0;
1426
        barrierDesc[numBarriers].Transition.StateBefore = sourceState;
1427
        barrierDesc[numBarriers].Transition.StateAfter = destinationState;
1428
        barrierDesc[numBarriers].Transition.pResource = resource;
1429
        barrierDesc[numBarriers].Transition.Subresource = subresourceIndex;
1430

1431
        numBarriers += 1;
1432
    }
1433

1434
    if (needsUavBarrier) {
1435
        barrierDesc[numBarriers].Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
1436
        barrierDesc[numBarriers].Flags = (D3D12_RESOURCE_BARRIER_FLAGS)0;
1437
        barrierDesc[numBarriers].UAV.pResource = resource;
1438

1439
        numBarriers += 1;
1440
    }
1441

1442
    if (numBarriers > 0) {
1443
        ID3D12GraphicsCommandList_ResourceBarrier(
1444
            commandBuffer->graphicsCommandList,
1445
            numBarriers,
1446
            barrierDesc);
1447
    }
1448
}
1449

1450
static void D3D12_INTERNAL_TextureSubresourceBarrier(
1451
    D3D12CommandBuffer *commandBuffer,
1452
    D3D12_RESOURCE_STATES sourceState,
1453
    D3D12_RESOURCE_STATES destinationState,
1454
    D3D12TextureSubresource *textureSubresource)
1455
{
1456
    D3D12_INTERNAL_ResourceBarrier(
1457
        commandBuffer,
1458
        sourceState,
1459
        destinationState,
1460
        textureSubresource->parent->resource,
1461
        textureSubresource->index,
1462
        textureSubresource->parent->container->header.info.usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE);
1463
}
1464

1465
static D3D12_RESOURCE_STATES D3D12_INTERNAL_DefaultTextureResourceState(
1466
    SDL_GPUTextureUsageFlags usageFlags)
1467
{
1468
    // NOTE: order matters here!
1469

1470
    if (usageFlags & SDL_GPU_TEXTUREUSAGE_SAMPLER) {
1471
        return D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
1472
    } else if (usageFlags & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ) {
1473
        return D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
1474
    } else if (usageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
1475
        return D3D12_RESOURCE_STATE_RENDER_TARGET;
1476
    } else if (usageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
1477
        return D3D12_RESOURCE_STATE_DEPTH_WRITE;
1478
    } else if (usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ) {
1479
        return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
1480
    } else if (usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
1481
        return D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
1482
    } else {
1483
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Texture has no default usage mode!");
1484
        return D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
1485
    }
1486
}
1487

1488
static void D3D12_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
1489
    D3D12CommandBuffer *commandBuffer,
1490
    D3D12_RESOURCE_STATES destinationUsageMode,
1491
    D3D12TextureSubresource *textureSubresource)
1492
{
1493
    D3D12_INTERNAL_TextureSubresourceBarrier(
1494
        commandBuffer,
1495
        D3D12_INTERNAL_DefaultTextureResourceState(textureSubresource->parent->container->header.info.usageFlags),
1496
        destinationUsageMode,
1497
        textureSubresource);
1498
}
1499

1500
static void D3D12_INTERNAL_TextureTransitionFromDefaultUsage(
1501
    D3D12CommandBuffer *commandBuffer,
1502
    D3D12_RESOURCE_STATES destinationUsageMode,
1503
    D3D12Texture *texture)
1504
{
1505
    for (Uint32 i = 0; i < texture->subresourceCount; i += 1) {
1506
        D3D12_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
1507
            commandBuffer,
1508
            destinationUsageMode,
1509
            &texture->subresources[i]);
1510
    }
1511
}
1512

1513
static void D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
1514
    D3D12CommandBuffer *commandBuffer,
1515
    D3D12_RESOURCE_STATES sourceUsageMode,
1516
    D3D12TextureSubresource *textureSubresource)
1517
{
1518
    D3D12_INTERNAL_TextureSubresourceBarrier(
1519
        commandBuffer,
1520
        sourceUsageMode,
1521
        D3D12_INTERNAL_DefaultTextureResourceState(textureSubresource->parent->container->header.info.usageFlags),
1522
        textureSubresource);
1523
}
1524

1525
static void D3D12_INTERNAL_TextureTransitionToDefaultUsage(
1526
    D3D12CommandBuffer *commandBuffer,
1527
    D3D12_RESOURCE_STATES sourceUsageMode,
1528
    D3D12Texture *texture)
1529
{
1530
    for (Uint32 i = 0; i < texture->subresourceCount; i += 1) {
1531
        D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
1532
            commandBuffer,
1533
            sourceUsageMode,
1534
            &texture->subresources[i]);
1535
    }
1536
}
1537

1538
static D3D12_RESOURCE_STATES D3D12_INTERNAL_DefaultBufferResourceState(
1539
    D3D12Buffer *buffer)
1540
{
1541
    if (buffer->container->usageFlags & SDL_GPU_BUFFERUSAGE_VERTEX) {
1542
        return D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
1543
    } else if (buffer->container->usageFlags & SDL_GPU_BUFFERUSAGE_INDEX) {
1544
        return D3D12_RESOURCE_STATE_INDEX_BUFFER;
1545
    } else if (buffer->container->usageFlags & SDL_GPU_BUFFERUSAGE_INDIRECT) {
1546
        return D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
1547
    } else if (buffer->container->usageFlags & SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ) {
1548
        return D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
1549
    } else if (buffer->container->usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ) {
1550
        return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
1551
    } else if (buffer->container->usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE) {
1552
        return D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
1553
    } else {
1554
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Buffer has no default usage mode!");
1555
        return D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
1556
    }
1557
}
1558

1559
static void D3D12_INTERNAL_BufferBarrier(
1560
    D3D12CommandBuffer *commandBuffer,
1561
    D3D12_RESOURCE_STATES sourceState,
1562
    D3D12_RESOURCE_STATES destinationState,
1563
    D3D12Buffer *buffer)
1564
{
1565
    D3D12_INTERNAL_ResourceBarrier(
1566
        commandBuffer,
1567
        buffer->transitioned ? sourceState : D3D12_RESOURCE_STATE_COMMON,
1568
        destinationState,
1569
        buffer->handle,
1570
        0,
1571
        buffer->container->usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE);
1572

1573
    buffer->transitioned = true;
1574
}
1575

1576
static void D3D12_INTERNAL_BufferTransitionFromDefaultUsage(
1577
    D3D12CommandBuffer *commandBuffer,
1578
    D3D12_RESOURCE_STATES destinationState,
1579
    D3D12Buffer *buffer)
1580
{
1581
    D3D12_INTERNAL_BufferBarrier(
1582
        commandBuffer,
1583
        D3D12_INTERNAL_DefaultBufferResourceState(buffer),
1584
        destinationState,
1585
        buffer);
1586
}
1587

1588
static void D3D12_INTERNAL_BufferTransitionToDefaultUsage(
1589
    D3D12CommandBuffer *commandBuffer,
1590
    D3D12_RESOURCE_STATES sourceState,
1591
    D3D12Buffer *buffer)
1592
{
1593
    D3D12_INTERNAL_BufferBarrier(
1594
        commandBuffer,
1595
        sourceState,
1596
        D3D12_INTERNAL_DefaultBufferResourceState(buffer),
1597
        buffer);
1598
}
1599

1600
// Resource tracking
1601

1602
#define TRACK_RESOURCE(resource, type, array, count, capacity) \
1603
    Uint32 i;                                                  \
1604
                                                               \
1605
    for (i = 0; i < commandBuffer->count; i += 1) {            \
1606
        if (commandBuffer->array[i] == resource) {             \
1607
            return;                                            \
1608
        }                                                      \
1609
    }                                                          \
1610
                                                               \
1611
    if (commandBuffer->count == commandBuffer->capacity) {     \
1612
        commandBuffer->capacity += 1;                          \
1613
        commandBuffer->array = (type *)SDL_realloc(            \
1614
            commandBuffer->array,                              \
1615
            commandBuffer->capacity * sizeof(type));           \
1616
    }                                                          \
1617
    commandBuffer->array[commandBuffer->count] = resource;     \
1618
    commandBuffer->count += 1;                                 \
1619
    SDL_AtomicIncRef(&resource->referenceCount);
1620

1621
static void D3D12_INTERNAL_TrackTexture(
1622
    D3D12CommandBuffer *commandBuffer,
1623
    D3D12Texture *texture)
1624
{
1625
    TRACK_RESOURCE(
1626
        texture,
1627
        D3D12Texture *,
1628
        usedTextures,
1629
        usedTextureCount,
1630
        usedTextureCapacity)
1631
}
1632

1633
static void D3D12_INTERNAL_TrackBuffer(
1634
    D3D12CommandBuffer *commandBuffer,
1635
    D3D12Buffer *buffer)
1636
{
1637
    TRACK_RESOURCE(
1638
        buffer,
1639
        D3D12Buffer *,
1640
        usedBuffers,
1641
        usedBufferCount,
1642
        usedBufferCapacity)
1643
}
1644

1645
static void D3D12_INTERNAL_TrackSampler(
1646
    D3D12CommandBuffer *commandBuffer,
1647
    D3D12Sampler *sampler)
1648
{
1649
    TRACK_RESOURCE(
1650
        sampler,
1651
        D3D12Sampler *,
1652
        usedSamplers,
1653
        usedSamplerCount,
1654
        usedSamplerCapacity)
1655
}
1656

1657
static void D3D12_INTERNAL_TrackGraphicsPipeline(
1658
    D3D12CommandBuffer *commandBuffer,
1659
    D3D12GraphicsPipeline *graphicsPipeline)
1660
{
1661
    TRACK_RESOURCE(
1662
        graphicsPipeline,
1663
        D3D12GraphicsPipeline *,
1664
        usedGraphicsPipelines,
1665
        usedGraphicsPipelineCount,
1666
        usedGraphicsPipelineCapacity)
1667
}
1668

1669
static void D3D12_INTERNAL_TrackComputePipeline(
1670
    D3D12CommandBuffer *commandBuffer,
1671
    D3D12ComputePipeline *computePipeline)
1672
{
1673
    TRACK_RESOURCE(
1674
        computePipeline,
1675
        D3D12ComputePipeline *,
1676
        usedComputePipelines,
1677
        usedComputePipelineCount,
1678
        usedComputePipelineCapacity)
1679
}
1680

1681
#undef TRACK_RESOURCE
1682

1683
// State Creation
1684

1685
static D3D12DescriptorHeap *D3D12_INTERNAL_CreateDescriptorHeap(
1686
    D3D12Renderer *renderer,
1687
    D3D12_DESCRIPTOR_HEAP_TYPE type,
1688
    Uint32 descriptorCount,
1689
    bool staging)
1690
{
1691
    D3D12DescriptorHeap *heap;
1692
    ID3D12DescriptorHeap *handle;
1693
    D3D12_DESCRIPTOR_HEAP_DESC heapDesc;
1694
    HRESULT res;
1695

1696
    heap = (D3D12DescriptorHeap *)SDL_calloc(1, sizeof(D3D12DescriptorHeap));
1697
    if (!heap) {
1698
        return NULL;
1699
    }
1700

1701
    heap->currentDescriptorIndex = 0;
1702
    heap->inactiveDescriptorCount = 0;
1703
    heap->inactiveDescriptorIndices = NULL;
1704

1705
    if (staging) {
1706
        heap->inactiveDescriptorIndices = (Uint32 *)SDL_calloc(descriptorCount, sizeof(Uint32));
1707
        if (!heap->inactiveDescriptorIndices) {
1708
            D3D12_INTERNAL_DestroyDescriptorHeap(heap);
1709
            return NULL;
1710
        }
1711
    }
1712

1713
    heapDesc.NumDescriptors = descriptorCount;
1714
    heapDesc.Type = type;
1715
    heapDesc.Flags = staging ? D3D12_DESCRIPTOR_HEAP_FLAG_NONE : D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
1716
    heapDesc.NodeMask = 0;
1717

1718
    res = ID3D12Device_CreateDescriptorHeap(
1719
        renderer->device,
1720
        &heapDesc,
1721
        D3D_GUID(D3D_IID_ID3D12DescriptorHeap),
1722
        (void **)&handle);
1723

1724
    if (FAILED(res)) {
1725
        D3D12_INTERNAL_LogError(renderer->device, "Failed to create descriptor heap!", res);
1726
        D3D12_INTERNAL_DestroyDescriptorHeap(heap);
1727
        return NULL;
1728
    }
1729

1730
    heap->handle = handle;
1731
    heap->heapType = type;
1732
    heap->maxDescriptors = descriptorCount;
1733
    heap->staging = staging;
1734
    heap->descriptorSize = ID3D12Device_GetDescriptorHandleIncrementSize(renderer->device, type);
1735
    D3D_CALL_RET(handle, GetCPUDescriptorHandleForHeapStart, &heap->descriptorHeapCPUStart);
1736
    if (!staging) {
1737
        D3D_CALL_RET(handle, GetGPUDescriptorHandleForHeapStart, &heap->descriptorHeapGPUStart);
1738
    }
1739

1740
    return heap;
1741
}
1742

1743
static D3D12DescriptorHeap *D3D12_INTERNAL_AcquireDescriptorHeapFromPool(
1744
    D3D12CommandBuffer *commandBuffer,
1745
    D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType)
1746
{
1747
    D3D12DescriptorHeap *result;
1748
    D3D12Renderer *renderer = commandBuffer->renderer;
1749
    D3D12DescriptorHeapPool *pool = &renderer->descriptorHeapPools[descriptorHeapType];
1750

1751
    SDL_LockMutex(pool->lock);
1752
    if (pool->count > 0) {
1753
        result = pool->heaps[pool->count - 1];
1754
        pool->count -= 1;
1755
    } else {
1756
        result = D3D12_INTERNAL_CreateDescriptorHeap(
1757
            renderer,
1758
            descriptorHeapType,
1759
            descriptorHeapType == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ? VIEW_GPU_DESCRIPTOR_COUNT : SAMPLER_GPU_DESCRIPTOR_COUNT,
1760
            false);
1761
    }
1762
    SDL_UnlockMutex(pool->lock);
1763

1764
    return result;
1765
}
1766

1767
static void D3D12_INTERNAL_ReturnDescriptorHeapToPool(
1768
    D3D12Renderer *renderer,
1769
    D3D12DescriptorHeap *heap)
1770
{
1771
    D3D12DescriptorHeapPool *pool = &renderer->descriptorHeapPools[heap->heapType];
1772

1773
    heap->currentDescriptorIndex = 0;
1774

1775
    SDL_LockMutex(pool->lock);
1776
    if (pool->count >= pool->capacity) {
1777
        pool->capacity *= 2;
1778
        pool->heaps = (D3D12DescriptorHeap **)SDL_realloc(
1779
            pool->heaps,
1780
            pool->capacity * sizeof(D3D12DescriptorHeap *));
1781
    }
1782

1783
    pool->heaps[pool->count] = heap;
1784
    pool->count += 1;
1785
    SDL_UnlockMutex(pool->lock);
1786
}
1787

1788
/*
1789
 * The root signature lets us define "root parameters" which are essentially bind points for resources.
1790
 * These let us define the register ranges as well as the register "space".
1791
 * The register space is akin to the descriptor set index in Vulkan, which allows us to group resources
1792
 * by stage so that the registers from the vertex and fragment shaders don't clobber each other.
1793
 *
1794
 * Most of our root parameters are implemented as "descriptor tables" so we can
1795
 * copy and then point to contiguous descriptor regions.
1796
 * Uniform buffers are the exception - these have to be implemented as raw "root descriptors" so
1797
 * that we can dynamically update the address that the constant buffer view points to.
1798
 *
1799
 * The root signature has a maximum size of 64 DWORDs.
1800
 * A descriptor table uses 1 DWORD.
1801
 * A root descriptor uses 2 DWORDS.
1802
 * This means our biggest root signature uses 24 DWORDs total, well under the limit.
1803
 *
1804
 * The root parameter indices are created dynamically and stored in the D3D12GraphicsRootSignature struct.
1805
 */
1806
static D3D12GraphicsRootSignature *D3D12_INTERNAL_CreateGraphicsRootSignature(
1807
    D3D12Renderer *renderer,
1808
    D3D12Shader *vertexShader,
1809
    D3D12Shader *fragmentShader)
1810
{
1811
    // FIXME: I think the max can be smaller...
1812
    D3D12_ROOT_PARAMETER rootParameters[MAX_ROOT_SIGNATURE_PARAMETERS];
1813
    D3D12_DESCRIPTOR_RANGE descriptorRanges[MAX_ROOT_SIGNATURE_PARAMETERS];
1814
    Uint32 parameterCount = 0;
1815
    Uint32 rangeCount = 0;
1816
    D3D12_DESCRIPTOR_RANGE descriptorRange;
1817
    D3D12_ROOT_PARAMETER rootParameter;
1818
    D3D12GraphicsRootSignature *d3d12GraphicsRootSignature =
1819
        (D3D12GraphicsRootSignature *)SDL_calloc(1, sizeof(D3D12GraphicsRootSignature));
1820
    if (!d3d12GraphicsRootSignature) {
1821
        return NULL;
1822
    }
1823

1824
    SDL_zeroa(rootParameters);
1825
    SDL_zeroa(descriptorRanges);
1826
    SDL_zero(rootParameter);
1827

1828
    d3d12GraphicsRootSignature->vertexSamplerRootIndex = -1;
1829
    d3d12GraphicsRootSignature->vertexSamplerTextureRootIndex = -1;
1830
    d3d12GraphicsRootSignature->vertexStorageTextureRootIndex = -1;
1831
    d3d12GraphicsRootSignature->vertexStorageBufferRootIndex = -1;
1832

1833
    d3d12GraphicsRootSignature->fragmentSamplerRootIndex = -1;
1834
    d3d12GraphicsRootSignature->fragmentSamplerTextureRootIndex = -1;
1835
    d3d12GraphicsRootSignature->fragmentStorageTextureRootIndex = -1;
1836
    d3d12GraphicsRootSignature->fragmentStorageBufferRootIndex = -1;
1837

1838
    for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
1839
        d3d12GraphicsRootSignature->vertexUniformBufferRootIndex[i] = -1;
1840
        d3d12GraphicsRootSignature->fragmentUniformBufferRootIndex[i] = -1;
1841
    }
1842

1843
    if (vertexShader->samplerCount > 0) {
1844
        // Vertex Samplers
1845
        descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
1846
        descriptorRange.NumDescriptors = vertexShader->samplerCount;
1847
        descriptorRange.BaseShaderRegister = 0;
1848
        descriptorRange.RegisterSpace = 0;
1849
        descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
1850
        descriptorRanges[rangeCount] = descriptorRange;
1851

1852
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
1853
        rootParameter.DescriptorTable.NumDescriptorRanges = 1;
1854
        rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
1855
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
1856
        rootParameters[parameterCount] = rootParameter;
1857
        d3d12GraphicsRootSignature->vertexSamplerRootIndex = parameterCount;
1858
        rangeCount += 1;
1859
        parameterCount += 1;
1860

1861
        descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
1862
        descriptorRange.NumDescriptors = vertexShader->samplerCount;
1863
        descriptorRange.BaseShaderRegister = 0;
1864
        descriptorRange.RegisterSpace = 0;
1865
        descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
1866
        descriptorRanges[rangeCount] = descriptorRange;
1867

1868
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
1869
        rootParameter.DescriptorTable.NumDescriptorRanges = 1;
1870
        rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
1871
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
1872
        rootParameters[parameterCount] = rootParameter;
1873
        d3d12GraphicsRootSignature->vertexSamplerTextureRootIndex = parameterCount;
1874
        rangeCount += 1;
1875
        parameterCount += 1;
1876
    }
1877

1878
    if (vertexShader->storageTextureCount) {
1879
        // Vertex storage textures
1880
        descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
1881
        descriptorRange.NumDescriptors = vertexShader->storageTextureCount;
1882
        descriptorRange.BaseShaderRegister = vertexShader->samplerCount;
1883
        descriptorRange.RegisterSpace = 0;
1884
        descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
1885
        descriptorRanges[rangeCount] = descriptorRange;
1886

1887
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
1888
        rootParameter.DescriptorTable.NumDescriptorRanges = 1;
1889
        rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
1890
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
1891
        rootParameters[parameterCount] = rootParameter;
1892
        d3d12GraphicsRootSignature->vertexStorageTextureRootIndex = parameterCount;
1893
        rangeCount += 1;
1894
        parameterCount += 1;
1895
    }
1896

1897
    if (vertexShader->storageBufferCount) {
1898

1899
        // Vertex storage buffers
1900
        descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
1901
        descriptorRange.NumDescriptors = vertexShader->storageBufferCount;
1902
        descriptorRange.BaseShaderRegister = vertexShader->samplerCount + vertexShader->storageTextureCount;
1903
        descriptorRange.RegisterSpace = 0;
1904
        descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
1905
        descriptorRanges[rangeCount] = descriptorRange;
1906

1907
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
1908
        rootParameter.DescriptorTable.NumDescriptorRanges = 1;
1909
        rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
1910
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
1911
        rootParameters[parameterCount] = rootParameter;
1912
        d3d12GraphicsRootSignature->vertexStorageBufferRootIndex = parameterCount;
1913
        rangeCount += 1;
1914
        parameterCount += 1;
1915
    }
1916

1917
    // Vertex Uniforms
1918
    for (Uint32 i = 0; i < vertexShader->uniformBufferCount; i += 1) {
1919
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
1920
        rootParameter.Descriptor.ShaderRegister = i;
1921
        rootParameter.Descriptor.RegisterSpace = 1;
1922
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
1923
        rootParameters[parameterCount] = rootParameter;
1924
        d3d12GraphicsRootSignature->vertexUniformBufferRootIndex[i] = parameterCount;
1925
        parameterCount += 1;
1926
    }
1927

1928
    if (fragmentShader->samplerCount) {
1929
        // Fragment Samplers
1930
        descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
1931
        descriptorRange.NumDescriptors = fragmentShader->samplerCount;
1932
        descriptorRange.BaseShaderRegister = 0;
1933
        descriptorRange.RegisterSpace = 2;
1934
        descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
1935
        descriptorRanges[rangeCount] = descriptorRange;
1936

1937
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
1938
        rootParameter.DescriptorTable.NumDescriptorRanges = 1;
1939
        rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
1940
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
1941
        rootParameters[parameterCount] = rootParameter;
1942
        d3d12GraphicsRootSignature->fragmentSamplerRootIndex = parameterCount;
1943
        rangeCount += 1;
1944
        parameterCount += 1;
1945

1946
        descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
1947
        descriptorRange.NumDescriptors = fragmentShader->samplerCount;
1948
        descriptorRange.BaseShaderRegister = 0;
1949
        descriptorRange.RegisterSpace = 2;
1950
        descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
1951
        descriptorRanges[rangeCount] = descriptorRange;
1952

1953
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
1954
        rootParameter.DescriptorTable.NumDescriptorRanges = 1;
1955
        rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
1956
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
1957
        rootParameters[parameterCount] = rootParameter;
1958
        d3d12GraphicsRootSignature->fragmentSamplerTextureRootIndex = parameterCount;
1959
        rangeCount += 1;
1960
        parameterCount += 1;
1961
    }
1962

1963
    if (fragmentShader->storageTextureCount) {
1964
        // Fragment Storage Textures
1965
        descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
1966
        descriptorRange.NumDescriptors = fragmentShader->storageTextureCount;
1967
        descriptorRange.BaseShaderRegister = fragmentShader->samplerCount;
1968
        descriptorRange.RegisterSpace = 2;
1969
        descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
1970
        descriptorRanges[rangeCount] = descriptorRange;
1971

1972
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
1973
        rootParameter.DescriptorTable.NumDescriptorRanges = 1;
1974
        rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
1975
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
1976
        rootParameters[parameterCount] = rootParameter;
1977
        d3d12GraphicsRootSignature->fragmentStorageTextureRootIndex = parameterCount;
1978
        rangeCount += 1;
1979
        parameterCount += 1;
1980
    }
1981

1982
    if (fragmentShader->storageBufferCount) {
1983
        // Fragment Storage Buffers
1984
        descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
1985
        descriptorRange.NumDescriptors = fragmentShader->storageBufferCount;
1986
        descriptorRange.BaseShaderRegister = fragmentShader->samplerCount + fragmentShader->storageTextureCount;
1987
        descriptorRange.RegisterSpace = 2;
1988
        descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
1989
        descriptorRanges[rangeCount] = descriptorRange;
1990

1991
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
1992
        rootParameter.DescriptorTable.NumDescriptorRanges = 1;
1993
        rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
1994
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
1995
        rootParameters[parameterCount] = rootParameter;
1996
        d3d12GraphicsRootSignature->fragmentStorageBufferRootIndex = parameterCount;
1997
        rangeCount += 1;
1998
        parameterCount += 1;
1999
    }
2000

2001
    // Fragment Uniforms
2002
    for (Uint32 i = 0; i < fragmentShader->uniformBufferCount; i += 1) {
2003
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
2004
        rootParameter.Descriptor.ShaderRegister = i;
2005
        rootParameter.Descriptor.RegisterSpace = 3;
2006
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
2007
        rootParameters[parameterCount] = rootParameter;
2008
        d3d12GraphicsRootSignature->fragmentUniformBufferRootIndex[i] = parameterCount;
2009
        parameterCount += 1;
2010
    }
2011

2012
    // FIXME: shouldn't have to assert here
2013
    SDL_assert(parameterCount <= MAX_ROOT_SIGNATURE_PARAMETERS);
2014
    SDL_assert(rangeCount <= MAX_ROOT_SIGNATURE_PARAMETERS);
2015

2016
    // Create the root signature description
2017
    D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc;
2018
    rootSignatureDesc.NumParameters = parameterCount;
2019
    rootSignatureDesc.pParameters = rootParameters;
2020
    rootSignatureDesc.NumStaticSamplers = 0;
2021
    rootSignatureDesc.pStaticSamplers = NULL;
2022
    rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
2023

2024
    // Serialize the root signature
2025
    ID3DBlob *serializedRootSignature;
2026
    ID3DBlob *errorBlob;
2027
    HRESULT res = renderer->D3D12SerializeRootSignature_func(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &serializedRootSignature, &errorBlob);
2028

2029
    if (FAILED(res)) {
2030
        if (errorBlob) {
2031
            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to serialize RootSignature: %s", (const char *)ID3D10Blob_GetBufferPointer(errorBlob));
2032
            ID3D10Blob_Release(errorBlob);
2033
        }
2034
        D3D12_INTERNAL_DestroyGraphicsRootSignature(d3d12GraphicsRootSignature);
2035
        return NULL;
2036
    }
2037

2038
    // Create the root signature
2039
    ID3D12RootSignature *rootSignature;
2040

2041
    res = ID3D12Device_CreateRootSignature(
2042
        renderer->device,
2043
        0,
2044
        ID3D10Blob_GetBufferPointer(serializedRootSignature),
2045
        ID3D10Blob_GetBufferSize(serializedRootSignature),
2046
        D3D_GUID(D3D_IID_ID3D12RootSignature),
2047
        (void **)&rootSignature);
2048

2049
    if (FAILED(res)) {
2050
        if (errorBlob) {
2051
            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create RootSignature");
2052
            ID3D10Blob_Release(errorBlob);
2053
        }
2054
        D3D12_INTERNAL_DestroyGraphicsRootSignature(d3d12GraphicsRootSignature);
2055
        return NULL;
2056
    }
2057

2058
    d3d12GraphicsRootSignature->handle = rootSignature;
2059
    return d3d12GraphicsRootSignature;
2060
}
2061

2062
static bool D3D12_INTERNAL_CreateShaderBytecode(
2063
    D3D12Renderer *renderer,
2064
    Uint32 stage,
2065
    SDL_GPUShaderFormat format,
2066
    const Uint8 *code,
2067
    size_t codeSize,
2068
    const char *entryPointName,
2069
    void **pBytecode,
2070
    size_t *pBytecodeSize)
2071
{
2072
    if (pBytecode != NULL) {
2073
        *pBytecode = SDL_malloc(codeSize);
2074
        if (!*pBytecode) {
2075
            return false;
2076
        }
2077
        SDL_memcpy(*pBytecode, code, codeSize);
2078
        *pBytecodeSize = codeSize;
2079
    }
2080

2081
    return true;
2082
}
2083

2084
static D3D12ComputeRootSignature *D3D12_INTERNAL_CreateComputeRootSignature(
2085
    D3D12Renderer *renderer,
2086
    SDL_GPUComputePipelineCreateInfo *createInfo)
2087
{
2088
    // FIXME: I think the max can be smaller...
2089
    D3D12_ROOT_PARAMETER rootParameters[MAX_ROOT_SIGNATURE_PARAMETERS];
2090
    D3D12_DESCRIPTOR_RANGE descriptorRanges[MAX_ROOT_SIGNATURE_PARAMETERS];
2091
    Uint32 parameterCount = 0;
2092
    Uint32 rangeCount = 0;
2093
    D3D12_DESCRIPTOR_RANGE descriptorRange;
2094
    D3D12_ROOT_PARAMETER rootParameter;
2095
    D3D12ComputeRootSignature *d3d12ComputeRootSignature =
2096
        (D3D12ComputeRootSignature *)SDL_calloc(1, sizeof(D3D12ComputeRootSignature));
2097
    if (!d3d12ComputeRootSignature) {
2098
        return NULL;
2099
    }
2100

2101
    SDL_zeroa(rootParameters);
2102
    SDL_zeroa(descriptorRanges);
2103
    SDL_zero(rootParameter);
2104

2105
    d3d12ComputeRootSignature->readOnlyStorageTextureRootIndex = -1;
2106
    d3d12ComputeRootSignature->readOnlyStorageBufferRootIndex = -1;
2107
    d3d12ComputeRootSignature->writeOnlyStorageTextureRootIndex = -1;
2108
    d3d12ComputeRootSignature->writeOnlyStorageBufferRootIndex = -1;
2109

2110
    for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
2111
        d3d12ComputeRootSignature->uniformBufferRootIndex[i] = -1;
2112
    }
2113

2114
    if (createInfo->readOnlyStorageTextureCount) {
2115
        descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
2116
        descriptorRange.NumDescriptors = createInfo->readOnlyStorageTextureCount;
2117
        descriptorRange.BaseShaderRegister = 0;
2118
        descriptorRange.RegisterSpace = 0;
2119
        descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
2120
        descriptorRanges[rangeCount] = descriptorRange;
2121

2122
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
2123
        rootParameter.DescriptorTable.NumDescriptorRanges = 1;
2124
        rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
2125
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // ALL is used for compute
2126
        rootParameters[parameterCount] = rootParameter;
2127
        d3d12ComputeRootSignature->readOnlyStorageTextureRootIndex = parameterCount;
2128
        rangeCount += 1;
2129
        parameterCount += 1;
2130
    }
2131

2132
    if (createInfo->readOnlyStorageBufferCount) {
2133
        descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
2134
        descriptorRange.NumDescriptors = createInfo->readOnlyStorageBufferCount;
2135
        descriptorRange.BaseShaderRegister = createInfo->readOnlyStorageTextureCount;
2136
        descriptorRange.RegisterSpace = 0;
2137
        descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
2138
        descriptorRanges[rangeCount] = descriptorRange;
2139

2140
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
2141
        rootParameter.DescriptorTable.NumDescriptorRanges = 1;
2142
        rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
2143
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // ALL is used for compute
2144
        rootParameters[parameterCount] = rootParameter;
2145
        d3d12ComputeRootSignature->readOnlyStorageBufferRootIndex = parameterCount;
2146
        rangeCount += 1;
2147
        parameterCount += 1;
2148
    }
2149

2150
    if (createInfo->writeOnlyStorageTextureCount) {
2151
        descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
2152
        descriptorRange.NumDescriptors = createInfo->writeOnlyStorageTextureCount;
2153
        descriptorRange.BaseShaderRegister = 0;
2154
        descriptorRange.RegisterSpace = 1;
2155
        descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
2156
        descriptorRanges[rangeCount] = descriptorRange;
2157

2158
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
2159
        rootParameter.DescriptorTable.NumDescriptorRanges = 1;
2160
        rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
2161
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // ALL is used for compute
2162
        rootParameters[parameterCount] = rootParameter;
2163
        d3d12ComputeRootSignature->writeOnlyStorageTextureRootIndex = parameterCount;
2164
        rangeCount += 1;
2165
        parameterCount += 1;
2166
    }
2167

2168
    if (createInfo->writeOnlyStorageBufferCount) {
2169
        descriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
2170
        descriptorRange.NumDescriptors = createInfo->writeOnlyStorageBufferCount;
2171
        descriptorRange.BaseShaderRegister = createInfo->writeOnlyStorageTextureCount;
2172
        descriptorRange.RegisterSpace = 1;
2173
        descriptorRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
2174
        descriptorRanges[rangeCount] = descriptorRange;
2175

2176
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
2177
        rootParameter.DescriptorTable.NumDescriptorRanges = 1;
2178
        rootParameter.DescriptorTable.pDescriptorRanges = &descriptorRanges[rangeCount];
2179
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // ALL is used for compute
2180
        rootParameters[parameterCount] = rootParameter;
2181
        d3d12ComputeRootSignature->writeOnlyStorageBufferRootIndex = parameterCount;
2182
        rangeCount += 1;
2183
        parameterCount += 1;
2184
    }
2185

2186
    for (Uint32 i = 0; i < createInfo->uniformBufferCount; i += 1) {
2187
        rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
2188
        rootParameter.Descriptor.ShaderRegister = i;
2189
        rootParameter.Descriptor.RegisterSpace = 2;
2190
        rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; // ALL is used for compute
2191
        rootParameters[parameterCount] = rootParameter;
2192
        d3d12ComputeRootSignature->uniformBufferRootIndex[i] = parameterCount;
2193
        parameterCount += 1;
2194
    }
2195

2196
    D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc;
2197
    rootSignatureDesc.NumParameters = parameterCount;
2198
    rootSignatureDesc.pParameters = rootParameters;
2199
    rootSignatureDesc.NumStaticSamplers = 0;
2200
    rootSignatureDesc.pStaticSamplers = NULL;
2201
    rootSignatureDesc.Flags = (D3D12_ROOT_SIGNATURE_FLAGS)0;
2202

2203
    ID3DBlob *serializedRootSignature;
2204
    ID3DBlob *errorBlob;
2205
    HRESULT res = renderer->D3D12SerializeRootSignature_func(
2206
        &rootSignatureDesc,
2207
        D3D_ROOT_SIGNATURE_VERSION_1,
2208
        &serializedRootSignature,
2209
        &errorBlob);
2210

2211
    if (FAILED(res)) {
2212
        if (errorBlob) {
2213
            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to serialize RootSignature: %s", (const char *)ID3D10Blob_GetBufferPointer(errorBlob));
2214
            ID3D10Blob_Release(errorBlob);
2215
        }
2216
        D3D12_INTERNAL_DestroyComputeRootSignature(d3d12ComputeRootSignature);
2217
        return NULL;
2218
    }
2219

2220
    ID3D12RootSignature *rootSignature;
2221

2222
    res = ID3D12Device_CreateRootSignature(
2223
        renderer->device,
2224
        0,
2225
        ID3D10Blob_GetBufferPointer(serializedRootSignature),
2226
        ID3D10Blob_GetBufferSize(serializedRootSignature),
2227
        D3D_GUID(D3D_IID_ID3D12RootSignature),
2228
        (void **)&rootSignature);
2229

2230
    if (FAILED(res)) {
2231
        if (errorBlob) {
2232
            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create RootSignature");
2233
            ID3D10Blob_Release(errorBlob);
2234
        }
2235
        D3D12_INTERNAL_DestroyComputeRootSignature(d3d12ComputeRootSignature);
2236
        return NULL;
2237
    }
2238

2239
    d3d12ComputeRootSignature->handle = rootSignature;
2240
    return d3d12ComputeRootSignature;
2241
}
2242

2243
static SDL_GPUComputePipeline *D3D12_CreateComputePipeline(
2244
    SDL_GPURenderer *driverData,
2245
    SDL_GPUComputePipelineCreateInfo *pipelineCreateInfo)
2246
{
2247
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
2248
    void *bytecode;
2249
    size_t bytecodeSize;
2250
    ID3D12PipelineState *pipelineState;
2251

2252
    if (!D3D12_INTERNAL_CreateShaderBytecode(
2253
            renderer,
2254
            SDL_GPU_SHADERSTAGE_COMPUTE,
2255
            pipelineCreateInfo->format,
2256
            pipelineCreateInfo->code,
2257
            pipelineCreateInfo->codeSize,
2258
            pipelineCreateInfo->entryPointName,
2259
            &bytecode,
2260
            &bytecodeSize)) {
2261
        return NULL;
2262
    }
2263

2264
    D3D12ComputeRootSignature *rootSignature = D3D12_INTERNAL_CreateComputeRootSignature(
2265
        renderer,
2266
        pipelineCreateInfo);
2267

2268
    if (rootSignature == NULL) {
2269
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not create root signature!");
2270
        SDL_free(bytecode);
2271
        return NULL;
2272
    }
2273

2274
    D3D12_COMPUTE_PIPELINE_STATE_DESC pipelineDesc;
2275
    pipelineDesc.CS.pShaderBytecode = bytecode;
2276
    pipelineDesc.CS.BytecodeLength = bytecodeSize;
2277
    pipelineDesc.pRootSignature = rootSignature->handle;
2278
    pipelineDesc.CachedPSO.CachedBlobSizeInBytes = 0;
2279
    pipelineDesc.CachedPSO.pCachedBlob = NULL;
2280
    pipelineDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
2281
    pipelineDesc.NodeMask = 0;
2282

2283
    HRESULT res = ID3D12Device_CreateComputePipelineState(
2284
        renderer->device,
2285
        &pipelineDesc,
2286
        D3D_GUID(D3D_IID_ID3D12PipelineState),
2287
        (void **)&pipelineState);
2288

2289
    if (FAILED(res)) {
2290
        D3D12_INTERNAL_LogError(renderer->device, "Could not create compute pipeline state", res);
2291
        SDL_free(bytecode);
2292
        return NULL;
2293
    }
2294

2295
    D3D12ComputePipeline *computePipeline =
2296
        (D3D12ComputePipeline *)SDL_calloc(1, sizeof(D3D12ComputePipeline));
2297

2298
    if (!computePipeline) {
2299
        ID3D12PipelineState_Release(pipelineState);
2300
        SDL_free(bytecode);
2301
        return NULL;
2302
    }
2303

2304
    computePipeline->pipelineState = pipelineState;
2305
    computePipeline->rootSignature = rootSignature;
2306
    computePipeline->readOnlyStorageTextureCount = pipelineCreateInfo->readOnlyStorageTextureCount;
2307
    computePipeline->readOnlyStorageBufferCount = pipelineCreateInfo->readOnlyStorageBufferCount;
2308
    computePipeline->writeOnlyStorageTextureCount = pipelineCreateInfo->writeOnlyStorageTextureCount;
2309
    computePipeline->writeOnlyStorageBufferCount = pipelineCreateInfo->writeOnlyStorageBufferCount;
2310
    computePipeline->uniformBufferCount = pipelineCreateInfo->uniformBufferCount;
2311
    SDL_AtomicSet(&computePipeline->referenceCount, 0);
2312

2313
    return (SDL_GPUComputePipeline *)computePipeline;
2314
}
2315

2316
static bool D3D12_INTERNAL_ConvertRasterizerState(SDL_GPURasterizerState rasterizerState, D3D12_RASTERIZER_DESC *desc)
2317
{
2318
    if (!desc) {
2319
        return false;
2320
    }
2321

2322
    desc->FillMode = SDLToD3D12_FillMode[rasterizerState.fillMode];
2323
    desc->CullMode = SDLToD3D12_CullMode[rasterizerState.cullMode];
2324

2325
    switch (rasterizerState.frontFace) {
2326
    case SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE:
2327
        desc->FrontCounterClockwise = TRUE;
2328
        break;
2329
    case SDL_GPU_FRONTFACE_CLOCKWISE:
2330
        desc->FrontCounterClockwise = FALSE;
2331
        break;
2332
    default:
2333
        return false;
2334
    }
2335

2336
    if (rasterizerState.depthBiasEnable) {
2337
        desc->DepthBias = SDL_lroundf(rasterizerState.depthBiasConstantFactor);
2338
        desc->DepthBiasClamp = rasterizerState.depthBiasClamp;
2339
        desc->SlopeScaledDepthBias = rasterizerState.depthBiasSlopeFactor;
2340
    } else {
2341
        desc->DepthBias = 0;
2342
        desc->DepthBiasClamp = 0.0f;
2343
        desc->SlopeScaledDepthBias = 0.0f;
2344
    }
2345

2346
    desc->DepthClipEnable = TRUE;
2347
    desc->MultisampleEnable = FALSE;
2348
    desc->AntialiasedLineEnable = FALSE;
2349
    desc->ForcedSampleCount = 0;
2350
    desc->ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
2351

2352
    return true;
2353
}
2354

2355
static bool D3D12_INTERNAL_ConvertBlendState(SDL_GPUGraphicsPipelineCreateInfo *pipelineInfo, D3D12_BLEND_DESC *blendDesc)
2356
{
2357
    if (!blendDesc) {
2358
        return false;
2359
    }
2360

2361
    SDL_zerop(blendDesc);
2362
    blendDesc->AlphaToCoverageEnable = FALSE;
2363
    blendDesc->IndependentBlendEnable = FALSE;
2364

2365
    for (UINT i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) {
2366
        D3D12_RENDER_TARGET_BLEND_DESC rtBlendDesc;
2367
        rtBlendDesc.BlendEnable = FALSE;
2368
        rtBlendDesc.LogicOpEnable = FALSE;
2369
        rtBlendDesc.SrcBlend = D3D12_BLEND_ONE;
2370
        rtBlendDesc.DestBlend = D3D12_BLEND_ZERO;
2371
        rtBlendDesc.BlendOp = D3D12_BLEND_OP_ADD;
2372
        rtBlendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;
2373
        rtBlendDesc.DestBlendAlpha = D3D12_BLEND_ZERO;
2374
        rtBlendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
2375
        rtBlendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
2376
        rtBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
2377

2378
        // If attachmentInfo has more blend states, you can set IndependentBlendEnable to TRUE and assign different blend states to each render target slot
2379
        if (i < pipelineInfo->attachmentInfo.colorAttachmentCount) {
2380

2381
            SDL_GPUColorAttachmentBlendState sdlBlendState = pipelineInfo->attachmentInfo.colorAttachmentDescriptions[i].blendState;
2382

2383
            rtBlendDesc.BlendEnable = sdlBlendState.blendEnable;
2384
            rtBlendDesc.SrcBlend = SDLToD3D12_BlendFactor[sdlBlendState.srcColorBlendFactor];
2385
            rtBlendDesc.DestBlend = SDLToD3D12_BlendFactor[sdlBlendState.dstColorBlendFactor];
2386
            rtBlendDesc.BlendOp = SDLToD3D12_BlendOp[sdlBlendState.colorBlendOp];
2387
            rtBlendDesc.SrcBlendAlpha = SDLToD3D12_BlendFactorAlpha[sdlBlendState.srcAlphaBlendFactor];
2388
            rtBlendDesc.DestBlendAlpha = SDLToD3D12_BlendFactorAlpha[sdlBlendState.dstAlphaBlendFactor];
2389
            rtBlendDesc.BlendOpAlpha = SDLToD3D12_BlendOp[sdlBlendState.alphaBlendOp];
2390
            rtBlendDesc.RenderTargetWriteMask = sdlBlendState.colorWriteMask;
2391

2392
            if (i > 0) {
2393
                blendDesc->IndependentBlendEnable = TRUE;
2394
            }
2395
        }
2396

2397
        blendDesc->RenderTarget[i] = rtBlendDesc;
2398
    }
2399

2400
    return true;
2401
}
2402

2403
static bool D3D12_INTERNAL_ConvertDepthStencilState(SDL_GPUDepthStencilState depthStencilState, D3D12_DEPTH_STENCIL_DESC *desc)
2404
{
2405
    if (desc == NULL) {
2406
        return false;
2407
    }
2408

2409
    desc->DepthEnable = depthStencilState.depthTestEnable == true ? TRUE : FALSE;
2410
    desc->DepthWriteMask = depthStencilState.depthWriteEnable == true ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
2411
    desc->DepthFunc = SDLToD3D12_CompareOp[depthStencilState.compareOp];
2412
    desc->StencilEnable = depthStencilState.stencilTestEnable == true ? TRUE : FALSE;
2413
    desc->StencilReadMask = depthStencilState.compareMask;
2414
    desc->StencilWriteMask = depthStencilState.writeMask;
2415

2416
    desc->FrontFace.StencilFailOp = SDLToD3D12_StencilOp[depthStencilState.frontStencilState.failOp];
2417
    desc->FrontFace.StencilDepthFailOp = SDLToD3D12_StencilOp[depthStencilState.frontStencilState.depthFailOp];
2418
    desc->FrontFace.StencilPassOp = SDLToD3D12_StencilOp[depthStencilState.frontStencilState.passOp];
2419
    desc->FrontFace.StencilFunc = SDLToD3D12_CompareOp[depthStencilState.frontStencilState.compareOp];
2420

2421
    desc->BackFace.StencilFailOp = SDLToD3D12_StencilOp[depthStencilState.backStencilState.failOp];
2422
    desc->BackFace.StencilDepthFailOp = SDLToD3D12_StencilOp[depthStencilState.backStencilState.depthFailOp];
2423
    desc->BackFace.StencilPassOp = SDLToD3D12_StencilOp[depthStencilState.backStencilState.passOp];
2424
    desc->BackFace.StencilFunc = SDLToD3D12_CompareOp[depthStencilState.backStencilState.compareOp];
2425

2426
    return true;
2427
}
2428

2429
static bool D3D12_INTERNAL_ConvertVertexInputState(SDL_GPUVertexInputState vertexInputState, D3D12_INPUT_ELEMENT_DESC *desc, const char *semantic)
2430
{
2431
    if (desc == NULL || vertexInputState.vertexAttributeCount == 0) {
2432
        return false;
2433
    }
2434

2435
    for (Uint32 i = 0; i < vertexInputState.vertexAttributeCount; i += 1) {
2436
        SDL_GPUVertexAttribute attribute = vertexInputState.vertexAttributes[i];
2437

2438
        desc[i].SemanticName = semantic;
2439
        desc[i].SemanticIndex = attribute.location;
2440
        desc[i].Format = SDLToD3D12_VertexFormat[attribute.format];
2441
        desc[i].InputSlot = attribute.binding;
2442
        desc[i].AlignedByteOffset = attribute.offset;
2443
        desc[i].InputSlotClass = SDLToD3D12_InputRate[vertexInputState.vertexBindings[attribute.binding].inputRate];
2444
        desc[i].InstanceDataStepRate = (vertexInputState.vertexBindings[attribute.binding].inputRate == SDL_GPU_VERTEXINPUTRATE_INSTANCE) ? vertexInputState.vertexBindings[attribute.binding].instanceStepRate : 0;
2445
    }
2446

2447
    return true;
2448
}
2449

2450
static void D3D12_INTERNAL_AssignCpuDescriptorHandle(
2451
    D3D12Renderer *renderer,
2452
    D3D12_DESCRIPTOR_HEAP_TYPE heapType,
2453
    D3D12CPUDescriptor *cpuDescriptor)
2454
{
2455
    D3D12DescriptorHeap *heap = renderer->stagingDescriptorHeaps[heapType];
2456
    Uint32 descriptorIndex;
2457

2458
    cpuDescriptor->heap = heap;
2459

2460
    SDL_LockMutex(renderer->stagingDescriptorHeapLock);
2461

2462
    if (heap->inactiveDescriptorCount > 0) {
2463
        descriptorIndex = heap->inactiveDescriptorIndices[heap->inactiveDescriptorCount - 1];
2464
        heap->inactiveDescriptorCount -= 1;
2465
    } else if (heap->currentDescriptorIndex < heap->maxDescriptors) {
2466
        descriptorIndex = heap->currentDescriptorIndex;
2467
        heap->currentDescriptorIndex += 1;
2468
    } else {
2469
        cpuDescriptor->cpuHandleIndex = SDL_MAX_UINT32;
2470
        cpuDescriptor->cpuHandle.ptr = 0;
2471
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Out of CPU descriptor handles, many bad things are going to happen!");
2472
        SDL_UnlockMutex(renderer->stagingDescriptorHeapLock);
2473
        return;
2474
    }
2475

2476
    SDL_UnlockMutex(renderer->stagingDescriptorHeapLock);
2477

2478
    cpuDescriptor->cpuHandleIndex = descriptorIndex;
2479
    cpuDescriptor->cpuHandle.ptr = heap->descriptorHeapCPUStart.ptr + (descriptorIndex * heap->descriptorSize);
2480
}
2481

2482
static SDL_GPUGraphicsPipeline *D3D12_CreateGraphicsPipeline(
2483
    SDL_GPURenderer *driverData,
2484
    SDL_GPUGraphicsPipelineCreateInfo *pipelineCreateInfo)
2485
{
2486
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
2487
    D3D12Shader *vertShader = (D3D12Shader *)pipelineCreateInfo->vertexShader;
2488
    D3D12Shader *fragShader = (D3D12Shader *)pipelineCreateInfo->fragmentShader;
2489

2490
    D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
2491
    SDL_zero(psoDesc);
2492
    psoDesc.VS.pShaderBytecode = vertShader->bytecode;
2493
    psoDesc.VS.BytecodeLength = vertShader->bytecodeSize;
2494
    psoDesc.PS.pShaderBytecode = fragShader->bytecode;
2495
    psoDesc.PS.BytecodeLength = fragShader->bytecodeSize;
2496

2497
    D3D12_INPUT_ELEMENT_DESC inputElementDescs[D3D12_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT];
2498
    if (pipelineCreateInfo->vertexInputState.vertexAttributeCount > 0) {
2499
        psoDesc.InputLayout.pInputElementDescs = inputElementDescs;
2500
        psoDesc.InputLayout.NumElements = pipelineCreateInfo->vertexInputState.vertexAttributeCount;
2501
        D3D12_INTERNAL_ConvertVertexInputState(pipelineCreateInfo->vertexInputState, inputElementDescs, renderer->semantic);
2502
    }
2503

2504
    psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
2505

2506
    if (!D3D12_INTERNAL_ConvertRasterizerState(pipelineCreateInfo->rasterizerState, &psoDesc.RasterizerState)) {
2507
        return NULL;
2508
    }
2509
    if (!D3D12_INTERNAL_ConvertBlendState(pipelineCreateInfo, &psoDesc.BlendState)) {
2510
        return NULL;
2511
    }
2512
    if (!D3D12_INTERNAL_ConvertDepthStencilState(pipelineCreateInfo->depthStencilState, &psoDesc.DepthStencilState)) {
2513
        return NULL;
2514
    }
2515

2516
    D3D12GraphicsPipeline *pipeline = (D3D12GraphicsPipeline *)SDL_calloc(1, sizeof(D3D12GraphicsPipeline));
2517
    if (!pipeline) {
2518
        return NULL;
2519
    }
2520

2521
    psoDesc.SampleMask = UINT_MAX;
2522
    psoDesc.SampleDesc.Count = SDLToD3D12_SampleCount[pipelineCreateInfo->multisampleState.sampleCount];
2523
    psoDesc.SampleDesc.Quality = 0;
2524

2525
    psoDesc.DSVFormat = SDLToD3D12_TextureFormat[pipelineCreateInfo->attachmentInfo.depthStencilFormat];
2526
    psoDesc.NumRenderTargets = pipelineCreateInfo->attachmentInfo.colorAttachmentCount;
2527
    for (uint32_t i = 0; i < pipelineCreateInfo->attachmentInfo.colorAttachmentCount; i += 1) {
2528
        psoDesc.RTVFormats[i] = SDLToD3D12_TextureFormat[pipelineCreateInfo->attachmentInfo.colorAttachmentDescriptions[i].format];
2529
    }
2530

2531
    // Assuming some default values or further initialization
2532
    psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
2533
    psoDesc.CachedPSO.CachedBlobSizeInBytes = 0;
2534
    psoDesc.CachedPSO.pCachedBlob = NULL;
2535

2536
    psoDesc.NodeMask = 0;
2537

2538
    D3D12GraphicsRootSignature *rootSignature = D3D12_INTERNAL_CreateGraphicsRootSignature(
2539
        renderer,
2540
        vertShader,
2541
        fragShader);
2542

2543
    if (rootSignature == NULL) {
2544
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not create root signature!");
2545
        D3D12_INTERNAL_DestroyGraphicsPipeline(pipeline);
2546
        return NULL;
2547
    }
2548
    pipeline->rootSignature = rootSignature;
2549

2550
    psoDesc.pRootSignature = rootSignature->handle;
2551
    ID3D12PipelineState *pipelineState;
2552

2553
    HRESULT res = ID3D12Device_CreateGraphicsPipelineState(
2554
        renderer->device,
2555
        &psoDesc,
2556
        D3D_GUID(D3D_IID_ID3D12PipelineState),
2557
        (void **)&pipelineState);
2558
    if (FAILED(res)) {
2559
        D3D12_INTERNAL_LogError(renderer->device, "Could not create graphics pipeline state", res);
2560
        D3D12_INTERNAL_DestroyGraphicsPipeline(pipeline);
2561
        return NULL;
2562
    }
2563

2564
    pipeline->pipelineState = pipelineState;
2565

2566
    for (Uint32 i = 0; i < pipelineCreateInfo->vertexInputState.vertexBindingCount; i += 1) {
2567
        pipeline->vertexStrides[i] = pipelineCreateInfo->vertexInputState.vertexBindings[i].stride;
2568
    }
2569

2570
    pipeline->primitiveType = pipelineCreateInfo->primitiveType;
2571
    pipeline->blendConstants[0] = pipelineCreateInfo->blendConstants[0];
2572
    pipeline->blendConstants[1] = pipelineCreateInfo->blendConstants[1];
2573
    pipeline->blendConstants[2] = pipelineCreateInfo->blendConstants[2];
2574
    pipeline->blendConstants[3] = pipelineCreateInfo->blendConstants[3];
2575
    pipeline->stencilRef = pipelineCreateInfo->depthStencilState.reference;
2576

2577
    pipeline->vertexSamplerCount = vertShader->samplerCount;
2578
    pipeline->vertexStorageTextureCount = vertShader->storageTextureCount;
2579
    pipeline->vertexStorageBufferCount = vertShader->storageBufferCount;
2580
    pipeline->vertexUniformBufferCount = vertShader->uniformBufferCount;
2581

2582
    pipeline->fragmentSamplerCount = fragShader->samplerCount;
2583
    pipeline->fragmentStorageTextureCount = fragShader->storageTextureCount;
2584
    pipeline->fragmentStorageBufferCount = fragShader->storageBufferCount;
2585
    pipeline->fragmentUniformBufferCount = fragShader->uniformBufferCount;
2586

2587
    SDL_AtomicSet(&pipeline->referenceCount, 0);
2588
    return (SDL_GPUGraphicsPipeline *)pipeline;
2589
}
2590

2591
static SDL_GPUSampler *D3D12_CreateSampler(
2592
    SDL_GPURenderer *driverData,
2593
    SDL_GPUSamplerCreateInfo *samplerCreateInfo)
2594
{
2595
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
2596
    D3D12Sampler *sampler = (D3D12Sampler *)SDL_calloc(1, sizeof(D3D12Sampler));
2597
    if (!sampler) {
2598
        return NULL;
2599
    }
2600
    D3D12_SAMPLER_DESC samplerDesc;
2601

2602
    samplerDesc.Filter = SDLToD3D12_Filter(
2603
        samplerCreateInfo->minFilter,
2604
        samplerCreateInfo->magFilter,
2605
        samplerCreateInfo->mipmapMode,
2606
        samplerCreateInfo->compareEnable,
2607
        samplerCreateInfo->anisotropyEnable);
2608
    samplerDesc.AddressU = SDLToD3D12_SamplerAddressMode[samplerCreateInfo->addressModeU];
2609
    samplerDesc.AddressV = SDLToD3D12_SamplerAddressMode[samplerCreateInfo->addressModeV];
2610
    samplerDesc.AddressW = SDLToD3D12_SamplerAddressMode[samplerCreateInfo->addressModeW];
2611
    samplerDesc.MaxAnisotropy = (Uint32)samplerCreateInfo->maxAnisotropy;
2612
    samplerDesc.ComparisonFunc = SDLToD3D12_CompareOp[samplerCreateInfo->compareOp];
2613
    samplerDesc.MinLOD = samplerCreateInfo->minLod;
2614
    samplerDesc.MaxLOD = samplerCreateInfo->maxLod;
2615
    samplerDesc.MipLODBias = samplerCreateInfo->mipLodBias;
2616
    samplerDesc.BorderColor[0] = 0;
2617
    samplerDesc.BorderColor[1] = 0;
2618
    samplerDesc.BorderColor[2] = 0;
2619
    samplerDesc.BorderColor[3] = 0;
2620

2621
    D3D12_INTERNAL_AssignCpuDescriptorHandle(
2622
        renderer,
2623
        D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
2624
        &sampler->handle);
2625

2626
    ID3D12Device_CreateSampler(
2627
        renderer->device,
2628
        &samplerDesc,
2629
        sampler->handle.cpuHandle);
2630

2631
    sampler->createInfo = *samplerCreateInfo;
2632
    SDL_AtomicSet(&sampler->referenceCount, 0);
2633
    return (SDL_GPUSampler *)sampler;
2634
}
2635

2636
static SDL_GPUShader *D3D12_CreateShader(
2637
    SDL_GPURenderer *driverData,
2638
    SDL_GPUShaderCreateInfo *shaderCreateInfo)
2639
{
2640
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
2641
    void *bytecode;
2642
    size_t bytecodeSize;
2643
    D3D12Shader *shader;
2644

2645
    if (!D3D12_INTERNAL_CreateShaderBytecode(
2646
            renderer,
2647
            shaderCreateInfo->stage,
2648
            shaderCreateInfo->format,
2649
            shaderCreateInfo->code,
2650
            shaderCreateInfo->codeSize,
2651
            shaderCreateInfo->entryPointName,
2652
            &bytecode,
2653
            &bytecodeSize)) {
2654
        return NULL;
2655
    }
2656
    shader = (D3D12Shader *)SDL_calloc(1, sizeof(D3D12Shader));
2657
    if (!shader) {
2658
        SDL_free(bytecode);
2659
        return NULL;
2660
    }
2661
    shader->samplerCount = shaderCreateInfo->samplerCount;
2662
    shader->storageBufferCount = shaderCreateInfo->storageBufferCount;
2663
    shader->storageTextureCount = shaderCreateInfo->storageTextureCount;
2664
    shader->uniformBufferCount = shaderCreateInfo->uniformBufferCount;
2665

2666
    shader->bytecode = bytecode;
2667
    shader->bytecodeSize = bytecodeSize;
2668

2669
    return (SDL_GPUShader *)shader;
2670
}
2671

2672
static D3D12Texture *D3D12_INTERNAL_CreateTexture(
2673
    D3D12Renderer *renderer,
2674
    SDL_GPUTextureCreateInfo *textureCreateInfo,
2675
    bool isSwapchainTexture)
2676
{
2677
    D3D12Texture *texture;
2678
    ID3D12Resource *handle;
2679
    D3D12_HEAP_PROPERTIES heapProperties;
2680
    D3D12_HEAP_FLAGS heapFlags = (D3D12_HEAP_FLAGS)0;
2681
    D3D12_RESOURCE_DESC desc;
2682
    D3D12_RESOURCE_FLAGS resourceFlags = (D3D12_RESOURCE_FLAGS)0;
2683
    D3D12_RESOURCE_STATES initialState = (D3D12_RESOURCE_STATES)0;
2684
    D3D12_CLEAR_VALUE clearValue;
2685
    bool useClearValue = false;
2686
    HRESULT res;
2687

2688
    texture = (D3D12Texture *)SDL_calloc(1, sizeof(D3D12Texture));
2689
    if (!texture) {
2690
        return NULL;
2691
    }
2692

2693
    Uint32 layerCount = textureCreateInfo->type == SDL_GPU_TEXTURETYPE_3D ? 1 : textureCreateInfo->layerCountOrDepth;
2694
    Uint32 depth = textureCreateInfo->type == SDL_GPU_TEXTURETYPE_3D ? textureCreateInfo->layerCountOrDepth : 1;
2695

2696
    if (textureCreateInfo->usageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
2697
        resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
2698
        useClearValue = true;
2699
        clearValue.Color[0] = SDL_GetFloatProperty(textureCreateInfo->props, SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_R_FLOAT, 0);
2700
        clearValue.Color[1] = SDL_GetFloatProperty(textureCreateInfo->props, SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_G_FLOAT, 0);
2701
        clearValue.Color[2] = SDL_GetFloatProperty(textureCreateInfo->props, SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_B_FLOAT, 0);
2702
        clearValue.Color[3] = SDL_GetFloatProperty(textureCreateInfo->props, SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_A_FLOAT, 0);
2703
    }
2704

2705
    if (textureCreateInfo->usageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
2706
        resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
2707
        useClearValue = true;
2708
        clearValue.DepthStencil.Depth = SDL_GetFloatProperty(textureCreateInfo->props, SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_DEPTH_FLOAT, 0);
2709
        clearValue.DepthStencil.Stencil = (UINT8)SDL_GetNumberProperty(textureCreateInfo->props, SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_STENCIL_UINT8, 0);
2710
    }
2711

2712
    if (textureCreateInfo->usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
2713
        resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
2714
    }
2715

2716
    heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
2717
    heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
2718
    heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
2719
    heapProperties.CreationNodeMask = 0; // We don't do multi-adapter operation
2720
    heapProperties.VisibleNodeMask = 0;  // We don't do multi-adapter operation
2721

2722
    heapFlags = isSwapchainTexture ? D3D12_HEAP_FLAG_ALLOW_DISPLAY : D3D12_HEAP_FLAG_NONE;
2723

2724
    if (textureCreateInfo->type != SDL_GPU_TEXTURETYPE_3D) {
2725
        desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
2726
        desc.Alignment = isSwapchainTexture ? 0 : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
2727
        desc.Width = textureCreateInfo->width;
2728
        desc.Height = textureCreateInfo->height;
2729
        desc.DepthOrArraySize = textureCreateInfo->layerCountOrDepth;
2730
        desc.MipLevels = textureCreateInfo->levelCount;
2731
        desc.Format = SDLToD3D12_TextureFormat[textureCreateInfo->format];
2732
        desc.SampleDesc.Count = 1;
2733
        desc.SampleDesc.Quality = 0;
2734
        desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; // Apparently this is the most efficient choice
2735
        desc.Flags = resourceFlags;
2736
    } else {
2737
        desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D;
2738
        desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
2739
        desc.Width = textureCreateInfo->width;
2740
        desc.Height = textureCreateInfo->height;
2741
        desc.DepthOrArraySize = textureCreateInfo->layerCountOrDepth;
2742
        desc.MipLevels = textureCreateInfo->levelCount;
2743
        desc.Format = SDLToD3D12_TextureFormat[textureCreateInfo->format];
2744
        desc.SampleDesc.Count = 1;
2745
        desc.SampleDesc.Quality = 0;
2746
        desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
2747
        desc.Flags = resourceFlags;
2748
    }
2749

2750
    initialState = isSwapchainTexture ? D3D12_RESOURCE_STATE_PRESENT : D3D12_INTERNAL_DefaultTextureResourceState(textureCreateInfo->usageFlags);
2751
    clearValue.Format = desc.Format;
2752

2753
    res = ID3D12Device_CreateCommittedResource(
2754
        renderer->device,
2755
        &heapProperties,
2756
        heapFlags,
2757
        &desc,
2758
        initialState,
2759
        useClearValue ? &clearValue : NULL,
2760
        D3D_GUID(D3D_IID_ID3D12Resource),
2761
        (void **)&handle);
2762
    if (FAILED(res)) {
2763
        D3D12_INTERNAL_LogError(renderer->device, "Failed to create texture!", res);
2764
        D3D12_INTERNAL_DestroyTexture(renderer, texture);
2765
        return NULL;
2766
    }
2767

2768
    texture->resource = handle;
2769

2770
    // Create the SRV if applicable
2771
    if ((textureCreateInfo->usageFlags & SDL_GPU_TEXTUREUSAGE_SAMPLER) ||
2772
        (textureCreateInfo->usageFlags & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ) ||
2773
        (textureCreateInfo->usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ)) {
2774
        D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
2775

2776
        D3D12_INTERNAL_AssignCpuDescriptorHandle(
2777
            renderer,
2778
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
2779
            &texture->srvHandle);
2780

2781
        srvDesc.Format = SDLToD3D12_TextureFormat[textureCreateInfo->format];
2782
        srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
2783

2784
        if (textureCreateInfo->type == SDL_GPU_TEXTURETYPE_CUBE) {
2785
            srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
2786
            srvDesc.TextureCube.MipLevels = textureCreateInfo->levelCount;
2787
            srvDesc.TextureCube.MostDetailedMip = 0;
2788
            srvDesc.TextureCube.ResourceMinLODClamp = 0;
2789
        } else if (textureCreateInfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY) {
2790
            srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
2791
            srvDesc.Texture2DArray.MipLevels = textureCreateInfo->levelCount;
2792
            srvDesc.Texture2DArray.MostDetailedMip = 0;
2793
            srvDesc.Texture2DArray.FirstArraySlice = 0;
2794
            srvDesc.Texture2DArray.ArraySize = layerCount;
2795
            srvDesc.Texture2DArray.ResourceMinLODClamp = 0;
2796
            srvDesc.Texture2DArray.PlaneSlice = 0;
2797
        } else if (textureCreateInfo->type == SDL_GPU_TEXTURETYPE_3D) {
2798
            srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
2799
            srvDesc.Texture3D.MipLevels = textureCreateInfo->levelCount;
2800
            srvDesc.Texture3D.MostDetailedMip = 0;
2801
            srvDesc.Texture3D.ResourceMinLODClamp = 0; // default behavior
2802
        } else {
2803
            srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
2804
            srvDesc.Texture2D.MipLevels = textureCreateInfo->levelCount;
2805
            srvDesc.Texture2D.MostDetailedMip = 0;
2806
            srvDesc.Texture2D.PlaneSlice = 0;
2807
            srvDesc.Texture2D.ResourceMinLODClamp = 0; // default behavior
2808
        }
2809

2810
        ID3D12Device_CreateShaderResourceView(
2811
            renderer->device,
2812
            handle,
2813
            &srvDesc,
2814
            texture->srvHandle.cpuHandle);
2815
    }
2816

2817
    SDL_AtomicSet(&texture->referenceCount, 0);
2818

2819
    texture->subresourceCount = textureCreateInfo->levelCount * layerCount;
2820
    texture->subresources = (D3D12TextureSubresource *)SDL_calloc(
2821
        texture->subresourceCount, sizeof(D3D12TextureSubresource));
2822
    if (!texture->subresources) {
2823
        D3D12_INTERNAL_DestroyTexture(renderer, texture);
2824
        return NULL;
2825
    }
2826
    for (Uint32 layerIndex = 0; layerIndex < layerCount; layerIndex += 1) {
2827
        for (Uint32 levelIndex = 0; levelIndex < textureCreateInfo->levelCount; levelIndex += 1) {
2828
            Uint32 subresourceIndex = D3D12_INTERNAL_CalcSubresource(
2829
                levelIndex,
2830
                layerIndex,
2831
                textureCreateInfo->levelCount);
2832

2833
            texture->subresources[subresourceIndex].parent = texture;
2834
            texture->subresources[subresourceIndex].layer = layerIndex;
2835
            texture->subresources[subresourceIndex].level = levelIndex;
2836
            texture->subresources[subresourceIndex].depth = depth;
2837
            texture->subresources[subresourceIndex].index = subresourceIndex;
2838

2839
            texture->subresources[subresourceIndex].rtvHandles = NULL;
2840
            texture->subresources[subresourceIndex].uavHandle.heap = NULL;
2841
            texture->subresources[subresourceIndex].dsvHandle.heap = NULL;
2842

2843
            // Create RTV if needed
2844
            if (textureCreateInfo->usageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
2845
                texture->subresources[subresourceIndex].rtvHandles = (D3D12CPUDescriptor *)SDL_calloc(depth, sizeof(D3D12CPUDescriptor));
2846

2847
                for (Uint32 depthIndex = 0; depthIndex < depth; depthIndex += 1) {
2848
                    D3D12_RENDER_TARGET_VIEW_DESC rtvDesc;
2849

2850
                    D3D12_INTERNAL_AssignCpuDescriptorHandle(
2851
                        renderer,
2852
                        D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
2853
                        &texture->subresources[subresourceIndex].rtvHandles[depthIndex]);
2854

2855
                    rtvDesc.Format = SDLToD3D12_TextureFormat[textureCreateInfo->format];
2856

2857
                    if (textureCreateInfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY || textureCreateInfo->type == SDL_GPU_TEXTURETYPE_CUBE) {
2858
                        rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
2859
                        rtvDesc.Texture2DArray.MipSlice = levelIndex;
2860
                        rtvDesc.Texture2DArray.FirstArraySlice = layerIndex;
2861
                        rtvDesc.Texture2DArray.ArraySize = 1;
2862
                        rtvDesc.Texture2DArray.PlaneSlice = 0;
2863
                    } else if (textureCreateInfo->type == SDL_GPU_TEXTURETYPE_3D) {
2864
                        rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
2865
                        rtvDesc.Texture3D.MipSlice = levelIndex;
2866
                        rtvDesc.Texture3D.FirstWSlice = depthIndex;
2867
                        rtvDesc.Texture3D.WSize = 1;
2868
                    } else {
2869
                        rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
2870
                        rtvDesc.Texture2D.MipSlice = levelIndex;
2871
                        rtvDesc.Texture2D.PlaneSlice = 0;
2872
                    }
2873

2874
                    ID3D12Device_CreateRenderTargetView(
2875
                        renderer->device,
2876
                        texture->resource,
2877
                        &rtvDesc,
2878
                        texture->subresources[subresourceIndex].rtvHandles[depthIndex].cpuHandle);
2879
                }
2880
            }
2881

2882
            // Create DSV if needed
2883
            if (textureCreateInfo->usageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
2884
                D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
2885

2886
                D3D12_INTERNAL_AssignCpuDescriptorHandle(
2887
                    renderer,
2888
                    D3D12_DESCRIPTOR_HEAP_TYPE_DSV,
2889
                    &texture->subresources[subresourceIndex].dsvHandle);
2890

2891
                dsvDesc.Format = SDLToD3D12_TextureFormat[textureCreateInfo->format];
2892
                dsvDesc.Flags = (D3D12_DSV_FLAGS)0;
2893
                dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
2894
                dsvDesc.Texture2D.MipSlice = levelIndex;
2895

2896
                ID3D12Device_CreateDepthStencilView(
2897
                    renderer->device,
2898
                    texture->resource,
2899
                    &dsvDesc,
2900
                    texture->subresources[subresourceIndex].dsvHandle.cpuHandle);
2901
            }
2902

2903
            // Create subresource UAV if necessary
2904
            if (textureCreateInfo->usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
2905
                D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc;
2906

2907
                D3D12_INTERNAL_AssignCpuDescriptorHandle(
2908
                    renderer,
2909
                    D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
2910
                    &texture->subresources[subresourceIndex].uavHandle);
2911

2912
                uavDesc.Format = SDLToD3D12_TextureFormat[textureCreateInfo->format];
2913

2914
                if (textureCreateInfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY || textureCreateInfo->type == SDL_GPU_TEXTURETYPE_CUBE) {
2915
                    uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
2916
                    uavDesc.Texture2DArray.MipSlice = levelIndex;
2917
                    uavDesc.Texture2DArray.FirstArraySlice = layerIndex;
2918
                    uavDesc.Texture2DArray.ArraySize = 1;
2919
                } else if (textureCreateInfo->type == SDL_GPU_TEXTURETYPE_3D) {
2920
                    uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
2921
                    uavDesc.Texture3D.MipSlice = levelIndex;
2922
                    uavDesc.Texture3D.FirstWSlice = 0;
2923
                    uavDesc.Texture3D.WSize = depth;
2924
                } else {
2925
                    uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
2926
                    uavDesc.Texture2D.MipSlice = levelIndex;
2927
                    uavDesc.Texture2D.PlaneSlice = 0;
2928
                }
2929

2930
                ID3D12Device_CreateUnorderedAccessView(
2931
                    renderer->device,
2932
                    texture->resource,
2933
                    NULL,
2934
                    &uavDesc,
2935
                    texture->subresources[subresourceIndex].uavHandle.cpuHandle);
2936
            }
2937
        }
2938
    }
2939

2940
    return texture;
2941
}
2942

2943
static SDL_GPUTexture *D3D12_CreateTexture(
2944
    SDL_GPURenderer *driverData,
2945
    SDL_GPUTextureCreateInfo *textureCreateInfo)
2946
{
2947
    D3D12TextureContainer *container = (D3D12TextureContainer *)SDL_calloc(1, sizeof(D3D12TextureContainer));
2948
    if (!container) {
2949
        return NULL;
2950
    }
2951

2952
    container->header.info = *textureCreateInfo;
2953
    container->textureCapacity = 1;
2954
    container->textureCount = 1;
2955
    container->textures = (D3D12Texture **)SDL_calloc(
2956
        container->textureCapacity, sizeof(D3D12Texture *));
2957

2958
    if (!container->textures) {
2959
        SDL_free(container);
2960
        return NULL;
2961
    }
2962

2963
    container->debugName = NULL;
2964
    container->canBeCycled = true;
2965

2966
    D3D12Texture *texture = D3D12_INTERNAL_CreateTexture(
2967
        (D3D12Renderer *)driverData,
2968
        textureCreateInfo,
2969
        false);
2970

2971
    if (!texture) {
2972
        SDL_free(container->textures);
2973
        SDL_free(container);
2974
        return NULL;
2975
    }
2976

2977
    container->textures[0] = texture;
2978
    container->activeTexture = texture;
2979

2980
    texture->container = container;
2981
    texture->containerIndex = 0;
2982

2983
    return (SDL_GPUTexture *)container;
2984
}
2985

2986
static D3D12Buffer *D3D12_INTERNAL_CreateBuffer(
2987
    D3D12Renderer *renderer,
2988
    SDL_GPUBufferUsageFlags usageFlags,
2989
    Uint32 sizeInBytes,
2990
    D3D12BufferType type)
2991
{
2992
    D3D12Buffer *buffer;
2993
    ID3D12Resource *handle;
2994
    D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc;
2995
    D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
2996
    D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
2997
    D3D12_HEAP_PROPERTIES heapProperties;
2998
    D3D12_RESOURCE_DESC desc;
2999
    D3D12_HEAP_FLAGS heapFlags = (D3D12_HEAP_FLAGS)0;
3000
    D3D12_RESOURCE_FLAGS resourceFlags = (D3D12_RESOURCE_FLAGS)0;
3001
    D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON;
3002
    HRESULT res;
3003

3004
    buffer = (D3D12Buffer *)SDL_calloc(1, sizeof(D3D12Buffer));
3005

3006
    if (!buffer) {
3007
        return NULL;
3008
    }
3009

3010
    if (usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE) {
3011
        resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
3012
    }
3013
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
3014
    if (usageFlags & SDL_GPU_BUFFERUSAGE_INDIRECT) {
3015
        resourceFlags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_INDIRECT_BUFFER;
3016
    }
3017
#endif
3018

3019
    heapProperties.CreationNodeMask = 0; // We don't do multi-adapter operation
3020
    heapProperties.VisibleNodeMask = 0;  // We don't do multi-adapter operation
3021

3022
    if (type == D3D12_BUFFER_TYPE_GPU) {
3023
        heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
3024
        heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
3025
        heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
3026
        heapFlags = D3D12_HEAP_FLAG_NONE;
3027
    } else if (type == D3D12_BUFFER_TYPE_UPLOAD) {
3028
        heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
3029
        heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
3030
        heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
3031
        heapFlags = D3D12_HEAP_FLAG_NONE;
3032
        initialState = D3D12_RESOURCE_STATE_GENERIC_READ;
3033
    } else if (type == D3D12_BUFFER_TYPE_DOWNLOAD) {
3034
        heapProperties.Type = D3D12_HEAP_TYPE_READBACK;
3035
        heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
3036
        heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
3037
        heapFlags = D3D12_HEAP_FLAG_NONE;
3038
        initialState = D3D12_RESOURCE_STATE_COPY_DEST;
3039
    } else if (type == D3D12_BUFFER_TYPE_UNIFORM) {
3040
        // D3D12 is badly designed, so we have to check if the fast path for uniform buffers is enabled
3041
        if (renderer->GPUUploadHeapSupported) {
3042
            heapProperties.Type = D3D12_HEAP_TYPE_GPU_UPLOAD;
3043
            heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
3044
            heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
3045
        } else {
3046
            heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
3047
            heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
3048
            heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
3049
            initialState = D3D12_RESOURCE_STATE_GENERIC_READ;
3050
        }
3051
        heapFlags = D3D12_HEAP_FLAG_NONE;
3052
    } else {
3053
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized buffer type!");
3054
        return NULL;
3055
    }
3056

3057
    desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
3058
    desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
3059
    desc.Width = sizeInBytes;
3060
    desc.Height = 1;
3061
    desc.DepthOrArraySize = 1;
3062
    desc.MipLevels = 1;
3063
    desc.Format = DXGI_FORMAT_UNKNOWN;
3064
    desc.SampleDesc.Count = 1;
3065
    desc.SampleDesc.Quality = 0;
3066
    desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
3067
    desc.Flags = resourceFlags;
3068

3069
    res = ID3D12Device_CreateCommittedResource(
3070
        renderer->device,
3071
        &heapProperties,
3072
        heapFlags,
3073
        &desc,
3074
        initialState,
3075
        NULL,
3076
        D3D_GUID(D3D_IID_ID3D12Resource),
3077
        (void **)&handle);
3078
    if (FAILED(res)) {
3079
        D3D12_INTERNAL_LogError(renderer->device, "Could not create buffer!", res);
3080
        D3D12_INTERNAL_DestroyBuffer(renderer, buffer);
3081
        return NULL;
3082
    }
3083

3084
    buffer->handle = handle;
3085
    SDL_AtomicSet(&buffer->referenceCount, 0);
3086

3087
    buffer->uavDescriptor.heap = NULL;
3088
    buffer->srvDescriptor.heap = NULL;
3089
    buffer->cbvDescriptor.heap = NULL;
3090

3091
    if (usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE) {
3092
        D3D12_INTERNAL_AssignCpuDescriptorHandle(
3093
            renderer,
3094
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
3095
            &buffer->uavDescriptor);
3096

3097
        uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
3098
        uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
3099
        uavDesc.Buffer.FirstElement = 0;
3100
        uavDesc.Buffer.NumElements = sizeInBytes / sizeof(Uint32);
3101
        uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
3102
        uavDesc.Buffer.CounterOffsetInBytes = 0; // TODO: support counters?
3103
        uavDesc.Buffer.StructureByteStride = 0;
3104

3105
        // Create UAV
3106
        ID3D12Device_CreateUnorderedAccessView(
3107
            renderer->device,
3108
            handle,
3109
            NULL, // TODO: support counters?
3110
            &uavDesc,
3111
            buffer->uavDescriptor.cpuHandle);
3112
    }
3113

3114
    if (
3115
        (usageFlags & SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ) ||
3116
        (usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ)) {
3117
        D3D12_INTERNAL_AssignCpuDescriptorHandle(
3118
            renderer,
3119
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
3120
            &buffer->srvDescriptor);
3121

3122
        srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
3123
        srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
3124
        srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
3125
        srvDesc.Buffer.FirstElement = 0;
3126
        srvDesc.Buffer.NumElements = sizeInBytes / sizeof(Uint32);
3127
        srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
3128
        srvDesc.Buffer.StructureByteStride = 0;
3129

3130
        // Create SRV
3131
        ID3D12Device_CreateShaderResourceView(
3132
            renderer->device,
3133
            handle,
3134
            &srvDesc,
3135
            buffer->srvDescriptor.cpuHandle);
3136
    }
3137

3138
    // FIXME: we may not need a CBV since we use root descriptors
3139
    if (type == D3D12_BUFFER_TYPE_UNIFORM) {
3140
        D3D12_INTERNAL_AssignCpuDescriptorHandle(
3141
            renderer,
3142
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
3143
            &buffer->cbvDescriptor);
3144

3145
        cbvDesc.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(handle);
3146
        cbvDesc.SizeInBytes = sizeInBytes;
3147

3148
        // Create CBV
3149
        ID3D12Device_CreateConstantBufferView(
3150
            renderer->device,
3151
            &cbvDesc,
3152
            buffer->cbvDescriptor.cpuHandle);
3153
    }
3154

3155
    buffer->virtualAddress = 0;
3156
    if (type == D3D12_BUFFER_TYPE_GPU || type == D3D12_BUFFER_TYPE_UNIFORM) {
3157
        buffer->virtualAddress = ID3D12Resource_GetGPUVirtualAddress(buffer->handle);
3158
    }
3159

3160
    buffer->mapPointer = NULL;
3161
    // Persistently map upload buffers
3162
    if (type == D3D12_BUFFER_TYPE_UPLOAD) {
3163
        res = ID3D12Resource_Map(
3164
            buffer->handle,
3165
            0,
3166
            NULL,
3167
            (void **)&buffer->mapPointer);
3168
        if (FAILED(res)) {
3169
            D3D12_INTERNAL_LogError(renderer->device, "Failed to map upload buffer!", res);
3170
            D3D12_INTERNAL_DestroyBuffer(renderer, buffer);
3171
            return NULL;
3172
        }
3173
    }
3174

3175
    buffer->container = NULL;
3176
    buffer->containerIndex = 0;
3177

3178
    buffer->transitioned = initialState != D3D12_RESOURCE_STATE_COMMON;
3179
    SDL_AtomicSet(&buffer->referenceCount, 0);
3180
    return buffer;
3181
}
3182

3183
static D3D12BufferContainer *D3D12_INTERNAL_CreateBufferContainer(
3184
    D3D12Renderer *renderer,
3185
    SDL_GPUBufferUsageFlags usageFlags,
3186
    Uint32 sizeInBytes,
3187
    D3D12BufferType type)
3188
{
3189
    D3D12BufferContainer *container;
3190
    D3D12Buffer *buffer;
3191

3192
    container = (D3D12BufferContainer *)SDL_calloc(1, sizeof(D3D12BufferContainer));
3193
    if (!container) {
3194
        return NULL;
3195
    }
3196

3197
    container->usageFlags = usageFlags;
3198
    container->size = sizeInBytes;
3199
    container->type = type;
3200

3201
    container->bufferCapacity = 1;
3202
    container->bufferCount = 1;
3203
    container->buffers = (D3D12Buffer **)SDL_calloc(
3204
        container->bufferCapacity, sizeof(D3D12Buffer *));
3205
    if (!container->buffers) {
3206
        SDL_free(container);
3207
        return NULL;
3208
    }
3209
    container->debugName = NULL;
3210

3211
    buffer = D3D12_INTERNAL_CreateBuffer(
3212
        renderer,
3213
        usageFlags,
3214
        sizeInBytes,
3215
        type);
3216

3217
    if (buffer == NULL) {
3218
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create buffer!");
3219
        SDL_free(container->buffers);
3220
        SDL_free(container);
3221
        return NULL;
3222
    }
3223

3224
    container->activeBuffer = buffer;
3225
    container->buffers[0] = buffer;
3226
    buffer->container = container;
3227
    buffer->containerIndex = 0;
3228

3229
    return container;
3230
}
3231

3232
static SDL_GPUBuffer *D3D12_CreateBuffer(
3233
    SDL_GPURenderer *driverData,
3234
    SDL_GPUBufferUsageFlags usageFlags,
3235
    Uint32 sizeInBytes)
3236
{
3237
    return (SDL_GPUBuffer *)D3D12_INTERNAL_CreateBufferContainer(
3238
        (D3D12Renderer *)driverData,
3239
        usageFlags,
3240
        sizeInBytes,
3241
        D3D12_BUFFER_TYPE_GPU);
3242
}
3243

3244
static SDL_GPUTransferBuffer *D3D12_CreateTransferBuffer(
3245
    SDL_GPURenderer *driverData,
3246
    SDL_GPUTransferBufferUsage usage,
3247
    Uint32 sizeInBytes)
3248
{
3249
    return (SDL_GPUTransferBuffer *)D3D12_INTERNAL_CreateBufferContainer(
3250
        (D3D12Renderer *)driverData,
3251
        0,
3252
        sizeInBytes,
3253
        usage == SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD ? D3D12_BUFFER_TYPE_UPLOAD : D3D12_BUFFER_TYPE_DOWNLOAD);
3254
}
3255

3256
// Debug Naming
3257

3258
static void D3D12_SetBufferName(
3259
    SDL_GPURenderer *driverData,
3260
    SDL_GPUBuffer *buffer,
3261
    const char *text)
3262
{
3263
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
3264
    D3D12BufferContainer *container = (D3D12BufferContainer *)buffer;
3265
    size_t textLength = SDL_strlen(text) + 1;
3266

3267
    if (renderer->debugMode) {
3268
        container->debugName = (char *)SDL_realloc(
3269
            container->debugName,
3270
            textLength);
3271

3272
        SDL_utf8strlcpy(
3273
            container->debugName,
3274
            text,
3275
            textLength);
3276

3277
        for (Uint32 i = 0; i < container->bufferCount; i += 1) {
3278
            D3D12_INTERNAL_SetResourceName(
3279
                renderer,
3280
                container->buffers[i]->handle,
3281
                text);
3282
        }
3283
    }
3284
}
3285

3286
static void D3D12_SetTextureName(
3287
    SDL_GPURenderer *driverData,
3288
    SDL_GPUTexture *texture,
3289
    const char *text)
3290
{
3291
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
3292
    D3D12TextureContainer *container = (D3D12TextureContainer *)texture;
3293
    size_t textLength = SDL_strlen(text) + 1;
3294

3295
    if (renderer->debugMode) {
3296
        container->debugName = (char *)SDL_realloc(
3297
            container->debugName,
3298
            textLength);
3299

3300
        SDL_utf8strlcpy(
3301
            container->debugName,
3302
            text,
3303
            textLength);
3304

3305
        for (Uint32 i = 0; i < container->textureCount; i += 1) {
3306
            D3D12_INTERNAL_SetResourceName(
3307
                renderer,
3308
                container->textures[i]->resource,
3309
                text);
3310
        }
3311
    }
3312
}
3313

3314
/* These debug functions are all marked as "for internal usage only"
3315
 * on D3D12... works on renderdoc!
3316
 */
3317

3318
static bool D3D12_INTERNAL_StrToWStr(
3319
    D3D12Renderer *renderer,
3320
    const char *str,
3321
    wchar_t *wstr,
3322
    size_t wstr_size,
3323
    Uint32 *outSize)
3324
{
3325
    size_t inlen, result;
3326
    size_t outlen = wstr_size;
3327

3328
    if (renderer->iconv == NULL) {
3329
        renderer->iconv = SDL_iconv_open("WCHAR_T", "UTF-8");
3330
        SDL_assert(renderer->iconv);
3331
    }
3332

3333
    // Convert...
3334
    inlen = SDL_strlen(str) + 1;
3335
    result = SDL_iconv(
3336
        renderer->iconv,
3337
        &str,
3338
        &inlen,
3339
        (char **)&wstr,
3340
        &outlen);
3341

3342
    *outSize = (Uint32)outlen;
3343

3344
    // Check...
3345
    switch (result) {
3346
    case SDL_ICONV_ERROR:
3347
    case SDL_ICONV_E2BIG:
3348
    case SDL_ICONV_EILSEQ:
3349
    case SDL_ICONV_EINVAL:
3350
        SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Failed to convert string to wchar_t!");
3351
        return false;
3352
    default:
3353
        break;
3354
    }
3355

3356
    return true;
3357
}
3358

3359
static void D3D12_InsertDebugLabel(
3360
    SDL_GPUCommandBuffer *commandBuffer,
3361
    const char *text)
3362
{
3363
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
3364
    wchar_t wstr[256];
3365
    Uint32 convSize;
3366

3367
    if (!D3D12_INTERNAL_StrToWStr(
3368
            d3d12CommandBuffer->renderer,
3369
            text,
3370
            wstr,
3371
            sizeof(wstr),
3372
            &convSize)) {
3373
        return;
3374
    }
3375

3376
    ID3D12GraphicsCommandList_SetMarker(
3377
        d3d12CommandBuffer->graphicsCommandList,
3378
        0,
3379
        wstr,
3380
        convSize);
3381
}
3382

3383
static void D3D12_PushDebugGroup(
3384
    SDL_GPUCommandBuffer *commandBuffer,
3385
    const char *name)
3386
{
3387
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
3388
    wchar_t wstr[256];
3389
    Uint32 convSize;
3390

3391
    if (!D3D12_INTERNAL_StrToWStr(
3392
            d3d12CommandBuffer->renderer,
3393
            name,
3394
            wstr,
3395
            sizeof(wstr),
3396
            &convSize)) {
3397
        return;
3398
    }
3399

3400
    ID3D12GraphicsCommandList_BeginEvent(
3401
        d3d12CommandBuffer->graphicsCommandList,
3402
        0,
3403
        wstr,
3404
        convSize);
3405
}
3406

3407
static void D3D12_PopDebugGroup(
3408
    SDL_GPUCommandBuffer *commandBuffer)
3409
{
3410
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
3411
    ID3D12GraphicsCommandList_EndEvent(d3d12CommandBuffer->graphicsCommandList);
3412
}
3413

3414
// Disposal
3415

3416
static void D3D12_ReleaseTexture(
3417
    SDL_GPURenderer *driverData,
3418
    SDL_GPUTexture *texture)
3419
{
3420
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
3421
    D3D12TextureContainer *container = (D3D12TextureContainer *)texture;
3422

3423
    D3D12_INTERNAL_ReleaseTextureContainer(
3424
        renderer,
3425
        container);
3426
}
3427

3428
static void D3D12_ReleaseSampler(
3429
    SDL_GPURenderer *driverData,
3430
    SDL_GPUSampler *sampler)
3431
{
3432
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
3433
    D3D12Sampler *d3d12Sampler = (D3D12Sampler *)sampler;
3434

3435
    SDL_LockMutex(renderer->disposeLock);
3436

3437
    EXPAND_ARRAY_IF_NEEDED(
3438
        renderer->samplersToDestroy,
3439
        D3D12Sampler *,
3440
        renderer->samplersToDestroyCount + 1,
3441
        renderer->samplersToDestroyCapacity,
3442
        renderer->samplersToDestroyCapacity * 2)
3443

3444
    renderer->samplersToDestroy[renderer->samplersToDestroyCount] = d3d12Sampler;
3445
    renderer->samplersToDestroyCount += 1;
3446

3447
    SDL_UnlockMutex(renderer->disposeLock);
3448
}
3449

3450
static void D3D12_ReleaseBuffer(
3451
    SDL_GPURenderer *driverData,
3452
    SDL_GPUBuffer *buffer)
3453
{
3454
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
3455
    D3D12BufferContainer *bufferContainer = (D3D12BufferContainer *)buffer;
3456

3457
    D3D12_INTERNAL_ReleaseBufferContainer(
3458
        renderer,
3459
        bufferContainer);
3460
}
3461

3462
static void D3D12_ReleaseTransferBuffer(
3463
    SDL_GPURenderer *driverData,
3464
    SDL_GPUTransferBuffer *transferBuffer)
3465
{
3466
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
3467
    D3D12BufferContainer *transferBufferContainer = (D3D12BufferContainer *)transferBuffer;
3468

3469
    D3D12_INTERNAL_ReleaseBufferContainer(
3470
        renderer,
3471
        transferBufferContainer);
3472
}
3473

3474
static void D3D12_ReleaseShader(
3475
    SDL_GPURenderer *driverData,
3476
    SDL_GPUShader *shader)
3477
{
3478
    /* D3D12Renderer *renderer = (D3D12Renderer *)driverData; */
3479
    D3D12Shader *d3d12shader = (D3D12Shader *)shader;
3480

3481
    if (d3d12shader->bytecode) {
3482
        SDL_free(d3d12shader->bytecode);
3483
        d3d12shader->bytecode = NULL;
3484
    }
3485
    SDL_free(d3d12shader);
3486
}
3487

3488
static void D3D12_ReleaseComputePipeline(
3489
    SDL_GPURenderer *driverData,
3490
    SDL_GPUComputePipeline *computePipeline)
3491
{
3492
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
3493
    D3D12ComputePipeline *d3d12ComputePipeline = (D3D12ComputePipeline *)computePipeline;
3494

3495
    SDL_LockMutex(renderer->disposeLock);
3496

3497
    EXPAND_ARRAY_IF_NEEDED(
3498
        renderer->computePipelinesToDestroy,
3499
        D3D12ComputePipeline *,
3500
        renderer->computePipelinesToDestroyCount + 1,
3501
        renderer->computePipelinesToDestroyCapacity,
3502
        renderer->computePipelinesToDestroyCapacity * 2)
3503

3504
    renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount] = d3d12ComputePipeline;
3505
    renderer->computePipelinesToDestroyCount += 1;
3506

3507
    SDL_UnlockMutex(renderer->disposeLock);
3508
}
3509

3510
static void D3D12_ReleaseGraphicsPipeline(
3511
    SDL_GPURenderer *driverData,
3512
    SDL_GPUGraphicsPipeline *graphicsPipeline)
3513
{
3514
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
3515
    D3D12GraphicsPipeline *d3d12GraphicsPipeline = (D3D12GraphicsPipeline *)graphicsPipeline;
3516

3517
    SDL_LockMutex(renderer->disposeLock);
3518

3519
    EXPAND_ARRAY_IF_NEEDED(
3520
        renderer->graphicsPipelinesToDestroy,
3521
        D3D12GraphicsPipeline *,
3522
        renderer->graphicsPipelinesToDestroyCount + 1,
3523
        renderer->graphicsPipelinesToDestroyCapacity,
3524
        renderer->graphicsPipelinesToDestroyCapacity * 2)
3525

3526
    renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount] = d3d12GraphicsPipeline;
3527
    renderer->graphicsPipelinesToDestroyCount += 1;
3528

3529
    SDL_UnlockMutex(renderer->disposeLock);
3530
}
3531

3532
static void D3D12_INTERNAL_ReleaseBlitPipelines(SDL_GPURenderer *driverData)
3533
{
3534
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
3535
    D3D12_ReleaseSampler(driverData, renderer->blitLinearSampler);
3536
    D3D12_ReleaseSampler(driverData, renderer->blitNearestSampler);
3537
    D3D12_ReleaseShader(driverData, renderer->blitVertexShader);
3538
    D3D12_ReleaseShader(driverData, renderer->blitFrom2DShader);
3539
    D3D12_ReleaseShader(driverData, renderer->blitFrom2DArrayShader);
3540
    D3D12_ReleaseShader(driverData, renderer->blitFrom3DShader);
3541
    D3D12_ReleaseShader(driverData, renderer->blitFromCubeShader);
3542

3543
    for (Uint32 i = 0; i < renderer->blitPipelineCount; i += 1) {
3544
        D3D12_ReleaseGraphicsPipeline(driverData, renderer->blitPipelines[i].pipeline);
3545
    }
3546
    SDL_free(renderer->blitPipelines);
3547
}
3548

3549
// Render Pass
3550

3551
static void D3D12_SetViewport(
3552
    SDL_GPUCommandBuffer *commandBuffer,
3553
    SDL_GPUViewport *viewport)
3554
{
3555
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
3556
    D3D12_VIEWPORT d3d12Viewport;
3557
    d3d12Viewport.TopLeftX = viewport->x;
3558
    d3d12Viewport.TopLeftY = viewport->y;
3559
    d3d12Viewport.Width = viewport->w;
3560
    d3d12Viewport.Height = viewport->h;
3561
    d3d12Viewport.MinDepth = viewport->minDepth;
3562
    d3d12Viewport.MaxDepth = viewport->maxDepth;
3563
    ID3D12GraphicsCommandList_RSSetViewports(d3d12CommandBuffer->graphicsCommandList, 1, &d3d12Viewport);
3564
}
3565

3566
static void D3D12_SetScissor(
3567
    SDL_GPUCommandBuffer *commandBuffer,
3568
    SDL_Rect *scissor)
3569
{
3570
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
3571
    D3D12_RECT scissorRect;
3572
    scissorRect.left = scissor->x;
3573
    scissorRect.top = scissor->y;
3574
    scissorRect.right = scissor->x + scissor->w;
3575
    scissorRect.bottom = scissor->y + scissor->h;
3576
    ID3D12GraphicsCommandList_RSSetScissorRects(d3d12CommandBuffer->graphicsCommandList, 1, &scissorRect);
3577
}
3578

3579
static D3D12TextureSubresource *D3D12_INTERNAL_FetchTextureSubresource(
3580
    D3D12TextureContainer *container,
3581
    Uint32 layer,
3582
    Uint32 level)
3583
{
3584
    Uint32 index = D3D12_INTERNAL_CalcSubresource(
3585
        level,
3586
        layer,
3587
        container->header.info.levelCount);
3588
    return &container->activeTexture->subresources[index];
3589
}
3590

3591
static void D3D12_INTERNAL_CycleActiveTexture(
3592
    D3D12Renderer *renderer,
3593
    D3D12TextureContainer *container)
3594
{
3595
    D3D12Texture *texture;
3596

3597
    // If a previously-cycled texture is available, we can use that.
3598
    for (Uint32 i = 0; i < container->textureCount; i += 1) {
3599
        texture = container->textures[i];
3600

3601
        if (SDL_AtomicGet(&texture->referenceCount) == 0) {
3602
            container->activeTexture = texture;
3603
            return;
3604
        }
3605
    }
3606

3607
    // No texture is available, generate a new one.
3608
    texture = D3D12_INTERNAL_CreateTexture(
3609
        renderer,
3610
        &container->header.info,
3611
        false);
3612

3613
    if (!texture) {
3614
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to cycle active texture!");
3615
        return;
3616
    }
3617

3618
    EXPAND_ARRAY_IF_NEEDED(
3619
        container->textures,
3620
        D3D12Texture *,
3621
        container->textureCount + 1,
3622
        container->textureCapacity,
3623
        container->textureCapacity * 2);
3624

3625
    container->textures[container->textureCount] = texture;
3626
    texture->container = container;
3627
    texture->containerIndex = container->textureCount;
3628
    container->textureCount += 1;
3629

3630
    container->activeTexture = texture;
3631

3632
    if (renderer->debugMode && container->debugName != NULL) {
3633
        D3D12_INTERNAL_SetResourceName(
3634
            renderer,
3635
            container->activeTexture->resource,
3636
            container->debugName);
3637
    }
3638
}
3639

3640
static D3D12TextureSubresource *D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
3641
    D3D12CommandBuffer *commandBuffer,
3642
    D3D12TextureContainer *container,
3643
    Uint32 layer,
3644
    Uint32 level,
3645
    bool cycle,
3646
    D3D12_RESOURCE_STATES destinationUsageMode)
3647
{
3648
    D3D12TextureSubresource *subresource = D3D12_INTERNAL_FetchTextureSubresource(
3649
        container,
3650
        layer,
3651
        level);
3652

3653
    if (
3654
        container->canBeCycled &&
3655
        cycle &&
3656
        SDL_AtomicGet(&subresource->parent->referenceCount) > 0) {
3657
        D3D12_INTERNAL_CycleActiveTexture(
3658
            commandBuffer->renderer,
3659
            container);
3660

3661
        subresource = D3D12_INTERNAL_FetchTextureSubresource(
3662
            container,
3663
            layer,
3664
            level);
3665
    }
3666

3667
    D3D12_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
3668
        commandBuffer,
3669
        destinationUsageMode,
3670
        subresource);
3671

3672
    return subresource;
3673
}
3674

3675
static void D3D12_INTERNAL_CycleActiveBuffer(
3676
    D3D12Renderer *renderer,
3677
    D3D12BufferContainer *container)
3678
{
3679
    // If a previously-cycled buffer is available, we can use that.
3680
    for (Uint32 i = 0; i < container->bufferCount; i += 1) {
3681
        D3D12Buffer *buffer = container->buffers[i];
3682
        if (SDL_AtomicGet(&buffer->referenceCount) == 0) {
3683
            container->activeBuffer = buffer;
3684
            return;
3685
        }
3686
    }
3687

3688
    // No buffer handle is available, create a new one.
3689
    D3D12Buffer *buffer = D3D12_INTERNAL_CreateBuffer(
3690
        renderer,
3691
        container->usageFlags,
3692
        container->size,
3693
        container->type);
3694

3695
    if (!buffer) {
3696
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to cycle active buffer!");
3697
        return;
3698
    }
3699

3700
    EXPAND_ARRAY_IF_NEEDED(
3701
        container->buffers,
3702
        D3D12Buffer *,
3703
        container->bufferCount + 1,
3704
        container->bufferCapacity,
3705
        container->bufferCapacity * 2);
3706

3707
    container->buffers[container->bufferCount] = buffer;
3708
    buffer->container = container;
3709
    buffer->containerIndex = container->bufferCount;
3710
    container->bufferCount += 1;
3711

3712
    container->activeBuffer = buffer;
3713

3714
    if (renderer->debugMode && container->debugName != NULL) {
3715
        D3D12_INTERNAL_SetResourceName(
3716
            renderer,
3717
            container->activeBuffer->handle,
3718
            container->debugName);
3719
    }
3720
}
3721

3722
static D3D12Buffer *D3D12_INTERNAL_PrepareBufferForWrite(
3723
    D3D12CommandBuffer *commandBuffer,
3724
    D3D12BufferContainer *container,
3725
    bool cycle,
3726
    D3D12_RESOURCE_STATES destinationState)
3727
{
3728
    if (
3729
        cycle &&
3730
        SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0) {
3731
        D3D12_INTERNAL_CycleActiveBuffer(
3732
            commandBuffer->renderer,
3733
            container);
3734
    }
3735

3736
    D3D12_INTERNAL_BufferTransitionFromDefaultUsage(
3737
        commandBuffer,
3738
        destinationState,
3739
        container->activeBuffer);
3740

3741
    return container->activeBuffer;
3742
}
3743

3744
static void D3D12_BeginRenderPass(
3745
    SDL_GPUCommandBuffer *commandBuffer,
3746
    SDL_GPUColorAttachmentInfo *colorAttachmentInfos,
3747
    Uint32 colorAttachmentCount,
3748
    SDL_GPUDepthStencilAttachmentInfo *depthStencilAttachmentInfo)
3749
{
3750
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
3751
    /* D3D12Renderer *renderer = d3d12CommandBuffer->renderer; */
3752

3753
    Uint32 framebufferWidth = SDL_MAX_UINT32;
3754
    Uint32 framebufferHeight = SDL_MAX_UINT32;
3755

3756
    for (Uint32 i = 0; i < colorAttachmentCount; i += 1) {
3757
        D3D12TextureContainer *container = (D3D12TextureContainer *)colorAttachmentInfos[i].texture;
3758
        Uint32 h = container->header.info.height >> colorAttachmentInfos[i].mipLevel;
3759
        Uint32 w = container->header.info.width >> colorAttachmentInfos[i].mipLevel;
3760

3761
        // The framebuffer cannot be larger than the smallest attachment.
3762

3763
        if (w < framebufferWidth) {
3764
            framebufferWidth = w;
3765
        }
3766

3767
        if (h < framebufferHeight) {
3768
            framebufferHeight = h;
3769
        }
3770

3771
        if (!(container->header.info.usageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET)) {
3772
            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Color attachment texture was not designated as a color target!");
3773
            return;
3774
        }
3775
    }
3776

3777
    if (depthStencilAttachmentInfo != NULL) {
3778
        D3D12TextureContainer *container = (D3D12TextureContainer *)depthStencilAttachmentInfo->texture;
3779

3780
        Uint32 h = container->header.info.height;
3781
        Uint32 w = container->header.info.width;
3782

3783
        // The framebuffer cannot be larger than the smallest attachment.
3784

3785
        if (w < framebufferWidth) {
3786
            framebufferWidth = w;
3787
        }
3788

3789
        if (h < framebufferHeight) {
3790
            framebufferHeight = h;
3791
        }
3792

3793
        // Fixme:
3794
        if (!(container->header.info.usageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) {
3795
            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Depth stencil attachment texture was not designated as a depth target!");
3796
            return;
3797
        }
3798
    }
3799

3800
    D3D12_CPU_DESCRIPTOR_HANDLE rtvs[MAX_COLOR_TARGET_BINDINGS];
3801

3802
    for (Uint32 i = 0; i < colorAttachmentCount; i += 1) {
3803
        D3D12TextureContainer *container = (D3D12TextureContainer *)colorAttachmentInfos[i].texture;
3804
        D3D12TextureSubresource *subresource = D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
3805
            d3d12CommandBuffer,
3806
            container,
3807
            container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : colorAttachmentInfos[i].layerOrDepthPlane,
3808
            colorAttachmentInfos[i].mipLevel,
3809
            colorAttachmentInfos[i].cycle,
3810
            D3D12_RESOURCE_STATE_RENDER_TARGET);
3811

3812
        Uint32 rtvIndex = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? colorAttachmentInfos[i].layerOrDepthPlane : 0;
3813
        D3D12_CPU_DESCRIPTOR_HANDLE rtv =
3814
            subresource->rtvHandles[rtvIndex].cpuHandle;
3815

3816
        if (colorAttachmentInfos[i].loadOp == SDL_GPU_LOADOP_CLEAR) {
3817
            float clearColor[4];
3818
            clearColor[0] = colorAttachmentInfos[i].clearColor.r;
3819
            clearColor[1] = colorAttachmentInfos[i].clearColor.g;
3820
            clearColor[2] = colorAttachmentInfos[i].clearColor.b;
3821
            clearColor[3] = colorAttachmentInfos[i].clearColor.a;
3822

3823
            ID3D12GraphicsCommandList_ClearRenderTargetView(
3824
                d3d12CommandBuffer->graphicsCommandList,
3825
                rtv,
3826
                clearColor,
3827
                0,
3828
                NULL);
3829
        }
3830

3831
        rtvs[i] = rtv;
3832
        d3d12CommandBuffer->colorAttachmentTextureSubresources[i] = subresource;
3833

3834
        D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, subresource->parent);
3835
    }
3836

3837
    d3d12CommandBuffer->colorAttachmentTextureSubresourceCount = colorAttachmentCount;
3838

3839
    D3D12_CPU_DESCRIPTOR_HANDLE dsv;
3840
    if (depthStencilAttachmentInfo != NULL) {
3841
        D3D12TextureContainer *container = (D3D12TextureContainer *)depthStencilAttachmentInfo->texture;
3842
        D3D12TextureSubresource *subresource = D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
3843
            d3d12CommandBuffer,
3844
            container,
3845
            0,
3846
            0,
3847
            depthStencilAttachmentInfo->cycle,
3848
            D3D12_RESOURCE_STATE_DEPTH_WRITE);
3849

3850
        if (
3851
            depthStencilAttachmentInfo->loadOp == SDL_GPU_LOADOP_CLEAR ||
3852
            depthStencilAttachmentInfo->stencilLoadOp == SDL_GPU_LOADOP_CLEAR) {
3853
            D3D12_CLEAR_FLAGS clearFlags = (D3D12_CLEAR_FLAGS)0;
3854
            if (depthStencilAttachmentInfo->loadOp == SDL_GPU_LOADOP_CLEAR) {
3855
                clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
3856
            }
3857
            if (depthStencilAttachmentInfo->stencilLoadOp == SDL_GPU_LOADOP_CLEAR) {
3858
                clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
3859
            }
3860

3861
            ID3D12GraphicsCommandList_ClearDepthStencilView(
3862
                d3d12CommandBuffer->graphicsCommandList,
3863
                subresource->dsvHandle.cpuHandle,
3864
                clearFlags,
3865
                depthStencilAttachmentInfo->depthStencilClearValue.depth,
3866
                depthStencilAttachmentInfo->depthStencilClearValue.stencil,
3867
                0,
3868
                NULL);
3869
        }
3870

3871
        dsv = subresource->dsvHandle.cpuHandle;
3872
        d3d12CommandBuffer->depthStencilTextureSubresource = subresource;
3873
        D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, subresource->parent);
3874
    }
3875

3876
    ID3D12GraphicsCommandList_OMSetRenderTargets(
3877
        d3d12CommandBuffer->graphicsCommandList,
3878
        colorAttachmentCount,
3879
        rtvs,
3880
        false,
3881
        (depthStencilAttachmentInfo == NULL) ? NULL : &dsv);
3882

3883
    // Set sensible default viewport state
3884
    SDL_GPUViewport defaultViewport;
3885
    defaultViewport.x = 0;
3886
    defaultViewport.y = 0;
3887
    defaultViewport.w = (float)framebufferWidth;
3888
    defaultViewport.h = (float)framebufferHeight;
3889
    defaultViewport.minDepth = 0;
3890
    defaultViewport.maxDepth = 1;
3891

3892
    D3D12_SetViewport(
3893
        commandBuffer,
3894
        &defaultViewport);
3895

3896
    SDL_Rect defaultScissor;
3897
    defaultScissor.x = 0;
3898
    defaultScissor.y = 0;
3899
    defaultScissor.w = (Sint32)framebufferWidth;
3900
    defaultScissor.h = (Sint32)framebufferHeight;
3901

3902
    D3D12_SetScissor(
3903
        commandBuffer,
3904
        &defaultScissor);
3905
}
3906

3907
static void D3D12_INTERNAL_TrackUniformBuffer(
3908
    D3D12CommandBuffer *commandBuffer,
3909
    D3D12UniformBuffer *uniformBuffer)
3910
{
3911
    Uint32 i;
3912
    for (i = 0; i < commandBuffer->usedUniformBufferCount; i += 1) {
3913
        if (commandBuffer->usedUniformBuffers[i] == uniformBuffer) {
3914
            return;
3915
        }
3916
    }
3917

3918
    if (commandBuffer->usedUniformBufferCount == commandBuffer->usedUniformBufferCapacity) {
3919
        commandBuffer->usedUniformBufferCapacity += 1;
3920
        commandBuffer->usedUniformBuffers = (D3D12UniformBuffer **)SDL_realloc(
3921
            commandBuffer->usedUniformBuffers,
3922
            commandBuffer->usedUniformBufferCapacity * sizeof(D3D12UniformBuffer *));
3923
    }
3924

3925
    commandBuffer->usedUniformBuffers[commandBuffer->usedUniformBufferCount] = uniformBuffer;
3926
    commandBuffer->usedUniformBufferCount += 1;
3927

3928
    D3D12_INTERNAL_TrackBuffer(
3929
        commandBuffer,
3930
        uniformBuffer->buffer);
3931
}
3932

3933
static D3D12UniformBuffer *D3D12_INTERNAL_AcquireUniformBufferFromPool(
3934
    D3D12CommandBuffer *commandBuffer)
3935
{
3936
    D3D12Renderer *renderer = commandBuffer->renderer;
3937
    D3D12UniformBuffer *uniformBuffer;
3938

3939
    SDL_LockMutex(renderer->acquireUniformBufferLock);
3940

3941
    if (renderer->uniformBufferPoolCount > 0) {
3942
        uniformBuffer = renderer->uniformBufferPool[renderer->uniformBufferPoolCount - 1];
3943
        renderer->uniformBufferPoolCount -= 1;
3944
    } else {
3945
        uniformBuffer = (D3D12UniformBuffer *)SDL_calloc(1, sizeof(D3D12UniformBuffer));
3946
        if (!uniformBuffer) {
3947
            SDL_UnlockMutex(renderer->acquireUniformBufferLock);
3948
            return NULL;
3949
        }
3950

3951
        uniformBuffer->buffer = D3D12_INTERNAL_CreateBuffer(
3952
            renderer,
3953
            0,
3954
            UNIFORM_BUFFER_SIZE,
3955
            D3D12_BUFFER_TYPE_UNIFORM);
3956
        if (!uniformBuffer->buffer) {
3957
            SDL_UnlockMutex(renderer->acquireUniformBufferLock);
3958
            return NULL;
3959
        }
3960
    }
3961

3962
    SDL_UnlockMutex(renderer->acquireUniformBufferLock);
3963

3964
    uniformBuffer->currentBlockSize = 0;
3965
    uniformBuffer->drawOffset = 0;
3966
    uniformBuffer->writeOffset = 0;
3967

3968
    HRESULT res = ID3D12Resource_Map(
3969
        uniformBuffer->buffer->handle,
3970
        0,
3971
        NULL,
3972
        (void **)&uniformBuffer->buffer->mapPointer);
3973
    ERROR_CHECK_RETURN("Failed to map buffer pool!", NULL);
3974

3975
    D3D12_INTERNAL_TrackUniformBuffer(commandBuffer, uniformBuffer);
3976

3977
    return uniformBuffer;
3978
}
3979

3980
static void D3D12_INTERNAL_ReturnUniformBufferToPool(
3981
    D3D12Renderer *renderer,
3982
    D3D12UniformBuffer *uniformBuffer)
3983
{
3984
    if (renderer->uniformBufferPoolCount >= renderer->uniformBufferPoolCapacity) {
3985
        renderer->uniformBufferPoolCapacity *= 2;
3986
        renderer->uniformBufferPool = (D3D12UniformBuffer **)SDL_realloc(
3987
            renderer->uniformBufferPool,
3988
            renderer->uniformBufferPoolCapacity * sizeof(D3D12UniformBuffer *));
3989
    }
3990

3991
    renderer->uniformBufferPool[renderer->uniformBufferPoolCount] = uniformBuffer;
3992
    renderer->uniformBufferPoolCount += 1;
3993
}
3994

3995
static void D3D12_INTERNAL_PushUniformData(
3996
    D3D12CommandBuffer *commandBuffer,
3997
    SDL_GPUShaderStage shaderStage,
3998
    Uint32 slotIndex,
3999
    const void *data,
4000
    Uint32 dataLengthInBytes)
4001
{
4002
    D3D12UniformBuffer *uniformBuffer;
4003

4004
    if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) {
4005
        if (commandBuffer->vertexUniformBuffers[slotIndex] == NULL) {
4006
            commandBuffer->vertexUniformBuffers[slotIndex] = D3D12_INTERNAL_AcquireUniformBufferFromPool(
4007
                commandBuffer);
4008
        }
4009
        uniformBuffer = commandBuffer->vertexUniformBuffers[slotIndex];
4010
    } else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) {
4011
        if (commandBuffer->fragmentUniformBuffers[slotIndex] == NULL) {
4012
            commandBuffer->fragmentUniformBuffers[slotIndex] = D3D12_INTERNAL_AcquireUniformBufferFromPool(
4013
                commandBuffer);
4014
        }
4015
        uniformBuffer = commandBuffer->fragmentUniformBuffers[slotIndex];
4016
    } else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) {
4017
        if (commandBuffer->computeUniformBuffers[slotIndex] == NULL) {
4018
            commandBuffer->computeUniformBuffers[slotIndex] = D3D12_INTERNAL_AcquireUniformBufferFromPool(
4019
                commandBuffer);
4020
        }
4021
        uniformBuffer = commandBuffer->computeUniformBuffers[slotIndex];
4022
    } else {
4023
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
4024
        return;
4025
    }
4026

4027
    uniformBuffer->currentBlockSize =
4028
        D3D12_INTERNAL_Align(
4029
            dataLengthInBytes,
4030
            256);
4031

4032
    // If there is no more room, acquire a new uniform buffer
4033
    if (uniformBuffer->writeOffset + uniformBuffer->currentBlockSize >= UNIFORM_BUFFER_SIZE) {
4034
        ID3D12Resource_Unmap(
4035
            uniformBuffer->buffer->handle,
4036
            0,
4037
            NULL);
4038
        uniformBuffer->buffer->mapPointer = NULL;
4039

4040
        uniformBuffer = D3D12_INTERNAL_AcquireUniformBufferFromPool(commandBuffer);
4041

4042
        uniformBuffer->drawOffset = 0;
4043
        uniformBuffer->writeOffset = 0;
4044

4045
        if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) {
4046
            commandBuffer->vertexUniformBuffers[slotIndex] = uniformBuffer;
4047
        } else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) {
4048
            commandBuffer->fragmentUniformBuffers[slotIndex] = uniformBuffer;
4049
        } else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) {
4050
            commandBuffer->computeUniformBuffers[slotIndex] = uniformBuffer;
4051
        } else {
4052
            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
4053
        }
4054
    }
4055

4056
    uniformBuffer->drawOffset = uniformBuffer->writeOffset;
4057

4058
    SDL_memcpy(
4059
        (Uint8 *)uniformBuffer->buffer->mapPointer + uniformBuffer->writeOffset,
4060
        data,
4061
        dataLengthInBytes);
4062

4063
    uniformBuffer->writeOffset += uniformBuffer->currentBlockSize;
4064

4065
    if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) {
4066
        commandBuffer->needVertexUniformBufferBind[slotIndex] = true;
4067
    } else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) {
4068
        commandBuffer->needFragmentUniformBufferBind[slotIndex] = true;
4069
    } else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) {
4070
        commandBuffer->needComputeUniformBufferBind[slotIndex] = true;
4071
    } else {
4072
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
4073
    }
4074
}
4075

4076
static void D3D12_BindGraphicsPipeline(
4077
    SDL_GPUCommandBuffer *commandBuffer,
4078
    SDL_GPUGraphicsPipeline *graphicsPipeline)
4079
{
4080
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4081
    D3D12GraphicsPipeline *pipeline = (D3D12GraphicsPipeline *)graphicsPipeline;
4082
    Uint32 i;
4083

4084
    d3d12CommandBuffer->currentGraphicsPipeline = pipeline;
4085

4086
    // Set the pipeline state
4087
    ID3D12GraphicsCommandList_SetPipelineState(d3d12CommandBuffer->graphicsCommandList, pipeline->pipelineState);
4088

4089
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(d3d12CommandBuffer->graphicsCommandList, pipeline->rootSignature->handle);
4090

4091
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(d3d12CommandBuffer->graphicsCommandList, SDLToD3D12_PrimitiveType[pipeline->primitiveType]);
4092

4093
    float blendFactor[4] = {
4094
        pipeline->blendConstants[0],
4095
        pipeline->blendConstants[1],
4096
        pipeline->blendConstants[2],
4097
        pipeline->blendConstants[3]
4098
    };
4099
    ID3D12GraphicsCommandList_OMSetBlendFactor(d3d12CommandBuffer->graphicsCommandList, blendFactor);
4100

4101
    ID3D12GraphicsCommandList_OMSetStencilRef(d3d12CommandBuffer->graphicsCommandList, pipeline->stencilRef);
4102

4103
    // Mark that bindings are needed
4104
    d3d12CommandBuffer->needVertexSamplerBind = true;
4105
    d3d12CommandBuffer->needVertexStorageTextureBind = true;
4106
    d3d12CommandBuffer->needVertexStorageBufferBind = true;
4107
    d3d12CommandBuffer->needFragmentSamplerBind = true;
4108
    d3d12CommandBuffer->needFragmentStorageTextureBind = true;
4109
    d3d12CommandBuffer->needFragmentStorageBufferBind = true;
4110

4111
    for (i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
4112
        d3d12CommandBuffer->needVertexUniformBufferBind[i] = true;
4113
        d3d12CommandBuffer->needFragmentUniformBufferBind[i] = true;
4114
    }
4115

4116
    for (i = 0; i < pipeline->vertexUniformBufferCount; i += 1) {
4117
        if (d3d12CommandBuffer->vertexUniformBuffers[i] == NULL) {
4118
            d3d12CommandBuffer->vertexUniformBuffers[i] = D3D12_INTERNAL_AcquireUniformBufferFromPool(
4119
                d3d12CommandBuffer);
4120
        }
4121
    }
4122

4123
    for (i = 0; i < pipeline->fragmentUniformBufferCount; i += 1) {
4124
        if (d3d12CommandBuffer->fragmentUniformBuffers[i] == NULL) {
4125
            d3d12CommandBuffer->fragmentUniformBuffers[i] = D3D12_INTERNAL_AcquireUniformBufferFromPool(
4126
                d3d12CommandBuffer);
4127
        }
4128
    }
4129

4130
    D3D12_INTERNAL_TrackGraphicsPipeline(d3d12CommandBuffer, pipeline);
4131
}
4132

4133
static void D3D12_BindVertexBuffers(
4134
    SDL_GPUCommandBuffer *commandBuffer,
4135
    Uint32 firstBinding,
4136
    SDL_GPUBufferBinding *pBindings,
4137
    Uint32 bindingCount)
4138
{
4139
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4140

4141
    for (Uint32 i = 0; i < bindingCount; i += 1) {
4142
        D3D12Buffer *currentBuffer = ((D3D12BufferContainer *)pBindings[i].buffer)->activeBuffer;
4143
        d3d12CommandBuffer->vertexBuffers[firstBinding + i] = currentBuffer;
4144
        d3d12CommandBuffer->vertexBufferOffsets[firstBinding + i] = pBindings[i].offset;
4145
        D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, currentBuffer);
4146
    }
4147

4148
    d3d12CommandBuffer->vertexBufferCount =
4149
        SDL_max(d3d12CommandBuffer->vertexBufferCount, firstBinding + bindingCount);
4150

4151
    d3d12CommandBuffer->needVertexBufferBind = true;
4152
}
4153

4154
static void D3D12_BindIndexBuffer(
4155
    SDL_GPUCommandBuffer *commandBuffer,
4156
    SDL_GPUBufferBinding *pBinding,
4157
    SDL_GPUIndexElementSize indexElementSize)
4158
{
4159
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4160
    D3D12Buffer *buffer = ((D3D12BufferContainer *)pBinding->buffer)->activeBuffer;
4161
    D3D12_INDEX_BUFFER_VIEW view;
4162

4163
    D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, buffer);
4164

4165
    view.BufferLocation = buffer->virtualAddress + pBinding->offset;
4166
    view.SizeInBytes = buffer->container->size - pBinding->offset;
4167
    view.Format =
4168
        indexElementSize == SDL_GPU_INDEXELEMENTSIZE_16BIT ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
4169

4170
    ID3D12GraphicsCommandList_IASetIndexBuffer(
4171
        d3d12CommandBuffer->graphicsCommandList,
4172
        &view);
4173
}
4174

4175
static void D3D12_BindVertexSamplers(
4176
    SDL_GPUCommandBuffer *commandBuffer,
4177
    Uint32 firstSlot,
4178
    SDL_GPUTextureSamplerBinding *textureSamplerBindings,
4179
    Uint32 bindingCount)
4180
{
4181
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4182

4183
    for (Uint32 i = 0; i < bindingCount; i += 1) {
4184
        D3D12TextureContainer *container = (D3D12TextureContainer *)textureSamplerBindings[i].texture;
4185
        D3D12Sampler *sampler = (D3D12Sampler *)textureSamplerBindings[i].sampler;
4186

4187
        D3D12_INTERNAL_TrackTexture(
4188
            d3d12CommandBuffer,
4189
            container->activeTexture);
4190

4191
        D3D12_INTERNAL_TrackSampler(
4192
            d3d12CommandBuffer,
4193
            sampler);
4194

4195
        d3d12CommandBuffer->vertexSamplers[firstSlot + i] = sampler;
4196
        d3d12CommandBuffer->vertexSamplerTextures[firstSlot + i] = container->activeTexture;
4197
    }
4198

4199
    d3d12CommandBuffer->needVertexSamplerBind = true;
4200
}
4201

4202
static void D3D12_BindVertexStorageTextures(
4203
    SDL_GPUCommandBuffer *commandBuffer,
4204
    Uint32 firstSlot,
4205
    SDL_GPUTexture **storageTextures,
4206
    Uint32 bindingCount)
4207
{
4208
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4209

4210
    for (Uint32 i = 0; i < bindingCount; i += 1) {
4211
        D3D12TextureContainer *container = (D3D12TextureContainer *)storageTextures[i];
4212
        D3D12Texture *texture = container->activeTexture;
4213

4214
        D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, texture);
4215

4216
        d3d12CommandBuffer->vertexStorageTextures[firstSlot + i] = texture;
4217
    }
4218

4219
    d3d12CommandBuffer->needVertexStorageTextureBind = true;
4220
}
4221

4222
static void D3D12_BindVertexStorageBuffers(
4223
    SDL_GPUCommandBuffer *commandBuffer,
4224
    Uint32 firstSlot,
4225
    SDL_GPUBuffer **storageBuffers,
4226
    Uint32 bindingCount)
4227
{
4228
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4229

4230
    for (Uint32 i = 0; i < bindingCount; i += 1) {
4231
        D3D12BufferContainer *container = (D3D12BufferContainer *)storageBuffers[i];
4232

4233
        D3D12_INTERNAL_TrackBuffer(
4234
            d3d12CommandBuffer,
4235
            container->activeBuffer);
4236

4237
        d3d12CommandBuffer->vertexStorageBuffers[firstSlot + i] = container->activeBuffer;
4238
    }
4239

4240
    d3d12CommandBuffer->needVertexStorageBufferBind = true;
4241
}
4242

4243
static void D3D12_BindFragmentSamplers(
4244
    SDL_GPUCommandBuffer *commandBuffer,
4245
    Uint32 firstSlot,
4246
    SDL_GPUTextureSamplerBinding *textureSamplerBindings,
4247
    Uint32 bindingCount)
4248
{
4249
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4250

4251
    for (Uint32 i = 0; i < bindingCount; i += 1) {
4252
        D3D12TextureContainer *container = (D3D12TextureContainer *)textureSamplerBindings[i].texture;
4253
        D3D12Sampler *sampler = (D3D12Sampler *)textureSamplerBindings[i].sampler;
4254

4255
        D3D12_INTERNAL_TrackTexture(
4256
            d3d12CommandBuffer,
4257
            container->activeTexture);
4258

4259
        D3D12_INTERNAL_TrackSampler(
4260
            d3d12CommandBuffer,
4261
            sampler);
4262

4263
        d3d12CommandBuffer->fragmentSamplers[firstSlot + i] = sampler;
4264
        d3d12CommandBuffer->fragmentSamplerTextures[firstSlot + i] = container->activeTexture;
4265
    }
4266

4267
    d3d12CommandBuffer->needFragmentSamplerBind = true;
4268
}
4269

4270
static void D3D12_BindFragmentStorageTextures(
4271
    SDL_GPUCommandBuffer *commandBuffer,
4272
    Uint32 firstSlot,
4273
    SDL_GPUTexture **storageTextures,
4274
    Uint32 bindingCount)
4275
{
4276
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4277

4278
    for (Uint32 i = 0; i < bindingCount; i += 1) {
4279
        D3D12TextureContainer *container = (D3D12TextureContainer *)storageTextures[i];
4280
        D3D12Texture *texture = container->activeTexture;
4281

4282
        D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, texture);
4283

4284
        d3d12CommandBuffer->fragmentStorageTextures[firstSlot + i] = texture;
4285
    }
4286

4287
    d3d12CommandBuffer->needFragmentStorageTextureBind = true;
4288
}
4289

4290
static void D3D12_BindFragmentStorageBuffers(
4291
    SDL_GPUCommandBuffer *commandBuffer,
4292
    Uint32 firstSlot,
4293
    SDL_GPUBuffer **storageBuffers,
4294
    Uint32 bindingCount)
4295
{
4296
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4297

4298
    for (Uint32 i = 0; i < bindingCount; i += 1) {
4299
        D3D12BufferContainer *container = (D3D12BufferContainer *)storageBuffers[i];
4300

4301
        D3D12_INTERNAL_TrackBuffer(
4302
            d3d12CommandBuffer,
4303
            container->activeBuffer);
4304

4305
        d3d12CommandBuffer->fragmentStorageBuffers[firstSlot + i] = container->activeBuffer;
4306
    }
4307

4308
    d3d12CommandBuffer->needFragmentStorageBufferBind = true;
4309
}
4310

4311
static void D3D12_PushVertexUniformData(
4312
    SDL_GPUCommandBuffer *commandBuffer,
4313
    Uint32 slotIndex,
4314
    const void *data,
4315
    Uint32 dataLengthInBytes)
4316
{
4317
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4318

4319
    D3D12_INTERNAL_PushUniformData(
4320
        d3d12CommandBuffer,
4321
        SDL_GPU_SHADERSTAGE_VERTEX,
4322
        slotIndex,
4323
        data,
4324
        dataLengthInBytes);
4325
}
4326

4327
static void D3D12_PushFragmentUniformData(
4328
    SDL_GPUCommandBuffer *commandBuffer,
4329
    Uint32 slotIndex,
4330
    const void *data,
4331
    Uint32 dataLengthInBytes)
4332
{
4333
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4334

4335
    D3D12_INTERNAL_PushUniformData(
4336
        d3d12CommandBuffer,
4337
        SDL_GPU_SHADERSTAGE_FRAGMENT,
4338
        slotIndex,
4339
        data,
4340
        dataLengthInBytes);
4341
}
4342

4343
static void D3D12_INTERNAL_WriteGPUDescriptors(
4344
    D3D12CommandBuffer *commandBuffer,
4345
    D3D12_DESCRIPTOR_HEAP_TYPE heapType,
4346
    D3D12_CPU_DESCRIPTOR_HANDLE *resourceDescriptorHandles,
4347
    Uint32 resourceHandleCount,
4348
    D3D12_GPU_DESCRIPTOR_HANDLE *gpuBaseDescriptor)
4349
{
4350
    D3D12DescriptorHeap *heap = commandBuffer->gpuDescriptorHeaps[heapType];
4351
    D3D12_CPU_DESCRIPTOR_HANDLE gpuHeapCpuHandle;
4352

4353
    // FIXME: need to error on overflow
4354
    gpuHeapCpuHandle.ptr = heap->descriptorHeapCPUStart.ptr + (heap->currentDescriptorIndex * heap->descriptorSize);
4355
    gpuBaseDescriptor->ptr = heap->descriptorHeapGPUStart.ptr + (heap->currentDescriptorIndex * heap->descriptorSize);
4356

4357
    for (Uint32 i = 0; i < resourceHandleCount; i += 1) {
4358
        ID3D12Device_CopyDescriptorsSimple(
4359
            commandBuffer->renderer->device,
4360
            1,
4361
            gpuHeapCpuHandle,
4362
            resourceDescriptorHandles[i],
4363
            heapType);
4364

4365
        heap->currentDescriptorIndex += 1;
4366
        gpuHeapCpuHandle.ptr += heap->descriptorSize;
4367
    }
4368
}
4369

4370
static void D3D12_INTERNAL_BindGraphicsResources(
4371
    D3D12CommandBuffer *commandBuffer)
4372
{
4373
    D3D12GraphicsPipeline *graphicsPipeline = commandBuffer->currentGraphicsPipeline;
4374

4375
    D3D12_CPU_DESCRIPTOR_HANDLE cpuHandles[MAX_TEXTURE_SAMPLERS_PER_STAGE];
4376
    D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorHandle;
4377
    D3D12_VERTEX_BUFFER_VIEW vertexBufferViews[MAX_BUFFER_BINDINGS];
4378

4379
    if (commandBuffer->needVertexBufferBind) {
4380
        for (Uint32 i = 0; i < commandBuffer->vertexBufferCount; i += 1) {
4381
            vertexBufferViews[i].BufferLocation = commandBuffer->vertexBuffers[i]->virtualAddress + commandBuffer->vertexBufferOffsets[i];
4382
            vertexBufferViews[i].SizeInBytes = commandBuffer->vertexBuffers[i]->container->size - commandBuffer->vertexBufferOffsets[i];
4383
            vertexBufferViews[i].StrideInBytes = graphicsPipeline->vertexStrides[i];
4384
        }
4385

4386
        ID3D12GraphicsCommandList_IASetVertexBuffers(
4387
            commandBuffer->graphicsCommandList,
4388
            0,
4389
            commandBuffer->vertexBufferCount,
4390
            vertexBufferViews);
4391
    }
4392

4393
    if (commandBuffer->needVertexSamplerBind) {
4394
        if (graphicsPipeline->vertexSamplerCount > 0) {
4395
            for (Uint32 i = 0; i < graphicsPipeline->vertexSamplerCount; i += 1) {
4396
                cpuHandles[i] = commandBuffer->vertexSamplers[i]->handle.cpuHandle;
4397
            }
4398

4399
            D3D12_INTERNAL_WriteGPUDescriptors(
4400
                commandBuffer,
4401
                D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
4402
                cpuHandles,
4403
                graphicsPipeline->vertexSamplerCount,
4404
                &gpuDescriptorHandle);
4405

4406
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
4407
                commandBuffer->graphicsCommandList,
4408
                graphicsPipeline->rootSignature->vertexSamplerRootIndex,
4409
                gpuDescriptorHandle);
4410

4411
            for (Uint32 i = 0; i < graphicsPipeline->vertexSamplerCount; i += 1) {
4412
                cpuHandles[i] = commandBuffer->vertexSamplerTextures[i]->srvHandle.cpuHandle;
4413
            }
4414

4415
            D3D12_INTERNAL_WriteGPUDescriptors(
4416
                commandBuffer,
4417
                D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
4418
                cpuHandles,
4419
                graphicsPipeline->vertexSamplerCount,
4420
                &gpuDescriptorHandle);
4421

4422
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
4423
                commandBuffer->graphicsCommandList,
4424
                graphicsPipeline->rootSignature->vertexSamplerTextureRootIndex,
4425
                gpuDescriptorHandle);
4426
        }
4427
        commandBuffer->needVertexSamplerBind = false;
4428
    }
4429

4430
    if (commandBuffer->needVertexStorageTextureBind) {
4431
        if (graphicsPipeline->vertexStorageTextureCount > 0) {
4432
            for (Uint32 i = 0; i < graphicsPipeline->vertexStorageTextureCount; i += 1) {
4433
                cpuHandles[i] = commandBuffer->vertexStorageTextures[i]->srvHandle.cpuHandle;
4434
            }
4435

4436
            D3D12_INTERNAL_WriteGPUDescriptors(
4437
                commandBuffer,
4438
                D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
4439
                cpuHandles,
4440
                graphicsPipeline->vertexStorageTextureCount,
4441
                &gpuDescriptorHandle);
4442

4443
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
4444
                commandBuffer->graphicsCommandList,
4445
                graphicsPipeline->rootSignature->vertexStorageTextureRootIndex,
4446
                gpuDescriptorHandle);
4447
        }
4448
        commandBuffer->needVertexStorageTextureBind = false;
4449
    }
4450

4451
    if (commandBuffer->needVertexStorageBufferBind) {
4452
        if (graphicsPipeline->vertexStorageBufferCount > 0) {
4453
            for (Uint32 i = 0; i < graphicsPipeline->vertexStorageBufferCount; i += 1) {
4454
                cpuHandles[i] = commandBuffer->vertexStorageBuffers[i]->srvDescriptor.cpuHandle;
4455
            }
4456

4457
            D3D12_INTERNAL_WriteGPUDescriptors(
4458
                commandBuffer,
4459
                D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
4460
                cpuHandles,
4461
                graphicsPipeline->vertexStorageBufferCount,
4462
                &gpuDescriptorHandle);
4463

4464
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
4465
                commandBuffer->graphicsCommandList,
4466
                graphicsPipeline->rootSignature->vertexStorageBufferRootIndex,
4467
                gpuDescriptorHandle);
4468
        }
4469
        commandBuffer->needVertexStorageBufferBind = false;
4470
    }
4471

4472
    for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
4473
        if (commandBuffer->needVertexUniformBufferBind[i]) {
4474
            if (graphicsPipeline->vertexUniformBufferCount > i) {
4475
                ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(
4476
                    commandBuffer->graphicsCommandList,
4477
                    graphicsPipeline->rootSignature->vertexUniformBufferRootIndex[i],
4478
                    commandBuffer->vertexUniformBuffers[i]->buffer->virtualAddress + commandBuffer->vertexUniformBuffers[i]->drawOffset);
4479
            }
4480
            commandBuffer->needVertexUniformBufferBind[i] = false;
4481
        }
4482
    }
4483

4484
    if (commandBuffer->needFragmentSamplerBind) {
4485
        if (graphicsPipeline->fragmentSamplerCount > 0) {
4486
            for (Uint32 i = 0; i < graphicsPipeline->fragmentSamplerCount; i += 1) {
4487
                cpuHandles[i] = commandBuffer->fragmentSamplers[i]->handle.cpuHandle;
4488
            }
4489

4490
            D3D12_INTERNAL_WriteGPUDescriptors(
4491
                commandBuffer,
4492
                D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
4493
                cpuHandles,
4494
                graphicsPipeline->fragmentSamplerCount,
4495
                &gpuDescriptorHandle);
4496

4497
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
4498
                commandBuffer->graphicsCommandList,
4499
                graphicsPipeline->rootSignature->fragmentSamplerRootIndex,
4500
                gpuDescriptorHandle);
4501

4502
            for (Uint32 i = 0; i < graphicsPipeline->fragmentSamplerCount; i += 1) {
4503
                cpuHandles[i] = commandBuffer->fragmentSamplerTextures[i]->srvHandle.cpuHandle;
4504
            }
4505

4506
            D3D12_INTERNAL_WriteGPUDescriptors(
4507
                commandBuffer,
4508
                D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
4509
                cpuHandles,
4510
                graphicsPipeline->fragmentSamplerCount,
4511
                &gpuDescriptorHandle);
4512

4513
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
4514
                commandBuffer->graphicsCommandList,
4515
                graphicsPipeline->rootSignature->fragmentSamplerTextureRootIndex,
4516
                gpuDescriptorHandle);
4517
        }
4518
        commandBuffer->needFragmentSamplerBind = false;
4519
    }
4520

4521
    if (commandBuffer->needFragmentStorageTextureBind) {
4522
        if (graphicsPipeline->fragmentStorageTextureCount > 0) {
4523
            for (Uint32 i = 0; i < graphicsPipeline->fragmentStorageTextureCount; i += 1) {
4524
                cpuHandles[i] = commandBuffer->fragmentStorageTextures[i]->srvHandle.cpuHandle;
4525
            }
4526

4527
            D3D12_INTERNAL_WriteGPUDescriptors(
4528
                commandBuffer,
4529
                D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
4530
                cpuHandles,
4531
                graphicsPipeline->fragmentStorageTextureCount,
4532
                &gpuDescriptorHandle);
4533

4534
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
4535
                commandBuffer->graphicsCommandList,
4536
                graphicsPipeline->rootSignature->fragmentStorageTextureRootIndex,
4537
                gpuDescriptorHandle);
4538
        }
4539
        commandBuffer->needFragmentStorageTextureBind = false;
4540
    }
4541

4542
    if (commandBuffer->needFragmentStorageBufferBind) {
4543
        if (graphicsPipeline->fragmentStorageBufferCount > 0) {
4544
            for (Uint32 i = 0; i < graphicsPipeline->fragmentStorageBufferCount; i += 1) {
4545
                cpuHandles[i] = commandBuffer->fragmentStorageBuffers[i]->srvDescriptor.cpuHandle;
4546
            }
4547

4548
            D3D12_INTERNAL_WriteGPUDescriptors(
4549
                commandBuffer,
4550
                D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
4551
                cpuHandles,
4552
                graphicsPipeline->fragmentStorageBufferCount,
4553
                &gpuDescriptorHandle);
4554

4555
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(
4556
                commandBuffer->graphicsCommandList,
4557
                graphicsPipeline->rootSignature->fragmentStorageBufferRootIndex,
4558
                gpuDescriptorHandle);
4559
        }
4560
        commandBuffer->needFragmentStorageBufferBind = false;
4561
    }
4562

4563
    for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
4564
        if (commandBuffer->needFragmentUniformBufferBind[i]) {
4565
            if (graphicsPipeline->fragmentUniformBufferCount > i) {
4566
                ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(
4567
                    commandBuffer->graphicsCommandList,
4568
                    graphicsPipeline->rootSignature->fragmentUniformBufferRootIndex[i],
4569
                    commandBuffer->fragmentUniformBuffers[i]->buffer->virtualAddress + commandBuffer->fragmentUniformBuffers[i]->drawOffset);
4570
            }
4571
            commandBuffer->needFragmentUniformBufferBind[i] = false;
4572
        }
4573
    }
4574
}
4575

4576
static void D3D12_DrawIndexedPrimitives(
4577
    SDL_GPUCommandBuffer *commandBuffer,
4578
    Uint32 indexCount,
4579
    Uint32 instanceCount,
4580
    Uint32 firstIndex,
4581
    Sint32 vertexOffset,
4582
    Uint32 firstInstance)
4583
{
4584
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4585
    D3D12_INTERNAL_BindGraphicsResources(d3d12CommandBuffer);
4586

4587
    ID3D12GraphicsCommandList_DrawIndexedInstanced(
4588
        d3d12CommandBuffer->graphicsCommandList,
4589
        indexCount,
4590
        instanceCount,
4591
        firstIndex,
4592
        vertexOffset,
4593
        firstInstance);
4594
}
4595

4596
static void D3D12_DrawPrimitives(
4597
    SDL_GPUCommandBuffer *commandBuffer,
4598
    Uint32 vertexCount,
4599
    Uint32 instanceCount,
4600
    Uint32 firstVertex,
4601
    Uint32 firstInstance)
4602
{
4603
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4604
    D3D12_INTERNAL_BindGraphicsResources(d3d12CommandBuffer);
4605

4606
    ID3D12GraphicsCommandList_DrawInstanced(
4607
        d3d12CommandBuffer->graphicsCommandList,
4608
        vertexCount,
4609
        instanceCount,
4610
        firstVertex,
4611
        firstInstance);
4612
}
4613

4614
static void D3D12_DrawPrimitivesIndirect(
4615
    SDL_GPUCommandBuffer *commandBuffer,
4616
    SDL_GPUBuffer *buffer,
4617
    Uint32 offsetInBytes,
4618
    Uint32 drawCount,
4619
    Uint32 stride)
4620
{
4621
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4622
    D3D12Buffer *d3d12Buffer = ((D3D12BufferContainer *)buffer)->activeBuffer;
4623

4624
    D3D12_INTERNAL_BindGraphicsResources(d3d12CommandBuffer);
4625

4626
    if (stride == sizeof(SDL_GPUIndirectDrawCommand)) {
4627
        // Real multi-draw!
4628
        ID3D12GraphicsCommandList_ExecuteIndirect(
4629
            d3d12CommandBuffer->graphicsCommandList,
4630
            d3d12CommandBuffer->renderer->indirectDrawCommandSignature,
4631
            drawCount,
4632
            d3d12Buffer->handle,
4633
            offsetInBytes,
4634
            NULL,
4635
            0);
4636
    } else {
4637
        /* Fake multi-draw...
4638
         * FIXME: we could make this real multi-draw
4639
         * if we have a lookup to get command signature per stride value
4640
         */
4641
        for (Uint32 i = 0; i < drawCount; i += 1) {
4642
            ID3D12GraphicsCommandList_ExecuteIndirect(
4643
                d3d12CommandBuffer->graphicsCommandList,
4644
                d3d12CommandBuffer->renderer->indirectDrawCommandSignature,
4645
                1,
4646
                d3d12Buffer->handle,
4647
                offsetInBytes + (stride * i),
4648
                NULL,
4649
                0);
4650
        }
4651
    }
4652
}
4653

4654
static void D3D12_DrawIndexedPrimitivesIndirect(
4655
    SDL_GPUCommandBuffer *commandBuffer,
4656
    SDL_GPUBuffer *buffer,
4657
    Uint32 offsetInBytes,
4658
    Uint32 drawCount,
4659
    Uint32 stride)
4660
{
4661
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4662
    D3D12Buffer *d3d12Buffer = ((D3D12BufferContainer *)buffer)->activeBuffer;
4663

4664
    D3D12_INTERNAL_BindGraphicsResources(d3d12CommandBuffer);
4665

4666
    if (stride == sizeof(SDL_GPUIndexedIndirectDrawCommand)) {
4667
        // Real multi-draw!
4668
        ID3D12GraphicsCommandList_ExecuteIndirect(
4669
            d3d12CommandBuffer->graphicsCommandList,
4670
            d3d12CommandBuffer->renderer->indirectIndexedDrawCommandSignature,
4671
            drawCount,
4672
            d3d12Buffer->handle,
4673
            offsetInBytes,
4674
            NULL,
4675
            0);
4676
    } else {
4677
        /* Fake multi-draw...
4678
         * FIXME: we could make this real multi-draw
4679
         * if we have a lookup to get command signature per stride value
4680
         */
4681
        for (Uint32 i = 0; i < drawCount; i += 1) {
4682
            ID3D12GraphicsCommandList_ExecuteIndirect(
4683
                d3d12CommandBuffer->graphicsCommandList,
4684
                d3d12CommandBuffer->renderer->indirectIndexedDrawCommandSignature,
4685
                1,
4686
                d3d12Buffer->handle,
4687
                offsetInBytes + (stride * i),
4688
                NULL,
4689
                0);
4690
        }
4691
    }
4692
}
4693

4694
static void D3D12_EndRenderPass(
4695
    SDL_GPUCommandBuffer *commandBuffer)
4696
{
4697
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4698
    Uint32 i;
4699

4700
    for (i = 0; i < d3d12CommandBuffer->colorAttachmentTextureSubresourceCount; i += 1) {
4701
        D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
4702
            d3d12CommandBuffer,
4703
            D3D12_RESOURCE_STATE_RENDER_TARGET,
4704
            d3d12CommandBuffer->colorAttachmentTextureSubresources[i]);
4705

4706
        d3d12CommandBuffer->colorAttachmentTextureSubresources[i] = NULL;
4707
    }
4708
    d3d12CommandBuffer->colorAttachmentTextureSubresourceCount = 0;
4709

4710
    if (d3d12CommandBuffer->depthStencilTextureSubresource != NULL) {
4711
        D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
4712
            d3d12CommandBuffer,
4713
            D3D12_RESOURCE_STATE_DEPTH_WRITE,
4714
            d3d12CommandBuffer->depthStencilTextureSubresource);
4715

4716
        d3d12CommandBuffer->depthStencilTextureSubresource = NULL;
4717
    }
4718

4719
    d3d12CommandBuffer->currentGraphicsPipeline = NULL;
4720

4721
    ID3D12GraphicsCommandList_OMSetRenderTargets(
4722
        d3d12CommandBuffer->graphicsCommandList,
4723
        0,
4724
        NULL,
4725
        false,
4726
        NULL);
4727

4728
    // Reset bind state
4729
    SDL_zeroa(d3d12CommandBuffer->colorAttachmentTextureSubresources);
4730
    d3d12CommandBuffer->depthStencilTextureSubresource = NULL;
4731

4732
    SDL_zeroa(d3d12CommandBuffer->vertexBuffers);
4733
    SDL_zeroa(d3d12CommandBuffer->vertexBufferOffsets);
4734
    d3d12CommandBuffer->vertexBufferCount = 0;
4735

4736
    SDL_zeroa(d3d12CommandBuffer->vertexSamplerTextures);
4737
    SDL_zeroa(d3d12CommandBuffer->vertexSamplers);
4738
    SDL_zeroa(d3d12CommandBuffer->vertexStorageTextures);
4739
    SDL_zeroa(d3d12CommandBuffer->vertexStorageBuffers);
4740

4741
    SDL_zeroa(d3d12CommandBuffer->fragmentSamplerTextures);
4742
    SDL_zeroa(d3d12CommandBuffer->fragmentSamplers);
4743
    SDL_zeroa(d3d12CommandBuffer->fragmentStorageTextures);
4744
    SDL_zeroa(d3d12CommandBuffer->fragmentStorageBuffers);
4745
}
4746

4747
// Compute Pass
4748

4749
static void D3D12_BeginComputePass(
4750
    SDL_GPUCommandBuffer *commandBuffer,
4751
    SDL_GPUStorageTextureWriteOnlyBinding *storageTextureBindings,
4752
    Uint32 storageTextureBindingCount,
4753
    SDL_GPUStorageBufferWriteOnlyBinding *storageBufferBindings,
4754
    Uint32 storageBufferBindingCount)
4755
{
4756
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4757

4758
    d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresourceCount = storageTextureBindingCount;
4759
    d3d12CommandBuffer->computeWriteOnlyStorageBufferCount = storageBufferBindingCount;
4760

4761
    /* Write-only resources will be actually bound in BindComputePipeline
4762
     * after the root signature is set.
4763
     * We also have to scan to see which barriers we actually need because depth slices aren't separate subresources
4764
     */
4765
    if (storageTextureBindingCount > 0) {
4766
        for (Uint32 i = 0; i < storageTextureBindingCount; i += 1) {
4767
            D3D12TextureContainer *container = (D3D12TextureContainer *)storageTextureBindings[i].texture;
4768
            if (!(container->header.info.usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE)) {
4769
                SDL_LogError(SDL_LOG_CATEGORY_GPU, "Attempted to bind read-only texture as compute write texture");
4770
            }
4771

4772
            D3D12TextureSubresource *subresource = D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
4773
                d3d12CommandBuffer,
4774
                container,
4775
                storageTextureBindings[i].layer,
4776
                storageTextureBindings[i].mipLevel,
4777
                storageTextureBindings[i].cycle,
4778
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
4779

4780
            d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresources[i] = subresource;
4781

4782
            D3D12_INTERNAL_TrackTexture(
4783
                d3d12CommandBuffer,
4784
                subresource->parent);
4785
        }
4786
    }
4787

4788
    if (storageBufferBindingCount > 0) {
4789
        for (Uint32 i = 0; i < storageBufferBindingCount; i += 1) {
4790
            D3D12BufferContainer *container = (D3D12BufferContainer *)storageBufferBindings[i].buffer;
4791
            if (!(container->usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE)) {
4792
                SDL_LogError(SDL_LOG_CATEGORY_GPU, "Attempted to bind read-only texture as compute write texture");
4793
            }
4794
            D3D12Buffer *buffer = D3D12_INTERNAL_PrepareBufferForWrite(
4795
                d3d12CommandBuffer,
4796
                container,
4797
                storageBufferBindings[i].cycle,
4798
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
4799

4800
            d3d12CommandBuffer->computeWriteOnlyStorageBuffers[i] = buffer;
4801

4802
            D3D12_INTERNAL_TrackBuffer(
4803
                d3d12CommandBuffer,
4804
                buffer);
4805
        }
4806
    }
4807
}
4808

4809
static void D3D12_BindComputePipeline(
4810
    SDL_GPUCommandBuffer *commandBuffer,
4811
    SDL_GPUComputePipeline *computePipeline)
4812
{
4813
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4814
    D3D12ComputePipeline *pipeline = (D3D12ComputePipeline *)computePipeline;
4815
    D3D12_CPU_DESCRIPTOR_HANDLE cpuHandles[MAX_TEXTURE_SAMPLERS_PER_STAGE];
4816
    D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorHandle;
4817

4818
    ID3D12GraphicsCommandList_SetPipelineState(
4819
        d3d12CommandBuffer->graphicsCommandList,
4820
        pipeline->pipelineState);
4821

4822
    ID3D12GraphicsCommandList_SetComputeRootSignature(
4823
        d3d12CommandBuffer->graphicsCommandList,
4824
        pipeline->rootSignature->handle);
4825

4826
    d3d12CommandBuffer->currentComputePipeline = pipeline;
4827

4828
    d3d12CommandBuffer->needComputeReadOnlyStorageTextureBind = true;
4829
    d3d12CommandBuffer->needComputeReadOnlyStorageBufferBind = true;
4830

4831
    for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
4832
        d3d12CommandBuffer->needComputeUniformBufferBind[i] = true;
4833
    }
4834

4835
    for (Uint32 i = 0; i < pipeline->uniformBufferCount; i += 1) {
4836
        if (d3d12CommandBuffer->computeUniformBuffers[i] == NULL) {
4837
            d3d12CommandBuffer->computeUniformBuffers[i] = D3D12_INTERNAL_AcquireUniformBufferFromPool(
4838
                d3d12CommandBuffer);
4839
        }
4840
    }
4841

4842
    D3D12_INTERNAL_TrackComputePipeline(d3d12CommandBuffer, pipeline);
4843

4844
    // Bind write-only resources after setting root signature
4845
    if (pipeline->writeOnlyStorageTextureCount > 0) {
4846
        for (Uint32 i = 0; i < pipeline->writeOnlyStorageTextureCount; i += 1) {
4847
            cpuHandles[i] = d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresources[i]->uavHandle.cpuHandle;
4848
        }
4849

4850
        D3D12_INTERNAL_WriteGPUDescriptors(
4851
            d3d12CommandBuffer,
4852
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
4853
            cpuHandles,
4854
            d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresourceCount,
4855
            &gpuDescriptorHandle);
4856

4857
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(
4858
            d3d12CommandBuffer->graphicsCommandList,
4859
            d3d12CommandBuffer->currentComputePipeline->rootSignature->writeOnlyStorageTextureRootIndex,
4860
            gpuDescriptorHandle);
4861
    }
4862

4863
    if (pipeline->writeOnlyStorageBufferCount > 0) {
4864
        for (Uint32 i = 0; i < pipeline->writeOnlyStorageBufferCount; i += 1) {
4865
            cpuHandles[i] = d3d12CommandBuffer->computeWriteOnlyStorageBuffers[i]->uavDescriptor.cpuHandle;
4866
        }
4867

4868
        D3D12_INTERNAL_WriteGPUDescriptors(
4869
            d3d12CommandBuffer,
4870
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
4871
            cpuHandles,
4872
            d3d12CommandBuffer->computeWriteOnlyStorageBufferCount,
4873
            &gpuDescriptorHandle);
4874

4875
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(
4876
            d3d12CommandBuffer->graphicsCommandList,
4877
            d3d12CommandBuffer->currentComputePipeline->rootSignature->writeOnlyStorageBufferRootIndex,
4878
            gpuDescriptorHandle);
4879
    }
4880
}
4881

4882
static void D3D12_BindComputeStorageTextures(
4883
    SDL_GPUCommandBuffer *commandBuffer,
4884
    Uint32 firstSlot,
4885
    SDL_GPUTexture **storageTextures,
4886
    Uint32 bindingCount)
4887
{
4888
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4889

4890
    for (Uint32 i = 0; i < bindingCount; i += 1) {
4891
        if (d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i] != NULL) {
4892
            D3D12_INTERNAL_TextureTransitionToDefaultUsage(
4893
                d3d12CommandBuffer,
4894
                D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
4895
                d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i]);
4896
        }
4897

4898
        D3D12TextureContainer *container = (D3D12TextureContainer *)storageTextures[i];
4899
        d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i] = container->activeTexture;
4900

4901
        D3D12_INTERNAL_TextureTransitionFromDefaultUsage(
4902
            d3d12CommandBuffer,
4903
            D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
4904
            container->activeTexture);
4905

4906
        D3D12_INTERNAL_TrackTexture(
4907
            d3d12CommandBuffer,
4908
            container->activeTexture);
4909
    }
4910

4911
    d3d12CommandBuffer->needComputeReadOnlyStorageTextureBind = true;
4912
}
4913

4914
static void D3D12_BindComputeStorageBuffers(
4915
    SDL_GPUCommandBuffer *commandBuffer,
4916
    Uint32 firstSlot,
4917
    SDL_GPUBuffer **storageBuffers,
4918
    Uint32 bindingCount)
4919
{
4920
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4921

4922
    for (Uint32 i = 0; i < bindingCount; i += 1) {
4923
        if (d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i] != NULL) {
4924
            D3D12_INTERNAL_BufferTransitionToDefaultUsage(
4925
                d3d12CommandBuffer,
4926
                D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
4927
                d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i]);
4928
        }
4929

4930
        D3D12BufferContainer *container = (D3D12BufferContainer *)storageBuffers[i];
4931
        D3D12Buffer *buffer = container->activeBuffer;
4932

4933
        d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i] = buffer;
4934

4935
        D3D12_INTERNAL_BufferTransitionFromDefaultUsage(
4936
            d3d12CommandBuffer,
4937
            D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
4938
            buffer);
4939

4940
        D3D12_INTERNAL_TrackBuffer(
4941
            d3d12CommandBuffer,
4942
            buffer);
4943
    }
4944

4945
    d3d12CommandBuffer->needComputeReadOnlyStorageBufferBind = true;
4946
}
4947

4948
static void D3D12_PushComputeUniformData(
4949
    SDL_GPUCommandBuffer *commandBuffer,
4950
    Uint32 slotIndex,
4951
    const void *data,
4952
    Uint32 dataLengthInBytes)
4953
{
4954
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
4955

4956
    D3D12_INTERNAL_PushUniformData(
4957
        d3d12CommandBuffer,
4958
        SDL_GPU_SHADERSTAGE_COMPUTE,
4959
        slotIndex,
4960
        data,
4961
        dataLengthInBytes);
4962
}
4963

4964
static void D3D12_INTERNAL_BindComputeResources(
4965
    D3D12CommandBuffer *commandBuffer)
4966
{
4967
    D3D12ComputePipeline *computePipeline = commandBuffer->currentComputePipeline;
4968

4969
    D3D12_CPU_DESCRIPTOR_HANDLE cpuHandles[MAX_TEXTURE_SAMPLERS_PER_STAGE];
4970
    D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorHandle;
4971

4972
    if (commandBuffer->needComputeReadOnlyStorageTextureBind) {
4973
        if (computePipeline->readOnlyStorageTextureCount > 0) {
4974
            for (Uint32 i = 0; i < computePipeline->readOnlyStorageTextureCount; i += 1) {
4975
                cpuHandles[i] = commandBuffer->computeReadOnlyStorageTextures[i]->srvHandle.cpuHandle;
4976
            }
4977

4978
            D3D12_INTERNAL_WriteGPUDescriptors(
4979
                commandBuffer,
4980
                D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
4981
                cpuHandles,
4982
                computePipeline->readOnlyStorageTextureCount,
4983
                &gpuDescriptorHandle);
4984

4985
            ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(
4986
                commandBuffer->graphicsCommandList,
4987
                computePipeline->rootSignature->readOnlyStorageTextureRootIndex,
4988
                gpuDescriptorHandle);
4989
        }
4990
        commandBuffer->needComputeReadOnlyStorageTextureBind = false;
4991
    }
4992

4993
    if (commandBuffer->needComputeReadOnlyStorageBufferBind) {
4994
        if (computePipeline->readOnlyStorageBufferCount > 0) {
4995
            for (Uint32 i = 0; i < computePipeline->readOnlyStorageBufferCount; i += 1) {
4996
                cpuHandles[i] = commandBuffer->computeReadOnlyStorageBuffers[i]->srvDescriptor.cpuHandle;
4997
            }
4998

4999
            D3D12_INTERNAL_WriteGPUDescriptors(
5000
                commandBuffer,
5001
                D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
5002
                cpuHandles,
5003
                computePipeline->readOnlyStorageBufferCount,
5004
                &gpuDescriptorHandle);
5005

5006
            ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(
5007
                commandBuffer->graphicsCommandList,
5008
                computePipeline->rootSignature->readOnlyStorageBufferRootIndex,
5009
                gpuDescriptorHandle);
5010
        }
5011
        commandBuffer->needComputeReadOnlyStorageBufferBind = false;
5012
    }
5013

5014
    for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
5015
        if (commandBuffer->needComputeUniformBufferBind[i]) {
5016
            if (computePipeline->uniformBufferCount > i) {
5017
                ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(
5018
                    commandBuffer->graphicsCommandList,
5019
                    computePipeline->rootSignature->uniformBufferRootIndex[i],
5020
                    commandBuffer->computeUniformBuffers[i]->buffer->virtualAddress + commandBuffer->computeUniformBuffers[i]->drawOffset);
5021
            }
5022
        }
5023
        commandBuffer->needComputeUniformBufferBind[i] = false;
5024
    }
5025
}
5026

5027
static void D3D12_DispatchCompute(
5028
    SDL_GPUCommandBuffer *commandBuffer,
5029
    Uint32 groupCountX,
5030
    Uint32 groupCountY,
5031
    Uint32 groupCountZ)
5032
{
5033
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
5034

5035
    D3D12_INTERNAL_BindComputeResources(d3d12CommandBuffer);
5036
    ID3D12GraphicsCommandList_Dispatch(
5037
        d3d12CommandBuffer->graphicsCommandList,
5038
        groupCountX,
5039
        groupCountY,
5040
        groupCountZ);
5041
}
5042

5043
static void D3D12_DispatchComputeIndirect(
5044
    SDL_GPUCommandBuffer *commandBuffer,
5045
    SDL_GPUBuffer *buffer,
5046
    Uint32 offsetInBytes)
5047
{
5048
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
5049
    D3D12Buffer *d3d12Buffer = ((D3D12BufferContainer *)buffer)->activeBuffer;
5050

5051
    D3D12_INTERNAL_BindComputeResources(d3d12CommandBuffer);
5052
    ID3D12GraphicsCommandList_ExecuteIndirect(
5053
        d3d12CommandBuffer->graphicsCommandList,
5054
        d3d12CommandBuffer->renderer->indirectDispatchCommandSignature,
5055
        1,
5056
        d3d12Buffer->handle,
5057
        offsetInBytes,
5058
        NULL,
5059
        0);
5060
}
5061

5062
static void D3D12_EndComputePass(
5063
    SDL_GPUCommandBuffer *commandBuffer)
5064
{
5065
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
5066

5067
    for (Uint32 i = 0; i < d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresourceCount; i += 1) {
5068
        if (d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresources[i]) {
5069
            D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
5070
                d3d12CommandBuffer,
5071
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
5072
                d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresources[i]);
5073

5074
            d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresources[i] = NULL;
5075
        }
5076
    }
5077
    d3d12CommandBuffer->computeWriteOnlyStorageTextureSubresourceCount = 0;
5078

5079
    for (Uint32 i = 0; i < d3d12CommandBuffer->computeWriteOnlyStorageBufferCount; i += 1) {
5080
        if (d3d12CommandBuffer->computeWriteOnlyStorageBuffers[i]) {
5081
            D3D12_INTERNAL_BufferTransitionToDefaultUsage(
5082
                d3d12CommandBuffer,
5083
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
5084
                d3d12CommandBuffer->computeWriteOnlyStorageBuffers[i]);
5085

5086
            d3d12CommandBuffer->computeWriteOnlyStorageBuffers[i] = NULL;
5087
        }
5088
    }
5089
    d3d12CommandBuffer->computeWriteOnlyStorageBufferCount = 0;
5090

5091
    for (Uint32 i = 0; i < MAX_STORAGE_TEXTURES_PER_STAGE; i += 1) {
5092
        if (d3d12CommandBuffer->computeReadOnlyStorageTextures[i]) {
5093
            D3D12_INTERNAL_TextureTransitionToDefaultUsage(
5094
                d3d12CommandBuffer,
5095
                D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
5096
                d3d12CommandBuffer->computeReadOnlyStorageTextures[i]);
5097

5098
            d3d12CommandBuffer->computeReadOnlyStorageTextures[i] = NULL;
5099
        }
5100
    }
5101

5102
    for (Uint32 i = 0; i < MAX_STORAGE_BUFFERS_PER_STAGE; i += 1) {
5103
        if (d3d12CommandBuffer->computeReadOnlyStorageBuffers[i]) {
5104
            D3D12_INTERNAL_BufferTransitionToDefaultUsage(
5105
                d3d12CommandBuffer,
5106
                D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
5107
                d3d12CommandBuffer->computeReadOnlyStorageBuffers[i]);
5108

5109
            d3d12CommandBuffer->computeReadOnlyStorageBuffers[i] = NULL;
5110
        }
5111
    }
5112

5113
    d3d12CommandBuffer->currentComputePipeline = NULL;
5114
}
5115

5116
// TransferBuffer Data
5117

5118
static void *D3D12_MapTransferBuffer(
5119
    SDL_GPURenderer *driverData,
5120
    SDL_GPUTransferBuffer *transferBuffer,
5121
    bool cycle)
5122
{
5123
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
5124
    D3D12BufferContainer *container = (D3D12BufferContainer *)transferBuffer;
5125
    void *dataPointer;
5126

5127
    if (
5128
        cycle &&
5129
        SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0) {
5130
        D3D12_INTERNAL_CycleActiveBuffer(
5131
            renderer,
5132
            container);
5133
    }
5134

5135
    // Upload buffers are persistently mapped, download buffers are not
5136
    if (container->type == D3D12_BUFFER_TYPE_UPLOAD) {
5137
        dataPointer = container->activeBuffer->mapPointer;
5138
    } else {
5139
        ID3D12Resource_Map(
5140
            container->activeBuffer->handle,
5141
            0,
5142
            NULL,
5143
            (void **)&dataPointer);
5144
    }
5145

5146
    return dataPointer;
5147
}
5148

5149
static void D3D12_UnmapTransferBuffer(
5150
    SDL_GPURenderer *driverData,
5151
    SDL_GPUTransferBuffer *transferBuffer)
5152
{
5153
    (void)driverData;
5154
    D3D12BufferContainer *container = (D3D12BufferContainer *)transferBuffer;
5155

5156
    // Upload buffers are persistently mapped, download buffers are not
5157
    if (container->type == D3D12_BUFFER_TYPE_DOWNLOAD) {
5158
        ID3D12Resource_Unmap(
5159
            container->activeBuffer->handle,
5160
            0,
5161
            NULL);
5162
    }
5163
}
5164

5165
// Copy Pass
5166

5167
static void D3D12_BeginCopyPass(
5168
    SDL_GPUCommandBuffer *commandBuffer)
5169
{
5170
    // no-op
5171
    (void)commandBuffer;
5172
}
5173

5174
static void D3D12_UploadToTexture(
5175
    SDL_GPUCommandBuffer *commandBuffer,
5176
    SDL_GPUTextureTransferInfo *source,
5177
    SDL_GPUTextureRegion *destination,
5178
    bool cycle)
5179
{
5180
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
5181
    D3D12BufferContainer *transferBufferContainer = (D3D12BufferContainer *)source->transferBuffer;
5182
    D3D12Buffer *temporaryBuffer = NULL;
5183
    D3D12_TEXTURE_COPY_LOCATION sourceLocation;
5184
    D3D12_TEXTURE_COPY_LOCATION destinationLocation;
5185
    Uint32 pixelsPerRow = source->imagePitch;
5186
    Uint32 rowPitch;
5187
    Uint32 alignedRowPitch;
5188
    Uint32 rowsPerSlice = source->imageHeight;
5189
    Uint32 bytesPerSlice;
5190
    bool needsRealignment;
5191
    bool needsPlacementCopy;
5192

5193
    // Note that the transfer buffer does not need a barrier, as it is synced by the client.
5194

5195
    D3D12TextureContainer *textureContainer = (D3D12TextureContainer *)destination->texture;
5196
    D3D12TextureSubresource *textureSubresource = D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
5197
        d3d12CommandBuffer,
5198
        textureContainer,
5199
        destination->layer,
5200
        destination->mipLevel,
5201
        cycle,
5202
        D3D12_RESOURCE_STATE_COPY_DEST);
5203

5204
    /* D3D12 requires texture data row pitch to be 256 byte aligned, which is obviously insane.
5205
     * Instead of exposing that restriction to the client, which is a huge rake to step on,
5206
     * and a restriction that no other backend requires, we're going to copy data to a temporary buffer,
5207
     * copy THAT data to the texture, and then get rid of the temporary buffer ASAP.
5208
     * If we're lucky and the row pitch and depth pitch are already aligned, we can skip all of that.
5209
     *
5210
     * D3D12 also requires offsets to be 512 byte aligned. We'll fix that for the client and warn them as well.
5211
     *
5212
     * And just for some extra fun, D3D12 doesn't actually support depth pitch, so we have to realign that too!
5213
     */
5214

5215
    if (pixelsPerRow == 0) {
5216
        pixelsPerRow = destination->w;
5217
    }
5218

5219
    rowPitch = BytesPerRow(pixelsPerRow, textureContainer->header.info.format);
5220

5221
    if (rowsPerSlice == 0) {
5222
        rowsPerSlice = destination->h;
5223
    }
5224

5225
    bytesPerSlice = rowsPerSlice * rowPitch;
5226

5227
    alignedRowPitch = D3D12_INTERNAL_Align(rowPitch, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
5228
    needsRealignment = rowsPerSlice != destination->h || rowPitch != alignedRowPitch;
5229
    needsPlacementCopy = source->offset % D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT != 0;
5230

5231
    sourceLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
5232
    sourceLocation.PlacedFootprint.Footprint.Format = SDLToD3D12_TextureFormat[textureContainer->header.info.format];
5233
    sourceLocation.PlacedFootprint.Footprint.RowPitch = alignedRowPitch;
5234

5235
    destinationLocation.pResource = textureContainer->activeTexture->resource;
5236
    destinationLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
5237
    destinationLocation.SubresourceIndex = textureSubresource->index;
5238

5239
    if (needsRealignment) {
5240
        temporaryBuffer = D3D12_INTERNAL_CreateBuffer(
5241
            d3d12CommandBuffer->renderer,
5242
            0,
5243
            alignedRowPitch * destination->h * destination->d,
5244
            D3D12_BUFFER_TYPE_UPLOAD);
5245

5246
        if (!temporaryBuffer) {
5247
            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create temporary upload buffer.");
5248
            return;
5249
        }
5250

5251
        sourceLocation.pResource = temporaryBuffer->handle;
5252

5253
        for (Uint32 sliceIndex = 0; sliceIndex < destination->d; sliceIndex += 1) {
5254
            // copy row count minus one to avoid overread
5255
            for (Uint32 rowIndex = 0; rowIndex < rowsPerSlice - 1; rowIndex += 1) {
5256
                SDL_memcpy(
5257
                    temporaryBuffer->mapPointer + (sliceIndex * rowsPerSlice) + (rowIndex * alignedRowPitch),
5258
                    transferBufferContainer->activeBuffer->mapPointer + source->offset + (sliceIndex * bytesPerSlice) + (rowIndex * rowPitch),
5259
                    alignedRowPitch);
5260
            }
5261
            Uint32 offset = source->offset + (sliceIndex * bytesPerSlice) + ((rowsPerSlice - 1) * rowPitch);
5262
            SDL_memcpy(
5263
                temporaryBuffer->mapPointer + (sliceIndex * rowsPerSlice) + ((rowsPerSlice - 1) * alignedRowPitch),
5264
                transferBufferContainer->activeBuffer->mapPointer + offset,
5265
                SDL_min(alignedRowPitch, transferBufferContainer->size - offset));
5266

5267
            sourceLocation.PlacedFootprint.Footprint.Width = destination->w;
5268
            sourceLocation.PlacedFootprint.Footprint.Height = rowsPerSlice;
5269
            sourceLocation.PlacedFootprint.Footprint.Depth = 1;
5270
            sourceLocation.PlacedFootprint.Offset = (sliceIndex * bytesPerSlice);
5271

5272
            ID3D12GraphicsCommandList_CopyTextureRegion(
5273
                d3d12CommandBuffer->graphicsCommandList,
5274
                &destinationLocation,
5275
                destination->x,
5276
                destination->y,
5277
                sliceIndex,
5278
                &sourceLocation,
5279
                NULL);
5280
        }
5281

5282
        D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, temporaryBuffer);
5283
        D3D12_INTERNAL_ReleaseBuffer(
5284
            d3d12CommandBuffer->renderer,
5285
            temporaryBuffer);
5286
    } else if (needsPlacementCopy) {
5287
        temporaryBuffer = D3D12_INTERNAL_CreateBuffer(
5288
            d3d12CommandBuffer->renderer,
5289
            0,
5290
            alignedRowPitch * destination->h * destination->d,
5291
            D3D12_BUFFER_TYPE_UPLOAD);
5292

5293
        if (!temporaryBuffer) {
5294
            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create temporary upload buffer.");
5295
            return;
5296
        }
5297

5298
        SDL_memcpy(
5299
            temporaryBuffer->mapPointer,
5300
            transferBufferContainer->activeBuffer->mapPointer + source->offset,
5301
            alignedRowPitch * destination->h * destination->d);
5302

5303
        sourceLocation.pResource = temporaryBuffer->handle;
5304
        sourceLocation.PlacedFootprint.Offset = 0;
5305
        sourceLocation.PlacedFootprint.Footprint.Width = destination->w;
5306
        sourceLocation.PlacedFootprint.Footprint.Height = destination->h;
5307
        sourceLocation.PlacedFootprint.Footprint.Depth = 1;
5308

5309
        ID3D12GraphicsCommandList_CopyTextureRegion(
5310
            d3d12CommandBuffer->graphicsCommandList,
5311
            &destinationLocation,
5312
            destination->x,
5313
            destination->y,
5314
            destination->z,
5315
            &sourceLocation,
5316
            NULL);
5317

5318
        D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, temporaryBuffer);
5319
        D3D12_INTERNAL_ReleaseBuffer(
5320
            d3d12CommandBuffer->renderer,
5321
            temporaryBuffer);
5322

5323
        SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Texture upload offset not aligned to 512 bytes! This is suboptimal on D3D12!");
5324
    } else {
5325
        sourceLocation.pResource = transferBufferContainer->activeBuffer->handle;
5326
        sourceLocation.PlacedFootprint.Offset = source->offset;
5327
        sourceLocation.PlacedFootprint.Footprint.Width = destination->w;
5328
        sourceLocation.PlacedFootprint.Footprint.Height = destination->h;
5329
        sourceLocation.PlacedFootprint.Footprint.Depth = destination->d;
5330

5331
        ID3D12GraphicsCommandList_CopyTextureRegion(
5332
            d3d12CommandBuffer->graphicsCommandList,
5333
            &destinationLocation,
5334
            destination->x,
5335
            destination->y,
5336
            destination->z,
5337
            &sourceLocation,
5338
            NULL);
5339
    }
5340

5341
    D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
5342
        d3d12CommandBuffer,
5343
        D3D12_RESOURCE_STATE_COPY_DEST,
5344
        textureSubresource);
5345

5346
    D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, transferBufferContainer->activeBuffer);
5347
    D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, textureSubresource->parent);
5348
}
5349

5350
static void D3D12_UploadToBuffer(
5351
    SDL_GPUCommandBuffer *commandBuffer,
5352
    SDL_GPUTransferBufferLocation *source,
5353
    SDL_GPUBufferRegion *destination,
5354
    bool cycle)
5355
{
5356
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
5357
    D3D12BufferContainer *transferBufferContainer = (D3D12BufferContainer *)source->transferBuffer;
5358
    D3D12BufferContainer *bufferContainer = (D3D12BufferContainer *)destination->buffer;
5359

5360
    // The transfer buffer does not need a barrier, it is synced by the client.
5361

5362
    D3D12Buffer *buffer = D3D12_INTERNAL_PrepareBufferForWrite(
5363
        d3d12CommandBuffer,
5364
        bufferContainer,
5365
        cycle,
5366
        D3D12_RESOURCE_STATE_COPY_DEST);
5367

5368
    ID3D12GraphicsCommandList_CopyBufferRegion(
5369
        d3d12CommandBuffer->graphicsCommandList,
5370
        buffer->handle,
5371
        destination->offset,
5372
        transferBufferContainer->activeBuffer->handle,
5373
        source->offset,
5374
        destination->size);
5375

5376
    D3D12_INTERNAL_BufferTransitionToDefaultUsage(
5377
        d3d12CommandBuffer,
5378
        D3D12_RESOURCE_STATE_COPY_DEST,
5379
        buffer);
5380

5381
    D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, transferBufferContainer->activeBuffer);
5382
    D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, buffer);
5383
}
5384

5385
static void D3D12_CopyTextureToTexture(
5386
    SDL_GPUCommandBuffer *commandBuffer,
5387
    SDL_GPUTextureLocation *source,
5388
    SDL_GPUTextureLocation *destination,
5389
    Uint32 w,
5390
    Uint32 h,
5391
    Uint32 d,
5392
    bool cycle)
5393
{
5394
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
5395
    D3D12_TEXTURE_COPY_LOCATION sourceLocation;
5396
    D3D12_TEXTURE_COPY_LOCATION destinationLocation;
5397

5398
    D3D12TextureSubresource *sourceSubresource = D3D12_INTERNAL_FetchTextureSubresource(
5399
        (D3D12TextureContainer *)source->texture,
5400
        source->layer,
5401
        source->mipLevel);
5402

5403
    D3D12TextureSubresource *destinationSubresource = D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
5404
        d3d12CommandBuffer,
5405
        (D3D12TextureContainer *)destination->texture,
5406
        destination->layer,
5407
        destination->mipLevel,
5408
        cycle,
5409
        D3D12_RESOURCE_STATE_COPY_DEST);
5410

5411
    D3D12_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
5412
        d3d12CommandBuffer,
5413
        D3D12_RESOURCE_STATE_COPY_SOURCE,
5414
        sourceSubresource);
5415

5416
    sourceLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
5417
    sourceLocation.SubresourceIndex = sourceSubresource->index;
5418
    sourceLocation.pResource = sourceSubresource->parent->resource;
5419

5420
    destinationLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
5421
    destinationLocation.SubresourceIndex = destinationSubresource->index;
5422
    destinationLocation.pResource = destinationSubresource->parent->resource;
5423

5424
    D3D12_BOX sourceBox = { source->x, source->y, source->z, source->x + w, source->y + h, source->z + d };
5425

5426
    ID3D12GraphicsCommandList_CopyTextureRegion(
5427
        d3d12CommandBuffer->graphicsCommandList,
5428
        &destinationLocation,
5429
        destination->x,
5430
        destination->y,
5431
        destination->z,
5432
        &sourceLocation,
5433
        &sourceBox);
5434

5435
    D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
5436
        d3d12CommandBuffer,
5437
        D3D12_RESOURCE_STATE_COPY_SOURCE,
5438
        sourceSubresource);
5439

5440
    D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
5441
        d3d12CommandBuffer,
5442
        D3D12_RESOURCE_STATE_COPY_DEST,
5443
        destinationSubresource);
5444

5445
    D3D12_INTERNAL_TrackTexture(
5446
        d3d12CommandBuffer,
5447
        sourceSubresource->parent);
5448

5449
    D3D12_INTERNAL_TrackTexture(
5450
        d3d12CommandBuffer,
5451
        destinationSubresource->parent);
5452
}
5453

5454
static void D3D12_CopyBufferToBuffer(
5455
    SDL_GPUCommandBuffer *commandBuffer,
5456
    SDL_GPUBufferLocation *source,
5457
    SDL_GPUBufferLocation *destination,
5458
    Uint32 size,
5459
    bool cycle)
5460
{
5461
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
5462
    D3D12BufferContainer *sourceContainer = (D3D12BufferContainer *)source->buffer;
5463
    D3D12BufferContainer *destinationContainer = (D3D12BufferContainer *)destination->buffer;
5464

5465
    D3D12Buffer *sourceBuffer = sourceContainer->activeBuffer;
5466
    D3D12Buffer *destinationBuffer = D3D12_INTERNAL_PrepareBufferForWrite(
5467
        d3d12CommandBuffer,
5468
        destinationContainer,
5469
        cycle,
5470
        D3D12_RESOURCE_STATE_COPY_DEST);
5471

5472
    D3D12_INTERNAL_BufferTransitionFromDefaultUsage(
5473
        d3d12CommandBuffer,
5474
        D3D12_RESOURCE_STATE_COPY_SOURCE,
5475
        sourceBuffer);
5476

5477
    ID3D12GraphicsCommandList_CopyBufferRegion(
5478
        d3d12CommandBuffer->graphicsCommandList,
5479
        destinationBuffer->handle,
5480
        destination->offset,
5481
        sourceBuffer->handle,
5482
        source->offset,
5483
        size);
5484

5485
    D3D12_INTERNAL_BufferTransitionToDefaultUsage(
5486
        d3d12CommandBuffer,
5487
        D3D12_RESOURCE_STATE_COPY_SOURCE,
5488
        sourceBuffer);
5489

5490
    D3D12_INTERNAL_BufferTransitionToDefaultUsage(
5491
        d3d12CommandBuffer,
5492
        D3D12_RESOURCE_STATE_COPY_DEST,
5493
        destinationBuffer);
5494

5495
    D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, sourceBuffer);
5496
    D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, destinationBuffer);
5497
}
5498

5499
static void D3D12_DownloadFromTexture(
5500
    SDL_GPUCommandBuffer *commandBuffer,
5501
    SDL_GPUTextureRegion *source,
5502
    SDL_GPUTextureTransferInfo *destination)
5503
{
5504
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
5505
    D3D12_TEXTURE_COPY_LOCATION sourceLocation;
5506
    D3D12_TEXTURE_COPY_LOCATION destinationLocation;
5507
    Uint32 pixelsPerRow = destination->imagePitch;
5508
    Uint32 rowPitch;
5509
    Uint32 alignedRowPitch;
5510
    Uint32 rowsPerSlice = destination->imageHeight;
5511
    bool needsRealignment;
5512
    bool needsPlacementCopy;
5513
    D3D12TextureDownload *textureDownload = NULL;
5514
    D3D12TextureContainer *sourceContainer = (D3D12TextureContainer *)source->texture;
5515
    D3D12TextureSubresource *sourceSubresource = D3D12_INTERNAL_FetchTextureSubresource(
5516
        sourceContainer,
5517
        source->layer,
5518
        source->mipLevel);
5519
    D3D12BufferContainer *destinationContainer = (D3D12BufferContainer *)destination->transferBuffer;
5520
    D3D12Buffer *destinationBuffer = destinationContainer->activeBuffer;
5521

5522
    /* D3D12 requires texture data row pitch to be 256 byte aligned, which is obviously insane.
5523
     * Instead of exposing that restriction to the client, which is a huge rake to step on,
5524
     * and a restriction that no other backend requires, we're going to copy data to a temporary buffer,
5525
     * copy THAT data to the texture, and then get rid of the temporary buffer ASAP.
5526
     * If we're lucky and the row pitch and depth pitch are already aligned, we can skip all of that.
5527
     *
5528
     * D3D12 also requires offsets to be 512 byte aligned. We'll fix that for the client and warn them as well.
5529
     *
5530
     * And just for some extra fun, D3D12 doesn't actually support depth pitch, so we have to realign that too!
5531
     *
5532
     * Since this is an async download we have to do all these fixups after the command is finished,
5533
     * so we'll cache the metadata similar to D3D11 and map and copy it when the command buffer is cleaned.
5534
     */
5535

5536
    if (pixelsPerRow == 0) {
5537
        pixelsPerRow = source->w;
5538
    }
5539

5540
    rowPitch = BytesPerRow(pixelsPerRow, sourceContainer->header.info.format);
5541

5542
    if (rowsPerSlice == 0) {
5543
        rowsPerSlice = source->h;
5544
    }
5545

5546
    alignedRowPitch = D3D12_INTERNAL_Align(rowPitch, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
5547
    needsRealignment = rowsPerSlice != source->h || rowPitch != alignedRowPitch;
5548
    needsPlacementCopy = destination->offset % D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT != 0;
5549

5550
    sourceLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
5551
    sourceLocation.SubresourceIndex = sourceSubresource->index;
5552
    sourceLocation.pResource = sourceSubresource->parent->resource;
5553

5554
    D3D12_BOX sourceBox = { source->x, source->y, source->z, source->x + source->w, source->y + rowsPerSlice, source->z + source->d };
5555

5556
    destinationLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
5557
    destinationLocation.PlacedFootprint.Footprint.Format = SDLToD3D12_TextureFormat[sourceContainer->header.info.format];
5558
    destinationLocation.PlacedFootprint.Footprint.Width = source->w;
5559
    destinationLocation.PlacedFootprint.Footprint.Height = rowsPerSlice;
5560
    destinationLocation.PlacedFootprint.Footprint.Depth = source->d;
5561
    destinationLocation.PlacedFootprint.Footprint.RowPitch = alignedRowPitch;
5562

5563
    if (needsRealignment || needsPlacementCopy) {
5564
        textureDownload = (D3D12TextureDownload *)SDL_malloc(sizeof(D3D12TextureDownload));
5565

5566
        if (!textureDownload) {
5567
            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create texture download structure!");
5568
            return;
5569
        }
5570

5571
        textureDownload->temporaryBuffer = D3D12_INTERNAL_CreateBuffer(
5572
            d3d12CommandBuffer->renderer,
5573
            0,
5574
            alignedRowPitch * rowsPerSlice * source->d,
5575
            D3D12_BUFFER_TYPE_DOWNLOAD);
5576

5577
        if (!textureDownload->temporaryBuffer) {
5578
            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create temporary download buffer!");
5579
            SDL_free(textureDownload);
5580
            return;
5581
        }
5582

5583
        textureDownload->destinationBuffer = destinationBuffer;
5584
        textureDownload->bufferOffset = destination->offset;
5585
        textureDownload->width = source->w;
5586
        textureDownload->height = rowsPerSlice;
5587
        textureDownload->depth = source->d;
5588
        textureDownload->bytesPerRow = rowPitch;
5589
        textureDownload->bytesPerDepthSlice = rowPitch * rowsPerSlice;
5590
        textureDownload->alignedBytesPerRow = alignedRowPitch;
5591

5592
        destinationLocation.pResource = textureDownload->temporaryBuffer->handle;
5593
        destinationLocation.PlacedFootprint.Offset = 0;
5594
    } else {
5595
        destinationLocation.pResource = destinationBuffer->handle;
5596
        destinationLocation.PlacedFootprint.Offset = destination->offset;
5597
    }
5598

5599
    D3D12_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
5600
        d3d12CommandBuffer,
5601
        D3D12_RESOURCE_STATE_COPY_SOURCE,
5602
        sourceSubresource);
5603

5604
    ID3D12GraphicsCommandList_CopyTextureRegion(
5605
        d3d12CommandBuffer->graphicsCommandList,
5606
        &destinationLocation,
5607
        0,
5608
        0,
5609
        0,
5610
        &sourceLocation,
5611
        &sourceBox);
5612

5613
    D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
5614
        d3d12CommandBuffer,
5615
        D3D12_RESOURCE_STATE_COPY_SOURCE,
5616
        sourceSubresource);
5617

5618
    D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, destinationBuffer);
5619
    D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, sourceSubresource->parent);
5620

5621
    if (textureDownload != NULL) {
5622
        D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, textureDownload->temporaryBuffer);
5623

5624
        if (d3d12CommandBuffer->textureDownloadCount >= d3d12CommandBuffer->textureDownloadCapacity) {
5625
            d3d12CommandBuffer->textureDownloadCapacity *= 2;
5626
            d3d12CommandBuffer->textureDownloads = (D3D12TextureDownload **)SDL_realloc(
5627
                d3d12CommandBuffer->textureDownloads,
5628
                d3d12CommandBuffer->textureDownloadCapacity * sizeof(D3D12TextureDownload *));
5629
        }
5630

5631
        d3d12CommandBuffer->textureDownloads[d3d12CommandBuffer->textureDownloadCount] = textureDownload;
5632
        d3d12CommandBuffer->textureDownloadCount += 1;
5633

5634
        D3D12_INTERNAL_ReleaseBuffer(d3d12CommandBuffer->renderer, textureDownload->temporaryBuffer);
5635
    }
5636
}
5637

5638
static void D3D12_DownloadFromBuffer(
5639
    SDL_GPUCommandBuffer *commandBuffer,
5640
    SDL_GPUBufferRegion *source,
5641
    SDL_GPUTransferBufferLocation *destination)
5642
{
5643
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
5644
    D3D12BufferContainer *sourceContainer = (D3D12BufferContainer *)source->buffer;
5645
    D3D12BufferContainer *destinationContainer = (D3D12BufferContainer *)destination->transferBuffer;
5646

5647
    D3D12Buffer *sourceBuffer = sourceContainer->activeBuffer;
5648
    D3D12_INTERNAL_BufferTransitionFromDefaultUsage(
5649
        d3d12CommandBuffer,
5650
        D3D12_RESOURCE_STATE_COPY_SOURCE,
5651
        sourceBuffer);
5652

5653
    D3D12Buffer *destinationBuffer = destinationContainer->activeBuffer;
5654

5655
    ID3D12GraphicsCommandList_CopyBufferRegion(
5656
        d3d12CommandBuffer->graphicsCommandList,
5657
        destinationBuffer->handle,
5658
        destination->offset,
5659
        sourceBuffer->handle,
5660
        source->offset,
5661
        source->size);
5662

5663
    D3D12_INTERNAL_BufferTransitionToDefaultUsage(
5664
        d3d12CommandBuffer,
5665
        D3D12_RESOURCE_STATE_COPY_SOURCE,
5666
        sourceBuffer);
5667

5668
    D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, sourceBuffer);
5669
    D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, destinationBuffer);
5670
}
5671

5672
static void D3D12_EndCopyPass(
5673
    SDL_GPUCommandBuffer *commandBuffer)
5674
{
5675
    // no-op
5676
    (void)commandBuffer;
5677
}
5678

5679
static void D3D12_GenerateMipmaps(
5680
    SDL_GPUCommandBuffer *commandBuffer,
5681
    SDL_GPUTexture *texture)
5682
{
5683
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
5684
    D3D12Renderer *renderer = d3d12CommandBuffer->renderer;
5685
    D3D12TextureContainer *container = (D3D12TextureContainer *)texture;
5686
    SDL_GPUGraphicsPipeline *blitPipeline;
5687
    SDL_GPUBlitRegion srcRegion, dstRegion;
5688

5689
    blitPipeline = SDL_GPU_FetchBlitPipeline(
5690
        renderer->sdlGPUDevice,
5691
        container->header.info.type,
5692
        container->header.info.format,
5693
        renderer->blitVertexShader,
5694
        renderer->blitFrom2DShader,
5695
        renderer->blitFrom2DArrayShader,
5696
        renderer->blitFrom3DShader,
5697
        renderer->blitFromCubeShader,
5698
        &renderer->blitPipelines,
5699
        &renderer->blitPipelineCount,
5700
        &renderer->blitPipelineCapacity);
5701

5702
    if (blitPipeline == NULL) {
5703
        SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not fetch blit pipeline");
5704
        return;
5705
    }
5706

5707
    // We have to do this one subresource at a time
5708
    for (Uint32 layerOrDepthIndex = 0; layerOrDepthIndex < container->header.info.layerCountOrDepth; layerOrDepthIndex += 1) {
5709
        for (Uint32 levelIndex = 1; levelIndex < container->header.info.levelCount; levelIndex += 1) {
5710

5711
            srcRegion.texture = texture;
5712
            srcRegion.mipLevel = levelIndex - 1;
5713
            srcRegion.layerOrDepthPlane = layerOrDepthIndex;
5714
            srcRegion.x = 0;
5715
            srcRegion.y = 0;
5716
            srcRegion.w = container->header.info.width >> (levelIndex - 1);
5717
            srcRegion.h = container->header.info.height >> (levelIndex - 1);
5718

5719
            dstRegion.texture = texture;
5720
            dstRegion.mipLevel = levelIndex;
5721
            dstRegion.layerOrDepthPlane = layerOrDepthIndex;
5722
            dstRegion.x = 0;
5723
            dstRegion.y = 0;
5724
            dstRegion.w = container->header.info.width >> levelIndex;
5725
            dstRegion.h = container->header.info.height >> levelIndex;
5726

5727
            SDL_BlitGPUTexture(
5728
                commandBuffer,
5729
                &srcRegion,
5730
                &dstRegion,
5731
                SDL_FLIP_NONE,
5732
                SDL_GPU_FILTER_LINEAR,
5733
                false);
5734
        }
5735
    }
5736

5737
    D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, container->activeTexture);
5738
}
5739

5740
static void D3D12_Blit(
5741
    SDL_GPUCommandBuffer *commandBuffer,
5742
    SDL_GPUBlitRegion *source,
5743
    SDL_GPUBlitRegion *destination,
5744
    SDL_FlipMode flipMode,
5745
    SDL_GPUFilter filterMode,
5746
    bool cycle)
5747
{
5748
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
5749
    D3D12Renderer *renderer = (D3D12Renderer *)d3d12CommandBuffer->renderer;
5750

5751
    SDL_GPU_BlitCommon(
5752
        commandBuffer,
5753
        source,
5754
        destination,
5755
        flipMode,
5756
        filterMode,
5757
        cycle,
5758
        renderer->blitLinearSampler,
5759
        renderer->blitNearestSampler,
5760
        renderer->blitVertexShader,
5761
        renderer->blitFrom2DShader,
5762
        renderer->blitFrom2DArrayShader,
5763
        renderer->blitFrom3DShader,
5764
        renderer->blitFromCubeShader,
5765
        &renderer->blitPipelines,
5766
        &renderer->blitPipelineCount,
5767
        &renderer->blitPipelineCapacity);
5768
}
5769

5770
// Submission/Presentation
5771

5772
static D3D12WindowData *D3D12_INTERNAL_FetchWindowData(
5773
    SDL_Window *window)
5774
{
5775
    SDL_PropertiesID properties = SDL_GetWindowProperties(window);
5776
    return (D3D12WindowData *)SDL_GetPointerProperty(properties, WINDOW_PROPERTY_DATA, NULL);
5777
}
5778

5779
static bool D3D12_SupportsSwapchainComposition(
5780
    SDL_GPURenderer *driverData,
5781
    SDL_Window *window,
5782
    SDL_GPUSwapchainComposition swapchainComposition)
5783
{
5784
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
5785
    // FIXME: HDR support would be nice to add, but it seems complicated...
5786
    return swapchainComposition == SDL_GPU_SWAPCHAINCOMPOSITION_SDR ||
5787
           swapchainComposition == SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR;
5788
#else
5789
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
5790
    DXGI_FORMAT format;
5791
    D3D12_FEATURE_DATA_FORMAT_SUPPORT formatSupport;
5792
    Uint32 colorSpaceSupport;
5793
    HRESULT res;
5794

5795
    format = SwapchainCompositionToTextureFormat[swapchainComposition];
5796

5797
    formatSupport.Format = format;
5798
    res = ID3D12Device_CheckFeatureSupport(
5799
        renderer->device,
5800
        D3D12_FEATURE_FORMAT_SUPPORT,
5801
        &formatSupport,
5802
        sizeof(formatSupport));
5803
    if (FAILED(res)) {
5804
        // Format is apparently unknown
5805
        return false;
5806
    }
5807

5808
    if (!(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_DISPLAY)) {
5809
        return false;
5810
    }
5811

5812
    D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window);
5813
    if (windowData == NULL) {
5814
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Must claim window before querying swapchain composition support!");
5815
        return false;
5816
    }
5817

5818
    // Check the color space support if necessary
5819
    if (swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_SDR) {
5820
        IDXGISwapChain3_CheckColorSpaceSupport(
5821
            windowData->swapchain,
5822
            SwapchainCompositionToColorSpace[swapchainComposition],
5823
            &colorSpaceSupport);
5824

5825
        if (!(colorSpaceSupport & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) {
5826
            return false;
5827
        }
5828
    }
5829
#endif
5830

5831
    return true;
5832
}
5833

5834
static bool D3D12_SupportsPresentMode(
5835
    SDL_GPURenderer *driverData,
5836
    SDL_Window *window,
5837
    SDL_GPUPresentMode presentMode)
5838
{
5839
    (void)driverData;
5840
    (void)window;
5841

5842
    switch (presentMode) {
5843
    case SDL_GPU_PRESENTMODE_IMMEDIATE:
5844
    case SDL_GPU_PRESENTMODE_VSYNC:
5845
        return true;
5846
    case SDL_GPU_PRESENTMODE_MAILBOX:
5847
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
5848
        return false;
5849
#else
5850
        return true;
5851
#endif
5852
    default:
5853
        SDL_assert(!"Unrecognized present mode");
5854
        return false;
5855
    }
5856
}
5857

5858
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
5859
static bool D3D12_INTERNAL_CreateSwapchain(
5860
    D3D12Renderer *renderer,
5861
    D3D12WindowData *windowData,
5862
    SDL_GPUSwapchainComposition swapchainComposition,
5863
    SDL_GPUPresentMode presentMode)
5864
{
5865
    int width, height;
5866
    SDL_GPUTextureCreateInfo createInfo;
5867
    D3D12Texture *texture;
5868

5869
    // Get the swapchain size
5870
    SDL_GetWindowSize(windowData->window, &width, &height);
5871

5872
    // Create the swapchain textures
5873
    SDL_zero(createInfo);
5874
    createInfo.type = SDL_GPU_TEXTURETYPE_2D;
5875
    createInfo.width = width;
5876
    createInfo.height = height;
5877
    createInfo.format = SwapchainCompositionToSDLTextureFormat[swapchainComposition];
5878
    createInfo.usageFlags = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
5879
    createInfo.layerCountOrDepth = 1;
5880
    createInfo.levelCount = 1;
5881

5882
    for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
5883
        texture = D3D12_INTERNAL_CreateTexture(renderer, &createInfo, true);
5884
        texture->container = &windowData->textureContainers[i];
5885
        windowData->textureContainers[i].activeTexture = texture;
5886
        windowData->textureContainers[i].canBeCycled = false;
5887
        windowData->textureContainers[i].header.info = createInfo;
5888
        windowData->textureContainers[i].textureCapacity = 1;
5889
        windowData->textureContainers[i].textureCount = 1;
5890
        windowData->textureContainers[i].textures = &windowData->textureContainers[i].activeTexture;
5891
    }
5892

5893
    // Initialize the swapchain data
5894
    windowData->presentMode = presentMode;
5895
    windowData->swapchainComposition = swapchainComposition;
5896
    windowData->swapchainColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
5897
    windowData->frameCounter = 0;
5898
    windowData->swapchainWidth = width;
5899
    windowData->swapchainHeight = height;
5900

5901
    // Precache blit pipelines for the swapchain format
5902
    for (Uint32 i = 0; i < 4; i += 1) {
5903
        SDL_GPU_FetchBlitPipeline(
5904
            renderer->sdlGPUDevice,
5905
            (SDL_GPUTextureType)i,
5906
            createInfo.format,
5907
            renderer->blitVertexShader,
5908
            renderer->blitFrom2DShader,
5909
            renderer->blitFrom2DArrayShader,
5910
            renderer->blitFrom3DShader,
5911
            renderer->blitFromCubeShader,
5912
            &renderer->blitPipelines,
5913
            &renderer->blitPipelineCount,
5914
            &renderer->blitPipelineCapacity);
5915
    }
5916

5917
    return true;
5918
}
5919

5920
static void D3D12_INTERNAL_DestroySwapchain(
5921
    D3D12Renderer *renderer,
5922
    D3D12WindowData *windowData)
5923
{
5924
    renderer->commandQueue->PresentX(0, NULL, NULL);
5925
    for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
5926
        D3D12_INTERNAL_DestroyTexture(
5927
            renderer,
5928
            windowData->textureContainers[i].activeTexture);
5929
    }
5930
}
5931

5932
static bool D3D12_INTERNAL_ResizeSwapchainIfNeeded(
5933
    D3D12Renderer *renderer,
5934
    D3D12WindowData *windowData)
5935
{
5936
    int w, h;
5937
    SDL_GetWindowSize(windowData->window, &w, &h);
5938

5939
    if (w != windowData->swapchainWidth || h != windowData->swapchainHeight) {
5940
        // Wait so we don't release in-flight views
5941
        D3D12_Wait((SDL_GPURenderer *)renderer);
5942

5943
        // Present a black screen
5944
        renderer->commandQueue->PresentX(0, NULL, NULL);
5945

5946
        // Clean up the previous swapchain textures
5947
        for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
5948
            D3D12_INTERNAL_DestroyTexture(
5949
                renderer,
5950
                windowData->textureContainers[i].activeTexture);
5951
        }
5952

5953
        // Create a new swapchain
5954
        D3D12_INTERNAL_CreateSwapchain(
5955
            renderer,
5956
            windowData,
5957
            windowData->swapchainComposition,
5958
            windowData->presentMode);
5959
    }
5960

5961
    return true;
5962
}
5963
#else
5964
static bool D3D12_INTERNAL_InitializeSwapchainTexture(
5965
    D3D12Renderer *renderer,
5966
    IDXGISwapChain3 *swapchain,
5967
    SDL_GPUSwapchainComposition composition,
5968
    Uint32 index,
5969
    D3D12TextureContainer *pTextureContainer)
5970
{
5971
    D3D12Texture *pTexture;
5972
    ID3D12Resource *swapchainTexture;
5973
    D3D12_RESOURCE_DESC textureDesc;
5974
    D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
5975
    D3D12_RENDER_TARGET_VIEW_DESC rtvDesc;
5976
    DXGI_FORMAT swapchainFormat = SwapchainCompositionToTextureFormat[composition];
5977
    HRESULT res;
5978

5979
    res = IDXGISwapChain_GetBuffer(
5980
        swapchain,
5981
        index,
5982
        D3D_GUID(D3D_IID_ID3D12Resource),
5983
        (void **)&swapchainTexture);
5984
    ERROR_CHECK_RETURN("Could not get buffer from swapchain!", 0);
5985

5986
    pTexture = (D3D12Texture *)SDL_calloc(1, sizeof(D3D12Texture));
5987
    if (!pTexture) {
5988
        ID3D12Resource_Release(swapchainTexture);
5989
        return false;
5990
    }
5991
    pTexture->resource = NULL; // This will be set in AcquireSwapchainTexture
5992
    SDL_AtomicSet(&pTexture->referenceCount, 0);
5993
    pTexture->subresourceCount = 1;
5994
    pTexture->subresources = (D3D12TextureSubresource *)SDL_calloc(1, sizeof(D3D12TextureSubresource));
5995
    if (!pTexture->subresources) {
5996
        SDL_free(pTexture);
5997
        ID3D12Resource_Release(swapchainTexture);
5998
        return false;
5999
    }
6000
    pTexture->subresources[0].rtvHandles = SDL_calloc(1, sizeof(D3D12CPUDescriptor));
6001
    pTexture->subresources[0].uavHandle.heap = NULL;
6002
    pTexture->subresources[0].dsvHandle.heap = NULL;
6003
    pTexture->subresources[0].parent = pTexture;
6004
    pTexture->subresources[0].index = 0;
6005
    pTexture->subresources[0].layer = 0;
6006
    pTexture->subresources[0].depth = 1;
6007
    pTexture->subresources[0].level = 0;
6008

6009
    ID3D12Resource_GetDesc(swapchainTexture, &textureDesc);
6010
    pTextureContainer->header.info.width = (Uint32)textureDesc.Width;
6011
    pTextureContainer->header.info.height = (Uint32)textureDesc.Height;
6012
    pTextureContainer->header.info.layerCountOrDepth = 1;
6013
    pTextureContainer->header.info.levelCount = 1;
6014
    pTextureContainer->header.info.type = SDL_GPU_TEXTURETYPE_2D;
6015
    pTextureContainer->header.info.usageFlags = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
6016
    pTextureContainer->header.info.sampleCount = SDL_GPU_SAMPLECOUNT_1;
6017
    pTextureContainer->header.info.format = SwapchainCompositionToSDLTextureFormat[composition];
6018

6019
    pTextureContainer->debugName = NULL;
6020
    pTextureContainer->textures = (D3D12Texture **)SDL_calloc(1, sizeof(D3D12Texture *));
6021
    if (!pTextureContainer->textures) {
6022
        SDL_free(pTexture->subresources);
6023
        SDL_free(pTexture);
6024
        ID3D12Resource_Release(swapchainTexture);
6025
        return false;
6026
    }
6027

6028
    pTextureContainer->textureCapacity = 1;
6029
    pTextureContainer->textureCount = 1;
6030
    pTextureContainer->textures[0] = pTexture;
6031
    pTextureContainer->activeTexture = pTexture;
6032
    pTextureContainer->canBeCycled = false;
6033

6034
    pTexture->container = pTextureContainer;
6035
    pTexture->containerIndex = 0;
6036

6037
    // Create the SRV for the swapchain
6038
    D3D12_INTERNAL_AssignCpuDescriptorHandle(
6039
        renderer,
6040
        D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
6041
        &pTexture->srvHandle);
6042

6043
    srvDesc.Format = SwapchainCompositionToTextureFormat[composition];
6044
    srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
6045
    srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
6046
    srvDesc.Texture2D.MipLevels = 1;
6047
    srvDesc.Texture2D.MostDetailedMip = 0;
6048
    srvDesc.Texture2D.ResourceMinLODClamp = 0;
6049
    srvDesc.Texture2D.PlaneSlice = 0;
6050

6051
    ID3D12Device_CreateShaderResourceView(
6052
        renderer->device,
6053
        swapchainTexture,
6054
        &srvDesc,
6055
        pTexture->srvHandle.cpuHandle);
6056

6057
    // Create the RTV for the swapchain
6058
    D3D12_INTERNAL_AssignCpuDescriptorHandle(
6059
        renderer,
6060
        D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
6061
        &pTexture->subresources[0].rtvHandles[0]);
6062

6063
    rtvDesc.Format = (composition == SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR) ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : swapchainFormat;
6064
    rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
6065
    rtvDesc.Texture2D.MipSlice = 0;
6066
    rtvDesc.Texture2D.PlaneSlice = 0;
6067

6068
    ID3D12Device_CreateRenderTargetView(
6069
        renderer->device,
6070
        swapchainTexture,
6071
        &rtvDesc,
6072
        pTexture->subresources[0].rtvHandles[0].cpuHandle);
6073

6074
    ID3D12Resource_Release(swapchainTexture);
6075

6076
    return true;
6077
}
6078

6079
static bool D3D12_INTERNAL_ResizeSwapchainIfNeeded(
6080
    D3D12Renderer *renderer,
6081
    D3D12WindowData *windowData)
6082
{
6083
    DXGI_SWAP_CHAIN_DESC swapchainDesc;
6084
    int w, h;
6085

6086
    IDXGISwapChain_GetDesc(windowData->swapchain, &swapchainDesc);
6087
    SDL_GetWindowSize(windowData->window, &w, &h);
6088

6089
    if (w != swapchainDesc.BufferDesc.Width || h != swapchainDesc.BufferDesc.Height) {
6090
        // Wait so we don't release in-flight views
6091
        D3D12_Wait((SDL_GPURenderer *)renderer);
6092

6093
        // Release views and clean up
6094
        for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
6095
            D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
6096
                renderer,
6097
                &windowData->textureContainers[i].activeTexture->srvHandle);
6098
            D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
6099
                renderer,
6100
                &windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles[0]);
6101

6102
            SDL_free(windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles);
6103
            SDL_free(windowData->textureContainers[i].activeTexture->subresources);
6104
            SDL_free(windowData->textureContainers[i].activeTexture);
6105
            SDL_free(windowData->textureContainers[i].textures);
6106
        }
6107

6108
        // Resize the swapchain
6109
        HRESULT res = IDXGISwapChain_ResizeBuffers(
6110
            windowData->swapchain,
6111
            0, // Keep buffer count the same
6112
            w,
6113
            h,
6114
            DXGI_FORMAT_UNKNOWN, // Keep the old format
6115
            renderer->supportsTearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
6116
        ERROR_CHECK_RETURN("Could not resize swapchain buffers", 0)
6117

6118
        // Create texture object for the swapchain
6119
        for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
6120
            if (!D3D12_INTERNAL_InitializeSwapchainTexture(
6121
                    renderer,
6122
                    windowData->swapchain,
6123
                    windowData->swapchainComposition,
6124
                    i,
6125
                    &windowData->textureContainers[i])) {
6126
                return false;
6127
            }
6128
        }
6129
    }
6130

6131
    return true;
6132
}
6133

6134
static void D3D12_INTERNAL_DestroySwapchain(
6135
    D3D12Renderer *renderer,
6136
    D3D12WindowData *windowData)
6137
{
6138
    // Release views and clean up
6139
    for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
6140
        D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
6141
            renderer,
6142
            &windowData->textureContainers[i].activeTexture->srvHandle);
6143
        D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
6144
            renderer,
6145
            &windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles[0]);
6146

6147
        SDL_free(windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles);
6148
        SDL_free(windowData->textureContainers[i].activeTexture->subresources);
6149
        SDL_free(windowData->textureContainers[i].activeTexture);
6150
        SDL_free(windowData->textureContainers[i].textures);
6151
    }
6152

6153
    IDXGISwapChain_Release(windowData->swapchain);
6154
    windowData->swapchain = NULL;
6155
}
6156

6157
static bool D3D12_INTERNAL_CreateSwapchain(
6158
    D3D12Renderer *renderer,
6159
    D3D12WindowData *windowData,
6160
    SDL_GPUSwapchainComposition swapchainComposition,
6161
    SDL_GPUPresentMode presentMode)
6162
{
6163
    HWND dxgiHandle;
6164
    int width, height;
6165
    DXGI_SWAP_CHAIN_DESC1 swapchainDesc;
6166
    DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullscreenDesc;
6167
    DXGI_FORMAT swapchainFormat;
6168
    IDXGIFactory1 *pParent;
6169
    IDXGISwapChain1 *swapchain;
6170
    IDXGISwapChain3 *swapchain3;
6171
    HRESULT res;
6172

6173
    // Get the DXGI handle
6174
#ifdef _WIN32
6175
    dxgiHandle = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(windowData->window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
6176
#else
6177
    dxgiHandle = (HWND)windowData->window;
6178
#endif
6179

6180
    // Get the window size
6181
    SDL_GetWindowSize(windowData->window, &width, &height);
6182

6183
    swapchainFormat = SwapchainCompositionToTextureFormat[swapchainComposition];
6184

6185
    // Initialize the swapchain buffer descriptor
6186
    swapchainDesc.Width = 0;
6187
    swapchainDesc.Height = 0;
6188
    swapchainDesc.Format = swapchainFormat;
6189
    swapchainDesc.SampleDesc.Count = 1;
6190
    swapchainDesc.SampleDesc.Quality = 0;
6191
    swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
6192
    swapchainDesc.BufferCount = MAX_FRAMES_IN_FLIGHT;
6193
    swapchainDesc.Scaling = DXGI_SCALING_STRETCH;
6194
    swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
6195
    swapchainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
6196
    swapchainDesc.Flags = 0;
6197
    swapchainDesc.Stereo = 0;
6198

6199
    // Initialize the fullscreen descriptor (if needed)
6200
    fullscreenDesc.RefreshRate.Numerator = 0;
6201
    fullscreenDesc.RefreshRate.Denominator = 0;
6202
    fullscreenDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
6203
    fullscreenDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
6204
    fullscreenDesc.Windowed = true;
6205

6206
    if (renderer->supportsTearing) {
6207
        swapchainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
6208
    } else {
6209
        swapchainDesc.Flags = 0;
6210
    }
6211

6212
#ifndef SDL_PLATFORM_WINRT
6213
    if (!IsWindow(dxgiHandle)) {
6214
        return false;
6215
    }
6216
#endif
6217

6218
    // Create the swapchain!
6219
    res = IDXGIFactory4_CreateSwapChainForHwnd(
6220
        renderer->factory,
6221
        (IUnknown *)renderer->commandQueue,
6222
        dxgiHandle,
6223
        &swapchainDesc,
6224
        &fullscreenDesc,
6225
        NULL,
6226
        &swapchain);
6227
    ERROR_CHECK_RETURN("Could not create swapchain", 0);
6228

6229
    res = IDXGISwapChain1_QueryInterface(
6230
        swapchain,
6231
        D3D_GUID(D3D_IID_IDXGISwapChain3),
6232
        (void **)&swapchain3);
6233
    IDXGISwapChain1_Release(swapchain);
6234
    ERROR_CHECK_RETURN("Could not create IDXGISwapChain3", 0);
6235

6236
    if (swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_SDR) {
6237
        // Support already verified if we hit this block
6238
        IDXGISwapChain3_SetColorSpace1(
6239
            swapchain3,
6240
            SwapchainCompositionToColorSpace[swapchainComposition]);
6241
    }
6242

6243
    /*
6244
     * The swapchain's parent is a separate factory from the factory that
6245
     * we used to create the swapchain, and only that parent can be used to
6246
     * set the window association. Trying to set an association on our factory
6247
     * will silently fail and doesn't even verify arguments or return errors.
6248
     * See https://gamedev.net/forums/topic/634235-dxgidisabling-altenter/4999955/
6249
     */
6250
    res = IDXGISwapChain3_GetParent(
6251
        swapchain3,
6252
        D3D_GUID(D3D_IID_IDXGIFactory1),
6253
        (void **)&pParent);
6254
    if (FAILED(res)) {
6255
        SDL_LogWarn(
6256
            SDL_LOG_CATEGORY_APPLICATION,
6257
            "Could not get swapchain parent! Error Code: " HRESULT_FMT,
6258
            res);
6259
    } else {
6260
        // Disable DXGI window crap
6261
        res = IDXGIFactory1_MakeWindowAssociation(
6262
            pParent,
6263
            dxgiHandle,
6264
            DXGI_MWA_NO_WINDOW_CHANGES);
6265
        if (FAILED(res)) {
6266
            SDL_LogWarn(
6267
                SDL_LOG_CATEGORY_APPLICATION,
6268
                "MakeWindowAssociation failed! Error Code: " HRESULT_FMT,
6269
                res);
6270
        }
6271

6272
        // We're done with the parent now
6273
        IDXGIFactory1_Release(pParent);
6274
    }
6275

6276
    // Initialize the swapchain data
6277
    windowData->swapchain = swapchain3;
6278
    windowData->presentMode = presentMode;
6279
    windowData->swapchainComposition = swapchainComposition;
6280
    windowData->swapchainColorSpace = SwapchainCompositionToColorSpace[swapchainComposition];
6281
    windowData->frameCounter = 0;
6282

6283
    // Precache blit pipelines for the swapchain format
6284
    for (Uint32 i = 0; i < 4; i += 1) {
6285
        SDL_GPU_FetchBlitPipeline(
6286
            renderer->sdlGPUDevice,
6287
            (SDL_GPUTextureType)i,
6288
            SwapchainCompositionToSDLTextureFormat[swapchainComposition],
6289
            renderer->blitVertexShader,
6290
            renderer->blitFrom2DShader,
6291
            renderer->blitFrom2DArrayShader,
6292
            renderer->blitFrom3DShader,
6293
            renderer->blitFromCubeShader,
6294
            &renderer->blitPipelines,
6295
            &renderer->blitPipelineCount,
6296
            &renderer->blitPipelineCapacity);
6297
    }
6298

6299
    /* If a you are using a FLIP model format you can't create the swapchain as DXGI_FORMAT_B8G8R8A8_UNORM_SRGB.
6300
     * 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
6301
     */
6302
    for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
6303
        if (!D3D12_INTERNAL_InitializeSwapchainTexture(
6304
                renderer,
6305
                swapchain3,
6306
                swapchainComposition,
6307
                i,
6308
                &windowData->textureContainers[i])) {
6309
            IDXGISwapChain3_Release(swapchain3);
6310
            return false;
6311
        }
6312
    }
6313

6314
    return true;
6315
}
6316
#endif
6317

6318
static bool D3D12_ClaimWindow(
6319
    SDL_GPURenderer *driverData,
6320
    SDL_Window *window)
6321
{
6322
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
6323
    D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window);
6324

6325
    if (windowData == NULL) {
6326
        windowData = (D3D12WindowData *)SDL_calloc(1, sizeof(D3D12WindowData));
6327
        if (!windowData) {
6328
            return false;
6329
        }
6330
        windowData->window = window;
6331

6332
        if (D3D12_INTERNAL_CreateSwapchain(renderer, windowData, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC)) {
6333
            SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData);
6334

6335
            SDL_LockMutex(renderer->windowLock);
6336

6337
            if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity) {
6338
                renderer->claimedWindowCapacity *= 2;
6339
                renderer->claimedWindows = (D3D12WindowData **)SDL_realloc(
6340
                    renderer->claimedWindows,
6341
                    renderer->claimedWindowCapacity * sizeof(D3D12WindowData *));
6342
            }
6343
            renderer->claimedWindows[renderer->claimedWindowCount] = windowData;
6344
            renderer->claimedWindowCount += 1;
6345

6346
            SDL_UnlockMutex(renderer->windowLock);
6347

6348
            return true;
6349
        } else {
6350
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not create swapchain, failed to claim window!");
6351
            SDL_free(windowData);
6352
            return false;
6353
        }
6354
    } else {
6355
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Window already claimed!");
6356
        return false;
6357
    }
6358
}
6359

6360
static void D3D12_ReleaseWindow(
6361
    SDL_GPURenderer *driverData,
6362
    SDL_Window *window)
6363
{
6364
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
6365
    D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window);
6366

6367
    if (windowData == NULL) {
6368
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Window already unclaimed!");
6369
        return;
6370
    }
6371

6372
    D3D12_Wait(driverData);
6373

6374
    for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
6375
        if (windowData->inFlightFences[i] != NULL) {
6376
            D3D12_ReleaseFence(
6377
                driverData,
6378
                (SDL_GPUFence *)windowData->inFlightFences[i]);
6379
            windowData->inFlightFences[i] = NULL;
6380
        }
6381
    }
6382

6383
    D3D12_INTERNAL_DestroySwapchain(renderer, windowData);
6384

6385
    SDL_LockMutex(renderer->windowLock);
6386
    for (Uint32 i = 0; i < renderer->claimedWindowCount; i += 1) {
6387
        if (renderer->claimedWindows[i]->window == window) {
6388
            renderer->claimedWindows[i] = renderer->claimedWindows[renderer->claimedWindowCount - 1];
6389
            renderer->claimedWindowCount -= 1;
6390
            break;
6391
        }
6392
    }
6393
    SDL_UnlockMutex(renderer->windowLock);
6394

6395
    SDL_free(windowData);
6396
    SDL_ClearProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA);
6397
}
6398

6399
static bool D3D12_SetSwapchainParameters(
6400
    SDL_GPURenderer *driverData,
6401
    SDL_Window *window,
6402
    SDL_GPUSwapchainComposition swapchainComposition,
6403
    SDL_GPUPresentMode presentMode)
6404
{
6405
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
6406
    D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window);
6407

6408
    if (windowData == NULL) {
6409
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Cannot set swapchain parameters on unclaimed window!");
6410
        return false;
6411
    }
6412

6413
    if (!D3D12_SupportsSwapchainComposition(driverData, window, swapchainComposition)) {
6414
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Swapchain composition not supported!");
6415
        return false;
6416
    }
6417

6418
    if (!D3D12_SupportsPresentMode(driverData, window, presentMode)) {
6419
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Present mode not supported!");
6420
        return false;
6421
    }
6422

6423
    if (
6424
        swapchainComposition != windowData->swapchainComposition ||
6425
        presentMode != windowData->presentMode) {
6426
        D3D12_Wait(driverData);
6427

6428
        // Recreate the swapchain
6429
        D3D12_INTERNAL_DestroySwapchain(
6430
            renderer,
6431
            windowData);
6432

6433
        return D3D12_INTERNAL_CreateSwapchain(
6434
            renderer,
6435
            windowData,
6436
            swapchainComposition,
6437
            presentMode);
6438
    }
6439

6440
    return true;
6441
}
6442

6443
static SDL_GPUTextureFormat D3D12_GetSwapchainTextureFormat(
6444
    SDL_GPURenderer *driverData,
6445
    SDL_Window *window)
6446
{
6447
    D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window);
6448

6449
    if (windowData == NULL) {
6450
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Cannot get swapchain format, window has not been claimed!");
6451
        return SDL_GPU_TEXTUREFORMAT_INVALID;
6452
    }
6453

6454
    return windowData->textureContainers[windowData->frameCounter].header.info.format;
6455
}
6456

6457
static D3D12Fence *D3D12_INTERNAL_AcquireFence(
6458
    D3D12Renderer *renderer)
6459
{
6460
    D3D12Fence *fence;
6461
    ID3D12Fence *handle;
6462
    HRESULT res;
6463

6464
    SDL_LockMutex(renderer->fenceLock);
6465

6466
    if (renderer->availableFenceCount == 0) {
6467
        res = ID3D12Device_CreateFence(
6468
            renderer->device,
6469
            D3D12_FENCE_UNSIGNALED_VALUE,
6470
            D3D12_FENCE_FLAG_NONE,
6471
            D3D_GUID(D3D_IID_ID3D12Fence),
6472
            (void **)&handle);
6473
        if (FAILED(res)) {
6474
            D3D12_INTERNAL_LogError(renderer->device, "Failed to create fence!", res);
6475
            SDL_UnlockMutex(renderer->fenceLock);
6476
            return NULL;
6477
        }
6478

6479
        fence = (D3D12Fence *)SDL_calloc(1, sizeof(D3D12Fence));
6480
        if (!fence) {
6481
            ID3D12Fence_Release(handle);
6482
            SDL_UnlockMutex(renderer->fenceLock);
6483
            return NULL;
6484
        }
6485
        fence->handle = handle;
6486
        fence->event = CreateEventEx(NULL, 0, 0, EVENT_ALL_ACCESS);
6487
        SDL_AtomicSet(&fence->referenceCount, 0);
6488
    } else {
6489
        fence = renderer->availableFences[renderer->availableFenceCount - 1];
6490
        renderer->availableFenceCount -= 1;
6491
        ID3D12Fence_Signal(fence->handle, D3D12_FENCE_UNSIGNALED_VALUE);
6492
    }
6493

6494
    SDL_UnlockMutex(renderer->fenceLock);
6495

6496
    (void)SDL_AtomicIncRef(&fence->referenceCount);
6497
    return fence;
6498
}
6499

6500
static void D3D12_INTERNAL_AllocateCommandBuffer(
6501
    D3D12Renderer *renderer)
6502
{
6503
    D3D12CommandBuffer *commandBuffer;
6504
    HRESULT res;
6505
    ID3D12CommandAllocator *commandAllocator;
6506
    ID3D12GraphicsCommandList *commandList;
6507

6508
    commandBuffer = (D3D12CommandBuffer *)SDL_calloc(1, sizeof(D3D12CommandBuffer));
6509
    if (!commandBuffer) {
6510
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create ID3D12CommandList. Out of Memory");
6511
        return;
6512
    }
6513

6514
    res = ID3D12Device_CreateCommandAllocator(
6515
        renderer->device,
6516
        D3D12_COMMAND_LIST_TYPE_DIRECT,
6517
        D3D_GUID(D3D_IID_ID3D12CommandAllocator),
6518
        (void **)&commandAllocator);
6519
    if (FAILED(res)) {
6520
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create ID3D12CommandAllocator");
6521
        D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer);
6522
        return;
6523
    }
6524
    commandBuffer->commandAllocator = commandAllocator;
6525

6526
    res = ID3D12Device_CreateCommandList(
6527
        renderer->device,
6528
        0,
6529
        D3D12_COMMAND_LIST_TYPE_DIRECT,
6530
        commandAllocator,
6531
        NULL,
6532
        D3D_GUID(D3D_IID_ID3D12GraphicsCommandList),
6533
        (void **)&commandList);
6534

6535
    if (FAILED(res)) {
6536
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create ID3D12CommandList");
6537
        D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer);
6538
        return;
6539
    }
6540
    commandBuffer->graphicsCommandList = commandList;
6541

6542
    commandBuffer->renderer = renderer;
6543
    commandBuffer->inFlightFence = NULL;
6544

6545
    // Window handling
6546
    commandBuffer->presentDataCapacity = 1;
6547
    commandBuffer->presentDataCount = 0;
6548
    commandBuffer->presentDatas = (D3D12PresentData *)SDL_calloc(
6549
        commandBuffer->presentDataCapacity, sizeof(D3D12PresentData));
6550

6551
    // Resource tracking
6552
    commandBuffer->usedTextureCapacity = 4;
6553
    commandBuffer->usedTextureCount = 0;
6554
    commandBuffer->usedTextures = (D3D12Texture **)SDL_calloc(
6555
        commandBuffer->usedTextureCapacity, sizeof(D3D12Texture *));
6556

6557
    commandBuffer->usedBufferCapacity = 4;
6558
    commandBuffer->usedBufferCount = 0;
6559
    commandBuffer->usedBuffers = (D3D12Buffer **)SDL_calloc(
6560
        commandBuffer->usedBufferCapacity, sizeof(D3D12Buffer *));
6561

6562
    commandBuffer->usedSamplerCapacity = 4;
6563
    commandBuffer->usedSamplerCount = 0;
6564
    commandBuffer->usedSamplers = (D3D12Sampler **)SDL_calloc(
6565
        commandBuffer->usedSamplerCapacity, sizeof(D3D12Sampler *));
6566

6567
    commandBuffer->usedGraphicsPipelineCapacity = 4;
6568
    commandBuffer->usedGraphicsPipelineCount = 0;
6569
    commandBuffer->usedGraphicsPipelines = (D3D12GraphicsPipeline **)SDL_calloc(
6570
        commandBuffer->usedGraphicsPipelineCapacity, sizeof(D3D12GraphicsPipeline *));
6571

6572
    commandBuffer->usedComputePipelineCapacity = 4;
6573
    commandBuffer->usedComputePipelineCount = 0;
6574
    commandBuffer->usedComputePipelines = (D3D12ComputePipeline **)SDL_calloc(
6575
        commandBuffer->usedComputePipelineCapacity, sizeof(D3D12ComputePipeline *));
6576

6577
    commandBuffer->usedUniformBufferCapacity = 4;
6578
    commandBuffer->usedUniformBufferCount = 0;
6579
    commandBuffer->usedUniformBuffers = (D3D12UniformBuffer **)SDL_calloc(
6580
        commandBuffer->usedUniformBufferCapacity, sizeof(D3D12UniformBuffer *));
6581

6582
    commandBuffer->textureDownloadCapacity = 4;
6583
    commandBuffer->textureDownloadCount = 0;
6584
    commandBuffer->textureDownloads = (D3D12TextureDownload **)SDL_calloc(
6585
        commandBuffer->textureDownloadCapacity, sizeof(D3D12TextureDownload *));
6586

6587
    if (
6588
        (!commandBuffer->presentDatas) ||
6589
        (!commandBuffer->usedTextures) ||
6590
        (!commandBuffer->usedBuffers) ||
6591
        (!commandBuffer->usedSamplers) ||
6592
        (!commandBuffer->usedGraphicsPipelines) ||
6593
        (!commandBuffer->usedComputePipelines) ||
6594
        (!commandBuffer->usedUniformBuffers) ||
6595
        (!commandBuffer->textureDownloads)) {
6596
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create ID3D12CommandList. Out of Memory");
6597
        D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer);
6598
        return;
6599
    }
6600

6601
    D3D12CommandBuffer **resizedAvailableCommandBuffers = (D3D12CommandBuffer **)SDL_realloc(
6602
        renderer->availableCommandBuffers,
6603
        sizeof(D3D12CommandBuffer *) * (renderer->availableCommandBufferCapacity + 1));
6604

6605
    if (!resizedAvailableCommandBuffers) {
6606
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create ID3D12CommandList. Out of Memory");
6607
        D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer);
6608
        return;
6609
    }
6610
    // Add to inactive command buffer array
6611
    renderer->availableCommandBufferCapacity += 1;
6612
    renderer->availableCommandBuffers = resizedAvailableCommandBuffers;
6613

6614
    renderer->availableCommandBuffers[renderer->availableCommandBufferCount] = commandBuffer;
6615
    renderer->availableCommandBufferCount += 1;
6616
}
6617

6618
static D3D12CommandBuffer *D3D12_INTERNAL_AcquireCommandBufferFromPool(
6619
    D3D12Renderer *renderer)
6620
{
6621
    D3D12CommandBuffer *commandBuffer;
6622

6623
    if (renderer->availableCommandBufferCount == 0) {
6624
        D3D12_INTERNAL_AllocateCommandBuffer(renderer);
6625
    }
6626

6627
    commandBuffer = renderer->availableCommandBuffers[renderer->availableCommandBufferCount - 1];
6628
    renderer->availableCommandBufferCount -= 1;
6629

6630
    return commandBuffer;
6631
}
6632

6633
static SDL_GPUCommandBuffer *D3D12_AcquireCommandBuffer(
6634
    SDL_GPURenderer *driverData)
6635
{
6636
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
6637
    D3D12CommandBuffer *commandBuffer;
6638
    ID3D12DescriptorHeap *heaps[2];
6639
    SDL_zeroa(heaps);
6640

6641
    SDL_LockMutex(renderer->acquireCommandBufferLock);
6642
    commandBuffer = D3D12_INTERNAL_AcquireCommandBufferFromPool(renderer);
6643
    SDL_UnlockMutex(renderer->acquireCommandBufferLock);
6644

6645
    if (commandBuffer == NULL) {
6646
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to acquire command buffer!");
6647
        return NULL;
6648
    }
6649

6650
    // Set the descriptor heaps!
6651
    commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] =
6652
        D3D12_INTERNAL_AcquireDescriptorHeapFromPool(commandBuffer, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
6653

6654
    if (!commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV]) {
6655
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to acquire descriptor heap!");
6656
        D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer);
6657
        return NULL;
6658
    }
6659

6660
    commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER] =
6661
        D3D12_INTERNAL_AcquireDescriptorHeapFromPool(commandBuffer, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
6662

6663
    if (!commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER]) {
6664
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to acquire descriptor heap!");
6665
        D3D12_INTERNAL_DestroyCommandBuffer(commandBuffer);
6666
        return NULL;
6667
    }
6668

6669
    heaps[0] = commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV]->handle;
6670
    heaps[1] = commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER]->handle;
6671

6672
    ID3D12GraphicsCommandList_SetDescriptorHeaps(
6673
        commandBuffer->graphicsCommandList,
6674
        2,
6675
        heaps);
6676

6677
    // Set the bind state
6678
    commandBuffer->currentGraphicsPipeline = NULL;
6679

6680
    SDL_zeroa(commandBuffer->colorAttachmentTextureSubresources);
6681
    commandBuffer->colorAttachmentTextureSubresourceCount = 0;
6682
    commandBuffer->depthStencilTextureSubresource = NULL;
6683

6684
    SDL_zeroa(commandBuffer->vertexBuffers);
6685
    SDL_zeroa(commandBuffer->vertexBufferOffsets);
6686
    commandBuffer->vertexBufferCount = 0;
6687

6688
    SDL_zeroa(commandBuffer->vertexSamplerTextures);
6689
    SDL_zeroa(commandBuffer->vertexSamplers);
6690
    SDL_zeroa(commandBuffer->vertexStorageTextures);
6691
    SDL_zeroa(commandBuffer->vertexStorageBuffers);
6692
    SDL_zeroa(commandBuffer->vertexUniformBuffers);
6693

6694
    SDL_zeroa(commandBuffer->fragmentSamplerTextures);
6695
    SDL_zeroa(commandBuffer->fragmentSamplers);
6696
    SDL_zeroa(commandBuffer->fragmentStorageTextures);
6697
    SDL_zeroa(commandBuffer->fragmentStorageBuffers);
6698
    SDL_zeroa(commandBuffer->fragmentUniformBuffers);
6699

6700
    SDL_zeroa(commandBuffer->computeReadOnlyStorageTextures);
6701
    SDL_zeroa(commandBuffer->computeReadOnlyStorageBuffers);
6702
    SDL_zeroa(commandBuffer->computeWriteOnlyStorageTextureSubresources);
6703
    SDL_zeroa(commandBuffer->computeWriteOnlyStorageBuffers);
6704
    SDL_zeroa(commandBuffer->computeUniformBuffers);
6705

6706
    commandBuffer->autoReleaseFence = true;
6707

6708
    return (SDL_GPUCommandBuffer *)commandBuffer;
6709
}
6710

6711
static SDL_GPUTexture *D3D12_AcquireSwapchainTexture(
6712
    SDL_GPUCommandBuffer *commandBuffer,
6713
    SDL_Window *window,
6714
    Uint32 *pWidth,
6715
    Uint32 *pHeight)
6716
{
6717
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
6718
    D3D12Renderer *renderer = d3d12CommandBuffer->renderer;
6719
    D3D12WindowData *windowData;
6720
    Uint32 swapchainIndex;
6721
    HRESULT res;
6722

6723
    windowData = D3D12_INTERNAL_FetchWindowData(window);
6724
    if (windowData == NULL) {
6725
        return NULL;
6726
    }
6727

6728
    res = D3D12_INTERNAL_ResizeSwapchainIfNeeded(
6729
        renderer,
6730
        windowData);
6731
    ERROR_CHECK_RETURN("Could not resize swapchain", NULL);
6732

6733
    if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
6734
        if (windowData->presentMode == SDL_GPU_PRESENTMODE_VSYNC) {
6735
            // In VSYNC mode, block until the least recent presented frame is done
6736
            D3D12_WaitForFences(
6737
                (SDL_GPURenderer *)renderer,
6738
                true,
6739
                (SDL_GPUFence **)&windowData->inFlightFences[windowData->frameCounter],
6740
                1);
6741
        } else {
6742
            if (!D3D12_QueryFence(
6743
                    (SDL_GPURenderer *)renderer,
6744
                    (SDL_GPUFence *)windowData->inFlightFences[windowData->frameCounter])) {
6745
                /*
6746
                 * In MAILBOX or IMMEDIATE mode, if the least recent fence is not signaled,
6747
                 * return NULL to indicate that rendering should be skipped
6748
                 */
6749
                return NULL;
6750
            }
6751
        }
6752

6753
        D3D12_ReleaseFence(
6754
            (SDL_GPURenderer *)renderer,
6755
            (SDL_GPUFence *)windowData->inFlightFences[windowData->frameCounter]);
6756

6757
        windowData->inFlightFences[windowData->frameCounter] = NULL;
6758
    }
6759

6760
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
6761
    // FIXME: Should this happen before the inFlightFences stuff above?
6762
    windowData->frameToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL;
6763
    renderer->device->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, NULL, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &windowData->frameToken);
6764
    swapchainIndex = windowData->frameCounter;
6765
#else
6766
    swapchainIndex = IDXGISwapChain3_GetCurrentBackBufferIndex(windowData->swapchain);
6767

6768
    // Set the handle on the windowData texture data.
6769
    res = IDXGISwapChain_GetBuffer(
6770
        windowData->swapchain,
6771
        swapchainIndex,
6772
        D3D_GUID(D3D_IID_ID3D12Resource),
6773
        (void **)&windowData->textureContainers[swapchainIndex].activeTexture->resource);
6774
    ERROR_CHECK_RETURN("Could not acquire swapchain!", NULL);
6775
#endif
6776

6777
    // Send the dimensions to the out parameters.
6778
    *pWidth = windowData->textureContainers[swapchainIndex].header.info.width;
6779
    *pHeight = windowData->textureContainers[swapchainIndex].header.info.height;
6780

6781
    // Set up presentation
6782
    if (d3d12CommandBuffer->presentDataCount == d3d12CommandBuffer->presentDataCapacity) {
6783
        d3d12CommandBuffer->presentDataCapacity += 1;
6784
        d3d12CommandBuffer->presentDatas = (D3D12PresentData *)SDL_realloc(
6785
            d3d12CommandBuffer->presentDatas,
6786
            d3d12CommandBuffer->presentDataCapacity * sizeof(D3D12PresentData));
6787
    }
6788
    d3d12CommandBuffer->presentDatas[d3d12CommandBuffer->presentDataCount].windowData = windowData;
6789
    d3d12CommandBuffer->presentDatas[d3d12CommandBuffer->presentDataCount].swapchainImageIndex = swapchainIndex;
6790
    d3d12CommandBuffer->presentDataCount += 1;
6791

6792
    // Set up resource barrier
6793
    D3D12_RESOURCE_BARRIER barrierDesc;
6794
    barrierDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
6795
    barrierDesc.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
6796
    barrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
6797
    barrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
6798
    barrierDesc.Transition.pResource = windowData->textureContainers[swapchainIndex].activeTexture->resource;
6799
    barrierDesc.Transition.Subresource = 0;
6800

6801
    ID3D12GraphicsCommandList_ResourceBarrier(
6802
        d3d12CommandBuffer->graphicsCommandList,
6803
        1,
6804
        &barrierDesc);
6805

6806
    return (SDL_GPUTexture *)&windowData->textureContainers[swapchainIndex];
6807
}
6808

6809
static void D3D12_INTERNAL_PerformPendingDestroys(D3D12Renderer *renderer)
6810
{
6811
    SDL_LockMutex(renderer->disposeLock);
6812

6813
    for (Sint32 i = renderer->buffersToDestroyCount - 1; i >= 0; i -= 1) {
6814
        if (SDL_AtomicGet(&renderer->buffersToDestroy[i]->referenceCount) == 0) {
6815
            D3D12_INTERNAL_DestroyBuffer(
6816
                renderer,
6817
                renderer->buffersToDestroy[i]);
6818

6819
            renderer->buffersToDestroy[i] = renderer->buffersToDestroy[renderer->buffersToDestroyCount - 1];
6820
            renderer->buffersToDestroyCount -= 1;
6821
        }
6822
    }
6823

6824
    for (Sint32 i = renderer->texturesToDestroyCount - 1; i >= 0; i -= 1) {
6825
        if (SDL_AtomicGet(&renderer->texturesToDestroy[i]->referenceCount) == 0) {
6826
            D3D12_INTERNAL_DestroyTexture(
6827
                renderer,
6828
                renderer->texturesToDestroy[i]);
6829

6830
            renderer->texturesToDestroy[i] = renderer->texturesToDestroy[renderer->texturesToDestroyCount - 1];
6831
            renderer->texturesToDestroyCount -= 1;
6832
        }
6833
    }
6834

6835
    for (Sint32 i = renderer->samplersToDestroyCount - 1; i >= 0; i -= 1) {
6836
        if (SDL_AtomicGet(&renderer->samplersToDestroy[i]->referenceCount) == 0) {
6837
            D3D12_INTERNAL_DestroySampler(
6838
                renderer,
6839
                renderer->samplersToDestroy[i]);
6840

6841
            renderer->samplersToDestroy[i] = renderer->samplersToDestroy[renderer->samplersToDestroyCount - 1];
6842
            renderer->samplersToDestroyCount -= 1;
6843
        }
6844
    }
6845

6846
    for (Sint32 i = renderer->graphicsPipelinesToDestroyCount - 1; i >= 0; i -= 1) {
6847
        if (SDL_AtomicGet(&renderer->graphicsPipelinesToDestroy[i]->referenceCount) == 0) {
6848
            D3D12_INTERNAL_DestroyGraphicsPipeline(
6849
                renderer->graphicsPipelinesToDestroy[i]);
6850

6851
            renderer->graphicsPipelinesToDestroy[i] = renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount - 1];
6852
            renderer->graphicsPipelinesToDestroyCount -= 1;
6853
        }
6854
    }
6855

6856
    for (Sint32 i = renderer->computePipelinesToDestroyCount - 1; i >= 0; i -= 1) {
6857
        if (SDL_AtomicGet(&renderer->computePipelinesToDestroy[i]->referenceCount) == 0) {
6858
            D3D12_INTERNAL_DestroyComputePipeline(
6859
                renderer->computePipelinesToDestroy[i]);
6860

6861
            renderer->computePipelinesToDestroy[i] = renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount - 1];
6862
            renderer->computePipelinesToDestroyCount -= 1;
6863
        }
6864
    }
6865

6866
    SDL_UnlockMutex(renderer->disposeLock);
6867
}
6868

6869
static void D3D12_INTERNAL_CopyTextureDownload(
6870
    D3D12CommandBuffer *commandBuffer,
6871
    D3D12TextureDownload *download)
6872
{
6873
    Uint8 *sourcePtr;
6874
    Uint8 *destPtr;
6875
    HRESULT res;
6876

6877
    res = ID3D12Resource_Map(
6878
        download->temporaryBuffer->handle,
6879
        0,
6880
        NULL,
6881
        (void **)&sourcePtr);
6882

6883
    if (FAILED(res)) {
6884
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to map temporary buffer!");
6885
        return;
6886
    }
6887

6888
    res = ID3D12Resource_Map(
6889
        download->destinationBuffer->handle,
6890
        0,
6891
        NULL,
6892
        (void **)&destPtr);
6893

6894
    if (FAILED(res)) {
6895
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to map destination buffer!");
6896
        return;
6897
    }
6898

6899
    for (Uint32 sliceIndex = 0; sliceIndex < download->depth; sliceIndex += 1) {
6900
        for (Uint32 rowIndex = 0; rowIndex < download->height; rowIndex += 1) {
6901
            SDL_memcpy(
6902
                destPtr + download->bufferOffset + (sliceIndex * download->bytesPerDepthSlice) + (rowIndex * download->bytesPerRow),
6903
                sourcePtr + (sliceIndex * download->height) + (rowIndex * download->alignedBytesPerRow),
6904
                download->bytesPerRow);
6905
        }
6906
    }
6907

6908
    ID3D12Resource_Unmap(
6909
        download->temporaryBuffer->handle,
6910
        0,
6911
        NULL);
6912

6913
    ID3D12Resource_Unmap(
6914
        download->destinationBuffer->handle,
6915
        0,
6916
        NULL);
6917
}
6918

6919
static void D3D12_INTERNAL_CleanCommandBuffer(
6920
    D3D12Renderer *renderer,
6921
    D3D12CommandBuffer *commandBuffer)
6922
{
6923
    Uint32 i;
6924
    HRESULT res;
6925

6926
    // Perform deferred texture data copies
6927

6928
    for (i = 0; i < commandBuffer->textureDownloadCount; i += 1) {
6929
        D3D12_INTERNAL_CopyTextureDownload(
6930
            commandBuffer,
6931
            commandBuffer->textureDownloads[i]);
6932
        SDL_free(commandBuffer->textureDownloads[i]);
6933
    }
6934
    commandBuffer->textureDownloadCount = 0;
6935

6936
    res = ID3D12CommandAllocator_Reset(commandBuffer->commandAllocator);
6937
    ERROR_CHECK("Could not reset command allocator")
6938

6939
    res = ID3D12GraphicsCommandList_Reset(
6940
        commandBuffer->graphicsCommandList,
6941
        commandBuffer->commandAllocator,
6942
        NULL);
6943
    ERROR_CHECK("Could not reset graphicsCommandList")
6944

6945
    // Return descriptor heaps to pool
6946
    D3D12_INTERNAL_ReturnDescriptorHeapToPool(
6947
        renderer,
6948
        commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV]);
6949
    D3D12_INTERNAL_ReturnDescriptorHeapToPool(
6950
        renderer,
6951
        commandBuffer->gpuDescriptorHeaps[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER]);
6952

6953
    // Uniform buffers are now available
6954
    SDL_LockMutex(renderer->acquireUniformBufferLock);
6955

6956
    for (i = 0; i < commandBuffer->usedUniformBufferCount; i += 1) {
6957
        D3D12_INTERNAL_ReturnUniformBufferToPool(
6958
            renderer,
6959
            commandBuffer->usedUniformBuffers[i]);
6960
    }
6961
    commandBuffer->usedUniformBufferCount = 0;
6962

6963
    SDL_UnlockMutex(renderer->acquireUniformBufferLock);
6964

6965
    // TODO: More reference counting
6966

6967
    for (i = 0; i < commandBuffer->usedTextureCount; i += 1) {
6968
        (void)SDL_AtomicDecRef(&commandBuffer->usedTextures[i]->referenceCount);
6969
    }
6970
    commandBuffer->usedTextureCount = 0;
6971

6972
    for (i = 0; i < commandBuffer->usedBufferCount; i += 1) {
6973
        (void)SDL_AtomicDecRef(&commandBuffer->usedBuffers[i]->referenceCount);
6974
    }
6975
    commandBuffer->usedBufferCount = 0;
6976

6977
    for (i = 0; i < commandBuffer->usedSamplerCount; i += 1) {
6978
        (void)SDL_AtomicDecRef(&commandBuffer->usedSamplers[i]->referenceCount);
6979
    }
6980
    commandBuffer->usedSamplerCount = 0;
6981

6982
    for (i = 0; i < commandBuffer->usedGraphicsPipelineCount; i += 1) {
6983
        (void)SDL_AtomicDecRef(&commandBuffer->usedGraphicsPipelines[i]->referenceCount);
6984
    }
6985
    commandBuffer->usedGraphicsPipelineCount = 0;
6986

6987
    for (i = 0; i < commandBuffer->usedComputePipelineCount; i += 1) {
6988
        (void)SDL_AtomicDecRef(&commandBuffer->usedComputePipelines[i]->referenceCount);
6989
    }
6990
    commandBuffer->usedComputePipelineCount = 0;
6991

6992
    // Reset presentation
6993
    commandBuffer->presentDataCount = 0;
6994

6995
    // The fence is now available (unless SubmitAndAcquireFence was called)
6996
    if (commandBuffer->autoReleaseFence) {
6997
        D3D12_ReleaseFence(
6998
            (SDL_GPURenderer *)renderer,
6999
            (SDL_GPUFence *)commandBuffer->inFlightFence);
7000

7001
        commandBuffer->inFlightFence = NULL;
7002
    }
7003

7004
    // Return command buffer to pool
7005
    SDL_LockMutex(renderer->acquireCommandBufferLock);
7006

7007
    if (renderer->availableCommandBufferCount == renderer->availableCommandBufferCapacity) {
7008
        renderer->availableCommandBufferCapacity += 1;
7009
        renderer->availableCommandBuffers = (D3D12CommandBuffer **)SDL_realloc(
7010
            renderer->availableCommandBuffers,
7011
            renderer->availableCommandBufferCapacity * sizeof(D3D12CommandBuffer *));
7012
    }
7013

7014
    renderer->availableCommandBuffers[renderer->availableCommandBufferCount] = commandBuffer;
7015
    renderer->availableCommandBufferCount += 1;
7016

7017
    SDL_UnlockMutex(renderer->acquireCommandBufferLock);
7018

7019
    // Remove this command buffer from the submitted list
7020
    for (i = 0; i < renderer->submittedCommandBufferCount; i += 1) {
7021
        if (renderer->submittedCommandBuffers[i] == commandBuffer) {
7022
            renderer->submittedCommandBuffers[i] = renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount - 1];
7023
            renderer->submittedCommandBufferCount -= 1;
7024
        }
7025
    }
7026
}
7027

7028
static void D3D12_Submit(
7029
    SDL_GPUCommandBuffer *commandBuffer)
7030
{
7031
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
7032
    D3D12Renderer *renderer = d3d12CommandBuffer->renderer;
7033
    ID3D12CommandList *commandLists[1];
7034
    HRESULT res;
7035

7036
    SDL_LockMutex(renderer->submitLock);
7037

7038
    // Unmap uniform buffers
7039
    for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
7040
        if (d3d12CommandBuffer->vertexUniformBuffers[i] != NULL) {
7041
            ID3D12Resource_Unmap(
7042
                d3d12CommandBuffer->vertexUniformBuffers[i]->buffer->handle,
7043
                0,
7044
                NULL);
7045
            d3d12CommandBuffer->vertexUniformBuffers[i]->buffer->mapPointer = NULL;
7046
        }
7047

7048
        if (d3d12CommandBuffer->fragmentUniformBuffers[i] != NULL) {
7049
            ID3D12Resource_Unmap(
7050
                d3d12CommandBuffer->fragmentUniformBuffers[i]->buffer->handle,
7051
                0,
7052
                NULL);
7053
            d3d12CommandBuffer->fragmentUniformBuffers[i]->buffer->mapPointer = NULL;
7054
        }
7055

7056
        // TODO: compute uniforms
7057
    }
7058

7059
    // Transition present textures to present mode
7060
    for (Uint32 i = 0; i < d3d12CommandBuffer->presentDataCount; i += 1) {
7061
        Uint32 swapchainIndex = d3d12CommandBuffer->presentDatas[i].swapchainImageIndex;
7062
        D3D12TextureContainer *container = &d3d12CommandBuffer->presentDatas[i].windowData->textureContainers[swapchainIndex];
7063
        D3D12TextureSubresource *subresource = D3D12_INTERNAL_FetchTextureSubresource(container, 0, 0);
7064

7065
        D3D12_RESOURCE_BARRIER barrierDesc;
7066
        barrierDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
7067
        barrierDesc.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
7068
        barrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
7069
        barrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
7070
        barrierDesc.Transition.pResource = subresource->parent->resource;
7071
        barrierDesc.Transition.Subresource = subresource->index;
7072

7073
        ID3D12GraphicsCommandList_ResourceBarrier(
7074
            d3d12CommandBuffer->graphicsCommandList,
7075
            1,
7076
            &barrierDesc);
7077
    }
7078

7079
    // Notify the command buffer that we have completed recording
7080
    res = ID3D12GraphicsCommandList_Close(d3d12CommandBuffer->graphicsCommandList);
7081
    ERROR_CHECK("Failed to close command list!");
7082

7083
    res = ID3D12GraphicsCommandList_QueryInterface(
7084
        d3d12CommandBuffer->graphicsCommandList,
7085
        D3D_GUID(D3D_IID_ID3D12CommandList),
7086
        (void **)&commandLists[0]);
7087
    ERROR_CHECK("Failed to convert command list!")
7088

7089
    // Submit the command list to the queue
7090
    ID3D12CommandQueue_ExecuteCommandLists(
7091
        renderer->commandQueue,
7092
        1,
7093
        commandLists);
7094

7095
    ID3D12CommandList_Release(commandLists[0]);
7096

7097
    // Acquire a fence and set it to the in-flight fence
7098
    d3d12CommandBuffer->inFlightFence = D3D12_INTERNAL_AcquireFence(renderer);
7099
    if (!d3d12CommandBuffer->inFlightFence) {
7100
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to acquire fence.");
7101
    }
7102

7103
    // Mark that a fence should be signaled after command list execution
7104
    res = ID3D12CommandQueue_Signal(
7105
        renderer->commandQueue,
7106
        d3d12CommandBuffer->inFlightFence->handle,
7107
        D3D12_FENCE_SIGNAL_VALUE);
7108
    ERROR_CHECK("Failed to enqueue fence signal!");
7109

7110
    // Mark the command buffer as submitted
7111
    if (renderer->submittedCommandBufferCount + 1 >= renderer->submittedCommandBufferCapacity) {
7112
        renderer->submittedCommandBufferCapacity = renderer->submittedCommandBufferCount + 1;
7113

7114
        renderer->submittedCommandBuffers = (D3D12CommandBuffer **)SDL_realloc(
7115
            renderer->submittedCommandBuffers,
7116
            sizeof(D3D12CommandBuffer *) * renderer->submittedCommandBufferCapacity);
7117
    }
7118

7119
    renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount] = d3d12CommandBuffer;
7120
    renderer->submittedCommandBufferCount += 1;
7121

7122
    // Present, if applicable
7123
    for (Uint32 i = 0; i < d3d12CommandBuffer->presentDataCount; i += 1) {
7124
        D3D12PresentData *presentData = &d3d12CommandBuffer->presentDatas[i];
7125
        D3D12WindowData *windowData = presentData->windowData;
7126

7127
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
7128
        D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParams;
7129
        SDL_zero(planeParams);
7130
        planeParams.Token = windowData->frameToken;
7131
        planeParams.ResourceCount = 1;
7132
        planeParams.ppResources = &windowData->textureContainers[windowData->frameCounter].activeTexture->resource;
7133
        planeParams.ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // FIXME
7134

7135
        D3D12XBOX_PRESENT_PARAMETERS presentParams;
7136
        SDL_zero(presentParams);
7137
        presentParams.Flags = (windowData->presentMode == SDL_GPU_PRESENTMODE_IMMEDIATE) ? D3D12XBOX_PRESENT_FLAG_IMMEDIATE : D3D12XBOX_PRESENT_FLAG_NONE;
7138

7139
        renderer->commandQueue->PresentX(1, &planeParams, &presentParams);
7140
#else
7141
        // NOTE: flip discard always supported since DXGI 1.4 is required
7142
        Uint32 syncInterval = 1;
7143
        if (windowData->presentMode == SDL_GPU_PRESENTMODE_IMMEDIATE ||
7144
            windowData->presentMode == SDL_GPU_PRESENTMODE_MAILBOX) {
7145
            syncInterval = 0;
7146
        }
7147

7148
        Uint32 presentFlags = 0;
7149
        if (renderer->supportsTearing &&
7150
            windowData->presentMode == SDL_GPU_PRESENTMODE_IMMEDIATE) {
7151
            presentFlags = DXGI_PRESENT_ALLOW_TEARING;
7152
        }
7153

7154
        IDXGISwapChain_Present(
7155
            windowData->swapchain,
7156
            syncInterval,
7157
            presentFlags);
7158

7159
        ID3D12Resource_Release(windowData->textureContainers[presentData->swapchainImageIndex].activeTexture->resource);
7160
#endif
7161

7162
        windowData->inFlightFences[windowData->frameCounter] = d3d12CommandBuffer->inFlightFence;
7163
        (void)SDL_AtomicIncRef(&d3d12CommandBuffer->inFlightFence->referenceCount);
7164
        windowData->frameCounter = (windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT;
7165
    }
7166

7167
    // Check for cleanups
7168
    for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
7169
        Uint64 fenceValue = ID3D12Fence_GetCompletedValue(
7170
            renderer->submittedCommandBuffers[i]->inFlightFence->handle);
7171

7172
        if (fenceValue == D3D12_FENCE_SIGNAL_VALUE) {
7173
            D3D12_INTERNAL_CleanCommandBuffer(
7174
                renderer,
7175
                renderer->submittedCommandBuffers[i]);
7176
        }
7177
    }
7178

7179
    D3D12_INTERNAL_PerformPendingDestroys(renderer);
7180

7181
    SDL_UnlockMutex(renderer->submitLock);
7182
}
7183

7184
static SDL_GPUFence *D3D12_SubmitAndAcquireFence(
7185
    SDL_GPUCommandBuffer *commandBuffer)
7186
{
7187
    D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
7188
    d3d12CommandBuffer->autoReleaseFence = false;
7189
    D3D12_Submit(commandBuffer);
7190
    return (SDL_GPUFence *)d3d12CommandBuffer->inFlightFence;
7191
}
7192

7193
static void D3D12_Wait(
7194
    SDL_GPURenderer *driverData)
7195
{
7196
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
7197
    D3D12Fence *fence = D3D12_INTERNAL_AcquireFence(renderer);
7198
    if (!fence) {
7199
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to acquire fence.");
7200
        return;
7201
    }
7202
    HRESULT res;
7203

7204
    SDL_LockMutex(renderer->submitLock);
7205

7206
    if (renderer->commandQueue) {
7207
        // Insert a signal into the end of the command queue...
7208
        ID3D12CommandQueue_Signal(
7209
            renderer->commandQueue,
7210
            fence->handle,
7211
            D3D12_FENCE_SIGNAL_VALUE);
7212

7213
        // ...and then block on it.
7214
        if (ID3D12Fence_GetCompletedValue(fence->handle) != D3D12_FENCE_SIGNAL_VALUE) {
7215
            res = ID3D12Fence_SetEventOnCompletion(
7216
                fence->handle,
7217
                D3D12_FENCE_SIGNAL_VALUE,
7218
                fence->event);
7219
            ERROR_CHECK_RETURN("Setting fence event failed", )
7220

7221
            WaitForSingleObject(fence->event, INFINITE);
7222
        }
7223
    }
7224

7225
    D3D12_ReleaseFence(
7226
        (SDL_GPURenderer *)renderer,
7227
        (SDL_GPUFence *)fence);
7228

7229
    // Clean up
7230
    for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
7231
        D3D12_INTERNAL_CleanCommandBuffer(renderer, renderer->submittedCommandBuffers[i]);
7232
    }
7233

7234
    D3D12_INTERNAL_PerformPendingDestroys(renderer);
7235

7236
    SDL_UnlockMutex(renderer->submitLock);
7237
}
7238

7239
static void D3D12_WaitForFences(
7240
    SDL_GPURenderer *driverData,
7241
    bool waitAll,
7242
    SDL_GPUFence **pFences,
7243
    Uint32 fenceCount)
7244
{
7245
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
7246
    D3D12Fence *fence;
7247
    HANDLE *events = SDL_stack_alloc(HANDLE, fenceCount);
7248
    HRESULT res;
7249

7250
    SDL_LockMutex(renderer->submitLock);
7251

7252
    for (Uint32 i = 0; i < fenceCount; i += 1) {
7253
        fence = (D3D12Fence *)pFences[i];
7254

7255
        res = ID3D12Fence_SetEventOnCompletion(
7256
            fence->handle,
7257
            D3D12_FENCE_SIGNAL_VALUE,
7258
            fence->event);
7259
        ERROR_CHECK_RETURN("Setting fence event failed", )
7260

7261
        events[i] = fence->event;
7262
    }
7263

7264
    WaitForMultipleObjects(
7265
        fenceCount,
7266
        events,
7267
        waitAll,
7268
        INFINITE);
7269

7270
    // Check for cleanups
7271
    for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
7272
        Uint64 fenceValue = ID3D12Fence_GetCompletedValue(
7273
            renderer->submittedCommandBuffers[i]->inFlightFence->handle);
7274

7275
        if (fenceValue == D3D12_FENCE_SIGNAL_VALUE) {
7276
            D3D12_INTERNAL_CleanCommandBuffer(
7277
                renderer,
7278
                renderer->submittedCommandBuffers[i]);
7279
        }
7280
    }
7281

7282
    D3D12_INTERNAL_PerformPendingDestroys(renderer);
7283

7284
    SDL_stack_free(events);
7285

7286
    SDL_UnlockMutex(renderer->submitLock);
7287
}
7288

7289
// Feature Queries
7290

7291
static bool D3D12_SupportsTextureFormat(
7292
    SDL_GPURenderer *driverData,
7293
    SDL_GPUTextureFormat format,
7294
    SDL_GPUTextureType type,
7295
    SDL_GPUTextureUsageFlags usage)
7296
{
7297
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
7298
    DXGI_FORMAT dxgiFormat = SDLToD3D12_TextureFormat[format];
7299
    D3D12_FEATURE_DATA_FORMAT_SUPPORT formatSupport = { dxgiFormat, D3D12_FORMAT_SUPPORT1_NONE, D3D12_FORMAT_SUPPORT2_NONE };
7300
    HRESULT res;
7301

7302
    res = ID3D12Device_CheckFeatureSupport(
7303
        renderer->device,
7304
        D3D12_FEATURE_FORMAT_SUPPORT,
7305
        &formatSupport,
7306
        sizeof(formatSupport));
7307
    if (FAILED(res)) {
7308
        // Format is apparently unknown
7309
        return false;
7310
    }
7311

7312
    // Is the texture type supported?
7313
    if (type == SDL_GPU_TEXTURETYPE_2D && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D)) {
7314
        return false;
7315
    }
7316
    if (type == SDL_GPU_TEXTURETYPE_2D_ARRAY && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D)) {
7317
        return false;
7318
    }
7319
    if (type == SDL_GPU_TEXTURETYPE_3D && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE3D)) {
7320
        return false;
7321
    }
7322
    if (type == SDL_GPU_TEXTURETYPE_CUBE && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURECUBE)) {
7323
        return false;
7324
    }
7325

7326
    // Are the usage flags supported?
7327
    if ((usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE)) {
7328
        return false;
7329
    }
7330
    if ((usage & (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ)) && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_SHADER_LOAD)) {
7331
        return false;
7332
    }
7333
    if ((usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) && !(formatSupport.Support2 & D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE)) {
7334
        return false;
7335
    }
7336
    if ((usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_RENDER_TARGET)) {
7337
        return false;
7338
    }
7339
    if ((usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) && !(formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL)) {
7340
        return false;
7341
    }
7342

7343
    return true;
7344
}
7345

7346
static bool D3D12_SupportsSampleCount(
7347
    SDL_GPURenderer *driverData,
7348
    SDL_GPUTextureFormat format,
7349
    SDL_GPUSampleCount sampleCount)
7350
{
7351
    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
7352
    D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS featureData;
7353
    HRESULT res;
7354

7355
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
7356
    featureData.Flags = (D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG)0;
7357
#else
7358
    featureData.Flags = (D3D12_MULTISAMPLE_QUALITY_LEVEL_FLAGS)0;
7359
#endif
7360
    featureData.Format = SDLToD3D12_TextureFormat[format];
7361
    featureData.SampleCount = SDLToD3D12_SampleCount[sampleCount];
7362
    res = ID3D12Device_CheckFeatureSupport(
7363
        renderer->device,
7364
        D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
7365
        &featureData,
7366
        sizeof(featureData));
7367

7368
    return SUCCEEDED(res) && featureData.NumQualityLevels > 0;
7369
}
7370

7371
static void D3D12_INTERNAL_InitBlitResources(
7372
    D3D12Renderer *renderer)
7373
{
7374
    SDL_GPUShaderCreateInfo shaderCreateInfo;
7375
    SDL_GPUSamplerCreateInfo samplerCreateInfo;
7376

7377
    renderer->blitPipelineCapacity = 2;
7378
    renderer->blitPipelineCount = 0;
7379
    renderer->blitPipelines = (BlitPipelineCacheEntry *)SDL_malloc(
7380
        renderer->blitPipelineCapacity * sizeof(BlitPipelineCacheEntry));
7381

7382
    // Fullscreen vertex shader
7383
    SDL_zero(shaderCreateInfo);
7384
    shaderCreateInfo.code = (Uint8 *)D3D12_FullscreenVert;
7385
    shaderCreateInfo.codeSize = sizeof(D3D12_FullscreenVert);
7386
    shaderCreateInfo.stage = SDL_GPU_SHADERSTAGE_VERTEX;
7387
    shaderCreateInfo.format = SDL_GPU_SHADERFORMAT_DXBC;
7388
    shaderCreateInfo.entryPointName = "main";
7389

7390
    renderer->blitVertexShader = D3D12_CreateShader(
7391
        (SDL_GPURenderer *)renderer,
7392
        &shaderCreateInfo);
7393

7394
    if (renderer->blitVertexShader == NULL) {
7395
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile vertex shader for blit!");
7396
    }
7397

7398
    // BlitFrom2D pixel shader
7399
    shaderCreateInfo.code = (Uint8 *)D3D12_BlitFrom2D;
7400
    shaderCreateInfo.codeSize = sizeof(D3D12_BlitFrom2D);
7401
    shaderCreateInfo.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
7402
    shaderCreateInfo.samplerCount = 1;
7403
    shaderCreateInfo.uniformBufferCount = 1;
7404

7405
    renderer->blitFrom2DShader = D3D12_CreateShader(
7406
        (SDL_GPURenderer *)renderer,
7407
        &shaderCreateInfo);
7408

7409
    if (renderer->blitFrom2DShader == NULL) {
7410
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFrom2D pixel shader!");
7411
    }
7412

7413
    // BlitFrom2DArray pixel shader
7414
    shaderCreateInfo.code = (Uint8 *)D3D12_BlitFrom2DArray;
7415
    shaderCreateInfo.codeSize = sizeof(D3D12_BlitFrom2DArray);
7416

7417
    renderer->blitFrom2DArrayShader = D3D12_CreateShader(
7418
        (SDL_GPURenderer *)renderer,
7419
        &shaderCreateInfo);
7420

7421
    if (renderer->blitFrom2DArrayShader == NULL) {
7422
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFrom2DArray pixel shader!");
7423
    }
7424

7425
    // BlitFrom3D pixel shader
7426
    shaderCreateInfo.code = (Uint8 *)D3D12_BlitFrom3D;
7427
    shaderCreateInfo.codeSize = sizeof(D3D12_BlitFrom3D);
7428

7429
    renderer->blitFrom3DShader = D3D12_CreateShader(
7430
        (SDL_GPURenderer *)renderer,
7431
        &shaderCreateInfo);
7432

7433
    if (renderer->blitFrom3DShader == NULL) {
7434
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFrom3D pixel shader!");
7435
    }
7436

7437
    // BlitFromCube pixel shader
7438
    shaderCreateInfo.code = (Uint8 *)D3D12_BlitFromCube;
7439
    shaderCreateInfo.codeSize = sizeof(D3D12_BlitFromCube);
7440

7441
    renderer->blitFromCubeShader = D3D12_CreateShader(
7442
        (SDL_GPURenderer *)renderer,
7443
        &shaderCreateInfo);
7444

7445
    if (renderer->blitFromCubeShader == NULL) {
7446
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFromCube pixel shader!");
7447
    }
7448

7449
    // Create samplers
7450
    samplerCreateInfo.addressModeU = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
7451
    samplerCreateInfo.addressModeV = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
7452
    samplerCreateInfo.addressModeW = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
7453
    samplerCreateInfo.anisotropyEnable = 0;
7454
    samplerCreateInfo.compareEnable = 0;
7455
    samplerCreateInfo.magFilter = SDL_GPU_FILTER_NEAREST;
7456
    samplerCreateInfo.minFilter = SDL_GPU_FILTER_NEAREST;
7457
    samplerCreateInfo.mipmapMode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST;
7458
    samplerCreateInfo.mipLodBias = 0.0f;
7459
    samplerCreateInfo.minLod = 0;
7460
    samplerCreateInfo.maxLod = 1000;
7461
    samplerCreateInfo.maxAnisotropy = 1.0f;
7462
    samplerCreateInfo.compareOp = SDL_GPU_COMPAREOP_ALWAYS;
7463

7464
    renderer->blitNearestSampler = D3D12_CreateSampler(
7465
        (SDL_GPURenderer *)renderer,
7466
        &samplerCreateInfo);
7467

7468
    if (renderer->blitNearestSampler == NULL) {
7469
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create blit nearest sampler!");
7470
    }
7471

7472
    samplerCreateInfo.magFilter = SDL_GPU_FILTER_LINEAR;
7473
    samplerCreateInfo.minFilter = SDL_GPU_FILTER_LINEAR;
7474
    samplerCreateInfo.mipmapMode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR;
7475

7476
    renderer->blitLinearSampler = D3D12_CreateSampler(
7477
        (SDL_GPURenderer *)renderer,
7478
        &samplerCreateInfo);
7479

7480
    if (renderer->blitLinearSampler == NULL) {
7481
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create blit linear sampler!");
7482
    }
7483
}
7484

7485
static bool D3D12_PrepareDriver(SDL_VideoDevice *_this)
7486
{
7487
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
7488
    return true;
7489
#else
7490
    void *d3d12_dll;
7491
    void *dxgi_dll;
7492
    PFN_D3D12_CREATE_DEVICE D3D12CreateDeviceFunc;
7493
    PFN_CREATE_DXGI_FACTORY1 CreateDXGIFactoryFunc;
7494
    HRESULT res;
7495
    ID3D12Device *device;
7496
    IDXGIFactory1 *factory;
7497
    IDXGIFactory4 *factory4;
7498
    IDXGIFactory6 *factory6;
7499
    IDXGIAdapter1 *adapter;
7500

7501
    // Can we load D3D12?
7502

7503
    d3d12_dll = SDL_LoadObject(D3D12_DLL);
7504
    if (d3d12_dll == NULL) {
7505
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not find " D3D12_DLL);
7506
        return false;
7507
    }
7508

7509
    D3D12CreateDeviceFunc = (PFN_D3D12_CREATE_DEVICE)SDL_LoadFunction(
7510
        d3d12_dll,
7511
        D3D12_CREATE_DEVICE_FUNC);
7512
    if (D3D12CreateDeviceFunc == NULL) {
7513
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not find function " D3D12_CREATE_DEVICE_FUNC " in " D3D12_DLL);
7514
        SDL_UnloadObject(d3d12_dll);
7515
        return false;
7516
    }
7517

7518
    // Can we load DXGI?
7519

7520
    dxgi_dll = SDL_LoadObject(DXGI_DLL);
7521
    if (dxgi_dll == NULL) {
7522
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not find " DXGI_DLL);
7523
        return false;
7524
    }
7525

7526
    CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY1)SDL_LoadFunction(
7527
        dxgi_dll,
7528
        CREATE_DXGI_FACTORY1_FUNC);
7529
    if (CreateDXGIFactoryFunc == NULL) {
7530
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not find function " CREATE_DXGI_FACTORY1_FUNC " in " DXGI_DLL);
7531
        SDL_UnloadObject(dxgi_dll);
7532
        return false;
7533
    }
7534

7535
    // Can we create a device?
7536

7537
    // Create the DXGI factory
7538
    res = CreateDXGIFactoryFunc(
7539
        &D3D_IID_IDXGIFactory1,
7540
        (void **)&factory);
7541
    if (FAILED(res)) {
7542
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not create DXGIFactory");
7543
        SDL_UnloadObject(d3d12_dll);
7544
        SDL_UnloadObject(dxgi_dll);
7545
        return false;
7546
    }
7547

7548
    // Check for DXGI 1.4 support
7549
    res = IDXGIFactory1_QueryInterface(
7550
        factory,
7551
        D3D_GUID(D3D_IID_IDXGIFactory4),
7552
        (void **)&factory4);
7553
    if (FAILED(res)) {
7554
        IDXGIFactory1_Release(factory);
7555
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Failed to find DXGI1.4 support, required for DX12");
7556
        SDL_UnloadObject(d3d12_dll);
7557
        SDL_UnloadObject(dxgi_dll);
7558
        return false;
7559
    }
7560
    IDXGIFactory4_Release(factory4);
7561

7562
    res = IDXGIFactory1_QueryInterface(
7563
        factory,
7564
        D3D_GUID(D3D_IID_IDXGIFactory6),
7565
        (void **)&factory6);
7566
    if (SUCCEEDED(res)) {
7567
        res = IDXGIFactory6_EnumAdapterByGpuPreference(
7568
            factory6,
7569
            0,
7570
            DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
7571
            D3D_GUID(D3D_IID_IDXGIAdapter1),
7572
            (void **)&adapter);
7573
        IDXGIFactory6_Release(factory6);
7574
    } else {
7575
        res = IDXGIFactory1_EnumAdapters1(
7576
            factory,
7577
            0,
7578
            &adapter);
7579
    }
7580
    if (FAILED(res)) {
7581
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Failed to find adapter for D3D12Device");
7582
        IDXGIFactory1_Release(factory);
7583
        SDL_UnloadObject(d3d12_dll);
7584
        SDL_UnloadObject(dxgi_dll);
7585
        return false;
7586
    }
7587

7588
    res = D3D12CreateDeviceFunc(
7589
        (IUnknown *)adapter,
7590
        D3D_FEATURE_LEVEL_CHOICE,
7591
        D3D_GUID(D3D_IID_ID3D12Device),
7592
        (void **)&device);
7593

7594
    if (SUCCEEDED(res)) {
7595
        ID3D12Device_Release(device);
7596
    }
7597
    IDXGIAdapter1_Release(adapter);
7598
    IDXGIFactory1_Release(factory);
7599

7600
    SDL_UnloadObject(d3d12_dll);
7601
    SDL_UnloadObject(dxgi_dll);
7602

7603
    if (FAILED(res)) {
7604
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D12: Could not create D3D12Device with feature level " D3D_FEATURE_LEVEL_CHOICE_STR);
7605
        return false;
7606
    }
7607

7608
    return true;
7609
#endif
7610
}
7611

7612
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
7613
static void D3D12_INTERNAL_TryInitializeDXGIDebug(D3D12Renderer *renderer)
7614
{
7615
    PFN_DXGI_GET_DEBUG_INTERFACE DXGIGetDebugInterfaceFunc;
7616
    HRESULT res;
7617

7618
    renderer->dxgidebug_dll = SDL_LoadObject(DXGIDEBUG_DLL);
7619
    if (renderer->dxgidebug_dll == NULL) {
7620
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not find " DXGIDEBUG_DLL);
7621
        return;
7622
    }
7623

7624
    DXGIGetDebugInterfaceFunc = (PFN_DXGI_GET_DEBUG_INTERFACE)SDL_LoadFunction(
7625
        renderer->dxgidebug_dll,
7626
        DXGI_GET_DEBUG_INTERFACE_FUNC);
7627
    if (DXGIGetDebugInterfaceFunc == NULL) {
7628
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " DXGI_GET_DEBUG_INTERFACE_FUNC);
7629
        return;
7630
    }
7631

7632
    res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIDebug, (void **)&renderer->dxgiDebug);
7633
    if (FAILED(res)) {
7634
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get IDXGIDebug interface");
7635
    }
7636

7637
    res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIInfoQueue, (void **)&renderer->dxgiInfoQueue);
7638
    if (FAILED(res)) {
7639
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get IDXGIInfoQueue interface");
7640
    }
7641
}
7642
#endif
7643

7644
static void D3D12_INTERNAL_TryInitializeD3D12Debug(D3D12Renderer *renderer)
7645
{
7646
    PFN_D3D12_GET_DEBUG_INTERFACE D3D12GetDebugInterfaceFunc;
7647
    HRESULT res;
7648

7649
    D3D12GetDebugInterfaceFunc = (PFN_D3D12_GET_DEBUG_INTERFACE)SDL_LoadFunction(
7650
        renderer->d3d12_dll,
7651
        D3D12_GET_DEBUG_INTERFACE_FUNC);
7652
    if (D3D12GetDebugInterfaceFunc == NULL) {
7653
        SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Could not load function: " D3D12_GET_DEBUG_INTERFACE_FUNC);
7654
        return;
7655
    }
7656

7657
    res = D3D12GetDebugInterfaceFunc(D3D_GUID(D3D_IID_ID3D12Debug), (void **)&renderer->d3d12Debug);
7658
    if (FAILED(res)) {
7659
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get ID3D12Debug interface");
7660
        return;
7661
    }
7662

7663
    ID3D12Debug_EnableDebugLayer(renderer->d3d12Debug);
7664
}
7665

7666
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
7667
static void D3D12_INTERNAL_TryInitializeD3D12DebugInfoQueue(D3D12Renderer *renderer)
7668
{
7669
    ID3D12InfoQueue *infoQueue = NULL;
7670
    D3D12_MESSAGE_SEVERITY severities[] = { D3D12_MESSAGE_SEVERITY_INFO };
7671
    D3D12_INFO_QUEUE_FILTER filter;
7672
    HRESULT res;
7673

7674
    res = ID3D12Device_QueryInterface(
7675
        renderer->device,
7676
        D3D_GUID(D3D_IID_ID3D12InfoQueue),
7677
        (void **)&infoQueue);
7678
    if (FAILED(res)) {
7679
        ERROR_CHECK_RETURN("Failed to convert ID3D12Device to ID3D12InfoQueue", );
7680
    }
7681

7682
    SDL_zero(filter);
7683
    filter.DenyList.NumSeverities = 1;
7684
    filter.DenyList.pSeverityList = severities;
7685
    ID3D12InfoQueue_PushStorageFilter(
7686
        infoQueue,
7687
        &filter);
7688

7689
    ID3D12InfoQueue_SetBreakOnSeverity(
7690
        infoQueue,
7691
        D3D12_MESSAGE_SEVERITY_ERROR,
7692
        true);
7693

7694
    ID3D12InfoQueue_SetBreakOnSeverity(
7695
        infoQueue,
7696
        D3D12_MESSAGE_SEVERITY_CORRUPTION,
7697
        true);
7698

7699
    ID3D12InfoQueue_Release(infoQueue);
7700
}
7701
#endif
7702

7703
static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SDL_PropertiesID props)
7704
{
7705
    SDL_GPUDevice *result;
7706
    D3D12Renderer *renderer;
7707
    HRESULT res;
7708

7709
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
7710
    PFN_D3D12_XBOX_CREATE_DEVICE D3D12XboxCreateDeviceFunc;
7711
    D3D12XBOX_CREATE_DEVICE_PARAMETERS createDeviceParams;
7712
#else
7713
    PFN_CREATE_DXGI_FACTORY1 CreateDXGIFactoryFunc;
7714
    IDXGIFactory1 *factory1;
7715
    IDXGIFactory5 *factory5;
7716
    IDXGIFactory6 *factory6;
7717
    DXGI_ADAPTER_DESC1 adapterDesc;
7718
    PFN_D3D12_CREATE_DEVICE D3D12CreateDeviceFunc;
7719
#endif
7720
    D3D12_FEATURE_DATA_ARCHITECTURE architecture;
7721
    D3D12_COMMAND_QUEUE_DESC queueDesc;
7722

7723
    renderer = (D3D12Renderer *)SDL_calloc(1, sizeof(D3D12Renderer));
7724

7725
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
7726
    // Load the DXGI library
7727
    renderer->dxgi_dll = SDL_LoadObject(DXGI_DLL);
7728
    if (renderer->dxgi_dll == NULL) {
7729
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not find " DXGI_DLL);
7730
        D3D12_INTERNAL_DestroyRenderer(renderer);
7731
        return NULL;
7732
    }
7733

7734
    // Initialize the DXGI debug layer, if applicable
7735
    if (debugMode) {
7736
        D3D12_INTERNAL_TryInitializeDXGIDebug(renderer);
7737
    }
7738

7739
    // Load the CreateDXGIFactory1 function
7740
    CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY1)SDL_LoadFunction(
7741
        renderer->dxgi_dll,
7742
        CREATE_DXGI_FACTORY1_FUNC);
7743
    if (CreateDXGIFactoryFunc == NULL) {
7744
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " CREATE_DXGI_FACTORY1_FUNC);
7745
        D3D12_INTERNAL_DestroyRenderer(renderer);
7746
        return NULL;
7747
    }
7748

7749
    // Create the DXGI factory
7750
    res = CreateDXGIFactoryFunc(
7751
        &D3D_IID_IDXGIFactory1,
7752
        (void **)&factory1);
7753
    if (FAILED(res)) {
7754
        D3D12_INTERNAL_DestroyRenderer(renderer);
7755
        ERROR_CHECK_RETURN("Could not create DXGIFactory", NULL);
7756
    }
7757

7758
    // Check for DXGI 1.4 support
7759
    res = IDXGIFactory1_QueryInterface(
7760
        factory1,
7761
        D3D_GUID(D3D_IID_IDXGIFactory4),
7762
        (void **)&renderer->factory);
7763
    if (FAILED(res)) {
7764
        D3D12_INTERNAL_DestroyRenderer(renderer);
7765
        ERROR_CHECK_RETURN("DXGI1.4 support not found, required for DX12", NULL);
7766
    }
7767
    IDXGIFactory1_Release(factory1);
7768

7769
    // Check for explicit tearing support
7770
    res = IDXGIFactory4_QueryInterface(
7771
        renderer->factory,
7772
        D3D_GUID(D3D_IID_IDXGIFactory5),
7773
        (void **)&factory5);
7774
    if (SUCCEEDED(res)) {
7775
        res = IDXGIFactory5_CheckFeatureSupport(
7776
            factory5,
7777
            DXGI_FEATURE_PRESENT_ALLOW_TEARING,
7778
            &renderer->supportsTearing,
7779
            sizeof(renderer->supportsTearing));
7780
        if (FAILED(res)) {
7781
            renderer->supportsTearing = false;
7782
        }
7783
        IDXGIFactory5_Release(factory5);
7784
    }
7785

7786
    // Select the appropriate device for rendering
7787
    res = IDXGIFactory4_QueryInterface(
7788
        renderer->factory,
7789
        D3D_GUID(D3D_IID_IDXGIFactory6),
7790
        (void **)&factory6);
7791
    if (SUCCEEDED(res)) {
7792
        res = IDXGIFactory6_EnumAdapterByGpuPreference(
7793
            factory6,
7794
            0,
7795
            preferLowPower ? DXGI_GPU_PREFERENCE_MINIMUM_POWER : DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
7796
            D3D_GUID(D3D_IID_IDXGIAdapter1),
7797
            (void **)&renderer->adapter);
7798
        IDXGIFactory6_Release(factory6);
7799
    } else {
7800
        res = IDXGIFactory4_EnumAdapters1(
7801
            renderer->factory,
7802
            0,
7803
            &renderer->adapter);
7804
    }
7805

7806
    if (FAILED(res)) {
7807
        D3D12_INTERNAL_DestroyRenderer(renderer);
7808
        ERROR_CHECK_RETURN("Could not find adapter for D3D12Device", NULL);
7809
    }
7810

7811
    // Get information about the selected adapter. Used for logging info.
7812
    res = IDXGIAdapter1_GetDesc1(renderer->adapter, &adapterDesc);
7813
    if (FAILED(res)) {
7814
        D3D12_INTERNAL_DestroyRenderer(renderer);
7815
        ERROR_CHECK_RETURN("Could not get adapter description", NULL);
7816
    }
7817

7818
    SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: D3D12");
7819
    SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "D3D12 Adapter: %S", adapterDesc.Description);
7820
#endif
7821

7822
    // Load the D3D library
7823
    renderer->d3d12_dll = SDL_LoadObject(D3D12_DLL);
7824
    if (renderer->d3d12_dll == NULL) {
7825
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not find " D3D12_DLL);
7826
        D3D12_INTERNAL_DestroyRenderer(renderer);
7827
        return NULL;
7828
    }
7829

7830
    // Load the CreateDevice function
7831
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
7832
    D3D12XboxCreateDeviceFunc = (PFN_D3D12_XBOX_CREATE_DEVICE)SDL_LoadFunction(
7833
        renderer->d3d12_dll,
7834
        "D3D12XboxCreateDevice");
7835
    if (D3D12XboxCreateDeviceFunc == NULL) {
7836
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: D3D12XboxCreateDevice");
7837
        D3D12_INTERNAL_DestroyRenderer(renderer);
7838
        return NULL;
7839
    }
7840
#else
7841
    D3D12CreateDeviceFunc = (PFN_D3D12_CREATE_DEVICE)SDL_LoadFunction(
7842
        renderer->d3d12_dll,
7843
        D3D12_CREATE_DEVICE_FUNC);
7844
    if (D3D12CreateDeviceFunc == NULL) {
7845
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " D3D12_CREATE_DEVICE_FUNC);
7846
        D3D12_INTERNAL_DestroyRenderer(renderer);
7847
        return NULL;
7848
    }
7849
#endif
7850

7851
    renderer->D3D12SerializeRootSignature_func = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)SDL_LoadFunction(
7852
        renderer->d3d12_dll,
7853
        D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC);
7854
    if (renderer->D3D12SerializeRootSignature_func == NULL) {
7855
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " D3D12_SERIALIZE_ROOT_SIGNATURE_FUNC);
7856
        D3D12_INTERNAL_DestroyRenderer(renderer);
7857
        return NULL;
7858
    }
7859

7860
    // Initialize the D3D12 debug layer, if applicable
7861
    if (debugMode) {
7862
        D3D12_INTERNAL_TryInitializeD3D12Debug(renderer);
7863
    }
7864

7865
    // Create the D3D12Device
7866
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
7867
    if (s_Device != NULL) {
7868
        renderer->device = s_Device;
7869
    } else {
7870
        SDL_zero(createDeviceParams);
7871
        createDeviceParams.Version = D3D12_SDK_VERSION;
7872
        createDeviceParams.GraphicsCommandQueueRingSizeBytes = D3D12XBOX_DEFAULT_SIZE_BYTES;
7873
        createDeviceParams.GraphicsScratchMemorySizeBytes = D3D12XBOX_DEFAULT_SIZE_BYTES;
7874
        createDeviceParams.ComputeScratchMemorySizeBytes = D3D12XBOX_DEFAULT_SIZE_BYTES;
7875
        createDeviceParams.DisableGeometryShaderAllocations = TRUE;
7876
        createDeviceParams.DisableTessellationShaderAllocations = TRUE;
7877
#if defined(SDL_PLATFORM_XBOXSERIES)
7878
        createDeviceParams.DisableDXR = TRUE;
7879
#endif
7880
        if (debugMode) {
7881
            createDeviceParams.ProcessDebugFlags = D3D12XBOX_PROCESS_DEBUG_FLAG_DEBUG;
7882
        }
7883

7884
        res = D3D12XboxCreateDeviceFunc(
7885
            NULL,
7886
            &createDeviceParams,
7887
            IID_GRAPHICS_PPV_ARGS(&renderer->device));
7888
        if (FAILED(res)) {
7889
            D3D12_INTERNAL_DestroyRenderer(renderer);
7890
            ERROR_CHECK_RETURN("Could not create D3D12Device", NULL);
7891
        }
7892

7893
        res = renderer->device->SetFrameIntervalX(
7894
            NULL,
7895
            D3D12XBOX_FRAME_INTERVAL_60_HZ,
7896
            MAX_FRAMES_IN_FLIGHT - 1,
7897
            D3D12XBOX_FRAME_INTERVAL_FLAG_NONE);
7898
        if (FAILED(res)) {
7899
            D3D12_INTERNAL_DestroyRenderer(renderer);
7900
            ERROR_CHECK_RETURN("Could not get set frame interval", NULL);
7901
        }
7902

7903
        res = renderer->device->ScheduleFrameEventX(
7904
            D3D12XBOX_FRAME_EVENT_ORIGIN,
7905
            0,
7906
            NULL,
7907
            D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE);
7908
        if (FAILED(res)) {
7909
            D3D12_INTERNAL_DestroyRenderer(renderer);
7910
            ERROR_CHECK_RETURN("Could not schedule frame events", NULL);
7911
        }
7912

7913
        s_Device = renderer->device;
7914
    }
7915
#else
7916
    res = D3D12CreateDeviceFunc(
7917
        (IUnknown *)renderer->adapter,
7918
        D3D_FEATURE_LEVEL_CHOICE,
7919
        D3D_GUID(D3D_IID_ID3D12Device),
7920
        (void **)&renderer->device);
7921

7922
    if (FAILED(res)) {
7923
        D3D12_INTERNAL_DestroyRenderer(renderer);
7924
        ERROR_CHECK_RETURN("Could not create D3D12Device", NULL);
7925
    }
7926

7927
    // Initialize the D3D12 debug info queue, if applicable
7928
    if (debugMode) {
7929
        D3D12_INTERNAL_TryInitializeD3D12DebugInfoQueue(renderer);
7930
    }
7931
#endif
7932

7933
    // Check UMA
7934
    architecture.NodeIndex = 0;
7935
    res = ID3D12Device_CheckFeatureSupport(
7936
        renderer->device,
7937
        D3D12_FEATURE_ARCHITECTURE,
7938
        &architecture,
7939
        sizeof(D3D12_FEATURE_DATA_ARCHITECTURE));
7940
    if (FAILED(res)) {
7941
        D3D12_INTERNAL_DestroyRenderer(renderer);
7942
        ERROR_CHECK_RETURN("Could not get device architecture", NULL);
7943
    }
7944

7945
    renderer->UMA = (bool)architecture.UMA;
7946
    renderer->UMACacheCoherent = (bool)architecture.CacheCoherentUMA;
7947

7948
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
7949
    renderer->GPUUploadHeapSupported = false;
7950
#else
7951
    // Check "GPU Upload Heap" support (for fast uniform buffers)
7952
    D3D12_FEATURE_DATA_D3D12_OPTIONS16 options16; // 15 wasn't enough, huh?
7953
    renderer->GPUUploadHeapSupported = false;
7954
    res = ID3D12Device_CheckFeatureSupport(
7955
        renderer->device,
7956
        D3D12_FEATURE_D3D12_OPTIONS16,
7957
        &options16,
7958
        sizeof(options16));
7959

7960
    if (SUCCEEDED(res)) {
7961
        renderer->GPUUploadHeapSupported = options16.GPUUploadHeapSupported;
7962
    }
7963
#endif
7964

7965
    // Create command queue
7966
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
7967
    if (s_CommandQueue != NULL) {
7968
        renderer->commandQueue = s_CommandQueue;
7969
    } else {
7970
#endif
7971
        queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
7972
        queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
7973
        queueDesc.NodeMask = 0;
7974
        queueDesc.Priority = 0;
7975

7976
        res = ID3D12Device_CreateCommandQueue(
7977
            renderer->device,
7978
            &queueDesc,
7979
            D3D_GUID(D3D_IID_ID3D12CommandQueue),
7980
            (void **)&renderer->commandQueue);
7981

7982
        if (FAILED(res)) {
7983
            D3D12_INTERNAL_DestroyRenderer(renderer);
7984
            ERROR_CHECK_RETURN("Could not create D3D12CommandQueue", NULL);
7985
        }
7986
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
7987
        s_CommandQueue = renderer->commandQueue;
7988
    }
7989
#endif
7990

7991
    // Create indirect command signatures
7992

7993
    D3D12_COMMAND_SIGNATURE_DESC commandSignatureDesc;
7994
    D3D12_INDIRECT_ARGUMENT_DESC indirectArgumentDesc;
7995
    SDL_zero(indirectArgumentDesc);
7996

7997
    indirectArgumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
7998
    commandSignatureDesc.NodeMask = 0;
7999
    commandSignatureDesc.ByteStride = sizeof(SDL_GPUIndirectDrawCommand);
8000
    commandSignatureDesc.NumArgumentDescs = 1;
8001
    commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc;
8002

8003
    res = ID3D12Device_CreateCommandSignature(
8004
        renderer->device,
8005
        &commandSignatureDesc,
8006
        NULL,
8007
        D3D_GUID(D3D_IID_ID3D12CommandSignature),
8008
        (void **)&renderer->indirectDrawCommandSignature);
8009
    if (FAILED(res)) {
8010
        D3D12_INTERNAL_DestroyRenderer(renderer);
8011
        ERROR_CHECK_RETURN("Could not create indirect draw command signature", NULL)
8012
    }
8013

8014
    indirectArgumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
8015
    commandSignatureDesc.ByteStride = sizeof(SDL_GPUIndexedIndirectDrawCommand);
8016
    commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc;
8017

8018
    res = ID3D12Device_CreateCommandSignature(
8019
        renderer->device,
8020
        &commandSignatureDesc,
8021
        NULL,
8022
        D3D_GUID(D3D_IID_ID3D12CommandSignature),
8023
        (void **)&renderer->indirectIndexedDrawCommandSignature);
8024
    if (FAILED(res)) {
8025
        D3D12_INTERNAL_DestroyRenderer(renderer);
8026
        ERROR_CHECK_RETURN("Could not create indirect indexed draw command signature", NULL)
8027
    }
8028

8029
    indirectArgumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
8030
    commandSignatureDesc.ByteStride = sizeof(SDL_GPUIndirectDispatchCommand);
8031
    commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc;
8032

8033
    res = ID3D12Device_CreateCommandSignature(
8034
        renderer->device,
8035
        &commandSignatureDesc,
8036
        NULL,
8037
        D3D_GUID(D3D_IID_ID3D12CommandSignature),
8038
        (void **)&renderer->indirectDispatchCommandSignature);
8039
    if (FAILED(res)) {
8040
        D3D12_INTERNAL_DestroyRenderer(renderer);
8041
        ERROR_CHECK_RETURN("Could not create indirect dispatch command signature", NULL)
8042
    }
8043

8044
    // Initialize pools
8045

8046
    renderer->submittedCommandBufferCapacity = 4;
8047
    renderer->submittedCommandBufferCount = 0;
8048
    renderer->submittedCommandBuffers = (D3D12CommandBuffer **)SDL_calloc(
8049
        renderer->submittedCommandBufferCapacity, sizeof(D3D12CommandBuffer *));
8050
    if (!renderer->submittedCommandBuffers) {
8051
        D3D12_INTERNAL_DestroyRenderer(renderer);
8052
        return NULL;
8053
    }
8054

8055
    renderer->uniformBufferPoolCapacity = 4;
8056
    renderer->uniformBufferPoolCount = 0;
8057
    renderer->uniformBufferPool = (D3D12UniformBuffer **)SDL_calloc(
8058
        renderer->uniformBufferPoolCapacity, sizeof(D3D12UniformBuffer *));
8059
    if (!renderer->uniformBufferPool) {
8060
        D3D12_INTERNAL_DestroyRenderer(renderer);
8061
        return NULL;
8062
    }
8063

8064
    renderer->claimedWindowCapacity = 4;
8065
    renderer->claimedWindowCount = 0;
8066
    renderer->claimedWindows = (D3D12WindowData **)SDL_calloc(
8067
        renderer->claimedWindowCapacity, sizeof(D3D12WindowData *));
8068
    if (!renderer->claimedWindows) {
8069
        D3D12_INTERNAL_DestroyRenderer(renderer);
8070
        return NULL;
8071
    }
8072

8073
    renderer->availableFenceCapacity = 4;
8074
    renderer->availableFenceCount = 0;
8075
    renderer->availableFences = (D3D12Fence **)SDL_calloc(
8076
        renderer->availableFenceCapacity, sizeof(D3D12Fence *));
8077
    if (!renderer->availableFences) {
8078
        D3D12_INTERNAL_DestroyRenderer(renderer);
8079
        return NULL;
8080
    }
8081

8082
    // Initialize CPU descriptor heaps
8083
    for (Uint32 i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; i += 1) {
8084
        renderer->stagingDescriptorHeaps[i] = D3D12_INTERNAL_CreateDescriptorHeap(
8085
            renderer,
8086
            (D3D12_DESCRIPTOR_HEAP_TYPE)i,
8087
            (i <= D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) ? VIEW_SAMPLER_STAGING_DESCRIPTOR_COUNT : TARGET_STAGING_DESCRIPTOR_COUNT,
8088
            true);
8089
    }
8090

8091
    // Initialize GPU descriptor heaps
8092
    for (Uint32 i = 0; i < 2; i += 1) {
8093
        renderer->descriptorHeapPools[i].lock = SDL_CreateMutex();
8094
        renderer->descriptorHeapPools[i].capacity = 4;
8095
        renderer->descriptorHeapPools[i].count = 4;
8096
        renderer->descriptorHeapPools[i].heaps = (D3D12DescriptorHeap **)SDL_calloc(
8097
            renderer->descriptorHeapPools[i].capacity, sizeof(D3D12DescriptorHeap *));
8098

8099
        for (Uint32 j = 0; j < renderer->descriptorHeapPools[i].capacity; j += 1) {
8100
            renderer->descriptorHeapPools[i].heaps[j] = D3D12_INTERNAL_CreateDescriptorHeap(
8101
                renderer,
8102
                (D3D12_DESCRIPTOR_HEAP_TYPE)i,
8103
                i == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ? VIEW_GPU_DESCRIPTOR_COUNT : SAMPLER_GPU_DESCRIPTOR_COUNT,
8104
                false);
8105
        }
8106
    }
8107

8108
    // Deferred resource releasing
8109

8110
    renderer->buffersToDestroyCapacity = 4;
8111
    renderer->buffersToDestroyCount = 0;
8112
    renderer->buffersToDestroy = (D3D12Buffer **)SDL_calloc(
8113
        renderer->buffersToDestroyCapacity, sizeof(D3D12Buffer *));
8114
    if (!renderer->buffersToDestroy) {
8115
        D3D12_INTERNAL_DestroyRenderer(renderer);
8116
        return NULL;
8117
    }
8118

8119
    renderer->texturesToDestroyCapacity = 4;
8120
    renderer->texturesToDestroyCount = 0;
8121
    renderer->texturesToDestroy = (D3D12Texture **)SDL_calloc(
8122
        renderer->texturesToDestroyCapacity, sizeof(D3D12Texture *));
8123
    if (!renderer->texturesToDestroy) {
8124
        D3D12_INTERNAL_DestroyRenderer(renderer);
8125
        return NULL;
8126
    }
8127

8128
    renderer->samplersToDestroyCapacity = 4;
8129
    renderer->samplersToDestroyCount = 0;
8130
    renderer->samplersToDestroy = (D3D12Sampler **)SDL_calloc(
8131
        renderer->samplersToDestroyCapacity, sizeof(D3D12Sampler *));
8132
    if (!renderer->samplersToDestroy) {
8133
        D3D12_INTERNAL_DestroyRenderer(renderer);
8134
        return NULL;
8135
    }
8136

8137
    renderer->graphicsPipelinesToDestroyCapacity = 4;
8138
    renderer->graphicsPipelinesToDestroyCount = 0;
8139
    renderer->graphicsPipelinesToDestroy = (D3D12GraphicsPipeline **)SDL_calloc(
8140
        renderer->graphicsPipelinesToDestroyCapacity, sizeof(D3D12GraphicsPipeline *));
8141
    if (!renderer->graphicsPipelinesToDestroy) {
8142
        D3D12_INTERNAL_DestroyRenderer(renderer);
8143
        return NULL;
8144
    }
8145

8146
    renderer->computePipelinesToDestroyCapacity = 4;
8147
    renderer->computePipelinesToDestroyCount = 0;
8148
    renderer->computePipelinesToDestroy = (D3D12ComputePipeline **)SDL_calloc(
8149
        renderer->computePipelinesToDestroyCapacity, sizeof(D3D12ComputePipeline *));
8150
    if (!renderer->computePipelinesToDestroy) {
8151
        D3D12_INTERNAL_DestroyRenderer(renderer);
8152
        return NULL;
8153
    }
8154

8155
    // Locks
8156
    renderer->stagingDescriptorHeapLock = SDL_CreateMutex();
8157
    renderer->acquireCommandBufferLock = SDL_CreateMutex();
8158
    renderer->acquireUniformBufferLock = SDL_CreateMutex();
8159
    renderer->submitLock = SDL_CreateMutex();
8160
    renderer->windowLock = SDL_CreateMutex();
8161
    renderer->fenceLock = SDL_CreateMutex();
8162
    renderer->disposeLock = SDL_CreateMutex();
8163

8164
    renderer->debugMode = debugMode;
8165

8166
    renderer->semantic = SDL_GetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING, "TEXCOORD");
8167

8168
    // Blit resources
8169
    D3D12_INTERNAL_InitBlitResources(renderer);
8170

8171
    // Create the SDL_GPU Device
8172
    result = (SDL_GPUDevice *)SDL_calloc(1, sizeof(SDL_GPUDevice));
8173

8174
    if (!result) {
8175
        D3D12_INTERNAL_DestroyRenderer(renderer);
8176
        return NULL;
8177
    }
8178

8179
    ASSIGN_DRIVER(D3D12)
8180
    result->driverData = (SDL_GPURenderer *)renderer;
8181
    result->debugMode = debugMode;
8182
    renderer->sdlGPUDevice = result;
8183

8184
    return result;
8185
}
8186

8187
SDL_GPUBootstrap D3D12Driver = {
8188
    "direct3d12",
8189
    SDL_GPU_DRIVER_D3D12,
8190
    SDL_GPU_SHADERFORMAT_DXIL,
8191
    D3D12_PrepareDriver,
8192
    D3D12_CreateDevice
8193
};
8194

8195
#endif // SDL_GPU_D3D12
8196

8197
// GDK-specific APIs
8198

8199
#ifdef SDL_PLATFORM_GDK
8200

8201
void SDL_GDKSuspendGPU(SDL_GPUDevice *device)
8202
{
8203
#if defined(SDL_GPU_D3D12) && (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
8204
    D3D12Renderer *renderer = (D3D12Renderer *)device->driverData;
8205
    HRESULT res;
8206
    if (device == NULL) {
8207
        SDL_SetError("Invalid GPU device");
8208
        return;
8209
    }
8210

8211
    SDL_LockMutex(renderer->submitLock);
8212
    res = renderer->commandQueue->SuspendX(0);
8213
    if (FAILED(res)) {
8214
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "SuspendX failed: %X", res);
8215
    }
8216
    SDL_UnlockMutex(renderer->submitLock);
8217
#endif
8218
}
8219

8220
void SDL_GDKResumeGPU(SDL_GPUDevice *device)
8221
{
8222
#if defined(SDL_GPU_D3D12) && (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
8223
    D3D12Renderer *renderer = (D3D12Renderer *)device->driverData;
8224
    HRESULT res;
8225
    if (device == NULL) {
8226
        SDL_SetError("Invalid GPU device");
8227
        return;
8228
    }
8229

8230
    SDL_LockMutex(renderer->submitLock);
8231
    res = renderer->commandQueue->ResumeX();
8232
    if (FAILED(res)) {
8233
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "ResumeX failed: %X", res);
8234
    }
8235
    SDL_UnlockMutex(renderer->submitLock);
8236

8237
    res = renderer->device->SetFrameIntervalX(
8238
        NULL,
8239
        D3D12XBOX_FRAME_INTERVAL_60_HZ,
8240
        MAX_FRAMES_IN_FLIGHT - 1,
8241
        D3D12XBOX_FRAME_INTERVAL_FLAG_NONE);
8242
    if (FAILED(res)) {
8243
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not set frame interval: %X", res);
8244
    }
8245

8246
    res = renderer->device->ScheduleFrameEventX(
8247
        D3D12XBOX_FRAME_EVENT_ORIGIN,
8248
        0,
8249
        NULL,
8250
        D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE);
8251
    if (FAILED(res)) {
8252
        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not schedule frame events: %X", res);
8253
    }
8254
#endif
8255
}
8256

8257
#endif // SDL_PLATFORM_GDK
8258

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.