mirror of
https://github.com/FunkyFr3sh/cnc-ddraw.git
synced 2025-03-15 06:04:49 +01:00
use waitable timers for better precision (limit game ticks / flip wait)
This commit is contained in:
parent
093486ec7f
commit
435854456b
2
ddraw.rc
2
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)
|
||||
|
13
inc/main.h
13
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;
|
||||
|
||||
|
56
src/main.c
56
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 */
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user