src/render/direct3d/SDL_render_d3d.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 06 Feb 2011 00:48:41 -0800
changeset 5203 25ffd4e5255c
parent 5195 bb45ecd958d8
child 5224 2178ffe17222
permissions -rw-r--r--
Minor consistency cleanup and documentation link update.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2010 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #if SDL_VIDEO_RENDER_D3D
    25 
    26 #include "../../core/windows/SDL_windows.h"
    27 
    28 #include "SDL_loadso.h"
    29 #include "SDL_syswm.h"
    30 #include "../SDL_sysrender.h"
    31 
    32 #if SDL_VIDEO_RENDER_D3D
    33 #define D3D_DEBUG_INFO
    34 #include <d3d9.h>
    35 #endif
    36 
    37 #ifdef ASSEMBLE_SHADER
    38 ///////////////////////////////////////////////////////////////////////////
    39 // ID3DXBuffer:
    40 // ------------
    41 // The buffer object is used by D3DX to return arbitrary size data.
    42 //
    43 // GetBufferPointer -
    44 //    Returns a pointer to the beginning of the buffer.
    45 //
    46 // GetBufferSize -
    47 //    Returns the size of the buffer, in bytes.
    48 ///////////////////////////////////////////////////////////////////////////
    49 
    50 typedef interface ID3DXBuffer ID3DXBuffer;
    51 typedef interface ID3DXBuffer *LPD3DXBUFFER;
    52 
    53 // {8BA5FB08-5195-40e2-AC58-0D989C3A0102}
    54 DEFINE_GUID(IID_ID3DXBuffer, 
    55 0x8ba5fb08, 0x5195, 0x40e2, 0xac, 0x58, 0xd, 0x98, 0x9c, 0x3a, 0x1, 0x2);
    56 
    57 #undef INTERFACE
    58 #define INTERFACE ID3DXBuffer
    59 
    60 typedef interface ID3DXBuffer {
    61     const struct ID3DXBufferVtbl FAR* lpVtbl;
    62 } ID3DXBuffer;
    63 typedef const struct ID3DXBufferVtbl ID3DXBufferVtbl;
    64 const struct ID3DXBufferVtbl
    65 {
    66     // IUnknown
    67     STDMETHOD(QueryInterface)(THIS_ REFIID iid, LPVOID *ppv) PURE;
    68     STDMETHOD_(ULONG, AddRef)(THIS) PURE;
    69     STDMETHOD_(ULONG, Release)(THIS) PURE;
    70 
    71     // ID3DXBuffer
    72     STDMETHOD_(LPVOID, GetBufferPointer)(THIS) PURE;
    73     STDMETHOD_(DWORD, GetBufferSize)(THIS) PURE;
    74 };
    75 
    76 HRESULT WINAPI
    77     D3DXAssembleShader(
    78         LPCSTR                          pSrcData,
    79         UINT                            SrcDataLen,
    80         CONST LPVOID*                   pDefines,
    81         LPVOID                          pInclude,
    82         DWORD                           Flags,
    83         LPD3DXBUFFER*                   ppShader,
    84         LPD3DXBUFFER*                   ppErrorMsgs);
    85 
    86 #endif /* ASSEMBLE_SHADER */
    87 
    88 
    89 /* Direct3D renderer implementation */
    90 
    91 static SDL_Renderer *D3D_CreateRenderer(SDL_Window * window, Uint32 flags);
    92 static int D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    93 static int D3D_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    94                              const SDL_Rect * rect, const void *pixels,
    95                              int pitch);
    96 static int D3D_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    97                            const SDL_Rect * rect, void **pixels, int *pitch);
    98 static void D3D_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    99 static int D3D_RenderDrawPoints(SDL_Renderer * renderer,
   100                                 const SDL_Point * points, int count);
   101 static int D3D_RenderDrawLines(SDL_Renderer * renderer,
   102                                const SDL_Point * points, int count);
   103 static int D3D_RenderFillRects(SDL_Renderer * renderer,
   104                                const SDL_Rect ** rects, int count);
   105 static int D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
   106                           const SDL_Rect * srcrect, const SDL_Rect * dstrect);
   107 static int D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   108                                 Uint32 format, void * pixels, int pitch);
   109 static void D3D_RenderPresent(SDL_Renderer * renderer);
   110 static void D3D_DestroyTexture(SDL_Renderer * renderer,
   111                                SDL_Texture * texture);
   112 static void D3D_DestroyRenderer(SDL_Renderer * renderer);
   113 
   114 
   115 SDL_RenderDriver D3D_RenderDriver = {
   116     D3D_CreateRenderer,
   117     {
   118      "direct3d",
   119      (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
   120      1,
   121      {SDL_PIXELFORMAT_ARGB8888},
   122      0,
   123      0}
   124 };
   125 
   126 typedef struct
   127 {
   128     void* d3dDLL;
   129     IDirect3D9 *d3d;
   130     IDirect3DDevice9 *device;
   131     UINT adapter;
   132     D3DPRESENT_PARAMETERS pparams;
   133     SDL_bool beginScene;
   134 } D3D_RenderData;
   135 
   136 typedef struct
   137 {
   138     IDirect3DTexture9 *texture;
   139 } D3D_TextureData;
   140 
   141 typedef struct
   142 {
   143     float x, y, z;
   144     float rhw;
   145     DWORD color;
   146     float u, v;
   147 } Vertex;
   148 
   149 static void
   150 D3D_SetError(const char *prefix, HRESULT result)
   151 {
   152     const char *error;
   153 
   154     switch (result) {
   155     case D3DERR_WRONGTEXTUREFORMAT:
   156         error = "WRONGTEXTUREFORMAT";
   157         break;
   158     case D3DERR_UNSUPPORTEDCOLOROPERATION:
   159         error = "UNSUPPORTEDCOLOROPERATION";
   160         break;
   161     case D3DERR_UNSUPPORTEDCOLORARG:
   162         error = "UNSUPPORTEDCOLORARG";
   163         break;
   164     case D3DERR_UNSUPPORTEDALPHAOPERATION:
   165         error = "UNSUPPORTEDALPHAOPERATION";
   166         break;
   167     case D3DERR_UNSUPPORTEDALPHAARG:
   168         error = "UNSUPPORTEDALPHAARG";
   169         break;
   170     case D3DERR_TOOMANYOPERATIONS:
   171         error = "TOOMANYOPERATIONS";
   172         break;
   173     case D3DERR_CONFLICTINGTEXTUREFILTER:
   174         error = "CONFLICTINGTEXTUREFILTER";
   175         break;
   176     case D3DERR_UNSUPPORTEDFACTORVALUE:
   177         error = "UNSUPPORTEDFACTORVALUE";
   178         break;
   179     case D3DERR_CONFLICTINGRENDERSTATE:
   180         error = "CONFLICTINGRENDERSTATE";
   181         break;
   182     case D3DERR_UNSUPPORTEDTEXTUREFILTER:
   183         error = "UNSUPPORTEDTEXTUREFILTER";
   184         break;
   185     case D3DERR_CONFLICTINGTEXTUREPALETTE:
   186         error = "CONFLICTINGTEXTUREPALETTE";
   187         break;
   188     case D3DERR_DRIVERINTERNALERROR:
   189         error = "DRIVERINTERNALERROR";
   190         break;
   191     case D3DERR_NOTFOUND:
   192         error = "NOTFOUND";
   193         break;
   194     case D3DERR_MOREDATA:
   195         error = "MOREDATA";
   196         break;
   197     case D3DERR_DEVICELOST:
   198         error = "DEVICELOST";
   199         break;
   200     case D3DERR_DEVICENOTRESET:
   201         error = "DEVICENOTRESET";
   202         break;
   203     case D3DERR_NOTAVAILABLE:
   204         error = "NOTAVAILABLE";
   205         break;
   206     case D3DERR_OUTOFVIDEOMEMORY:
   207         error = "OUTOFVIDEOMEMORY";
   208         break;
   209     case D3DERR_INVALIDDEVICE:
   210         error = "INVALIDDEVICE";
   211         break;
   212     case D3DERR_INVALIDCALL:
   213         error = "INVALIDCALL";
   214         break;
   215     case D3DERR_DRIVERINVALIDCALL:
   216         error = "DRIVERINVALIDCALL";
   217         break;
   218     case D3DERR_WASSTILLDRAWING:
   219         error = "WASSTILLDRAWING";
   220         break;
   221     default:
   222         error = "UNKNOWN";
   223         break;
   224     }
   225     SDL_SetError("%s: %s", prefix, error);
   226 }
   227 
   228 static D3DFORMAT
   229 PixelFormatToD3DFMT(Uint32 format)
   230 {
   231     switch (format) {
   232     case SDL_PIXELFORMAT_RGB565:
   233         return D3DFMT_R5G6B5;
   234     case SDL_PIXELFORMAT_RGB888:
   235         return D3DFMT_X8R8G8B8;
   236     case SDL_PIXELFORMAT_ARGB8888:
   237         return D3DFMT_A8R8G8B8;
   238     default:
   239         return D3DFMT_UNKNOWN;
   240     }
   241 }
   242 
   243 static Uint32
   244 D3DFMTToPixelFormat(D3DFORMAT format)
   245 {
   246     switch (format) {
   247     case D3DFMT_R5G6B5:
   248         return SDL_PIXELFORMAT_RGB565;
   249     case D3DFMT_X8R8G8B8:
   250         return SDL_PIXELFORMAT_RGB888;
   251     case D3DFMT_A8R8G8B8:
   252         return SDL_PIXELFORMAT_ARGB8888;
   253     default:
   254         return SDL_PIXELFORMAT_UNKNOWN;
   255     }
   256 }
   257 
   258 SDL_Renderer *
   259 D3D_CreateRenderer(SDL_Window * window, Uint32 flags)
   260 {
   261     SDL_Renderer *renderer;
   262     D3D_RenderData *data;
   263     SDL_SysWMinfo windowinfo;
   264     HRESULT result;
   265     D3DPRESENT_PARAMETERS pparams;
   266     IDirect3DSwapChain9 *chain;
   267     D3DCAPS9 caps;
   268     Uint32 window_flags;
   269     int w, h;
   270     SDL_DisplayMode fullscreen_mode;
   271 
   272     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   273     if (!renderer) {
   274         SDL_OutOfMemory();
   275         return NULL;
   276     }
   277 
   278     data = (D3D_RenderData *) SDL_calloc(1, sizeof(*data));
   279     if (!data) {
   280         SDL_free(renderer);
   281         SDL_OutOfMemory();
   282         return NULL;
   283     }
   284 
   285     data->d3dDLL = SDL_LoadObject("D3D9.DLL");
   286     if (data->d3dDLL) {
   287         IDirect3D9 *(WINAPI * D3DCreate) (UINT SDKVersion);
   288 
   289         D3DCreate =
   290             (IDirect3D9 * (WINAPI *) (UINT)) SDL_LoadFunction(data->d3dDLL,
   291                                                             "Direct3DCreate9");
   292         if (D3DCreate) {
   293             data->d3d = D3DCreate(D3D_SDK_VERSION);
   294         }
   295         if (!data->d3d) {
   296             SDL_UnloadObject(data->d3dDLL);
   297             data->d3dDLL = NULL;
   298         }
   299     }
   300     if (!data->d3d) {
   301         SDL_free(renderer);
   302         SDL_free(data);
   303         SDL_SetError("Unable to create Direct3D interface");
   304         return NULL;
   305     }
   306 
   307     renderer->CreateTexture = D3D_CreateTexture;
   308     renderer->UpdateTexture = D3D_UpdateTexture;
   309     renderer->LockTexture = D3D_LockTexture;
   310     renderer->UnlockTexture = D3D_UnlockTexture;
   311     renderer->RenderDrawPoints = D3D_RenderDrawPoints;
   312     renderer->RenderDrawLines = D3D_RenderDrawLines;
   313     renderer->RenderFillRects = D3D_RenderFillRects;
   314     renderer->RenderCopy = D3D_RenderCopy;
   315     renderer->RenderReadPixels = D3D_RenderReadPixels;
   316     renderer->RenderPresent = D3D_RenderPresent;
   317     renderer->DestroyTexture = D3D_DestroyTexture;
   318     renderer->DestroyRenderer = D3D_DestroyRenderer;
   319     renderer->info = D3D_RenderDriver.info;
   320     renderer->driverdata = data;
   321 
   322     renderer->info.flags = SDL_RENDERER_ACCELERATED;
   323 
   324     SDL_VERSION(&windowinfo.version);
   325     SDL_GetWindowWMInfo(window, &windowinfo);
   326 
   327     window_flags = SDL_GetWindowFlags(window);
   328     SDL_GetWindowSize(window, &w, &h);
   329     SDL_GetWindowDisplayMode(window, &fullscreen_mode);
   330 
   331     SDL_zero(pparams);
   332     pparams.hDeviceWindow = windowinfo.info.win.window;
   333     pparams.BackBufferWidth = w;
   334     pparams.BackBufferHeight = h;
   335     if (window_flags & SDL_WINDOW_FULLSCREEN) {
   336         pparams.BackBufferFormat =
   337             PixelFormatToD3DFMT(fullscreen_mode.format);
   338     } else {
   339         pparams.BackBufferFormat = D3DFMT_UNKNOWN;
   340     }
   341     pparams.BackBufferCount = 1;
   342     pparams.SwapEffect = D3DSWAPEFFECT_DISCARD;
   343 
   344     if (window_flags & SDL_WINDOW_FULLSCREEN) {
   345         pparams.Windowed = FALSE;
   346         pparams.FullScreen_RefreshRateInHz =
   347             fullscreen_mode.refresh_rate;
   348     } else {
   349         pparams.Windowed = TRUE;
   350         pparams.FullScreen_RefreshRateInHz = 0;
   351     }
   352     if (flags & SDL_RENDERER_PRESENTVSYNC) {
   353         pparams.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
   354     } else {
   355         pparams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
   356     }
   357 
   358     /* FIXME: Which adapter? */
   359     data->adapter = D3DADAPTER_DEFAULT;
   360     IDirect3D9_GetDeviceCaps(data->d3d, data->adapter, D3DDEVTYPE_HAL, &caps);
   361 
   362     result = IDirect3D9_CreateDevice(data->d3d, data->adapter,
   363                                      D3DDEVTYPE_HAL,
   364                                      pparams.hDeviceWindow,
   365                                      (caps.
   366                                       DevCaps &
   367                                       D3DDEVCAPS_HWTRANSFORMANDLIGHT) ?
   368                                      D3DCREATE_HARDWARE_VERTEXPROCESSING :
   369                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
   370                                      &pparams, &data->device);
   371     if (FAILED(result)) {
   372         D3D_DestroyRenderer(renderer);
   373         D3D_SetError("CreateDevice()", result);
   374         return NULL;
   375     }
   376     data->beginScene = SDL_TRUE;
   377 
   378     /* Get presentation parameters to fill info */
   379     result = IDirect3DDevice9_GetSwapChain(data->device, 0, &chain);
   380     if (FAILED(result)) {
   381         D3D_DestroyRenderer(renderer);
   382         D3D_SetError("GetSwapChain()", result);
   383         return NULL;
   384     }
   385     result = IDirect3DSwapChain9_GetPresentParameters(chain, &pparams);
   386     if (FAILED(result)) {
   387         IDirect3DSwapChain9_Release(chain);
   388         D3D_DestroyRenderer(renderer);
   389         D3D_SetError("GetPresentParameters()", result);
   390         return NULL;
   391     }
   392     IDirect3DSwapChain9_Release(chain);
   393     if (pparams.PresentationInterval == D3DPRESENT_INTERVAL_ONE) {
   394         renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
   395     }
   396     data->pparams = pparams;
   397 
   398     IDirect3DDevice9_GetDeviceCaps(data->device, &caps);
   399     renderer->info.max_texture_width = caps.MaxTextureWidth;
   400     renderer->info.max_texture_height = caps.MaxTextureHeight;
   401 
   402     /* Set up parameters for rendering */
   403     IDirect3DDevice9_SetVertexShader(data->device, NULL);
   404     IDirect3DDevice9_SetFVF(data->device,
   405                             D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);
   406     IDirect3DDevice9_SetRenderState(data->device, D3DRS_ZENABLE, D3DZB_FALSE);
   407     IDirect3DDevice9_SetRenderState(data->device, D3DRS_CULLMODE,
   408                                     D3DCULL_NONE);
   409     IDirect3DDevice9_SetRenderState(data->device, D3DRS_LIGHTING, FALSE);
   410     /* Enable color modulation by diffuse color */
   411     IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_COLOROP,
   412                                           D3DTOP_MODULATE);
   413     IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_COLORARG1,
   414                                           D3DTA_TEXTURE);
   415     IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_COLORARG2,
   416                                           D3DTA_DIFFUSE);
   417     /* Enable alpha modulation by diffuse alpha */
   418     IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_ALPHAOP,
   419                                           D3DTOP_MODULATE);
   420     IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_ALPHAARG1,
   421                                           D3DTA_TEXTURE);
   422     IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_ALPHAARG2,
   423                                           D3DTA_DIFFUSE);
   424     /* Disable second texture stage, since we're done */
   425     IDirect3DDevice9_SetTextureStageState(data->device, 1, D3DTSS_COLOROP,
   426                                           D3DTOP_DISABLE);
   427     IDirect3DDevice9_SetTextureStageState(data->device, 1, D3DTSS_ALPHAOP,
   428                                           D3DTOP_DISABLE);
   429 
   430     return renderer;
   431 }
   432 
   433 static int
   434 D3D_Reset(SDL_Renderer * renderer)
   435 {
   436     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
   437     HRESULT result;
   438 
   439     result = IDirect3DDevice9_Reset(data->device, &data->pparams);
   440     if (FAILED(result)) {
   441         if (result == D3DERR_DEVICELOST) {
   442             /* Don't worry about it, we'll reset later... */
   443             return 0;
   444         } else {
   445             D3D_SetError("Reset()", result);
   446             return -1;
   447         }
   448     }
   449     IDirect3DDevice9_SetVertexShader(data->device, NULL);
   450     IDirect3DDevice9_SetFVF(data->device,
   451                             D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);
   452     IDirect3DDevice9_SetRenderState(data->device, D3DRS_CULLMODE,
   453                                     D3DCULL_NONE);
   454     IDirect3DDevice9_SetRenderState(data->device, D3DRS_LIGHTING, FALSE);
   455     return 0;
   456 }
   457 
   458 /* FIXME: This needs to be called... when? */
   459 #if 0
   460 static int
   461 D3D_DisplayModeChanged(SDL_Renderer * renderer)
   462 {
   463     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
   464     SDL_Window *window = renderer->window;
   465     SDL_VideoDisplay *display = window->display;
   466 
   467     data->pparams.BackBufferWidth = window->w;
   468     data->pparams.BackBufferHeight = window->h;
   469     if (window->flags & SDL_WINDOW_FULLSCREEN) {
   470         data->pparams.BackBufferFormat =
   471             PixelFormatToD3DFMT(window->fullscreen_mode.format);
   472     } else {
   473         data->pparams.BackBufferFormat = D3DFMT_UNKNOWN;
   474     }
   475     return D3D_Reset(renderer);
   476 }
   477 #endif
   478 
   479 static int
   480 D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   481 {
   482     D3D_RenderData *renderdata = (D3D_RenderData *) renderer->driverdata;
   483     SDL_Window *window = renderer->window;
   484     D3DFORMAT display_format = renderdata->pparams.BackBufferFormat;
   485     D3D_TextureData *data;
   486     D3DPOOL pool;
   487     DWORD usage;
   488     HRESULT result;
   489 
   490     data = (D3D_TextureData *) SDL_calloc(1, sizeof(*data));
   491     if (!data) {
   492         SDL_OutOfMemory();
   493         return -1;
   494     }
   495 
   496     texture->driverdata = data;
   497 
   498 #ifdef USE_DYNAMIC_TEXTURE
   499     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   500         pool = D3DPOOL_DEFAULT;
   501         usage = D3DUSAGE_DYNAMIC;
   502     } else
   503 #endif
   504     {
   505         pool = D3DPOOL_MANAGED;
   506         usage = 0;
   507     }
   508 
   509     result =
   510         IDirect3DDevice9_CreateTexture(renderdata->device, texture->w,
   511                                        texture->h, 1, usage,
   512                                        PixelFormatToD3DFMT(texture->format),
   513                                        pool, &data->texture, NULL);
   514     if (FAILED(result)) {
   515         D3D_SetError("CreateTexture()", result);
   516         return -1;
   517     }
   518 
   519     return 0;
   520 }
   521 
   522 static int
   523 D3D_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   524                   const SDL_Rect * rect, const void *pixels, int pitch)
   525 {
   526     D3D_TextureData *data = (D3D_TextureData *) texture->driverdata;
   527     D3D_RenderData *renderdata = (D3D_RenderData *) renderer->driverdata;
   528     RECT d3drect;
   529     D3DLOCKED_RECT locked;
   530     const Uint8 *src;
   531     Uint8 *dst;
   532     int row, length;
   533     HRESULT result;
   534 
   535 #ifdef USE_DYNAMIC_TEXTURE
   536     if (texture->access == SDL_TEXTUREACCESS_STREAMING &&
   537         rect->x == 0 && rect->y == 0 &&
   538         rect->w == texture->w && rect->h == texture->h) {
   539         result = IDirect3DTexture9_LockRect(data->texture, 0, &locked, NULL, D3DLOCK_DISCARD);
   540     } else
   541 #endif
   542     {
   543         d3drect.left = rect->x;
   544         d3drect.right = rect->x + rect->w;
   545         d3drect.top = rect->y;
   546         d3drect.bottom = rect->y + rect->h;
   547         result = IDirect3DTexture9_LockRect(data->texture, 0, &locked, &d3drect, 0);
   548     }
   549 
   550     if (FAILED(result)) {
   551         D3D_SetError("LockRect()", result);
   552         return -1;
   553     }
   554 
   555     src = pixels;
   556     dst = locked.pBits;
   557     length = rect->w * SDL_BYTESPERPIXEL(texture->format);
   558     if (length == pitch && length == locked.Pitch) {
   559         SDL_memcpy(dst, src, length*rect->h);
   560     } else {
   561         for (row = 0; row < rect->h; ++row) {
   562             SDL_memcpy(dst, src, length);
   563             src += pitch;
   564             dst += locked.Pitch;
   565         }
   566     }
   567     IDirect3DTexture9_UnlockRect(data->texture, 0);
   568 
   569     return 0;
   570 }
   571 
   572 static int
   573 D3D_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   574                 const SDL_Rect * rect, void **pixels, int *pitch)
   575 {
   576     D3D_TextureData *data = (D3D_TextureData *) texture->driverdata;
   577     RECT d3drect;
   578     D3DLOCKED_RECT locked;
   579     HRESULT result;
   580 
   581     d3drect.left = rect->x;
   582     d3drect.right = rect->x + rect->w;
   583     d3drect.top = rect->y;
   584     d3drect.bottom = rect->y + rect->h;
   585 
   586     result = IDirect3DTexture9_LockRect(data->texture, 0, &locked, &d3drect, 0);
   587     if (FAILED(result)) {
   588         D3D_SetError("LockRect()", result);
   589         return -1;
   590     }
   591     *pixels = locked.pBits;
   592     *pitch = locked.Pitch;
   593     return 0;
   594 }
   595 
   596 static void
   597 D3D_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   598 {
   599     D3D_TextureData *data = (D3D_TextureData *) texture->driverdata;
   600 
   601     IDirect3DTexture9_UnlockRect(data->texture, 0);
   602 }
   603 
   604 static void
   605 D3D_SetBlendMode(D3D_RenderData * data, int blendMode)
   606 {
   607     switch (blendMode) {
   608     case SDL_BLENDMODE_NONE:
   609         IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
   610                                         FALSE);
   611         break;
   612     case SDL_BLENDMODE_BLEND:
   613         IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
   614                                         TRUE);
   615         IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND,
   616                                         D3DBLEND_SRCALPHA);
   617         IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
   618                                         D3DBLEND_INVSRCALPHA);
   619         break;
   620     case SDL_BLENDMODE_ADD:
   621         IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
   622                                         TRUE);
   623         IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND,
   624                                         D3DBLEND_SRCALPHA);
   625         IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
   626                                         D3DBLEND_ONE);
   627         break;
   628     case SDL_BLENDMODE_MOD:
   629         IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
   630                                         TRUE);
   631         IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND,
   632                                         D3DBLEND_ZERO);
   633         IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
   634                                         D3DBLEND_SRCCOLOR);
   635         break;
   636     }
   637 }
   638 
   639 static int
   640 D3D_RenderDrawPoints(SDL_Renderer * renderer, const SDL_Point * points,
   641                      int count)
   642 {
   643     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
   644     DWORD color;
   645     Vertex *vertices;
   646     int i;
   647     HRESULT result;
   648 
   649     if (data->beginScene) {
   650         IDirect3DDevice9_BeginScene(data->device);
   651         data->beginScene = SDL_FALSE;
   652     }
   653 
   654     D3D_SetBlendMode(data, renderer->blendMode);
   655 
   656     result =
   657         IDirect3DDevice9_SetTexture(data->device, 0,
   658                                     (IDirect3DBaseTexture9 *) 0);
   659     if (FAILED(result)) {
   660         D3D_SetError("SetTexture()", result);
   661         return -1;
   662     }
   663 
   664     color = D3DCOLOR_ARGB(renderer->a, renderer->r, renderer->g, renderer->b);
   665 
   666     vertices = SDL_stack_alloc(Vertex, count);
   667     for (i = 0; i < count; ++i) {
   668         vertices[i].x = (float) points[i].x;
   669         vertices[i].y = (float) points[i].y;
   670         vertices[i].z = 0.0f;
   671         vertices[i].rhw = 1.0f;
   672         vertices[i].color = color;
   673         vertices[i].u = 0.0f;
   674         vertices[i].v = 0.0f;
   675     }
   676     result =
   677         IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, count,
   678                                          vertices, sizeof(*vertices));
   679     SDL_stack_free(vertices);
   680     if (FAILED(result)) {
   681         D3D_SetError("DrawPrimitiveUP()", result);
   682         return -1;
   683     }
   684     return 0;
   685 }
   686 
   687 static int
   688 D3D_RenderDrawLines(SDL_Renderer * renderer, const SDL_Point * points,
   689                     int count)
   690 {
   691     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
   692     DWORD color;
   693     Vertex *vertices;
   694     int i;
   695     HRESULT result;
   696 
   697     if (data->beginScene) {
   698         IDirect3DDevice9_BeginScene(data->device);
   699         data->beginScene = SDL_FALSE;
   700     }
   701 
   702     D3D_SetBlendMode(data, renderer->blendMode);
   703 
   704     result =
   705         IDirect3DDevice9_SetTexture(data->device, 0,
   706                                     (IDirect3DBaseTexture9 *) 0);
   707     if (FAILED(result)) {
   708         D3D_SetError("SetTexture()", result);
   709         return -1;
   710     }
   711 
   712     color = D3DCOLOR_ARGB(renderer->a, renderer->r, renderer->g, renderer->b);
   713 
   714     vertices = SDL_stack_alloc(Vertex, count);
   715     for (i = 0; i < count; ++i) {
   716         vertices[i].x = (float) points[i].x;
   717         vertices[i].y = (float) points[i].y;
   718         vertices[i].z = 0.0f;
   719         vertices[i].rhw = 1.0f;
   720         vertices[i].color = color;
   721         vertices[i].u = 0.0f;
   722         vertices[i].v = 0.0f;
   723     }
   724     result =
   725         IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_LINESTRIP, count-1,
   726                                          vertices, sizeof(*vertices));
   727 
   728     /* DirectX 9 has the same line rasterization semantics as GDI,
   729        so we need to close the endpoint of the line */
   730     if (points[0].x != points[count-1].x || points[0].y != points[count-1].y) {
   731         vertices[0].x = (float) points[count-1].x;
   732         vertices[0].y = (float) points[count-1].y;
   733         result = IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, 1, vertices, sizeof(*vertices));
   734     }
   735 
   736     SDL_stack_free(vertices);
   737     if (FAILED(result)) {
   738         D3D_SetError("DrawPrimitiveUP()", result);
   739         return -1;
   740     }
   741     return 0;
   742 }
   743 
   744 static int
   745 D3D_RenderFillRects(SDL_Renderer * renderer, const SDL_Rect ** rects,
   746                     int count)
   747 {
   748     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
   749     DWORD color;
   750     int i;
   751     float minx, miny, maxx, maxy;
   752     Vertex vertices[4];
   753     HRESULT result;
   754 
   755     if (data->beginScene) {
   756         IDirect3DDevice9_BeginScene(data->device);
   757         data->beginScene = SDL_FALSE;
   758     }
   759 
   760     D3D_SetBlendMode(data, renderer->blendMode);
   761 
   762     result =
   763         IDirect3DDevice9_SetTexture(data->device, 0,
   764                                     (IDirect3DBaseTexture9 *) 0);
   765     if (FAILED(result)) {
   766         D3D_SetError("SetTexture()", result);
   767         return -1;
   768     }
   769 
   770     color = D3DCOLOR_ARGB(renderer->a, renderer->r, renderer->g, renderer->b);
   771 
   772     for (i = 0; i < count; ++i) {
   773         const SDL_Rect *rect = rects[i];
   774 
   775         minx = (float) rect->x;
   776         miny = (float) rect->y;
   777         maxx = (float) rect->x + rect->w;
   778         maxy = (float) rect->y + rect->h;
   779 
   780         vertices[0].x = minx;
   781         vertices[0].y = miny;
   782         vertices[0].z = 0.0f;
   783         vertices[0].rhw = 1.0f;
   784         vertices[0].color = color;
   785         vertices[0].u = 0.0f;
   786         vertices[0].v = 0.0f;
   787 
   788         vertices[1].x = maxx;
   789         vertices[1].y = miny;
   790         vertices[1].z = 0.0f;
   791         vertices[1].rhw = 1.0f;
   792         vertices[1].color = color;
   793         vertices[1].u = 0.0f;
   794         vertices[1].v = 0.0f;
   795 
   796         vertices[2].x = maxx;
   797         vertices[2].y = maxy;
   798         vertices[2].z = 0.0f;
   799         vertices[2].rhw = 1.0f;
   800         vertices[2].color = color;
   801         vertices[2].u = 0.0f;
   802         vertices[2].v = 0.0f;
   803 
   804         vertices[3].x = minx;
   805         vertices[3].y = maxy;
   806         vertices[3].z = 0.0f;
   807         vertices[3].rhw = 1.0f;
   808         vertices[3].color = color;
   809         vertices[3].u = 0.0f;
   810         vertices[3].v = 0.0f;
   811 
   812         result =
   813             IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN,
   814                                              2, vertices, sizeof(*vertices));
   815         if (FAILED(result)) {
   816             D3D_SetError("DrawPrimitiveUP()", result);
   817             return -1;
   818         }
   819     }
   820     return 0;
   821 }
   822 
   823 static int
   824 D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
   825                const SDL_Rect * srcrect, const SDL_Rect * dstrect)
   826 {
   827     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
   828     D3D_TextureData *texturedata = (D3D_TextureData *) texture->driverdata;
   829     LPDIRECT3DPIXELSHADER9 shader = NULL;
   830     float minx, miny, maxx, maxy;
   831     float minu, maxu, minv, maxv;
   832     DWORD color;
   833     Vertex vertices[4];
   834     HRESULT result;
   835 
   836     if (data->beginScene) {
   837         IDirect3DDevice9_BeginScene(data->device);
   838         data->beginScene = SDL_FALSE;
   839     }
   840 
   841     minx = (float) dstrect->x - 0.5f;
   842     miny = (float) dstrect->y - 0.5f;
   843     maxx = (float) dstrect->x + dstrect->w - 0.5f;
   844     maxy = (float) dstrect->y + dstrect->h - 0.5f;
   845 
   846     minu = (float) srcrect->x / texture->w;
   847     maxu = (float) (srcrect->x + srcrect->w) / texture->w;
   848     minv = (float) srcrect->y / texture->h;
   849     maxv = (float) (srcrect->y + srcrect->h) / texture->h;
   850 
   851     color = D3DCOLOR_ARGB(texture->a, texture->r, texture->g, texture->b);
   852 
   853     vertices[0].x = minx;
   854     vertices[0].y = miny;
   855     vertices[0].z = 0.0f;
   856     vertices[0].rhw = 1.0f;
   857     vertices[0].color = color;
   858     vertices[0].u = minu;
   859     vertices[0].v = minv;
   860 
   861     vertices[1].x = maxx;
   862     vertices[1].y = miny;
   863     vertices[1].z = 0.0f;
   864     vertices[1].rhw = 1.0f;
   865     vertices[1].color = color;
   866     vertices[1].u = maxu;
   867     vertices[1].v = minv;
   868 
   869     vertices[2].x = maxx;
   870     vertices[2].y = maxy;
   871     vertices[2].z = 0.0f;
   872     vertices[2].rhw = 1.0f;
   873     vertices[2].color = color;
   874     vertices[2].u = maxu;
   875     vertices[2].v = maxv;
   876 
   877     vertices[3].x = minx;
   878     vertices[3].y = maxy;
   879     vertices[3].z = 0.0f;
   880     vertices[3].rhw = 1.0f;
   881     vertices[3].color = color;
   882     vertices[3].u = minu;
   883     vertices[3].v = maxv;
   884 
   885     D3D_SetBlendMode(data, texture->blendMode);
   886 
   887     IDirect3DDevice9_SetSamplerState(data->device, 0, D3DSAMP_MINFILTER,
   888                                      D3DTEXF_LINEAR);
   889     IDirect3DDevice9_SetSamplerState(data->device, 0, D3DSAMP_MAGFILTER,
   890                                      D3DTEXF_LINEAR);
   891 
   892     result =
   893         IDirect3DDevice9_SetTexture(data->device, 0, (IDirect3DBaseTexture9 *)
   894                                     texturedata->texture);
   895     if (FAILED(result)) {
   896         D3D_SetError("SetTexture()", result);
   897         return -1;
   898     }
   899     if (shader) {
   900         result = IDirect3DDevice9_SetPixelShader(data->device, shader);
   901         if (FAILED(result)) {
   902             D3D_SetError("SetShader()", result);
   903             return -1;
   904         }
   905     }
   906     result =
   907         IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2,
   908                                          vertices, sizeof(*vertices));
   909     if (FAILED(result)) {
   910         D3D_SetError("DrawPrimitiveUP()", result);
   911         return -1;
   912     }
   913     if (shader) {
   914         result = IDirect3DDevice9_SetPixelShader(data->device, NULL);
   915         if (FAILED(result)) {
   916             D3D_SetError("SetShader()", result);
   917             return -1;
   918         }
   919     }
   920     return 0;
   921 }
   922 
   923 static int
   924 D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   925                      Uint32 format, void * pixels, int pitch)
   926 {
   927     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
   928     D3DSURFACE_DESC desc;
   929     LPDIRECT3DSURFACE9 backBuffer;
   930     LPDIRECT3DSURFACE9 surface;
   931     RECT d3drect;
   932     D3DLOCKED_RECT locked;
   933     HRESULT result;
   934 
   935     result = IDirect3DDevice9_GetBackBuffer(data->device, 0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuffer);
   936     if (FAILED(result)) {
   937         D3D_SetError("GetBackBuffer()", result);
   938         return -1;
   939     }
   940 
   941     result = IDirect3DSurface9_GetDesc(backBuffer, &desc);
   942     if (FAILED(result)) {
   943         D3D_SetError("GetDesc()", result);
   944         IDirect3DSurface9_Release(backBuffer);
   945         return -1;
   946     }
   947 
   948     result = IDirect3DDevice9_CreateOffscreenPlainSurface(data->device, desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &surface, NULL);
   949     if (FAILED(result)) {
   950         D3D_SetError("CreateOffscreenPlainSurface()", result);
   951         IDirect3DSurface9_Release(backBuffer);
   952         return -1;
   953     }
   954 
   955     result = IDirect3DDevice9_GetRenderTargetData(data->device, backBuffer, surface);
   956     if (FAILED(result)) {
   957         D3D_SetError("GetRenderTargetData()", result);
   958         IDirect3DSurface9_Release(surface);
   959         IDirect3DSurface9_Release(backBuffer);
   960         return -1;
   961     }
   962 
   963     d3drect.left = rect->x;
   964     d3drect.right = rect->x + rect->w;
   965     d3drect.top = rect->y;
   966     d3drect.bottom = rect->y + rect->h;
   967 
   968     result = IDirect3DSurface9_LockRect(surface, &locked, &d3drect, D3DLOCK_READONLY);
   969     if (FAILED(result)) {
   970         D3D_SetError("LockRect()", result);
   971         IDirect3DSurface9_Release(surface);
   972         IDirect3DSurface9_Release(backBuffer);
   973         return -1;
   974     }
   975 
   976     SDL_ConvertPixels(rect->w, rect->h,
   977                       D3DFMTToPixelFormat(desc.Format), locked.pBits, locked.Pitch,
   978                       format, pixels, pitch);
   979 
   980     IDirect3DSurface9_UnlockRect(surface);
   981 
   982     IDirect3DSurface9_Release(surface);
   983     IDirect3DSurface9_Release(backBuffer);
   984 
   985     return 0;
   986 }
   987 
   988 static void
   989 D3D_RenderPresent(SDL_Renderer * renderer)
   990 {
   991     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
   992     HRESULT result;
   993 
   994     if (!data->beginScene) {
   995         IDirect3DDevice9_EndScene(data->device);
   996         data->beginScene = SDL_TRUE;
   997     }
   998 
   999     result = IDirect3DDevice9_TestCooperativeLevel(data->device);
  1000     if (result == D3DERR_DEVICELOST) {
  1001         /* We'll reset later */
  1002         return;
  1003     }
  1004     if (result == D3DERR_DEVICENOTRESET) {
  1005         D3D_Reset(renderer);
  1006     }
  1007     result = IDirect3DDevice9_Present(data->device, NULL, NULL, NULL, NULL);
  1008     if (FAILED(result)) {
  1009         D3D_SetError("Present()", result);
  1010     }
  1011 }
  1012 
  1013 static void
  1014 D3D_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
  1015 {
  1016     D3D_TextureData *data = (D3D_TextureData *) texture->driverdata;
  1017 
  1018     if (!data) {
  1019         return;
  1020     }
  1021     if (data->texture) {
  1022         IDirect3DTexture9_Release(data->texture);
  1023     }
  1024     SDL_free(data);
  1025     texture->driverdata = NULL;
  1026 }
  1027 
  1028 static void
  1029 D3D_DestroyRenderer(SDL_Renderer * renderer)
  1030 {
  1031     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
  1032 
  1033     if (data) {
  1034         if (data->device) {
  1035             IDirect3DDevice9_Release(data->device);
  1036         }
  1037         if (data->d3d) {
  1038             IDirect3D9_Release(data->d3d);
  1039             SDL_UnloadObject(data->d3dDLL);
  1040         }
  1041         SDL_free(data);
  1042     }
  1043     SDL_free(renderer);
  1044 }
  1045 
  1046 #endif /* SDL_VIDEO_RENDER_D3D */
  1047 
  1048 /* vi: set ts=4 sw=4 expandtab: */