From 97ca023f0955109867fcfdb29db8ccdc3d2d0d07 Mon Sep 17 00:00:00 2001
From: FunkyFr3sh <cc.red.alert.1@googlemail.com>
Date: Thu, 29 Nov 2018 21:26:43 +0100
Subject: [PATCH] use waitable timer also for fps limiter

---
 inc/main.h        |  1 +
 src/main.c        |  9 ++++++-
 src/render.c      | 68 ++++++++++++++++++++++++++++++++---------------
 src/render_d3d9.c | 52 ++++++++++++++++++++++++++----------
 src/render_soft.c | 17 +++++++-----
 src/settings.c    |  8 +++++-
 6 files changed, 111 insertions(+), 44 deletions(-)

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)
     {