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