1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

Removed dependency on D3DKMTPresent

Fixes presentation issues when Hardware Accelerated GPU Scheduling is enabled
(issue #73).
This commit is contained in:
narzoul 2020-07-13 23:44:05 +02:00
parent b8bff744e5
commit 799e9a95ae
15 changed files with 244 additions and 332 deletions

View File

@ -0,0 +1,48 @@
#pragma once
#include <Windows.h>
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;
};
}

View File

@ -100,5 +100,6 @@ namespace D3dDdi
void uninstallHooks() void uninstallHooks()
{ {
unhookOpenAdapter(); unhookOpenAdapter();
KernelModeThunks::stopVsyncThread();
} }
} }

View File

@ -1,15 +1,8 @@
#include <atomic>
#include <map>
#include <string> #include <string>
#include <d3d.h>
#include <d3dumddi.h>
#include <winternl.h>
#include <../km/d3dkmthk.h>
#include <Common/Log.h> #include <Common/Log.h>
#include <Common/Hook.h> #include <Common/Hook.h>
#include <Common/ScopedCriticalSection.h> #include <Common/ScopedSrwLock.h>
#include <Common/Time.h> #include <Common/Time.h>
#include <D3dDdi/Device.h> #include <D3dDdi/Device.h>
#include <D3dDdi/Hooks.h> #include <D3dDdi/Hooks.h>
@ -32,33 +25,24 @@ namespace
RECT monitorRect; RECT monitorRect;
}; };
struct ContextInfo
{
D3DKMT_HANDLE device;
ContextInfo() : device(0) {}
};
std::map<D3DKMT_HANDLE, ContextInfo> g_contexts;
D3DDDIFORMAT g_dcFormatOverride = D3DDDIFMT_UNKNOWN; D3DDDIFORMAT g_dcFormatOverride = D3DDDIFMT_UNKNOWN;
bool g_dcPaletteOverride = false; bool g_dcPaletteOverride = false;
AdapterInfo g_gdiAdapterInfo = {}; AdapterInfo g_gdiAdapterInfo = {};
AdapterInfo g_lastOpenAdapterInfo = {}; AdapterInfo g_lastOpenAdapterInfo = {};
Compat::SrwLock g_lastOpenAdapterInfoSrwLock;
std::string g_lastDDrawCreateDcDevice; std::string g_lastDDrawCreateDcDevice;
UINT g_lastFlipInterval = 0;
UINT g_flipIntervalOverride = 0;
D3DKMT_HANDLE g_lastPresentContext = 0;
UINT g_presentCount = 0;
std::atomic<long long> 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) NTSTATUS APIENTRY closeAdapter(const D3DKMT_CLOSEADAPTER* pData)
{ {
Compat::ScopedCriticalSection lock(g_vblankCs); Compat::ScopedSrwLockExclusive lock(g_lastOpenAdapterInfoSrwLock);
if (pData && pData->hAdapter == g_lastOpenAdapterInfo.adapter) if (pData && pData->hAdapter == g_lastOpenAdapterInfo.adapter)
{ {
g_lastOpenAdapterInfo = {}; g_lastOpenAdapterInfo = {};
@ -66,30 +50,6 @@ namespace
return D3DKMTCloseAdapter(pData); 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<decltype(D3DKMTCreateContextVirtual)*>(
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) NTSTATUS APIENTRY createDcFromMemory(D3DKMT_CREATEDCFROMMEMORY* pData)
{ {
LOG_FUNC("D3DKMTCreateDCFromMemory", pData); LOG_FUNC("D3DKMTCreateDCFromMemory", pData);
@ -154,21 +114,6 @@ namespace
return LOG_RESULT(CreateDCA(pwszDriver, pwszDevice, pszPort, pdm)); 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) BOOL CALLBACK findDDrawMonitorRect(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData)
{ {
MONITORINFOEX mi = {}; MONITORINFOEX mi = {};
@ -208,40 +153,12 @@ namespace
NTSTATUS result = D3DKMTOpenAdapterFromHdc(pData); NTSTATUS result = D3DKMTOpenAdapterFromHdc(pData);
if (SUCCEEDED(result)) if (SUCCEEDED(result))
{ {
Compat::ScopedCriticalSection lock(g_vblankCs); Compat::ScopedSrwLockExclusive lock(g_lastOpenAdapterInfoSrwLock);
g_lastOpenAdapterInfo = getAdapterInfo(*pData); g_lastOpenAdapterInfo = getAdapterInfo(*pData);
} }
return LOG_RESULT(result); 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<D3DDDI_FLIPINTERVAL_TYPE>(g_flipIntervalOverride);
}
return LOG_RESULT(D3DKMTPresent(pData));
}
NTSTATUS APIENTRY queryAdapterInfo(const D3DKMT_QUERYADAPTERINFO* pData) NTSTATUS APIENTRY queryAdapterInfo(const D3DKMT_QUERYADAPTERINFO* pData)
{ {
LOG_FUNC("D3DKMTQueryAdapterInfo", pData); LOG_FUNC("D3DKMTQueryAdapterInfo", pData);
@ -283,12 +200,13 @@ namespace
NTSTATUS APIENTRY setGammaRamp(const D3DKMT_SETGAMMARAMP* pData) NTSTATUS APIENTRY setGammaRamp(const D3DKMT_SETGAMMARAMP* pData)
{ {
LOG_FUNC("D3DKMTSetGammaRamp", pData); LOG_FUNC("D3DKMTSetGammaRamp", pData);
HANDLE vsyncThread = CreateThread(nullptr, 0, &waitForVsyncThreadProc, nullptr, 0, nullptr); UINT vsyncCounter = D3dDdi::KernelModeThunks::getVsyncCounter();
SetThreadPriority(vsyncThread, THREAD_PRIORITY_TIME_CRITICAL);
DDraw::RealPrimarySurface::flush(); DDraw::RealPrimarySurface::flush();
HRESULT result = D3DKMTSetGammaRamp(pData); HRESULT result = D3DKMTSetGammaRamp(pData);
WaitForSingleObject(vsyncThread, INFINITE); if (SUCCEEDED(result))
CloseHandle(vsyncThread); {
D3dDdi::KernelModeThunks::waitForVsyncCounter(vsyncCounter + 1);
}
return LOG_RESULT(result); 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; 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 D3dDdi
{ {
namespace KernelModeThunks 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() RECT getMonitorRect()
{ {
auto primary(DDraw::PrimarySurface::getPrimary()); auto primary(DDraw::PrimarySurface::getPrimary());
@ -389,28 +308,23 @@ namespace D3dDdi
return g_lastOpenAdapterInfo.monitorRect; return g_lastOpenAdapterInfo.monitorRect;
} }
long long getQpcLastVerticalBlank() UINT getVsyncCounter()
{ {
return g_qpcLastVerticalBlank; Compat::ScopedSrwLockShared lock(g_vsyncCounterSrwLock);
return g_vsyncCounter;
} }
void installHooks(HMODULE origDDrawModule) void installHooks(HMODULE origDDrawModule)
{ {
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "CreateDCA", ddrawCreateDcA); Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "CreateDCA", ddrawCreateDcA);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTCloseAdapter", closeAdapter); 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", "D3DKMTCreateDCFromMemory", createDcFromMemory);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTDestroyContext", destroyContext);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTOpenAdapterFromHdc", openAdapterFromHdc); Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTOpenAdapterFromHdc", openAdapterFromHdc);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTQueryAdapterInfo", queryAdapterInfo); Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTQueryAdapterInfo", queryAdapterInfo);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTPresent", present);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTSetGammaRamp", setGammaRamp); Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "D3DKMTSetGammaRamp", setGammaRamp);
}
void setFlipIntervalOverride(UINT flipInterval) g_vsyncThread = CreateThread(nullptr, 0, &vsyncThreadProc, nullptr, 0, nullptr);
{ SetThreadPriority(g_vsyncThread, THREAD_PRIORITY_TIME_CRITICAL);
g_flipIntervalOverride = flipInterval;
} }
void setDcFormatOverride(UINT format) void setDcFormatOverride(UINT format)
@ -423,30 +337,33 @@ namespace D3dDdi
g_dcPaletteOverride = enable; 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<INT>(g_vsyncCounter - counter) < 0)
{ {
Compat::ScopedCriticalSection lock(g_vblankCs); SleepConditionVariableSRW(&g_vsyncCounterCv, &g_vsyncCounterSrwLock, INFINITE,
if (g_lastOpenAdapterInfo.adapter) CONDITION_VARIABLE_LOCKMODE_SHARED);
{ waited = true;
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();
} }
return waited;
} }
} }
} }

