diff src/render/direct3d/SDL_render_d3d.c @ 5198:bb45ecd958d8

Renamed files for consistency
author Sam Lantinga <slouken@libsdl.org>
date Sat, 05 Feb 2011 12:01:11 -0800
parents src/render/direct3d/SDL_d3drender.c@69f47f2c1856
children 25ffd4e5255c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/render/direct3d/SDL_render_d3d.c	Sat Feb 05 12:01:11 2011 -0800
@@ -0,0 +1,1048 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2010 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+#if SDL_VIDEO_RENDER_D3D
+
+#include "../../core/windows/SDL_windows.h"
+
+#include "SDL_loadso.h"
+#include "SDL_syswm.h"
+#include "../SDL_sysrender.h"
+
+#if SDL_VIDEO_RENDER_D3D
+#define D3D_DEBUG_INFO
+#include <d3d9.h>
+#endif
+
+#ifdef ASSEMBLE_SHADER
+///////////////////////////////////////////////////////////////////////////
+// ID3DXBuffer:
+// ------------
+// The buffer object is used by D3DX to return arbitrary size data.
+//
+// GetBufferPointer -
+//    Returns a pointer to the beginning of the buffer.
+//
+// GetBufferSize -
+//    Returns the size of the buffer, in bytes.
+///////////////////////////////////////////////////////////////////////////
+
+typedef interface ID3DXBuffer ID3DXBuffer;
+typedef interface ID3DXBuffer *LPD3DXBUFFER;
+
+// {8BA5FB08-5195-40e2-AC58-0D989C3A0102}
+DEFINE_GUID(IID_ID3DXBuffer, 
+0x8ba5fb08, 0x5195, 0x40e2, 0xac, 0x58, 0xd, 0x98, 0x9c, 0x3a, 0x1, 0x2);
+
+#undef INTERFACE
+#define INTERFACE ID3DXBuffer
+
+typedef interface ID3DXBuffer {
+    const struct ID3DXBufferVtbl FAR* lpVtbl;
+} ID3DXBuffer;
+typedef const struct ID3DXBufferVtbl ID3DXBufferVtbl;
+const struct ID3DXBufferVtbl
+{
+    // IUnknown
+    STDMETHOD(QueryInterface)(THIS_ REFIID iid, LPVOID *ppv) PURE;
+    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
+    STDMETHOD_(ULONG, Release)(THIS) PURE;
+
+    // ID3DXBuffer
+    STDMETHOD_(LPVOID, GetBufferPointer)(THIS) PURE;
+    STDMETHOD_(DWORD, GetBufferSize)(THIS) PURE;
+};
+
+HRESULT WINAPI
+    D3DXAssembleShader(
+        LPCSTR                          pSrcData,
+        UINT                            SrcDataLen,
+        CONST LPVOID*                   pDefines,
+        LPVOID                          pInclude,
+        DWORD                           Flags,
+        LPD3DXBUFFER*                   ppShader,
+        LPD3DXBUFFER*                   ppErrorMsgs);
+
+#endif /* ASSEMBLE_SHADER */
+
+
+/* Direct3D renderer implementation */
+
+static SDL_Renderer *D3D_CreateRenderer(SDL_Window * window, Uint32 flags);
+static int D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
+static int D3D_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
+                             const SDL_Rect * rect, const void *pixels,
+                             int pitch);
+static int D3D_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
+                           const SDL_Rect * rect, void **pixels, int *pitch);
+static void D3D_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
+static int D3D_RenderDrawPoints(SDL_Renderer * renderer,
+                                const SDL_Point * points, int count);
+static int D3D_RenderDrawLines(SDL_Renderer * renderer,
+                               const SDL_Point * points, int count);
+static int D3D_RenderFillRects(SDL_Renderer * renderer,
+                               const SDL_Rect ** rects, int count);
+static int D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
+                          const SDL_Rect * srcrect, const SDL_Rect * dstrect);
+static int D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
+                                Uint32 format, void * pixels, int pitch);
+static void D3D_RenderPresent(SDL_Renderer * renderer);
+static void D3D_DestroyTexture(SDL_Renderer * renderer,
+                               SDL_Texture * texture);
+static void D3D_DestroyRenderer(SDL_Renderer * renderer);
+
+
+SDL_RenderDriver D3D_RenderDriver = {
+    D3D_CreateRenderer,
+    {
+     "direct3d",
+     (SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED),
+     1,
+     {SDL_PIXELFORMAT_ARGB8888},
+     0,
+     0}
+};
+
+typedef struct
+{
+    void* d3dDLL;
+    IDirect3D9 *d3d;
+    IDirect3DDevice9 *device;
+    UINT adapter;
+    D3DPRESENT_PARAMETERS pparams;
+    SDL_bool beginScene;
+} D3D_RenderData;
+
+typedef struct
+{
+    IDirect3DTexture9 *texture;
+} D3D_TextureData;
+
+typedef struct
+{
+    float x, y, z;
+    float rhw;
+    DWORD color;
+    float u, v;
+} Vertex;
+
+static void
+D3D_SetError(const char *prefix, HRESULT result)
+{
+    const char *error;
+
+    switch (result) {
+    case D3DERR_WRONGTEXTUREFORMAT:
+        error = "WRONGTEXTUREFORMAT";
+        break;
+    case D3DERR_UNSUPPORTEDCOLOROPERATION:
+        error = "UNSUPPORTEDCOLOROPERATION";
+        break;
+    case D3DERR_UNSUPPORTEDCOLORARG:
+        error = "UNSUPPORTEDCOLORARG";
+        break;
+    case D3DERR_UNSUPPORTEDALPHAOPERATION:
+        error = "UNSUPPORTEDALPHAOPERATION";
+        break;
+    case D3DERR_UNSUPPORTEDALPHAARG:
+        error = "UNSUPPORTEDALPHAARG";
+        break;
+    case D3DERR_TOOMANYOPERATIONS:
+        error = "TOOMANYOPERATIONS";
+        break;
+    case D3DERR_CONFLICTINGTEXTUREFILTER:
+        error = "CONFLICTINGTEXTUREFILTER";
+        break;
+    case D3DERR_UNSUPPORTEDFACTORVALUE:
+        error = "UNSUPPORTEDFACTORVALUE";
+        break;
+    case D3DERR_CONFLICTINGRENDERSTATE:
+        error = "CONFLICTINGRENDERSTATE";
+        break;
+    case D3DERR_UNSUPPORTEDTEXTUREFILTER:
+        error = "UNSUPPORTEDTEXTUREFILTER";
+        break;
+    case D3DERR_CONFLICTINGTEXTUREPALETTE:
+        error = "CONFLICTINGTEXTUREPALETTE";
+        break;
+    case D3DERR_DRIVERINTERNALERROR:
+        error = "DRIVERINTERNALERROR";
+        break;
+    case D3DERR_NOTFOUND:
+        error = "NOTFOUND";
+        break;
+    case D3DERR_MOREDATA:
+        error = "MOREDATA";
+        break;
+    case D3DERR_DEVICELOST:
+        error = "DEVICELOST";
+        break;
+    case D3DERR_DEVICENOTRESET:
+        error = "DEVICENOTRESET";
+        break;
+    case D3DERR_NOTAVAILABLE:
+        error = "NOTAVAILABLE";
+        break;
+    case D3DERR_OUTOFVIDEOMEMORY:
+        error = "OUTOFVIDEOMEMORY";
+        break;
+    case D3DERR_INVALIDDEVICE:
+        error = "INVALIDDEVICE";
+        break;
+    case D3DERR_INVALIDCALL:
+        error = "INVALIDCALL";
+        break;
+    case D3DERR_DRIVERINVALIDCALL:
+        error = "DRIVERINVALIDCALL";
+        break;
+    case D3DERR_WASSTILLDRAWING:
+        error = "WASSTILLDRAWING";
+        break;
+    default:
+        error = "UNKNOWN";
+        break;
+    }
+    SDL_SetError("%s: %s", prefix, error);
+}
+
+static D3DFORMAT
+PixelFormatToD3DFMT(Uint32 format)
+{
+    switch (format) {
+    case SDL_PIXELFORMAT_RGB565:
+        return D3DFMT_R5G6B5;
+    case SDL_PIXELFORMAT_RGB888:
+        return D3DFMT_X8R8G8B8;
+    case SDL_PIXELFORMAT_ARGB8888:
+        return D3DFMT_A8R8G8B8;
+    default:
+        return D3DFMT_UNKNOWN;
+    }
+}
+
+static Uint32
+D3DFMTToPixelFormat(D3DFORMAT format)
+{
+    switch (format) {
+    case D3DFMT_R5G6B5:
+        return SDL_PIXELFORMAT_RGB565;
+    case D3DFMT_X8R8G8B8:
+        return SDL_PIXELFORMAT_RGB888;
+    case D3DFMT_A8R8G8B8:
+        return SDL_PIXELFORMAT_ARGB8888;
+    default:
+        return SDL_PIXELFORMAT_UNKNOWN;
+    }
+}
+
+SDL_Renderer *
+D3D_CreateRenderer(SDL_Window * window, Uint32 flags)
+{
+    SDL_Renderer *renderer;
+    D3D_RenderData *data;
+    SDL_SysWMinfo windowinfo;
+    HRESULT result;
+    D3DPRESENT_PARAMETERS pparams;
+    IDirect3DSwapChain9 *chain;
+    D3DCAPS9 caps;
+    Uint32 window_flags;
+    int w, h;
+    SDL_DisplayMode fullscreen_mode;
+
+    renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
+    if (!renderer) {
+        SDL_OutOfMemory();
+        return NULL;
+    }
+
+    data = (D3D_RenderData *) SDL_calloc(1, sizeof(*data));
+    if (!data) {
+        SDL_free(renderer);
+        SDL_OutOfMemory();
+        return NULL;
+    }
+
+    data->d3dDLL = SDL_LoadObject("D3D9.DLL");
+    if (data->d3dDLL) {
+        IDirect3D9 *(WINAPI * D3DCreate) (UINT SDKVersion);
+
+        D3DCreate =
+            (IDirect3D9 * (WINAPI *) (UINT)) SDL_LoadFunction(data->d3dDLL,
+                                                            "Direct3DCreate9");
+        if (D3DCreate) {
+            data->d3d = D3DCreate(D3D_SDK_VERSION);
+        }
+        if (!data->d3d) {
+            SDL_UnloadObject(data->d3dDLL);
+            data->d3dDLL = NULL;
+        }
+    }
+    if (!data->d3d) {
+        SDL_free(renderer);
+        SDL_free(data);
+        SDL_SetError("Unable to create Direct3D interface");
+        return NULL;
+    }
+
+    renderer->CreateTexture = D3D_CreateTexture;
+    renderer->UpdateTexture = D3D_UpdateTexture;
+    renderer->LockTexture = D3D_LockTexture;
+    renderer->UnlockTexture = D3D_UnlockTexture;
+    renderer->RenderDrawPoints = D3D_RenderDrawPoints;
+    renderer->RenderDrawLines = D3D_RenderDrawLines;
+    renderer->RenderFillRects = D3D_RenderFillRects;
+    renderer->RenderCopy = D3D_RenderCopy;
+    renderer->RenderReadPixels = D3D_RenderReadPixels;
+    renderer->RenderPresent = D3D_RenderPresent;
+    renderer->DestroyTexture = D3D_DestroyTexture;
+    renderer->DestroyRenderer = D3D_DestroyRenderer;
+    renderer->info = D3D_RenderDriver.info;
+    renderer->driverdata = data;
+
+    renderer->info.flags = SDL_RENDERER_ACCELERATED;
+
+    SDL_VERSION(&windowinfo.version);
+    SDL_GetWindowWMInfo(window, &windowinfo);
+
+    window_flags = SDL_GetWindowFlags(window);
+    SDL_GetWindowSize(window, &w, &h);
+    SDL_GetWindowDisplayMode(window, &fullscreen_mode);
+
+    SDL_zero(pparams);
+    pparams.hDeviceWindow = windowinfo.info.win.window;
+    pparams.BackBufferWidth = w;
+    pparams.BackBufferHeight = h;
+    if (window_flags & SDL_WINDOW_FULLSCREEN) {
+        pparams.BackBufferFormat =
+            PixelFormatToD3DFMT(fullscreen_mode.format);
+    } else {
+        pparams.BackBufferFormat = D3DFMT_UNKNOWN;
+    }
+    pparams.BackBufferCount = 1;
+    pparams.SwapEffect = D3DSWAPEFFECT_DISCARD;
+
+    if (window_flags & SDL_WINDOW_FULLSCREEN) {
+        pparams.Windowed = FALSE;
+        pparams.FullScreen_RefreshRateInHz =
+            fullscreen_mode.refresh_rate;
+    } else {
+        pparams.Windowed = TRUE;
+        pparams.FullScreen_RefreshRateInHz = 0;
+    }
+    if (flags & SDL_RENDERER_PRESENTVSYNC) {
+        pparams.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
+    } else {
+        pparams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
+    }
+
+    /* FIXME: Which adapter? */
+    data->adapter = D3DADAPTER_DEFAULT;
+    IDirect3D9_GetDeviceCaps(data->d3d, data->adapter, D3DDEVTYPE_HAL, &caps);
+
+    result = IDirect3D9_CreateDevice(data->d3d, data->adapter,
+                                     D3DDEVTYPE_HAL,
+                                     pparams.hDeviceWindow,
+                                     (caps.
+                                      DevCaps &
+                                      D3DDEVCAPS_HWTRANSFORMANDLIGHT) ?
+                                     D3DCREATE_HARDWARE_VERTEXPROCESSING :
+                                     D3DCREATE_SOFTWARE_VERTEXPROCESSING,
+                                     &pparams, &data->device);
+    if (FAILED(result)) {
+        D3D_DestroyRenderer(renderer);
+        D3D_SetError("CreateDevice()", result);
+        return NULL;
+    }
+    data->beginScene = SDL_TRUE;
+
+    /* Get presentation parameters to fill info */
+    result = IDirect3DDevice9_GetSwapChain(data->device, 0, &chain);
+    if (FAILED(result)) {
+        D3D_DestroyRenderer(renderer);
+        D3D_SetError("GetSwapChain()", result);
+        return NULL;
+    }
+    result = IDirect3DSwapChain9_GetPresentParameters(chain, &pparams);
+    if (FAILED(result)) {
+        IDirect3DSwapChain9_Release(chain);
+        D3D_DestroyRenderer(renderer);
+        D3D_SetError("GetPresentParameters()", result);
+        return NULL;
+    }
+    IDirect3DSwapChain9_Release(chain);
+    if (pparams.PresentationInterval == D3DPRESENT_INTERVAL_ONE) {
+        renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
+    }
+    data->pparams = pparams;
+
+    IDirect3DDevice9_GetDeviceCaps(data->device, &caps);
+    renderer->info.max_texture_width = caps.MaxTextureWidth;
+    renderer->info.max_texture_height = caps.MaxTextureHeight;
+
+    /* Set up parameters for rendering */
+    IDirect3DDevice9_SetVertexShader(data->device, NULL);
+    IDirect3DDevice9_SetFVF(data->device,
+                            D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);
+    IDirect3DDevice9_SetRenderState(data->device, D3DRS_ZENABLE, D3DZB_FALSE);
+    IDirect3DDevice9_SetRenderState(data->device, D3DRS_CULLMODE,
+                                    D3DCULL_NONE);
+    IDirect3DDevice9_SetRenderState(data->device, D3DRS_LIGHTING, FALSE);
+    /* Enable color modulation by diffuse color */
+    IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_COLOROP,
+                                          D3DTOP_MODULATE);
+    IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_COLORARG1,
+                                          D3DTA_TEXTURE);
+    IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_COLORARG2,
+                                          D3DTA_DIFFUSE);
+    /* Enable alpha modulation by diffuse alpha */
+    IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_ALPHAOP,
+                                          D3DTOP_MODULATE);
+    IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_ALPHAARG1,
+                                          D3DTA_TEXTURE);
+    IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_ALPHAARG2,
+                                          D3DTA_DIFFUSE);
+    /* Disable second texture stage, since we're done */
+    IDirect3DDevice9_SetTextureStageState(data->device, 1, D3DTSS_COLOROP,
+                                          D3DTOP_DISABLE);
+    IDirect3DDevice9_SetTextureStageState(data->device, 1, D3DTSS_ALPHAOP,
+                                          D3DTOP_DISABLE);
+
+    return renderer;
+}
+
+static int
+D3D_Reset(SDL_Renderer * renderer)
+{
+    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
+    HRESULT result;
+
+    result = IDirect3DDevice9_Reset(data->device, &data->pparams);
+    if (FAILED(result)) {
+        if (result == D3DERR_DEVICELOST) {
+            /* Don't worry about it, we'll reset later... */
+            return 0;
+        } else {
+            D3D_SetError("Reset()", result);
+            return -1;
+        }
+    }
+    IDirect3DDevice9_SetVertexShader(data->device, NULL);
+    IDirect3DDevice9_SetFVF(data->device,
+                            D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);
+    IDirect3DDevice9_SetRenderState(data->device, D3DRS_CULLMODE,
+                                    D3DCULL_NONE);
+    IDirect3DDevice9_SetRenderState(data->device, D3DRS_LIGHTING, FALSE);
+    return 0;
+}
+
+/* FIXME: This needs to be called... when? */
+#if 0
+static int
+D3D_DisplayModeChanged(SDL_Renderer * renderer)
+{
+    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
+    SDL_Window *window = renderer->window;
+    SDL_VideoDisplay *display = window->display;
+
+    data->pparams.BackBufferWidth = window->w;
+    data->pparams.BackBufferHeight = window->h;
+    if (window->flags & SDL_WINDOW_FULLSCREEN) {
+        data->pparams.BackBufferFormat =
+            PixelFormatToD3DFMT(window->fullscreen_mode.format);
+    } else {
+        data->pparams.BackBufferFormat = D3DFMT_UNKNOWN;
+    }
+    return D3D_Reset(renderer);
+}
+#endif
+
+static int
+D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
+{
+    D3D_RenderData *renderdata = (D3D_RenderData *) renderer->driverdata;
+    SDL_Window *window = renderer->window;
+    D3DFORMAT display_format = renderdata->pparams.BackBufferFormat;
+    D3D_TextureData *data;
+    D3DPOOL pool;
+    DWORD usage;
+    HRESULT result;
+
+    data = (D3D_TextureData *) SDL_calloc(1, sizeof(*data));
+    if (!data) {
+        SDL_OutOfMemory();
+        return -1;
+    }
+
+    texture->driverdata = data;
+
+#ifdef USE_DYNAMIC_TEXTURE
+    if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
+        pool = D3DPOOL_DEFAULT;
+        usage = D3DUSAGE_DYNAMIC;
+    } else
+#endif
+    {
+        pool = D3DPOOL_MANAGED;
+        usage = 0;
+    }
+
+    result =
+        IDirect3DDevice9_CreateTexture(renderdata->device, texture->w,
+                                       texture->h, 1, usage,
+                                       PixelFormatToD3DFMT(texture->format),
+                                       pool, &data->texture, NULL);
+    if (FAILED(result)) {
+        D3D_SetError("CreateTexture()", result);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+D3D_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
+                  const SDL_Rect * rect, const void *pixels, int pitch)
+{
+    D3D_TextureData *data = (D3D_TextureData *) texture->driverdata;
+    D3D_RenderData *renderdata = (D3D_RenderData *) renderer->driverdata;
+    RECT d3drect;
+    D3DLOCKED_RECT locked;
+    const Uint8 *src;
+    Uint8 *dst;
+    int row, length;
+    HRESULT result;
+
+#ifdef USE_DYNAMIC_TEXTURE
+    if (texture->access == SDL_TEXTUREACCESS_STREAMING &&
+        rect->x == 0 && rect->y == 0 &&
+        rect->w == texture->w && rect->h == texture->h) {
+        result = IDirect3DTexture9_LockRect(data->texture, 0, &locked, NULL, D3DLOCK_DISCARD);
+    } else
+#endif
+    {
+        d3drect.left = rect->x;
+        d3drect.right = rect->x + rect->w;
+        d3drect.top = rect->y;
+        d3drect.bottom = rect->y + rect->h;
+        result = IDirect3DTexture9_LockRect(data->texture, 0, &locked, &d3drect, 0);
+    }
+
+    if (FAILED(result)) {
+        D3D_SetError("LockRect()", result);
+        return -1;
+    }
+
+    src = pixels;
+    dst = locked.pBits;
+    length = rect->w * SDL_BYTESPERPIXEL(texture->format);
+    if (length == pitch && length == locked.Pitch) {
+        SDL_memcpy(dst, src, length*rect->h);
+    } else {
+        for (row = 0; row < rect->h; ++row) {
+            SDL_memcpy(dst, src, length);
+            src += pitch;
+            dst += locked.Pitch;
+        }
+    }
+    IDirect3DTexture9_UnlockRect(data->texture, 0);
+
+    return 0;
+}
+
+static int
+D3D_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
+                const SDL_Rect * rect, void **pixels, int *pitch)
+{
+    D3D_TextureData *data = (D3D_TextureData *) texture->driverdata;
+    RECT d3drect;
+    D3DLOCKED_RECT locked;
+    HRESULT result;
+
+    d3drect.left = rect->x;
+    d3drect.right = rect->x + rect->w;
+    d3drect.top = rect->y;
+    d3drect.bottom = rect->y + rect->h;
+
+    result = IDirect3DTexture9_LockRect(data->texture, 0, &locked, &d3drect, 0);
+    if (FAILED(result)) {
+        D3D_SetError("LockRect()", result);
+        return -1;
+    }
+    *pixels = locked.pBits;
+    *pitch = locked.Pitch;
+    return 0;
+}
+
+static void
+D3D_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
+{
+    D3D_TextureData *data = (D3D_TextureData *) texture->driverdata;
+
+    IDirect3DTexture9_UnlockRect(data->texture, 0);
+}
+
+static void
+D3D_SetBlendMode(D3D_RenderData * data, int blendMode)
+{
+    switch (blendMode) {
+    case SDL_BLENDMODE_NONE:
+        IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
+                                        FALSE);
+        break;
+    case SDL_BLENDMODE_BLEND:
+        IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
+                                        TRUE);
+        IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND,
+                                        D3DBLEND_SRCALPHA);
+        IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
+                                        D3DBLEND_INVSRCALPHA);
+        break;
+    case SDL_BLENDMODE_ADD:
+        IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
+                                        TRUE);
+        IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND,
+                                        D3DBLEND_SRCALPHA);
+        IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
+                                        D3DBLEND_ONE);
+        break;
+    case SDL_BLENDMODE_MOD:
+        IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
+                                        TRUE);
+        IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND,
+                                        D3DBLEND_ZERO);
+        IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
+                                        D3DBLEND_SRCCOLOR);
+        break;
+    }
+}
+
+static int
+D3D_RenderDrawPoints(SDL_Renderer * renderer, const SDL_Point * points,
+                     int count)
+{
+    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
+    DWORD color;
+    Vertex *vertices;
+    int i;
+    HRESULT result;
+
+    if (data->beginScene) {
+        IDirect3DDevice9_BeginScene(data->device);
+        data->beginScene = SDL_FALSE;
+    }
+
+    D3D_SetBlendMode(data, renderer->blendMode);
+
+    result =
+        IDirect3DDevice9_SetTexture(data->device, 0,
+                                    (IDirect3DBaseTexture9 *) 0);
+    if (FAILED(result)) {
+        D3D_SetError("SetTexture()", result);
+        return -1;
+    }
+
+    color = D3DCOLOR_ARGB(renderer->a, renderer->r, renderer->g, renderer->b);
+
+    vertices = SDL_stack_alloc(Vertex, count);
+    for (i = 0; i < count; ++i) {
+        vertices[i].x = (float) points[i].x;
+        vertices[i].y = (float) points[i].y;
+        vertices[i].z = 0.0f;
+        vertices[i].rhw = 1.0f;
+        vertices[i].color = color;
+        vertices[i].u = 0.0f;
+        vertices[i].v = 0.0f;
+    }
+    result =
+        IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, count,
+                                         vertices, sizeof(*vertices));
+    SDL_stack_free(vertices);
+    if (FAILED(result)) {
+        D3D_SetError("DrawPrimitiveUP()", result);
+        return -1;
+    }
+    return 0;
+}
+
+static int
+D3D_RenderDrawLines(SDL_Renderer * renderer, const SDL_Point * points,
+                    int count)
+{
+    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
+    DWORD color;
+    Vertex *vertices;
+    int i;
+    HRESULT result;
+
+    if (data->beginScene) {
+        IDirect3DDevice9_BeginScene(data->device);
+        data->beginScene = SDL_FALSE;
+    }
+
+    D3D_SetBlendMode(data, renderer->blendMode);
+
+    result =
+        IDirect3DDevice9_SetTexture(data->device, 0,
+                                    (IDirect3DBaseTexture9 *) 0);
+    if (FAILED(result)) {
+        D3D_SetError("SetTexture()", result);
+        return -1;
+    }
+
+    color = D3DCOLOR_ARGB(renderer->a, renderer->r, renderer->g, renderer->b);
+
+    vertices = SDL_stack_alloc(Vertex, count);
+    for (i = 0; i < count; ++i) {
+        vertices[i].x = (float) points[i].x;
+        vertices[i].y = (float) points[i].y;
+        vertices[i].z = 0.0f;
+        vertices[i].rhw = 1.0f;
+        vertices[i].color = color;
+        vertices[i].u = 0.0f;
+        vertices[i].v = 0.0f;
+    }
+    result =
+        IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_LINESTRIP, count-1,
+                                         vertices, sizeof(*vertices));
+
+    /* DirectX 9 has the same line rasterization semantics as GDI,
+       so we need to close the endpoint of the line */
+    if (points[0].x != points[count-1].x || points[0].y != points[count-1].y) {
+        vertices[0].x = (float) points[count-1].x;
+        vertices[0].y = (float) points[count-1].y;
+        result = IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, 1, vertices, sizeof(*vertices));
+    }
+
+    SDL_stack_free(vertices);
+    if (FAILED(result)) {
+        D3D_SetError("DrawPrimitiveUP()", result);
+        return -1;
+    }
+    return 0;
+}
+
+static int
+D3D_RenderFillRects(SDL_Renderer * renderer, const SDL_Rect ** rects,
+                    int count)
+{
+    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
+    DWORD color;
+    int i;
+    float minx, miny, maxx, maxy;
+    Vertex vertices[4];
+    HRESULT result;
+
+    if (data->beginScene) {
+        IDirect3DDevice9_BeginScene(data->device);
+        data->beginScene = SDL_FALSE;
+    }
+
+    D3D_SetBlendMode(data, renderer->blendMode);
+
+    result =
+        IDirect3DDevice9_SetTexture(data->device, 0,
+                                    (IDirect3DBaseTexture9 *) 0);
+    if (FAILED(result)) {
+        D3D_SetError("SetTexture()", result);
+        return -1;
+    }
+
+    color = D3DCOLOR_ARGB(renderer->a, renderer->r, renderer->g, renderer->b);
+
+    for (i = 0; i < count; ++i) {
+        const SDL_Rect *rect = rects[i];
+
+        minx = (float) rect->x;
+        miny = (float) rect->y;
+        maxx = (float) rect->x + rect->w;
+        maxy = (float) rect->y + rect->h;
+
+        vertices[0].x = minx;
+        vertices[0].y = miny;
+        vertices[0].z = 0.0f;
+        vertices[0].rhw = 1.0f;
+        vertices[0].color = color;
+        vertices[0].u = 0.0f;
+        vertices[0].v = 0.0f;
+
+        vertices[1].x = maxx;
+        vertices[1].y = miny;
+        vertices[1].z = 0.0f;
+        vertices[1].rhw = 1.0f;
+        vertices[1].color = color;
+        vertices[1].u = 0.0f;
+        vertices[1].v = 0.0f;
+
+        vertices[2].x = maxx;
+        vertices[2].y = maxy;
+        vertices[2].z = 0.0f;
+        vertices[2].rhw = 1.0f;
+        vertices[2].color = color;
+        vertices[2].u = 0.0f;
+        vertices[2].v = 0.0f;
+
+        vertices[3].x = minx;
+        vertices[3].y = maxy;
+        vertices[3].z = 0.0f;
+        vertices[3].rhw = 1.0f;
+        vertices[3].color = color;
+        vertices[3].u = 0.0f;
+        vertices[3].v = 0.0f;
+
+        result =
+            IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN,
+                                             2, vertices, sizeof(*vertices));
+        if (FAILED(result)) {
+            D3D_SetError("DrawPrimitiveUP()", result);
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static int
+D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
+               const SDL_Rect * srcrect, const SDL_Rect * dstrect)
+{
+    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
+    D3D_TextureData *texturedata = (D3D_TextureData *) texture->driverdata;
+    LPDIRECT3DPIXELSHADER9 shader = NULL;
+    float minx, miny, maxx, maxy;
+    float minu, maxu, minv, maxv;
+    DWORD color;
+    Vertex vertices[4];
+    HRESULT result;
+
+    if (data->beginScene) {
+        IDirect3DDevice9_BeginScene(data->device);
+        data->beginScene = SDL_FALSE;
+    }
+
+    minx = (float) dstrect->x - 0.5f;
+    miny = (float) dstrect->y - 0.5f;
+    maxx = (float) dstrect->x + dstrect->w - 0.5f;
+    maxy = (float) dstrect->y + dstrect->h - 0.5f;
+
+    minu = (float) srcrect->x / texture->w;
+    maxu = (float) (srcrect->x + srcrect->w) / texture->w;
+    minv = (float) srcrect->y / texture->h;
+    maxv = (float) (srcrect->y + srcrect->h) / texture->h;
+
+    color = D3DCOLOR_ARGB(texture->a, texture->r, texture->g, texture->b);
+
+    vertices[0].x = minx;
+    vertices[0].y = miny;
+    vertices[0].z = 0.0f;
+    vertices[0].rhw = 1.0f;
+    vertices[0].color = color;
+    vertices[0].u = minu;
+    vertices[0].v = minv;
+
+    vertices[1].x = maxx;
+    vertices[1].y = miny;
+    vertices[1].z = 0.0f;
+    vertices[1].rhw = 1.0f;
+    vertices[1].color = color;
+    vertices[1].u = maxu;
+    vertices[1].v = minv;
+
+    vertices[2].x = maxx;
+    vertices[2].y = maxy;
+    vertices[2].z = 0.0f;
+    vertices[2].rhw = 1.0f;
+    vertices[2].color = color;
+    vertices[2].u = maxu;
+    vertices[2].v = maxv;
+
+    vertices[3].x = minx;
+    vertices[3].y = maxy;
+    vertices[3].z = 0.0f;
+    vertices[3].rhw = 1.0f;
+    vertices[3].color = color;
+    vertices[3].u = minu;
+    vertices[3].v = maxv;
+
+    D3D_SetBlendMode(data, texture->blendMode);
+
+    IDirect3DDevice9_SetSamplerState(data->device, 0, D3DSAMP_MINFILTER,
+                                     D3DTEXF_LINEAR);
+    IDirect3DDevice9_SetSamplerState(data->device, 0, D3DSAMP_MAGFILTER,
+                                     D3DTEXF_LINEAR);
+
+    result =
+        IDirect3DDevice9_SetTexture(data->device, 0, (IDirect3DBaseTexture9 *)
+                                    texturedata->texture);
+    if (FAILED(result)) {
+        D3D_SetError("SetTexture()", result);
+        return -1;
+    }
+    if (shader) {
+        result = IDirect3DDevice9_SetPixelShader(data->device, shader);
+        if (FAILED(result)) {
+            D3D_SetError("SetShader()", result);
+            return -1;
+        }
+    }
+    result =
+        IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2,
+                                         vertices, sizeof(*vertices));
+    if (FAILED(result)) {
+        D3D_SetError("DrawPrimitiveUP()", result);
+        return -1;
+    }
+    if (shader) {
+        result = IDirect3DDevice9_SetPixelShader(data->device, NULL);
+        if (FAILED(result)) {
+            D3D_SetError("SetShader()", result);
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static int
+D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
+                     Uint32 format, void * pixels, int pitch)
+{
+    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
+    D3DSURFACE_DESC desc;
+    LPDIRECT3DSURFACE9 backBuffer;
+    LPDIRECT3DSURFACE9 surface;
+    RECT d3drect;
+    D3DLOCKED_RECT locked;
+    HRESULT result;
+
+    result = IDirect3DDevice9_GetBackBuffer(data->device, 0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuffer);
+    if (FAILED(result)) {
+        D3D_SetError("GetBackBuffer()", result);
+        return -1;
+    }
+
+    result = IDirect3DSurface9_GetDesc(backBuffer, &desc);
+    if (FAILED(result)) {
+        D3D_SetError("GetDesc()", result);
+        IDirect3DSurface9_Release(backBuffer);
+        return -1;
+    }
+
+    result = IDirect3DDevice9_CreateOffscreenPlainSurface(data->device, desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &surface, NULL);
+    if (FAILED(result)) {
+        D3D_SetError("CreateOffscreenPlainSurface()", result);
+        IDirect3DSurface9_Release(backBuffer);
+        return -1;
+    }
+
+    result = IDirect3DDevice9_GetRenderTargetData(data->device, backBuffer, surface);
+    if (FAILED(result)) {
+        D3D_SetError("GetRenderTargetData()", result);
+        IDirect3DSurface9_Release(surface);
+        IDirect3DSurface9_Release(backBuffer);
+        return -1;
+    }
+
+    d3drect.left = rect->x;
+    d3drect.right = rect->x + rect->w;
+    d3drect.top = rect->y;
+    d3drect.bottom = rect->y + rect->h;
+
+    result = IDirect3DSurface9_LockRect(surface, &locked, &d3drect, D3DLOCK_READONLY);
+    if (FAILED(result)) {
+        D3D_SetError("LockRect()", result);
+        IDirect3DSurface9_Release(surface);
+        IDirect3DSurface9_Release(backBuffer);
+        return -1;
+    }
+
+    SDL_ConvertPixels(rect->w, rect->h,
+                      D3DFMTToPixelFormat(desc.Format), locked.pBits, locked.Pitch,
+                      format, pixels, pitch);
+
+    IDirect3DSurface9_UnlockRect(surface);
+
+    IDirect3DSurface9_Release(surface);
+    IDirect3DSurface9_Release(backBuffer);
+
+    return 0;
+}
+
+static void
+D3D_RenderPresent(SDL_Renderer * renderer)
+{
+    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
+    HRESULT result;
+
+    if (!data->beginScene) {
+        IDirect3DDevice9_EndScene(data->device);
+        data->beginScene = SDL_TRUE;
+    }
+
+    result = IDirect3DDevice9_TestCooperativeLevel(data->device);
+    if (result == D3DERR_DEVICELOST) {
+        /* We'll reset later */
+        return;
+    }
+    if (result == D3DERR_DEVICENOTRESET) {
+        D3D_Reset(renderer);
+    }
+    result = IDirect3DDevice9_Present(data->device, NULL, NULL, NULL, NULL);
+    if (FAILED(result)) {
+        D3D_SetError("Present()", result);
+    }
+}
+
+static void
+D3D_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
+{
+    D3D_TextureData *data = (D3D_TextureData *) texture->driverdata;
+
+    if (!data) {
+        return;
+    }
+    if (data->texture) {
+        IDirect3DTexture9_Release(data->texture);
+    }
+    SDL_free(data);
+    texture->driverdata = NULL;
+}
+
+static void
+D3D_DestroyRenderer(SDL_Renderer * renderer)
+{
+    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
+
+    if (data) {
+        if (data->device) {
+            IDirect3DDevice9_Release(data->device);
+        }
+        if (data->d3d) {
+            IDirect3D9_Release(data->d3d);
+            SDL_UnloadObject(data->d3dDLL);
+        }
+        SDL_free(data);
+    }
+    SDL_free(renderer);
+}
+
+#endif /* SDL_VIDEO_RENDER_D3D */
+
+/* vi: set ts=4 sw=4 expandtab: */