Browse Source
fix config, initial gfx stuff
fix config, initial gfx stuff
Signed-off-by: crueter <crueter@eden-emu.dev>pull/3016/head
No known key found for this signature in database
GPG Key ID: 425ACD2D4830EBC6
45 changed files with 1213 additions and 432 deletions
-
44src/Eden/Config/GlobalConfigureDialog.qml
-
9src/Eden/Config/fields/BaseField.qml
-
3src/Eden/Config/fields/ConfigCheckbox.qml
-
5src/Eden/Config/fields/FieldCheckbox.qml
-
7src/Eden/Config/pages/SettingsList.qml
-
4src/Eden/Config/pages/audio/AudioGeneralPage.qml
-
4src/Eden/Config/pages/cpu/CpuGeneralPage.qml
-
4src/Eden/Config/pages/debug/DebugAdvancedPage.qml
-
4src/Eden/Config/pages/debug/DebugCpuPage.qml
-
5src/Eden/Config/pages/debug/DebugGeneralPage.qml
-
4src/Eden/Config/pages/debug/DebugGraphicsPage.qml
-
4src/Eden/Config/pages/general/UiGameListPage.qml
-
8src/Eden/Config/pages/general/UiGeneralPage.qml
-
4src/Eden/Config/pages/graphics/RendererAdvancedPage.qml
-
4src/Eden/Config/pages/graphics/RendererExtensionsPage.qml
-
10src/Eden/Config/pages/graphics/RendererPage.qml
-
5src/Eden/Config/pages/system/AppletsPage.qml
-
4src/Eden/Config/pages/system/FileSystemPage.qml
-
4src/Eden/Config/pages/system/SystemCorePage.qml
-
5src/Eden/Config/pages/system/SystemGeneralPage.qml
-
4src/Eden/Interface/CMakeLists.txt
-
330src/Eden/Interface/MainWindowInterface.cpp
-
41src/Eden/Interface/MainWindowInterface.h
-
3src/Eden/Interface/QMLConfig.h
-
6src/Eden/Interface/QMLSetting.cpp
-
230src/Eden/Interface/RenderWindow.cpp
-
55src/Eden/Interface/RenderWindow.h
-
1src/Eden/Interface/SettingsInterface.h
-
10src/Eden/Main/Main.qml
-
16src/Eden/Native/EdenApplication.cpp
-
5src/frontend_common/config.cpp
-
3src/qt_common/CMakeLists.txt
-
23src/qt_common/config/qt_config.cpp
-
1src/qt_common/config/qt_config.h
-
1src/qt_common/qt_common.cpp
-
15src/qt_common/qt_common.h
-
104src/qt_common/render/context.h
-
79src/qt_common/render/emu_thread.cpp
-
94src/qt_common/render/emu_thread.h
-
38src/qt_common/util/content.cpp
-
3src/qt_common/util/content.h
-
201src/yuzu/bootmanager.cpp
-
107src/yuzu/bootmanager.h
-
123src/yuzu/main_window.cpp
-
11src/yuzu/main_window.h
@ -0,0 +1,230 @@ |
|||
#include <glad/glad.h>
|
|||
|
|||
#include <QQuickWindow>
|
|||
#include "RenderWindow.h"
|
|||
#include "common/scm_rev.h"
|
|||
#include "common/settings.h"
|
|||
#include "common/settings_enums.h"
|
|||
#include "input_common/main.h"
|
|||
#include "qt_common/qt_common.h"
|
|||
#include "qt_common/render/context.h"
|
|||
#include "qt_common/abstract/frontend.h"
|
|||
|
|||
struct OpenGLRenderItem : public QQuickItem { |
|||
explicit OpenGLRenderItem(RenderWindow* parent) : QQuickItem(parent) { |
|||
window()->setSurfaceType(QWindow::OpenGLSurface); |
|||
} |
|||
|
|||
void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) { |
|||
context = std::move(context_); |
|||
} |
|||
|
|||
private: |
|||
std::unique_ptr<Core::Frontend::GraphicsContext> context; |
|||
}; |
|||
|
|||
struct VulkanRenderItem : public QQuickItem { |
|||
explicit VulkanRenderItem(RenderWindow* parent) : QQuickItem(parent) { |
|||
window()->setSurfaceType(QWindow::VulkanSurface); |
|||
} |
|||
}; |
|||
|
|||
struct NullRenderItem : public QQuickItem { |
|||
explicit NullRenderItem(RenderWindow* parent) : QQuickItem(parent) {} |
|||
}; |
|||
|
|||
bool RenderWindow::initializeOpenGL() { |
|||
#ifdef HAS_OPENGL
|
|||
if (!QOpenGLContext::supportsThreadedOpenGL()) { |
|||
QtCommon::Frontend::Warning(tr("OpenGL not available!"), |
|||
tr("OpenGL shared contexts are not supported.")); |
|||
return false; |
|||
} |
|||
|
|||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
|||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
|||
auto child = new OpenGLRenderItem(this); |
|||
child_item = child; |
|||
child_item->window()->create(); |
|||
auto context = std::make_shared<OpenGLSharedContext>(child->window()); |
|||
main_context = context; |
|||
child->SetContext( |
|||
std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->window())); |
|||
|
|||
return true; |
|||
#else
|
|||
QtCommon::Frontend::Warning(tr("OpenGL not available!"), |
|||
tr("Eden has not been compiled with OpenGL support.")); |
|||
return false; |
|||
#endif
|
|||
} |
|||
|
|||
bool RenderWindow::initializeVulkan() { |
|||
qDebug() << "initializing Vulkan."; |
|||
auto child = new VulkanRenderItem(this); |
|||
child_item = child; |
|||
// child_item->window()->create();
|
|||
main_context = std::make_unique<DummyContext>(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void RenderWindow::initializeNull() { |
|||
child_item = new NullRenderItem(this); |
|||
main_context = std::make_unique<DummyContext>(); |
|||
} |
|||
|
|||
RenderWindow::RenderWindow(QQuickWindow* window, |
|||
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_) |
|||
: QQuickItem(window->contentItem()), input_subsystem{std::move(input_subsystem_)} { |
|||
// STUBBED
|
|||
window->setTitle(QStringLiteral("Eden %1 | %2-%3") |
|||
.arg(QString::fromUtf8(Common::g_build_name), |
|||
QString::fromUtf8(Common::g_scm_branch), |
|||
QString::fromUtf8(Common::g_scm_desc))); |
|||
input_subsystem->Initialize(); |
|||
|
|||
strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") || |
|||
QGuiApplication::platformName() == QStringLiteral("wayland-egl"); |
|||
} |
|||
|
|||
void RenderWindow::OnFrameDisplayed() { |
|||
input_subsystem->GetTas()->UpdateThread(); |
|||
const InputCommon::TasInput::TasState new_tas_state = |
|||
std::get<0>(input_subsystem->GetTas()->GetStatus()); |
|||
|
|||
if (!first_frame) { |
|||
last_tas_state = new_tas_state; |
|||
first_frame = true; |
|||
emit FirstFrameDisplayed(); |
|||
} |
|||
|
|||
if (new_tas_state != last_tas_state) { |
|||
last_tas_state = new_tas_state; |
|||
emit TasPlaybackStateChanged(); |
|||
} |
|||
} |
|||
|
|||
std::unique_ptr<Core::Frontend::GraphicsContext> RenderWindow::CreateSharedContext() const { |
|||
#ifdef HAS_OPENGL
|
|||
if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) { |
|||
auto c = static_cast<OpenGLSharedContext*>(main_context.get()); |
|||
// Bind the shared contexts to the main surface in case the backend wants to take over
|
|||
// presentation
|
|||
return std::make_unique<OpenGLSharedContext>(c->GetShareContext(), child_item->window()); |
|||
} |
|||
#endif
|
|||
return std::make_unique<DummyContext>(); |
|||
} |
|||
|
|||
bool RenderWindow::IsShown() const { |
|||
// STUBBED
|
|||
return true; |
|||
} |
|||
|
|||
bool RenderWindow::initRenderTarget() { |
|||
// STUBBED
|
|||
main_context.reset(); |
|||
|
|||
{ |
|||
// Create a dummy render widget so that Qt
|
|||
// places the render window at the correct position.
|
|||
const QQuickItem dummy_item{this}; |
|||
} |
|||
|
|||
first_frame = false; |
|||
|
|||
switch (Settings::values.renderer_backend.GetValue()) { |
|||
case Settings::RendererBackend::OpenGL: |
|||
if (!initializeOpenGL()) { |
|||
return false; |
|||
} |
|||
break; |
|||
case Settings::RendererBackend::Vulkan: |
|||
if (!initializeVulkan()) { |
|||
return false; |
|||
} |
|||
break; |
|||
case Settings::RendererBackend::Null: |
|||
initializeNull(); |
|||
break; |
|||
} |
|||
|
|||
// Update the Window System information with the new render target
|
|||
window_info = QtCommon::GetWindowSystemInfo(window()); |
|||
|
|||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
|||
onFramebufferSizeChanged(); |
|||
// BackupGeometry();
|
|||
|
|||
if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) { |
|||
if (!loadOpenGL()) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void RenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) { |
|||
window()->setMinimumSize(QSize(minimal_size.first, minimal_size.second)); |
|||
} |
|||
|
|||
bool RenderWindow::loadOpenGL() { |
|||
auto context = CreateSharedContext(); |
|||
auto scope = context->Acquire(); |
|||
if (!gladLoadGL()) { |
|||
QtCommon::Frontend::Warning( |
|||
tr("Error while initializing OpenGL!"), |
|||
tr("Your GPU may not support OpenGL, or you do not have the latest graphics driver.")); |
|||
return false; |
|||
} |
|||
// Display various warnings (but not fatal errors) for missing OpenGL extensions or lack of
|
|||
// OpenGL 4.6 support
|
|||
const QString renderer = QString::fromUtf8(reinterpret_cast<const char*>(glGetString(GL_RENDERER))); |
|||
if (!GLAD_GL_VERSION_4_6) { |
|||
QtCommon::Frontend::Warning(tr("Error while initializing OpenGL 4.6!"), |
|||
tr("Your GPU may not support OpenGL 4.6, or you do not have the " |
|||
"latest graphics driver.<br><br>GL Renderer:<br>%1") |
|||
.arg(renderer)); |
|||
return false; |
|||
} |
|||
if (QStringList missing_ext = getUnsupportedGLExtensions(); !missing_ext.empty()) { |
|||
QtCommon::Frontend::Warning( |
|||
tr("Error while initializing OpenGL!"), |
|||
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, missing_ext.join(QStringLiteral("<br>")))); |
|||
// Non fatal
|
|||
} |
|||
return true; |
|||
} |
|||
|
|||
QStringList RenderWindow::getUnsupportedGLExtensions() const { |
|||
QStringList missing_ext; |
|||
// Extensions required to support some texture formats.
|
|||
if (!GLAD_GL_EXT_texture_compression_s3tc) |
|||
missing_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); |
|||
if (!GLAD_GL_ARB_texture_compression_rgtc) |
|||
missing_ext.append(QStringLiteral("ARB_texture_compression_rgtc")); |
|||
if (!missing_ext.empty()) |
|||
LOG_ERROR(Frontend, "GPU does not support all required extensions"); |
|||
for (const QString& ext : missing_ext) |
|||
LOG_ERROR(Frontend, "Unsupported GL extension: {}", ext.toStdString()); |
|||
return missing_ext; |
|||
} |
|||
|
|||
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
|
|||
//
|
|||
// Older versions get the window size (density independent pixels),
|
|||
// and hence, do not support DPI scaling ("retina" displays).
|
|||
// The result will be a viewport that is smaller than the extent of the window.
|
|||
void RenderWindow::onFramebufferSizeChanged() { |
|||
// Screen changes potentially incur a change in screen DPI, hence we should update the
|
|||
// framebuffer size
|
|||
const qreal pixel_ratio = window()->devicePixelRatio(); |
|||
const u32 width = this->width() * pixel_ratio; |
|||
const u32 height = this->height() * pixel_ratio; |
|||
UpdateCurrentFramebufferLayout(width, height); |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
#pragma once |
|||
|
|||
#include <QQuickItem> |
|||
|
|||
#include "core/frontend/emu_window.h" |
|||
#include "input_common/drivers/tas_input.h" |
|||
|
|||
namespace InputCommon { |
|||
class InputSubsystem; |
|||
} |
|||
namespace Core { |
|||
class System; |
|||
} |
|||
|
|||
class EmuThread; |
|||
class RenderWindow : public QQuickItem, public Core::Frontend::EmuWindow { |
|||
Q_OBJECT |
|||
public: |
|||
RenderWindow(QQuickWindow* window, |
|||
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_); |
|||
|
|||
// EmuWindow interface |
|||
public: |
|||
void OnFrameDisplayed(); |
|||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const; |
|||
bool IsShown() const; |
|||
|
|||
bool initRenderTarget(); |
|||
|
|||
signals: |
|||
void FirstFrameDisplayed(); |
|||
void TasPlaybackStateChanged(); |
|||
|
|||
private: |
|||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size); |
|||
|
|||
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem; |
|||
|
|||
// Main context that will be shared with all other contexts that are requested. |
|||
// If this is used in a shared context setting, then this should not be used directly, but |
|||
// should instead be shared from |
|||
std::shared_ptr<Core::Frontend::GraphicsContext> main_context; |
|||
|
|||
bool first_frame = false; |
|||
InputCommon::TasInput::TasState last_tas_state; |
|||
|
|||
QQuickItem* child_item = nullptr; |
|||
|
|||
void initializeNull(); |
|||
bool initializeVulkan(); |
|||
bool initializeOpenGL(); |
|||
bool loadOpenGL(); |
|||
QStringList getUnsupportedGLExtensions() const; |
|||
void onFramebufferSizeChanged(); |
|||
}; |
|||
@ -0,0 +1,104 @@ |
|||
#pragma once |
|||
|
|||
#include <QSurface> |
|||
#include "common/settings.h" |
|||
#include "core/frontend/graphics_context.h" |
|||
#include "common/logging/log.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,79 @@ |
|||
#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,94 @@ |
|||
#pragma once |
|||
|
|||
#include <QThread> |
|||
#include "common/logging/log.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); |
|||
}; |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue