aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorcrueter <crueter@eden-emu.dev>2026-05-20 04:49:16 +0200
committercrueter <crueter@eden-emu.dev>2026-05-20 04:49:16 +0200
commitfeb8c5f88eae046c1b76f46e4fa103a844fb0f23 (patch)
tree70f71937a8930c1a5b562f69047e35491d6156b4 /src
parent300a646a34b6729879e3fb71d53e625a09683bb2 (diff)
[desktop] More qt_common reorganization (#3916)
Ported from QML branch. Main "big" change is that EmuThread is now a shared state in QtCommon, not individually managed/passed around by GRenderWindow and MainWindow. Signed-off-by: crueter <crueter@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3916 Reviewed-by: Lizzie <lizzie@eden-emu.dev> Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Diffstat (limited to 'src')
-rw-r--r--src/common/settings.cpp12
-rw-r--r--src/common/settings.h2
-rw-r--r--src/core/file_sys/vfs/vfs.h9
-rw-r--r--src/dedicated_room/CMakeLists.txt3
-rw-r--r--src/qt_common/CMakeLists.txt11
-rw-r--r--src/qt_common/config/shared_translation.cpp5
-rw-r--r--src/qt_common/qt_common.cpp216
-rw-r--r--src/qt_common/qt_common.h19
-rw-r--r--src/qt_common/render/context.h107
-rw-r--r--src/qt_common/render/emu_thread.cpp83
-rw-r--r--src/qt_common/render/emu_thread.h97
-rw-r--r--src/qt_common/util/content.cpp92
-rw-r--r--src/qt_common/util/content.h8
-rw-r--r--src/qt_common/util/game.cpp109
-rw-r--r--src/qt_common/util/game.h8
-rw-r--r--src/qt_common/util/vk.cpp (renamed from src/yuzu/vk_device_info.cpp)2
-rw-r--r--src/qt_common/util/vk.h71
-rw-r--r--src/yuzu/CMakeLists.txt6
-rw-r--r--src/yuzu/bootmanager.cpp213
-rw-r--r--src/yuzu/bootmanager.h100
-rw-r--r--src/yuzu/configuration/addon/mod_select_dialog.cpp4
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.cpp6
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp2
-rw-r--r--src/yuzu/configuration/configure_dialog.h2
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp46
-rw-r--r--src/yuzu/configuration/configure_graphics.h4
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp2
-rw-r--r--src/yuzu/configuration/configure_per_game.h2
-rw-r--r--src/yuzu/data_dialog.cpp19
-rw-r--r--src/yuzu/debugger/console.cpp1
-rw-r--r--src/yuzu/game/game_card.cpp3
-rw-r--r--src/yuzu/game/game_list.cpp4
-rw-r--r--src/yuzu/game/game_list.h2
-rw-r--r--src/yuzu/game/game_list_worker.cpp7
-rw-r--r--src/yuzu/main_window.cpp396
-rw-r--r--src/yuzu/main_window.h17
-rw-r--r--src/yuzu/updater/update_dialog.h4
-rw-r--r--src/yuzu/util/util.cpp105
-rw-r--r--src/yuzu/util/util.h11
-rw-r--r--src/yuzu/vk_device_info.h36
41 files changed, 944 insertions, 905 deletions
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 54848c4dd1..61f9acb1e7 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -435,4 +435,16 @@ void ToggleSlowMode() {
SetSpeedMode(SpeedMode::Standard);
}
+bool IsOpenGL() {
+ const auto backend = Settings::values.renderer_backend.GetValue();
+ switch (backend) {
+ case RendererBackend::OpenGL_GLSL:
+ case RendererBackend::OpenGL_GLASM:
+ case RendererBackend::OpenGL_SPIRV:
+ return true;
+ default:
+ return false;
+ }
+}
+
} // namespace Settings
diff --git a/src/common/settings.h b/src/common/settings.h
index e10f5105a1..e8877fce1e 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -863,6 +863,8 @@ bool IsFastmemEnabled();
void SetNceEnabled(bool is_64bit);
bool IsNceEnabled();
+bool IsOpenGL();
+
bool IsDockedMode();
float Volume();
diff --git a/src/core/file_sys/vfs/vfs.h b/src/core/file_sys/vfs/vfs.h
index f846a9669c..c3d73364bc 100644
--- a/src/core/file_sys/vfs/vfs.h
+++ b/src/core/file_sys/vfs/vfs.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -16,6 +19,12 @@
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/vfs/vfs_types.h"
+#undef CreateFile
+#undef CopyFile
+#undef MoveFile
+#undef DeleteFile
+#undef CreateDirectory
+
namespace FileSys {
// An enumeration representing what can be at the end of a path in a VfsFilesystem
diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt
index 36c8af5e34..8ca138473f 100644
--- a/src/dedicated_room/CMakeLists.txt
+++ b/src/dedicated_room/CMakeLists.txt
@@ -7,8 +7,7 @@
add_library(yuzu-room STATIC EXCLUDE_FROM_ALL
yuzu_room.cpp
yuzu_room.h
- yuzu_room.rc
-)
+ yuzu_room.rc)
target_link_libraries(yuzu-room PRIVATE common network)
if (ENABLE_WEB_SERVICE)
diff --git a/src/qt_common/CMakeLists.txt b/src/qt_common/CMakeLists.txt
index 399fbe67a0..a8c4e51240 100644
--- a/src/qt_common/CMakeLists.txt
+++ b/src/qt_common/CMakeLists.txt
@@ -26,6 +26,7 @@ add_library(qt_common STATIC
util/compress.h util/compress.cpp
util/fs.h util/fs.cpp
util/mod.h util/mod.cpp
+ util/vk.h util/vk.cpp
abstract/frontend.h abstract/frontend.cpp
abstract/progress.h abstract/progress.cpp
@@ -33,7 +34,9 @@ add_library(qt_common STATIC
qt_string_lookup.h
qt_compat.h
- discord/discord.h)
+ discord/discord.h
+ render/context.h
+ render/emu_thread.h render/emu_thread.cpp)
if (UNIX)
target_sources(qt_common PRIVATE gui_settings.cpp gui_settings.h)
@@ -56,9 +59,7 @@ if (USE_DISCORD_PRESENCE)
endif()
# TODO(crueter)
-if (ENABLE_QT)
- target_link_libraries(qt_common PRIVATE Qt6::Widgets)
-endif()
+target_link_libraries(qt_common PRIVATE Qt6::Widgets)
target_compile_definitions(qt_common PUBLIC
# Use QStringBuilder for string concatenation to reduce
@@ -80,7 +81,7 @@ target_compile_definitions(qt_common PUBLIC
)
target_link_libraries(qt_common PRIVATE core Qt6::Core Qt6::Concurrent SimpleIni::SimpleIni QuaZip::QuaZip)
-target_link_libraries(qt_common PUBLIC frozen::frozen-headers)
+target_link_libraries(qt_common PUBLIC frozen::frozen-headers Vulkan::Headers)
target_link_libraries(qt_common PRIVATE gamemode::headers frontend_common)
if (ENABLE_OPENGL)
diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp
index 506d74084f..6b8f8e73b6 100644
--- a/src/qt_common/config/shared_translation.cpp
+++ b/src/qt_common/config/shared_translation.cpp
@@ -165,8 +165,9 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
INSERT(Settings, use_disk_shader_cache, tr("Use persistent pipeline cache"),
tr("Allows saving shaders to storage for faster loading on following game "
"boots.\nDisabling it is only intended for debugging."));
- INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
- tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled."));
+ INSERT(
+ Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
+ tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled."));
INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"),
tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for "
"decoding, or perform no decoding at all (black screen on videos).\n"
diff --git a/src/qt_common/qt_common.cpp b/src/qt_common/qt_common.cpp
index 3091171df8..0a134d5047 100644
--- a/src/qt_common/qt_common.cpp
+++ b/src/qt_common/qt_common.cpp
@@ -1,25 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
-#include "common/fs/fs.h"
+#include "common/literals.h"
+
+#include "common/memory_detect.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "hid_core/hid_core.h"
+#include "network/network.h"
#include "qt_common.h"
+#include "common/fs/fs.h"
+#include "common/fs/path_util.h"
+#include "common/logging.h"
+#include "common/scm_rev.h"
+#include "core/memory.h"
+
+#ifdef ARCHITECTURE_x86_64
+#include "common/x64/cpu_detect.h"
+#endif
+
#include <QGuiApplication>
#include <QStringLiteral>
-#include "common/logging.h"
#include "core/frontend/emu_window.h"
+#include "qt_common/util/meta.h"
#include <QFile>
+#include <QMessageBox>
+
+#include <thread>
#include <JlCompress.h>
#if !defined(WIN32) && !defined(__APPLE__)
#include <qpa/qplatformnativeinterface.h>
#elif defined(__APPLE__)
#include <objc/message.h>
-#include <objc/runtime.h>
#endif
+#ifdef _WIN32
+#include <QApplication>
+#include <QSettings>
+#include <windows.h>
+#include "common/windows/timer_resolution.h"
+#include "core/core_timing.h"
+#endif
+
+using namespace Common::Literals;
+
namespace QtCommon {
QWidget* rootObject = nullptr;
@@ -27,6 +54,11 @@ QWidget* rootObject = nullptr;
std::unique_ptr<Core::System> system = nullptr;
std::shared_ptr<FileSys::RealVfsFilesystem> vfs = nullptr;
std::unique_ptr<FileSys::ManualContentProvider> provider = nullptr;
+std::unique_ptr<EmuThread> emu_thread = nullptr;
+
+const QStringList supported_file_extensions = {QStringLiteral("nro"), QStringLiteral("nso"),
+ QStringLiteral("nca"), QStringLiteral("xci"),
+ QStringLiteral("nsp"), QStringLiteral("kip")};
Core::Frontend::WindowSystemType GetWindowSystemType() {
// Determine WSI type based on Qt platform.
@@ -58,37 +90,8 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
// Our Win32 Qt external doesn't have the private API.
wsi.render_surface = reinterpret_cast<void*>(window->winId());
#elif defined(__APPLE__)
- id layer = reinterpret_cast<id (*)(id, SEL)>(objc_msgSend)(
+ wsi.render_surface = reinterpret_cast<void* (*)(id, SEL)>(objc_msgSend)(
reinterpret_cast<id>(window->winId()), sel_registerName("layer"));
-
- // In Qt 6, the layer of the NSView might be a QContainerLayer.
- // MoltenVK needs a CAMetalLayer. We search for it in sublayers.
- Class metal_layer_class = objc_getClass("CAMetalLayer");
- id metal_layer = nullptr;
-
- if (layer) {
- if (reinterpret_cast<bool (*)(id, SEL, Class)>(objc_msgSend)(
- layer, sel_registerName("isKindOfClass:"), metal_layer_class)) {
- metal_layer = layer;
- } else {
- id sublayers = reinterpret_cast<id (*)(id, SEL)>(objc_msgSend)(
- layer, sel_registerName("sublayers"));
- if (sublayers) {
- unsigned long count = reinterpret_cast<unsigned long (*)(id, SEL)>(objc_msgSend)(
- sublayers, sel_registerName("count"));
- for (unsigned long i = 0; i < count; ++i) {
- id sublayer = reinterpret_cast<id (*)(id, SEL, unsigned long)>(objc_msgSend)(
- sublayers, sel_registerName("objectAtIndex:"), i);
- if (reinterpret_cast<bool (*)(id, SEL, Class)>(objc_msgSend)(
- sublayer, sel_registerName("isKindOfClass:"), metal_layer_class)) {
- metal_layer = sublayer;
- break;
- }
- }
- }
- }
- }
- wsi.render_surface = reinterpret_cast<void*>(metal_layer ? metal_layer : layer);
#else
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
wsi.display_connection = pni->nativeResourceForWindow("display", window);
@@ -103,18 +106,148 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
}
const QString tr(const char* str) {
- return QGuiApplication::tr(str);
+ return rootObject->tr(str);
}
const QString tr(const std::string& str) {
- return QGuiApplication::tr(str.c_str());
+ return rootObject->tr(str.c_str());
+}
+
+static void LogRuntimes() {
+#ifdef _MSC_VER
+ // It is possible that the name of the dll will change.
+ // vcruntime140.dll is for 2015 and onwards
+ static constexpr char runtime_dll_name[] = "vcruntime140.dll";
+ UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr);
+ bool runtime_version_inspection_worked = false;
+ if (sz > 0) {
+ std::vector<u8> buf(sz);
+ if (GetFileVersionInfoA(runtime_dll_name, 0, sz, buf.data())) {
+ VS_FIXEDFILEINFO* pvi;
+ sz = sizeof(VS_FIXEDFILEINFO);
+ if (VerQueryValueA(buf.data(), "\\", reinterpret_cast<LPVOID*>(&pvi), &sz)) {
+ if (pvi->dwSignature == VS_FFI_SIGNATURE) {
+ runtime_version_inspection_worked = true;
+ LOG_INFO(Frontend, "MSVC Compiler: {} Runtime: {}.{}.{}.{}", _MSC_VER,
+ pvi->dwProductVersionMS >> 16, pvi->dwProductVersionMS & 0xFFFF,
+ pvi->dwProductVersionLS >> 16, pvi->dwProductVersionLS & 0xFFFF);
+ }
+ }
+ }
+ }
+ if (!runtime_version_inspection_worked) {
+ LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name);
+ }
+#endif
+ LOG_INFO(Frontend, "Qt Compile: {} Runtime: {}", QT_VERSION_STR, qVersion());
+}
+
+static QString PrettyProductName() {
+#ifdef _WIN32
+ // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
+ // With that notation change they changed the registry key used to denote the current version
+ QSettings windows_registry(
+ QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
+ QSettings::NativeFormat);
+ const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
+ if (release_id == QStringLiteral("2009")) {
+ const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
+ const QString display_version =
+ windows_registry.value(QStringLiteral("DisplayVersion")).toString();
+ const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
+ u32 version = 10;
+ if (current_build >= 22000) {
+ version = 11;
+ }
+ return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
+ .arg(QString::number(version), display_version, QString::number(current_build),
+ QString::number(ubr));
+ }
+#endif
+ return QSysInfo::prettyProductName();
+}
+
+static void RemoveCachedContents() {
+ const auto cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir);
+ const auto offline_fonts = cache_dir / "fonts";
+ const auto offline_manual = cache_dir / "offline_web_applet_manual";
+ const auto offline_legal_information = cache_dir / "offline_web_applet_legal_information";
+ const auto offline_system_data = cache_dir / "offline_web_applet_system_data";
+
+ Common::FS::RemoveDirRecursively(offline_fonts);
+ Common::FS::RemoveDirRecursively(offline_manual);
+ Common::FS::RemoveDirRecursively(offline_legal_information);
+ Common::FS::RemoveDirRecursively(offline_system_data);
}
void Init(QWidget* root) {
- system = std::make_unique<Core::System>();
rootObject = root;
+
+ system = std::make_unique<Core::System>();
vfs = std::make_unique<FileSys::RealVfsFilesystem>();
provider = std::make_unique<FileSys::ManualContentProvider>();
+
+ // initialization stuff
+ Common::FS::CreateEdenPaths();
+
+ system->Initialize();
+
+ Common::Log::Initialize();
+ Common::Log::Start();
+
+ Network::Init();
+
+ QtCommon::Meta::RegisterMetaTypes();
+
+ // build version
+ const auto branch_name = std::string(Common::g_scm_branch);
+ const auto description = std::string(Common::g_scm_desc);
+ const auto build_id = std::string(Common::g_build_id);
+
+ const auto yuzu_build = fmt::format("Eden Development Build | {}-{}", branch_name, description);
+ const auto override_build =
+ fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
+ const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
+ const auto processor_count = std::thread::hardware_concurrency();
+
+ // info logging
+ LOG_INFO(Frontend, "Eden Version: {}", yuzu_build_version);
+ LogRuntimes();
+#ifdef ARCHITECTURE_x86_64
+ const auto& caps = Common::GetCPUCaps();
+ std::string cpu_string = caps.cpu_string;
+ if (caps.avx || caps.avx2 || caps.avx512f) {
+ cpu_string += " | AVX";
+ if (caps.avx512f) {
+ cpu_string += "512";
+ } else if (caps.avx2) {
+ cpu_string += '2';
+ }
+ if (caps.fma || caps.fma4) {
+ cpu_string += " | FMA";
+ }
+ }
+ LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
+ if (std::optional<int> processor_core = Common::GetProcessorCount()) {
+ LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core);
+ }
+#endif
+ LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);
+ LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
+ LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
+ Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
+ LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
+#ifdef _WIN32
+ LOG_INFO(Frontend, "Host Timer Resolution: {:.4f} ms",
+ std::chrono::duration_cast<std::chrono::duration<f64, std::milli>>(
+ Common::Windows::SetCurrentTimerResolutionToMaximum())
+ .count());
+ QtCommon::system->CoreTiming().SetTimerResolutionNs(
+ Common::Windows::GetCurrentTimerResolution());
+#endif
+
+ // Remove cached contents generated during the previous session
+ RemoveCachedContents();
}
std::filesystem::path GetEdenCommand() {
@@ -137,4 +270,15 @@ std::filesystem::path GetEdenCommand() {
return command;
}
+void SetupContentProviders() {
+ system->SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
+ system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
+ provider.get());
+ system->GetFileSystemController().CreateFactories(*vfs);
+}
+
+void SetupHID() {
+ system->HIDCore().ReloadInputDevices();
+}
+
} // namespace QtCommon
diff --git a/src/qt_common/qt_common.h b/src/qt_common/qt_common.h
index 19c09ef5d0..dd860ef5f7 100644
--- a/src/qt_common/qt_common.h
+++ b/src/qt_common/qt_common.h
@@ -6,19 +6,31 @@
#include <memory>
#include <QWindow>
-#include <core/frontend/emu_window.h>
+
#include "core/core.h"
#include "core/file_sys/registered_cache.h"
+#include "core/frontend/emu_window.h"
+
+#include "qt_common/render/emu_thread.h"
+
+#include "core/file_sys/vfs/vfs_real.h"
-#include <core/file_sys/vfs/vfs_real.h>
+enum class StartGameType {
+ Normal, // Can use custom configuration
+ Global, // Only uses global configuration
+};
namespace QtCommon {
+// TODO: Remove QWidget dependency
extern QWidget* rootObject;
extern std::unique_ptr<Core::System> system;
extern std::shared_ptr<FileSys::RealVfsFilesystem> vfs;
extern std::unique_ptr<FileSys::ManualContentProvider> provider;
+extern std::unique_ptr<EmuThread> emu_thread;
+
+extern const QStringList supported_file_extensions;
typedef std::function<bool(std::size_t, std::size_t)> QtProgressCallback;
@@ -28,6 +40,9 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
void Init(QWidget* root);
+void SetupContentProviders();
+void SetupHID();
+
const QString tr(const char* str);
const QString tr(const std::string& str);
diff --git a/src/qt_common/render/context.h b/src/qt_common/render/context.h
new file mode 100644
index 0000000000..a0ecade9c8
--- /dev/null
+++ b/src/qt_common/render/context.h
@@ -0,0 +1,107 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <QSurface>
+#include "common/logging.h"
+#include "common/settings.h"
+#include "core/frontend/graphics_context.h"
+
+#ifdef HAS_OPENGL
+#include <QOffscreenSurface>
+#include <QOpenGLContext>
+#endif
+
+#ifdef HAS_OPENGL
+class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
+public:
+ /// Create the original context that should be shared from
+ explicit OpenGLSharedContext(QSurface* surface_) : surface{surface_} {
+ QSurfaceFormat format;
+ format.setVersion(4, 6);
+ format.setProfile(QSurfaceFormat::CompatibilityProfile);
+ format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
+ if (Settings::values.renderer_debug) {
+ format.setOption(QSurfaceFormat::FormatOption::DebugContext);
+ }
+ // TODO: expose a setting for buffer value (ie default/single/double/triple)
+ format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
+ format.setSwapInterval(0);
+
+ context = std::make_unique<QOpenGLContext>();
+ context->setFormat(format);
+ if (!context->create()) {
+ LOG_ERROR(Frontend, "Unable to create main openGL context");
+ }
+ }
+
+ /// Create the shared contexts for rendering and presentation
+ explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) {
+
+ // disable vsync for any shared contexts
+ auto format = share_context->format();
+ const int swap_interval =
+ Settings::values.vsync_mode.GetValue() == Settings::VSyncMode::Immediate ? 0 : 1;
+
+ format.setSwapInterval(main_surface ? swap_interval : 0);
+
+ context = std::make_unique<QOpenGLContext>();
+ context->setShareContext(share_context);
+ context->setFormat(format);
+ if (!context->create()) {
+ LOG_ERROR(Frontend, "Unable to create shared openGL context");
+ }
+
+ if (!main_surface) {
+ offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr);
+ offscreen_surface->setFormat(format);
+ offscreen_surface->create();
+ surface = offscreen_surface.get();
+ } else {
+ surface = main_surface;
+ }
+ }
+
+ ~OpenGLSharedContext() {
+ DoneCurrent();
+ }
+
+ void SwapBuffers() override {
+ context->swapBuffers(surface);
+ }
+
+ void MakeCurrent() override {
+ // We can't track the current state of the underlying context in this wrapper class because
+ // Qt may make the underlying context not current for one reason or another. In particular,
+ // the WebBrowser uses GL, so it seems to conflict if we aren't careful.
+ // Instead of always just making the context current (which does not have any caching to
+ // check if the underlying context is already current) we can check for the current context
+ // in the thread local data by calling `currentContext()` and checking if its ours.
+ if (QOpenGLContext::currentContext() != context.get()) {
+ context->makeCurrent(surface);
+ }
+ }
+
+ void DoneCurrent() override {
+ context->doneCurrent();
+ }
+
+ QOpenGLContext* GetShareContext() {
+ return context.get();
+ }
+
+ const QOpenGLContext* GetShareContext() const {
+ return context.get();
+ }
+
+private:
+ // Avoid using Qt parent system here since we might move the QObjects to new threads
+ // As a note, this means we should avoid using slots/signals with the objects too
+ std::unique_ptr<QOpenGLContext> context;
+ std::unique_ptr<QOffscreenSurface> offscreen_surface{};
+ QSurface* surface;
+};
+#endif
+
+class DummyContext : public Core::Frontend::GraphicsContext {};
diff --git a/src/qt_common/render/emu_thread.cpp b/src/qt_common/render/emu_thread.cpp
new file mode 100644
index 0000000000..1b65867509
--- /dev/null
+++ b/src/qt_common/render/emu_thread.cpp
@@ -0,0 +1,83 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <qdebug.h>
+#include "core/core.h"
+#include "core/cpu_manager.h"
+#include "emu_thread.h"
+#include "qt_common/qt_common.h"
+#include "video_core/gpu.h"
+#include "video_core/rasterizer_interface.h"
+#include "video_core/renderer_base.h"
+
+EmuThread::EmuThread() {}
+
+EmuThread::~EmuThread() = default;
+
+void EmuThread::run() {
+ Common::SetCurrentThreadName("EmuControlThread");
+
+ auto& gpu = QtCommon::system->GPU();
+ auto stop_token = m_stop_source.get_token();
+
+ QtCommon::system->RegisterHostThread();
+
+ // Main process has been loaded. Make the context current to this thread and begin GPU and CPU
+ // execution.
+ gpu.ObtainContext();
+
+ emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
+ if (Settings::values.use_disk_shader_cache.GetValue()) {
+ QtCommon::system->Renderer().ReadRasterizer()->LoadDiskResources(
+ QtCommon::system->GetApplicationProcessProgramID(), stop_token,
+ [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
+ emit LoadProgress(stage, value, total);
+ });
+ }
+ emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
+
+ gpu.ReleaseContext();
+ gpu.Start();
+
+ QtCommon::system->GetCpuManager().OnGpuReady();
+
+ if (QtCommon::system->DebuggerEnabled()) {
+ QtCommon::system->InitializeDebugger();
+ }
+
+ while (!stop_token.stop_requested()) {
+ std::unique_lock lk{m_should_run_mutex};
+ if (m_should_run) {
+ QtCommon::system->Run();
+ m_stopped.Reset();
+
+ m_should_run_cv.wait(lk, stop_token, [&] { return !m_should_run; });
+ } else {
+ QtCommon::system->Pause();
+ m_stopped.Set();
+
+ EmulationPaused(lk);
+ m_should_run_cv.wait(lk, stop_token, [&] { return m_should_run; });
+ EmulationResumed(lk);
+ }
+ }
+
+ // Shutdown the main emulated process
+ QtCommon::system->DetachDebugger();
+ QtCommon::system->ShutdownMainProcess();
+}
+
+// Unlock while emitting signals so that the main thread can
+// continue pumping events.
+
+void EmuThread::EmulationPaused(std::unique_lock<std::mutex>& lk) {
+ lk.unlock();
+ emit DebugModeEntered();
+ lk.lock();
+}
+
+void EmuThread::EmulationResumed(std::unique_lock<std::mutex>& lk) {
+ lk.unlock();
+ emit DebugModeLeft();
+ lk.lock();
+}
diff --git a/src/qt_common/render/emu_thread.h b/src/qt_common/render/emu_thread.h
new file mode 100644
index 0000000000..92bae50979
--- /dev/null
+++ b/src/qt_common/render/emu_thread.h
@@ -0,0 +1,97 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <QThread>
+#include "common/logging.h"
+#include "common/thread.h"
+
+namespace Core {
+class System;
+} // namespace Core
+
+namespace VideoCore {
+enum class LoadCallbackStage;
+} // namespace VideoCore
+
+class EmuThread final : public QThread {
+ Q_OBJECT
+
+public:
+ explicit EmuThread();
+ ~EmuThread() override;
+
+ /**
+ * Start emulation (on new thread)
+ * @warning Only call when not running!
+ */
+ void run() override;
+
+ /**
+ * Sets whether the emulation thread should run or not
+ * @param should_run Boolean value, set the emulation thread to running if true
+ */
+ void SetRunning(bool should_run) {
+ // TODO: Prevent other threads from modifying the state until we finish.
+ {
+ // Notify the running thread to change state.
+ std::unique_lock run_lk{m_should_run_mutex};
+ m_should_run = should_run;
+ m_should_run_cv.notify_one();
+ }
+
+ // Wait until paused, if pausing.
+ if (!should_run) {
+ m_stopped.Wait();
+ }
+ }
+
+ /**
+ * Check if the emulation thread is running or not
+ * @return True if the emulation thread is running, otherwise false
+ */
+ bool IsRunning() const {
+ return m_should_run;
+ }
+
+ /**
+ * Requests for the emulation thread to immediately stop running
+ */
+ void ForceStop() {
+ LOG_WARNING(Frontend, "Force stopping EmuThread");
+ m_stop_source.request_stop();
+ }
+
+private:
+ void EmulationPaused(std::unique_lock<std::mutex>& lk);
+ void EmulationResumed(std::unique_lock<std::mutex>& lk);
+
+private:
+ std::stop_source m_stop_source;
+ std::mutex m_should_run_mutex;
+ std::condition_variable_any m_should_run_cv;
+ Common::Event m_stopped;
+ bool m_should_run{true};
+
+signals:
+ /**
+ * Emitted when the CPU has halted execution
+ *
+ * @warning When connecting to this signal from other threads, make sure to specify either
+ * Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
+ * Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
+ */
+ void DebugModeEntered();
+
+ /**
+ * Emitted right before the CPU continues execution
+ *
+ * @warning When connecting to this signal from other threads, make sure to specify either
+ * Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
+ * Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
+ */
+ void DebugModeLeft();
+
+ void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
+};
diff --git a/src/qt_common/util/content.cpp b/src/qt_common/util/content.cpp
index f705b3e00f..f22242e7f7 100644
--- a/src/qt_common/util/content.cpp
+++ b/src/qt_common/util/content.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
+#include "core/file_sys/card_image.h"
#include "qt_common/util/content.h"
#include "qt_common/util/game.h"
@@ -494,5 +495,94 @@ void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir, const std::str
});
}
-// TODO(crueter): Port InstallFirmware et al. from QML Branch
+bool CheckKeys() {
+ if (!ContentManager::AreKeysPresent()) {
+ QtCommon::Frontend::Information(
+ tr("Keys not installed"),
+ tr("Install decryption keys and restart Eden before attempting to install firmware."));
+ return false;
+ }
+
+ return true;
+}
+
+void InstallFirmware() {
+ if (!CheckKeys())
+ return;
+
+ const QString firmware_source_location =
+ QtCommon::Frontend::GetExistingDirectory(tr("Select Dumped Firmware Source Location"), {});
+
+ if (!firmware_source_location.isEmpty())
+ QtCommon::Content::InstallFirmware(firmware_source_location, false);
+}
+
+void InstallFirmwareZip() {
+ if (!CheckKeys())
+ return;
+
+ const QString firmware_zip_location = QtCommon::Frontend::GetOpenFileName(
+ tr("Select Dumped Firmware ZIP"), {}, tr("Zipped Archives (*.zip)"));
+
+ if (firmware_zip_location.isEmpty())
+ return;
+
+ const QString qCacheDir = QtCommon::Content::UnzipFirmwareToTmp(firmware_zip_location);
+
+ // In this case, it has to be done recursively, since sometimes people
+ // will pack it into a subdirectory after dumping
+ if (!qCacheDir.isEmpty()) {
+ QtCommon::Content::InstallFirmware(qCacheDir, true);
+ std::error_code ec;
+ std::filesystem::remove_all(std::filesystem::temp_directory_path() / "eden" / "firmware",
+ ec);
+
+ if (ec) {
+ QtCommon::Frontend::Warning(
+ tr("Firmware cleanup failed"),
+ tr("Failed to clean up extracted firmware cache.\n"
+ "Check write permissions in the system temp directory and try "
+ "again.\nOS reported error: %1")
+ .arg(QString::fromStdString(ec.message())));
+ }
+ }
+}
+
+void configureFilesystemProvider(const std::string& filepath) {
+ // Ensure all NCAs are registered before launching the game
+ const auto file = QtCommon::vfs->OpenFile(filepath, FileSys::OpenMode::Read);
+ if (!file) {
+ return;
+ }
+
+ auto loader = Loader::GetLoader(*QtCommon::system, file);
+ if (!loader) {
+ return;
+ }
+
+ const auto file_type = loader->GetFileType();
+ if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
+ return;
+ }
+
+ u64 program_id = 0;
+ const auto res2 = loader->ReadProgramId(program_id);
+ if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
+ QtCommon::provider->AddEntry(FileSys::TitleType::Application,
+ FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
+ program_id, file);
+ } else if (res2 == Loader::ResultStatus::Success &&
+ (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
+ const auto nsp = file_type == Loader::FileType::NSP
+ ? std::make_shared<FileSys::NSP>(file)
+ : FileSys::XCI{file}.GetSecurePartitionNSP();
+ for (const auto& title : nsp->GetNCAs()) {
+ for (const auto& entry : title.second) {
+ QtCommon::provider->AddEntry(entry.first.first, entry.first.second, title.first,
+ entry.second->GetBaseFile());
+ }
+ }
+ }
+}
+
} // namespace QtCommon::Content
diff --git a/src/qt_common/util/content.h b/src/qt_common/util/content.h
index 20dacf540a..452764ef54 100644
--- a/src/qt_common/util/content.h
+++ b/src/qt_common/util/content.h
@@ -37,9 +37,12 @@ inline const QString GetKeyInstallResultString(FirmwareManager::KeyInstallResult
}
void InstallFirmware(const QString& location, bool recursive);
-
QString UnzipFirmwareToTmp(const QString& location);
+bool CheckKeys();
+void InstallFirmware();
+void InstallFirmwareZip();
+
// Keys //
void InstallKeys();
@@ -54,6 +57,9 @@ void ExportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string&
void ImportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id = "",
std::function<void()> callback = {});
+// Loader //
+void configureFilesystemProvider(const std::string& filepath);
+
// Profiles //
void FixProfiles();
} // namespace QtCommon::Content
diff --git a/src/qt_common/util/game.cpp b/src/qt_common/util/game.cpp
index e037fdae6c..ddbe979765 100644
--- a/src/qt_common/util/game.cpp
+++ b/src/qt_common/util/game.cpp
@@ -11,7 +11,6 @@
#include "qt_common/abstract/frontend.h"
#include "qt_common/config/uisettings.h"
#include "qt_common/qt_common.h"
-#include "yuzu/util/util.h"
#include <QDesktopServices>
#include <QStandardPaths>
@@ -22,6 +21,7 @@
#include <windows.h>
#include "common/scope_exit.h"
#include "common/string_util.h"
+#include "common/fs/file.h"
#else
#include <fstream>
#include "fmt/ostream.h"
@@ -367,6 +367,7 @@ void ResetMetadata(bool show_message) {
// Uhhh //
+// TODO(crueter): Make QtCommon::Shortcut
// Messages in pre-defined message boxes for less code spaghetti
inline constexpr bool CreateShortcutMessagesGUI(ShortcutMessages imsg, const QString& game_title) {
int result = 0;
@@ -533,4 +534,110 @@ void CreateHomeMenuShortcut(ShortcutTarget target) {
CreateShortcut(game_path, QLaunchId, "Switch Home Menu", target, "-qlaunch", false);
}
+bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) {
+#if defined(WIN32)
+#pragma pack(push, 2)
+ struct IconDir {
+ WORD id_reserved;
+ WORD id_type;
+ WORD id_count;
+ };
+
+ struct IconDirEntry {
+ BYTE width;
+ BYTE height;
+ BYTE color_count;
+ BYTE reserved;
+ WORD planes;
+ WORD bit_count;
+ DWORD bytes_in_res;
+ DWORD image_offset;
+ };
+#pragma pack(pop)
+
+ const QImage source_image = image.convertToFormat(QImage::Format_RGB32);
+ constexpr std::array<int, 7> scale_sizes{256, 128, 64, 48, 32, 24, 16};
+ constexpr int bytes_per_pixel = 4;
+
+ const IconDir icon_dir{
+ .id_reserved = 0,
+ .id_type = 1,
+ .id_count = static_cast<WORD>(scale_sizes.size()),
+ };
+
+ Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write,
+ Common::FS::FileType::BinaryFile);
+ if (!icon_file.IsOpen()) {
+ return false;
+ }
+
+ if (!icon_file.Write(icon_dir)) {
+ return false;
+ }
+
+ std::size_t image_offset = sizeof(IconDir) + (sizeof(IconDirEntry) * scale_sizes.size());
+ for (std::size_t i = 0; i < scale_sizes.size(); i++) {
+ const int image_size = scale_sizes[i] * scale_sizes[i] * bytes_per_pixel;
+ const IconDirEntry icon_entry{
+ .width = static_cast<BYTE>(scale_sizes[i]),
+ .height = static_cast<BYTE>(scale_sizes[i]),
+ .color_count = 0,
+ .reserved = 0,
+ .planes = 1,
+ .bit_count = bytes_per_pixel * 8,
+ .bytes_in_res = static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size),
+ .image_offset = static_cast<DWORD>(image_offset),
+ };
+ image_offset += icon_entry.bytes_in_res;
+ if (!icon_file.Write(icon_entry)) {
+ return false;
+ }
+ }
+
+ for (std::size_t i = 0; i < scale_sizes.size(); i++) {
+ const QImage scaled_image = source_image.scaled(
+ scale_sizes[i], scale_sizes[i], Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ const BITMAPINFOHEADER info_header{
+ .biSize = sizeof(BITMAPINFOHEADER),
+ .biWidth = scaled_image.width(),
+ .biHeight = scaled_image.height() * 2,
+ .biPlanes = 1,
+ .biBitCount = bytes_per_pixel * 8,
+ .biCompression = BI_RGB,
+ .biSizeImage{},
+ .biXPelsPerMeter{},
+ .biYPelsPerMeter{},
+ .biClrUsed{},
+ .biClrImportant{},
+ };
+
+ if (!icon_file.Write(info_header)) {
+ return false;
+ }
+
+ for (int y = 0; y < scaled_image.height(); y++) {
+ const auto* line = scaled_image.scanLine(scaled_image.height() - 1 - y);
+ std::vector<u8> line_data(scaled_image.width() * bytes_per_pixel);
+ std::memcpy(line_data.data(), line, line_data.size());
+ if (!icon_file.Write(line_data)) {
+ return false;
+ }
+ }
+ }
+ icon_file.Close();
+
+ return true;
+#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
+ // Convert and write the icon as a PNG
+ if (!image.save(QString::fromStdString(icon_path.string()))) {
+ LOG_ERROR(Frontend, "Could not write icon as PNG to file");
+ } else {
+ LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string());
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
} // namespace QtCommon::Game
diff --git a/src/qt_common/util/game.h b/src/qt_common/util/game.h
index 0d7f03fa86..9a61bb9349 100644
--- a/src/qt_common/util/game.h
+++ b/src/qt_common/util/game.h
@@ -69,6 +69,14 @@ void CreateShortcut(const std::string& game_path, const u64 program_id,
std::string GetShortcutPath(ShortcutTarget target);
void CreateHomeMenuShortcut(ShortcutTarget target);
+/**
+ * Saves a windows icon to a file
+ * @param path The icons path
+ * @param image The image to save
+ * @return bool If the operation succeeded
+ */
+[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image);
+
} // namespace QtCommon::Game
#endif // QT_GAME_UTIL_H
diff --git a/src/yuzu/vk_device_info.cpp b/src/qt_common/util/vk.cpp
index 1e8e140d77..8ca7f4a665 100644
--- a/src/yuzu/vk_device_info.cpp
+++ b/src/qt_common/util/vk.cpp
@@ -11,13 +11,13 @@
#include "common/dynamic_library.h"
#include "common/logging.h"
+#include "qt_common/util/vk.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_instance.h"
#include "video_core/vulkan_common/vulkan_library.h"
#include "video_core/vulkan_common/vulkan_surface.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
#include "vulkan/vulkan_core.h"
-#include "yuzu/vk_device_info.h"
class QWindow;
diff --git a/src/qt_common/util/vk.h b/src/qt_common/util/vk.h
new file mode 100644
index 0000000000..3e6730f643
--- /dev/null
+++ b/src/qt_common/util/vk.h
@@ -0,0 +1,71 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <string>
+#include <string_view>
+#include <vector>
+#include "common/common_types.h"
+#include "common/settings_enums.h"
+#include "vulkan/vulkan_core.h"
+
+static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR,
+ VK_PRESENT_MODE_FIFO_KHR};
+
+// Converts a setting to a present mode (or vice versa)
+static inline constexpr VkPresentModeKHR VSyncSettingToMode(Settings::VSyncMode mode) {
+ switch (mode) {
+ case Settings::VSyncMode::Immediate:
+ return VK_PRESENT_MODE_IMMEDIATE_KHR;
+ case Settings::VSyncMode::Mailbox:
+ return VK_PRESENT_MODE_MAILBOX_KHR;
+ case Settings::VSyncMode::Fifo:
+ return VK_PRESENT_MODE_FIFO_KHR;
+ case Settings::VSyncMode::FifoRelaxed:
+ return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
+ default:
+ return VK_PRESENT_MODE_FIFO_KHR;
+ }
+}
+
+static inline constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode) {
+ switch (mode) {
+ case VK_PRESENT_MODE_IMMEDIATE_KHR:
+ return Settings::VSyncMode::Immediate;
+ case VK_PRESENT_MODE_MAILBOX_KHR:
+ return Settings::VSyncMode::Mailbox;
+ case VK_PRESENT_MODE_FIFO_KHR:
+ return Settings::VSyncMode::Fifo;
+ case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
+ return Settings::VSyncMode::FifoRelaxed;
+ default:
+ return Settings::VSyncMode::Fifo;
+ }
+}
+
+class QWindow;
+
+namespace Settings {
+enum class VSyncMode : u32;
+}
+
+namespace VkDeviceInfo {
+// Short class to record Vulkan driver information for configuration purposes
+class Record {
+public:
+ explicit Record(std::string_view name, const std::vector<VkPresentModeKHR>& vsync_modes,
+ bool has_broken_compute);
+ ~Record();
+
+ const std::string name;
+ const std::vector<VkPresentModeKHR> vsync_support;
+ const bool has_broken_compute;
+};
+
+// TODO(crueter): Port some configure_graphics.cpp stuff
+void PopulateRecords(std::vector<Record>& records, QWindow* window);
+} // namespace VkDeviceInfo
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 7468c06c06..0bddcca3e8 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -217,8 +217,6 @@ add_executable(yuzu
util/url_request_interceptor.h
util/util.cpp
util/util.h
- vk_device_info.cpp
- vk_device_info.h
compatdb.cpp
compatdb.h
user_data_migration.cpp
@@ -245,9 +243,7 @@ add_executable(yuzu
render/performance_overlay.h render/performance_overlay.cpp render/performance_overlay.ui
libqt_common.h libqt_common.cpp
- updater/update_dialog.h updater/update_dialog.cpp updater/update_dialog.ui
-
-)
+ updater/update_dialog.h updater/update_dialog.cpp updater/update_dialog.ui)
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index dd55034e4e..074b2360ee 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -6,8 +6,6 @@
#include <cmath>
#include <cstring>
#include <string>
-#include <tuple>
-#include <type_traits>
#ifdef HAS_OPENGL
#include <glad/glad.h>
@@ -45,16 +43,13 @@
#include <QOpenGLContext>
#endif
-#include "common/polyfill_thread.h"
#include "common/scm_rev.h"
#include "common/settings.h"
#include "common/settings_input.h"
-#include "common/thread.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/frontend/framebuffer_layout.h"
#include "core/frontend/graphics_context.h"
-#include "input_common/drivers/camera.h"
#include "input_common/drivers/keyboard.h"
#include "input_common/drivers/mouse.h"
#include "input_common/drivers/tas_input.h"
@@ -62,189 +57,19 @@
#include "input_common/main.h"
#include "qt_common/qt_common.h"
#include "video_core/gpu.h"
-#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
#include "yuzu/bootmanager.h"
#include "yuzu/main_window.h"
+#include "qt_common/render/context.h"
+#include "qt_common/render/emu_thread.h"
+
class QObject;
class QPaintEngine;
class QSurface;
constexpr int default_mouse_constrain_timeout = 10;
-EmuThread::EmuThread(Core::System& system) : m_system{system} {}
-
-EmuThread::~EmuThread() = default;
-
-void EmuThread::run() {
- Common::SetCurrentThreadName("EmuControlThread");
-
- auto& gpu = m_system.GPU();
- auto stop_token = m_stop_source.get_token();
-
- m_system.RegisterHostThread();
-
- // Main process has been loaded. Make the context current to this thread and begin GPU and CPU
- // execution.
- gpu.ObtainContext();
-
- emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
- if (Settings::values.use_disk_shader_cache.GetValue()) {
- m_system.Renderer().ReadRasterizer()->LoadDiskResources(
- m_system.GetApplicationProcessProgramID(), stop_token,
- [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
- emit LoadProgress(stage, value, total);
- });
- }
- emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
-
- gpu.ReleaseContext();
- gpu.Start();
-
- m_system.GetCpuManager().OnGpuReady();
-
- if (m_system.DebuggerEnabled()) {
- m_system.InitializeDebugger();
- }
-
- while (!stop_token.stop_requested()) {
- std::unique_lock lk{m_should_run_mutex};
- if (m_should_run) {
- m_system.Run();
- m_stopped.Reset();
-
- m_should_run_cv.wait(lk, stop_token, [&] { return !m_should_run; });
- } else {
- m_system.Pause();
- m_stopped.Set();
-
- EmulationPaused(lk);
- m_should_run_cv.wait(lk, stop_token, [&] { return m_should_run; });
- EmulationResumed(lk);
- }
- }
-
- // Shutdown the main emulated process
- m_system.DetachDebugger();
- m_system.ShutdownMainProcess();
-}
-
-// Unlock while emitting signals so that the main thread can
-// continue pumping events.
-
-void EmuThread::EmulationPaused(std::unique_lock<std::mutex>& lk) {
- lk.unlock();
- emit DebugModeEntered();
- lk.lock();
-}
-
-void EmuThread::EmulationResumed(std::unique_lock<std::mutex>& lk) {
- lk.unlock();
- emit DebugModeLeft();
- lk.lock();
-}
-
-#ifdef HAS_OPENGL
-class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
-public:
- /// Create the original context that should be shared from
- explicit OpenGLSharedContext(QSurface* surface_) : surface{surface_} {
- QSurfaceFormat format;
- format.setVersion(4, 6);
- format.setProfile(QSurfaceFormat::CompatibilityProfile);
- format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
- if (Settings::values.renderer_debug) {
- format.setOption(QSurfaceFormat::FormatOption::DebugContext);
- }
- // TODO: expose a setting for buffer value (ie default/single/double/triple)
- format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
- format.setSwapInterval(0);
-
- context = std::make_unique<QOpenGLContext>();
- context->setFormat(format);
- if (!context->create()) {
- LOG_ERROR(Frontend, "Unable to create main openGL context");
- }
- }
-
- /// Create the shared contexts for rendering and presentation
- explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) {
-
- // disable vsync for any shared contexts
- auto format = share_context->format();
- const int swap_interval =
- Settings::values.vsync_mode.GetValue() == Settings::VSyncMode::Immediate ? 0 : 1;
-
- format.setSwapInterval(main_surface ? swap_interval : 0);
-
- context = std::make_unique<QOpenGLContext>();
- context->setShareContext(share_context);
- context->setFormat(format);
- if (!context->create()) {
- LOG_ERROR(Frontend, "Unable to create shared openGL context");
- }
-
- if (!main_surface) {
- offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr);
- offscreen_surface->setFormat(format);
- offscreen_surface->create();
- surface = offscreen_surface.get();
- } else {
- surface = main_surface;
- }
- }
-
- ~OpenGLSharedContext() {
- DoneCurrent();
- }
-
- void SwapBuffers() override {
- if (auto window = static_cast<QWindow*>(surface)) {
- if (!window->isExposed()) {
- LOG_DEBUG(Frontend, "SwapBuffers ignored: window not exposed");
- return;
- }
- }
-
- context->swapBuffers(surface);
- }
-
- void MakeCurrent() override {
- // We can't track the current state of the underlying context in this wrapper class because
- // Qt may make the underlying context not current for one reason or another. In particular,
- // the WebBrowser uses GL, so it seems to conflict if we aren't careful.
- // Instead of always just making the context current (which does not have any caching to
- // check if the underlying context is already current) we can check for the current context
- // in the thread local data by calling `currentContext()` and checking if its ours.
- if (QOpenGLContext::currentContext() != context.get()) {
- context->makeCurrent(surface);
- }
- }
-
- void DoneCurrent() override {
- context->doneCurrent();
- }
-
- QOpenGLContext* GetShareContext() {
- return context.get();
- }
-
- const QOpenGLContext* GetShareContext() const {
- return context.get();
- }
-
-private:
- // Avoid using Qt parent system here since we might move the QObjects to new threads
- // As a note, this means we should avoid using slots/signals with the objects too
- std::unique_ptr<QOpenGLContext> context;
- std::unique_ptr<QOffscreenSurface> offscreen_surface{};
- QSurface* surface;
-};
-#endif
-
-class DummyContext : public Core::Frontend::GraphicsContext {};
-
class RenderWidget : public QWidget {
public:
explicit RenderWidget(GRenderWindow* parent) : QWidget(parent) {
@@ -285,11 +110,9 @@ struct NullRenderWidget : public RenderWidget {
explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {}
};
-GRenderWindow::GRenderWindow(MainWindow* parent, EmuThread* emu_thread_,
- std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
- Core::System& system_)
- : QWidget(parent), emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)},
- system{system_} {
+GRenderWindow::GRenderWindow(MainWindow* parent,
+ std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_)
+ : QWidget(parent), input_subsystem{std::move(input_subsystem_)} {
setWindowTitle(QStringLiteral("Eden %1 | %2-%3")
.arg(QString::fromUtf8(Common::g_build_name),
QString::fromUtf8(Common::g_scm_branch),
@@ -708,10 +531,11 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
}
void GRenderWindow::ConstrainMouse() {
- if (emu_thread == nullptr || !Settings::values.mouse_panning) {
+ if (QtCommon::emu_thread == nullptr || !Settings::values.mouse_panning) {
mouse_constrain_timer.stop();
return;
}
+
if (!this->isActiveWindow()) {
mouse_constrain_timer.stop();
return;
@@ -964,7 +788,7 @@ void GRenderWindow::ReleaseRenderTarget() {
}
void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
- auto& renderer = system.Renderer();
+ auto& renderer = QtCommon::system->Renderer();
if (renderer.IsScreenshotPending()) {
LOG_WARNING(Render,
@@ -989,7 +813,12 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
screenshot_image.bits(),
[=, this](bool invert_y) {
const std::string std_screenshot_path = screenshot_path.toStdString();
+#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
+ if (screenshot_image.flipped(invert_y ? Qt::Vertical : (Qt::Orientations)0)
+ .save(screenshot_path)) {
+#else
if (screenshot_image.mirrored(false, invert_y).save(screenshot_path)) {
+#endif
LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
} else {
LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path);
@@ -1026,7 +855,8 @@ bool GRenderWindow::InitializeOpenGL() {
return true;
#else
- QMessageBox::warning(this, tr("OpenGL not available!"), tr("Eden has not been compiled with OpenGL support."));
+ QMessageBox::warning(this, tr("OpenGL not available!"),
+ tr("Eden has not been compiled with OpenGL support."));
return false;
#endif
}
@@ -1072,8 +902,7 @@ bool GRenderWindow::LoadOpenGL() {
tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you "
"have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported "
"extensions:<br>%2")
- .arg(renderer)
- .arg(missing_ext.join(QStringLiteral("<br>"))));
+ .arg(renderer, missing_ext.join(QStringLiteral("<br>"))));
// Non fatal
}
return true;
@@ -1101,14 +930,6 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
return missing_ext;
}
-void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread_) {
- emu_thread = emu_thread_;
-}
-
-void GRenderWindow::OnEmulationStopping() {
- emu_thread = nullptr;
-}
-
void GRenderWindow::showEvent(QShowEvent* event) {
QWidget::showEvent(event);
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index e763cd9868..1ed61a8191 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -6,12 +6,9 @@
#pragma once
-#include <condition_variable>
#include <cstddef>
#include <memory>
-#include <mutex>
#include <utility>
-#include <vector>
#include <QByteArray>
#include <QImage>
@@ -27,9 +24,6 @@
#include <qobjectdefs.h>
#include "common/common_types.h"
-#include "common/logging.h"
-#include "common/polyfill_thread.h"
-#include "common/thread.h"
#include "core/frontend/emu_window.h"
class MainWindow;
@@ -46,6 +40,7 @@ class QShowEvent;
class QTouchEvent;
class QWheelEvent;
+class EmuThread;
namespace Core {
class System;
} // namespace Core
@@ -63,96 +58,12 @@ namespace VideoCore {
enum class LoadCallbackStage;
} // namespace VideoCore
-class EmuThread final : public QThread {
- Q_OBJECT
-
-public:
- explicit EmuThread(Core::System& system);
- ~EmuThread() override;
-
- /**
- * Start emulation (on new thread)
- * @warning Only call when not running!
- */
- void run() override;
-
- /**
- * Sets whether the emulation thread should run or not
- * @param should_run Boolean value, set the emulation thread to running if true
- */
- void SetRunning(bool should_run) {
- // TODO: Prevent other threads from modifying the state until we finish.
- {
- // Notify the running thread to change state.
- std::unique_lock run_lk{m_should_run_mutex};
- m_should_run = should_run;
- m_should_run_cv.notify_one();
- }
-
- // Wait until paused, if pausing.
- if (!should_run) {
- m_stopped.Wait();
- }
- }
-
- /**
- * Check if the emulation thread is running or not
- * @return True if the emulation thread is running, otherwise false
- */
- bool IsRunning() const {
- return m_should_run;
- }
-
- /**
- * Requests for the emulation thread to immediately stop running
- */
- void ForceStop() {
- LOG_WARNING(Frontend, "Force stopping EmuThread");
- m_stop_source.request_stop();
- }
-
-private:
- void EmulationPaused(std::unique_lock<std::mutex>& lk);
- void EmulationResumed(std::unique_lock<std::mutex>& lk);
-
-private:
- Core::System& m_system;
-
- std::stop_source m_stop_source;
- std::mutex m_should_run_mutex;
- std::condition_variable_any m_should_run_cv;
- Common::Event m_stopped;
- bool m_should_run{true};
-
-signals:
- /**
- * Emitted when the CPU has halted execution
- *
- * @warning When connecting to this signal from other threads, make sure to specify either
- * Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
- * Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
- */
- void DebugModeEntered();
-
- /**
- * Emitted right before the CPU continues execution
- *
- * @warning When connecting to this signal from other threads, make sure to specify either
- * Qt::QueuedConnection (invoke slot within the destination object's message thread) or even
- * Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
- */
- void DebugModeLeft();
-
- void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
-};
-
class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
Q_OBJECT
public:
- explicit GRenderWindow(MainWindow* parent, EmuThread* emu_thread_,
- std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
- Core::System& system_);
+ explicit GRenderWindow(MainWindow* parent,
+ std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_);
~GRenderWindow() override;
// EmuWindow implementation.
@@ -217,8 +128,6 @@ public:
void Exit();
public slots:
- void OnEmulationStarting(EmuThread* emu_thread_);
- void OnEmulationStopping();
void OnFramebufferSizeChanged();
signals:
@@ -247,7 +156,6 @@ private:
bool LoadOpenGL();
QStringList GetUnsupportedGLExtensions() const;
- EmuThread* emu_thread;
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
// Main context that will be shared with all other contexts that are requested.
@@ -277,8 +185,6 @@ private:
QTimer mouse_constrain_timer;
- Core::System& system;
-
protected:
void showEvent(QShowEvent* event) override;
bool eventFilter(QObject* object, QEvent* event) override;
diff --git a/src/yuzu/configuration/addon/mod_select_dialog.cpp b/src/yuzu/configuration/addon/mod_select_dialog.cpp
index 49f556a66a..f025a3b390 100644
--- a/src/yuzu/configuration/addon/mod_select_dialog.cpp
+++ b/src/yuzu/configuration/addon/mod_select_dialog.cpp
@@ -33,8 +33,8 @@ ModSelectDialog::ModSelectDialog(const QStringList& mods, QWidget* parent)
ui->treeView->expandAll();
int rows = item_model->rowCount();
- int height =
- 4 + ui->treeView->contentsMargins().top() * 4 + ui->treeView->contentsMargins().bottom() * 4;
+ int height = 4 + ui->treeView->contentsMargins().top() * 4 +
+ ui->treeView->contentsMargins().bottom() * 4;
int width = 0;
for (int i = 0; i < rows; ++i) {
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index b31ca849a6..0511465ee6 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -76,7 +76,8 @@ void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) {
} else if (setting->Id() == Settings::values.cpu_backend.Id()) {
backend_layout->addWidget(widget);
backend_combobox = widget->combobox;
- } else if (setting->Id() == Settings::values.fast_cpu_time.Id() || setting->Id() == Settings::values.cpu_ticks.Id()) {
+ } else if (setting->Id() == Settings::values.fast_cpu_time.Id() ||
+ setting->Id() == Settings::values.cpu_ticks.Id()) {
ui->general_layout->addWidget(widget);
} else {
// Presently, all other settings here are unsafe checkboxes
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 9db48c084b..c7ca85ce99 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -51,8 +51,10 @@ void ConfigureDebug::SetConfiguration() {
ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue());
- ui->serial_battery_edit->setText(QString::fromStdString(std::to_string(Settings::values.serial_battery.GetValue())));
- ui->serial_board_edit->setText(QString::fromStdString(std::to_string(Settings::values.serial_unit.GetValue())));
+ ui->serial_battery_edit->setText(
+ QString::fromStdString(std::to_string(Settings::values.serial_battery.GetValue())));
+ ui->serial_board_edit->setText(
+ QString::fromStdString(std::to_string(Settings::values.serial_unit.GetValue())));
#ifdef YUZU_USE_QT_WEB_ENGINE
ui->disable_web_applet->setChecked(Settings::values.disable_web_applet.GetValue());
#else
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index d4a16e9f73..547008262e 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -10,8 +10,8 @@
#include "common/settings_enums.h"
#include "core/core.h"
#include "qt_common/config/uisettings.h"
+#include "qt_common/util/vk.h"
#include "ui_configure.h"
-#include "vk_device_info.h"
#include "yuzu/configuration/configure_applets.h"
#include "yuzu/configuration/configure_audio.h"
#include "yuzu/configuration/configure_cpu.h"
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 5c504c9a16..73db11238e 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -11,8 +11,8 @@
#include <QDialog>
#include "configuration/shared_widget.h"
#include "qt_common/config/shared_translation.h"
+#include "qt_common/util/vk.h"
#include "yuzu/configuration/configuration_shared.h"
-#include "yuzu/vk_device_info.h"
namespace Core {
class System;
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 54835aea5a..344cbe2406 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -4,14 +4,7 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <algorithm>
#include <functional>
-#include <iosfwd>
-#include <iterator>
-#include <string>
-#include <tuple>
-#include <typeinfo>
-#include <utility>
#include <vector>
#include <QBoxLayout>
#include <QCheckBox>
@@ -34,52 +27,15 @@
#include <vulkan/vulkan_core.h>
#include "common/common_types.h"
-#include "common/dynamic_library.h"
-#include "common/logging.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "core/core.h"
#include "qt_common/config/uisettings.h"
-#include "qt_common/qt_common.h"
+#include "qt_common/util/vk.h"
#include "ui_configure_graphics.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics.h"
#include "yuzu/configuration/shared_widget.h"
-#include "yuzu/vk_device_info.h"
-
-static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR,
- VK_PRESENT_MODE_FIFO_KHR};
-
-// Converts a setting to a present mode (or vice versa)
-static constexpr VkPresentModeKHR VSyncSettingToMode(Settings::VSyncMode mode) {
- switch (mode) {
- case Settings::VSyncMode::Immediate:
- return VK_PRESENT_MODE_IMMEDIATE_KHR;
- case Settings::VSyncMode::Mailbox:
- return VK_PRESENT_MODE_MAILBOX_KHR;
- case Settings::VSyncMode::Fifo:
- return VK_PRESENT_MODE_FIFO_KHR;
- case Settings::VSyncMode::FifoRelaxed:
- return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
- default:
- return VK_PRESENT_MODE_FIFO_KHR;
- }
-}
-
-static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode) {
- switch (mode) {
- case VK_PRESENT_MODE_IMMEDIATE_KHR:
- return Settings::VSyncMode::Immediate;
- case VK_PRESENT_MODE_MAILBOX_KHR:
- return Settings::VSyncMode::Mailbox;
- case VK_PRESENT_MODE_FIFO_KHR:
- return Settings::VSyncMode::Fifo;
- case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
- return Settings::VSyncMode::FifoRelaxed;
- default:
- return Settings::VSyncMode::Fifo;
- }
-}
ConfigureGraphics::ConfigureGraphics(
const Core::System& system_, std::vector<VkDeviceInfo::Record>& records_,
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 3009edf4b6..9002af437b 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
@@ -19,7 +19,7 @@
#include "common/common_types.h"
#include "common/settings_enums.h"
#include "qt_common/config/shared_translation.h"
-#include "vk_device_info.h"
+#include "qt_common/util/vk.h"
#include "yuzu/configuration/configuration_shared.h"
class QPushButton;
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index dcf40d678f..f84448dd29 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -30,6 +30,7 @@
#include "core/loader/loader.h"
#include "frontend_common/config.h"
#include "qt_common/config/uisettings.h"
+#include "qt_common/util/vk.h"
#include "ui_configure_per_game.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_applets.h"
@@ -44,7 +45,6 @@
#include "yuzu/configuration/configure_per_game_addons.h"
#include "yuzu/configuration/configure_system.h"
#include "yuzu/util/util.h"
-#include "yuzu/vk_device_info.h"
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name,
std::vector<VkDeviceInfo::Record>& vk_device_records,
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index ca973db8b0..bc6561235e 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -18,7 +18,7 @@
#include "frontend_common/config.h"
#include "qt_common/config/qt_config.h"
#include "qt_common/config/shared_translation.h"
-#include "vk_device_info.h"
+#include "qt_common/util/vk.h"
#include "yuzu/configuration/configuration_shared.h"
namespace Core {
diff --git a/src/yuzu/data_dialog.cpp b/src/yuzu/data_dialog.cpp
index 8d50843eb3..ffdedcf1c2 100644
--- a/src/yuzu/data_dialog.cpp
+++ b/src/yuzu/data_dialog.cpp
@@ -63,27 +63,31 @@ DataWidget::DataWidget(FrontendCommon::DataManager::DataDir data_dir,
void DataWidget::clear() {
std::optional<std::string> user_id = selectProfile();
- if (!user_id) return;
+ if (!user_id)
+ return;
QtCommon::Content::ClearDataDir(m_dir, user_id.value());
scan();
}
void DataWidget::open() {
std::optional<std::string> user_id = selectProfile();
- if (!user_id) return;
- QDesktopServices::openUrl(QUrl::fromLocalFile(
- QString::fromStdString(FrontendCommon::DataManager::GetDataDirString(m_dir, user_id.value()))));
+ if (!user_id)
+ return;
+ QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(
+ FrontendCommon::DataManager::GetDataDirString(m_dir, user_id.value()))));
}
void DataWidget::upload() {
std::optional<std::string> user_id = selectProfile();
- if (!user_id) return;
+ if (!user_id)
+ return;
QtCommon::Content::ExportDataDir(m_dir, user_id.value(), m_exportName);
}
void DataWidget::download() {
std::optional<std::string> user_id = selectProfile();
- if (!user_id) return;
+ if (!user_id)
+ return;
QtCommon::Content::ImportDataDir(m_dir, user_id.value(), std::bind(&DataWidget::scan, this));
}
@@ -107,7 +111,8 @@ std::optional<std::string> DataWidget::selectProfile() {
std::string user_id{};
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
user_id = GetProfileIDString();
- if (user_id.empty()) return std::nullopt;
+ if (user_id.empty())
+ return std::nullopt;
}
return user_id;
diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp
index 8965c7a08f..3506f1edf3 100644
--- a/src/yuzu/debugger/console.cpp
+++ b/src/yuzu/debugger/console.cpp
@@ -11,7 +11,6 @@
#endif
#include "common/logging.h"
-#include "yuzu/debugger/console.h"
#include "qt_common/config/uisettings.h"
#include "yuzu/debugger/console.h"
diff --git a/src/yuzu/game/game_card.cpp b/src/yuzu/game/game_card.cpp
index 05167fe455..03c71449f8 100644
--- a/src/yuzu/game/game_card.cpp
+++ b/src/yuzu/game/game_card.cpp
@@ -36,7 +36,8 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
const auto x_pos = option.rect.left() - (column * cell_width) + static_cast<int>(relative_x);
// also, add some additional padding here to prevent card overlap
- QRect cardRect(x_pos + 4, option.rect.top() + 4, fixed_card_width - 8, option.rect.height() - margins);
+ QRect cardRect(x_pos + 4, option.rect.top() + 4, fixed_card_width - 8,
+ option.rect.height() - margins);
// colors
QPalette palette = option.palette;
diff --git a/src/yuzu/game/game_list.cpp b/src/yuzu/game/game_list.cpp
index 87b5f87474..b5967bacca 100644
--- a/src/yuzu/game/game_list.cpp
+++ b/src/yuzu/game/game_list.cpp
@@ -1113,10 +1113,6 @@ void GameList::LoadInterfaceLayout() {
header->resizeSection(COLUMN_NAME, 840);
}
-const QStringList GameList::supported_file_extensions = {
- QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"),
- QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
-
void GameList::RefreshGameDirectory() {
// Reset the externals watcher whenever the game list is reloaded,
// primarily ensures that new titles and external dirs are caught.
diff --git a/src/yuzu/game/game_list.h b/src/yuzu/game/game_list.h
index 42efcbdc3b..6cf857c4e1 100644
--- a/src/yuzu/game/game_list.h
+++ b/src/yuzu/game/game_list.h
@@ -95,8 +95,6 @@ public:
/// Disables events from the emulated controller
void UnloadController();
- static const QStringList supported_file_extensions;
-
bool IsTreeMode();
void ResetViewMode();
diff --git a/src/yuzu/game/game_list_worker.cpp b/src/yuzu/game/game_list_worker.cpp
index 777122e135..df3560a813 100644
--- a/src/yuzu/game/game_list_worker.cpp
+++ b/src/yuzu/game/game_list_worker.cpp
@@ -20,17 +20,18 @@
#include "common/settings.h"
#include "core/core.h"
#include "core/file_sys/card_image.h"
-#include "core/file_sys/common_funcs.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
-#include "core/file_sys/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/loader/loader.h"
+
#include "qt_common/config/uisettings.h"
+#include "qt_common/qt_common.h"
+
#include "yuzu/compatibility_list.h"
#include "yuzu/game/game_list.h"
#include "yuzu/game/game_list_p.h"
@@ -148,7 +149,7 @@ void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const
bool HasSupportedFileExtension(const std::string& file_name) {
const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
- return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
+ return QtCommon::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
}
bool IsExtractedNCAMain(const std::string& file_name) {
diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp
index a43a06cd31..3ca4b40a6d 100644
--- a/src/yuzu/main_window.cpp
+++ b/src/yuzu/main_window.cpp
@@ -2,17 +2,17 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Qt on macOS doesn't define VMA shit
+#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
+#undef VMA_IMPLEMENTATION
+#endif
+
#include <boost/algorithm/string/split.hpp>
#include "common/fs/path_util.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "frontend_common/settings_generator.h"
-#include "qt_common/qt_string_lookup.h"
#include "render/performance_overlay.h"
#include "updater/update_dialog.h"
-#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
-#undef VMA_IMPLEMENTATION
-#endif
#include "common/fs/ryujinx_compat.h"
#include "main_window.h"
@@ -31,10 +31,10 @@
#include "bootmanager.h"
#include "loading_screen.h"
+#include "qt_common/util/vk.h"
#include "ryujinx_dialog.h"
#include "set_play_time_dialog.h"
#include "util/util.h"
-#include "vk_device_info.h"
#include "yuzu/game/game_list.h"
#include "applets/qt_amiibo_settings.h"
@@ -86,6 +86,7 @@
#include "qt_common/abstract/frontend.h"
#include "qt_common/qt_common.h"
+#include "qt_common/qt_string_lookup.h"
#include "qt_common/util/content.h"
#include "qt_common/util/fs.h"
@@ -93,6 +94,8 @@
#include "qt_common/util/mod.h"
#include "qt_common/util/path.h"
+#include "qt_common/render/emu_thread.h"
+
// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
// defines.
static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(const std::string& path,
@@ -315,73 +318,6 @@ enum class CalloutFlag : uint32_t {
const int MainWindow::max_recent_files_item;
-static void RemoveCachedContents() {
- const auto cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir);
- const auto offline_fonts = cache_dir / "fonts";
- const auto offline_manual = cache_dir / "offline_web_applet_manual";
- const auto offline_legal_information = cache_dir / "offline_web_applet_legal_information";
- const auto offline_system_data = cache_dir / "offline_web_applet_system_data";
-
- Common::FS::RemoveDirRecursively(offline_fonts);
- Common::FS::RemoveDirRecursively(offline_manual);
- Common::FS::RemoveDirRecursively(offline_legal_information);
- Common::FS::RemoveDirRecursively(offline_system_data);
-}
-
-static void LogRuntimes() {
-#ifdef _MSC_VER
- // It is possible that the name of the dll will change.
- // vcruntime140.dll is for 2015 and onwards
- static constexpr char runtime_dll_name[] = "vcruntime140.dll";
- UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr);
- bool runtime_version_inspection_worked = false;
- if (sz > 0) {
- std::vector<u8> buf(sz);
- if (GetFileVersionInfoA(runtime_dll_name, 0, sz, buf.data())) {
- VS_FIXEDFILEINFO* pvi;
- sz = sizeof(VS_FIXEDFILEINFO);
- if (VerQueryValueA(buf.data(), "\\", reinterpret_cast<LPVOID*>(&pvi), &sz)) {
- if (pvi->dwSignature == VS_FFI_SIGNATURE) {
- runtime_version_inspection_worked = true;
- LOG_INFO(Frontend, "MSVC Compiler: {} Runtime: {}.{}.{}.{}", _MSC_VER,
- pvi->dwProductVersionMS >> 16, pvi->dwProductVersionMS & 0xFFFF,
- pvi->dwProductVersionLS >> 16, pvi->dwProductVersionLS & 0xFFFF);
- }
- }
- }
- }
- if (!runtime_version_inspection_worked) {
- LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name);
- }
-#endif
- LOG_INFO(Frontend, "Qt Compile: {} Runtime: {}", QT_VERSION_STR, qVersion());
-}
-
-static QString PrettyProductName() {
-#ifdef _WIN32
- // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
- // With that notation change they changed the registry key used to denote the current version
- QSettings windows_registry(
- QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
- QSettings::NativeFormat);
- const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
- if (release_id == QStringLiteral("2009")) {
- const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
- const QString display_version =
- windows_registry.value(QStringLiteral("DisplayVersion")).toString();
- const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
- u32 version = 10;
- if (current_build >= 22000) {
- version = 11;
- }
- return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
- .arg(QString::number(version), display_version, QString::number(current_build),
- QString::number(ubr));
- }
-#endif
- return QSysInfo::prettyProductName();
-}
-
namespace {
constexpr std::array<std::pair<u32, const char*>, 5> default_game_icon_sizes{
@@ -447,11 +383,6 @@ MainWindow::MainWindow(bool has_broken_vulkan)
UISettings::RestoreWindowState(config);
- QtCommon::system->Initialize();
-
- Common::Log::Initialize();
- Common::Log::Start();
-
LoadTranslation();
FrontendCommon::GenerateSettings();
@@ -472,10 +403,6 @@ MainWindow::MainWindow(bool has_broken_vulkan)
play_time_manager = std::make_unique<PlayTime::PlayTimeManager>();
- Network::Init();
-
- QtCommon::Meta::RegisterMetaTypes();
-
InitializeWidgets();
InitializeDebugWidgets();
InitializeRecentFileMenuActions();
@@ -487,53 +414,9 @@ MainWindow::MainWindow(bool has_broken_vulkan)
ConnectMenuEvents();
ConnectWidgetEvents();
- QtCommon::system->HIDCore().ReloadInputDevices();
+ QtCommon::SetupHID();
controller_dialog->refreshConfiguration();
- const auto branch_name = std::string(Common::g_scm_branch);
- const auto description = std::string(Common::g_scm_desc);
- const auto build_id = std::string(Common::g_build_id);
-
- const auto yuzu_build = fmt::format("Eden Development Build | {}-{}", branch_name, description);
- const auto override_build =
- fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
- const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
- const auto processor_count = std::thread::hardware_concurrency();
-
- LOG_INFO(Frontend, "Eden Version: {}", yuzu_build_version);
- LogRuntimes();
-#ifdef ARCHITECTURE_x86_64
- const auto& caps = Common::GetCPUCaps();
- std::string cpu_string = caps.cpu_string;
- if (caps.avx || caps.avx2 || caps.avx512f) {
- cpu_string += " | AVX";
- if (caps.avx512f) {
- cpu_string += "512";
- } else if (caps.avx2) {
- cpu_string += '2';
- }
- if (caps.fma || caps.fma4) {
- cpu_string += " | FMA";
- }
- }
- LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
- if (std::optional<int> processor_core = Common::GetProcessorCount()) {
- LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core);
- }
-#endif
- LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);
- LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
- LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
- Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
- LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
-#ifdef _WIN32
- LOG_INFO(Frontend, "Host Timer Resolution: {:.4f} ms",
- std::chrono::duration_cast<std::chrono::duration<f64, std::milli>>(
- Common::Windows::SetCurrentTimerResolutionToMaximum())
- .count());
- QtCommon::system->CoreTiming().SetTimerResolutionNs(
- Common::Windows::GetCurrentTimerResolution());
-#endif
UpdateWindowTitle();
show();
@@ -548,13 +431,8 @@ MainWindow::MainWindow(bool has_broken_vulkan)
}
#endif
- QtCommon::system->SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
- QtCommon::system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
- QtCommon::provider.get());
- QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
-
- // Remove cached contents generated during the previous session
- RemoveCachedContents();
+ // Setup content providers.
+ QtCommon::SetupContentProviders();
// Gen keys if necessary
OnCheckFirmwareDecryption();
@@ -1079,7 +957,7 @@ void MainWindow::InitializeWidgets() {
#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
ui->action_Report_Compatibility->setVisible(true);
#endif
- render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem, *QtCommon::system);
+ render_window = new GRenderWindow(this, input_subsystem);
render_window->hide();
game_list = new GameList(QtCommon::vfs, QtCommon::provider.get(), *play_time_manager,
@@ -1539,11 +1417,12 @@ void MainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
return;
}
if (UISettings::values.pause_when_in_background) {
- if (emu_thread->IsRunning() &&
+ if (QtCommon::emu_thread->IsRunning() &&
(state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) {
auto_paused = true;
OnPauseGame();
- } else if (!emu_thread->IsRunning() && auto_paused && (state & Qt::ApplicationActive)) {
+ } else if (!QtCommon::emu_thread->IsRunning() && auto_paused &&
+ (state & Qt::ApplicationActive)) {
auto_paused = false;
OnStartGame();
}
@@ -1595,11 +1474,6 @@ void MainWindow::ConnectWidgetEvents() {
connect(this, &MainWindow::UpdateInstallProgress, this, &MainWindow::IncrementInstallProgress);
- connect(this, &MainWindow::EmulationStarting, render_window,
- &GRenderWindow::OnEmulationStarting);
- connect(this, &MainWindow::EmulationStopping, render_window,
- &GRenderWindow::OnEmulationStopping);
-
// Software Keyboard Applet
connect(this, &MainWindow::EmulationStarting, this, &MainWindow::SoftwareKeyboardExit);
connect(this, &MainWindow::EmulationStopping, this, &MainWindow::SoftwareKeyboardExit);
@@ -1753,7 +1627,7 @@ void MainWindow::ConnectMenuEvents() {
}
void MainWindow::UpdateMenuState() {
- const bool is_paused = emu_thread == nullptr || !emu_thread->IsRunning();
+ const bool is_paused = QtCommon::emu_thread == nullptr || !QtCommon::emu_thread->IsRunning();
const bool is_firmware_available = CheckFirmwarePresence();
const std::array running_actions{
@@ -1821,17 +1695,16 @@ void MainWindow::SetupPrepareForSleep() {
}
void MainWindow::OnPrepareForSleep(bool prepare_sleep) {
- if (emu_thread == nullptr) {
+ if (QtCommon::emu_thread == nullptr)
return;
- }
if (prepare_sleep) {
- if (emu_thread->IsRunning()) {
+ if (QtCommon::emu_thread->IsRunning()) {
auto_paused = true;
OnPauseGame();
}
} else {
- if (!emu_thread->IsRunning() && auto_paused) {
+ if (!QtCommon::emu_thread->IsRunning() && auto_paused) {
auto_paused = false;
OnStartGame();
}
@@ -1904,19 +1777,16 @@ void MainWindow::AllowOSSleep() {
bool MainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletParameters params) {
// Shutdown previous session if the emu thread is still active...
- if (emu_thread != nullptr) {
+ if (QtCommon::emu_thread != nullptr)
ShutdownGame();
- }
- if (!render_window->InitRenderTarget()) {
+ if (!render_window->InitRenderTarget())
return false;
- }
QtCommon::system->SetFilesystem(QtCommon::vfs);
- if (params.launch_type == Service::AM::LaunchType::FrontendInitiated) {
+ if (params.launch_type == Service::AM::LaunchType::FrontendInitiated)
QtCommon::system->GetUserChannel().clear();
- }
QtCommon::system->SetFrontendAppletSet({
std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings
@@ -2023,36 +1893,6 @@ bool MainWindow::SelectAndSetCurrentUser(
return true;
}
-void MainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
- // Ensure all NCAs are registered before launching the game
- const auto file = QtCommon::vfs->OpenFile(filepath, FileSys::OpenMode::Read);
- if (!file) {
- return;
- }
-
- if (QtCommon::provider->AddEntriesFromContainer(file)) {
- return;
- }
-
- auto loader = Loader::GetLoader(*QtCommon::system, file);
- if (!loader) {
- return;
- }
-
- const auto file_type = loader->GetFileType();
- if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
- return;
- }
-
- u64 program_id = 0;
- const auto res2 = loader->ReadProgramId(program_id);
- if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
- QtCommon::provider->AddEntry(FileSys::TitleType::Application,
- FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
- program_id, file);
- }
-}
-
void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletParameters params,
StartGameType type) {
LOG_INFO(Frontend, "Eden starting...");
@@ -2071,7 +1911,7 @@ void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletPa
last_filename_booted = filename;
- ConfigureFilesystemProvider(filename.toStdString());
+ QtCommon::Content::configureFilesystemProvider(filename.toStdString());
const auto v_file = Core::GetGameFileFromPath(QtCommon::vfs, filename.toUtf8().constData());
const auto loader =
Loader::GetLoader(*QtCommon::system, v_file, params.program_id, params.program_index);
@@ -2116,23 +1956,23 @@ void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletPa
game_list->setDisabled(true);
// Create and start the emulation thread
- emu_thread = std::make_unique<EmuThread>(*QtCommon::system);
- emit EmulationStarting(emu_thread.get());
- emu_thread->start();
+ QtCommon::emu_thread = std::make_unique<EmuThread>();
+ emit EmulationStarting();
+ QtCommon::emu_thread->start();
// Register an ExecuteProgram callback such that Core can execute a sub-program
QtCommon::system->RegisterExecuteProgramCallback(
[this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
QtCommon::system->RegisterExitCallback([this] {
- emu_thread->ForceStop();
+ QtCommon::emu_thread->ForceStop();
render_window->Exit();
});
connect(render_window, &GRenderWindow::Closed, this, &MainWindow::OnStopGame);
connect(render_window, &GRenderWindow::MouseActivity, this, &MainWindow::OnMouseActivity);
- connect(emu_thread.get(), &EmuThread::LoadProgress, loading_screen,
+ connect(QtCommon::emu_thread.get(), &EmuThread::LoadProgress, loading_screen,
&LoadingScreen::OnLoadProgress, Qt::QueuedConnection);
// Update the GUI
@@ -2227,9 +2067,10 @@ bool MainWindow::OnShutdownBegin() {
discord_rpc->Pause();
RequestGameExit();
- emu_thread->disconnect();
- emu_thread->SetRunning(true);
+ QtCommon::emu_thread->disconnect();
+ QtCommon::emu_thread->SetRunning(true);
+ connect(QtCommon::emu_thread.get(), &QThread::finished, this, &MainWindow::OnEmulationStopped);
emit EmulationStopping();
int shutdown_time = 1000;
@@ -2243,7 +2084,6 @@ bool MainWindow::OnShutdownBegin() {
shutdown_timer.setSingleShot(true);
shutdown_timer.start(shutdown_time);
connect(&shutdown_timer, &QTimer::timeout, this, &MainWindow::OnEmulationStopTimeExpired);
- connect(emu_thread.get(), &QThread::finished, this, &MainWindow::OnEmulationStopped);
// Disable everything to prevent anything from being triggered here
ui->action_Pause->setEnabled(false);
@@ -2261,17 +2101,17 @@ void MainWindow::OnShutdownBeginDialog() {
}
void MainWindow::OnEmulationStopTimeExpired() {
- if (emu_thread) {
- emu_thread->ForceStop();
+ if (QtCommon::emu_thread) {
+ QtCommon::emu_thread->ForceStop();
}
}
void MainWindow::OnEmulationStopped() {
shutdown_timer.stop();
- if (emu_thread) {
- emu_thread->disconnect();
- emu_thread->wait();
- emu_thread.reset();
+ if (QtCommon::emu_thread) {
+ QtCommon::emu_thread->disconnect();
+ QtCommon::emu_thread->wait();
+ QtCommon::emu_thread.reset();
}
if (shutdown_dialog) {
@@ -2912,7 +2752,7 @@ void MainWindow::OnMenuLoadFile() {
is_load_file_select_active = true;
const QString extensions =
QStringLiteral("*.")
- .append(GameList::supported_file_extensions.join(QStringLiteral(" *.")))
+ .append(QtCommon::supported_file_extensions.join(QStringLiteral(" *.")))
.append(QStringLiteral(" main"));
const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)",
"%1 is an identifier for the Switch executable file extensions.")
@@ -3148,7 +2988,7 @@ void MainWindow::OnMenuRecentFile() {
void MainWindow::OnStartGame() {
PreventOSSleep();
- emu_thread->SetRunning(true);
+ QtCommon::emu_thread->SetRunning(true);
UpdateMenuState();
OnTasStateChanged();
@@ -3174,7 +3014,7 @@ void MainWindow::OnRestartGame() {
}
void MainWindow::OnPauseGame() {
- emu_thread->SetRunning(false);
+ QtCommon::emu_thread->SetRunning(false);
play_time_manager->Stop();
UpdateMenuState();
AllowOSSleep();
@@ -3183,7 +3023,7 @@ void MainWindow::OnPauseGame() {
void MainWindow::OnPauseContinueGame() {
if (emulation_running) {
- if (emu_thread->IsRunning()) {
+ if (QtCommon::emu_thread->IsRunning()) {
OnPauseGame();
} else {
OnStartGame();
@@ -3863,12 +3703,9 @@ void MainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_
}
void MainWindow::OnLoadAmiibo() {
- if (emu_thread == nullptr || !emu_thread->IsRunning()) {
+ if (QtCommon::emu_thread == nullptr || !QtCommon::emu_thread->IsRunning() ||
+ is_amiibo_file_select_active)
return;
- }
- if (is_amiibo_file_select_active) {
- return;
- }
auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
@@ -3972,79 +3809,21 @@ void MainWindow::OnVerifyInstalledContents() {
QtCommon::Content::VerifyInstalledContents();
}
-void MainWindow::InstallFirmware(const QString& location, bool recursive) {
- QtCommon::Content::InstallFirmware(location, recursive);
- OnCheckFirmwareDecryption();
-}
-
void MainWindow::OnInstallFirmware() {
- // Don't do this while emulation is running, that'd probably be a bad idea.
- if (emu_thread != nullptr && emu_thread->IsRunning()) {
- return;
- }
-
- // Check for installed keys, error out, suggest restart?
- if (!ContentManager::AreKeysPresent()) {
- QMessageBox::information(
- this, tr("Keys not installed"),
- tr("Install decryption keys and restart Eden before attempting to install firmware."));
- return;
- }
-
- const QString firmware_source_location = QFileDialog::getExistingDirectory(
- this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly);
- if (firmware_source_location.isEmpty()) {
- return;
- }
-
- InstallFirmware(firmware_source_location);
+ QtCommon::Content::InstallFirmware();
+ OnCheckFirmwareDecryption();
}
void MainWindow::OnInstallFirmwareFromZIP() {
- // Don't do this while emulation is running, that'd probably be a bad idea.
- if (emu_thread != nullptr && emu_thread->IsRunning()) {
- return;
- }
-
- // Check for installed keys, error out, suggest restart?
- if (!ContentManager::AreKeysPresent()) {
- QMessageBox::information(
- this, tr("Keys not installed"),
- tr("Install decryption keys and restart Eden before attempting to install firmware."));
- return;
- }
-
- const QString firmware_zip_location = QFileDialog::getOpenFileName(
- this, tr("Select Dumped Firmware ZIP"), {}, tr("Zipped Archives (*.zip)"));
- if (firmware_zip_location.isEmpty()) {
- return;
- }
-
- const QString qCacheDir = QtCommon::Content::UnzipFirmwareToTmp(firmware_zip_location);
-
- // In this case, it has to be done recursively, since sometimes people
- // will pack it into a subdirectory after dumping
- if (!qCacheDir.isEmpty()) {
- InstallFirmware(qCacheDir, true);
- std::error_code ec;
- std::filesystem::remove_all(std::filesystem::temp_directory_path() / "eden" / "firmware",
- ec);
-
- if (ec) {
- QMessageBox::warning(this, tr("Firmware cleanup failed"),
- tr("Failed to clean up extracted firmware cache.\n"
- "Check write permissions in the system temp directory and try "
- "again.\nOS reported error: %1")
- .arg(QString::fromStdString(ec.message())));
- }
- }
+ QtCommon::Content::InstallFirmwareZip();
+ OnCheckFirmwareDecryption();
}
+// TODO(crueter): QtCommon this: game list populate can be a signal?
void MainWindow::OnInstallDecryptionKeys() {
// Don't do this while emulation is running.
- if (emu_thread != nullptr && emu_thread->IsRunning()) {
+ if (QtCommon::emu_thread != nullptr && QtCommon::emu_thread->IsRunning())
return;
- }
QtCommon::Content::InstallKeys();
@@ -4072,11 +3851,10 @@ void MainWindow::OnDataDialog() {
void MainWindow::OnToggleFilterBar() {
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
- if (ui->action_Show_Filter_Bar->isChecked()) {
+ if (ui->action_Show_Filter_Bar->isChecked())
game_list->SetFilterFocus();
- } else {
+ else
game_list->ClearFilter();
- }
}
void MainWindow::OnToggleStatusBar() {
@@ -4084,9 +3862,8 @@ void MainWindow::OnToggleStatusBar() {
}
void MainWindow::OnTogglePerfOverlay() {
- if (perf_overlay) {
+ if (perf_overlay)
perf_overlay->setVisible(ui->action_Show_Performance_Overlay->isChecked());
- }
}
void MainWindow::OnGameListRefresh() {
@@ -4191,9 +3968,8 @@ void MainWindow::OnCreateHomeMenuApplicationMenuShortcut() {
}
void MainWindow::OnCaptureScreenshot() {
- if (emu_thread == nullptr || !emu_thread->IsRunning()) {
+ if (QtCommon::emu_thread == nullptr || !QtCommon::emu_thread->IsRunning())
return;
- }
const u64 title_id = QtCommon::system->GetApplicationProcessProgramID();
const auto screenshot_path =
@@ -4205,9 +3981,8 @@ void MainWindow::OnCaptureScreenshot() {
.arg(title_id, 16, 16, QLatin1Char{'0'})
.arg(date);
- if (!Common::FS::CreateDir(screenshot_path.toStdString())) {
+ if (!Common::FS::CreateDir(screenshot_path.toStdString()))
return;
- }
#ifdef _WIN32
if (UISettings::values.enable_screenshot_save_as) {
@@ -4313,16 +4088,15 @@ void MainWindow::OnTasStateChanged() {
}
void MainWindow::UpdateStatusBar() {
- if (emu_thread == nullptr || !QtCommon::system->IsPoweredOn()) {
+ if (QtCommon::emu_thread == nullptr || !QtCommon::system->IsPoweredOn()) {
status_bar_update_timer.stop();
return;
}
- if (Settings::values.tas_enable) {
+ if (Settings::values.tas_enable)
tas_label->setText(GetTasStateDescription());
- } else {
+ else
tas_label->clear();
- }
auto results = QtCommon::system->GetAndResetPerfStats();
auto& shader_notify = QtCommon::system->GPU().ShaderNotify();
@@ -4444,14 +4218,13 @@ void MainWindow::UpdateUISettings() {
}
void MainWindow::UpdateInputDrivers() {
- if (!input_subsystem) {
+ if (!input_subsystem)
return;
- }
input_subsystem->PumpEvents();
}
void MainWindow::HideMouseCursor() {
- if (emu_thread == nullptr && UISettings::values.hide_mouse) {
+ if (QtCommon::emu_thread == nullptr && UISettings::values.hide_mouse) {
mouse_hide_timer.stop();
ShowMouseCursor();
return;
@@ -4461,9 +4234,8 @@ void MainWindow::HideMouseCursor() {
void MainWindow::ShowMouseCursor() {
render_window->unsetCursor();
- if (emu_thread != nullptr && UISettings::values.hide_mouse) {
+ if (QtCommon::emu_thread != nullptr && UISettings::values.hide_mouse)
mouse_hide_timer.start();
- }
}
void MainWindow::OnMouseActivity() {
@@ -4643,14 +4415,14 @@ bool MainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed
}
bool MainWindow::ConfirmClose() {
- if (emu_thread == nullptr ||
- UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) {
+ if (QtCommon::emu_thread == nullptr ||
+ UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never)
return true;
- }
+
if (!QtCommon::system->GetExitLocked() &&
- UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) {
+ UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game)
return true;
- }
+
const auto text = tr("Are you sure you want to close Eden?");
return question(this, tr("Eden"), text);
}
@@ -4671,9 +4443,8 @@ void MainWindow::closeEvent(QCloseEvent* event) {
game_list->UnloadController();
// Shutdown session if the emu thread is active...
- if (emu_thread != nullptr) {
+ if (QtCommon::emu_thread != nullptr)
ShutdownGame();
- }
render_window->close();
multiplayer_state->Close();
@@ -4736,7 +4507,7 @@ void MainWindow::dragMoveEvent(QDragMoveEvent* event) {
}
bool MainWindow::ConfirmChangeGame() {
- if (emu_thread == nullptr)
+ if (QtCommon::emu_thread == nullptr)
return true;
// Use custom question to link controller navigation
@@ -4747,9 +4518,9 @@ bool MainWindow::ConfirmChangeGame() {
}
bool MainWindow::ConfirmForceLockedExit() {
- if (emu_thread == nullptr) {
+ if (QtCommon::emu_thread == nullptr)
return true;
- }
+
const auto text = tr("The currently running application has requested Eden to not exit.\n\n"
"Would you like to bypass this and exit anyway?");
@@ -4757,9 +4528,8 @@ bool MainWindow::ConfirmForceLockedExit() {
}
void MainWindow::RequestGameExit() {
- if (!QtCommon::system->IsPoweredOn()) {
+ if (!QtCommon::system->IsPoweredOn())
return;
- }
QtCommon::system->SetExitRequested(true);
QtCommon::system->GetAppletManager().RequestExit();
@@ -4772,14 +4542,14 @@ void MainWindow::filterBarSetChecked(bool state) {
static void AdjustLinkColor() {
QPalette new_pal(qApp->palette());
- if (UISettings::IsDarkTheme()) {
+
+ if (UISettings::IsDarkTheme())
new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
- } else {
+ else
new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
- }
- if (qApp->palette().color(QPalette::Link) != new_pal.color(QPalette::Link)) {
+
+ if (qApp->palette().color(QPalette::Link) != new_pal.color(QPalette::Link))
qApp->setPalette(new_pal);
- }
}
void MainWindow::UpdateUITheme() {
@@ -4787,9 +4557,8 @@ void MainWindow::UpdateUITheme() {
UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second);
QString current_theme = QString::fromStdString(UISettings::values.theme);
- if (current_theme.isEmpty()) {
+ if (current_theme.isEmpty())
current_theme = default_theme;
- }
#ifdef _WIN32
QIcon::setThemeName(current_theme);
@@ -4850,17 +4619,15 @@ void MainWindow::LoadTranslation() {
QStringLiteral(":/languages/"));
}
- if (loaded) {
+ if (loaded)
qApp->installTranslator(&translator);
- } else {
+ else
UISettings::values.language = std::string("en");
- }
}
void MainWindow::OnLanguageChanged(const QString& locale) {
- if (UISettings::values.language.GetValue() != std::string("en")) {
+ if (UISettings::values.language.GetValue() != std::string("en"))
qApp->removeTranslator(&translator);
- }
QList<QAction*> actions = game_size_actions->actions();
for (size_t i = 0; i < default_game_icon_sizes.size(); i++) {
@@ -4876,11 +4643,10 @@ void MainWindow::OnLanguageChanged(const QString& locale) {
void MainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
#ifdef USE_DISCORD_PRESENCE
- if (state) {
+ if (state)
discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(*QtCommon::system);
- } else {
+ else
discord_rpc = std::make_unique<DiscordRPC::NullImpl>();
- }
#else
discord_rpc = std::make_unique<DiscordRPC::NullImpl>();
#endif
diff --git a/src/yuzu/main_window.h b/src/yuzu/main_window.h
index b676c0534a..65da01e9ca 100644
--- a/src/yuzu/main_window.h
+++ b/src/yuzu/main_window.h
@@ -20,9 +20,9 @@
#include "common/common_types.h"
#include "common/settings_enums.h"
#include "frontend_common/content_manager.h"
-#include "frontend_common/update_checker.h"
#include "input_common/drivers/tas_input.h"
#include "qt_common/config/qt_config.h"
+#include "qt_common/qt_common.h"
#include "qt_common/util/game.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/hotkeys.h"
@@ -38,6 +38,7 @@
#ifdef ENABLE_UPDATE_CHECKER
#include <QFuture>
#include <QFutureWatcher>
+#include "common/net/net.h"
#endif
class QtConfig;
@@ -66,11 +67,6 @@ class QtProfileSelectionDialog;
class QtSoftwareKeyboardDialog;
class QtNXWebEngineView;
-enum class StartGameType {
- Normal, // Can use custom configuration
- Global, // Only uses global configuration
-};
-
namespace VideoCore {
class ShaderNotify;
}
@@ -184,11 +180,8 @@ signals:
* Signal that is emitted when a new EmuThread has been created and an emulation session is
* about to start. At this time, the core system emulation has been initialized, and all
* emulation handles and memory should be valid.
- *
- * @param emu_thread Pointer to the newly created EmuThread (to be used by widgets that need to
- * access/change emulation state).
*/
- void EmulationStarting(EmuThread* emu_thread);
+ void EmulationStarting();
/**
* Signal that is emitted when emulation is about to stop. At this time, the EmuThread and core
@@ -465,7 +458,6 @@ private:
bool CheckFirmwarePresence();
void SetFirmwareVersion();
void SetFPSSuffix();
- void ConfigureFilesystemProvider(const std::string& filepath);
/**
* Open (or not) the right confirm dialog based on current setting and game exit lock
* @returns true if the player confirmed or the settings do no require it
@@ -538,7 +530,6 @@ private:
// Whether emulation is currently running in yuzu.
bool emulation_running = false;
- std::unique_ptr<EmuThread> emu_thread;
// The path to the game currently running
QString current_game_path;
// Whether a user was set on the command line (skips UserSelector if it's forced to show up)
@@ -599,8 +590,6 @@ private:
const std::string& game_title, QtCommon::Game::ShortcutTarget target,
std::string arguments, const bool needs_title);
- void InstallFirmware(const QString& location, bool recursive = false);
-
protected:
void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
diff --git a/src/yuzu/updater/update_dialog.h b/src/yuzu/updater/update_dialog.h
index 692481b710..e821ef660d 100644
--- a/src/yuzu/updater/update_dialog.h
+++ b/src/yuzu/updater/update_dialog.h
@@ -15,7 +15,7 @@ class UpdateDialog : public QDialog {
Q_OBJECT
public:
- explicit UpdateDialog(const Common::Net::Release &release, QWidget* parent = nullptr);
+ explicit UpdateDialog(const Common::Net::Release& release, QWidget* parent = nullptr);
~UpdateDialog();
private slots:
@@ -23,6 +23,6 @@ private slots:
private:
Ui::UpdateDialog* ui;
- QList<QRadioButton *> m_buttons;
+ QList<QRadioButton*> m_buttons;
Common::Net::Asset m_asset;
};
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp
index a3933d9b63..e37d9d03e4 100644
--- a/src/yuzu/util/util.cpp
+++ b/src/yuzu/util/util.cpp
@@ -43,111 +43,6 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) {
return circle_pixmap;
}
-bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) {
-#if defined(WIN32)
-#pragma pack(push, 2)
- struct IconDir {
- WORD id_reserved;
- WORD id_type;
- WORD id_count;
- };
-
- struct IconDirEntry {
- BYTE width;
- BYTE height;
- BYTE color_count;
- BYTE reserved;
- WORD planes;
- WORD bit_count;
- DWORD bytes_in_res;
- DWORD image_offset;
- };
-#pragma pack(pop)
-
- const QImage source_image = image.convertToFormat(QImage::Format_RGB32);
- constexpr std::array<int, 7> scale_sizes{256, 128, 64, 48, 32, 24, 16};
- constexpr int bytes_per_pixel = 4;
-
- const IconDir icon_dir{
- .id_reserved = 0,
- .id_type = 1,
- .id_count = static_cast<WORD>(scale_sizes.size()),
- };
-
- Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write,
- Common::FS::FileType::BinaryFile);
- if (!icon_file.IsOpen()) {
- return false;
- }
-
- if (!icon_file.Write(icon_dir)) {
- return false;
- }
-
- std::size_t image_offset = sizeof(IconDir) + (sizeof(IconDirEntry) * scale_sizes.size());
- for (std::size_t i = 0; i < scale_sizes.size(); i++) {
- const int image_size = scale_sizes[i] * scale_sizes[i] * bytes_per_pixel;
- const IconDirEntry icon_entry{
- .width = static_cast<BYTE>(scale_sizes[i]),
- .height = static_cast<BYTE>(scale_sizes[i]),
- .color_count = 0,
- .reserved = 0,
- .planes = 1,
- .bit_count = bytes_per_pixel * 8,
- .bytes_in_res = static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size),
- .image_offset = static_cast<DWORD>(image_offset),
- };
- image_offset += icon_entry.bytes_in_res;
- if (!icon_file.Write(icon_entry)) {
- return false;
- }
- }
-
- for (std::size_t i = 0; i < scale_sizes.size(); i++) {
- const QImage scaled_image = source_image.scaled(
- scale_sizes[i], scale_sizes[i], Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
- const BITMAPINFOHEADER info_header{
- .biSize = sizeof(BITMAPINFOHEADER),
- .biWidth = scaled_image.width(),
- .biHeight = scaled_image.height() * 2,
- .biPlanes = 1,
- .biBitCount = bytes_per_pixel * 8,
- .biCompression = BI_RGB,
- .biSizeImage{},
- .biXPelsPerMeter{},
- .biYPelsPerMeter{},
- .biClrUsed{},
- .biClrImportant{},
- };
-
- if (!icon_file.Write(info_header)) {
- return false;
- }
-
- for (int y = 0; y < scaled_image.height(); y++) {
- const auto* line = scaled_image.scanLine(scaled_image.height() - 1 - y);
- std::vector<u8> line_data(scaled_image.width() * bytes_per_pixel);
- std::memcpy(line_data.data(), line, line_data.size());
- if (!icon_file.Write(line_data)) {
- return false;
- }
- }
- }
- icon_file.Close();
-
- return true;
-#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
- // Convert and write the icon as a PNG
- if (!image.save(QString::fromStdString(icon_path.string()))) {
- LOG_ERROR(Frontend, "Could not write icon as PNG to file");
- } else {
- LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string());
- }
- return true;
-#else
- return false;
-#endif
-}
const std::optional<Common::UUID> GetProfileID() {
// if there's only a single profile, the user probably wants to use that... right?
const auto& profiles = QtCommon::system->GetProfileManager().FindExistingProfileUUIDs();
diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h
index 7b482aa11d..9c3d5aa84a 100644
--- a/src/yuzu/util/util.h
+++ b/src/yuzu/util/util.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
@@ -6,7 +6,6 @@
#pragma once
-#include <filesystem>
#include <QFont>
#include <QString>
#include "common/uuid.h"
@@ -25,14 +24,6 @@
[[nodiscard]] QPixmap CreateCirclePixmapFromColor(const QColor& color);
/**
- * Saves a windows icon to a file
- * @param path The icons path
- * @param image The image to save
- * @return bool If the operation succeeded
- */
-[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image);
-
-/**
* Prompt the user for a profile ID. If there is only one valid profile, returns that profile.
* @return The selected profile, or an std::nullopt if none were selected
*/
diff --git a/src/yuzu/vk_device_info.h b/src/yuzu/vk_device_info.h
deleted file mode 100644
index bda8262f4e..0000000000
--- a/src/yuzu/vk_device_info.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <algorithm>
-#include <iterator>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <vector>
-#include "common/common_types.h"
-#include "vulkan/vulkan_core.h"
-
-class QWindow;
-
-namespace Settings {
-enum class VSyncMode : u32;
-}
-// #include "common/settings.h"
-
-namespace VkDeviceInfo {
-// Short class to record Vulkan driver information for configuration purposes
-class Record {
-public:
- explicit Record(std::string_view name, const std::vector<VkPresentModeKHR>& vsync_modes,
- bool has_broken_compute);
- ~Record();
-
- const std::string name;
- const std::vector<VkPresentModeKHR> vsync_support;
- const bool has_broken_compute;
-};
-
-void PopulateRecords(std::vector<Record>& records, QWindow* window);
-} // namespace VkDeviceInfo