| | |
| | |
| |
|
| | #include <unordered_map> |
| | #include <QBuffer> |
| | #include <QByteArray> |
| | #include <QGraphicsOpacityEffect> |
| | #include <QIODevice> |
| | #include <QImage> |
| | #include <QPainter> |
| | #include <QPixmap> |
| | #include <QPropertyAnimation> |
| | #include <QStyleOption> |
| | #include "core/frontend/framebuffer_layout.h" |
| | #include "core/loader/loader.h" |
| | #include "ui_loading_screen.h" |
| | #include "video_core/rasterizer_interface.h" |
| | #include "yuzu/loading_screen.h" |
| |
|
| | |
| | |
| | #if !YUZU_QT_MOVIE_MISSING |
| | #include <QMovie> |
| | #endif |
| |
|
| | constexpr char PROGRESSBAR_STYLE_PREPARE[] = R"( |
| | QProgressBar {} |
| | QProgressBar::chunk {})"; |
| |
|
| | constexpr char PROGRESSBAR_STYLE_BUILD[] = R"( |
| | QProgressBar { |
| | background-color: black; |
| | border: 2px solid white; |
| | border-radius: 4px; |
| | padding: 2px; |
| | } |
| | QProgressBar::chunk { |
| | background-color: #ff3c28; |
| | width: 1px; |
| | })"; |
| |
|
| | constexpr 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>()), |
| | previous_stage(VideoCore::LoadCallbackStage::Complete) { |
| | ui->setupUi(this); |
| | setMinimumSize(Layout::MinimumSize::Width, Layout::MinimumSize::Height); |
| |
|
| | |
| | |
| | |
| | |
| | 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); |
| |
|
| | |
| | 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::Build, tr("Loading Shaders %1 / %2")}, |
| | {VideoCore::LoadCallbackStage::Complete, tr("Launching...")}, |
| | }; |
| | progressbar_style = { |
| | {VideoCore::LoadCallbackStage::Prepare, PROGRESSBAR_STYLE_PREPARE}, |
| | {VideoCore::LoadCallbackStage::Build, PROGRESSBAR_STYLE_BUILD}, |
| | {VideoCore::LoadCallbackStage::Complete, PROGRESSBAR_STYLE_COMPLETE}, |
| | }; |
| | } |
| |
|
| | LoadingScreen::~LoadingScreen() = default; |
| |
|
| | void LoadingScreen::Prepare(Loader::AppLoader& loader) { |
| | std::vector<u8> 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<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()); |
| | animation->start(); |
| | ui->banner->setMovie(animation.get()); |
| | #endif |
| | buffer.clear(); |
| | } |
| | if (loader.ReadLogo(buffer) == Loader::ResultStatus::Success) { |
| | QPixmap map; |
| | 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(VideoCore::LoadCallbackStage stage, std::size_t value, |
| | std::size_t total) { |
| | using namespace std::chrono; |
| | const auto now = steady_clock::now(); |
| | |
| | if (stage != previous_stage) { |
| | ui->progress_bar->setStyleSheet(QString::fromUtf8(progressbar_style[stage])); |
| | |
| | if (stage == VideoCore::LoadCallbackStage::Prepare) { |
| | ui->progress_bar->hide(); |
| | } else { |
| | ui->progress_bar->show(); |
| | } |
| | previous_stage = stage; |
| | |
| | slow_shader_compile_start = false; |
| | } |
| | |
| | if (total != previous_total) { |
| | ui->progress_bar->setMaximum(static_cast<int>(total)); |
| | previous_total = total; |
| | } |
| | |
| | if (stage == VideoCore::LoadCallbackStage::Complete) { |
| | ui->progress_bar->setRange(0, 0); |
| | } |
| |
|
| | QString estimate; |
| | |
| | if (now - previous_time > milliseconds{50} || slow_shader_compile_start) { |
| | if (!slow_shader_compile_start) { |
| | slow_shader_start = steady_clock::now(); |
| | slow_shader_compile_start = true; |
| | slow_shader_first_value = value; |
| | } |
| | |
| | const auto diff = duration_cast<milliseconds>(now - slow_shader_start); |
| | if (diff > seconds{1}) { |
| | const 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(QStringLiteral("mm:ss"))); |
| | } |
| | } |
| |
|
| | |
| | if (stage == VideoCore::LoadCallbackStage::Build) { |
| | ui->stage->setText(stage_translations[stage].arg(value).arg(total)); |
| | } else { |
| | ui->stage->setText(stage_translations[stage]); |
| | } |
| | ui->value->setText(estimate); |
| | ui->progress_bar->setValue(static_cast<int>(value)); |
| | previous_time = now; |
| | } |
| |
|
| | void LoadingScreen::paintEvent(QPaintEvent* event) { |
| | QStyleOption opt; |
| | opt.initFrom(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 |
| | } |
| |
|