SDL: Added support for SDL_COLORSPACE_BT709_FULL to the hardware renderers

From dab77fe29b1893d1a554a2fb88bc64bc4940cf73 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 4 Feb 2024 00:13:11 -0800
Subject: [PATCH] Added support for SDL_COLORSPACE_BT709_FULL to the hardware
 renderers

---
 src/render/direct3d/D3D9_PixelShader_YUV.h    |  164 +
 src/render/direct3d/D3D9_PixelShader_YUV.hlsl |   47 +
 src/render/direct3d/SDL_render_d3d.c          |   65 +-
 src/render/direct3d/SDL_shaders_d3d.c         |  238 +-
 src/render/direct3d/SDL_shaders_d3d.h         |    5 +-
 src/render/direct3d/compile_shaders.bat       |    1 +
 .../direct3d11/D3D11_PixelShader_Colors.h     |  134 +
 .../direct3d11/D3D11_PixelShader_Colors.hlsl  |   11 +
 .../direct3d11/D3D11_PixelShader_NV12.h       |  347 +
 .../direct3d11/D3D11_PixelShader_NV12.hlsl    |   36 +
 .../direct3d11/D3D11_PixelShader_NV21.h       |  350 +
 .../direct3d11/D3D11_PixelShader_NV21.hlsl    |   36 +
 .../direct3d11/D3D11_PixelShader_Textures.h   |  198 +
 .../D3D11_PixelShader_Textures.hlsl           |   14 +
 src/render/direct3d11/D3D11_PixelShader_YUV.h |  382 +
 .../direct3d11/D3D11_PixelShader_YUV.hlsl     |   38 +
 src/render/direct3d11/D3D11_VertexShader.h    |  339 +
 src/render/direct3d11/D3D11_VertexShader.hlsl |   38 +
 src/render/direct3d11/SDL_render_d3d11.c      |  167 +-
 src/render/direct3d11/SDL_shaders_d3d11.c     | 1874 +----
 src/render/direct3d11/SDL_shaders_d3d11.h     |   13 +-
 src/render/direct3d11/compile_shaders.bat     |    6 +
 .../direct3d12/D3D12_PixelShader_Colors.h     |  387 +
 .../direct3d12/D3D12_PixelShader_Colors.hlsl  |   19 +
 .../direct3d12/D3D12_PixelShader_NV12.h       |  594 ++
 .../direct3d12/D3D12_PixelShader_NV12.hlsl    |   52 +
 .../direct3d12/D3D12_PixelShader_NV21.h       |  594 ++
 .../direct3d12/D3D12_PixelShader_NV21.hlsl    |   52 +
 .../direct3d12/D3D12_PixelShader_Textures.h   |  495 ++
 .../D3D12_PixelShader_Textures.hlsl           |   24 +
 src/render/direct3d12/D3D12_PixelShader_YUV.h |  610 ++
 .../direct3d12/D3D12_PixelShader_YUV.hlsl     |   55 +
 src/render/direct3d12/D3D12_RootSig_Color.h   |   16 +
 src/render/direct3d12/D3D12_RootSig_NV.h      |   27 +
 src/render/direct3d12/D3D12_RootSig_Texture.h |   23 +
 src/render/direct3d12/D3D12_RootSig_YUV.h     |   31 +
 src/render/direct3d12/D3D12_VertexShader.hlsl |   98 +
 .../direct3d12/D3D12_VertexShader_Color.h     |  637 ++
 src/render/direct3d12/D3D12_VertexShader_NV.h |  647 ++
 .../direct3d12/D3D12_VertexShader_Texture.h   |  645 ++
 .../direct3d12/D3D12_VertexShader_YUV.h       |  651 ++
 src/render/direct3d12/SDL_render_d3d12.c      |  119 +-
 src/render/direct3d12/SDL_shaders_d3d12.c     | 6869 +----------------
 src/render/direct3d12/SDL_shaders_d3d12.h     |   12 +-
 src/render/metal/SDL_render_metal.m           |   49 +-
 src/render/opengl/SDL_render_gl.c             |   67 +-
 src/render/opengl/SDL_shaders_gl.c            |  180 +-
 src/render/opengl/SDL_shaders_gl.h            |   18 +-
 src/render/opengles2/SDL_gles2funcs.h         |    2 +
 src/render/opengles2/SDL_render_gles2.c       |  162 +-
 src/render/opengles2/SDL_shaders_gles2.c      |  151 +-
 src/render/opengles2/SDL_shaders_gles2.h      |   16 +-
 src/render/vitagxm/SDL_render_vita_gxm.c      |    2 +-
 src/video/SDL_pixels.c                        |   57 +
 src/video/SDL_pixels_c.h                      |    1 +
 55 files changed, 8400 insertions(+), 9465 deletions(-)
 create mode 100755 src/render/direct3d/D3D9_PixelShader_YUV.h
 create mode 100644 src/render/direct3d/D3D9_PixelShader_YUV.hlsl
 create mode 100644 src/render/direct3d/compile_shaders.bat
 create mode 100755 src/render/direct3d11/D3D11_PixelShader_Colors.h
 create mode 100644 src/render/direct3d11/D3D11_PixelShader_Colors.hlsl
 create mode 100755 src/render/direct3d11/D3D11_PixelShader_NV12.h
 create mode 100644 src/render/direct3d11/D3D11_PixelShader_NV12.hlsl
 create mode 100755 src/render/direct3d11/D3D11_PixelShader_NV21.h
 create mode 100644 src/render/direct3d11/D3D11_PixelShader_NV21.hlsl
 create mode 100755 src/render/direct3d11/D3D11_PixelShader_Textures.h
 create mode 100644 src/render/direct3d11/D3D11_PixelShader_Textures.hlsl
 create mode 100755 src/render/direct3d11/D3D11_PixelShader_YUV.h
 create mode 100644 src/render/direct3d11/D3D11_PixelShader_YUV.hlsl
 create mode 100755 src/render/direct3d11/D3D11_VertexShader.h
 create mode 100644 src/render/direct3d11/D3D11_VertexShader.hlsl
 create mode 100644 src/render/direct3d11/compile_shaders.bat
 create mode 100755 src/render/direct3d12/D3D12_PixelShader_Colors.h
 create mode 100644 src/render/direct3d12/D3D12_PixelShader_Colors.hlsl
 create mode 100755 src/render/direct3d12/D3D12_PixelShader_NV12.h
 create mode 100644 src/render/direct3d12/D3D12_PixelShader_NV12.hlsl
 create mode 100755 src/render/direct3d12/D3D12_PixelShader_NV21.h
 create mode 100644 src/render/direct3d12/D3D12_PixelShader_NV21.hlsl
 create mode 100755 src/render/direct3d12/D3D12_PixelShader_Textures.h
 create mode 100644 src/render/direct3d12/D3D12_PixelShader_Textures.hlsl
 create mode 100755 src/render/direct3d12/D3D12_PixelShader_YUV.h
 create mode 100644 src/render/direct3d12/D3D12_PixelShader_YUV.hlsl
 create mode 100755 src/render/direct3d12/D3D12_RootSig_Color.h
 create mode 100755 src/render/direct3d12/D3D12_RootSig_NV.h
 create mode 100755 src/render/direct3d12/D3D12_RootSig_Texture.h
 create mode 100755 src/render/direct3d12/D3D12_RootSig_YUV.h
 create mode 100644 src/render/direct3d12/D3D12_VertexShader.hlsl
 create mode 100755 src/render/direct3d12/D3D12_VertexShader_Color.h
 create mode 100755 src/render/direct3d12/D3D12_VertexShader_NV.h
 create mode 100755 src/render/direct3d12/D3D12_VertexShader_Texture.h
 create mode 100755 src/render/direct3d12/D3D12_VertexShader_YUV.h

