mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Merge pull request #5071 from jroweboy/loading-bar
QT Frontend: Add disk shader loading progress bar
This commit is contained in:
		
						commit
						f3e6c4f841
					
				
					 8 changed files with 542 additions and 6 deletions
				
			
		|  | @ -115,6 +115,9 @@ add_executable(citra-qt | ||||||
|     game_list_worker.h |     game_list_worker.h | ||||||
|     hotkeys.cpp |     hotkeys.cpp | ||||||
|     hotkeys.h |     hotkeys.h | ||||||
|  |     loading_screen.cpp | ||||||
|  |     loading_screen.h | ||||||
|  |     loading_screen.ui | ||||||
|     main.cpp |     main.cpp | ||||||
|     main.h |     main.h | ||||||
|     main.ui |     main.ui | ||||||
|  |  | ||||||
|  | @ -46,12 +46,15 @@ void EmuThread::run() { | ||||||
|     MicroProfileOnThreadCreate("EmuThread"); |     MicroProfileOnThreadCreate("EmuThread"); | ||||||
|     Frontend::ScopeAcquireContext scope(core_context); |     Frontend::ScopeAcquireContext scope(core_context); | ||||||
| 
 | 
 | ||||||
|  |     emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | ||||||
|  | 
 | ||||||
|     Core::System::GetInstance().Renderer().Rasterizer()->LoadDiskResources( |     Core::System::GetInstance().Renderer().Rasterizer()->LoadDiskResources( | ||||||
|         stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { |         stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { | ||||||
|             LOG_DEBUG(Frontend, "Loading stage {} progress {} {}", static_cast<u32>(stage), value, |             emit LoadProgress(stage, value, total); | ||||||
|                       total); |  | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|  |     emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | ||||||
|  | 
 | ||||||
|     // Holds whether the cpu was running during the last iteration,
 |     // Holds whether the cpu was running during the last iteration,
 | ||||||
|     // so that the DebugModeLeft signal can be emitted before the
 |     // so that the DebugModeLeft signal can be emitted before the
 | ||||||
|     // next execution step.
 |     // next execution step.
 | ||||||
|  | @ -127,6 +130,7 @@ OpenGLWindow::~OpenGLWindow() { | ||||||
| void OpenGLWindow::Present() { | void OpenGLWindow::Present() { | ||||||
|     if (!isExposed()) |     if (!isExposed()) | ||||||
|         return; |         return; | ||||||
|  | 
 | ||||||
|     context->makeCurrent(this); |     context->makeCurrent(this); | ||||||
|     VideoCore::g_renderer->TryPresent(100); |     VideoCore::g_renderer->TryPresent(100); | ||||||
|     context->swapBuffers(this); |     context->swapBuffers(this); | ||||||
|  | @ -182,8 +186,8 @@ void OpenGLWindow::exposeEvent(QExposeEvent* event) { | ||||||
|     QWindow::exposeEvent(event); |     QWindow::exposeEvent(event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) | ||||||
|     : QWidget(parent), emu_thread(emu_thread) { |     : QWidget(parent_), emu_thread(emu_thread) { | ||||||
| 
 | 
 | ||||||
|     setWindowTitle(QStringLiteral("Citra %1 | %2-%3") |     setWindowTitle(QStringLiteral("Citra %1 | %2-%3") | ||||||
|                        .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); |                        .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); | ||||||
|  | @ -192,6 +196,9 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | ||||||
|     layout->setMargin(0); |     layout->setMargin(0); | ||||||
|     setLayout(layout); |     setLayout(layout); | ||||||
|     InputCommon::Init(); |     InputCommon::Init(); | ||||||
|  | 
 | ||||||
|  |     GMainWindow* parent = GetMainWindow(); | ||||||
|  |     connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GRenderWindow::~GRenderWindow() { | GRenderWindow::~GRenderWindow() { | ||||||
|  | @ -206,7 +213,12 @@ void GRenderWindow::DoneCurrent() { | ||||||
|     core_context->DoneCurrent(); |     core_context->DoneCurrent(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::PollEvents() {} | void GRenderWindow::PollEvents() { | ||||||
|  |     if (!first_frame) { | ||||||
|  |         first_frame = true; | ||||||
|  |         emit FirstFrameDisplayed(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
 | ||||||
| //
 | //
 | ||||||
|  | @ -363,12 +375,15 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) { | ||||||
| void GRenderWindow::InitRenderTarget() { | void GRenderWindow::InitRenderTarget() { | ||||||
|     ReleaseRenderTarget(); |     ReleaseRenderTarget(); | ||||||
| 
 | 
 | ||||||
|  |     first_frame = false; | ||||||
|  | 
 | ||||||
|     GMainWindow* parent = GetMainWindow(); |     GMainWindow* parent = GetMainWindow(); | ||||||
|     QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; |     QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; | ||||||
|     child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext()); |     child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext()); | ||||||
|     child_window->create(); |     child_window->create(); | ||||||
|     child_widget = createWindowContainer(child_window, this); |     child_widget = createWindowContainer(child_window, this); | ||||||
|     child_widget->resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); |     child_widget->resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); | ||||||
|  | 
 | ||||||
|     layout()->addWidget(child_widget); |     layout()->addWidget(child_widget); | ||||||
| 
 | 
 | ||||||
|     core_context = CreateSharedContext(); |     core_context = CreateSharedContext(); | ||||||
|  |  | ||||||
|  | @ -23,6 +23,10 @@ class QOpenGLContext; | ||||||
| class GMainWindow; | class GMainWindow; | ||||||
| class GRenderWindow; | class GRenderWindow; | ||||||
| 
 | 
 | ||||||
|  | namespace VideoCore { | ||||||
|  | enum class LoadCallbackStage; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| class GLContext : public Frontend::GraphicsContext { | class GLContext : public Frontend::GraphicsContext { | ||||||
| public: | public: | ||||||
|     explicit GLContext(QOpenGLContext* shared_context); |     explicit GLContext(QOpenGLContext* shared_context); | ||||||
|  | @ -116,6 +120,8 @@ signals: | ||||||
|     void DebugModeLeft(); |     void DebugModeLeft(); | ||||||
| 
 | 
 | ||||||
|     void ErrorThrown(Core::System::ResultStatus, std::string); |     void ErrorThrown(Core::System::ResultStatus, std::string); | ||||||
|  | 
 | ||||||
|  |     void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class OpenGLWindow : public QWindow { | class OpenGLWindow : public QWindow { | ||||||
|  | @ -188,6 +194,11 @@ signals: | ||||||
|     /// Emitted when the window is closed
 |     /// Emitted when the window is closed
 | ||||||
|     void Closed(); |     void Closed(); | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Emitted when the guest first calls SwapBuffers. This is used to hide the loading screen | ||||||
|  |      */ | ||||||
|  |     void FirstFrameDisplayed(); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     std::pair<u32, u32> ScaleTouch(QPointF pos) const; |     std::pair<u32, u32> ScaleTouch(QPointF pos) const; | ||||||
|     void TouchBeginEvent(const QTouchEvent* event); |     void TouchBeginEvent(const QTouchEvent* event); | ||||||
|  | @ -212,6 +223,7 @@ private: | ||||||
| 
 | 
 | ||||||
|     /// Temporary storage of the screenshot taken
 |     /// Temporary storage of the screenshot taken
 | ||||||
|     QImage screenshot_image; |     QImage screenshot_image; | ||||||
|  |     bool first_frame = false; | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     void showEvent(QShowEvent* event) override; |     void showEvent(QShowEvent* event) override; | ||||||
|  |  | ||||||
							
								
								
									
										212
									
								
								src/citra_qt/loading_screen.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								src/citra_qt/loading_screen.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,212 @@ | ||||||
|  | // Copyright 2020 Citra Emulator Project
 | ||||||
|  | // 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> | ||||||
|  | #include <QLabel> | ||||||
|  | #include <QPainter> | ||||||
|  | #include <QPalette> | ||||||
|  | #include <QPixmap> | ||||||
|  | #include <QProgressBar> | ||||||
|  | #include <QPropertyAnimation> | ||||||
|  | #include <QStyleOption> | ||||||
|  | #include <QTime> | ||||||
|  | #include <QtConcurrent/QtConcurrentRun> | ||||||
|  | #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; | ||||||
|  | })"; | ||||||
|  | 
 | ||||||
|  | // Definitions for the differences in text and styling for each stage
 | ||||||
|  | const static std::unordered_map<VideoCore::LoadCallbackStage, const char*> stage_translations{ | ||||||
|  |     {VideoCore::LoadCallbackStage::Prepare, QT_TRANSLATE_NOOP("LoadingScreen", "Loading...")}, | ||||||
|  |     {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::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; | ||||||
|  |     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); | ||||||
|  | 
 | ||||||
|  |     // 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>(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | LoadingScreen::~LoadingScreen() = default; | ||||||
|  | 
 | ||||||
|  | void LoadingScreen::Prepare(Loader::AppLoader& loader) { | ||||||
|  |     std::vector<u8> buffer; | ||||||
|  |     // TODO when banner becomes supported, decode it and add it as a movie
 | ||||||
|  | 
 | ||||||
|  |     if (loader.ReadIcon(buffer) == Loader::ResultStatus::Success) { | ||||||
|  |         QPixmap icon = GetQPixmapFromSMDH(buffer); | ||||||
|  |         ui->icon->setPixmap(icon); | ||||||
|  |     } | ||||||
|  |     std::string title; | ||||||
|  |     if (loader.ReadTitle(title) == Loader::ResultStatus::Success) { | ||||||
|  |         ui->title->setText(QString("Now Loading\n") + 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(); | ||||||
|  |     // reset the timer if the stage changes
 | ||||||
|  |     if (stage != previous_stage) { | ||||||
|  |         ui->progress_bar->setStyleSheet(progressbar_style.at(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; | ||||||
|  |     } | ||||||
|  |     // update the max of the progress bar if the number of shaders change
 | ||||||
|  |     if (total != previous_total) { | ||||||
|  |         ui->progress_bar->setMaximum(static_cast<int>(total)); | ||||||
|  |         previous_total = total; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // calculate a simple rolling average after the first shader is loaded
 | ||||||
|  |     if (value > 0) { | ||||||
|  |         rolling_average -= rolling_average / NumberOfDataPoints; | ||||||
|  |         rolling_average += (now - previous_time) / NumberOfDataPoints; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     QString estimate; | ||||||
|  | 
 | ||||||
|  |     // After 25 shader load times were put into the rolling average, determine if the ETA is long
 | ||||||
|  |     // enough to show it
 | ||||||
|  |     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)); | ||||||
|  |         estimate = tr("Estimated Time %1") | ||||||
|  |                        .arg(QTime(0, 0, 0, 0) | ||||||
|  |                                 .addMSecs(std::max<long>(eta_mseconds.count(), 1000)) | ||||||
|  |                                 .toString(QStringLiteral("mm:ss"))); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // update labels and progress bar
 | ||||||
|  |     const auto& stg = tr(stage_translations.at(stage)); | ||||||
|  |     if (stage == VideoCore::LoadCallbackStage::Decompile || | ||||||
|  |         stage == VideoCore::LoadCallbackStage::Build) { | ||||||
|  |         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.init(this); | ||||||
|  |     QPainter p(this); | ||||||
|  |     style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); | ||||||
|  |     QWidget::paintEvent(event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LoadingScreen::Clear() {} | ||||||
							
								
								
									
										78
									
								
								src/citra_qt/loading_screen.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/citra_qt/loading_screen.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | ||||||
|  | // Copyright 2020 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <chrono> | ||||||
|  | #include <memory> | ||||||
|  | #include <QString> | ||||||
|  | #include <QWidget> | ||||||
|  | 
 | ||||||
|  | namespace Loader { | ||||||
|  | class AppLoader; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace Ui { | ||||||
|  | class LoadingScreen; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace VideoCore { | ||||||
|  | enum class LoadCallbackStage; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class QGraphicsOpacityEffect; | ||||||
|  | class QPropertyAnimation; | ||||||
|  | 
 | ||||||
|  | class LoadingScreen : public QWidget { | ||||||
|  |     Q_OBJECT | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     explicit LoadingScreen(QWidget* parent = nullptr); | ||||||
|  | 
 | ||||||
|  |     ~LoadingScreen(); | ||||||
|  | 
 | ||||||
|  |     /// Call before showing the loading screen to load the widgets with the logo and banner for the
 | ||||||
|  |     /// currently loaded application.
 | ||||||
|  |     void Prepare(Loader::AppLoader& loader); | ||||||
|  | 
 | ||||||
|  |     /// After the loading screen is hidden, the owner of this class can call this to clean up any
 | ||||||
|  |     /// 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; | ||||||
|  | 
 | ||||||
|  | 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: | ||||||
|  |     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; | ||||||
|  | 
 | ||||||
|  |     // Variables used to keep track of the current ETA.
 | ||||||
|  |     // If the rolling_average * shaders_remaining > eta_break_point then we want to display the eta.
 | ||||||
|  |     // We don't want to always display it since showing an ETA leads people to think its taking
 | ||||||
|  |     // longer that it is because ETAs are often wrong
 | ||||||
|  |     static constexpr std::chrono::seconds ETABreakPoint = std::chrono::seconds{10}; | ||||||
|  |     static constexpr std::size_t NumberOfDataPoints = 25; | ||||||
|  |     std::chrono::high_resolution_clock::time_point previous_time; | ||||||
|  |     std::chrono::duration<double> rolling_average = {}; | ||||||
|  |     bool eta_shown = false; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage); | ||||||
							
								
								
									
										188
									
								
								src/citra_qt/loading_screen.ui
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								src/citra_qt/loading_screen.ui
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,188 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <ui version="4.0"> | ||||||
|  |  <class>LoadingScreen</class> | ||||||
|  |  <widget class="QWidget" name="LoadingScreen"> | ||||||
|  |   <property name="geometry"> | ||||||
|  |    <rect> | ||||||
|  |     <x>0</x> | ||||||
|  |     <y>0</y> | ||||||
|  |     <width>746</width> | ||||||
|  |     <height>495</height> | ||||||
|  |    </rect> | ||||||
|  |   </property> | ||||||
|  |   <property name="styleSheet"> | ||||||
|  |    <string notr="true">background-color: rgb(0, 0, 0);</string> | ||||||
|  |   </property> | ||||||
|  |   <layout class="QVBoxLayout"> | ||||||
|  |    <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> | ||||||
|  |     <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> | ||||||
|  |        <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0,0,0,1,0"> | ||||||
|  |         <property name="spacing"> | ||||||
|  |          <number>15</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="sizeConstraint"> | ||||||
|  |          <enum>QLayout::SetNoConstraint</enum> | ||||||
|  |         </property> | ||||||
|  |         <item> | ||||||
|  |          <spacer name="verticalSpacer_2"> | ||||||
|  |           <property name="orientation"> | ||||||
|  |            <enum>Qt::Vertical</enum> | ||||||
|  |           </property> | ||||||
|  |           <property name="sizeHint" stdset="0"> | ||||||
|  |            <size> | ||||||
|  |             <width>20</width> | ||||||
|  |             <height>40</height> | ||||||
|  |            </size> | ||||||
|  |           </property> | ||||||
|  |          </spacer> | ||||||
|  |         </item> | ||||||
|  |         <item> | ||||||
|  |          <widget class="QLabel" name="icon"> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string/> | ||||||
|  |           </property> | ||||||
|  |           <property name="alignment"> | ||||||
|  |            <set>Qt::AlignCenter</set> | ||||||
|  |           </property> | ||||||
|  |           <property name="margin"> | ||||||
|  |            <number>5</number> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item> | ||||||
|  |          <widget class="QLabel" name="title"> | ||||||
|  |           <property name="styleSheet"> | ||||||
|  |            <string notr="true">background-color: black; color: white; | ||||||
|  | font: 75 20pt "Arial";</string> | ||||||
|  |           </property> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string/> | ||||||
|  |           </property> | ||||||
|  |           <property name="alignment"> | ||||||
|  |            <set>Qt::AlignCenter</set> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <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>Estimated Time 5m 4s</string> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item> | ||||||
|  |          <spacer name="verticalSpacer"> | ||||||
|  |           <property name="orientation"> | ||||||
|  |            <enum>Qt::Vertical</enum> | ||||||
|  |           </property> | ||||||
|  |           <property name="sizeHint" stdset="0"> | ||||||
|  |            <size> | ||||||
|  |             <width>20</width> | ||||||
|  |             <height>40</height> | ||||||
|  |            </size> | ||||||
|  |           </property> | ||||||
|  |          </spacer> | ||||||
|  |         </item> | ||||||
|  |        </layout> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |   </layout> | ||||||
|  |  </widget> | ||||||
|  |  <resources/> | ||||||
|  |  <connections/> | ||||||
|  | </ui> | ||||||
|  | @ -47,6 +47,7 @@ | ||||||
| #include "citra_qt/discord.h" | #include "citra_qt/discord.h" | ||||||
| #include "citra_qt/game_list.h" | #include "citra_qt/game_list.h" | ||||||
| #include "citra_qt/hotkeys.h" | #include "citra_qt/hotkeys.h" | ||||||
|  | #include "citra_qt/loading_screen.h" | ||||||
| #include "citra_qt/main.h" | #include "citra_qt/main.h" | ||||||
| #include "citra_qt/multiplayer/state.h" | #include "citra_qt/multiplayer/state.h" | ||||||
| #include "citra_qt/qt_image_interface.h" | #include "citra_qt/qt_image_interface.h" | ||||||
|  | @ -222,6 +223,17 @@ void GMainWindow::InitializeWidgets() { | ||||||
|     ui.horizontalLayout->addWidget(game_list_placeholder); |     ui.horizontalLayout->addWidget(game_list_placeholder); | ||||||
|     game_list_placeholder->setVisible(false); |     game_list_placeholder->setVisible(false); | ||||||
| 
 | 
 | ||||||
|  |     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(); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui.action_Leave_Room, |     multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui.action_Leave_Room, | ||||||
|                                              ui.action_Show_Room); |                                              ui.action_Show_Room); | ||||||
|     multiplayer_state->setVisible(false); |     multiplayer_state->setVisible(false); | ||||||
|  | @ -917,6 +929,9 @@ void GMainWindow::BootGame(const QString& filename) { | ||||||
|     connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget, |     connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget, | ||||||
|             &WaitTreeWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection); |             &WaitTreeWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection); | ||||||
| 
 | 
 | ||||||
|  |     connect(emu_thread.get(), &EmuThread::LoadProgress, loading_screen, | ||||||
|  |             &LoadingScreen::OnLoadProgress, Qt::QueuedConnection); | ||||||
|  | 
 | ||||||
|     // Update the GUI
 |     // Update the GUI
 | ||||||
|     registersWidget->OnDebugModeEntered(); |     registersWidget->OnDebugModeEntered(); | ||||||
|     if (ui.action_Single_Window_Mode->isChecked()) { |     if (ui.action_Single_Window_Mode->isChecked()) { | ||||||
|  | @ -925,8 +940,12 @@ void GMainWindow::BootGame(const QString& filename) { | ||||||
|     } |     } | ||||||
|     status_bar_update_timer.start(2000); |     status_bar_update_timer.start(2000); | ||||||
| 
 | 
 | ||||||
|  |     // show and hide the render_window to create the context
 | ||||||
|     render_window->show(); |     render_window->show(); | ||||||
|     render_window->setFocus(); |     render_window->hide(); | ||||||
|  | 
 | ||||||
|  |     loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); | ||||||
|  |     loading_screen->show(); | ||||||
| 
 | 
 | ||||||
|     emulation_running = true; |     emulation_running = true; | ||||||
|     if (ui.action_Fullscreen->isChecked()) { |     if (ui.action_Fullscreen->isChecked()) { | ||||||
|  | @ -1003,6 +1022,8 @@ void GMainWindow::ShutdownGame() { | ||||||
|     ui.action_Advance_Frame->setEnabled(false); |     ui.action_Advance_Frame->setEnabled(false); | ||||||
|     ui.action_Capture_Screenshot->setEnabled(false); |     ui.action_Capture_Screenshot->setEnabled(false); | ||||||
|     render_window->hide(); |     render_window->hide(); | ||||||
|  |     loading_screen->hide(); | ||||||
|  |     loading_screen->Clear(); | ||||||
|     if (game_list->isEmpty()) |     if (game_list->isEmpty()) | ||||||
|         game_list_placeholder->show(); |         game_list_placeholder->show(); | ||||||
|     else |     else | ||||||
|  | @ -1326,6 +1347,10 @@ void GMainWindow::OnStopGame() { | ||||||
|     ShutdownGame(); |     ShutdownGame(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void GMainWindow::OnLoadComplete() { | ||||||
|  |     loading_screen->OnLoadComplete(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void GMainWindow::OnMenuReportCompatibility() { | void GMainWindow::OnMenuReportCompatibility() { | ||||||
|     if (!Settings::values.citra_token.empty() && !Settings::values.citra_username.empty()) { |     if (!Settings::values.citra_token.empty() && !Settings::values.citra_username.empty()) { | ||||||
|         CompatDB compatdb{this}; |         CompatDB compatdb{this}; | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ class GraphicsVertexShaderWidget; | ||||||
| class GRenderWindow; | class GRenderWindow; | ||||||
| class IPCRecorderWidget; | class IPCRecorderWidget; | ||||||
| class LLEServiceModulesWidget; | class LLEServiceModulesWidget; | ||||||
|  | class LoadingScreen; | ||||||
| class MicroProfileDialog; | class MicroProfileDialog; | ||||||
| class MultiplayerState; | class MultiplayerState; | ||||||
| class ProfilerWidget; | class ProfilerWidget; | ||||||
|  | @ -75,6 +76,7 @@ public: | ||||||
| 
 | 
 | ||||||
| public slots: | public slots: | ||||||
|     void OnAppFocusStateChanged(Qt::ApplicationState state); |     void OnAppFocusStateChanged(Qt::ApplicationState state); | ||||||
|  |     void OnLoadComplete(); | ||||||
| 
 | 
 | ||||||
| signals: | signals: | ||||||
| 
 | 
 | ||||||
|  | @ -221,6 +223,7 @@ private: | ||||||
|     GRenderWindow* render_window; |     GRenderWindow* render_window; | ||||||
| 
 | 
 | ||||||
|     GameListPlaceholder* game_list_placeholder; |     GameListPlaceholder* game_list_placeholder; | ||||||
|  |     LoadingScreen* loading_screen; | ||||||
| 
 | 
 | ||||||
|     // Status bar elements
 |     // Status bar elements
 | ||||||
|     QProgressBar* progress_bar = nullptr; |     QProgressBar* progress_bar = nullptr; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue