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(); } }