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:
James Rowe 2019-01-17 00:01:00 -07:00
parent 83f8d1aa2e
commit 08fcf41b0a
9 changed files with 244 additions and 12 deletions

View file

@ -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)

View file

@ -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

View file

@ -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;

View file

@ -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;
};

View 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
View 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;
};

View 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>

View file

@ -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."));
}

View file

@ -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;