]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
Direct3D updates.
authorToni Wilen <twilen@winuae.net>
Wed, 27 Dec 2017 18:44:15 +0000 (20:44 +0200)
committerToni Wilen <twilen@winuae.net>
Wed, 27 Dec 2017 18:44:15 +0000 (20:44 +0200)
cfgfile.cpp
include/options.h
main.cpp
od-win32/direct3d.h
od-win32/direct3d11.cpp
od-win32/win32gfx.cpp

index 541377100013d6b59e3f59e4c62f2dbf0a69b1ea..1c389159d4bcff5cab34649c360979b4d7587bb0 100644 (file)
@@ -201,6 +201,7 @@ static const TCHAR *aspects[] = { _T("none"), _T("vga"), _T("tv"), 0 };
 static const TCHAR *vsyncmodes[] = { _T("adaptive"), _T("false"), _T("true"), _T("autoswitch"), 0 };
 static const TCHAR *vsyncmodes2[] = { _T("normal"), _T("busywait"), 0 };
 static const TCHAR *filterapi[] = { _T("directdraw"), _T("direct3d"), _T("direct3d11"), 0 };
+static const TCHAR *filterapiopts[] = { _T("hardware"), _T("software"), 0 };
 static const TCHAR *dongles[] =
 {
        _T("none"),
@@ -1914,7 +1915,8 @@ void cfgfile_save_options (struct zfile *f, struct uae_prefs *p, int type)
        cfgfile_dwrite_str(f, _T("gfx_atari_palette_fix"), threebitcolors[p->gfx_threebitcolors]);
        cfgfile_dwrite_bool(f, _T("gfx_black_frame_insertion"), p->lightboost_strobo);
        cfgfile_dwrite(f, _T("gfx_black_frame_insertion_ratio"), _T("%d"), p->lightboost_strobo_ratio);
-       cfgfile_write_str (f, _T("gfx_api"), filterapi[p->gfx_api]);
+       cfgfile_write_str(f, _T("gfx_api"), filterapi[p->gfx_api]);
+       cfgfile_write_str(f, _T("gfx_api_options"), filterapiopts[p->gfx_api_options]);
        cfgfile_dwrite (f, _T("gfx_horizontal_tweak"), _T("%d"), p->gfx_extrawidth);
 
 #ifdef GFXFILTER
@@ -3072,7 +3074,8 @@ static int cfgfile_parse_host (struct uae_prefs *p, TCHAR *option, TCHAR *value)
                || cfgfile_strval (option, value, _T("gfx_color_mode"), &p->color_mode, colormode2, 0)
                || cfgfile_strval (option, value, _T("gfx_max_horizontal"), &p->gfx_max_horizontal, maxhoriz, 0)
                || cfgfile_strval (option, value, _T("gfx_max_vertical"), &p->gfx_max_vertical, maxvert, 0)
-               || cfgfile_strval (option, value, _T("gfx_api"), &p->gfx_api, filterapi, 0)
+               || cfgfile_strval(option, value, _T("gfx_api"), &p->gfx_api, filterapi, 0)
+               || cfgfile_strval(option, value, _T("gfx_api_options"), &p->gfx_api_options, filterapiopts, 0)
                || cfgfile_strval(option, value, _T("gfx_atari_palette_fix"), &p->gfx_threebitcolors, threebitcolors, 0)
                || cfgfile_strval (option, value, _T("magic_mousecursor"), &p->input_magic_mouse_cursor, magiccursors, 0)
                || cfgfile_strval (option, value, _T("absolute_mouse"), &p->input_tablet, abspointers, 0))
index e94148838bd94d1cc1e017da96b7c755cbd210e1..e066cebe7b21e29f5c0fb91a0b0eda02d1dd1e54 100644 (file)
@@ -504,6 +504,7 @@ struct uae_prefs {
        bool gfx_blackerthanblack;
        int gfx_threebitcolors;
        int gfx_api;
+       int gfx_api_options;
        int color_mode;
        int gfx_extrawidth;
        bool lightboost_strobo;
index c19740fce9121a9797e2073968ca6c42bab84726..397a4e7c8e8b6178fe66ee5f1fb29669ce40ade5 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -169,9 +169,6 @@ void fixup_prefs_dimensions (struct uae_prefs *prefs)
                struct apmode *ap = &prefs->gfx_apmode[i];
                ap->gfx_vflip = 0;
                ap->gfx_strobo = false;
-               // no llvs support yet
-               if (prefs->gfx_api > 1)
-                       ap->gfx_vsyncmode = 0;
                if (ap->gfx_vsync < 0) {
                        // adaptive sync
                        ap->gfx_vsyncmode = 0;
@@ -194,6 +191,8 @@ void fixup_prefs_dimensions (struct uae_prefs *prefs)
                                        ap->gfx_strobo = prefs->lightboost_strobo;
                        }
                } else {
+                       if (ap->gfx_backbuffers > 0 && prefs->gfx_api > 1)
+                               ap->gfx_strobo = prefs->lightboost_strobo;
                        // no vsync: wait if triple bufferirng
                        if (ap->gfx_backbuffers >= 2)
                                ap->gfx_vflip = -1;
index 3175aa699249cd50cf53022da0824d3345f9e545..ccda776169c843b328eeae1b55665a2fe589e3b9 100644 (file)
@@ -34,6 +34,8 @@ void d3d9_select(void);
 void d3d11_select(void);
 void d3d_select(struct uae_prefs *p);
 int can_D3D11(bool checkdevice);
+bool d3d11_vsync_isdone(void);
+double d3d11_get_hz(void);
 
 #define CURSORMAXWIDTH 64
 #define CURSORMAXHEIGHT 64
index 37ab0d482fd5880543e3ed56755515b1106ad0c3..cfcdfad886f2558a95505b6265dbf3e18f422841 100644 (file)
@@ -1,6 +1,4 @@
 
-#define CUSTOMSHADERS 1
-
 #include <windows.h>
 #include "resource.h"
 
@@ -36,6 +34,9 @@ using Microsoft::WRL::ComPtr;
 
 #include "FX11/d3dx11effect.h"
 
+#include <process.h>
+#include <Dwmapi.h>
+
 void (*D3D_free)(bool immediate);
 const TCHAR* (*D3D_init)(HWND ahwnd, int w_w, int h_h, int depth, int *freq, int mmult);
 bool (*D3D_alloctexture)(int, int);
@@ -62,7 +63,11 @@ void(*D3D_resize)(int);
 void (*D3D_change)(int);
 bool(*D3D_getscalerect)(float *mx, float *my, float *sx, float *sy);
 
-static HANDLE hd3d11, hdxgi, hd3dcompiler;
+static volatile int presentthread_mode, vblankthread_mode;
+static HMODULE hd3d11, hdxgi, hd3dcompiler, dwmapi;
+static HANDLE flipevent, flipevent2, vblankevent;
+static CRITICAL_SECTION present_cs;
+static bool present_cs_init;
 
 static struct gfx_filterdata *filterd3d;
 static int filterd3didx;
@@ -221,6 +226,13 @@ struct d3d11struct
        bool fsmodechange;
        bool invalidmode;
        int vblankintervals;
+       bool blackscreen;
+       int framecount;
+       UINT syncinterval;
+       bool flipped;
+       double vblank;
+       DWM_FRAME_COUNT lastframe;
+       int frames_since_init;
 
        struct d3d11sprite osd;
        struct d3d11sprite hwsprite;
@@ -233,6 +245,7 @@ struct d3d11struct
        RECT mask2rect;
 
        IDXGISurface1 *hdc_surface;
+       HANDLE filenotificationhandle;
 
        RECT sr2, dr2, zr2;
        int guimode;
@@ -252,13 +265,10 @@ struct d3d11struct
 };
 
 #define NUMVERTICES 8
