diff --git a/extern/aurora b/extern/aurora index 509021de0a..e145b9ec20 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 509021de0a45f3318769a0f15265b432747bc103 +Subproject commit e145b9ec206c70c3b67c4041e544b456f7037bbb diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 2aeb2bf43a..9ea07b2330 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -277,7 +277,6 @@ struct UserSettings { ConfigVar isoVerification; ConfigVar graphicsBackend; ConfigVar skipPreLaunchUI; - ConfigVar showPipelineCompilation; ConfigVar wasPresetChosen; ConfigVar checkForUpdates; ConfigVar cardFileType; diff --git a/res/rml/overlay.rcss b/res/rml/overlay.rcss index 9a86843295..384c7a2a9b 100644 --- a/res/rml/overlay.rcss +++ b/res/rml/overlay.rcss @@ -21,6 +21,7 @@ body { } fps, +pipeline-progress, toast { position: absolute; border: 1dp #92875B; @@ -98,7 +99,7 @@ toast message row.muted { opacity: 0.5; } -toast progress { +progress { height: 4dp; position: absolute; left: 0; @@ -106,10 +107,50 @@ toast progress { width: 100%; } -toast progress fill { +progress fill { background-color: rgba(194, 164, 45, 80%); } +pipeline-progress { + left: 12dp; + bottom: 12dp; + display: flex; + flex-flow: column; + z-index: 100; + min-width: 260dp; + max-width: 90%; + padding: 10dp 16dp 12dp; + border-radius: 7dp; + overflow: hidden; + filter: opacity(0); + transition: filter 0.2s linear-in-out; + pointer-events: none; +} + +pipeline-progress[open] { + filter: opacity(1); +} + +pipeline-status { + display: flex; + align-items: center; + gap: 8dp; + font-size: 18dp; + font-weight: normal; + white-space: nowrap; +} + +icon.pipeline-spinner { + width: 1.2em; + height: 1.2em; + line-height: 1.2em; + font-size: 1.2em; + color: #C2A42D; + text-align: center; + transform-origin: center; + animation: 1s linear infinite pipeline-spinner-spin; +} + toast.achievement { border: 1dp #C2A42D; } @@ -310,6 +351,15 @@ logo img.outer { } } +@keyframes pipeline-spinner-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + @media (max-height: 640dp) { toast { top: 20dp; diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index e6d44e1d83..f34594deb2 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -381,7 +381,6 @@ namespace dusk { void ImGuiConsole::PostDraw() { m_menuTools.afterDraw(); - ShowPipelineProgress(); } void ImGuiConsole::UpdateDragScroll() { @@ -530,31 +529,4 @@ namespace dusk { return false; } - void ImGuiConsole::ShowPipelineProgress() { - const auto* stats = aurora_get_stats(); - const u32 queuedPipelines = stats->queuedPipelines; - if (queuedPipelines == 0 || !getSettings().backend.showPipelineCompilation) { - return; - } - const u32 createdPipelines = stats->createdPipelines; - const u32 totalPipelines = queuedPipelines + createdPipelines; - - const auto* viewport = ImGui::GetMainViewport(); - const auto padding = viewport->WorkPos.y + 10.f; - const auto halfWidth = viewport->GetWorkCenter().x; - ImGui::SetNextWindowPos(ImVec2{halfWidth, padding}, ImGuiCond_Always, ImVec2{0.5f, 0.f}); - ImGui::SetNextWindowSize(ImVec2{halfWidth, 0.f}, ImGuiCond_Always); - ImGui::SetNextWindowBgAlpha(0.65f); - ImGui::Begin("Pipelines", nullptr, - ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing); - const auto percent = static_cast(createdPipelines) / static_cast(totalPipelines); - const auto progressStr = fmt::format("Processing pipelines: {} / {}", createdPipelines, totalPipelines); - const auto textSize = ImGui::CalcTextSize(progressStr.data(), progressStr.data() + progressStr.size()); - ImGui::NewLine(); - ImGui::SameLine(ImGui::GetWindowWidth() / 2.f - textSize.x + textSize.x / 2.f); - ImGuiStringViewText(progressStr); - ImGui::ProgressBar(percent); - ImGui::End(); - } } diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index 5fe823d1cd..0abe1b36b7 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -40,7 +40,6 @@ private: // Keep always last ImGuiMenuTools m_menuTools; - void ShowPipelineProgress(); void UpdateDragScroll(); }; diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index a2fa271822..b4ef5f827e 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -160,7 +160,6 @@ UserSettings g_userSettings = { .isoVerification {"backend.isoVerification", DiscVerificationState::Unknown}, .graphicsBackend {"backend.graphicsBackend", "auto"}, .skipPreLaunchUI {"backend.skipPreLaunchUI", false}, - .showPipelineCompilation {"backend.showPipelineCompilation", false}, .wasPresetChosen {"backend.wasPresetChosen", false}, .checkForUpdates {"backend.checkForUpdates", true}, .cardFileType {"backend.cardFileType", static_cast(CARD_GCIFOLDER)}, @@ -357,7 +356,6 @@ void registerSettings() { Register(g_userSettings.backend.isoVerification); Register(g_userSettings.backend.graphicsBackend); Register(g_userSettings.backend.skipPreLaunchUI); - Register(g_userSettings.backend.showPipelineCompilation); Register(g_userSettings.backend.wasPresetChosen); Register(g_userSettings.backend.checkForUpdates); Register(g_userSettings.backend.cardFileType); diff --git a/src/dusk/ui/overlay.cpp b/src/dusk/ui/overlay.cpp index 192b0ebf6a..49ca861a5e 100644 --- a/src/dusk/ui/overlay.cpp +++ b/src/dusk/ui/overlay.cpp @@ -1,9 +1,9 @@ #include "overlay.hpp" #include "aurora/lib/logging.hpp" +#include "controller_config.hpp" #include "dusk/achievements.h" #include "dusk/action_bindings.h" -#include "controller_config.hpp" #include "dusk/livesplit.h" #include "dusk/settings.h" #include "dusk/speedrun.h" @@ -33,6 +33,13 @@ const Rml::String kDocumentSource = R"RML( + + + + + + + @@ -48,6 +55,7 @@ constexpr std::array, 3> kAutoSaveLayers{{ }}; constexpr auto kMenuNotificationDuration = std::chrono::milliseconds(2500); +constexpr auto kPipelineProgressOpenDelay = std::chrono::milliseconds(250); constexpr std::array kFpsCorners = {"tl", "tr", "bl", "br"}; @@ -160,8 +168,8 @@ Rml::Element* create_menu_notification(Rml::Element* parent) { Rml::String padButton{}; SDL_Gamepad* gamepad = gamepad_for_port(PAD_CHAN0); if (isActionBound(ActionBinds::OPEN_DUSKLIGHT_MENU, PAD_CHAN0) && gamepad != nullptr) { - padButton = native_button_name(gamepad, - getActionBindButton(ActionBinds::OPEN_DUSKLIGHT_MENU, PAD_CHAN0)); + padButton = native_button_name( + gamepad, getActionBindButton(ActionBinds::OPEN_DUSKLIGHT_MENU, PAD_CHAN0)); } else { padButton = back_button_name(); } @@ -197,6 +205,9 @@ static std::string FormatTime(OSTime ticks) { Overlay::Overlay() : Document(kDocumentSource, true) { mFpsCounter = mDocument->GetElementById("fps"); + mPipelineProgress = mDocument->GetElementById("pipeline-progress"); + mPipelineProgressLabel = mDocument->GetElementById("pipeline-progress-label"); + mPipelineProgressBar = mDocument->GetElementById("pipeline-progress-bar"); mSpeedrunTimer = mDocument->GetElementById("speedrun-timer"); mSpeedrunRta = mDocument->GetElementById("speedrun-rta"); mSpeedrunIgt = mDocument->GetElementById("speedrun-igt"); @@ -258,6 +269,8 @@ void Overlay::update() { } } + update_pipeline_progress(); + #if !(defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST)) if (getSettings().game.speedrunMode && getSettings().game.liveSplitEnabled) { dusk::speedrun::updateLiveSplit(); @@ -309,7 +322,8 @@ void Overlay::update() { mSpeedrunRta->RemoveAttribute("open"); } - mSpeedrunIgt->SetInnerRML(escape(fmt::format("IGT {}", FormatTime(m_speedrunInfo.m_igtTimer)))); + mSpeedrunIgt->SetInnerRML( + escape(fmt::format("IGT {}", FormatTime(m_speedrunInfo.m_igtTimer)))); } else { mSpeedrunTimer->RemoveAttribute("open"); } @@ -373,10 +387,8 @@ void Overlay::update() { std::chrono::duration(clock::now() - mCurrentToastStartTime).count(); const float ratio = duration > 0.0f ? std::clamp(elapsed / duration, 0.0f, 1.0f) : 1.0f; const auto remaining = 1.f - ratio; - Rml::ElementList list; - mDocument->GetElementsByTagName(list, "progress"); - for (auto* elem : list) { - elem->SetAttribute("value", remaining); + if (auto* progress = mCurrentToast->QuerySelector("progress")) { + progress->SetAttribute("value", remaining); } if (remaining == 0.f) { if (mCurrentToast->IsPseudoClassSet("done") || @@ -395,6 +407,52 @@ void Overlay::update() { } } +void Overlay::update_pipeline_progress() { + if (mPipelineProgress == nullptr || mPipelineProgressLabel == nullptr || + mPipelineProgressBar == nullptr) + { + return; + } + + const auto* stats = aurora_get_stats(); + const uint32_t queuedPipelines = stats != nullptr ? stats->queuedPipelines : 0; + if (queuedPipelines == 0) { + mPipelineProgress->RemoveAttribute("open"); + mPipelineProgressActive = false; + mPipelineBatchCreatedBase = 0; + mLastQueuedPipelines = 0; + return; + } + + const uint32_t createdPipelines = stats->createdPipelines; + if (!mPipelineProgressActive || createdPipelines < mPipelineBatchCreatedBase) { + mPipelineProgressActive = true; + mPipelineBatchCreatedBase = createdPipelines; + mPipelineProgressStartTime = clock::now(); + mLastQueuedPipelines = 0; + } + + const uint32_t builtPipelines = createdPipelines - mPipelineBatchCreatedBase; + const uint32_t totalPipelines = queuedPipelines + builtPipelines; + const float progress = totalPipelines > 0 ? static_cast(builtPipelines) / + static_cast(totalPipelines) : + 0.0f; + + if (queuedPipelines != mLastQueuedPipelines) { + mLastQueuedPipelines = queuedPipelines; + const auto noun = queuedPipelines == 1 ? "pipeline" : "pipelines"; + mPipelineProgressLabel->SetInnerRML( + escape(fmt::format("Building {} {}", queuedPipelines, noun))); + } + mPipelineProgressBar->SetAttribute("value", progress); + + if (clock::now() >= mPipelineProgressStartTime + kPipelineProgressOpenDelay) { + mPipelineProgress->SetAttribute("open", ""); + } else { + mPipelineProgress->RemoveAttribute("open"); + } +} + bool Overlay::handle_nav_command(Rml::Event& event, NavCommand cmd) { Log.warn("Overlay received nav command: {}", magic_enum::enum_name(cmd)); return false; diff --git a/src/dusk/ui/overlay.hpp b/src/dusk/ui/overlay.hpp index a8ccde5b9d..34af2b1d52 100644 --- a/src/dusk/ui/overlay.hpp +++ b/src/dusk/ui/overlay.hpp @@ -16,7 +16,13 @@ public: protected: bool handle_nav_command(Rml::Event& event, NavCommand cmd) override; +private: + void update_pipeline_progress(); + Rml::Element* mFpsCounter = nullptr; + Rml::Element* mPipelineProgress = nullptr; + Rml::Element* mPipelineProgressLabel = nullptr; + Rml::Element* mPipelineProgressBar = nullptr; Rml::Element* mCurrentToast = nullptr; Rml::Element* mControllerWarning = nullptr; Rml::Element* mMenuNotification = nullptr; @@ -25,7 +31,11 @@ protected: Rml::Element* mSpeedrunIgt = nullptr; clock::time_point mCurrentToastStartTime; clock::time_point mMenuNotificationStartTime; + clock::time_point mPipelineProgressStartTime; Uint64 mFpsLastUpdate = 0; + uint32_t mPipelineBatchCreatedBase = 0; + uint32_t mLastQueuedPipelines = 0; + bool mPipelineProgressActive = false; }; } // namespace dusk::ui diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index f4f075a6dc..b255e101c1 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -1485,11 +1485,6 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { .helpText = "When starting Dusklight, skip the main menu and boot straight into the " "game if a disc image is available.", }); - config_bool_select(leftPane, rightPane, getSettings().backend.showPipelineCompilation, - { - .key = "Show Pipeline Compilation", - .helpText = "Show an overlay when shaders are being compiled for your hardware.", - }); config_bool_select(leftPane, rightPane, getSettings().backend.checkForUpdates, { .key = "Check for Updates",