diff --git a/src/gfxlib2/win32/fb_gfx_win32.h b/src/gfxlib2/win32/fb_gfx_win32.h index 0e727112e0..577bc44d57 100644 --- a/src/gfxlib2/win32/fb_gfx_win32.h +++ b/src/gfxlib2/win32/fb_gfx_win32.h @@ -65,6 +65,7 @@ typedef struct { extern WIN32DRIVER fb_win32; #ifndef HOST_CYGWIN +extern GFXDRIVER fb_gfxDriverD2D; extern GFXDRIVER fb_gfxDriverDirectDraw; #endif extern GFXDRIVER fb_gfxDriverGDI; diff --git a/src/gfxlib2/win32/gfx_driver_d2d.c b/src/gfxlib2/win32/gfx_driver_d2d.c new file mode 100644 index 0000000000..df6b471cce --- /dev/null +++ b/src/gfxlib2/win32/gfx_driver_d2d.c @@ -0,0 +1,881 @@ +#define CINTERFACE +#define COBJMACROS +#define D2D_USE_C_DEFINITIONS +/* This file requires a Windows SDK capable of targeting Windows 7 */ +#define _WIN32_WINNT 0x0601 + +#ifndef HOST_CYGWIN + +#include "../fb_gfx.h" +#include "fb_gfx_win32.h" +#include +#include +#include +#include +#include "gfx_driver_d2d_fixed.h" + +/* Defining DEBUG or D2D_DEBUG will turn on debugging for Direct2D & Direct3D +// (if it's used). To see any output from them you'll need to install +// the debug layer and have a debugger attached to the process +// (DebugView won't cut it) +// If you're finding you're getting fails on D3D10CreateDevice +// or D2D1CreateFactory with this defined, it's likely you don't +// have the debug layers installed - See here https://pastebin.com/8ptZxmim +// +// These defines will also turn on debug printing of any unexpected D2D/D3D failures +// (DebugView is fine for these) and throw an exception (C000003E, -1073741762) +// if the global state becomes corrupted +*/ +#if defined(DEBUG) || defined(D2D_DEBUG) +/* ntstatus.h doesn't define the NTSTATUS type :-( */ +typedef long NTSTATUS; +#include +#define DO_DEBUG +#define D2D1_FACTORY_INFO_TYPE D2D1_DEBUG_LEVEL_INFORMATION +#define D3D10_CREATE_DEVICE_DEBUG_FLAG D3D10_CREATE_DEVICE_DEBUG + +static void PrintDebugText(const char* pFmt, ...) +{ + char text[300] = {0}; + va_list args; + va_start(args, pFmt); + vsnprintf(text, ARRAYSIZE(text) - 1, pFmt, args); + OutputDebugStringA(text); + /* DebugView will put each message on its own line, WinDbg needs a newline */ + OutputDebugStringA("\n"); + va_end(args); +} + +static BOOL PrintFailedHResult(HRESULT hr, const char* pFailure, int line) +{ + BOOL didFail = FAILED(hr); + if(didFail) + { + PrintDebugText("%s failed with hresult %#lx on line %d", pFailure, hr, line); + } + return didFail; +} + +#define NOTIFY_FAILED_HR(Func) \ + PrintFailedHResult(Func, #Func, __LINE__) + +#define DBG_TEXT(string, ...) \ + PrintDebugText(string, __FUNCTION__, __VA_ARGS__) + +#else /* DEBUG || D2D_DEBUG */ +#define D2D1_FACTORY_INFO_TYPE D2D1_DEBUG_LEVEL_NONE +#define D3D10_CREATE_DEVICE_DEBUG_FLAG 0 +#define NOTIFY_FAILED_HR(x) FAILED(x) +#define DBG_TEXT(string, ...) (void)0 +#endif + +static int D2DDriverInit(char *title, int w, int h, int depth, int refresh_rate, int flags); +static int* D2DFetchModes(int depth, int* size); + +GFXDRIVER fb_gfxDriverD2D = { + "Direct2D", /* char *name; */ + &D2DDriverInit, /* int (*init)(int w, int h, char *title, int fullscreen); */ + &fb_hWin32Exit, /* void (*exit)(void); */ + &fb_hWin32Lock, /* void (*lock)(void); */ + &fb_hWin32Unlock, /* void (*unlock)(void); */ + &fb_hWin32SetPalette, /* void (*set_palette)(int index, int r, int g, int b); */ + &fb_hWin32WaitVSync, /* void (*wait_vsync)(void); */ + &fb_hWin32GetMouse, /* int (*get_mouse)(int *x, int *y, int *z, int *buttons, int *clip); */ + &fb_hWin32SetMouse, /* void (*set_mouse)(int x, int y, int cursor, int clip); */ + &fb_hWin32SetWindowTitle,/* void (*set_window_title)(char *title); */ + &fb_hWin32SetWindowPos, /* int (*set_window_pos)(int x, int y); */ + &D2DFetchModes, /* int *(*fetch_modes)(int depth, int *size); */ + NULL, /* void (*flip)(void); */ + NULL /* void (*poll_events)(void); */ +}; + +#define DX_GUID(id,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID id = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} + +/* IIDs of various interfaces so we don't need to link uuid.lib +// which is bad because of bloat +*/ +DX_GUID(__fb_IID_IDXGIFactory, 0x7b7166ec, 0x21c7, 0x44ae, 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69); +DX_GUID(__fb_IID_ID2D1Factory, 0x06152247, 0x6f50, 0x465a, 0x92, 0x45, 0x11, 0x8b, 0xfd, 0x3b, 0x60, 0x07); +DX_GUID(__fb_IID_IDXGISurface, 0xcafcb56c, 0x6ac3, 0x4889, 0xbf, 0x47, 0x9e, 0x23, 0xbb, 0xd2, 0x60, 0xec); +DX_GUID(__fb_IID_ID2D1GdiInteropRenderTarget, 0xe0db51c3, 0x6f77, 0x4bae, 0xb3, 0xd5, 0xe4, 0x75, 0x09, 0xb3, 0x58, 0x38); +DX_GUID(__fb_IID_ID2D1RenderTarget, 0x2cd90694, 0x12e2, 0x11dc, 0x9f, 0xed, 0x00, 0x11, 0x43, 0xa0, 0x55, 0xf9); + +/* Direct2D HwndRenderTargets only support 32-bpp formats +// https://docs.microsoft.com/en-gb/windows/win32/direct2d/supported-pixel-formats-and-alpha-modes#supported-formats-for-id2d1hwndrendertarget +*/ +static const DXGI_FORMAT THIRTY_TWO_BPP_FORMATS[] = {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN}; +/* Pick numbers unlikely to be used by somebody else */ +static const USHORT D2D_PROP_ID = 0x56A2; + +/* Prototypes for external functions */ +typedef HRESULT (WINAPI*pfnCreateDXGIFactory)(REFIID riid, void** ppFactory); +typedef HRESULT (WINAPI*pfnCreateD2D1Factory)( + D2D1_FACTORY_TYPE factoryType, + REFIID riid, + CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, + void** ppIFactory +); +typedef BOOL (WINAPI*pfnUpdateLayeredWindow)( + HWND hwnd, + HDC hdcDst, + POINT* pDest, + SIZE* pSize, + HDC hdcSrc, + POINT* pSrc, + COLORREF crKey, + BLENDFUNCTION* pBlendFn, + DWORD dwFlags +); +typedef HRESULT (WINAPI*pfnD3D10CreateDevice1)( + IDXGIAdapter* pAdapter, + D3D10_DRIVER_TYPE type, + HMODULE hMod, + UINT flags, + D3D10_FEATURE_LEVEL1 level, + UINT sdkver, + ID3D10Device1** ppDevice +); + +/* and for internal functions */ +struct D2DGlobalState; +typedef void (*pfnD2DShutdown)(struct D2DGlobalState* pState); +typedef void (*pfnD2DPaint)(struct D2DGlobalState* pState, const D2D1_RECT_U* pDirtyRect); +typedef int (*pfnD2DSetup)(struct D2DGlobalState* ppState, HWND hwnd); + +typedef struct D2DGlobalState +{ +#ifdef DO_DEBUG + ULONG_PTR cookie; +#endif + WNDPROC wndProcCookie; /* The original window proc of fb_win32.wnd */ + DXGI_FORMAT renderFormat; /* The format we're rendering to */ + HMODULE hD2D; /* d2d1.dll handle */ + ID2D1Factory* pD2DFactory;/* The D2D creation object */ + ID2D1RenderTarget* pRenderTarget; /* Target that we render to */ + ID2D1Bitmap* pBackBufferBitmap; /* The bitmap, filled with data from fb_gfx->framebuffer, that we render */ + + pfnD2DShutdown Shutdown; /* Function pointer for mode specific cleanup */ + pfnD2DSetup Setup; /* Function pointer for doing setup specific to whichever mode */ + pfnD2DPaint Paint; /* Function pointer for mode specific rendering */ + + unsigned char* pTempBuffer;/* Optional intermediate buffer used during RGB->BGR conversion */ + BLITTER* pBlitter; /* Option FB Blitter function to do any required color conversion */ + BYTE vkToFBKeyTranslation[256]; /* Translation table for VK_ value to the appropriate index in __fb_keytable */ + + /* Creating shaped windows still requires + // interop with GDI, which complicates things a lot. + // In order to get hardware accelerated rendering, we need to + // involve all of Direct3D, DXGI, Direct2D & GDI + // This is what the GdiInterop functions achieve. + // + // (And now you know why The Brotherhood of NOD + // wanted to get rid of GDI once and for all :-) + // https://msdn.microsoft.com/en-us/magazine/ee819134.aspx + // + // If the user doesn't want shaped windows, then we can just use + // D2D directly to render to the window + // This is faster and simpler and what the D2DDirect functions do + */ + union RenderParams + { + /* These are used by the GDIInterop functions */ + struct LayeredWindowRenderParams + { + HMODULE hD3D; /* D3D10_1.dll handle */ + ID3D10Device1* pD3DDevice; /* The D3D Device */ + IDXGISurface* pBackBuffer; /* The DXGI interface of the Direct3D Texture2D */ + ID2D1GdiInteropRenderTarget* pGDITarget; /* The D2D interface that'll give us a GDI HDC */ + pfnUpdateLayeredWindow updateLayeredWindow; /* The function from User32 that'll update any transparent areas */ + } Layered; + /* These are used by the D2DDirect functions */ + struct NonLayeredRenderParams + { + ID2D1HwndRenderTarget* pHwndTarget; /* The guy that lets us render to a window */ + } NonLayered; + } RenderParams; +} D2DGlobalState; + +/* We could statically allocate the state as a global, but that would +// increase the size of apps even further, so we dynamicaly allocate it +// and attach it to our render window, thus they don't pay for it if we're +// not in use +*/ +static D2DGlobalState* GetGlobalState(HWND hwnd) +{ + D2DGlobalState* pState = GetProp(hwnd, MAKEINTRESOURCE(D2D_PROP_ID)); + +#ifdef DO_DEBUG + if(pState) { + if(IsBadReadPtr(pState, sizeof(*pState)) || (pState->cookie != ~PtrToUlong(&GetGlobalState))) { + ULONG_PTR arg = (ULONG_PTR)"Direct2D GFXDriver State Corruption"; + RaiseException(STATUS_DATA_ERROR, 0, 1, &arg); + } + } +#endif + return pState; +} + +static D2DGlobalState* CreateGlobalState(HWND hwnd, HMODULE hD2D, ID2D1Factory* pFactory, WNDPROC wndprocCookie) +{ + D2DGlobalState* pState = calloc(1, sizeof(*pState)); + + if(pState) { + int i = 0; +#ifdef DO_DEBUG + pState->cookie = ~PtrToUlong(&GetGlobalState); +#endif + pState->hD2D = hD2D; + pState->wndProcCookie = wndprocCookie; + pState->pD2DFactory = pFactory; + for(; i < 256; ++i) { + if(__fb_keytable[i][1]) { + pState->vkToFBKeyTranslation[__fb_keytable[i][1]] = (BYTE)i; + } + } + if(!SetProp(hwnd, MAKEINTRESOURCE(D2D_PROP_ID), (HANDLE)pState)) { + free(pState); + pState = NULL; + } + } + DBG_TEXT("%s - returning state at %p", pState); + return pState; +} + +static LRESULT CALLBACK D2DWndProcSubclass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + D2DGlobalState* pGlobalState = GetGlobalState(hwnd); + + if(!pGlobalState) return DefWindowProc(hwnd, msg, wParam, lParam); + switch(msg) { + /* The other Win32 graphic drivers do the input handling in + // the main thread loop using GetKeyState, which happens even + // if nothing has changed. + // Since that's just a reflection of the state of the keys as per + // the message loop, we subclass and react here and update as the changes + // arrive rather than poll. We have to subclass as a) the WndProc is in + // gfx_win32.c and b) contains a lot of important stuff + */ + case WM_KEYDOWN: + case WM_SYSKEYDOWN: { + BYTE fbKeyIndex = pGlobalState->vkToFBKeyTranslation[wParam]; + BYTE alternateKey = __fb_keytable[fbKeyIndex][2]; + DBG_ASSERT(wParam < 256); + if(alternateKey) { + __fb_gfx->key[fbKeyIndex] = (0x80 | ((GetKeyState(alternateKey) & 0x8000) ? TRUE : FALSE)); + } + else { + __fb_gfx->key[fbKeyIndex] = TRUE; + } + } + break; + case WM_KEYUP: + case WM_SYSKEYUP: { + BYTE fbKeyIndex = pGlobalState->vkToFBKeyTranslation[wParam]; + BYTE alternateKey = __fb_keytable[fbKeyIndex][2]; + DBG_ASSERT(wParam < 256); + if(alternateKey) { + __fb_gfx->key[fbKeyIndex] = (GetKeyState(alternateKey) & 0x8000) ? TRUE : FALSE; + } + else { + __fb_gfx->key[fbKeyIndex] = FALSE; + } + } + break; + } + return CallWindowProc(pGlobalState->wndProcCookie, hwnd, msg, wParam, lParam); +} + +static HWND CreateTheWindow(WNDPROC* pOriginalProc) +{ + HWND hwnd = NULL; + RECT winSize = {0}; + RECT monitorSize = {0}; + DWORD windowStyle = 0; + DWORD exStyle = 0; + DEVMODE mode = {}; + int fbFlags = fb_win32.flags; + HMONITOR hCurMonitor = fb_win32.monitor; + MONITORINFOEX mInf; + BOOL isFullScreen = (fbFlags & DRIVER_FULLSCREEN); + + mode.dmSize = sizeof(mode); + mInf.cbSize = sizeof(mInf); + fb_win32.GetMonitorInfo(hCurMonitor, (MONITORINFO*)&mInf); + monitorSize = mInf.rcMonitor; + if(!(isFullScreen || (fbFlags & DRIVER_NO_FRAME))) { + windowStyle |= (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME); + } + else { + windowStyle |= WS_POPUP; + } + if(fbFlags & DRIVER_NO_SWITCH) { + windowStyle &= ~WS_MAXIMIZEBOX; + } + winSize.right = fb_win32.w; + winSize.bottom = fb_win32.h; + winSize.left = winSize.top = 0; + if(isFullScreen) { + mode.dmPelsWidth = fb_win32.w; + mode.dmPelsHeight = fb_win32.h; + mode.dmBitsPerPel = fb_win32.depth; + mode.dmDisplayFrequency = fb_win32.refresh_rate; + mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; + if(fb_win32.ChangeDisplaySettingsEx(mInf.szDevice, &mode, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL) { + mode.dmDisplayFrequency = 0; + mode.dmFields ^= DM_DISPLAYFREQUENCY; + if(fb_win32.ChangeDisplaySettingsEx(mInf.szDevice, &mode, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL) { + return NULL; + } + } + DBG_TEXT("%s - Successfully full screen on display %s", mInf.szDevice); + } + else { + exStyle = (fbFlags & DRIVER_SHAPED_WINDOW) ? WS_EX_LAYERED : 0; + AdjustWindowRect(&winSize, windowStyle, FALSE); + OffsetRect(&winSize, (monitorSize.right - winSize.right - winSize.left) / 2, (monitorSize.bottom - winSize.bottom - winSize.top) / 2); + } + if(fb_hInitWindow(windowStyle, exStyle, winSize.left, winSize.top, winSize.right - winSize.left, winSize.bottom - winSize.top)) { + return NULL; + } + if(EnumDisplaySettings(mInf.szDevice, ENUM_CURRENT_SETTINGS, &mode)) { + DBG_TEXT("%s - current refresh rate is %d", mode.dmDisplayFrequency); + __fb_gfx->refresh_rate = mode.dmDisplayFrequency; + } + hwnd = fb_win32.wnd; + *pOriginalProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)&D2DWndProcSubclass); + DBG_TEXT("%s - Created window at %p, size=%dx%d, original wndproc at %p", hwnd, winSize.right - winSize.left, winSize.bottom - winSize.top, *pOriginalProc); + return hwnd; +} + +static void GdiInteropPaint(D2DGlobalState* pGlobalState, const D2D1_RECT_U* pDirtyRect) +{ + struct LayeredWindowRenderParams* pLp = &pGlobalState->RenderParams.Layered; + HDC hdc = NULL; + /* The empty rect tels D2D that we didn't + // write to the DC, passing NULL instead of this + // indicates a full redraw, which we definitely don't want + */ + RECT empty = {}; + SIZE winSize = {fb_win32.w, fb_win32.h}; + POINT origin = {}; + + ID2D1GdiInteropRenderTarget_GetDC(pLp->pGDITarget, D2D1_DC_INITIALIZE_MODE_COPY, &hdc); + pLp->updateLayeredWindow(fb_win32.wnd, NULL, NULL, &winSize, hdc, &origin, MASK_COLOR_32, NULL, ULW_COLORKEY); + ID2D1GdiInteropRenderTarget_ReleaseDC(pLp->pGDITarget, &empty); +} + +static void GdiInteropShutdown(D2DGlobalState* pGlobalState) +{ + struct LayeredWindowRenderParams* pLp = &pGlobalState->RenderParams.Layered; + + ID2D1GdiInteropRenderTarget_Release(pLp->pGDITarget); + IDXGISurface_Release(pLp->pBackBuffer); + ID3D10Device1_Release(pLp->pD3DDevice); + FreeLibrary(pLp->hD3D); +} + +static int GdiInteropSetup(D2DGlobalState* pGlobalState, HWND hwnd) +{ + int ret = -1; + ID3D10Device1* pD3DDevice = NULL; + ID3D10Texture2D* pD3DTexture = NULL; + IDXGISurface* pDXGISurface = NULL; + ID2D1RenderTarget* pRenderTarget = NULL; + ID2D1GdiInteropRenderTarget* pGdiTarget = NULL; + ID2D1Bitmap* pBitmap = NULL; + HMODULE hD3D = LoadLibrary("d3d10_1.dll"); + + if(hD3D != NULL) { + D2D1_RENDER_TARGET_PROPERTIES renderProps = {}; + D2D1_BITMAP_PROPERTIES bitmapProps = {}; + D2D1_SIZE_U bitmapSize = {}; + D3D10_TEXTURE2D_DESC texDesc = {}; + HMODULE hUser32 = GetModuleHandle("user32.dll"); + struct LayeredWindowRenderParams* pLayeredParams = &pGlobalState->RenderParams.Layered; + + pfnD3D10CreateDevice1 CreateD3D10Device = (pfnD3D10CreateDevice1)GetProcAddress(hD3D, "D3D10CreateDevice1"); + if(CreateD3D10Device == NULL) { + goto errorReturn; + } + if(NOTIFY_FAILED_HR(CreateD3D10Device( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_SINGLETHREADED | D3D10_CREATE_DEVICE_BGRA_SUPPORT | D3D10_CREATE_DEVICE_DEBUG_FLAG, + D3D10_FEATURE_LEVEL_10_0, + D3D10_1_SDK_VERSION, + &pD3DDevice + ))) { + goto errorReturn; + } + texDesc.ArraySize = texDesc.MipLevels = texDesc.SampleDesc.Count = 1; + texDesc.BindFlags = D3D10_BIND_RENDER_TARGET; + texDesc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE; + texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + bitmapSize.width = texDesc.Width = fb_win32.w; + bitmapSize.height = texDesc.Height = fb_win32.h; + if(NOTIFY_FAILED_HR(ID3D10Device1_CreateTexture2D(pD3DDevice, &texDesc, NULL, &pD3DTexture))) { + goto errorReturn; + } + if(NOTIFY_FAILED_HR(ID3D10Texture2D_QueryInterface(pD3DTexture, &__fb_IID_IDXGISurface, (void**)&pDXGISurface))) { + goto errorReturn; + } + /* We don't need this interface, just the DXGI one */ + ID3D10Texture2D_Release(pD3DTexture); + pD3DTexture = NULL; + renderProps.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE; + renderProps.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE; + if(NOTIFY_FAILED_HR(ID2D1Factory_CreateDxgiSurfaceRenderTarget(pGlobalState->pD2DFactory, pDXGISurface, &renderProps, &pRenderTarget))) { + goto errorReturn; + } + bitmapProps.pixelFormat.format = texDesc.Format; + bitmapProps.pixelFormat.alphaMode = renderProps.pixelFormat.alphaMode; + if(NOTIFY_FAILED_HR(ID2D1RenderTarget_CreateBitmap(pRenderTarget, bitmapSize, NULL, bitmapSize.width * 4, &bitmapProps, &pBitmap))) { + goto errorReturn; + } + if(NOTIFY_FAILED_HR(ID2D1RenderTarget_QueryInterface(pRenderTarget, &__fb_IID_ID2D1GdiInteropRenderTarget, (void**)&pGdiTarget))) { + goto errorReturn; + } + if(!(pLayeredParams->updateLayeredWindow = (pfnUpdateLayeredWindow)GetProcAddress(hUser32, "UpdateLayeredWindow"))) + { + goto errorReturn; + } + pLayeredParams->hD3D = hD3D; + pLayeredParams->pD3DDevice = pD3DDevice; + pLayeredParams->pBackBuffer = pDXGISurface; + pLayeredParams->pGDITarget = pGdiTarget; + pGlobalState->pRenderTarget = pRenderTarget; + pGlobalState->pBackBufferBitmap = pBitmap; + pGlobalState->Paint = &GdiInteropPaint; + pGlobalState->Setup = &GdiInteropSetup; + pGlobalState->Shutdown = &GdiInteropShutdown; + /* We don't need to set a blitter because if we're not BRGA, we don't get this far */ + pGlobalState->pBlitter = NULL; + ret = 0; + } + return ret; +errorReturn: + if(pGdiTarget) ID2D1GdiInteropRenderTarget_Release(pGdiTarget); + if(pBitmap) ID2D1Bitmap_Release(pBitmap); + if(pRenderTarget) ID2D1RenderTarget_Release(pRenderTarget); + if(pDXGISurface) IDXGISurface_Release(pDXGISurface); + if(pD3DTexture) ID3D10Texture2D_Release(pD3DTexture); + if(pD3DDevice) ID3D10Device1_Release(pD3DDevice); + if(hD3D) FreeLibrary(hD3D); + return ret; +} + +static void D2DDirectShutdown(D2DGlobalState* pGlobalState) +{ + struct NonLayeredRenderParams* pNlp = &pGlobalState->RenderParams.NonLayered; + + ID2D1HwndRenderTarget_Release(pNlp->pHwndTarget); +} + +static int D2DDirectSetup(D2DGlobalState* pGlobalState, HWND hwnd) +{ + int ret = -1; + struct NonLayeredRenderParams* pNlp = &pGlobalState->RenderParams.NonLayered; + ID2D1HwndRenderTarget* pHwndRenderTarget = NULL; + ID2D1RenderTarget* pRenderTarget = NULL; + ID2D1Bitmap* pBackBufferBitmap = NULL; + const DXGI_FORMAT* pFormats = THIRTY_TWO_BPP_FORMATS; + D2D1_RENDER_TARGET_PROPERTIES renderProps = {0}; + D2D1_HWND_RENDER_TARGET_PROPERTIES hwndRenderProps = {0}; + D2D1_BITMAP_PROPERTIES bitmapProps = {}; + UINT stride = 0; + D2D1_SIZE_U actualSize = {0}; + + hwndRenderProps.hwnd = hwnd; + hwndRenderProps.presentOptions = D2D1_PRESENT_OPTIONS_IMMEDIATELY; + hwndRenderProps.pixelSize.height = fb_win32.h; + hwndRenderProps.pixelSize.width = fb_win32.w; + stride = hwndRenderProps.pixelSize.width * 4; + renderProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT; + renderProps.usage = D2D1_RENDER_TARGET_USAGE_NONE; + renderProps.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE; + while(*pFormats != DXGI_FORMAT_UNKNOWN) { + renderProps.pixelFormat.format = *(pFormats++); + if(!NOTIFY_FAILED_HR(ID2D1Factory_CreateHwndRenderTarget( + pGlobalState->pD2DFactory, + &renderProps, + &hwndRenderProps, + &pHwndRenderTarget + ))) { + break; + } + } + if(!pHwndRenderTarget) { + goto errorReturn; + } + bitmapProps.pixelFormat = renderProps.pixelFormat; + if(NOTIFY_FAILED_HR(ID2D1HwndRenderTarget_CreateBitmap( + pHwndRenderTarget, + hwndRenderProps.pixelSize, + NULL, + stride, + &bitmapProps, + &pBackBufferBitmap + ))) + { + goto errorReturn; + } + ID2D1Bitmap_GetPixelSize(pBackBufferBitmap, &actualSize); + DBG_TEXT("%s created bitmap of size %lux%lu, stride = %lu", actualSize.width, actualSize.height, stride); + /* if no colour conversion is necessary, then we can directly + // use the framebuffer, no blitter is required. + // + // Otherwise we need a blitter and a temp buffer, since you can't map or lock D2D bitmaps + */ + if(renderProps.pixelFormat.format < DXGI_FORMAT_B8G8R8A8_UNORM) { + pGlobalState->pBlitter = fb_hGetBlitter(32, TRUE); + if(!(pGlobalState->pTempBuffer = calloc(1, hwndRenderProps.pixelSize.height * stride))) { + goto errorReturn; + } + } + if(NOTIFY_FAILED_HR(ID2D1HwndRenderTarget_QueryInterface(pHwndRenderTarget, &__fb_IID_ID2D1RenderTarget, (void**)&pRenderTarget))) { + goto errorReturn; + } + pNlp->pHwndTarget = pHwndRenderTarget; + pGlobalState->pRenderTarget = pRenderTarget; + pGlobalState->pBackBufferBitmap = pBackBufferBitmap; + pGlobalState->Shutdown = &D2DDirectShutdown; + pGlobalState->Setup = &D2DDirectSetup; + /* CommonPaint does everything the D2DDirect method needs */ + pGlobalState->Paint = NULL; + ret = 0; + + return ret; +errorReturn: + if(pBackBufferBitmap) ID2D1Bitmap_Release(pBackBufferBitmap); + if(pHwndRenderTarget) ID2D1HwndRenderTarget_Release(pHwndRenderTarget); + return ret; +} + +static void D2DCommonPaintInternal(D2DGlobalState* pGlobalState) +{ + HRESULT hr = S_OK; + ID2D1RenderTarget* pRT = pGlobalState->pRenderTarget; + ID2D1Bitmap* pBackBuffer = pGlobalState->pBackBufferBitmap; + const int winWidth = fb_win32.w, winHeight = fb_win32.h; + D2D1_RECT_U dirtyRect; + D2D1_RECT_F drawRect; + char* pDirtyStart, *pDirtyEnd; + char* pFirstDirtyRow = NULL, *pLastDirtyRow = NULL; + unsigned char* pBitmapDataSrc = NULL; + const UINT stride = winWidth * 4; + const ULONG ptrSize = sizeof(ULONG_PTR); + ULONG_PTR dirtyRows; + ULONG bitPos; + + /* this seems to be the only case for us with 24/32bpp on Windows + // but document it, since it has the ability to not be 1 + */ + DBG_ASSERT(__fb_gfx->scanline_size == 1); + + fb_gfxDriverD2D.lock(); + + pDirtyStart = __fb_gfx->dirty; + pDirtyEnd = pDirtyStart + winHeight; + pFirstDirtyRow = memchr(pDirtyStart, TRUE, winHeight); + /* nothing's dirty, don't do anything */ + if(!pFirstDirtyRow) { + fb_gfxDriverD2D.unlock(); + return; + } + /* why is there strchr and strrchr, but not memchr and memrchr? + // anyway, since we're only searching for TRUE and not any character + // we optimize and look at ptr-size bytes per time, instead of just looping + // over single ones. + */ + while(PtrToUlong(pDirtyEnd) & (ptrSize - 1)) { + if(*(--pDirtyEnd)) { + pLastDirtyRow = pDirtyEnd; + goto fillInDirtyRect; + } + } + while((dirtyRows = *(ULONG_PTR*)(pDirtyEnd -= ptrSize)) == 0); +#ifdef _WIN64 + BitScanReverse64(&bitPos, dirtyRows); +#else + BitScanReverse(&bitPos, dirtyRows); +#endif + pLastDirtyRow = pDirtyEnd + ((bitPos >> 3) & (ptrSize - 1)); +fillInDirtyRect: + dirtyRect.left = 0; + dirtyRect.right = winWidth - 1; + dirtyRect.top = (pFirstDirtyRow - pDirtyStart); + dirtyRect.bottom = (pLastDirtyRow - pDirtyStart); + drawRect.left = dirtyRect.left; + drawRect.right = dirtyRect.right; + drawRect.top = dirtyRect.top; + drawRect.bottom = dirtyRect.bottom; + + DBG_TEXT("%s - dirty rect L-%lu, T-%lu, R-%lu, B-%lu", dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom); + + /* if we need to convert from RGB to BGR, then do that + // otherwise we can just direcly use the framebuffer + */ + if(pGlobalState->pBlitter) { + pGlobalState->pBlitter(pBitmapDataSrc = pGlobalState->pTempBuffer, stride); + } + else { + pBitmapDataSrc = __fb_gfx->framebuffer; + } + pBitmapDataSrc += (stride * dirtyRect.top); + + ID2D1Bitmap_CopyFromMemory(pBackBuffer, &dirtyRect, pBitmapDataSrc, stride); + + ID2D1RenderTarget_BeginDraw(pRT); + ID2D1RenderTarget_DrawBitmap(pRT, pBackBuffer, &drawRect, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, &drawRect); + if(pGlobalState->Paint) + { + pGlobalState->Paint(pGlobalState, &dirtyRect); + } + hr = ID2D1RenderTarget_EndDraw(pRT, NULL, NULL); + /* If we have to reset the drawing surfaces, we mark it all dirty + // otherwise we mark it all clean */ + fb_hMemSet(pDirtyStart, SUCCEEDED(hr) ? FALSE : TRUE, winHeight); + + fb_gfxDriverD2D.unlock(); + + /* This value is cast to HRESULT in the headers in the Microsoft SDK + // not so much in MinGW's + */ + if(hr == (HRESULT)D2DERR_RECREATE_TARGET) { + DBG_TEXT("%s - told to recreate targets", 0); + pGlobalState->Shutdown(pGlobalState); + pGlobalState->Setup(pGlobalState, fb_win32.wnd); + } +} + +static int D2DCommonInit() +{ + int ret = -1; + D2DGlobalState* pGlobalState = NULL; + HMODULE hD2D = LoadLibrary("d2d1.dll"); + ID2D1Factory* pFactory = NULL; + HWND hwnd = NULL; + + DBG_TEXT("%s called", 0); + if(hD2D != NULL) { + D2D1_FACTORY_OPTIONS opts = {D2D1_FACTORY_INFO_TYPE}; + WNDPROC wndProcCookie = NULL; + pfnCreateD2D1Factory D2DCreateFactory = (pfnCreateD2D1Factory)GetProcAddress(hD2D, "D2D1CreateFactory"); + + if((D2DCreateFactory == NULL) || + (NOTIFY_FAILED_HR(D2DCreateFactory( + D2D1_FACTORY_TYPE_SINGLE_THREADED, + &__fb_IID_ID2D1Factory, + &opts, + (void**)&pFactory)) + )) { + goto errorReturn; + } + hwnd = CreateTheWindow(&wndProcCookie); + if(hwnd == NULL) { + goto errorReturn; + } + if(!(pGlobalState = CreateGlobalState(hwnd, hD2D, pFactory, wndProcCookie))) { + goto errorReturn; + } + if(fb_win32.flags & DRIVER_SHAPED_WINDOW) { + ret = GdiInteropSetup(pGlobalState, hwnd); + } + else { + ret = D2DDirectSetup(pGlobalState, hwnd); + } + if(ret == 0) { + ShowWindow(hwnd, SW_SHOWNORMAL); + } + } + if(ret != 0) { +errorReturn: + if(hwnd) DestroyWindow(hwnd); + if(pGlobalState) free(pGlobalState); + if(pFactory) ID2D1Factory_Release(pFactory); + if(hD2D) FreeLibrary(hD2D); + } + return ret; +} + +static void D2DCommonPaint() +{ + D2DGlobalState* pGlobalState = GetGlobalState(fb_win32.wnd); + + if(pGlobalState) { + D2DCommonPaintInternal(pGlobalState); + } +} + +static void D2DCommonShutdown() +{ + HWND hwnd = fb_win32.wnd; + D2DGlobalState* pGlobalState = GetGlobalState(hwnd); + + DBG_TEXT("%s called with hwnd=%p, pState=%p", hwnd, pGlobalState); + if(hwnd) { + DestroyWindow(hwnd); + fb_win32.wnd = NULL; + } + if(pGlobalState) { + ID2D1Bitmap_Release(pGlobalState->pBackBufferBitmap); + ID2D1RenderTarget_Release(pGlobalState->pRenderTarget); + pGlobalState->Shutdown(pGlobalState); + if(fb_win32.flags & DRIVER_FULLSCREEN) { + MONITORINFOEX mInf; + mInf.cbSize = sizeof(mInf); + fb_win32.GetMonitorInfo(fb_win32.monitor, (MONITORINFO*)&mInf); + fb_win32.ChangeDisplaySettingsEx(mInf.szDevice, NULL, NULL, 0, NULL); + } + ID2D1Factory_Release(pGlobalState->pD2DFactory); + FreeLibrary(pGlobalState->hD2D); + free(pGlobalState->pTempBuffer); + free(pGlobalState); + } +} + +static +#ifdef HOST_MINGW +unsigned int +#else +DWORD +#endif +WINAPI D2DRenderThread(void* p) +{ + DBG_TEXT("%s started", 0); + if(D2DCommonInit()) return 1; + fb_win32.is_active = TRUE; + + SetEvent((HANDLE)p); + while(fb_win32.is_running) { + DWORD ret = MsgWaitForMultipleObjects(0, NULL, TRUE, 10, QS_ALLINPUT); + + if(ret == WAIT_OBJECT_0) { + fb_hHandleMessages(); + } + D2DCommonPaint(); + } + DBG_TEXT("%s exiting", 0); + D2DCommonShutdown(); + return 0; +} + +static int D2DDriverInit(char *title, int w, int h, int depth, int refresh_rate, int flags) +{ + DBG_TEXT("%s called with title=%s, res=%dx%d, depth=%d, refresh_rate=%d, flags=%#x", title, w, h, depth, refresh_rate, flags); + if ((flags & DRIVER_OPENGL) || (depth < 24) || ((GetVersion() & 0xff) < 6)) + return -1; + fb_hMemSet(&fb_win32, 0, sizeof(fb_win32)); + fb_win32.init = &D2DCommonInit; + fb_win32.exit = &D2DCommonShutdown; + fb_win32.paint = &D2DCommonPaint; + fb_win32.thread = &D2DRenderThread; + + return fb_hWin32Init(title, w, h, depth, refresh_rate, flags); +} + +typedef struct MiniVector +{ + int* pData; + int elems; + int capacity; +} MiniVector; + +static void D2DPushToVector(MiniVector* pVector, int elemToAdd) +{ + if(pVector->elems == pVector->capacity) + { + int newCapacity = pVector->capacity + 20; + int* pNewData = realloc(pVector->pData, newCapacity * sizeof(*pNewData)); + if(!pNewData) + { + return; + } + pVector->pData = pNewData; + pVector->capacity = newCapacity; + } + pVector->pData[pVector->elems++] = elemToAdd; +} + +static void D2DEnumOutputModes(IDXGIOutput* pOutput, MiniVector* pDepthModes) +{ + UINT numOutputModes = 0; + const DXGI_FORMAT* pFormats = THIRTY_TWO_BPP_FORMATS; + + while(*pFormats != DXGI_FORMAT_UNKNOWN) { + IDXGIOutput_GetDisplayModeList(pOutput, *pFormats, DXGI_ENUM_MODES_INTERLACED, &numOutputModes, NULL); + if(numOutputModes != 0) { + DXGI_MODE_DESC* pModeList = calloc(sizeof(*pModeList), numOutputModes); + if(pModeList && (!NOTIFY_FAILED_HR(IDXGIOutput_GetDisplayModeList(pOutput, *pFormats, DXGI_ENUM_MODES_INTERLACED, &numOutputModes, pModeList)))) { + UINT i; + for(i = 0; i < numOutputModes; ++i) { + int mode = ((pModeList[i].Width << 16) | pModeList[i].Height); + DBG_TEXT("%s found mode %dx%d - %fHz", pModeList[i].Width, pModeList[i].Height, ((float)pModeList[i].RefreshRate.Numerator / pModeList[i].RefreshRate.Denominator)); + D2DPushToVector(pDepthModes, mode); + } + } + free(pModeList); + /* got some (or rather should have) so exit */ + break; + } + ++pFormats; + } +} + +static int* D2DFetchModes(int depth, int *size) +{ + IDXGIFactory* pFactory = NULL; + pfnCreateDXGIFactory CreateDXGIFactory = NULL; + HMODULE hDxgi = NULL; + UINT adapterIter = 0; + IDXGIAdapter* pAdapter = NULL; + MiniVector hardwareModes = {NULL, 0, 0}; + MiniVector softwareModes = {NULL, 0, 0}; + DBG_TEXT("%s called with depth=%d", depth); + + /* What happens here is that we enumerate everything + // until we have a set of hardware modes + // We record the software driver modes, but those are + // only returned if we don't get any hardware ones + // + // Direct2D only supports 32bpp formats for RenderTargets + */ + if(depth >= 24) { + hDxgi = LoadLibrary("dxgi.dll"); + if (hDxgi != NULL) { + CreateDXGIFactory = (pfnCreateDXGIFactory)GetProcAddress(hDxgi, "CreateDXGIFactory"); + if ((CreateDXGIFactory) && (!NOTIFY_FAILED_HR(CreateDXGIFactory(&__fb_IID_IDXGIFactory, (void**)&pFactory)))) { + while((hardwareModes.elems == 0) && SUCCEEDED(IDXGIFactory_EnumAdapters(pFactory, adapterIter++, &pAdapter))) { + IDXGIOutput* pOutput = NULL; + DXGI_ADAPTER_DESC adapterDesc = {}; + if(SUCCEEDED(IDXGIAdapter_GetDesc(pAdapter, &adapterDesc))) { + /* The 'random' ids are defined here for the software/warp adapter + // https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/d3d10-graphics-programming-guide-dxgi#new-info-about-enumerating-adapters-for-windows-8 + */ + UINT outputIter = 0; + MiniVector* pWhichModes = ((adapterDesc.VendorId == 0x1414) && (adapterDesc.DeviceId == 0x8c)) ? &softwareModes : &hardwareModes; + while((hardwareModes.elems == 0) && SUCCEEDED(IDXGIAdapter_EnumOutputs(pAdapter, outputIter++, &pOutput))) { + DBG_TEXT("%s - Enumerated %ws output %lu", adapterDesc.Description, outputIter); + D2DEnumOutputModes(pOutput, pWhichModes); + IDXGIOutput_Release(pOutput); + } + } + IDXGIAdapter_Release(pAdapter); + } + } + FreeLibrary(hDxgi); + } + } + if(hardwareModes.pData) { + *size = hardwareModes.elems; + free(softwareModes.pData); + return hardwareModes.pData; + } + else { + *size = softwareModes.elems; + return softwareModes.pData; + } +} + +#endif /* HOST_CYGWIN */ diff --git a/src/gfxlib2/win32/gfx_driver_d2d_fixed.h b/src/gfxlib2/win32/gfx_driver_d2d_fixed.h new file mode 100644 index 0000000000..f23cef8ce2 --- /dev/null +++ b/src/gfxlib2/win32/gfx_driver_d2d_fixed.h @@ -0,0 +1,112 @@ +/* What are we fixing? +// A lot of functions in the D2D interfaces +// are declared to return structures by value +// i.e. +// D2D1_SIZE_U ID2D1Bitmap::GetPixelSize(); +// And +// D2D1_PIXEL_FORMAT ID2D1HwndRenderTarget::GetPixelFormat(ID2D1HwndRenderTarget*); +// +// While this is completely valid C/C++, how it is handled by +// compilers differs. As the Direct2D subsystem is +// coded in C++, what happens is that Visual Studio's C++ compiler +// translates those prototypes to a function definition of +// D2D1_SIZE_U* ID2D1Bitmap::GetPixelSize(D2D1_SIZE_U* pStackObject); +// and +// D2D1_PIXEL_FORMAT* ID2D1HwndRenderTarget::GetPixelFormat(ID2D1HwndRenderTarget*. D2D1_PIXEL_FORMAT*); +// i.e., it allocates the struct on the stack and sends a pointer to it +// as a hidden last parameter, and makes the return value a pointer too +// +// If you compile with Visual C++ in C++ mode, everything is fine, +// that's how it behaves and it's the same for Microsoft +// as it is for you - things work fine +// +// If you compile with Visual C++ in C mode or use different compilers +// then that same 'magic' conversion doesn't happen and things go wrong. +// In 32-bit it'll probably crash your program quite quickly since +// the ret instruction of the D2D function will pop the wrong number of args +// from the stack and unbalance it +// In 64-bit, as args are passed in registers, it won't crash +// in the same manner, but in both cases D2D1 will write through the 'invented' but +// non-existant pointer, so memory corruption is highly likely. +// +// See here for sample code and annotated assembly showing this is +// indeed how it is +// https://blog.airesoft.co.uk/2014/12/direct2d-scene-of-the-accident/ +// +// This file thus reimplements the C macros as inline functions for those functions +// to make this hidden pointer explicit so we can call the functions without issue +// +// NOTE: This only applies to functions whose prototypes directly return structs by value +// Lots of functions take structures by value, and they work fine +// Also, this isn't a complete list by any means +*/ + +/* Don't need these if we're not compiling in C */ +#ifdef D2D_USE_C_DEFINITIONS + +/* we undef and replace macros, which is useless +// if they haven't been included yet +// The first define is Mirosoft's name. the second MinGW +*/ +#if !defined(_D2D1_H_) && !defined(_D2D1_H) +#error "d2d1.h must be included first, or unsupported toolchain" +#endif + +#undef ID2D1Bitmap_GetPixelSize +static inline D2D1_SIZE_U* ID2D1Bitmap_GetPixelSize(ID2D1Bitmap* pBitmap, D2D1_SIZE_U* pSize) { + typedef D2D1_SIZE_U* (STDMETHODCALLTYPE*pfnD2DF_BitmapGetPixelSize)(ID2D1Bitmap* pBm, D2D1_SIZE_U* pSize); + pfnD2DF_BitmapGetPixelSize GetPixelSize = (pfnD2DF_BitmapGetPixelSize)(pBitmap->lpVtbl->GetPixelSize); + return GetPixelSize(pBitmap, pSize); +} + +#undef ID2D1Bitmap_GetSize +static inline D2D1_SIZE_F* ID2D1Bitmap_GetSize(ID2D1Bitmap* pBitmap, D2D1_SIZE_F* pSize) { + typedef D2D1_SIZE_F* (STDMETHODCALLTYPE*pfnD2DF_BitmapGetSize)(ID2D1Bitmap* pBm, D2D1_SIZE_F* pSize); + pfnD2DF_BitmapGetSize GetSize = (pfnD2DF_BitmapGetSize)(pBitmap->lpVtbl->GetSize); + return GetSize(pBitmap, pSize); +} + +#undef ID2D1RenderTarget_GetSize +static inline D2D1_SIZE_F* ID2D1RenderTarget_GetSize(ID2D1RenderTarget* pRT, D2D1_SIZE_F* pSize) { + typedef D2D1_SIZE_F* (STDMETHODCALLTYPE*pfnD2DF_RenderTargetGetSize)(ID2D1RenderTarget* pRT, D2D1_SIZE_F* pSize); + pfnD2DF_RenderTargetGetSize GetSize = (pfnD2DF_RenderTargetGetSize)(pRT->lpVtbl->GetSize); + return GetSize(pRT, pSize); +} + +#undef ID2D1RenderTarget_GetPixelSize +static inline D2D1_SIZE_U* ID2D1RenderTarget_GetPixelSize(ID2D1RenderTarget* pRT, D2D1_SIZE_U* pSize) { + typedef D2D1_SIZE_U* (STDMETHODCALLTYPE*pfnD2DF_RenderTargetGetPixelSize)(ID2D1RenderTarget* pRT, D2D1_SIZE_U* pSize); + pfnD2DF_RenderTargetGetPixelSize GetPixelSize = (pfnD2DF_RenderTargetGetPixelSize)(pRT->lpVtbl->GetPixelSize); + return GetPixelSize(pRT, pSize); +} + +#undef ID2D1RenderTarget_GetPixelFormat +static inline D2D1_PIXEL_FORMAT* ID2D1RenderTarget_GetPixelFormat(ID2D1RenderTarget* pRT, D2D1_PIXEL_FORMAT* pFormat) { + typedef D2D1_PIXEL_FORMAT* (STDMETHODCALLTYPE*pfnD2DF_RenderTargetGetPixFmt)(ID2D1RenderTarget* pRT, D2D1_PIXEL_FORMAT* pSize); + pfnD2DF_RenderTargetGetPixFmt GetPixFmt = (pfnD2DF_RenderTargetGetPixFmt)(pRT->lpVtbl->GetPixelFormat); + return GetPixFmt(pRT, pFormat); +} + +#undef ID2D1HwndRenderTarget_GetSize +static inline D2D1_SIZE_F* ID2D1HwndRenderTarget_GetSize(ID2D1HwndRenderTarget* pRT, D2D1_SIZE_F* pSize) { + typedef D2D1_SIZE_F* (STDMETHODCALLTYPE*pfnD2DF_HwndRenderTargetGetSize)(ID2D1HwndRenderTarget* pRT, D2D1_SIZE_F* pSize); + pfnD2DF_HwndRenderTargetGetSize GetSize = (pfnD2DF_HwndRenderTargetGetSize)(pRT->lpVtbl->Base.GetSize); + return GetSize(pRT, pSize); +} + +#undef ID2D1HwndRenderTarget_GetPixelSize +static inline D2D1_SIZE_U* ID2D1HwndRenderTarget_GetPixelSize(ID2D1HwndRenderTarget* pRT, D2D1_SIZE_U* pSize) { + typedef D2D1_SIZE_U* (STDMETHODCALLTYPE*pfnD2DF_HwndRenderTargetGetPixelSize)(ID2D1HwndRenderTarget* pRT, D2D1_SIZE_U* pSize); + pfnD2DF_HwndRenderTargetGetPixelSize GetPixelSize = (pfnD2DF_HwndRenderTargetGetPixelSize)(pRT->lpVtbl->Base.GetPixelSize); + return GetPixelSize(pRT, pSize); +} + +#undef ID2D1HwndRenderTarget_GetPixelFormat +static inline D2D1_PIXEL_FORMAT* ID2D1HwndRenderTarget_GetPixelFormat(ID2D1HwndRenderTarget* pRT, D2D1_PIXEL_FORMAT* pFormat) { + typedef D2D1_PIXEL_FORMAT* (STDMETHODCALLTYPE*pfnD2DF_HwndRenderTargetGetPixFmt)(ID2D1HwndRenderTarget* pRT, D2D1_PIXEL_FORMAT* pSize); + pfnD2DF_HwndRenderTargetGetPixFmt GetPixFmt = (pfnD2DF_HwndRenderTargetGetPixFmt)(pRT->lpVtbl->Base.GetPixelFormat); + return GetPixFmt(pRT, pFormat); +} + +/* D2D_USE_C_DEFINITIONS */ +#endif diff --git a/src/gfxlib2/win32/gfx_win32.c b/src/gfxlib2/win32/gfx_win32.c index 57e0cb0ecc..ac2beb6fd6 100644 --- a/src/gfxlib2/win32/gfx_win32.c +++ b/src/gfxlib2/win32/gfx_win32.c @@ -30,6 +30,7 @@ WIN32DRIVER fb_win32; const GFXDRIVER *__fb_gfx_drivers_list[] = { #ifndef HOST_CYGWIN + &fb_gfxDriverD2D, &fb_gfxDriverDirectDraw, #endif &fb_gfxDriverGDI,