-#define TLVERTEX_DIFFUSE 1
 
 struct TLVERTEX {
        D3DXVECTOR3 position;       // vertex position
-#if TLVERTEX_DIFFUSE
        D3DCOLOR    diffuse;
-#endif
        D3DXVECTOR2 texcoord;       // texture coords
 };
 
@@ -314,10 +324,13 @@ typedef HRESULT (WINAPI* D3DCOMPILE2)(LPCVOID pSrcData,
        SIZE_T SecondaryDataSize,
        ID3DBlob** ppCode,
        ID3DBlob** ppErrorMsgs);
-typedef HRESULT(WINAPID3DREFLECT)(LPCVOID pSrcData, SIZE_T SrcDataSize, REFIID pInterface, void **ppReflector);
+typedef HRESULT(WINAPI *D3DREFLECT)(LPCVOID pSrcData, SIZE_T SrcDataSize, REFIID pInterface, void **ppReflector);
 typedef HRESULT(WINAPI *D3DGETBLOBPART)(LPCVOID pSrcData, SIZE_T SrcDataSize, D3D_BLOB_PART Part, UINT Flags, ID3DBlob** ppPart);
+typedef HRESULT(WINAPI *DWMGETCOMPOSITIONTIMINGINFO)(HWND hwnd, DWM_TIMING_INFO *pTimingInfo);
+
 static PFN_D3D11_CREATE_DEVICE pD3D11CreateDevice;
 static CREATEDXGIFACTORY1 pCreateDXGIFactory1;
+static DWMGETCOMPOSITIONTIMINGINFO pDwmGetCompositionTimingInfo;
 D3DCOMPILE ppD3DCompile;
 D3DCOMPILE2 ppD3DCompile2;
 D3DCOMPILEFROMFILE pD3DCompileFromFile;
@@ -360,8 +373,6 @@ static void TurnOffAlphaBlending(struct d3d11struct *d3d)
        d3d->m_deviceContext->OMSetBlendState(d3d->m_alphaDisableBlendingState, blendFactor, 0xffffffff);
 }
 
-#if CUSTOMSHADERS
-
 #define D3DX_DEFAULT ((UINT) -1)
 
 static bool psEffect_ParseParameters(struct d3d11struct *d3d, ID3DX11Effect *effect, struct shaderdata11 *s, char *fxname, char *data, UINT flags1)
@@ -672,49 +683,33 @@ static bool createfxvertices(struct d3d11struct *d3d, struct shaderdata11 *s)
        //Setup vertices
 
        vertices[0].position.x = -0.5f; vertices[0].position.y = -0.5f;
-#if TLVERTEX_DIFFUSE
        vertices[0].diffuse = 0xFFFFFFFF;
-#endif
        vertices[0].texcoord.x = 0.0f; vertices[0].texcoord.y = sizey;
 
        vertices[1].position.x = -0.5f; vertices[1].position.y = 0.5f;
-#if TLVERTEX_DIFFUSE
        vertices[1].diffuse = 0xFFFFFFFF;
-#endif
        vertices[1].texcoord.x = 0.0f; vertices[1].texcoord.y = 0.0f;
 
        vertices[2].position.x = 0.5f; vertices[2].position.y = -0.5f;
-#if TLVERTEX_DIFFUSE
        vertices[2].diffuse = 0xFFFFFFFF;
-#endif
        vertices[2].texcoord.x = sizex; vertices[2].texcoord.y = sizey;
 
        vertices[3].position.x = 0.5f; vertices[3].position.y = 0.5f;
-#if TLVERTEX_DIFFUSE
        vertices[3].diffuse = 0xFFFFFFFF;
-#endif
        vertices[3].texcoord.x = sizex; vertices[3].texcoord.y = 0.0f;
 
        // Additional vertices required for some PS effects
        vertices[4].position.x = 0.0f; vertices[4].position.y = 0.0f;
-#if TLVERTEX_DIFFUSE
        vertices[4].diffuse = 0xFFFFFF00;
-#endif
        vertices[4].texcoord.x = 0.0f; vertices[4].texcoord.y = 1.0f;
        vertices[5].position.x = 0.0f; vertices[5].position.y = 1.0f;
-#if TLVERTEX_DIFFUSE
        vertices[5].diffuse = 0xFFFFFF00;
-#endif
        vertices[5].texcoord.x = 0.0f; vertices[5].texcoord.y = 0.0f;
        vertices[6].position.x = 1.0f; vertices[6].position.y = 0.0f;
-#if TLVERTEX_DIFFUSE
        vertices[6].diffuse = 0xFFFFFF00;
-#endif
        vertices[6].texcoord.x = 1.0f; vertices[6].texcoord.y = 1.0f;
        vertices[7].position.x = 1.0f; vertices[7].position.y = 1.0f;
-#if TLVERTEX_DIFFUSE
        vertices[7].diffuse = 0xFFFFFF00;
-#endif
        vertices[7].texcoord.x = 1.0f; vertices[7].texcoord.y = 0.0f;
 
        d3d->m_deviceContext->Unmap(s->vertexBuffer, 0);
