summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/render/direct3d12/SDL_render_d3d12.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/render/direct3d12/SDL_render_d3d12.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/render/direct3d12/SDL_render_d3d12.c')
-rw-r--r--contrib/SDL-3.2.8/src/render/direct3d12/SDL_render_d3d12.c3309
1 files changed, 3309 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/render/direct3d12/SDL_render_d3d12.c b/contrib/SDL-3.2.8/src/render/direct3d12/SDL_render_d3d12.c
new file mode 100644
index 0000000..06535f0
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/render/direct3d12/SDL_render_d3d12.c
@@ -0,0 +1,3309 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_RENDER_D3D12
24
25#define SDL_D3D12_NUM_BUFFERS 2
26#define SDL_D3D12_NUM_VERTEX_BUFFERS 256
27#define SDL_D3D12_MAX_NUM_TEXTURES 16384
28#define SDL_D3D12_NUM_UPLOAD_BUFFERS 32
29
30#include "../../core/windows/SDL_windows.h"
31#include "../../video/windows/SDL_windowswindow.h"
32#include "../SDL_sysrender.h"
33#include "../SDL_d3dmath.h"
34#include "../../video/directx/SDL_d3d12.h"
35
36#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
37#include "SDL_render_d3d12_xbox.h"
38#endif
39
40#include "SDL_shaders_d3d12.h"
41
42#if defined(_MSC_VER) && !defined(__clang__)
43#define SDL_COMPOSE_ERROR(str) __FUNCTION__ ", " str
44#else
45#define SDL_COMPOSE_ERROR(str) SDL_STRINGIFY_ARG(__FUNCTION__) ", " str
46#endif
47
48// Set up for C function definitions, even when using C++
49#ifdef __cplusplus
50extern "C" {
51#endif
52
53// This must be included here as the function definitions in SDL_pixels.c/_c.h are C, not C++
54#include "../../video/SDL_pixels_c.h"
55
56/* !!! FIXME: vertex buffer bandwidth could be lower; only use UV coords when
57 !!! FIXME: textures are needed. */
58
59// Sampler types
60typedef enum
61{
62 D3D12_SAMPLER_NEAREST_CLAMP,
63 D3D12_SAMPLER_NEAREST_WRAP,
64 D3D12_SAMPLER_LINEAR_CLAMP,
65 D3D12_SAMPLER_LINEAR_WRAP,
66 D3D12_SAMPLER_COUNT
67} D3D12_Sampler;
68
69// Vertex shader, common values
70typedef struct
71{
72 Float4X4 model;
73 Float4X4 projectionAndView;
74} D3D12_VertexShaderConstants;
75
76// These should mirror the definitions in D3D12_PixelShader_Common.hlsli
77//static const float TONEMAP_NONE = 0;
78//static const float TONEMAP_LINEAR = 1;
79static const float TONEMAP_CHROME = 2;
80
81//static const float TEXTURETYPE_NONE = 0;
82static const float TEXTURETYPE_RGB = 1;
83static const float TEXTURETYPE_NV12 = 2;
84static const float TEXTURETYPE_NV21 = 3;
85static const float TEXTURETYPE_YUV = 4;
86
87static const float INPUTTYPE_UNSPECIFIED = 0;
88static const float INPUTTYPE_SRGB = 1;
89static const float INPUTTYPE_SCRGB = 2;
90static const float INPUTTYPE_HDR10 = 3;
91
92typedef struct
93{
94 float scRGB_output;
95 float texture_type;
96 float input_type;
97 float color_scale;
98
99 float tonemap_method;
100 float tonemap_factor1;
101 float tonemap_factor2;
102 float sdr_white_point;
103
104 float YCbCr_matrix[16];
105} D3D12_PixelShaderConstants;
106
107// Per-vertex data
108typedef struct
109{
110 Float2 pos;
111 Float2 tex;
112 SDL_FColor color;
113} D3D12_VertexPositionColor;
114
115// Per-texture data
116typedef struct
117{
118 int w, h;
119 ID3D12Resource *mainTexture;
120 D3D12_CPU_DESCRIPTOR_HANDLE mainTextureResourceView;
121 D3D12_RESOURCE_STATES mainResourceState;
122 SIZE_T mainSRVIndex;
123 D3D12_CPU_DESCRIPTOR_HANDLE mainTextureRenderTargetView;
124 DXGI_FORMAT mainTextureFormat;
125 ID3D12Resource *stagingBuffer;
126 D3D12_RESOURCE_STATES stagingResourceState;
127 D3D12_FILTER scaleMode;
128 D3D12_Shader shader;
129 const float *YCbCr_matrix;
130#ifdef SDL_HAVE_YUV
131 // YV12 texture support
132 bool yuv;
133 ID3D12Resource *mainTextureU;
134 D3D12_CPU_DESCRIPTOR_HANDLE mainTextureResourceViewU;
135 D3D12_RESOURCE_STATES mainResourceStateU;
136 SIZE_T mainSRVIndexU;
137 ID3D12Resource *mainTextureV;
138 D3D12_CPU_DESCRIPTOR_HANDLE mainTextureResourceViewV;
139 D3D12_RESOURCE_STATES mainResourceStateV;
140 SIZE_T mainSRVIndexV;
141
142 // NV12 texture support
143 bool nv12;
144 D3D12_CPU_DESCRIPTOR_HANDLE mainTextureResourceViewNV;
145 SIZE_T mainSRVIndexNV;
146
147 Uint8 *pixels;
148 int pitch;
149#endif
150 SDL_Rect lockedRect;
151} D3D12_TextureData;
152
153// Pipeline State Object data
154typedef struct
155{
156 D3D12_Shader shader;
157 D3D12_PixelShaderConstants shader_constants;
158 SDL_BlendMode blendMode;
159 D3D12_PRIMITIVE_TOPOLOGY_TYPE topology;
160 DXGI_FORMAT rtvFormat;
161 ID3D12PipelineState *pipelineState;
162} D3D12_PipelineState;
163
164// Vertex Buffer
165typedef struct
166{
167 ID3D12Resource *resource;
168 D3D12_VERTEX_BUFFER_VIEW view;
169 size_t size;
170} D3D12_VertexBuffer;
171
172// For SRV pool allocator
173typedef struct
174{
175 SIZE_T index;
176 void *next;
177} D3D12_SRVPoolNode;
178
179// Private renderer data
180typedef struct
181{
182 SDL_SharedObject *hDXGIMod;
183 SDL_SharedObject *hD3D12Mod;
184#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
185 UINT64 frameToken;
186#else
187 IDXGIFactory6 *dxgiFactory;
188 IDXGIAdapter4 *dxgiAdapter;
189 IDXGIDebug *dxgiDebug;
190 IDXGISwapChain4 *swapChain;
191#endif
192 ID3D12Device1 *d3dDevice;
193 ID3D12Debug *debugInterface;
194 ID3D12CommandQueue *commandQueue;
195 ID3D12GraphicsCommandList2 *commandList;
196 DXGI_SWAP_EFFECT swapEffect;
197 UINT swapFlags;
198 UINT syncInterval;
199 UINT presentFlags;
200 DXGI_FORMAT renderTargetFormat;
201 bool pixelSizeChanged;
202
203 // Descriptor heaps
204 ID3D12DescriptorHeap *rtvDescriptorHeap;
205 UINT rtvDescriptorSize;
206 ID3D12DescriptorHeap *textureRTVDescriptorHeap;
207 ID3D12DescriptorHeap *srvDescriptorHeap;
208 UINT srvDescriptorSize;
209 ID3D12DescriptorHeap *samplerDescriptorHeap;
210 UINT samplerDescriptorSize;
211
212 // Data needed per backbuffer
213 ID3D12CommandAllocator *commandAllocators[SDL_D3D12_NUM_BUFFERS];
214 ID3D12Resource *renderTargets[SDL_D3D12_NUM_BUFFERS];
215 UINT64 fenceValue;
216 int currentBackBufferIndex;
217
218 // Fences
219 ID3D12Fence *fence;
220 HANDLE fenceEvent;
221
222 // Root signature and pipeline state data
223 ID3D12RootSignature *rootSignatures[NUM_ROOTSIGS];
224 int pipelineStateCount;
225 D3D12_PipelineState *pipelineStates;
226 D3D12_PipelineState *currentPipelineState;
227
228 D3D12_VertexBuffer vertexBuffers[SDL_D3D12_NUM_VERTEX_BUFFERS];
229 D3D12_CPU_DESCRIPTOR_HANDLE samplers[D3D12_SAMPLER_COUNT];
230
231 // Data for staging/allocating textures
232 ID3D12Resource *uploadBuffers[SDL_D3D12_NUM_UPLOAD_BUFFERS];
233 int currentUploadBuffer;
234
235 // Pool allocator to handle reusing SRV heap indices
236 D3D12_SRVPoolNode *srvPoolHead;
237 D3D12_SRVPoolNode srvPoolNodes[SDL_D3D12_MAX_NUM_TEXTURES];
238
239 // Vertex buffer constants
240 D3D12_VertexShaderConstants vertexShaderConstantsData;
241
242 // Cached renderer properties
243 DXGI_MODE_ROTATION rotation;
244 D3D12_TextureData *textureRenderTarget;
245 D3D12_CPU_DESCRIPTOR_HANDLE currentRenderTargetView;
246 D3D12_CPU_DESCRIPTOR_HANDLE currentShaderResource;
247 D3D12_CPU_DESCRIPTOR_HANDLE currentSampler;
248 bool cliprectDirty;
249 bool currentCliprectEnabled;
250 SDL_Rect currentCliprect;
251 SDL_Rect currentViewport;
252 int currentViewportRotation;
253 bool viewportDirty;
254 Float4X4 identity;
255 int currentVertexBuffer;
256 bool issueBatch;
257} D3D12_RenderData;
258
259// Define D3D GUIDs here so we don't have to include uuid.lib.
260
261#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
262#pragma GCC diagnostic push
263#pragma GCC diagnostic ignored "-Wunused-const-variable"
264#endif
265
266static const GUID SDL_IID_IDXGIFactory6 = { 0xc1b6694f, 0xff09, 0x44a9, { 0xb0, 0x3c, 0x77, 0x90, 0x0a, 0x0a, 0x1d, 0x17 } };
267static const GUID SDL_IID_IDXGIAdapter4 = { 0x3c8d99d1, 0x4fbf, 0x4181, { 0xa8, 0x2c, 0xaf, 0x66, 0xbf, 0x7b, 0xd2, 0x4e } };
268static const GUID SDL_IID_IDXGIDevice1 = { 0x77db970f, 0x6276, 0x48ba, { 0xba, 0x28, 0x07, 0x01, 0x43, 0xb4, 0x39, 0x2c } };
269static const GUID SDL_IID_ID3D12Device1 = { 0x77acce80, 0x638e, 0x4e65, { 0x88, 0x95, 0xc1, 0xf2, 0x33, 0x86, 0x86, 0x3e } };
270static const GUID SDL_IID_IDXGISwapChain4 = { 0x3D585D5A, 0xBD4A, 0x489E, { 0xB1, 0xF4, 0x3D, 0xBC, 0xB6, 0x45, 0x2F, 0xFB } };
271static const GUID SDL_IID_IDXGIDebug1 = { 0xc5a05f0c, 0x16f2, 0x4adf, { 0x9f, 0x4d, 0xa8, 0xc4, 0xd5, 0x8a, 0xc5, 0x50 } };
272static const GUID SDL_IID_IDXGIInfoQueue = { 0xD67441C7, 0x672A, 0x476f, { 0x9E, 0x82, 0xCD, 0x55, 0xB4, 0x49, 0x49, 0xCE } };
273static const GUID SDL_IID_ID3D12Debug = { 0x344488b7, 0x6846, 0x474b, { 0xb9, 0x89, 0xf0, 0x27, 0x44, 0x82, 0x45, 0xe0 } };
274static const GUID SDL_DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, { 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x8 } };
275static const GUID SDL_IID_ID3D12CommandQueue = { 0x0ec870a6, 0x5d7e, 0x4c22, { 0x8c, 0xfc, 0x5b, 0xaa, 0xe0, 0x76, 0x16, 0xed } };
276static const GUID SDL_IID_ID3D12DescriptorHeap = { 0x8efb471d, 0x616c, 0x4f49, { 0x90, 0xf7, 0x12, 0x7b, 0xb7, 0x63, 0xfa, 0x51 } };
277static const GUID SDL_IID_ID3D12CommandAllocator = { 0x6102dee4, 0xaf59, 0x4b09, { 0xb9, 0x99, 0xb4, 0x4d, 0x73, 0xf0, 0x9b, 0x24 } };
278static const GUID SDL_IID_ID3D12GraphicsCommandList2 = { 0x38C3E585, 0xFF17, 0x412C, { 0x91, 0x50, 0x4F, 0xC6, 0xF9, 0xD7, 0x2A, 0x28 } };
279static const GUID SDL_IID_ID3D12Fence = { 0x0a753dcf, 0xc4d8, 0x4b91, { 0xad, 0xf6, 0xbe, 0x5a, 0x60, 0xd9, 0x5a, 0x76 } };
280static const GUID SDL_IID_ID3D12Resource = { 0x696442be, 0xa72e, 0x4059, { 0xbc, 0x79, 0x5b, 0x5c, 0x98, 0x04, 0x0f, 0xad } };
281static const GUID SDL_IID_ID3D12RootSignature = { 0xc54a6b66, 0x72df, 0x4ee8, { 0x8b, 0xe5, 0xa9, 0x46, 0xa1, 0x42, 0x92, 0x14 } };
282static const GUID SDL_IID_ID3D12PipelineState = { 0x765a30f3, 0xf624, 0x4c6f, { 0xa8, 0x28, 0xac, 0xe9, 0x48, 0x62, 0x24, 0x45 } };
283static const GUID SDL_IID_ID3D12Heap = { 0x6b3b2502, 0x6e51, 0x45b3, { 0x90, 0xee, 0x98, 0x84, 0x26, 0x5e, 0x8d, 0xf3 } };
284static const GUID SDL_IID_ID3D12InfoQueue = { 0x0742a90b, 0xc387, 0x483f, { 0xb9, 0x46, 0x30, 0xa7, 0xe4, 0xe6, 0x14, 0x58 } };
285
286#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
287#pragma GCC diagnostic pop
288#endif
289
290static UINT D3D12_Align(UINT location, UINT alignment)
291{
292 return (location + (alignment - 1)) & ~(alignment - 1);
293}
294
295static SDL_PixelFormat D3D12_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat)
296{
297 switch (dxgiFormat) {
298 case DXGI_FORMAT_B8G8R8A8_UNORM:
299 case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
300 return SDL_PIXELFORMAT_ARGB8888;
301 case DXGI_FORMAT_R8G8B8A8_UNORM:
302 case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
303 return SDL_PIXELFORMAT_ABGR8888;
304 case DXGI_FORMAT_B8G8R8X8_UNORM:
305 case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
306 return SDL_PIXELFORMAT_XRGB8888;
307 case DXGI_FORMAT_R10G10B10A2_UNORM:
308 return SDL_PIXELFORMAT_ABGR2101010;
309 case DXGI_FORMAT_R16G16B16A16_FLOAT:
310 return SDL_PIXELFORMAT_RGBA64_FLOAT;
311 default:
312 return SDL_PIXELFORMAT_UNKNOWN;
313 }
314}
315
316static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 output_colorspace)
317{
318 switch (format) {
319 case SDL_PIXELFORMAT_RGBA64_FLOAT:
320 return DXGI_FORMAT_R16G16B16A16_FLOAT;
321 case SDL_PIXELFORMAT_ABGR2101010:
322 return DXGI_FORMAT_R10G10B10A2_UNORM;
323 case SDL_PIXELFORMAT_ARGB8888:
324 if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
325 return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
326 }
327 return DXGI_FORMAT_B8G8R8A8_UNORM;
328 case SDL_PIXELFORMAT_ABGR8888:
329 if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
330 return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
331 }
332 return DXGI_FORMAT_R8G8B8A8_UNORM;
333 case SDL_PIXELFORMAT_XRGB8888:
334 if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
335 return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
336 }
337 return DXGI_FORMAT_B8G8R8X8_UNORM;
338 case SDL_PIXELFORMAT_YV12:
339 case SDL_PIXELFORMAT_IYUV:
340 return DXGI_FORMAT_R8_UNORM;
341 case SDL_PIXELFORMAT_NV12:
342 case SDL_PIXELFORMAT_NV21:
343 return DXGI_FORMAT_NV12;
344 case SDL_PIXELFORMAT_P010:
345 return DXGI_FORMAT_P010;
346 default:
347 return DXGI_FORMAT_UNKNOWN;
348 }
349}
350
351static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uint32 colorspace)
352{
353 switch (format) {
354 case SDL_PIXELFORMAT_RGBA64_FLOAT:
355 return DXGI_FORMAT_R16G16B16A16_FLOAT;
356 case SDL_PIXELFORMAT_ABGR2101010:
357 return DXGI_FORMAT_R10G10B10A2_UNORM;
358 case SDL_PIXELFORMAT_ARGB8888:
359 if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
360 return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
361 }
362 return DXGI_FORMAT_B8G8R8A8_UNORM;
363 case SDL_PIXELFORMAT_ABGR8888:
364 if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
365 return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
366 }
367 return DXGI_FORMAT_R8G8B8A8_UNORM;
368 case SDL_PIXELFORMAT_XRGB8888:
369 if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
370 return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
371 }
372 return DXGI_FORMAT_B8G8R8X8_UNORM;
373 case SDL_PIXELFORMAT_YV12:
374 case SDL_PIXELFORMAT_IYUV:
375 case SDL_PIXELFORMAT_NV12: // For the Y texture
376 case SDL_PIXELFORMAT_NV21: // For the Y texture
377 return DXGI_FORMAT_R8_UNORM;
378 case SDL_PIXELFORMAT_P010: // For the Y texture
379 return DXGI_FORMAT_R16_UNORM;
380 default:
381 return DXGI_FORMAT_UNKNOWN;
382 }
383}
384
385static void D3D12_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture);
386
387static void D3D12_ReleaseAll(SDL_Renderer *renderer)
388{
389 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
390
391 SDL_PropertiesID props = SDL_GetRendererProperties(renderer);
392 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_D3D12_DEVICE_POINTER, NULL);
393 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER, NULL);
394
395 // Release all textures
396 for (SDL_Texture *texture = renderer->textures; texture; texture = texture->next) {
397 D3D12_DestroyTexture(renderer, texture);
398 }
399
400 // Release/reset everything else
401 if (data) {
402 int i;
403
404#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
405 D3D_SAFE_RELEASE(data->dxgiFactory);
406 D3D_SAFE_RELEASE(data->dxgiAdapter);
407 D3D_SAFE_RELEASE(data->swapChain);
408#endif
409 D3D_SAFE_RELEASE(data->d3dDevice);
410 D3D_SAFE_RELEASE(data->debugInterface);
411 D3D_SAFE_RELEASE(data->commandQueue);
412 D3D_SAFE_RELEASE(data->commandList);
413 D3D_SAFE_RELEASE(data->rtvDescriptorHeap);
414 D3D_SAFE_RELEASE(data->textureRTVDescriptorHeap);
415 D3D_SAFE_RELEASE(data->srvDescriptorHeap);
416 D3D_SAFE_RELEASE(data->samplerDescriptorHeap);
417 D3D_SAFE_RELEASE(data->fence);
418
419 for (i = 0; i < SDL_D3D12_NUM_BUFFERS; ++i) {
420 D3D_SAFE_RELEASE(data->commandAllocators[i]);
421 D3D_SAFE_RELEASE(data->renderTargets[i]);
422 }
423
424 if (data->pipelineStateCount > 0) {
425 for (i = 0; i < data->pipelineStateCount; ++i) {
426 D3D_SAFE_RELEASE(data->pipelineStates[i].pipelineState);
427 }
428 SDL_free(data->pipelineStates);
429 data->pipelineStates = NULL;
430 data->pipelineStateCount = 0;
431 }
432
433 for (i = 0; i < NUM_ROOTSIGS; ++i) {
434 D3D_SAFE_RELEASE(data->rootSignatures[i]);
435 }
436
437 for (i = 0; i < SDL_D3D12_NUM_VERTEX_BUFFERS; ++i) {
438 D3D_SAFE_RELEASE(data->vertexBuffers[i].resource);
439 data->vertexBuffers[i].size = 0;
440 }
441
442 data->swapEffect = (DXGI_SWAP_EFFECT)0;
443 data->swapFlags = 0;
444 data->currentRenderTargetView.ptr = 0;
445 data->currentSampler.ptr = 0;
446
447#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
448 // Check for any leaks if in debug mode
449 if (data->dxgiDebug) {
450 DXGI_DEBUG_RLO_FLAGS rloFlags = (DXGI_DEBUG_RLO_FLAGS)(DXGI_DEBUG_RLO_DETAIL | DXGI_DEBUG_RLO_IGNORE_INTERNAL);
451 IDXGIDebug_ReportLiveObjects(data->dxgiDebug, SDL_DXGI_DEBUG_ALL, rloFlags);
452 D3D_SAFE_RELEASE(data->dxgiDebug);
453 }
454#endif
455
456 /* Unload the D3D libraries. This should be done last, in order
457 * to prevent IUnknown::Release() calls from crashing.
458 */
459 if (data->hD3D12Mod) {
460 SDL_UnloadObject(data->hD3D12Mod);
461 data->hD3D12Mod = NULL;
462 }
463 if (data->hDXGIMod) {
464 SDL_UnloadObject(data->hDXGIMod);
465 data->hDXGIMod = NULL;
466 }
467 }
468}
469
470static D3D12_GPU_DESCRIPTOR_HANDLE D3D12_CPUtoGPUHandle(ID3D12DescriptorHeap *heap, D3D12_CPU_DESCRIPTOR_HANDLE CPUHandle)
471{
472 D3D12_CPU_DESCRIPTOR_HANDLE CPUHeapStart;
473 D3D12_GPU_DESCRIPTOR_HANDLE GPUHandle;
474 SIZE_T offset;
475
476 // Calculate the correct offset into the heap
477 D3D_CALL_RET(heap, GetCPUDescriptorHandleForHeapStart, &CPUHeapStart);
478 offset = CPUHandle.ptr - CPUHeapStart.ptr;
479
480 D3D_CALL_RET(heap, GetGPUDescriptorHandleForHeapStart, &GPUHandle);
481 GPUHandle.ptr += offset;
482
483 return GPUHandle;
484}
485
486static void D3D12_WaitForGPU(D3D12_RenderData *data)
487{
488 if (data->commandQueue && data->fence && data->fenceEvent) {
489 ID3D12CommandQueue_Signal(data->commandQueue, data->fence, data->fenceValue);
490 if (ID3D12Fence_GetCompletedValue(data->fence) < data->fenceValue) {
491 ID3D12Fence_SetEventOnCompletion(data->fence,
492 data->fenceValue,
493 data->fenceEvent);
494 WaitForSingleObjectEx(data->fenceEvent, INFINITE, FALSE);
495 }
496
497 data->fenceValue++;
498 }
499}
500
501static D3D12_CPU_DESCRIPTOR_HANDLE D3D12_GetCurrentRenderTargetView(SDL_Renderer *renderer)
502{
503 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
504 D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptor;
505
506 if (data->textureRenderTarget) {
507 return data->textureRenderTarget->mainTextureRenderTargetView;
508 }
509
510 SDL_zero(rtvDescriptor);
511 D3D_CALL_RET(data->rtvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &rtvDescriptor);
512 rtvDescriptor.ptr += data->currentBackBufferIndex * data->rtvDescriptorSize;
513 return rtvDescriptor;
514}
515
516static void D3D12_TransitionResource(D3D12_RenderData *data,
517 ID3D12Resource *resource,
518 D3D12_RESOURCE_STATES beforeState,
519 D3D12_RESOURCE_STATES afterState)
520{
521 D3D12_RESOURCE_BARRIER barrier;
522
523 if (beforeState != afterState) {
524 SDL_zero(barrier);
525 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
526 barrier.Transition.pResource = resource;
527 barrier.Transition.StateBefore = beforeState;
528 barrier.Transition.StateAfter = afterState;
529 barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
530
531 ID3D12GraphicsCommandList2_ResourceBarrier(data->commandList, 1, &barrier);
532 }
533}
534
535static void D3D12_ResetCommandList(D3D12_RenderData *data)
536{
537 int i;
538 ID3D12DescriptorHeap *rootDescriptorHeaps[] = { data->srvDescriptorHeap, data->samplerDescriptorHeap };
539 ID3D12CommandAllocator *commandAllocator = data->commandAllocators[data->currentBackBufferIndex];
540
541 ID3D12CommandAllocator_Reset(commandAllocator);
542 ID3D12GraphicsCommandList2_Reset(data->commandList, commandAllocator, NULL);
543 data->currentPipelineState = NULL;
544 data->currentVertexBuffer = 0;
545 data->issueBatch = false;
546 data->cliprectDirty = true;
547 data->viewportDirty = true;
548 data->currentRenderTargetView.ptr = 0;
549 // FIXME should we also clear currentSampler.ptr and currentRenderTargetView.ptr ? (and use D3D12_InvalidateCachedState() instead)
550
551 // Release any upload buffers that were inflight
552 for (i = 0; i < data->currentUploadBuffer; ++i) {
553 D3D_SAFE_RELEASE(data->uploadBuffers[i]);
554 }
555 data->currentUploadBuffer = 0;
556
557 ID3D12GraphicsCommandList2_SetDescriptorHeaps(data->commandList, 2, rootDescriptorHeaps);
558}
559
560static HRESULT D3D12_IssueBatch(D3D12_RenderData *data)
561{
562 HRESULT result = S_OK;
563
564 // Issue the command list
565 result = ID3D12GraphicsCommandList2_Close(data->commandList);
566 if (FAILED(result)) {
567 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("D3D12_IssueBatch"), result);
568 return result;
569 }
570 ID3D12CommandQueue_ExecuteCommandLists(data->commandQueue, 1, (ID3D12CommandList *const *)&data->commandList);
571
572 D3D12_WaitForGPU(data);
573
574 D3D12_ResetCommandList(data);
575
576 return result;
577}
578
579static void D3D12_DestroyRenderer(SDL_Renderer *renderer)
580{
581 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
582 if (data) {
583 D3D12_WaitForGPU(data);
584 D3D12_ReleaseAll(renderer);
585 SDL_free(data);
586 }
587}
588
589static D3D12_BLEND GetBlendFunc(SDL_BlendFactor factor)
590{
591 switch (factor) {
592 case SDL_BLENDFACTOR_ZERO:
593 return D3D12_BLEND_ZERO;
594 case SDL_BLENDFACTOR_ONE:
595 return D3D12_BLEND_ONE;
596 case SDL_BLENDFACTOR_SRC_COLOR:
597 return D3D12_BLEND_SRC_COLOR;
598 case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR:
599 return D3D12_BLEND_INV_SRC_COLOR;
600 case SDL_BLENDFACTOR_SRC_ALPHA:
601 return D3D12_BLEND_SRC_ALPHA;
602 case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA:
603 return D3D12_BLEND_INV_SRC_ALPHA;
604 case SDL_BLENDFACTOR_DST_COLOR:
605 return D3D12_BLEND_DEST_COLOR;
606 case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR:
607 return D3D12_BLEND_INV_DEST_COLOR;
608 case SDL_BLENDFACTOR_DST_ALPHA:
609 return D3D12_BLEND_DEST_ALPHA;
610 case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA:
611 return D3D12_BLEND_INV_DEST_ALPHA;
612 default:
613 return (D3D12_BLEND)0;
614 }
615}
616
617static D3D12_BLEND_OP GetBlendEquation(SDL_BlendOperation operation)
618{
619 switch (operation) {
620 case SDL_BLENDOPERATION_ADD:
621 return D3D12_BLEND_OP_ADD;
622 case SDL_BLENDOPERATION_SUBTRACT:
623 return D3D12_BLEND_OP_SUBTRACT;
624 case SDL_BLENDOPERATION_REV_SUBTRACT:
625 return D3D12_BLEND_OP_REV_SUBTRACT;
626 case SDL_BLENDOPERATION_MINIMUM:
627 return D3D12_BLEND_OP_MIN;
628 case SDL_BLENDOPERATION_MAXIMUM:
629 return D3D12_BLEND_OP_MAX;
630 default:
631 return (D3D12_BLEND_OP)0;
632 }
633}
634
635static void D3D12_CreateBlendState(SDL_Renderer *renderer, SDL_BlendMode blendMode, D3D12_BLEND_DESC *outBlendDesc)
636{
637 SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode);
638 SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode);
639 SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode);
640 SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode);
641 SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode);
642 SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode);
643
644 SDL_zerop(outBlendDesc);
645 outBlendDesc->AlphaToCoverageEnable = FALSE;
646 outBlendDesc->IndependentBlendEnable = FALSE;
647 outBlendDesc->RenderTarget[0].BlendEnable = TRUE;
648 outBlendDesc->RenderTarget[0].SrcBlend = GetBlendFunc(srcColorFactor);
649 outBlendDesc->RenderTarget[0].DestBlend = GetBlendFunc(dstColorFactor);
650 outBlendDesc->RenderTarget[0].BlendOp = GetBlendEquation(colorOperation);
651 outBlendDesc->RenderTarget[0].SrcBlendAlpha = GetBlendFunc(srcAlphaFactor);
652 outBlendDesc->RenderTarget[0].DestBlendAlpha = GetBlendFunc(dstAlphaFactor);
653 outBlendDesc->RenderTarget[0].BlendOpAlpha = GetBlendEquation(alphaOperation);
654 outBlendDesc->RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
655}
656
657static D3D12_PipelineState *D3D12_CreatePipelineState(SDL_Renderer *renderer,
658 D3D12_Shader shader,
659 SDL_BlendMode blendMode,
660 D3D12_PRIMITIVE_TOPOLOGY_TYPE topology,
661 DXGI_FORMAT rtvFormat)
662{
663 const D3D12_INPUT_ELEMENT_DESC vertexDesc[] = {
664 { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
665 { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
666 { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
667 };
668 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
669 D3D12_GRAPHICS_PIPELINE_STATE_DESC pipelineDesc;
670 ID3D12PipelineState *pipelineState = NULL;
671 D3D12_PipelineState *pipelineStates;
672 HRESULT result = S_OK;
673
674 SDL_zero(pipelineDesc);
675 pipelineDesc.pRootSignature = data->rootSignatures[D3D12_GetRootSignatureType(shader)];
676 D3D12_GetVertexShader(shader, &pipelineDesc.VS);
677 D3D12_GetPixelShader(shader, &pipelineDesc.PS);
678 D3D12_CreateBlendState(renderer, blendMode, &pipelineDesc.BlendState);
679 pipelineDesc.SampleMask = 0xffffffff;
680
681 pipelineDesc.RasterizerState.AntialiasedLineEnable = FALSE;
682 pipelineDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
683 pipelineDesc.RasterizerState.DepthBias = 0;
684 pipelineDesc.RasterizerState.DepthBiasClamp = 0.0f;
685 pipelineDesc.RasterizerState.DepthClipEnable = TRUE;
686 pipelineDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
687 pipelineDesc.RasterizerState.FrontCounterClockwise = FALSE;
688 pipelineDesc.RasterizerState.MultisampleEnable = FALSE;
689 pipelineDesc.RasterizerState.SlopeScaledDepthBias = 0.0f;
690
691 pipelineDesc.InputLayout.pInputElementDescs = vertexDesc;
692 pipelineDesc.InputLayout.NumElements = 3;
693
694 pipelineDesc.PrimitiveTopologyType = topology;
695
696 pipelineDesc.NumRenderTargets = 1;
697 pipelineDesc.RTVFormats[0] = rtvFormat;
698 pipelineDesc.SampleDesc.Count = 1;
699 pipelineDesc.SampleDesc.Quality = 0;
700
701 result = ID3D12Device1_CreateGraphicsPipelineState(data->d3dDevice,
702 &pipelineDesc,
703 D3D_GUID(SDL_IID_ID3D12PipelineState),
704 (void **)&pipelineState);
705 if (FAILED(result)) {
706 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateGraphicsPipelineState"), result);
707 return NULL;
708 }
709
710 pipelineStates = (D3D12_PipelineState *)SDL_realloc(data->pipelineStates, (data->pipelineStateCount + 1) * sizeof(*pipelineStates));
711 if (!pipelineStates) {
712 D3D_SAFE_RELEASE(pipelineState);
713 return NULL;
714 }
715
716 pipelineStates[data->pipelineStateCount].shader = shader;
717 pipelineStates[data->pipelineStateCount].blendMode = blendMode;
718 pipelineStates[data->pipelineStateCount].topology = topology;
719 pipelineStates[data->pipelineStateCount].rtvFormat = rtvFormat;
720 pipelineStates[data->pipelineStateCount].pipelineState = pipelineState;
721 data->pipelineStates = pipelineStates;
722 ++data->pipelineStateCount;
723
724 return &pipelineStates[data->pipelineStateCount - 1];
725}
726
727static HRESULT D3D12_CreateVertexBuffer(D3D12_RenderData *data, size_t vbidx, size_t size)
728{
729 D3D12_HEAP_PROPERTIES vbufferHeapProps;
730 D3D12_RESOURCE_DESC vbufferDesc;
731 HRESULT result;
732
733 D3D_SAFE_RELEASE(data->vertexBuffers[vbidx].resource);
734
735 SDL_zero(vbufferHeapProps);
736 vbufferHeapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
737 vbufferHeapProps.CreationNodeMask = 1;
738 vbufferHeapProps.VisibleNodeMask = 1;
739
740 SDL_zero(vbufferDesc);
741 vbufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
742 vbufferDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
743 vbufferDesc.Width = size;
744 vbufferDesc.Height = 1;
745 vbufferDesc.DepthOrArraySize = 1;
746 vbufferDesc.MipLevels = 1;
747 vbufferDesc.Format = DXGI_FORMAT_UNKNOWN;
748 vbufferDesc.SampleDesc.Count = 1;
749 vbufferDesc.SampleDesc.Quality = 0;
750 vbufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
751 vbufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
752
753 result = ID3D12Device1_CreateCommittedResource(data->d3dDevice,
754 &vbufferHeapProps,
755 D3D12_HEAP_FLAG_NONE,
756 &vbufferDesc,
757 D3D12_RESOURCE_STATE_GENERIC_READ,
758 NULL,
759 D3D_GUID(SDL_IID_ID3D12Resource),
760 (void **)&data->vertexBuffers[vbidx].resource);
761
762 if (FAILED(result)) {
763 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreatePlacedResource [vertex buffer]"), result);
764 return result;
765 }
766
767 data->vertexBuffers[vbidx].view.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(data->vertexBuffers[vbidx].resource);
768 data->vertexBuffers[vbidx].view.StrideInBytes = sizeof(D3D12_VertexPositionColor);
769 data->vertexBuffers[vbidx].size = size;
770
771 return result;
772}
773
774// Create resources that depend on the device.
775static HRESULT D3D12_CreateDeviceResources(SDL_Renderer *renderer)
776{
777#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
778 typedef HRESULT(WINAPI * PFN_CREATE_DXGI_FACTORY)(UINT flags, REFIID riid, void **ppFactory);
779 PFN_CREATE_DXGI_FACTORY CreateDXGIFactoryFunc;
780 PFN_D3D12_CREATE_DEVICE D3D12CreateDeviceFunc;
781#endif
782 typedef HANDLE(WINAPI * PFN_CREATE_EVENT_EX)(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCWSTR lpName, DWORD dwFlags, DWORD dwDesiredAccess);
783 PFN_CREATE_EVENT_EX CreateEventExFunc;
784
785 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
786 ID3D12Device *d3dDevice = NULL;
787 HRESULT result = S_OK;
788 UINT creationFlags = 0;
789 int i;
790 bool createDebug;
791
792 D3D12_COMMAND_QUEUE_DESC queueDesc;
793 D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc;
794 D3D12_SAMPLER_DESC samplerDesc;
795 ID3D12DescriptorHeap *rootDescriptorHeaps[2];
796
797 // See if we need debug interfaces
798 createDebug = SDL_GetHintBoolean(SDL_HINT_RENDER_DIRECT3D11_DEBUG, false);
799
800#ifdef SDL_PLATFORM_GDK
801 CreateEventExFunc = CreateEventExW;
802#else
803 // CreateEventEx() arrived in Vista, so we need to load it with GetProcAddress for XP.
804 {
805 HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
806 CreateEventExFunc = NULL;
807 if (kernel32) {
808 CreateEventExFunc = (PFN_CREATE_EVENT_EX)GetProcAddress(kernel32, "CreateEventExW");
809 }
810 }
811#endif
812 if (!CreateEventExFunc) {
813 result = E_FAIL;
814 goto done;
815 }
816
817#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
818 data->hDXGIMod = SDL_LoadObject("dxgi.dll");
819 if (!data->hDXGIMod) {
820 result = E_FAIL;
821 goto done;
822 }
823
824 CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY)SDL_LoadFunction(data->hDXGIMod, "CreateDXGIFactory2");
825 if (!CreateDXGIFactoryFunc) {
826 result = E_FAIL;
827 goto done;
828 }
829
830 data->hD3D12Mod = SDL_LoadObject("D3D12.dll");
831 if (!data->hD3D12Mod) {
832 result = E_FAIL;
833 goto done;
834 }
835
836 D3D12CreateDeviceFunc = (PFN_D3D12_CREATE_DEVICE)SDL_LoadFunction(data->hD3D12Mod, "D3D12CreateDevice");
837 if (!D3D12CreateDeviceFunc) {
838 result = E_FAIL;
839 goto done;
840 }
841
842 if (createDebug) {
843 PFN_D3D12_GET_DEBUG_INTERFACE D3D12GetDebugInterfaceFunc;
844
845 D3D12GetDebugInterfaceFunc = (PFN_D3D12_GET_DEBUG_INTERFACE)SDL_LoadFunction(data->hD3D12Mod, "D3D12GetDebugInterface");
846 if (!D3D12GetDebugInterfaceFunc) {
847 result = E_FAIL;
848 goto done;
849 }
850 if (SUCCEEDED(D3D12GetDebugInterfaceFunc(D3D_GUID(SDL_IID_ID3D12Debug), (void **)&data->debugInterface))) {
851 ID3D12Debug_EnableDebugLayer(data->debugInterface);
852 }
853 }
854#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
855
856#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
857 result = D3D12_XBOX_CreateDevice(&d3dDevice, createDebug);
858 if (FAILED(result)) {
859 // SDL Error is set by D3D12_XBOX_CreateDevice
860 goto done;
861 }
862#else
863 if (createDebug) {
864#ifdef __IDXGIInfoQueue_INTERFACE_DEFINED__
865 IDXGIInfoQueue *dxgiInfoQueue = NULL;
866 PFN_CREATE_DXGI_FACTORY DXGIGetDebugInterfaceFunc;
867
868 // If the debug hint is set, also create the DXGI factory in debug mode
869 DXGIGetDebugInterfaceFunc = (PFN_CREATE_DXGI_FACTORY)SDL_LoadFunction(data->hDXGIMod, "DXGIGetDebugInterface1");
870 if (!DXGIGetDebugInterfaceFunc) {
871 result = E_FAIL;
872 goto done;
873 }
874
875 result = DXGIGetDebugInterfaceFunc(0, D3D_GUID(SDL_IID_IDXGIDebug1), (void **)&data->dxgiDebug);
876 if (FAILED(result)) {
877 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("DXGIGetDebugInterface1"), result);
878 goto done;
879 }
880
881 result = DXGIGetDebugInterfaceFunc(0, D3D_GUID(SDL_IID_IDXGIInfoQueue), (void **)&dxgiInfoQueue);
882 if (FAILED(result)) {
883 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("DXGIGetDebugInterface1"), result);
884 goto done;
885 }
886
887 IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfoQueue, SDL_DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE);
888 IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfoQueue, SDL_DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE);
889 D3D_SAFE_RELEASE(dxgiInfoQueue);
890#endif // __IDXGIInfoQueue_INTERFACE_DEFINED__
891 creationFlags = DXGI_CREATE_FACTORY_DEBUG;
892 }
893
894 result = CreateDXGIFactoryFunc(creationFlags, D3D_GUID(SDL_IID_IDXGIFactory6), (void **)&data->dxgiFactory);
895 if (FAILED(result)) {
896 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("CreateDXGIFactory"), result);
897 goto done;
898 }
899
900 // Prefer a high performance adapter if there are multiple choices
901 result = IDXGIFactory6_EnumAdapterByGpuPreference(data->dxgiFactory,
902 0,
903 DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
904 D3D_GUID(SDL_IID_IDXGIAdapter4),
905 (void **)&data->dxgiAdapter);
906 if (FAILED(result)) {
907 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGIFactory6::EnumAdapterByGPUPreference"), result);
908 goto done;
909 }
910
911 result = D3D12CreateDeviceFunc((IUnknown *)data->dxgiAdapter,
912 D3D_FEATURE_LEVEL_11_0, // Request minimum feature level 11.0 for maximum compatibility
913 D3D_GUID(SDL_IID_ID3D12Device1),
914 (void **)&d3dDevice);
915 if (FAILED(result)) {
916 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("D3D12CreateDevice"), result);
917 goto done;
918 }
919
920 // Setup the info queue if in debug mode
921 if (createDebug) {
922 ID3D12InfoQueue *infoQueue = NULL;
923 D3D12_MESSAGE_SEVERITY severities[] = { D3D12_MESSAGE_SEVERITY_INFO };
924 D3D12_INFO_QUEUE_FILTER filter;
925
926 result = ID3D12Device1_QueryInterface(d3dDevice, D3D_GUID(SDL_IID_ID3D12InfoQueue), (void **)&infoQueue);
927 if (FAILED(result)) {
928 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device to ID3D12InfoQueue"), result);
929 goto done;
930 }
931
932 SDL_zero(filter);
933 filter.DenyList.NumSeverities = 1;
934 filter.DenyList.pSeverityList = severities;
935 ID3D12InfoQueue_PushStorageFilter(infoQueue, &filter);
936
937 ID3D12InfoQueue_SetBreakOnSeverity(infoQueue, D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
938 ID3D12InfoQueue_SetBreakOnSeverity(infoQueue, D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE);
939
940 D3D_SAFE_RELEASE(infoQueue);
941 }
942#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
943
944 result = ID3D12Device_QueryInterface(d3dDevice, D3D_GUID(SDL_IID_ID3D12Device1), (void **)&data->d3dDevice);
945 if (FAILED(result)) {
946 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device to ID3D12Device1"), result);
947 goto done;
948 }
949
950 // Create a command queue
951 SDL_zero(queueDesc);
952 queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
953 queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
954
955 result = ID3D12Device1_CreateCommandQueue(data->d3dDevice,
956 &queueDesc,
957 D3D_GUID(SDL_IID_ID3D12CommandQueue),
958 (void **)&data->commandQueue);
959 if (FAILED(result)) {
960 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommandQueue"), result);
961 goto done;
962 }
963
964 // Create the descriptor heaps for the render target view, texture SRVs, and samplers
965 SDL_zero(descriptorHeapDesc);
966 descriptorHeapDesc.NumDescriptors = SDL_D3D12_NUM_BUFFERS;
967 descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
968 result = ID3D12Device1_CreateDescriptorHeap(data->d3dDevice,
969 &descriptorHeapDesc,
970 D3D_GUID(SDL_IID_ID3D12DescriptorHeap),
971 (void **)&data->rtvDescriptorHeap);
972 if (FAILED(result)) {
973 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateDescriptorHeap [rtv]"), result);
974 goto done;
975 }
976 data->rtvDescriptorSize = ID3D12Device1_GetDescriptorHandleIncrementSize(d3dDevice, D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
977
978 descriptorHeapDesc.NumDescriptors = SDL_D3D12_MAX_NUM_TEXTURES;
979 result = ID3D12Device1_CreateDescriptorHeap(data->d3dDevice,
980 &descriptorHeapDesc,
981 D3D_GUID(SDL_IID_ID3D12DescriptorHeap),
982 (void **)&data->textureRTVDescriptorHeap);
983 if (FAILED(result)) {
984 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateDescriptorHeap [texture rtv]"), result);
985 goto done;
986 }
987
988 SDL_zero(descriptorHeapDesc);
989 descriptorHeapDesc.NumDescriptors = SDL_D3D12_MAX_NUM_TEXTURES;
990 descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
991 descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
992 result = ID3D12Device1_CreateDescriptorHeap(data->d3dDevice,
993 &descriptorHeapDesc,
994 D3D_GUID(SDL_IID_ID3D12DescriptorHeap),
995 (void **)&data->srvDescriptorHeap);
996 if (FAILED(result)) {
997 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateDescriptorHeap [srv]"), result);
998 goto done;
999 }
1000 rootDescriptorHeaps[0] = data->srvDescriptorHeap;
1001 data->srvDescriptorSize = ID3D12Device1_GetDescriptorHandleIncrementSize(d3dDevice, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
1002
1003 SDL_zero(descriptorHeapDesc);
1004 descriptorHeapDesc.NumDescriptors = 4;
1005 descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
1006 descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
1007 result = ID3D12Device1_CreateDescriptorHeap(data->d3dDevice,
1008 &descriptorHeapDesc,
1009 D3D_GUID(SDL_IID_ID3D12DescriptorHeap),
1010 (void **)&data->samplerDescriptorHeap);
1011 if (FAILED(result)) {
1012 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateDescriptorHeap [sampler]"), result);
1013 goto done;
1014 }
1015 rootDescriptorHeaps[1] = data->samplerDescriptorHeap;
1016 data->samplerDescriptorSize = ID3D12Device1_GetDescriptorHandleIncrementSize(d3dDevice, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
1017
1018 // Create a command allocator for each back buffer
1019 for (i = 0; i < SDL_D3D12_NUM_BUFFERS; ++i) {
1020 result = ID3D12Device1_CreateCommandAllocator(data->d3dDevice,
1021 D3D12_COMMAND_LIST_TYPE_DIRECT,
1022 D3D_GUID(SDL_IID_ID3D12CommandAllocator),
1023 (void **)&data->commandAllocators[i]);
1024 if (FAILED(result)) {
1025 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommandAllocator"), result);
1026 goto done;
1027 }
1028 }
1029
1030 // Create the command list
1031 result = ID3D12Device1_CreateCommandList(data->d3dDevice,
1032 0,
1033 D3D12_COMMAND_LIST_TYPE_DIRECT,
1034 data->commandAllocators[0],
1035 NULL,
1036 D3D_GUID(SDL_IID_ID3D12GraphicsCommandList2),
1037 (void **)&data->commandList);
1038 if (FAILED(result)) {
1039 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommandList"), result);
1040 goto done;
1041 }
1042
1043 // Set the descriptor heaps to the correct initial value
1044 ID3D12GraphicsCommandList2_SetDescriptorHeaps(data->commandList, 2, rootDescriptorHeaps);
1045
1046 // Create the fence and fence event
1047 result = ID3D12Device_CreateFence(data->d3dDevice,
1048 data->fenceValue,
1049 D3D12_FENCE_FLAG_NONE,
1050 D3D_GUID(SDL_IID_ID3D12Fence),
1051 (void **)&data->fence);
1052 if (FAILED(result)) {
1053 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateFence"), result);
1054 goto done;
1055 }
1056
1057 data->fenceValue++;
1058
1059 data->fenceEvent = CreateEventExFunc(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
1060 if (!data->fenceEvent) {
1061 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("CreateEventEx"), result);
1062 goto done;
1063 }
1064
1065 // Create all the root signatures
1066 for (i = 0; i < NUM_ROOTSIGS; ++i) {
1067 D3D12_SHADER_BYTECODE rootSigData;
1068 D3D12_GetRootSignatureData((D3D12_RootSignature)i, &rootSigData);
1069 result = ID3D12Device1_CreateRootSignature(data->d3dDevice,
1070 0,
1071 rootSigData.pShaderBytecode,
1072 rootSigData.BytecodeLength,
1073 D3D_GUID(SDL_IID_ID3D12RootSignature),
1074 (void **)&data->rootSignatures[i]);
1075 if (FAILED(result)) {
1076 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateRootSignature"), result);
1077 goto done;
1078 }
1079 }
1080
1081 {
1082 const SDL_BlendMode defaultBlendModes[] = {
1083 SDL_BLENDMODE_BLEND,
1084 };
1085 const DXGI_FORMAT defaultRTVFormats[] = {
1086 DXGI_FORMAT_B8G8R8A8_UNORM,
1087 };
1088 int j, k, l;
1089
1090 // Create a few default pipeline state objects, to verify that this renderer will work
1091 for (i = 0; i < NUM_SHADERS; ++i) {
1092 for (j = 0; j < SDL_arraysize(defaultBlendModes); ++j) {
1093 for (k = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; k < D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH; ++k) {
1094 for (l = 0; l < SDL_arraysize(defaultRTVFormats); ++l) {
1095 if (!D3D12_CreatePipelineState(renderer, (D3D12_Shader)i, defaultBlendModes[j], (D3D12_PRIMITIVE_TOPOLOGY_TYPE)k, defaultRTVFormats[l])) {
1096 // D3D12_CreatePipelineState will set the SDL error, if it fails
1097 result = E_FAIL;
1098 goto done;
1099 }
1100 }
1101 }
1102 }
1103 }
1104 }
1105
1106 // Create default vertex buffers
1107 for (i = 0; i < SDL_D3D12_NUM_VERTEX_BUFFERS; ++i) {
1108 D3D12_CreateVertexBuffer(data, i, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT);
1109 }
1110
1111 // Create samplers to use when drawing textures:
1112 static struct
1113 {
1114 D3D12_FILTER filter;
1115 D3D12_TEXTURE_ADDRESS_MODE address;
1116 } samplerParams[] = {
1117 { D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP },
1118 { D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_WRAP },
1119 { D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP },
1120 { D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_WRAP },
1121 };
1122 SDL_COMPILE_TIME_ASSERT(samplerParams_SIZE, SDL_arraysize(samplerParams) == D3D12_SAMPLER_COUNT);
1123 SDL_zero(samplerDesc);
1124 samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
1125 samplerDesc.MipLODBias = 0.0f;
1126 samplerDesc.MaxAnisotropy = 1;
1127 samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
1128 samplerDesc.MinLOD = 0.0f;
1129 samplerDesc.MaxLOD = D3D12_FLOAT32_MAX;
1130 D3D_CALL_RET(data->samplerDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &data->samplers[0]);
1131 for (i = 0; i < SDL_arraysize(samplerParams); ++i) {
1132 samplerDesc.Filter = samplerParams[i].filter;
1133 samplerDesc.AddressU = samplerParams[i].address;
1134 samplerDesc.AddressV = samplerParams[i].address;
1135 data->samplers[i].ptr = data->samplers[0].ptr + i * data->samplerDescriptorSize;
1136 ID3D12Device1_CreateSampler(data->d3dDevice, &samplerDesc, data->samplers[i]);
1137 }
1138
1139 // Initialize the pool allocator for SRVs
1140 for (i = 0; i < SDL_D3D12_MAX_NUM_TEXTURES; ++i) {
1141 data->srvPoolNodes[i].index = (SIZE_T)i;
1142 if (i != SDL_D3D12_MAX_NUM_TEXTURES - 1) {
1143 data->srvPoolNodes[i].next = &data->srvPoolNodes[i + 1];
1144 }
1145 }
1146 data->srvPoolHead = &data->srvPoolNodes[0];
1147
1148 SDL_PropertiesID props = SDL_GetRendererProperties(renderer);
1149 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_D3D12_DEVICE_POINTER, data->d3dDevice);
1150 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER, data->commandQueue);
1151
1152done:
1153 D3D_SAFE_RELEASE(d3dDevice);
1154 return result;
1155}
1156
1157static DXGI_MODE_ROTATION D3D12_GetCurrentRotation(void)
1158{
1159 // FIXME
1160 return DXGI_MODE_ROTATION_IDENTITY;
1161}
1162
1163static BOOL D3D12_IsDisplayRotated90Degrees(DXGI_MODE_ROTATION rotation)
1164{
1165 switch (rotation) {
1166 case DXGI_MODE_ROTATION_ROTATE90:
1167 case DXGI_MODE_ROTATION_ROTATE270:
1168 return TRUE;
1169 default:
1170 return FALSE;
1171 }
1172}
1173
1174static int D3D12_GetRotationForCurrentRenderTarget(SDL_Renderer *renderer)
1175{
1176 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
1177 if (data->textureRenderTarget) {
1178 return DXGI_MODE_ROTATION_IDENTITY;
1179 } else {
1180 return data->rotation;
1181 }
1182}
1183
1184static bool D3D12_GetViewportAlignedD3DRect(SDL_Renderer *renderer, const SDL_Rect *sdlRect, D3D12_RECT *outRect, BOOL includeViewportOffset)
1185{
1186 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
1187 const int rotation = D3D12_GetRotationForCurrentRenderTarget(renderer);
1188 const SDL_Rect *viewport = &data->currentViewport;
1189
1190 switch (rotation) {
1191 case DXGI_MODE_ROTATION_IDENTITY:
1192 outRect->left = sdlRect->x;
1193 outRect->right = (LONG)sdlRect->x + sdlRect->w;
1194 outRect->top = sdlRect->y;
1195 outRect->bottom = (LONG)sdlRect->y + sdlRect->h;
1196 if (includeViewportOffset) {
1197 outRect->left += viewport->x;
1198 outRect->right += viewport->x;
1199 outRect->top += viewport->y;
1200 outRect->bottom += viewport->y;
1201 }
1202 break;
1203 case DXGI_MODE_ROTATION_ROTATE270:
1204 outRect->left = sdlRect->y;
1205 outRect->right = (LONG)sdlRect->y + sdlRect->h;
1206 outRect->top = viewport->w - sdlRect->x - sdlRect->w;
1207 outRect->bottom = viewport->w - sdlRect->x;
1208 break;
1209 case DXGI_MODE_ROTATION_ROTATE180:
1210 outRect->left = viewport->w - sdlRect->x - sdlRect->w;
1211 outRect->right = viewport->w - sdlRect->x;
1212 outRect->top = viewport->h - sdlRect->y - sdlRect->h;
1213 outRect->bottom = viewport->h - sdlRect->y;
1214 break;
1215 case DXGI_MODE_ROTATION_ROTATE90:
1216 outRect->left = viewport->h - sdlRect->y - sdlRect->h;
1217 outRect->right = viewport->h - sdlRect->y;
1218 outRect->top = sdlRect->x;
1219 outRect->bottom = (LONG)sdlRect->x + sdlRect->h;
1220 break;
1221 default:
1222 return SDL_SetError("The physical display is in an unknown or unsupported rotation");
1223 }
1224 return true;
1225}
1226
1227#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
1228static HRESULT D3D12_CreateSwapChain(SDL_Renderer *renderer, int w, int h)
1229{
1230 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
1231 IDXGISwapChain1 *swapChain = NULL;
1232 HRESULT result = S_OK;
1233
1234 // Create a swap chain using the same adapter as the existing Direct3D device.
1235 DXGI_SWAP_CHAIN_DESC1 swapChainDesc;
1236 SDL_zero(swapChainDesc);
1237 swapChainDesc.Width = w;
1238 swapChainDesc.Height = h;
1239 switch (renderer->output_colorspace) {
1240 case SDL_COLORSPACE_SRGB_LINEAR:
1241 swapChainDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
1242 data->renderTargetFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
1243 break;
1244 case SDL_COLORSPACE_HDR10:
1245 swapChainDesc.Format = DXGI_FORMAT_R10G10B10A2_UNORM;
1246 data->renderTargetFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
1247 break;
1248 default:
1249 swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
1250 data->renderTargetFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
1251 break;
1252 }
1253 swapChainDesc.Stereo = FALSE;
1254 swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
1255 swapChainDesc.SampleDesc.Quality = 0;
1256 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
1257 swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
1258 if (WIN_IsWindows8OrGreater()) {
1259 swapChainDesc.Scaling = DXGI_SCALING_NONE;
1260 } else {
1261 swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
1262 }
1263 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect.
1264 swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT | // To support SetMaximumFrameLatency
1265 DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; // To support presenting with allow tearing on
1266
1267 HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(renderer->window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
1268 if (!hwnd) {
1269 SDL_SetError("Couldn't get window handle");
1270 result = E_FAIL;
1271 goto done;
1272 }
1273
1274 result = IDXGIFactory2_CreateSwapChainForHwnd(data->dxgiFactory,
1275 (IUnknown *)data->commandQueue,
1276 hwnd,
1277 &swapChainDesc,
1278 NULL,
1279 NULL, // Allow on all displays.
1280 &swapChain);
1281 if (FAILED(result)) {
1282 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGIFactory2::CreateSwapChainForHwnd"), result);
1283 goto done;
1284 }
1285
1286 IDXGIFactory6_MakeWindowAssociation(data->dxgiFactory, hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
1287
1288 result = IDXGISwapChain1_QueryInterface(swapChain, D3D_GUID(SDL_IID_IDXGISwapChain4), (void **)&data->swapChain);
1289 if (FAILED(result)) {
1290 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain1::QueryInterface"), result);
1291 goto done;
1292 }
1293
1294 /* Ensure that the swapchain does not queue more than one frame at a time. This both reduces latency
1295 * and ensures that the application will only render after each VSync, minimizing power consumption.
1296 */
1297 result = IDXGISwapChain4_SetMaximumFrameLatency(data->swapChain, 1);
1298 if (FAILED(result)) {
1299 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain4::SetMaximumFrameLatency"), result);
1300 goto done;
1301 }
1302
1303 data->swapEffect = swapChainDesc.SwapEffect;
1304 data->swapFlags = swapChainDesc.Flags;
1305
1306 UINT colorspace_support = 0;
1307 DXGI_COLOR_SPACE_TYPE colorspace;
1308 switch (renderer->output_colorspace) {
1309 case SDL_COLORSPACE_SRGB_LINEAR:
1310 colorspace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
1311 break;
1312 case SDL_COLORSPACE_HDR10:
1313 colorspace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
1314 break;
1315 default:
1316 // sRGB
1317 colorspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
1318 break;
1319 }
1320 if (SUCCEEDED(IDXGISwapChain3_CheckColorSpaceSupport(data->swapChain, colorspace, &colorspace_support)) &&
1321 (colorspace_support & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) {
1322 result = IDXGISwapChain3_SetColorSpace1(data->swapChain, colorspace);
1323 if (FAILED(result)) {
1324 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain3::SetColorSpace1"), result);
1325 goto done;
1326 }
1327 } else {
1328 // Not the default, we're not going to be able to present in this colorspace
1329 SDL_SetError("Unsupported output colorspace");
1330 result = DXGI_ERROR_UNSUPPORTED;
1331 }
1332
1333 SDL_SetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_D3D12_SWAPCHAIN_POINTER, data->swapChain);
1334
1335done:
1336 D3D_SAFE_RELEASE(swapChain);
1337 return result;
1338}
1339#endif
1340
1341// Initialize all resources that change when the window's size changes.
1342static HRESULT D3D12_CreateWindowSizeDependentResources(SDL_Renderer *renderer)
1343{
1344 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
1345 HRESULT result = S_OK;
1346 int i, w, h;
1347
1348 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc;
1349 D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptor;
1350
1351 // Release resources in the current command list
1352 D3D12_IssueBatch(data);
1353 ID3D12GraphicsCommandList2_OMSetRenderTargets(data->commandList, 0, NULL, FALSE, NULL);
1354
1355 // Release render targets
1356 for (i = 0; i < SDL_D3D12_NUM_BUFFERS; ++i) {
1357 D3D_SAFE_RELEASE(data->renderTargets[i]);
1358 }
1359
1360 /* The width and height of the swap chain must be based on the display's
1361 * non-rotated size.
1362 */
1363 SDL_GetWindowSizeInPixels(renderer->window, &w, &h);
1364 data->rotation = D3D12_GetCurrentRotation();
1365 if (D3D12_IsDisplayRotated90Degrees(data->rotation)) {
1366 int tmp = w;
1367 w = h;
1368 h = tmp;
1369 }
1370
1371#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
1372 if (data->swapChain) {
1373 // If the swap chain already exists, resize it.
1374 result = IDXGISwapChain_ResizeBuffers(data->swapChain,
1375 0,
1376 w, h,
1377 DXGI_FORMAT_UNKNOWN,
1378 data->swapFlags);
1379 if (FAILED(result)) {
1380 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain::ResizeBuffers"), result);
1381 goto done;
1382 }
1383 } else {
1384 result = D3D12_CreateSwapChain(renderer, w, h);
1385 if (FAILED(result) || !data->swapChain) {
1386 goto done;
1387 }
1388 }
1389
1390 // Set the proper rotation for the swap chain.
1391 if (WIN_IsWindows8OrGreater()) {
1392 if (data->swapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) {
1393 result = IDXGISwapChain4_SetRotation(data->swapChain, data->rotation); // NOLINT(clang-analyzer-core.NullDereference)
1394 if (FAILED(result)) {
1395 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain4::SetRotation"), result);
1396 goto done;
1397 }
1398 }
1399 }
1400#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
1401
1402 // Get each back buffer render target and create render target views
1403 for (i = 0; i < SDL_D3D12_NUM_BUFFERS; ++i) {
1404#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
1405 result = D3D12_XBOX_CreateBackBufferTarget(data->d3dDevice, renderer->window->w, renderer->window->h, (void **)&data->renderTargets[i]);
1406 if (FAILED(result)) {
1407 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("D3D12_XBOX_CreateBackBufferTarget"), result);
1408 goto done;
1409 }
1410#else
1411 result = IDXGISwapChain4_GetBuffer(data->swapChain, // NOLINT(clang-analyzer-core.NullDereference)
1412 i,
1413 D3D_GUID(SDL_IID_ID3D12Resource),
1414 (void **)&data->renderTargets[i]);
1415 if (FAILED(result)) {
1416 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain4::GetBuffer"), result);
1417 goto done;
1418 }
1419#endif
1420
1421 SDL_zero(rtvDesc);
1422 rtvDesc.Format = data->renderTargetFormat;
1423 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
1424
1425 SDL_zero(rtvDescriptor);
1426 D3D_CALL_RET(data->rtvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &rtvDescriptor);
1427 rtvDescriptor.ptr += i * data->rtvDescriptorSize;
1428 ID3D12Device1_CreateRenderTargetView(data->d3dDevice, data->renderTargets[i], &rtvDesc, rtvDescriptor);
1429 }
1430
1431 // Set back buffer index to current buffer
1432#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
1433 data->currentBackBufferIndex = 0;
1434#else
1435 data->currentBackBufferIndex = IDXGISwapChain4_GetCurrentBackBufferIndex(data->swapChain);
1436#endif
1437
1438 /* Set the swap chain target immediately, so that a target is always set
1439 * even before we get to SetDrawState. Without this it's possible to hit
1440 * null references in places like ReadPixels!
1441 */
1442 data->currentRenderTargetView = D3D12_GetCurrentRenderTargetView(renderer);
1443 ID3D12GraphicsCommandList2_OMSetRenderTargets(data->commandList, 1, &data->currentRenderTargetView, FALSE, NULL);
1444 D3D12_TransitionResource(data,
1445 data->renderTargets[data->currentBackBufferIndex],
1446 D3D12_RESOURCE_STATE_PRESENT,
1447 D3D12_RESOURCE_STATE_RENDER_TARGET);
1448
1449 data->viewportDirty = true;
1450
1451#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
1452 D3D12_XBOX_StartFrame(data->d3dDevice, &data->frameToken);
1453#endif
1454
1455done:
1456 return result;
1457}
1458
1459static bool D3D12_HandleDeviceLost(SDL_Renderer *renderer)
1460{
1461 bool recovered = false;
1462
1463 D3D12_ReleaseAll(renderer);
1464
1465 if (SUCCEEDED(D3D12_CreateDeviceResources(renderer)) &&
1466 SUCCEEDED(D3D12_CreateWindowSizeDependentResources(renderer))) {
1467 recovered = true;
1468 } else {
1469 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Renderer couldn't recover from device lost: %s", SDL_GetError());
1470 D3D12_ReleaseAll(renderer);
1471 }
1472
1473 // Let the application know that the device has been reset or lost
1474 SDL_Event event;
1475 SDL_zero(event);
1476 event.type = recovered ? SDL_EVENT_RENDER_DEVICE_RESET : SDL_EVENT_RENDER_DEVICE_LOST;
1477 event.render.windowID = SDL_GetWindowID(SDL_GetRenderWindow(renderer));
1478 SDL_PushEvent(&event);
1479
1480 return recovered;
1481}
1482
1483// This method is called when the window's size changes.
1484static HRESULT D3D12_UpdateForWindowSizeChange(SDL_Renderer *renderer)
1485{
1486 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
1487 // If the GPU has previous work, wait for it to be done first
1488 D3D12_WaitForGPU(data);
1489 return D3D12_CreateWindowSizeDependentResources(renderer);
1490}
1491
1492static void D3D12_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
1493{
1494 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
1495
1496 if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
1497 data->pixelSizeChanged = true;
1498 }
1499}
1500
1501static bool D3D12_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode)
1502{
1503 SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode);
1504 SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode);
1505 SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode);
1506 SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode);
1507 SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode);
1508 SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode);
1509
1510 if (!GetBlendFunc(srcColorFactor) || !GetBlendFunc(srcAlphaFactor) ||
1511 !GetBlendEquation(colorOperation) ||
1512 !GetBlendFunc(dstColorFactor) || !GetBlendFunc(dstAlphaFactor) ||
1513 !GetBlendEquation(alphaOperation)) {
1514 return false;
1515 }
1516 return true;
1517}
1518
1519static SIZE_T D3D12_GetAvailableSRVIndex(SDL_Renderer *renderer)
1520{
1521 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
1522 if (rendererData->srvPoolHead) {
1523 SIZE_T index = rendererData->srvPoolHead->index;
1524 rendererData->srvPoolHead = (D3D12_SRVPoolNode *)(rendererData->srvPoolHead->next);
1525 return index;
1526 } else {
1527 SDL_SetError("[d3d12] Cannot allocate more than %d textures!", SDL_D3D12_MAX_NUM_TEXTURES);
1528 return SDL_D3D12_MAX_NUM_TEXTURES + 1;
1529 }
1530}
1531
1532static void D3D12_FreeSRVIndex(SDL_Renderer *renderer, SIZE_T index)
1533{
1534 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
1535 rendererData->srvPoolNodes[index].next = rendererData->srvPoolHead;
1536 rendererData->srvPoolHead = &rendererData->srvPoolNodes[index];
1537}
1538
1539static bool GetTextureProperty(SDL_PropertiesID props, const char *name, ID3D12Resource **texture)
1540{
1541 IUnknown *unknown = (IUnknown*)SDL_GetPointerProperty(props, name, NULL);
1542 if (unknown) {
1543#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
1544 HRESULT result = unknown->QueryInterface(D3D_GUID(SDL_IID_ID3D12Resource), (void **)texture);
1545#else
1546 HRESULT result = IUnknown_QueryInterface(unknown, D3D_GUID(SDL_IID_ID3D12Resource), (void **)texture);
1547#endif
1548 if (FAILED(result)) {
1549 return WIN_SetErrorFromHRESULT(name, result);
1550 }
1551 }
1552 return true;
1553}
1554
1555static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
1556{
1557 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
1558 D3D12_TextureData *textureData;
1559 HRESULT result;
1560 DXGI_FORMAT textureFormat = SDLPixelFormatToDXGITextureFormat(texture->format, renderer->output_colorspace);
1561 D3D12_RESOURCE_DESC textureDesc;
1562 D3D12_HEAP_PROPERTIES heapProps;
1563 D3D12_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
1564
1565 if (!rendererData->d3dDevice) {
1566 return SDL_SetError("Device lost and couldn't be recovered");
1567 }
1568
1569 if (textureFormat == DXGI_FORMAT_UNKNOWN) {
1570 return SDL_SetError("%s, An unsupported SDL pixel format (0x%x) was specified", __FUNCTION__, texture->format);
1571 }
1572
1573 textureData = (D3D12_TextureData *)SDL_calloc(1, sizeof(*textureData));
1574 if (!textureData) {
1575 return false;
1576 }
1577 textureData->scaleMode = (texture->scaleMode == SDL_SCALEMODE_NEAREST) ? D3D12_FILTER_MIN_MAG_MIP_POINT : D3D12_FILTER_MIN_MAG_MIP_LINEAR;
1578
1579 texture->internal = textureData;
1580 textureData->mainTextureFormat = textureFormat;
1581
1582 SDL_zero(textureDesc);
1583 textureDesc.Width = texture->w;
1584 textureDesc.Height = texture->h;
1585 textureDesc.MipLevels = 1;
1586 textureDesc.DepthOrArraySize = 1;
1587 textureDesc.Format = textureFormat;
1588 textureDesc.SampleDesc.Count = 1;
1589 textureDesc.SampleDesc.Quality = 0;
1590 textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
1591 textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
1592
1593 // NV12 textures must have even width and height
1594 if (texture->format == SDL_PIXELFORMAT_NV12 ||
1595 texture->format == SDL_PIXELFORMAT_NV21 ||
1596 texture->format == SDL_PIXELFORMAT_P010) {
1597 textureDesc.Width = (textureDesc.Width + 1) & ~1;
1598 textureDesc.Height = (textureDesc.Height + 1) & ~1;
1599 }
1600 textureData->w = (int)textureDesc.Width;
1601 textureData->h = (int)textureDesc.Height;
1602 if (SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_SRGB) {
1603 textureData->shader = SHADER_RGB;
1604 } else {
1605 textureData->shader = SHADER_ADVANCED;
1606 }
1607
1608 if (texture->access == SDL_TEXTUREACCESS_TARGET) {
1609 textureDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
1610 }
1611
1612 SDL_zero(heapProps);
1613 heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
1614 heapProps.CreationNodeMask = 1;
1615 heapProps.VisibleNodeMask = 1;
1616
1617 if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_POINTER, &textureData->mainTexture)) {
1618 return false;
1619 }
1620 if (!textureData->mainTexture) {
1621 result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice,
1622 &heapProps,
1623 D3D12_HEAP_FLAG_NONE,
1624 &textureDesc,
1625 D3D12_RESOURCE_STATE_COPY_DEST,
1626 NULL,
1627 D3D_GUID(SDL_IID_ID3D12Resource),
1628 (void **)&textureData->mainTexture);
1629 if (FAILED(result)) {
1630 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommittedResource [texture]"), result);
1631 }
1632 }
1633 textureData->mainResourceState = D3D12_RESOURCE_STATE_COPY_DEST;
1634 SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_POINTER, textureData->mainTexture);
1635#ifdef SDL_HAVE_YUV
1636 if (texture->format == SDL_PIXELFORMAT_YV12 ||
1637 texture->format == SDL_PIXELFORMAT_IYUV) {
1638 textureData->yuv = true;
1639
1640 textureDesc.Width = (textureDesc.Width + 1) / 2;
1641 textureDesc.Height = (textureDesc.Height + 1) / 2;
1642
1643 if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_U_POINTER, &textureData->mainTextureU)) {
1644 return false;
1645 }
1646 if (!textureData->mainTextureU) {
1647 result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice,
1648 &heapProps,
1649 D3D12_HEAP_FLAG_NONE,
1650 &textureDesc,
1651 D3D12_RESOURCE_STATE_COPY_DEST,
1652 NULL,
1653 D3D_GUID(SDL_IID_ID3D12Resource),
1654 (void **)&textureData->mainTextureU);
1655 if (FAILED(result)) {
1656 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommittedResource [texture]"), result);
1657 }
1658 }
1659 textureData->mainResourceStateU = D3D12_RESOURCE_STATE_COPY_DEST;
1660 SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_U_POINTER, textureData->mainTextureU);
1661
1662 if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_V_POINTER, &textureData->mainTextureV)) {
1663 return false;
1664 }
1665 if (!textureData->mainTextureV) {
1666 result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice,
1667 &heapProps,
1668 D3D12_HEAP_FLAG_NONE,
1669 &textureDesc,
1670 D3D12_RESOURCE_STATE_COPY_DEST,
1671 NULL,
1672 D3D_GUID(SDL_IID_ID3D12Resource),
1673 (void **)&textureData->mainTextureV);
1674 if (FAILED(result)) {
1675 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommittedResource [texture]"), result);
1676 }
1677 }
1678 textureData->mainResourceStateV = D3D12_RESOURCE_STATE_COPY_DEST;
1679 SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_V_POINTER, textureData->mainTextureV);
1680
1681 textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8);
1682 if (!textureData->YCbCr_matrix) {
1683 return SDL_SetError("Unsupported YUV colorspace");
1684 }
1685 }
1686
1687 if (texture->format == SDL_PIXELFORMAT_NV12 ||
1688 texture->format == SDL_PIXELFORMAT_NV21 ||
1689 texture->format == SDL_PIXELFORMAT_P010) {
1690 int bits_per_pixel;
1691
1692 textureData->nv12 = true;
1693
1694 switch (texture->format) {
1695 case SDL_PIXELFORMAT_P010:
1696 bits_per_pixel = 10;
1697 break;
1698 default:
1699 bits_per_pixel = 8;
1700 break;
1701 }
1702 textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel);
1703 if (!textureData->YCbCr_matrix) {
1704 return SDL_SetError("Unsupported YUV colorspace");
1705 }
1706 }
1707#endif // SDL_HAVE_YUV
1708 SDL_zero(resourceViewDesc);
1709 resourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
1710 resourceViewDesc.Format = SDLPixelFormatToDXGIMainResourceViewFormat(texture->format, renderer->output_colorspace);
1711 resourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
1712 resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels;
1713
1714 textureData->mainSRVIndex = D3D12_GetAvailableSRVIndex(renderer);
1715 D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureResourceView);
1716 textureData->mainTextureResourceView.ptr += textureData->mainSRVIndex * rendererData->srvDescriptorSize;
1717
1718 ID3D12Device1_CreateShaderResourceView(rendererData->d3dDevice,
1719 textureData->mainTexture,
1720 &resourceViewDesc,
1721 textureData->mainTextureResourceView);
1722#ifdef SDL_HAVE_YUV
1723 if (textureData->yuv) {
1724 D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureResourceViewU);
1725 textureData->mainSRVIndexU = D3D12_GetAvailableSRVIndex(renderer);
1726 textureData->mainTextureResourceViewU.ptr += textureData->mainSRVIndexU * rendererData->srvDescriptorSize;
1727 ID3D12Device1_CreateShaderResourceView(rendererData->d3dDevice,
1728 textureData->mainTextureU,
1729 &resourceViewDesc,
1730 textureData->mainTextureResourceViewU);
1731
1732 D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureResourceViewV);
1733 textureData->mainSRVIndexV = D3D12_GetAvailableSRVIndex(renderer);
1734 textureData->mainTextureResourceViewV.ptr += textureData->mainSRVIndexV * rendererData->srvDescriptorSize;
1735 ID3D12Device1_CreateShaderResourceView(rendererData->d3dDevice,
1736 textureData->mainTextureV,
1737 &resourceViewDesc,
1738 textureData->mainTextureResourceViewV);
1739 }
1740
1741 if (textureData->nv12) {
1742 D3D12_SHADER_RESOURCE_VIEW_DESC nvResourceViewDesc = resourceViewDesc;
1743
1744 if (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21) {
1745 nvResourceViewDesc.Format = DXGI_FORMAT_R8G8_UNORM;
1746 } else if (texture->format == SDL_PIXELFORMAT_P010) {
1747 nvResourceViewDesc.Format = DXGI_FORMAT_R16G16_UNORM;
1748 }
1749 nvResourceViewDesc.Texture2D.PlaneSlice = 1;
1750
1751 D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureResourceViewNV);
1752 textureData->mainSRVIndexNV = D3D12_GetAvailableSRVIndex(renderer);
1753 textureData->mainTextureResourceViewNV.ptr += textureData->mainSRVIndexNV * rendererData->srvDescriptorSize;
1754 ID3D12Device1_CreateShaderResourceView(rendererData->d3dDevice,
1755 textureData->mainTexture,
1756 &nvResourceViewDesc,
1757 textureData->mainTextureResourceViewNV);
1758 }
1759#endif // SDL_HAVE_YUV
1760
1761 if (texture->access & SDL_TEXTUREACCESS_TARGET) {
1762 D3D12_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
1763 SDL_zero(renderTargetViewDesc);
1764 renderTargetViewDesc.Format = textureDesc.Format;
1765 renderTargetViewDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
1766 renderTargetViewDesc.Texture2D.MipSlice = 0;
1767
1768 D3D_CALL_RET(rendererData->textureRTVDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureRenderTargetView);
1769 textureData->mainTextureRenderTargetView.ptr += textureData->mainSRVIndex * rendererData->rtvDescriptorSize;
1770
1771 ID3D12Device1_CreateRenderTargetView(rendererData->d3dDevice,
1772 (ID3D12Resource *)textureData->mainTexture,
1773 &renderTargetViewDesc,
1774 textureData->mainTextureRenderTargetView);
1775 }
1776
1777 return true;
1778}
1779
1780static void D3D12_DestroyTexture(SDL_Renderer *renderer,
1781 SDL_Texture *texture)
1782{
1783 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
1784 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal;
1785
1786 if (!textureData) {
1787 return;
1788 }
1789
1790 /* Because SDL_DestroyTexture might be called while the data is in-flight, we need to issue the batch first
1791 Unfortunately, this means that deleting a lot of textures mid-frame will have poor performance. */
1792 D3D12_IssueBatch(rendererData);
1793
1794 D3D_SAFE_RELEASE(textureData->mainTexture);
1795 D3D_SAFE_RELEASE(textureData->stagingBuffer);
1796 D3D12_FreeSRVIndex(renderer, textureData->mainSRVIndex);
1797#ifdef SDL_HAVE_YUV
1798 D3D_SAFE_RELEASE(textureData->mainTextureU);
1799 D3D_SAFE_RELEASE(textureData->mainTextureV);
1800 if (textureData->yuv) {
1801 D3D12_FreeSRVIndex(renderer, textureData->mainSRVIndexU);
1802 D3D12_FreeSRVIndex(renderer, textureData->mainSRVIndexV);
1803 }
1804 if (textureData->nv12) {
1805 D3D12_FreeSRVIndex(renderer, textureData->mainSRVIndexNV);
1806 }
1807 SDL_free(textureData->pixels);
1808#endif
1809 SDL_free(textureData);
1810 texture->internal = NULL;
1811}
1812
1813static bool D3D12_UpdateTextureInternal(D3D12_RenderData *rendererData, ID3D12Resource *texture, int plane, int x, int y, int w, int h, const void *pixels, int pitch, D3D12_RESOURCE_STATES *resourceState)
1814{
1815 const Uint8 *src;
1816 Uint8 *dst;
1817 UINT length;
1818 HRESULT result;
1819 D3D12_RESOURCE_DESC textureDesc;
1820 D3D12_RESOURCE_DESC uploadDesc;
1821 D3D12_HEAP_PROPERTIES heapProps;
1822 D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedTextureDesc;
1823 D3D12_TEXTURE_COPY_LOCATION srcLocation;
1824 D3D12_TEXTURE_COPY_LOCATION dstLocation;
1825 BYTE *textureMemory;
1826 ID3D12Resource *uploadBuffer;
1827 UINT row, NumRows, RowPitch;
1828 UINT64 RowLength;
1829
1830 // Create an upload buffer, which will be used to write to the main texture.
1831 SDL_zero(textureDesc);
1832 D3D_CALL_RET(texture, GetDesc, &textureDesc);
1833 textureDesc.Width = w;
1834 textureDesc.Height = h;
1835 if (textureDesc.Format == DXGI_FORMAT_NV12 ||
1836 textureDesc.Format == DXGI_FORMAT_P010) {
1837 textureDesc.Width = (textureDesc.Width + 1) & ~1;
1838 textureDesc.Height = (textureDesc.Height + 1) & ~1;
1839 }
1840
1841 SDL_zero(uploadDesc);
1842 uploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
1843 uploadDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
1844 uploadDesc.Height = 1;
1845 uploadDesc.DepthOrArraySize = 1;
1846 uploadDesc.MipLevels = 1;
1847 uploadDesc.Format = DXGI_FORMAT_UNKNOWN;
1848 uploadDesc.SampleDesc.Count = 1;
1849 uploadDesc.SampleDesc.Quality = 0;
1850 uploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
1851 uploadDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
1852
1853 // Figure out how much we need to allocate for the upload buffer
1854 ID3D12Device1_GetCopyableFootprints(rendererData->d3dDevice,
1855 &textureDesc,
1856 plane,
1857 1,
1858 0,
1859 &placedTextureDesc,
1860 &NumRows,
1861 &RowLength,
1862 &uploadDesc.Width);
1863 RowPitch = placedTextureDesc.Footprint.RowPitch;
1864
1865 SDL_zero(heapProps);
1866 heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
1867 heapProps.CreationNodeMask = 1;
1868 heapProps.VisibleNodeMask = 1;
1869
1870 // Create the upload buffer
1871 result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice,
1872 &heapProps,
1873 D3D12_HEAP_FLAG_NONE,
1874 &uploadDesc,
1875 D3D12_RESOURCE_STATE_GENERIC_READ,
1876 NULL,
1877 D3D_GUID(SDL_IID_ID3D12Resource),
1878 (void **)&rendererData->uploadBuffers[rendererData->currentUploadBuffer]);
1879 if (FAILED(result)) {
1880 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommittedResource [create upload buffer]"), result);
1881 }
1882
1883 // Get a write-only pointer to data in the upload buffer:
1884 uploadBuffer = rendererData->uploadBuffers[rendererData->currentUploadBuffer];
1885 result = ID3D12Resource_Map(uploadBuffer,
1886 0,
1887 NULL,
1888 (void **)&textureMemory);
1889 if (FAILED(result)) {
1890 D3D_SAFE_RELEASE(rendererData->uploadBuffers[rendererData->currentUploadBuffer]);
1891 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Resource::Map [map staging texture]"), result);
1892 }
1893
1894 src = (const Uint8 *)pixels;
1895 dst = textureMemory;
1896 length = (UINT)RowLength;
1897 if (length == (UINT)pitch && length == RowPitch) {
1898 SDL_memcpy(dst, src, (size_t)length * NumRows);
1899 } else {
1900 if (length > (UINT)pitch) {
1901 length = pitch;
1902 }
1903 if (length > RowPitch) {
1904 length = RowPitch;
1905 }
1906 for (row = NumRows; row--; ) {
1907 SDL_memcpy(dst, src, length);
1908 src += pitch;
1909 dst += RowPitch;
1910 }
1911 }
1912
1913 // Commit the changes back to the upload buffer:
1914 ID3D12Resource_Unmap(uploadBuffer, 0, NULL);
1915
1916 // Make sure the destination is in the correct resource state
1917 D3D12_TransitionResource(rendererData, texture, *resourceState, D3D12_RESOURCE_STATE_COPY_DEST);
1918 *resourceState = D3D12_RESOURCE_STATE_COPY_DEST;
1919
1920 SDL_zero(dstLocation);
1921 dstLocation.pResource = texture;
1922 dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
1923 dstLocation.SubresourceIndex = plane;
1924
1925 SDL_zero(srcLocation);
1926 srcLocation.pResource = rendererData->uploadBuffers[rendererData->currentUploadBuffer];
1927 srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
1928 srcLocation.PlacedFootprint = placedTextureDesc;
1929
1930 ID3D12GraphicsCommandList2_CopyTextureRegion(rendererData->commandList,
1931 &dstLocation,
1932 x,
1933 y,
1934 0,
1935 &srcLocation,
1936 NULL);
1937
1938 // Transition the texture to be shader accessible
1939 D3D12_TransitionResource(rendererData, texture, *resourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
1940 *resourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
1941
1942 rendererData->currentUploadBuffer++;
1943 // If we've used up all the upload buffers, we need to issue the batch
1944 if (rendererData->currentUploadBuffer == SDL_D3D12_NUM_UPLOAD_BUFFERS) {
1945 D3D12_IssueBatch(rendererData);
1946 }
1947
1948 return true;
1949}
1950
1951static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
1952 const SDL_Rect *rect, const void *srcPixels,
1953 int srcPitch)
1954{
1955 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
1956 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal;
1957
1958 if (!textureData) {
1959 return SDL_SetError("Texture is not currently available");
1960 }
1961
1962 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainResourceState)) {
1963 return false;
1964 }
1965#ifdef SDL_HAVE_YUV
1966 if (textureData->yuv) {
1967 // Skip to the correct offset into the next texture
1968 srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch);
1969
1970 if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureV : textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateV : &textureData->mainResourceStateU)) {
1971 return false;
1972 }
1973
1974 // Skip to the correct offset into the next texture
1975 srcPixels = (const void *)((const Uint8 *)srcPixels + ((rect->h + 1) / 2) * ((srcPitch + 1) / 2));
1976 if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureU : textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateU : &textureData->mainResourceStateV)) {
1977 return false;
1978 }
1979 }
1980
1981 if (textureData->nv12) {
1982 // Skip to the correct offset into the next texture
1983 srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch);
1984
1985 if (texture->format == SDL_PIXELFORMAT_P010) {
1986 srcPitch = (srcPitch + 3) & ~3;
1987 } else {
1988 srcPitch = (srcPitch + 1) & ~1;
1989 }
1990 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 1, rect->x, rect->y, (rect->w + 1) & ~1, (rect->h + 1) & ~1, srcPixels, srcPitch, &textureData->mainResourceState)) {
1991 return false;
1992 }
1993 }
1994#endif // SDL_HAVE_YUV
1995 return true;
1996}
1997
1998#ifdef SDL_HAVE_YUV
1999static bool D3D12_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
2000 const SDL_Rect *rect,
2001 const Uint8 *Yplane, int Ypitch,
2002 const Uint8 *Uplane, int Upitch,
2003 const Uint8 *Vplane, int Vpitch)
2004{
2005 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
2006 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal;
2007
2008 if (!textureData) {
2009 return SDL_SetError("Texture is not currently available");
2010 }
2011
2012 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainResourceState)) {
2013 return false;
2014 }
2015 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Uplane, Upitch, &textureData->mainResourceStateU)) {
2016 return false;
2017 }
2018 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Vplane, Vpitch, &textureData->mainResourceStateV)) {
2019 return false;
2020 }
2021 return true;
2022}
2023
2024static bool D3D12_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
2025 const SDL_Rect *rect,
2026 const Uint8 *Yplane, int Ypitch,
2027 const Uint8 *UVplane, int UVpitch)
2028{
2029 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
2030 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal;
2031
2032 if (!textureData) {
2033 return SDL_SetError("Texture is not currently available");
2034 }
2035
2036 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainResourceState)) {
2037 return false;
2038 }
2039
2040 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 1, rect->x, rect->y, rect->w, rect->h, UVplane, UVpitch, &textureData->mainResourceState)) {
2041 return false;
2042 }
2043 return true;
2044}
2045#endif
2046
2047static bool D3D12_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture,
2048 const SDL_Rect *rect, void **pixels, int *pitch)
2049{
2050 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
2051 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal;
2052 HRESULT result = S_OK;
2053
2054 D3D12_RESOURCE_DESC textureDesc;
2055 D3D12_RESOURCE_DESC uploadDesc;
2056 D3D12_HEAP_PROPERTIES heapProps;
2057 D3D12_SUBRESOURCE_FOOTPRINT pitchedDesc;
2058 BYTE *textureMemory;
2059 int bpp;
2060
2061 if (!textureData) {
2062 return SDL_SetError("Texture is not currently available");
2063 }
2064#ifdef SDL_HAVE_YUV
2065 if (textureData->yuv || textureData->nv12) {
2066 // It's more efficient to upload directly...
2067 if (!textureData->pixels) {
2068 textureData->pitch = texture->w;
2069 textureData->pixels = (Uint8 *)SDL_malloc((texture->h * textureData->pitch * 3) / 2);
2070 if (!textureData->pixels) {
2071 return false;
2072 }
2073 }
2074 textureData->lockedRect = *rect;
2075 *pixels =
2076 (void *)(textureData->pixels + rect->y * textureData->pitch +
2077 rect->x * SDL_BYTESPERPIXEL(texture->format));
2078 *pitch = textureData->pitch;
2079 return true;
2080 }
2081#endif
2082 if (textureData->stagingBuffer) {
2083 return SDL_SetError("texture is already locked");
2084 }
2085
2086 // Create an upload buffer, which will be used to write to the main texture.
2087 SDL_zero(textureDesc);
2088 D3D_CALL_RET(textureData->mainTexture, GetDesc, &textureDesc);
2089 textureDesc.Width = rect->w;
2090 textureDesc.Height = rect->h;
2091
2092 SDL_zero(uploadDesc);
2093 uploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
2094 uploadDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
2095 uploadDesc.Height = 1;
2096 uploadDesc.DepthOrArraySize = 1;
2097 uploadDesc.MipLevels = 1;
2098 uploadDesc.Format = DXGI_FORMAT_UNKNOWN;
2099 uploadDesc.SampleDesc.Count = 1;
2100 uploadDesc.SampleDesc.Quality = 0;
2101 uploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
2102 uploadDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
2103
2104 // Figure out how much we need to allocate for the upload buffer
2105 ID3D12Device1_GetCopyableFootprints(rendererData->d3dDevice,
2106 &textureDesc,
2107 0,
2108 1,
2109 0,
2110 NULL,
2111 NULL,
2112 NULL,
2113 &uploadDesc.Width);
2114
2115 SDL_zero(heapProps);
2116 heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
2117 heapProps.CreationNodeMask = 1;
2118 heapProps.VisibleNodeMask = 1;
2119
2120 // Create the upload buffer
2121 result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice,
2122 &heapProps,
2123 D3D12_HEAP_FLAG_NONE,
2124 &uploadDesc,
2125 D3D12_RESOURCE_STATE_GENERIC_READ,
2126 NULL,
2127 D3D_GUID(SDL_IID_ID3D12Resource),
2128 (void **)&textureData->stagingBuffer);
2129 if (FAILED(result)) {
2130 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommittedResource [create upload buffer]"), result);
2131 }
2132
2133 // Get a write-only pointer to data in the upload buffer:
2134 result = ID3D12Resource_Map(textureData->stagingBuffer,
2135 0,
2136 NULL,
2137 (void **)&textureMemory);
2138 if (FAILED(result)) {
2139 D3D_SAFE_RELEASE(rendererData->uploadBuffers[rendererData->currentUploadBuffer]);
2140 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Resource::Map [map staging texture]"), result);
2141 }
2142
2143 SDL_zero(pitchedDesc);
2144 pitchedDesc.Format = textureDesc.Format;
2145 pitchedDesc.Width = rect->w;
2146 pitchedDesc.Height = rect->h;
2147 pitchedDesc.Depth = 1;
2148 if (pitchedDesc.Format == DXGI_FORMAT_R8_UNORM) {
2149 bpp = 1;
2150 } else {
2151 bpp = 4;
2152 }
2153 pitchedDesc.RowPitch = D3D12_Align(rect->w * bpp, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
2154
2155 /* Make note of where the staging texture will be written to
2156 * (on a call to SDL_UnlockTexture):
2157 */
2158 textureData->lockedRect = *rect;
2159
2160 /* Make sure the caller has information on the texture's pixel buffer,
2161 * then return:
2162 */
2163 *pixels = textureMemory;
2164 *pitch = pitchedDesc.RowPitch;
2165 return true;
2166}
2167
2168static void D3D12_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
2169{
2170 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
2171 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal;
2172
2173 D3D12_RESOURCE_DESC textureDesc;
2174 D3D12_SUBRESOURCE_FOOTPRINT pitchedDesc;
2175 D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedTextureDesc;
2176 D3D12_TEXTURE_COPY_LOCATION srcLocation;
2177 D3D12_TEXTURE_COPY_LOCATION dstLocation;
2178 int bpp;
2179
2180 if (!textureData) {
2181 return;
2182 }
2183#ifdef SDL_HAVE_YUV
2184 if (textureData->yuv || textureData->nv12) {
2185 const SDL_Rect *rect = &textureData->lockedRect;
2186 void *pixels =
2187 (void *)(textureData->pixels + rect->y * textureData->pitch +
2188 rect->x * SDL_BYTESPERPIXEL(texture->format));
2189 D3D12_UpdateTexture(renderer, texture, rect, pixels, textureData->pitch);
2190 return;
2191 }
2192#endif
2193 // Commit the pixel buffer's changes back to the staging texture:
2194 ID3D12Resource_Unmap(textureData->stagingBuffer, 0, NULL);
2195
2196 SDL_zero(textureDesc);
2197 D3D_CALL_RET(textureData->mainTexture, GetDesc, &textureDesc);
2198 textureDesc.Width = textureData->lockedRect.w;
2199 textureDesc.Height = textureData->lockedRect.h;
2200
2201 SDL_zero(pitchedDesc);
2202 pitchedDesc.Format = textureDesc.Format;
2203 pitchedDesc.Width = (UINT)textureDesc.Width;
2204 pitchedDesc.Height = textureDesc.Height;
2205 pitchedDesc.Depth = 1;
2206 if (pitchedDesc.Format == DXGI_FORMAT_R8_UNORM) {
2207 bpp = 1;
2208 } else {
2209 bpp = 4;
2210 }
2211 pitchedDesc.RowPitch = D3D12_Align(textureData->lockedRect.w * bpp, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
2212
2213 SDL_zero(placedTextureDesc);
2214 placedTextureDesc.Offset = 0;
2215 placedTextureDesc.Footprint = pitchedDesc;
2216
2217 D3D12_TransitionResource(rendererData, textureData->mainTexture, textureData->mainResourceState, D3D12_RESOURCE_STATE_COPY_DEST);
2218 textureData->mainResourceState = D3D12_RESOURCE_STATE_COPY_DEST;
2219
2220 SDL_zero(dstLocation);
2221 dstLocation.pResource = textureData->mainTexture;
2222 dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
2223 dstLocation.SubresourceIndex = 0;
2224
2225 SDL_zero(srcLocation);
2226 srcLocation.pResource = textureData->stagingBuffer;
2227 srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
2228 srcLocation.PlacedFootprint = placedTextureDesc;
2229
2230 ID3D12GraphicsCommandList2_CopyTextureRegion(rendererData->commandList,
2231 &dstLocation,
2232 textureData->lockedRect.x,
2233 textureData->lockedRect.y,
2234 0,
2235 &srcLocation,
2236 NULL);
2237
2238 // Transition the texture to be shader accessible
2239 D3D12_TransitionResource(rendererData, textureData->mainTexture, textureData->mainResourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
2240 textureData->mainResourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
2241
2242 // Execute the command list before releasing the staging buffer
2243 D3D12_IssueBatch(rendererData);
2244 D3D_SAFE_RELEASE(textureData->stagingBuffer);
2245}
2246
2247static void D3D12_SetTextureScaleMode(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ScaleMode scaleMode)
2248{
2249 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal;
2250
2251 if (!textureData) {
2252 return;
2253 }
2254
2255 textureData->scaleMode = (scaleMode == SDL_SCALEMODE_NEAREST) ? D3D12_FILTER_MIN_MAG_MIP_POINT : D3D12_FILTER_MIN_MAG_MIP_LINEAR;
2256}
2257
2258static bool D3D12_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
2259{
2260 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
2261 D3D12_TextureData *textureData = NULL;
2262
2263 if (!texture) {
2264 if (rendererData->textureRenderTarget) {
2265 D3D12_TransitionResource(rendererData,
2266 rendererData->textureRenderTarget->mainTexture,
2267 rendererData->textureRenderTarget->mainResourceState,
2268 D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
2269 rendererData->textureRenderTarget->mainResourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
2270 }
2271 rendererData->textureRenderTarget = NULL;
2272 return true;
2273 }
2274
2275 textureData = (D3D12_TextureData *)texture->internal;
2276
2277 if (!textureData->mainTextureRenderTargetView.ptr) {
2278 return SDL_SetError("specified texture is not a render target");
2279 }
2280
2281 rendererData->textureRenderTarget = textureData;
2282 D3D12_TransitionResource(rendererData,
2283 rendererData->textureRenderTarget->mainTexture,
2284 rendererData->textureRenderTarget->mainResourceState,
2285 D3D12_RESOURCE_STATE_RENDER_TARGET);
2286 rendererData->textureRenderTarget->mainResourceState = D3D12_RESOURCE_STATE_RENDER_TARGET;
2287
2288 return true;
2289}
2290
2291static bool D3D12_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
2292{
2293 return true; // nothing to do in this backend.
2294}
2295
2296static bool D3D12_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count)
2297{
2298 D3D12_VertexPositionColor *verts = (D3D12_VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(D3D12_VertexPositionColor), 0, &cmd->data.draw.first);
2299 int i;
2300 SDL_FColor color = cmd->data.draw.color;
2301 bool convert_color = SDL_RenderingLinearSpace(renderer);
2302
2303 if (!verts) {
2304 return false;
2305 }
2306
2307 cmd->data.draw.count = count;
2308
2309 if (convert_color) {
2310 SDL_ConvertToLinear(&color);
2311 }
2312
2313 for (i = 0; i < count; i++) {
2314 verts->pos.x = points[i].x + 0.5f;
2315 verts->pos.y = points[i].y + 0.5f;
2316 verts->tex.x = 0.0f;
2317 verts->tex.y = 0.0f;
2318 verts->color = color;
2319 verts++;
2320 }
2321
2322 return true;
2323}
2324
2325static bool D3D12_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
2326 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride,
2327 int num_vertices, const void *indices, int num_indices, int size_indices,
2328 float scale_x, float scale_y)
2329{
2330 int i;
2331 int count = indices ? num_indices : num_vertices;
2332 D3D12_VertexPositionColor *verts = (D3D12_VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(D3D12_VertexPositionColor), 0, &cmd->data.draw.first);
2333 bool convert_color = SDL_RenderingLinearSpace(renderer);
2334 D3D12_TextureData *textureData = texture ? (D3D12_TextureData *)texture->internal : NULL;
2335 float u_scale = textureData ? (float)texture->w / textureData->w : 0.0f;
2336 float v_scale = textureData ? (float)texture->h / textureData->h : 0.0f;
2337
2338 if (!verts) {
2339 return false;
2340 }
2341
2342 cmd->data.draw.count = count;
2343 size_indices = indices ? size_indices : 0;
2344
2345 for (i = 0; i < count; i++) {
2346 int j;
2347 float *xy_;
2348 if (size_indices == 4) {
2349 j = ((const Uint32 *)indices)[i];
2350 } else if (size_indices == 2) {
2351 j = ((const Uint16 *)indices)[i];
2352 } else if (size_indices == 1) {
2353 j = ((const Uint8 *)indices)[i];
2354 } else {
2355 j = i;
2356 }
2357
2358 xy_ = (float *)((char *)xy + j * xy_stride);
2359
2360 verts->pos.x = xy_[0] * scale_x;
2361 verts->pos.y = xy_[1] * scale_y;
2362 verts->color = *(SDL_FColor *)((char *)color + j * color_stride);
2363 if (convert_color) {
2364 SDL_ConvertToLinear(&verts->color);
2365 }
2366
2367 if (texture) {
2368 float *uv_ = (float *)((char *)uv + j * uv_stride);
2369 verts->tex.x = uv_[0] * u_scale;
2370 verts->tex.y = uv_[1] * v_scale;
2371 } else {
2372 verts->tex.x = 0.0f;
2373 verts->tex.y = 0.0f;
2374 }
2375
2376 verts += 1;
2377 }
2378 return true;
2379}
2380
2381static bool D3D12_UpdateVertexBuffer(SDL_Renderer *renderer,
2382 const void *vertexData, size_t dataSizeInBytes)
2383{
2384 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
2385 HRESULT result = S_OK;
2386 const int vbidx = rendererData->currentVertexBuffer;
2387 UINT8 *vertexBufferData = NULL;
2388 D3D12_RANGE range;
2389 ID3D12Resource *vertexBuffer;
2390
2391 range.Begin = 0;
2392 range.End = 0;
2393
2394 if (dataSizeInBytes == 0) {
2395 return true; // nothing to do.
2396 }
2397
2398 if (rendererData->issueBatch) {
2399 if (FAILED(D3D12_IssueBatch(rendererData))) {
2400 return SDL_SetError("Failed to issue intermediate batch");
2401 }
2402 }
2403
2404 // If the existing vertex buffer isn't big enough, we need to recreate a big enough one
2405 if (dataSizeInBytes > rendererData->vertexBuffers[vbidx].size) {
2406 D3D12_CreateVertexBuffer(rendererData, vbidx, dataSizeInBytes);
2407 }
2408
2409 vertexBuffer = rendererData->vertexBuffers[vbidx].resource;
2410 result = ID3D12Resource_Map(vertexBuffer, 0, &range, (void **)&vertexBufferData);
2411 if (FAILED(result)) {
2412 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Resource::Map [vertex buffer]"), result);
2413 }
2414 SDL_memcpy(vertexBufferData, vertexData, dataSizeInBytes);
2415 ID3D12Resource_Unmap(vertexBuffer, 0, NULL);
2416
2417 rendererData->vertexBuffers[vbidx].view.SizeInBytes = (UINT)dataSizeInBytes;
2418
2419 ID3D12GraphicsCommandList2_IASetVertexBuffers(rendererData->commandList, 0, 1, &rendererData->vertexBuffers[vbidx].view);
2420
2421 rendererData->currentVertexBuffer++;
2422 if (rendererData->currentVertexBuffer >= SDL_D3D12_NUM_VERTEX_BUFFERS) {
2423 rendererData->currentVertexBuffer = 0;
2424 rendererData->issueBatch = true;
2425 }
2426
2427 return true;
2428}
2429
2430static bool D3D12_UpdateViewport(SDL_Renderer *renderer)
2431{
2432 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
2433 const SDL_Rect *viewport = &data->currentViewport;
2434 Float4X4 projection;
2435 Float4X4 view;
2436 SDL_FRect orientationAlignedViewport;
2437 BOOL swapDimensions;
2438 D3D12_VIEWPORT d3dviewport;
2439 const int rotation = D3D12_GetRotationForCurrentRenderTarget(renderer);
2440
2441 if (viewport->w == 0 || viewport->h == 0) {
2442 /* If the viewport is empty, assume that it is because
2443 * SDL_CreateRenderer is calling it, and will call it again later
2444 * with a non-empty viewport.
2445 */
2446 // SDL_Log("%s, no viewport was set!", __FUNCTION__);
2447 return false;
2448 }
2449
2450 /* Make sure the SDL viewport gets rotated to that of the physical display's rotation.
2451 * Keep in mind here that the Y-axis will be been inverted (from Direct3D's
2452 * default coordinate system) so rotations will be done in the opposite
2453 * direction of the DXGI_MODE_ROTATION enumeration.
2454 */
2455 switch (rotation) {
2456 case DXGI_MODE_ROTATION_IDENTITY:
2457 projection = MatrixIdentity();
2458 break;
2459 case DXGI_MODE_ROTATION_ROTATE270:
2460 projection = MatrixRotationZ(SDL_PI_F * 0.5f);
2461 break;
2462 case DXGI_MODE_ROTATION_ROTATE180:
2463 projection = MatrixRotationZ(SDL_PI_F);
2464 break;
2465 case DXGI_MODE_ROTATION_ROTATE90:
2466 projection = MatrixRotationZ(-SDL_PI_F * 0.5f);
2467 break;
2468 default:
2469 return SDL_SetError("An unknown DisplayOrientation is being used");
2470 }
2471
2472 // Update the view matrix
2473 SDL_zero(view);
2474 view.m[0][0] = 2.0f / viewport->w;
2475 view.m[1][1] = -2.0f / viewport->h;
2476 view.m[2][2] = 1.0f;
2477 view.m[3][0] = -1.0f;
2478 view.m[3][1] = 1.0f;
2479 view.m[3][3] = 1.0f;
2480
2481 /* Combine the projection + view matrix together now, as both only get
2482 * set here (as of this writing, on Dec 26, 2013). When done, store it
2483 * for eventual transfer to the GPU.
2484 */
2485 data->vertexShaderConstantsData.projectionAndView = MatrixMultiply(
2486 view,
2487 projection);
2488
2489 /* Update the Direct3D viewport, which seems to be aligned to the
2490 * swap buffer's coordinate space, which is always in either
2491 * a landscape mode, for all Windows 8/RT devices, or a portrait mode,
2492 * for Windows Phone devices.
2493 */
2494 swapDimensions = D3D12_IsDisplayRotated90Degrees((DXGI_MODE_ROTATION)rotation);
2495 if (swapDimensions) {
2496 orientationAlignedViewport.x = (float)viewport->y;
2497 orientationAlignedViewport.y = (float)viewport->x;
2498 orientationAlignedViewport.w = (float)viewport->h;
2499 orientationAlignedViewport.h = (float)viewport->w;
2500 } else {
2501 orientationAlignedViewport.x = (float)viewport->x;
2502 orientationAlignedViewport.y = (float)viewport->y;
2503 orientationAlignedViewport.w = (float)viewport->w;
2504 orientationAlignedViewport.h = (float)viewport->h;
2505 }
2506
2507 d3dviewport.TopLeftX = orientationAlignedViewport.x;
2508 d3dviewport.TopLeftY = orientationAlignedViewport.y;
2509 d3dviewport.Width = orientationAlignedViewport.w;
2510 d3dviewport.Height = orientationAlignedViewport.h;
2511 d3dviewport.MinDepth = 0.0f;
2512 d3dviewport.MaxDepth = 1.0f;
2513 // SDL_Log("%s: D3D viewport = {%f,%f,%f,%f}", __FUNCTION__, d3dviewport.TopLeftX, d3dviewport.TopLeftY, d3dviewport.Width, d3dviewport.Height);
2514 ID3D12GraphicsCommandList_RSSetViewports(data->commandList, 1, &d3dviewport);
2515
2516 data->viewportDirty = false;
2517
2518 return true;
2519}
2520
2521static void D3D12_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Texture *texture, D3D12_PixelShaderConstants *constants)
2522{
2523 float output_headroom;
2524
2525 SDL_zerop(constants);
2526
2527 constants->scRGB_output = (float)SDL_RenderingLinearSpace(renderer);
2528 constants->color_scale = cmd->data.draw.color_scale;
2529
2530 if (texture) {
2531 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal;
2532
2533 switch (texture->format) {
2534 case SDL_PIXELFORMAT_YV12:
2535 case SDL_PIXELFORMAT_IYUV:
2536 constants->texture_type = TEXTURETYPE_YUV;
2537 constants->input_type = INPUTTYPE_SRGB;
2538 break;
2539 case SDL_PIXELFORMAT_NV12:
2540 constants->texture_type = TEXTURETYPE_NV12;
2541 constants->input_type = INPUTTYPE_SRGB;
2542 break;
2543 case SDL_PIXELFORMAT_NV21:
2544 constants->texture_type = TEXTURETYPE_NV21;
2545 constants->input_type = INPUTTYPE_SRGB;
2546 break;
2547 case SDL_PIXELFORMAT_P010:
2548 constants->texture_type = TEXTURETYPE_NV12;
2549 constants->input_type = INPUTTYPE_HDR10;
2550 break;
2551 default:
2552 constants->texture_type = TEXTURETYPE_RGB;
2553 if (texture->colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
2554 constants->input_type = INPUTTYPE_SCRGB;
2555 } else if (texture->colorspace == SDL_COLORSPACE_HDR10) {
2556 constants->input_type = INPUTTYPE_HDR10;
2557 } else {
2558 // The sampler will convert from sRGB to linear on load if working in linear colorspace
2559 constants->input_type = INPUTTYPE_UNSPECIFIED;
2560 }
2561 break;
2562 }
2563
2564 constants->sdr_white_point = texture->SDR_white_point;
2565
2566 if (renderer->target) {
2567 output_headroom = renderer->target->HDR_headroom;
2568 } else {
2569 output_headroom = renderer->HDR_headroom;
2570 }
2571
2572 if (texture->HDR_headroom > output_headroom) {
2573 constants->tonemap_method = TONEMAP_CHROME;
2574 constants->tonemap_factor1 = (output_headroom / (texture->HDR_headroom * texture->HDR_headroom));
2575 constants->tonemap_factor2 = (1.0f / output_headroom);
2576 }
2577
2578 if (textureData->YCbCr_matrix) {
2579 SDL_memcpy(constants->YCbCr_matrix, textureData->YCbCr_matrix, sizeof(constants->YCbCr_matrix));
2580 }
2581 }
2582}
2583
2584static bool D3D12_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, D3D12_Shader shader, const D3D12_PixelShaderConstants *shader_constants,
2585 D3D12_PRIMITIVE_TOPOLOGY_TYPE topology,
2586 const int numShaderResources, D3D12_CPU_DESCRIPTOR_HANDLE *shaderResources,
2587 D3D12_CPU_DESCRIPTOR_HANDLE *sampler, const Float4X4 *matrix)
2588
2589{
2590 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
2591 const Float4X4 *newmatrix = matrix ? matrix : &rendererData->identity;
2592 D3D12_CPU_DESCRIPTOR_HANDLE renderTargetView = D3D12_GetCurrentRenderTargetView(renderer);
2593 const SDL_BlendMode blendMode = cmd->data.draw.blend;
2594 bool updateSubresource = false;
2595 int i;
2596 D3D12_CPU_DESCRIPTOR_HANDLE firstShaderResource;
2597 DXGI_FORMAT rtvFormat = rendererData->renderTargetFormat;
2598 D3D12_PipelineState *currentPipelineState = rendererData->currentPipelineState;;
2599 D3D12_PixelShaderConstants solid_constants;
2600
2601 if (rendererData->textureRenderTarget) {
2602 rtvFormat = rendererData->textureRenderTarget->mainTextureFormat;
2603 }
2604
2605 // See if we need to change the pipeline state
2606 if (!currentPipelineState ||
2607 currentPipelineState->shader != shader ||
2608 currentPipelineState->blendMode != blendMode ||
2609 currentPipelineState->topology != topology ||
2610 currentPipelineState->rtvFormat != rtvFormat) {
2611
2612 /* Find the matching pipeline.
2613 NOTE: Although it may seem inefficient to linearly search through ~450 pipelines
2614 to find the correct one, in profiling this doesn't come up at all.
2615 It's unlikely that using a hash table would affect performance a measurable amount unless
2616 it's a degenerate case that's changing the pipeline state dozens of times per frame.
2617 */
2618 currentPipelineState = NULL;
2619 for (i = 0; i < rendererData->pipelineStateCount; ++i) {
2620 D3D12_PipelineState *candidatePiplineState = &rendererData->pipelineStates[i];
2621 if (candidatePiplineState->shader == shader &&
2622 candidatePiplineState->blendMode == blendMode &&
2623 candidatePiplineState->topology == topology &&
2624 candidatePiplineState->rtvFormat == rtvFormat) {
2625 currentPipelineState = candidatePiplineState;
2626 break;
2627 }
2628 }
2629
2630 // If we didn't find a match, create a new one -- it must mean the blend mode is non-standard
2631 if (!currentPipelineState) {
2632 currentPipelineState = D3D12_CreatePipelineState(renderer, shader, blendMode, topology, rtvFormat);
2633 }
2634
2635 if (!currentPipelineState) {
2636 // The error has been set inside D3D12_CreatePipelineState()
2637 return false;
2638 }
2639
2640 ID3D12GraphicsCommandList2_SetPipelineState(rendererData->commandList, currentPipelineState->pipelineState);
2641 ID3D12GraphicsCommandList2_SetGraphicsRootSignature(rendererData->commandList,
2642 rendererData->rootSignatures[D3D12_GetRootSignatureType(currentPipelineState->shader)]);
2643 // When we change these we will need to re-upload the constant buffer and reset any descriptors
2644 updateSubresource = true;
2645 rendererData->currentSampler.ptr = 0;
2646 rendererData->currentShaderResource.ptr = 0;
2647 rendererData->currentPipelineState = currentPipelineState;
2648 }
2649
2650 if (renderTargetView.ptr != rendererData->currentRenderTargetView.ptr) {
2651 ID3D12GraphicsCommandList2_OMSetRenderTargets(rendererData->commandList, 1, &renderTargetView, FALSE, NULL);
2652 rendererData->currentRenderTargetView = renderTargetView;
2653 }
2654
2655 if (rendererData->viewportDirty) {
2656 if (D3D12_UpdateViewport(renderer)) {
2657 // vertexShaderConstantsData.projectionAndView has changed
2658 updateSubresource = true;
2659 }
2660 }
2661
2662 if (rendererData->cliprectDirty) {
2663 D3D12_RECT scissorRect;
2664 if (!D3D12_GetViewportAlignedD3DRect(renderer, &rendererData->currentCliprect, &scissorRect, TRUE)) {
2665 // D3D12_GetViewportAlignedD3DRect will have set the SDL error
2666 return false;
2667 }
2668 ID3D12GraphicsCommandList2_RSSetScissorRects(rendererData->commandList, 1, &scissorRect);
2669 rendererData->cliprectDirty = false;
2670 }
2671
2672 if (numShaderResources > 0) {
2673 firstShaderResource = shaderResources[0];
2674 } else {
2675 firstShaderResource.ptr = 0;
2676 }
2677 if (firstShaderResource.ptr != rendererData->currentShaderResource.ptr) {
2678 for (i = 0; i < numShaderResources; ++i) {
2679 D3D12_GPU_DESCRIPTOR_HANDLE GPUHandle = D3D12_CPUtoGPUHandle(rendererData->srvDescriptorHeap, shaderResources[i]);
2680 ID3D12GraphicsCommandList2_SetGraphicsRootDescriptorTable(rendererData->commandList, i + 2, GPUHandle);
2681 }
2682 rendererData->currentShaderResource.ptr = firstShaderResource.ptr;
2683 }
2684
2685 if (sampler && sampler->ptr != rendererData->currentSampler.ptr) {
2686 D3D12_GPU_DESCRIPTOR_HANDLE GPUHandle = D3D12_CPUtoGPUHandle(rendererData->samplerDescriptorHeap, *sampler);
2687 UINT tableIndex = 0;
2688
2689 // Figure out the correct sampler descriptor table index based on the type of shader
2690 switch (shader) {
2691 case SHADER_RGB:
2692 tableIndex = 3;
2693 break;
2694 case SHADER_ADVANCED:
2695 tableIndex = 5;
2696 break;
2697 default:
2698 return SDL_SetError("[direct3d12] Trying to set a sampler for a shader which doesn't have one");
2699 break;
2700 }
2701
2702 ID3D12GraphicsCommandList2_SetGraphicsRootDescriptorTable(rendererData->commandList, tableIndex, GPUHandle);
2703 rendererData->currentSampler = *sampler;
2704 }
2705
2706 if (updateSubresource == true || SDL_memcmp(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof(*newmatrix)) != 0) {
2707 SDL_memcpy(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof(*newmatrix));
2708 ID3D12GraphicsCommandList2_SetGraphicsRoot32BitConstants(rendererData->commandList,
2709 0,
2710 32,
2711 &rendererData->vertexShaderConstantsData,
2712 0);
2713 }
2714
2715 if (!shader_constants) {
2716 D3D12_SetupShaderConstants(renderer, cmd, NULL, &solid_constants);
2717 shader_constants = &solid_constants;
2718 }
2719
2720 if (updateSubresource == true ||
2721 SDL_memcmp(shader_constants, &currentPipelineState->shader_constants, sizeof(*shader_constants)) != 0) {
2722 ID3D12GraphicsCommandList2_SetGraphicsRoot32BitConstants(rendererData->commandList,
2723 1,
2724 sizeof(*shader_constants) / sizeof(float),
2725 shader_constants,
2726 0);
2727
2728 SDL_memcpy(&currentPipelineState->shader_constants, shader_constants, sizeof(*shader_constants));
2729 }
2730
2731 return true;
2732}
2733
2734static bool D3D12_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const Float4X4 *matrix)
2735{
2736 SDL_Texture *texture = cmd->data.draw.texture;
2737 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
2738 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal;
2739 D3D12_CPU_DESCRIPTOR_HANDLE *textureSampler;
2740 D3D12_PixelShaderConstants constants;
2741
2742 if (!textureData) {
2743 return SDL_SetError("Texture is not currently available");
2744 }
2745
2746 D3D12_SetupShaderConstants(renderer, cmd, texture, &constants);
2747
2748 switch (textureData->scaleMode) {
2749 case D3D12_FILTER_MIN_MAG_MIP_POINT:
2750 switch (cmd->data.draw.texture_address_mode) {
2751 case SDL_TEXTURE_ADDRESS_CLAMP:
2752 textureSampler = &rendererData->samplers[D3D12_SAMPLER_NEAREST_CLAMP];
2753 break;
2754 case SDL_TEXTURE_ADDRESS_WRAP:
2755 textureSampler = &rendererData->samplers[D3D12_SAMPLER_NEAREST_WRAP];
2756 break;
2757 default:
2758 return SDL_SetError("Unknown texture address mode: %d", cmd->data.draw.texture_address_mode);
2759 }
2760 break;
2761 case D3D12_FILTER_MIN_MAG_MIP_LINEAR:
2762 switch (cmd->data.draw.texture_address_mode) {
2763 case SDL_TEXTURE_ADDRESS_CLAMP:
2764 textureSampler = &rendererData->samplers[D3D12_SAMPLER_LINEAR_CLAMP];
2765 break;
2766 case SDL_TEXTURE_ADDRESS_WRAP:
2767 textureSampler = &rendererData->samplers[D3D12_SAMPLER_LINEAR_WRAP];
2768 break;
2769 default:
2770 return SDL_SetError("Unknown texture address mode: %d", cmd->data.draw.texture_address_mode);
2771 }
2772 break;
2773 default:
2774 return SDL_SetError("Unknown scale mode: %d", textureData->scaleMode);
2775 }
2776#ifdef SDL_HAVE_YUV
2777 if (textureData->yuv) {
2778 D3D12_CPU_DESCRIPTOR_HANDLE shaderResources[3];
2779
2780 shaderResources[0] = textureData->mainTextureResourceView;
2781 shaderResources[1] = textureData->mainTextureResourceViewU;
2782 shaderResources[2] = textureData->mainTextureResourceViewV;
2783
2784 // Make sure each texture is in the correct state to be accessed by the pixel shader.
2785 D3D12_TransitionResource(rendererData, textureData->mainTexture, textureData->mainResourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
2786 textureData->mainResourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
2787 D3D12_TransitionResource(rendererData, textureData->mainTextureU, textureData->mainResourceStateU, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
2788 textureData->mainResourceStateU = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
2789 D3D12_TransitionResource(rendererData, textureData->mainTextureV, textureData->mainResourceStateV, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
2790 textureData->mainResourceStateV = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
2791
2792 return D3D12_SetDrawState(renderer, cmd, textureData->shader, &constants, D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, SDL_arraysize(shaderResources), shaderResources, textureSampler, matrix);
2793 } else if (textureData->nv12) {
2794 D3D12_CPU_DESCRIPTOR_HANDLE shaderResources[2];
2795
2796 shaderResources[0] = textureData->mainTextureResourceView;
2797 shaderResources[1] = textureData->mainTextureResourceViewNV;
2798
2799 // Make sure each texture is in the correct state to be accessed by the pixel shader.
2800 D3D12_TransitionResource(rendererData, textureData->mainTexture, textureData->mainResourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
2801 textureData->mainResourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
2802
2803 return D3D12_SetDrawState(renderer, cmd, textureData->shader, &constants, D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, SDL_arraysize(shaderResources), shaderResources, textureSampler, matrix);
2804 }
2805#endif // SDL_HAVE_YUV
2806 D3D12_TransitionResource(rendererData, textureData->mainTexture, textureData->mainResourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
2807 textureData->mainResourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
2808 return D3D12_SetDrawState(renderer, cmd, textureData->shader, &constants, D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, 1, &textureData->mainTextureResourceView, textureSampler, matrix);
2809}
2810
2811static void D3D12_DrawPrimitives(SDL_Renderer *renderer, D3D12_PRIMITIVE_TOPOLOGY primitiveTopology, const size_t vertexStart, const size_t vertexCount)
2812{
2813 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
2814 ID3D12GraphicsCommandList2_IASetPrimitiveTopology(rendererData->commandList, primitiveTopology);
2815 ID3D12GraphicsCommandList2_DrawInstanced(rendererData->commandList, (UINT)vertexCount, 1, (UINT)vertexStart, 0);
2816}
2817
2818static void D3D12_InvalidateCachedState(SDL_Renderer *renderer)
2819{
2820 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
2821 data->currentRenderTargetView.ptr = 0;
2822 data->currentShaderResource.ptr = 0;
2823 data->currentSampler.ptr = 0;
2824 data->cliprectDirty = true;
2825 data->viewportDirty = true;
2826}
2827
2828static bool D3D12_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
2829{
2830 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
2831 const int viewportRotation = D3D12_GetRotationForCurrentRenderTarget(renderer);
2832
2833 if (!rendererData->d3dDevice) {
2834 return SDL_SetError("Device lost and couldn't be recovered");
2835 }
2836
2837 if (rendererData->pixelSizeChanged) {
2838 D3D12_UpdateForWindowSizeChange(renderer);
2839 rendererData->pixelSizeChanged = false;
2840 }
2841
2842 if (rendererData->currentViewportRotation != viewportRotation) {
2843 rendererData->currentViewportRotation = viewportRotation;
2844 rendererData->viewportDirty = true;
2845 }
2846
2847 if (!D3D12_UpdateVertexBuffer(renderer, vertices, vertsize)) {
2848 return false;
2849 }
2850
2851 while (cmd) {
2852 switch (cmd->command) {
2853 case SDL_RENDERCMD_SETDRAWCOLOR:
2854 {
2855 break; // this isn't currently used in this render backend.
2856 }
2857
2858 case SDL_RENDERCMD_SETVIEWPORT:
2859 {
2860 SDL_Rect *viewport = &rendererData->currentViewport;
2861 if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) {
2862 SDL_copyp(viewport, &cmd->data.viewport.rect);
2863 rendererData->viewportDirty = true;
2864 rendererData->cliprectDirty = true;
2865 }
2866 break;
2867 }
2868
2869 case SDL_RENDERCMD_SETCLIPRECT:
2870 {
2871 const SDL_Rect *rect = &cmd->data.cliprect.rect;
2872 SDL_Rect viewport_cliprect;
2873 if (rendererData->currentCliprectEnabled != cmd->data.cliprect.enabled) {
2874 rendererData->currentCliprectEnabled = cmd->data.cliprect.enabled;
2875 rendererData->cliprectDirty = true;
2876 }
2877 if (!rendererData->currentCliprectEnabled) {
2878 /* If the clip rect is disabled, then the scissor rect should be the whole viewport,
2879 since direct3d12 doesn't allow disabling the scissor rectangle */
2880 viewport_cliprect.x = 0;
2881 viewport_cliprect.y = 0;
2882 viewport_cliprect.w = rendererData->currentViewport.w;
2883 viewport_cliprect.h = rendererData->currentViewport.h;
2884 rect = &viewport_cliprect;
2885 }
2886 if (SDL_memcmp(&rendererData->currentCliprect, rect, sizeof(*rect)) != 0) {
2887 SDL_copyp(&rendererData->currentCliprect, rect);
2888 rendererData->cliprectDirty = true;
2889 }
2890 break;
2891 }
2892
2893 case SDL_RENDERCMD_CLEAR:
2894 {
2895 D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptor = D3D12_GetCurrentRenderTargetView(renderer);
2896 bool convert_color = SDL_RenderingLinearSpace(renderer);
2897 SDL_FColor color = cmd->data.color.color;
2898 if (convert_color) {
2899 SDL_ConvertToLinear(&color);
2900 }
2901 color.r *= cmd->data.color.color_scale;
2902 color.g *= cmd->data.color.color_scale;
2903 color.b *= cmd->data.color.color_scale;
2904 ID3D12GraphicsCommandList2_ClearRenderTargetView(rendererData->commandList, rtvDescriptor, &color.r, 0, NULL);
2905 break;
2906 }
2907
2908 case SDL_RENDERCMD_DRAW_POINTS:
2909 {
2910 const size_t count = cmd->data.draw.count;
2911 const size_t first = cmd->data.draw.first;
2912 const size_t start = first / sizeof(D3D12_VertexPositionColor);
2913 D3D12_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT, 0, NULL, NULL, NULL);
2914 D3D12_DrawPrimitives(renderer, D3D_PRIMITIVE_TOPOLOGY_POINTLIST, start, count);
2915 break;
2916 }
2917
2918 case SDL_RENDERCMD_DRAW_LINES:
2919 {
2920 const size_t count = cmd->data.draw.count;
2921 const size_t first = cmd->data.draw.first;
2922 const size_t start = first / sizeof(D3D12_VertexPositionColor);
2923 const D3D12_VertexPositionColor *verts = (D3D12_VertexPositionColor *)(((Uint8 *)vertices) + first);
2924 D3D12_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, 0, NULL, NULL, NULL);
2925 D3D12_DrawPrimitives(renderer, D3D_PRIMITIVE_TOPOLOGY_LINESTRIP, start, count);
2926 if (verts[0].pos.x != verts[count - 1].pos.x || verts[0].pos.y != verts[count - 1].pos.y) {
2927 D3D12_DrawPrimitives(renderer, D3D_PRIMITIVE_TOPOLOGY_POINTLIST, start + (count - 1), 1);
2928 }
2929 break;
2930 }
2931
2932 case SDL_RENDERCMD_FILL_RECTS: // unused
2933 break;
2934
2935 case SDL_RENDERCMD_COPY: // unused
2936 break;
2937
2938 case SDL_RENDERCMD_COPY_EX: // unused
2939 break;
2940
2941 case SDL_RENDERCMD_GEOMETRY:
2942 {
2943 SDL_Texture *texture = cmd->data.draw.texture;
2944 const size_t count = cmd->data.draw.count;
2945 const size_t first = cmd->data.draw.first;
2946 const size_t start = first / sizeof(D3D12_VertexPositionColor);
2947
2948 if (texture) {
2949 D3D12_SetCopyState(renderer, cmd, NULL);
2950 } else {
2951 D3D12_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, 0, NULL, NULL, NULL);
2952 }
2953
2954 D3D12_DrawPrimitives(renderer, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, start, count);
2955 break;
2956 }
2957
2958 case SDL_RENDERCMD_NO_OP:
2959 break;
2960 }
2961
2962 cmd = cmd->next;
2963 }
2964
2965 return true;
2966}
2967
2968static SDL_Surface *D3D12_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
2969{
2970 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
2971 ID3D12Resource *backBuffer = NULL;
2972 ID3D12Resource *readbackBuffer = NULL;
2973 HRESULT result;
2974 D3D12_RESOURCE_DESC textureDesc;
2975 D3D12_RESOURCE_DESC readbackDesc;
2976 D3D12_HEAP_PROPERTIES heapProps;
2977 D3D12_RECT srcRect = { 0, 0, 0, 0 };
2978 D3D12_BOX srcBox;
2979 D3D12_TEXTURE_COPY_LOCATION dstLocation;
2980 D3D12_TEXTURE_COPY_LOCATION srcLocation;
2981 D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedTextureDesc;
2982 D3D12_SUBRESOURCE_FOOTPRINT pitchedDesc;
2983 BYTE *textureMemory;
2984 int bpp;
2985 SDL_Surface *output = NULL;
2986
2987 if (data->textureRenderTarget) {
2988 backBuffer = data->textureRenderTarget->mainTexture;
2989 } else {
2990 backBuffer = data->renderTargets[data->currentBackBufferIndex];
2991 }
2992
2993 // Create a staging texture to copy the screen's data to:
2994 SDL_zero(textureDesc);
2995 D3D_CALL_RET(backBuffer, GetDesc, &textureDesc);
2996 textureDesc.Width = rect->w;
2997 textureDesc.Height = rect->h;
2998
2999 SDL_zero(readbackDesc);
3000 readbackDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
3001 readbackDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
3002 readbackDesc.Height = 1;
3003 readbackDesc.DepthOrArraySize = 1;
3004 readbackDesc.MipLevels = 1;
3005 readbackDesc.Format = DXGI_FORMAT_UNKNOWN;
3006 readbackDesc.SampleDesc.Count = 1;
3007 readbackDesc.SampleDesc.Quality = 0;
3008 readbackDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
3009 readbackDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
3010
3011 // Figure out how much we need to allocate for the upload buffer
3012 ID3D12Device1_GetCopyableFootprints(data->d3dDevice,
3013 &textureDesc,
3014 0,
3015 1,
3016 0,
3017 NULL,
3018 NULL,
3019 NULL,
3020 &readbackDesc.Width);
3021
3022 SDL_zero(heapProps);
3023 heapProps.Type = D3D12_HEAP_TYPE_READBACK;
3024 heapProps.CreationNodeMask = 1;
3025 heapProps.VisibleNodeMask = 1;
3026
3027 result = ID3D12Device1_CreateCommittedResource(data->d3dDevice,
3028 &heapProps,
3029 D3D12_HEAP_FLAG_NONE,
3030 &readbackDesc,
3031 D3D12_RESOURCE_STATE_COPY_DEST,
3032 NULL,
3033 D3D_GUID(SDL_IID_ID3D12Resource),
3034 (void **)&readbackBuffer);
3035 if (FAILED(result)) {
3036 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateTexture2D [create staging texture]"), result);
3037 goto done;
3038 }
3039
3040 // Transition the render target to be copyable from
3041 D3D12_TransitionResource(data, backBuffer, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
3042
3043 // Copy the desired portion of the back buffer to the staging texture:
3044 if (!D3D12_GetViewportAlignedD3DRect(renderer, rect, &srcRect, FALSE)) {
3045 // D3D12_GetViewportAlignedD3DRect will have set the SDL error
3046 goto done;
3047 }
3048 srcBox.left = srcRect.left;
3049 srcBox.right = srcRect.right;
3050 srcBox.top = srcRect.top;
3051 srcBox.bottom = srcRect.bottom;
3052 srcBox.front = 0;
3053 srcBox.back = 1;
3054
3055 // Issue the copy texture region
3056 SDL_zero(pitchedDesc);
3057 pitchedDesc.Format = textureDesc.Format;
3058 pitchedDesc.Width = (UINT)textureDesc.Width;
3059 pitchedDesc.Height = textureDesc.Height;
3060 pitchedDesc.Depth = 1;
3061 bpp = SDL_BYTESPERPIXEL(D3D12_DXGIFormatToSDLPixelFormat(pitchedDesc.Format));
3062 pitchedDesc.RowPitch = D3D12_Align(pitchedDesc.Width * bpp, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
3063
3064 SDL_zero(placedTextureDesc);
3065 placedTextureDesc.Offset = 0;
3066 placedTextureDesc.Footprint = pitchedDesc;
3067
3068 SDL_zero(dstLocation);
3069 dstLocation.pResource = readbackBuffer;
3070 dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
3071 dstLocation.PlacedFootprint = placedTextureDesc;
3072
3073 SDL_zero(srcLocation);
3074 srcLocation.pResource = backBuffer;
3075 srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
3076 srcLocation.SubresourceIndex = 0;
3077
3078 ID3D12GraphicsCommandList2_CopyTextureRegion(data->commandList,
3079 &dstLocation,
3080 0, 0, 0,
3081 &srcLocation,
3082 &srcBox);
3083
3084 // We need to issue the command list for the copy to finish
3085 D3D12_IssueBatch(data);
3086
3087 // Transition the render target back to a render target
3088 D3D12_TransitionResource(data, backBuffer, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
3089
3090 // Map the staging texture's data to CPU-accessible memory:
3091 result = ID3D12Resource_Map(readbackBuffer,
3092 0,
3093 NULL,
3094 (void **)&textureMemory);
3095 if (FAILED(result)) {
3096 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Resource::Map [map staging texture]"), result);
3097 goto done;
3098 }
3099
3100 output = SDL_DuplicatePixels(
3101 rect->w, rect->h,
3102 D3D12_DXGIFormatToSDLPixelFormat(textureDesc.Format),
3103 renderer->target ? renderer->target->colorspace : renderer->output_colorspace,
3104 textureMemory,
3105 pitchedDesc.RowPitch);
3106
3107 // Unmap the texture:
3108 ID3D12Resource_Unmap(readbackBuffer, 0, NULL);
3109
3110done:
3111 D3D_SAFE_RELEASE(readbackBuffer);
3112 return output;
3113}
3114
3115static bool D3D12_RenderPresent(SDL_Renderer *renderer)
3116{
3117 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
3118 HRESULT result;
3119
3120 if (!data->d3dDevice) {
3121 return SDL_SetError("Device lost and couldn't be recovered");
3122 }
3123
3124 // Transition the render target to present state
3125 D3D12_TransitionResource(data,
3126 data->renderTargets[data->currentBackBufferIndex],
3127 D3D12_RESOURCE_STATE_RENDER_TARGET,
3128 D3D12_RESOURCE_STATE_PRESENT);
3129
3130 // Issue the command list
3131 result = ID3D12GraphicsCommandList2_Close(data->commandList);
3132 ID3D12CommandQueue_ExecuteCommandLists(data->commandQueue, 1, (ID3D12CommandList *const *)&data->commandList);
3133
3134#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
3135 result = D3D12_XBOX_PresentFrame(data->commandQueue, data->frameToken, data->renderTargets[data->currentBackBufferIndex]);
3136#else
3137 /* The application may optionally specify "dirty" or "scroll"
3138 * rects to improve efficiency in certain scenarios.
3139 */
3140 result = IDXGISwapChain_Present(data->swapChain, data->syncInterval, data->presentFlags);
3141#endif
3142
3143 if (FAILED(result) && result != DXGI_ERROR_WAS_STILL_DRAWING) {
3144 /* If the device was removed either by a disconnect or a driver upgrade, we
3145 * must recreate all device resources.
3146 */
3147 if (result == DXGI_ERROR_DEVICE_REMOVED) {
3148 if (D3D12_HandleDeviceLost(renderer)) {
3149 SDL_SetError("Present failed, device lost");
3150 } else {
3151 // Recovering from device lost failed, error is already set
3152 }
3153 } else if (result == DXGI_ERROR_INVALID_CALL) {
3154 // We probably went through a fullscreen <-> windowed transition
3155 D3D12_CreateWindowSizeDependentResources(renderer);
3156 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain::Present"), result);
3157 } else {
3158 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain::Present"), result);
3159 }
3160 return false;
3161 } else {
3162 // Wait for the GPU and move to the next frame
3163 result = ID3D12CommandQueue_Signal(data->commandQueue, data->fence, data->fenceValue);
3164
3165 if (ID3D12Fence_GetCompletedValue(data->fence) < data->fenceValue) {
3166 result = ID3D12Fence_SetEventOnCompletion(data->fence,
3167 data->fenceValue,
3168 data->fenceEvent);
3169 WaitForSingleObjectEx(data->fenceEvent, INFINITE, FALSE);
3170 }
3171
3172 data->fenceValue++;
3173#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
3174 data->currentBackBufferIndex++;
3175 data->currentBackBufferIndex %= SDL_D3D12_NUM_BUFFERS;
3176#else
3177 data->currentBackBufferIndex = IDXGISwapChain4_GetCurrentBackBufferIndex(data->swapChain);
3178#endif
3179
3180 // Reset the command allocator and command list, and transition back to render target
3181 D3D12_ResetCommandList(data);
3182 D3D12_TransitionResource(data,
3183 data->renderTargets[data->currentBackBufferIndex],
3184 D3D12_RESOURCE_STATE_PRESENT,
3185 D3D12_RESOURCE_STATE_RENDER_TARGET);
3186
3187#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
3188 D3D12_XBOX_StartFrame(data->d3dDevice, &data->frameToken);
3189#endif
3190 return true;
3191 }
3192}
3193
3194static bool D3D12_SetVSync(SDL_Renderer *renderer, const int vsync)
3195{
3196 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
3197
3198 if (vsync < 0) {
3199 return SDL_Unsupported();
3200 }
3201
3202 if (vsync > 0) {
3203 data->syncInterval = vsync;
3204 data->presentFlags = 0;
3205 } else {
3206 data->syncInterval = 0;
3207 data->presentFlags = DXGI_PRESENT_ALLOW_TEARING;
3208 }
3209 return true;
3210}
3211
3212bool D3D12_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props)
3213{
3214 D3D12_RenderData *data;
3215
3216 HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
3217 if (!hwnd) {
3218 return SDL_SetError("Couldn't get window handle");
3219 }
3220
3221 if (SDL_GetWindowFlags(window) & SDL_WINDOW_TRANSPARENT) {
3222 // D3D12 removed the swap effect needed to support transparent windows, use D3D11 instead
3223 return SDL_SetError("The direct3d12 renderer doesn't work with transparent windows");
3224 }
3225
3226 SDL_SetupRendererColorspace(renderer, create_props);
3227
3228 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB &&
3229 renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR
3230 /*&& renderer->output_colorspace != SDL_COLORSPACE_HDR10*/) {
3231 return SDL_SetError("Unsupported output colorspace");
3232 }
3233
3234 data = (D3D12_RenderData *)SDL_calloc(1, sizeof(*data));
3235 if (!data) {
3236 return false;
3237 }
3238
3239 data->identity = MatrixIdentity();
3240
3241 renderer->WindowEvent = D3D12_WindowEvent;
3242 renderer->SupportsBlendMode = D3D12_SupportsBlendMode;
3243 renderer->CreateTexture = D3D12_CreateTexture;
3244 renderer->UpdateTexture = D3D12_UpdateTexture;
3245#ifdef SDL_HAVE_YUV
3246 renderer->UpdateTextureYUV = D3D12_UpdateTextureYUV;
3247 renderer->UpdateTextureNV = D3D12_UpdateTextureNV;
3248#endif
3249 renderer->LockTexture = D3D12_LockTexture;
3250 renderer->UnlockTexture = D3D12_UnlockTexture;
3251 renderer->SetTextureScaleMode = D3D12_SetTextureScaleMode;
3252 renderer->SetRenderTarget = D3D12_SetRenderTarget;
3253 renderer->QueueSetViewport = D3D12_QueueNoOp;
3254 renderer->QueueSetDrawColor = D3D12_QueueNoOp;
3255 renderer->QueueDrawPoints = D3D12_QueueDrawPoints;
3256 renderer->QueueDrawLines = D3D12_QueueDrawPoints; // lines and points queue vertices the same way.
3257 renderer->QueueGeometry = D3D12_QueueGeometry;
3258 renderer->InvalidateCachedState = D3D12_InvalidateCachedState;
3259 renderer->RunCommandQueue = D3D12_RunCommandQueue;
3260 renderer->RenderReadPixels = D3D12_RenderReadPixels;
3261 renderer->RenderPresent = D3D12_RenderPresent;
3262 renderer->DestroyTexture = D3D12_DestroyTexture;
3263 renderer->DestroyRenderer = D3D12_DestroyRenderer;
3264 renderer->SetVSync = D3D12_SetVSync;
3265 renderer->internal = data;
3266 D3D12_InvalidateCachedState(renderer);
3267
3268 renderer->name = D3D12_RenderDriver.name;
3269 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888);
3270 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR8888);
3271 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB8888);
3272 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR2101010);
3273 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA64_FLOAT);
3274 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
3275 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
3276 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
3277 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
3278 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
3279 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 16384);
3280
3281 data->syncInterval = 0;
3282 data->presentFlags = DXGI_PRESENT_ALLOW_TEARING;
3283
3284 /* HACK: make sure the SDL_Renderer references the SDL_Window data now, in
3285 * order to give init functions access to the underlying window handle:
3286 */
3287 renderer->window = window;
3288
3289 // Initialize Direct3D resources
3290 if (FAILED(D3D12_CreateDeviceResources(renderer))) {
3291 return false;
3292 }
3293 if (FAILED(D3D12_CreateWindowSizeDependentResources(renderer))) {
3294 return false;
3295 }
3296
3297 return true;
3298}
3299
3300SDL_RenderDriver D3D12_RenderDriver = {
3301 D3D12_CreateRenderer, "direct3d12"
3302};
3303
3304// Ends C function definitions when using C++
3305#ifdef __cplusplus
3306}
3307#endif
3308
3309#endif // SDL_VIDEO_RENDER_D3D12