// Copyright 2019 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #include #include #include #include #include #include #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 // showing the full animation #if !YUZU_QT_MOVIE_MISSING #include #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()), previous_stage(VideoCore::LoadCallbackStage::Complete) { ui->setupUi(this); connect(this, &LoadingScreen::LoadProgress, this, &LoadingScreen::OnLoadProgress, Qt::QueuedConnection); qRegisterMetaType(); 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; void LoadingScreen::Prepare(Loader::AppLoader& loader) { std::vector buffer; if (loader.ReadBanner(buffer) == Loader::ResultStatus::Success) { #ifdef YUZU_QT_MOVIE_MISSING QPixmap map; map.loadFromData(buffer.data(), buffer.size()); ui->banner->setPixmap(map); #else backing_mem = 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()); animation->start(); ui->banner->setMovie(animation.get()); #endif buffer.clear(); } if (loader.ReadLogo(buffer) == Loader::ResultStatus::Success) { QPixmap map; map.loadFromData(buffer.data(), buffer.size()); ui->logo->setPixmap(map); } OnLoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); } 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]); 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); previous_total = total; } 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(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) { QStyleOption opt; opt.init(this); QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); QWidget::paintEvent(event); } void LoadingScreen::Clear() { #ifndef YUZU_QT_MOVIE_MISSING animation.reset(); backing_buf.reset(); backing_mem.reset(); #endif }