diff --git a/inc/main.h b/inc/main.h index 385ac18..72ddacb 100644 --- a/inc/main.h +++ b/inc/main.h @@ -126,6 +126,7 @@ typedef struct IDirectDrawImpl BOOL hidecursor; SpeedLimiter ticksLimiter; SpeedLimiter flipLimiter; + SpeedLimiter fpsLimiter; } IDirectDrawImpl; diff --git a/src/main.c b/src/main.c index 0653a1b..9805f8f 100644 --- a/src/main.c +++ b/src/main.c @@ -162,7 +162,7 @@ void LimitGameTicks() { if (ddraw->ticksLimiter.hTimer) { - FILETIME ft; + FILETIME ft = { 0 }; GetSystemTimeAsFileTime(&ft); if (CompareFileTime((FILETIME *)&ddraw->ticksLimiter.dueTime, &ft) == -1) @@ -1499,6 +1499,13 @@ ULONG __stdcall ddraw_Release(IDirectDrawImpl *This) This->flipLimiter.hTimer = NULL; } + if (This->fpsLimiter.hTimer) + { + CancelWaitableTimer(This->fpsLimiter.hTimer); + CloseHandle(This->fpsLimiter.hTimer); + This->fpsLimiter.hTimer = NULL; + } + DeleteCriticalSection(&This->cs); /* restore old wndproc, subsequent ddraw creation will otherwise fail */ diff --git a/src/render.c b/src/render.c index a8baf25..3d475c0 100644 --- a/src/render.c +++ b/src/render.c @@ -25,8 +25,6 @@ #define TEXTURE_COUNT 4 static HGLRC OpenGLContext; -static int MaxFPS; -static DWORD FrameLength; static GLuint MainProgram; static GLuint ScaleProgram; static BOOL GotError; @@ -50,7 +48,7 @@ static BOOL UseOpenGL; static BOOL AdjustAlignment; static HGLRC CreateContext(HDC hdc); -static void SetMaxFPS(int baseMaxFPS); +static void SetMaxFPS(); static void BuildPrograms(); static void CreateTextures(int width, int height); static void InitMainProgram(); @@ -69,7 +67,7 @@ DWORD WINAPI render_main(void) if (OpenGLContext) { OpenGL_Init(); - SetMaxFPS(ddraw->render.maxfps); + SetMaxFPS(); BuildPrograms(); CreateTextures(ddraw->width, ddraw->height); InitMainProgram(); @@ -114,9 +112,11 @@ static HGLRC CreateContext(HDC hdc) return context; } -static void SetMaxFPS(int baseMaxFPS) +static void SetMaxFPS() { - MaxFPS = baseMaxFPS; + int maxFPS = ddraw->render.maxfps; + ddraw->fpsLimiter.tickLengthNs = 0; + ddraw->fpsLimiter.ticklength = 0; if (OpenGL_ExtExists("WGL_EXT_swap_control_tear", ddraw->render.hDC)) { @@ -125,7 +125,7 @@ static void SetMaxFPS(int baseMaxFPS) if (ddraw->vsync) { wglSwapIntervalEXT(-1); - MaxFPS = 1000; + maxFPS = 0; } else wglSwapIntervalEXT(0); @@ -138,21 +138,25 @@ static void SetMaxFPS(int baseMaxFPS) if (ddraw->vsync) { wglSwapIntervalEXT(1); - MaxFPS = 1000; + maxFPS = 0; } else wglSwapIntervalEXT(0); } } - if (MaxFPS < 0) - MaxFPS = ddraw->mode.dmDisplayFrequency; + if (maxFPS < 0) + maxFPS = ddraw->mode.dmDisplayFrequency; - if (MaxFPS >= 1000) - MaxFPS = 0; + if (maxFPS > 1000) + maxFPS = 0; - if (MaxFPS > 0) - FrameLength = 1000.0f / MaxFPS; + if (maxFPS > 0) + { + float len = 1000.0f / maxFPS; + ddraw->fpsLimiter.tickLengthNs = len * 10000; + ddraw->fpsLimiter.ticklength = len + 0.5f; + } } static void BuildPrograms() @@ -543,8 +547,8 @@ static void InitScaleProgram() static void Render() { - DWORD tick_start = 0; - DWORD tick_end = 0; + DWORD tickStart = 0; + DWORD tickEnd = 0; BOOL needsUpdate = FALSE; glViewport( @@ -569,8 +573,8 @@ static void Render() BOOL scaleChanged = FALSE; - if (MaxFPS > 0) - tick_start = timeGetTime(); + if (ddraw->fpsLimiter.ticklength > 0) + tickStart = timeGetTime(); EnterCriticalSection(&ddraw->cs); @@ -785,12 +789,32 @@ static void Render() DrawFrameInfoEnd(); #endif - if (MaxFPS > 0) + if (ddraw->fpsLimiter.ticklength > 0) { - tick_end = timeGetTime(); + if (ddraw->fpsLimiter.hTimer) + { + FILETIME ft = { 0 }; + GetSystemTimeAsFileTime(&ft); - if (tick_end - tick_start < FrameLength) - Sleep(FrameLength - (tick_end - tick_start)); + if (CompareFileTime((FILETIME *)&ddraw->fpsLimiter.dueTime, &ft) == -1) + { + memcpy(&ddraw->fpsLimiter.dueTime, &ft, sizeof(LARGE_INTEGER)); + } + else + { + WaitForSingleObject(ddraw->fpsLimiter.hTimer, ddraw->fpsLimiter.ticklength * 2); + } + + ddraw->fpsLimiter.dueTime.QuadPart += ddraw->fpsLimiter.tickLengthNs; + SetWaitableTimer(ddraw->fpsLimiter.hTimer, &ddraw->fpsLimiter.dueTime, 0, NULL, NULL, FALSE); + } + else + { + tickEnd = timeGetTime(); + + if (tickEnd - tickStart < ddraw->fpsLimiter.ticklength) + Sleep(ddraw->fpsLimiter.ticklength - (tickEnd - tickStart)); + } } } } diff --git a/src/render_d3d9.c b/src/render_d3d9.c index 3dc0d85..6e077ca 100644 --- a/src/render_d3d9.c +++ b/src/render_d3d9.c @@ -19,8 +19,6 @@ static IDirect3DTexture9 *PaletteTex[TEXTURE_COUNT]; static IDirect3DPixelShader9 *PixelShader; static float ScaleW; static float ScaleH; -static int MaxFPS; -static DWORD FrameLength; static int BitsPerPixel; static BOOL CreateResources(); @@ -276,16 +274,22 @@ static BOOL UpdateVertices(BOOL inCutscene, BOOL stretch) static void SetMaxFPS() { - MaxFPS = ddraw->render.maxfps; + int maxFPS = ddraw->render.maxfps; + ddraw->fpsLimiter.tickLengthNs = 0; + ddraw->fpsLimiter.ticklength = 0; - if (MaxFPS < 0) - MaxFPS = ddraw->mode.dmDisplayFrequency; + if (maxFPS < 0) + maxFPS = ddraw->mode.dmDisplayFrequency; - if (MaxFPS >= 1000 || ddraw->vsync) - MaxFPS = 0; + if (maxFPS > 1000 || ddraw->vsync) + maxFPS = 0; - if (MaxFPS > 0) - FrameLength = 1000.0f / MaxFPS; + if (maxFPS > 0) + { + float len = 1000.0f / maxFPS; + ddraw->fpsLimiter.tickLengthNs = len * 10000; + ddraw->fpsLimiter.ticklength = len + 0.5f; + } } DWORD WINAPI render_d3d9_main(void) @@ -312,7 +316,7 @@ DWORD WINAPI render_d3d9_main(void) static int texIndex = 0, palIndex = 0; - if (MaxFPS > 0) + if (ddraw->fpsLimiter.ticklength > 0) tickStart = timeGetTime(); EnterCriticalSection(&ddraw->cs); @@ -416,12 +420,32 @@ DWORD WINAPI render_d3d9_main(void) DrawFrameInfoEnd(); #endif - if (MaxFPS > 0) + if (ddraw->fpsLimiter.ticklength > 0) { - tickEnd = timeGetTime(); + if (ddraw->fpsLimiter.hTimer) + { + FILETIME ft = { 0 }; + GetSystemTimeAsFileTime(&ft); - if (tickEnd - tickStart < FrameLength) - Sleep(FrameLength - (tickEnd - tickStart)); + if (CompareFileTime((FILETIME *)&ddraw->fpsLimiter.dueTime, &ft) == -1) + { + memcpy(&ddraw->fpsLimiter.dueTime, &ft, sizeof(LARGE_INTEGER)); + } + else + { + WaitForSingleObject(ddraw->fpsLimiter.hTimer, ddraw->fpsLimiter.ticklength * 2); + } + + ddraw->fpsLimiter.dueTime.QuadPart += ddraw->fpsLimiter.tickLengthNs; + SetWaitableTimer(ddraw->fpsLimiter.hTimer, &ddraw->fpsLimiter.dueTime, 0, NULL, NULL, FALSE); + } + else + { + tickEnd = timeGetTime(); + + if (tickEnd - tickStart < ddraw->fpsLimiter.ticklength) + Sleep(ddraw->fpsLimiter.ticklength - (tickEnd - tickStart)); + } } } return 0; diff --git a/src/render_soft.c b/src/render_soft.c index e438815..82feb12 100644 --- a/src/render_soft.c +++ b/src/render_soft.c @@ -40,25 +40,30 @@ DWORD WINAPI render_soft_main(void) else Sleep(500); - int maxFPS = ddraw->render.maxfps; - DWORD frameLength = 0; DWORD lastTick = 0; + int maxFPS = ddraw->render.maxfps; + ddraw->fpsLimiter.tickLengthNs = 0; + ddraw->fpsLimiter.ticklength = 0; if (maxFPS < 0) maxFPS = ddraw->mode.dmDisplayFrequency; - if (maxFPS >= 1000) + if (maxFPS > 1000) maxFPS = 0; if (maxFPS > 0) - frameLength = 1000.0f / maxFPS; + { + float len = 1000.0f / maxFPS; + ddraw->fpsLimiter.tickLengthNs = len * 10000; + ddraw->fpsLimiter.ticklength = len + 0.5f; + } while (ddraw->render.run && WaitForSingleObject(ddraw->render.sem, INFINITE) != WAIT_FAILED) { - if (maxFPS > 0) + if (ddraw->fpsLimiter.ticklength > 0) { DWORD curTick = timeGetTime(); - if (lastTick + frameLength > curTick) + if (lastTick + ddraw->fpsLimiter.ticklength > curTick) { ReleaseSemaphore(ddraw->render.sem, 1, NULL); SetEvent(ddraw->render.ev); diff --git a/src/settings.c b/src/settings.c index eb85a87..93a7181 100644 --- a/src/settings.c +++ b/src/settings.c @@ -41,12 +41,18 @@ void Settings_Load() ddraw->noactivateapp = GetBool("noactivateapp", FALSE); ddraw->vhack = GetBool("vhack", FALSE); - ddraw->render.maxfps = GetInt("maxfps", 125); WindowRect.right = GetInt("width", 0); WindowRect.bottom = GetInt("height", 0); WindowRect.left = GetInt("posX", -32000); WindowRect.top = GetInt("posY", -32000); + ddraw->render.maxfps = GetInt("maxfps", 125); + if (ddraw->render.maxfps) + { + ddraw->fpsLimiter.hTimer = CreateWaitableTimer(NULL, TRUE, NULL); + //can't fully set it up here due to missing ddraw->mode.dmDisplayFrequency + } + int maxTicks = GetInt("maxgameticks", 0); if (maxTicks > 0 && maxTicks <= 1000) {