| | |
| | |
| | |
| |
|
| | #include <unordered_map> |
| | #include <QGraphicsOpacityEffect> |
| | #include <QImage> |
| | #include <QLabel> |
| | #include <QPainter> |
| | #include <QPixmap> |
| | #include <QProgressBar> |
| | #include <QPropertyAnimation> |
| | #include <QString> |
| | #include <QStyleOption> |
| | #include <QTime> |
| | #include <fmt/format.h> |
| | #include "citra_qt/loading_screen.h" |
| | #include "common/logging/log.h" |
| | #include "core/loader/loader.h" |
| | #include "core/loader/smdh.h" |
| | #include "ui_loading_screen.h" |
| | #include "video_core/rasterizer_interface.h" |
| |
|
| | constexpr char PROGRESSBAR_STYLE_PREPARE[] = R"( |
| | QProgressBar {} |
| | QProgressBar::chunk {})"; |
| |
|
| | constexpr char PROGRESSBAR_STYLE_DECOMPILE[] = R"( |
| | QProgressBar { |
| | background-color: black; |
| | border: 2px solid white; |
| | border-radius: 4px; |
| | padding: 2px; |
| | } |
| | QProgressBar::chunk { |
| | background-color: #fd8507; |
| | width: 1px; |
| | })"; |
| |
|
| | constexpr char PROGRESSBAR_STYLE_BUILD[] = R"( |
| | QProgressBar { |
| | background-color: black; |
| | border: 2px solid white; |
| | border-radius: 4px; |
| | padding: 2px; |
| | } |
| | QProgressBar::chunk { |
| | background-color: #ffe402; |
| | width: 1px; |
| | })"; |
| |
|
| | constexpr char PROGRESSBAR_STYLE_COMPLETE[] = R"( |
| | QProgressBar { |
| | background-color: #fd8507; |
| | border: 2px solid white; |
| | border-radius: 4px; |
| | padding: 2px; |
| | } |
| | QProgressBar::chunk { |
| | background-color: #ffe402; |
| | })"; |
| |
|
| | |
| | const static std::unordered_map<VideoCore::LoadCallbackStage, const char*> stage_translations{ |
| | {VideoCore::LoadCallbackStage::Prepare, QT_TRANSLATE_NOOP("LoadingScreen", "Loading...")}, |
| | {VideoCore::LoadCallbackStage::Preload, |
| | QT_TRANSLATE_NOOP("LoadingScreen", "Preloading Textures %1 / %2")}, |
| | {VideoCore::LoadCallbackStage::Decompile, |
| | QT_TRANSLATE_NOOP("LoadingScreen", "Preparing Shaders %1 / %2")}, |
| | {VideoCore::LoadCallbackStage::Build, |
| | QT_TRANSLATE_NOOP("LoadingScreen", "Loading Shaders %1 / %2")}, |
| | {VideoCore::LoadCallbackStage::Complete, QT_TRANSLATE_NOOP("LoadingScreen", "Launching...")}, |
| | }; |
| | const static std::unordered_map<VideoCore::LoadCallbackStage, const char*> progressbar_style{ |
| | {VideoCore::LoadCallbackStage::Prepare, PROGRESSBAR_STYLE_PREPARE}, |
| | {VideoCore::LoadCallbackStage::Preload, PROGRESSBAR_STYLE_BUILD}, |
| | {VideoCore::LoadCallbackStage::Decompile, PROGRESSBAR_STYLE_DECOMPILE}, |
| | {VideoCore::LoadCallbackStage::Build, PROGRESSBAR_STYLE_BUILD}, |
| | {VideoCore::LoadCallbackStage::Complete, PROGRESSBAR_STYLE_COMPLETE}, |
| | }; |
| |
|
| | static QPixmap GetQPixmapFromSMDH(std::vector<u8>& smdh_data) { |
| | Loader::SMDH smdh; |
| | std::memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH)); |
| |
|
| | bool large = true; |
| | std::vector<u16> icon_data = smdh.GetIcon(large); |
| | const uchar* data = reinterpret_cast<const uchar*>(icon_data.data()); |
| | int size = large ? 48 : 24; |
| | QImage icon(data, size, size, QImage::Format::Format_RGB16); |
| | return QPixmap::fromImage(icon); |
| | } |
| |
|
| | LoadingScreen::LoadingScreen(QWidget* parent) |
| | : QWidget(parent), ui(std::make_unique<Ui::LoadingScreen>()), |
| | previous_stage(VideoCore::LoadCallbackStage::Complete) { |
| | ui->setupUi(this); |
| | setMinimumSize(400, 240); |
| |
|
| | |
| | |
| | |
| | |
| | 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>(); |
| | } |
| |
|
| | LoadingScreen::~LoadingScreen() = default; |
| |
|
| | void LoadingScreen::Prepare(Loader::AppLoader& loader) { |
| | std::vector<u8> buffer; |
| | |
| |
|
| | if (loader.ReadIcon(buffer) == Loader::ResultStatus::Success) { |
| | QPixmap icon = GetQPixmapFromSMDH(buffer); |
| | ui->icon->setPixmap(icon); |
| | } else { |
| | ui->icon->clear(); |
| | } |
| | std::string title; |
| | if (loader.ReadTitle(title) != Loader::ResultStatus::Success) { |
| | u64 program_id; |
| | if (loader.ReadProgramId(program_id) == Loader::ResultStatus::Success) { |
| | title = fmt::format("{:016x}", program_id); |
| | } |
| | } |
| | ui->title->setText(tr("Now Loading\n%1").arg(QString::fromStdString(title))); |
| | eta_shown = 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 = high_resolution_clock::now(); |
| | |
| | if (stage != previous_stage) { |
| | ui->progress_bar->setStyleSheet(QString::fromUtf8(progressbar_style.at(stage))); |
| | |
| | if (stage == VideoCore::LoadCallbackStage::Prepare) { |
| | ui->progress_bar->hide(); |
| | } else { |
| | ui->progress_bar->show(); |
| | } |
| | previous_stage = stage; |
| | } |
| | |
| | if (total != previous_total) { |
| | ui->progress_bar->setMaximum(static_cast<int>(total)); |
| | previous_total = total; |
| | } |
| |
|
| | |
| | if (value > 0) { |
| | rolling_average -= rolling_average / NumberOfDataPoints; |
| | rolling_average += (now - previous_time) / NumberOfDataPoints; |
| | } |
| |
|
| | QString estimate; |
| |
|
| | |
| | |
| | if (value > NumberOfDataPoints && |
| | (eta_shown || rolling_average * (total - value) > ETABreakPoint)) { |
| | if (!eta_shown) { |
| | eta_shown = true; |
| | } |
| | const auto eta_mseconds = std::chrono::duration_cast<std::chrono::milliseconds>( |
| | rolling_average * (total - value)); |
| | const auto limited_mseconds = std::max<long>(eta_mseconds.count(), 1000); |
| | estimate = tr("Estimated Time %1") |
| | .arg(QTime(0, 0, 0, 0) |
| | .addMSecs(static_cast<int>(limited_mseconds)) |
| | .toString(QStringLiteral("mm:ss"))); |
| | } |
| |
|
| | |
| | const auto& stg = tr(stage_translations.at(stage)); |
| | if (stage == VideoCore::LoadCallbackStage::Decompile || |
| | stage == VideoCore::LoadCallbackStage::Build || |
| | stage == VideoCore::LoadCallbackStage::Preload) { |
| | ui->stage->setText(stg.arg(value).arg(total)); |
| | } else { |
| | ui->stage->setText(stg); |
| | } |
| | 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() {} |
| |
|