forked from suyu/suyu
QT Frontend: Add a Loading screen with progressbar
With shader caches on the horizon, one requirement is to provide visible feedback for the progress. The shader cache reportedly takes several minutes to load for large caches that were invalidated, and as such we should provide a loading screen with progress. Adds a loading screen widget that will be shown until the first frame of the game is swapped. This was chosen in case shader caches are not being used, several games still take more than a few seconds to launch and could benefit from a loading screen.
This commit is contained in:
parent
83f8d1aa2e
commit
08fcf41b0a
9 changed files with 244 additions and 12 deletions
|
@ -45,5 +45,8 @@ function(copy_yuzu_Qt5_deps target_dir)
|
|||
|
||||
windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
|
||||
qjpeg$<$<CONFIG:Debug>:d>.*
|
||||
qgif$<$<CONFIG:Debug>:d>.*
|
||||
)
|
||||
endfunction(copy_yuzu_Qt5_deps)
|
||||
|
|
|
@ -68,6 +68,8 @@ add_executable(yuzu
|
|||
game_list_p.h
|
||||
game_list_worker.cpp
|
||||
game_list_worker.h
|
||||
loading_screen.cpp
|
||||
loading_screen.h
|
||||
hotkeys.cpp
|
||||
hotkeys.h
|
||||
main.cpp
|
||||
|
@ -102,9 +104,10 @@ set(UIS
|
|||
configuration/configure_system.ui
|
||||
configuration/configure_touchscreen_advanced.ui
|
||||
configuration/configure_web.ui
|
||||
hotkeys.ui
|
||||
main.ui
|
||||
compatdb.ui
|
||||
hotkeys.ui
|
||||
loading_screen.ui
|
||||
main.ui
|
||||
)
|
||||
|
||||
file(GLOB COMPAT_LIST
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
#include <QKeyEvent>
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "core/core.h"
|
||||
|
@ -17,6 +15,7 @@
|
|||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
#include "yuzu/bootmanager.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
|
||||
|
||||
|
@ -114,6 +113,8 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
|
|||
|
||||
InputCommon::Init();
|
||||
InputCommon::StartJoystickEventHandler();
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast<GMainWindow*>(parent),
|
||||
&GMainWindow::OnLoadComplete);
|
||||
}
|
||||
|
||||
GRenderWindow::~GRenderWindow() {
|
||||
|
@ -141,6 +142,10 @@ void GRenderWindow::SwapBuffers() {
|
|||
child->makeCurrent();
|
||||
|
||||
child->swapBuffers();
|
||||
if (!first_frame) {
|
||||
emit FirstFrameDisplayed();
|
||||
first_frame = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::MakeCurrent() {
|
||||
|
@ -309,6 +314,8 @@ void GRenderWindow::InitRenderTarget() {
|
|||
delete layout();
|
||||
}
|
||||
|
||||
first_frame = false;
|
||||
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
QGLFormat fmt;
|
||||
|
|
|
@ -152,6 +152,7 @@ public slots:
|
|||
signals:
|
||||
/// Emitted when the window is closed
|
||||
void Closed();
|
||||
void FirstFrameDisplayed();
|
||||
|
||||
private:
|
||||
std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
|
||||
|
@ -171,6 +172,8 @@ private:
|
|||
/// Temporary storage of the screenshot taken
|
||||
QImage screenshot_image;
|
||||
|
||||
bool first_frame = false;
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
};
|
||||
|
|
71
src/yuzu/loading_screen.cpp
Normal file
71
src/yuzu/loading_screen.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QByteArray>
|
||||
#include <QHBoxLayout>
|
||||
#include <QIODevice>
|
||||
#include <QImage>
|
||||
#include <QLabel>
|
||||
#include <QMovie>
|
||||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
#include <QPixmap>
|
||||
#include <QProgressBar>
|
||||
#include <QStyleOption>
|
||||
#include <QWindow>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "ui_loading_screen.h"
|
||||
#include "yuzu/loading_screen.h"
|
||||
|
||||
LoadingScreen::LoadingScreen(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::LoadingScreen>()) {
|
||||
ui->setupUi(this);
|
||||
// Progress bar is hidden until we have a use for it.
|
||||
ui->progress_bar->hide();
|
||||
}
|
||||
|
||||
LoadingScreen::~LoadingScreen() = default;
|
||||
|
||||
void LoadingScreen::Prepare(Loader::AppLoader& loader) {
|
||||
std::vector<u8> buffer;
|
||||
if (loader.ReadBanner(buffer) == Loader::ResultStatus::Success) {
|
||||
backing_mem =
|
||||
std::make_unique<QByteArray>(reinterpret_cast<char*>(buffer.data()), buffer.size());
|
||||
backing_buf = std::make_unique<QBuffer>(backing_mem.get());
|
||||
backing_buf->open(QIODevice::ReadOnly);
|
||||
animation = std::make_unique<QMovie>(backing_buf.get(), QByteArray("GIF"));
|
||||
animation->start();
|
||||
ui->banner->setMovie(animation.get());
|
||||
buffer.clear();
|
||||
}
|
||||
if (loader.ReadLogo(buffer) == Loader::ResultStatus::Success) {
|
||||
QPixmap map;
|
||||
map.loadFromData(buffer.data(), buffer.size());
|
||||
ui->logo->setPixmap(map);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadingScreen::OnLoadProgress(std::size_t value, std::size_t total) {
|
||||
if (total != previous_total) {
|
||||
ui->progress_bar->setMaximum(total);
|
||||
previous_total = total;
|
||||
}
|
||||
ui->progress_bar->setValue(value);
|
||||
}
|
||||
|
||||
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() {
|
||||
animation.reset();
|
||||
backing_buf.reset();
|
||||
backing_mem.reset();
|
||||
}
|
50
src/yuzu/loading_screen.h
Normal file
50
src/yuzu/loading_screen.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
|
||||
namespace Loader {
|
||||
class AppLoader;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class LoadingScreen;
|
||||
}
|
||||
|
||||
class QBuffer;
|
||||
class QByteArray;
|
||||
class QMovie;
|
||||
|
||||
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();
|
||||
|
||||
// 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;
|
||||
|
||||
void OnLoadProgress(std::size_t value, std::size_t total);
|
||||
|
||||
private:
|
||||
std::unique_ptr<QMovie> animation;
|
||||
std::unique_ptr<QBuffer> backing_buf;
|
||||
std::unique_ptr<QByteArray> backing_mem;
|
||||
std::unique_ptr<Ui::LoadingScreen> ui;
|
||||
std::size_t previous_total = 0;
|
||||
};
|
79
src/yuzu/loading_screen.ui
Normal file
79
src/yuzu/loading_screen.ui
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?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="QLabel" name="logo">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>30</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progress_bar">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-size: 26px;</string>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="format">
|
||||
<string>Loading Shaders %v out of %m</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item alignment="Qt::AlignRight|Qt::AlignBottom">
|
||||
<widget class="QLabel" name="banner">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: black;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>30</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -92,6 +92,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
|||
#include "yuzu/game_list.h"
|
||||
#include "yuzu/game_list_p.h"
|
||||
#include "yuzu/hotkeys.h"
|
||||
#include "yuzu/loading_screen.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/ui_settings.h"
|
||||
|
||||
|
@ -411,6 +412,10 @@ void GMainWindow::InitializeWidgets() {
|
|||
game_list = new GameList(vfs, this);
|
||||
ui.horizontalLayout->addWidget(game_list);
|
||||
|
||||
loading_screen = new LoadingScreen(this);
|
||||
loading_screen->hide();
|
||||
ui.horizontalLayout->addWidget(loading_screen);
|
||||
|
||||
// Create status bar
|
||||
message_label = new QLabel();
|
||||
// Configured separately for left alignment
|
||||
|
@ -897,8 +902,9 @@ void GMainWindow::BootGame(const QString& filename) {
|
|||
.arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc,
|
||||
QString::fromStdString(title_name)));
|
||||
|
||||
render_window->show();
|
||||
render_window->setFocus();
|
||||
loading_screen->Prepare(Core::System::GetInstance().GetAppLoader());
|
||||
loading_screen->show();
|
||||
loading_screen->setFocus();
|
||||
|
||||
emulation_running = true;
|
||||
if (ui.action_Fullscreen->isChecked()) {
|
||||
|
@ -932,6 +938,8 @@ void GMainWindow::ShutdownGame() {
|
|||
ui.action_Load_Amiibo->setEnabled(false);
|
||||
ui.action_Capture_Screenshot->setEnabled(false);
|
||||
render_window->hide();
|
||||
loading_screen->hide();
|
||||
loading_screen->Clear();
|
||||
game_list->show();
|
||||
game_list->setFilterFocus();
|
||||
setWindowTitle(QString("yuzu %1| %2-%3")
|
||||
|
@ -1505,6 +1513,13 @@ void GMainWindow::OnStopGame() {
|
|||
ShutdownGame();
|
||||
}
|
||||
|
||||
void GMainWindow::OnLoadComplete() {
|
||||
loading_screen->hide();
|
||||
loading_screen->Clear();
|
||||
render_window->show();
|
||||
render_window->setFocus();
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuReportCompatibility() {
|
||||
if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) {
|
||||
CompatDB compatdb{this};
|
||||
|
@ -1771,9 +1786,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
|
|||
this, tr("Confirm Key Rederivation"),
|
||||
tr("You are about to force rederive all of your keys. \nIf you do not know what this "
|
||||
"means or what you are doing, \nthis is a potentially destructive action. \nPlease "
|
||||
"make "
|
||||
"sure this is what you want \nand optionally make backups.\n\nThis will delete your "
|
||||
"autogenerated key files and re-run the key derivation module."),
|
||||
"make sure this is what you want \nand optionally make backups.\n\nThis will delete "
|
||||
"your autogenerated key files and re-run the key derivation module."),
|
||||
QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
|
||||
|
||||
if (res == QMessageBox::Cancel)
|
||||
|
@ -1818,7 +1832,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
|
|||
errors +
|
||||
tr("<br><br>You can get all of these and dump all of your games easily by "
|
||||
"following <a href='https://yuzu-emu.org/help/quickstart/'>the "
|
||||
"quickstart guide</a>. Alternatively, you can use another method of dumping "
|
||||
"quickstart guide</a>. Alternatively, you can use another method of dumping"
|
||||
"to obtain all of your keys."));
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ class GImageInfo;
|
|||
class GraphicsBreakPointsWidget;
|
||||
class GraphicsSurfaceWidget;
|
||||
class GRenderWindow;
|
||||
class LoadingScreen;
|
||||
class MicroProfileDialog;
|
||||
class ProfilerWidget;
|
||||
class QLabel;
|
||||
|
@ -109,10 +110,10 @@ signals:
|
|||
void WebBrowserFinishedBrowsing();
|
||||
|
||||
public slots:
|
||||
void OnLoadComplete();
|
||||
void ProfileSelectorSelectProfile();
|
||||
void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
|
||||
void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
|
||||
|
||||
void WebBrowserOpenPage(std::string_view filename, std::string_view arguments);
|
||||
|
||||
private:
|
||||
|
@ -212,6 +213,7 @@ private:
|
|||
|
||||
GRenderWindow* render_window;
|
||||
GameList* game_list;
|
||||
LoadingScreen* loading_screen;
|
||||
|
||||
// Status bar elements
|
||||
QLabel* message_label = nullptr;
|
||||
|
|
Loading…
Reference in a new issue