diff options
Diffstat (limited to 'src/yuzu')
-rw-r--r-- | src/yuzu/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/yuzu/about_dialog.cpp | 9 | ||||
-rw-r--r-- | src/yuzu/bootmanager.cpp | 412 | ||||
-rw-r--r-- | src/yuzu/bootmanager.h | 39 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_graphics.cpp | 42 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_input_player.cpp | 15 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_input_player.ui | 72 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_input_simple.cpp | 9 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_mouse_advanced.cpp | 15 | ||||
-rw-r--r-- | src/yuzu/game_list.cpp | 6 | ||||
-rw-r--r-- | src/yuzu/game_list_p.h | 24 | ||||
-rw-r--r-- | src/yuzu/main.cpp | 11 |
12 files changed, 302 insertions, 356 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index d34b47b3f..8b9404718 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -150,6 +150,10 @@ target_link_libraries(yuzu PRIVATE common core input_common video_core) target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) +if (ENABLE_VULKAN AND NOT WIN32) + target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) +endif() + target_compile_definitions(yuzu PRIVATE # Use QStringBuilder for string concatenation to reduce # the overall number of temporary strings created. diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp index d39b3f07a..695b2ef5f 100644 --- a/src/yuzu/about_dialog.cpp +++ b/src/yuzu/about_dialog.cpp @@ -3,15 +3,22 @@ // Refer to the license.txt file included. #include <QIcon> +#include <fmt/format.h> #include "common/scm_rev.h" #include "ui_aboutdialog.h" #include "yuzu/about_dialog.h" AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) { + const auto build_id = std::string(Common::g_build_id); + const auto fmt = std::string(Common::g_title_bar_format_idle); + const auto yuzu_build_version = + fmt::format(fmt.empty() ? "yuzu Development Build" : fmt, std::string{}, std::string{}, + std::string{}, std::string{}, std::string{}, build_id); + ui->setupUi(this); ui->labelLogo->setPixmap(QIcon::fromTheme(QStringLiteral("yuzu")).pixmap(200)); ui->labelBuildInfo->setText(ui->labelBuildInfo->text().arg( - QString::fromUtf8(Common::g_build_fullname), QString::fromUtf8(Common::g_scm_branch), + QString::fromStdString(yuzu_build_version), QString::fromUtf8(Common::g_scm_branch), QString::fromUtf8(Common::g_scm_desc), QString::fromUtf8(Common::g_build_date).left(10))); } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index c3dbb1a88..1cac2f942 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -10,15 +10,13 @@ #include <QMessageBox> #include <QOffscreenSurface> #include <QOpenGLContext> -#include <QOpenGLFunctions> -#include <QOpenGLFunctions_4_3_Core> -#include <QOpenGLWindow> #include <QPainter> #include <QScreen> #include <QStringList> #include <QWindow> -#ifdef HAS_VULKAN -#include <QVulkanWindow> + +#if !defined(WIN32) && HAS_VULKAN +#include <qpa/qplatformnativeinterface.h> #endif #include <fmt/format.h> @@ -29,7 +27,6 @@ #include "common/scope_exit.h" #include "core/core.h" #include "core/frontend/framebuffer_layout.h" -#include "core/frontend/scope_acquire_context.h" #include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" @@ -39,26 +36,16 @@ #include "yuzu/bootmanager.h" #include "yuzu/main.h" -EmuThread::EmuThread(GRenderWindow& window) - : shared_context{window.CreateSharedContext()}, - context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context - : window} {} +EmuThread::EmuThread() = default; EmuThread::~EmuThread() = default; -static GMainWindow* GetMainWindow() { - for (QWidget* w : qApp->topLevelWidgets()) { - if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) { - return main; - } - } - return nullptr; -} - void EmuThread::run() { MicroProfileOnThreadCreate("EmuThread"); - Core::Frontend::ScopeAcquireContext acquire_context{context}; + // Main process has been loaded. Make the context current to this thread and begin GPU and CPU + // execution. + Core::System::GetInstance().GPU().Start(); emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); @@ -111,163 +98,190 @@ void EmuThread::run() { #endif } -class GGLContext : public Core::Frontend::GraphicsContext { +class OpenGLSharedContext : public Core::Frontend::GraphicsContext { public: - explicit GGLContext(QOpenGLContext* shared_context) - : context(new QOpenGLContext(shared_context->parent())), - surface(new QOffscreenSurface(nullptr)) { + /// Create the original context that should be shared from + explicit OpenGLSharedContext(QSurface* surface) : surface(surface) { + QSurfaceFormat format; + format.setVersion(4, 3); + format.setProfile(QSurfaceFormat::CompatibilityProfile); + format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); + // 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 = shared_context->format(); - format.setSwapInterval(0); + auto format = share_context->format(); + format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0); - context->setShareContext(shared_context); + context = std::make_unique<QOpenGLContext>(); + context->setShareContext(share_context); context->setFormat(format); - context->create(); - surface->setParent(shared_context->parent()); - surface->setFormat(format); - surface->create(); + 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 { - context->makeCurrent(surface); + if (is_current) { + return; + } + is_current = context->makeCurrent(surface); } void DoneCurrent() override { + if (!is_current) { + return; + } context->doneCurrent(); + is_current = false; } -private: - QOpenGLContext* context; - QOffscreenSurface* surface; -}; - -class ChildRenderWindow : public QWindow { -public: - ChildRenderWindow(QWindow* parent, QWidget* event_handler) - : QWindow{parent}, event_handler{event_handler} {} - - virtual ~ChildRenderWindow() = default; - - virtual void Present() = 0; - -protected: - bool event(QEvent* event) override { - switch (event->type()) { - case QEvent::UpdateRequest: - Present(); - return true; - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::MouseMove: - case QEvent::KeyPress: - case QEvent::KeyRelease: - case QEvent::FocusIn: - case QEvent::FocusOut: - case QEvent::FocusAboutToChange: - case QEvent::Enter: - case QEvent::Leave: - case QEvent::Wheel: - case QEvent::TabletMove: - case QEvent::TabletPress: - case QEvent::TabletRelease: - case QEvent::TabletEnterProximity: - case QEvent::TabletLeaveProximity: - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - case QEvent::InputMethodQuery: - case QEvent::TouchCancel: - return QCoreApplication::sendEvent(event_handler, event); - case QEvent::Drop: - GetMainWindow()->DropAction(static_cast<QDropEvent*>(event)); - return true; - case QEvent::DragResponse: - case QEvent::DragEnter: - case QEvent::DragLeave: - case QEvent::DragMove: - GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event)); - return true; - default: - return QWindow::event(event); - } + QOpenGLContext* GetShareContext() { + return context.get(); } - void exposeEvent(QExposeEvent* event) override { - QWindow::requestUpdate(); - QWindow::exposeEvent(event); + const QOpenGLContext* GetShareContext() const { + return context.get(); } private: - QWidget* event_handler{}; + // 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; + bool is_current = false; }; -class OpenGLWindow final : public ChildRenderWindow { +class DummyContext : public Core::Frontend::GraphicsContext {}; + +class RenderWidget : public QWidget { public: - OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) - : ChildRenderWindow{parent, event_handler}, - context(new QOpenGLContext(shared_context->parent())) { + explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { + setAttribute(Qt::WA_NativeWindow); + setAttribute(Qt::WA_PaintOnScreen); + } - // disable vsync for any shared contexts - auto format = shared_context->format(); - format.setSwapInterval(Settings::values.use_vsync ? 1 : 0); - this->setFormat(format); + virtual ~RenderWidget() = default; - context->setShareContext(shared_context); - context->setScreen(this->screen()); - context->setFormat(format); - context->create(); + /// Called on the UI thread when this Widget is ready to draw + /// Dervied classes can override this to draw the latest frame. + virtual void Present() {} - setSurfaceType(QWindow::OpenGLSurface); + void paintEvent(QPaintEvent* event) override { + Present(); + update(); + } - // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, - // WA_DontShowOnScreen, WA_DeleteOnClose + QPaintEngine* paintEngine() const override { + return nullptr; } - ~OpenGLWindow() override { - context->doneCurrent(); +private: + GRenderWindow* render_window; +}; + +class OpenGLRenderWidget : public RenderWidget { +public: + explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { + windowHandle()->setSurfaceType(QWindow::OpenGLSurface); + } + + void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) { + context = std::move(context_); } void Present() override { - if (!isExposed()) { + if (!isVisible()) { return; } - context->makeCurrent(this); - Core::System::GetInstance().Renderer().TryPresent(100); - context->swapBuffers(this); - auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>(); - f->glFinish(); - QWindow::requestUpdate(); + context->MakeCurrent(); + if (Core::System::GetInstance().Renderer().TryPresent(100)) { + context->SwapBuffers(); + glFinish(); + } } private: - QOpenGLContext* context{}; + std::unique_ptr<Core::Frontend::GraphicsContext> context{}; }; #ifdef HAS_VULKAN -class VulkanWindow final : public ChildRenderWindow { +class VulkanRenderWidget : public RenderWidget { public: - VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance) - : ChildRenderWindow{parent, event_handler} { - setSurfaceType(QSurface::SurfaceType::VulkanSurface); - setVulkanInstance(instance); + explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { + windowHandle()->setSurfaceType(QWindow::VulkanSurface); } +}; +#endif - ~VulkanWindow() override = default; +static Core::Frontend::WindowSystemType GetWindowSystemType() { + // Determine WSI type based on Qt platform. + QString platform_name = QGuiApplication::platformName(); + if (platform_name == QStringLiteral("windows")) + return Core::Frontend::WindowSystemType::Windows; + else if (platform_name == QStringLiteral("xcb")) + return Core::Frontend::WindowSystemType::X11; + else if (platform_name == QStringLiteral("wayland")) + return Core::Frontend::WindowSystemType::Wayland; - void Present() override { - // TODO(bunnei): ImplementMe - } + LOG_CRITICAL(Frontend, "Unknown Qt platform!"); + return Core::Frontend::WindowSystemType::Windows; +} -private: - QWidget* event_handler{}; -}; +static Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) { + Core::Frontend::EmuWindow::WindowSystemInfo wsi; + wsi.type = GetWindowSystemType(); + +#ifdef HAS_VULKAN + // Our Win32 Qt external doesn't have the private API. +#if defined(WIN32) || defined(__APPLE__) + wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr; +#else + QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); + wsi.display_connection = pni->nativeResourceForWindow("display", window); + if (wsi.type == Core::Frontend::WindowSystemType::Wayland) + wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr; + else + wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr; +#endif + wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f; #endif -GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) - : QWidget(parent_), emu_thread(emu_thread) { + return wsi; +} + +GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_) + : QWidget(parent_), emu_thread(emu_thread_) { setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") .arg(QString::fromUtf8(Common::g_build_name), QString::fromUtf8(Common::g_scm_branch), @@ -278,26 +292,13 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) setLayout(layout); InputCommon::Init(); - GMainWindow* parent = GetMainWindow(); - connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); + connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete); } GRenderWindow::~GRenderWindow() { InputCommon::Shutdown(); } -void GRenderWindow::MakeCurrent() { - if (core_context) { - core_context->MakeCurrent(); - } -} - -void GRenderWindow::DoneCurrent() { - if (core_context) { - core_context->DoneCurrent(); - } -} - void GRenderWindow::PollEvents() { if (!first_frame) { first_frame = true; @@ -309,21 +310,6 @@ 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_window); - - 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), @@ -367,7 +353,7 @@ qreal GRenderWindow::windowPixelRatio() const { return devicePixelRatio(); } -std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { +std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const { const qreal pixel_ratio = windowPixelRatio(); 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}))}; @@ -387,8 +373,10 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { } void GRenderWindow::mousePressEvent(QMouseEvent* event) { - if (event->source() == Qt::MouseEventSynthesizedBySystem) - return; // touch input is handled in TouchBeginEvent + // touch input is handled in TouchBeginEvent + if (event->source() == Qt::MouseEventSynthesizedBySystem) { + return; + } auto pos = event->pos(); if (event->button() == Qt::LeftButton) { @@ -400,8 +388,10 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { } void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { - if (event->source() == Qt::MouseEventSynthesizedBySystem) - return; // touch input is handled in TouchUpdateEvent + // touch input is handled in TouchUpdateEvent + if (event->source() == Qt::MouseEventSynthesizedBySystem) { + return; + } auto pos = event->pos(); const auto [x, y] = ScaleTouch(pos); @@ -410,13 +400,16 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { } void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { - if (event->source() == Qt::MouseEventSynthesizedBySystem) - return; // touch input is handled in TouchEndEvent + // touch input is handled in TouchEndEvent + if (event->source() == Qt::MouseEventSynthesizedBySystem) { + return; + } - if (event->button() == Qt::LeftButton) + if (event->button() == Qt::LeftButton) { this->TouchReleased(); - else if (event->button() == Qt::RightButton) + } else if (event->button() == Qt::RightButton) { InputCommon::GetMotionEmu()->EndTilt(); + } } void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { @@ -474,9 +467,13 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) { std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { - return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext()); + 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_widget->windowHandle()); } - return {}; + return std::make_unique<DummyContext>(); } bool GRenderWindow::InitRenderTarget() { @@ -497,14 +494,14 @@ bool GRenderWindow::InitRenderTarget() { break; } + // Update the Window System information with the new render target + window_info = GetWindowSystemInfo(child_widget->windowHandle()); + + child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); + layout()->addWidget(child_widget); // 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 gl context as well, but we don't want - // the widget to be shown yet, so immediately hide it. - show(); - hide(); - resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); @@ -523,9 +520,10 @@ bool GRenderWindow::InitRenderTarget() { void GRenderWindow::ReleaseRenderTarget() { if (child_widget) { layout()->removeWidget(child_widget); - delete child_widget; + child_widget->deleteLater(); child_widget = nullptr; } + main_context.reset(); } void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { @@ -557,60 +555,23 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal 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); - fmt.setSwapInterval(0); - QSurfaceFormat::setDefaultFormat(fmt); - - GMainWindow* parent = GetMainWindow(); - QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; - child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext()); - child_window->create(); - child_widget = createWindowContainer(child_window, this); - child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); - layout()->addWidget(child_widget); - - core_context = CreateSharedContext(); + auto child = new OpenGLRenderWidget(this); + child_widget = child; + child_widget->windowHandle()->create(); + auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle()); + main_context = context; + child->SetContext( + std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle())); 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; - } - - GMainWindow* parent = GetMainWindow(); - QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; - child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get()); - child_window->create(); - child_widget = createWindowContainer(child_window, this); - child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); - layout()->addWidget(child_widget); + auto child = new VulkanRenderWidget(this); + child_widget = child; + child_widget->windowHandle()->create(); + main_context = std::make_unique<DummyContext>(); return true; #else @@ -621,7 +582,8 @@ bool GRenderWindow::InitializeVulkan() { } bool GRenderWindow::LoadOpenGL() { - Core::Frontend::ScopeAcquireContext acquire_context{*this}; + auto context = CreateSharedContext(); + auto scope = context->Acquire(); 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 " diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 79b030304..3626604ca 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -18,15 +18,10 @@ #include "core/frontend/emu_window.h" class GRenderWindow; +class GMainWindow; class QKeyEvent; -class QScreen; class QTouchEvent; class QStringList; -class QSurface; -class QOpenGLContext; -#ifdef HAS_VULKAN -class QVulkanInstance; -#endif namespace VideoCore { enum class LoadCallbackStage; @@ -36,7 +31,7 @@ class EmuThread final : public QThread { Q_OBJECT public: - explicit EmuThread(GRenderWindow& window); + explicit EmuThread(); ~EmuThread() override; /** @@ -90,12 +85,6 @@ private: std::mutex running_mutex; std::condition_variable running_cv; - /// Only used in asynchronous GPU mode - std::unique_ptr<Core::Frontend::GraphicsContext> shared_context; - - /// This is shared_context in asynchronous GPU mode, core_context in synchronous GPU mode - Core::Frontend::GraphicsContext& context; - signals: /** * Emitted when the CPU has halted execution @@ -124,16 +113,12 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { Q_OBJECT public: - GRenderWindow(QWidget* parent, EmuThread* emu_thread); + GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); ~GRenderWindow() override; // EmuWindow implementation. - 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 BackupGeometry(); @@ -165,6 +150,8 @@ public: void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); + std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; + public slots: void OnEmulationStarting(EmuThread* emu_thread); void OnEmulationStopping(); @@ -176,7 +163,6 @@ signals: void FirstFrameDisplayed(); private: - std::pair<u32, u32> ScaleTouch(QPointF pos) const; void TouchBeginEvent(const QTouchEvent* event); void TouchUpdateEvent(const QTouchEvent* event); void TouchEndEvent(); @@ -190,23 +176,16 @@ private: EmuThread* emu_thread; - std::unique_ptr<GraphicsContext> core_context; - -#ifdef HAS_VULKAN - std::unique_ptr<QVulkanInstance> vk_instance; -#endif + // 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; /// Temporary storage of the screenshot taken QImage screenshot_image; QByteArray geometry; - /// Native window handle that backs this presentation widget - QWindow* child_window = nullptr; - - /// In order to embed the window into GRenderWindow, you need to use createWindowContainer to - /// put the child_window into a widget then add it to the layout. This child_widget can be - /// parented to GRenderWindow and use Qt's lifetime system QWidget* child_widget = nullptr; bool first_frame = false; diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index a821c7b3c..ea667caef 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -15,6 +15,10 @@ #include "ui_configure_graphics.h" #include "yuzu/configuration/configure_graphics.h" +#ifdef HAS_VULKAN +#include "video_core/renderer_vulkan/renderer_vulkan.h" +#endif + namespace { enum class Resolution : int { Auto, @@ -165,41 +169,9 @@ void ConfigureGraphics::UpdateDeviceComboBox() { 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)); + vulkan_devices.clear(); + for (auto& name : Vulkan::RendererVulkan::EnumerateDevices()) { + vulkan_devices.push_back(QString::fromStdString(name)); } #endif } diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 96dec50e2..15ac30f12 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -541,18 +541,19 @@ void ConfigureInputPlayer::HandleClick( button->setText(tr("[press key]")); button->setFocus(); - const auto iter = std::find(button_map.begin(), button_map.end(), button); - ASSERT(iter != button_map.end()); - const auto index = std::distance(button_map.begin(), iter); - ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); + // Keyboard keys can only be used as button devices + want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; + if (want_keyboard_keys) { + const auto iter = std::find(button_map.begin(), button_map.end(), button); + ASSERT(iter != button_map.end()); + const auto index = std::distance(button_map.begin(), iter); + ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); + } input_setter = new_input_setter; device_pollers = InputCommon::Polling::GetPollers(type); - // Keyboard keys can only be used as button devices - want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; - for (auto& poller : device_pollers) { poller->Start(); } diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 1556481d0..4b37746a1 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui @@ -839,21 +839,21 @@ <bool>false</bool> </property> <layout class="QGridLayout" name="gridLayout_3"> - <item row="3" column="0"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout"> <item> - <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout"> + <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout"> <item> - <widget class="QLabel" name="labelSL"> + <widget class="QLabel" name="labelL"> <property name="text"> - <string>SL:</string> + <string>L:</string> </property> </widget> </item> </layout> </item> <item> - <widget class="QPushButton" name="buttonSL"> + <widget class="QPushButton" name="buttonL"> <property name="text"> <string/> </property> @@ -861,21 +861,21 @@ </item> </layout> </item> - <item row="2" column="1"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout"> + <item row="0" column="1"> + <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> <item> - <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout"> + <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout"> <item> - <widget class="QLabel" name="labelZR"> + <widget class="QLabel" name="labelR"> <property name="text"> - <string>ZR:</string> + <string>R:</string> </property> </widget> </item> </layout> </item> <item> - <widget class="QPushButton" name="buttonZR"> + <widget class="QPushButton" name="buttonR"> <property name="text"> <string/> </property> @@ -883,21 +883,21 @@ </item> </layout> </item> - <item row="3" column="1"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> + <item row="1" column="0"> + <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout"> <item> - <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout"> + <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout"> <item> - <widget class="QLabel" name="labelSR"> + <widget class="QLabel" name="labelZL"> <property name="text"> - <string>SR:</string> + <string>ZL:</string> </property> </widget> </item> </layout> </item> <item> - <widget class="QPushButton" name="buttonSR"> + <widget class="QPushButton" name="buttonZL"> <property name="text"> <string/> </property> @@ -905,21 +905,21 @@ </item> </layout> </item> - <item row="0" column="1"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout"> + <item row="1" column="1"> + <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout"> <item> - <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout"> + <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout"> <item> - <widget class="QLabel" name="labelZL"> + <widget class="QLabel" name="labelZR"> <property name="text"> - <string>ZL:</string> + <string>ZR:</string> </property> </widget> </item> </layout> </item> <item> - <widget class="QPushButton" name="buttonZL"> + <widget class="QPushButton" name="buttonZR"> <property name="text"> <string/> </property> @@ -927,21 +927,21 @@ </item> </layout> </item> - <item row="0" column="0"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout"> + <item row="0" column="2"> + <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> <item> - <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout"> + <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout"> <item> - <widget class="QLabel" name="labelL"> + <widget class="QLabel" name="labelSL"> <property name="text"> - <string>L:</string> + <string>SL:</string> </property> </widget> </item> </layout> </item> <item> - <widget class="QPushButton" name="buttonL"> + <widget class="QPushButton" name="buttonSL"> <property name="text"> <string/> </property> @@ -949,21 +949,21 @@ </item> </layout> </item> - <item row="2" column="0"> - <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> + <item row="1" column="2"> + <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> <item> - <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout"> + <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout"> <item> - <widget class="QLabel" name="labelR"> + <widget class="QLabel" name="labelSR"> <property name="text"> - <string>R:</string> + <string>SR:</string> </property> </widget> </item> </layout> </item> <item> - <widget class="QPushButton" name="buttonR"> + <widget class="QPushButton" name="buttonSR"> <property name="text"> <string/> </property> diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp index ab3a11d30..0e0e8f113 100644 --- a/src/yuzu/configuration/configure_input_simple.cpp +++ b/src/yuzu/configuration/configure_input_simple.cpp @@ -35,6 +35,7 @@ void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) { // - Open any dialogs // - Block in any way +constexpr std::size_t PLAYER_0_INDEX = 0; constexpr std::size_t HANDHELD_INDEX = 8; void HandheldOnProfileSelect() { @@ -53,8 +54,8 @@ void HandheldOnProfileSelect() { } void DualJoyconsDockedOnProfileSelect() { - Settings::values.players[0].connected = true; - Settings::values.players[0].type = Settings::ControllerType::DualJoycon; + Settings::values.players[PLAYER_0_INDEX].connected = true; + Settings::values.players[PLAYER_0_INDEX].type = Settings::ControllerType::DualJoycon; for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) { Settings::values.players[player].connected = false; @@ -64,7 +65,7 @@ void DualJoyconsDockedOnProfileSelect() { Settings::values.keyboard_enabled = false; Settings::values.mouse_enabled = false; Settings::values.debug_pad_enabled = false; - Settings::values.touchscreen.enabled = false; + Settings::values.touchscreen.enabled = true; } // Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure @@ -78,7 +79,7 @@ constexpr std::array<InputProfile, 3> INPUT_PROFILES{{ }}, {QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect, [](ConfigureInputSimple* caller) { - CallConfigureDialog<ConfigureInputPlayer>(caller, 1, false); + CallConfigureDialog<ConfigureInputPlayer>(caller, PLAYER_0_INDEX, false); }}, {QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>}, }}; diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp index 0a4abe34f..e0647ea5b 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.cpp +++ b/src/yuzu/configuration/configure_mouse_advanced.cpp @@ -184,18 +184,19 @@ void ConfigureMouseAdvanced::HandleClick( button->setText(tr("[press key]")); button->setFocus(); - const auto iter = std::find(button_map.begin(), button_map.end(), button); - ASSERT(iter != button_map.end()); - const auto index = std::distance(button_map.begin(), iter); - ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); + // Keyboard keys can only be used as button devices + want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; + if (want_keyboard_keys) { + const auto iter = std::find(button_map.begin(), button_map.end(), button); + ASSERT(iter != button_map.end()); + const auto index = std::distance(button_map.begin(), iter); + ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); + } input_setter = new_input_setter; device_pollers = InputCommon::Polling::GetPollers(type); - // Keyboard keys can only be used as button devices - want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; - for (auto& poller : device_pollers) { poller->Start(); } diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index a2b88c787..dccbabcbf 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -315,7 +315,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type")); item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size")); } - item_model->setSortRole(GameListItemPath::TitleRole); + item_model->setSortRole(GameListItemPath::SortRole); connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::onUpdateThemedIcons); connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); @@ -441,6 +441,8 @@ void GameList::DonePopulating(QStringList watch_list) { if (children_total > 0) { search_field->setFocus(); } + item_model->sort(tree_view->header()->sortIndicatorSection(), + tree_view->header()->sortIndicatorOrder()); } void GameList::PopupContextMenu(const QPoint& menu_location) { @@ -666,8 +668,6 @@ void GameList::LoadInterfaceLayout() { // so make it as large as possible as default. header->resizeSection(COLUMN_NAME, header->width()); } - - item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); } const QStringList GameList::supported_file_extensions = { diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 7cde72d1b..3e6d5a7cd 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -65,10 +65,10 @@ public: */ class GameListItemPath : public GameListItem { public: - static const int TitleRole = SortRole; - static const int FullPathRole = SortRole + 1; - static const int ProgramIdRole = SortRole + 2; - static const int FileTypeRole = SortRole + 3; + static const int TitleRole = SortRole + 1; + static const int FullPathRole = SortRole + 2; + static const int ProgramIdRole = SortRole + 3; + static const int FileTypeRole = SortRole + 4; GameListItemPath() = default; GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data, @@ -95,7 +95,7 @@ public: } QVariant data(int role) const override { - if (role == Qt::DisplayRole) { + if (role == Qt::DisplayRole || role == SortRole) { std::string filename; Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, nullptr); @@ -110,6 +110,9 @@ public: const auto& row1 = row_data.at(UISettings::values.row_1_text_id); const int row2_id = UISettings::values.row_2_text_id; + if (role == SortRole) + return row1.toLower(); + if (row2_id == 4) // None return row1; @@ -123,6 +126,13 @@ public: return GameListItem::data(role); } + + /** + * Override to prevent automatic sorting. + */ + bool operator<(const QStandardItem& other) const override { + return false; + } }; class GameListItemCompat : public GameListItem { @@ -289,6 +299,10 @@ public: int type() const override { return static_cast<int>(GameListItemType::AddDir); } + + bool operator<(const QStandardItem& other) const override { + return false; + } }; class GameList; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d7e59d0cd..1717e06f9 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -205,7 +205,13 @@ GMainWindow::GMainWindow() ConnectMenuEvents(); ConnectWidgetEvents(); - LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, + const auto build_id = std::string(Common::g_build_id); + const auto fmt = std::string(Common::g_title_bar_format_idle); + const auto yuzu_build_version = + fmt::format(fmt.empty() ? "yuzu Development Build" : fmt, std::string{}, std::string{}, + std::string{}, std::string{}, std::string{}, build_id); + + LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", yuzu_build_version, Common::g_scm_branch, Common::g_scm_desc); #ifdef ARCHITECTURE_x86_64 LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string); @@ -984,7 +990,7 @@ void GMainWindow::BootGame(const QString& filename) { return; // Create and start the emulation thread - emu_thread = std::make_unique<EmuThread>(*render_window); + emu_thread = std::make_unique<EmuThread>(); emit EmulationStarting(emu_thread.get()); emu_thread->start(); @@ -2378,7 +2384,6 @@ int main(int argc, char* argv[]) { // Enables the core to make the qt created contexts current on std::threads QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); - QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); QApplication app(argc, argv); // Qt changes the locale and causes issues in float conversion using std::to_string() when |