diff options
Diffstat (limited to 'src/yuzu')
-rw-r--r-- | src/yuzu/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/yuzu/bootmanager.cpp | 284 | ||||
-rw-r--r-- | src/yuzu/bootmanager.h | 30 | ||||
-rw-r--r-- | src/yuzu/configuration/config.cpp | 7 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_debug.cpp | 3 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_debug.ui | 116 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_graphics.cpp | 94 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_graphics.h | 12 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_graphics.ui | 72 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_input_player.cpp | 26 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_input_player.h | 2 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_input_player.ui | 79 | ||||
-rw-r--r-- | src/yuzu/main.cpp | 184 | ||||
-rw-r--r-- | src/yuzu/main.h | 5 |
14 files changed, 744 insertions, 175 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index a3fb91d29..b841e63fa 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -200,3 +200,8 @@ if (MSVC) copy_yuzu_SDL_deps(yuzu) copy_yuzu_unicorn_deps(yuzu) endif() + +if (ENABLE_VULKAN) + target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) + target_compile_definitions(yuzu PRIVATE HAS_VULKAN) +endif() diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 7490fb718..55a37fffa 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -2,19 +2,30 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <glad/glad.h> + #include <QApplication> #include <QHBoxLayout> #include <QKeyEvent> +#include <QMessageBox> #include <QOffscreenSurface> #include <QOpenGLWindow> #include <QPainter> #include <QScreen> +#include <QStringList> #include <QWindow> +#ifdef HAS_VULKAN +#include <QVulkanWindow> +#endif + #include <fmt/format.h> + +#include "common/assert.h" #include "common/microprofile.h" #include "common/scm_rev.h" #include "core/core.h" #include "core/frontend/framebuffer_layout.h" +#include "core/frontend/scope_acquire_window_context.h" #include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" @@ -114,19 +125,10 @@ private: QOpenGLContext context; }; -// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL -// context. -// The corresponding functionality is handled in EmuThread instead -class GGLWidgetInternal : public QOpenGLWindow { +class GWidgetInternal : public QWindow { public: - GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) - : QOpenGLWindow(shared_context), parent(parent) {} - - void paintEvent(QPaintEvent* ev) override { - if (do_painting) { - QPainter painter(this); - } - } + GWidgetInternal(GRenderWindow* parent) : parent(parent) {} + virtual ~GWidgetInternal() = default; void resizeEvent(QResizeEvent* ev) override { parent->OnClientAreaResized(ev->size().width(), ev->size().height()); @@ -182,11 +184,47 @@ public: do_painting = true; } + std::pair<unsigned, unsigned> GetSize() const { + return std::make_pair(width(), height()); + } + +protected: + bool IsPaintingEnabled() const { + return do_painting; + } + private: GRenderWindow* parent; - bool do_painting; + bool do_painting = false; +}; + +// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL +// context. +// The corresponding functionality is handled in EmuThread instead +class GGLWidgetInternal final : public GWidgetInternal, public QOpenGLWindow { +public: + GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) + : GWidgetInternal(parent), QOpenGLWindow(shared_context) {} + ~GGLWidgetInternal() override = default; + + void paintEvent(QPaintEvent* ev) override { + if (IsPaintingEnabled()) { + QPainter painter(this); + } + } }; +#ifdef HAS_VULKAN +class GVKWidgetInternal final : public GWidgetInternal { +public: + GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) { + setSurfaceType(QSurface::SurfaceType::VulkanSurface); + setVulkanInstance(instance); + } + ~GVKWidgetInternal() override = default; +}; +#endif + GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) : QWidget(parent), emu_thread(emu_thread) { setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") @@ -201,9 +239,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) GRenderWindow::~GRenderWindow() { InputCommon::Shutdown(); + + // Avoid an unordered destruction that generates a segfault + delete child; } void GRenderWindow::moveContext() { + if (!context) { + return; + } DoneCurrent(); // If the thread started running, move the GL Context to the new thread. Otherwise, move it @@ -215,8 +259,9 @@ void GRenderWindow::moveContext() { } void GRenderWindow::SwapBuffers() { - context->swapBuffers(child); - + if (context) { + context->swapBuffers(child); + } if (!first_frame) { first_frame = true; emit FirstFrameDisplayed(); @@ -224,15 +269,38 @@ void GRenderWindow::SwapBuffers() { } void GRenderWindow::MakeCurrent() { - context->makeCurrent(child); + if (context) { + context->makeCurrent(child); + } } void GRenderWindow::DoneCurrent() { - context->doneCurrent(); + if (context) { + context->doneCurrent(); + } } void GRenderWindow::PollEvents() {} +bool GRenderWindow::IsShown() const { + return !isMinimized(); +} + +void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, + void* surface) const { +#ifdef HAS_VULKAN + const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); + const VkInstance instance_copy = vk_instance->vkInstance(); + const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child); + + std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); + std::memcpy(instance, &instance_copy, sizeof(instance_copy)); + std::memcpy(surface, &surface_copy, sizeof(surface_copy)); +#else + UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan"); +#endif +} + // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). // // Older versions get the window size (density independent pixels), @@ -241,10 +309,9 @@ void GRenderWindow::PollEvents() {} void GRenderWindow::OnFramebufferSizeChanged() { // Screen changes potentially incur a change in screen DPI, hence we should update the // framebuffer size - const qreal pixel_ratio = GetWindowPixelRatio(); - const u32 width = child->QPaintDevice::width() * pixel_ratio; - const u32 height = child->QPaintDevice::height() * pixel_ratio; - UpdateCurrentFramebufferLayout(width, height); + const qreal pixelRatio{GetWindowPixelRatio()}; + const auto size{child->GetSize()}; + UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio); } void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { @@ -290,7 +357,7 @@ qreal GRenderWindow::GetWindowPixelRatio() const { } std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { - const qreal pixel_ratio = GetWindowPixelRatio(); + const qreal pixel_ratio{GetWindowPixelRatio()}; return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; } @@ -356,50 +423,46 @@ std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedCont return std::make_unique<GGLContext>(context.get()); } -void GRenderWindow::InitRenderTarget() { +bool GRenderWindow::InitRenderTarget() { shared_context.reset(); context.reset(); - - delete child; - child = nullptr; - - delete container; - container = nullptr; - - delete layout(); + if (child) { + delete child; + } + if (container) { + delete container; + } + if (layout()) { + delete layout(); + } first_frame = false; - // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, - // WA_DontShowOnScreen, WA_DeleteOnClose - QSurfaceFormat fmt; - fmt.setVersion(4, 3); - fmt.setProfile(QSurfaceFormat::CompatibilityProfile); - fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); - // TODO: expose a setting for buffer value (ie default/single/double/triple) - fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); - shared_context = std::make_unique<QOpenGLContext>(); - shared_context->setFormat(fmt); - shared_context->create(); - context = std::make_unique<QOpenGLContext>(); - context->setShareContext(shared_context.get()); - context->setFormat(fmt); - context->create(); - fmt.setSwapInterval(0); + switch (Settings::values.renderer_backend) { + case Settings::RendererBackend::OpenGL: + if (!InitializeOpenGL()) { + return false; + } + break; + case Settings::RendererBackend::Vulkan: + if (!InitializeVulkan()) { + return false; + } + break; + } - child = new GGLWidgetInternal(this, shared_context.get()); container = QWidget::createWindowContainer(child, this); - QBoxLayout* layout = new QHBoxLayout(this); + layout->addWidget(container); layout->setMargin(0); setLayout(layout); - // Reset minimum size to avoid unwanted resizes when this function is called for a second time. + // Reset minimum required size to avoid resizing issues on the main window after restarting. setMinimumSize(1, 1); - // Show causes the window to actually be created and the OpenGL context as well, but we don't - // want the widget to be shown yet, so immediately hide it. + // Show causes the window to actually be created and the gl context as well, but we don't want + // the widget to be shown yet, so immediately hide it. show(); hide(); @@ -410,9 +473,17 @@ void GRenderWindow::InitRenderTarget() { OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); OnFramebufferSizeChanged(); - NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(child->width(), child->height())); + NotifyClientAreaSizeChanged(child->GetSize()); BackupGeometry(); + + if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { + if (!LoadOpenGL()) { + return false; + } + } + + return true; } void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { @@ -441,6 +512,113 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal setMinimumSize(minimal_size.first, minimal_size.second); } +bool GRenderWindow::InitializeOpenGL() { + // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, + // WA_DontShowOnScreen, WA_DeleteOnClose + QSurfaceFormat fmt; + fmt.setVersion(4, 3); + fmt.setProfile(QSurfaceFormat::CompatibilityProfile); + fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); + // TODO: expose a setting for buffer value (ie default/single/double/triple) + fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); + shared_context = std::make_unique<QOpenGLContext>(); + shared_context->setFormat(fmt); + shared_context->create(); + context = std::make_unique<QOpenGLContext>(); + context->setShareContext(shared_context.get()); + context->setFormat(fmt); + context->create(); + fmt.setSwapInterval(false); + + child = new GGLWidgetInternal(this, shared_context.get()); + return true; +} + +bool GRenderWindow::InitializeVulkan() { +#ifdef HAS_VULKAN + vk_instance = std::make_unique<QVulkanInstance>(); + vk_instance->setApiVersion(QVersionNumber(1, 1, 0)); + vk_instance->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect); + if (Settings::values.renderer_debug) { + const auto supported_layers{vk_instance->supportedLayers()}; + const bool found = + std::find_if(supported_layers.begin(), supported_layers.end(), [](const auto& layer) { + constexpr const char searched_layer[] = "VK_LAYER_LUNARG_standard_validation"; + return layer.name == searched_layer; + }); + if (found) { + vk_instance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation"); + vk_instance->setExtensions(QByteArrayList() << VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + } + if (!vk_instance->create()) { + QMessageBox::critical( + this, tr("Error while initializing Vulkan 1.1!"), + tr("Your OS doesn't seem to support Vulkan 1.1 instances, or you do not have the " + "latest graphics drivers.")); + return false; + } + + child = new GVKWidgetInternal(this, vk_instance.get()); + return true; +#else + QMessageBox::critical(this, tr("Vulkan not available!"), + tr("yuzu has not been compiled with Vulkan support.")); + return false; +#endif +} + +bool GRenderWindow::LoadOpenGL() { + Core::Frontend::ScopeAcquireWindowContext acquire_context{*this}; + if (!gladLoadGL()) { + QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), + tr("Your GPU may not support OpenGL 4.3, or you do not have the " + "latest graphics driver.")); + return false; + } + + QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); + if (!unsupported_gl_extensions.empty()) { + QMessageBox::critical( + this, 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>Unsupported extensions:<br>") + + unsupported_gl_extensions.join(QStringLiteral("<br>"))); + return false; + } + return true; +} + +QStringList GRenderWindow::GetUnsupportedGLExtensions() const { + QStringList unsupported_ext; + + if (!GLAD_GL_ARB_buffer_storage) + unsupported_ext.append(QStringLiteral("ARB_buffer_storage")); + if (!GLAD_GL_ARB_direct_state_access) + unsupported_ext.append(QStringLiteral("ARB_direct_state_access")); + if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) + unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev")); + if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) + unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge")); + if (!GLAD_GL_ARB_multi_bind) + unsupported_ext.append(QStringLiteral("ARB_multi_bind")); + if (!GLAD_GL_ARB_clip_control) + unsupported_ext.append(QStringLiteral("ARB_clip_control")); + + // Extensions required to support some texture formats. + if (!GLAD_GL_EXT_texture_compression_s3tc) + unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); + if (!GLAD_GL_ARB_texture_compression_rgtc) + unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc")); + if (!GLAD_GL_ARB_depth_buffer_float) + unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); + + for (const QString& ext : unsupported_ext) + LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); + + return unsupported_ext; +} + void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { this->emu_thread = emu_thread; child->DisablePainting(); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 2fc64895f..71a2fa321 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -7,17 +7,28 @@ #include <atomic> #include <condition_variable> #include <mutex> + #include <QImage> #include <QThread> #include <QWidget> + +#include "common/thread.h" #include "core/core.h" #include "core/frontend/emu_window.h" class QKeyEvent; class QScreen; class QTouchEvent; +class QStringList; +class QSurface; +class QOpenGLContext; +#ifdef HAS_VULKAN +class QVulkanInstance; +#endif +class GWidgetInternal; class GGLWidgetInternal; +class GVKWidgetInternal; class GMainWindow; class GRenderWindow; class QSurface; @@ -123,6 +134,9 @@ public: void MakeCurrent() override; void DoneCurrent() override; void PollEvents() override; + bool IsShown() const override; + void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, + void* surface) const override; std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; void ForwardKeyPressEvent(QKeyEvent* event); @@ -142,7 +156,7 @@ public: void OnClientAreaResized(u32 width, u32 height); - void InitRenderTarget(); + bool InitRenderTarget(); void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); @@ -165,10 +179,13 @@ private: void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; - QWidget* container = nullptr; - GGLWidgetInternal* child = nullptr; + bool InitializeOpenGL(); + bool InitializeVulkan(); + bool LoadOpenGL(); + QStringList GetUnsupportedGLExtensions() const; - QByteArray geometry; + QWidget* container = nullptr; + GWidgetInternal* child = nullptr; EmuThread* emu_thread; // Context that backs the GGLWidgetInternal (and will be used by core to render) @@ -177,9 +194,14 @@ private: // current std::unique_ptr<QOpenGLContext> shared_context; +#ifdef HAS_VULKAN + std::unique_ptr<QVulkanInstance> vk_instance; +#endif + /// Temporary storage of the screenshot taken QImage screenshot_image; + QByteArray geometry; bool first_frame = false; protected: diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 59918847a..280d81ba9 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -624,6 +624,10 @@ void Config::ReadPathValues() { void Config::ReadRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); + Settings::values.renderer_backend = + static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt()); + Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool(); + Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt(); Settings::values.resolution_factor = ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); Settings::values.use_frame_limit = @@ -1056,6 +1060,9 @@ void Config::SavePathValues() { void Config::SaveRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); + WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0); + WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false); + WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); WriteSetting(QStringLiteral("resolution_factor"), static_cast<double>(Settings::values.resolution_factor), 1.0); WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 90c1f9459..9631059c7 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -36,6 +36,8 @@ void ConfigureDebug::SetConfiguration() { ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); ui->reporting_services->setChecked(Settings::values.reporting_services); ui->quest_flag->setChecked(Settings::values.quest_flag); + ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); } void ConfigureDebug::ApplyConfiguration() { @@ -46,6 +48,7 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); Settings::values.reporting_services = ui->reporting_services->isChecked(); Settings::values.quest_flag = ui->quest_flag->isChecked(); + Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); Debugger::ToggleConsole(); Log::Filter filter; filter.ParseFilterString(Settings::values.log_filter); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index ce49569bb..e028c4c80 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>400</width> - <height>474</height> + <height>467</height> </rect> </property> <property name="windowTitle"> @@ -103,6 +103,80 @@ </item> </layout> </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Homebrew</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Arguments String</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="homebrew_args_edit"/> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_4"> + <property name="title"> + <string>Graphics</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <widget class="QCheckBox" name="enable_graphics_debugging"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="whatsThis"> + <string>When checked, the graphics API enters in a slower debugging mode</string> + </property> + <property name="text"> + <string>Enable Graphics Debugging</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_5"> + <property name="title"> + <string>Dump</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <widget class="QCheckBox" name="dump_decompressed_nso"> + <property name="whatsThis"> + <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string> + </property> + <property name="text"> + <string>Dump Decompressed NSOs</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="dump_exefs"> + <property name="whatsThis"> + <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string> + </property> + <property name="text"> + <string>Dump ExeFS</string> + </property> + </widget> + </item> <item> <widget class="QCheckBox" name="reporting_services"> <property name="text"> @@ -129,11 +203,11 @@ </widget> </item> <item> - <widget class="QGroupBox" name="groupBox_5"> + <widget class="QGroupBox" name="groupBox_6"> <property name="title"> <string>Advanced</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> + <layout class="QVBoxLayout" name="verticalLayout_7"> <item> <widget class="QCheckBox" name="quest_flag"> <property name="text"> @@ -145,29 +219,6 @@ </widget> </item> <item> - <widget class="QGroupBox" name="groupBox_3"> - <property name="title"> - <string>Homebrew</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_5"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <item> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>Arguments String</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="homebrew_args_edit"/> - </item> - </layout> - </item> - </layout> - </widget> - </item> - <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -185,6 +236,19 @@ </item> </layout> </widget> + <tabstops> + <tabstop>toggle_gdbstub</tabstop> + <tabstop>gdbport_spinbox</tabstop> + <tabstop>log_filter_edit</tabstop> + <tabstop>toggle_console</tabstop> + <tabstop>open_log_button</tabstop> + <tabstop>homebrew_args_edit</tabstop> + <tabstop>enable_graphics_debugging</tabstop> + <tabstop>dump_decompressed_nso</tabstop> + <tabstop>dump_exefs</tabstop> + <tabstop>reporting_services</tabstop> + <tabstop>quest_flag</tabstop> + </tabstops> <resources/> <connections> <connection> diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 2c9e322c9..f57a24e36 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -3,6 +3,13 @@ // Refer to the license.txt file included. #include <QColorDialog> +#include <QComboBox> +#ifdef HAS_VULKAN +#include <QVulkanInstance> +#endif + +#include "common/common_types.h" +#include "common/logging/log.h" #include "core/core.h" #include "core/settings.h" #include "ui_configure_graphics.h" @@ -51,10 +58,18 @@ Resolution FromResolutionFactor(float factor) { ConfigureGraphics::ConfigureGraphics(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureGraphics) { + vulkan_device = Settings::values.vulkan_device; + RetrieveVulkanDevices(); + ui->setupUi(this); SetConfiguration(); + connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, + [this] { UpdateDeviceComboBox(); }); + connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, + [this](int device) { UpdateDeviceSelection(device); }); + connect(ui->bg_button, &QPushButton::clicked, this, [this] { const QColor new_bg_color = QColorDialog::getColor(bg_color); if (!new_bg_color.isValid()) { @@ -64,11 +79,22 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) }); } +void ConfigureGraphics::UpdateDeviceSelection(int device) { + if (device == -1) { + return; + } + if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) { + vulkan_device = device; + } +} + ConfigureGraphics::~ConfigureGraphics() = default; void ConfigureGraphics::SetConfiguration() { const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); + ui->api->setEnabled(runtime_lock); + ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend)); ui->resolution_factor_combobox->setCurrentIndex( static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); ui->use_disk_shader_cache->setEnabled(runtime_lock); @@ -80,9 +106,12 @@ void ConfigureGraphics::SetConfiguration() { ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue)); + UpdateDeviceComboBox(); } void ConfigureGraphics::ApplyConfiguration() { + Settings::values.renderer_backend = GetCurrentGraphicsBackend(); + Settings::values.vulkan_device = vulkan_device; Settings::values.resolution_factor = ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); @@ -116,3 +145,68 @@ void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) { const QIcon color_icon(pixmap); ui->bg_button->setIcon(color_icon); } + +void ConfigureGraphics::UpdateDeviceComboBox() { + ui->device->clear(); + + bool enabled = false; + switch (GetCurrentGraphicsBackend()) { + case Settings::RendererBackend::OpenGL: + ui->device->addItem(tr("OpenGL Graphics Device")); + enabled = false; + break; + case Settings::RendererBackend::Vulkan: + for (const auto device : vulkan_devices) { + ui->device->addItem(device); + } + ui->device->setCurrentIndex(vulkan_device); + enabled = !vulkan_devices.empty(); + break; + } + ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn()); +} + +void ConfigureGraphics::RetrieveVulkanDevices() { +#ifdef HAS_VULKAN + QVulkanInstance instance; + instance.setApiVersion(QVersionNumber(1, 1, 0)); + if (!instance.create()) { + LOG_INFO(Frontend, "Vulkan 1.1 not available"); + return; + } + const auto vkEnumeratePhysicalDevices{reinterpret_cast<PFN_vkEnumeratePhysicalDevices>( + instance.getInstanceProcAddr("vkEnumeratePhysicalDevices"))}; + if (vkEnumeratePhysicalDevices == nullptr) { + LOG_INFO(Frontend, "Failed to get pointer to vkEnumeratePhysicalDevices"); + return; + } + u32 physical_device_count; + if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, nullptr) != + VK_SUCCESS) { + LOG_INFO(Frontend, "Failed to get physical devices count"); + return; + } + std::vector<VkPhysicalDevice> physical_devices(physical_device_count); + if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, + physical_devices.data()) != VK_SUCCESS) { + LOG_INFO(Frontend, "Failed to get physical devices"); + return; + } + + const auto vkGetPhysicalDeviceProperties{reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>( + instance.getInstanceProcAddr("vkGetPhysicalDeviceProperties"))}; + if (vkGetPhysicalDeviceProperties == nullptr) { + LOG_INFO(Frontend, "Failed to get pointer to vkGetPhysicalDeviceProperties"); + return; + } + for (const auto physical_device : physical_devices) { + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(physical_device, &properties); + vulkan_devices.push_back(QString::fromUtf8(properties.deviceName)); + } +#endif +} + +Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { + return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); +} diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index fae28d98e..7e0596d9c 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -5,7 +5,10 @@ #pragma once #include <memory> +#include <vector> +#include <QString> #include <QWidget> +#include "core/settings.h" namespace Ui { class ConfigureGraphics; @@ -27,7 +30,16 @@ private: void SetConfiguration(); void UpdateBackgroundColorButton(QColor color); + void UpdateDeviceComboBox(); + void UpdateDeviceSelection(int device); + + void RetrieveVulkanDevices(); + + Settings::RendererBackend GetCurrentGraphicsBackend() const; std::unique_ptr<Ui::ConfigureGraphics> ui; QColor bg_color; + + std::vector<QString> vulkan_devices; + u32 vulkan_device{}; }; diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 0309ee300..e24372204 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -7,21 +7,69 @@ <x>0</x> <y>0</y> <width>400</width> - <height>300</height> + <height>321</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> + <layout class="QVBoxLayout" name="verticalLayout_1"> <item> - <layout class="QVBoxLayout" name="verticalLayout_3"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>API Settings</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>API:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="api"> + <item> + <property name="text"> + <string notr="true">OpenGL</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">Vulkan</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Device:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="device"/> + </item> + </layout> + </item> + </layout> + </widget> + </item> <item> <widget class="QGroupBox" name="groupBox"> <property name="title"> - <string>Graphics</string> + <string>Graphics Settings</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> + <layout class="QVBoxLayout" name="verticalLayout_4"> <item> <widget class="QCheckBox" name="use_disk_shader_cache"> <property name="text"> @@ -30,16 +78,16 @@ </widget> </item> <item> - <widget class="QCheckBox" name="use_accurate_gpu_emulation"> + <widget class="QCheckBox" name="use_asynchronous_gpu_emulation"> <property name="text"> - <string>Use accurate GPU emulation (slow)</string> + <string>Use asynchronous GPU emulation</string> </property> </widget> </item> <item> - <widget class="QCheckBox" name="use_asynchronous_gpu_emulation"> + <widget class="QCheckBox" name="use_accurate_gpu_emulation"> <property name="text"> - <string>Use asynchronous GPU emulation</string> + <string>Use accurate GPU emulation (slow)</string> </property> </widget> </item> @@ -51,11 +99,11 @@ </widget> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QLabel" name="label"> <property name="text"> - <string>Internal Resolution</string> + <string>Internal Resolution:</string> </property> </widget> </item> @@ -91,7 +139,7 @@ </layout> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout_6"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> <widget class="QLabel" name="bg_label"> <property name="text"> diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 67c9a7c6d..96dec50e2 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -236,6 +236,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i widget->setVisible(false); analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; + analog_map_deadzone = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone}; + analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone}; for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { auto* const button = button_map[button_id]; @@ -326,6 +328,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i InputCommon::Polling::DeviceType::Analog); } }); + connect(analog_map_deadzone[analog_id], &QSlider::valueChanged, [=] { + const float deadzone = analog_map_deadzone[analog_id]->value() / 100.0f; + analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1").arg(deadzone)); + analogs_param[analog_id].Set("deadzone", deadzone); + }); } connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); @@ -484,7 +491,7 @@ void ConfigureInputPlayer::ClearAll() { continue; } - analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); + analogs_param[analog_id].Clear(); } } @@ -508,6 +515,23 @@ void ConfigureInputPlayer::UpdateButtonLabels() { AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); } analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); + + auto& param = analogs_param[analog_id]; + auto* const analog_deadzone_slider = analog_map_deadzone[analog_id]; + auto* const analog_deadzone_label = analog_map_deadzone_label[analog_id]; + + if (param.Has("engine") && param.Get("engine", "") == "sdl") { + if (!param.Has("deadzone")) { + param.Set("deadzone", 0.1f); + } + + analog_deadzone_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100)); + analog_deadzone_slider->setVisible(true); + analog_deadzone_label->setVisible(true); + } else { + analog_deadzone_slider->setVisible(false); + analog_deadzone_label->setVisible(false); + } } } diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index c66027651..045704e47 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -97,6 +97,8 @@ private: /// Analog inputs are also represented each with a single button, used to configure with an /// actual analog stick std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; + std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone; + std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label; static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 42db020be..1556481d0 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui @@ -170,6 +170,44 @@ </item> </layout> </item> + <item row="4" column="0" colspan="2"> + <layout class="QVBoxLayout" name="sliderRStickDeadzoneVerticalLayout"> + <item> + <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout"> + <item> + <widget class="QLabel" name="labelRStickDeadzone"> + <property name="text"> + <string>Deadzone: 0</string> + </property> + <property name="alignment"> + <enum>Qt::AlignHCenter</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QSlider" name="sliderRStickDeadzone"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </item> + <item row="5" column="0"> + <spacer name="RStick_verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> </layout> </widget> </item> @@ -745,6 +783,47 @@ </item> </layout> </item> + <item row="5" column="1" colspan="2"> + <layout class="QVBoxLayout" name="sliderLStickDeadzoneVerticalLayout"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout"> + <item> + <widget class="QLabel" name="labelLStickDeadzone"> + <property name="text"> + <string>Deadzone: 0</string> + </property> + <property name="alignment"> + <enum>Qt::AlignHCenter</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QSlider" name="sliderLStickDeadzone"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </item> + <item row="6" column="1"> + <spacer name="LStick_verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> </layout> </widget> </item> diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b5dd3e0d6..54ca2dc1d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -454,7 +454,6 @@ void GMainWindow::InitializeWidgets() { // Create status bar message_label = new QLabel(); // Configured separately for left alignment - message_label->setVisible(false); message_label->setFrameStyle(QFrame::NoFrame); message_label->setContentsMargins(4, 0, 4, 0); message_label->setAlignment(Qt::AlignLeft); @@ -476,8 +475,73 @@ void GMainWindow::InitializeWidgets() { label->setVisible(false); label->setFrameStyle(QFrame::NoFrame); label->setContentsMargins(4, 0, 4, 0); - statusBar()->addPermanentWidget(label, 0); + statusBar()->addPermanentWidget(label); } + + // Setup Dock button + dock_status_button = new QPushButton(); + dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); + dock_status_button->setFocusPolicy(Qt::NoFocus); + connect(dock_status_button, &QPushButton::clicked, [&] { + Settings::values.use_docked_mode = !Settings::values.use_docked_mode; + dock_status_button->setChecked(Settings::values.use_docked_mode); + OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode); + }); + dock_status_button->setText(tr("DOCK")); + dock_status_button->setCheckable(true); + dock_status_button->setChecked(Settings::values.use_docked_mode); + statusBar()->insertPermanentWidget(0, dock_status_button); + + // Setup ASync button + async_status_button = new QPushButton(); + async_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); + async_status_button->setFocusPolicy(Qt::NoFocus); + connect(async_status_button, &QPushButton::clicked, [&] { + if (emulation_running) { + return; + } + Settings::values.use_asynchronous_gpu_emulation = + !Settings::values.use_asynchronous_gpu_emulation; + async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); + Settings::Apply(); + }); + async_status_button->setText(tr("ASYNC")); + async_status_button->setCheckable(true); + async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); + statusBar()->insertPermanentWidget(0, async_status_button); + + // Setup Renderer API button + renderer_status_button = new QPushButton(); + renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton")); + renderer_status_button->setCheckable(true); + renderer_status_button->setFocusPolicy(Qt::NoFocus); + connect(renderer_status_button, &QPushButton::toggled, [=](bool checked) { + renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL")); + }); + renderer_status_button->toggle(); + +#ifndef HAS_VULKAN + renderer_status_button->setChecked(false); + renderer_status_button->setCheckable(false); + renderer_status_button->setDisabled(true); +#else + renderer_status_button->setChecked(Settings::values.renderer_backend == + Settings::RendererBackend::Vulkan); + connect(renderer_status_button, &QPushButton::clicked, [=] { + if (emulation_running) { + return; + } + if (renderer_status_button->isChecked()) { + Settings::values.renderer_backend = Settings::RendererBackend::Vulkan; + } else { + Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; + } + + Settings::Apply(); + }); +#endif // HAS_VULKAN + statusBar()->insertPermanentWidget(0, renderer_status_button); + statusBar()->setVisible(true); setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}")); } @@ -640,6 +704,7 @@ void GMainWindow::InitializeHotkeys() { Settings::values.use_docked_mode = !Settings::values.use_docked_mode; OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode); + dock_status_button->setChecked(Settings::values.use_docked_mode); }); } @@ -806,70 +871,12 @@ void GMainWindow::AllowOSSleep() { #endif } -QStringList GMainWindow::GetUnsupportedGLExtensions() { - QStringList unsupported_ext; - - if (!GLAD_GL_ARB_buffer_storage) { - unsupported_ext.append(QStringLiteral("ARB_buffer_storage")); - } - if (!GLAD_GL_ARB_direct_state_access) { - unsupported_ext.append(QStringLiteral("ARB_direct_state_access")); - } - if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) { - unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev")); - } - if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) { - unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge")); - } - if (!GLAD_GL_ARB_multi_bind) { - unsupported_ext.append(QStringLiteral("ARB_multi_bind")); - } - if (!GLAD_GL_ARB_clip_control) { - unsupported_ext.append(QStringLiteral("ARB_clip_control")); - } - - // Extensions required to support some texture formats. - if (!GLAD_GL_EXT_texture_compression_s3tc) { - unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); - } - if (!GLAD_GL_ARB_texture_compression_rgtc) { - unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc")); - } - if (!GLAD_GL_ARB_depth_buffer_float) { - unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); - } - - for (const QString& ext : unsupported_ext) { - LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); - } - - return unsupported_ext; -} - bool GMainWindow::LoadROM(const QString& filename) { // Shutdown previous session if the emu thread is still active... if (emu_thread != nullptr) ShutdownGame(); - render_window->InitRenderTarget(); - - { - Core::Frontend::ScopeAcquireWindowContext acquire_context{*render_window}; - if (!gladLoadGL()) { - QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"), - tr("Your GPU may not support OpenGL 4.3, or you do not " - "have the latest graphics driver.")); - return false; - } - } - - const QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); - if (!unsupported_gl_extensions.empty()) { - QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"), - tr("Your GPU may not support one or more required OpenGL" - "extensions. Please ensure you have the latest graphics " - "driver.<br><br>Unsupported extensions:<br>") + - unsupported_gl_extensions.join(QStringLiteral("<br>"))); + if (!render_window->InitRenderTarget()) { return false; } @@ -980,7 +987,9 @@ void GMainWindow::BootGame(const QString& filename) { // Create and start the emulation thread emu_thread = std::make_unique<EmuThread>(render_window); emit EmulationStarting(emu_thread.get()); - render_window->moveContext(); + if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { + render_window->moveContext(); + } emu_thread->start(); connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); @@ -1000,6 +1009,8 @@ void GMainWindow::BootGame(const QString& filename) { game_list_placeholder->hide(); } status_bar_update_timer.start(2000); + async_status_button->setDisabled(true); + renderer_status_button->setDisabled(true); const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); @@ -1065,10 +1076,13 @@ void GMainWindow::ShutdownGame() { // Disable status bar updates status_bar_update_timer.stop(); - message_label->setVisible(false); emu_speed_label->setVisible(false); game_fps_label->setVisible(false); emu_frametime_label->setVisible(false); + async_status_button->setEnabled(true); +#ifdef HAS_VULKAN + renderer_status_button->setEnabled(true); +#endif emulation_running = false; @@ -1836,6 +1850,13 @@ void GMainWindow::OnConfigure() { } config->Save(); + + dock_status_button->setChecked(Settings::values.use_docked_mode); + async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); +#ifdef HAS_VULKAN + renderer_status_button->setChecked(Settings::values.renderer_backend == + Settings::RendererBackend::Vulkan); +#endif } void GMainWindow::OnLoadAmiibo() { @@ -2028,7 +2049,6 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det if (emu_thread) { emu_thread->SetRunning(true); message_label->setText(status_message); - message_label->setVisible(true); } } } @@ -2195,6 +2215,18 @@ void GMainWindow::closeEvent(QCloseEvent* event) { QWidget::closeEvent(event); } +void GMainWindow::keyPressEvent(QKeyEvent* event) { + if (render_window) { + render_window->ForwardKeyPressEvent(event); + } +} + +void GMainWindow::keyReleaseEvent(QKeyEvent* event) { + if (render_window) { + render_window->ForwardKeyReleaseEvent(event); + } +} + static bool IsSingleFileDropEvent(QDropEvent* event) { const QMimeData* mimeData = event->mimeData(); return mimeData->hasUrls() && mimeData->urls().length() == 1; @@ -2227,18 +2259,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { event->acceptProposedAction(); } -void GMainWindow::keyPressEvent(QKeyEvent* event) { - if (render_window) { - render_window->ForwardKeyPressEvent(event); - } -} - -void GMainWindow::keyReleaseEvent(QKeyEvent* event) { - if (render_window) { - render_window->ForwardKeyReleaseEvent(event); - } -} - bool GMainWindow::ConfirmChangeGame() { if (emu_thread == nullptr) return true; @@ -2290,8 +2310,16 @@ void GMainWindow::UpdateUITheme() { QStringList theme_paths(default_theme_paths); if (is_default_theme || current_theme.isEmpty()) { - qApp->setStyleSheet({}); - setStyleSheet({}); + const QString theme_uri(QStringLiteral(":default/style.qss")); + QFile f(theme_uri); + if (f.open(QFile::ReadOnly | QFile::Text)) { + QTextStream ts(&f); + qApp->setStyleSheet(ts.readAll()); + setStyleSheet(ts.readAll()); + } else { + qApp->setStyleSheet({}); + setStyleSheet({}); + } theme_paths.append(default_icons); QIcon::setThemeName(default_icons); } else { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index a56f9a981..8eba2172c 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -27,6 +27,7 @@ class LoadingScreen; class MicroProfileDialog; class ProfilerWidget; class QLabel; +class QPushButton; class WaitTreeWidget; enum class GameListOpenTarget; class GameListPlaceholder; @@ -130,7 +131,6 @@ private: void PreventOSSleep(); void AllowOSSleep(); - QStringList GetUnsupportedGLExtensions(); bool LoadROM(const QString& filename); void BootGame(const QString& filename); void ShutdownGame(); @@ -229,6 +229,9 @@ private: QLabel* emu_speed_label = nullptr; QLabel* game_fps_label = nullptr; QLabel* emu_frametime_label = nullptr; + QPushButton* async_status_button = nullptr; + QPushButton* renderer_status_button = nullptr; + QPushButton* dock_status_button = nullptr; QTimer status_bar_update_timer; std::unique_ptr<Config> config; |