diff --git a/Makefile b/Makefile
index 0af89b2..8d7aa61 100644
--- a/Makefile
+++ b/Makefile
@@ -33,6 +33,7 @@ FILES = src/IDirect3D/IDirect3D.c \
src/dllmain.c \
src/wndproc.c \
src/utils.c \
+ src/fps_limiter.c \
src/opengl_utils.c
all:
diff --git a/cnc-ddraw.vcxproj b/cnc-ddraw.vcxproj
index aea7ddb..2200a1a 100644
--- a/cnc-ddraw.vcxproj
+++ b/cnc-ddraw.vcxproj
@@ -32,6 +32,7 @@
+
@@ -63,6 +64,7 @@
+
diff --git a/cnc-ddraw.vcxproj.filters b/cnc-ddraw.vcxproj.filters
index 5aef37b..e250556 100644
--- a/cnc-ddraw.vcxproj.filters
+++ b/cnc-ddraw.vcxproj.filters
@@ -147,6 +147,9 @@
Source Files\IDirectDraw
+
+ Source Files
+
@@ -248,6 +251,9 @@
Header Files
+
+ Header Files
+
diff --git a/inc/dd.h b/inc/dd.h
index d18cc70..e7ecf2f 100644
--- a/inc/dd.h
+++ b/inc/dd.h
@@ -118,7 +118,6 @@ typedef struct cnc_ddraw
BOOL show_driver_warning;
speed_limiter ticks_limiter;
speed_limiter flip_limiter;
- speed_limiter fps_limiter;
} cnc_ddraw;
diff --git a/inc/fps_limiter.h b/inc/fps_limiter.h
new file mode 100644
index 0000000..e2a8a10
--- /dev/null
+++ b/inc/fps_limiter.h
@@ -0,0 +1,52 @@
+#ifndef FPS_LIMITER_H
+#define FPS_LIMITER_H
+
+#include
+#include
+
+
+typedef struct _D3DKMT_WAITFORVERTICALBLANKEVENT {
+ UINT hAdapter;
+ UINT hDevice;
+ UINT VidPnSourceId;
+} D3DKMT_WAITFORVERTICALBLANKEVENT;
+
+typedef struct _D3DKMT_OPENADAPTERFROMHDC {
+ HDC hDc;
+ UINT hAdapter;
+ LUID AdapterLuid;
+ UINT VidPnSourceId;
+} D3DKMT_OPENADAPTERFROMHDC;
+
+typedef struct _D3DKMT_CLOSEADAPTER {
+ UINT hAdapter;
+} D3DKMT_CLOSEADAPTER;
+
+typedef struct fps_limiter
+{
+ DWORD tick_start;
+ DWORD tick_end;
+ DWORD tick_length;
+ LONGLONG tick_length_ns;
+ HANDLE htimer;
+ LARGE_INTEGER due_time;
+ D3DKMT_WAITFORVERTICALBLANKEVENT vblank_event;
+ D3DKMT_OPENADAPTERFROMHDC adapter;
+ D3DKMT_CLOSEADAPTER close_adapter;
+ HRESULT(WINAPI* DwmFlush)(VOID);
+ HRESULT(WINAPI* DwmIsCompositionEnabled)(BOOL*);
+ NTSTATUS(WINAPI* D3DKMTWaitForVerticalBlankEvent)(const D3DKMT_WAITFORVERTICALBLANKEVENT* Arg1);
+ NTSTATUS(WINAPI* D3DKMTOpenAdapterFromHdc)(D3DKMT_OPENADAPTERFROMHDC* Arg1);
+ NTSTATUS(WINAPI* D3DKMTCloseAdapter)(D3DKMT_CLOSEADAPTER* Arg1);
+ BOOL got_adapter;
+} fps_limiter;
+
+extern fps_limiter g_fpsl;
+
+void fpsl_init();
+BOOL fpsl_wait_for_vblank();
+BOOL fpsl_dwn_is_enabled();
+void fpsl_frame_start();
+void fpsl_frame_end();
+
+#endif
diff --git a/src/config.c b/src/config.c
index e1fbaeb..24bc298 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1,7 +1,7 @@
-#define WIN32_LEAN_AND_MEAN
#include
#include
#include
+#include "fps_limiter.h"
#include "config.h"
#include "dd.h"
#include "render_d3d9.h"
@@ -78,7 +78,7 @@ void cfg_load()
}
if (g_ddraw->accurate_timers || g_ddraw->vsync)
- g_ddraw->fps_limiter.htimer = CreateWaitableTimer(NULL, TRUE, NULL);
+ g_fpsl.htimer = CreateWaitableTimer(NULL, TRUE, NULL);
//can't fully set it up here due to missing g_ddraw->mode.dmDisplayFrequency
int max_ticks = cfg_get_int("maxgameticks", 0);
diff --git a/src/dd.c b/src/dd.c
index 700fb25..53aa93b 100644
--- a/src/dd.c
+++ b/src/dd.c
@@ -9,6 +9,7 @@
#include "render_d3d9.h"
#include "render_gdi.h"
#include "render_ogl.h"
+#include "fps_limiter.h"
#include "debug.h"
#include "utils.h"
@@ -817,11 +818,11 @@ ULONG dd_Release()
g_ddraw->flip_limiter.htimer = NULL;
}
- if (g_ddraw->fps_limiter.htimer)
+ if (g_fpsl.htimer)
{
- CancelWaitableTimer(g_ddraw->fps_limiter.htimer);
- CloseHandle(g_ddraw->fps_limiter.htimer);
- g_ddraw->fps_limiter.htimer = NULL;
+ CancelWaitableTimer(g_fpsl.htimer);
+ CloseHandle(g_fpsl.htimer);
+ g_fpsl.htimer = NULL;
}
DeleteCriticalSection(&g_ddraw->cs);
diff --git a/src/fps_limiter.c b/src/fps_limiter.c
new file mode 100644
index 0000000..14019be
--- /dev/null
+++ b/src/fps_limiter.c
@@ -0,0 +1,140 @@
+#include
+#include "fps_limiter.h"
+#include "dd.h"
+#include "debug.h"
+
+fps_limiter g_fpsl;
+
+void fpsl_init()
+{
+ int max_fps = g_ddraw->render.maxfps;
+
+ g_fpsl.tick_length_ns = 0;
+ g_fpsl.tick_length = 0;
+
+ if (max_fps < 0 || g_ddraw->vsync)
+ max_fps = g_ddraw->mode.dmDisplayFrequency;
+
+ if (max_fps > 1000)
+ max_fps = 0;
+
+ if (max_fps > 0)
+ {
+ float len = 1000.0f / max_fps;
+ g_fpsl.tick_length_ns = len * 10000;
+ g_fpsl.tick_length = len;// + 0.5f;
+ }
+
+ if (g_fpsl.got_adapter && g_fpsl.D3DKMTCloseAdapter)
+ {
+ g_fpsl.got_adapter = FALSE;
+ g_fpsl.close_adapter.hAdapter = g_fpsl.adapter.hAdapter;
+ g_fpsl.D3DKMTCloseAdapter(&g_fpsl.close_adapter);
+ }
+
+ g_fpsl.DwmFlush =
+ (HRESULT(WINAPI*)(VOID))GetProcAddress(GetModuleHandleA("Dwmapi.dll"), "DwmFlush");
+
+ g_fpsl.DwmIsCompositionEnabled =
+ (HRESULT(WINAPI*)(BOOL*))GetProcAddress(GetModuleHandleA("Dwmapi.dll"), "DwmIsCompositionEnabled");
+
+ g_fpsl.D3DKMTWaitForVerticalBlankEvent =
+ (NTSTATUS(WINAPI*)(const D3DKMT_WAITFORVERTICALBLANKEVENT * Arg1))
+ GetProcAddress(GetModuleHandleA("gdi32.dll"), "D3DKMTWaitForVerticalBlankEvent");
+
+ g_fpsl.D3DKMTOpenAdapterFromHdc =
+ (NTSTATUS(WINAPI*)(D3DKMT_OPENADAPTERFROMHDC * Arg1))
+ GetProcAddress(GetModuleHandleA("gdi32.dll"), "D3DKMTOpenAdapterFromHdc");
+
+ g_fpsl.D3DKMTCloseAdapter =
+ (NTSTATUS(WINAPI*)(D3DKMT_CLOSEADAPTER * Arg1))
+ GetProcAddress(GetModuleHandleA("gdi32.dll"), "D3DKMTCloseAdapter");
+}
+
+BOOL fpsl_wait_for_vblank()
+{
+ if (g_fpsl.D3DKMTOpenAdapterFromHdc && !g_fpsl.got_adapter)
+ {
+ g_fpsl.adapter.hDc = g_ddraw->render.hdc;
+
+ if (g_fpsl.D3DKMTOpenAdapterFromHdc(&g_fpsl.adapter) == 0)
+ {
+ g_fpsl.vblank_event.hAdapter = g_fpsl.adapter.hAdapter;
+ g_fpsl.got_adapter = TRUE;
+ }
+ }
+
+ if (g_fpsl.got_adapter && g_fpsl.D3DKMTWaitForVerticalBlankEvent)
+ {
+ return g_fpsl.D3DKMTWaitForVerticalBlankEvent(&g_fpsl.vblank_event) == 0;
+ }
+
+ return FALSE;
+}
+
+BOOL fpsl_dwn_is_enabled()
+{
+ BOOL dwm_enabled = FALSE;
+
+ if (g_fpsl.DwmIsCompositionEnabled)
+ g_fpsl.DwmIsCompositionEnabled(&dwm_enabled);
+
+ return dwm_enabled;
+}
+
+void fpsl_frame_start()
+{
+ if (g_fpsl.tick_length > 0)
+ g_fpsl.tick_start = timeGetTime();
+}
+
+void fpsl_frame_end()
+{
+ if (g_ddraw->render.maxfps < 0 || g_ddraw->vsync)
+ {
+ if (fpsl_dwn_is_enabled() && g_fpsl.DwmFlush && SUCCEEDED(g_fpsl.DwmFlush()))
+ return;
+
+ if (fpsl_wait_for_vblank())
+ return;
+ }
+
+ if (g_fpsl.tick_length > 0)
+ {
+ if (g_fpsl.htimer)
+ {
+ if (g_ddraw->vsync)
+ {
+ WaitForSingleObject(g_fpsl.htimer, g_fpsl.tick_length * 2);
+ LARGE_INTEGER due_time = { .QuadPart = -g_fpsl.tick_length_ns };
+ SetWaitableTimer(g_fpsl.htimer, &due_time, 0, NULL, NULL, FALSE);
+ }
+ else
+ {
+ FILETIME ft = { 0 };
+ GetSystemTimeAsFileTime(&ft);
+
+ if (CompareFileTime((FILETIME*)&g_fpsl.due_time, &ft) == -1)
+ {
+ memcpy(&g_fpsl.due_time, &ft, sizeof(LARGE_INTEGER));
+ }
+ else
+ {
+ WaitForSingleObject(g_fpsl.htimer, g_fpsl.tick_length * 2);
+ }
+
+ g_fpsl.due_time.QuadPart += g_fpsl.tick_length_ns;
+ SetWaitableTimer(g_fpsl.htimer, &g_fpsl.due_time, 0, NULL, NULL, FALSE);
+ }
+ }
+ else
+ {
+ g_fpsl.tick_end = timeGetTime();
+
+ if (g_fpsl.tick_end - g_fpsl.tick_start < g_fpsl.tick_length)
+ {
+ Sleep(g_fpsl.tick_length - (g_fpsl.tick_end - g_fpsl.tick_start));
+ }
+ }
+ }
+}
diff --git a/src/render_d3d9.c b/src/render_d3d9.c
index 5882d0e..6c65773 100644
--- a/src/render_d3d9.c
+++ b/src/render_d3d9.c
@@ -1,6 +1,7 @@
#include
#include
#include
+#include "fps_limiter.h"
#include "dd.h"
#include "ddsurface.h"
#include "d3d9shader.h"
@@ -13,7 +14,6 @@
static BOOL d3d9_create_resouces();
static BOOL d3d9_set_states();
static BOOL d3d9_update_vertices(BOOL in_cutscene, BOOL stretch);
-static void d3d9_set_max_fps();
static d3d9_renderer g_d3d9;
@@ -305,35 +305,12 @@ static BOOL d3d9_update_vertices(BOOL in_cutscene, BOOL stretch)
return FALSE;
}
-static void d3d9_set_max_fps()
-{
- int max_fps = g_ddraw->render.maxfps;
-
- g_ddraw->fps_limiter.tick_length_ns = 0;
- g_ddraw->fps_limiter.tick_length = 0;
-
- if (max_fps < 0 || g_ddraw->vsync)
- max_fps = g_ddraw->mode.dmDisplayFrequency;
-
- if (max_fps > 1000)
- max_fps = 0;
-
- if (max_fps > 0)
- {
- float len = 1000.0f / max_fps;
- g_ddraw->fps_limiter.tick_length_ns = len * 10000;
- g_ddraw->fps_limiter.tick_length = len;// + 0.5f;
- }
-}
-
DWORD WINAPI d3d9_render_main(void)
{
Sleep(500);
- d3d9_set_max_fps();
+ fpsl_init();
- DWORD tick_start = 0;
- DWORD tick_end = 0;
BOOL needs_update = FALSE;
DWORD timeout = g_ddraw->render.minfps > 0 ? g_ddraw->render.minfps_tick_len : 200;
@@ -347,8 +324,7 @@ DWORD WINAPI d3d9_render_main(void)
static int tex_index = 0, palIndex = 0;
- if (g_ddraw->fps_limiter.tick_length > 0)
- tick_start = timeGetTime();
+ fpsl_frame_start();
EnterCriticalSection(&g_ddraw->cs);
@@ -456,44 +432,7 @@ DWORD WINAPI d3d9_render_main(void)
dbg_draw_frame_info_end();
#endif
- if (g_ddraw->fps_limiter.tick_length > 0)
- {
- if (g_ddraw->fps_limiter.htimer)
- {
- if (g_ddraw->vsync)
- {
- WaitForSingleObject(g_ddraw->fps_limiter.htimer, g_ddraw->fps_limiter.tick_length * 2);
- LARGE_INTEGER due_time = { .QuadPart = -g_ddraw->fps_limiter.tick_length_ns };
- SetWaitableTimer(g_ddraw->fps_limiter.htimer, &due_time, 0, NULL, NULL, FALSE);
- }
- else
- {
- FILETIME ft = { 0 };
- GetSystemTimeAsFileTime(&ft);
-
- if (CompareFileTime((FILETIME *)&g_ddraw->fps_limiter.due_time, &ft) == -1)
- {
- memcpy(&g_ddraw->fps_limiter.due_time, &ft, sizeof(LARGE_INTEGER));
- }
- else
- {
- WaitForSingleObject(g_ddraw->fps_limiter.htimer, g_ddraw->fps_limiter.tick_length * 2);
- }
-
- g_ddraw->fps_limiter.due_time.QuadPart += g_ddraw->fps_limiter.tick_length_ns;
- SetWaitableTimer(g_ddraw->fps_limiter.htimer, &g_ddraw->fps_limiter.due_time, 0, NULL, NULL, FALSE);
- }
- }
- else
- {
- tick_end = timeGetTime();
-
- if (tick_end - tick_start < g_ddraw->fps_limiter.tick_length)
- {
- Sleep(g_ddraw->fps_limiter.tick_length - (tick_end - tick_start));
- }
- }
- }
+ fpsl_frame_end();
}
return 0;
}
diff --git a/src/render_gdi.c b/src/render_gdi.c
index ef7169f..1c04bb9 100644
--- a/src/render_gdi.c
+++ b/src/render_gdi.c
@@ -1,5 +1,6 @@
#include
#include
+#include "fps_limiter.h"
#include "dd.h"
#include "ddsurface.h"
#include "opengl_utils.h"
@@ -30,26 +31,7 @@ DWORD WINAPI gdi_render_main(void)
Sleep(500);
- DWORD tick_start = 0;
- DWORD tick_end = 0;
-
- int max_fps = g_ddraw->render.maxfps;
-
- g_ddraw->fps_limiter.tick_length_ns = 0;
- g_ddraw->fps_limiter.tick_length = 0;
-
- if (max_fps < 0)
- max_fps = g_ddraw->mode.dmDisplayFrequency;
-
- if (max_fps > 1000)
- max_fps = 0;
-
- if (max_fps > 0)
- {
- float len = 1000.0f / max_fps;
- g_ddraw->fps_limiter.tick_length_ns = len * 10000;
- g_ddraw->fps_limiter.tick_length = len + (g_ddraw->accurate_timers ? 0.5f : 0.0f);
- }
+ fpsl_init();
DWORD timeout = g_ddraw->render.minfps > 0 ? g_ddraw->render.minfps_tick_len : INFINITE;
@@ -60,8 +42,7 @@ DWORD WINAPI gdi_render_main(void)
dbg_draw_frame_info_start();
#endif
- if (g_ddraw->fps_limiter.tick_length > 0)
- tick_start = timeGetTime();
+ fpsl_frame_start();
EnterCriticalSection(&g_ddraw->cs);
@@ -155,35 +136,7 @@ DWORD WINAPI gdi_render_main(void)
dbg_draw_frame_info_end();
#endif
- if (g_ddraw->fps_limiter.tick_length > 0)
- {
- if (g_ddraw->fps_limiter.htimer)
- {
- FILETIME ft = { 0 };
- GetSystemTimeAsFileTime(&ft);
-
- if (CompareFileTime((FILETIME*)&g_ddraw->fps_limiter.due_time, &ft) == -1)
- {
- memcpy(&g_ddraw->fps_limiter.due_time, &ft, sizeof(LARGE_INTEGER));
- }
- else
- {
- WaitForSingleObject(g_ddraw->fps_limiter.htimer, g_ddraw->fps_limiter.tick_length * 2);
- }
-
- g_ddraw->fps_limiter.due_time.QuadPart += g_ddraw->fps_limiter.tick_length_ns;
- SetWaitableTimer(g_ddraw->fps_limiter.htimer, &g_ddraw->fps_limiter.due_time, 0, NULL, NULL, FALSE);
- }
- else
- {
- tick_end = timeGetTime();
-
- if (tick_end - tick_start < g_ddraw->fps_limiter.tick_length)
- {
- Sleep(g_ddraw->fps_limiter.tick_length - (tick_end - tick_start));
- }
- }
- }
+ fpsl_frame_end();
}
return TRUE;
diff --git a/src/render_ogl.c b/src/render_ogl.c
index a37ef07..48a4a1b 100644
--- a/src/render_ogl.c
+++ b/src/render_ogl.c
@@ -1,5 +1,6 @@
#include
#include
+#include "fps_limiter.h"
#include "opengl_utils.h"
#include "dd.h"
#include "ddsurface.h"
@@ -12,7 +13,6 @@
static HGLRC ogl_create_core_context(HDC hdc);
static HGLRC ogl_create_context(HDC hdc);
-static void ogl_set_max_fps();
static void ogl_build_programs();
static void ogl_create_textures(int width, int height);
static void ogl_init_main_program();
@@ -36,7 +36,10 @@ DWORD WINAPI ogl_render_main(void)
g_ogl.context = ogl_create_core_context(g_ddraw->render.hdc);
- ogl_set_max_fps();
+ if (oglu_ext_exists("WGL_EXT_swap_control", g_ddraw->render.hdc) && wglSwapIntervalEXT)
+ wglSwapIntervalEXT(g_ddraw->vsync ? 1 : 0);
+
+ fpsl_init();
ogl_build_programs();
ogl_create_textures(g_ddraw->width, g_ddraw->height);
ogl_init_main_program();
@@ -110,56 +113,6 @@ static HGLRC ogl_create_context(HDC hdc)
return context;
}
-static void ogl_set_max_fps()
-{
- int max_fps = g_ddraw->render.maxfps;
-
- g_ddraw->fps_limiter.tick_length_ns = 0;
- g_ddraw->fps_limiter.tick_length = 0;
-
- /*
- if (oglu_ext_exists("WGL_EXT_swap_control_tear", g_ddraw->render.hDC))
- {
- if (wglSwapIntervalEXT)
- {
- if (g_ddraw->vsync)
- {
- wglSwapIntervalEXT(-1);
- max_fps = g_ddraw->mode.dmDisplayFrequency;
- }
- else
- wglSwapIntervalEXT(0);
- }
- }
- else */
- if (oglu_ext_exists("WGL_EXT_swap_control", g_ddraw->render.hdc))
- {
- if (wglSwapIntervalEXT)
- {
- if (g_ddraw->vsync)
- {
- wglSwapIntervalEXT(1);
- max_fps = g_ddraw->mode.dmDisplayFrequency;
- }
- else
- wglSwapIntervalEXT(0);
- }
- }
-
- if (max_fps < 0)
- max_fps = g_ddraw->mode.dmDisplayFrequency;
-
- if (max_fps > 1000)
- max_fps = 0;
-
- if (max_fps > 0)
- {
- float len = 1000.0f / max_fps;
- g_ddraw->fps_limiter.tick_length_ns = len * 10000;
- g_ddraw->fps_limiter.tick_length = len;// + 0.5f;
- }
-}
-
static void ogl_build_programs()
{
g_ogl.main_program = g_ogl.scale_program = 0;
@@ -573,8 +526,6 @@ static void ogl_init_scale_program()
static void ogl_render()
{
- DWORD tick_start = 0;
- DWORD tick_end = 0;
BOOL needs_update = FALSE;
glViewport(
@@ -606,8 +557,7 @@ static void ogl_render()
BOOL scale_changed = FALSE;
- if (g_ddraw->fps_limiter.tick_length > 0)
- tick_start = timeGetTime();
+ fpsl_frame_start();
EnterCriticalSection(&g_ddraw->cs);
@@ -836,44 +786,7 @@ static void ogl_render()
dbg_draw_frame_info_end();
#endif
- if (g_ddraw->fps_limiter.tick_length > 0)
- {
- if (g_ddraw->fps_limiter.htimer)
- {
- if (g_ddraw->vsync)
- {
- WaitForSingleObject(g_ddraw->fps_limiter.htimer, g_ddraw->fps_limiter.tick_length * 2);
- LARGE_INTEGER due_time = { .QuadPart = -g_ddraw->fps_limiter.tick_length_ns };
- SetWaitableTimer(g_ddraw->fps_limiter.htimer, &due_time, 0, NULL, NULL, FALSE);
- }
- else
- {
- FILETIME ft = { 0 };
- GetSystemTimeAsFileTime(&ft);
-
- if (CompareFileTime((FILETIME *)&g_ddraw->fps_limiter.due_time, &ft) == -1)
- {
- memcpy(&g_ddraw->fps_limiter.due_time, &ft, sizeof(LARGE_INTEGER));
- }
- else
- {
- WaitForSingleObject(g_ddraw->fps_limiter.htimer, g_ddraw->fps_limiter.tick_length * 2);
- }
-
- g_ddraw->fps_limiter.due_time.QuadPart += g_ddraw->fps_limiter.tick_length_ns;
- SetWaitableTimer(g_ddraw->fps_limiter.htimer, &g_ddraw->fps_limiter.due_time, 0, NULL, NULL, FALSE);
- }
- }
- else
- {
- tick_end = timeGetTime();
-
- if (tick_end - tick_start < g_ddraw->fps_limiter.tick_length)
- {
- Sleep(g_ddraw->fps_limiter.tick_length - (tick_end - tick_start));
- }
- }
- }
+ fpsl_frame_end();
}
}