Skip to content

Commit

Permalink
Fixed performance issues caused by D3DKMTWaitForVerticalBlankEvent
Browse files Browse the repository at this point in the history
See issue #104 and #120.
  • Loading branch information
narzoul committed Sep 27, 2022
1 parent 175d7e7 commit 664c573
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 45 deletions.
100 changes: 74 additions & 26 deletions DDrawCompat/D3dDdi/KernelModeThunks.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include <atomic>
#include <string>

#include <Windows.h>
#include <VersionHelpers.h>

#include <Common/Log.h>
#include <Common/Hook.h>
#include <Common/ScopedSrwLock.h>
Expand All @@ -22,19 +25,21 @@ namespace
PALETTEENTRY* g_dcPaletteOverride = nullptr;
D3dDdi::KernelModeThunks::AdapterInfo g_gdiAdapterInfo = {};
D3dDdi::KernelModeThunks::AdapterInfo g_lastOpenAdapterInfo = {};
Compat::SrwLock g_lastOpenAdapterInfoSrwLock;
Compat::SrwLock g_adapterInfoSrwLock;
std::string g_lastDDrawDeviceName;

std::atomic<long long> g_qpcLastVsync = 0;
long long g_qpcLastVsync = 0;
UINT g_vsyncCounter = 0;
CONDITION_VARIABLE g_vsyncCounterCv = CONDITION_VARIABLE_INIT;
Compat::SrwLock g_vsyncCounterSrwLock;

void getVidPnSource(D3DKMT_HANDLE& adapter, UINT& vidPnSourceId);
void updateGdiAdapterInfo();
void waitForVerticalBlank();