@@ -755,7 +750,6 @@ static int createfxlayout(struct d3d11struct *d3d, struct shaderdata11 *s, ID3DX
                polygonLayout[pidx].InstanceDataStepRate = 0;
                pidx++;
 
-#if TLVERTEX_DIFFUSE
                polygonLayout[pidx].SemanticName = "DIFFUSE";
                polygonLayout[pidx].SemanticIndex = 0;
                polygonLayout[pidx].Format = DXGI_FORMAT_R32_UINT;
@@ -764,7 +758,6 @@ static int createfxlayout(struct d3d11struct *d3d, struct shaderdata11 *s, ID3DX
                polygonLayout[pidx].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
                polygonLayout[pidx].InstanceDataStepRate = 0;
                pidx++;
-#endif
 
                polygonLayout[pidx].SemanticName = "TEXCOORD";
                polygonLayout[pidx].SemanticIndex = 0;
@@ -796,6 +789,55 @@ static bool islf(char c)
        return c == '\n' || c == '\r' || c == ';';
 }
 
+static bool fxneedconvert(char *s)
+{
+       char *t = s;
+       int len = strlen(s);
+       while (len > 0) {
+               if (t != s && isws(t[-1]) && (!strnicmp(t, "technique10", 11) || !strnicmp(t, "technique11", 11)) && isws(t[11])) {
+                       return false;
+               }
+               len--;
+               t++;
+       }
+       return true;
+}
+
+static void fxspecials(char *s, char *dst)
+{
+       char *t = s;
+       char *d = dst;
+       *d = 0;
+       int len = strlen(s);
+       while (len > 0) {
+               bool found = false;
+               if (t != s && !strnicmp(t, "minfilter", 9) && (isws(t[9]) || t[9] == '=') && isws(t[-1])) {
+                       found = true;
+                       t += 10;
+                       len -= 10;
+                       while (!islf(*t) && len > 0) {
+                               if (!strnicmp(t, "point", 5)) {
+                                       strcpy(d, "Filter=MIN_MAG_MIP_POINT");
+                                       d += strlen(d);
+                                       write_log("FX: 'minfilter' -> 'Filter=MIN_MAG_MIP_POINT'\n");
+                               }
+                               if (!strnicmp(t, "linear", 6)) {
+                                       strcpy(d, "Filter=MIN_MAG_MIP_LINEAR");
+                                       d += strlen(d);
+                                       write_log("FX: 'minfiler' -> 'Filter=MIN_MAG_MIP_LINEAR'\n");
+                               }
+                               t++;
+                               len--;
+                       }
+               }
+               if (!found) {
+                       *d++ = *t++;
+                       len--;
+               }
+       }
+       *d = 0;
+}
+
 static void fxconvert(char *s, char *dst, const char **convert1, const char **convert2)
 {
        char *t = s;
@@ -807,12 +849,13 @@ static void fxconvert(char *s, char *dst, const char **convert1, const char **co
                        int slen = strlen(convert1[i]);
                        int dlen = strlen(convert2[i]);
                        if (len > slen && !strnicmp(t, convert1[i], slen)) {
-                               if ((t == s || isws(t[-1])) || isws(t[slen])) {
+                               if ((t == s || isws(t[-1])) && isws(t[slen])) {
                                        memcpy(d, convert2[i], dlen);
                                        t += slen;
                                        d += dlen;
                                        len -= slen;
                                        found = true;
+                                       write_log("FX: '%s' -> '%s'\n", convert1[i], convert2[i]);
                                }
                        }
                }
@@ -852,6 +895,7 @@ static void fxremoveline(char *s, char *dst, const char **lines)
                                        len--;
                                }
                                found = true;
+                               write_log("FX: '%s' line removed\n", lines[i]);
                        }
                }
                if (!found) {
@@ -884,9 +928,9 @@ static bool psEffect_LoadEffect(struct d3d11struct *d3d, const TCHAR *shaderfile
 
        DWORD dwShaderFlags = D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY;
 #ifndef NDEBUG
-       dwShaderFlags |= D3DCOMPILE_DEBUG;
+       //dwShaderFlags |= D3DCOMPILE_DEBUG;
        //Disable optimizations to further improve shader debugging
-       dwShaderFlags |= D3DCOMPILE_SKIP_OPTIMIZATION;
+       //dwShaderFlags |= D3DCOMPILE_SKIP_OPTIMIZATION;
 #endif
 
        GetCurrentDirectory(MAX_DPATH, tmp3);
@@ -894,6 +938,11 @@ static bool psEffect_LoadEffect(struct d3d11struct *d3d, const TCHAR *shaderfile
 
        plugin_path = get_plugin_path(tmp2, sizeof tmp2 / sizeof(TCHAR), _T("filtershaders\\direct3d"));
        _tcscpy(tmp, tmp2);
+
+       d3d->frames_since_init = 0;
+       if (!d3d->filenotificationhandle)
+               d3d->filenotificationhandle = FindFirstChangeNotification(tmp, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
+
        _tcscat(tmp, shaderfile);
        write_log(_T("Direct3D11: Attempting to load '%s'\n"), tmp);
        _tcscpy(s->loadedshader, shaderfile);
@@ -917,15 +966,22 @@ static bool psEffect_LoadEffect(struct d3d11struct *d3d, const TCHAR *shaderfile
        zfile_fclose(z);
        z = NULL;
 
-       static const char *converts1[] = { "technique", "vs_3_0", "vs_2_0", "vs_1_1", "ps_3_0", "ps_2_0", NULL };
-       static const char *converts2[] = { "technique10", "vs_4_0_level_9_3", "vs_4_0_level_9_3", "vs_4_0_level_9_3", "ps_4_0_level_9_3", "ps_4_0_level_9_3", NULL };
-       fxconvert(fx1, fx2, converts1, converts2);
+       char *fx = fx1;
+       if (fxneedconvert(fx1)) {
+               static const char *converts1[] = { "technique", "vs_3_0", "vs_2_0", "vs_1_1", "ps_3_0", "ps_2_0", NULL };
+               static const char *converts2[] = { "technique10", "vs_4_0_level_9_3", "vs_4_0_level_9_3", "vs_4_0_level_9_3", "ps_4_0_level_9_3", "ps_4_0_level_9_3", NULL };
+               fxconvert(fx1, fx2, converts1, converts2);
+
+               static const char *lines[] = { "alphablendenable", "colorwriteenable", "srgbwriteenable", "magfilter", NULL };
+               fxremoveline(fx2, fx1, lines);
 
-       static const char *lines[] = { "alphablendenable", "colorwriteenable", "srgbwriteenable", NULL };
-       fxremoveline(fx2, fx1, lines);
+               fxspecials(fx1, fx2);
+               
+               fx = fx2;
+       }
 
        SetCurrentDirectory(tmp2);
-       hr = D3DX11CompileEffectFromMemory(fx1, strlen(fx1), name, NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE, dwShaderFlags, 0, d3d->m_device, &g_pEffect, &errors);
+       hr = D3DX11CompileEffectFromMemory(fx, strlen(fx), name, NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE, dwShaderFlags, 0, d3d->m_device, &g_pEffect, &errors);
 
 #if 0
        hr = D3DX11CompileEffectFromFile(tmp, nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, dwShaderFlags, 0, d3d->m_device, &g_pEffect, &errors);
@@ -947,7 +1003,7 @@ static bool psEffect_LoadEffect(struct d3d11struct *d3d, const TCHAR *shaderfile
                errors->Release();
        }
 
-       if (!psEffect_ParseParameters(d3d, g_pEffect, s, name, fx1, dwShaderFlags))
+       if (!psEffect_ParseParameters(d3d, g_pEffect, s, name, fx, dwShaderFlags))
                goto end;
 
        SetCurrentDirectory(tmp3);
@@ -1268,8 +1324,6 @@ pass2:
        return true;
 }
 
-#endif
-
 static bool UpdateVertexArray(struct d3d11struct *d3d, ID3D11Buffer *vertexbuffer,
        float left, float top, float right, float bottom,
        float slleft, float sltop, float slright, float slbottom)
@@ -2699,9 +2753,8 @@ static bool initd3d(struct d3d11struct *d3d)
                return false;
        if (!UpdateBuffers(d3d))
                return false;
-#if CUSTOMSHADERS
+
        settransform(d3d, NULL);
-#endif
 
        write_log(_T("D3D11 initd3d end\n"));
        return true;
@@ -2713,10 +2766,9 @@ static void setswapchainmode(struct d3d11struct *d3d, int fs)
        // It is recommended to always use the tearing flag when it is supported.
        d3d->swapChainDesc.Flags &= ~DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
        // tearing flag is not fullscreen compatible
-       if (d3d->m_tearingSupport && (d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL || d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD) && !apm->gfx_vflip) {
+       if (d3d->m_tearingSupport && (d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL || d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD) && !apm->gfx_vflip && apm->gfx_backbuffers == 0) {
                d3d->swapChainDesc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
        }
-
        d3d->swapChainDesc.Flags &= ~DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
        if (fs) {
                d3d->swapChainDesc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
@@ -2749,6 +2801,8 @@ int can_D3D11(bool checkdevice)
                hd3d11 = LoadLibrary(_T("D3D11.dll"));
        if (!hdxgi)
                hdxgi = LoadLibrary(_T("Dxgi.dll"));
+       if (!dwmapi)
+               dwmapi = LoadLibrary(_T("Dwmapi.dll"));
 
        if (!hd3dcompiler) {
                d3dcompiler = d3dcompiler1;
@@ -2783,6 +2837,10 @@ int can_D3D11(bool checkdevice)
                return 0;
        }
 
+       if (!pDwmGetCompositionTimingInfo && dwmapi) {
+               pDwmGetCompositionTimingInfo = (DWMGETCOMPOSITIONTIMINGINFO)GetProcAddress(dwmapi, "DwmGetCompositionTimingInfo");
+       }
+
        if (ppD3DCompile && pD3DReflect && pD3DGetBlobPart)
                ret |= 4;
 
@@ -2805,7 +2863,8 @@ int can_D3D11(bool checkdevice)
                UINT cdflags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
                ID3D11Device *m_device;
                ID3D11DeviceContext *m_deviceContext;
-               HRESULT hr = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, cdflags, levels, 1, D3D11_SDK_VERSION, &m_device, NULL, &m_deviceContext);
+               HRESULT hr = pD3D11CreateDevice(NULL, currprefs.gfx_api_options == 0 ? D3D_DRIVER_TYPE_HARDWARE : D3D_DRIVER_TYPE_WARP,
+                       NULL, cdflags, levels, 1, D3D11_SDK_VERSION, &m_device, NULL, &m_deviceContext);
                if (FAILED(hr)) {
                        return 0;
                }
@@ -2818,6 +2877,136 @@ int can_D3D11(bool checkdevice)
        return ret;
 }
 
+static void do_present(struct d3d11struct *d3d, int black)
+{
+       HRESULT hr;
+       UINT presentFlags = 0;
+
+       EnterCriticalSection(&present_cs);
+
+       if (black) {
+               float color[4];
+               color[0] = 0;
+               color[1] = 0;
+               color[2] = 0;
+               color[3] = 0;
+               // Clear the back buffer.
+               d3d->m_deviceContext->ClearRenderTargetView(d3d->m_renderTargetView, color);
+       }
+
+       struct apmode *apm = picasso_on ? &currprefs.gfx_apmode[APMODE_RTG] : &currprefs.gfx_apmode[APMODE_NATIVE];
+       int vsync = isvsync();
+       if (currprefs.turbo_emulation)
+               presentFlags |= DXGI_PRESENT_DO_NOT_WAIT;
+       if (d3d->m_tearingSupport && (d3d->swapChainDesc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING)) {
+               presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
+       }
+       UINT syncinterval = d3d->vblankintervals;
+       d3d->flipped = true;
+       if (!vsync) {
+               if (apm->gfx_backbuffers == 0 || (presentFlags & DXGI_PRESENT_ALLOW_TEARING) || (apm->gfx_vflip == 0 && isfs(d3d) <= 0) || (isfs(d3d) > 0 && apm->gfx_vsyncmode))
+                       syncinterval = 0;
+       }
+       d3d->syncinterval = syncinterval;
+       hr = d3d->m_swapChain->Present(syncinterval, presentFlags);
+       SetEvent(flipevent2);
+       if (currprefs.turbo_emulation && hr == DXGI_ERROR_WAS_STILL_DRAWING)
+               hr = S_OK;
+       if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
+               if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == E_OUTOFMEMORY)
+                       d3d->invalidmode = true;
+               write_log(_T("D3D11 Present %08x\n"), hr);
+       }
+
+       LeaveCriticalSection(&present_cs);
+}
+
+static unsigned int __stdcall vblankthread(void *dummy)
+{
+       struct d3d11struct *d3d = &d3d11data[0];
+
+       SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+       while (vblankthread_mode) {
+               HRESULT hr = d3d->outputAdapter->WaitForVBlank();
+               if (FAILED(hr)) {
+                       Sleep(10);
+               } else {
+                       if (d3d->blackscreen && (d3d->framecount & 1)) {
+                               do_present(d3d, 1);
+                       } else {
+                               SetEvent(vblankevent);
+                       }
+                       d3d->framecount++;
+               }
+       }
+       vblankthread_mode = -1;
+       write_log(_T("vblankthread exited\n"));
+       return 0;
+}
+
+static unsigned int __stdcall presentthread(void *dummy)
+{
+       struct d3d11struct *d3d = &d3d11data[0];
+
+       SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+       while (presentthread_mode) {
+               WaitForSingleObject(flipevent, INFINITE);
+               if (presentthread_mode == 0)
+                       break;
+               do_present(d3d, 0);
+       }
+       presentthread_mode = -1;
+       write_log(_T("presentthread exited\n"));
+       return 0;
+}
+
+static void initthread(struct d3d11struct *d3d)
+{
+       unsigned int th;
+
+       if (!present_cs_init) {
+               present_cs_init = true;
+               InitializeCriticalSection(&present_cs);
+       }
+
+       if (!presentthread_mode) {
+               flipevent = CreateEvent(NULL, FALSE, FALSE, NULL);
+               flipevent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
+               presentthread_mode = 1;
+               _beginthreadex(NULL, 0, presentthread, 0, 0, &th);
+       }
+       if (!vblankthread_mode && d3d->outputAdapter && (d3d->vblankintervals == 0 || d3d->blackscreen)) {
+               vblankevent = CreateEvent(NULL, TRUE, FALSE, NULL);
+               vblankthread_mode = 1;
+               _beginthreadex(NULL, 0, vblankthread, 0, 0, &th);
+       }
+}
+
+static void freethread(struct d3d11struct *d3d)
+{
+       if (presentthread_mode) {
+               presentthread_mode = 0;
+               while (presentthread_mode == 0) {
+                       SetEvent(flipevent);
+                       Sleep(10);
+               }
+               presentthread_mode = 0;
+               CloseHandle(flipevent);
+               CloseHandle(flipevent2);
+               flipevent = NULL;
+               flipevent2 = NULL;
+       }
+       if (vblankthread_mode) {
+               vblankthread_mode = 0;
+               while (vblankthread_mode == 0) {
+                       Sleep(10);
+               }
+               vblankthread_mode = 0;
+               CloseHandle(vblankevent);
+               vblankevent = NULL;
+       }
+}
+
 static int xxD3D11_init2(HWND ahwnd, int w_w, int w_h, int t_w, int t_h, int depth, int *freq, int mmult)
 {
        struct d3d11struct *d3d = &d3d11data[0];
@@ -2974,6 +3163,7 @@ static int xxD3D11_init2(HWND ahwnd, int w_w, int w_h, int t_w, int t_h, int dep
                                if ((int)(mhz + 0.5) == hz || (int)(mhz) == hz) {
                                        d3d->fsSwapChainDesc.RefreshRate.Denominator = m->RefreshRate.Denominator;
                                        d3d->fsSwapChainDesc.RefreshRate.Numerator = m->RefreshRate.Numerator;
+                                       *freq = hz;
                                        break;
                                }
                        }
@@ -2992,9 +3182,14 @@ static int xxD3D11_init2(HWND ahwnd, int w_w, int w_h, int t_w, int t_h, int dep
                } else {
                        d3d->fsSwapChainDesc.RefreshRate.Denominator = md2.RefreshRate.Denominator;
                        d3d->fsSwapChainDesc.RefreshRate.Numerator = md2.RefreshRate.Numerator;
+                       *freq = md2.RefreshRate.Numerator / md2.RefreshRate.Denominator;
                }
        }
 
+       if (isfs(d3d) <= 0) {
+               *freq = (int)d3d11_get_hz();
+       }
+
        // Get the adapter (video card) description.
        result = adapter->GetDesc(&adapterDesc);
        if (FAILED(result)) {
@@ -3028,18 +3223,20 @@ static int xxD3D11_init2(HWND ahwnd, int w_w, int w_h, int t_w, int t_h, int dep
 #endif
        static const D3D_FEATURE_LEVEL levels111[] = { D3D_FEATURE_LEVEL_11_1 };
        D3D_FEATURE_LEVEL outlevel;
-       result = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, cdflags, levels111, 1, D3D11_SDK_VERSION, &d3d->m_device, &outlevel, &d3d->m_deviceContext);
+       D3D_DRIVER_TYPE dt = currprefs.gfx_api_options == 0 ? D3D_DRIVER_TYPE_HARDWARE : D3D_DRIVER_TYPE_WARP;
+       result = pD3D11CreateDevice(NULL, dt, NULL, cdflags, levels111, 1, D3D11_SDK_VERSION, &d3d->m_device, &outlevel, &d3d->m_deviceContext);
        if (FAILED(result)) {
                write_log(_T("D3D11CreateDevice LEVEL_11_1: %08x\n"), result);
                if (result == E_INVALIDARG || result == DXGI_ERROR_UNSUPPORTED) {
-                       result = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, cdflags, NULL, 0, D3D11_SDK_VERSION, &d3d->m_device, &outlevel, &d3d->m_deviceContext);
+                       result = pD3D11CreateDevice(NULL, dt, NULL, cdflags, NULL, 0, D3D11_SDK_VERSION, &d3d->m_device, &outlevel, &d3d->m_deviceContext);
                }
                if (FAILED(result)) {
                        write_log(_T("D3D11CreateDevice %08x. Hardware does not support Direct3D11 Level 9.1 or higher.\n"), result);
                        return 0;
                }
        }
-       write_log(_T("D3D11CreateDevice succeeded with level %d.%d\n"), outlevel >> 12, (outlevel >> 8) & 15);
+       write_log(_T("D3D11CreateDevice succeeded with level %d.%d. %s.\n"), outlevel >> 12, (outlevel >> 8) & 15,
+               currprefs.gfx_api_options ? _T("Software WARP driver") : _T("Hardware accelerated"));
        d3d->feature_level = outlevel;
 
        UINT flags = 0;
@@ -3098,21 +3295,48 @@ static int xxD3D11_init2(HWND ahwnd, int w_w, int w_h, int t_w, int t_h, int dep
        if (d3d->swapChainDesc.BufferCount < 2)
                d3d->swapChainDesc.BufferCount = 2;
 
-       d3d->swapChainDesc.SwapEffect = os_win8 ? DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL : DXGI_SWAP_EFFECT_SEQUENTIAL;
+       if (apm->gfx_backbuffers == 0) {
+               d3d->swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+               if (os_win10 && d3d->m_tearingSupport && isfs(d3d) <= 0) {
+                       d3d->swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+               }
+       } else {
+               d3d->swapChainDesc.SwapEffect = os_win8 ? (os_win10 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) : DXGI_SWAP_EFFECT_SEQUENTIAL;
+               if (apm->gfx_vsyncmode && isfs(d3d) > 0) {
+                       d3d->swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+               }
+       }
 
        d3d->swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
 
        setswapchainmode(d3d, isfs(d3d) > 0);
 
-       d3d->swapChainDesc.Scaling = d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH;
+       d3d->swapChainDesc.Scaling = (d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL || d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD) ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH;
 
        d3d->vblankintervals = 1;
+       d3d->blackscreen = false;
+       if (apm->gfx_backbuffers == 0) {
+               d3d->vblankintervals = 0;
+       } else {
+               int hzmult = 0;
+               getvsyncrate(*freq, &hzmult);
+               if (hzmult < 0) {
+                       if (!apm->gfx_strobo) {
+                               if (isfullscreen() > 0) {
+                                       d3d->vblankintervals++;
+                               }
+                       } else {
+                               d3d->blackscreen = true;
+                       }
+               }
+       }
        int vsync = isvsync();
        if (vsync) {
                int hzmult;
                getvsyncrate(hz, &hzmult);
-               if (hzmult > 0)
-                       d3d->vblankintervals = hzmult + 1;
+               if (hzmult > 0) {
+                       d3d->vblankintervals = hzmult + (d3d->blackscreen ? 0 : 1);
+               }
        }
 
        // Create the swap chain, Direct3D device, and Direct3D device context.
@@ -3134,6 +3358,9 @@ static int xxD3D11_init2(HWND ahwnd, int w_w, int w_h, int t_w, int t_h, int dep
 
        d3d->invalidmode = false;
        d3d->fsmode = 0;
+
+       initthread(d3d);
+
        if (isfs(d3d) > 0)
                D3D_resize(1);
        D3D_resize(0);
@@ -3255,6 +3482,9 @@ static void freed3d(struct d3d11struct *d3d)
                memset(s, 0, sizeof(struct shaderdata11));
        }
 
+       if (d3d->filenotificationhandle)
+               CloseHandle(d3d->filenotificationhandle);
+       d3d->filenotificationhandle = NULL;
 
        if (d3d->m_deviceContext) {
                d3d->m_deviceContext->ClearState();
@@ -3268,6 +3498,8 @@ static void xD3D11_free(bool immediate)
 
        write_log(_T("D3D11 free start\n"));
 
+       freethread(d3d);
+
        freed3d(d3d);
 
        if (d3d->m_swapChain) {
@@ -3371,31 +3603,11 @@ static void RenderBuffers(struct d3d11struct *d3d, ID3D11Buffer *vertexbuffer, I
 
 static void EndScene(struct d3d11struct *d3d)
 {
-       HRESULT hr;
-       UINT presentFlags = 0;
-
-       struct apmode *apm = picasso_on ? &currprefs.gfx_apmode[APMODE_RTG] : &currprefs.gfx_apmode[APMODE_NATIVE];
-       int vsync = isvsync();
-       if (currprefs.turbo_emulation)
-               presentFlags |= DXGI_PRESENT_DO_NOT_WAIT;
-       if (d3d->m_tearingSupport && (d3d->swapChainDesc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING)) {
-               presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
-       }
-       UINT syncinterval = d3d->vblankintervals;
-       if (!vsync) {
-               if (apm->gfx_vflip == 0 || (presentFlags & DXGI_PRESENT_ALLOW_TEARING))
-                       syncinterval = 0;
-       }
-       hr = d3d->m_swapChain->Present(syncinterval, presentFlags);
-       if (currprefs.turbo_emulation && hr == DXGI_ERROR_WAS_STILL_DRAWING)
-               return;
-       if (FAILED(hr)) {
-               if (hr == DXGI_STATUS_OCCLUDED)
-                       return;
-               if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == E_OUTOFMEMORY)
-                       d3d->invalidmode = true;
-               write_log(_T("D3D11 Present %08x\n"), hr);
+       if ((d3d->syncinterval || (d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_DISCARD)) && d3d->flipped) {
+               WaitForSingleObject(flipevent2, 100);
+               d3d->flipped = false;
        }
+       SetEvent(flipevent);
 }
 
 static void TextureShaderClass_RenderShader(struct d3d11struct *d3d)
@@ -3528,7 +3740,7 @@ static void renderoverlay(struct d3d11struct *d3d)
        }
 }
 
-static bool TextureShaderClass_Render(struct d3d11struct *d3d)
+static bool renderframe(struct d3d11struct *d3d)
 {
        struct shadertex st;
        st.tex = d3d->texture2d;
@@ -3537,7 +3749,6 @@ static bool TextureShaderClass_Render(struct d3d11struct *d3d)
 
        TurnOffAlphaBlending(d3d);
 
-#if CUSTOMSHADERS
        for (int i = 0; i < MAX_SHADERS; i++) {
                struct shaderdata11 *s = &d3d->shaders[i];
                if (s->type == SHADERTYPE_BEFORE)
@@ -3553,7 +3764,6 @@ static bool TextureShaderClass_Render(struct d3d11struct *d3d)
                                return false;
                }
        }
-#endif
 
        d3d->m_matProj = d3d->m_matProj_out;
        d3d->m_matView = d3d->m_matView_out;
@@ -3630,6 +3840,12 @@ static bool TextureShaderClass_Render(struct d3d11struct *d3d)
                        }
                }
        }
+       return true;
+}
+
+static bool TextureShaderClass_Render(struct d3d11struct *d3d)
+{
+       renderframe(d3d);
 
        RenderSprite(d3d, &d3d->hwsprite);
 
@@ -3689,12 +3905,15 @@ static bool GraphicsClass_Render(struct d3d11struct *d3d, float rotation)
        // Generate the view matrix based on the camera's position.
        CameraClass_Render(d3d);
 
+       EnterCriticalSection(&present_cs);
+
        // Render the bitmap with the texture shader.
        result = TextureShaderClass_Render(d3d);
+
+       LeaveCriticalSection(&present_cs);
+
        if (!result)
-       {
                return false;
-       }
 
        return true;
 }
