summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/render/direct3d
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/render/direct3d')
-rw-r--r--contrib/SDL-3.2.8/src/render/direct3d/D3D9_PixelShader_YUV.h164
-rw-r--r--contrib/SDL-3.2.8/src/render/direct3d/D3D9_PixelShader_YUV.hlsl47
-rw-r--r--contrib/SDL-3.2.8/src/render/direct3d/SDL_render_d3d.c1778
-rw-r--r--contrib/SDL-3.2.8/src/render/direct3d/SDL_shaders_d3d.c48
-rw-r--r--contrib/SDL-3.2.8/src/render/direct3d/SDL_shaders_d3d.h32
-rw-r--r--contrib/SDL-3.2.8/src/render/direct3d/compile_shaders.bat1
6 files changed, 2070 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/render/direct3d/D3D9_PixelShader_YUV.h b/contrib/SDL-3.2.8/src/render/direct3d/D3D9_PixelShader_YUV.h
new file mode 100644
index 0000000..e6d9130
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/render/direct3d/D3D9_PixelShader_YUV.h
@@ -0,0 +1,164 @@
1#if 0
2//
3// Generated by Microsoft (R) HLSL Shader Compiler 10.1
4//
5// Parameters:
6//
7// float4 Bcoeff;
8// float4 Gcoeff;
9// float4 Rcoeff;
10// float4 Yoffset;
11// Texture2D theSampler+theTextureU;
12// Texture2D theSampler+theTextureV;
13// Texture2D theSampler+theTextureY;
14//
15//
16// Registers:
17//
18// Name Reg Size
19// ---------------------- ----- ----
20// Yoffset c0 1
21// Rcoeff c1 1
22// Gcoeff c2 1
23// Bcoeff c3 1
24// theSampler+theTextureY s0 1
25// theSampler+theTextureU s1 1
26// theSampler+theTextureV s2 1
27//
28
29 ps_2_0
30 def c4, 1, 0, 0, 0
31 dcl t0.xy
32 dcl v0
33 dcl_2d s0
34 dcl_2d s1
35 dcl_2d s2
36 texld r0, t0, s0
37 texld r1, t0, s1
38 texld r2, t0, s2
39 mov r0.y, r1.x
40 mov r0.z, r2.x
41 add r0.xyz, r0, c0
42 dp3 r1.x, r0, c1
43 dp3 r1.y, r0, c2
44 dp3 r1.z, r0, c3
45 mov r1.w, c4.x
46 mul r0, r1, v0
47 mov oC0, r0
48
49// approximately 12 instruction slots used (3 texture, 9 arithmetic)
50#endif
51
52const BYTE g_ps20_main[] =
53{
54 0, 2, 255, 255, 254, 255,
55 97, 0, 67, 84, 65, 66,
56 28, 0, 0, 0, 87, 1,
57 0, 0, 0, 2, 255, 255,
58 7, 0, 0, 0, 28, 0,
59 0, 0, 0, 1, 0, 0,
60 80, 1, 0, 0, 168, 0,
61 0, 0, 2, 0, 3, 0,
62 1, 0, 0, 0, 176, 0,
63 0, 0, 0, 0, 0, 0,
64 192, 0, 0, 0, 2, 0,
65 2, 0, 1, 0, 0, 0,
66 176, 0, 0, 0, 0, 0,
67 0, 0, 199, 0, 0, 0,
68 2, 0, 1, 0, 1, 0,
69 0, 0, 176, 0, 0, 0,
70 0, 0, 0, 0, 206, 0,
71 0, 0, 2, 0, 0, 0,
72 1, 0, 0, 0, 176, 0,
73 0, 0, 0, 0, 0, 0,
74 214, 0, 0, 0, 3, 0,
75 1, 0, 1, 0, 0, 0,
76 240, 0, 0, 0, 0, 0,
77 0, 0, 0, 1, 0, 0,
78 3, 0, 2, 0, 1, 0,
79 0, 0, 24, 1, 0, 0,
80 0, 0, 0, 0, 40, 1,
81 0, 0, 3, 0, 0, 0,
82 1, 0, 0, 0, 64, 1,
83 0, 0, 0, 0, 0, 0,
84 66, 99, 111, 101, 102, 102,
85 0, 171, 1, 0, 3, 0,
86 1, 0, 4, 0, 1, 0,
87 0, 0, 0, 0, 0, 0,
88 71, 99, 111, 101, 102, 102,
89 0, 82, 99, 111, 101, 102,
90 102, 0, 89, 111, 102, 102,
91 115, 101, 116, 0, 116, 104,
92 101, 83, 97, 109, 112, 108,
93 101, 114, 43, 116, 104, 101,
94 84, 101, 120, 116, 117, 114,
95 101, 85, 0, 171, 171, 171,
96 4, 0, 7, 0, 1, 0,
97 4, 0, 1, 0, 0, 0,
98 0, 0, 0, 0, 116, 104,
99 101, 83, 97, 109, 112, 108,
100 101, 114, 43, 116, 104, 101,
101 84, 101, 120, 116, 117, 114,
102 101, 86, 0, 171, 4, 0,
103 7, 0, 1, 0, 4, 0,
104 1, 0, 0, 0, 0, 0,
105 0, 0, 116, 104, 101, 83,
106 97, 109, 112, 108, 101, 114,
107 43, 116, 104, 101, 84, 101,
108 120, 116, 117, 114, 101, 89,
109 0, 171, 4, 0, 7, 0,
110 1, 0, 4, 0, 1, 0,
111 0, 0, 0, 0, 0, 0,
112 112, 115, 95, 50, 95, 48,
113 0, 77, 105, 99, 114, 111,
114 115, 111, 102, 116, 32, 40,
115 82, 41, 32, 72, 76, 83,
116 76, 32, 83, 104, 97, 100,
117 101, 114, 32, 67, 111, 109,
118 112, 105, 108, 101, 114, 32,
119 49, 48, 46, 49, 0, 171,
120 81, 0, 0, 5, 4, 0,
121 15, 160, 0, 0, 128, 63,
122 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0,
124 31, 0, 0, 2, 0, 0,
125 0, 128, 0, 0, 3, 176,
126 31, 0, 0, 2, 0, 0,
127 0, 128, 0, 0, 15, 144,
128 31, 0, 0, 2, 0, 0,
129 0, 144, 0, 8, 15, 160,
130 31, 0, 0, 2, 0, 0,
131 0, 144, 1, 8, 15, 160,
132 31, 0, 0, 2, 0, 0,
133 0, 144, 2, 8, 15, 160,
134 66, 0, 0, 3, 0, 0,
135 15, 128, 0, 0, 228, 176,
136 0, 8, 228, 160, 66, 0,
137 0, 3, 1, 0, 15, 128,
138 0, 0, 228, 176, 1, 8,
139 228, 160, 66, 0, 0, 3,
140 2, 0, 15, 128, 0, 0,
141 228, 176, 2, 8, 228, 160,
142 1, 0, 0, 2, 0, 0,
143 2, 128, 1, 0, 0, 128,
144 1, 0, 0, 2, 0, 0,
145 4, 128, 2, 0, 0, 128,
146 2, 0, 0, 3, 0, 0,
147 7, 128, 0, 0, 228, 128,
148 0, 0, 228, 160, 8, 0,
149 0, 3, 1, 0, 1, 128,
150 0, 0, 228, 128, 1, 0,
151 228, 160, 8, 0, 0, 3,
152 1, 0, 2, 128, 0, 0,
153 228, 128, 2, 0, 228, 160,
154 8, 0, 0, 3, 1, 0,
155 4, 128, 0, 0, 228, 128,
156 3, 0, 228, 160, 1, 0,
157 0, 2, 1, 0, 8, 128,
158 4, 0, 0, 160, 5, 0,
159 0, 3, 0, 0, 15, 128,
160 1, 0, 228, 128, 0, 0,
161 228, 144, 1, 0, 0, 2,
162 0, 8, 15, 128, 0, 0,
163 228, 128, 255, 255, 0, 0
164};
diff --git a/contrib/SDL-3.2.8/src/render/direct3d/D3D9_PixelShader_YUV.hlsl b/contrib/SDL-3.2.8/src/render/direct3d/D3D9_PixelShader_YUV.hlsl
new file mode 100644
index 0000000..8804848
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/render/direct3d/D3D9_PixelShader_YUV.hlsl
@@ -0,0 +1,47 @@
1
2Texture2D theTextureY : register(t0);
3Texture2D theTextureU : register(t1);
4Texture2D theTextureV : register(t2);
5
6SamplerState theSampler = sampler_state
7{
8 addressU = Clamp;
9 addressV = Clamp;
10 mipfilter = NONE;
11 minfilter = LINEAR;
12 magfilter = LINEAR;
13};
14
15struct PixelShaderInput
16{
17 float4 pos : SV_POSITION;
18 float2 tex : TEXCOORD0;
19 float4 color : COLOR0;
20};
21
22cbuffer Constants : register(b0)
23{
24 float4 Yoffset;
25 float4 Rcoeff;
26 float4 Gcoeff;
27 float4 Bcoeff;
28};
29
30
31float4 main(PixelShaderInput input) : SV_TARGET
32{
33 float4 Output;
34
35 float3 yuv;
36 yuv.x = theTextureY.Sample(theSampler, input.tex).r;
37 yuv.y = theTextureU.Sample(theSampler, input.tex).r;
38 yuv.z = theTextureV.Sample(theSampler, input.tex).r;
39
40 yuv += Yoffset.xyz;
41 Output.r = dot(yuv, Rcoeff.xyz);
42 Output.g = dot(yuv, Gcoeff.xyz);
43 Output.b = dot(yuv, Bcoeff.xyz);
44 Output.a = 1.0f;
45
46 return Output * input.color;
47}
diff --git a/contrib/SDL-3.2.8/src/render/direct3d/SDL_render_d3d.c b/contrib/SDL-3.2.8/src/render/direct3d/SDL_render_d3d.c
new file mode 100644
index 0000000..ccb5b3c
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/render/direct3d/SDL_render_d3d.c
@@ -0,0 +1,1778 @@
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_D3D
24
25#include "../../core/windows/SDL_windows.h"
26
27#include "../SDL_sysrender.h"
28#include "../SDL_d3dmath.h"
29#include "../../video/windows/SDL_windowsvideo.h"
30#include "../../video/SDL_pixels_c.h"
31
32#define D3D_DEBUG_INFO
33#include <d3d9.h>
34
35#include "SDL_shaders_d3d.h"
36
37typedef struct
38{
39 SDL_Rect viewport;
40 bool viewport_dirty;
41 SDL_Texture *texture;
42 SDL_BlendMode blend;
43 bool cliprect_enabled;
44 bool cliprect_enabled_dirty;
45 SDL_Rect cliprect;
46 bool cliprect_dirty;
47 D3D9_Shader shader;
48 const float *shader_params;
49} D3D_DrawStateCache;
50
51// Direct3D renderer implementation
52
53typedef struct
54{
55 void *d3dDLL;
56 IDirect3D9 *d3d;
57 IDirect3DDevice9 *device;
58 UINT adapter;
59 D3DPRESENT_PARAMETERS pparams;
60 bool updateSize;
61 bool beginScene;
62 bool enableSeparateAlphaBlend;
63 D3DTEXTUREFILTERTYPE scaleMode[3];
64 SDL_TextureAddressMode addressMode[3];
65 IDirect3DSurface9 *defaultRenderTarget;
66 IDirect3DSurface9 *currentRenderTarget;
67 void *d3dxDLL;
68#ifdef SDL_HAVE_YUV
69 LPDIRECT3DPIXELSHADER9 shaders[NUM_SHADERS];
70#endif
71 LPDIRECT3DVERTEXBUFFER9 vertexBuffers[8];
72 size_t vertexBufferSize[8];
73 int currentVertexBuffer;
74 bool reportedVboProblem;
75 D3D_DrawStateCache drawstate;
76} D3D_RenderData;
77
78typedef struct
79{
80 bool dirty;
81 int w, h;
82 DWORD usage;
83 Uint32 format;
84 D3DFORMAT d3dfmt;
85 IDirect3DTexture9 *texture;
86 IDirect3DTexture9 *staging;
87} D3D_TextureRep;
88
89typedef struct
90{
91 D3D_TextureRep texture;
92 D3DTEXTUREFILTERTYPE scaleMode;
93 D3D9_Shader shader;
94 const float *shader_params;
95
96#ifdef SDL_HAVE_YUV
97 // YV12 texture support
98 bool yuv;
99 D3D_TextureRep utexture;
100 D3D_TextureRep vtexture;
101 Uint8 *pixels;
102 int pitch;
103 SDL_Rect locked_rect;
104#endif
105} D3D_TextureData;
106
107typedef struct
108{
109 float x, y, z;
110 DWORD color;
111 float u, v;
112} Vertex;
113
114static bool D3D_SetError(const char *prefix, HRESULT result)
115{
116 const char *error;
117
118 switch (result) {
119 case D3DERR_WRONGTEXTUREFORMAT:
120 error = "WRONGTEXTUREFORMAT";
121 break;
122 case D3DERR_UNSUPPORTEDCOLOROPERATION:
123 error = "UNSUPPORTEDCOLOROPERATION";
124 break;
125 case D3DERR_UNSUPPORTEDCOLORARG:
126 error = "UNSUPPORTEDCOLORARG";
127 break;
128 case D3DERR_UNSUPPORTEDALPHAOPERATION:
129 error = "UNSUPPORTEDALPHAOPERATION";
130 break;
131 case D3DERR_UNSUPPORTEDALPHAARG:
132 error = "UNSUPPORTEDALPHAARG";
133 break;
134 case D3DERR_TOOMANYOPERATIONS:
135 error = "TOOMANYOPERATIONS";
136 break;
137 case D3DERR_CONFLICTINGTEXTUREFILTER:
138 error = "CONFLICTINGTEXTUREFILTER";
139 break;
140 case D3DERR_UNSUPPORTEDFACTORVALUE:
141 error = "UNSUPPORTEDFACTORVALUE";
142 break;
143 case D3DERR_CONFLICTINGRENDERSTATE:
144 error = "CONFLICTINGRENDERSTATE";
145 break;
146 case D3DERR_UNSUPPORTEDTEXTUREFILTER:
147 error = "UNSUPPORTEDTEXTUREFILTER";
148 break;
149 case D3DERR_CONFLICTINGTEXTUREPALETTE:
150 error = "CONFLICTINGTEXTUREPALETTE";
151 break;
152 case D3DERR_DRIVERINTERNALERROR:
153 error = "DRIVERINTERNALERROR";
154 break;
155 case D3DERR_NOTFOUND:
156 error = "NOTFOUND";
157 break;
158 case D3DERR_MOREDATA:
159 error = "MOREDATA";
160 break;
161 case D3DERR_DEVICELOST:
162 error = "DEVICELOST";
163 break;
164 case D3DERR_DEVICENOTRESET:
165 error = "DEVICENOTRESET";
166 break;
167 case D3DERR_NOTAVAILABLE:
168 error = "NOTAVAILABLE";
169 break;
170 case D3DERR_OUTOFVIDEOMEMORY:
171 error = "OUTOFVIDEOMEMORY";
172 break;
173 case D3DERR_INVALIDDEVICE:
174 error = "INVALIDDEVICE";
175 break;
176 case D3DERR_INVALIDCALL:
177 error = "INVALIDCALL";
178 break;
179 case D3DERR_DRIVERINVALIDCALL:
180 error = "DRIVERINVALIDCALL";
181 break;
182 case D3DERR_WASSTILLDRAWING:
183 error = "WASSTILLDRAWING";
184 break;
185 default:
186 error = "UNKNOWN";
187 break;
188 }
189 return SDL_SetError("%s: %s", prefix, error);
190}
191
192static D3DFORMAT PixelFormatToD3DFMT(Uint32 format)
193{
194 switch (format) {
195 case SDL_PIXELFORMAT_RGB565:
196 return D3DFMT_R5G6B5;
197 case SDL_PIXELFORMAT_XRGB8888:
198 return D3DFMT_X8R8G8B8;
199 case SDL_PIXELFORMAT_ARGB8888:
200 return D3DFMT_A8R8G8B8;
201 case SDL_PIXELFORMAT_YV12:
202 case SDL_PIXELFORMAT_IYUV:
203 case SDL_PIXELFORMAT_NV12:
204 case SDL_PIXELFORMAT_NV21:
205 return D3DFMT_L8;
206 default:
207 return D3DFMT_UNKNOWN;
208 }
209}
210
211static SDL_PixelFormat D3DFMTToPixelFormat(D3DFORMAT format)
212{
213 switch (format) {
214 case D3DFMT_R5G6B5:
215 return SDL_PIXELFORMAT_RGB565;
216 case D3DFMT_X8R8G8B8:
217 return SDL_PIXELFORMAT_XRGB8888;
218 case D3DFMT_A8R8G8B8:
219 return SDL_PIXELFORMAT_ARGB8888;
220 default:
221 return SDL_PIXELFORMAT_UNKNOWN;
222 }
223}
224
225static void D3D_InitRenderState(D3D_RenderData *data)
226{
227 D3DMATRIX matrix;
228
229 IDirect3DDevice9 *device = data->device;
230 IDirect3DDevice9_SetPixelShader(device, NULL);
231 IDirect3DDevice9_SetTexture(device, 0, NULL);
232 IDirect3DDevice9_SetTexture(device, 1, NULL);
233 IDirect3DDevice9_SetTexture(device, 2, NULL);
234 IDirect3DDevice9_SetFVF(device, D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
235 IDirect3DDevice9_SetVertexShader(device, NULL);
236 IDirect3DDevice9_SetRenderState(device, D3DRS_ZENABLE, D3DZB_FALSE);
237 IDirect3DDevice9_SetRenderState(device, D3DRS_CULLMODE, D3DCULL_NONE);
238 IDirect3DDevice9_SetRenderState(device, D3DRS_LIGHTING, FALSE);
239
240 // Enable color modulation by diffuse color
241 IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_COLOROP,
242 D3DTOP_MODULATE);
243 IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_COLORARG1,
244 D3DTA_TEXTURE);
245 IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_COLORARG2,
246 D3DTA_DIFFUSE);
247
248 // Enable alpha modulation by diffuse alpha
249 IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_ALPHAOP,
250 D3DTOP_MODULATE);
251 IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_ALPHAARG1,
252 D3DTA_TEXTURE);
253 IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_ALPHAARG2,
254 D3DTA_DIFFUSE);
255
256 // Enable separate alpha blend function, if possible
257 if (data->enableSeparateAlphaBlend) {
258 IDirect3DDevice9_SetRenderState(device, D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
259 }
260
261 // Disable second texture stage, since we're done
262 IDirect3DDevice9_SetTextureStageState(device, 1, D3DTSS_COLOROP,
263 D3DTOP_DISABLE);
264 IDirect3DDevice9_SetTextureStageState(device, 1, D3DTSS_ALPHAOP,
265 D3DTOP_DISABLE);
266
267 // Set an identity world and view matrix
268 SDL_zero(matrix);
269 matrix.m[0][0] = 1.0f;
270 matrix.m[1][1] = 1.0f;
271 matrix.m[2][2] = 1.0f;
272 matrix.m[3][3] = 1.0f;
273 IDirect3DDevice9_SetTransform(device, D3DTS_WORLD, &matrix);
274 IDirect3DDevice9_SetTransform(device, D3DTS_VIEW, &matrix);
275
276 // Reset our current scale mode
277 SDL_memset(data->scaleMode, 0xFF, sizeof(data->scaleMode));
278
279 // Reset our current address mode
280 SDL_zeroa(data->addressMode);
281
282 // Start the render with beginScene
283 data->beginScene = true;
284}
285
286static bool D3D_Reset(SDL_Renderer *renderer);
287
288static bool D3D_ActivateRenderer(SDL_Renderer *renderer)
289{
290 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
291 HRESULT result;
292
293 if (data->updateSize) {
294 SDL_Window *window = renderer->window;
295 int w, h;
296 const SDL_DisplayMode *fullscreen_mode = NULL;
297
298 SDL_GetWindowSizeInPixels(window, &w, &h);
299 data->pparams.BackBufferWidth = w;
300 data->pparams.BackBufferHeight = h;
301 if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
302 fullscreen_mode = SDL_GetWindowFullscreenMode(window);
303 }
304 if (fullscreen_mode) {
305 data->pparams.Windowed = FALSE;
306 data->pparams.BackBufferFormat = PixelFormatToD3DFMT(fullscreen_mode->format);
307 data->pparams.FullScreen_RefreshRateInHz = (UINT)SDL_ceilf(fullscreen_mode->refresh_rate);
308 } else {
309 data->pparams.Windowed = TRUE;
310 data->pparams.BackBufferFormat = D3DFMT_UNKNOWN;
311 data->pparams.FullScreen_RefreshRateInHz = 0;
312 }
313 if (!D3D_Reset(renderer)) {
314 return false;
315 }
316
317 data->updateSize = false;
318 }
319 if (data->beginScene) {
320 result = IDirect3DDevice9_BeginScene(data->device);
321 if (result == D3DERR_DEVICELOST) {
322 if (!D3D_Reset(renderer)) {
323 return false;
324 }
325 result = IDirect3DDevice9_BeginScene(data->device);
326 }
327 if (FAILED(result)) {
328 return D3D_SetError("BeginScene()", result);
329 }
330 data->beginScene = false;
331 }
332 return true;
333}
334
335static void D3D_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
336{
337 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
338
339 if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
340 data->updateSize = true;
341 }
342}
343
344static D3DBLEND GetBlendFunc(SDL_BlendFactor factor)
345{
346 switch (factor) {
347 case SDL_BLENDFACTOR_ZERO:
348 return D3DBLEND_ZERO;
349 case SDL_BLENDFACTOR_ONE:
350 return D3DBLEND_ONE;
351 case SDL_BLENDFACTOR_SRC_COLOR:
352 return D3DBLEND_SRCCOLOR;
353 case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR:
354 return D3DBLEND_INVSRCCOLOR;
355 case SDL_BLENDFACTOR_SRC_ALPHA:
356 return D3DBLEND_SRCALPHA;
357 case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA:
358 return D3DBLEND_INVSRCALPHA;
359 case SDL_BLENDFACTOR_DST_COLOR:
360 return D3DBLEND_DESTCOLOR;
361 case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR:
362 return D3DBLEND_INVDESTCOLOR;
363 case SDL_BLENDFACTOR_DST_ALPHA:
364 return D3DBLEND_DESTALPHA;
365 case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA:
366 return D3DBLEND_INVDESTALPHA;
367 default:
368 break;
369 }
370 return (D3DBLEND)0;
371}
372
373static D3DBLENDOP GetBlendEquation(SDL_BlendOperation operation)
374{
375 switch (operation) {
376 case SDL_BLENDOPERATION_ADD:
377 return D3DBLENDOP_ADD;
378 case SDL_BLENDOPERATION_SUBTRACT:
379 return D3DBLENDOP_SUBTRACT;
380 case SDL_BLENDOPERATION_REV_SUBTRACT:
381 return D3DBLENDOP_REVSUBTRACT;
382 case SDL_BLENDOPERATION_MINIMUM:
383 return D3DBLENDOP_MIN;
384 case SDL_BLENDOPERATION_MAXIMUM:
385 return D3DBLENDOP_MAX;
386 default:
387 break;
388 }
389 return (D3DBLENDOP)0;
390}
391
392static bool D3D_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode)
393{
394 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
395 SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode);
396 SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode);
397 SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode);
398 SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode);
399 SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode);
400 SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode);
401
402 if (!GetBlendFunc(srcColorFactor) || !GetBlendFunc(srcAlphaFactor) ||
403 !GetBlendEquation(colorOperation) ||
404 !GetBlendFunc(dstColorFactor) || !GetBlendFunc(dstAlphaFactor) ||
405 !GetBlendEquation(alphaOperation)) {
406 return false;
407 }
408
409 if (!data->enableSeparateAlphaBlend) {
410 if ((srcColorFactor != srcAlphaFactor) || (dstColorFactor != dstAlphaFactor) || (colorOperation != alphaOperation)) {
411 return false;
412 }
413 }
414 return true;
415}
416
417static bool D3D_CreateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, DWORD usage, Uint32 format, D3DFORMAT d3dfmt, int w, int h)
418{
419 HRESULT result;
420
421 texture->dirty = false;
422 texture->w = w;
423 texture->h = h;
424 texture->usage = usage;
425 texture->format = format;
426 texture->d3dfmt = d3dfmt;
427
428 result = IDirect3DDevice9_CreateTexture(device, w, h, 1, usage,
429 PixelFormatToD3DFMT(format),
430 D3DPOOL_DEFAULT, &texture->texture, NULL);
431 if (FAILED(result)) {
432 return D3D_SetError("CreateTexture(D3DPOOL_DEFAULT)", result);
433 }
434 return true;
435}
436
437static bool D3D_CreateStagingTexture(IDirect3DDevice9 *device, D3D_TextureRep *texture)
438{
439 HRESULT result;
440
441 if (!texture->staging) {
442 result = IDirect3DDevice9_CreateTexture(device, texture->w, texture->h, 1, 0,
443 texture->d3dfmt, D3DPOOL_SYSTEMMEM, &texture->staging, NULL);
444 if (FAILED(result)) {
445 return D3D_SetError("CreateTexture(D3DPOOL_SYSTEMMEM)", result);
446 }
447 }
448 return true;
449}
450
451static bool D3D_RecreateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture)
452{
453 if (texture->texture) {
454 IDirect3DTexture9_Release(texture->texture);
455 texture->texture = NULL;
456 }
457 if (texture->staging) {
458 IDirect3DTexture9_AddDirtyRect(texture->staging, NULL);
459 texture->dirty = true;
460 }
461 return true;
462}
463
464static bool D3D_UpdateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, int x, int y, int w, int h, const void *pixels, int pitch)
465{
466 RECT d3drect;
467 D3DLOCKED_RECT locked;
468 const Uint8 *src;
469 Uint8 *dst;
470 int row, length;
471 HRESULT result;
472
473 if (!D3D_CreateStagingTexture(device, texture)) {
474 return false;
475 }
476
477 d3drect.left = x;
478 d3drect.right = (LONG)x + w;
479 d3drect.top = y;
480 d3drect.bottom = (LONG)y + h;
481
482 result = IDirect3DTexture9_LockRect(texture->staging, 0, &locked, &d3drect, 0);
483 if (FAILED(result)) {
484 return D3D_SetError("LockRect()", result);
485 }
486
487 src = (const Uint8 *)pixels;
488 dst = (Uint8 *)locked.pBits;
489 length = w * SDL_BYTESPERPIXEL(texture->format);
490 if (length == pitch && length == locked.Pitch) {
491 SDL_memcpy(dst, src, (size_t)length * h);
492 } else {
493 if (length > pitch) {
494 length = pitch;
495 }
496 if (length > locked.Pitch) {
497 length = locked.Pitch;
498 }
499 for (row = 0; row < h; ++row) {
500 SDL_memcpy(dst, src, length);
501 src += pitch;
502 dst += locked.Pitch;
503 }
504 }
505 result = IDirect3DTexture9_UnlockRect(texture->staging, 0);
506 if (FAILED(result)) {
507 return D3D_SetError("UnlockRect()", result);
508 }
509 texture->dirty = true;
510
511 return true;
512}
513
514static void D3D_DestroyTextureRep(D3D_TextureRep *texture)
515{
516 if (texture->texture) {
517 IDirect3DTexture9_Release(texture->texture);
518 texture->texture = NULL;
519 }
520 if (texture->staging) {
521 IDirect3DTexture9_Release(texture->staging);
522 texture->staging = NULL;
523 }
524}
525
526static bool D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
527{
528 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
529 D3D_TextureData *texturedata;
530 DWORD usage;
531
532 texturedata = (D3D_TextureData *)SDL_calloc(1, sizeof(*texturedata));
533 if (!texturedata) {
534 return false;
535 }
536 texturedata->scaleMode = (texture->scaleMode == SDL_SCALEMODE_NEAREST) ? D3DTEXF_POINT : D3DTEXF_LINEAR;
537
538 texture->internal = texturedata;
539
540 if (texture->access == SDL_TEXTUREACCESS_TARGET) {
541 usage = D3DUSAGE_RENDERTARGET;
542 } else {
543 usage = 0;
544 }
545
546 if (!D3D_CreateTextureRep(data->device, &texturedata->texture, usage, texture->format, PixelFormatToD3DFMT(texture->format), texture->w, texture->h)) {
547 return false;
548 }
549#ifdef SDL_HAVE_YUV
550 if (texture->format == SDL_PIXELFORMAT_YV12 ||
551 texture->format == SDL_PIXELFORMAT_IYUV) {
552 texturedata->yuv = true;
553
554 if (!D3D_CreateTextureRep(data->device, &texturedata->utexture, usage, texture->format, PixelFormatToD3DFMT(texture->format), (texture->w + 1) / 2, (texture->h + 1) / 2)) {
555 return false;
556 }
557
558 if (!D3D_CreateTextureRep(data->device, &texturedata->vtexture, usage, texture->format, PixelFormatToD3DFMT(texture->format), (texture->w + 1) / 2, (texture->h + 1) / 2)) {
559 return false;
560 }
561
562 texturedata->shader = SHADER_YUV;
563 texturedata->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8);
564 if (texturedata->shader_params == NULL) {
565 return SDL_SetError("Unsupported YUV colorspace");
566 }
567 }
568#endif
569 return true;
570}
571
572static bool D3D_RecreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
573{
574 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
575 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
576
577 if (!texturedata) {
578 return true;
579 }
580
581 if (!D3D_RecreateTextureRep(data->device, &texturedata->texture)) {
582 return false;
583 }
584#ifdef SDL_HAVE_YUV
585 if (texturedata->yuv) {
586 if (!D3D_RecreateTextureRep(data->device, &texturedata->utexture)) {
587 return false;
588 }
589
590 if (!D3D_RecreateTextureRep(data->device, &texturedata->vtexture)) {
591 return false;
592 }
593 }
594#endif
595 return true;
596}
597
598static bool D3D_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
599 const SDL_Rect *rect, const void *pixels, int pitch)
600{
601 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
602 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
603
604 if (!texturedata) {
605 return SDL_SetError("Texture is not currently available");
606 }
607
608 if (!D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, pixels, pitch)) {
609 return false;
610 }
611#ifdef SDL_HAVE_YUV
612 if (texturedata->yuv) {
613 // Skip to the correct offset into the next texture
614 pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
615
616 if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->vtexture : &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) {
617 return false;
618 }
619
620 // Skip to the correct offset into the next texture
621 pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2));
622 if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->utexture : &texturedata->vtexture, rect->x / 2, (rect->y + 1) / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) {
623 return false;
624 }
625 }
626#endif
627 return true;
628}
629
630#ifdef SDL_HAVE_YUV
631static bool D3D_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
632 const SDL_Rect *rect,
633 const Uint8 *Yplane, int Ypitch,
634 const Uint8 *Uplane, int Upitch,
635 const Uint8 *Vplane, int Vpitch)
636{
637 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
638 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
639
640 if (!texturedata) {
641 return SDL_SetError("Texture is not currently available");
642 }
643
644 if (!D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch)) {
645 return false;
646 }
647 if (!D3D_UpdateTextureRep(data->device, &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch)) {
648 return false;
649 }
650 if (!D3D_UpdateTextureRep(data->device, &texturedata->vtexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch)) {
651 return false;
652 }
653 return true;
654}
655#endif
656
657static bool D3D_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture,
658 const SDL_Rect *rect, void **pixels, int *pitch)
659{
660 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
661 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
662 IDirect3DDevice9 *device = data->device;
663
664 if (!texturedata) {
665 return SDL_SetError("Texture is not currently available");
666 }
667#ifdef SDL_HAVE_YUV
668 texturedata->locked_rect = *rect;
669
670 if (texturedata->yuv) {
671 // It's more efficient to upload directly...
672 if (!texturedata->pixels) {
673 texturedata->pitch = texture->w;
674 texturedata->pixels = (Uint8 *)SDL_malloc((texture->h * texturedata->pitch * 3) / 2);
675 if (!texturedata->pixels) {
676 return false;
677 }
678 }
679 *pixels =
680 (void *)(texturedata->pixels + rect->y * texturedata->pitch +
681 rect->x * SDL_BYTESPERPIXEL(texture->format));
682 *pitch = texturedata->pitch;
683 } else
684#endif
685 {
686 RECT d3drect;
687 D3DLOCKED_RECT locked;
688 HRESULT result;
689
690 if (!D3D_CreateStagingTexture(device, &texturedata->texture)) {
691 return false;
692 }
693
694 d3drect.left = rect->x;
695 d3drect.right = (LONG)rect->x + rect->w;
696 d3drect.top = rect->y;
697 d3drect.bottom = (LONG)rect->y + rect->h;
698
699 result = IDirect3DTexture9_LockRect(texturedata->texture.staging, 0, &locked, &d3drect, 0);
700 if (FAILED(result)) {
701 return D3D_SetError("LockRect()", result);
702 }
703 *pixels = locked.pBits;
704 *pitch = locked.Pitch;
705 }
706 return true;
707}
708
709static void D3D_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
710{
711 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
712 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
713
714 if (!texturedata) {
715 return;
716 }
717#ifdef SDL_HAVE_YUV
718 if (texturedata->yuv) {
719 const SDL_Rect *rect = &texturedata->locked_rect;
720 void *pixels =
721 (void *)(texturedata->pixels + rect->y * texturedata->pitch +
722 rect->x * SDL_BYTESPERPIXEL(texture->format));
723 D3D_UpdateTexture(renderer, texture, rect, pixels, texturedata->pitch);
724 } else
725#endif
726 {
727 IDirect3DTexture9_UnlockRect(texturedata->texture.staging, 0);
728 texturedata->texture.dirty = true;
729 if (data->drawstate.texture == texture) {
730 data->drawstate.texture = NULL;
731 data->drawstate.shader = SHADER_NONE;
732 data->drawstate.shader_params = NULL;
733 IDirect3DDevice9_SetPixelShader(data->device, NULL);
734 IDirect3DDevice9_SetTexture(data->device, 0, NULL);
735 }
736 }
737}
738
739static void D3D_SetTextureScaleMode(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ScaleMode scaleMode)
740{
741 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
742
743 if (!texturedata) {
744 return;
745 }
746
747 texturedata->scaleMode = (scaleMode == SDL_SCALEMODE_NEAREST) ? D3DTEXF_POINT : D3DTEXF_LINEAR;
748}
749
750static bool D3D_SetRenderTargetInternal(SDL_Renderer *renderer, SDL_Texture *texture)
751{
752 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
753 D3D_TextureData *texturedata;
754 D3D_TextureRep *texturerep;
755 HRESULT result;
756 IDirect3DDevice9 *device = data->device;
757
758 // Release the previous render target if it wasn't the default one
759 if (data->currentRenderTarget) {
760 IDirect3DSurface9_Release(data->currentRenderTarget);
761 data->currentRenderTarget = NULL;
762 }
763
764 if (!texture) {
765 IDirect3DDevice9_SetRenderTarget(data->device, 0, data->defaultRenderTarget);
766 return true;
767 }
768
769 texturedata = (D3D_TextureData *)texture->internal;
770 if (!texturedata) {
771 return SDL_SetError("Texture is not currently available");
772 }
773
774 // Make sure the render target is updated if it was locked and written to
775 texturerep = &texturedata->texture;
776 if (texturerep->dirty && texturerep->staging) {
777 if (!texturerep->texture) {
778 result = IDirect3DDevice9_CreateTexture(device, texturerep->w, texturerep->h, 1, texturerep->usage,
779 PixelFormatToD3DFMT(texturerep->format), D3DPOOL_DEFAULT, &texturerep->texture, NULL);
780 if (FAILED(result)) {
781 return D3D_SetError("CreateTexture(D3DPOOL_DEFAULT)", result);
782 }
783 }
784
785 result = IDirect3DDevice9_UpdateTexture(device, (IDirect3DBaseTexture9 *)texturerep->staging, (IDirect3DBaseTexture9 *)texturerep->texture);
786 if (FAILED(result)) {
787 return D3D_SetError("UpdateTexture()", result);
788 }
789 texturerep->dirty = false;
790 }
791
792 result = IDirect3DTexture9_GetSurfaceLevel(texturedata->texture.texture, 0, &data->currentRenderTarget);
793 if (FAILED(result)) {
794 return D3D_SetError("GetSurfaceLevel()", result);
795 }
796 result = IDirect3DDevice9_SetRenderTarget(data->device, 0, data->currentRenderTarget);
797 if (FAILED(result)) {
798 return D3D_SetError("SetRenderTarget()", result);
799 }
800
801 return true;
802}
803
804static bool D3D_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
805{
806 if (!D3D_ActivateRenderer(renderer)) {
807 return false;
808 }
809
810 return D3D_SetRenderTargetInternal(renderer, texture);
811}
812
813static bool D3D_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
814{
815 return true; // nothing to do in this backend.
816}
817
818static bool D3D_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count)
819{
820 const DWORD color = D3DCOLOR_COLORVALUE(cmd->data.draw.color.r * cmd->data.draw.color_scale,
821 cmd->data.draw.color.g * cmd->data.draw.color_scale,
822 cmd->data.draw.color.b * cmd->data.draw.color_scale,
823 cmd->data.draw.color.a);
824 const size_t vertslen = count * sizeof(Vertex);
825 Vertex *verts = (Vertex *)SDL_AllocateRenderVertices(renderer, vertslen, 0, &cmd->data.draw.first);
826 int i;
827
828 if (!verts) {
829 return false;
830 }
831
832 SDL_memset(verts, '\0', vertslen);
833 cmd->data.draw.count = count;
834
835 for (i = 0; i < count; i++, verts++, points++) {
836 verts->x = points->x;
837 verts->y = points->y;
838 verts->color = color;
839 }
840
841 return true;
842}
843
844static bool D3D_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
845 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride,
846 int num_vertices, const void *indices, int num_indices, int size_indices,
847 float scale_x, float scale_y)
848{
849 int i;
850 int count = indices ? num_indices : num_vertices;
851 Vertex *verts = (Vertex *)SDL_AllocateRenderVertices(renderer, count * sizeof(Vertex), 0, &cmd->data.draw.first);
852 const float color_scale = cmd->data.draw.color_scale;
853
854 if (!verts) {
855 return false;
856 }
857
858 cmd->data.draw.count = count;
859 size_indices = indices ? size_indices : 0;
860
861 for (i = 0; i < count; i++) {
862 int j;
863 float *xy_;
864 SDL_FColor *col_;
865 if (size_indices == 4) {
866 j = ((const Uint32 *)indices)[i];
867 } else if (size_indices == 2) {
868 j = ((const Uint16 *)indices)[i];
869 } else if (size_indices == 1) {
870 j = ((const Uint8 *)indices)[i];
871 } else {
872 j = i;
873 }
874
875 xy_ = (float *)((char *)xy + j * xy_stride);
876 col_ = (SDL_FColor *)((char *)color + j * color_stride);
877
878 verts->x = xy_[0] * scale_x - 0.5f;
879 verts->y = xy_[1] * scale_y - 0.5f;
880 verts->z = 0.0f;
881 verts->color = D3DCOLOR_COLORVALUE(col_->r * color_scale, col_->g * color_scale, col_->b * color_scale, col_->a);
882
883 if (texture) {
884 float *uv_ = (float *)((char *)uv + j * uv_stride);
885 verts->u = uv_[0];
886 verts->v = uv_[1];
887 } else {
888 verts->u = 0.0f;
889 verts->v = 0.0f;
890 }
891
892 verts += 1;
893 }
894 return true;
895}
896
897static bool UpdateDirtyTexture(IDirect3DDevice9 *device, D3D_TextureRep *texture)
898{
899 if (texture->dirty && texture->staging) {
900 HRESULT result;
901 if (!texture->texture) {
902 result = IDirect3DDevice9_CreateTexture(device, texture->w, texture->h, 1, texture->usage,
903 PixelFormatToD3DFMT(texture->format), D3DPOOL_DEFAULT, &texture->texture, NULL);
904 if (FAILED(result)) {
905 return D3D_SetError("CreateTexture(D3DPOOL_DEFAULT)", result);
906 }
907 }
908
909 result = IDirect3DDevice9_UpdateTexture(device, (IDirect3DBaseTexture9 *)texture->staging, (IDirect3DBaseTexture9 *)texture->texture);
910 if (FAILED(result)) {
911 return D3D_SetError("UpdateTexture()", result);
912 }
913 texture->dirty = false;
914 }
915 return true;
916}
917
918static bool BindTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, DWORD sampler)
919{
920 HRESULT result;
921 UpdateDirtyTexture(device, texture);
922 result = IDirect3DDevice9_SetTexture(device, sampler, (IDirect3DBaseTexture9 *)texture->texture);
923 if (FAILED(result)) {
924 return D3D_SetError("SetTexture()", result);
925 }
926 return true;
927}
928
929static void UpdateTextureScaleMode(D3D_RenderData *data, D3D_TextureData *texturedata, unsigned index)
930{
931 if (texturedata->scaleMode != data->scaleMode[index]) {
932 IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_MINFILTER, texturedata->scaleMode);
933 IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_MAGFILTER, texturedata->scaleMode);
934 data->scaleMode[index] = texturedata->scaleMode;
935 }
936}
937
938static void UpdateTextureAddressMode(D3D_RenderData *data, SDL_TextureAddressMode addressMode, unsigned index)
939{
940 if (addressMode != data->addressMode[index]) {
941 switch (addressMode) {
942 case SDL_TEXTURE_ADDRESS_CLAMP:
943 IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
944 IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
945 break;
946 case SDL_TEXTURE_ADDRESS_WRAP:
947 IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
948 IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
949 break;
950 default:
951 break;
952 }
953 data->addressMode[index] = addressMode;
954 }
955}
956
957static bool SetupTextureState(D3D_RenderData *data, SDL_Texture *texture, SDL_TextureAddressMode addressMode, D3D9_Shader *shader, const float **shader_params)
958{
959 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
960
961 if (!texturedata) {
962 return SDL_SetError("Texture is not currently available");
963 }
964
965 UpdateTextureScaleMode(data, texturedata, 0);
966 UpdateTextureAddressMode(data, addressMode, 0);
967
968 *shader = texturedata->shader;
969 *shader_params = texturedata->shader_params;
970
971 if (!BindTextureRep(data->device, &texturedata->texture, 0)) {
972 return false;
973 }
974#ifdef SDL_HAVE_YUV
975 if (texturedata->yuv) {
976 UpdateTextureScaleMode(data, texturedata, 1);
977 UpdateTextureScaleMode(data, texturedata, 2);
978 UpdateTextureAddressMode(data, addressMode, 1);
979 UpdateTextureAddressMode(data, addressMode, 2);
980
981 if (!BindTextureRep(data->device, &texturedata->utexture, 1)) {
982 return false;
983 }
984 if (!BindTextureRep(data->device, &texturedata->vtexture, 2)) {
985 return false;
986 }
987 }
988#endif
989 return true;
990}
991
992static bool SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd)
993{
994 SDL_Texture *texture = cmd->data.draw.texture;
995 const SDL_BlendMode blend = cmd->data.draw.blend;
996
997 if (texture != data->drawstate.texture) {
998#ifdef SDL_HAVE_YUV
999 D3D_TextureData *oldtexturedata = data->drawstate.texture ? (D3D_TextureData *)data->drawstate.texture->internal : NULL;
1000 D3D_TextureData *newtexturedata = texture ? (D3D_TextureData *)texture->internal : NULL;
1001#endif
1002 D3D9_Shader shader = SHADER_NONE;
1003 const float *shader_params = NULL;
1004
1005 // disable any enabled textures we aren't going to use, let SetupTextureState() do the rest.
1006 if (!texture) {
1007 IDirect3DDevice9_SetTexture(data->device, 0, NULL);
1008 }
1009#ifdef SDL_HAVE_YUV
1010 if ((!newtexturedata || !newtexturedata->yuv) && (oldtexturedata && oldtexturedata->yuv)) {
1011 IDirect3DDevice9_SetTexture(data->device, 1, NULL);
1012 IDirect3DDevice9_SetTexture(data->device, 2, NULL);
1013 }
1014#endif
1015 if (texture && !SetupTextureState(data, texture, cmd->data.draw.texture_address_mode, &shader, &shader_params)) {
1016 return false;
1017 }
1018
1019#ifdef SDL_HAVE_YUV
1020 if (shader != data->drawstate.shader) {
1021 const HRESULT result = IDirect3DDevice9_SetPixelShader(data->device, data->shaders[shader]);
1022 if (FAILED(result)) {
1023 return D3D_SetError("IDirect3DDevice9_SetPixelShader()", result);
1024 }
1025 data->drawstate.shader = shader;
1026 }
1027
1028 if (shader_params != data->drawstate.shader_params) {
1029 if (shader_params) {
1030 const UINT shader_params_length = 4; // The YUV shader takes 4 float4 parameters
1031 const HRESULT result = IDirect3DDevice9_SetPixelShaderConstantF(data->device, 0, shader_params, shader_params_length);
1032 if (FAILED(result)) {
1033 return D3D_SetError("IDirect3DDevice9_SetPixelShaderConstantF()", result);
1034 }
1035 }
1036 data->drawstate.shader_params = shader_params;
1037 }
1038#endif // SDL_HAVE_YUV
1039
1040 data->drawstate.texture = texture;
1041 } else if (texture) {
1042 D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
1043 UpdateDirtyTexture(data->device, &texturedata->texture);
1044#ifdef SDL_HAVE_YUV
1045 if (texturedata->yuv) {
1046 UpdateDirtyTexture(data->device, &texturedata->utexture);
1047 UpdateDirtyTexture(data->device, &texturedata->vtexture);
1048 }
1049#endif
1050 }
1051
1052 if (blend != data->drawstate.blend) {
1053 if (blend == SDL_BLENDMODE_NONE) {
1054 IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE, FALSE);
1055 } else {
1056 IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE, TRUE);
1057 IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND,
1058 GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)));
1059 IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
1060 GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend)));
1061 IDirect3DDevice9_SetRenderState(data->device, D3DRS_BLENDOP,
1062 GetBlendEquation(SDL_GetBlendModeColorOperation(blend)));
1063 if (data->enableSeparateAlphaBlend) {
1064 IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLENDALPHA,
1065 GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend)));
1066 IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLENDALPHA,
1067 GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend)));
1068 IDirect3DDevice9_SetRenderState(data->device, D3DRS_BLENDOPALPHA,
1069 GetBlendEquation(SDL_GetBlendModeAlphaOperation(blend)));
1070 }
1071 }
1072
1073 data->drawstate.blend = blend;
1074 }
1075
1076 if (data->drawstate.viewport_dirty) {
1077 const SDL_Rect *viewport = &data->drawstate.viewport;
1078 D3DVIEWPORT9 d3dviewport;
1079 d3dviewport.X = viewport->x;
1080 d3dviewport.Y = viewport->y;
1081 d3dviewport.Width = viewport->w;
1082 d3dviewport.Height = viewport->h;
1083 d3dviewport.MinZ = 0.0f;
1084 d3dviewport.MaxZ = 1.0f;
1085 IDirect3DDevice9_SetViewport(data->device, &d3dviewport);
1086
1087 // Set an orthographic projection matrix
1088 if (viewport->w && viewport->h) {
1089 D3DMATRIX d3dmatrix;
1090 SDL_zero(d3dmatrix);
1091 d3dmatrix.m[0][0] = 2.0f / viewport->w;
1092 d3dmatrix.m[1][1] = -2.0f / viewport->h;
1093 d3dmatrix.m[2][2] = 1.0f;
1094 d3dmatrix.m[3][0] = -1.0f;
1095 d3dmatrix.m[3][1] = 1.0f;
1096 d3dmatrix.m[3][3] = 1.0f;
1097 IDirect3DDevice9_SetTransform(data->device, D3DTS_PROJECTION, &d3dmatrix);
1098 }
1099
1100 data->drawstate.viewport_dirty = false;
1101 }
1102
1103 if (data->drawstate.cliprect_enabled_dirty) {
1104 IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, data->drawstate.cliprect_enabled ? TRUE : FALSE);
1105 data->drawstate.cliprect_enabled_dirty = false;
1106 }
1107
1108 if (data->drawstate.cliprect_dirty) {
1109 const SDL_Rect *viewport = &data->drawstate.viewport;
1110 const SDL_Rect *rect = &data->drawstate.cliprect;
1111 RECT d3drect;
1112 d3drect.left = (LONG)viewport->x + rect->x;
1113 d3drect.top = (LONG)viewport->y + rect->y;
1114 d3drect.right = (LONG)viewport->x + rect->x + rect->w;
1115 d3drect.bottom = (LONG)viewport->y + rect->y + rect->h;
1116 IDirect3DDevice9_SetScissorRect(data->device, &d3drect);
1117 data->drawstate.cliprect_dirty = false;
1118 }
1119
1120 return true;
1121}
1122
1123static void D3D_InvalidateCachedState(SDL_Renderer *renderer)
1124{
1125 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
1126 data->drawstate.viewport_dirty = true;
1127 data->drawstate.cliprect_enabled_dirty = true;
1128 data->drawstate.cliprect_dirty = true;
1129 data->drawstate.blend = SDL_BLENDMODE_INVALID;
1130 data->drawstate.texture = NULL;
1131 data->drawstate.shader = SHADER_NONE;
1132 data->drawstate.shader_params = NULL;
1133}
1134
1135static bool D3D_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
1136{
1137 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
1138 const int vboidx = data->currentVertexBuffer;
1139 IDirect3DVertexBuffer9 *vbo = NULL;
1140 const bool istarget = renderer->target != NULL;
1141
1142 if (!D3D_ActivateRenderer(renderer)) {
1143 return false;
1144 }
1145
1146 if (vertsize > 0) {
1147 // upload the new VBO data for this set of commands.
1148 vbo = data->vertexBuffers[vboidx];
1149 if (data->vertexBufferSize[vboidx] < vertsize) {
1150 const DWORD usage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY;
1151 const DWORD fvf = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1;
1152 if (vbo) {
1153 IDirect3DVertexBuffer9_Release(vbo);
1154 }
1155
1156 if (FAILED(IDirect3DDevice9_CreateVertexBuffer(data->device, (UINT)vertsize, usage, fvf, D3DPOOL_DEFAULT, &vbo, NULL))) {
1157 vbo = NULL;
1158 }
1159 data->vertexBuffers[vboidx] = vbo;
1160 data->vertexBufferSize[vboidx] = vbo ? vertsize : 0;
1161 }
1162
1163 if (vbo) {
1164 void *ptr;
1165 if (FAILED(IDirect3DVertexBuffer9_Lock(vbo, 0, (UINT)vertsize, &ptr, D3DLOCK_DISCARD))) {
1166 vbo = NULL; // oh well, we'll do immediate mode drawing. :(
1167 } else {
1168 SDL_memcpy(ptr, vertices, vertsize);
1169 if (FAILED(IDirect3DVertexBuffer9_Unlock(vbo))) {
1170 vbo = NULL; // oh well, we'll do immediate mode drawing. :(
1171 }
1172 }
1173 }
1174
1175 // cycle through a few VBOs so D3D has some time with the data before we replace it.
1176 if (vbo) {
1177 data->currentVertexBuffer++;
1178 if (data->currentVertexBuffer >= SDL_arraysize(data->vertexBuffers)) {
1179 data->currentVertexBuffer = 0;
1180 }
1181 } else if (!data->reportedVboProblem) {
1182 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "SDL failed to get a vertex buffer for this Direct3D 9 rendering batch!");
1183 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Dropping back to a slower method.");
1184 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "This might be a brief hiccup, but if performance is bad, this is probably why.");
1185 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "This error will not be logged again for this renderer.");
1186 data->reportedVboProblem = true;
1187 }
1188 }
1189
1190 IDirect3DDevice9_SetStreamSource(data->device, 0, vbo, 0, sizeof(Vertex));
1191
1192 while (cmd) {
1193 switch (cmd->command) {
1194 case SDL_RENDERCMD_SETDRAWCOLOR:
1195 {
1196 /* currently this is sent with each vertex, but if we move to
1197 shaders, we can put this in a uniform here and reduce vertex
1198 buffer bandwidth */
1199 break;
1200 }
1201
1202 case SDL_RENDERCMD_SETVIEWPORT:
1203 {
1204 SDL_Rect *viewport = &data->drawstate.viewport;
1205 if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) {
1206 SDL_copyp(viewport, &cmd->data.viewport.rect);
1207 data->drawstate.viewport_dirty = true;
1208 data->drawstate.cliprect_dirty = true;
1209 }
1210 break;
1211 }
1212
1213 case SDL_RENDERCMD_SETCLIPRECT:
1214 {
1215 const SDL_Rect *rect = &cmd->data.cliprect.rect;
1216 if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) {
1217 data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled;
1218 data->drawstate.cliprect_enabled_dirty = true;
1219 }
1220
1221 if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof(*rect)) != 0) {
1222 SDL_copyp(&data->drawstate.cliprect, rect);
1223 data->drawstate.cliprect_dirty = true;
1224 }
1225 break;
1226 }
1227
1228 case SDL_RENDERCMD_CLEAR:
1229 {
1230 const DWORD color = D3DCOLOR_COLORVALUE(cmd->data.color.color.r * cmd->data.color.color_scale,
1231 cmd->data.color.color.g * cmd->data.color.color_scale,
1232 cmd->data.color.color.b * cmd->data.color.color_scale,
1233 cmd->data.color.color.a);
1234 const SDL_Rect *viewport = &data->drawstate.viewport;
1235 const int backw = istarget ? renderer->target->w : data->pparams.BackBufferWidth;
1236 const int backh = istarget ? renderer->target->h : data->pparams.BackBufferHeight;
1237 const bool viewport_equal = ((viewport->x == 0) && (viewport->y == 0) && (viewport->w == backw) && (viewport->h == backh));
1238
1239 if (data->drawstate.cliprect_enabled || data->drawstate.cliprect_enabled_dirty) {
1240 IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, FALSE);
1241 data->drawstate.cliprect_enabled_dirty = data->drawstate.cliprect_enabled;
1242 }
1243
1244 // Don't reset the viewport if we don't have to!
1245 if (!data->drawstate.viewport_dirty && viewport_equal) {
1246 IDirect3DDevice9_Clear(data->device, 0, NULL, D3DCLEAR_TARGET, color, 0.0f, 0);
1247 } else {
1248 // Clear is defined to clear the entire render target
1249 D3DVIEWPORT9 wholeviewport = { 0, 0, 0, 0, 0.0f, 1.0f };
1250 wholeviewport.Width = backw;
1251 wholeviewport.Height = backh;
1252 IDirect3DDevice9_SetViewport(data->device, &wholeviewport);
1253 data->drawstate.viewport_dirty = true; // we still need to (re)set orthographic projection, so always mark it dirty.
1254 IDirect3DDevice9_Clear(data->device, 0, NULL, D3DCLEAR_TARGET, color, 0.0f, 0);
1255 }
1256
1257 break;
1258 }
1259
1260 case SDL_RENDERCMD_DRAW_POINTS:
1261 {
1262 const size_t count = cmd->data.draw.count;
1263 const size_t first = cmd->data.draw.first;
1264 SetDrawState(data, cmd);
1265 if (vbo) {
1266 IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, (UINT)(first / sizeof(Vertex)), (UINT)count);
1267 } else {
1268 const Vertex *verts = (Vertex *)(((Uint8 *)vertices) + first);
1269 IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, (UINT)count, verts, sizeof(Vertex));
1270 }
1271 break;
1272 }
1273
1274 case SDL_RENDERCMD_DRAW_LINES:
1275 {
1276 const size_t count = cmd->data.draw.count;
1277 const size_t first = cmd->data.draw.first;
1278 const Vertex *verts = (Vertex *)(((Uint8 *)vertices) + first);
1279
1280 /* DirectX 9 has the same line rasterization semantics as GDI,
1281 so we need to close the endpoint of the line with a second draw call.
1282 NOLINTNEXTLINE(clang-analyzer-core.NullDereference): FIXME: Can verts truly not be NULL ? */
1283 const bool close_endpoint = ((count == 2) || (verts[0].x != verts[count - 1].x) || (verts[0].y != verts[count - 1].y));
1284
1285 SetDrawState(data, cmd);
1286
1287 if (vbo) {
1288 IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_LINESTRIP, (UINT)(first / sizeof(Vertex)), (UINT)(count - 1));
1289 if (close_endpoint) {
1290 IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, (UINT)((first / sizeof(Vertex)) + (count - 1)), 1);
1291 }
1292 } else {
1293 IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_LINESTRIP, (UINT)(count - 1), verts, sizeof(Vertex));
1294 if (close_endpoint) {
1295 IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, 1, &verts[count - 1], sizeof(Vertex));
1296 }
1297 }
1298 break;
1299 }
1300
1301 case SDL_RENDERCMD_FILL_RECTS: // unused
1302 break;
1303
1304 case SDL_RENDERCMD_COPY: // unused
1305 break;
1306
1307 case SDL_RENDERCMD_COPY_EX: // unused
1308 break;
1309
1310 case SDL_RENDERCMD_GEOMETRY:
1311 {
1312 const size_t count = cmd->data.draw.count;
1313 const size_t first = cmd->data.draw.first;
1314 SetDrawState(data, cmd);
1315 if (vbo) {
1316 IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLELIST, (UINT)(first / sizeof(Vertex)), (UINT)count / 3);
1317 } else {
1318 const Vertex *verts = (Vertex *)(((Uint8 *)vertices) + first);
1319 IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLELIST, (UINT)count / 3, verts, sizeof(Vertex));
1320 }
1321 break;
1322 }
1323
1324 case SDL_RENDERCMD_NO_OP:
1325 break;
1326 }
1327
1328 cmd = cmd->next;
1329 }
1330
1331 return true;
1332}
1333
1334static SDL_Surface *D3D_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
1335{
1336 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
1337 D3DSURFACE_DESC desc;
1338 LPDIRECT3DSURFACE9 backBuffer;
1339 LPDIRECT3DSURFACE9 surface;
1340 RECT d3drect;
1341 D3DLOCKED_RECT locked;
1342 HRESULT result;
1343 SDL_Surface *output;
1344
1345 if (data->currentRenderTarget) {
1346 backBuffer = data->currentRenderTarget;
1347 } else {
1348 backBuffer = data->defaultRenderTarget;
1349 }
1350
1351 result = IDirect3DSurface9_GetDesc(backBuffer, &desc);
1352 if (FAILED(result)) {
1353 D3D_SetError("GetDesc()", result);
1354 return NULL;
1355 }
1356
1357 result = IDirect3DDevice9_CreateOffscreenPlainSurface(data->device, desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &surface, NULL);
1358 if (FAILED(result)) {
1359 D3D_SetError("CreateOffscreenPlainSurface()", result);
1360 return NULL;
1361 }
1362
1363 result = IDirect3DDevice9_GetRenderTargetData(data->device, backBuffer, surface);
1364 if (FAILED(result)) {
1365 IDirect3DSurface9_Release(surface);
1366 D3D_SetError("GetRenderTargetData()", result);
1367 return NULL;
1368 }
1369
1370 d3drect.left = rect->x;
1371 d3drect.right = (LONG)rect->x + rect->w;
1372 d3drect.top = rect->y;
1373 d3drect.bottom = (LONG)rect->y + rect->h;
1374
1375 result = IDirect3DSurface9_LockRect(surface, &locked, &d3drect, D3DLOCK_READONLY);
1376 if (FAILED(result)) {
1377 IDirect3DSurface9_Release(surface);
1378 D3D_SetError("LockRect()", result);
1379 return NULL;
1380 }
1381
1382 output = SDL_DuplicatePixels(rect->w, rect->h, D3DFMTToPixelFormat(desc.Format), SDL_COLORSPACE_SRGB, locked.pBits, locked.Pitch);
1383
1384 IDirect3DSurface9_UnlockRect(surface);
1385
1386 IDirect3DSurface9_Release(surface);
1387
1388 return output;
1389}
1390
1391static bool D3D_RenderPresent(SDL_Renderer *renderer)
1392{
1393 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
1394 HRESULT result;
1395
1396 if (!data->beginScene) {
1397 IDirect3DDevice9_EndScene(data->device);
1398 data->beginScene = true;
1399 }
1400
1401 result = IDirect3DDevice9_TestCooperativeLevel(data->device);
1402 if (result == D3DERR_DEVICELOST) {
1403 // We'll reset later
1404 return false;
1405 }
1406 if (result == D3DERR_DEVICENOTRESET) {
1407 D3D_Reset(renderer);
1408 }
1409 result = IDirect3DDevice9_Present(data->device, NULL, NULL, NULL, NULL);
1410 if (FAILED(result)) {
1411 return D3D_SetError("Present()", result);
1412 }
1413 return true;
1414}
1415
1416static void D3D_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture)
1417{
1418 D3D_RenderData *renderdata = (D3D_RenderData *)renderer->internal;
1419 D3D_TextureData *data = (D3D_TextureData *)texture->internal;
1420
1421 if (renderdata->drawstate.texture == texture) {
1422 renderdata->drawstate.texture = NULL;
1423 renderdata->drawstate.shader = SHADER_NONE;
1424 renderdata->drawstate.shader_params = NULL;
1425 IDirect3DDevice9_SetPixelShader(renderdata->device, NULL);
1426 IDirect3DDevice9_SetTexture(renderdata->device, 0, NULL);
1427#ifdef SDL_HAVE_YUV
1428 if (data->yuv) {
1429 IDirect3DDevice9_SetTexture(renderdata->device, 1, NULL);
1430 IDirect3DDevice9_SetTexture(renderdata->device, 2, NULL);
1431 }
1432#endif
1433 }
1434
1435 if (!data) {
1436 return;
1437 }
1438
1439 D3D_DestroyTextureRep(&data->texture);
1440#ifdef SDL_HAVE_YUV
1441 D3D_DestroyTextureRep(&data->utexture);
1442 D3D_DestroyTextureRep(&data->vtexture);
1443 SDL_free(data->pixels);
1444#endif
1445 SDL_free(data);
1446 texture->internal = NULL;
1447}
1448
1449static void D3D_DestroyRenderer(SDL_Renderer *renderer)
1450{
1451 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
1452
1453 if (data) {
1454 int i;
1455
1456 // Release the render target
1457 if (data->defaultRenderTarget) {
1458 IDirect3DSurface9_Release(data->defaultRenderTarget);
1459 data->defaultRenderTarget = NULL;
1460 }
1461 if (data->currentRenderTarget) {
1462 IDirect3DSurface9_Release(data->currentRenderTarget);
1463 data->currentRenderTarget = NULL;
1464 }
1465#ifdef SDL_HAVE_YUV
1466 for (i = 0; i < SDL_arraysize(data->shaders); ++i) {
1467 if (data->shaders[i]) {
1468 IDirect3DPixelShader9_Release(data->shaders[i]);
1469 data->shaders[i] = NULL;
1470 }
1471 }
1472#endif
1473 // Release all vertex buffers
1474 for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) {
1475 if (data->vertexBuffers[i]) {
1476 IDirect3DVertexBuffer9_Release(data->vertexBuffers[i]);
1477 }
1478 data->vertexBuffers[i] = NULL;
1479 }
1480 if (data->device) {
1481 IDirect3DDevice9_Release(data->device);
1482 data->device = NULL;
1483 }
1484 if (data->d3d) {
1485 IDirect3D9_Release(data->d3d);
1486 SDL_UnloadObject(data->d3dDLL);
1487 }
1488 SDL_free(data);
1489 }
1490}
1491
1492static bool D3D_Reset(SDL_Renderer *renderer)
1493{
1494 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
1495 const Float4X4 d3dmatrix = MatrixIdentity();
1496 HRESULT result;
1497 SDL_Texture *texture;
1498 int i;
1499
1500 // Cancel any scene that we've started
1501 if (!data->beginScene) {
1502 IDirect3DDevice9_EndScene(data->device);
1503 data->beginScene = true;
1504 }
1505
1506 // Release the default render target before reset
1507 if (data->defaultRenderTarget) {
1508 IDirect3DSurface9_Release(data->defaultRenderTarget);
1509 data->defaultRenderTarget = NULL;
1510 }
1511 if (data->currentRenderTarget) {
1512 IDirect3DSurface9_Release(data->currentRenderTarget);
1513 data->currentRenderTarget = NULL;
1514 }
1515
1516 // Release application render targets
1517 for (texture = renderer->textures; texture; texture = texture->next) {
1518 if (texture->access == SDL_TEXTUREACCESS_TARGET) {
1519 D3D_DestroyTexture(renderer, texture);
1520 } else {
1521 D3D_RecreateTexture(renderer, texture);
1522 }
1523 }
1524
1525 // Release all vertex buffers
1526 for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) {
1527 if (data->vertexBuffers[i]) {
1528 IDirect3DVertexBuffer9_Release(data->vertexBuffers[i]);
1529 }
1530 data->vertexBuffers[i] = NULL;
1531 data->vertexBufferSize[i] = 0;
1532 }
1533
1534 result = IDirect3DDevice9_Reset(data->device, &data->pparams);
1535 if (FAILED(result)) {
1536 if (result == D3DERR_DEVICELOST) {
1537 // Don't worry about it, we'll reset later...
1538 return true;
1539 } else {
1540 return D3D_SetError("Reset()", result);
1541 }
1542 }
1543
1544 // Allocate application render targets
1545 for (texture = renderer->textures; texture; texture = texture->next) {
1546 if (texture->access == SDL_TEXTUREACCESS_TARGET) {
1547 D3D_CreateTexture(renderer, texture, 0);
1548 }
1549 }
1550
1551 IDirect3DDevice9_GetRenderTarget(data->device, 0, &data->defaultRenderTarget);
1552 D3D_InitRenderState(data);
1553 D3D_SetRenderTargetInternal(renderer, renderer->target);
1554
1555 D3D_InvalidateCachedState(renderer);
1556
1557 IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX *)&d3dmatrix);
1558
1559 // Let the application know that render targets were reset
1560 {
1561 SDL_Event event;
1562 SDL_zero(event);
1563 event.type = SDL_EVENT_RENDER_TARGETS_RESET;
1564 event.render.windowID = SDL_GetWindowID(SDL_GetRenderWindow(renderer));
1565 SDL_PushEvent(&event);
1566 }
1567
1568 return true;
1569}
1570
1571static bool D3D_SetVSync(SDL_Renderer *renderer, const int vsync)
1572{
1573 D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
1574
1575 DWORD PresentationInterval;
1576 switch (vsync) {
1577 case 0:
1578 PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
1579 break;
1580 case 1:
1581 PresentationInterval = D3DPRESENT_INTERVAL_ONE;
1582 break;
1583 case 2:
1584 PresentationInterval = D3DPRESENT_INTERVAL_TWO;
1585 break;
1586 case 3:
1587 PresentationInterval = D3DPRESENT_INTERVAL_THREE;
1588 break;
1589 case 4:
1590 PresentationInterval = D3DPRESENT_INTERVAL_FOUR;
1591 break;
1592 default:
1593 return SDL_Unsupported();
1594 }
1595
1596 D3DCAPS9 caps;
1597 HRESULT result = IDirect3D9_GetDeviceCaps(data->d3d, data->adapter, D3DDEVTYPE_HAL, &caps);
1598 if (FAILED(result)) {
1599 return D3D_SetError("GetDeviceCaps()", result);
1600 }
1601 if (!(caps.PresentationIntervals & PresentationInterval)) {
1602 return SDL_Unsupported();
1603 }
1604 data->pparams.PresentationInterval = PresentationInterval;
1605
1606 if (!D3D_Reset(renderer)) {
1607 // D3D_Reset will call SDL_SetError()
1608 return false;
1609 }
1610 return true;
1611}
1612
1613static bool D3D_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props)
1614{
1615 D3D_RenderData *data;
1616 HRESULT result;
1617 HWND hwnd;
1618 D3DPRESENT_PARAMETERS pparams;
1619 IDirect3DSwapChain9 *chain;
1620 D3DCAPS9 caps;
1621 DWORD device_flags;
1622 int w, h;
1623 SDL_DisplayID displayID;
1624 const SDL_DisplayMode *fullscreen_mode = NULL;
1625
1626 hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
1627 if (!hwnd) {
1628 return SDL_SetError("Couldn't get window handle");
1629 }
1630
1631 SDL_SetupRendererColorspace(renderer, create_props);
1632
1633 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) {
1634 return SDL_SetError("Unsupported output colorspace");
1635 }
1636
1637 data = (D3D_RenderData *)SDL_calloc(1, sizeof(*data));
1638 if (!data) {
1639 return false;
1640 }
1641
1642 if (!D3D_LoadDLL(&data->d3dDLL, &data->d3d)) {
1643 SDL_free(data);
1644 return SDL_SetError("Unable to create Direct3D interface");
1645 }
1646
1647 renderer->WindowEvent = D3D_WindowEvent;
1648 renderer->SupportsBlendMode = D3D_SupportsBlendMode;
1649 renderer->CreateTexture = D3D_CreateTexture;
1650 renderer->UpdateTexture = D3D_UpdateTexture;
1651#ifdef SDL_HAVE_YUV
1652 renderer->UpdateTextureYUV = D3D_UpdateTextureYUV;
1653#endif
1654 renderer->LockTexture = D3D_LockTexture;
1655 renderer->UnlockTexture = D3D_UnlockTexture;
1656 renderer->SetTextureScaleMode = D3D_SetTextureScaleMode;
1657 renderer->SetRenderTarget = D3D_SetRenderTarget;
1658 renderer->QueueSetViewport = D3D_QueueNoOp;
1659 renderer->QueueSetDrawColor = D3D_QueueNoOp;
1660 renderer->QueueDrawPoints = D3D_QueueDrawPoints;
1661 renderer->QueueDrawLines = D3D_QueueDrawPoints; // lines and points queue vertices the same way.
1662 renderer->QueueGeometry = D3D_QueueGeometry;
1663 renderer->InvalidateCachedState = D3D_InvalidateCachedState;
1664 renderer->RunCommandQueue = D3D_RunCommandQueue;
1665 renderer->RenderReadPixels = D3D_RenderReadPixels;
1666 renderer->RenderPresent = D3D_RenderPresent;
1667 renderer->DestroyTexture = D3D_DestroyTexture;
1668 renderer->DestroyRenderer = D3D_DestroyRenderer;
1669 renderer->SetVSync = D3D_SetVSync;
1670 renderer->internal = data;
1671 D3D_InvalidateCachedState(renderer);
1672
1673 renderer->name = D3D_RenderDriver.name;
1674 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888);
1675
1676 SDL_GetWindowSizeInPixels(window, &w, &h);
1677 if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
1678 fullscreen_mode = SDL_GetWindowFullscreenMode(window);
1679 }
1680
1681 SDL_zero(pparams);
1682 pparams.hDeviceWindow = hwnd;
1683 pparams.BackBufferWidth = w;
1684 pparams.BackBufferHeight = h;
1685 pparams.BackBufferCount = 1;
1686 pparams.SwapEffect = D3DSWAPEFFECT_DISCARD;
1687
1688 if (fullscreen_mode) {
1689 pparams.Windowed = FALSE;
1690 pparams.BackBufferFormat = PixelFormatToD3DFMT(fullscreen_mode->format);
1691 pparams.FullScreen_RefreshRateInHz = (UINT)SDL_ceilf(fullscreen_mode->refresh_rate);
1692 } else {
1693 pparams.Windowed = TRUE;
1694 pparams.BackBufferFormat = D3DFMT_UNKNOWN;
1695 pparams.FullScreen_RefreshRateInHz = 0;
1696 }
1697 pparams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
1698
1699 // Get the adapter for the display that the window is on
1700 displayID = SDL_GetDisplayForWindow(window);
1701 data->adapter = SDL_GetDirect3D9AdapterIndex(displayID);
1702
1703 result = IDirect3D9_GetDeviceCaps(data->d3d, data->adapter, D3DDEVTYPE_HAL, &caps);
1704 if (FAILED(result)) {
1705 return D3D_SetError("GetDeviceCaps()", result);
1706 }
1707
1708 device_flags = D3DCREATE_FPU_PRESERVE;
1709 if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) {
1710 device_flags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
1711 } else {
1712 device_flags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
1713 }
1714
1715 if (SDL_GetHintBoolean(SDL_HINT_RENDER_DIRECT3D_THREADSAFE, false)) {
1716 device_flags |= D3DCREATE_MULTITHREADED;
1717 }
1718
1719 result = IDirect3D9_CreateDevice(data->d3d, data->adapter,
1720 D3DDEVTYPE_HAL,
1721 pparams.hDeviceWindow,
1722 device_flags,
1723 &pparams, &data->device);
1724 if (FAILED(result)) {
1725 return D3D_SetError("CreateDevice()", result);
1726 }
1727
1728 // Get presentation parameters to fill info
1729 result = IDirect3DDevice9_GetSwapChain(data->device, 0, &chain);
1730 if (FAILED(result)) {
1731 return D3D_SetError("GetSwapChain()", result);
1732 }
1733 result = IDirect3DSwapChain9_GetPresentParameters(chain, &pparams);
1734 if (FAILED(result)) {
1735 IDirect3DSwapChain9_Release(chain);
1736 return D3D_SetError("GetPresentParameters()", result);
1737 }
1738 IDirect3DSwapChain9_Release(chain);
1739 data->pparams = pparams;
1740
1741 IDirect3DDevice9_GetDeviceCaps(data->device, &caps);
1742 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, SDL_min(caps.MaxTextureWidth, caps.MaxTextureHeight));
1743
1744 if (caps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND) {
1745 data->enableSeparateAlphaBlend = true;
1746 }
1747
1748 // Store the default render target
1749 IDirect3DDevice9_GetRenderTarget(data->device, 0, &data->defaultRenderTarget);
1750 data->currentRenderTarget = NULL;
1751
1752 // Set up parameters for rendering
1753 D3D_InitRenderState(data);
1754#ifdef SDL_HAVE_YUV
1755 if (caps.MaxSimultaneousTextures >= 3) {
1756 int i;
1757 for (i = SHADER_NONE + 1; i < SDL_arraysize(data->shaders); ++i) {
1758 result = D3D9_CreatePixelShader(data->device, (D3D9_Shader)i, &data->shaders[i]);
1759 if (FAILED(result)) {
1760 D3D_SetError("CreatePixelShader()", result);
1761 }
1762 }
1763 if (data->shaders[SHADER_YUV]) {
1764 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
1765 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
1766 }
1767 }
1768#endif
1769
1770 SDL_SetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_D3D9_DEVICE_POINTER, data->device);
1771
1772 return true;
1773}
1774
1775SDL_RenderDriver D3D_RenderDriver = {
1776 D3D_CreateRenderer, "direct3d"
1777};
1778#endif // SDL_VIDEO_RENDER_D3D
diff --git a/contrib/SDL-3.2.8/src/render/direct3d/SDL_shaders_d3d.c b/contrib/SDL-3.2.8/src/render/direct3d/SDL_shaders_d3d.c
new file mode 100644
index 0000000..9b1b4b5
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/render/direct3d/SDL_shaders_d3d.c
@@ -0,0 +1,48 @@
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_D3D
24
25#include "../../core/windows/SDL_windows.h"
26
27#include <d3d9.h>
28
29#include "SDL_shaders_d3d.h"
30
31// The shaders here were compiled with compile_shaders.bat
32
33#define g_ps20_main D3D9_PixelShader_YUV
34#include "D3D9_PixelShader_YUV.h"
35#undef g_ps20_main
36
37static const BYTE *D3D9_shaders[] = {
38 NULL,
39 D3D9_PixelShader_YUV
40};
41SDL_COMPILE_TIME_ASSERT(D3D9_shaders, SDL_arraysize(D3D9_shaders) == NUM_SHADERS);
42
43HRESULT D3D9_CreatePixelShader(IDirect3DDevice9 *d3dDevice, D3D9_Shader shader, IDirect3DPixelShader9 **pixelShader)
44{
45 return IDirect3DDevice9_CreatePixelShader(d3dDevice, (const DWORD *)D3D9_shaders[shader], pixelShader);
46}
47
48#endif // SDL_VIDEO_RENDER_D3D
diff --git a/contrib/SDL-3.2.8/src/render/direct3d/SDL_shaders_d3d.h b/contrib/SDL-3.2.8/src/render/direct3d/SDL_shaders_d3d.h
new file mode 100644
index 0000000..90ef7a9
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/render/direct3d/SDL_shaders_d3d.h
@@ -0,0 +1,32 @@
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// D3D9 shader implementation
24
25typedef enum
26{
27 SHADER_NONE,
28 SHADER_YUV,
29 NUM_SHADERS
30} D3D9_Shader;
31
32extern HRESULT D3D9_CreatePixelShader(IDirect3DDevice9 *d3dDevice, D3D9_Shader shader, IDirect3DPixelShader9 **pixelShader);
diff --git a/contrib/SDL-3.2.8/src/render/direct3d/compile_shaders.bat b/contrib/SDL-3.2.8/src/render/direct3d/compile_shaders.bat
new file mode 100644
index 0000000..81d513e
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/render/direct3d/compile_shaders.bat
@@ -0,0 +1 @@
fxc /T ps_2_0 /Fh D3D9_PixelShader_YUV.h D3D9_PixelShader_YUV.hlsl