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