| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  | // Copyright 2019 yuzu Emulator Project
 | 
					
						
							|  |  |  | // Licensed under GPLv2 or any later version
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  | #include <unordered_map>
 | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  | #include <QBuffer>
 | 
					
						
							|  |  |  | #include <QByteArray>
 | 
					
						
							| 
									
										
										
										
											2019-01-21 09:20:16 -07:00
										 |  |  | #include <QGraphicsOpacityEffect>
 | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  | #include <QHBoxLayout>
 | 
					
						
							|  |  |  | #include <QIODevice>
 | 
					
						
							|  |  |  | #include <QImage>
 | 
					
						
							|  |  |  | #include <QLabel>
 | 
					
						
							|  |  |  | #include <QPainter>
 | 
					
						
							|  |  |  | #include <QPalette>
 | 
					
						
							|  |  |  | #include <QPixmap>
 | 
					
						
							|  |  |  | #include <QProgressBar>
 | 
					
						
							| 
									
										
										
										
											2019-01-21 09:20:16 -07:00
										 |  |  | #include <QPropertyAnimation>
 | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  | #include <QStyleOption>
 | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  | #include <QTime>
 | 
					
						
							|  |  |  | #include <QtConcurrent/QtConcurrentRun>
 | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  | #include "common/logging/log.h"
 | 
					
						
							|  |  |  | #include "core/loader/loader.h"
 | 
					
						
							|  |  |  | #include "ui_loading_screen.h"
 | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  | #include "video_core/rasterizer_interface.h"
 | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  | #include "yuzu/loading_screen.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-18 10:02:27 -07:00
										 |  |  | // Mingw seems to not have QMovie at all. If QMovie is missing then use a single frame instead of an
 | 
					
						
							|  |  |  | // showing the full animation
 | 
					
						
							|  |  |  | #if !YUZU_QT_MOVIE_MISSING
 | 
					
						
							|  |  |  | #include <QMovie>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-21 10:28:32 -07:00
										 |  |  | constexpr const char PROGRESSBAR_STYLE_PREPARE[] = R"( | 
					
						
							| 
									
										
										
										
											2019-01-20 15:09:14 -07:00
										 |  |  | QProgressBar {} | 
					
						
							| 
									
										
										
										
											2019-01-20 18:20:21 -07:00
										 |  |  | QProgressBar::chunk {})"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-21 10:28:32 -07:00
										 |  |  | constexpr const char PROGRESSBAR_STYLE_DECOMPILE[] = R"( | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  | QProgressBar { | 
					
						
							|  |  |  |   background-color: black; | 
					
						
							|  |  |  |   border: 2px solid white; | 
					
						
							|  |  |  |   border-radius: 4px; | 
					
						
							|  |  |  |   padding: 2px; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | QProgressBar::chunk { | 
					
						
							|  |  |  |   background-color: #0ab9e6; | 
					
						
							| 
									
										
										
										
											2019-01-21 16:38:37 -03:00
										 |  |  |   width: 1px; | 
					
						
							| 
									
										
										
										
											2019-01-20 18:20:21 -07:00
										 |  |  | })"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-21 10:28:32 -07:00
										 |  |  | constexpr const char PROGRESSBAR_STYLE_BUILD[] = R"( | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  | QProgressBar { | 
					
						
							|  |  |  |   background-color: black; | 
					
						
							|  |  |  |   border: 2px solid white; | 
					
						
							|  |  |  |   border-radius: 4px; | 
					
						
							|  |  |  |   padding: 2px; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | QProgressBar::chunk { | 
					
						
							| 
									
										
										
										
											2019-01-21 16:38:37 -03:00
										 |  |  |   background-color: #ff3c28; | 
					
						
							|  |  |  |   width: 1px; | 
					
						
							| 
									
										
										
										
											2019-01-20 18:20:21 -07:00
										 |  |  | })"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-21 10:28:32 -07:00
										 |  |  | constexpr const char PROGRESSBAR_STYLE_COMPLETE[] = R"( | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  | QProgressBar { | 
					
						
							| 
									
										
										
										
											2019-01-20 19:14:14 -07:00
										 |  |  |   background-color: #0ab9e6; | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  |   border: 2px solid white; | 
					
						
							|  |  |  |   border-radius: 4px; | 
					
						
							|  |  |  |   padding: 2px; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | QProgressBar::chunk { | 
					
						
							|  |  |  |   background-color: #ff3c28; | 
					
						
							| 
									
										
										
										
											2019-01-20 18:20:21 -07:00
										 |  |  | })"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | LoadingScreen::LoadingScreen(QWidget* parent) | 
					
						
							|  |  |  |     : QWidget(parent), ui(std::make_unique<Ui::LoadingScreen>()), | 
					
						
							|  |  |  |       previous_stage(VideoCore::LoadCallbackStage::Complete) { | 
					
						
							|  |  |  |     ui->setupUi(this); | 
					
						
							| 
									
										
										
										
											2019-01-21 08:51:37 -07:00
										 |  |  |     setMinimumSize(1280, 720); | 
					
						
							| 
									
										
										
										
											2019-01-20 18:20:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-21 09:20:16 -07:00
										 |  |  |     // 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(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-01-20 18:20:21 -07:00
										 |  |  |     connect(this, &LoadingScreen::LoadProgress, this, &LoadingScreen::OnLoadProgress, | 
					
						
							|  |  |  |             Qt::QueuedConnection); | 
					
						
							|  |  |  |     qRegisterMetaType<VideoCore::LoadCallbackStage>(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     stage_translations = { | 
					
						
							|  |  |  |         {VideoCore::LoadCallbackStage::Prepare, tr("Loading...")}, | 
					
						
							| 
									
										
										
										
											2019-01-20 18:40:25 -07:00
										 |  |  |         {VideoCore::LoadCallbackStage::Decompile, tr("Preparing Shaders %1 / %2")}, | 
					
						
							|  |  |  |         {VideoCore::LoadCallbackStage::Build, tr("Loading Shaders %1 / %2")}, | 
					
						
							| 
									
										
										
										
											2019-01-20 18:20:21 -07:00
										 |  |  |         {VideoCore::LoadCallbackStage::Complete, tr("Launching...")}, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     progressbar_style = { | 
					
						
							|  |  |  |         {VideoCore::LoadCallbackStage::Prepare, PROGRESSBAR_STYLE_PREPARE}, | 
					
						
							| 
									
										
										
										
											2019-01-20 18:40:25 -07:00
										 |  |  |         {VideoCore::LoadCallbackStage::Decompile, PROGRESSBAR_STYLE_DECOMPILE}, | 
					
						
							|  |  |  |         {VideoCore::LoadCallbackStage::Build, PROGRESSBAR_STYLE_BUILD}, | 
					
						
							| 
									
										
										
										
											2019-01-20 18:20:21 -07:00
										 |  |  |         {VideoCore::LoadCallbackStage::Complete, PROGRESSBAR_STYLE_COMPLETE}, | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | LoadingScreen::~LoadingScreen() = default; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void LoadingScreen::Prepare(Loader::AppLoader& loader) { | 
					
						
							|  |  |  |     std::vector<u8> buffer; | 
					
						
							|  |  |  |     if (loader.ReadBanner(buffer) == Loader::ResultStatus::Success) { | 
					
						
							| 
									
										
										
										
											2019-01-18 10:02:27 -07:00
										 |  |  | #ifdef YUZU_QT_MOVIE_MISSING
 | 
					
						
							|  |  |  |         QPixmap map; | 
					
						
							|  |  |  |         map.loadFromData(buffer.data(), buffer.size()); | 
					
						
							|  |  |  |         ui->banner->setPixmap(map); | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2019-01-21 09:39:45 -07:00
										 |  |  |         backing_mem = std::make_unique<QByteArray>(reinterpret_cast<char*>(buffer.data()), | 
					
						
							|  |  |  |                                                    static_cast<int>(buffer.size())); | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  |         backing_buf = std::make_unique<QBuffer>(backing_mem.get()); | 
					
						
							|  |  |  |         backing_buf->open(QIODevice::ReadOnly); | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  |         animation = std::make_unique<QMovie>(backing_buf.get(), QByteArray()); | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  |         animation->start(); | 
					
						
							|  |  |  |         ui->banner->setMovie(animation.get()); | 
					
						
							| 
									
										
										
										
											2019-01-18 10:02:27 -07:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  |         buffer.clear(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (loader.ReadLogo(buffer) == Loader::ResultStatus::Success) { | 
					
						
							|  |  |  |         QPixmap map; | 
					
						
							| 
									
										
										
										
											2019-01-21 09:39:45 -07:00
										 |  |  |         map.loadFromData(buffer.data(), static_cast<uint>(buffer.size())); | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  |         ui->logo->setPixmap(map); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-21 09:20:16 -07:00
										 |  |  |     slow_shader_compile_start = false; | 
					
						
							| 
									
										
										
										
											2019-01-20 15:09:14 -07:00
										 |  |  |     OnLoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-21 09:20:16 -07:00
										 |  |  | void LoadingScreen::OnLoadComplete() { | 
					
						
							|  |  |  |     fadeout_animation->start(QPropertyAnimation::KeepWhenStopped); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  | 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]); | 
					
						
							| 
									
										
										
										
											2019-01-21 09:20:16 -07:00
										 |  |  |         // Hide the progress bar during the prepare stage
 | 
					
						
							| 
									
										
										
										
											2019-01-20 15:09:14 -07:00
										 |  |  |         if (stage == VideoCore::LoadCallbackStage::Prepare) { | 
					
						
							|  |  |  |             ui->progress_bar->hide(); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             ui->progress_bar->show(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  |         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
 | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  |     if (total != previous_total) { | 
					
						
							| 
									
										
										
										
											2019-01-21 09:39:45 -07:00
										 |  |  |         ui->progress_bar->setMaximum(static_cast<int>(total)); | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  |         previous_total = total; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     QString estimate; | 
					
						
							|  |  |  |     // If theres a drastic slowdown in the rate, then display an estimate
 | 
					
						
							| 
									
										
										
										
											2019-01-20 18:22:29 -07:00
										 |  |  |     if (now - previous_time > milliseconds{50} || slow_shader_compile_start) { | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  |         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); | 
					
						
							| 
									
										
										
										
											2019-01-21 09:39:45 -07:00
										 |  |  |     ui->progress_bar->setValue(static_cast<int>(value)); | 
					
						
							| 
									
										
										
										
											2019-01-19 21:03:26 -07:00
										 |  |  |     previous_time = now; | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void LoadingScreen::paintEvent(QPaintEvent* event) { | 
					
						
							|  |  |  |     QStyleOption opt; | 
					
						
							|  |  |  |     opt.init(this); | 
					
						
							|  |  |  |     QPainter p(this); | 
					
						
							|  |  |  |     style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); | 
					
						
							|  |  |  |     QWidget::paintEvent(event); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void LoadingScreen::Clear() { | 
					
						
							| 
									
										
										
										
											2019-01-18 10:02:27 -07:00
										 |  |  | #ifndef YUZU_QT_MOVIE_MISSING
 | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  |     animation.reset(); | 
					
						
							|  |  |  |     backing_buf.reset(); | 
					
						
							|  |  |  |     backing_mem.reset(); | 
					
						
							| 
									
										
										
										
											2019-01-18 10:02:27 -07:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-01-17 00:01:00 -07:00
										 |  |  | } |