diff --git a/src/render/direct3d/D3D9_PixelShader_YUV.h b/src/render/direct3d/D3D9_PixelShader_YUV.h
new file mode 100755
index 000000000000..4f3c20a4d8dd
--- /dev/null
+++ b/src/render/direct3d/D3D9_PixelShader_YUV.h
@@ -0,0 +1,164 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+//   float4 Bcoeff;
+//   float4 Gcoeff;
+//   float4 Rcoeff;
+//   float4 Yoffset;
+//   Texture2D theSampler+theTextureU;
+//   Texture2D theSampler+theTextureV;
+//   Texture2D theSampler+theTextureY;
+//
+//
+// Registers:
+//
+//   Name                   Reg   Size
+//   ---------------------- ----- ----
+//   Yoffset                c0       1
+//   Rcoeff                 c1       1
+//   Gcoeff                 c2       1
+//   Bcoeff                 c3       1
+//   theSampler+theTextureY s0       1
+//   theSampler+theTextureU s1       1
+//   theSampler+theTextureV s2       1
+//
+
+    ps_2_0
+    def c4, 1, 0, 0, 0
+    dcl t0.xy
+    dcl v0
+    dcl_2d s0
+    dcl_2d s1
+    dcl_2d s2
+    texld r0, t0, s0
+    texld r1, t0, s1
+    texld r2, t0, s2
+    mov r0.y, r1.x
+    mov r0.z, r2.x
+    add r0.xyz, r0, c0
+    dp3 r1.x, r0, c1
+    dp3 r1.y, r0, c2
+    dp3 r1.z, r0, c3
+    mov r1.w, c4.x
+    mul r0, r1, v0
+    mov oC0, r0
+
+// approximately 12 instruction slots used (3 texture, 9 arithmetic)
+#endif
+
+const BYTE g_ps20_main[] =
+{
+      0,   2, 255, 255, 254, 255, 
+     97,   0,  67,  84,  65,  66, 
+     28,   0,   0,   0,  87,   1, 
+      0,   0,   0,   2, 255, 255, 
+      7,   0,   0,   0,  28,   0, 
+      0,   0,   0,   1,   0,   0, 
+     80,   1,   0,   0, 168,   0, 
+      0,   0,   2,   0,   3,   0, 
+      1,   0,   0,   0, 176,   0, 
+      0,   0,   0,   0,   0,   0, 
+    192,   0,   0,   0,   2,   0, 
+      2,   0,   1,   0,   0,   0, 
+    176,   0,   0,   0,   0,   0, 
+      0,   0, 199,   0,   0,   0, 
+      2,   0,   1,   0,   1,   0, 
+      0,   0, 176,   0,   0,   0, 
+      0,   0,   0,   0, 206,   0, 
+      0,   0,   2,   0,   0,   0, 
+      1,   0,   0,   0, 176,   0, 
+      0,   0,   0,   0,   0,   0, 
+    214,   0,   0,   0,   3,   0, 
+      1,   0,   1,   0,   0,   0, 
+    240,   0,   0,   0,   0,   0, 
+      0,   0,   0,   1,   0,   0, 
+      3,   0,   2,   0,   1,   0, 
+      0,   0,  24,   1,   0,   0, 
+      0,   0,   0,   0,  40,   1, 
+      0,   0,   3,   0,   0,   0, 
+      1,   0,   0,   0,  64,   1, 
+      0,   0,   0,   0,   0,   0, 
+     66,  99, 111, 101, 102, 102, 
+      0, 171,   1,   0,   3,   0, 
+      1,   0,   4,   0,   1,   0, 
+      0,   0,   0,   0,   0,   0, 
+     71,  99, 111, 101, 102, 102, 
+      0,  82,  99, 111, 101, 102, 
+    102,   0,  89, 111, 102, 102, 
+    115, 101, 116,   0, 116, 104, 
+    101,  83,  97, 109, 112, 108, 
+    101, 114,  43, 116, 104, 101, 
+     84, 101, 120, 116, 117, 114, 
+    101,  85,   0, 171, 171, 171, 
+      4,   0,   7,   0,   1,   0, 
+      4,   0,   1,   0,   0,   0, 
+      0,   0,   0,   0, 116, 104, 
+    101,  83,  97, 109, 112, 108, 
+    101, 114,  43, 116, 104, 101, 
+     84, 101, 120, 116, 117, 114, 
+    101,  86,   0, 171,   4,   0, 
+      7,   0,   1,   0,   4,   0, 
+      1,   0,   0,   0,   0,   0, 
+      0,   0, 116, 104, 101,  83, 
+     97, 109, 112, 108, 101, 114, 
+     43, 116, 104, 101,  84, 101, 
+    120, 116, 117, 114, 101,  89, 
+      0, 171,   4,   0,   7,   0, 
+      1,   0,   4,   0,   1,   0, 
+      0,   0,   0,   0,   0,   0, 
+    112, 115,  95,  50,  95,  48, 
+      0,  77, 105,  99, 114, 111, 
+    115, 111, 102, 116,  32,  40, 
+     82,  41,  32,  72,  76,  83, 
+     76,  32,  83, 104,  97, 100, 
+    101, 114,  32,  67, 111, 109, 
+    112, 105, 108, 101, 114,  32, 
+     49,  48,  46,  49,   0, 171, 
+     81,   0,   0,   5,   4,   0, 
+     15, 160,   0,   0, 128,  63, 
+      0,   0,   0,   0,   0,   0, 
+      0,   0,   0,   0,   0,   0, 
+     31,   0,   0,   2,   0,   0, 
+      0, 128,   0,   0,   3, 176, 
+     31,   0,   0,   2,   0,   0, 
+      0, 128,   0,   0,  15, 144, 
+     31,   0,   0,   2,   0,   0, 
+      0, 144,   0,   8,  15, 160, 
+     31,   0,   0,   2,   0,   0, 
+      0, 144,   1,   8,  15, 160, 
+     31,   0,   0,   2,   0,   0, 
+      0, 144,   2,   8,  15, 160, 
+     66,   0,   0,   3,   0,   0, 
+     15, 128,   0,   0, 228, 176, 
+      0,   8, 228, 160,  66,   0, 
+      0,   3,   1,   0,  15, 128, 
+      0,   0, 228, 176,   1,   8, 
+    228, 160,  66,   0,   0,   3, 
+      2,   0,  15, 128,   0,   0, 
+    228, 176,   2,   8, 228, 160, 
+      1,   0,   0,   2,   0,   0, 
+      2, 128,   1,   0,   0, 128, 
+      1,   0,   0,   2,   0,   0, 
+      4, 128,   2,   0,   0, 128, 
+      2,   0,   0,   3,   0,   0, 
+      7, 128,   0,   0, 228, 128, 
+      0,   0, 228, 160,   8,   0, 
+      0,   3,   1,   0,   1, 128, 
+      0,   0, 228, 128,   1,   0, 
+    228, 160,   8,   0,   0,   3, 
+      1,   0,   2, 128,   0,   0, 
+    228, 128,   2,   0, 228, 160, 
+      8,   0,   0,   3,   1,   0, 
+      4, 128,   0,   0, 228, 128, 
+      3,   0, 228, 160,   1,   0, 
+      0,   2,   1,   0,   8, 128, 
+      4,   0,   0, 160,   5,   0, 
+      0,   3,   0,   0,  15, 128, 
+      1,   0, 228, 128,   0,   0, 
+    228, 144,   1,   0,   0,   2, 
+      0,   8,  15, 128,   0,   0, 
+    228, 128, 255, 255,   0,   0
+};
diff --git a/src/render/direct3d/D3D9_PixelShader_YUV.hlsl b/src/render/direct3d/D3D9_PixelShader_YUV.hlsl
new file mode 100644
index 000000000000..880484807cf2
--- /dev/null
+++ b/src/render/direct3d/D3D9_PixelShader_YUV.hlsl
@@ -0,0 +1,47 @@
+
+Texture2D theTextureY : register(t0);
+Texture2D theTextureU : register(t1);
+Texture2D theTextureV : register(t2);
+
+SamplerState theSampler = sampler_state
+{
+    addressU = Clamp;
+    addressV = Clamp;
+    mipfilter = NONE;
+    minfilter = LINEAR;
+    magfilter = LINEAR;
+};
+
+struct PixelShaderInput
+{
+    float4 pos : SV_POSITION;
+    float2 tex : TEXCOORD0;
+    float4 color : COLOR0;
+};
+
+cbuffer Constants : register(b0)
+{
+    float4 Yoffset;
+    float4 Rcoeff;
+    float4 Gcoeff;
+    float4 Bcoeff;
+};
+
+
+float4 main(PixelShaderInput input) : SV_TARGET
+{
+    float4 Output;
+
+    float3 yuv;
+    yuv.x = theTextureY.Sample(theSampler, input.tex).r;
+    yuv.y = theTextureU.Sample(theSampler, input.tex).r;
+    yuv.z = theTextureV.Sample(theSampler, input.tex).r;
+
+    yuv += Yoffset.xyz;
+    Output.r = dot(yuv, Rcoeff.xyz);
+    Output.g = dot(yuv, Gcoeff.xyz);
+    Output.b = dot(yuv, Bcoeff.xyz);
+    Output.a = 1.0f;
+
+    return Output * input.color;
+}
diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c
index 5abd04a5b207..6ff737b39c5d 100644
--- a/src/render/direct3d/SDL_render_d3d.c
+++ b/src/render/direct3d/SDL_render_d3d.c
@@ -46,7 +46,8 @@ typedef struct
     SDL_bool cliprect_enabled_dirty;
     SDL_Rect cliprect;
     SDL_bool cliprect_dirty;
