summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core.cpp16
-rw-r--r--src/core/core.h13
-rw-r--r--src/core/file_sys/content_archive.cpp6
-rw-r--r--src/core/file_sys/content_archive.h10
-rw-r--r--src/core/file_sys/directory.h23
-rw-r--r--src/core/frontend/applets/web_browser.cpp2
-rw-r--r--src/core/frontend/applets/web_browser.h4
-rw-r--r--src/core/frontend/scope_acquire_window_context.cpp18
-rw-r--r--src/core/frontend/scope_acquire_window_context.h23
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp12
-rw-r--r--src/core/loader/loader.h4
-rw-r--r--src/core/loader/nax.cpp9
-rw-r--r--src/core/loader/nax.h3
-rw-r--r--src/core/loader/nca.cpp19
-rw-r--r--src/core/loader/nca.h3
-rw-r--r--src/core/loader/nsp.cpp9
-rw-r--r--src/core/loader/nsp.h3
-rw-r--r--src/core/loader/xci.cpp8
-rw-r--r--src/core/loader/xci.h3
-rw-r--r--src/core/settings.cpp29
-rw-r--r--src/core/settings.h1
-rw-r--r--src/video_core/engines/maxwell_3d.cpp24
-rw-r--r--src/video_core/engines/maxwell_3d.h5
-rw-r--r--src/video_core/rasterizer_interface.h9
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp93
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h24
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp27
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h3
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp27
-rw-r--r--src/video_core/renderer_opengl/gl_state.h9
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp26
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.h3
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp46
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h10
-rw-r--r--src/yuzu/CMakeLists.txt5
-rw-r--r--src/yuzu/applets/web_browser.cpp6
-rw-r--r--src/yuzu/applets/web_browser.h9
-rw-r--r--src/yuzu/bootmanager.cpp11
-rw-r--r--src/yuzu/bootmanager.h3
-rw-r--r--src/yuzu/configuration/configure_debug.cpp1
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp1
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp1
-rw-r--r--src/yuzu/configuration/configure_input_player.h8
-rw-r--r--src/yuzu/configuration/configure_per_general.cpp1
-rw-r--r--src/yuzu/configuration/configure_per_general.h6
-rw-r--r--src/yuzu/configuration/configure_system.cpp12
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.h2
-rw-r--r--src/yuzu/configuration/configure_web.cpp11
-rw-r--r--src/yuzu/loading_screen.cpp213
-rw-r--r--src/yuzu/loading_screen.h92
-rw-r--r--src/yuzu/loading_screen.ui161
-rw-r--r--src/yuzu/main.cpp49
-rw-r--r--src/yuzu/main.h4
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp1
57 files changed, 896 insertions, 234 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index aa9e05089..965c28787 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -95,6 +95,8 @@ add_library(core STATIC
frontend/framebuffer_layout.cpp
frontend/framebuffer_layout.h
frontend/input.h
+ frontend/scope_acquire_window_context.cpp
+ frontend/scope_acquire_window_context.h
gdbstub/gdbstub.cpp
gdbstub/gdbstub.h
hle/ipc.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 31c590866..572814e4b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -443,27 +443,31 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
-void System::SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet) {
+void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) {
impl->profile_selector = std::move(applet);
}
-const Core::Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
+const Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
return *impl->profile_selector;
}
-void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
+void System::SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet) {
impl->software_keyboard = std::move(applet);
}
-const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
+const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
return *impl->software_keyboard;
}
-void System::SetWebBrowser(std::unique_ptr<Core::Frontend::WebBrowserApplet> applet) {
+void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) {
impl->web_browser = std::move(applet);
}
-const Core::Frontend::WebBrowserApplet& System::GetWebBrowser() const {
+Frontend::WebBrowserApplet& System::GetWebBrowser() {
+ return *impl->web_browser;
+}
+
+const Frontend::WebBrowserApplet& System::GetWebBrowser() const {
return *impl->web_browser;
}
diff --git a/src/core/core.h b/src/core/core.h
index a53dbb4d4..511a5ad3a 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -243,17 +243,18 @@ public:
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
- void SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet);
+ void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
- const Core::Frontend::ProfileSelectApplet& GetProfileSelector() const;
+ const Frontend::ProfileSelectApplet& GetProfileSelector() const;
- void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
+ void SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet);
- const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
+ const Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
- void SetWebBrowser(std::unique_ptr<Core::Frontend::WebBrowserApplet> applet);
+ void SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet);
- const Core::Frontend::WebBrowserApplet& GetWebBrowser() const;
+ Frontend::WebBrowserApplet& GetWebBrowser();
+ const Frontend::WebBrowserApplet& GetWebBrowser() const;
private:
System();
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 19b6f8600..5aa3b600b 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -359,6 +359,8 @@ bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTable
dirs.push_back(std::move(npfs));
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
+ else if (IsDirectoryLogoPartition(dirs.back()))
+ logo = dirs.back();
} else {
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
@@ -546,4 +548,8 @@ u64 NCA::GetBaseIVFCOffset() const {
return ivfc_offset;
}
+VirtualDir NCA::GetLogoPartition() const {
+ return logo;
+}
+
} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 99294cbb4..5d4d05c82 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -74,6 +74,13 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
}
+inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) {
+ // NintendoLogo is the static image in the top left corner while StartupMovie is the animation
+ // in the bottom right corner.
+ return pfs->GetFile("NintendoLogo.png") != nullptr &&
+ pfs->GetFile("StartupMovie.gif") != nullptr;
+}
+
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
// After construction, use GetStatus to determine if the file is valid and ready to be used.
class NCA : public ReadOnlyVfsDirectory {
@@ -102,6 +109,8 @@ public:
// Returns the base ivfc offset used in BKTR patching.
u64 GetBaseIVFCOffset() const;
+ VirtualDir GetLogoPartition() const;
+
private:
bool CheckSupportedNCA(const NCAHeader& header);
bool HandlePotentialHeaderDecryption();
@@ -122,6 +131,7 @@ private:
VirtualFile romfs = nullptr;
VirtualDir exefs = nullptr;
+ VirtualDir logo = nullptr;
VirtualFile file;
VirtualFile bktr_base_romfs;
u64 ivfc_offset = 0;
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h
index 6690aa575..7b5c509fb 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory.h
@@ -39,27 +39,4 @@ static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x31
static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry.");
static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry.");
-class DirectoryBackend : NonCopyable {
-public:
- DirectoryBackend() {}
- virtual ~DirectoryBackend() {}
-
- /**
- * List files contained in the directory
- * @param count Number of entries to return at once in entries
- * @param entries Buffer to read data into
- * @return Number of entries listed
- */
- virtual u64 Read(const u64 count, Entry* entries) = 0;
-
- /// Returns the number of entries still left to read.
- virtual u64 GetEntryCount() const = 0;
-
- /**
- * Close the directory
- * @return true if the directory closed correctly
- */
- virtual bool Close() const = 0;
-};
-
} // namespace FileSys
diff --git a/src/core/frontend/applets/web_browser.cpp b/src/core/frontend/applets/web_browser.cpp
index 6a36b4b8f..3a3d3d0bf 100644
--- a/src/core/frontend/applets/web_browser.cpp
+++ b/src/core/frontend/applets/web_browser.cpp
@@ -13,7 +13,7 @@ DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
void DefaultWebBrowserApplet::OpenPage(std::string_view filename,
std::function<void()> unpack_romfs_callback,
- std::function<void()> finished_callback) const {
+ std::function<void()> finished_callback) {
LOG_INFO(Service_AM,
"(STUBBED) called - No suitable web browser implementation found to open website page "
"at '{}'!",
diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h
index 41d272d26..f952856af 100644
--- a/src/core/frontend/applets/web_browser.h
+++ b/src/core/frontend/applets/web_browser.h
@@ -14,7 +14,7 @@ public:
virtual ~WebBrowserApplet();
virtual void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback,
- std::function<void()> finished_callback) const = 0;
+ std::function<void()> finished_callback) = 0;
};
class DefaultWebBrowserApplet final : public WebBrowserApplet {
@@ -22,7 +22,7 @@ public:
~DefaultWebBrowserApplet() override;
void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback,
- std::function<void()> finished_callback) const override;
+ std::function<void()> finished_callback) override;
};
} // namespace Core::Frontend
diff --git a/src/core/frontend/scope_acquire_window_context.cpp b/src/core/frontend/scope_acquire_window_context.cpp
new file mode 100644
index 000000000..3663dad17
--- /dev/null
+++ b/src/core/frontend/scope_acquire_window_context.cpp
@@ -0,0 +1,18 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/frontend/emu_window.h"
+#include "core/frontend/scope_acquire_window_context.h"
+
+namespace Core::Frontend {
+
+ScopeAcquireWindowContext::ScopeAcquireWindowContext(Core::Frontend::EmuWindow& emu_window_)
+ : emu_window{emu_window_} {
+ emu_window.MakeCurrent();
+}
+ScopeAcquireWindowContext::~ScopeAcquireWindowContext() {
+ emu_window.DoneCurrent();
+}
+
+} // namespace Core::Frontend
diff --git a/src/core/frontend/scope_acquire_window_context.h b/src/core/frontend/scope_acquire_window_context.h
new file mode 100644
index 000000000..2d9f6e825
--- /dev/null
+++ b/src/core/frontend/scope_acquire_window_context.h
@@ -0,0 +1,23 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Core::Frontend {
+
+class EmuWindow;
+
+/// Helper class to acquire/release window context within a given scope
+class ScopeAcquireWindowContext : NonCopyable {
+public:
+ explicit ScopeAcquireWindowContext(Core::Frontend::EmuWindow& window);
+ ~ScopeAcquireWindowContext();
+
+private:
+ Core::Frontend::EmuWindow& emu_window;
+};
+
+} // namespace Core::Frontend
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index d975207f5..9b0aa7f5f 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -2,9 +2,16 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
+#include <cstring>
+#include <vector>
+
+#include "common/assert.h"
+#include "common/common_funcs.h"
#include "common/common_paths.h"
+#include "common/file_util.h"
#include "common/hex_util.h"
-#include "common/logging/backend.h"
+#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
@@ -12,7 +19,6 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
-#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs_types.h"
#include "core/frontend/applets/web_browser.h"
#include "core/hle/kernel/process.h"
@@ -146,7 +152,7 @@ void WebBrowser::Execute() {
return;
}
- const auto& frontend{Core::System::GetInstance().GetWebBrowser()};
+ auto& frontend{Core::System::GetInstance().GetWebBrowser()};
frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
}
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 01f984098..bb925f4a6 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -178,6 +178,8 @@ public:
/**
* Get the banner (typically banner section) of the application
+ * In the context of NX, this is the animation that displays in the bottom right of the screen
+ * when a game boots. Stored in GIF format.
* @param buffer Reference to buffer to store data
* @return ResultStatus result of function
*/
@@ -187,6 +189,8 @@ public:
/**
* Get the logo (typically logo section) of the application
+ * In the context of NX, this is the static image that displays in the top left of the screen
+ * when a game boots. Stored in JPEG format.
* @param buffer Reference to buffer to store data
* @return ResultStatus result of function
*/
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index a093e3d36..93a970d10 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -79,4 +79,13 @@ u64 AppLoader_NAX::ReadRomFSIVFCOffset() const {
ResultStatus AppLoader_NAX::ReadProgramId(u64& out_program_id) {
return nca_loader->ReadProgramId(out_program_id);
}
+
+ResultStatus AppLoader_NAX::ReadBanner(std::vector<u8>& buffer) {
+ return nca_loader->ReadBanner(buffer);
+}
+
+ResultStatus AppLoader_NAX::ReadLogo(std::vector<u8>& buffer) {
+ return nca_loader->ReadLogo(buffer);
+}
+
} // namespace Loader
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index 0a97511b8..f40079574 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -39,6 +39,9 @@ public:
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadProgramId(u64& out_program_id) override;
+ ResultStatus ReadBanner(std::vector<u8>& buffer) override;
+ ResultStatus ReadLogo(std::vector<u8>& buffer) override;
+
private:
std::unique_ptr<FileSys::NAX> nax;
std::unique_ptr<AppLoader_NCA> nca_loader;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 7e1b0d84f..ce8196fcf 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -84,4 +84,23 @@ ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
return ResultStatus::Success;
}
+ResultStatus AppLoader_NCA::ReadBanner(std::vector<u8>& buffer) {
+ if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
+ return ResultStatus::ErrorNotInitialized;
+ const auto logo = nca->GetLogoPartition();
+ if (logo == nullptr)
+ return ResultStatus::ErrorNoIcon;
+ buffer = logo->GetFile("StartupMovie.gif")->ReadAllBytes();
+ return ResultStatus::Success;
+}
+
+ResultStatus AppLoader_NCA::ReadLogo(std::vector<u8>& buffer) {
+ if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
+ return ResultStatus::ErrorNotInitialized;
+ const auto logo = nca->GetLogoPartition();
+ if (logo == nullptr)
+ return ResultStatus::ErrorNoIcon;
+ buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes();
+ return ResultStatus::Success;
+}
} // namespace Loader
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index cbbe701d2..b9f077468 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -39,6 +39,9 @@ public:
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadProgramId(u64& out_program_id) override;
+ ResultStatus ReadBanner(std::vector<u8>& buffer) override;
+ ResultStatus ReadLogo(std::vector<u8>& buffer) override;
+
private:
std::unique_ptr<FileSys::NCA> nca;
std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 7fcb12aa2..7da1f8960 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -166,4 +166,13 @@ ResultStatus AppLoader_NSP::ReadManualRomFS(FileSys::VirtualFile& file) {
file = nca->GetRomFS();
return file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
}
+
+ResultStatus AppLoader_NSP::ReadBanner(std::vector<u8>& buffer) {
+ return secondary_loader->ReadBanner(buffer);
+}
+
+ResultStatus AppLoader_NSP::ReadLogo(std::vector<u8>& buffer) {
+ return secondary_loader->ReadLogo(buffer);
+}
+
} // namespace Loader
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index b6b309400..953a1b508 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -46,6 +46,9 @@ public:
ResultStatus ReadControlData(FileSys::NACP& nacp) override;
ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) override;
+ ResultStatus ReadBanner(std::vector<u8>& buffer) override;
+ ResultStatus ReadLogo(std::vector<u8>& buffer) override;
+
private:
std::unique_ptr<FileSys::NSP> nsp;
std::unique_ptr<AppLoader> secondary_loader;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index ff60a3756..89f7bbf77 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -137,4 +137,12 @@ ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& file) {
return file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
}
+ResultStatus AppLoader_XCI::ReadBanner(std::vector<u8>& buffer) {
+ return nca_loader->ReadBanner(buffer);
+}
+
+ResultStatus AppLoader_XCI::ReadLogo(std::vector<u8>& buffer) {
+ return nca_loader->ReadLogo(buffer);
+}
+
} // namespace Loader
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index e18531c93..d6995b61e 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -46,6 +46,9 @@ public:
ResultStatus ReadControlData(FileSys::NACP& control) override;
ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) override;
+ ResultStatus ReadBanner(std::vector<u8>& buffer) override;
+ ResultStatus ReadLogo(std::vector<u8>& buffer) override;
+
private:
std::unique_ptr<FileSys::XCI> xci;
std::unique_ptr<AppLoader_NCA> nca_loader;
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 26fcd3405..2e232e1e7 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -74,4 +74,33 @@ void Apply() {
Service::HID::ReloadInputDevices();
}
+template <typename T>
+void LogSetting(const std::string& name, const T& value) {
+ LOG_INFO(Config, "{}: {}", name, value);
+}
+
+void LogSettings() {
+ LOG_INFO(Config, "yuzu Configuration:");
+ LogSetting("System_UseDockedMode", Settings::values.use_docked_mode);
+ LogSetting("System_EnableNfc", Settings::values.enable_nfc);
+ LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
+ LogSetting("System_CurrentUser", Settings::values.current_user);
+ LogSetting("System_LanguageIndex", Settings::values.language_index);
+ LogSetting("Core_UseCpuJit", Settings::values.use_cpu_jit);
+ LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
+ LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
+ LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
+ LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
+ LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
+ LogSetting("Audio_OutputEngine", Settings::values.sink_id);
+ LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
+ LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
+ LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
+ LogSetting("DataStorage_NandDir", Settings::values.nand_dir);
+ LogSetting("DataStorage_SdmcDir", Settings::values.sdmc_dir);
+ LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
+ LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
+ LogSetting("Debugging_ProgramArgs", Settings::values.program_args);
+}
+
} // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index 29ce98983..c97387fc7 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -425,4 +425,5 @@ struct Values {
} extern values;
void Apply();
+void LogSettings();
} // namespace Settings
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 0ed7bc5d8..a388b3944 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -37,6 +37,7 @@ void Maxwell3D::InitializeRegisterDefaults() {
regs.viewports[viewport].depth_range_near = 0.0f;
regs.viewports[viewport].depth_range_far = 1.0f;
}
+
// Doom and Bomberman seems to use the uninitialized registers and just enable blend
// so initialize blend registers with sane values
regs.blend.equation_rgb = Regs::Blend::Equation::Add;
@@ -66,6 +67,7 @@ void Maxwell3D::InitializeRegisterDefaults() {
regs.stencil_back_func_func = Regs::ComparisonOp::Always;
regs.stencil_back_func_mask = 0xFFFFFFFF;
regs.stencil_back_mask = 0xFFFFFFFF;
+
// TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
// register carrying a default value. Assume it's OpenGL's default (1).
regs.point_size = 1.0f;
@@ -78,6 +80,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
regs.color_mask[color_mask].B.Assign(1);
regs.color_mask[color_mask].A.Assign(1);
}
+
+ // Commercial games seem to assume this value is enabled and nouveau sets this value manually.
+ regs.rt_separate_frag_data = 1;
}
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
@@ -135,6 +140,25 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
if (regs.reg_array[method_call.method] != method_call.argument) {
regs.reg_array[method_call.method] = method_call.argument;
+ // Color buffers
+ constexpr u32 first_rt_reg = MAXWELL3D_REG_INDEX(rt);
+ constexpr u32 registers_per_rt = sizeof(regs.rt[0]) / sizeof(u32);
+ if (method_call.method >= first_rt_reg &&
+ method_call.method < first_rt_reg + registers_per_rt * Regs::NumRenderTargets) {
+ const std::size_t rt_index = (method_call.method - first_rt_reg) / registers_per_rt;
+ dirty_flags.color_buffer |= 1u << static_cast<u32>(rt_index);
+ }
+
+ // Zeta buffer
+ constexpr u32 registers_in_zeta = sizeof(regs.zeta) / sizeof(u32);
+ if (method_call.method == MAXWELL3D_REG_INDEX(zeta_enable) ||
+ method_call.method == MAXWELL3D_REG_INDEX(zeta_width) ||
+ method_call.method == MAXWELL3D_REG_INDEX(zeta_height) ||
+ (method_call.method >= MAXWELL3D_REG_INDEX(zeta) &&
+ method_call.method < MAXWELL3D_REG_INDEX(zeta) + registers_in_zeta)) {
+ dirty_flags.zeta_buffer = true;
+ }
+
// Shader
constexpr u32 shader_registers_count =
sizeof(regs.shader_config[0]) * Regs::MaxShaderProgram / sizeof(u32);
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index d50e5a126..1f76aa670 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1089,12 +1089,17 @@ public:
MemoryManager& memory_manager;
struct DirtyFlags {
+ u8 color_buffer = 0xFF;
+ bool zeta_buffer = true;
+
bool shaders = true;
bool vertex_attrib_format = true;
u32 vertex_array = 0xFFFFFFFF;
void OnMemoryWrite() {
+ color_buffer = 0xFF;
+ zeta_buffer = true;
shaders = true;
vertex_array = 0xFFFFFFFF;
}
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 06fc59dbe..ff5310848 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -4,6 +4,7 @@
#pragma once
+#include <functional>
#include "common/common_types.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/gpu.h"
@@ -11,6 +12,14 @@
namespace VideoCore {
+enum class LoadCallbackStage {
+ Prepare,
+ Decompile,
+ Build,
+ Complete,
+};
+using DiskResourceLoadCallback = std::function<void(LoadCallbackStage, std::size_t, std::size_t)>;
+
class RasterizerInterface {
public:
virtual ~RasterizerInterface() {}
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 46a6c0308..bd2b30e77 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -14,7 +14,7 @@
namespace OpenGL {
OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
- : RasterizerCache{rasterizer}, stream_buffer(GL_ARRAY_BUFFER, size) {}
+ : RasterizerCache{rasterizer}, stream_buffer(size, true) {}
GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size,
std::size_t alignment, bool cache) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 97412590b..71829fee0 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -135,27 +135,31 @@ void RasterizerOpenGL::CheckExtensions() {
}
}
-void RasterizerOpenGL::SetupVertexFormat() {
+GLuint RasterizerOpenGL::SetupVertexFormat() {
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& regs = gpu.regs;
- if (!gpu.dirty_flags.vertex_attrib_format)
- return;
+ if (!gpu.dirty_flags.vertex_attrib_format) {
+ return state.draw.vertex_array;
+ }
gpu.dirty_flags.vertex_attrib_format = false;
MICROPROFILE_SCOPE(OpenGL_VAO);
auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format);
- auto& VAO = iter->second;
+ auto& vao_entry = iter->second;
if (is_cache_miss) {
- VAO.Create();
- state.draw.vertex_array = VAO.handle;
- state.ApplyVertexBufferState();
+ vao_entry.Create();
+ const GLuint vao = vao_entry.handle;
- // The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work
- // around.
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_cache.GetHandle());
+ // Eventhough we are using DSA to create this vertex array, there is a bug on Intel's blob
+ // that fails to properly create the vertex array if it's not bound even after creating it
+ // with glCreateVertexArrays
+ state.draw.vertex_array = vao;
+ state.ApplyVertexArrayState();
+
+ glVertexArrayElementBuffer(vao, buffer_cache.GetHandle());
// Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL.
// Enables the first 16 vertex attributes always, as we don't know which ones are actually
@@ -163,7 +167,7 @@ void RasterizerOpenGL::SetupVertexFormat() {
// for now to avoid OpenGL errors.
// TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
// assume every shader uses them all.
- for (unsigned index = 0; index < 16; ++index) {
+ for (u32 index = 0; index < 16; ++index) {
const auto& attrib = regs.vertex_attrib_format[index];
// Ignore invalid attributes.
@@ -178,28 +182,29 @@ void RasterizerOpenGL::SetupVertexFormat() {
ASSERT(buffer.IsEnabled());
- glEnableVertexAttribArray(index);
+ glEnableVertexArrayAttrib(vao, index);
if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
attrib.type ==
Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
- glVertexAttribIFormat(index, attrib.ComponentCount(),
- MaxwellToGL::VertexType(attrib), attrib.offset);
+ glVertexArrayAttribIFormat(vao, index, attrib.ComponentCount(),
+ MaxwellToGL::VertexType(attrib), attrib.offset);
} else {
- glVertexAttribFormat(index, attrib.ComponentCount(),
- MaxwellToGL::VertexType(attrib),
- attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
+ glVertexArrayAttribFormat(
+ vao, index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
+ attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
}
- glVertexAttribBinding(index, attrib.buffer);
+ glVertexArrayAttribBinding(vao, index, attrib.buffer);
}
}
- state.draw.vertex_array = VAO.handle;
- state.ApplyVertexBufferState();
// Rebinding the VAO invalidates the vertex buffer bindings.
gpu.dirty_flags.vertex_array = 0xFFFFFFFF;
+
+ state.draw.vertex_array = vao_entry.handle;
+ return vao_entry.handle;
}
-void RasterizerOpenGL::SetupVertexBuffer() {
+void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& regs = gpu.regs;
@@ -217,7 +222,7 @@ void RasterizerOpenGL::SetupVertexBuffer() {
if (!vertex_array.IsEnabled())
continue;
- Tegra::GPUVAddr start = vertex_array.StartAddress();
+ const Tegra::GPUVAddr start = vertex_array.StartAddress();
const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
ASSERT(end > start);
@@ -225,21 +230,18 @@ void RasterizerOpenGL::SetupVertexBuffer() {
const GLintptr vertex_buffer_offset = buffer_cache.UploadMemory(start, size);
// Bind the vertex array to the buffer at the current offset.
- glBindVertexBuffer(index, buffer_cache.GetHandle(), vertex_buffer_offset,
- vertex_array.stride);
+ glVertexArrayVertexBuffer(vao, index, buffer_cache.GetHandle(), vertex_buffer_offset,
+ vertex_array.stride);
if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
// Enable vertex buffer instancing with the specified divisor.
- glVertexBindingDivisor(index, vertex_array.divisor);
+ glVertexArrayBindingDivisor(vao, index, vertex_array.divisor);
} else {
// Disable the vertex buffer instancing.
- glVertexBindingDivisor(index, 0);
+ glVertexArrayBindingDivisor(vao, index, 0);
}
}
- // Implicit set by glBindVertexBuffer. Stupid glstate handling...
- state.draw.vertex_buffer = buffer_cache.GetHandle();
-
gpu.dirty_flags.vertex_array = 0;
}
@@ -365,7 +367,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
// (sometimes it's half the screen, sometimes three quarters). To avoid this, enable the
// clip distances only when it's written by a shader stage.
for (std::size_t i = 0; i < Maxwell::NumClipDistances; ++i) {
- clip_distances[i] |= shader->GetShaderEntries().clip_distances[i];
+ clip_distances[i] = clip_distances[i] || shader->GetShaderEntries().clip_distances[i];
}
// When VertexA is enabled, we have dual vertex shaders
@@ -488,17 +490,26 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us
bool using_depth_fb, bool preserve_contents,
std::optional<std::size_t> single_color_target) {
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
- const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
+ const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
+ const auto& regs = gpu.regs;
+
+ const FramebufferConfigState fb_config_state{using_color_fb, using_depth_fb, preserve_contents,
+ single_color_target};
+ if (fb_config_state == current_framebuffer_config_state && gpu.dirty_flags.color_buffer == 0 &&
+ !gpu.dirty_flags.zeta_buffer) {
+ // Only skip if the previous ConfigureFramebuffers call was from the same kind (multiple or
+ // single color targets). This is done because the guest registers may not change but the
+ // host framebuffer may contain different attachments
+ return;
+ }
+ current_framebuffer_config_state = fb_config_state;
Surface depth_surface;
if (using_depth_fb) {
depth_surface = res_cache.GetDepthBufferSurface(preserve_contents);
}
- // TODO(bunnei): Figure out how the below register works. According to envytools, this should be
- // used to enable multiple render targets. However, it is left unset on all games that I have
- // tested.
- UNIMPLEMENTED_IF(regs.rt_separate_frag_data != 0);
+ UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0);
// Bind the framebuffer surfaces
current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
@@ -632,8 +643,6 @@ void RasterizerOpenGL::Clear() {
return;
}
- ScopeAcquireGLContext acquire_context{emu_window};
-
ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false,
regs.clear_buffers.RT.Value());
if (regs.clear_flags.scissor) {
@@ -667,8 +676,6 @@ void RasterizerOpenGL::DrawArrays() {
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& regs = gpu.regs;
- ScopeAcquireGLContext acquire_context{emu_window};
-
ConfigureFramebuffers(state);
SyncColorMask();
SyncFragmentColorClampState();
@@ -691,9 +698,6 @@ void RasterizerOpenGL::DrawArrays() {
// Draw the vertex batch
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
- state.draw.vertex_buffer = buffer_cache.GetHandle();
- state.ApplyVertexBufferState();
-
std::size_t buffer_size = CalculateVertexArraysSize();
// Add space for index buffer (keeping in mind non-core primitives)
@@ -723,8 +727,9 @@ void RasterizerOpenGL::DrawArrays() {
gpu.dirty_flags.vertex_array = 0xFFFFFFFF;
}
- SetupVertexFormat();
- SetupVertexBuffer();
+ const GLuint vao = SetupVertexFormat();
+ SetupVertexBuffer(vao);
+
DrawParameters params = SetupDraw();
SetupShaders(params.primitive_mode);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index a53edee6d..21c51f874 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -99,6 +99,23 @@ private:
float max_anisotropic = 1.0f;
};
+ struct FramebufferConfigState {
+ bool using_color_fb{};
+ bool using_depth_fb{};
+ bool preserve_contents{};
+ std::optional<std::size_t> single_color_target;
+
+ bool operator==(const FramebufferConfigState& rhs) const {
+ return std::tie(using_color_fb, using_depth_fb, preserve_contents,
+ single_color_target) == std::tie(rhs.using_color_fb, rhs.using_depth_fb,
+ rhs.preserve_contents,
+ rhs.single_color_target);
+ }
+ bool operator!=(const FramebufferConfigState& rhs) const {
+ return !operator==(rhs);
+ }
+ };
+
/**
* Configures the color and depth framebuffer states.
* @param use_color_fb If true, configure color framebuffers.
@@ -203,6 +220,7 @@ private:
vertex_array_cache;
std::map<FramebufferCacheKey, OGLFramebuffer> framebuffer_cache;
+ FramebufferConfigState current_framebuffer_config_state;
std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers;
@@ -215,8 +233,10 @@ private:
std::size_t CalculateIndexBufferSize() const;
- void SetupVertexFormat();
- void SetupVertexBuffer();
+ /// Updates and returns a vertex array object representing current vertex format
+ GLuint SetupVertexFormat();
+
+ void SetupVertexBuffer(GLuint vao);
DrawParameters SetupDraw();
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index bff0c65cd..a05b8b936 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -919,9 +919,16 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
}
Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) {
- const auto& regs{Core::System::GetInstance().GPU().Maxwell3D().regs};
+ auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()};
+ const auto& regs{gpu.regs};
+
+ if (!gpu.dirty_flags.zeta_buffer) {
+ return last_depth_buffer;
+ }
+ gpu.dirty_flags.zeta_buffer = false;
+
if (!regs.zeta.Address() || !regs.zeta_enable) {
- return {};
+ return last_depth_buffer = {};
}
SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer(
@@ -929,25 +936,31 @@ Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) {
regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
- return GetSurface(depth_params, preserve_contents);
+ return last_depth_buffer = GetSurface(depth_params, preserve_contents);
}
Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool preserve_contents) {
- const auto& regs{Core::System::GetInstance().GPU().Maxwell3D().regs};
+ auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()};
+ const auto& regs{gpu.regs};
+
+ if ((gpu.dirty_flags.color_buffer & (1u << static_cast<u32>(index))) == 0) {
+ return last_color_buffers[index];
+ }
+ gpu.dirty_flags.color_buffer &= ~(1u << static_cast<u32>(index));
ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
if (index >= regs.rt_control.count) {
- return {};
+ return last_color_buffers[index] = {};
}
if (regs.rt[index].Address() == 0 || regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
- return {};
+ return last_color_buffers[index] = {};
}
const SurfaceParams color_params{SurfaceParams::CreateForFramebuffer(index)};
- return GetSurface(color_params, preserve_contents);
+ return last_color_buffers[index] = GetSurface(color_params, preserve_contents);
}
void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 7223700c4..37611c4fc 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -396,6 +396,9 @@ private:
/// Use a Pixel Buffer Object to download the previous texture and then upload it to the new one
/// using the new format.
OGLBuffer copy_pbo;
+
+ std::array<Surface, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> last_color_buffers;
+ Surface last_depth_buffer;
};
} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index c17d5ac00..1da744158 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -117,7 +117,7 @@ void OGLBuffer::Create() {
return;
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
- glGenBuffers(1, &handle);
+ glCreateBuffers(1, &handle);
}
void OGLBuffer::Release() {
@@ -126,7 +126,6 @@ void OGLBuffer::Release() {
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
glDeleteBuffers(1, &handle);
- OpenGLState::GetCurState().ResetBuffer(handle).Apply();
handle = 0;
}
@@ -152,7 +151,7 @@ void OGLVertexArray::Create() {
return;
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
- glGenVertexArrays(1, &handle);
+ glCreateVertexArrays(1, &handle);
}
void OGLVertexArray::Release() {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index dc0a5ed5e..b7ba59350 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -83,8 +83,6 @@ OpenGLState::OpenGLState() {
draw.read_framebuffer = 0;
draw.draw_framebuffer = 0;
draw.vertex_array = 0;
- draw.vertex_buffer = 0;
- draw.uniform_buffer = 0;
draw.shader_program = 0;
draw.program_pipeline = 0;
@@ -505,7 +503,6 @@ void OpenGLState::ApplySamplers() const {
}
void OpenGLState::ApplyFramebufferState() const {
- // Framebuffer
if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
}
@@ -514,16 +511,10 @@ void OpenGLState::ApplyFramebufferState() const {
}
}
-void OpenGLState::ApplyVertexBufferState() const {
- // Vertex array
+void OpenGLState::ApplyVertexArrayState() const {
if (draw.vertex_array != cur_state.draw.vertex_array) {
glBindVertexArray(draw.vertex_array);
}
-
- // Vertex buffer
- if (draw.vertex_buffer != cur_state.draw.vertex_buffer) {
- glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer);
- }
}
void OpenGLState::ApplyDepthClamp() const {
@@ -543,11 +534,7 @@ void OpenGLState::ApplyDepthClamp() const {
void OpenGLState::Apply() const {
ApplyFramebufferState();
- ApplyVertexBufferState();
- // Uniform buffer
- if (draw.uniform_buffer != cur_state.draw.uniform_buffer) {
- glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer);
- }
+ ApplyVertexArrayState();
// Shader program
if (draw.shader_program != cur_state.draw.shader_program) {
@@ -638,16 +625,6 @@ OpenGLState& OpenGLState::ResetPipeline(GLuint handle) {
return *this;
}
-OpenGLState& OpenGLState::ResetBuffer(GLuint handle) {
- if (draw.vertex_buffer == handle) {
- draw.vertex_buffer = 0;
- }
- if (draw.uniform_buffer == handle) {
- draw.uniform_buffer = 0;
- }
- return *this;
-}
-
OpenGLState& OpenGLState::ResetVertexArray(GLuint handle) {
if (draw.vertex_array == handle) {
draw.vertex_array = 0;
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 439bfbc98..a5a7c0920 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -154,8 +154,6 @@ public:
GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING
GLuint draw_framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING
GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
- GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
- GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING
GLuint shader_program; // GL_CURRENT_PROGRAM
GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING
} draw;
@@ -206,10 +204,10 @@ public:
}
/// Apply this state as the current OpenGL state
void Apply() const;
- /// Apply only the state afecting the framebuffer
+ /// Apply only the state affecting the framebuffer
void ApplyFramebufferState() const;
- /// Apply only the state afecting the vertex buffer
- void ApplyVertexBufferState() const;
+ /// Apply only the state affecting the vertex array
+ void ApplyVertexArrayState() const;
/// Set the initial OpenGL state
static void ApplyDefaultState();
/// Resets any references to the given resource
@@ -217,7 +215,6 @@ public:
OpenGLState& ResetSampler(GLuint handle);
OpenGLState& ResetProgram(GLuint handle);
OpenGLState& ResetPipeline(GLuint handle);
- OpenGLState& ResetBuffer(GLuint handle);
OpenGLState& ResetVertexArray(GLuint handle);
OpenGLState& ResetFramebuffer(GLuint handle);
void EmulateViewportWithScissor();
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
index b97b895a4..d0b14b3f6 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
@@ -15,13 +15,12 @@ MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning",
namespace OpenGL {
-OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent)
- : gl_target(target), buffer_size(size) {
+OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent)
+ : buffer_size(size) {
gl_buffer.Create();
- glBindBuffer(gl_target, gl_buffer.handle);
GLsizeiptr allocate_size = size;
- if (target == GL_ARRAY_BUFFER) {
+ if (vertex_data_usage) {
// On AMD GPU there is a strange crash in indexed drawing. The crash happens when the buffer
// read position is near the end and is an out-of-bound access to the vertex buffer. This is
// probably a bug in the driver and is related to the usage of vec3<byte> attributes in the
@@ -35,18 +34,17 @@ OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coh
coherent = prefer_coherent;
const GLbitfield flags =
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0);
- glBufferStorage(gl_target, allocate_size, nullptr, flags);
- mapped_ptr = static_cast<u8*>(glMapBufferRange(
- gl_target, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT)));
+ glNamedBufferStorage(gl_buffer.handle, allocate_size, nullptr, flags);
+ mapped_ptr = static_cast<u8*>(glMapNamedBufferRange(
+ gl_buffer.handle, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT)));
} else {
- glBufferData(gl_target, allocate_size, nullptr, GL_STREAM_DRAW);
+ glNamedBufferData(gl_buffer.handle, allocate_size, nullptr, GL_STREAM_DRAW);
}
}
OGLStreamBuffer::~OGLStreamBuffer() {
if (persistent) {
- glBindBuffer(gl_target, gl_buffer.handle);
- glUnmapBuffer(gl_target);
+ glUnmapNamedBuffer(gl_buffer.handle);
}
gl_buffer.Release();
}
@@ -74,7 +72,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
invalidate = true;
if (persistent) {
- glUnmapBuffer(gl_target);
+ glUnmapNamedBuffer(gl_buffer.handle);
}
}
@@ -84,7 +82,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
(coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) |
(invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT);
mapped_ptr = static_cast<u8*>(
- glMapBufferRange(gl_target, buffer_pos, buffer_size - buffer_pos, flags));
+ glMapNamedBufferRange(gl_buffer.handle, buffer_pos, buffer_size - buffer_pos, flags));
mapped_offset = buffer_pos;
}
@@ -95,11 +93,11 @@ void OGLStreamBuffer::Unmap(GLsizeiptr size) {
ASSERT(size <= mapped_size);
if (!coherent && size > 0) {
- glFlushMappedBufferRange(gl_target, buffer_pos - mapped_offset, size);
+ glFlushMappedNamedBufferRange(gl_buffer.handle, buffer_pos - mapped_offset, size);
}
if (!persistent) {
- glUnmapBuffer(gl_target);
+ glUnmapNamedBuffer(gl_buffer.handle);
}
buffer_pos += size;
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h
index ae7961bd7..3d18ecb4d 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.h
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.h
@@ -13,7 +13,7 @@ namespace OpenGL {
class OGLStreamBuffer : private NonCopyable {
public:
- explicit OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent = false);
+ explicit OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent = false);
~OGLStreamBuffer();
GLuint GetHandle() const;
@@ -33,7 +33,6 @@ public:
private:
OGLBuffer gl_buffer;
- GLenum gl_target;
bool coherent = false;
bool persistent = false;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 235732d86..e37b65b38 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -14,6 +14,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
+#include "core/frontend/scope_acquire_window_context.h"
#include "core/memory.h"
#include "core/perf_stats.h"
#include "core/settings.h"
@@ -97,18 +98,6 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons
return matrix;
}
-ScopeAcquireGLContext::ScopeAcquireGLContext(Core::Frontend::EmuWindow& emu_window_)
- : emu_window{emu_window_} {
- if (Settings::values.use_multi_core) {
- emu_window.MakeCurrent();
- }
-}
-ScopeAcquireGLContext::~ScopeAcquireGLContext() {
- if (Settings::values.use_multi_core) {
- emu_window.DoneCurrent();
- }
-}
-
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window)
: VideoCore::RendererBase{window} {}
@@ -117,7 +106,6 @@ RendererOpenGL::~RendererOpenGL() = default;
/// Swap buffers (render frame)
void RendererOpenGL::SwapBuffers(
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
- ScopeAcquireGLContext acquire_context{render_window};
Core::System::GetInstance().GetPerfStats().EndSystemFrame();
@@ -245,20 +233,20 @@ void RendererOpenGL::InitOpenGLObjects() {
// Generate VAO
vertex_array.Create();
-
state.draw.vertex_array = vertex_array.handle;
- state.draw.vertex_buffer = vertex_buffer.handle;
- state.draw.uniform_buffer = 0;
- state.Apply();
// Attach vertex data to VAO
- glBufferData(GL_ARRAY_BUFFER, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
- glVertexAttribPointer(attrib_position, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex),
- (GLvoid*)offsetof(ScreenRectVertex, position));
- glVertexAttribPointer(attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex),
- (GLvoid*)offsetof(ScreenRectVertex, tex_coord));
- glEnableVertexAttribArray(attrib_position);
- glEnableVertexAttribArray(attrib_tex_coord);
+ glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
+ glVertexArrayAttribFormat(vertex_array.handle, attrib_position, 2, GL_FLOAT, GL_FALSE,
+ offsetof(ScreenRectVertex, position));
+ glVertexArrayAttribFormat(vertex_array.handle, attrib_tex_coord, 2, GL_FLOAT, GL_FALSE,
+ offsetof(ScreenRectVertex, tex_coord));
+ glVertexArrayAttribBinding(vertex_array.handle, attrib_position, 0);
+ glVertexArrayAttribBinding(vertex_array.handle, attrib_tex_coord, 0);
+ glEnableVertexArrayAttrib(vertex_array.handle, attrib_position);
+ glEnableVertexArrayAttrib(vertex_array.handle, attrib_tex_coord);
+ glVertexArrayVertexBuffer(vertex_array.handle, 0, vertex_buffer.handle, 0,
+ sizeof(ScreenRectVertex));
// Allocate textures for the screen
screen_info.texture.resource.Create();
@@ -370,14 +358,12 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
state.texture_units[0].texture = screen_info.display_texture;
state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
// Workaround brigthness problems in SMO by enabling sRGB in the final output
- // if it has been used in the frame
- // Needed because of this bug in QT
- // QTBUG-50987
+ // if it has been used in the frame. Needed because of this bug in QT: QTBUG-50987
state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed();
state.Apply();
- glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
+ glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- // restore default state
+ // Restore default state
state.framebuffer_srgb.enabled = false;
state.texture_units[0].texture = 0;
state.Apply();
@@ -508,7 +494,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
/// Initialize the renderer
bool RendererOpenGL::Init() {
- ScopeAcquireGLContext acquire_context{render_window};
+ Core::Frontend::ScopeAcquireWindowContext acquire_context{render_window};
if (GLAD_GL_KHR_debug) {
glEnable(GL_DEBUG_OUTPUT);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index b85cc262f..1665018db 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -39,16 +39,6 @@ struct ScreenInfo {
TextureInfo texture;
};
-/// Helper class to acquire/release OpenGL context within a given scope
-class ScopeAcquireGLContext : NonCopyable {
-public:
- explicit ScopeAcquireGLContext(Core::Frontend::EmuWindow& window);
- ~ScopeAcquireGLContext();
-
-private:
- Core::Frontend::EmuWindow& emu_window;
-};
-
class RendererOpenGL : public VideoCore::RendererBase {
public:
explicit RendererOpenGL(Core::Frontend::EmuWindow& window);
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 1f852df4b..4cab599b4 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -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
+ compatdb.ui
hotkeys.ui
+ loading_screen.ui
main.ui
- compatdb.ui
)
file(GLOB COMPAT_LIST
diff --git a/src/yuzu/applets/web_browser.cpp b/src/yuzu/applets/web_browser.cpp
index c59b7ade1..6a9138d53 100644
--- a/src/yuzu/applets/web_browser.cpp
+++ b/src/yuzu/applets/web_browser.cpp
@@ -86,9 +86,9 @@ QtWebBrowser::QtWebBrowser(GMainWindow& main_window) {
QtWebBrowser::~QtWebBrowser() = default;
void QtWebBrowser::OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback,
- std::function<void()> finished_callback) const {
- this->unpack_romfs_callback = unpack_romfs_callback;
- this->finished_callback = finished_callback;
+ std::function<void()> finished_callback) {
+ this->unpack_romfs_callback = std::move(unpack_romfs_callback);
+ this->finished_callback = std::move(finished_callback);
const auto index = url.find('?');
if (index == std::string::npos) {
diff --git a/src/yuzu/applets/web_browser.h b/src/yuzu/applets/web_browser.h
index bba273767..1a3d67353 100644
--- a/src/yuzu/applets/web_browser.h
+++ b/src/yuzu/applets/web_browser.h
@@ -38,16 +38,15 @@ public:
~QtWebBrowser() override;
void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback,
- std::function<void()> finished_callback) const override;
+ std::function<void()> finished_callback) override;
signals:
void MainWindowOpenPage(std::string_view filename, std::string_view additional_args) const;
-public slots:
+private:
void MainWindowUnpackRomFS();
void MainWindowFinishedBrowsing();
-private:
- mutable std::function<void()> unpack_romfs_callback;
- mutable std::function<void()> finished_callback;
+ std::function<void()> unpack_romfs_callback;
+ std::function<void()> finished_callback;
};
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 40db7a5e9..f74cb693a 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -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;
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 4e3028215..d1f37e503 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -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;
};
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index aa7de7b54..550cf9dca 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -7,7 +7,6 @@
#include "common/file_util.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
-#include "common/logging/log.h"
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_debug.h"
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index d802443d0..777050405 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -39,6 +39,7 @@ void ConfigureDialog::applyConfiguration() {
ui->debugTab->applyConfiguration();
ui->webTab->applyConfiguration();
Settings::Apply();
+ Settings::LogSettings();
}
void ConfigureDialog::PopulateSelectionList() {
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index ba2b32c4f..c5a245ebe 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -7,6 +7,7 @@
#include <utility>
#include <QColorDialog>
#include <QGridLayout>
+#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QTimer>
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 7a53f6715..ade8d4435 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -11,17 +11,21 @@
#include <string>
#include <QDialog>
-#include <QKeyEvent>
#include "common/param_package.h"
#include "core/settings.h"
-#include "input_common/main.h"
#include "ui_configure_input.h"
+class QKeyEvent;
class QPushButton;
class QString;
class QTimer;
+namespace InputCommon::Polling {
+class DevicePoller;
+enum class DeviceType;
+} // namespace InputCommon::Polling
+
namespace Ui {
class ConfigureInputPlayer;
}
diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_general.cpp
index e13d2eac8..022b94609 100644
--- a/src/yuzu/configuration/configure_per_general.cpp
+++ b/src/yuzu/configuration/configure_per_general.cpp
@@ -8,7 +8,6 @@
#include <QHeaderView>
#include <QMenu>
-#include <QMessageBox>
#include <QStandardItemModel>
#include <QString>
#include <QTimer>
diff --git a/src/yuzu/configuration/configure_per_general.h b/src/yuzu/configuration/configure_per_general.h
index a4494446c..f8a7d5326 100644
--- a/src/yuzu/configuration/configure_per_general.h
+++ b/src/yuzu/configuration/configure_per_general.h
@@ -7,16 +7,16 @@
#include <memory>
#include <vector>
-#include <QKeyEvent>
+#include <QDialog>
#include <QList>
-#include <QWidget>
#include "core/file_sys/vfs_types.h"
-class QTreeView;
class QGraphicsScene;
class QStandardItem;
class QStandardItemModel;
+class QTreeView;
+class QVBoxLayout;
namespace Ui {
class ConfigurePerGameGeneral;
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 94e27349d..10645a2b3 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -2,23 +2,19 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
+#include <array>
+#include <chrono>
+#include <optional>
+
#include <QFileDialog>
#include <QGraphicsItem>
-#include <QGraphicsScene>
-#include <QHeaderView>
#include <QMessageBox>
-#include <QStandardItemModel>
-#include <QTreeView>
-#include <QVBoxLayout>
#include "common/assert.h"
#include "common/file_util.h"
-#include "common/string_util.h"
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_system.h"
#include "yuzu/configuration/configure_system.h"
-#include "yuzu/util/limitable_input_dialog.h"
namespace {
constexpr std::array<int, 12> days_in_month = {{
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.h b/src/yuzu/configuration/configure_touchscreen_advanced.h
index 41cd255fb..3d0772c87 100644
--- a/src/yuzu/configuration/configure_touchscreen_advanced.h
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.h
@@ -6,8 +6,6 @@
#include <memory>
#include <QDialog>
-#include <QWidget>
-#include "yuzu/configuration/config.h"
namespace Ui {
class ConfigureTouchscreenAdvanced;
diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp
index 3c2ccb76f..18566d028 100644
--- a/src/yuzu/configuration/configure_web.cpp
+++ b/src/yuzu/configuration/configure_web.cpp
@@ -89,12 +89,11 @@ void ConfigureWeb::OnLoginChanged() {
void ConfigureWeb::VerifyLogin() {
ui->button_verify_login->setDisabled(true);
- ui->button_verify_login->setText(tr("Verifying"));
- verify_watcher.setFuture(
- QtConcurrent::run([this, username = ui->edit_username->text().toStdString(),
- token = ui->edit_token->text().toStdString()]() {
- return Core::VerifyLogin(username, token);
- }));
+ ui->button_verify_login->setText(tr("Verifying..."));
+ verify_watcher.setFuture(QtConcurrent::run([username = ui->edit_username->text().toStdString(),
+ token = ui->edit_token->text().toStdString()] {
+ return Core::VerifyLogin(username, token);
+ }));
}
void ConfigureWeb::OnLoginVerified() {
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp
new file mode 100644
index 000000000..907aac4f1
--- /dev/null
+++ b/src/yuzu/loading_screen.cpp
@@ -0,0 +1,213 @@
+// Copyright 2019 yuzu 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 "common/logging/log.h"
+#include "core/loader/loader.h"
+#include "ui_loading_screen.h"
+#include "video_core/rasterizer_interface.h"
+#include "yuzu/loading_screen.h"
+
+// Mingw seems to not have QMovie at all. If QMovie is missing then use a single frame instead of an
+// showing the full animation
+#if !YUZU_QT_MOVIE_MISSING
+#include <QMovie>
+#endif
+
+constexpr const char PROGRESSBAR_STYLE_PREPARE[] = R"(
+QProgressBar {}
+QProgressBar::chunk {})";
+
+constexpr const char PROGRESSBAR_STYLE_DECOMPILE[] = R"(
+QProgressBar {
+ background-color: black;
+ border: 2px solid white;
+ border-radius: 4px;
+ padding: 2px;
+}
+QProgressBar::chunk {
+ background-color: #0ab9e6;
+})";
+
+constexpr const char PROGRESSBAR_STYLE_BUILD[] = R"(
+QProgressBar {
+ background-color: black;
+ border: 2px solid white;
+ border-radius: 4px;
+ padding: 2px;
+}
+QProgressBar::chunk {
+ background-color: #ff3c28;
+})";
+
+constexpr const char PROGRESSBAR_STYLE_COMPLETE[] = R"(
+QProgressBar {
+ background-color: #0ab9e6;
+ border: 2px solid white;
+ border-radius: 4px;
+ padding: 2px;
+}
+QProgressBar::chunk {
+ background-color: #ff3c28;
+})";
+
+LoadingScreen::LoadingScreen(QWidget* parent)
+ : QWidget(parent), ui(std::make_unique<Ui::LoadingScreen>()),
+ previous_stage(VideoCore::LoadCallbackStage::Complete) {
+ ui->setupUi(this);
+ setMinimumSize(1280, 720);
+
+ // 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>();
+
+ stage_translations = {
+ {VideoCore::LoadCallbackStage::Prepare, tr("Loading...")},
+ {VideoCore::LoadCallbackStage::Decompile, tr("Preparing Shaders %1 / %2")},
+ {VideoCore::LoadCallbackStage::Build, tr("Loading Shaders %1 / %2")},
+ {VideoCore::LoadCallbackStage::Complete, tr("Launching...")},
+ };
+ 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},
+ };
+}
+
+LoadingScreen::~LoadingScreen() = default;
+
+void LoadingScreen::Prepare(Loader::AppLoader& loader) {
+ std::vector<u8> buffer;
+ if (loader.ReadBanner(buffer) == Loader::ResultStatus::Success) {
+#ifdef YUZU_QT_MOVIE_MISSING
+ QPixmap map;
+ map.loadFromData(buffer.data(), buffer.size());
+ ui->banner->setPixmap(map);
+#else
+ backing_mem = std::make_unique<QByteArray>(reinterpret_cast<char*>(buffer.data()),
+ static_cast<int>(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());
+ animation->start();
+ ui->banner->setMovie(animation.get());
+#endif
+ buffer.clear();
+ }
+ if (loader.ReadLogo(buffer) == Loader::ResultStatus::Success) {
+ QPixmap map;
+ map.loadFromData(buffer.data(), static_cast<uint>(buffer.size()));
+ ui->logo->setPixmap(map);
+ }
+
+ slow_shader_compile_start = 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;
+ auto now = high_resolution_clock::now();
+ // reset the timer if the stage changes
+ if (stage != previous_stage) {
+ ui->progress_bar->setStyleSheet(progressbar_style[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;
+ // reset back to fast shader compiling since the stage changed
+ slow_shader_compile_start = false;
+ }
+ // 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;
+ }
+
+ QString estimate;
+ // If theres a drastic slowdown in the rate, then display an estimate
+ if (now - previous_time > milliseconds{50} || slow_shader_compile_start) {
+ if (!slow_shader_compile_start) {
+ slow_shader_start = high_resolution_clock::now();
+ slow_shader_compile_start = true;
+ slow_shader_first_value = value;
+ }
+ // only calculate an estimate time after a second has passed since stage change
+ auto diff = duration_cast<milliseconds>(now - slow_shader_start);
+ if (diff > seconds{1}) {
+ auto eta_mseconds =
+ static_cast<long>(static_cast<double>(total - slow_shader_first_value) /
+ (value - slow_shader_first_value) * diff.count());
+ estimate =
+ tr("Estimated Time %1")
+ .arg(QTime(0, 0, 0, 0)
+ .addMSecs(std::max<long>(eta_mseconds - diff.count() + 1000, 1000))
+ .toString("mm:ss"));
+ }
+ }
+
+ // update labels and progress bar
+ ui->stage->setText(stage_translations[stage].arg(value).arg(total));
+ 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() {
+#ifndef YUZU_QT_MOVIE_MISSING
+ animation.reset();
+ backing_buf.reset();
+ backing_mem.reset();
+#endif
+}
diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h
new file mode 100644
index 000000000..801d08e1a
--- /dev/null
+++ b/src/yuzu/loading_screen.h
@@ -0,0 +1,92 @@
+// Copyright 2019 yuzu 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>
+
+#if !QT_CONFIG(movie)
+#define YUZU_QT_MOVIE_MISSING 1
+#endif
+
+namespace Loader {
+class AppLoader;
+}
+
+namespace Ui {
+class LoadingScreen;
+}
+
+namespace VideoCore {
+enum class LoadCallbackStage;
+}
+
+class QBuffer;
+class QByteArray;
+class QGraphicsOpacityEffect;
+class QMovie;
+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:
+#ifndef YUZU_QT_MOVIE_MISSING
+ std::unique_ptr<QMovie> animation;
+ std::unique_ptr<QBuffer> backing_buf;
+ std::unique_ptr<QByteArray> backing_mem;
+#endif
+ 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;
+
+ // Definitions for the differences in text and styling for each stage
+ std::unordered_map<VideoCore::LoadCallbackStage, const char*> progressbar_style;
+ std::unordered_map<VideoCore::LoadCallbackStage, QString> stage_translations;
+
+ // newly generated shaders are added to the end of the file, so when loading and compiling
+ // shaders, it will start quickly but end slow if new shaders were added since previous launch.
+ // These variables are used to detect the change in speed so we can generate an ETA
+ bool slow_shader_compile_start = false;
+ std::chrono::high_resolution_clock::time_point slow_shader_start;
+ std::chrono::high_resolution_clock::time_point previous_time;
+ std::size_t slow_shader_first_value = 0;
+};
+
+Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage);
diff --git a/src/yuzu/loading_screen.ui b/src/yuzu/loading_screen.ui
new file mode 100644
index 000000000..a67d273fd
--- /dev/null
+++ b/src/yuzu/loading_screen.ui
@@ -0,0 +1,161 @@
+<?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 alignment="Qt::AlignLeft|Qt::AlignTop">
+ <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="QVBoxLayout" name="verticalLayout" stretch="1,0,1">
+ <property name="spacing">
+ <number>15</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetNoConstraint</enum>
+ </property>
+ <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 &quot;Arial&quot;;</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 &quot;Arial&quot;;</string>
+ </property>
+ <property name="text">
+ <string>Stage 1 of 2. Estimate Time 5m 4s</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>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f564de994..ab403b3ac 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -14,6 +14,7 @@
#include "configuration/configure_per_general.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
+#include "core/frontend/scope_acquire_window_context.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/hid/controllers/npad.h"
@@ -92,6 +93,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 +413,17 @@ 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);
+ connect(loading_screen, &LoadingScreen::Hidden, [&] {
+ loading_screen->Clear();
+ if (emulation_running) {
+ render_window->show();
+ render_window->setFocus();
+ }
+ });
+
// Create status bar
message_label = new QLabel();
// Configured separately for left alignment
@@ -735,13 +748,15 @@ bool GMainWindow::LoadROM(const QString& filename) {
ShutdownGame();
render_window->InitRenderTarget();
- render_window->MakeCurrent();
- if (!gladLoadGL()) {
- QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"),
- tr("Your GPU may not support OpenGL 4.3, or you do not "
- "have the latest graphics driver."));
- return false;
+ {
+ Core::Frontend::ScopeAcquireWindowContext acquire_context{*render_window};
+ if (!gladLoadGL()) {
+ QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"),
+ tr("Your GPU may not support OpenGL 4.3, or you do not "
+ "have the latest graphics driver."));
+ return false;
+ }
}
QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions();
@@ -782,8 +797,6 @@ bool GMainWindow::LoadROM(const QString& filename) {
"wiki</a>. This message will not be shown again."));
}
- render_window->DoneCurrent();
-
if (result != Core::System::ResultStatus::Success) {
switch (result) {
case Core::System::ResultStatus::ErrorGetLoader:
@@ -897,8 +910,8 @@ 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();
emulation_running = true;
if (ui.action_Fullscreen->isChecked()) {
@@ -932,6 +945,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 +1520,10 @@ void GMainWindow::OnStopGame() {
ShutdownGame();
}
+void GMainWindow::OnLoadComplete() {
+ loading_screen->OnLoadComplete();
+}
+
void GMainWindow::OnMenuReportCompatibility() {
if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) {
CompatDB compatdb{this};
@@ -1771,9 +1790,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 +1836,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."));
}
@@ -2025,6 +2043,9 @@ int main(int argc, char* argv[]) {
GMainWindow main_window;
// After settings have been loaded by GMainWindow, apply the filter
main_window.show();
+
+ Settings::LogSettings();
+
int result = app.exec();
detached_tasks.WaitForAllTasks();
return result;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 2d705ad54..e07c892cf 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -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;
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index a557f2884..7df8eff53 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -195,6 +195,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
SDL_GL_SetSwapInterval(false);
LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
Common::g_scm_desc);
+ Settings::LogSettings();
DoneCurrent();
}