View File

@ -6,15 +6,13 @@ namespace D3dDdi
{ {
namespace KernelModeThunks namespace KernelModeThunks
{ {
UINT getLastFlipInterval();
UINT getLastDisplayedFrameCount();
UINT getLastSubmittedFrameCount();
RECT getMonitorRect(); RECT getMonitorRect();
long long getQpcLastVerticalBlank(); UINT getVsyncCounter();
void installHooks(HMODULE origDDrawModule); void installHooks(HMODULE origDDrawModule);
void setFlipIntervalOverride(UINT flipInterval);
void setDcFormatOverride(UINT format); void setDcFormatOverride(UINT format);
void setDcPaletteOverride(bool enable); void setDcPaletteOverride(bool enable);
void waitForVerticalBlank(); void stopVsyncThread();
void waitForVsync();
bool waitForVsyncCounter(UINT counter);
} }
} }

View File

@ -141,7 +141,7 @@ namespace DDraw
DWORD scanLine = 0; DWORD scanLine = 0;
if (DDERR_VERTICALBLANKINPROGRESS != s_origVtable.GetScanLine(This, &scanLine)) if (DDERR_VERTICALBLANKINPROGRESS != s_origVtable.GetScanLine(This, &scanLine))
{ {
D3dDdi::KernelModeThunks::waitForVerticalBlank(); D3dDdi::KernelModeThunks::waitForVsync();
} }
if (DDWAITVB_BLOCKEND == dwFlags) if (DDWAITVB_BLOCKEND == dwFlags)

