diff --git a/src/base/system.c b/src/base/system.c index 7633fe913..cf16f4696 100644 --- a/src/base/system.c +++ b/src/base/system.c @@ -944,14 +944,9 @@ void set_new_tick() } /* ----- time ----- */ -int64 time_get() +int64 time_get_impl() { static int64 last = 0; - if(new_tick == 0) - return last; - if(new_tick != -1) - new_tick = 0; - { #if defined(CONF_PLATFORM_MACOSX) static int got_timebase = 0; @@ -971,7 +966,7 @@ int64 time_get() #elif defined(CONF_FAMILY_UNIX) struct timespec spec; clock_gettime(CLOCK_MONOTONIC, &spec); - last = (int64)spec.tv_sec*(int64)1000000+(int64)spec.tv_nsec/1000; + last = (int64)spec.tv_sec*(int64)1000000 + (int64)spec.tv_nsec / 1000; return last; #elif defined(CONF_FAMILY_WINDOWS) int64 t; @@ -986,6 +981,18 @@ int64 time_get() } } +int64 time_get() +{ + static int64 last = 0; + if(new_tick == 0) + return last; + if(new_tick != -1) + new_tick = 0; + + last = time_get_impl(); + return last; +} + int64 time_freq() { #if defined(CONF_PLATFORM_MACOSX) @@ -1001,6 +1008,15 @@ int64 time_freq() #endif } +int64 time_get_microseconds() +{ +#if defined(CONF_FAMILY_WINDOWS) + return (time_get_impl() * (int64)1000000) / time_freq(); +#else + return time_get_impl() / (time_freq() / 1000 / 1000); +#endif +} + /* ----- network ----- */ static void netaddr_to_sockaddr_in(const NETADDR *src, struct sockaddr_in *dest) { diff --git a/src/base/system.h b/src/base/system.h index 9a1922387..177891851 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -591,6 +591,18 @@ typedef unsigned long long uint64; void set_new_tick(); +/* + Function: time_get_impl + Fetches a sample from a high resolution timer. + + Returns: + Current value of the timer. + + Remarks: + To know how fast the timer is ticking, see . +*/ +int64 time_get_impl(); + /* Function: time_get Fetches a sample from a high resolution timer. @@ -600,6 +612,7 @@ void set_new_tick(); Remarks: To know how fast the timer is ticking, see . + Uses to fetch the sample. */ int64 time_get(); @@ -621,6 +634,15 @@ int64 time_freq(); */ int time_timestamp(); +/* +Function: time_get_microseconds +Fetches a sample from a high resolution timer and converts it in microseconds. + +Returns: +Current value of the timer in microseconds. +*/ +int64 time_get_microseconds(); + /* Group: Network General */ typedef struct { diff --git a/src/engine/client/backend_sdl.cpp b/src/engine/client/backend_sdl.cpp index 40f35e1b0..acd8680fd 100644 --- a/src/engine/client/backend_sdl.cpp +++ b/src/engine/client/backend_sdl.cpp @@ -901,8 +901,8 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Create(const CCommandBuffe switch(StoreOglformat) { case GL_RGB: StoreOglformat = GL_COMPRESSED_RGB; break; - // This needs further checks. it seems on some gpus COMPRESSED_ALPHA isn't in the core profile - case GL_RED: StoreOglformat = GL_COMPRESSED_RGBA; break; + // COMPRESSED_ALPHA is deprecated, so use different single channel format. + case GL_RED: StoreOglformat = GL_COMPRESSED_RED; break; case GL_RGBA: StoreOglformat = GL_COMPRESSED_RGBA; break; default: StoreOglformat = GL_COMPRESSED_RGBA; } @@ -927,7 +927,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Create(const CCommandBuffe //Bind the texture 2D. GLint swizzleMask[] = {GL_ONE, GL_ONE, GL_ONE, GL_RED}; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); - StoreOglformat = GL_RGBA; + StoreOglformat = GL_R8; } if(pCommand->m_Flags&CCommandBuffer::TEXFLAG_NOMIPMAPS) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index cc45af6b2..6f943e92a 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -343,7 +343,7 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta) m_VersionInfo.m_State = CVersionInfo::STATE_INIT; - if (g_Config.m_ClDummy == 0) + if(g_Config.m_ClDummy == 0) m_LastDummyConnectTime = 0; m_ReconnectTime = 0; @@ -605,7 +605,7 @@ void CClient::OnEnterGame() m_CurGameTick[g_Config.m_ClDummy] = 0; m_PrevGameTick[g_Config.m_ClDummy] = 0; - if (g_Config.m_ClDummy == 0) + if(g_Config.m_ClDummy == 0) m_LastDummyConnectTime = 0; GameClient()->OnEnterGame(); @@ -2378,7 +2378,7 @@ void CClient::Update() m_CurGameTick[g_Config.m_ClDummy] = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick; m_PrevGameTick[g_Config.m_ClDummy] = m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick; - if (m_LastDummy2 == (bool)g_Config.m_ClDummy && m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] && m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]) + if(m_LastDummy2 == (bool)g_Config.m_ClDummy && m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] && m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]) { GameClient()->OnNewSnapshot(); Repredict = 1; @@ -2391,7 +2391,7 @@ void CClient::Update() break; } - if (m_LastDummy2 != (bool)g_Config.m_ClDummy) + if(m_LastDummy2 != (bool)g_Config.m_ClDummy) { m_LastDummy2 = g_Config.m_ClDummy; } @@ -2699,10 +2699,11 @@ void CClient::Run() bool LastQ = false; bool LastE = false; bool LastG = false; + + int64 LastTime = time_get_microseconds(); + int64 LastRenderTime = time_get(); - int64 LastTime = time_get(); - - while (1) + while(1) { set_new_tick(); @@ -2715,7 +2716,7 @@ void CClient::Run() } // progress on dummy connect if security token handshake skipped/passed - if (m_DummySendConnInfo && !m_NetClient[1].SecurityTokenUnknown()) + if(m_DummySendConnInfo && !m_NetClient[1].SecurityTokenUnknown()) { m_DummySendConnInfo = false; @@ -2789,7 +2790,7 @@ void CClient::Run() if((g_Config.m_GfxBackgroundRender || m_pGraphics->WindowOpen()) && (!g_Config.m_GfxAsyncRenderOld || m_pGraphics->IsIdle()) - && (!g_Config.m_GfxRefreshRate || Now >= m_LastRenderTime + time_freq() / g_Config.m_GfxRefreshRate)) + && (!g_Config.m_GfxRefreshRate || (time_freq() / (int64)g_Config.m_GfxRefreshRate) <= Now - LastRenderTime)) { m_RenderFrames++; @@ -2801,6 +2802,12 @@ void CClient::Run() m_RenderFrameTimeHigh = m_RenderFrameTime; m_FpsGraph.Add(1.0f/m_RenderFrameTime, 1,1,1); + // keep the overflow time - it's used to make sure the gfx refreshrate is reached + int64 AdditionalTime = g_Config.m_GfxRefreshRate ? ((Now - LastRenderTime) - (time_freq() / (int64)g_Config.m_GfxRefreshRate)) : 0; + // if the value is over a second time loose, reset the additional time (drop the frames, that are lost already) + if(AdditionalTime > time_freq()) + AdditionalTime = time_freq(); + LastRenderTime = Now - AdditionalTime; m_LastRenderTime = Now; #ifdef CONF_DEBUG @@ -2830,8 +2837,10 @@ void CClient::Run() } m_pGraphics->Swap(); } + Input()->NextFrame(); } + if(Input()->VideoRestartNeeded()) { m_pGraphics->Init(); @@ -2852,20 +2861,44 @@ void CClient::Run() #endif // beNice - int64 Now = time_get(); + int64 Now = time_get_microseconds(); + int64 SleepTimeInMicroSeconds = 0; + bool Slept = false; if( #ifdef CONF_DEBUG g_Config.m_DbgStress || #endif (g_Config.m_ClRefreshRateInactive && !m_pGraphics->WindowActive())) { - thread_sleep(max(1000 * (LastTime + time_freq() / g_Config.m_ClRefreshRateInactive - Now) / time_freq(), (int64)0)); + SleepTimeInMicroSeconds = ((int64)1000000 / (int64)g_Config.m_ClRefreshRateInactive) - (Now - LastTime); + if(SleepTimeInMicroSeconds / (int64)1000 > (int64)0) + thread_sleep(SleepTimeInMicroSeconds / (int64)1000); + Slept = true; } else if(g_Config.m_ClRefreshRate) { - net_socket_read_wait(m_NetClient[0].m_Socket, max(1000000 * (LastTime + time_freq() / g_Config.m_ClRefreshRate - Now) / time_freq(), (int64)0)); + SleepTimeInMicroSeconds = ((int64)1000000 / (int64)g_Config.m_ClRefreshRate) - (Now - LastTime); + if(SleepTimeInMicroSeconds > (int64)0) + net_socket_read_wait(m_NetClient[0].m_Socket, SleepTimeInMicroSeconds); + Slept = true; } - LastTime = Now; + if(Slept) + { + // if the diff gets too small it shouldn't get even smaller (drop the updates, that could not be handled) + if(SleepTimeInMicroSeconds < (int64)-1000000) + SleepTimeInMicroSeconds = (int64)-1000000; + // don't go higher than the game ticks speed, because the network is waking up the client with the server's snapshots anyway + else if(SleepTimeInMicroSeconds > (int64)1000000 / m_GameTickSpeed) + SleepTimeInMicroSeconds = (int64)1000000 / m_GameTickSpeed; + // the time diff between the time that was used actually used and the time the thread should sleep/wait + // will be calculated in the sleep time of the next update tick by faking the time it should have slept/wait. + // so two cases (and the case it slept exactly the time it should): + // - the thread slept/waited too long, then it adjust the time to sleep/wait less in the next update tick + // - the thread slept/waited too less, then it adjust the time to sleep/wait more in the next update tick + LastTime = Now + SleepTimeInMicroSeconds; + } + else + LastTime = Now; if(g_Config.m_DbgHitch) { @@ -2900,7 +2933,7 @@ bool CClient::CtrlShiftKey(int Key, bool &Last) Last = true; return true; } - else if (Last && !Input()->KeyIsPressed(Key)) + else if(Last && !Input()->KeyIsPressed(Key)) Last = false; return false; @@ -2999,15 +3032,15 @@ void CClient::AutoStatScreenshot_Cleanup() void CClient::AutoCSV_Start() { - if (g_Config.m_ClAutoCSV) + if(g_Config.m_ClAutoCSV) m_AutoCSVRecycle = true; } void CClient::AutoCSV_Cleanup() { - if (m_AutoCSVRecycle) + if(m_AutoCSVRecycle) { - if (g_Config.m_ClAutoCSVMax) + if(g_Config.m_ClAutoCSVMax) { // clean up auto csvs CFileCollection AutoRecord; @@ -3083,7 +3116,7 @@ void CClient::Con_DemoSliceEnd(IConsole::IResult *pResult, void *pUserData) void CClient::DemoSlice(const char *pDstPath, CLIENTFUNC_FILTER pfnFilter, void *pUser) { - if (m_DemoPlayer.IsPlaying()) + if(m_DemoPlayer.IsPlaying()) { const char *pDemoFileName = m_DemoPlayer.GetDemoFileName(); m_DemoEditor.Slice(pDemoFileName, pDstPath, g_Config.m_ClDemoSliceBegin, g_Config.m_ClDemoSliceEnd, pfnFilter, pUser); diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index d9b862ef0..f092f36fe 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -112,7 +112,7 @@ MACRO_CONFIG_INT(GfxHighDetail, gfx_high_detail, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_C MACRO_CONFIG_INT(GfxTextureQuality, gfx_texture_quality, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") #endif MACRO_CONFIG_INT(GfxFsaaSamples, gfx_fsaa_samples, 0, 0, 16, CFGFLAG_SAVE|CFGFLAG_CLIENT, "FSAA Samples") -MACRO_CONFIG_INT(GfxRefreshRate, gfx_refresh_rate, 0, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen refresh rate") +MACRO_CONFIG_INT(GfxRefreshRate, gfx_refresh_rate, 0, 0, 10000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen refresh rate") MACRO_CONFIG_INT(GfxFinish, gfx_finish, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(GfxBackgroundRender, gfx_backgroundrender, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Render graphics when window is in background") MACRO_CONFIG_INT(GfxTextOverlay, gfx_text_overlay, 10, 1, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Stop rendering textoverlay in editor or with entities: high value = less details = more speed") diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 5114b4104..79691c6d1 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -1077,7 +1077,9 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView) str_format(aBuf, sizeof(aBuf), "%s: %s", Localize("Refresh Rate"), "∞"); UI()->DoLabelScaled(&Label, aBuf, 14.0f, -1); Button.HMargin(2.0f, &Button); - g_Config.m_GfxRefreshRate = static_cast(DoScrollbarH(&g_Config.m_GfxRefreshRate, &Button, g_Config.m_GfxRefreshRate/1000.0f)*1000.0f+0.1f); + int NewRefreshRate = static_cast(DoScrollbarH(&g_Config.m_GfxRefreshRate, &Button, (min(g_Config.m_GfxRefreshRate, 1000))/1000.0f)*1000.0f+0.1f); + if(g_Config.m_GfxRefreshRate <= 1000 || NewRefreshRate < 1000) + g_Config.m_GfxRefreshRate = NewRefreshRate; CUIRect Text; MainView.HSplitTop(20.0f, 0, &MainView);