Implement frame pacing for Windows using D3DKMTWaitForVerticalBlankEvent
This commit is contained in:
parent
8e257ab241
commit
404eaa44e4
3 changed files with 160 additions and 7 deletions
|
|
@ -34,7 +34,7 @@ win32 {
|
|||
LIBS += -L$$PWD/../libs/windows/lib/x64
|
||||
}
|
||||
|
||||
LIBS += ws2_32.lib winmm.lib dxva2.lib ole32.lib dwmapi.lib
|
||||
LIBS += ws2_32.lib winmm.lib dxva2.lib ole32.lib gdi32.lib user32.lib
|
||||
}
|
||||
macx {
|
||||
INCLUDEPATH += $$PWD/../libs/mac/include $$PWD/../libs/mac/Frameworks/SDL2.framework/Versions/A/Headers
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
#include "dxvsyncsource.h"
|
||||
|
||||
#include <dwmapi.h>
|
||||
// Useful references:
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=467617
|
||||
// https://chromium.googlesource.com/chromium/src.git/+/c564f2fe339b2b2abb0c8773c90c83215670ea71/gpu/ipc/service/gpu_vsync_provider_win.cc
|
||||
|
||||
DxVsyncSource::DxVsyncSource(Pacer* pacer) :
|
||||
m_Pacer(pacer),
|
||||
m_Thread(nullptr)
|
||||
m_Thread(nullptr),
|
||||
m_Gdi32Handle(nullptr)
|
||||
{
|
||||
SDL_AtomicSet(&m_Stopping, 0);
|
||||
}
|
||||
|
|
@ -15,10 +18,47 @@ DxVsyncSource::~DxVsyncSource()
|
|||
SDL_AtomicSet(&m_Stopping, 1);
|
||||
SDL_WaitThread(m_Thread, nullptr);
|
||||
}
|
||||
|
||||
if (m_Gdi32Handle != nullptr) {
|
||||
FreeLibrary(m_Gdi32Handle);
|
||||
}
|
||||
}
|
||||
|
||||
bool DxVsyncSource::initialize(SDL_Window* window)
|
||||
{
|
||||
m_Gdi32Handle = LoadLibraryA("gdi32.dll");
|
||||
if (m_Gdi32Handle == nullptr) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to load gdi32.dll: %d",
|
||||
GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_D3DKMTOpenAdapterFromHdc = (PFND3DKMTOPENADAPTERFROMHDC)GetProcAddress(m_Gdi32Handle, "D3DKMTOpenAdapterFromHdc");
|
||||
m_D3DKMTCloseAdapter = (PFND3DKMTCLOSEADAPTER)GetProcAddress(m_Gdi32Handle, "D3DKMTCloseAdapter");
|
||||
m_D3DKMTWaitForVerticalBlankEvent = (PFND3DKMTWAITFORVERTICALBLANKEVENT)GetProcAddress(m_Gdi32Handle, "D3DKMTWaitForVerticalBlankEvent");
|
||||
|
||||
if (m_D3DKMTOpenAdapterFromHdc == nullptr ||
|
||||
m_D3DKMTCloseAdapter == nullptr ||
|
||||
m_D3DKMTWaitForVerticalBlankEvent == nullptr) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Missing required function in gdi32.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SysWMinfo info;
|
||||
|
||||
SDL_VERSION(&info.version);
|
||||
|
||||
if (!SDL_GetWindowWMInfo(window, &info)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"SDL_GetWindowWMInfo() failed: %s",
|
||||
SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Window = info.info.win.window;
|
||||
|
||||
m_Thread = SDL_CreateThread(vsyncThread, "DX Vsync Thread", this);
|
||||
if (m_Thread == nullptr) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
|
|
@ -36,14 +76,95 @@ int DxVsyncSource::vsyncThread(void* context)
|
|||
|
||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
||||
|
||||
D3DKMT_OPENADAPTERFROMHDC openAdapterParams = {};
|
||||
HMONITOR lastMonitor = nullptr;
|
||||
DEVMODEA monitorMode;
|
||||
monitorMode.dmSize = sizeof(monitorMode);
|
||||
|
||||
while (SDL_AtomicGet(&me->m_Stopping) == 0) {
|
||||
// FIXME: We should really use D3DKMTWaitForVerticalBlankEvent() instead!
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=467617
|
||||
// https://chromium.googlesource.com/chromium/src.git/+/c564f2fe339b2b2abb0c8773c90c83215670ea71/gpu/ipc/service/gpu_vsync_provider_win.cc
|
||||
DwmFlush();
|
||||
D3DKMT_WAITFORVERTICALBLANKEVENT waitForVblankEventParams;
|
||||
NTSTATUS status;
|
||||
|
||||
// If the monitor has changed from last time, open the new adapter
|
||||
HMONITOR currentMonitor = MonitorFromWindow(me->m_Window, MONITOR_DEFAULTTONEAREST);
|
||||
if (currentMonitor != lastMonitor) {
|
||||
MONITORINFOEXA monitorInfo = {};
|
||||
monitorInfo.cbSize = sizeof(monitorInfo);
|
||||
if (!GetMonitorInfoA(currentMonitor, &monitorInfo)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"GetMonitorInfo() failed: %d",
|
||||
GetLastError());
|
||||
SDL_Delay(10);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!EnumDisplaySettingsA(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &monitorMode)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"EnumDisplaySettings() failed: %d",
|
||||
GetLastError());
|
||||
SDL_Delay(10);
|
||||
continue;
|
||||
}
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Monitor changed: %s %d Hz",
|
||||
monitorInfo.szDevice,
|
||||
monitorMode.dmDisplayFrequency);
|
||||
|
||||
if (openAdapterParams.hAdapter != 0) {
|
||||
D3DKMT_CLOSEADAPTER closeAdapterParams = {};
|
||||
closeAdapterParams.hAdapter = openAdapterParams.hAdapter;
|
||||
me->m_D3DKMTCloseAdapter(&closeAdapterParams);
|
||||
}
|
||||
|
||||
openAdapterParams.hDc = CreateDCA(nullptr, monitorInfo.szDevice, nullptr, nullptr);
|
||||
if (!openAdapterParams.hDc) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"CreateDC() failed: %d",
|
||||
GetLastError());
|
||||
SDL_Delay(10);
|
||||
continue;
|
||||
}
|
||||
|
||||
status = me->m_D3DKMTOpenAdapterFromHdc(&openAdapterParams);
|
||||
DeleteDC(openAdapterParams.hDc);
|
||||
|
||||
if (status != STATUS_SUCCESS) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"D3DKMTOpenAdapterFromHdc() failed: %x",
|
||||
status);
|
||||
SDL_Delay(10);
|
||||
continue;
|
||||
}
|
||||
|
||||
lastMonitor = currentMonitor;
|
||||
}
|
||||
|
||||
waitForVblankEventParams.hAdapter = openAdapterParams.hAdapter;
|
||||
waitForVblankEventParams.hDevice = 0;
|
||||
waitForVblankEventParams.VidPnSourceId = openAdapterParams.VidPnSourceId;
|
||||
|
||||
status = me->m_D3DKMTWaitForVerticalBlankEvent(&waitForVblankEventParams);
|
||||
if (status != STATUS_SUCCESS) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"D3DKMTWaitForVerticalBlankEvent() failed: %x",
|
||||
status);
|
||||
SDL_Delay(10);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to delay to the middle of the V-sync period to give the frame
|
||||
// from the host time to arrive.
|
||||
SDL_Delay(500 / monitorMode.dmDisplayFrequency);
|
||||
|
||||
me->m_Pacer->vsyncCallback();
|
||||
}
|
||||
|
||||
if (openAdapterParams.hAdapter != 0) {
|
||||
D3DKMT_CLOSEADAPTER closeAdapterParams = {};
|
||||
closeAdapterParams.hAdapter = openAdapterParams.hAdapter;
|
||||
me->m_D3DKMTCloseAdapter(&closeAdapterParams);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,32 @@
|
|||
|
||||
#include "pacer.h"
|
||||
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
// from <D3dkmthk.h>
|
||||
typedef LONG NTSTATUS;
|
||||
typedef UINT D3DKMT_HANDLE;
|
||||
typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||
#define STATUS_GRAPHICS_PRESENT_OCCLUDED ((NTSTATUS)0xC01E0006L)
|
||||
typedef struct _D3DKMT_OPENADAPTERFROMHDC {
|
||||
HDC hDc;
|
||||
D3DKMT_HANDLE hAdapter;
|
||||
LUID AdapterLuid;
|
||||
D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
|
||||
} D3DKMT_OPENADAPTERFROMHDC;
|
||||
typedef struct _D3DKMT_CLOSEADAPTER {
|
||||
D3DKMT_HANDLE hAdapter;
|
||||
} D3DKMT_CLOSEADAPTER;
|
||||
typedef struct _D3DKMT_WAITFORVERTICALBLANKEVENT {
|
||||
D3DKMT_HANDLE hAdapter;
|
||||
D3DKMT_HANDLE hDevice;
|
||||
D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
|
||||
} D3DKMT_WAITFORVERTICALBLANKEVENT;
|
||||
typedef NTSTATUS(APIENTRY* PFND3DKMTOPENADAPTERFROMHDC)(D3DKMT_OPENADAPTERFROMHDC*);
|
||||
typedef NTSTATUS(APIENTRY* PFND3DKMTCLOSEADAPTER)(D3DKMT_CLOSEADAPTER*);
|
||||
typedef NTSTATUS(APIENTRY* PFND3DKMTWAITFORVERTICALBLANKEVENT)(D3DKMT_WAITFORVERTICALBLANKEVENT*);
|
||||
|
||||
class DxVsyncSource : public IVsyncSource
|
||||
{
|
||||
public:
|
||||
|
|
@ -17,4 +43,10 @@ private:
|
|||
Pacer* m_Pacer;
|
||||
SDL_Thread* m_Thread;
|
||||
SDL_atomic_t m_Stopping;
|
||||
HMODULE m_Gdi32Handle;
|
||||
HWND m_Window;
|
||||
|
||||
PFND3DKMTOPENADAPTERFROMHDC m_D3DKMTOpenAdapterFromHdc;
|
||||
PFND3DKMTCLOSEADAPTER m_D3DKMTCloseAdapter;
|
||||
PFND3DKMTWAITFORVERTICALBLANKEVENT m_D3DKMTWaitForVerticalBlankEvent;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue