SDL: DirectX 12 Renderer (#5761)

From 4082821822edd2cdffdeb130718821c5503140ea Mon Sep 17 00:00:00 2001
From: chalonverse <[EMAIL REDACTED]>
Date: Mon, 6 Jun 2022 17:42:30 -0700
Subject: [PATCH] DirectX 12 Renderer (#5761)

* DirectX 12 Renderer (27 squashed commits)

* Add missing SDL_hidapi.h of merge of SDL.vcxproj.filters

* Fixed OpenWatcom build failure

* Dynapi fix

Co-authored-by: Ryan C. Gordon <icculus@icculus.org>
---
 CMakeLists.txt                            |    7 +-
 Makefile.w32                              |    3 +-
 VisualC/SDL/SDL.vcxproj                   |    5 +-
 VisualC/SDL/SDL.vcxproj.filters           |   14 +-
 configure.ac                              |    5 +
 include/SDL_config.h.cmake                |    1 +
 include/SDL_config.h.in                   |    1 +
 include/SDL_config_windows.h              |   18 +
 include/SDL_config_winrt.h                |    3 +
 include/SDL_hints.h                       |    2 +
 include/SDL_system.h                      |   16 +
 src/core/windows/SDL_windows.h            |    4 +
 src/dynapi/SDL2.exports                   |    1 +
 src/dynapi/SDL_dynapi_overrides.h         |    1 +
 src/dynapi/SDL_dynapi_procs.h             |    3 +
 src/render/SDL_d3dmath.c                  |    4 +-
 src/render/SDL_d3dmath.h                  |    4 +-
 src/render/SDL_render.c                   |    3 +
 src/render/SDL_sysrender.h                |    1 +
 src/render/direct3d12/SDL_render_d3d12.c  | 3042 +++++++++
 src/render/direct3d12/SDL_shaders_d3d12.c | 6967 +++++++++++++++++++++
 src/render/direct3d12/SDL_shaders_d3d12.h |   57 +
 22 files changed, 10154 insertions(+), 8 deletions(-)
 create mode 100644 src/render/direct3d12/SDL_render_d3d12.c
 create mode 100644 src/render/direct3d12/SDL_shaders_d3d12.c
 create mode 100644 src/render/direct3d12/SDL_shaders_d3d12.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 18cf7302b55..f5e0e344e98 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1596,6 +1596,7 @@ elseif(WINDOWS)
 
     check_include_file(d3d9.h HAVE_D3D_H)
     check_include_file(d3d11_1.h HAVE_D3D11_H)
+    check_include_file(d3d12.h HAVE_D3D12_H)
     check_include_file(ddraw.h HAVE_DDRAW_H)
     check_include_file(dsound.h HAVE_DSOUND_H)
     check_include_file(dinput.h HAVE_DINPUT_H)
@@ -1603,7 +1604,7 @@ elseif(WINDOWS)
       set(HAVE_DINPUT_H 0)
     endif()
     check_include_file(dxgi.h HAVE_DXGI_H)
-    if(HAVE_D3D_H OR HAVE_D3D11_H OR HAVE_DDRAW_H OR HAVE_DSOUND_H OR HAVE_DINPUT_H)
+    if(HAVE_D3D_H OR HAVE_D3D11_H OR HAVE_D3D12_H OR HAVE_DDRAW_H OR HAVE_DSOUND_H OR HAVE_DINPUT_H)
       set(HAVE_DIRECTX TRUE)
       if(NOT MINGW AND NOT USE_WINSDK_DIRECTX)
       # TODO: change $ENV{DXSDL_DIR} to get the path from the include checks
@@ -1697,6 +1698,10 @@ elseif(WINDOWS)
       set(SDL_VIDEO_RENDER_D3D11 1)
       set(HAVE_RENDER_D3D TRUE)
     endif()
+    if(SDL_RENDER_D3D AND HAVE_D3D12_H AND NOT WINDOWS_STORE)
+      set(SDL_VIDEO_RENDER_D3D12 1)
+      set(HAVE_RENDER_D3D TRUE)
+    endif()
     set(HAVE_SDL_VIDEO TRUE)
   endif()
 
diff --git a/Makefile.w32 b/Makefile.w32
index 60775f13fef..ee5c4b10ff7 100644
--- a/Makefile.w32
+++ b/Makefile.w32
@@ -80,6 +80,7 @@ SRCS+= SDL_syspower.c
 SRCS+= SDL_d3dmath.c
 SRCS+= SDL_render_d3d.c SDL_shaders_d3d.c
 SRCS+= SDL_render_d3d11.c SDL_shaders_d3d11.c
+SRCS+= SDL_render_d3d12.c SDL_shaders_d3d12.c
 SRCS+= SDL_render_gl.c SDL_shaders_gl.c
 SRCS+= SDL_render_gles2.c SDL_shaders_gles2.c
 SRCS+= SDL_windowssensor.c
@@ -99,7 +100,7 @@ RCOBJS= $(RCSRCS:.rc=.res)
 .c: ./src;./src/dynapi;./src/audio;./src/cpuinfo;./src/events;./src/file;./src/haptic;./src/joystick;./src/power;./src/render;./src/render/software;./src/sensor;./src/stdlib;./src/thread;./src/timer;./src/video;./src/video/yuv2rgb;./src/atomic;./src/audio/disk;
 .c: ./src/haptic/dummy;./src/joystick/dummy;./src/joystick/virtual;./src/audio/dummy;./src/video/dummy;./src/sensor/dummy;
 .c: ./src/core/windows;./src/audio/winmm;./src/audio/directsound;./src/audio/wasapi;./src/loadso/windows;./src/filesystem/windows;./src/haptic/windows;./src/joystick/windows;./src/sensor/windows;./src/thread/windows;./src/timer/windows;./src/video/windows;
-.c: ./src/locale/;./src/locale/windows;./src/misc;./src/misc/windows;./src/power/windows;./src/joystick/hidapi;./src/hidapi;./src/render/direct3d;./src/render/direct3d11;./src/render/opengl;./src/render/opengles2
+.c: ./src/locale/;./src/locale/windows;./src/misc;./src/misc/windows;./src/power/windows;./src/joystick/hidapi;./src/hidapi;./src/render/direct3d;./src/render/direct3d11;./src/render/direct3d12;./src/render/opengl;./src/render/opengles2
 .rc: ./src/main/windows
 
 all: $(DLLFILE) $(LIBFILE) $(TLIB) .symbolic
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index aaeda871783..04ed6b46f1e 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -348,6 +348,7 @@
     <ClInclude Include="..\..\src\misc\SDL_sysurl.h" />
     <ClInclude Include="..\..\src\power\SDL_syspower.h" />
     <ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h" />
+    <ClInclude Include="..\..\src\render\direct3d12\SDL_shaders_d3d12.h" />
     <ClInclude Include="..\..\src\render\direct3d\SDL_shaders_d3d.h" />
     <ClInclude Include="..\..\src\render\opengles2\SDL_gles2funcs.h" />
     <ClInclude Include="..\..\src\render\opengles2\SDL_shaders_gles2.h" />
@@ -527,6 +528,8 @@
     <ClCompile Include="..\..\src\power\SDL_power.c" />
     <ClCompile Include="..\..\src\power\windows\SDL_syspower.c" />
     <ClCompile Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.c" />
+    <ClCompile Include="..\..\src\render\direct3d12\SDL_render_d3d12.c" />
+    <ClCompile Include="..\..\src\render\direct3d12\SDL_shaders_d3d12.c" />
     <ClCompile Include="..\..\src\render\direct3d\SDL_render_d3d.c" />
     <ClCompile Include="..\..\src\render\direct3d11\SDL_render_d3d11.c" />
     <ClCompile Include="..\..\src\render\direct3d\SDL_shaders_d3d.c" />
@@ -617,4 +620,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index a3d9d5c8090..a093f4dd672 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -169,6 +169,9 @@
     <Filter Include="video\khronos\vulkan">
       <UniqueIdentifier>{4755f3a6-49ac-46d6-86be-21f5c21f2197}</UniqueIdentifier>
     </Filter>
+    <Filter Include="render\direct3d12">
+      <UniqueIdentifier>{f48c2b17-1bee-4fec-a7c8-24cf619abe08}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\include\begin_code.h">
@@ -829,6 +832,9 @@
     <ClInclude Include="..\..\src\SDL_hints_c.h" />
     <ClInclude Include="..\..\src\SDL_internal.h" />
     <ClInclude Include="..\..\src\SDL_log_c.h" />
+    <ClInclude Include="..\..\src\render\direct3d12\SDL_shaders_d3d12.h">
+      <Filter>render\direct3d12</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
@@ -1326,8 +1332,14 @@
     <ClCompile Include="..\..\src\power\windows\SDL_syspower.c">
       <Filter>power\windows</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\render\direct3d12\SDL_render_d3d12.c">
+      <Filter>render\direct3d12</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\render\direct3d12\SDL_shaders_d3d12.c">
+      <Filter>render\direct3d12</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\..\src\main\windows\version.rc" />
   </ItemGroup>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/configure.ac b/configure.ac
index 421ea98ce91..dc5c4c718b5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3264,6 +3264,7 @@ CheckDIRECTX()
     if test x$enable_directx = xyes; then
         AC_CHECK_HEADER(d3d9.h, have_d3d=yes)
         AC_CHECK_HEADER(d3d11_1.h, have_d3d11=yes)
+        AC_CHECK_HEADER(d3d12.h, have_d3d12=yes)
         AC_CHECK_HEADER(ddraw.h, have_ddraw=yes)
         AC_CHECK_HEADER(dsound.h, have_dsound=yes)
         AC_CHECK_HEADER(dinput.h, have_dinput=yes)
@@ -3991,6 +3992,10 @@ case "$host" in
                 AC_DEFINE(SDL_VIDEO_RENDER_D3D11, 1, [ ])
                 SUMMARY_video="${SUMMARY_video} d3d11"
             fi
+            if test x$enable_render_d3d = xyes -a x$have_d3d12 = xyes; then
+                AC_DEFINE(SDL_VIDEO_RENDER_D3D12, 1, [ ])
+                SUMMARY_video="${SUMMARY_video} d3d12"
+            fi
         fi
         # Set up files for the audio library
         if test x$enable_audio = xyes; then
diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake
index 9e058218d65..b6b58399296 100644
--- a/include/SDL_config.h.cmake
+++ b/include/SDL_config.h.cmake
@@ -243,6 +243,7 @@
 
 #cmakedefine HAVE_D3D_H @HAVE_D3D_H@
 #cmakedefine HAVE_D3D11_H @HAVE_D3D11_H@
+#cmakedefine HAVE_D3D12_H @HAVE_D3D12_H@
 #cmakedefine HAVE_DDRAW_H @HAVE_DDRAW_H@
 #cmakedefine HAVE_DSOUND_H @HAVE_DSOUND_H@
 #cmakedefine HAVE_DINPUT_H @HAVE_DINPUT_H@
diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in
index 9a80d45b7c0..c18077feac3 100644
--- a/include/SDL_config.h.in
+++ b/include/SDL_config.h.in
@@ -413,6 +413,7 @@
 
 #undef SDL_VIDEO_RENDER_D3D
 #undef SDL_VIDEO_RENDER_D3D11
+#undef SDL_VIDEO_RENDER_D3D12
 #undef SDL_VIDEO_RENDER_OGL
 #undef SDL_VIDEO_RENDER_OGL_ES
 #undef SDL_VIDEO_RENDER_OGL_ES2
diff --git a/include/SDL_config_windows.h b/include/SDL_config_windows.h
index 81fde40c869..076bea4103c 100644
--- a/include/SDL_config_windows.h
+++ b/include/SDL_config_windows.h
@@ -38,6 +38,18 @@
 #include <winsdkver.h>
 #endif
 
+/* sdkddkver.h defines more specific SDK version numbers. This is needed because older versions of the
+ * Windows 10 SDK have broken declarations for the C API for DirectX 12. */
+#if !defined(HAVE_SDKDDKVER_H) && defined(__has_include)
+#if __has_include(<sdkddkver.h>)
+#define HAVE_SDKDDKVER_H 1
+#endif
+#endif
+
+#ifdef HAVE_SDKDDKVER_H
+#include <sdkddkver.h>
+#endif
+
 /* This is a set of defines to configure the SDL features */
 
 #if !defined(_STDINT_H_) && (!defined(HAVE_STDINT_H) || !_HAVE_STDINT_H)
@@ -107,6 +119,9 @@ typedef unsigned int uintptr_t;
 #define HAVE_D3D11_H 1
 #define HAVE_ROAPI_H 1
 #endif
+#if defined(WDK_NTDDI_VERSION) && WDK_NTDDI_VERSION > 0x0A000008 /* 10.0.19041.0 */
+#define HAVE_D3D12_H 1
+#endif
 #define HAVE_MMDEVICEAPI_H 1
 #define HAVE_AUDIOCLIENT_H 1
 #define HAVE_TPCSHRD_H 1
@@ -295,6 +310,9 @@ typedef unsigned int uintptr_t;
 #if !defined(SDL_VIDEO_RENDER_D3D11) && defined(HAVE_D3D11_H)
 #define SDL_VIDEO_RENDER_D3D11  1
 #endif
+#if !defined(SDL_VIDEO_RENDER_D3D12) && defined(HAVE_D3D12_H)
+#define SDL_VIDEO_RENDER_D3D12  1
+#endif
 
 /* Enable OpenGL support */
 #ifndef SDL_VIDEO_OPENGL
diff --git a/include/SDL_config_winrt.h b/include/SDL_config_winrt.h
index 75aef4ebbdf..55694036902 100644
--- a/include/SDL_config_winrt.h
+++ b/include/SDL_config_winrt.h
@@ -250,6 +250,9 @@ typedef unsigned int uintptr_t;
 /* Enable appropriate renderer(s) */
 #define SDL_VIDEO_RENDER_D3D11  1
 
+/* Disable D3D12 as it's not implemented for WinRT */
+#define SDL_VIDEO_RENDER_D3D12  0
+
 #if SDL_VIDEO_OPENGL_ES2
 #define SDL_VIDEO_RENDER_OGL_ES2 1
 #endif
diff --git a/include/SDL_hints.h b/include/SDL_hints.h
index 83cfe89a69a..2699b6ae911 100644
--- a/include/SDL_hints.h
+++ b/include/SDL_hints.h
@@ -1200,6 +1200,8 @@ extern "C" {
  *
  *  This variable is case insensitive and can be set to the following values:
  *    "direct3d"
+ *    "direct3d11"
+ *    "direct3d12"
  *    "opengl"
  *    "opengles2"
  *    "opengles"
diff --git a/include/SDL_system.h b/include/SDL_system.h
index bbe8d45586d..60871ff9005 100644
--- a/include/SDL_system.h
+++ b/include/SDL_system.h
@@ -102,6 +102,22 @@ typedef struct ID3D11Device ID3D11Device;
  */
 extern DECLSPEC ID3D11Device* SDLCALL SDL_RenderGetD3D11Device(SDL_Renderer * renderer);
 
+typedef struct ID3D12Device ID3D12Device;
+
+/**
+ * Get the D3D12 device associated with a renderer.
+ *
+ * Once you are done using the device, you should release it to avoid a
+ * resource leak.
+ *
+ * \param renderer the renderer from which to get the associated D3D12 device
+ * \returns the D3D12 device associated with given renderer or NULL if it is
+ *          not a D3D12 renderer; call SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 2.0.23.
+ */
+extern DECLSPEC ID3D12Device* SDLCALL SDL_RenderGetD3D12Device(SDL_Renderer* renderer);
+
 /**
  * Get the DXGI Adapter and Output indices for the specified display index.
  *
diff --git a/src/core/windows/SDL_windows.h b/src/core/windows/SDL_windows.h
index 681f01b8744..9b014925318 100644
--- a/src/core/windows/SDL_windows.h
+++ b/src/core/windows/SDL_windows.h
@@ -33,7 +33,11 @@
 #undef WINVER
 #define WINVER 0x0501
 #undef _WIN32_WINNT
+#if !defined(SDL_VIDEO_RENDER_D3D12)
 #define _WIN32_WINNT  0x501   /* Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input */
+#else
+#define _WIN32_WINNT  0xA00   /* For D3D12, 0xA00 is required */
+#endif
 #endif
 
 #include <windows.h>
diff --git a/src/dynapi/SDL2.exports b/src/dynapi/SDL2.exports
index 481e1db0b2f..5bc965dfd25 100644
--- a/src/dynapi/SDL2.exports
+++ b/src/dynapi/SDL2.exports
@@ -847,3 +847,4 @@
 ++'_SDL_GUIDFromString'.'SDL2.dll'.'SDL_GUIDFromString'
 ++'_SDL_HasLSX'.'SDL2.dll'.'SDL_HasLSX'
 ++'_SDL_HasLASX'.'SDL2.dll'.'SDL_HasLASX'
+++'_SDL_RenderGetD3D12Device'.'SDL2.dll'.'SDL_RenderGetD3D12Device'
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 97f3b800767..76c9e91998c 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -873,3 +873,4 @@
 #define SDL_GUIDFromString SDL_GUIDFromString_REAL
 #define SDL_HasLSX SDL_HasLSX_REAL
 #define SDL_HasLASX SDL_HasLASX_REAL
+#define SDL_RenderGetD3D12Device SDL_RenderGetD3D12Device_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index a61333a3956..ddc655760bc 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -948,3 +948,6 @@ SDL_DYNAPI_PROC(void,SDL_GUIDToString,(SDL_GUID a, char *b, int c),(a,b,c),)
 SDL_DYNAPI_PROC(SDL_GUID,SDL_GUIDFromString,(const char *a),(a),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_HasLSX,(void),(),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_HasLASX,(void),(),return)
+#ifdef __WIN32__
+SDL_DYNAPI_PROC(ID3D12Device*,SDL_RenderGetD3D12Device,(SDL_Renderer *a),(a),return)
+#endif
diff --git a/src/render/SDL_d3dmath.c b/src/render/SDL_d3dmath.c
index 63048bb88ca..fb7de371fa6 100644
--- a/src/render/SDL_d3dmath.c
+++ b/src/render/SDL_d3dmath.c
@@ -20,7 +20,7 @@
 */
 #include "../SDL_internal.h"
 
-#if (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11) && !SDL_RENDER_DISABLED
+#if (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12) && !SDL_RENDER_DISABLED
 #include "SDL_stdinc.h"
 
 #include "SDL_d3dmath.h"
@@ -131,6 +131,6 @@ Float4X4 MatrixRotationZ(float r)
 
 }
 
-#endif /* (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11) && !SDL_RENDER_DISABLED */
+#endif /* (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12) && !SDL_RENDER_DISABLED */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/render/SDL_d3dmath.h b/src/render/SDL_d3dmath.h
index 7b74cfe7573..211bfdd5a67 100644
--- a/src/render/SDL_d3dmath.h
+++ b/src/render/SDL_d3dmath.h
@@ -20,7 +20,7 @@
 */
 #include "../SDL_internal.h"
 
-#if (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11) && !SDL_RENDER_DISABLED
+#if (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12) && !SDL_RENDER_DISABLED
 
 /* Direct3D matrix math functions */
 
@@ -67,6 +67,6 @@ Float4X4 MatrixRotationX(float r);
 Float4X4 MatrixRotationY(float r);
 Float4X4 MatrixRotationZ(float r);
 
-#endif /* (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11) && !SDL_RENDER_DISABLED */
+#endif /* (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12) && !SDL_RENDER_DISABLED */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index d94b0a988b0..4224a70f6f6 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -96,6 +96,9 @@ static const SDL_RenderDriver *render_drivers[] = {
 #if SDL_VIDEO_RENDER_D3D11
     &D3D11_RenderDriver,
 #endif
+#if SDL_VIDEO_RENDER_D3D12
+    &D3D12_RenderDriver,
+#endif
 #if SDL_VIDEO_RENDER_METAL
     &METAL_RenderDriver,
 #endif
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
index 7aeb46710bc..481ae91bfc5 100644
--- a/src/render/SDL_sysrender.h
+++ b/src/render/SDL_sysrender.h
@@ -283,6 +283,7 @@ struct SDL_RenderDriver
 /* Not all of these are available in a given build. Use #ifdefs, etc. */
 extern SDL_RenderDriver D3D_RenderDriver;
 extern SDL_RenderDriver D3D11_RenderDriver;
+extern SDL_RenderDriver D3D12_RenderDriver;
 extern SDL_RenderDriver GL_RenderDriver;
 extern SDL_RenderDriver GLES2_RenderDriver;
 extern SDL_RenderDriver GLES_RenderDriver;
diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c
new file mode 100644
index 00000000000..447e6a07b54
--- /dev/null
+++ b/src/render/direct3d12/SDL_render_d3d12.c
@@ -0,0 +1,3042 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#include "SDL_render.h"
+#include "SDL_system.h"
+
+#if SDL_VIDEO_RENDER_D3D12 && !SDL_RENDER_DISABLED
+
+#define SDL_D3D12_NUM_BUFFERS 2
+#define SDL_D3D12_NUM_VERTEX_BUFFERS 256
+#define SDL_D3D12_VERTEX_BUFFER_MAX_TRIS 2048
+#define SDL_D3D12_MAX_NUM_TEXTURES 16384
+#define SDL_D3D12_NUM_UPLOAD_BUFFERS 32
+
+#define COBJMACROS
+#include "../../core/windows/SDL_windows.h"
+#include "SDL_hints.h"
+#include "SDL_loadso.h"
+#include "SDL_syswm.h"
+#include "../SDL_sysrender.h"
+#include "../SDL_d3dmath.h"
+
+#include <d3d12.h>
+#include <dxgi1_6.h>
+#include <dxgidebug.h>
+#include <d3d12sdklayers.h>
+
+#include "SDL_shaders_d3d12.h"
+
+#if defined(_MSC_VER) && !defined(__clang__)
+#define SDL_COMPOSE_ERROR(str) __FUNCTION__ ", " str
+#else
+#define SDL_COMPOSE_ERROR(str) SDL_STRINGIFY_ARG(__FUNCTION__) ", " str
+#endif
+
+#define SAFE_RELEASE(X) if ((X)) { IUnknown_Release(SDL_static_cast(IUnknown*, X)); X = NULL; }
+
+
+/* !!! FIXME: vertex buffer bandwidth could be lower; only use UV coords when
+   !!! FIXME:  textures are needed. */
+
+/* Vertex shader, common values */
+typedef struct
+{
+    Float4X4 model;
+    Float4X4 projectionAndView;
+} VertexShaderConstants;
+
+/* Per-vertex data */
+typedef struct
+{
+    Float2 pos;
+    Float2 tex;
+    SDL_Color color;
+} VertexPositionColor;
+
+/* Per-texture data */
+typedef struct
+{
+    ID3D12Resource *mainTexture;
+    D3D12_CPU_DESCRIPTOR_HANDLE mainTextureResourceView;
+    D3D12_RESOURCE_STATES mainResourceState;
+    SIZE_T mainSRVIndex;
+    D3D12_CPU_DESCRIPTOR_HANDLE mainTextureRenderTargetView;
+    DXGI_FORMAT mainTextureFormat;
+    ID3D12Resource *stagingBuffer;
+    D3D12_RESOURCE_STATES stagingResourceState;
+    D3D12_FILTER scaleMode;
+#if SDL_HAVE_YUV
+    /* YV12 texture support */
+    SDL_bool yuv;
+    ID3D12Resource *mainTextureU;
+    D3D12_CPU_DESCRIPTOR_HANDLE mainTextureResourceViewU;
+    D3D12_RESOURCE_STATES mainResourceStateU;
+    SIZE_T mainSRVIndexU;
+    ID3D12Resource *mainTextureV;
+    D3D12_CPU_DESCRIPTOR_HANDLE mainTextureResourceViewV;
+    D3D12_RESOURCE_STATES mainResourceStateV;
+    SIZE_T mainSRVIndexV;
+
+    /* NV12 texture support */
+    SDL_bool nv12;
+    ID3D12Resource *mainTextureNV;
+    D3D12_CPU_DESCRIPTOR_HANDLE mainTextureResourceViewNV;
+    D3D12_RESOURCE_STATES mainResourceStateNV;
+    SIZE_T mainSRVIndexNV;
+
+    Uint8 *pixels;
+    int pitch;
+#endif
+    SDL_Rect lockedRect;
+} D3D12_TextureData;
+
+/* Pipeline State Object data */
+typedef struct
+{
+    D3D12_Shader shader;
+    SDL_BlendMode blendMode;
+    D3D12_PRIMITIVE_TOPOLOGY_TYPE topology;
+    DXGI_FORMAT rtvFormat;
+    ID3D12PipelineState *pipelineState;
+} D3D12_PipelineState;
+
+/* Vertex Buffer */
+typedef struct 
+{
+    ID3D12Resource *resource;
+    D3D12_VERTEX_BUFFER_VIEW view;
+} D3D12_VertexBuffer;
+
+/* For SRV pool allocator */
+typedef struct
+{
+    SIZE_T index;
+    void *next;
+} D3D12_SRVPoolNode;
+
+/* Private renderer data */
+typedef struct
+{
+    void *hDXGIMod;
+    void *hD3D12Mod;
+    IDXGIFactory6 *dxgiFactory;
+    IDXGIAdapter4 *dxgiAdapter;
+    ID3D12Device5 *d3dDevice;
+    ID3D12Debug *debugInterface;
+    IDXGIDebug1 *dxgiDebug;
+    ID3D12CommandQueue *commandQueue;
+    ID3D12GraphicsCommandList2 *commandList;
+    IDXGISwapChain4 *swapChain;
+    DXGI_SWAP_EFFECT swapEffect;
+
+    /* Descriptor heaps */
+    ID3D12DescriptorHeap* rtvDescriptorHeap;
+    UINT rtvDescriptorSize;
+    ID3D12DescriptorHeap* textureRTVDescriptorHeap;
+    ID3D12DescriptorHeap* srvDescriptorHeap;
+    UINT srvDescriptorSize;
+    ID3D12DescriptorHeap* samplerDescriptorHeap;
+    UINT samplerDescriptorSize;
+
+    /* Data needed per backbuffer */
+    ID3D12CommandAllocator *commandAllocators[SDL_D3D12_NUM_BUFFERS];
+    ID3D12Resource *renderTargets[SDL_D3D12_NUM_BUFFERS];
+    UINT64 fenceValue;
+    int currentBackBufferIndex;
+    
+    /* Fences */
+    ID3D12Fence *fence;
+    HANDLE fenceEvent;
+
+    /* Root signature and pipeline state data */
+    ID3D12RootSignature *rootSignatures[NUM_ROOTSIGS];
+    int pipelineStateCount;
+    D3D12_PipelineState *pipelineStates;
+    D3D12_PipelineState *currentPipelineState;
+
+    D3D12_VertexBuffer vertexBuffers[SDL_D3D12_NUM_VERTEX_BUFFERS];
+    ID3D12Heap *vertexBufferHeap;
+    D3D12_CPU_DESCRIPTOR_HANDLE nearestPixelSampler;
+    D3D12_CPU_DESCRIPTOR_HANDLE linearSampler;
+
+    /* Data for staging/allocating textures */
+    ID3D12Resource *uploadBuffers[SDL_D3D12_NUM_UPLOAD_BUFFERS];
+    int currentUploadBuffer;
+
+    /* Pool allocator to handle reusing SRV heap indices */
+    D3D12_SRVPoolNode *srvPoolHead;
+    D3D12_SRVPoolNode srvPoolNodes[SDL_D3D12_MAX_NUM_TEXTURES];
+
+    /* Vertex buffer constants */
+    VertexShaderConstants vertexShaderConstantsData;
+ 
+    /* Cached renderer properties */
+    DXGI_MODE_ROTATION rotation;
+    D3D12_TextureData* textureRenderTarget;
+    D3D12_CPU_DESCRIPTOR_HANDLE currentRenderTargetView;
+    D3D12_CPU_DESCRIPTOR_HANDLE currentShaderResource;
+    D3D12_CPU_DESCRIPTOR_HANDLE currentSampler;
+    SDL_bool cliprectDirty;
+    SDL_bool currentCliprectEnabled;
+    SDL_Rect currentCliprect;
+    SDL_Rect currentViewport;
+    int currentViewportRotation;
+    SDL_bool viewportDirty;
+    Float4X4 identity;
+    int currentVertexBuffer;
+    SDL_bool issueBatch;
+} D3D12_RenderData;
+
+
+/* Define D3D GUIDs here so we don't have to include uuid.lib. */
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-const-variable"
+#endif
+
+static const GUID SDL_IID_IDXGIFactory6 = { 0xc1b6694f, 0xff09, 0x44a9, { 0xb0, 0x3c, 0x77, 0x90, 0x0a, 0x0a, 0x1d, 0x17 } };
+static const GUID SDL_IID_IDXGIAdapter4 = { 0x3c8d99d1, 0x4fbf, 0x4181, { 0xa8, 0x2c, 0xaf, 0x66, 0xbf, 0x7b, 0xd2, 0x4e } };
+static const GUID SDL_IID_IDXGIDevice1 = { 0x77db970f, 0x6276, 0x48ba, { 0xba, 0x28, 0x07, 0x01, 0x43, 0xb4, 0x39, 0x2c } };
+static const GUID SDL_IID_ID3D12Device5 = { 0x8b4f173b, 0x2fea, 0x4b80, { 0x8f, 0x58, 0x43, 0x07, 0x19, 0x1a, 0xb9, 0x5d } };
+static const GUID SDL_IID_IDXGISwapChain4 = { 0x3D585D5A, 0xBD4A, 0x489E, { 0xB1, 0xF4, 0x3D, 0xBC, 0xB6, 0x45, 0x2F, 0xFB } };
+static const GUID SDL_IID_IDXGIDebug1 = { 0xc5a05f0c, 0x16f2, 0x4adf, { 0x9f, 0x4d, 0xa8, 0xc4, 0xd5, 0x8a, 0xc5, 0x50 } };
+static const GUID SDL_IID_IDXGIInfoQueue = { 0xD67441C7,0x672A,0x476f, { 0x9E,0x82,0xCD,0x55,0xB4,0x49,0x49,0xCE } };
+static const GUID SDL_IID_ID3D12Debug = { 0x344488b7, 0x6846, 0x474b, { 0xb9, 0x89, 0xf0, 0x27, 0x44, 0x82, 0x45, 0xe0 } };
+static const GUID SDL_DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, { 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x8 } };
+static const GUID SDL_IID_ID3D12CommandQueue = { 0x0ec870a6, 0x5d7e, 0x4c22, { 0x8c, 0xfc, 0x5b, 0xaa, 0xe0, 0x76, 0x16, 0xed } };
+static const GUID SDL_IID_ID3D12DescriptorHeap = { 0x8efb471d, 0x616c, 0x4f49, { 0x90, 0xf7, 0x12, 0x7b, 0xb7, 0x63, 0xfa, 0x51 } };
+static const GUID SDL_IID_ID3D12CommandAllocator = { 0x6102dee4, 0xaf59, 0x4b09, { 0xb9, 0x99, 0xb4, 0x4d, 0x73, 0xf0, 0x9b, 0x24 } };
+static const GUID SDL_IID_ID3D12GraphicsCommandList2 = { 0x38C3E585, 0xFF17, 0x412C, { 0x91, 0x50, 0x4F, 0xC6, 0xF9, 0xD7, 0x2A, 0x28 } };
+static const GUID SDL_IID_ID3D12Fence = { 0x0a753dcf, 0xc4d8, 0x4b91, { 0xad, 0xf6, 0xbe, 0x5a, 0x60, 0xd9, 0x5a, 0x76 } };
+static const GUID SDL_IID_ID3D12Resource = { 0x696442be, 0xa72e, 0x4059, { 0xbc, 0x79, 0x5b, 0x5c, 0x98, 0x04, 0x0f, 0xad } };
+static const GUID SDL_IID_ID3D12RootSignature = { 0xc54a6b66, 0x72df, 0x4ee8, { 0x8b, 0xe5, 0xa9, 0x46, 0xa1, 0x42, 0x92, 0x14 } };
+static const GUID SDL_IID_ID3D12PipelineState = { 0x765a30f3, 0xf624, 0x4c6f, { 0xa8, 0x28, 0xac, 0xe9, 0x48, 0x62, 0x24, 0x45 } };
+static const GUID SDL_IID_ID3D12Heap = { 0x6b3b2502, 0x6e51, 0x45b3, { 0x90, 0xee, 0x98, 0x84, 0x26, 0x5e, 0x8d, 0xf3 } };
+static const GUID SDL_IID_ID3D12InfoQueue = { 0x0742a90b, 0xc387, 0x483f, { 0xb9, 0x46, 0x30, 0xa7, 0xe4, 0xe6, 0x14, 0x58 } };
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+
+
+UINT
+D3D12_Align(UINT location, UINT alignment)
+{
+    return ((location + (alignment - 1)) & ~(alignment - 1));
+}
+
+Uint32
+D3D12_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat)
+{
+    switch (dxgiFormat) {
+        case DXGI_FORMAT_B8G8R8A8_UNORM:
+            return SDL_PIXELFORMAT_ARGB8888;
+        case DXGI_FORMAT_B8G8R8X8_UNORM:
+            return SDL_PIXELFORMAT_RGB888;
+        default:
+            return SDL_PIXELFORMAT_UNKNOWN;
+    }
+}
+
+static DXGI_FORMAT
+SDLPixelFormatToDXGIFormat(Uint32 sdlFormat)
+{
+    switch (sdlFormat) {
+        case SDL_PIXELFORMAT_ARGB8888:
+            return DXGI_FORMAT_B8G8R8A8_UNORM;
+        case SDL_PIXELFORMAT_RGB888:
+            return DXGI_FORMAT_B8G8R8X8_UNORM;
+        case SDL_PIXELFORMAT_YV12:
+        case SDL_PIXELFORMAT_IYUV:
+        case SDL_PIXELFORMAT_NV12:  /* For the Y texture */
+        case SDL_PIXELFORMAT_NV21:  /* For the Y texture */
+            return DXGI_FORMAT_R8_UNORM;
+        default:
+            return DXGI_FORMAT_UNKNOWN;
+    }
+}
+
+static void D3D12_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
+
+static void
+D3D12_ReleaseAll(SDL_Renderer * renderer)
+{
+    D3D12_RenderData *data = (D3D12_RenderData *) renderer->driverdata;
+    SDL_Texture *texture = NULL;
+
+    /* Release all textures */
+    for (texture = renderer->textures; texture; texture = texture->next) {
+        D3D12_DestroyTexture(renderer, texture);
+    }
+
+    /* Release/reset everything else */
+    if (data) {
+        int i;
+
+        SAFE_RELEASE(data->dxgiFactory);
+        SAFE_RELEASE(data->dxgiAdapter);
+        SAFE_RELEASE(data->d3dDevice);
+        SAFE_RELEASE(data->debugInterface);
+        SAFE_RELEASE(data->commandQueue);
+        SAFE_RELEASE(data->commandList);
+        SAFE_RELEASE(data->rtvDescriptorHeap);
+        SAFE_RELEASE(data->textureRTVDescriptorHeap);
+        SAFE_RELEASE(data->srvDescriptorHeap);
+        SAFE_RELEASE(data->samplerDescriptorHeap);
+        SAFE_RELEASE(data->swapChain);
+        SAFE_RELEASE(data->fence);
+        SAFE_RELEASE(data->vertexBufferHeap);
+
+        for (i = 0; i < SDL_D3D12_NUM_BUFFERS; ++i) {
+            SAFE_RELEASE(data->commandAllocators[i]);
+            SAFE_RELEASE(data->renderTargets[i]);
+        }
+
+        if (data->pipelineStateCount > 0) {
+            for (i = 0; i < data->pipelineStateCount; ++i) {
+                SAFE_RELEASE(data->pipelineStates[i].pipelineState);
+            }
+            SDL_free(data->pipelineStates);
+            data->pipelineStateCount = 0;
+        }
+
+        for (i = 0; i < NUM_ROOTSIGS; ++i) {
+            SAFE_RELEASE(data->rootSignatures[i]);
+        }
+
+        for (i = 0; i < SDL_D3D12_NUM_VERTEX_BUFFERS; ++i) {
+            SAFE_RELEASE(data->vertexBuffers[i].resource);
+        }
+        
+        data->swapEffect = (DXGI_SWAP_EFFECT) 0;
+        data->currentRenderTargetView.ptr = 0;
+        data->currentSampler.ptr = 0;
+
+        /* Check for any leaks if in debug mode */
+        if (data->dxgiDebug) {
+            IDXGIDebug1_ReportLiveObjects(data->dxgiDebug, SDL_DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_IGNORE_INTERNAL);
+            SAFE_RELEASE(data->dxgiDebug);
+        }
+
+        /* Unload the D3D libraries.  This should be done last, in order
+         * to prevent IUnknown::Release() calls from crashing.
+         */
+        if (data->hD3D12Mod) {
+            SDL_UnloadObject(data->hD3D12Mod);
+            data->hD3D12Mod = NULL;
+        }
+        if (data->hDXGIMod) {
+            SDL_UnloadObject(data->hDXGIMod);
+            data->hDXGIMod = NULL;
+        }
+    }
+}
+
+static D3D12_GPU_DESCRIPTOR_HANDLE
+D3D12_CPUtoGPUHandle(ID3D12DescriptorHeap * heap, D3D12_CPU_DESCRIPTOR_HANDLE CPUHandle)
+{
+    D3D12_CPU_DESCRIPTOR_HANDLE CPUHeapStart;
+    D3D12_GPU_DESCRIPTOR_HANDLE GPUHandle;
+    SIZE_T offset;
+    
+    /* Calculate the correct offset into the heap */
+    ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap, &CPUHeapStart);
+    offset = CPUHandle.ptr - CPUHeapStart.ptr;
+    
+    ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap, &GPUHandle);
+    GPUHandle.ptr += offset;
+
+    return GPUHandle;
+}
+
+static void
+D3D12_WaitForGPU(D3D12_RenderData * data)
+{
+    HRESULT result;
+
+    if (data->commandQueue && data->fence && data->fenceEvent)
+    {
+        result = ID3D12CommandQueue_Signal(data->commandQueue, data->fence, data->fenceValue);
+        if (ID3D12Fence_GetCompletedValue(data->fence) < data->fenceValue) {
+            result = ID3D12Fence_SetEventOnCompletion(
+                data->fence,
+                data->fenceValue,
+                data->fenceEvent
+            );
+            WaitForSingleObjectEx(data->fenceEvent, INFINITE, FALSE);
+        }
+
+        data->fenceValue++;
+    }
+}
+
+static D3D12_CPU_DESCRIPTOR_HANDLE
+D3D12_GetCurrentRenderTargetView(SDL_Renderer * renderer)
+{
+    D3D12_RenderData* data = (D3D12_RenderData*)renderer->driverdata;
+    D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptor;
+
+    if (data->textureRenderTarget) {
+        return data->textureRenderTarget->mainTextureRenderTargetView;
+    }
+
+    SDL_zero(rtvDescriptor);
+    ID3D12Descript

(Patch may be truncated, please check the link at the top of this post.)