From 799e9a95ae72260bbc878b4c6ba502cf16dff2b3 Mon Sep 17 00:00:00 2001 From: narzoul Date: Mon, 13 Jul 2020 23:44:05 +0200 Subject: [PATCH] Removed dependency on D3DKMTPresent Fixes presentation issues when Hardware Accelerated GPU Scheduling is enabled (issue #73). --- DDrawCompat/Common/ScopedSrwLock.h | 48 ++++ DDrawCompat/D3dDdi/Hooks.cpp | 1 + DDrawCompat/D3dDdi/KernelModeThunks.cpp | 239 ++++++------------ DDrawCompat/D3dDdi/KernelModeThunks.h | 10 +- DDrawCompat/DDraw/DirectDraw.cpp | 2 +- DDrawCompat/DDraw/RealPrimarySurface.cpp | 234 +++++++---------- DDrawCompat/DDraw/RealPrimarySurface.h | 6 +- .../DDraw/Surfaces/PrimarySurfaceImpl.cpp | 16 +- DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp | 4 + DDrawCompat/DDraw/Surfaces/SurfaceImpl.h | 4 +- DDrawCompat/DDrawCompat.vcxproj | 1 + DDrawCompat/DDrawCompat.vcxproj.filters | 3 + DDrawCompat/Gdi/AccessGuard.cpp | 2 +- DDrawCompat/Gdi/VirtualScreen.cpp | 2 +- DDrawCompat/Gdi/Window.cpp | 4 +- 15 files changed, 244 insertions(+), 332 deletions(-) create mode 100644 DDrawCompat/Common/ScopedSrwLock.h diff --git a/DDrawCompat/Common/ScopedSrwLock.h b/DDrawCompat/Common/ScopedSrwLock.h new file mode 100644 index 0000000..59a3e50 --- /dev/null +++ b/DDrawCompat/Common/ScopedSrwLock.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +namespace Compat +{ + class SrwLock : public SRWLOCK + { + public: + SrwLock() : SRWLOCK(SRWLOCK_INIT) + { + } + }; + + class ScopedSrwLockExclusive + { + public: + ScopedSrwLockExclusive(SRWLOCK& lock) : m_lock(lock) + { + AcquireSRWLockExclusive(&m_lock); + } + + ~ScopedSrwLockExclusive() + { + ReleaseSRWLockExclusive(&m_lock); + } + + private: + SRWLOCK& m_lock; + }; + + class ScopedSrwLockShared + { + public: + ScopedSrwLockShared(SRWLOCK& lock) : m_lock(lock) + { + AcquireSRWLockShared(&m_lock); + } + + ~ScopedSrwLockShared() + { + ReleaseSRWLockShared(&m_lock); + } + + private: + SRWLOCK& m_lock; + }; +} diff --git a/DDrawCompat/D3dDdi/Hooks.cpp b/DDrawCompat/D3dDdi/Hooks.cpp index b8c81c3..d095140 100644 --- a/DDrawCompat/D3dDdi/Hooks.cpp +++ b/DDrawCompat/D3dDdi/Hooks.cpp @@ -100,5 +100,6 @@ namespace D3dDdi void uninstallHooks() { unhookOpenAdapter(); + KernelModeThunks::stopVsyncThread(); } } diff --git a/DDrawCompat/D3dDdi/KernelModeThunks.cpp b/DDrawCompat/D3dDdi/KernelModeThunks.cpp index 4c6d4f6..cb44cd6 100644 --- a/DDrawCompat/D3dDdi/KernelModeThunks.cpp +++ b/DDrawCompat/D3dDdi/KernelModeThunks.cpp @@ -1,15 +1,8 @@ -#include -#include #include -#include -#include -#include -#include <../km/d3dkmthk.h> - #include #include -#include +#include #include #include #include @@ -32,33 +25,24 @@ namespace RECT monitorRect; }; - struct ContextInfo - { - D3DKMT_HANDLE device; - - ContextInfo() : device(0) {} - }; - - std::map g_contexts; D3DDDIFORMAT g_dcFormatOverride = D3DDDIFMT_UNKNOWN; bool g_dcPaletteOverride = false; AdapterInfo g_gdiAdapterInfo = {}; AdapterInfo g_lastOpenAdapterInfo = {}; + Compat::SrwLock g_lastOpenAdapterInfoSrwLock; std::string g_lastDDrawCreateDcDevice; - UINT g_lastFlipInterval = 0; - UINT g_flipIntervalOverride = 0; - D3DKMT_HANDLE g_lastPresentContext = 0; - UINT g_presentCount = 0; - std::atomic g_qpcLastVerticalBlank = 0; - Compat::CriticalSection g_vblankCs; - decltype(D3DKMTCreateContextVirtual)* g_origD3dKmtCreateContextVirtual = nullptr; + HANDLE g_vsyncThread = nullptr; + bool g_stopVsyncThread = false; + UINT g_vsyncCounter = 0; + CONDITION_VARIABLE g_vsyncCounterCv = CONDITION_VARIABLE_INIT; + Compat::SrwLock g_vsyncCounterSrwLock; - DWORD WINAPI waitForVsyncThreadProc(LPVOID lpParameter); + void waitForVerticalBlank(); NTSTATUS APIENTRY closeAdapter(const D3DKMT_CLOSEADAPTER* pData) { - Compat::ScopedCriticalSection lock(g_vblankCs); + Compat::ScopedSrwLockExclusive lock(g_lastOpenAdapterInfoSrwLock); if (pData && pData->hAdapter == g_lastOpenAdapterInfo.adapter) { g_lastOpenAdapterInfo = {}; @@ -66,30 +50,6 @@ namespace return D3DKMTCloseAdapter(pData); } - NTSTATUS APIENTRY createContext(D3DKMT_CREATECONTEXT* pData) - { - LOG_FUNC("D3DKMTCreateContext", pData); - NTSTATUS result = D3DKMTCreateContext(pData); - if (SUCCEEDED(result)) - { - g_contexts[pData->hContext].device = pData->hDevice; - } - return LOG_RESULT(result); - } - - NTSTATUS APIENTRY createContextVirtual(D3DKMT_CREATECONTEXTVIRTUAL* pData) - { - LOG_FUNC("D3DKMTCreateContextVirtual", pData); - static auto d3dKmtCreateContextVirtual = reinterpret_cast( - GetProcAddress(GetModuleHandle("gdi32"), "D3DKMTCreateContextVirtual")); - NTSTATUS result = d3dKmtCreateContextVirtual(pData); - if (SUCCEEDED(result)) - { - g_contexts[pData->hContext].device = pData->hDevice; - } - return LOG_RESULT(result); - } - NTSTATUS APIENTRY createDcFromMemory(D3DKMT_CREATEDCFROMMEMORY* pData) { LOG_FUNC("D3DKMTCreateDCFromMemory", pData); @@ -154,21 +114,6 @@ namespace return LOG_RESULT(CreateDCA(pwszDriver, pwszDevice, pszPort, pdm)); } - NTSTATUS APIENTRY destroyContext(const D3DKMT_DESTROYCONTEXT* pData) - { - LOG_FUNC("D3DKMTDestroyContext", pData); - NTSTATUS result = D3DKMTDestroyContext(pData); - if (SUCCEEDED(result)) - { - g_contexts.erase(pData->hContext); - if (g_lastPresentContext == pData->hContext) - { - g_lastPresentContext = 0; - } - } - return LOG_RESULT(result); - } - BOOL CALLBACK findDDrawMonitorRect(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData) { MONITORINFOEX mi = {}; @@ -208,40 +153,12 @@ namespace NTSTATUS result = D3DKMTOpenAdapterFromHdc(pData); if (SUCCEEDED(result)) { - Compat::ScopedCriticalSection lock(g_vblankCs); + Compat::ScopedSrwLockExclusive lock(g_lastOpenAdapterInfoSrwLock); g_lastOpenAdapterInfo = getAdapterInfo(*pData); } return LOG_RESULT(result); } - NTSTATUS APIENTRY present(D3DKMT_PRESENT* pData) - { - LOG_FUNC("D3DKMTPresent", pData); - - if (pData->Flags.Flip) - { - g_lastFlipInterval = pData->FlipInterval; - g_lastPresentContext = pData->hContext; - - if (UINT_MAX == g_flipIntervalOverride) - { - return LOG_RESULT(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); - } - - return LOG_RESULT(D3DKMTPresent(pData)); - } - NTSTATUS APIENTRY queryAdapterInfo(const D3DKMT_QUERYADAPTERINFO* pData) { LOG_FUNC("D3DKMTQueryAdapterInfo", pData); @@ -283,12 +200,13 @@ namespace NTSTATUS APIENTRY setGammaRamp(const D3DKMT_SETGAMMARAMP* pData) { LOG_FUNC("D3DKMTSetGammaRamp", pData); - HANDLE vsyncThread = CreateThread(nullptr, 0, &waitForVsyncThreadProc, nullptr, 0, nullptr); - SetThreadPriority(vsyncThread, THREAD_PRIORITY_TIME_CRITICAL); + UINT vsyncCounter = D3dDdi::KernelModeThunks::getVsyncCounter(); DDraw::RealPrimarySurface::flush(); HRESULT result = D3DKMTSetGammaRamp(pData); - WaitForSingleObject(vsyncThread, INFINITE); - CloseHandle(vsyncThread); + if (SUCCEEDED(result)) + { + D3dDdi::KernelModeThunks::waitForVsyncCounter(vsyncCounter + 1); + } return LOG_RESULT(result); } @@ -322,49 +240,50 @@ namespace } } - DWORD WINAPI waitForVsyncThreadProc(LPVOID /*lpParameter*/) + DWORD WINAPI vsyncThreadProc(LPVOID /*lpParameter*/) { - D3dDdi::KernelModeThunks::waitForVerticalBlank(); + while (!g_stopVsyncThread) + { + waitForVerticalBlank(); + + { + Compat::ScopedSrwLockExclusive lock(g_vsyncCounterSrwLock); + ++g_vsyncCounter; + } + + WakeAllConditionVariable(&g_vsyncCounterCv); + } return 0; } + + void waitForVerticalBlank() + { + D3DKMT_WAITFORVERTICALBLANKEVENT data = {}; + + { + Compat::ScopedSrwLockShared lock(g_lastOpenAdapterInfoSrwLock); + data.hAdapter = g_lastOpenAdapterInfo.adapter; + data.VidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId; + } + + if (!data.hAdapter) + { + updateGdiAdapterInfo(); + data.hAdapter = g_gdiAdapterInfo.adapter; + data.VidPnSourceId = g_gdiAdapterInfo.vidPnSourceId; + } + + if (!data.hAdapter || FAILED(D3DKMTWaitForVerticalBlankEvent(&data))) + { + Sleep(16); + } + } } namespace D3dDdi { namespace KernelModeThunks { - UINT getLastFlipInterval() - { - return g_lastFlipInterval; - } - - UINT getLastDisplayedFrameCount() - { - auto contextIter = g_contexts.find(g_lastPresentContext); - if (contextIter == g_contexts.end()) - { - return g_presentCount; - } - - 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()); @@ -389,28 +308,23 @@ namespace D3dDdi return g_lastOpenAdapterInfo.monitorRect; } - long long getQpcLastVerticalBlank() + UINT getVsyncCounter() { - return g_qpcLastVerticalBlank; + Compat::ScopedSrwLockShared lock(g_vsyncCounterSrwLock); + return g_vsyncCounter; } void installHooks(HMODULE origDDrawModule) { Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "CreateDCA", ddrawCreateDcA); Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTCloseAdapter", closeAdapter); - Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTCreateContext", createContext); - Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTCreateContextVirtual", createContextVirtual); Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTCreateDCFromMemory", createDcFromMemory); - Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTDestroyContext", destroyContext); Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTOpenAdapterFromHdc", openAdapterFromHdc); Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTQueryAdapterInfo", queryAdapterInfo); - Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTPresent", present); Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTSetGammaRamp", setGammaRamp); - } - void setFlipIntervalOverride(UINT flipInterval) - { - g_flipIntervalOverride = flipInterval; + g_vsyncThread = CreateThread(nullptr, 0, &vsyncThreadProc, nullptr, 0, nullptr); + SetThreadPriority(g_vsyncThread, THREAD_PRIORITY_TIME_CRITICAL); } void setDcFormatOverride(UINT format) @@ -423,30 +337,33 @@ namespace D3dDdi g_dcPaletteOverride = enable; } - void waitForVerticalBlank() + void stopVsyncThread() { - D3DKMT_WAITFORVERTICALBLANKEVENT data = {}; + g_stopVsyncThread = true; + if (WAIT_OBJECT_0 != WaitForSingleObject(g_vsyncThread, 100)) + { + TerminateThread(g_vsyncThread, 0); + Compat::Log() << "The vsync thread was terminated forcefully"; + } + g_vsyncThread = nullptr; + } + void waitForVsync() + { + waitForVsyncCounter(getVsyncCounter() + 1); + } + + bool waitForVsyncCounter(UINT counter) + { + bool waited = false; + Compat::ScopedSrwLockShared lock(g_vsyncCounterSrwLock); + while (static_cast(g_vsyncCounter - counter) < 0) { - Compat::ScopedCriticalSection lock(g_vblankCs); - if (g_lastOpenAdapterInfo.adapter) - { - 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(); + SleepConditionVariableSRW(&g_vsyncCounterCv, &g_vsyncCounterSrwLock, INFINITE, + CONDITION_VARIABLE_LOCKMODE_SHARED); + waited = true; } + return waited; } } } diff --git a/DDrawCompat/D3dDdi/KernelModeThunks.h b/DDrawCompat/D3dDdi/KernelModeThunks.h index 6db7bc9..88055a2 100644 --- a/DDrawCompat/D3dDdi/KernelModeThunks.h +++ b/DDrawCompat/D3dDdi/KernelModeThunks.h @@ -6,15 +6,13 @@ namespace D3dDdi { namespace KernelModeThunks { - UINT getLastFlipInterval(); - UINT getLastDisplayedFrameCount(); - UINT getLastSubmittedFrameCount(); RECT getMonitorRect(); - long long getQpcLastVerticalBlank(); + UINT getVsyncCounter(); void installHooks(HMODULE origDDrawModule); - void setFlipIntervalOverride(UINT flipInterval); void setDcFormatOverride(UINT format); void setDcPaletteOverride(bool enable); - void waitForVerticalBlank(); + void stopVsyncThread(); + void waitForVsync(); + bool waitForVsyncCounter(UINT counter); } } diff --git a/DDrawCompat/DDraw/DirectDraw.cpp b/DDrawCompat/DDraw/DirectDraw.cpp index 056726b..8dc232f 100644 --- a/DDrawCompat/DDraw/DirectDraw.cpp +++ b/DDrawCompat/DDraw/DirectDraw.cpp @@ -141,7 +141,7 @@ namespace DDraw DWORD scanLine = 0; if (DDERR_VERTICALBLANKINPROGRESS != s_origVtable.GetScanLine(This, &scanLine)) { - D3dDdi::KernelModeThunks::waitForVerticalBlank(); + D3dDdi::KernelModeThunks::waitForVsync(); } if (DDWAITVB_BLOCKEND == dwFlags) diff --git a/DDrawCompat/DDraw/RealPrimarySurface.cpp b/DDrawCompat/DDraw/RealPrimarySurface.cpp index f773e5c..665d133 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.cpp +++ b/DDrawCompat/DDraw/RealPrimarySurface.cpp @@ -1,30 +1,30 @@ +#include #include #include -#include "Common/CompatPtr.h" -#include "Common/Hook.h" -#include "Common/Time.h" -#include "Config/Config.h" -#include "D3dDdi/Device.h" -#include "D3dDdi/KernelModeThunks.h" -#include "DDraw/DirectDraw.h" -#include "DDraw/DirectDrawSurface.h" -#include "DDraw/IReleaseNotifier.h" -#include "DDraw/RealPrimarySurface.h" -#include "DDraw/ScopedThreadLock.h" -#include "DDraw/Surfaces/PrimarySurface.h" -#include "DDraw/Types.h" -#include "Gdi/AccessGuard.h" -#include "Gdi/Caret.h" -#include "Gdi/Gdi.h" -#include "Gdi/VirtualScreen.h" -#include "Gdi/Window.h" -#include "Win32/DisplayMode.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace { void onRelease(); - DWORD WINAPI updateThreadProc(LPVOID lpParameter); CompatWeakPtr g_frontBuffer; CompatWeakPtr g_paletteConverter; @@ -34,16 +34,15 @@ namespace bool g_stopUpdateThread = false; HANDLE g_updateThread = nullptr; - bool g_isGdiUpdatePending = false; - 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; + + bool g_isUpdatePending = false; + bool g_waitingForPrimaryUnlock = false; + std::atomic g_qpcLastUpdate = 0; + long long g_qpcFlipEnd = 0; + UINT g_flipEndVsyncCount = 0; + UINT g_presentEndVsyncCount = 0; CompatPtr getBackBuffer(); CompatPtr getLastSurface(); @@ -246,13 +245,13 @@ namespace return lastSurface; } - UINT getFlipIntervalFromFlags(DWORD flags) + UINT getFlipInterval(DWORD flags) { if (flags & DDFLIP_NOVSYNC) { return 0; } - + if (flags & (DDFLIP_INTERVAL2 | DDFLIP_INTERVAL3 | DDFLIP_INTERVAL4)) { UINT flipInterval = (flags & (DDFLIP_INTERVAL2 | DDFLIP_INTERVAL3 | DDFLIP_INTERVAL4)) >> 24; @@ -266,39 +265,14 @@ namespace 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; + return static_cast(D3dDdi::KernelModeThunks::getVsyncCounter() - g_flipEndVsyncCount) < 0; } bool isPresentPending() { - if (g_isPresentPending) - { - g_isPresentPending = static_cast(D3dDdi::KernelModeThunks::getLastDisplayedFrameCount() - - D3dDdi::KernelModeThunks::getLastSubmittedFrameCount()) < 0; - } - return g_isPresentPending; + return static_cast(D3dDdi::KernelModeThunks::getVsyncCounter() - g_presentEndVsyncCount) < 0; } void onRelease() @@ -308,8 +282,6 @@ namespace g_frontBuffer = nullptr; g_clipper.release(); g_isFullScreen = false; - g_isFlipPending = false; - g_isPresentPending = false; g_waitingForPrimaryUnlock = false; g_paletteConverter.release(); g_surfaceDesc = {}; @@ -332,32 +304,16 @@ namespace 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(); + g_frontBuffer->Flip(g_frontBuffer, getLastSurface(), DDFLIP_WAIT); + g_flipEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + 1; + g_presentEndVsyncCount = g_flipEndVsyncCount; + D3dDdi::KernelModeThunks::waitForVsyncCounter(g_flipEndVsyncCount); } - - D3dDdi::KernelModeThunks::waitForVerticalBlank(); } void presentToPrimaryChain(CompatWeakPtr src) @@ -410,47 +366,19 @@ namespace void updateNow(CompatWeakPtr src, UINT flipInterval) { - DDraw::ScopedThreadLock lock; - if (flipInterval <= 1 && isPresentPending()) - { - g_isUpdatePending = true; - return; - } - presentToPrimaryChain(src); g_isUpdatePending = false; + g_waitingForPrimaryUnlock = false; - if (!g_isFullScreen) + if (g_isFullScreen) { - g_isPresentPending = true; - return; + g_frontBuffer->Flip(g_frontBuffer, getBackBuffer(), DDFLIP_WAIT); } - - 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; + g_presentEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + max(flipInterval, 1); } void updateNowIfNotBusy() { - if (g_isGdiUpdatePending) - { - g_qpcLastUpdate = Time::queryPerformanceCounter(); - g_isUpdatePending = true; - g_isGdiUpdatePending = false; - } - auto primary(DDraw::PrimarySurface::getPrimary()); RECT emptyRect = {}; HRESULT result = primary ? primary->BltFast(primary, 0, 0, primary, &emptyRect, DDBLTFAST_WAIT) : DD_OK; @@ -465,16 +393,30 @@ namespace DWORD WINAPI updateThreadProc(LPVOID /*lpParameter*/) { + bool skipWaitForVsync = false; + while (!g_stopUpdateThread) { - D3dDdi::KernelModeThunks::waitForVerticalBlank(); - if (!g_isFullScreen) + if (!skipWaitForVsync) { - g_isPresentPending = false; + D3dDdi::KernelModeThunks::waitForVsync(); } - + skipWaitForVsync = false; + Gdi::Caret::blink(); Sleep(1); - DDraw::RealPrimarySurface::flush(); + + DDraw::ScopedThreadLock lock; + if (g_isUpdatePending && !isPresentPending()) + { + if (Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcFlipEnd) < 1) + { + skipWaitForVsync = true; + } + else + { + updateNowIfNotBusy(); + } + } } return 0; @@ -533,62 +475,53 @@ namespace DDraw HRESULT RealPrimarySurface::flip(CompatPtr surfaceTargetOverride, DWORD flags) { - DDraw::ScopedThreadLock lock; + const DWORD flipInterval = getFlipInterval(flags); + if (0 == flipInterval) + { + g_isUpdatePending = true; + return DD_OK; + } - 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); + if (!isPresentPending()) + { + CompatPtr prevPrimarySurface( + surfaceTargetOverride ? surfaceTargetOverride : PrimarySurface::getLastSurface()); + updateNow(prevPrimarySurface, 0); + } g_isUpdatePending = true; } - - const bool isFlipEmulated = 0 != (PrimarySurface::getOrigCaps() & DDSCAPS_SYSTEMMEMORY); - if (isFlipEmulated) - { - surfaceTargetOverride.get()->lpVtbl->Blt( - surfaceTargetOverride, nullptr, PrimarySurface::getPrimary(), nullptr, DDBLT_WAIT, nullptr); - } - - if (!isFlipDelayed) + else { updateNow(PrimarySurface::getPrimary(), flipInterval); } + g_flipEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + flipInterval; + g_presentEndVsyncCount = g_flipEndVsyncCount; 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()); } - + else + { + g_lastFlipSurface = nullptr; + } return DD_OK; } void RealPrimarySurface::flush() { DDraw::ScopedThreadLock lock; - Gdi::Caret::blink(); - if ((g_isUpdatePending || g_isGdiUpdatePending) && !isPresentPending()) + if (g_isUpdatePending && !isPresentPending()) { updateNowIfNotBusy(); } } - void RealPrimarySurface::gdiUpdate() - { - g_isGdiUpdatePending = true; - } - HRESULT RealPrimarySurface::getGammaRamp(DDGAMMARAMP* rampData) { DDraw::ScopedThreadLock lock; @@ -656,6 +589,12 @@ namespace DDraw return result; } + void RealPrimarySurface::scheduleUpdate() + { + g_qpcLastUpdate = Time::queryPerformanceCounter(); + g_isUpdatePending = true; + } + HRESULT RealPrimarySurface::setGammaRamp(DDGAMMARAMP* rampData) { DDraw::ScopedThreadLock lock; @@ -694,11 +633,10 @@ namespace DDraw return !isFlipPending(); } - while (isFlipPending()) + if (D3dDdi::KernelModeThunks::waitForVsyncCounter(g_flipEndVsyncCount)) { - D3dDdi::KernelModeThunks::waitForVerticalBlank(); + g_qpcFlipEnd = Time::queryPerformanceCounter(); } - return true; } } diff --git a/DDrawCompat/DDraw/RealPrimarySurface.h b/DDrawCompat/DDraw/RealPrimarySurface.h index fa72cee..7d3504e 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.h +++ b/DDrawCompat/DDraw/RealPrimarySurface.h @@ -2,8 +2,8 @@ #include -#include "Common/CompatPtr.h" -#include "Common/CompatRef.h" +#include +#include namespace DDraw { @@ -17,7 +17,6 @@ namespace DDraw static HRESULT flip(CompatPtr surfaceTargetOverride, DWORD flags); static void flush(); - static void gdiUpdate(); static HRESULT getGammaRamp(DDGAMMARAMP* rampData); static CompatWeakPtr getSurface(); static void init(); @@ -26,6 +25,7 @@ namespace DDraw static void release(); static void removeUpdateThread(); static HRESULT restore(); + static void scheduleUpdate(); static HRESULT setGammaRamp(DDGAMMARAMP* rampData); static void update(); static bool waitForFlip(Surface* surface, bool wait = true); diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp index 2a9ed8a..a9ef953 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp @@ -120,20 +120,22 @@ namespace DDraw template HRESULT PrimarySurfaceImpl::Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags) { - const bool wait = (dwFlags & DDFLIP_WAIT) || !(dwFlags & DDFLIP_DONOTWAIT) && - CompatVtable::s_origVtablePtr == static_cast(This->lpVtbl); - if (!DDraw::RealPrimarySurface::waitForFlip(Surface::getSurface(*This), wait)) + if (!waitForFlip(This, dwFlags, DDFLIP_WAIT, DDFLIP_DONOTWAIT)) { return DDERR_WASSTILLDRAWING; } auto surfaceTargetOverride(CompatPtr::from(lpDDSurfaceTargetOverride)); const bool isFlipEmulated = 0 != (PrimarySurface::getOrigCaps() & DDSCAPS_SYSTEMMEMORY); - if (isFlipEmulated && !surfaceTargetOverride) + if (isFlipEmulated) { - TDdsCaps caps = {}; - caps.dwCaps = DDSCAPS_BACKBUFFER; - s_origVtable.GetAttachedSurface(This, &caps, &surfaceTargetOverride.getRef()); + if (!surfaceTargetOverride) + { + TDdsCaps caps = {}; + caps.dwCaps = DDSCAPS_BACKBUFFER; + s_origVtable.GetAttachedSurface(This, &caps, &surfaceTargetOverride.getRef()); + } + return Blt(This, nullptr, surfaceTargetOverride.get(), nullptr, DDBLT_WAIT, nullptr); } HRESULT result = SurfaceImpl::Flip(This, surfaceTargetOverride, DDFLIP_WAIT); diff --git a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp index d14daf3..493aed5 100644 --- a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp @@ -35,6 +35,10 @@ 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; + } return s_origVtable.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); } diff --git a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h index 2c49e36..8cf7393 100644 --- a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h +++ b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h @@ -42,11 +42,11 @@ namespace DDraw virtual HRESULT Unlock(TSurface* This, TUnlockParam lpRect); protected: + bool waitForFlip(TSurface* This, DWORD flags, DWORD waitFlag, DWORD doNotWaitFlag); + static const Vtable& s_origVtable; private: - bool waitForFlip(TSurface* This, DWORD flags, DWORD waitFlag, DWORD doNotWaitFlag); - Surface* m_data; }; } diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index b1911d4..5bbb9ae 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -140,6 +140,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index cee5d4f..bad23fa 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -396,6 +396,9 @@ Header Files\Win32 + + Header Files\Common + diff --git a/DDrawCompat/Gdi/AccessGuard.cpp b/DDrawCompat/Gdi/AccessGuard.cpp index 7a114d2..ea107df 100644 --- a/DDrawCompat/Gdi/AccessGuard.cpp +++ b/DDrawCompat/Gdi/AccessGuard.cpp @@ -34,7 +34,7 @@ namespace Gdi if (ACCESS_WRITE == m_access && (!gdiResource || DDraw::PrimarySurface::getFrontResource() == *gdiResource)) { - DDraw::RealPrimarySurface::gdiUpdate(); + DDraw::RealPrimarySurface::scheduleUpdate(); } } } diff --git a/DDrawCompat/Gdi/VirtualScreen.cpp b/DDrawCompat/Gdi/VirtualScreen.cpp index b952f15..36b6028 100644 --- a/DDrawCompat/Gdi/VirtualScreen.cpp +++ b/DDrawCompat/Gdi/VirtualScreen.cpp @@ -266,7 +266,7 @@ namespace Gdi } } - DDraw::RealPrimarySurface::gdiUpdate(); + DDraw::RealPrimarySurface::scheduleUpdate(); } } } diff --git a/DDrawCompat/Gdi/Window.cpp b/DDrawCompat/Gdi/Window.cpp index dcf305e..3c57313 100644 --- a/DDrawCompat/Gdi/Window.cpp +++ b/DDrawCompat/Gdi/Window.cpp @@ -403,7 +403,7 @@ namespace Gdi { m_presentationWindow = hwnd; SendNotifyMessage(m_presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast(m_hwnd)); - DDraw::RealPrimarySurface::gdiUpdate(); + DDraw::RealPrimarySurface::scheduleUpdate(); } } @@ -477,7 +477,7 @@ namespace Gdi { window->m_colorKey = colorKey; window->m_alpha = alpha; - DDraw::RealPrimarySurface::gdiUpdate(); + DDraw::RealPrimarySurface::scheduleUpdate(); } }