-    LPDIRECT3DPIXELSHADER9 shader;
+    D3D9_Shader shader;
+    const float *shader_params;
 } D3D_DrawStateCache;
 
 /* Direct3D renderer implementation */
@@ -90,6 +91,8 @@ typedef struct
 {
     D3D_TextureRep texture;
     D3DTEXTUREFILTERTYPE scaleMode;
+    D3D9_Shader shader;
+    const float *shader_params;
 
 #if SDL_HAVE_YUV
     /* YV12 texture support */
@@ -553,6 +556,12 @@ static int D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P
         if (D3D_CreateTextureRep(data->device, &texturedata->vtexture, usage, texture->format, PixelFormatToD3DFMT(texture->format), (texture->w + 1) / 2, (texture->h + 1) / 2) < 0) {
             return -1;
         }
+
+        texturedata->shader = SHADER_YUV;
+        texturedata->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace);
+        if (texturedata->shader_params == NULL) {
+            return SDL_SetError("Unsupported YUV colorspace");
+        }
     }
 #endif
     return 0;
@@ -717,7 +726,8 @@ static void D3D_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
         texturedata->texture.dirty = SDL_TRUE;
         if (data->drawstate.texture == texture) {
             data->drawstate.texture = NULL;
-            data->drawstate.shader = NULL;
+            data->drawstate.shader = SHADER_NONE;
+            data->drawstate.shader_params = NULL;
             IDirect3DDevice9_SetPixelShader(data->device, NULL);
             IDirect3DDevice9_SetTexture(data->device, 0, NULL);
         }
