From 537ef9c5959c7ab89aaa99486925530ced368ced Mon Sep 17 00:00:00 2001 From: narzoul Date: Wed, 3 Oct 2018 20:49:50 +0200 Subject: [PATCH] Improved presentation scheduling Fixes cursor flickering issues in Siege of Avalon (issue #34) and multimon flip issues on Windows 8.1 (issue #28). --- DDrawCompat/Common/Time.h | 6 +- DDrawCompat/Config/Config.h | 1 + DDrawCompat/D3dDdi/Device.cpp | 5 +- DDrawCompat/D3dDdi/KernelModeThunks.cpp | 309 +++++----- DDrawCompat/D3dDdi/KernelModeThunks.h | 20 +- DDrawCompat/DDraw/DirectDraw.cpp | 26 + DDrawCompat/DDraw/DirectDraw.h | 2 + DDrawCompat/DDraw/DirectDrawSurface.cpp | 2 + DDrawCompat/DDraw/RealPrimarySurface.cpp | 559 +++++++++++------- DDrawCompat/DDraw/RealPrimarySurface.h | 7 +- DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp | 80 ++- DDrawCompat/DDraw/Surfaces/PrimarySurface.h | 7 +- .../DDraw/Surfaces/PrimarySurfaceImpl.cpp | 41 +- DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp | 95 ++- DDrawCompat/DDraw/Surfaces/SurfaceImpl.h | 4 +- DDrawCompat/Gdi/AccessGuard.cpp | 7 +- 16 files changed, 708 insertions(+), 463 deletions(-) diff --git a/DDrawCompat/Common/Time.h b/DDrawCompat/Common/Time.h index d1eccc3..5aac53e 100644 --- a/DDrawCompat/Common/Time.h +++ b/DDrawCompat/Common/Time.h @@ -10,14 +10,14 @@ namespace Time void init(); - inline long long msToQpc(int ms) + inline long long msToQpc(long long ms) { return static_cast(ms) * g_qpcFrequency / 1000; } - inline int qpcToMs(long long qpc) + inline long long qpcToMs(long long qpc) { - return static_cast(qpc * 1000 / g_qpcFrequency); + return qpc * 1000 / g_qpcFrequency; } inline long long queryPerformanceCounter() diff --git a/DDrawCompat/Config/Config.h b/DDrawCompat/Config/Config.h index 17fa87b..a5fc504 100644 --- a/DDrawCompat/Config/Config.h +++ b/DDrawCompat/Config/Config.h @@ -4,6 +4,7 @@ typedef unsigned long DWORD; namespace Config { + const int delayedFlipModeTimeout = 200; const int maxPaletteUpdatesPerMs = 5; const int minExpectedFlipsPerSec = 5; const DWORD primarySurfaceExtraRows = 2; diff --git a/DDrawCompat/D3dDdi/Device.cpp b/DDrawCompat/D3dDdi/Device.cpp index 117d146..d80c176 100644 --- a/DDrawCompat/D3dDdi/Device.cpp +++ b/DDrawCompat/D3dDdi/Device.cpp @@ -1,3 +1,6 @@ +#include +#include <../km/d3dkmthk.h> + #include "D3dDdi/AdapterFuncs.h" #include "D3dDdi/Device.h" #include "D3dDdi/DeviceFuncs.h" @@ -208,7 +211,7 @@ namespace D3dDdi { if (resource == m_sharedPrimary) { - KernelModeThunks::releaseVidPnSources(); + D3DKMTReleaseProcessVidPnSourceOwners(GetCurrentProcess()); } HRESULT result = m_origVtable->pfnDestroyResource(m_device, resource); diff --git a/DDrawCompat/D3dDdi/KernelModeThunks.cpp b/DDrawCompat/D3dDdi/KernelModeThunks.cpp index 603e84a..e424da7 100644 --- a/DDrawCompat/D3dDdi/KernelModeThunks.cpp +++ b/DDrawCompat/D3dDdi/KernelModeThunks.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -6,12 +7,23 @@ #include "Common/Log.h" #include "Common/Hook.h" +#include "Common/ScopedCriticalSection.h" +#include "Common/Time.h" #include "D3dDdi/Hooks.h" #include "D3dDdi/KernelModeThunks.h" +#include "D3dDdi/Log/KernelModeThunksLog.h" #include "DDraw/Surfaces/PrimarySurface.h" +#include "Win32/DisplayMode.h" namespace { + struct AdapterInfo + { + UINT adapter; + UINT vidPnSourceId; + RECT monitorRect; + }; + struct ContextInfo { D3DKMT_HANDLE device; @@ -19,40 +31,26 @@ namespace ContextInfo() : device(0) {} }; - struct DeviceInfo - { - D3DKMT_HANDLE adapter; - D3DDDI_VIDEO_PRESENT_SOURCE_ID vidPnSourceId; - - DeviceInfo() : adapter(0), vidPnSourceId(D3DDDI_ID_UNINITIALIZED) {} - }; - std::map g_contexts; - std::map g_devices; - HMONITOR g_lastOpenAdapterMonitor = nullptr; + AdapterInfo g_gdiAdapterInfo = {}; + AdapterInfo g_lastOpenAdapterInfo = {}; + UINT g_lastFlipInterval = 0; + UINT g_flipIntervalOverride = 0; + D3DKMT_HANDLE g_lastPresentContext = 0; + UINT g_presentCount = 0; + std::atomic g_qpcLastVerticalBlank = 0; + CRITICAL_SECTION g_vblankCs = {}; decltype(D3DKMTCreateContextVirtual)* g_origD3dKmtCreateContextVirtual = nullptr; - decltype(D3DKMTSetVidPnSourceOwner1)* g_origD3dKmtSetVidPnSourceOwner1 = nullptr; - D3DDDI_FLIPINTERVAL_TYPE g_overrideFlipInterval = D3DDDI_FLIPINTERVAL_NOOVERRIDE; - UINT g_presentCount = 0; - - NTSTATUS APIENTRY createDevice(D3DKMT_CREATEDEVICE* pData) + NTSTATUS APIENTRY closeAdapter(const D3DKMT_CLOSEADAPTER* pData) { - Compat::LogEnter("D3DKMTCreateDevice", pData); - NTSTATUS result = CALL_ORIG_FUNC(D3DKMTCreateDevice)(pData); - if (SUCCEEDED(result)) + Compat::ScopedCriticalSection lock(g_vblankCs); + if (pData && pData->hAdapter == g_lastOpenAdapterInfo.adapter) { - g_devices[pData->hDevice].adapter = pData->hAdapter; - - D3DKMT_SETQUEUEDLIMIT limit = {}; - limit.hDevice = pData->hDevice; - limit.Type = D3DKMT_SET_QUEUEDLIMIT_PRESENT; - limit.QueuedPresentLimit = 1; - CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(&limit); + g_lastOpenAdapterInfo = {}; } - Compat::LogLeave("D3DKMTCreateDevice", pData) << result; - return result; + return CALL_ORIG_FUNC(D3DKMTCloseAdapter)(pData); } NTSTATUS APIENTRY createContext(D3DKMT_CREATECONTEXT* pData) @@ -98,6 +96,22 @@ namespace return result; } + NTSTATUS APIENTRY createDevice(D3DKMT_CREATEDEVICE* pData) + { + Compat::LogEnter("D3DKMTCreateDevice", pData); + NTSTATUS result = CALL_ORIG_FUNC(D3DKMTCreateDevice)(pData); + if (SUCCEEDED(result)) + { + D3DKMT_SETQUEUEDLIMIT limit = {}; + limit.hDevice = pData->hDevice; + limit.Type = D3DKMT_SET_QUEUEDLIMIT_PRESENT; + limit.QueuedPresentLimit = 2; + CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(&limit); + } + Compat::LogLeave("D3DKMTCreateDevice", pData) << result; + return result; + } + NTSTATUS APIENTRY destroyContext(const D3DKMT_DESTROYCONTEXT* pData) { Compat::LogEnter("D3DKMTDestroyContext", pData); @@ -105,89 +119,71 @@ namespace if (SUCCEEDED(result)) { g_contexts.erase(pData->hContext); + if (g_lastPresentContext == pData->hContext) + { + g_lastPresentContext = 0; + } } Compat::LogLeave("D3DKMTDestroyContext", pData) << result; return result; } - NTSTATUS APIENTRY destroyDevice(const D3DKMT_DESTROYDEVICE* pData) + AdapterInfo getAdapterInfo(const D3DKMT_OPENADAPTERFROMHDC& data) { - Compat::LogEnter("D3DKMTDestroyDevice", pData); - NTSTATUS result = CALL_ORIG_FUNC(D3DKMTDestroyDevice)(pData); - if (SUCCEEDED(result)) - { - g_devices.erase(pData->hDevice); - } - Compat::LogLeave("D3DKMTDestroyDevice", pData) << result; - return result; + AdapterInfo adapterInfo = {}; + adapterInfo.adapter = data.hAdapter; + adapterInfo.vidPnSourceId = data.VidPnSourceId; + + POINT p = {}; + GetDCOrgEx(data.hDc, &p); + MONITORINFO mi = {}; + mi.cbSize = sizeof(mi); + GetMonitorInfo(MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY), &mi); + adapterInfo.monitorRect = mi.rcMonitor; + + return adapterInfo; } NTSTATUS APIENTRY openAdapterFromHdc(D3DKMT_OPENADAPTERFROMHDC* pData) { Compat::LogEnter("D3DKMTOpenAdapterFromHdc", pData); NTSTATUS result = CALL_ORIG_FUNC(D3DKMTOpenAdapterFromHdc)(pData); - if (pData) + if (SUCCEEDED(result)) { - POINT p = {}; - GetDCOrgEx(pData->hDc, &p); - g_lastOpenAdapterMonitor = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY); + Compat::ScopedCriticalSection lock(g_vblankCs); + g_lastOpenAdapterInfo = getAdapterInfo(*pData); } Compat::LogLeave("D3DKMTOpenAdapterFromHdc", pData) << result; return result; } - bool isPresentReady(D3DKMT_HANDLE device, D3DDDI_VIDEO_PRESENT_SOURCE_ID vidPnSourceId) - { - D3DKMT_GETDEVICESTATE deviceState = {}; - deviceState.hDevice = device; - deviceState.StateType = D3DKMT_DEVICESTATE_PRESENT; - deviceState.PresentState.VidPnSourceId = vidPnSourceId; - NTSTATUS stateResult = D3DKMTGetDeviceState(&deviceState); - return FAILED(stateResult) || - g_presentCount == deviceState.PresentState.PresentStats.PresentCount || - 0 == deviceState.PresentState.PresentStats.PresentCount; - } - NTSTATUS APIENTRY present(D3DKMT_PRESENT* pData) { Compat::LogEnter("D3DKMTPresent", pData); - if (pData->Flags.Flip && D3DDDI_FLIPINTERVAL_NOOVERRIDE != g_overrideFlipInterval) + if (pData->Flags.Flip) { - pData->FlipInterval = g_overrideFlipInterval; - } + g_lastFlipInterval = pData->FlipInterval; + g_lastPresentContext = pData->hContext; - ++g_presentCount; - pData->Flags.PresentCountValid = 1; - pData->PresentCount = g_presentCount; + if (UINT_MAX == g_flipIntervalOverride) + { + Compat::LogLeave("D3DKMTPresent", pData) << S_OK; + return S_OK; + } + + ++g_presentCount; + if (0 == g_presentCount) + { + g_presentCount = 1; + } + + pData->PresentCount = g_presentCount; + pData->Flags.PresentCountValid = 1; + pData->FlipInterval = static_cast(g_flipIntervalOverride); + } NTSTATUS result = CALL_ORIG_FUNC(D3DKMTPresent)(pData); - if (SUCCEEDED(result) && - 1 == DDraw::PrimarySurface::getDesc().dwBackBufferCount && - pData->Flags.Flip && - D3DDDI_FLIPINTERVAL_IMMEDIATE != pData->FlipInterval && - D3DDDI_FLIPINTERVAL_NOOVERRIDE == g_overrideFlipInterval) - { - auto contextIt = g_contexts.find(pData->hContext); - auto deviceIt = (contextIt != g_contexts.end()) - ? g_devices.find(contextIt->second.device) - : g_devices.find(pData->hDevice); - if (deviceIt != g_devices.end()) - { - D3DKMT_WAITFORVERTICALBLANKEVENT vbEvent = {}; - vbEvent.hAdapter = deviceIt->second.adapter; - vbEvent.hDevice = deviceIt->first; - vbEvent.VidPnSourceId = deviceIt->second.vidPnSourceId; - - while (!isPresentReady(deviceIt->first, deviceIt->second.vidPnSourceId)) - { - if (FAILED(D3DKMTWaitForVerticalBlankEvent(&vbEvent))) - { - Sleep(1); - } - } - } - } Compat::LogLeave("D3DKMTPresent", pData) << result; return result; @@ -212,7 +208,7 @@ namespace if (D3DKMT_SET_QUEUEDLIMIT_PRESENT == pData->Type) { const UINT origLimit = pData->QueuedPresentLimit; - const_cast(pData)->QueuedPresentLimit = 1; + const_cast(pData)->QueuedPresentLimit = 2; NTSTATUS result = CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(pData); const_cast(pData)->QueuedPresentLimit = origLimit; Compat::LogLeave("D3DKMTSetQueuedLimit", pData) << result; @@ -223,42 +219,30 @@ namespace return result; } - void processSetVidPnSourceOwner(const D3DKMT_SETVIDPNSOURCEOWNER* pData) + void updateGdiAdapterInfo() { - auto& vidPnSourceId = g_devices[pData->hDevice].vidPnSourceId; - for (UINT i = 0; i < pData->VidPnSourceCount; ++i) + static auto lastDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1; + const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness(); + if (currentDisplaySettingsUniqueness != lastDisplaySettingsUniqueness) { - if (D3DKMT_VIDPNSOURCEOWNER_UNOWNED != pData->pType[i]) + if (g_gdiAdapterInfo.adapter) { - vidPnSourceId = pData->pVidPnSourceId[i]; - return; + D3DKMT_CLOSEADAPTER data = {}; + data.hAdapter = g_gdiAdapterInfo.adapter; + CALL_ORIG_FUNC(D3DKMTCloseAdapter)(&data); + g_gdiAdapterInfo = {}; } - } - vidPnSourceId = D3DDDI_ID_UNINITIALIZED; - } - NTSTATUS APIENTRY setVidPnSourceOwner(const D3DKMT_SETVIDPNSOURCEOWNER* pData) - { - Compat::LogEnter("D3DKMTSetVidPnSourceOwner", pData); - NTSTATUS result = CALL_ORIG_FUNC(D3DKMTSetVidPnSourceOwner)(pData); - if (SUCCEEDED(result)) - { - processSetVidPnSourceOwner(pData); - } - Compat::LogLeave("D3DKMTSetVidPnSourceOwner", pData) << result; - return result; - } + D3DKMT_OPENADAPTERFROMHDC data = {}; + data.hDc = GetDC(nullptr); + if (SUCCEEDED(CALL_ORIG_FUNC(D3DKMTOpenAdapterFromHdc)(&data))) + { + g_gdiAdapterInfo = getAdapterInfo(data); + } + ReleaseDC(nullptr, data.hDc); - NTSTATUS APIENTRY setVidPnSourceOwner1(const D3DKMT_SETVIDPNSOURCEOWNER1* pData) - { - Compat::LogEnter("D3DKMTSetVidPnSourceOwner1", pData); - NTSTATUS result = g_origD3dKmtSetVidPnSourceOwner1(pData); - if (SUCCEEDED(result)) - { - processSetVidPnSourceOwner(&pData->Version0); + lastDisplaySettingsUniqueness = currentDisplaySettingsUniqueness; } - Compat::LogLeave("D3DKMTSetVidPnSourceOwner1", pData) << result; - return result; } } @@ -266,58 +250,113 @@ namespace D3dDdi { namespace KernelModeThunks { - HMONITOR getLastOpenAdapterMonitor() + UINT getLastFlipInterval() { - return g_lastOpenAdapterMonitor; + return g_lastFlipInterval; } - bool isPresentReady() + UINT getLastDisplayedFrameCount() { - for (auto it : g_devices) + auto contextIter = g_contexts.find(g_lastPresentContext); + if (contextIter == g_contexts.end()) { - if (D3DDDI_ID_UNINITIALIZED != it.second.vidPnSourceId) - { - return ::isPresentReady(it.first, it.second.vidPnSourceId); - } + return g_presentCount; } - return true; + + D3DKMT_GETDEVICESTATE data = {}; + data.hDevice = contextIter->second.device; + data.StateType = D3DKMT_DEVICESTATE_PRESENT; + data.PresentState.VidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId; + D3DKMTGetDeviceState(&data); + + if (0 == data.PresentState.PresentStats.PresentCount) + { + return g_presentCount; + } + + return data.PresentState.PresentStats.PresentCount; + } + + UINT getLastSubmittedFrameCount() + { + return g_presentCount; + } + + RECT getMonitorRect() + { + auto primary(DDraw::PrimarySurface::getPrimary()); + if (!primary) + { + return {}; + } + + static auto lastDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1; + const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness(); + if (currentDisplaySettingsUniqueness != lastDisplaySettingsUniqueness) + { + lastDisplaySettingsUniqueness = currentDisplaySettingsUniqueness; + CompatPtr ddUnk; + primary->GetDDInterface(primary, reinterpret_cast(&ddUnk.getRef())); + CompatPtr dd7(ddUnk); + + DDDEVICEIDENTIFIER2 di = {}; + dd7->GetDeviceIdentifier(dd7, &di, 0); + } + + return g_lastOpenAdapterInfo.monitorRect; + } + + long long getQpcLastVerticalBlank() + { + return g_qpcLastVerticalBlank; } void installHooks() { + InitializeCriticalSection(&g_vblankCs); + HOOK_FUNCTION(gdi32, D3DKMTCloseAdapter, closeAdapter); HOOK_FUNCTION(gdi32, D3DKMTCreateContext, createContext); HOOK_FUNCTION(gdi32, D3DKMTCreateDevice, createDevice); HOOK_FUNCTION(gdi32, D3DKMTCreateDCFromMemory, createDcFromMemory); HOOK_FUNCTION(gdi32, D3DKMTDestroyContext, destroyContext); - HOOK_FUNCTION(gdi32, D3DKMTDestroyDevice, destroyDevice); HOOK_FUNCTION(gdi32, D3DKMTOpenAdapterFromHdc, openAdapterFromHdc); HOOK_FUNCTION(gdi32, D3DKMTQueryAdapterInfo, queryAdapterInfo); HOOK_FUNCTION(gdi32, D3DKMTPresent, present); HOOK_FUNCTION(gdi32, D3DKMTSetQueuedLimit, setQueuedLimit); - HOOK_FUNCTION(gdi32, D3DKMTSetVidPnSourceOwner, setVidPnSourceOwner); // Functions not available in Windows Vista Compat::hookFunction("gdi32", "D3DKMTCreateContextVirtual", reinterpret_cast(g_origD3dKmtCreateContextVirtual), createContextVirtual); - Compat::hookFunction("gdi32", "D3DKMTSetVidPnSourceOwner1", - reinterpret_cast(g_origD3dKmtSetVidPnSourceOwner1), setVidPnSourceOwner1); } - void overrideFlipInterval(D3DDDI_FLIPINTERVAL_TYPE flipInterval) + void setFlipIntervalOverride(UINT flipInterval) { - g_overrideFlipInterval = flipInterval; + g_flipIntervalOverride = flipInterval; } - void releaseVidPnSources() + void waitForVerticalBlank() { - for (auto it : g_devices) + D3DKMT_WAITFORVERTICALBLANKEVENT data = {}; + { - if (D3DDDI_ID_UNINITIALIZED != it.second.vidPnSourceId) + Compat::ScopedCriticalSection lock(g_vblankCs); + if (g_lastOpenAdapterInfo.adapter) { - D3DKMT_SETVIDPNSOURCEOWNER vidPnSourceOwner = {}; - vidPnSourceOwner.hDevice = it.first; - D3DKMTSetVidPnSourceOwner(&vidPnSourceOwner); + data.hAdapter = g_lastOpenAdapterInfo.adapter; + data.VidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId; } + else + { + updateGdiAdapterInfo(); + data.hAdapter = g_gdiAdapterInfo.adapter; + data.VidPnSourceId = g_gdiAdapterInfo.vidPnSourceId; + } + } + + if (data.hAdapter) + { + D3DKMTWaitForVerticalBlankEvent(&data); + g_qpcLastVerticalBlank = Time::queryPerformanceCounter(); } } } diff --git a/DDrawCompat/D3dDdi/KernelModeThunks.h b/DDrawCompat/D3dDdi/KernelModeThunks.h index d63d184..d56553f 100644 --- a/DDrawCompat/D3dDdi/KernelModeThunks.h +++ b/DDrawCompat/D3dDdi/KernelModeThunks.h @@ -1,22 +1,20 @@ #pragma once -#include -#include -#include <../km/d3dkmthk.h> +#define WIN32_LEAN_AND_MEAN + #include -#include "D3dDdi/Log/KernelModeThunksLog.h" - -static const auto D3DDDI_FLIPINTERVAL_NOOVERRIDE = static_cast(5); - namespace D3dDdi { namespace KernelModeThunks { - HMONITOR getLastOpenAdapterMonitor(); + UINT getLastFlipInterval(); + UINT getLastDisplayedFrameCount(); + UINT getLastSubmittedFrameCount(); + RECT getMonitorRect(); + long long getQpcLastVerticalBlank(); void installHooks(); - bool isPresentReady(); - void overrideFlipInterval(D3DDDI_FLIPINTERVAL_TYPE flipInterval); - void releaseVidPnSources(); + void setFlipIntervalOverride(UINT flipInterval); + void waitForVerticalBlank(); } } diff --git a/DDrawCompat/DDraw/DirectDraw.cpp b/DDrawCompat/DDraw/DirectDraw.cpp index b44d428..84491ab 100644 --- a/DDrawCompat/DDraw/DirectDraw.cpp +++ b/DDrawCompat/DDraw/DirectDraw.cpp @@ -1,4 +1,5 @@ #include "Common/CompatPtr.h" +#include "D3dDdi/KernelModeThunks.h" #include "DDraw/ActivateAppHandler.h" #include "DDraw/DirectDraw.h" #include "DDraw/Repository.h" @@ -117,6 +118,7 @@ namespace DDraw vtable.GetGDISurface = &GetGDISurface; vtable.SetCooperativeLevel = &SetCooperativeLevel; vtable.SetDisplayMode = &SetDisplayMode; + vtable.WaitForVerticalBlank = &WaitForVerticalBlank; } template @@ -206,6 +208,30 @@ namespace DDraw return setDisplayMode(This, dwWidth, dwHeight, dwBPP, params...); } + template + HRESULT STDMETHODCALLTYPE DirectDraw::WaitForVerticalBlank( + TDirectDraw* This, DWORD dwFlags, HANDLE hEvent) + { + if (!This || (DDWAITVB_BLOCKBEGIN != dwFlags && DDWAITVB_BLOCKEND != dwFlags)) + { + return s_origVtable.WaitForVerticalBlank(This, dwFlags, hEvent); + } + + DWORD scanLine = 0; + if (DDERR_VERTICALBLANKINPROGRESS != s_origVtable.GetScanLine(This, &scanLine)) + { + D3dDdi::KernelModeThunks::waitForVerticalBlank(); + } + + if (DDWAITVB_BLOCKEND == dwFlags) + { + while (DDERR_VERTICALBLANKINPROGRESS == s_origVtable.GetScanLine(This, &scanLine)); + } + + return DD_OK; + } + + template DirectDraw; template DirectDraw; template DirectDraw; diff --git a/DDrawCompat/DDraw/DirectDraw.h b/DDrawCompat/DDraw/DirectDraw.h index 9a7e232..4c1ab09 100644 --- a/DDrawCompat/DDraw/DirectDraw.h +++ b/DDrawCompat/DDraw/DirectDraw.h @@ -46,6 +46,8 @@ namespace DDraw DWORD dwHeight, DWORD dwBPP, Params... params); + + static HRESULT STDMETHODCALLTYPE WaitForVerticalBlank(TDirectDraw* This, DWORD dwFlags, HANDLE hEvent); }; } diff --git a/DDrawCompat/DDraw/DirectDrawSurface.cpp b/DDrawCompat/DDraw/DirectDrawSurface.cpp index 05472d7..b912b8c 100644 --- a/DDrawCompat/DDraw/DirectDrawSurface.cpp +++ b/DDrawCompat/DDraw/DirectDrawSurface.cpp @@ -32,8 +32,10 @@ namespace DDraw SET_COMPAT_METHOD(Blt); SET_COMPAT_METHOD(BltFast); SET_COMPAT_METHOD(Flip); + SET_COMPAT_METHOD(GetBltStatus); SET_COMPAT_METHOD(GetCaps); SET_COMPAT_METHOD(GetDC); + SET_COMPAT_METHOD(GetFlipStatus); SET_COMPAT_METHOD(GetSurfaceDesc); SET_COMPAT_METHOD(IsLost); SET_COMPAT_METHOD(Lock); diff --git a/DDrawCompat/DDraw/RealPrimarySurface.cpp b/DDrawCompat/DDraw/RealPrimarySurface.cpp index f927602..969400b 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.cpp +++ b/DDrawCompat/DDraw/RealPrimarySurface.cpp @@ -1,4 +1,3 @@ -#include #include #include @@ -39,7 +38,6 @@ namespace DWORD WINAPI updateThreadProc(LPVOID lpParameter); CompatWeakPtr g_frontBuffer; - CompatWeakPtr g_backBuffer; CompatWeakPtr g_paletteConverter; CompatWeakPtr g_clipper; DDSURFACEDESC2 g_surfaceDesc = {}; @@ -47,15 +45,19 @@ namespace bool g_stopUpdateThread = false; HANDLE g_updateThread = nullptr; - HANDLE g_updateEvent = nullptr; - std::atomic g_disableUpdateCount = 0; - bool g_isUpdateSuspended = false; - long long g_qpcFlipModeTimeout = 0; - long long g_qpcLastFlip = 0; - long long g_qpcNextUpdate = 0; - long long g_qpcUpdateInterval = 0; + unsigned int g_disableUpdateCount = 0; + bool g_isFlipPending = false; + bool g_isPresentPending = false; + bool g_isUpdatePending = false; + bool g_isFullScreen = false; + bool g_waitingForPrimaryUnlock = false; + UINT g_flipIntervalDriverOverride = UINT_MAX; + UINT g_lastFlipFrameCount = 0; + DDraw::Surface* g_lastFlipSurface = nullptr; + long long g_qpcLastUpdate = 0; - std::atomic g_isFullScreen(false); + CompatPtr getBackBuffer(); + CompatPtr getLastSurface(); BOOL CALLBACK addVisibleLayeredWindowToVector(HWND hwnd, LPARAM lParam) { @@ -140,8 +142,14 @@ namespace return; } + auto backBuffer(getBackBuffer()); + if (!backBuffer) + { + return; + } + HDC backBufferDc = nullptr; - g_backBuffer->GetDC(g_backBuffer, &backBufferDc); + backBuffer->GetDC(backBuffer, &backBufferDc); for (auto it = visibleLayeredWindows.rbegin(); it != visibleLayeredWindows.rend(); ++it) { @@ -159,76 +167,22 @@ namespace ReleaseDC(*it, windowDc); } - g_backBuffer->ReleaseDC(g_backBuffer, backBufferDc); + backBuffer->ReleaseDC(backBuffer, backBufferDc); } - HRESULT bltToPrimaryChain(CompatRef src) + void bltToPrimaryChain(CompatRef src) { - if (g_isFullScreen) + if (!g_isFullScreen) { - return g_backBuffer->Blt(g_backBuffer, nullptr, &src, nullptr, DDBLT_WAIT, nullptr); + EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindow, reinterpret_cast(&src)); + return; } - EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindow, reinterpret_cast(&src)); - return DD_OK; - } - - bool compatBlt() - { - Compat::LogEnter("RealPrimarySurface::compatBlt"); - - BltToWindowViaGdiArgs bltToWindowViaGdiArgs; - if (!g_frontBuffer || (!g_isFullScreen && DDraw::RealPrimarySurface::isLost())) + auto backBuffer(getBackBuffer()); + if (backBuffer) { - EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi, - reinterpret_cast(&bltToWindowViaGdiArgs)); - Compat::LogLeave("RealPrimarySurface::compatBlt") << false; - return false; + backBuffer->Blt(backBuffer, nullptr, &src, nullptr, DDBLT_WAIT, nullptr); } - - Gdi::Region primaryRegion(DDraw::PrimarySurface::getMonitorRect()); - bltToWindowViaGdiArgs.primaryRegion = &primaryRegion; - EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi, - reinterpret_cast(&bltToWindowViaGdiArgs)); - - bool result = false; - auto primary(DDraw::PrimarySurface::getPrimary()); - Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, DDraw::PrimarySurface::isGdiSurface(primary.get())); - if (DDraw::PrimarySurface::getDesc().ddpfPixelFormat.dwRGBBitCount <= 8) - { - HDC paletteConverterDc = nullptr; - g_paletteConverter->GetDC(g_paletteConverter, &paletteConverterDc); - HDC primaryDc = nullptr; - D3dDdi::Device::setReadOnlyGdiLock(true); - primary->GetDC(primary, &primaryDc); - D3dDdi::Device::setReadOnlyGdiLock(false); - - if (paletteConverterDc && primaryDc) - { - result = TRUE == CALL_ORIG_FUNC(BitBlt)(paletteConverterDc, - 0, 0, g_surfaceDesc.dwWidth, g_surfaceDesc.dwHeight, primaryDc, 0, 0, SRCCOPY); - } - - primary->ReleaseDC(primary, primaryDc); - g_paletteConverter->ReleaseDC(g_paletteConverter, paletteConverterDc); - - if (result) - { - result = SUCCEEDED(bltToPrimaryChain(*g_paletteConverter)); - } - } - else - { - result = SUCCEEDED(bltToPrimaryChain(*primary)); - } - - if (result && g_isFullScreen && primary == DDraw::PrimarySurface::getGdiSurface()) - { - bltVisibleLayeredWindowsToBackBuffer(); - } - - Compat::LogLeave("RealPrimarySurface::compatBlt") << result; - return result; } template @@ -263,124 +217,278 @@ namespace return result; } - template - HRESULT init(CompatRef dd, CompatPtr surface) + CompatPtr getBackBuffer() { - DDSURFACEDESC2 desc = {}; - desc.dwSize = sizeof(desc); - surface->GetSurfaceDesc(surface, &desc); - - const bool isFlippable = 0 != (desc.ddsCaps.dwCaps & DDSCAPS_FLIP); + DDSCAPS2 caps = {}; + caps.dwCaps = DDSCAPS_BACKBUFFER; CompatPtr backBuffer; - if (isFlippable) + if (g_frontBuffer) { - DDSCAPS2 backBufferCaps = {}; - backBufferCaps.dwCaps = DDSCAPS_BACKBUFFER; - surface->GetAttachedSurface(surface, &backBufferCaps, &backBuffer.getRef()); + g_frontBuffer->GetAttachedSurface(g_frontBuffer, &caps, &backBuffer.getRef()); } - else - { - CALL_ORIG_PROC(DirectDrawCreateClipper, 0, &g_clipper.getRef(), nullptr); - surface->SetClipper(surface, g_clipper); - } - - g_qpcLastFlip = Time::queryPerformanceCounter() - g_qpcFlipModeTimeout; - g_qpcNextUpdate = Time::queryPerformanceCounter(); - - typename DDraw::Types::TSurfaceDesc dm = {}; - dm.dwSize = sizeof(dm); - dd->GetDisplayMode(&dd, &dm); - if (dm.dwRefreshRate <= 1 || dm.dwRefreshRate >= 1000) - { - dm.dwRefreshRate = 60; - } - g_qpcUpdateInterval = Time::g_qpcFrequency / dm.dwRefreshRate; - - surface->SetPrivateData(surface, IID_IReleaseNotifier, - &g_releaseNotifier, sizeof(&g_releaseNotifier), DDSPD_IUNKNOWNPOINTER); - - g_frontBuffer = surface.detach(); - g_backBuffer = backBuffer; - g_surfaceDesc = desc; - g_isFullScreen = isFlippable; - - return DD_OK; + return backBuffer; } - bool isUpdateScheduled() + CompatPtr getLastSurface() { - return WAIT_OBJECT_0 == WaitForSingleObject(g_updateEvent, 0); + DDSCAPS2 caps = {}; + caps.dwCaps = DDSCAPS_FLIP;; + CompatPtr backBuffer(getBackBuffer()); + CompatPtr lastSurface; + if (backBuffer) + { + backBuffer->GetAttachedSurface(backBuffer, &caps, &lastSurface.getRef()); + } + return lastSurface; } - int msUntilNextUpdate() + UINT getFlipIntervalFromFlags(DWORD flags) { - DDraw::ScopedThreadLock lock; - const auto qpcNow = Time::queryPerformanceCounter(); - const int result = max(0, Time::qpcToMs(g_qpcNextUpdate - qpcNow)); - if (0 == result && g_isFullScreen && qpcNow - g_qpcLastFlip >= g_qpcFlipModeTimeout) + if (flags & DDFLIP_NOVSYNC) { - return D3dDdi::KernelModeThunks::isPresentReady() ? 0 : 2; + return 0; } - return result; + + if (flags & (DDFLIP_INTERVAL2 | DDFLIP_INTERVAL3 | DDFLIP_INTERVAL4)) + { + UINT flipInterval = (flags & (DDFLIP_INTERVAL2 | DDFLIP_INTERVAL3 | DDFLIP_INTERVAL4)) >> 24; + if (flipInterval < 2 || flipInterval > 4) + { + flipInterval = 1; + } + return flipInterval; + } + + return 1; + } + + UINT getFlipInterval(DWORD flags) + { + if (0 == g_flipIntervalDriverOverride) + { + return 0; + } + + UINT flipInterval = getFlipIntervalFromFlags(flags); + if (UINT_MAX != g_flipIntervalDriverOverride) + { + return max(flipInterval, g_flipIntervalDriverOverride); + } + return flipInterval; + } + + bool isFlipPending() + { + if (g_isFlipPending) + { + g_isFlipPending = static_cast(D3dDdi::KernelModeThunks::getLastDisplayedFrameCount() - + g_lastFlipFrameCount) < 0; + } + return g_isFlipPending; + } + + bool isPresentPending() + { + if (g_isPresentPending) + { + g_isPresentPending = static_cast(D3dDdi::KernelModeThunks::getLastDisplayedFrameCount() - + D3dDdi::KernelModeThunks::getLastSubmittedFrameCount()) < 0; + } + return g_isPresentPending; } void onRelease() { Compat::LogEnter("RealPrimarySurface::onRelease"); - ResetEvent(g_updateEvent); g_frontBuffer = nullptr; - g_backBuffer = nullptr; g_clipper.release(); g_isFullScreen = false; + g_isFlipPending = false; + g_isPresentPending = false; + g_waitingForPrimaryUnlock = false; g_paletteConverter.release(); - g_qpcUpdateInterval = Time::g_qpcFrequency / 60; - - ZeroMemory(&g_surfaceDesc, sizeof(g_surfaceDesc)); + g_surfaceDesc = {}; Compat::LogLeave("RealPrimarySurface::onRelease"); } - void updateNow() + void onRestore() { - ResetEvent(g_updateEvent); + DDSURFACEDESC2 desc = {}; + desc.dwSize = sizeof(desc); + g_frontBuffer->GetSurfaceDesc(g_frontBuffer, &desc); + + g_clipper.release(); + + const bool isFlippable = 0 != (desc.ddsCaps.dwCaps & DDSCAPS_FLIP); + if (!isFlippable) + { + CALL_ORIG_PROC(DirectDrawCreateClipper, 0, &g_clipper.getRef(), nullptr); + g_frontBuffer->SetClipper(g_frontBuffer, g_clipper); + } + + g_surfaceDesc = desc; + g_isFullScreen = isFlippable; + g_flipIntervalDriverOverride = UINT_MAX; + g_isFlipPending = false; + g_isPresentPending = false; + g_isUpdatePending = false; + g_qpcLastUpdate = Time::queryPerformanceCounter() - Time::msToQpc(Config::delayedFlipModeTimeout); + + if (isFlippable) + { + D3dDdi::KernelModeThunks::setFlipIntervalOverride(UINT_MAX); + g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT | DDFLIP_NOVSYNC); + g_flipIntervalDriverOverride = D3dDdi::KernelModeThunks::getLastFlipInterval(); + g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT); + if (0 == g_flipIntervalDriverOverride) + { + g_flipIntervalDriverOverride = D3dDdi::KernelModeThunks::getLastFlipInterval(); + if (1 == g_flipIntervalDriverOverride) + { + g_flipIntervalDriverOverride = UINT_MAX; + } + } + g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT); + D3dDdi::KernelModeThunks::setFlipIntervalOverride(0); + g_lastFlipFrameCount = D3dDdi::KernelModeThunks::getLastSubmittedFrameCount(); + } + + D3dDdi::KernelModeThunks::waitForVerticalBlank(); + } + + void presentToPrimaryChain(CompatWeakPtr src) + { + Compat::LogEnter("RealPrimarySurface::presentToPrimaryChain", src.get()); Gdi::VirtualScreen::update(); - if (compatBlt() && g_isFullScreen) + + BltToWindowViaGdiArgs bltToWindowViaGdiArgs; + if (!g_frontBuffer || !src || DDraw::RealPrimarySurface::isLost()) { - D3dDdi::KernelModeThunks::overrideFlipInterval( - Time::queryPerformanceCounter() - g_qpcLastFlip >= g_qpcFlipModeTimeout - ? D3DDDI_FLIPINTERVAL_ONE - : D3DDDI_FLIPINTERVAL_IMMEDIATE); - g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT); - D3dDdi::KernelModeThunks::overrideFlipInterval(D3DDDI_FLIPINTERVAL_NOOVERRIDE); + EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi, + reinterpret_cast(&bltToWindowViaGdiArgs)); + Compat::LogLeave("RealPrimarySurface::presentToPrimaryChain", src.get()) << false; + return; + } + + Gdi::Region primaryRegion(D3dDdi::KernelModeThunks::getMonitorRect()); + bltToWindowViaGdiArgs.primaryRegion = &primaryRegion; + EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi, + reinterpret_cast(&bltToWindowViaGdiArgs)); + + Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, DDraw::PrimarySurface::isGdiSurface(src.get())); + if (DDraw::PrimarySurface::getDesc().ddpfPixelFormat.dwRGBBitCount <= 8) + { + HDC paletteConverterDc = nullptr; + g_paletteConverter->GetDC(g_paletteConverter, &paletteConverterDc); + HDC srcDc = nullptr; + D3dDdi::Device::setReadOnlyGdiLock(true); + src->GetDC(src, &srcDc); + D3dDdi::Device::setReadOnlyGdiLock(false); + + if (paletteConverterDc && srcDc) + { + CALL_ORIG_FUNC(BitBlt)(paletteConverterDc, + 0, 0, g_surfaceDesc.dwWidth, g_surfaceDesc.dwHeight, srcDc, 0, 0, SRCCOPY); + } + + src->ReleaseDC(src, srcDc); + g_paletteConverter->ReleaseDC(g_paletteConverter, paletteConverterDc); + + bltToPrimaryChain(*g_paletteConverter); + } + else + { + bltToPrimaryChain(*src); + } + + if (g_isFullScreen && src == DDraw::PrimarySurface::getGdiSurface()) + { + bltVisibleLayeredWindowsToBackBuffer(); + } + + Compat::LogLeave("RealPrimarySurface::presentToPrimaryChain", src.get()); + } + + void updateNow(CompatWeakPtr src, UINT flipInterval) + { + DDraw::ScopedThreadLock lock; + if (flipInterval <= 1 && isPresentPending()) + { + g_isUpdatePending = true; + return; + } + + presentToPrimaryChain(src); + g_isUpdatePending = false; + + if (!g_isFullScreen) + { + g_isPresentPending = true; + return; + } + + if (flipInterval > 1 && isPresentPending()) + { + --flipInterval; + } + + D3dDdi::KernelModeThunks::setFlipIntervalOverride(flipInterval); + g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT); + + // Workaround for Windows 8 multimon display glitches when presenting from GDI shared primary surface + D3dDdi::KernelModeThunks::setFlipIntervalOverride(UINT_MAX); + g_frontBuffer->Flip(g_frontBuffer, getLastSurface(), DDFLIP_WAIT); + D3dDdi::KernelModeThunks::setFlipIntervalOverride(0); + + g_isPresentPending = 0 != flipInterval; + } + + void updateNowIfNotBusy() + { + auto primary(DDraw::PrimarySurface::getPrimary()); + RECT emptyRect = {}; + HRESULT result = primary ? primary->BltFast(primary, 0, 0, primary, &emptyRect, DDBLTFAST_WAIT) : DD_OK; + g_waitingForPrimaryUnlock = DDERR_SURFACEBUSY == result || DDERR_LOCKEDSURFACES == result; + + if (!g_waitingForPrimaryUnlock && DDERR_SURFACELOST != result) + { + const auto msSinceLastUpdate = Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcLastUpdate); + updateNow(primary, msSinceLastUpdate > Config::delayedFlipModeTimeout ? 0 : 1); } } DWORD WINAPI updateThreadProc(LPVOID /*lpParameter*/) { - while (true) + const int msPresentDelayAfterVBlank = 1; + bool waitForVBlank = true; + + while (!g_stopUpdateThread) { - WaitForSingleObject(g_updateEvent, INFINITE); - - if (g_stopUpdateThread) + if (waitForVBlank) { - return 0; + D3dDdi::KernelModeThunks::waitForVerticalBlank(); + if (!g_isFullScreen) + { + g_isPresentPending = false; + } } - const int waitTime = msUntilNextUpdate(); - if (waitTime > 0) - { - Sleep(waitTime); - continue; - } + Sleep(msPresentDelayAfterVBlank); DDraw::ScopedThreadLock lock; - if (isUpdateScheduled() && msUntilNextUpdate() <= 0) + waitForVBlank = Time::qpcToMs(Time::queryPerformanceCounter() - + D3dDdi::KernelModeThunks::getQpcLastVerticalBlank()) >= msPresentDelayAfterVBlank; + + if (waitForVBlank && g_isUpdatePending && 0 == g_disableUpdateCount && !isPresentPending()) { - updateNow(); + updateNowIfNotBusy(); } } + + return 0; } } @@ -389,6 +497,7 @@ namespace DDraw template HRESULT RealPrimarySurface::create(CompatRef dd) { + DDraw::ScopedThreadLock lock; HRESULT result = createPaletteConverter(dd); if (FAILED(result)) { @@ -400,7 +509,7 @@ namespace DDraw desc.dwSize = sizeof(desc); desc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP; - desc.dwBackBufferCount = 1; + desc.dwBackBufferCount = 2; CompatPtr::TCreatedSurface> surface; result = dd->CreateSurface(&dd, &desc, &surface.getRef(), nullptr); @@ -420,7 +529,12 @@ namespace DDraw return result; } - return ::init(dd, surface); + g_frontBuffer = CompatPtr::from(surface.get()).detach(); + g_frontBuffer->SetPrivateData(g_frontBuffer, IID_IReleaseNotifier, + &g_releaseNotifier, sizeof(&g_releaseNotifier), DDSPD_IUNKNOWNPOINTER); + onRestore(); + + return DD_OK; } template HRESULT RealPrimarySurface::create(CompatRef); @@ -430,41 +544,73 @@ namespace DDraw void RealPrimarySurface::disableUpdates() { - if (0 == g_disableUpdateCount++ && isUpdateScheduled()) - { - ResetEvent(g_updateEvent); - g_isUpdateSuspended = true; - } + DDraw::ScopedThreadLock lock; + --g_disableUpdateCount; } void RealPrimarySurface::enableUpdates() { - if (0 == --g_disableUpdateCount && g_isUpdateSuspended) - { - SetEvent(g_updateEvent); - g_isUpdateSuspended = false; - } + DDraw::ScopedThreadLock lock; + ++g_disableUpdateCount; } - HRESULT RealPrimarySurface::flip(DWORD flags) + HRESULT RealPrimarySurface::flip(CompatPtr surfaceTargetOverride, DWORD flags) { - if (!g_isFullScreen) + DDraw::ScopedThreadLock lock; + + auto primary(PrimarySurface::getPrimary()); + const bool isFlipEmulated = 0 != (PrimarySurface::getOrigCaps() & DDSCAPS_SYSTEMMEMORY); + if (isFlipEmulated && !surfaceTargetOverride) { - return DDERR_NOTFLIPPABLE; + surfaceTargetOverride = PrimarySurface::getBackBuffer(); } - ResetEvent(g_updateEvent); - g_isUpdateSuspended = false; + HRESULT result = primary->Flip(primary, surfaceTargetOverride, DDFLIP_WAIT); + if (FAILED(result)) + { + return result; + } - g_qpcLastFlip = Time::queryPerformanceCounter(); - compatBlt(); - HRESULT result = g_frontBuffer->Flip(g_frontBuffer, nullptr, flags); - g_qpcNextUpdate = Time::queryPerformanceCounter(); - return result; + DWORD flipInterval = getFlipInterval(flags); + const auto msSinceLastUpdate = Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcLastUpdate); + const bool isFlipDelayed = msSinceLastUpdate >= 0 && msSinceLastUpdate <= Config::delayedFlipModeTimeout; + if (isFlipDelayed) + { + CompatPtr prevPrimarySurface( + surfaceTargetOverride ? surfaceTargetOverride : PrimarySurface::getLastSurface()); + updateNow(prevPrimarySurface, flipInterval); + g_isUpdatePending = true; + } + + if (isFlipEmulated) + { + surfaceTargetOverride->Blt(surfaceTargetOverride, nullptr, primary, nullptr, DDBLT_WAIT, nullptr); + } + + if (!isFlipDelayed) + { + updateNow(primary, flipInterval); + } + + if (0 != flipInterval) + { + g_isFlipPending = true; + g_lastFlipFrameCount = D3dDdi::KernelModeThunks::getLastSubmittedFrameCount(); + } + + g_lastFlipSurface = nullptr; + if (g_isFlipPending && !isFlipEmulated) + { + g_lastFlipSurface = Surface::getSurface( + surfaceTargetOverride ? *surfaceTargetOverride : *PrimarySurface::getLastSurface()); + } + + return DD_OK; } HRESULT RealPrimarySurface::getGammaRamp(DDGAMMARAMP* rampData) { + DDraw::ScopedThreadLock lock; auto gammaControl(CompatPtr::from(g_frontBuffer.get())); if (!gammaControl) { @@ -481,11 +627,6 @@ namespace DDraw void RealPrimarySurface::init() { - g_qpcNextUpdate = Time::queryPerformanceCounter(); - g_qpcUpdateInterval = Time::g_qpcFrequency / 60; - g_qpcFlipModeTimeout = Time::g_qpcFrequency / Config::minExpectedFlipsPerSec; - g_updateEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - g_updateThread = CreateThread(nullptr, 0, &updateThreadProc, nullptr, 0, nullptr); SetThreadPriority(g_updateThread, THREAD_PRIORITY_TIME_CRITICAL); } @@ -497,11 +638,13 @@ namespace DDraw bool RealPrimarySurface::isLost() { + DDraw::ScopedThreadLock lock; return g_frontBuffer && DDERR_SURFACELOST == g_frontBuffer->IsLost(g_frontBuffer); } void RealPrimarySurface::release() { + DDraw::ScopedThreadLock lock; g_frontBuffer.release(); } @@ -513,24 +656,28 @@ namespace DDraw } g_stopUpdateThread = true; - SetEvent(g_updateEvent); if (WAIT_OBJECT_0 != WaitForSingleObject(g_updateThread, 1000)) { TerminateThread(g_updateThread, 0); Compat::Log() << "The update thread was terminated forcefully"; } - ResetEvent(g_updateEvent); - g_stopUpdateThread = false; g_updateThread = nullptr; } HRESULT RealPrimarySurface::restore() { - return g_frontBuffer->Restore(g_frontBuffer); + DDraw::ScopedThreadLock lock; + HRESULT result = g_frontBuffer->Restore(g_frontBuffer); + if (SUCCEEDED(result)) + { + onRestore(); + } + return result; } HRESULT RealPrimarySurface::setGammaRamp(DDGAMMARAMP* rampData) { + DDraw::ScopedThreadLock lock; auto gammaControl(CompatPtr::from(g_frontBuffer.get())); if (!gammaControl) { @@ -542,6 +689,7 @@ namespace DDraw void RealPrimarySurface::setPalette() { + DDraw::ScopedThreadLock lock; if (g_surfaceDesc.ddpfPixelFormat.dwRGBBitCount <= 8) { g_frontBuffer->SetPalette(g_frontBuffer, PrimarySurface::s_palette); @@ -552,41 +700,44 @@ namespace DDraw void RealPrimarySurface::update() { - if (g_isUpdateSuspended) + DDraw::ScopedThreadLock lock; + g_qpcLastUpdate = Time::queryPerformanceCounter(); + g_isUpdatePending = true; + if (g_waitingForPrimaryUnlock) { - return; - } - - if (!isUpdateScheduled()) - { - const auto qpcNow = Time::queryPerformanceCounter(); - const long long missedIntervals = (qpcNow - g_qpcNextUpdate) / g_qpcUpdateInterval; - g_qpcNextUpdate += g_qpcUpdateInterval * (missedIntervals + 1); - if (Time::qpcToMs(g_qpcNextUpdate - qpcNow) < 2) - { - g_qpcNextUpdate += g_qpcUpdateInterval; - } - - if (g_disableUpdateCount <= 0) - { - SetEvent(g_updateEvent); - } - else - { - g_isUpdateSuspended = true; - } - } - else if (msUntilNextUpdate() <= 0) - { - updateNow(); + updateNowIfNotBusy(); } } void RealPrimarySurface::updatePalette() { + DDraw::ScopedThreadLock lock; if (PrimarySurface::s_palette) { update(); } } + + bool RealPrimarySurface::waitForFlip(Surface* surface, bool wait) + { + auto primary(DDraw::PrimarySurface::getPrimary()); + if (!surface || !primary || + surface != g_lastFlipSurface && + surface != Surface::getSurface(*DDraw::PrimarySurface::getPrimary())) + { + return true; + } + + if (!wait) + { + return !isFlipPending(); + } + + while (isFlipPending()) + { + D3dDdi::KernelModeThunks::waitForVerticalBlank(); + } + + return true; + } } diff --git a/DDrawCompat/DDraw/RealPrimarySurface.h b/DDrawCompat/DDraw/RealPrimarySurface.h index 0e67826..5126453 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.h +++ b/DDrawCompat/DDraw/RealPrimarySurface.h @@ -2,11 +2,13 @@ #include -#include "Common/CompatWeakPtr.h" +#include "Common/CompatPtr.h" #include "Common/CompatRef.h" namespace DDraw { + class Surface; + class RealPrimarySurface { public: @@ -15,7 +17,7 @@ namespace DDraw static void disableUpdates(); static void enableUpdates(); - static HRESULT flip(DWORD flags); + static HRESULT flip(CompatPtr surfaceTargetOverride, DWORD flags); static HRESULT getGammaRamp(DDGAMMARAMP* rampData); static CompatWeakPtr getSurface(); static void init(); @@ -28,5 +30,6 @@ namespace DDraw static void setPalette(); static void update(); static void updatePalette(); + static bool waitForFlip(Surface* surface, bool wait = true); }; } diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp b/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp index c796e02..4811ec2 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp @@ -1,5 +1,3 @@ -#include - #include "Common/CompatPtr.h" #include "Common/CompatRef.h" #include "Config/Config.h" @@ -16,24 +14,6 @@ namespace CompatWeakPtr g_primarySurface; HANDLE g_gdiResourceHandle = nullptr; DWORD g_origCaps = 0; - RECT g_monitorRect = {}; - - RECT getDdMonitorRect(CompatRef dd) - { - DDDEVICEIDENTIFIER2 di = {}; - dd->GetDeviceIdentifier(&dd, &di, 0); // Calls D3DKMTOpenAdapterFromHdc, which updates last monitor below - - HMONITOR monitor = D3dDdi::KernelModeThunks::getLastOpenAdapterMonitor(); - if (!monitor) - { - monitor = MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY); - } - - MONITORINFO mi = {}; - mi.cbSize = sizeof(mi); - GetMonitorInfo(monitor, &mi); - return mi.rcMonitor; - } template HANDLE getResourceHandle(TSurface& surface) @@ -56,7 +36,6 @@ namespace DDraw g_gdiResourceHandle = nullptr; g_primarySurface = nullptr; g_origCaps = 0; - g_monitorRect = {}; s_palette = nullptr; s_surfaceBuffers.clear(); ZeroMemory(&s_paletteEntries, sizeof(s_paletteEntries)); @@ -100,19 +79,8 @@ namespace DDraw g_primarySurface = surface7; g_origCaps = origCaps; - g_monitorRect = getDdMonitorRect(*CompatPtr::from(&dd)); - ZeroMemory(&g_primarySurfaceDesc, sizeof(g_primarySurfaceDesc)); - g_primarySurfaceDesc.dwSize = sizeof(g_primarySurfaceDesc); - CompatVtable::s_origVtable.GetSurfaceDesc(surface7, &g_primarySurfaceDesc); - - if (g_primarySurfaceDesc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) - { - resizeBuffers(*surface7); - } - - g_gdiResourceHandle = getResourceHandle(*surface7); - D3dDdi::Device::setGdiResourceHandle(*reinterpret_cast(g_gdiResourceHandle)); + onRestore(); return DD_OK; } @@ -178,9 +146,29 @@ namespace DDraw return nullptr; } - RECT PrimarySurface::getMonitorRect() + CompatPtr PrimarySurface::getBackBuffer() { - return g_monitorRect; + DDSCAPS2 caps = {}; + caps.dwCaps = DDSCAPS_BACKBUFFER; + CompatPtr backBuffer; + g_primarySurface->GetAttachedSurface(g_primarySurface, &caps, &backBuffer.getRef()); + return backBuffer; + } + + CompatPtr PrimarySurface::getLastSurface() + { + DDSCAPS2 caps = {}; + caps.dwCaps = DDSCAPS_FLIP; + auto surface(CompatPtr::from(g_primarySurface.get())); + CompatPtr nextSurface; + + while (SUCCEEDED(surface->GetAttachedSurface(surface, &caps, &nextSurface.getRef())) && + nextSurface != g_primarySurface) + { + surface = nextSurface; + } + + return surface; } CompatWeakPtr PrimarySurface::getPrimary() @@ -207,14 +195,20 @@ namespace DDraw void PrimarySurface::onRestore() { - CompatPtr ddUnk; - g_primarySurface.get()->lpVtbl->GetDDInterface( - g_primarySurface, reinterpret_cast(&ddUnk.getRef())); - CompatPtr dd7(ddUnk); - g_monitorRect = getDdMonitorRect(*dd7); + g_primarySurfaceDesc = {}; + g_primarySurfaceDesc.dwSize = sizeof(g_primarySurfaceDesc); + g_primarySurface->GetSurfaceDesc(g_primarySurface, &g_primarySurfaceDesc); + + if (g_primarySurfaceDesc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) + { + resizeBuffers(); + } + + g_gdiResourceHandle = getResourceHandle(*g_primarySurface); + D3dDdi::Device::setGdiResourceHandle(*reinterpret_cast(g_gdiResourceHandle)); } - void PrimarySurface::resizeBuffers(CompatRef surface) + void PrimarySurface::resizeBuffers() { DDSCAPS2 flipCaps = {}; flipCaps.dwCaps = DDSCAPS_FLIP; @@ -226,7 +220,7 @@ namespace DDraw const DWORD newBufferSize = g_primarySurfaceDesc.lPitch * (g_primarySurfaceDesc.dwHeight + Config::primarySurfaceExtraRows); - auto surfacePtr(CompatPtr::from(&surface)); + auto surfacePtr(CompatPtr::from(g_primarySurface.get())); do { s_surfaceBuffers.push_back(std::vector(newBufferSize)); @@ -236,7 +230,7 @@ namespace DDraw CompatPtr nextSurface; surfacePtr->GetAttachedSurface(surfacePtr, &flipCaps, &nextSurface.getRef()); surfacePtr.swap(nextSurface); - } while (surfacePtr && surfacePtr != &surface); + } while (surfacePtr && surfacePtr != g_primarySurface.get()); } CompatWeakPtr PrimarySurface::s_palette; diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurface.h b/DDrawCompat/DDraw/Surfaces/PrimarySurface.h index b82f1b2..2f59f64 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurface.h +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurface.h @@ -2,6 +2,8 @@ #include +#include + #include "Common/CompatPtr.h" #include "Common/CompatRef.h" #include "DDraw/Surfaces/Surface.h" @@ -19,7 +21,8 @@ namespace DDraw static HRESULT flipToGdiSurface(); static const DDSURFACEDESC2& getDesc(); static CompatPtr getGdiSurface(); - static RECT getMonitorRect(); + static CompatPtr getBackBuffer(); + static CompatPtr getLastSurface(); static CompatWeakPtr getPrimary(); static DWORD getOrigCaps(); static void onRestore(); @@ -35,7 +38,7 @@ namespace DDraw virtual void createImpl() override; - static void resizeBuffers(CompatRef surface); + static void resizeBuffers(); std::unique_ptr m_surface; diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp index 8dbe53b..d813d52 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp @@ -1,4 +1,5 @@ #include "DDraw/DirectDrawPalette.h" +#include "DDraw/DirectDrawSurface.h" #include "DDraw/RealPrimarySurface.h" #include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Surfaces/PrimarySurfaceImpl.h" @@ -65,44 +66,14 @@ namespace DDraw template HRESULT PrimarySurfaceImpl::Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags) { - if (RealPrimarySurface::isLost()) + const bool wait = (dwFlags & DDFLIP_WAIT) || !(dwFlags & DDFLIP_DONOTWAIT) && + CompatVtable::s_origVtablePtr == static_cast(This->lpVtbl); + if (!DDraw::RealPrimarySurface::waitForFlip(Surface::getSurface(*This), wait)) { - return DDERR_SURFACELOST; + return DDERR_WASSTILLDRAWING; } - HRESULT result = m_impl.Flip(This, lpDDSurfaceTargetOverride, dwFlags); - if (FAILED(result)) - { - return result; - } - - const bool isFlipEmulated = 0 != (PrimarySurface::getOrigCaps() & DDSCAPS_SYSTEMMEMORY); - result = RealPrimarySurface::flip(dwFlags); - if (SUCCEEDED(result) && !isFlipEmulated) - { - return DD_OK; - } - - undoFlip(This, lpDDSurfaceTargetOverride); - - if (SUCCEEDED(result) && isFlipEmulated) - { - if (lpDDSurfaceTargetOverride) - { - s_origVtable.BltFast(This, 0, 0, lpDDSurfaceTargetOverride, nullptr, DDBLTFAST_WAIT); - } - else - { - TDdsCaps caps = {}; - caps.dwCaps = DDSCAPS_BACKBUFFER; - CompatPtr backBuffer; - s_origVtable.GetAttachedSurface(This, &caps, &backBuffer.getRef()); - - s_origVtable.BltFast(This, 0, 0, backBuffer, nullptr, DDBLTFAST_WAIT); - } - } - - return result; + return RealPrimarySurface::flip(CompatPtr::from(lpDDSurfaceTargetOverride), dwFlags); } template diff --git a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp index 51873b2..4ea6949 100644 --- a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp @@ -1,6 +1,7 @@ #include #include "Common/CompatRef.h" +#include "DDraw/RealPrimarySurface.h" #include "DDraw/Repository.h" #include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Surfaces/Surface.h" @@ -28,6 +29,20 @@ namespace dst->SetColorKey(&dst, ckFlag, &ck); } } + + template + bool waitForFlip(TSurface* This, DWORD flags, DWORD waitFlag, DWORD doNotWaitFlag) + { + if (!This) + { + return true; + } + + const bool wait = (flags & waitFlag) || !(flags & doNotWaitFlag) && + CompatVtable::s_origVtablePtr == static_cast(This->lpVtbl); + + return DDraw::RealPrimarySurface::waitForFlip(DDraw::Surface::getSurface(*This), wait); + } } namespace DDraw @@ -136,31 +151,16 @@ namespace DDraw } } - template - void SurfaceImpl::undoFlip(TSurface* This, TSurface* targetOverride) - { - if (targetOverride) - { - SurfaceImpl::Flip(This, targetOverride, DDFLIP_WAIT); - } - else - { - TSurfaceDesc desc = {}; - desc.dwSize = sizeof(desc); - s_origVtable.GetSurfaceDesc(This, &desc); - - for (DWORD i = 0; i < desc.dwBackBufferCount; ++i) - { - SurfaceImpl::Flip(This, nullptr, DDFLIP_WAIT); - } - } - } - template HRESULT SurfaceImpl::Blt( TSurface* This, LPRECT lpDestRect, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) { + if (!waitForFlip(This, dwFlags, DDBLT_WAIT, DDBLT_DONOTWAIT)) + { + return DDERR_WASSTILLDRAWING; + } + Gdi::DDrawAccessGuard dstAccessGuard(Gdi::ACCESS_WRITE, PrimarySurface::isGdiSurface(This)); Gdi::DDrawAccessGuard srcAccessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(lpDDSrcSurface)); HRESULT result = s_origVtable.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); @@ -182,6 +182,11 @@ namespace DDraw HRESULT SurfaceImpl::BltFast( TSurface* This, DWORD dwX, DWORD dwY, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) { + if (!waitForFlip(This, dwTrans, DDBLTFAST_WAIT, DDBLTFAST_DONOTWAIT)) + { + return DDERR_WASSTILLDRAWING; + } + Gdi::DDrawAccessGuard dstAccessGuard(Gdi::ACCESS_WRITE, PrimarySurface::isGdiSurface(This)); Gdi::DDrawAccessGuard srcAccessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(lpDDSrcSurface)); HRESULT result = s_origVtable.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); @@ -221,6 +226,21 @@ namespace DDraw return s_origVtable.Flip(This, lpDDSurfaceTargetOverride, dwFlags); } + template + HRESULT SurfaceImpl::GetBltStatus(TSurface* This, DWORD dwFlags) + { + HRESULT result = s_origVtable.GetBltStatus(This, dwFlags); + if (SUCCEEDED(result) && (dwFlags & DDGBS_CANBLT)) + { + const bool wait = false; + if (!RealPrimarySurface::waitForFlip(Surface::getSurface(*This), wait)) + { + return DDERR_WASSTILLDRAWING; + } + } + return result; + } + template HRESULT SurfaceImpl::GetCaps(TSurface* This, TDdsCaps* lpDDSCaps) { @@ -230,8 +250,19 @@ namespace DDraw template HRESULT SurfaceImpl::GetDC(TSurface* This, HDC* lphDC) { - Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_WRITE); - return s_origVtable.GetDC(This, lphDC); + HRESULT result = DD_OK; + + { + Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_WRITE); + result = s_origVtable.GetDC(This, lphDC); + } + + if (SUCCEEDED(result)) + { + RealPrimarySurface::waitForFlip(Surface::getSurface(*This)); + } + + return result; } template @@ -246,6 +277,21 @@ namespace DDraw reinterpret_cast(&dd), m_data->m_ddId, lplpDD); } + template + HRESULT SurfaceImpl::GetFlipStatus(TSurface* This, DWORD dwFlags) + { + HRESULT result = s_origVtable.GetFlipStatus(This, dwFlags); + if (SUCCEEDED(result)) + { + const bool wait = false; + if (!RealPrimarySurface::waitForFlip(Surface::getSurface(*This), wait)) + { + return DDERR_WASSTILLDRAWING; + } + } + return result; + } + template HRESULT SurfaceImpl::GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc) { @@ -263,6 +309,11 @@ namespace DDraw TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) { + if (!waitForFlip(This, dwFlags, DDLOCK_WAIT, DDLOCK_DONOTWAIT)) + { + return DDERR_WASSTILLDRAWING; + } + Gdi::DDrawAccessGuard accessGuard((dwFlags & DDLOCK_READONLY) ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE, PrimarySurface::isGdiSurface(This)); HRESULT result = s_origVtable.Lock(This, lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); diff --git a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h index ef2e0d7..4e0d301 100644 --- a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h +++ b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h @@ -40,8 +40,10 @@ namespace DDraw virtual HRESULT BltFast(TSurface* This, DWORD dwX, DWORD dwY, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans); virtual HRESULT Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags); + virtual HRESULT GetBltStatus(TSurface* This, DWORD dwFlags); virtual HRESULT GetCaps(TSurface* This, TDdsCaps* lpDDSCaps); virtual HRESULT GetDC(TSurface* This, HDC* lphDC); + virtual HRESULT GetFlipStatus(TSurface* This, DWORD dwFlags); virtual HRESULT GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc); virtual HRESULT IsLost(TSurface* This); virtual HRESULT Lock(TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc, @@ -52,8 +54,6 @@ namespace DDraw virtual HRESULT Unlock(TSurface* This, TUnlockParam lpRect); protected: - void undoFlip(TSurface* This, TSurface* targetOverride); - static const Vtable& s_origVtable; private: diff --git a/DDrawCompat/Gdi/AccessGuard.cpp b/DDrawCompat/Gdi/AccessGuard.cpp index 67070f5..8394039 100644 --- a/DDrawCompat/Gdi/AccessGuard.cpp +++ b/DDrawCompat/Gdi/AccessGuard.cpp @@ -2,6 +2,7 @@ #include +#include "D3dDdi/KernelModeThunks.h" #include "DDraw/RealPrimarySurface.h" #include "DDraw/ScopedThreadLock.h" #include "DDraw/Surfaces/PrimarySurface.h" @@ -83,7 +84,7 @@ namespace return false; } - auto gdiSurface(Gdi::VirtualScreen::createSurface(DDraw::PrimarySurface::getMonitorRect())); + auto gdiSurface(Gdi::VirtualScreen::createSurface(D3dDdi::KernelModeThunks::getMonitorRect())); if (!gdiSurface) { return false; @@ -96,8 +97,8 @@ namespace CompatPtr clipper; ddrawSurface->GetClipper(ddrawSurface, &clipper.getRef()); ddrawSurface->SetClipper(ddrawSurface, nullptr); - result = SUCCEEDED(ddrawSurface->BltFast( - ddrawSurface, 0, 0, gdiSurface, nullptr, DDBLTFAST_WAIT)); + result = SUCCEEDED(ddrawSurface->Blt( + ddrawSurface, nullptr, gdiSurface, nullptr, DDBLT_WAIT, nullptr)); ddrawSurface->SetClipper(ddrawSurface, clipper); } else