diff options
author | bunnei <bunneidev@gmail.com> | 2019-01-21 20:12:47 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-21 20:12:47 +0100 |
commit | 125599c2d51fba0bb9466d92382631ed7f34bed9 (patch) | |
tree | a0fc21fcf2d49ff81938f0463cf3a5da4dcd3184 /src | |
parent | Merge pull request #2034 from jroweboy/loading-widget (diff) | |
parent | Change const char* to const char[] (diff) | |
download | yuzu-125599c2d51fba0bb9466d92382631ed7f34bed9.tar yuzu-125599c2d51fba0bb9466d92382631ed7f34bed9.tar.gz yuzu-125599c2d51fba0bb9466d92382631ed7f34bed9.tar.bz2 yuzu-125599c2d51fba0bb9466d92382631ed7f34bed9.tar.lz yuzu-125599c2d51fba0bb9466d92382631ed7f34bed9.tar.xz yuzu-125599c2d51fba0bb9466d92382631ed7f34bed9.tar.zst yuzu-125599c2d51fba0bb9466d92382631ed7f34bed9.zip |
Diffstat (limited to 'src')
-rw-r--r-- | src/video_core/rasterizer_interface.h | 9 | ||||
-rw-r--r-- | src/yuzu/loading_screen.cpp | 151 | ||||
-rw-r--r-- | src/yuzu/loading_screen.h | 38 | ||||
-rw-r--r-- | src/yuzu/loading_screen.ui | 162 | ||||
-rw-r--r-- | src/yuzu/main.cpp | 13 |
5 files changed, 316 insertions, 57 deletions
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 06fc59dbe..ff5310848 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -4,6 +4,7 @@ #pragma once +#include <functional> #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, + Decompile, + Build, + Complete, +}; +using DiskResourceLoadCallback = std::function<void(LoadCallbackStage, std::size_t, std::size_t)>; + class RasterizerInterface { public: virtual ~RasterizerInterface() {} diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp index 0f3c4bb6c..907aac4f1 100644 --- a/src/yuzu/loading_screen.cpp +++ b/src/yuzu/loading_screen.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <unordered_map> #include <QBuffer> #include <QByteArray> +#include <QGraphicsOpacityEffect> #include <QHBoxLayout> #include <QIODevice> #include <QImage> @@ -12,11 +14,14 @@ #include <QPalette> #include <QPixmap> #include <QProgressBar> +#include <QPropertyAnimation> #include <QStyleOption> -#include <QWindow> +#include <QTime> +#include <QtConcurrent/QtConcurrentRun> #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 @@ -25,11 +30,84 @@ #include <QMovie> #endif +constexpr const char PROGRESSBAR_STYLE_PREPARE[] = R"( +QProgressBar {} +QProgressBar::chunk {})"; + +constexpr const char PROGRESSBAR_STYLE_DECOMPILE[] = R"( +QProgressBar { + background-color: black; + border: 2px solid white; + border-radius: 4px; + padding: 2px; +} +QProgressBar::chunk { + background-color: #0ab9e6; +})"; + +constexpr const char PROGRESSBAR_STYLE_BUILD[] = R"( +QProgressBar { + background-color: black; + border: 2px solid white; + border-radius: 4px; + padding: 2px; +} +QProgressBar::chunk { + background-color: #ff3c28; +})"; + +constexpr const char PROGRESSBAR_STYLE_COMPLETE[] = R"( +QProgressBar { + background-color: #0ab9e6; + border: 2px solid white; + border-radius: 4px; + padding: 2px; +} +QProgressBar::chunk { + background-color: #ff3c28; +})"; + LoadingScreen::LoadingScreen(QWidget* parent) - : QWidget(parent), ui(std::make_unique<Ui::LoadingScreen>()) { + : QWidget(parent), ui(std::make_unique<Ui::LoadingScreen>()), + previous_stage(VideoCore::LoadCallbackStage::Complete) { ui->setupUi(this); - // Progress bar is hidden until we have a use for it. - ui->progress_bar->hide(); + setMinimumSize(1280, 720); + + // Create a fade out effect to hide this loading screen widget. + // When fading opacity, it will fade to the parent widgets background color, which is why we + // create an internal widget named fade_widget that we use the effect on, while keeping the + // loading screen widget's background color black. This way we can create a fade to black effect + opacity_effect = new QGraphicsOpacityEffect(this); + opacity_effect->setOpacity(1); + ui->fade_parent->setGraphicsEffect(opacity_effect); + fadeout_animation = std::make_unique<QPropertyAnimation>(opacity_effect, "opacity"); + fadeout_animation->setDuration(500); + fadeout_animation->setStartValue(1); + fadeout_animation->setEndValue(0); + fadeout_animation->setEasingCurve(QEasingCurve::OutBack); + + // After the fade completes, hide the widget and reset the opacity + connect(fadeout_animation.get(), &QPropertyAnimation::finished, [this] { + hide(); + opacity_effect->setOpacity(1); + emit Hidden(); + }); + connect(this, &LoadingScreen::LoadProgress, this, &LoadingScreen::OnLoadProgress, + Qt::QueuedConnection); + qRegisterMetaType<VideoCore::LoadCallbackStage>(); + + stage_translations = { + {VideoCore::LoadCallbackStage::Prepare, tr("Loading...")}, + {VideoCore::LoadCallbackStage::Decompile, tr("Preparing Shaders %1 / %2")}, + {VideoCore::LoadCallbackStage::Build, tr("Loading Shaders %1 / %2")}, + {VideoCore::LoadCallbackStage::Complete, tr("Launching...")}, + }; + progressbar_style = { + {VideoCore::LoadCallbackStage::Prepare, PROGRESSBAR_STYLE_PREPARE}, + {VideoCore::LoadCallbackStage::Decompile, PROGRESSBAR_STYLE_DECOMPILE}, + {VideoCore::LoadCallbackStage::Build, PROGRESSBAR_STYLE_BUILD}, + {VideoCore::LoadCallbackStage::Complete, PROGRESSBAR_STYLE_COMPLETE}, + }; } LoadingScreen::~LoadingScreen() = default; @@ -42,11 +120,11 @@ void LoadingScreen::Prepare(Loader::AppLoader& loader) { map.loadFromData(buffer.data(), buffer.size()); ui->banner->setPixmap(map); #else - backing_mem = - std::make_unique<QByteArray>(reinterpret_cast<char*>(buffer.data()), buffer.size()); + backing_mem = std::make_unique<QByteArray>(reinterpret_cast<char*>(buffer.data()), + static_cast<int>(buffer.size())); backing_buf = std::make_unique<QBuffer>(backing_mem.get()); backing_buf->open(QIODevice::ReadOnly); - animation = std::make_unique<QMovie>(backing_buf.get(), QByteArray("GIF")); + animation = std::make_unique<QMovie>(backing_buf.get(), QByteArray()); animation->start(); ui->banner->setMovie(animation.get()); #endif @@ -54,17 +132,68 @@ void LoadingScreen::Prepare(Loader::AppLoader& loader) { } if (loader.ReadLogo(buffer) == Loader::ResultStatus::Success) { QPixmap map; - map.loadFromData(buffer.data(), buffer.size()); + map.loadFromData(buffer.data(), static_cast<uint>(buffer.size())); ui->logo->setPixmap(map); } + + slow_shader_compile_start = false; + OnLoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); +} + +void LoadingScreen::OnLoadComplete() { + fadeout_animation->start(QPropertyAnimation::KeepWhenStopped); } -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]); + // Hide the progress bar during the prepare stage + if (stage == VideoCore::LoadCallbackStage::Prepare) { + ui->progress_bar->hide(); + } else { + ui->progress_bar->show(); + } + 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); + ui->progress_bar->setMaximum(static_cast<int>(total)); previous_total = total; } - ui->progress_bar->setValue(value); + + QString estimate; + // If theres a drastic slowdown in the rate, then display an estimate + if (now - previous_time > milliseconds{50} || slow_shader_compile_start) { + 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<milliseconds>(now - slow_shader_start); + if (diff > seconds{1}) { + auto eta_mseconds = + static_cast<long>(static_cast<double>(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<long>(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(static_cast<int>(value)); + previous_time = now; } void LoadingScreen::paintEvent(QPaintEvent* event) { diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h index 2a6cf1142..801d08e1a 100644 --- a/src/yuzu/loading_screen.h +++ b/src/yuzu/loading_screen.h @@ -4,7 +4,9 @@ #pragma once +#include <chrono> #include <memory> +#include <QString> #include <QWidget> #if !QT_CONFIG(movie) @@ -19,9 +21,15 @@ namespace Ui { class LoadingScreen; } +namespace VideoCore { +enum class LoadCallbackStage; +} + class QBuffer; class QByteArray; +class QGraphicsOpacityEffect; class QMovie; +class QPropertyAnimation; class LoadingScreen : public QWidget { Q_OBJECT @@ -39,11 +47,21 @@ public: /// used resources such as the logo and banner. void Clear(); + /// Slot used to update the status of the progress bar + void OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); + + /// Hides the LoadingScreen with a fade out effect + void OnLoadComplete(); + // 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); + /// Signals that this widget is completely hidden now and should be replaced with the other + /// widget + void Hidden(); private: #ifndef YUZU_QT_MOVIE_MISSING @@ -53,4 +71,22 @@ private: #endif std::unique_ptr<Ui::LoadingScreen> ui; std::size_t previous_total = 0; + VideoCore::LoadCallbackStage previous_stage; + + QGraphicsOpacityEffect* opacity_effect = nullptr; + std::unique_ptr<QPropertyAnimation> fadeout_animation; + + // Definitions for the differences in text and styling for each stage + std::unordered_map<VideoCore::LoadCallbackStage, const char*> progressbar_style; + std::unordered_map<VideoCore::LoadCallbackStage, QString> 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 00579b670..a67d273fd 100644 --- a/src/yuzu/loading_screen.ui +++ b/src/yuzu/loading_screen.ui @@ -30,46 +30,128 @@ <number>0</number> </property> <item> - <widget class="QLabel" name="logo"> - <property name="text"> - <string/> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> - </property> - <property name="margin"> - <number>30</number> - </property> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QProgressBar" name="progress_bar"> - <property name="styleSheet"> - <string notr="true">font-size: 26px;</string> - </property> - <property name="value"> - <number>0</number> - </property> - <property name="format"> - <string>Loading Shaders %v out of %m</string> - </property> - </widget> - </item> - </layout> - </item> - <item alignment="Qt::AlignRight|Qt::AlignBottom"> - <widget class="QLabel" name="banner"> - <property name="styleSheet"> - <string notr="true">background-color: black;</string> - </property> - <property name="text"> - <string/> - </property> - <property name="margin"> - <number>30</number> - </property> + <widget class="QWidget" name="fade_parent" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item alignment="Qt::AlignLeft|Qt::AlignTop"> + <widget class="QLabel" name="logo"> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="margin"> + <number>30</number> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,1"> + <property name="spacing"> + <number>15</number> + </property> + <property name="sizeConstraint"> + <enum>QLayout::SetNoConstraint</enum> + </property> + <item alignment="Qt::AlignHCenter|Qt::AlignBottom"> + <widget class="QLabel" name="stage"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="styleSheet"> + <string notr="true">background-color: black; color: white; +font: 75 20pt "Arial";</string> + </property> + <property name="text"> + <string>Loading Shaders 387 / 1628</string> + </property> + </widget> + </item> + <item alignment="Qt::AlignHCenter|Qt::AlignTop"> + <widget class="QProgressBar" name="progress_bar"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>500</width> + <height>40</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">QProgressBar { +color: white; +border: 2px solid white; +outline-color: black; +border-radius: 20px; +} +QProgressBar::chunk { +background-color: white; +border-radius: 15px; +}</string> + </property> + <property name="value"> + <number>50</number> + </property> + <property name="textVisible"> + <bool>false</bool> + </property> + <property name="format"> + <string>Loading Shaders %v out of %m</string> + </property> + </widget> + </item> + <item alignment="Qt::AlignHCenter|Qt::AlignTop"> + <widget class="QLabel" name="value"> + <property name="toolTip"> + <string notr="true"/> + </property> + <property name="styleSheet"> + <string notr="true">background-color: black; color: white; +font: 75 15pt "Arial";</string> + </property> + <property name="text"> + <string>Stage 1 of 2. Estimate Time 5m 4s</string> + </property> + </widget> + </item> + </layout> + </item> + <item alignment="Qt::AlignRight|Qt::AlignBottom"> + <widget class="QLabel" name="banner"> + <property name="styleSheet"> + <string notr="true">background-color: black;</string> + </property> + <property name="text"> + <string/> + </property> + <property name="margin"> + <number>30</number> + </property> + </widget> + </item> + </layout> </widget> </item> </layout> diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 68bfa23ab..2c3e27c2e 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -415,6 +415,13 @@ void GMainWindow::InitializeWidgets() { loading_screen = new LoadingScreen(this); loading_screen->hide(); ui.horizontalLayout->addWidget(loading_screen); + connect(loading_screen, &LoadingScreen::Hidden, [&] { + loading_screen->Clear(); + if (emulation_running) { + render_window->show(); + render_window->setFocus(); + } + }); // Create status bar message_label = new QLabel(); @@ -904,7 +911,6 @@ void GMainWindow::BootGame(const QString& filename) { loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); loading_screen->show(); - loading_screen->setFocus(); emulation_running = true; if (ui.action_Fullscreen->isChecked()) { @@ -1514,10 +1520,7 @@ void GMainWindow::OnStopGame() { } void GMainWindow::OnLoadComplete() { - loading_screen->hide(); - loading_screen->Clear(); - render_window->show(); - render_window->setFocus(); + loading_screen->OnLoadComplete(); } void GMainWindow::OnMenuReportCompatibility() { |