NTSTATUS APIENTRY closeAdapter(const D3DKMT_CLOSEADAPTER* pData)
{
Compat::ScopedSrwLockExclusive lock(g_lastOpenAdapterInfoSrwLock);
Compat::ScopedSrwLockExclusive lock(g_adapterInfoSrwLock);
if (pData && pData->hAdapter == g_lastOpenAdapterInfo.adapter)
{
g_lastOpenAdapterInfo = {};
Expand Down Expand Up @@ -139,18 +144,60 @@ namespace
return adapterInfo;
}

int getScanLine()
{
D3DKMT_GETSCANLINE data = {};
getVidPnSource(data.hAdapter, data.VidPnSourceId);
if (!data.hAdapter || FAILED(D3DKMTGetScanLine(&data)) || data.InVerticalBlank)
{
return -1;
}
return data.ScanLine;
}

void getVidPnSource(D3DKMT_HANDLE& adapter, UINT& vidPnSourceId)
{
{
Compat::ScopedSrwLockShared lock(g_adapterInfoSrwLock);
if (g_lastOpenAdapterInfo.adapter)
{
adapter = g_lastOpenAdapterInfo.adapter;
vidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId;
return;
}
}

Compat::ScopedSrwLockExclusive lock(g_adapterInfoSrwLock);
updateGdiAdapterInfo();
adapter = g_gdiAdapterInfo.adapter;
vidPnSourceId = g_gdiAdapterInfo.vidPnSourceId;
}

NTSTATUS APIENTRY openAdapterFromHdc(D3DKMT_OPENADAPTERFROMHDC* pData)
{
LOG_FUNC("D3DKMTOpenAdapterFromHdc", pData);
NTSTATUS result = D3DKMTOpenAdapterFromHdc(pData);
if (SUCCEEDED(result))
{
Compat::ScopedSrwLockExclusive lock(g_lastOpenAdapterInfoSrwLock);
Compat::ScopedSrwLockExclusive lock(g_adapterInfoSrwLock);
g_lastOpenAdapterInfo = getAdapterInfo(g_lastDDrawDeviceName, *pData);
}
return LOG_RESULT(result);
}

void pollForVerticalBlank()
{
int scanLine = getScanLine();
int prevScanLine = scanLine;
auto qpcStart = Time::queryPerformanceCounter();
while (scanLine >= prevScanLine && Time::queryPerformanceCounter() - qpcStart < Time::g_qpcFrequency / 60)
{
Sleep(1);
prevScanLine = scanLine;
scanLine = getScanLine();
}
}

NTSTATUS APIENTRY queryAdapterInfo(const D3DKMT_QUERYADAPTERINFO* pData)
{
LOG_FUNC("D3DKMTQueryAdapterInfo", pData);
Expand Down Expand Up @@ -230,10 +277,10 @@ namespace
while (true)
{
waitForVerticalBlank();
g_qpcLastVsync = Time::queryPerformanceCounter();

{
Compat::ScopedSrwLockExclusive lock(g_vsyncCounterSrwLock);
g_qpcLastVsync = Time::queryPerformanceCounter();
++g_vsyncCounter;
}

Expand All @@ -244,25 +291,30 @@ namespace

void waitForVerticalBlank()
{
D3DKMT_WAITFORVERTICALBLANKEVENT data = {};

if (IsWindows8OrGreater())
{
Compat::ScopedSrwLockShared lock(g_lastOpenAdapterInfoSrwLock);
data.hAdapter = g_lastOpenAdapterInfo.adapter;
data.VidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId;
}
D3DKMT_WAITFORVERTICALBLANKEVENT data = {};

if (!data.hAdapter)
{
updateGdiAdapterInfo();
data.hAdapter = g_gdiAdapterInfo.adapter;
data.VidPnSourceId = g_gdiAdapterInfo.vidPnSourceId;
}
{
Compat::ScopedSrwLockShared lock(g_adapterInfoSrwLock);
data.hAdapter = g_lastOpenAdapterInfo.adapter;
data.VidPnSourceId = g_lastOpenAdapterInfo.vidPnSourceId;
}

if (!data.hAdapter || FAILED(D3DKMTWaitForVerticalBlankEvent(&data)))
{
Sleep(16);
if (!data.hAdapter)
{
updateGdiAdapterInfo();
data.hAdapter = g_gdiAdapterInfo.adapter;
data.VidPnSourceId = g_gdiAdapterInfo.vidPnSourceId;
}

if (data.hAdapter && SUCCEEDED(D3DKMTWaitForVerticalBlankEvent(&data)))
{
return;
}
}

pollForVerticalBlank();
}
}

Expand All @@ -280,12 +332,13 @@ namespace D3dDdi

AdapterInfo getLastOpenAdapterInfo()
{
Compat::ScopedSrwLockShared srwLock(g_lastOpenAdapterInfoSrwLock);
Compat::ScopedSrwLockShared srwLock(g_adapterInfoSrwLock);
return g_lastOpenAdapterInfo;
}

long long getQpcLastVsync()
{
Compat::ScopedSrwLockShared lock(g_vsyncCounterSrwLock);
return g_qpcLastVsync;
}

Expand Down Expand Up @@ -317,11 +370,6 @@ namespace D3dDdi
g_dcPaletteOverride = palette;
}

void waitForVsync()
{
waitForVsyncCounter(getVsyncCounter() + 1);
}

bool waitForVsyncCounter(UINT counter)
{
bool waited = false;
Expand Down
1 change: 0 additions & 1 deletion DDrawCompat/D3dDdi/KernelModeThunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ namespace D3dDdi
void installHooks();
void setDcFormatOverride(UINT format);
void setDcPaletteOverride(PALETTEENTRY* palette);
void waitForVsync();
bool waitForVsyncCounter(UINT counter);
}
}
19 changes: 2 additions & 17 deletions DDrawCompat/DDraw/DirectDraw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,23 +172,8 @@ namespace
template <typename TDirectDraw>
HRESULT STDMETHODCALLTYPE WaitForVerticalBlank(TDirectDraw* This, DWORD dwFlags, HANDLE hEvent)
{
if (!This || (DDWAITVB_BLOCKBEGIN != dwFlags && DDWAITVB_BLOCKEND != dwFlags))
{
return getOrigVtable(This).WaitForVerticalBlank(This, dwFlags, hEvent);
}

DWORD scanLine = 0;
if (DDERR_VERTICALBLANKINPROGRESS != getOrigVtable(This).GetScanLine(This, &scanLine))
{
D3dDdi::KernelModeThunks::waitForVsync();
}

if (DDWAITVB_BLOCKEND == dwFlags)
{
while (DDERR_VERTICALBLANKINPROGRESS == getOrigVtable(This).GetScanLine(This, &scanLine));
}

return DD_OK;
DDraw::RealPrimarySurface::flush();
return getOrigVtable(This).WaitForVerticalBlank(This, dwFlags, hEvent);
}

template <typename Vtable>
Expand Down
2 changes: 1 addition & 1 deletion DDrawCompat/DDraw/RealPrimarySurface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ namespace
{
if (!skipWaitForVsync)
{
D3dDdi::KernelModeThunks::waitForVsync();
D3dDdi::KernelModeThunks::waitForVsyncCounter(D3dDdi::KernelModeThunks::getVsyncCounter() + 1);
}
skipWaitForVsync = false;
Sleep(1);
Expand Down

0 comments on commit 664c573

Please sign in to comment.