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