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. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <new> #include <new>
#include <immintrin.h> //_mm_pause
#include <stdlib.h> // qsort #include <stdlib.h> // qsort
#include <stdarg.h> #include <stdarg.h>
@ -254,6 +255,8 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotD
m_RenderFrameTimeHigh = 0.0f; m_RenderFrameTimeHigh = 0.0f;
m_RenderFrames = 0; m_RenderFrames = 0;
m_LastRenderTime = time_get(); m_LastRenderTime = time_get();
m_LastCpuTime = time_get();
m_LastAvgCpuFrameTime = 0;
m_GameTickSpeed = SERVER_TICK_SPEED; m_GameTickSpeed = SERVER_TICK_SPEED;
@ -1091,7 +1094,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)
const unsigned char *pData = Unpacker.GetRaw(Size); const unsigned char *pData = Unpacker.GetRaw(Size);
if(Unpacker.Error()) if(Unpacker.Error())
return; return;
io_write(m_MapdownloadFile, pData, Size); io_write(m_MapdownloadFile, pData, Size);
++m_MapdownloadChunk; ++m_MapdownloadChunk;
m_MapdownloadAmount += Size; m_MapdownloadAmount += Size;
@ -1712,6 +1715,86 @@ void CClient::InitInterfaces()
m_Friends.Init(); 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() void CClient::Run()
{ {
m_LocalStartTime = time_get(); m_LocalStartTime = time_get();
@ -1901,14 +1984,17 @@ void CClient::Run()
m_EditorActive = false; m_EditorActive = false;
Update(); Update();
if(!g_Config.m_GfxAsyncRender || m_pGraphics->IsIdle()) const bool SkipFrame = LimitFps();
if(!SkipFrame && (!g_Config.m_GfxAsyncRender || m_pGraphics->IsIdle()))
{ {
m_RenderFrames++; m_RenderFrames++;
// update frametime // update frametime
int64 Now = time_get(); int64 Now = time_get();
m_RenderFrameTime = (Now - m_LastRenderTime) / (float)time_freq(); m_RenderFrameTime = (Now - m_LastRenderTime) / (float)time_freq();
if(m_RenderFrameTime < m_RenderFrameTimeLow) if(m_RenderFrameTime < m_RenderFrameTimeLow)
m_RenderFrameTimeLow = m_RenderFrameTime; m_RenderFrameTimeLow = m_RenderFrameTime;
if(m_RenderFrameTime > m_RenderFrameTimeHigh) if(m_RenderFrameTime > m_RenderFrameTimeHigh)
@ -2073,7 +2159,7 @@ void CClient::Con_RconAuth(IConsole::IResult *pResult, void *pUserData)
const char *CClient::DemoPlayer_Play(const char *pFilename, int StorageType) const char *CClient::DemoPlayer_Play(const char *pFilename, int StorageType)
{ {
int Crc; int Crc;
Disconnect(); Disconnect();
m_NetClient.ResetErrorString(); m_NetClient.ResetErrorString();

View file

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

View file

@ -217,7 +217,7 @@ public:
}; };
extern IEngineGraphics *CreateEngineGraphics(); extern IEngineGraphics *CreateEngineGraphics(); // NOTE: not used
extern IEngineGraphics *CreateEngineGraphicsThreaded(); extern IEngineGraphics *CreateEngineGraphicsThreaded();
#endif #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(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(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(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") 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_GfxTextureQuality = g_Config.m_GfxTextureQuality;
static int s_GfxTextureCompression = g_Config.m_GfxTextureCompression; static int s_GfxTextureCompression = g_Config.m_GfxTextureCompression;
CUIRect Label, Button, Screen, Texture, BottomView; CUIRect Label, Button, ScreenLeft, ScreenRight, Texture, BottomView;
// cut view // cut view
MainView.HSplitBottom(80.0f, &MainView, &BottomView); MainView.HSplitBottom(80.0f, &MainView, &BottomView);
BottomView.HSplitTop(20.f, 0, &BottomView); BottomView.HSplitTop(20.f, 0, &BottomView);
// render screen menu background // render screen menu background
int NumOptions = g_Config.m_GfxFullscreen ? 3 : 4; int NumOptions = 3 + (!g_Config.m_GfxFullscreen);
if(Graphics()->GetNumScreens() > 1) if(Graphics()->GetNumScreens() > 1)
++NumOptions; ++NumOptions;
float ButtonHeight = 20.0f; float ButtonHeight = 20.0f;
@ -1279,8 +1279,8 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
float BackgroundHeight = (float)(NumOptions+1)*ButtonHeight+(float)NumOptions*Spacing; float BackgroundHeight = (float)(NumOptions+1)*ButtonHeight+(float)NumOptions*Spacing;
MainView.HSplitTop(20.0f, 0, &MainView); MainView.HSplitTop(20.0f, 0, &MainView);
MainView.HSplitTop(BackgroundHeight, &Screen, &MainView); MainView.HSplitTop(BackgroundHeight, &ScreenLeft, &MainView);
RenderTools()->DrawUIRect(&Screen, vec4(0.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_ALL, 5.0f); RenderTools()->DrawUIRect(&ScreenLeft, vec4(0.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_ALL, 5.0f);
// render textures menu background // render textures menu background
NumOptions = 3; 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); RenderTools()->DrawUIRect(&Texture, vec4(0.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_ALL, 5.0f);
// render screen menu // render screen menu
Screen.HSplitTop(ButtonHeight, &Label, &Screen); ScreenLeft.HSplitTop(ButtonHeight, &Label, &ScreenLeft);
Label.y += 2.0f; Label.y += 2.0f;
UI()->DoLabel(&Label, Localize("Screen"), ButtonHeight*ms_FontmodHeight*0.8f, CUI::ALIGN_CENTER); UI()->DoLabel(&Label, Localize("Screen"), ButtonHeight*ms_FontmodHeight*0.8f, CUI::ALIGN_CENTER);
Screen.HSplitTop(Spacing, 0, &Screen); ScreenLeft.VSplitMid(&ScreenLeft, &ScreenRight);
Screen.HSplitTop(ButtonHeight, &Button, &Screen); 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; static int s_ButtonGfxFullscreen = 0;
if(DoButton_CheckBox(&s_ButtonGfxFullscreen, Localize("Fullscreen"), g_Config.m_GfxFullscreen, &Button)) if(DoButton_CheckBox(&s_ButtonGfxFullscreen, Localize("Fullscreen"), g_Config.m_GfxFullscreen, &Button))
Client()->ToggleFullscreen(); Client()->ToggleFullscreen();
if(!g_Config.m_GfxFullscreen) if(!g_Config.m_GfxFullscreen)
{ {
Screen.HSplitTop(Spacing, 0, &Screen); ScreenLeft.HSplitTop(Spacing, 0, &ScreenLeft);
Screen.HSplitTop(ButtonHeight, &Button, &Screen); ScreenLeft.HSplitTop(ButtonHeight, &Button, &ScreenLeft);
Button.VSplitLeft(ButtonHeight, 0, &Button); Button.VSplitLeft(ButtonHeight, 0, &Button);
static int s_ButtonGfxBorderless = 0; static int s_ButtonGfxBorderless = 0;
if(DoButton_CheckBox(&s_ButtonGfxBorderless, Localize("Borderless window"), g_Config.m_GfxBorderless, &Button)) if(DoButton_CheckBox(&s_ButtonGfxBorderless, Localize("Borderless window"), g_Config.m_GfxBorderless, &Button))
Client()->ToggleWindowBordered(); Client()->ToggleWindowBordered();
} }
Screen.HSplitTop(Spacing, 0, &Screen); ScreenLeft.HSplitTop(Spacing, 0, &ScreenLeft);
Screen.HSplitTop(ButtonHeight, &Button, &Screen); ScreenLeft.HSplitTop(ButtonHeight, &Button, &ScreenLeft);
static int s_ButtonGfxVsync = 0; static int s_ButtonGfxVsync = 0;
if(DoButton_CheckBox(&s_ButtonGfxVsync, Localize("V-Sync"), g_Config.m_GfxVsync, &Button)) if(DoButton_CheckBox(&s_ButtonGfxVsync, Localize("V-Sync"), g_Config.m_GfxVsync, &Button))
Client()->ToggleWindowVSync(); 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 // FSAA button
{ {
Screen.HSplitTop(Spacing, 0, &Screen); ScreenLeft.HSplitTop(Spacing, 0, &ScreenLeft);
Screen.HSplitTop(ButtonHeight, &Button, &Screen); ScreenLeft.HSplitTop(ButtonHeight, &Button, &ScreenLeft);
RenderTools()->DrawUIRect(&Button, vec4(0.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_ALL, 5.0f); RenderTools()->DrawUIRect(&Button, vec4(0.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_ALL, 5.0f);
CUIRect Text; CUIRect Text;
Button.VSplitLeft(ButtonHeight+5.0f, 0, &Button); 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 // render texture menu
Texture.HSplitTop(ButtonHeight, &Label, &Texture); Texture.HSplitTop(ButtonHeight, &Label, &Texture);
Label.y += 2.0f; Label.y += 2.0f;