From 78ddf14b5abfeb6ffbf99f6a17cfb4bdd4d4cf81 Mon Sep 17 00:00:00 2001 From: Mithrand Date: Tue, 8 Jul 2025 19:51:37 +0200 Subject: [PATCH] fix a performance issue in workshop gui this fix will make the main menu render faster: - by optizing the code, reducing loops; - by skipping some runs, sacrificing update rate for performance (it's a progress bar, one visual update per second would be more than enough) the menu was running at 60-110 fps before, now it runs at 600-700 fps --- .../rd_vgui_workshop_download_progress.cpp | 167 ++++++++++-------- 1 file changed, 96 insertions(+), 71 deletions(-) diff --git a/src/game/client/swarm/vgui/rd_vgui_workshop_download_progress.cpp b/src/game/client/swarm/vgui/rd_vgui_workshop_download_progress.cpp index a3b7f810e..2878ec5c0 100644 --- a/src/game/client/swarm/vgui/rd_vgui_workshop_download_progress.cpp +++ b/src/game/client/swarm/vgui/rd_vgui_workshop_download_progress.cpp @@ -33,113 +33,138 @@ void CRD_VGUI_Workshop_Download_Progress::ApplySchemeSettings( vgui::IScheme *pS void CRD_VGUI_Workshop_Download_Progress::OnThink() { + static int s_nFrameSkip = 0; + static int s_nLastAddonCount = -1; + static int s_nInQueue = 0; + static int s_iBestAddonIndex = -1; + static uint64 s_nBestBytesDownloaded = 0, s_nBestBytesTotal = 0; + static bool s_bFoundDownloadWithProgress = false; + + // Only update every nth call to OnThink (reduce update rate for performance) + float fTickInterval = gpGlobals->interval_per_tick; + float fTickRate = (fTickInterval > 0) ? (1.0f / fTickInterval) : 0.0f; + + if (++s_nFrameSkip < fTickRate) + { + return; + } + s_nFrameSkip = 0; + BaseClass::OnThink(); - ISteamUGC *pUGC = SteamUGC(); - AssertOnce( pUGC ); - if ( !pUGC ) + ISteamUGC* pUGC = SteamUGC(); + AssertOnce(pUGC); + if (!pUGC) { return; } - int nInQueue = 0; - FOR_EACH_VEC( g_ReactiveDropWorkshop.m_EnabledAddons, i ) + const int addonCount = g_ReactiveDropWorkshop.m_EnabledAddons.Count(); + + // Only re-scan the addon list if the count has changed (sacrifice accuracy for speed) + if (addonCount != s_nLastAddonCount) { - PublishedFileId_t nPublishedFileID = g_ReactiveDropWorkshop.m_EnabledAddons[i].details.m_nPublishedFileId; - if ( pUGC->GetItemState( nPublishedFileID ) & k_EItemStateDownloadPending ) + s_nLastAddonCount = addonCount; + s_nInQueue = 0; + s_iBestAddonIndex = -1; + s_bFoundDownloadWithProgress = false; + s_nBestBytesDownloaded = 0; + s_nBestBytesTotal = 0; + + for (int i = 0; i < addonCount; ++i) { - nInQueue++; + const auto& addon = g_ReactiveDropWorkshop.m_EnabledAddons[i]; + PublishedFileId_t nPublishedFileID = addon.details.m_nPublishedFileId; + uint32 itemState = pUGC->GetItemState(nPublishedFileID); + + if (itemState & k_EItemStateDownloadPending) + { + ++s_nInQueue; + if (s_iBestAddonIndex == -1) + s_iBestAddonIndex = i; + } + if (!s_bFoundDownloadWithProgress && (itemState & k_EItemStateDownloading)) + { + uint64 nBytesDownloaded, nBytesTotal; + if (pUGC->GetItemDownloadInfo(nPublishedFileID, &nBytesDownloaded, &nBytesTotal) && nBytesDownloaded > 0) + { + s_iBestAddonIndex = i; + s_nBestBytesDownloaded = nBytesDownloaded; + s_nBestBytesTotal = nBytesTotal; + s_bFoundDownloadWithProgress = true; + } + } } } - if ( nInQueue ) + if (s_nInQueue) { wchar_t wszQueueCount[21]; - V_snwprintf( wszQueueCount, ARRAYSIZE( wszQueueCount ), L"%d", nInQueue ); + V_snwprintf(wszQueueCount, ARRAYSIZE(wszQueueCount), L"%d", s_nInQueue); wchar_t wszQueue[128]; - g_pVGuiLocalize->ConstructString( wszQueue, sizeof( wszQueue ), g_pVGuiLocalize->Find( "#workshop_number_in_queue" ), 1, wszQueueCount ); - m_pLblQueue->SetText( wszQueue ); - m_pLblQueue->SetVisible( true ); - SetZPos( 20 ); + g_pVGuiLocalize->ConstructString(wszQueue, sizeof(wszQueue), g_pVGuiLocalize->Find("#workshop_number_in_queue"), 1, wszQueueCount); + m_pLblQueue->SetText(wszQueue); + m_pLblQueue->SetVisible(true); + SetZPos(20); } else { - m_pLblQueue->SetVisible( false ); - SetZPos( -1 ); + m_pLblQueue->SetVisible(false); + SetZPos(-1); } - int iBestAddonIndex = -1; - FOR_EACH_VEC( g_ReactiveDropWorkshop.m_EnabledAddons, i ) - { - PublishedFileId_t nPublishedFileID = g_ReactiveDropWorkshop.m_EnabledAddons[i].details.m_nPublishedFileId; - if ( pUGC->GetItemState( nPublishedFileID ) & k_EItemStateDownloadPending ) - { - iBestAddonIndex = i; - break; - } - } - FOR_EACH_VEC( g_ReactiveDropWorkshop.m_EnabledAddons, i ) - { - PublishedFileId_t nPublishedFileID = g_ReactiveDropWorkshop.m_EnabledAddons[i].details.m_nPublishedFileId; - if ( pUGC->GetItemState( nPublishedFileID ) & k_EItemStateDownloading ) - { - iBestAddonIndex = i; - break; - } - } - FOR_EACH_VEC( g_ReactiveDropWorkshop.m_EnabledAddons, i ) - { - PublishedFileId_t nPublishedFileID = g_ReactiveDropWorkshop.m_EnabledAddons[i].details.m_nPublishedFileId; - if ( pUGC->GetItemState( nPublishedFileID ) & k_EItemStateDownloading ) - { - uint64 nBytesDownloaded, nBytesTotal; - if ( pUGC->GetItemDownloadInfo( nPublishedFileID, &nBytesDownloaded, &nBytesTotal ) && nBytesDownloaded > 0 ) - { - iBestAddonIndex = i; - break; - } - } - } - if ( iBestAddonIndex == -1 ) + if (s_iBestAddonIndex == -1) { - m_pPnlBackground->SetVisible( false ); - m_pImgPreview->SetImage( (vgui::IImage *) NULL ); - m_pImgPreview->SetVisible( false ); - m_pLblName->SetVisible( false ); - m_pPrgDownload->SetVisible( false ); + m_pPnlBackground->SetVisible(false); + m_pImgPreview->SetImage((vgui::IImage*)NULL); + m_pImgPreview->SetVisible(false); + m_pLblName->SetVisible(false); + m_pPrgDownload->SetVisible(false); return; } - PublishedFileId_t nPublishedFileID = g_ReactiveDropWorkshop.m_EnabledAddons[iBestAddonIndex].details.m_nPublishedFileId; - if ( g_ReactiveDropWorkshop.m_EnabledAddons[iBestAddonIndex].pPreviewImage ) + PublishedFileId_t nPublishedFileID = g_ReactiveDropWorkshop.m_EnabledAddons[s_iBestAddonIndex].details.m_nPublishedFileId; + + if (g_ReactiveDropWorkshop.m_EnabledAddons[s_iBestAddonIndex].pPreviewImage) { - // avoid a repaint if the image is the same as the one we had before. - if ( m_pImgPreview->GetImage() != g_ReactiveDropWorkshop.m_EnabledAddons[iBestAddonIndex].pPreviewImage ) + if (m_pImgPreview->GetImage() != static_cast(g_ReactiveDropWorkshop.m_EnabledAddons[s_iBestAddonIndex].pPreviewImage)) { - m_pImgPreview->SetImage( g_ReactiveDropWorkshop.m_EnabledAddons[iBestAddonIndex].pPreviewImage ); - m_pImgPreview->SetVisible( true ); + m_pImgPreview->SetImage(static_cast(g_ReactiveDropWorkshop.m_EnabledAddons[s_iBestAddonIndex].pPreviewImage)); + m_pImgPreview->SetVisible(true); } } else { - m_pImgPreview->SetImage( (vgui::IImage *) NULL ); - m_pImgPreview->SetVisible( false ); + m_pImgPreview->SetImage((vgui::IImage*)NULL); + m_pImgPreview->SetVisible(false); } - m_pPnlBackground->SetVisible( true ); + m_pPnlBackground->SetVisible(true); wchar_t wszName[k_cchPublishedDocumentTitleMax]; - V_UTF8ToUnicode( g_ReactiveDropWorkshop.m_EnabledAddons[iBestAddonIndex].details.m_rgchTitle, wszName, sizeof( wszName ) ); - m_pLblName->SetText( wszName ); - m_pLblName->SetVisible( true ); + V_UTF8ToUnicode(g_ReactiveDropWorkshop.m_EnabledAddons[s_iBestAddonIndex].details.m_rgchTitle, wszName, sizeof(wszName) / sizeof(wszName[0])); + m_pLblName->SetText(wszName); + m_pLblName->SetVisible(true); - uint64 nBytesDownloaded, nBytesTotal; - if ( pUGC->GetItemDownloadInfo( nPublishedFileID, &nBytesDownloaded, &nBytesTotal ) && nBytesTotal > 0 ) + uint64 nBytesDownloaded = s_nBestBytesDownloaded, nBytesTotal = s_nBestBytesTotal; + if (!s_bFoundDownloadWithProgress) { - m_pPrgDownload->SetProgress( float( nBytesDownloaded ) / float( nBytesTotal ) ); - m_pPrgDownload->SetVisible( true ); + // Only query if not already found in the loop + pUGC->GetItemDownloadInfo(nPublishedFileID, &nBytesDownloaded, &nBytesTotal); + + // We are busy, reset s_nLastAddonCount to force a re-scan + s_nLastAddonCount = -1; + } + if (nBytesTotal > 0) + { + m_pPrgDownload->SetProgress(float(nBytesDownloaded) / float(nBytesTotal)); + m_pPrgDownload->SetVisible(true); + + // We are done, reset s_nLastAddonCount to force a re-scan + s_nLastAddonCount = -1; } else { - m_pPrgDownload->SetVisible( false ); + // We have no work to do + m_pPrgDownload->SetVisible(false); } }