1
0
mirror of https://github.com/FunkyFr3sh/cnc-ddraw.git synced 2025-03-24 17:49:52 +01:00

use waitable timers for better precision (limit game ticks / flip wait)

This commit is contained in:
FunkyFr3sh 2018-11-29 17:45:10 +01:00
parent 093486ec7f
commit 435854456b
5 changed files with 104 additions and 28 deletions

View File

@ -4,7 +4,7 @@
#define VERSION_MAJOR 1 #define VERSION_MAJOR 1
#define VERSION_MINOR 3 #define VERSION_MINOR 3
#define VERSION_BUILD 2 #define VERSION_BUILD 2
#define VERSION_REVISION 2 #define VERSION_REVISION 3
#define VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_REVISION #define VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_REVISION
#define VERSION_STRING ver_str(VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_REVISION) #define VERSION_STRING ver_str(VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_REVISION)

View File

@ -47,6 +47,15 @@ struct IDirectDrawSurfaceImpl;
extern struct IDirectDrawImpl *ddraw; extern struct IDirectDrawImpl *ddraw;
typedef struct SpeedLimiter
{
DWORD ticklength;
LONGLONG tickLengthNs;
HANDLE hTimer;
LARGE_INTEGER dueTime;
BOOL useBltOrFlip;
} SpeedLimiter;
typedef struct IDirectDrawImpl typedef struct IDirectDrawImpl
{ {
struct IDirectDrawImplVtbl *lpVtbl; struct IDirectDrawImplVtbl *lpVtbl;
@ -113,10 +122,10 @@ typedef struct IDirectDrawImpl
char shader[MAX_PATH]; char shader[MAX_PATH];
BOOL wine; BOOL wine;
LONG minimized; LONG minimized;
DWORD ticklength;
BOOL altenter; BOOL altenter;
BOOL hidecursor; BOOL hidecursor;
BOOL limitTicksOnBltOrFlip; SpeedLimiter ticksLimiter;
SpeedLimiter flipLimiter;
} IDirectDrawImpl; } IDirectDrawImpl;

View File

@ -160,20 +160,40 @@ BOOL detect_cutscene()
void LimitGameTicks() void LimitGameTicks()
{ {
static DWORD nextGameTick; if (ddraw->ticksLimiter.hTimer)
if (!nextGameTick)
{ {
nextGameTick = timeGetTime(); FILETIME ft;
return; GetSystemTimeAsFileTime(&ft);
}
nextGameTick += ddraw->ticklength;
DWORD tickCount = timeGetTime();
int sleepTime = nextGameTick - tickCount; if (CompareFileTime((FILETIME *)&ddraw->ticksLimiter.dueTime, &ft) == -1)
if (sleepTime <= 0 || sleepTime > ddraw->ticklength) {
nextGameTick = tickCount; 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 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) HRESULT __stdcall ddraw_Compact(IDirectDrawImpl *This)
@ -1465,6 +1485,20 @@ ULONG __stdcall ddraw_Release(IDirectDrawImpl *This)
ddraw->render.ev = NULL; 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); DeleteCriticalSection(&This->cs);
/* restore old wndproc, subsequent ddraw creation will otherwise fail */ /* restore old wndproc, subsequent ddraw creation will otherwise fail */

View File

@ -49,7 +49,18 @@ void Settings_Load()
int maxTicks = GetInt("maxgameticks", 0); int maxTicks = GetInt("maxgameticks", 0);
if (maxTicks > 0 && maxTicks <= 1000) 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))) if ((ddraw->fullscreen = GetBool("fullscreen", FALSE)))
WindowRect.left = WindowRect.top = -32000; WindowRect.left = WindowRect.top = -32000;

View File

@ -448,9 +448,9 @@ HRESULT __stdcall ddraw_surface_Blt(IDirectDrawSurfaceImpl *This, LPRECT lpDestR
SwitchToThread(); SwitchToThread();
} }
if (ddraw->ticklength > 0) if (ddraw->ticksLimiter.ticklength > 0)
{ {
ddraw->limitTicksOnBltOrFlip = TRUE; ddraw->ticksLimiter.useBltOrFlip = TRUE;
LimitGameTicks(); LimitGameTicks();
} }
} }
@ -661,6 +661,10 @@ HRESULT __stdcall ddraw_surface_Flip(IDirectDrawSurfaceImpl *This, LPDIRECTDRAWS
if(This->caps & DDSCAPS_PRIMARYSURFACE && ddraw->render.run) if(This->caps & DDSCAPS_PRIMARYSURFACE && ddraw->render.run)
{ {
FILETIME lastFlipFT = { 0 };
if (ddraw->flipLimiter.hTimer)
GetSystemTimeAsFileTime(&lastFlipFT);
This->lastFlipTick = timeGetTime(); This->lastFlipTick = timeGetTime();
InterlockedExchange(&ddraw->render.surfaceUpdated, TRUE); InterlockedExchange(&ddraw->render.surfaceUpdated, TRUE);
@ -679,21 +683,39 @@ HRESULT __stdcall ddraw_surface_Flip(IDirectDrawSurfaceImpl *This, LPDIRECTDRAWS
if (flags & DDFLIP_WAIT) if (flags & DDFLIP_WAIT)
{ {
DWORD tick = This->lastFlipTick; if (ddraw->flipLimiter.hTimer)
while (tick % 17) tick++; {
int sleepTime = tick - This->lastFlipTick; 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; SetWaitableTimer(ddraw->flipLimiter.hTimer, &ddraw->flipLimiter.dueTime, 0, NULL, NULL, FALSE);
if (renderTime > 0) WaitForSingleObject(ddraw->flipLimiter.hTimer, ddraw->flipLimiter.ticklength * 2);
sleepTime -= renderTime; }
}
else
{
DWORD tick = This->lastFlipTick;
while (tick % 17) tick++;
int sleepTime = tick - This->lastFlipTick;
if (sleepTime > 0 && sleepTime <= 17) int renderTime = timeGetTime() - This->lastFlipTick;
Sleep(sleepTime); 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(); LimitGameTicks();
} }
} }
@ -962,7 +984,7 @@ HRESULT __stdcall ddraw_surface_Unlock(IDirectDrawSurfaceImpl *This, LPVOID lpRe
InterlockedExchange(&ddraw->render.surfaceUpdated, TRUE); InterlockedExchange(&ddraw->render.surfaceUpdated, TRUE);
ReleaseSemaphore(ddraw->render.sem, 1, NULL); ReleaseSemaphore(ddraw->render.sem, 1, NULL);
if (ddraw->ticklength > 0 && !ddraw->limitTicksOnBltOrFlip) if (ddraw->ticksLimiter.ticklength > 0 && !ddraw->ticksLimiter.useBltOrFlip)
LimitGameTicks(); LimitGameTicks();
} }