From 11ca707485d3e70d0621e967e96db6c52423b7a8 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 16 Sep 2012 04:40:56 +0000 Subject: [PATCH] - Added vid_maxfps cvar to limit the frame rate to some arbitrary rate between 35 and 1000 FPS. It defaults to 200. Setting it to 0 will restore the previous behavior of having no frame rate limit. Note that vid_maxfps 35 is NOT the same as cl_capfps 1. cl_capfps caps the frame rate by tying the video update directly to the game timer. With vid_maxfps 35, the video update and game timer are running on separate timers, and results will not be as good as with cl_capfps 1, which uses only one timer. SVN r3872 (trunk) --- src/d_net.cpp | 14 +++++- src/sdl/hardware.cpp | 4 ++ src/sdl/hardware.h | 2 + src/win32/fb_d3d9.cpp | 5 +++ src/win32/fb_ddraw.cpp | 4 ++ src/win32/hardware.h | 3 ++ src/win32/win32iface.h | 2 + src/win32/win32video.cpp | 93 +++++++++++++++++++++++++++++++++++++++- 8 files changed, 125 insertions(+), 2 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 0137d911a..3acee3af4 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -60,6 +60,7 @@ #include "p_lnspec.h" #include "v_video.h" #include "p_spec.h" +#include "hardware.h" #include "intermission/intermission.h" EXTERN_CVAR (Int, disableautosave) @@ -135,7 +136,18 @@ static int oldentertics; extern bool advancedemo; -CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + // Do not use the separate FPS limit timer if we are limiting FPS with this. + if (self) + { + I_SetFPSLimit(0); + } + else + { + I_SetFPSLimit(-1); + } +} // [RH] Special "ticcmds" get stored in here static struct TicSpecial diff --git a/src/sdl/hardware.cpp b/src/sdl/hardware.cpp index a7af588ce..ef78b4c31 100644 --- a/src/sdl/hardware.cpp +++ b/src/sdl/hardware.cpp @@ -177,6 +177,10 @@ void I_ClosestResolution (int *width, int *height, int bits) } } +void I_SetFPSLimit(int limit) +{ +} + extern int NewWidth, NewHeight, NewBits, DisplayBits; CUSTOM_CVAR (Bool, fullscreen, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) diff --git a/src/sdl/hardware.h b/src/sdl/hardware.h index f82af7bb4..651d3ee2b 100644 --- a/src/sdl/hardware.h +++ b/src/sdl/hardware.h @@ -59,6 +59,8 @@ void I_InitGraphics (); void I_ShutdownGraphics (); void I_CreateRenderer(); +void I_SetFPSLimit(int limit); + extern IVideo *Video; #endif // __HARDWARE_H__ diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp index 876ebdbd9..3598f52d1 100644 --- a/src/win32/fb_d3d9.cpp +++ b/src/win32/fb_d3d9.cpp @@ -1208,6 +1208,11 @@ void D3DFB::Flip() BlockSurface[BlockNum]->UnlockRect(); } } + // Limiting the frame rate is as simple as waiting for the timer to signal this event. + if (FPSLimitEvent != NULL) + { + WaitForSingleObject(FPSLimitEvent, 1000); + } D3DDevice->Present(NULL, NULL, NULL, NULL); InScene = false; diff --git a/src/win32/fb_ddraw.cpp b/src/win32/fb_ddraw.cpp index 046c7ed4f..9fb65c2a7 100644 --- a/src/win32/fb_ddraw.cpp +++ b/src/win32/fb_ddraw.cpp @@ -1201,6 +1201,10 @@ void DDrawFB::Update () LockCount = 0; UpdatePending = false; + if (FPSLimitEvent != NULL) + { + WaitForSingleObject(FPSLimitEvent, 1000); + } if (!Windowed && AppActive && !SessionState /*&& !UseBlitter && !MustBuffer*/) { HRESULT hr = PrimarySurf->Flip (NULL, FlipFlags); diff --git a/src/win32/hardware.h b/src/win32/hardware.h index 38cb15ea8..e6b23ab2f 100644 --- a/src/win32/hardware.h +++ b/src/win32/hardware.h @@ -62,6 +62,9 @@ void I_CreateRenderer(); void I_SaveWindowedPos (); void I_RestoreWindowedPos (); +void I_SetFPSLimit(int limit); + + extern IVideo *Video; #endif // __HARDWARE_H__ diff --git a/src/win32/win32iface.h b/src/win32/win32iface.h index ec64f2164..07151fb02 100644 --- a/src/win32/win32iface.h +++ b/src/win32/win32iface.h @@ -52,6 +52,8 @@ EXTERN_CVAR (Bool, vid_vsync) +extern HANDLE FPSLimitEvent; + class D3DTex; class D3DPal; struct FSoftwareRenderer; diff --git a/src/win32/win32video.cpp b/src/win32/win32video.cpp index adc68af23..b6d6d670e 100644 --- a/src/win32/win32video.cpp +++ b/src/win32/win32video.cpp @@ -41,6 +41,7 @@ #define _WIN32_WINNT 0x0501 #define WIN32_LEAN_AND_MEAN #include +#include #include #include @@ -87,6 +88,8 @@ void DoBlending (const PalEntry *from, PalEntry *to, int count, int r, int g, in // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- +static void StopFPSLimit(); + // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern HWND Window; @@ -98,20 +101,38 @@ extern bool VidResizing; EXTERN_CVAR (Bool, fullscreen) EXTERN_CVAR (Float, Gamma) +EXTERN_CVAR (Bool, cl_capfps) // PRIVATE DATA DEFINITIONS ------------------------------------------------ static HMODULE D3D9_dll; static HMODULE DDraw_dll; +static UINT FPSLimitTimer; // PUBLIC DATA DEFINITIONS ------------------------------------------------- IDirectDraw2 *DDraw; IDirect3D9 *D3D; IDirect3DDevice9 *D3Device; +HANDLE FPSLimitEvent; CVAR (Bool, vid_forceddraw, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR (Int, vid_adapter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR (Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (vid_maxfps < TICRATE && vid_maxfps != 0) + { + vid_maxfps = TICRATE; + } + else if (vid_maxfps > 1000) + { + vid_maxfps = 1000; + } + else if (cl_capfps == 0) + { + I_SetFPSLimit(vid_maxfps); + } +} #if VID_FILE_DEBUG FILE *dbg; @@ -714,4 +735,74 @@ void Win32Video::SetWindowedScale (float scale) // FIXME } -// FrameBuffer implementation ----------------------------------------------- +//========================================================================== +// +// SetFPSLimit +// +// Initializes an event timer to fire at a rate of /sec. The video +// update will wait for this timer to trigger before updating. +// +// Pass 0 as the limit for unlimited. +// Pass a negative value for the limit to use the value of vid_maxfps. +// +//========================================================================== + +void I_SetFPSLimit(int limit) +{ + if (limit < 0) + { + limit = vid_maxfps; + } + // Kill any leftover timer. + if (FPSLimitTimer != 0) + { + timeKillEvent(FPSLimitTimer); + FPSLimitTimer = 0; + } + if (limit == 0) + { // no limit + if (FPSLimitEvent != NULL) + { + CloseHandle(FPSLimitEvent); + FPSLimitEvent = NULL; + } + DPrintf("FPS timer disabled\n"); + } + else + { + if (FPSLimitEvent == NULL) + { + FPSLimitEvent = CreateEvent(NULL, FALSE, TRUE, NULL); + if (FPSLimitEvent == NULL) + { // Could not create event, so cannot use timer. + Printf("Failed to create FPS limitter event\n"); + return; + } + } + atterm(StopFPSLimit); + // Set timer event as close as we can to limit/sec, in milliseconds. + UINT period = 1000 / limit; + FPSLimitTimer = timeSetEvent(period, 0, (LPTIMECALLBACK)FPSLimitEvent, 0, TIME_PERIODIC | TIME_CALLBACK_EVENT_SET); + if (FPSLimitTimer == 0) + { + CloseHandle(FPSLimitEvent); + FPSLimitEvent = NULL; + Printf("Failed to create FPS limitter timer\n"); + return; + } + DPrintf("FPS timer set to %u ms\n", period); + } +} + +//========================================================================== +// +// StopFPSLimit +// +// Used for cleanup during application shutdown. +// +//========================================================================== + +static void StopFPSLimit() +{ + I_SetFPSLimit(0); +}