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