@@ -3712,7 +3931,6 @@ static struct shaderdata11 *allocshaderslot(struct d3d11struct *d3d, int type)
 
 static bool restore(struct d3d11struct *d3d)
 {
-#if CUSTOMSHADERS
        for (int i = 0; i < MAX_FILTERSHADERS; i++) {
                if (filterd3d->gfx_filtershader[i][0]) {
                        struct shaderdata11 *s = allocshaderslot(d3d, SHADERTYPE_BEFORE);
@@ -3749,24 +3967,7 @@ static bool restore(struct d3d11struct *d3d)
                        createmasktexture(d3d, filterd3d->gfx_filtermask[i + MAX_FILTERSHADERS], s);
                }
        }
-#else
-       for (int i = 0; i < MAX_FILTERSHADERS; i++) {
-               if (filterd3d->gfx_filtermask[i][0]) {
-                       struct shaderdata11 *s = allocshaderslot(d3d, SHADERTYPE_MASK_BEFORE);
-                       createmasktexture(d3d, filterd3d->gfx_filtermask[i], s);
-               }
-       }
-       if (filterd3d->gfx_filtermask[2 * MAX_FILTERSHADERS][0]) {
-               struct shaderdata11 *s = allocshaderslot(d3d, SHADERTYPE_MASK_AFTER);
-               createmasktexture(d3d, filterd3d->gfx_filtermask[2 * MAX_FILTERSHADERS], s);
-       }
-       for (int i = 0; i < MAX_FILTERSHADERS; i++) {
-               if (filterd3d->gfx_filtermask[i + MAX_FILTERSHADERS][0]) {
-                       struct shaderdata11 *s = allocshaderslot(d3d, SHADERTYPE_MASK_AFTER);
-                       createmasktexture(d3d, filterd3d->gfx_filtermask[i + MAX_FILTERSHADERS], s);
-               }
-       }
-#endif
+
 
        createscanlines(d3d, 1);
        createmask2texture(d3d, filterd3d->gfx_filteroverlay);
@@ -3836,10 +4037,14 @@ static bool restore(struct d3d11struct *d3d)
        return true;
 }
 
