From 5f9d97a2aa827f9e098870851dd50cd076b32426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 21 Jun 2024 21:31:21 +0200 Subject: [PATCH 1/5] Extract `CUi::RenderProgressBar` function --- src/game/client/components/menus.cpp | 4 +--- src/game/client/components/menus_start.cpp | 8 +++----- src/game/client/ui.cpp | 8 ++++++++ src/game/client/ui.h | 3 +++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 0eb85a905..1983ce456 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -1915,9 +1915,7 @@ void CMenus::RenderPopupLoading(CUIRect Screen) Box.HSplitTop(20.0f, nullptr, &Box); Box.HSplitTop(24.0f, &ProgressBar, &Box); ProgressBar.VMargin(20.0f, &ProgressBar); - ProgressBar.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, 5.0f); - ProgressBar.w = maximum(10.0f, (ProgressBar.w * Client()->MapDownloadAmount()) / Client()->MapDownloadTotalsize()); - ProgressBar.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f), IGraphics::CORNER_ALL, 5.0f); + Ui()->RenderProgressBar(ProgressBar, Client()->MapDownloadAmount() / (float)Client()->MapDownloadTotalsize()); } CUIRect Button; diff --git a/src/game/client/components/menus_start.cpp b/src/game/client/components/menus_start.cpp index 46875a33d..cd5142ffe 100644 --- a/src/game/client/components/menus_start.cpp +++ b/src/game/client/components/menus_start.cpp @@ -263,13 +263,11 @@ void CMenus::RenderStartMenu(CUIRect MainView) } else if(State >= IUpdater::GETTING_MANIFEST && State < IUpdater::NEED_RESTART) { - CUIRect ProgressBar, Percent; - Part.VSplitLeft(100.0f, &ProgressBar, &Percent); + CUIRect ProgressBar; + Part.VSplitLeft(100.0f, &ProgressBar, nullptr); ProgressBar.y += 2.0f; ProgressBar.HMargin(1.0f, &ProgressBar); - ProgressBar.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, 5.0f); - ProgressBar.w = clamp((float)Updater()->GetCurrentPercent(), 10.0f, 100.0f); - ProgressBar.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f), IGraphics::CORNER_ALL, 5.0f); + Ui()->RenderProgressBar(ProgressBar, Updater()->GetCurrentPercent() / 100.0f); } #elif defined(CONF_INFORM_UPDATE) if(str_comp(Client()->LatestVersion(), "0") != 0) diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index a08545c43..e29646fa3 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -1333,6 +1333,14 @@ bool CUi::DoScrollbarOption(const void *pId, int *pOption, const CUIRect *pRect, return false; } +void CUi::RenderProgressBar(CUIRect ProgressBar, float Progress) +{ + const float Rounding = minimum(5.0f, ProgressBar.h / 2.0f); + ProgressBar.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, Rounding); + ProgressBar.w = maximum(ProgressBar.w * Progress, 2 * Rounding); + ProgressBar.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f), IGraphics::CORNER_ALL, Rounding); +} + void CUi::RenderProgressSpinner(vec2 Center, float OuterRadius, const SProgressSpinnerProperties &Props) const { Graphics()->TextureClear(); diff --git a/src/game/client/ui.h b/src/game/client/ui.h index f9a802e83..7ee062aea 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -599,6 +599,9 @@ public: float DoScrollbarH(const void *pId, const CUIRect *pRect, float Current, const ColorRGBA *pColorInner = nullptr); bool DoScrollbarOption(const void *pId, int *pOption, const CUIRect *pRect, const char *pStr, int Min, int Max, const IScrollbarScale *pScale = &ms_LinearScrollbarScale, unsigned Flags = 0u, const char *pSuffix = ""); + // progress bar + void RenderProgressBar(CUIRect ProgressBar, float Progress); + // progress spinner void RenderProgressSpinner(vec2 Center, float OuterRadius, const SProgressSpinnerProperties &Props = {}) const; From d1516a14a5c98e4e5beced9bb7a13ed982af985a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 23 Jun 2024 15:21:54 +0200 Subject: [PATCH 2/5] Refactor client loading screen rendering Use `CUi::RenderProgressBar` function also for client loading, which means the filled progress bar background will also be rendered while loading. Replace static variable `s_LastLoadRender` with member variable. Encapsulate menu loading state in class `CLoadingState`. --- src/game/client/components/menus.cpp | 48 +++++++++++++++------------- src/game/client/components/menus.h | 10 ++++-- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 1983ce456..9fe01f0b0 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -734,17 +734,16 @@ void CMenus::RenderLoading(const char *pCaption, const char *pContent, int Incre { // TODO: not supported right now due to separate render thread - static std::chrono::nanoseconds s_LastLoadRender{0}; - const int CurLoadRenderCount = m_LoadCurrent; - m_LoadCurrent += IncreaseCounter; - const float Percent = CurLoadRenderCount / (float)m_LoadTotal; + const int CurLoadRenderCount = m_LoadingState.m_Current; + m_LoadingState.m_Current += IncreaseCounter; // make sure that we don't render for each little thing we load // because that will slow down loading if we have vsync - if(time_get_nanoseconds() - s_LastLoadRender < std::chrono::nanoseconds(1s) / 60l) + const std::chrono::nanoseconds Now = time_get_nanoseconds(); + if(Now - m_LoadingState.m_LastRender < std::chrono::nanoseconds(1s) / 60l) return; - s_LastLoadRender = time_get_nanoseconds(); + m_LoadingState.m_LastRender = Now; // need up date this here to get correct ms_GuiColor = color_cast(ColorHSLA(g_Config.m_UiColor, true)); @@ -756,27 +755,30 @@ void CMenus::RenderLoading(const char *pCaption, const char *pContent, int Incre RenderBackground(); } - CUIRect Box = *Ui()->Screen(); - Box.Margin(160.0f, &Box); + CUIRect Box; + Ui()->Screen()->Margin(160.0f, &Box); Graphics()->BlendNormal(); - Graphics()->TextureClear(); - Box.Draw(ColorRGBA{0, 0, 0, 0.50f}, IGraphics::CORNER_ALL, 15.0f); + Box.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f), IGraphics::CORNER_ALL, 15.0f); + Box.Margin(20.0f, &Box); - CUIRect Part; - Box.HSplitTop(20.f, nullptr, &Box); - Box.HSplitTop(24.f, &Part, &Box); - Part.VMargin(20.f, &Part); - Ui()->DoLabel(&Part, pCaption, 24.f, TEXTALIGN_MC); + CUIRect Label; + Box.HSplitTop(24.0f, &Label, &Box); + Ui()->DoLabel(&Label, pCaption, 24.0f, TEXTALIGN_MC); - Box.HSplitTop(20.f, nullptr, &Box); - Box.HSplitTop(24.f, &Part, &Box); - Part.VMargin(20.f, &Part); - Ui()->DoLabel(&Part, pContent, 20.0f, TEXTALIGN_MC); + Box.HSplitTop(20.0f, nullptr, &Box); + Box.HSplitTop(24.0f, &Label, &Box); + Ui()->DoLabel(&Label, pContent, 20.0f, TEXTALIGN_MC); if(RenderLoadingBar) - Graphics()->DrawRect(Box.x + 40, Box.y + Box.h - 75, (Box.w - 80) * Percent, 25, ColorRGBA(1.0f, 1.0f, 1.0f, 0.75f), IGraphics::CORNER_ALL, 5.0f); + { + CUIRect ProgressBar; + Box.HSplitBottom(30.0f, &Box, nullptr); + Box.HSplitBottom(25.0f, &Box, &ProgressBar); + ProgressBar.VMargin(20.0f, &ProgressBar); + Ui()->RenderProgressBar(ProgressBar, CurLoadRenderCount / (float)m_LoadingState.m_Total); + } Client()->UpdateAndSwap(); } @@ -867,10 +869,10 @@ void CMenus::OnInit() // setup load amount const int NumMenuImages = 5; - m_LoadCurrent = 0; - m_LoadTotal = g_pData->m_NumImages + NumMenuImages + GameClient()->ComponentCount(); + m_LoadingState.m_Current = 0; + m_LoadingState.m_Total = g_pData->m_NumImages + NumMenuImages + GameClient()->ComponentCount(); if(!g_Config.m_ClThreadsoundloading) - m_LoadTotal += g_pData->m_NumSounds; + m_LoadingState.m_Total += g_pData->m_NumSounds; m_IsInit = true; diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 67cf1a408..7bd0e7303 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -186,8 +186,14 @@ protected: const CMenuImage *FindMenuImage(const char *pName); // loading - int m_LoadCurrent; - int m_LoadTotal; + class CLoadingState + { + public: + std::chrono::nanoseconds m_LastRender{0}; + int m_Current; + int m_Total; + }; + CLoadingState m_LoadingState; // char m_aMessageTopic[512]; From ab729e0346345b39a42fbb872daab83b49bfbcc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 23 Jun 2024 15:25:28 +0200 Subject: [PATCH 3/5] Minor refactoring of client loading Avoid duplicate `Localize` calls while loading. Start component counters at 1 instead of 0 since the value was always incremented by 1 before being used. Extract variable `NumComponents` and also use it for the special loading message, so it should consistently be shown only for the first component (i.e. the last component being initialized) also when more components will be added. --- src/game/client/gameclient.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index ee21b1e2a..8e0a77a46 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -265,20 +265,23 @@ void CGameClient::OnInit() Client()->UpdateAndSwap(); const char *pLoadingDDNetCaption = Localize("Loading DDNet Client"); + const char *pLoadingMessageComponents = Localize("Initializing components"); + const char *pLoadingMessageComponentsSpecial = Localize("Why are you slowmo replaying to read this?"); + char aLoadingMessage[256]; // init all components - int SkippedComps = 0; - int CompCounter = 0; - for(int i = m_vpAll.size() - 1; i >= 0; --i) + int SkippedComps = 1; + int CompCounter = 1; + const int NumComponents = ComponentCount(); + for(int i = NumComponents - 1; i >= 0; --i) { m_vpAll[i]->OnInit(); // try to render a frame after each component, also flushes GPU uploads if(m_Menus.IsInit()) { - char aBuff[256]; - str_format(aBuff, std::size(aBuff), "%s [%d/%d]", CompCounter == 40 ? Localize("Why are you slowmo replaying to read this?") : Localize("Initializing components"), (CompCounter + 1), (int)ComponentCount()); - m_Menus.RenderLoading(pLoadingDDNetCaption, aBuff, 1 + SkippedComps); - SkippedComps = 0; + str_format(aLoadingMessage, std::size(aLoadingMessage), "%s [%d/%d]", CompCounter == NumComponents ? pLoadingMessageComponentsSpecial : pLoadingMessageComponents, CompCounter, NumComponents); + m_Menus.RenderLoading(pLoadingDDNetCaption, aLoadingMessage, SkippedComps); + SkippedComps = 1; } else { @@ -293,6 +296,7 @@ void CGameClient::OnInit() m_HudSkinLoaded = false; // setup load amount, load textures + const char *pLoadingMessageAssets = Localize("Initializing assets"); for(int i = 0; i < g_pData->m_NumImages; i++) { if(i == IMAGE_GAME) @@ -309,7 +313,7 @@ void CGameClient::OnInit() g_pData->m_aImages[i].m_Id = IGraphics::CTextureHandle(); else g_pData->m_aImages[i].m_Id = Graphics()->LoadTexture(g_pData->m_aImages[i].m_pFilename, IStorage::TYPE_ALL); - m_Menus.RenderLoading(pLoadingDDNetCaption, Localize("Initializing assets"), 1); + m_Menus.RenderLoading(pLoadingDDNetCaption, pLoadingMessageAssets, 1); } m_GameWorld.m_pCollision = Collision(); From e135145363df5e99a71378cf8154acc0d90ccb65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 23 Jun 2024 15:31:39 +0200 Subject: [PATCH 4/5] Ensure logged time covers whole `CGameClient::OnInit` function Get start time as soon as possible and log time for gameclient initialization at the end of the `CGameClient::OnInit` function so this time measurement should cover the entire function. Use `log_trace` and change spelling for consistency (`initialisation` -> `initialization`). --- src/game/client/gameclient.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 8e0a77a46..6e6f2aab8 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -203,6 +204,8 @@ void CGameClient::OnConsoleInit() void CGameClient::OnInit() { + const int64_t OnInitStart = time_get(); + Client()->SetLoadingCallback([this](IClient::ELoadingCallbackDetail Detail) { const char *pTitle; if(Detail == IClient::LOADING_CALLBACK_DETAIL_DEMO || DemoPlayer()->IsPlaying()) @@ -236,8 +239,6 @@ void CGameClient::OnInit() m_UI.Init(Kernel()); m_RenderTools.Init(Graphics(), TextRender()); - int64_t Start = time_get(); - if(GIT_SHORTREV_HASH) { str_format(m_aDDNetVersionStr, sizeof(m_aDDNetVersionStr), "%s %s (%s)", GAME_NAME, GAME_RELEASE_VERSION, GIT_SHORTREV_HASH); @@ -345,11 +346,6 @@ void CGameClient::OnInit() } } - int64_t End = time_get(); - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "initialisation finished after %.2fms", ((End - Start) * 1000) / (float)time_freq()); - Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "gameclient", aBuf); - m_MapImages.SetTextureScale(g_Config.m_ClTextEntitiesSize); // Aggressively try to grab window again since some Windows users report @@ -368,6 +364,8 @@ void CGameClient::OnInit() int Size = m_vpAll[i]->Sizeof(); pChecksum->m_aComponentsChecksum[i] = Size; } + + log_trace("gameclient", "initialization finished after %.2fms", (time_get() - OnInitStart) * 1000.0f / (float)time_freq()); } void CGameClient::OnUpdate() From 877096f97708ebcf83467a5c222862e1c8969794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 23 Jun 2024 16:05:19 +0200 Subject: [PATCH 5/5] Fix alignment of progress bar while updating client The progress bar was positioned relative to the updating message. Now the progress bar is always right-aligned with the other buttons. --- src/game/client/components/menus_start.cpp | 80 ++++++++++------------ 1 file changed, 35 insertions(+), 45 deletions(-) diff --git a/src/game/client/components/menus_start.cpp b/src/game/client/components/menus_start.cpp index cd5142ffe..ec505159b 100644 --- a/src/game/client/components/menus_start.cpp +++ b/src/game/client/components/menus_start.cpp @@ -199,16 +199,42 @@ void CMenus::RenderStartMenu(CUIRect MainView) // render version CUIRect VersionUpdate, CurVersion; - MainView.HSplitBottom(20.0f, 0, &VersionUpdate); - - VersionUpdate.VSplitRight(50.0f, &CurVersion, 0); + MainView.HSplitBottom(20.0f, nullptr, &VersionUpdate); + VersionUpdate.VSplitRight(50.0f, &CurVersion, nullptr); VersionUpdate.VMargin(VMargin, &VersionUpdate); + Ui()->DoLabel(&CurVersion, GAME_RELEASE_VERSION, 14.0f, TEXTALIGN_MR); + #if defined(CONF_AUTOUPDATE) - char aBuf[64]; - CUIRect Part; - IUpdater::EUpdaterState State = Updater()->GetCurrentState(); - bool NeedUpdate = str_comp(Client()->LatestVersion(), "0"); + CUIRect UpdateButton; + VersionUpdate.VSplitRight(100.0f, &VersionUpdate, &UpdateButton); + VersionUpdate.VSplitRight(10.0f, &VersionUpdate, nullptr); + + char aBuf[128]; + const IUpdater::EUpdaterState State = Updater()->GetCurrentState(); + const bool NeedUpdate = str_comp(Client()->LatestVersion(), "0"); + + if(State == IUpdater::CLEAN && NeedUpdate) + { + static CButtonContainer s_VersionUpdate; + if(DoButton_Menu(&s_VersionUpdate, Localize("Update now"), 0, &UpdateButton, 0, IGraphics::CORNER_ALL, 5.0f, 0.0f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f))) + { + Updater()->InitiateUpdate(); + } + } + else if(State == IUpdater::NEED_RESTART) + { + static CButtonContainer s_VersionUpdate; + if(DoButton_Menu(&s_VersionUpdate, Localize("Restart"), 0, &UpdateButton, 0, IGraphics::CORNER_ALL, 5.0f, 0.0f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f))) + { + Client()->Restart(); + } + } + else if(State >= IUpdater::GETTING_MANIFEST && State < IUpdater::NEED_RESTART) + { + Ui()->RenderProgressBar(UpdateButton, Updater()->GetCurrentPercent() / 100.0f); + } + if(State == IUpdater::CLEAN && NeedUpdate) { str_format(aBuf, sizeof(aBuf), Localize("DDNet %s is out!"), Client()->LatestVersion()); @@ -235,53 +261,17 @@ void CMenus::RenderStartMenu(CUIRect MainView) TextRender()->TextColor(1.0f, 0.4f, 0.4f, 1.0f); } Ui()->DoLabel(&VersionUpdate, aBuf, 14.0f, TEXTALIGN_ML); - TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); - - VersionUpdate.VSplitLeft(TextRender()->TextWidth(14.0f, aBuf, -1, -1.0f) + 10.0f, 0, &Part); - - if(State == IUpdater::CLEAN && NeedUpdate) - { - CUIRect Update; - Part.VSplitLeft(100.0f, &Update, NULL); - - static CButtonContainer s_VersionUpdate; - if(DoButton_Menu(&s_VersionUpdate, Localize("Update now"), 0, &Update, 0, IGraphics::CORNER_ALL, 5.0f, 0.0f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f))) - { - Updater()->InitiateUpdate(); - } - } - else if(State == IUpdater::NEED_RESTART) - { - CUIRect Restart; - Part.VSplitLeft(50.0f, &Restart, &Part); - - static CButtonContainer s_VersionUpdate; - if(DoButton_Menu(&s_VersionUpdate, Localize("Restart"), 0, &Restart, 0, IGraphics::CORNER_ALL, 5.0f, 0.0f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f))) - { - Client()->Restart(); - } - } - else if(State >= IUpdater::GETTING_MANIFEST && State < IUpdater::NEED_RESTART) - { - CUIRect ProgressBar; - Part.VSplitLeft(100.0f, &ProgressBar, nullptr); - ProgressBar.y += 2.0f; - ProgressBar.HMargin(1.0f, &ProgressBar); - Ui()->RenderProgressBar(ProgressBar, Updater()->GetCurrentPercent() / 100.0f); - } + TextRender()->TextColor(TextRender()->DefaultTextColor()); #elif defined(CONF_INFORM_UPDATE) if(str_comp(Client()->LatestVersion(), "0") != 0) { char aBuf[64]; str_format(aBuf, sizeof(aBuf), Localize("DDNet %s is out!"), Client()->LatestVersion()); - TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); + TextRender()->TextColor(TextRender()->DefaultTextColor()); Ui()->DoLabel(&VersionUpdate, aBuf, 14.0f, TEXTALIGN_MC); - TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } #endif - Ui()->DoLabel(&CurVersion, GAME_RELEASE_VERSION, 14.0f, TEXTALIGN_MR); - if(NewPage != -1) { m_ShowStart = false;