@@ -928,39 +938,24 @@ static void UpdateTextureScaleMode(D3D_RenderData *data, D3D_TextureData *textur
     }
 }
 
-static int SetupTextureState(D3D_RenderData *data, SDL_Texture *texture, LPDIRECT3DPIXELSHADER9 *shader)
+static int SetupTextureState(D3D_RenderData *data, SDL_Texture *texture, D3D9_Shader *shader, const float **shader_params)
 {
     D3D_TextureData *texturedata = (D3D_TextureData *)texture->driverdata;
 
-    SDL_assert(*shader == NULL);
-
     if (!texturedata) {
         return SDL_SetError("Texture is not currently available");
     }
 
     UpdateTextureScaleMode(data, texturedata, 0);
 
+    *shader = texturedata->shader;
+    *shader_params = texturedata->shader_params;
+
     if (BindTextureRep(data->device, &texturedata->texture, 0) < 0) {
         return -1;
     }
 #if SDL_HAVE_YUV
     if (texturedata->yuv) {
-        if (SDL_ISCOLORSPACE_YUV_BT601(texture->colorspace)) {
-            if (SDL_ISCOLORSPACE_LIMITED_RANGE(texture->colorspace)) {
-                *shader = data->shaders[SHADER_YUV_BT601];
-            } else {
-                *shader = data->shaders[SHADER_YUV_JPEG];
-            }
-        } else if (SDL_ISCOLORSPACE_YUV_BT709(texture->colorspace)) {
-            if (SDL_ISCOLORSPACE_LIMITED_RANGE(texture->colorspace)) {
-                *shader = data->shaders[SHADER_YUV_BT709];
-            } else {
-                return SDL_SetError("Unsupported YUV conversion mode");
-            }
-        } else {
-            return SDL_SetError("Unsupported YUV conversion mode");
-        }
-
         UpdateTextureScaleMode(data, texturedata, 1);
         UpdateTextureScaleMode(data, texturedata, 2);
 
@@ -985,7 +980,8 @@ static int SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd)
         D3D_TextureData *oldtexturedata = data->drawstate.texture ? (D3D_TextureData *)data->drawstate.texture->driverdata : NULL;
         D3D_TextureData *newtexturedata = texture ? (D3D_TextureData *)texture->driverdata : NULL;
 #endif
-        LPDIRECT3DPIXELSHADER9 shader = NULL;
+        D3D9_Shader shader = SHADER_NONE;
+        const float *shader_params = NULL;
 
         /* disable any enabled textures we aren't going to use, let SetupTextureState() do the rest. */
         if (!texture) {
@@ -997,18 +993,29 @@ static int SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd)
             IDirect3DDevice9_SetTexture(data->device, 2, NULL);
         }
 #endif
