mirror of
https://github.com/Lime3DS/Lime3DS
synced 2024-12-26 17:12:37 -06: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…
Reference in a new issue