mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	QT Frontend: Add disk shader loading progress bar
Until we get a on screen display or async shader loading, we should at least have some measure of progress in the meantime. This is 90% a port from the loading screen I made for yuzu, but with a slightly different changed detection for when to display the ETA. Now we keep track of a rolling estimate for shader load ETA and only display a ETA if its going to take longer than 10 seconds.
This commit is contained in:
		
							parent
							
								
									c0df8271bf
								
							
						
					
					
						commit
						961a7b59c9
					
				
					 8 changed files with 542 additions and 6 deletions
				
			
		|  | @ -115,6 +115,9 @@ add_executable(citra-qt | |||
|     game_list_worker.h | ||||
|     hotkeys.cpp | ||||
|     hotkeys.h | ||||
|     loading_screen.cpp | ||||
|     loading_screen.h | ||||
|     loading_screen.ui | ||||
|     main.cpp | ||||
|     main.h | ||||
|     main.ui | ||||
|  |  | |||
|  | @ -46,12 +46,15 @@ void EmuThread::run() { | |||
|     MicroProfileOnThreadCreate("EmuThread"); | ||||
|     Frontend::ScopeAcquireContext scope(core_context); | ||||
| 
 | ||||
|     emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | ||||
| 
 | ||||
|     Core::System::GetInstance().Renderer().Rasterizer()->LoadDiskResources( | ||||
|         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, | ||||
|                       total); | ||||
|             emit LoadProgress(stage, value, total); | ||||
|         }); | ||||
| 
 | ||||
|     emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | ||||
| 
 | ||||
|     // Holds whether the cpu was running during the last iteration,
 | ||||
|     // so that the DebugModeLeft signal can be emitted before the
 | ||||
|     // next execution step.
 | ||||
|  | @ -127,6 +130,7 @@ OpenGLWindow::~OpenGLWindow() { | |||
| void OpenGLWindow::Present() { | ||||
|     if (!isExposed()) | ||||
|         return; | ||||
| 
 | ||||
|     context->makeCurrent(this); | ||||
|     VideoCore::g_renderer->TryPresent(100); | ||||
|     context->swapBuffers(this); | ||||
|  | @ -182,8 +186,8 @@ void OpenGLWindow::exposeEvent(QExposeEvent* event) { | |||
|     QWindow::exposeEvent(event); | ||||
| } | ||||
| 
 | ||||
| GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | ||||
|     : QWidget(parent), emu_thread(emu_thread) { | ||||
| GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) | ||||
|     : QWidget(parent_), emu_thread(emu_thread) { | ||||
| 
 | ||||
|     setWindowTitle(QStringLiteral("Citra %1 | %2-%3") | ||||
|                        .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); | ||||
|     setLayout(layout); | ||||
|     InputCommon::Init(); | ||||
| 
 | ||||
|     GMainWindow* parent = GetMainWindow(); | ||||
|     connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); | ||||
| } | ||||
| 
 | ||||
| GRenderWindow::~GRenderWindow() { | ||||
|  | @ -206,7 +213,12 @@ void GRenderWindow::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).
 | ||||
| //
 | ||||
|  | @ -363,12 +375,15 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) { | |||
| void GRenderWindow::InitRenderTarget() { | ||||
|     ReleaseRenderTarget(); | ||||
| 
 | ||||
|     first_frame = false; | ||||
| 
 | ||||
|     GMainWindow* parent = GetMainWindow(); | ||||
|     QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; | ||||
|     child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext()); | ||||
|     child_window->create(); | ||||
|     child_widget = createWindowContainer(child_window, this); | ||||
|     child_widget->resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); | ||||
| 
 | ||||
|     layout()->addWidget(child_widget); | ||||
| 
 | ||||
|     core_context = CreateSharedContext(); | ||||
|  |  | |||
|  | @ -23,6 +23,10 @@ class QOpenGLContext; | |||
| class GMainWindow; | ||||
| class GRenderWindow; | ||||
| 
 | ||||
| namespace VideoCore { | ||||
| enum class LoadCallbackStage; | ||||
| } | ||||
| 
 | ||||
| class GLContext : public Frontend::GraphicsContext { | ||||
| public: | ||||
|     explicit GLContext(QOpenGLContext* shared_context); | ||||
|  | @ -116,6 +120,8 @@ signals: | |||
|     void DebugModeLeft(); | ||||
| 
 | ||||
|     void ErrorThrown(Core::System::ResultStatus, std::string); | ||||
| 
 | ||||
|     void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); | ||||
| }; | ||||
| 
 | ||||