-        if (texture && SetupTextureState(data, texture, &shader) < 0) {
+        if (texture && SetupTextureState(data, texture, &shader, &shader_params) < 0) {
             return -1;
         }
 
         if (shader != data->drawstate.shader) {
-            const HRESULT result = IDirect3DDevice9_SetPixelShader(data->device, shader);
+            const HRESULT result = IDirect3DDevice9_SetPixelShader(data->device, data->shaders[shader]);
             if (FAILED(result)) {
                 return D3D_SetError("IDirect3DDevice9_SetPixelShader()", result);
             }
             data->drawstate.shader = shader;
         }
 
+        if (shader_params != data->drawstate.shader_params) {
+            if (shader_params) {
+                const UINT shader_params_length = 4; /* The YUV shader takes 4 float4 parameters */
+                const HRESULT result = IDirect3DDevice9_SetPixelShaderConstantF(data->device, 0, shader_params, shader_params_length);
+                if (FAILED(result)) {
+                    return D3D_SetError("IDirect3DDevice9_SetPixelShaderConstantF()", result);
+                }
+            }
+            data->drawstate.shader_params = shader_params;
+        }
+
         data->drawstate.texture = texture;
     } else if (texture) {
         D3D_TextureData *texturedata = (D3D_TextureData *)texture->driverdata;
@@ -1100,7 +1107,8 @@ static void D3D_InvalidateCachedState(SDL_Renderer *renderer)
     data->drawstate.cliprect_dirty = SDL_TRUE;
     data->drawstate.blend = SDL_BLENDMODE_INVALID;
     data->drawstate.texture = NULL;
-    data->drawstate.shader = NULL;
+    data->drawstate.shader = SHADER_NONE;
+    data->drawstate.shader_params = NULL;
 }
 
 static int D3D_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
@@ -1387,7 +1395,8 @@ static void D3D_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture)
 
     if (renderdata->drawstate.texture == texture) {
         renderdata->drawstate.texture = NULL;
-        renderdata->drawstate.shader = NULL;
+        renderdata->drawstate.shader = SHADER_NONE;
+        renderdata->drawstate.shader_params = NULL;
         IDirect3DDevice9_SetPixelShader(renderdata->device, NULL);
         IDirect3DDevice9_SetTexture(renderdata->device, 0, NULL);
 #if SDL_HAVE_YUV
@@ -1711,13 +1720,13 @@ SDL_Renderer *D3D_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_pro
 #if SDL_HAVE_YUV
     if (caps.MaxSimultaneousTextures >= 3) {
         int i;
-        for (i = 0; i < SDL_arraysize(data->shaders); ++i) {
+        for (i = SHADER_NONE + 1; i < SDL_arraysize(data->shaders); ++i) {
             result = D3D9_CreatePixelShader(data->device, (D3D9_Shader)i, &data->shaders[i]);
             if (FAILED(result)) {
                 D3D_SetError("CreatePixelShader()", result);
             }
         }
-        if (data->shaders[SHADER_YUV_JPEG] && data->shaders[SHADER_YUV_BT601] && data->shaders[SHADER_YUV_BT709]) {
+        if (data->shaders[SHADER_YUV]) {
             renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12;
             renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV;
         }
diff --git a/src/render/direct3d/SDL_shaders_d3d.c b/src/render/direct3d/SDL_shaders_d3d.c
index a05d23daa60d..6fa51d57e0a1 100644
--- a/src/render/direct3d/SDL_shaders_d3d.c
+++ b/src/render/direct3d/SDL_shaders_d3d.c
@@ -28,241 +28,21 @@
 
 #include "SDL_shaders_d3d.h"
 
-/* The shaders here were compiled with:
+/* The shaders here were compiled with compile_shaders.bat */
 
-       fxc /T ps_2_0 /Fo"<OUTPUT FILE>" "<INPUT FILE>"
+#define g_ps20_main D3D9_PixelShader_YUV
+#include "D3D9_PixelShader_YUV.h"
+#undef g_ps20_main
 
-   Shader object code was converted to a list of DWORDs via the following
-   *nix style command (available separately from Windows + MSVC):
-
-     hexdump -v -e '6/4 "0x%08.8x, " "\n"' <FILE>
-*/
-
-/* --- D3D9_PixelShader_YUV_JPEG.hlsl ---
-    Texture2D theTextureY : register(t0);
-    Texture2D theTextureU : register(t1);
-    Texture2D theTextureV : register(t2);
-    SamplerState theSampler = sampler_state
-    {
-        addressU = Clamp;
-        addressV = Clamp;
-        mipfilter = NONE;
-        minfilter = LINEAR;
-        magfilter = LINEAR;
-    };
-
-    struct PixelShaderInput
-    {
-        float4 pos : SV_POSITION;
-        float2 tex : TEXCOORD0;
-        float4 color : COLOR0;
-    };
-
-    float4 main(PixelShaderInput input) : SV_TARGET
-    {
-        const float3 offset = {0.0, -0.501960814, -0.501960814};
-        const float3 Rcoeff = {1.0000,  0.0000,  1.4020};
-        const float3 Gcoeff = {1.0000, -0.3441, -0.7141};
-        const float3 Bcoeff = {1.0000,  1.7720,  0.0000};
-
-        float4 Output;
-
-        float3 yuv;
-        yuv.x = theTextureY.Sample(theSampler, input.tex).r;
-        yuv.y = theTextureU.Sample(theSampler, input.tex).r;
-        yuv.z = theTextureV.Sample(theSampler, input.tex).r;
-
-        yuv += offset;
-        Output.r = dot(yuv, Rcoeff);
-        Output.g = dot(yuv, Gcoeff);
-        Output.b = dot(yuv, Bcoeff);
-        Output.a = 1.0f;
-
-        return Output * input.color;
-    }
-*/
-static const DWORD D3D9_PixelShader_YUV_JPEG[] = {
-    0xffff0200, 0x0044fffe, 0x42415443, 0x0000001c, 0x000000d7, 0xffff0200,
-    0x00000003, 0x0000001c, 0x00000100, 0x000000d0, 0x00000058, 0x00010003,
-    0x00000001, 0x00000070, 0x00000000, 0x00000080, 0x00020003, 0x00000001,
-    0x00000098, 0x00000000, 0x000000a8, 0x00000003, 0x00000001, 0x000000c0,
-    0x00000000, 0x53656874, 0x6c706d61, 0x742b7265, 0x65546568, 0x72757478,
-    0xab005565, 0x00070004, 0x00040001, 0x00000001, 0x00000000, 0x53656874,
-    0x6c706d61, 0x742b7265, 0x65546568, 0x72757478, 0xab005665, 0x00070004,
-    0x00040001, 0x00000001, 0x00000000, 0x53656874, 0x6c706d61, 0x742b7265,
-    0x65546568, 0x72757478, 0xab005965, 0x00070004, 0x00040001, 0x00000001,
-    0x00000000, 0x325f7370, 0x4d00305f, 0x6f726369, 0x74666f73, 0x29522820,
-    0x534c4820, 0x6853204c, 0x72656461, 0x6d6f4320, 0x656c6970, 0x2e362072,
-    0x36392e33, 0x312e3030, 0x34383336, 0xababab00, 0x05000051, 0xa00f0000,
-    0x00000000, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, 0xa00f0001,
-    0x3f800000, 0x00000000, 0x3fb374bc, 0x00000000, 0x05000051, 0xa00f0002,
-    0x3f800000, 0xbeb02de0, 0xbf36cf42, 0x00000000, 0x05000051, 0xa00f0003,
-    0x3f800000, 0x3fe2d0e5, 0x00000000, 0x00000000, 0x0200001f, 0x80000000,
-    0xb0030000, 0x0200001f, 0x80000000, 0x900f0000, 0x0200001f, 0x90000000,
-    0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, 0x90000000,
-    0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, 0x03000042,
-    0x800f0001, 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0002, 0xb0e40000,
-    0xa0e40802, 0x02000001, 0x80020000, 0x80000001, 0x02000001, 0x80040000,
-    0x80000002, 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, 0x03000008,
-    0x80010001, 0x80e40000, 0xa0e40001, 0x03000008, 0x80020001, 0x80e40000,
-    0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003,
-    0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001,
-    0x90e40000, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff
-};
-
-/* --- D3D9_PixelShader_YUV_BT601.hlsl ---
-    Texture2D theTextureY : register(t0);
-    Texture2D theTextureU : register(t1);
-    Texture2D theTextureV : register(t2);
-    SamplerState theSampler = sampler_state
-    {
-        addressU = Clamp;
-        addressV = Clamp;
-        mipfilter = NONE;
-        minfilter = LINEAR;
-        magfilter = LINEAR;
-    };
-
-    struct PixelShaderInput
-    {
-        float4 pos : SV_POSITION;
-        float2 tex : TEXCOORD0;
-        float4 color : COLOR0;
-    };
-
-    float4 main(PixelShaderInput input) : SV_TARGET
-    {
-        const float3 offset = {-0.0627451017, -0.501960814, -0.501960814};
-        const float3 Rcoeff = {1.1644,  0.0000,  1.5960};
-        const float3 Gcoeff = {1.1644, -0.3918, -0.8130};
-        const float3 Bcoeff = {1.1644,  2.0172,  0.0000};
-
-        float4 Output;
-
-        float3 yuv;
-        yuv.x = theTextureY.Sample(theSampler, input.tex).r;
-        yuv.y = theTextureU.Sample(theSampler, input.tex).r;
-        yuv.z = theTextureV.Sample(theSampler, input.tex).r;
-
-        yuv += offset;
-        Output.r = dot(yuv, Rcoeff);
-        Output.g = dot(yuv, Gcoeff);
-        Output.b = dot(yuv, Bcoeff);
-        Output.a = 1.0f;
-
-        return Output * input.color;
-    }
-*/
-static const DWORD D3D9_PixelShader_YUV_BT601[] = {
-    0xffff0200, 0x0044fffe, 0x42415443, 0x0000001c, 0x000000d7, 0xffff0200,
-    0x00000003, 0x0000001c, 0x00000100, 0x000000d0, 0x00000058, 0x00010003,
-    0x00000001, 0x00000070, 0x00000000, 0x00000080, 0x00020003, 0x00000001,
-    0x00000098, 0x00000000, 0x000000a8, 0x00000003, 0x00000001, 0x000000c0,
-    0x00000000, 0x53656874, 0x6c706d61, 0x742b7265, 0x65546568, 0x72757478,
-    0xab005565, 0x00070004, 0x00040001, 0x00000001, 0x00000000, 0x53656874,
-    0x6c706d61, 0x742b7265, 0x65546568, 0x72757478, 0xab005665, 0x00070004,
-    0x00040001, 0x00000001, 0x00000000, 0x53656874, 0x6c706d61, 0x742b7265,
-    0x65546568, 0x72757478, 0xab005965, 0x00070004, 0x00040001, 0x00000001,
-    0x00000000, 0x325f7370, 0x4d00305f, 0x6f726369, 0x74666f73, 0x29522820,
-    0x534c4820, 0x6853204c, 0x72656461, 0x6d6f4320, 0x656c6970, 0x2e362072,
-    0x36392e33, 0x312e3030, 0x34383336, 0xababab00, 0x05000051, 0xa00f0000,
-    0xbd808081, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, 0xa00f0001,
-    0x3f950b0f, 0x00000000, 0x3fcc49ba, 0x00000000, 0x05000051, 0xa00f0002,
-    0x3f950b0f, 0xbec89a02, 0xbf5020c5, 0x00000000, 0x05000051, 0xa00f0003,
-    0x3f950b0f, 0x400119ce, 0x00000000, 0x00000000, 0x0200001f, 0x80000000,
-    0xb0030000, 0x0200001f, 0x80000000, 0x900f0000, 0x0200001f, 0x90000000,
-    0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, 0x90000000,
-    0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, 0x03000042,
-    0x800f0001, 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0002, 0xb0e40000,
-    0xa0e40802, 0x02000001, 0x80020000, 0x80000001, 0x02000001, 0x80040000,
-    0x80000002, 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, 0x03000008,
-    0x80010001, 0x80e40000, 0xa0e40001, 0x03000008, 0x80020001, 0x80e40000,
-    0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003,
-    0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001,
-    0x90e40000, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff
-};
-
-/* --- D3D9_PixelShader_YUV_BT709.hlsl ---
-    Texture2D theTextureY : register(t0);
-    Texture2D theTextureU : register(t1);
-    Texture2D theTextureV : register(t2);
-    SamplerState theSampler = sampler_state
-    {
-        addressU = Clamp;
-        addressV = Clamp;
-        mipfilter = NONE;
-        minfilter = LINEAR;
-        magfilter = LINEAR;
-    };
-
-    struct PixelShaderInput
-    {
-        float4 pos : SV_POSITION;
-        float2 tex : TEXCOORD0;
-        float4 color : COLOR0;
-    };
-
-    float4 main(PixelShaderInput input) : SV_TARGET
-    {
-        const float3 offset = {-0.0627451017, -0.501960814, -0.501960814};
-        const float3 Rcoeff = {1.1644,  0.0000,  1.7927};
-        const float3 Gcoeff = {1.1644, -0.2132, -0.5329};
-        const float3 Bcoeff = {1.1644,  2.1124,  0.0000};
-
-        float4 Output;
-
-        float3 yuv;
-        yuv.x = theTextureY.Sample(theSampler, input.tex).r;
-        yuv.y = theTextureU.Sample(theSampler, input.tex).r;
-        yuv.z = theTextureV.Sample(theSampler, input.tex).r;
-
-        yuv += offset;
-        Output.r = dot(yuv, Rcoeff);
-        Output.g = dot(yuv, Gcoeff);
-        Output.b = dot(yuv, Bcoeff);
-        Output.a = 1.0f;
-
-        return Output * input.color;
-    }
-*/
-static const DWORD D3D9_PixelShader_YUV_BT709[] = {
-    0xffff0200, 0x0044fffe, 0x42415443, 0x0000001c, 0x000000d7, 0xffff0200,
-    0x00000003, 0x0000001c, 0x00000100, 0x000000d0, 0x00000058, 0x00010003,
-    0x00000001, 0x00000070, 0x00000000, 0x00000080, 0x00020003, 0x00000001,
-    0x00000098, 0x00000000, 0x000000a8, 0x00000003, 0x00000001, 0x000000c0,
-    0x00000000, 0x53656874, 0x6c706d61, 0x742b7265, 0x65546568, 0x72757478,
-    0xab005565, 0x00070004, 0x00040001, 0x00000001, 0x00000000, 0x53656874,
-    0x6c706d61, 0x742b7265, 0x65546568, 0x72757478, 0xab005665, 0x00070004,
-    0x00040001, 0x00000001, 0x00000000, 0x53656874, 0x6c706d61, 0x742b7265,
-    0x65546568, 0x72757478, 0xab005965, 0x00070004, 0x00040001, 0x00000001,
-    0x00000000, 0x325f7370, 0x4d00305f, 0x6f726369, 0x74666f73, 0x29522820,
-    0x534c4820, 0x6853204c, 0x72656461, 0x6d6f4320, 0x656c6970, 0x2e362072,
-    0x36392e33, 0x312e3030, 0x34383336, 0xababab00, 0x05000051, 0xa00f0000,
-    0xbd808081, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, 0xa00f0001,
-    0x3f950b0f, 0x00000000, 0x3fe57732, 0x00000000, 0x05000051, 0xa00f0002,
-    0x3f950b0f, 0xbe5a511a, 0xbf086c22, 0x00000000, 0x05000051, 0xa00f0003,
-    0x3f950b0f, 0x40073190, 0x00000000, 0x00000000, 0x0200001f, 0x80000000,
-    0xb0030000, 0x0200001f, 0x80000000, 0x900f0000, 0x0200001f, 0x90000000,
-    0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, 0x90000000,
-    0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, 0x03000042,
-    0x800f0001, 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0002, 0xb0e40000,
-    0xa0e40802, 0x02000001, 0x80020000, 0x80000001, 0x02000001, 0x80040000,
-    0x80000002, 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, 0x03000008,
-    0x80010001, 0x80e40000, 0xa0e40001, 0x03000008, 0x80020001, 0x80e40000,
-    0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003,
-    0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001,
-    0x90e40000, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff
-};
-
-static const DWORD *D3D9_shaders[] = {
-    D3D9_PixelShader_YUV_JPEG,
-    D3D9_PixelShader_YUV_BT601,
-    D3D9_PixelShader_YUV_BT709,
+static const BYTE *D3D9_shaders[] = {
+    NULL,
+    D3D9_PixelShader_YUV
 };
+SDL_COMPILE_TIME_ASSERT(D3D9_shaders, SDL_arraysize(D3D9_shaders) == NUM_SHADERS);
 
 HRESULT D3D9_CreatePixelShader(IDirect3DDevice9 *d3dDevice, D3D9_Shader shader, IDirect3DPixelShader9 **pixelShader)
 {
-    return IDirect3DDevice9_CreatePixelShader(d3dDevice, D3D9_shaders[shader], pixelShader);
+    return IDirect3DDevice9_CreatePixelShader(d3dDevice, (const DWORD *)D3D9_shaders[shader], pixelShader);
 }
 
 #endif /* SDL_VIDEO_RENDER_D3D && !SDL_RENDER_DISABLED */
diff --git a/src/render/direct3d/SDL_shaders_d3d.h b/src/render/direct3d/SDL_shaders_d3d.h
index 13ecf6fc8465..41d9fdc7eb76 100644
--- a/src/render/direct3d/SDL_shaders_d3d.h
+++ b/src/render/direct3d/SDL_shad

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