From 435854456b65adb7e79fea155be4168e9552f385 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Thu, 29 Nov 2018 17:45:10 +0100 Subject: [PATCH] use waitable timers for better precision (limit game ticks / flip wait) --- ddraw.rc | 2 +- inc/main.h | 13 ++++++++++-- src/main.c | 56 ++++++++++++++++++++++++++++++++++++++++---------- src/settings.c | 13 +++++++++++- src/surface.c | 48 +++++++++++++++++++++++++++++++------------ 5 files changed, 104 insertions(+), 28 deletions(-) diff --git a/ddraw.rc b/ddraw.rc index 38ba1cd..d5561a3 100644 --- a/ddraw.rc +++ b/ddraw.rc @@ -4,7 +4,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_BUILD 2 -#define VERSION_REVISION 2 +#define VERSION_REVISION 3 #define VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_REVISION #define VERSION_STRING ver_str(VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_REVISION) diff --git a/inc/main.h b/inc/main.h index d5ca136..385ac18 100644 --- a/inc/main.h +++ b/inc/main.h @@ -47,6 +47,15 @@ struct IDirectDrawSurfaceImpl; extern struct IDirectDrawImpl *ddraw; +typedef struct SpeedLimiter +{ + DWORD ticklength; + LONGLONG tickLengthNs; + HANDLE hTimer; + LARGE_INTEGER dueTime; + BOOL useBltOrFlip; +} SpeedLimiter; + typedef struct IDirectDrawImpl { struct IDirectDrawImplVtbl *lpVtbl; @@ -113,10 +122,10 @@ typedef struct IDirectDrawImpl char shader[MAX_PATH]; BOOL wine; LONG minimized; - DWORD ticklength; BOOL altenter; BOOL hidecursor; - BOOL limitTicksOnBltOrFlip; + SpeedLimiter ticksLimiter; + SpeedLimiter flipLimiter; } IDirectDrawImpl; diff --git a/src/main.c b/src/main.c index 1f6ff92..0653a1b 100644 --- a/src/main.c +++ b/src/main.c @@ -160,20 +160,40 @@ BOOL detect_cutscene() void LimitGameTicks() { - static DWORD nextGameTick; - if (!nextGameTick) + if (ddraw->ticksLimiter.hTimer) { - nextGameTick = timeGetTime(); - return; - } - nextGameTick += ddraw->ticklength; - DWORD tickCount = timeGetTime(); + FILETIME ft; + GetSystemTimeAsFileTime(&ft); - int sleepTime = nextGameTick - tickCount; - if (sleepTime <= 0 || sleepTime > ddraw->ticklength) - nextGameTick = tickCount; + if (CompareFileTime((FILETIME *)&ddraw->ticksLimiter.dueTime, &ft) == -1) + { + memcpy(&ddraw->ticksLimiter.dueTime, &ft, sizeof(LARGE_INTEGER)); + } + else + { + WaitForSingleObject(ddraw->ticksLimiter.hTimer, ddraw->ticksLimiter.ticklength * 2); + } + + ddraw->ticksLimiter.dueTime.QuadPart += ddraw->ticksLimiter.tickLengthNs; + SetWaitableTimer(ddraw->ticksLimiter.hTimer, &ddraw->ticksLimiter.dueTime, 0, NULL, NULL, FALSE); + } else - Sleep(sleepTime); + { + static DWORD nextGameTick; + if (!nextGameTick) + { + nextGameTick = timeGetTime(); + return; + } + nextGameTick += ddraw->ticksLimiter.ticklength; + DWORD tickCount = timeGetTime(); + + int sleepTime = nextGameTick - tickCount; + if (sleepTime <= 0 || sleepTime > ddraw->ticksLimiter.ticklength) + nextGameTick = tickCount; + else + Sleep(sleepTime); + } } HRESULT __stdcall ddraw_Compact(IDirectDrawImpl *This) @@ -1465,6 +1485,20 @@ ULONG __stdcall ddraw_Release(IDirectDrawImpl *This) ddraw->render.ev = NULL; } + if (This->ticksLimiter.hTimer) + { + CancelWaitableTimer(This->ticksLimiter.hTimer); + CloseHandle(This->ticksLimiter.hTimer); + This->ticksLimiter.hTimer = NULL; + } + + if (This->flipLimiter.hTimer) + { + CancelWaitableTimer(This->flipLimiter.hTimer); + CloseHandle(This->flipLimiter.hTimer); + This->flipLimiter.hTimer = NULL; + } + DeleteCriticalSection(&This->cs); /* restore old wndproc, subsequent ddraw creation will otherwise fail */ diff --git a/src/settings.c b/src/settings.c index ddd538a..eb85a87 100644 --- a/src/settings.c +++ b/src/settings.c @@ -49,7 +49,18 @@ void Settings_Load() int maxTicks = GetInt("maxgameticks", 0); if (maxTicks > 0 && maxTicks <= 1000) - ddraw->ticklength = 1000.0f / maxTicks + 0.5f; + { + ddraw->ticksLimiter.hTimer = CreateWaitableTimer(NULL, TRUE, NULL); + float len = 1000.0f / maxTicks; + ddraw->ticksLimiter.tickLengthNs = len * 10000; + ddraw->ticksLimiter.ticklength = len + 0.5f; + } + + //always using 60 fps for flip... + ddraw->flipLimiter.hTimer = CreateWaitableTimer(NULL, TRUE, NULL); + float flipLen = 1000.0f / 60; + ddraw->flipLimiter.tickLengthNs = flipLen * 10000; + ddraw->flipLimiter.ticklength = flipLen + 0.5f; if ((ddraw->fullscreen = GetBool("fullscreen", FALSE))) WindowRect.left = WindowRect.top = -32000; diff --git a/src/surface.c b/src/surface.c index 7e6a6c2..4f6a33f 100644 --- a/src/surface.c +++ b/src/surface.c @@ -448,9 +448,9 @@ HRESULT __stdcall ddraw_surface_Blt(IDirectDrawSurfaceImpl *This, LPRECT lpDestR SwitchToThread(); } - if (ddraw->ticklength > 0) + if (ddraw->ticksLimiter.ticklength > 0) { - ddraw->limitTicksOnBltOrFlip = TRUE; + ddraw->ticksLimiter.useBltOrFlip = TRUE; LimitGameTicks(); } } @@ -661,6 +661,10 @@ HRESULT __stdcall ddraw_surface_Flip(IDirectDrawSurfaceImpl *This, LPDIRECTDRAWS if(This->caps & DDSCAPS_PRIMARYSURFACE && ddraw->render.run) { + FILETIME lastFlipFT = { 0 }; + if (ddraw->flipLimiter.hTimer) + GetSystemTimeAsFileTime(&lastFlipFT); + This->lastFlipTick = timeGetTime(); InterlockedExchange(&ddraw->render.surfaceUpdated, TRUE); @@ -679,21 +683,39 @@ HRESULT __stdcall ddraw_surface_Flip(IDirectDrawSurfaceImpl *This, LPDIRECTDRAWS if (flags & DDFLIP_WAIT) { - DWORD tick = This->lastFlipTick; - while (tick % 17) tick++; - int sleepTime = tick - This->lastFlipTick; + if (ddraw->flipLimiter.hTimer) + { + if (!ddraw->flipLimiter.dueTime.QuadPart) + { + memcpy(&ddraw->flipLimiter.dueTime, &lastFlipFT, sizeof(LARGE_INTEGER)); + } + else + { + while (CompareFileTime((FILETIME *)&ddraw->flipLimiter.dueTime, &lastFlipFT) == -1) + ddraw->flipLimiter.dueTime.QuadPart += ddraw->flipLimiter.tickLengthNs; - int renderTime = timeGetTime() - This->lastFlipTick; - if (renderTime > 0) - sleepTime -= renderTime; + SetWaitableTimer(ddraw->flipLimiter.hTimer, &ddraw->flipLimiter.dueTime, 0, NULL, NULL, FALSE); + WaitForSingleObject(ddraw->flipLimiter.hTimer, ddraw->flipLimiter.ticklength * 2); + } + } + else + { + DWORD tick = This->lastFlipTick; + while (tick % 17) tick++; + int sleepTime = tick - This->lastFlipTick; - if (sleepTime > 0 && sleepTime <= 17) - Sleep(sleepTime); + int renderTime = timeGetTime() - This->lastFlipTick; + if (renderTime > 0) + sleepTime -= renderTime; + + if (sleepTime > 0 && sleepTime <= 17) + Sleep(sleepTime); + } } - if (ddraw->ticklength > 0) + if (ddraw->ticksLimiter.ticklength > 0) { - ddraw->limitTicksOnBltOrFlip = TRUE; + ddraw->ticksLimiter.useBltOrFlip = TRUE; LimitGameTicks(); } } @@ -962,7 +984,7 @@ HRESULT __stdcall ddraw_surface_Unlock(IDirectDrawSurfaceImpl *This, LPVOID lpRe InterlockedExchange(&ddraw->render.surfaceUpdated, TRUE); ReleaseSemaphore(ddraw->render.sem, 1, NULL); - if (ddraw->ticklength > 0 && !ddraw->limitTicksOnBltOrFlip) + if (ddraw->ticksLimiter.ticklength > 0 && !ddraw->ticksLimiter.useBltOrFlip) LimitGameTicks(); }