Browse Source
[desktop] More qt_common reorganization (#3916)
[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>lizzie/unaligned-attempt-2
No known key found for this signature in database
GPG Key ID: 425ACD2D4830EBC6
44 changed files with 963 additions and 932 deletions
-
32CMakeLists.txt
-
11CMakeModules/CPMUtil.cmake
-
3cpmfile.json
-
12src/common/settings.cpp
-
2src/common/settings.h
-
9src/core/file_sys/vfs/vfs.h
-
3src/dedicated_room/CMakeLists.txt
-
11src/qt_common/CMakeLists.txt
-
5src/qt_common/config/shared_translation.cpp
-
216src/qt_common/qt_common.cpp
-
19src/qt_common/qt_common.h
-
107src/qt_common/render/context.h
-
83src/qt_common/render/emu_thread.cpp
-
97src/qt_common/render/emu_thread.h
-
92src/qt_common/util/content.cpp
-
8src/qt_common/util/content.h
-
109src/qt_common/util/game.cpp
-
8src/qt_common/util/game.h
-
2src/qt_common/util/vk.cpp
-
71src/qt_common/util/vk.h
-
6src/yuzu/CMakeLists.txt
-
213src/yuzu/bootmanager.cpp
-
100src/yuzu/bootmanager.h
-
4src/yuzu/configuration/addon/mod_select_dialog.cpp
-
3src/yuzu/configuration/configure_cpu.cpp
-
6src/yuzu/configuration/configure_debug.cpp
-
2src/yuzu/configuration/configure_dialog.cpp
-
2src/yuzu/configuration/configure_dialog.h
-
46src/yuzu/configuration/configure_graphics.cpp
-
4src/yuzu/configuration/configure_graphics.h
-
2src/yuzu/configuration/configure_per_game.cpp
-
2src/yuzu/configuration/configure_per_game.h
-
19src/yuzu/data_dialog.cpp
-
1src/yuzu/debugger/console.cpp
-
3src/yuzu/game/game_card.cpp
-
4src/yuzu/game/game_list.cpp
-
2src/yuzu/game/game_list.h
-
7src/yuzu/game/game_list_worker.cpp
-
396src/yuzu/main_window.cpp
-
17src/yuzu/main_window.h
-
4src/yuzu/updater/update_dialog.h
-
105src/yuzu/util/util.cpp
-
11src/yuzu/util/util.h
-
36src/yuzu/vk_device_info.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 {}; |
||||
@ -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(); |
||||
|
} |
||||
@ -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); |
||||
|
}; |
||||
@ -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 |
||||
@ -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 |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue