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