Merge pull request #1553 from LordSk/feature/1537

Fps limit option
This commit is contained in:
oy 2018-10-29 18:25:46 +01:00 committed by GitHub
commit 9eeb7dffa5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 145 additions and 29 deletions

View file

@ -2,6 +2,7 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <new>
#include <immintrin.h> //_mm_pause
#include <stdlib.h> // qsort
#include <stdarg.h>
@ -254,6 +255,8 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotD
m_RenderFrameTimeHigh = 0.0f;
m_RenderFrames = 0;
m_LastRenderTime = time_get();
m_LastCpuTime = time_get();
m_LastAvgCpuFrameTime = 0;
m_GameTickSpeed = SERVER_TICK_SPEED;
@ -1712,6 +1715,86 @@ void CClient::InitInterfaces()
m_Friends.Init();
}
bool CClient::LimitFps()
{
if(g_Config.m_GfxVsync || !g_Config.m_GfxLimitFps) return false;
/**
If desired frame time is not reached:
Skip rendering the frame
Do another game loop
If we don't have the time to do another game loop:
Wait until desired frametime
Returns true if frame should be skipped
**/
#ifdef CONF_DEBUG
static double DbgTimeWaited = 0.0;
static int64 DbgFramesSkippedCount = 0;
static int64 DbgLastSkippedDbgMsg = time_get();
#endif
int64 Now = time_get();
const double LastCpuFrameTime = (Now - m_LastCpuTime) / (double)time_freq();
m_LastAvgCpuFrameTime = (m_LastAvgCpuFrameTime + LastCpuFrameTime * 4.0) / 5.0;
m_LastCpuTime = Now;
bool SkipFrame = true;
double RenderDeltaTime = (Now - m_LastRenderTime) / (double)time_freq();
const double DesiredTime = 1.0/g_Config.m_GfxMaxFps;
// we can't skip another frame, so wait instead
if(SkipFrame && RenderDeltaTime < DesiredTime &&
m_LastAvgCpuFrameTime * 1.20 > (DesiredTime - RenderDeltaTime))
{
#ifdef CONF_DEBUG
DbgTimeWaited += DesiredTime - RenderDeltaTime;
#endif
const double Freq = (double)time_freq();
const int64 LastT = m_LastRenderTime;
double d = DesiredTime - RenderDeltaTime;
while(d > 0.00001)
{
Now = time_get();
RenderDeltaTime = (Now - LastT) / Freq;
d = DesiredTime - RenderDeltaTime;
_mm_pause();
}
SkipFrame = false;
m_LastCpuTime = Now;
}
// RenderDeltaTime exceeds DesiredTime, render
if(SkipFrame && RenderDeltaTime > DesiredTime)
{
SkipFrame = false;
}
#ifdef CONF_DEBUG
DbgFramesSkippedCount += SkipFrame? 1:0;
Now = time_get();
if(g_Config.m_GfxLimitFps &&
g_Config.m_Debug &&
(Now - DbgLastSkippedDbgMsg) / (double)time_freq() > 5.0)
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "LimitFps: FramesSkippedCount=%d TimeWaited=%.3f (per sec)",
DbgFramesSkippedCount/5,
DbgTimeWaited/5.0);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client", aBuf);
DbgFramesSkippedCount = 0;
DbgTimeWaited = 0;
DbgLastSkippedDbgMsg = Now;
}
#endif
return SkipFrame;
}
void CClient::Run()
{
m_LocalStartTime = time_get();
@ -1902,13 +1985,16 @@ void CClient::Run()
Update();
if(!g_Config.m_GfxAsyncRender || m_pGraphics->IsIdle())
const bool SkipFrame = LimitFps();
if(!SkipFrame && (!g_Config.m_GfxAsyncRender || m_pGraphics->IsIdle()))
{
m_RenderFrames++;
// update frametime
int64 Now = time_get();
m_RenderFrameTime = (Now - m_LastRenderTime) / (float)time_freq();
if(m_RenderFrameTime < m_RenderFrameTimeLow)
m_RenderFrameTimeLow = m_RenderFrameTime;
if(m_RenderFrameTime > m_RenderFrameTimeHigh)

View file

@ -86,6 +86,8 @@ class CClient : public IClient, public CDemoPlayer::IListner
IGraphics::CTextureHandle m_DebugFont;
int64 m_LastRenderTime;
int64 m_LastCpuTime;
float m_LastAvgCpuFrameTime;
float m_RenderFrameTimeLow;
float m_RenderFrameTimeHigh;
int m_RenderFrames;
@ -273,6 +275,7 @@ public:
void RegisterInterfaces();
void InitInterfaces();
bool LimitFps();
void Run();

View file

@ -217,7 +217,7 @@ public:
};
extern IEngineGraphics *CreateEngineGraphics();
extern IEngineGraphics *CreateEngineGraphics(); // NOTE: not used
extern IEngineGraphics *CreateEngineGraphicsThreaded();
#endif

View file

@ -57,6 +57,8 @@ MACRO_CONFIG_INT(GfxFsaaSamples, gfx_fsaa_samples, 0, 0, 16, CFGFLAG_SAVE|CFGFLA
MACRO_CONFIG_INT(GfxRefreshRate, gfx_refresh_rate, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen refresh rate")
MACRO_CONFIG_INT(GfxFinish, gfx_finish, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
MACRO_CONFIG_INT(GfxAsyncRender, gfx_asyncrender, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Do rendering async from the the update")
MACRO_CONFIG_INT(GfxMaxFps, gfx_maxfps, 144, 30, 2000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Maximum fps (when limit fps is enabled)")
MACRO_CONFIG_INT(GfxLimitFps, gfx_limitfps, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Limit fps")
MACRO_CONFIG_INT(InpMousesens, inp_mousesens, 100, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity")

View file

@ -1264,14 +1264,14 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
static int s_GfxTextureQuality = g_Config.m_GfxTextureQuality;
static int s_GfxTextureCompression = g_Config.m_GfxTextureCompression;
CUIRect Label, Button, Screen, Texture, BottomView;
CUIRect Label, Button, ScreenLeft, ScreenRight, Texture, BottomView;
// cut view
MainView.HSplitBottom(80.0f, &MainView, &BottomView);
BottomView.HSplitTop(20.f, 0, &BottomView);
// render screen menu background
int NumOptions = g_Config.m_GfxFullscreen ? 3 : 4;
int NumOptions = 3 + (!g_Config.m_GfxFullscreen);
if(Graphics()->GetNumScreens() > 1)
++NumOptions;
float ButtonHeight = 20.0f;
@ -1279,8 +1279,8 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
float BackgroundHeight = (float)(NumOptions+1)*ButtonHeight+(float)NumOptions*Spacing;
MainView.HSplitTop(20.0f, 0, &MainView);
MainView.HSplitTop(BackgroundHeight, &Screen, &MainView);
RenderTools()->DrawUIRect(&Screen, vec4(0.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_ALL, 5.0f);
MainView.HSplitTop(BackgroundHeight, &ScreenLeft, &MainView);
RenderTools()->DrawUIRect(&ScreenLeft, vec4(0.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_ALL, 5.0f);
// render textures menu background
NumOptions = 3;
@ -1291,46 +1291,40 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
RenderTools()->DrawUIRect(&Texture, vec4(0.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_ALL, 5.0f);
// render screen menu
Screen.HSplitTop(ButtonHeight, &Label, &Screen);
ScreenLeft.HSplitTop(ButtonHeight, &Label, &ScreenLeft);
Label.y += 2.0f;
UI()->DoLabel(&Label, Localize("Screen"), ButtonHeight*ms_FontmodHeight*0.8f, CUI::ALIGN_CENTER);
Screen.HSplitTop(Spacing, 0, &Screen);
Screen.HSplitTop(ButtonHeight, &Button, &Screen);
ScreenLeft.VSplitMid(&ScreenLeft, &ScreenRight);
ScreenLeft.VSplitRight(Spacing * 0.5f, &ScreenLeft, 0);
ScreenRight.VSplitLeft(Spacing * 0.5f, 0, &ScreenRight);
ScreenLeft.HSplitTop(Spacing, 0, &ScreenLeft);
ScreenLeft.HSplitTop(ButtonHeight, &Button, &ScreenLeft);
static int s_ButtonGfxFullscreen = 0;
if(DoButton_CheckBox(&s_ButtonGfxFullscreen, Localize("Fullscreen"), g_Config.m_GfxFullscreen, &Button))
Client()->ToggleFullscreen();
if(!g_Config.m_GfxFullscreen)
{
Screen.HSplitTop(Spacing, 0, &Screen);
Screen.HSplitTop(ButtonHeight, &Button, &Screen);
ScreenLeft.HSplitTop(Spacing, 0, &ScreenLeft);
ScreenLeft.HSplitTop(ButtonHeight, &Button, &ScreenLeft);
Button.VSplitLeft(ButtonHeight, 0, &Button);
static int s_ButtonGfxBorderless = 0;
if(DoButton_CheckBox(&s_ButtonGfxBorderless, Localize("Borderless window"), g_Config.m_GfxBorderless, &Button))
Client()->ToggleWindowBordered();
}
Screen.HSplitTop(Spacing, 0, &Screen);
Screen.HSplitTop(ButtonHeight, &Button, &Screen);
ScreenLeft.HSplitTop(Spacing, 0, &ScreenLeft);
ScreenLeft.HSplitTop(ButtonHeight, &Button, &ScreenLeft);
static int s_ButtonGfxVsync = 0;
if(DoButton_CheckBox(&s_ButtonGfxVsync, Localize("V-Sync"), g_Config.m_GfxVsync, &Button))
Client()->ToggleWindowVSync();
if(Graphics()->GetNumScreens() > 1)
{
Screen.HSplitTop(Spacing, 0, &Screen);
Screen.HSplitTop(ButtonHeight, &Button, &Screen);
int Index = g_Config.m_GfxScreen;
DoScrollbarOption(&g_Config.m_GfxScreen, &Index, &Button, Localize("Screen"), 110.0f, 0, Graphics()->GetNumScreens()-1);
if(Index != g_Config.m_GfxScreen)
Client()->SwitchWindowScreen(Index);
}
// FSAA button
{
Screen.HSplitTop(Spacing, 0, &Screen);
Screen.HSplitTop(ButtonHeight, &Button, &Screen);
ScreenLeft.HSplitTop(Spacing, 0, &ScreenLeft);
ScreenLeft.HSplitTop(ButtonHeight, &Button, &ScreenLeft);
RenderTools()->DrawUIRect(&Button, vec4(0.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_ALL, 5.0f);
CUIRect Text;
Button.VSplitLeft(ButtonHeight+5.0f, 0, &Button);
@ -1356,6 +1350,37 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
}
}
ScreenRight.HSplitTop(Spacing, 0, &ScreenRight);
ScreenRight.HSplitTop(ButtonHeight, &Button, &ScreenRight);
// TODO: greyed out checkbox (not clickable)
if(!g_Config.m_GfxVsync)
{
static int s_ButtonGfxCapFps = 0;
if(DoButton_CheckBox(&s_ButtonGfxCapFps, Localize("Limit Fps"), g_Config.m_GfxLimitFps, &Button))
{
g_Config.m_GfxLimitFps ^= 1;
}
if(g_Config.m_GfxLimitFps > 0)
{
ScreenRight.HSplitTop(Spacing, 0, &ScreenRight);
ScreenRight.HSplitTop(ButtonHeight, &Button, &ScreenRight);
DoScrollbarOption(&g_Config.m_GfxMaxFps, &g_Config.m_GfxMaxFps,
&Button, Localize("Max fps"), 144.0f, 30, 300);
}
}
if(Graphics()->GetNumScreens() > 1)
{
ScreenRight.HSplitTop(Spacing, 0, &ScreenRight);
ScreenRight.HSplitTop(ButtonHeight, &Button, &ScreenRight);
int Index = g_Config.m_GfxScreen;
DoScrollbarOption(&g_Config.m_GfxScreen, &Index, &Button, Localize("Screen"), 110.0f, 0, Graphics()->GetNumScreens()-1);
if(Index != g_Config.m_GfxScreen)
Client()->SwitchWindowScreen(Index);
}
// render texture menu
Texture.HSplitTop(ButtonHeight, &Label, &Texture);
Label.y += 2.0f;