| class OpenGLWindow : public QWindow { | ||||
|  | @ -188,6 +194,11 @@ signals: | |||
|     /// Emitted when the window is closed
 | ||||
|     void Closed(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Emitted when the guest first calls SwapBuffers. This is used to hide the loading screen | ||||
|      */ | ||||
|     void FirstFrameDisplayed(); | ||||
| 
 | ||||
| private: | ||||
|     std::pair<u32, u32> ScaleTouch(QPointF pos) const; | ||||
|     void TouchBeginEvent(const QTouchEvent* event); | ||||
|  | @ -212,6 +223,7 @@ private: | |||
| 
 | ||||
|     /// Temporary storage of the screenshot taken
 | ||||
|     QImage screenshot_image; | ||||
|     bool first_frame = false; | ||||
| 
 | ||||
| protected: | ||||
|     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="0,0,0,1,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::AlignBottom|Qt::AlignHCenter</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::AlignHCenter|Qt::AlignTop</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/game_list.h" | ||||
| #include "citra_qt/hotkeys.h" | ||||
| #include "citra_qt/loading_screen.h" | ||||
| #include "citra_qt/main.h" | ||||
| #include "citra_qt/multiplayer/state.h" | ||||
| #include "citra_qt/qt_image_interface.h" | ||||
|  | @ -222,6 +223,17 @@ void GMainWindow::InitializeWidgets() { | |||
|     ui.horizontalLayout->addWidget(game_list_placeholder); | ||||
|     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, | ||||
|                                              ui.action_Show_Room); | ||||
|     multiplayer_state->setVisible(false); | ||||
|  | @ -917,6 +929,9 @@ void GMainWindow::BootGame(const QString& filename) { | |||
|     connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget, | ||||
|             &WaitTreeWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection); | ||||
| 
 | ||||
|     connect(emu_thread.get(), &EmuThread::LoadProgress, loading_screen, | ||||
|             &LoadingScreen::OnLoadProgress, Qt::QueuedConnection); | ||||
| 
 | ||||
|     // Update the GUI
 | ||||
|     registersWidget->OnDebugModeEntered(); | ||||
|     if (ui.action_Single_Window_Mode->isChecked()) { | ||||
|  | @ -925,8 +940,12 @@ void GMainWindow::BootGame(const QString& filename) { | |||
|     } | ||||
|     status_bar_update_timer.start(2000); | ||||
| 
 | ||||
|     // show and hide the render_window to create the context
 | ||||
|     render_window->show(); | ||||
|     render_window->setFocus(); | ||||
|     render_window->hide(); | ||||
| 
 | ||||
|     loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); | ||||
|     loading_screen->show(); | ||||
| 
 | ||||
|     emulation_running = true; | ||||
|     if (ui.action_Fullscreen->isChecked()) { | ||||
|  | @ -1003,6 +1022,8 @@ void GMainWindow::ShutdownGame() { | |||
|     ui.action_Advance_Frame->setEnabled(false); | ||||
|     ui.action_Capture_Screenshot->setEnabled(false); | ||||
|     render_window->hide(); | ||||
|     loading_screen->hide(); | ||||
|     loading_screen->Clear(); | ||||
|     if (game_list->isEmpty()) | ||||
|         game_list_placeholder->show(); | ||||
|     else | ||||
|  | @ -1326,6 +1347,10 @@ void GMainWindow::OnStopGame() { | |||
|     ShutdownGame(); | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::OnLoadComplete() { | ||||
|     loading_screen->OnLoadComplete(); | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::OnMenuReportCompatibility() { | ||||
|     if (!Settings::values.citra_token.empty() && !Settings::values.citra_username.empty()) { | ||||
|         CompatDB compatdb{this}; | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ class GraphicsVertexShaderWidget; | |||
| class GRenderWindow; | ||||
| class IPCRecorderWidget; | ||||
| class LLEServiceModulesWidget; | ||||
| class LoadingScreen; | ||||
| class MicroProfileDialog; | ||||
| class MultiplayerState; | ||||
| class ProfilerWidget; | ||||
|  | @ -75,6 +76,7 @@ public: | |||
| 
 | ||||
| public slots: | ||||
|     void OnAppFocusStateChanged(Qt::ApplicationState state); | ||||
|     void OnLoadComplete(); | ||||
| 
 | ||||
| signals: | ||||
| 
 | ||||
|  | @ -221,6 +223,7 @@ private: | |||
|     GRenderWindow* render_window; | ||||
| 
 | ||||
|     GameListPlaceholder* game_list_placeholder; | ||||
|     LoadingScreen* loading_screen; | ||||
| 
 | ||||
|     // Status bar elements
 | ||||
|     QProgressBar* progress_bar = nullptr; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue