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