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