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