View File

@ -1,30 +1,30 @@
#include <atomic>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "Common/CompatPtr.h" #include <Common/CompatPtr.h>
#include "Common/Hook.h" #include <Common/Hook.h>
#include "Common/Time.h" #include <Common/Time.h>
#include "Config/Config.h" #include <Config/Config.h>
#include "D3dDdi/Device.h" #include <D3dDdi/Device.h>
#include "D3dDdi/KernelModeThunks.h" #include <D3dDdi/KernelModeThunks.h>
#include "DDraw/DirectDraw.h" #include <DDraw/DirectDraw.h>
#include "DDraw/DirectDrawSurface.h" #include <DDraw/DirectDrawSurface.h>
#include "DDraw/IReleaseNotifier.h" #include <DDraw/IReleaseNotifier.h>
#include "DDraw/RealPrimarySurface.h" #include <DDraw/RealPrimarySurface.h>
#include "DDraw/ScopedThreadLock.h" #include <DDraw/ScopedThreadLock.h>
#include "DDraw/Surfaces/PrimarySurface.h" #include <DDraw/Surfaces/PrimarySurface.h>
#include "DDraw/Types.h" #include <DDraw/Types.h>
#include "Gdi/AccessGuard.h" #include <Gdi/AccessGuard.h>
#include "Gdi/Caret.h" #include <Gdi/Caret.h>
#include "Gdi/Gdi.h" #include <Gdi/Gdi.h>
#include "Gdi/VirtualScreen.h" #include <Gdi/VirtualScreen.h>
#include "Gdi/Window.h" #include <Gdi/Window.h>
#include "Win32/DisplayMode.h" #include <Win32/DisplayMode.h>
namespace namespace
{ {
void onRelease(); void onRelease();
DWORD WINAPI updateThreadProc(LPVOID lpParameter);
CompatWeakPtr<IDirectDrawSurface7> g_frontBuffer; CompatWeakPtr<IDirectDrawSurface7> g_frontBuffer;
CompatWeakPtr<IDirectDrawSurface7> g_paletteConverter; CompatWeakPtr<IDirectDrawSurface7> g_paletteConverter;
@ -34,16 +34,15 @@ namespace
bool g_stopUpdateThread = false; bool g_stopUpdateThread = false;
HANDLE g_updateThread = nullptr; 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_isFullScreen = false;
bool g_waitingForPrimaryUnlock = false;
UINT g_flipIntervalDriverOverride = UINT_MAX;
UINT g_lastFlipFrameCount = 0;
DDraw::Surface* g_lastFlipSurface = nullptr; DDraw::Surface* g_lastFlipSurface = nullptr;
long long g_qpcLastUpdate = 0;
bool g_isUpdatePending = false;
bool g_waitingForPrimaryUnlock = false;
std::atomic<long long> g_qpcLastUpdate = 0;
long long g_qpcFlipEnd = 0;
UINT g_flipEndVsyncCount = 0;
UINT g_presentEndVsyncCount = 0;
CompatPtr<IDirectDrawSurface7> getBackBuffer(); CompatPtr<IDirectDrawSurface7> getBackBuffer();
CompatPtr<IDirectDrawSurface7> getLastSurface(); CompatPtr<IDirectDrawSurface7> getLastSurface();
@ -246,13 +245,13 @@ namespace
return lastSurface; return lastSurface;
} }
UINT getFlipIntervalFromFlags(DWORD flags) UINT getFlipInterval(DWORD flags)
{ {
if (flags & DDFLIP_NOVSYNC) if (flags & DDFLIP_NOVSYNC)
{ {
return 0; return 0;
} }
if (flags & (DDFLIP_INTERVAL2 | DDFLIP_INTERVAL3 | DDFLIP_INTERVAL4)) if (flags & (DDFLIP_INTERVAL2 | DDFLIP_INTERVAL3 | DDFLIP_INTERVAL4))
{ {
UINT flipInterval = (flags & (DDFLIP_INTERVAL2 | DDFLIP_INTERVAL3 | DDFLIP_INTERVAL4)) >> 24; UINT flipInterval = (flags & (DDFLIP_INTERVAL2 | DDFLIP_INTERVAL3 | DDFLIP_INTERVAL4)) >> 24;
@ -266,39 +265,14 @@ namespace
return 1; 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() bool isFlipPending()
{ {
if (g_isFlipPending) return static_cast<INT>(D3dDdi::KernelModeThunks::getVsyncCounter() - g_flipEndVsyncCount) < 0;
{
g_isFlipPending = static_cast<int>(D3dDdi::KernelModeThunks::getLastDisplayedFrameCount() -
g_lastFlipFrameCount) < 0;
}
return g_isFlipPending;
} }
bool isPresentPending() bool isPresentPending()
{ {
if (g_isPresentPending) return static_cast<INT>(D3dDdi::KernelModeThunks::getVsyncCounter() - g_presentEndVsyncCount) < 0;
{
g_isPresentPending = static_cast<int>(D3dDdi::KernelModeThunks::getLastDisplayedFrameCount() -
D3dDdi::KernelModeThunks::getLastSubmittedFrameCount()) < 0;
}
return g_isPresentPending;
} }
void onRelease() void onRelease()
@ -308,8 +282,6 @@ namespace
g_frontBuffer = nullptr; g_frontBuffer = nullptr;
g_clipper.release(); g_clipper.release();
g_isFullScreen = false; g_isFullScreen = false;
g_isFlipPending = false;
g_isPresentPending = false;
g_waitingForPrimaryUnlock = false; g_waitingForPrimaryUnlock = false;
g_paletteConverter.release(); g_paletteConverter.release();
g_surfaceDesc = {}; g_surfaceDesc = {};
@ -332,32 +304,16 @@ namespace
g_surfaceDesc = desc; g_surfaceDesc = desc;
g_isFullScreen = isFlippable; g_isFullScreen = isFlippable;
g_flipIntervalDriverOverride = UINT_MAX;
g_isFlipPending = false;
g_isPresentPending = false;
g_isUpdatePending = false; g_isUpdatePending = false;
g_qpcLastUpdate = Time::queryPerformanceCounter() - Time::msToQpc(Config::delayedFlipModeTimeout); g_qpcLastUpdate = Time::queryPerformanceCounter() - Time::msToQpc(Config::delayedFlipModeTimeout);
if (isFlippable) if (isFlippable)
{ {
D3dDdi::KernelModeThunks::setFlipIntervalOverride(UINT_MAX); g_frontBuffer->Flip(g_frontBuffer, getLastSurface(), DDFLIP_WAIT);
g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT | DDFLIP_NOVSYNC); g_flipEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + 1;
g_flipIntervalDriverOverride = D3dDdi::KernelModeThunks::getLastFlipInterval(); g_presentEndVsyncCount = g_flipEndVsyncCount;
g_frontBuffer->Flip(g_frontBuffer, nullptr, DDFLIP_WAIT); D3dDdi::KernelModeThunks::waitForVsyncCounter(g_flipEndVsyncCount);
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<IDirectDrawSurface7> src) void presentToPrimaryChain(CompatWeakPtr<IDirectDrawSurface7> src)
@ -410,47 +366,19 @@ namespace
void updateNow(CompatWeakPtr<IDirectDrawSurface7> src, UINT flipInterval) void updateNow(CompatWeakPtr<IDirectDrawSurface7> src, UINT flipInterval)
{ {
DDraw::ScopedThreadLock lock;
if (flipInterval <= 1 && isPresentPending())
{
g_isUpdatePending = true;
return;
}
presentToPrimaryChain(src); presentToPrimaryChain(src);
g_isUpdatePending = false; g_isUpdatePending = false;
g_waitingForPrimaryUnlock = false;
if (!g_isFullScreen) if (g_isFullScreen)
{ {
g_isPresentPending = true; g_frontBuffer->Flip(g_frontBuffer, getBackBuffer(), DDFLIP_WAIT);
return;
} }
g_presentEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + max(flipInterval, 1);
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() void updateNowIfNotBusy()
{ {
if (g_isGdiUpdatePending)
{
g_qpcLastUpdate = Time::queryPerformanceCounter();
g_isUpdatePending = true;
g_isGdiUpdatePending = false;
}
auto primary(DDraw::PrimarySurface::getPrimary()); auto primary(DDraw::PrimarySurface::getPrimary());
RECT emptyRect = {}; RECT emptyRect = {};
HRESULT result = primary ? primary->BltFast(primary, 0, 0, primary, &emptyRect, DDBLTFAST_WAIT) : DD_OK; HRESULT result = primary ? primary->BltFast(primary, 0, 0, primary, &emptyRect, DDBLTFAST_WAIT) : DD_OK;
@ -465,16 +393,30 @@ namespace
DWORD WINAPI updateThreadProc(LPVOID /*lpParameter*/) DWORD WINAPI updateThreadProc(LPVOID /*lpParameter*/)
{ {
bool skipWaitForVsync = false;
while (!g_stopUpdateThread) while (!g_stopUpdateThread)
{ {
D3dDdi::KernelModeThunks::waitForVerticalBlank(); if (!skipWaitForVsync)
if (!g_isFullScreen)
{ {
g_isPresentPending = false; D3dDdi::KernelModeThunks::waitForVsync();
} }
skipWaitForVsync = false;
Gdi::Caret::blink();
Sleep(1); 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; return 0;
@ -533,62 +475,53 @@ namespace DDraw
HRESULT RealPrimarySurface::flip(CompatPtr<IDirectDrawSurface7> surfaceTargetOverride, DWORD flags) HRESULT RealPrimarySurface::flip(CompatPtr<IDirectDrawSurface7> 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 auto msSinceLastUpdate = Time::qpcToMs(Time::queryPerformanceCounter() - g_qpcLastUpdate);
const bool isFlipDelayed = msSinceLastUpdate >= 0 && msSinceLastUpdate <= Config::delayedFlipModeTimeout; const bool isFlipDelayed = msSinceLastUpdate >= 0 && msSinceLastUpdate <= Config::delayedFlipModeTimeout;
if (isFlipDelayed) if (isFlipDelayed)
{ {
CompatPtr<IDirectDrawSurface7> prevPrimarySurface( if (!isPresentPending())
surfaceTargetOverride ? surfaceTargetOverride : PrimarySurface::getLastSurface()); {
updateNow(prevPrimarySurface, flipInterval); CompatPtr<IDirectDrawSurface7> prevPrimarySurface(
surfaceTargetOverride ? surfaceTargetOverride : PrimarySurface::getLastSurface());
updateNow(prevPrimarySurface, 0);
}
g_isUpdatePending = true; g_isUpdatePending = true;
} }
else
const bool isFlipEmulated = 0 != (PrimarySurface::getOrigCaps() & DDSCAPS_SYSTEMMEMORY);
if (isFlipEmulated)
{
surfaceTargetOverride.get()->lpVtbl->Blt(
surfaceTargetOverride, nullptr, PrimarySurface::getPrimary(), nullptr, DDBLT_WAIT, nullptr);
}
if (!isFlipDelayed)
{ {
updateNow(PrimarySurface::getPrimary(), flipInterval); updateNow(PrimarySurface::getPrimary(), flipInterval);
} }
g_flipEndVsyncCount = D3dDdi::KernelModeThunks::getVsyncCounter() + flipInterval;
g_presentEndVsyncCount = g_flipEndVsyncCount;
if (0 != flipInterval) if (0 != flipInterval)
{
g_isFlipPending = true;
g_lastFlipFrameCount = D3dDdi::KernelModeThunks::getLastSubmittedFrameCount();
}
g_lastFlipSurface = nullptr;
if (g_isFlipPending && !isFlipEmulated)
{ {
g_lastFlipSurface = Surface::getSurface( g_lastFlipSurface = Surface::getSurface(
surfaceTargetOverride ? *surfaceTargetOverride : *PrimarySurface::getLastSurface()); surfaceTargetOverride ? *surfaceTargetOverride : *PrimarySurface::getLastSurface());
} }
else
{
g_lastFlipSurface = nullptr;
}
return DD_OK; return DD_OK;
} }
void RealPrimarySurface::flush() void RealPrimarySurface::flush()
{ {
DDraw::ScopedThreadLock lock; DDraw::ScopedThreadLock lock;
Gdi::Caret::blink(); if (g_isUpdatePending && !isPresentPending())
if ((g_isUpdatePending || g_isGdiUpdatePending) && !isPresentPending())
{ {
updateNowIfNotBusy(); updateNowIfNotBusy();
} }
} }
void RealPrimarySurface::gdiUpdate()
{
g_isGdiUpdatePending = true;
}
HRESULT RealPrimarySurface::getGammaRamp(DDGAMMARAMP* rampData) HRESULT RealPrimarySurface::getGammaRamp(DDGAMMARAMP* rampData)
{ {
DDraw::ScopedThreadLock lock; DDraw::ScopedThreadLock lock;
@ -656,6 +589,12 @@ namespace DDraw
return result; return result;
} }
void RealPrimarySurface::scheduleUpdate()
{
g_qpcLastUpdate = Time::queryPerformanceCounter();
g_isUpdatePending = true;
}
HRESULT RealPrimarySurface::setGammaRamp(DDGAMMARAMP* rampData) HRESULT RealPrimarySurface::setGammaRamp(DDGAMMARAMP* rampData)
{ {
DDraw::ScopedThreadLock lock; DDraw::ScopedThreadLock lock;
@ -694,11 +633,10 @@ namespace DDraw
return !isFlipPending(); return !isFlipPending();
} }
while (isFlipPending()) if (D3dDdi::KernelModeThunks::waitForVsyncCounter(g_flipEndVsyncCount))
{ {
D3dDdi::KernelModeThunks::waitForVerticalBlank(); g_qpcFlipEnd = Time::queryPerformanceCounter();
} }
return true; return true;
} }
} }

View File

@ -2,8 +2,8 @@
#include <ddraw.h> #include <ddraw.h>
#include "Common/CompatPtr.h" #include <Common/CompatPtr.h>
#include "Common/CompatRef.h" #include <Common/CompatRef.h>
namespace DDraw namespace DDraw
{ {
@ -17,7 +17,6 @@ namespace DDraw
static HRESULT flip(CompatPtr<IDirectDrawSurface7> surfaceTargetOverride, DWORD flags); static HRESULT flip(CompatPtr<IDirectDrawSurface7> surfaceTargetOverride, DWORD flags);
static void flush(); static void flush();
static void gdiUpdate();
static HRESULT getGammaRamp(DDGAMMARAMP* rampData); static HRESULT getGammaRamp(DDGAMMARAMP* rampData);
static CompatWeakPtr<IDirectDrawSurface7> getSurface(); static CompatWeakPtr<IDirectDrawSurface7> getSurface();
static void init(); static void init();
@ -26,6 +25,7 @@ namespace DDraw
static void release(); static void release();
static void removeUpdateThread(); static void removeUpdateThread();
static HRESULT restore(); static HRESULT restore();
static void scheduleUpdate();
static HRESULT setGammaRamp(DDGAMMARAMP* rampData); static HRESULT setGammaRamp(DDGAMMARAMP* rampData);
static void update(); static void update();
static bool waitForFlip(Surface* surface, bool wait = true); static bool waitForFlip(Surface* surface, bool wait = true);

View File

@ -120,20 +120,22 @@ namespace DDraw
template <typename TSurface> template <typename TSurface>
HRESULT PrimarySurfaceImpl<TSurface>::Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags) HRESULT PrimarySurfaceImpl<TSurface>::Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags)
{ {
const bool wait = (dwFlags & DDFLIP_WAIT) || !(dwFlags & DDFLIP_DONOTWAIT) && if (!waitForFlip(This, dwFlags, DDFLIP_WAIT, DDFLIP_DONOTWAIT))
CompatVtable<IDirectDrawSurface7Vtbl>::s_origVtablePtr == static_cast<void*>(This->lpVtbl);
if (!DDraw::RealPrimarySurface::waitForFlip(Surface::getSurface(*This), wait))
{ {
return DDERR_WASSTILLDRAWING; return DDERR_WASSTILLDRAWING;
} }
auto surfaceTargetOverride(CompatPtr<TSurface>::from(lpDDSurfaceTargetOverride)); auto surfaceTargetOverride(CompatPtr<TSurface>::from(lpDDSurfaceTargetOverride));
const bool isFlipEmulated = 0 != (PrimarySurface::getOrigCaps() & DDSCAPS_SYSTEMMEMORY); const bool isFlipEmulated = 0 != (PrimarySurface::getOrigCaps() & DDSCAPS_SYSTEMMEMORY);
if (isFlipEmulated && !surfaceTargetOverride) if (isFlipEmulated)
{ {
TDdsCaps caps = {}; if (!surfaceTargetOverride)
caps.dwCaps = DDSCAPS_BACKBUFFER; {
s_origVtable.GetAttachedSurface(This, &caps, &surfaceTargetOverride.getRef()); 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); HRESULT result = SurfaceImpl::Flip(This, surfaceTargetOverride, DDFLIP_WAIT);

View File

@ -35,6 +35,10 @@ namespace DDraw
HRESULT SurfaceImpl<TSurface>::BltFast( HRESULT SurfaceImpl<TSurface>::BltFast(
TSurface* This, DWORD dwX, DWORD dwY, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) 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); return s_origVtable.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans);
} }

View File

@ -42,11 +42,11 @@ namespace DDraw
virtual HRESULT Unlock(TSurface* This, TUnlockParam lpRect); virtual HRESULT Unlock(TSurface* This, TUnlockParam lpRect);
protected: protected:
bool waitForFlip(TSurface* This, DWORD flags, DWORD waitFlag, DWORD doNotWaitFlag);
static const Vtable<TSurface>& s_origVtable; static const Vtable<TSurface>& s_origVtable;
private: private:
bool waitForFlip(TSurface* This, DWORD flags, DWORD waitFlag, DWORD doNotWaitFlag);
Surface* m_data; Surface* m_data;
}; };
} }

View File

@ -140,6 +140,7 @@
<ClInclude Include="Common\HResultException.h" /> <ClInclude Include="Common\HResultException.h" />
<ClInclude Include="Common\Log.h" /> <ClInclude Include="Common\Log.h" />
<ClInclude Include="Common\LogWrapperVisitor.h" /> <ClInclude Include="Common\LogWrapperVisitor.h" />
<ClInclude Include="Common\ScopedSrwLock.h" />
<ClInclude Include="Common\VtableHookVisitor.h" /> <ClInclude Include="Common\VtableHookVisitor.h" />
<ClInclude Include="Common\VtableUpdateVisitor.h" /> <ClInclude Include="Common\VtableUpdateVisitor.h" />
<ClInclude Include="Common\VtableVisitor.h" /> <ClInclude Include="Common\VtableVisitor.h" />

View File

@ -396,6 +396,9 @@
<ClInclude Include="Win32\MemoryManagement.h"> <ClInclude Include="Win32\MemoryManagement.h">
<Filter>Header Files\Win32</Filter> <Filter>Header Files\Win32</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Common\ScopedSrwLock.h">
<Filter>Header Files\Common</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Gdi\Gdi.cpp"> <ClCompile Include="Gdi\Gdi.cpp">

View File

@ -34,7 +34,7 @@ namespace Gdi
if (ACCESS_WRITE == m_access && if (ACCESS_WRITE == m_access &&
(!gdiResource || DDraw::PrimarySurface::getFrontResource() == *gdiResource)) (!gdiResource || DDraw::PrimarySurface::getFrontResource() == *gdiResource))
{ {
DDraw::RealPrimarySurface::gdiUpdate(); DDraw::RealPrimarySurface::scheduleUpdate();
} }
} }
} }

View File

@ -266,7 +266,7 @@ namespace Gdi
} }
} }
DDraw::RealPrimarySurface::gdiUpdate(); DDraw::RealPrimarySurface::scheduleUpdate();
} }
} }
} }

View File

@ -403,7 +403,7 @@ namespace Gdi
{ {
m_presentationWindow = hwnd; m_presentationWindow = hwnd;
SendNotifyMessage(m_presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast<LPARAM>(m_hwnd)); SendNotifyMessage(m_presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast<LPARAM>(m_hwnd));
DDraw::RealPrimarySurface::gdiUpdate(); DDraw::RealPrimarySurface::scheduleUpdate();
} }
} }
@ -477,7 +477,7 @@ namespace Gdi
{ {
window->m_colorKey = colorKey; window->m_colorKey = colorKey;
window->m_alpha = alpha; window->m_alpha = alpha;
DDraw::RealPrimarySurface::gdiUpdate(); DDraw::RealPrimarySurface::scheduleUpdate();
} }
} }