diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 06fc59dbe8..b2c74afde1 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -4,6 +4,7 @@ #pragma once +#include #include "common/common_types.h" #include "video_core/engines/fermi_2d.h" #include "video_core/gpu.h" @@ -11,6 +12,14 @@ namespace VideoCore { +enum class LoadCallbackStage { + Prepare, + Raw, + Binary, + Complete, +}; +using DiskResourceLoadCallback = std::function; + class RasterizerInterface { public: virtual ~RasterizerInterface() {} diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp index 0f3c4bb6cb..617b8c858a 100644 --- a/src/yuzu/loading_screen.cpp +++ b/src/yuzu/loading_screen.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include @@ -13,10 +14,12 @@ #include #include #include -#include +#include +#include #include "common/logging/log.h" #include "core/loader/loader.h" #include "ui_loading_screen.h" +#include "video_core/rasterizer_interface.h" #include "yuzu/loading_screen.h" // Mingw seems to not have QMovie at all. If QMovie is missing then use a single frame instead of an @@ -26,10 +29,63 @@ #endif LoadingScreen::LoadingScreen(QWidget* parent) - : QWidget(parent), ui(std::make_unique()) { + : QWidget(parent), ui(std::make_unique()), + previous_stage(VideoCore::LoadCallbackStage::Complete) { ui->setupUi(this); - // Progress bar is hidden until we have a use for it. - ui->progress_bar->hide(); + + connect(this, &LoadingScreen::LoadProgress, this, &LoadingScreen::OnLoadProgress, + Qt::QueuedConnection); + qRegisterMetaType(); + + stage_translations = { + {VideoCore::LoadCallbackStage::Prepare, tr("Loading...")}, + {VideoCore::LoadCallbackStage::Raw, tr("Preparing Shaders %1 / %2")}, + {VideoCore::LoadCallbackStage::Binary, tr("Loading Shaders %1 / %2")}, + {VideoCore::LoadCallbackStage::Complete, tr("Launching...")}, + }; + progressbar_style = { + {VideoCore::LoadCallbackStage::Prepare, + R"( +QProgressBar { + background-color: black; +} +QProgressBar::chunk { + background-color: white; +})"}, + {VideoCore::LoadCallbackStage::Raw, + R"( +QProgressBar { + background-color: black; + border: 2px solid white; + border-radius: 4px; + padding: 2px; +} +QProgressBar::chunk { + background-color: #0ab9e6; +})"}, + {VideoCore::LoadCallbackStage::Binary, + R"( +QProgressBar { + background-color: black; + border: 2px solid white; + border-radius: 4px; + padding: 2px; +} +QProgressBar::chunk { + background-color: #ff3c28; +})"}, + {VideoCore::LoadCallbackStage::Complete, + R"( +QProgressBar { + background-color: black; + border: 2px solid white; + border-radius: 4px; + padding: 2px; +} +QProgressBar::chunk { + background-color: #ff3c28; +})"}, + }; } LoadingScreen::~LoadingScreen() = default; @@ -46,7 +102,7 @@ void LoadingScreen::Prepare(Loader::AppLoader& loader) { std::make_unique(reinterpret_cast(buffer.data()), buffer.size()); backing_buf = std::make_unique(backing_mem.get()); backing_buf->open(QIODevice::ReadOnly); - animation = std::make_unique(backing_buf.get(), QByteArray("GIF")); + animation = std::make_unique(backing_buf.get(), QByteArray()); animation->start(); ui->banner->setMovie(animation.get()); #endif @@ -57,14 +113,54 @@ void LoadingScreen::Prepare(Loader::AppLoader& loader) { map.loadFromData(buffer.data(), buffer.size()); ui->logo->setPixmap(map); } + + OnLoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 100); } -void LoadingScreen::OnLoadProgress(std::size_t value, std::size_t total) { +void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, + std::size_t total) { + using namespace std::chrono; + auto now = high_resolution_clock::now(); + // reset the timer if the stage changes + if (stage != previous_stage) { + ui->progress_bar->setStyleSheet(progressbar_style[stage]); + previous_stage = stage; + // reset back to fast shader compiling since the stage changed + slow_shader_compile_start = false; + } + // update the max of the progress bar if the number of shaders change if (total != previous_total) { ui->progress_bar->setMaximum(total); previous_total = total; } + + QString estimate; + // If theres a drastic slowdown in the rate, then display an estimate + if (now - previous_time > milliseconds{20}) { + if (!slow_shader_compile_start) { + slow_shader_start = high_resolution_clock::now(); + slow_shader_compile_start = true; + slow_shader_first_value = value; + } + // only calculate an estimate time after a second has passed since stage change + auto diff = duration_cast(now - slow_shader_start); + if (diff > seconds{1}) { + auto eta_mseconds = + static_cast(static_cast(total - slow_shader_first_value) / + (value - slow_shader_first_value) * diff.count()); + estimate = + tr("Estimated Time %1") + .arg(QTime(0, 0, 0, 0) + .addMSecs(std::max(eta_mseconds - diff.count() + 1000, 1000)) + .toString("mm:ss")); + } + } + + // update labels and progress bar + ui->stage->setText(stage_translations[stage].arg(value).arg(total)); + ui->value->setText(estimate); ui->progress_bar->setValue(value); + previous_time = now; } void LoadingScreen::paintEvent(QPaintEvent* event) { diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h index 2a6cf11421..9370ede693 100644 --- a/src/yuzu/loading_screen.h +++ b/src/yuzu/loading_screen.h @@ -4,7 +4,9 @@ #pragma once +#include #include +#include #include #if !QT_CONFIG(movie) @@ -19,6 +21,10 @@ namespace Ui { class LoadingScreen; } +namespace VideoCore { +enum class LoadCallbackStage; +} + class QBuffer; class QByteArray; class QMovie; @@ -39,11 +45,14 @@ public: /// used resources such as the logo and banner. void Clear(); + void OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); + // In order to use a custom widget with a stylesheet, you need to override the paintEvent // See https://wiki.qt.io/How_to_Change_the_Background_Color_of_QWidget void paintEvent(QPaintEvent* event) override; - void OnLoadProgress(std::size_t value, std::size_t total); +signals: + void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); private: #ifndef YUZU_QT_MOVIE_MISSING @@ -53,4 +62,19 @@ private: #endif std::unique_ptr ui; std::size_t previous_total = 0; + VideoCore::LoadCallbackStage previous_stage; + + // Definitions for the differences in text and styling for each stage + std::unordered_map progressbar_style; + std::unordered_map stage_translations; + + // newly generated shaders are added to the end of the file, so when loading and compiling + // shaders, it will start quickly but end slow if new shaders were added since previous launch. + // These variables are used to detect the change in speed so we can generate an ETA + bool slow_shader_compile_start = false; + std::chrono::high_resolution_clock::time_point slow_shader_start; + std::chrono::high_resolution_clock::time_point previous_time; + std::size_t slow_shader_first_value = 0; }; + +Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage); diff --git a/src/yuzu/loading_screen.ui b/src/yuzu/loading_screen.ui index 00579b6709..35d50741e1 100644 --- a/src/yuzu/loading_screen.ui +++ b/src/yuzu/loading_screen.ui @@ -43,20 +43,81 @@ - - - + + + 15 + + + QLayout::SetNoConstraint + + + + + + 0 + 0 + + - font-size: 26px; + background-color: black; color: white; +font: 75 20pt "Arial"; + + + Loading Shaders 387 / 1628 + + + + + + + + 0 + 0 + + + + + 500 + 40 + + + + QProgressBar { +color: white; +border: 2px solid white; +outline-color: black; +border-radius: 20px; +} +QProgressBar::chunk { +background-color: white; +border-radius: 15px; +} - 0 + 50 + + + false Loading Shaders %v out of %m + + + + + + + background-color: black; color: white; +font: 75 15pt "Arial"; + + + Stage 1 of 2. Estimate Time 5m 4s + + +