+static void resizemode(struct d3d11struct *d3d);
+
 static bool xD3D11_renderframe(bool immediate)
 {
        struct d3d11struct *d3d = &d3d11data[0];
 
+       d3d->frames_since_init++;
+
        if (!d3d->m_swapChain)
                return false;
 
@@ -3856,12 +4061,30 @@ static bool xD3D11_renderframe(bool immediate)
 
        GraphicsClass_Render(d3d, 0);
 
+       if (d3d->filenotificationhandle != NULL) {
+               bool notify = false;
+               while (WaitForSingleObject(d3d->filenotificationhandle, 0) == WAIT_OBJECT_0) {
+                       if (FindNextChangeNotification(d3d->filenotificationhandle)) {
+                               if (d3d->frames_since_init > 50)
+                                       notify = true;
+                       }
+               }
+               if (notify) {
+                       write_log(_T("D3D11 shader file modification notification.\n"));
+                       D3D_resize(0);
+               }
+       }
+
        return true;
 }
 
 static void xD3D11_showframe(void)
 {
        struct d3d11struct *d3d = &d3d11data[0];
+
+       if (vblankevent)
+               ResetEvent(vblankevent);
+
        if (d3d->invalidmode)
                return;
        if (!d3d->m_swapChain)
@@ -3908,7 +4131,8 @@ static void recheck(struct d3d11struct *d3d)
        if (d3d->delayedfs) {
                d3d->delayedfs = false;
                ShowWindow(d3d->ahwnd, SW_SHOWNORMAL);
-               if (!xxD3D11_init2(d3d->ahwnd, d3d->m_screenWidth, d3d->m_screenHeight, d3d->m_bitmapWidth2, d3d->m_bitmapHeight2, 32, NULL, d3d->dmultx))
+               int freq = 0;
+               if (!xxD3D11_init2(d3d->ahwnd, d3d->m_screenWidth, d3d->m_screenHeight, d3d->m_bitmapWidth2, d3d->m_bitmapHeight2, 32, &freq, d3d->dmultx))
                        d3d->invalidmode = true;
        }
 }
@@ -3957,8 +4181,14 @@ static uae_u8 *xD3D11_locktexture(int *pitch, int *height, bool fullupdate)
 
        if (d3d->invalidmode || !d3d->texture2d)
                return NULL;
+
+       EnterCriticalSection(&present_cs);
+
        D3D11_MAPPED_SUBRESOURCE map;
        HRESULT hr = d3d->m_deviceContext->Map(d3d->texture2dstaging, 0, D3D11_MAP_WRITE, 0, &map);
+
+       LeaveCriticalSection(&present_cs);
+
        if (FAILED(hr)) {
                write_log(_T("D3D11 Map() %08x\n"), hr);
                return NULL;
@@ -3978,6 +4208,8 @@ static void xD3D11_unlocktexture(void)
                return;
        d3d->texturelocked--;
 
+       EnterCriticalSection(&present_cs);
+
        d3d->m_deviceContext->Unmap(d3d->texture2dstaging, 0);
 
        if (currprefs.leds_on_screen & (STATUSLINE_CHIPSET | STATUSLINE_RTG)) {
@@ -3992,6 +4224,8 @@ static void xD3D11_unlocktexture(void)
        box.top = 0;
        box.bottom = d3d->m_bitmapHeight;
        d3d->m_deviceContext->CopySubresourceRegion(d3d->texture2d, 0, 0, 0, 0, d3d->texture2dstaging, 0, &box);
+
+       LeaveCriticalSection(&present_cs);
 }
 
 static void xD3D11_flushtexture(int miny, int maxy)
@@ -4013,8 +4247,6 @@ static void xD3D11_vblank_reset(double freq)
 
 static int xD3D11_canshaders(void)
 {
-       if (!CUSTOMSHADERS)
-               return 0;
        return (can_D3D11(false) & 4) != 0;
 }
 
@@ -4110,6 +4342,7 @@ static void xD3D11_resize(int activate)
                        d3d->fsmodechange = 0;
                }
                resizemode(d3d);
+               notice_screen_contents_lost();
        }
 
        recursive--;
@@ -4285,6 +4518,53 @@ static bool xD3D11_getscalerect(float *mx, float *my, float *sx, float *sy)
        return true;
 }
 
+double d3d11_get_hz(void)
+{
+       struct d3d11struct *d3d = &d3d11data[0];
+       d3d->lastframe = 0;
+       if (isfs(d3d) > 0) {
+               d3d->vblank = (double)d3d->fsSwapChainDesc.RefreshRate.Numerator / d3d->fsSwapChainDesc.RefreshRate.Denominator;
+               return d3d->vblank;
+       }
+       if (!pDwmGetCompositionTimingInfo)
+               return 0;
+       DWM_TIMING_INFO ti;
+       ti.cbSize = sizeof ti;
+       HRESULT hr = pDwmGetCompositionTimingInfo(NULL, &ti);
+       if (FAILED(hr)) {
+               write_log(_T("DwmGetCompositionTimingInfo1 %08x\n"), hr);
+               return 0;
+       }
+       d3d->vblank = (double)ti.rateRefresh.uiNumerator / ti.rateRefresh.uiDenominator;
+       return d3d->vblank;
+}
+
+bool d3d11_vsync_isdone(void)
+{
+       struct d3d11struct *d3d = &d3d11data[0];
+       if (vblankevent) {
+               if (WaitForSingleObject(vblankevent, 0) == WAIT_OBJECT_0)
+                       return true;
+               return false;
+       }
+       if (!pDwmGetCompositionTimingInfo)
+               return false;
+       DWM_TIMING_INFO ti;
+       ti.cbSize = sizeof ti;
+       HRESULT hr = pDwmGetCompositionTimingInfo(NULL, &ti);
+       if (FAILED(hr)) {
+               write_log(_T("DwmGetCompositionTimingInfo2 %08x\n"), hr);
+               return false;
+       }
+       QPC_TIME qpc = ti.qpcVBlank + ti.qpcRefreshPeriod;
+       LARGE_INTEGER now;
+       QueryPerformanceCounter(&now);
+       if (now.QuadPart >= qpc) {
+               return true;
+       }
+       return false;
+}
+
 void d3d11_select(void)
 {
        D3D_free = xD3D11_free;
index eb2cecd2f810288e82a0da3733103b2c27194532..c8a07f891ae1caf22f571153b5975a47af919e41 100644 (file)
@@ -1244,6 +1244,8 @@ void show_screen_special (void)
 {
        if (!screen_is_initialized)
                return;
+       if (!D3D_showframe_special)
+               return;
        if (currentmode->flags & DM_D3D) {
                gfx_lock();
                D3D_showframe_special (1);
@@ -1304,7 +1306,7 @@ void show_screen (int mode)
        strobo_active2 = false;
        gfx_lock();
        if (mode == 2) {
-               if (currentmode->flags & DM_D3D) {
+               if ((currentmode->flags & DM_D3D) && D3D_showframe_special) {
                        D3D_showframe_special (1);
                }
                gfx_unlock();
@@ -1316,7 +1318,7 @@ void show_screen (int mode)
        }
        if (currentmode->flags & DM_D3D) {
                struct apmode *ap = picasso_on ? &currprefs.gfx_apmode[1] : &currprefs.gfx_apmode[0];
-               if (ap->gfx_vsync < 0 && ap->gfx_strobo) {
+               if (ap->gfx_vsync < 0 && ap->gfx_strobo && currprefs.gfx_api < 2) {
                        double vblank = vblank_hz;
                        if (WIN32GFX_IsPicassoScreen()) {
                                if (currprefs.win32_rtgvblankrate > 0)
@@ -2069,7 +2071,8 @@ int check_prefs_changed_gfx (void)
 #endif
        c |= currprefs.gfx_autoresolution != changed_prefs.gfx_autoresolution ? (2|8|16) : 0;
        c |= currprefs.gfx_autoresolution_vga != changed_prefs.gfx_autoresolution_vga ? (2|8|16) : 0;
-       c |= currprefs.gfx_api != changed_prefs.gfx_api ? (1|8|32) : 0;
+       c |= currprefs.gfx_api != changed_prefs.gfx_api ? (1 | 8 | 32) : 0;
+       c |= currprefs.gfx_api_options != changed_prefs.gfx_api_options ? (1 | 8 | 32) : 0;
        c |= currprefs.lightboost_strobo != changed_prefs.lightboost_strobo ? (2|16) : 0;
 
        for (int j = 0; j < 2; j++) {
@@ -2141,9 +2144,9 @@ int check_prefs_changed_gfx (void)
        c |= currprefs.gfx_apmode[APMODE_NATIVE].gfx_display != changed_prefs.gfx_apmode[APMODE_NATIVE].gfx_display ? (2|4|8) : 0;
        c |= currprefs.gfx_apmode[APMODE_RTG].gfx_display != changed_prefs.gfx_apmode[APMODE_RTG].gfx_display ? (2|4|8) : 0;
        c |= currprefs.gfx_blackerthanblack != changed_prefs.gfx_blackerthanblack ? (2 | 8) : 0;
-       c |= currprefs.gfx_apmode[APMODE_NATIVE].gfx_backbuffers != changed_prefs.gfx_apmode[APMODE_NATIVE].gfx_backbuffers ? (2 | 8) : 0;
+       c |= currprefs.gfx_apmode[APMODE_NATIVE].gfx_backbuffers != changed_prefs.gfx_apmode[APMODE_NATIVE].gfx_backbuffers ? (2 | 16) : 0;
        c |= currprefs.gfx_apmode[APMODE_NATIVE].gfx_interlaced != changed_prefs.gfx_apmode[APMODE_NATIVE].gfx_interlaced ? (2 | 8) : 0;
-       c |= currprefs.gfx_apmode[APMODE_RTG].gfx_backbuffers != changed_prefs.gfx_apmode[APMODE_RTG].gfx_backbuffers ? (2 | 8) : 0;
+       c |= currprefs.gfx_apmode[APMODE_RTG].gfx_backbuffers != changed_prefs.gfx_apmode[APMODE_RTG].gfx_backbuffers ? (2 | 16) : 0;
 
        c |= currprefs.win32_main_alwaysontop != changed_prefs.win32_main_alwaysontop ? 32 : 0;
        c |= currprefs.win32_gui_alwaysontop != changed_prefs.win32_gui_alwaysontop ? 2 : 0;
@@ -2297,8 +2300,9 @@ int check_prefs_changed_gfx (void)
                                unacquired = true;
                        }
                        close_windows ();
-                       if (currprefs.gfx_api != changed_prefs.gfx_api) {
+                       if (currprefs.gfx_api != changed_prefs.gfx_api || currprefs.gfx_api_options != changed_prefs.gfx_api_options) {
                                currprefs.gfx_api = changed_prefs.gfx_api;
+                               currprefs.gfx_api_options = changed_prefs.gfx_api_options;
                                d3d_select(&currprefs);
                        }
                        graphics_init (dontcapture ? false : true);
@@ -3705,23 +3709,27 @@ bool vsync_isdone (void)
 {
        if (isvsync () == 0)
                return false;
-       if (!isthreadedvsync ()) {
-               int vp = -2;
-               getvblankpos (&vp, true);
-               if (!vblankthread_oddeven_got) {
-                       // need to get odd/even state early
-                       while (vp < 0) {
-                               if (!getvblankpos (&vp, true))
-                                       break;
+       if (currprefs.gfx_api == 2) {
+               return d3d11_vsync_isdone();
+       } else {
+               if (!isthreadedvsync()) {
+                       int vp = -2;
+                       getvblankpos(&vp, true);
+                       if (!vblankthread_oddeven_got) {
+                               // need to get odd/even state early
+                               while (vp < 0) {
+                                       if (!getvblankpos(&vp, true))
+                                               break;
+                               }
+                               vblankthread_oddeven = (vp & 1) != 0;
+                               vblankthread_oddeven_got = true;
                        }
-                       vblankthread_oddeven = (vp & 1) != 0;
-                       vblankthread_oddeven_got = true;
                }
+               if (dooddevenskip)
+                       return true;
+               if (vblank_found_chipset)
+                       return true;
        }
-       if (dooddevenskip)
-               return true;
-       if (vblank_found_chipset)
-               return true;
        return false;
 }
 
@@ -3747,6 +3755,11 @@ int vsync_busywait_do (int *freetime, bool lace, bool oddeven)
        frame_time_t prevtime = vblank_prev_time;
        struct apmode *ap = picasso_on ? &currprefs.gfx_apmode[1] : &currprefs.gfx_apmode[0];
 
+       if (currprefs.gfx_api == 2) {
+               show_screen(0);
+               return 1;
+       }
+
        vblank_sync_started = true;
        if (lace)
                vblankbaselace_chipset = oddeven == true ? 1 : 0;
@@ -3914,8 +3927,13 @@ double vblank_calibrate (double approx_vblank, bool waitonly)
        bool remembered = false;
        bool lace = false;
 
-       if (currprefs.gfx_api == 2)
-               goto fail;
+       if (currprefs.gfx_api == 2) {
+               double hz = d3d11_get_hz();
+               if (hz <= 0)
+                       goto fail;
+               write_log(_T("VSync: %.6fHz\n"), hz);
+               return hz;
+       }
 
        if (picasso_on) {
                width = picasso96_state.Width;
@@ -4668,7 +4686,7 @@ retry:
                                D3D_free(true);
                                error_log(_T("Direct3D9 failed to initialize, falling back to DirectDraw."));
                                changed_prefs.gfx_api = currprefs.gfx_api = 0;
-                               changed_prefs.gf[picasso_on].gfx_filter = currprefs.gf[picasso_on].gfx_filter = 0;
+                               changed_prefs.gf[picasso_on].gfx_filter = currprefs.gf[picasso_on].gfx_filter = 1;
                                currentmode->current_depth = currentmode->native_depth;
                                gfxmode_reset();
                                DirectDraw_Start();