src/render/direct3d/SDL_render_d3d.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 21 Jun 2019 15:07:39 -0400
changeset 12900 9d3f245739f8
parent 12898 89b3e1e9839c
permissions -rw-r--r--
direct3d: Use D3DPOOL_DEFAULT for vertex buffers after all, release correctly.

Fixes Bugzilla #4679.
Fixes Bugzilla #4537.
     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     } else if (texture) {
  1131         D3D_TextureData *texturedata = (D3D_TextureData *) texture->driverdata;
  1132         UpdateDirtyTexture(data->device, &texturedata->texture);
  1133         if (texturedata->yuv) {
  1134             UpdateDirtyTexture(data->device, &texturedata->utexture);
  1135             UpdateDirtyTexture(data->device, &texturedata->vtexture);
  1136         }
  1137     }
  1138 
  1139     if (blend != data->drawstate.blend) {
  1140         if (blend == SDL_BLENDMODE_NONE) {
  1141             IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE, FALSE);
  1142         } else {
  1143             IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE, TRUE);
  1144             IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND,
  1145                                             GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)));
  1146             IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
  1147                                             GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend)));
  1148             if (data->enableSeparateAlphaBlend) {
  1149                 IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLENDALPHA,
  1150                                                 GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend)));
  1151                 IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLENDALPHA,
  1152                                                 GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend)));
  1153             }
  1154         }
  1155 
  1156         data->drawstate.blend = blend;
  1157     }
  1158 
  1159     if (is_copy_ex != was_copy_ex) {
  1160         if (!is_copy_ex) {  /* SDL_RENDERCMD_COPY_EX will set this, we only want to reset it here if necessary. */
  1161             const Float4X4 d3dmatrix = MatrixIdentity();
  1162             IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*) &d3dmatrix);
  1163         }
  1164         data->drawstate.is_copy_ex = is_copy_ex;
  1165     }
  1166 
  1167     if (data->drawstate.viewport_dirty) {
  1168         const SDL_Rect *viewport = &data->drawstate.viewport;
  1169         const D3DVIEWPORT9 d3dviewport = { viewport->x, viewport->y, viewport->w, viewport->h, 0.0f, 1.0f };
  1170         IDirect3DDevice9_SetViewport(data->device, &d3dviewport);
  1171 
  1172         /* Set an orthographic projection matrix */
  1173         if (viewport->w && viewport->h) {
  1174             D3DMATRIX d3dmatrix;
  1175             SDL_zero(d3dmatrix);
  1176             d3dmatrix.m[0][0] = 2.0f / viewport->w;
  1177             d3dmatrix.m[1][1] = -2.0f / viewport->h;
  1178             d3dmatrix.m[2][2] = 1.0f;
  1179             d3dmatrix.m[3][0] = -1.0f;
  1180             d3dmatrix.m[3][1] = 1.0f;
  1181             d3dmatrix.m[3][3] = 1.0f;
  1182             IDirect3DDevice9_SetTransform(data->device, D3DTS_PROJECTION, &d3dmatrix);
  1183         }
  1184 
  1185         data->drawstate.viewport_dirty = SDL_FALSE;
  1186     }
  1187 
  1188     if (data->drawstate.cliprect_enabled_dirty) {
  1189         IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, data->drawstate.cliprect_enabled ? TRUE : FALSE);
  1190         data->drawstate.cliprect_enabled_dirty = SDL_FALSE;
  1191     }
  1192 
  1193     if (data->drawstate.cliprect_dirty) {
  1194         const SDL_Rect *viewport = &data->drawstate.viewport;
  1195         const SDL_Rect *rect = &data->drawstate.cliprect;
  1196         const RECT d3drect = { viewport->x + rect->x, viewport->y + rect->y, viewport->x + rect->x + rect->w, viewport->y + rect->y + rect->h };
  1197         IDirect3DDevice9_SetScissorRect(data->device, &d3drect);
  1198         data->drawstate.cliprect_dirty = SDL_FALSE;
  1199     }
  1200 
  1201     return 0;
  1202 }
  1203 
  1204 static int
  1205 D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
  1206 {
  1207     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
  1208     const int vboidx = data->currentVertexBuffer;
  1209     IDirect3DVertexBuffer9 *vbo = NULL;
  1210     const SDL_bool istarget = renderer->target != NULL;
  1211     size_t i;
  1212 
  1213     if (D3D_ActivateRenderer(renderer) < 0) {
  1214         return -1;
  1215     }
  1216 
  1217     /* upload the new VBO data for this set of commands. */
  1218     vbo = data->vertexBuffers[vboidx];
  1219     if (!vbo || (data->vertexBufferSize[vboidx] < vertsize)) {
  1220         const DWORD usage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY;
  1221         const DWORD fvf = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1;
  1222         if (vbo) {
  1223             IDirect3DVertexBuffer9_Release(vbo);
  1224         }
  1225 
  1226         if (FAILED(IDirect3DDevice9_CreateVertexBuffer(data->device, (UINT) vertsize, usage, fvf, D3DPOOL_DEFAULT, &vbo, NULL))) {
  1227             vbo = NULL;
  1228         }
  1229         data->vertexBuffers[vboidx] = vbo;
  1230         data->vertexBufferSize[vboidx] = vbo ? vertsize : 0;
  1231     }
  1232 
  1233     if (vbo) {
  1234         void *ptr;
  1235         if (FAILED(IDirect3DVertexBuffer9_Lock(vbo, 0, (UINT) vertsize, &ptr, D3DLOCK_DISCARD))) {
  1236             vbo = NULL;  /* oh well, we'll do immediate mode drawing.  :(  */
  1237         } else {
  1238             SDL_memcpy(ptr, vertices, vertsize);
  1239             if (FAILED(IDirect3DVertexBuffer9_Unlock(vbo))) {
  1240                 vbo = NULL;  /* oh well, we'll do immediate mode drawing.  :(  */
  1241             }
  1242         }
  1243     }
  1244 
  1245     /* cycle through a few VBOs so D3D has some time with the data before we replace it. */
  1246     if (vbo) {
  1247         data->currentVertexBuffer++;
  1248         if (data->currentVertexBuffer >= SDL_arraysize(data->vertexBuffers)) {
  1249             data->currentVertexBuffer = 0;
  1250         }
  1251     } else if (!data->reportedVboProblem) {
  1252         SDL_LogError(SDL_LOG_CATEGORY_RENDER, "SDL failed to get a vertex buffer for this Direct3D 9 rendering batch!");
  1253         SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Dropping back to a slower method.");
  1254         SDL_LogError(SDL_LOG_CATEGORY_RENDER, "This might be a brief hiccup, but if performance is bad, this is probably why.");
  1255         SDL_LogError(SDL_LOG_CATEGORY_RENDER, "This error will not be logged again for this renderer.");
  1256         data->reportedVboProblem = SDL_TRUE;
  1257     }
  1258 
  1259     IDirect3DDevice9_SetStreamSource(data->device, 0, vbo, 0, sizeof (Vertex));
  1260 
  1261     while (cmd) {
  1262         switch (cmd->command) {
  1263             case SDL_RENDERCMD_SETDRAWCOLOR: {
  1264                 /* currently this is sent with each vertex, but if we move to
  1265                    shaders, we can put this in a uniform here and reduce vertex
  1266                    buffer bandwidth */
  1267                 break;
  1268             }
  1269 
  1270             case SDL_RENDERCMD_SETVIEWPORT: {
  1271                 SDL_Rect *viewport = &data->drawstate.viewport;
  1272                 if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)) != 0) {
  1273                     SDL_memcpy(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect));
  1274                     data->drawstate.viewport_dirty = SDL_TRUE;
  1275                 }
  1276                 break;
  1277             }
  1278 
  1279             case SDL_RENDERCMD_SETCLIPRECT: {
  1280                 const SDL_Rect *rect = &cmd->data.cliprect.rect;
  1281                 if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) {
  1282                     data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled;
  1283                     data->drawstate.cliprect_enabled_dirty = SDL_TRUE;
  1284                 }
  1285 
  1286                 if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) {
  1287                     SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect));
  1288                     data->drawstate.cliprect_dirty = SDL_TRUE;
  1289                 }
  1290                 break;
  1291             }
  1292 
  1293             case SDL_RENDERCMD_CLEAR: {
  1294                 const DWORD color = D3DCOLOR_ARGB(cmd->data.color.a, cmd->data.color.r, cmd->data.color.g, cmd->data.color.b);
  1295                 const SDL_Rect *viewport = &data->drawstate.viewport;
  1296                 const int backw = istarget ? renderer->target->w : data->pparams.BackBufferWidth;
  1297                 const int backh = istarget ? renderer->target->h : data->pparams.BackBufferHeight;
  1298 
  1299                 if (data->drawstate.cliprect_enabled) {
  1300                     IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, FALSE);
  1301                     data->drawstate.cliprect_enabled_dirty = SDL_TRUE;
  1302                 }
  1303 
  1304                 /* Don't reset the viewport if we don't have to! */
  1305                 if (!viewport->x && !viewport->y && (viewport->w == backw) && (viewport->h == backh)) {
  1306                     IDirect3DDevice9_Clear(data->device, 0, NULL, D3DCLEAR_TARGET, color, 0.0f, 0);
  1307                 } else {
  1308                     /* Clear is defined to clear the entire render target */
  1309                     const D3DVIEWPORT9 wholeviewport = { 0, 0, backw, backh, 0.0f, 1.0f };
  1310                     IDirect3DDevice9_SetViewport(data->device, &wholeviewport);
  1311                     data->drawstate.viewport_dirty = SDL_TRUE;
  1312                     IDirect3DDevice9_Clear(data->device, 0, NULL, D3DCLEAR_TARGET, color, 0.0f, 0);
  1313                 }
  1314 
  1315                 break;
  1316             }
  1317 
  1318             case SDL_RENDERCMD_DRAW_POINTS: {
  1319                 const size_t count = cmd->data.draw.count;
  1320                 const size_t first = cmd->data.draw.first;
  1321                 SetDrawState(data, cmd);
  1322                 if (vbo) {
  1323                     IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, (UINT) (first / sizeof (Vertex)), (UINT) count);
  1324                 } else {
  1325                     const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first);
  1326                     IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, (UINT) count, verts, sizeof (Vertex));
  1327                 }
  1328                 break;
  1329             }
  1330 
  1331             case SDL_RENDERCMD_DRAW_LINES: {
  1332                 const size_t count = cmd->data.draw.count;
  1333                 const size_t first = cmd->data.draw.first;
  1334                 const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first);
  1335 
  1336                 /* DirectX 9 has the same line rasterization semantics as GDI,
  1337                    so we need to close the endpoint of the line with a second draw call. */
  1338                 const SDL_bool close_endpoint = ((count == 2) || (verts[0].x != verts[count-1].x) || (verts[0].y != verts[count-1].y));
  1339 
  1340                 SetDrawState(data, cmd);
  1341 
  1342                 if (vbo) {
  1343                     IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_LINESTRIP, (UINT) (first / sizeof (Vertex)), (UINT) (count - 1));
  1344                     if (close_endpoint) {
  1345                         IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, (UINT) ((first / sizeof (Vertex)) + (count - 1)), 1);
  1346                     }
  1347                 } else {
  1348                     IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_LINESTRIP, (UINT) (count - 1), verts, sizeof (Vertex));
  1349                     if (close_endpoint) {
  1350                         IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, 1, &verts[count-1], sizeof (Vertex));
  1351                     }
  1352                 }
  1353                 break;
  1354             }
  1355 
  1356             case SDL_RENDERCMD_FILL_RECTS: {
  1357                 const size_t count = cmd->data.draw.count;
  1358                 const size_t first = cmd->data.draw.first;
  1359                 SetDrawState(data, cmd);
  1360                 if (vbo) {
  1361                     size_t offset = 0;
  1362                     for (i = 0; i < count; ++i, offset += 4) {
  1363                         IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, (UINT) ((first / sizeof (Vertex)) + offset), 2);
  1364                     }
  1365                 } else {
  1366                     const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first);
  1367                     for (i = 0; i < count; ++i, verts += 4) {
  1368                         IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, verts, sizeof (Vertex));
  1369                     }
  1370                 }
  1371                 break;
  1372             }
  1373 
  1374             case SDL_RENDERCMD_COPY: {
  1375                 const size_t count = cmd->data.draw.count;
  1376                 const size_t first = cmd->data.draw.first;
  1377                 SetDrawState(data, cmd);
  1378                 if (vbo) {
  1379                     size_t offset = 0;
  1380                     for (i = 0; i < count; ++i, offset += 4) {
  1381                         IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, (UINT) ((first / sizeof (Vertex)) + offset), 2);
  1382                     }
  1383                 } else {
  1384                     const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first);
  1385                     for (i = 0; i < count; ++i, verts += 4) {
  1386                         IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, verts, sizeof (Vertex));
  1387                     }
  1388                 }
  1389                 break;
  1390             }
  1391 
  1392             case SDL_RENDERCMD_COPY_EX: {
  1393                 const size_t first = cmd->data.draw.first;
  1394                 const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first);
  1395                 const Vertex *transvert = verts + 4;
  1396                 const float translatex = transvert->x;
  1397                 const float translatey = transvert->y;
  1398                 const float rotation = transvert->z;
  1399                 const Float4X4 d3dmatrix = MatrixMultiply(MatrixRotationZ(rotation), MatrixTranslation(translatex, translatey, 0));
  1400                 SetDrawState(data, cmd);
  1401 
  1402                 IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*)&d3dmatrix);
  1403 
  1404                 if (vbo) {
  1405                     IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, (UINT) (first / sizeof (Vertex)), 2);
  1406                 } else {
  1407                     IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, verts, sizeof (Vertex));
  1408                 }
  1409                 break;
  1410             }
  1411 
  1412             case SDL_RENDERCMD_NO_OP:
  1413                 break;
  1414         }
  1415 
  1416         cmd = cmd->next;
  1417     }
  1418 
  1419     return 0;
  1420 }
  1421 
  1422 
  1423 static int
  1424 D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
  1425                      Uint32 format, void * pixels, int pitch)
  1426 {
  1427     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
  1428     D3DSURFACE_DESC desc;
  1429     LPDIRECT3DSURFACE9 backBuffer;
  1430     LPDIRECT3DSURFACE9 surface;
  1431     RECT d3drect;
  1432     D3DLOCKED_RECT locked;
  1433     HRESULT result;
  1434 
  1435     if (data->currentRenderTarget) {
  1436         backBuffer = data->currentRenderTarget;
  1437     } else {
  1438         backBuffer = data->defaultRenderTarget;
  1439     }
  1440 
  1441     result = IDirect3DSurface9_GetDesc(backBuffer, &desc);
  1442     if (FAILED(result)) {
  1443         IDirect3DSurface9_Release(backBuffer);
  1444         return D3D_SetError("GetDesc()", result);
  1445     }
  1446 
  1447     result = IDirect3DDevice9_CreateOffscreenPlainSurface(data->device, desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &surface, NULL);
  1448     if (FAILED(result)) {
  1449         IDirect3DSurface9_Release(backBuffer);
  1450         return D3D_SetError("CreateOffscreenPlainSurface()", result);
  1451     }
  1452 
  1453     result = IDirect3DDevice9_GetRenderTargetData(data->device, backBuffer, surface);
  1454     if (FAILED(result)) {
  1455         IDirect3DSurface9_Release(surface);
  1456         IDirect3DSurface9_Release(backBuffer);
  1457         return D3D_SetError("GetRenderTargetData()", result);
  1458     }
  1459 
  1460     d3drect.left = rect->x;
  1461     d3drect.right = rect->x + rect->w;
  1462     d3drect.top = rect->y;
  1463     d3drect.bottom = rect->y + rect->h;
  1464 
  1465     result = IDirect3DSurface9_LockRect(surface, &locked, &d3drect, D3DLOCK_READONLY);
  1466     if (FAILED(result)) {
  1467         IDirect3DSurface9_Release(surface);
  1468         IDirect3DSurface9_Release(backBuffer);
  1469         return D3D_SetError("LockRect()", result);
  1470     }
  1471 
  1472     SDL_ConvertPixels(rect->w, rect->h,
  1473                       D3DFMTToPixelFormat(desc.Format), locked.pBits, locked.Pitch,
  1474                       format, pixels, pitch);
  1475 
  1476     IDirect3DSurface9_UnlockRect(surface);
  1477 
  1478     IDirect3DSurface9_Release(surface);
  1479 
  1480     return 0;
  1481 }
  1482 
  1483 static void
  1484 D3D_RenderPresent(SDL_Renderer * renderer)
  1485 {
  1486     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
  1487     HRESULT result;
  1488 
  1489     if (!data->beginScene) {
  1490         IDirect3DDevice9_EndScene(data->device);
  1491         data->beginScene = SDL_TRUE;
  1492     }
  1493 
  1494     result = IDirect3DDevice9_TestCooperativeLevel(data->device);
  1495     if (result == D3DERR_DEVICELOST) {
  1496         /* We'll reset later */
  1497         return;
  1498     }
  1499     if (result == D3DERR_DEVICENOTRESET) {
  1500         D3D_Reset(renderer);
  1501     }
  1502     result = IDirect3DDevice9_Present(data->device, NULL, NULL, NULL, NULL);
  1503     if (FAILED(result)) {
  1504         D3D_SetError("Present()", result);
  1505     }
  1506 }
  1507 
  1508 static void
  1509 D3D_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
  1510 {
  1511     D3D_RenderData *renderdata = (D3D_RenderData *) renderer->driverdata;
  1512     D3D_TextureData *data = (D3D_TextureData *) texture->driverdata;
  1513 
  1514     if (renderdata->drawstate.texture == texture) {
  1515         renderdata->drawstate.texture = NULL;
  1516     }
  1517 
  1518     if (!data) {
  1519         return;
  1520     }
  1521 
  1522     D3D_DestroyTextureRep(&data->texture);
  1523     D3D_DestroyTextureRep(&data->utexture);
  1524     D3D_DestroyTextureRep(&data->vtexture);
  1525     SDL_free(data->pixels);
  1526     SDL_free(data);
  1527     texture->driverdata = NULL;
  1528 }
  1529 
  1530 static void
  1531 D3D_DestroyRenderer(SDL_Renderer * renderer)
  1532 {
  1533     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
  1534 
  1535     if (data) {
  1536         int i;
  1537 
  1538         /* Release the render target */
  1539         if (data->defaultRenderTarget) {
  1540             IDirect3DSurface9_Release(data->defaultRenderTarget);
  1541             data->defaultRenderTarget = NULL;
  1542         }
  1543         if (data->currentRenderTarget != NULL) {
  1544             IDirect3DSurface9_Release(data->currentRenderTarget);
  1545             data->currentRenderTarget = NULL;
  1546         }
  1547         for (i = 0; i < SDL_arraysize(data->shaders); ++i) {
  1548             if (data->shaders[i]) {
  1549                 IDirect3DPixelShader9_Release(data->shaders[i]);
  1550                 data->shaders[i] = NULL;
  1551             }
  1552         }
  1553         /* Release all vertex buffers */
  1554         for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) {
  1555             if (data->vertexBuffers[i]) {
  1556                 IDirect3DVertexBuffer9_Release(data->vertexBuffers[i]);
  1557             }
  1558             data->vertexBuffers[i] = NULL;
  1559         }
  1560         if (data->device) {
  1561             IDirect3DDevice9_Release(data->device);
  1562             data->device = NULL;
  1563         }
  1564         if (data->d3d) {
  1565             IDirect3D9_Release(data->d3d);
  1566             SDL_UnloadObject(data->d3dDLL);
  1567         }
  1568         SDL_free(data);
  1569     }
  1570     SDL_free(renderer);
  1571 }
  1572 
  1573 static int
  1574 D3D_Reset(SDL_Renderer * renderer)
  1575 {
  1576     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
  1577     const Float4X4 d3dmatrix = MatrixIdentity();
  1578     HRESULT result;
  1579     SDL_Texture *texture;
  1580     int i;
  1581 
  1582     /* Release the default render target before reset */
  1583     if (data->defaultRenderTarget) {
  1584         IDirect3DSurface9_Release(data->defaultRenderTarget);
  1585         data->defaultRenderTarget = NULL;
  1586     }
  1587     if (data->currentRenderTarget != NULL) {
  1588         IDirect3DSurface9_Release(data->currentRenderTarget);
  1589         data->currentRenderTarget = NULL;
  1590     }
  1591 
  1592     /* Release application render targets */
  1593     for (texture = renderer->textures; texture; texture = texture->next) {
  1594         if (texture->access == SDL_TEXTUREACCESS_TARGET) {
  1595             D3D_DestroyTexture(renderer, texture);
  1596         } else {
  1597             D3D_RecreateTexture(renderer, texture);
  1598         }
  1599     }
  1600 
  1601     /* Release all vertex buffers */
  1602     for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) {
  1603         if (data->vertexBuffers[i]) {
  1604             IDirect3DVertexBuffer9_Release(data->vertexBuffers[i]);
  1605         }
  1606         data->vertexBuffers[i] = NULL;
  1607     }
  1608 
  1609     result = IDirect3DDevice9_Reset(data->device, &data->pparams);
  1610     if (FAILED(result)) {
  1611         if (result == D3DERR_DEVICELOST) {
  1612             /* Don't worry about it, we'll reset later... */
  1613             return 0;
  1614         } else {
  1615             return D3D_SetError("Reset()", result);
  1616         }
  1617     }
  1618 
  1619     /* Allocate application render targets */
  1620     for (texture = renderer->textures; texture; texture = texture->next) {
  1621         if (texture->access == SDL_TEXTUREACCESS_TARGET) {
  1622             D3D_CreateTexture(renderer, texture);
  1623         }
  1624     }
  1625 
  1626     IDirect3DDevice9_GetRenderTarget(data->device, 0, &data->defaultRenderTarget);
  1627     D3D_InitRenderState(data);
  1628     D3D_SetRenderTargetInternal(renderer, renderer->target);
  1629     data->drawstate.viewport_dirty = SDL_TRUE;
  1630     data->drawstate.cliprect_dirty = SDL_TRUE;
  1631     data->drawstate.cliprect_enabled_dirty = SDL_TRUE;
  1632     data->drawstate.texture = NULL;
  1633     data->drawstate.shader = NULL;
  1634     data->drawstate.blend = SDL_BLENDMODE_INVALID;
  1635     data->drawstate.is_copy_ex = SDL_FALSE;
  1636     IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*)&d3dmatrix);
  1637 
  1638     /* Let the application know that render targets were reset */
  1639     {
  1640         SDL_Event event;
  1641         event.type = SDL_RENDER_TARGETS_RESET;
  1642         SDL_PushEvent(&event);
  1643     }
  1644 
  1645     return 0;
  1646 }
  1647 
  1648 SDL_Renderer *
  1649 D3D_CreateRenderer(SDL_Window * window, Uint32 flags)
  1650 {
  1651     SDL_Renderer *renderer;
  1652     D3D_RenderData *data;
  1653     SDL_SysWMinfo windowinfo;
  1654     HRESULT result;
  1655     D3DPRESENT_PARAMETERS pparams;
  1656     IDirect3DSwapChain9 *chain;
  1657     D3DCAPS9 caps;
  1658     DWORD device_flags;
  1659     Uint32 window_flags;
  1660     int w, h;
  1661     SDL_DisplayMode fullscreen_mode;
  1662     int displayIndex;
  1663 
  1664     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
  1665     if (!renderer) {
  1666         SDL_OutOfMemory();
  1667         return NULL;
  1668     }
  1669 
  1670     data = (D3D_RenderData *) SDL_calloc(1, sizeof(*data));
  1671     if (!data) {
  1672         SDL_free(renderer);
  1673         SDL_OutOfMemory();
  1674         return NULL;
  1675     }
  1676 
  1677     if (!D3D_LoadDLL(&data->d3dDLL, &data->d3d)) {
  1678         SDL_free(renderer);
  1679         SDL_free(data);
  1680         SDL_SetError("Unable to create Direct3D interface");
  1681         return NULL;
  1682     }
  1683 
  1684     renderer->WindowEvent = D3D_WindowEvent;
  1685     renderer->SupportsBlendMode = D3D_SupportsBlendMode;
  1686     renderer->CreateTexture = D3D_CreateTexture;
  1687     renderer->UpdateTexture = D3D_UpdateTexture;
  1688     renderer->UpdateTextureYUV = D3D_UpdateTextureYUV;
  1689     renderer->LockTexture = D3D_LockTexture;
  1690     renderer->UnlockTexture = D3D_UnlockTexture;
  1691     renderer->SetRenderTarget = D3D_SetRenderTarget;
  1692     renderer->QueueSetViewport = D3D_QueueSetViewport;
  1693     renderer->QueueSetDrawColor = D3D_QueueSetViewport;  /* SetViewport and SetDrawColor are (currently) no-ops. */
  1694     renderer->QueueDrawPoints = D3D_QueueDrawPoints;
  1695     renderer->QueueDrawLines = D3D_QueueDrawPoints;  /* lines and points queue vertices the same way. */
  1696     renderer->QueueFillRects = D3D_QueueFillRects;
  1697     renderer->QueueCopy = D3D_QueueCopy;
  1698     renderer->QueueCopyEx = D3D_QueueCopyEx;
  1699     renderer->RunCommandQueue = D3D_RunCommandQueue;
  1700     renderer->RenderReadPixels = D3D_RenderReadPixels;
  1701     renderer->RenderPresent = D3D_RenderPresent;
  1702     renderer->DestroyTexture = D3D_DestroyTexture;
  1703     renderer->DestroyRenderer = D3D_DestroyRenderer;
  1704     renderer->info = D3D_RenderDriver.info;
  1705     renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
  1706     renderer->driverdata = data;
  1707 
  1708     SDL_VERSION(&windowinfo.version);
  1709     SDL_GetWindowWMInfo(window, &windowinfo);
  1710 
  1711     window_flags = SDL_GetWindowFlags(window);
  1712     SDL_GetWindowSize(window, &w, &h);
  1713     SDL_GetWindowDisplayMode(window, &fullscreen_mode);
  1714 
  1715     SDL_zero(pparams);
  1716     pparams.hDeviceWindow = windowinfo.info.win.window;
  1717     pparams.BackBufferWidth = w;
  1718     pparams.BackBufferHeight = h;
  1719     pparams.BackBufferCount = 1;
  1720     pparams.SwapEffect = D3DSWAPEFFECT_DISCARD;
  1721 
  1722     if (window_flags & SDL_WINDOW_FULLSCREEN && (window_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) {
  1723         pparams.Windowed = FALSE;
  1724         pparams.BackBufferFormat = PixelFormatToD3DFMT(fullscreen_mode.format);
  1725         pparams.FullScreen_RefreshRateInHz = fullscreen_mode.refresh_rate;
  1726     } else {
  1727         pparams.Windowed = TRUE;
  1728         pparams.BackBufferFormat = D3DFMT_UNKNOWN;
  1729         pparams.FullScreen_RefreshRateInHz = 0;
  1730     }
  1731     if (flags & SDL_RENDERER_PRESENTVSYNC) {
  1732         pparams.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
  1733     } else {
  1734         pparams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
  1735     }
  1736 
  1737     /* Get the adapter for the display that the window is on */
  1738     displayIndex = SDL_GetWindowDisplayIndex(window);
  1739     data->adapter = SDL_Direct3D9GetAdapterIndex(displayIndex);
  1740 
  1741     IDirect3D9_GetDeviceCaps(data->d3d, data->adapter, D3DDEVTYPE_HAL, &caps);
  1742 
  1743     device_flags = D3DCREATE_FPU_PRESERVE;
  1744     if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) {
  1745         device_flags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
  1746     } else {
  1747         device_flags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
  1748     }
  1749 
  1750     if (SDL_GetHintBoolean(SDL_HINT_RENDER_DIRECT3D_THREADSAFE, SDL_FALSE)) {
  1751         device_flags |= D3DCREATE_MULTITHREADED;
  1752     }
  1753 
  1754     result = IDirect3D9_CreateDevice(data->d3d, data->adapter,
  1755                                      D3DDEVTYPE_HAL,
  1756                                      pparams.hDeviceWindow,
  1757                                      device_flags,
  1758                                      &pparams, &data->device);
  1759     if (FAILED(result)) {
  1760         D3D_DestroyRenderer(renderer);
  1761         D3D_SetError("CreateDevice()", result);
  1762         return NULL;
  1763     }
  1764 
  1765     /* Get presentation parameters to fill info */
  1766     result = IDirect3DDevice9_GetSwapChain(data->device, 0, &chain);
  1767     if (FAILED(result)) {
  1768         D3D_DestroyRenderer(renderer);
  1769         D3D_SetError("GetSwapChain()", result);
  1770         return NULL;
  1771     }
  1772     result = IDirect3DSwapChain9_GetPresentParameters(chain, &pparams);
  1773     if (FAILED(result)) {
  1774         IDirect3DSwapChain9_Release(chain);
  1775         D3D_DestroyRenderer(renderer);
  1776         D3D_SetError("GetPresentParameters()", result);
  1777         return NULL;
  1778     }
  1779     IDirect3DSwapChain9_Release(chain);
  1780     if (pparams.PresentationInterval == D3DPRESENT_INTERVAL_ONE) {
  1781         renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
  1782     }
  1783     data->pparams = pparams;
  1784 
  1785     IDirect3DDevice9_GetDeviceCaps(data->device, &caps);
  1786     renderer->info.max_texture_width = caps.MaxTextureWidth;
  1787     renderer->info.max_texture_height = caps.MaxTextureHeight;
  1788     if (caps.NumSimultaneousRTs >= 2) {
  1789         renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE;
  1790     }
  1791 
  1792     if (caps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND) {
  1793         data->enableSeparateAlphaBlend = SDL_TRUE;
  1794     }
  1795 
  1796     /* Store the default render target */
  1797     IDirect3DDevice9_GetRenderTarget(data->device, 0, &data->defaultRenderTarget);
  1798     data->currentRenderTarget = NULL;
  1799 
  1800     /* Set up parameters for rendering */
  1801     D3D_InitRenderState(data);
  1802 
  1803     if (caps.MaxSimultaneousTextures >= 3) {
  1804         int i;
  1805         for (i = 0; i < SDL_arraysize(data->shaders); ++i) {
  1806             result = D3D9_CreatePixelShader(data->device, (D3D9_Shader)i, &data->shaders[i]);
  1807             if (FAILED(result)) {
  1808                 D3D_SetError("CreatePixelShader()", result);
  1809             }
  1810         }
  1811         if (data->shaders[SHADER_YUV_JPEG] && data->shaders[SHADER_YUV_BT601] && data->shaders[SHADER_YUV_BT709]) {
  1812             renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12;
  1813             renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV;
  1814         }
  1815     }
  1816 
  1817     data->drawstate.blend = SDL_BLENDMODE_INVALID;
  1818 
  1819     return renderer;
  1820 }
  1821 
  1822 SDL_RenderDriver D3D_RenderDriver = {
  1823     D3D_CreateRenderer,
  1824     {
  1825      "direct3d",
  1826      (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),
  1827      1,
  1828      {SDL_PIXELFORMAT_ARGB8888},
  1829      0,
  1830      0}
  1831 };
  1832 #endif /* SDL_VIDEO_RENDER_D3D && !SDL_RENDER_DISABLED */
  1833 
  1834 #ifdef __WIN32__
  1835 /* This function needs to always exist on Windows, for the Dynamic API. */
  1836 IDirect3DDevice9 *
  1837 SDL_RenderGetD3D9Device(SDL_Renderer * renderer)
  1838 {
  1839     IDirect3DDevice9 *device = NULL;
  1840 
  1841 #if SDL_VIDEO_RENDER_D3D && !SDL_RENDER_DISABLED
  1842     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
  1843 
  1844     /* Make sure that this is a D3D renderer */
  1845     if (renderer->DestroyRenderer != D3D_DestroyRenderer) {
  1846         SDL_SetError("Renderer is not a D3D renderer");
  1847         return NULL;
  1848     }
  1849 
  1850     device = data->device;
  1851     if (device) {
  1852         IDirect3DDevice9_AddRef(device);
  1853     }
  1854 #endif /* SDL_VIDEO_RENDER_D3D && !SDL_RENDER_DISABLED */
  1855 
  1856     return device;
  1857 }
  1858 #endif /* __WIN32__ */
  1859 
  1860 /* vi: set ts=4 sw=4 expandtab: */