diff options
Diffstat (limited to 'src/yuzu')
| -rw-r--r-- | src/yuzu/bootmanager.cpp | 420 | ||||
| -rw-r--r-- | src/yuzu/bootmanager.h | 67 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.cpp | 3 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.ui | 10 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 49 | ||||
| -rw-r--r-- | src/yuzu/main.h | 7 | 
7 files changed, 314 insertions, 244 deletions
| diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 55a37fffa..c3dbb1a88 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -9,6 +9,9 @@  #include <QKeyEvent>  #include <QMessageBox>  #include <QOffscreenSurface> +#include <QOpenGLContext> +#include <QOpenGLFunctions> +#include <QOpenGLFunctions_4_3_Core>  #include <QOpenGLWindow>  #include <QPainter>  #include <QScreen> @@ -23,9 +26,10 @@  #include "common/assert.h"  #include "common/microprofile.h"  #include "common/scm_rev.h" +#include "common/scope_exit.h"  #include "core/core.h"  #include "core/frontend/framebuffer_layout.h" -#include "core/frontend/scope_acquire_window_context.h" +#include "core/frontend/scope_acquire_context.h"  #include "core/settings.h"  #include "input_common/keyboard.h"  #include "input_common/main.h" @@ -35,15 +39,27 @@  #include "yuzu/bootmanager.h"  #include "yuzu/main.h" -EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} +EmuThread::EmuThread(GRenderWindow& window) +    : shared_context{window.CreateSharedContext()}, +      context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context +                                                                                  : window} {}  EmuThread::~EmuThread() = default; -void EmuThread::run() { -    render_window->MakeCurrent(); +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}; +      emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);      Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( @@ -53,11 +69,6 @@ void EmuThread::run() {      emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); -    if (Settings::values.use_asynchronous_gpu_emulation) { -        // Release OpenGL context for the GPU thread -        render_window->DoneCurrent(); -    } -      // Holds whether the cpu was running during the last iteration,      // so that the DebugModeLeft signal can be emitted before the      // next execution step @@ -98,190 +109,202 @@ void EmuThread::run() {  #if MICROPROFILE_ENABLED      MicroProfileOnThreadExit();  #endif - -    render_window->moveContext();  }  class GGLContext : public Core::Frontend::GraphicsContext {  public: -    explicit GGLContext(QOpenGLContext* shared_context) : shared_context{shared_context} { -        context.setFormat(shared_context->format()); -        context.setShareContext(shared_context); -        context.create(); +    explicit GGLContext(QOpenGLContext* shared_context) +        : context(new QOpenGLContext(shared_context->parent())), +          surface(new QOffscreenSurface(nullptr)) { + +        // disable vsync for any shared contexts +        auto format = shared_context->format(); +        format.setSwapInterval(0); + +        context->setShareContext(shared_context); +        context->setFormat(format); +        context->create(); +        surface->setParent(shared_context->parent()); +        surface->setFormat(format); +        surface->create();      }      void MakeCurrent() override { -        context.makeCurrent(shared_context->surface()); +        context->makeCurrent(surface);      }      void DoneCurrent() override { -        context.doneCurrent(); +        context->doneCurrent();      } -    void SwapBuffers() override {} -  private: -    QOpenGLContext* shared_context; -    QOpenGLContext context; +    QOpenGLContext* context; +    QOffscreenSurface* surface;  }; -class GWidgetInternal : public QWindow { +class ChildRenderWindow : public QWindow {  public: -    GWidgetInternal(GRenderWindow* parent) : parent(parent) {} -    virtual ~GWidgetInternal() = default; +    ChildRenderWindow(QWindow* parent, QWidget* event_handler) +        : QWindow{parent}, event_handler{event_handler} {} -    void resizeEvent(QResizeEvent* ev) override { -        parent->OnClientAreaResized(ev->size().width(), ev->size().height()); -        parent->OnFramebufferSizeChanged(); -    } +    virtual ~ChildRenderWindow() = default; -    void keyPressEvent(QKeyEvent* event) override { -        InputCommon::GetKeyboard()->PressKey(event->key()); -    } +    virtual void Present() = 0; -    void keyReleaseEvent(QKeyEvent* event) override { -        InputCommon::GetKeyboard()->ReleaseKey(event->key()); +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); +        }      } -    void mousePressEvent(QMouseEvent* event) override { -        if (event->source() == Qt::MouseEventSynthesizedBySystem) -            return; // touch input is handled in TouchBeginEvent - -        const auto pos{event->pos()}; -        if (event->button() == Qt::LeftButton) { -            const auto [x, y] = parent->ScaleTouch(pos); -            parent->TouchPressed(x, y); -        } else if (event->button() == Qt::RightButton) { -            InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); -        } +    void exposeEvent(QExposeEvent* event) override { +        QWindow::requestUpdate(); +        QWindow::exposeEvent(event);      } -    void mouseMoveEvent(QMouseEvent* event) override { -        if (event->source() == Qt::MouseEventSynthesizedBySystem) -            return; // touch input is handled in TouchUpdateEvent +private: +    QWidget* event_handler{}; +}; -        const auto pos{event->pos()}; -        const auto [x, y] = parent->ScaleTouch(pos); -        parent->TouchMoved(x, y); -        InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); -    } +class OpenGLWindow final : public ChildRenderWindow { +public: +    OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) +        : ChildRenderWindow{parent, event_handler}, +          context(new QOpenGLContext(shared_context->parent())) { -    void mouseReleaseEvent(QMouseEvent* event) override { -        if (event->source() == Qt::MouseEventSynthesizedBySystem) -            return; // touch input is handled in TouchEndEvent +        // disable vsync for any shared contexts +        auto format = shared_context->format(); +        format.setSwapInterval(Settings::values.use_vsync ? 1 : 0); +        this->setFormat(format); -        if (event->button() == Qt::LeftButton) -            parent->TouchReleased(); -        else if (event->button() == Qt::RightButton) -            InputCommon::GetMotionEmu()->EndTilt(); -    } +        context->setShareContext(shared_context); +        context->setScreen(this->screen()); +        context->setFormat(format); +        context->create(); -    void DisablePainting() { -        do_painting = false; -    } +        setSurfaceType(QWindow::OpenGLSurface); -    void EnablePainting() { -        do_painting = true; +        // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, +        // WA_DontShowOnScreen, WA_DeleteOnClose      } -    std::pair<unsigned, unsigned> GetSize() const { -        return std::make_pair(width(), height()); +    ~OpenGLWindow() override { +        context->doneCurrent();      } -protected: -    bool IsPaintingEnabled() const { -        return do_painting; +    void Present() override { +        if (!isExposed()) { +            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();      }  private: -    GRenderWindow* parent; -    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); -        } -    } +    QOpenGLContext* context{};  };  #ifdef HAS_VULKAN -class GVKWidgetInternal final : public GWidgetInternal { +class VulkanWindow final : public ChildRenderWindow {  public: -    GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) { +    VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance) +        : ChildRenderWindow{parent, event_handler} {          setSurfaceType(QSurface::SurfaceType::VulkanSurface);          setVulkanInstance(instance);      } -    ~GVKWidgetInternal() override = default; + +    ~VulkanWindow() override = default; + +    void Present() override { +        // TODO(bunnei): ImplementMe +    } + +private: +    QWidget* event_handler{};  };  #endif -GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) -    : QWidget(parent), emu_thread(emu_thread) { +GRenderWindow::GRenderWindow(QWidget* 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),                              QString::fromUtf8(Common::g_scm_desc)));      setAttribute(Qt::WA_AcceptTouchEvents); - +    auto layout = new QHBoxLayout(this); +    layout->setMargin(0); +    setLayout(layout);      InputCommon::Init(); + +    GMainWindow* parent = GetMainWindow();      connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);  }  GRenderWindow::~GRenderWindow() {      InputCommon::Shutdown(); - -    // Avoid an unordered destruction that generates a segfault -    delete child;  } -void GRenderWindow::moveContext() { -    if (!context) { -        return; +void GRenderWindow::MakeCurrent() { +    if (core_context) { +        core_context->MakeCurrent();      } -    DoneCurrent(); - -    // If the thread started running, move the GL Context to the new thread. Otherwise, move it -    // back. -    auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) -                      ? emu_thread -                      : qApp->thread(); -    context->moveToThread(thread);  } -void GRenderWindow::SwapBuffers() { -    if (context) { -        context->swapBuffers(child); +void GRenderWindow::DoneCurrent() { +    if (core_context) { +        core_context->DoneCurrent();      } +} + +void GRenderWindow::PollEvents() {      if (!first_frame) {          first_frame = true;          emit FirstFrameDisplayed();      }  } -void GRenderWindow::MakeCurrent() { -    if (context) { -        context->makeCurrent(child); -    } -} - -void GRenderWindow::DoneCurrent() { -    if (context) { -        context->doneCurrent(); -    } -} - -void GRenderWindow::PollEvents() {} -  bool GRenderWindow::IsShown() const {      return !isMinimized();  } @@ -291,7 +314,7 @@ void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* i  #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); +    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)); @@ -309,21 +332,10 @@ void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* i  void GRenderWindow::OnFramebufferSizeChanged() {      // Screen changes potentially incur a change in screen DPI, hence we should update the      // framebuffer size -    const qreal pixelRatio{GetWindowPixelRatio()}; -    const auto size{child->GetSize()}; -    UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio); -} - -void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { -    if (child) { -        child->keyPressEvent(event); -    } -} - -void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) { -    if (child) { -        child->keyReleaseEvent(event); -    } +    const qreal pixel_ratio = windowPixelRatio(); +    const u32 width = this->width() * pixel_ratio; +    const u32 height = this->height() * pixel_ratio; +    UpdateCurrentFramebufferLayout(width, height);  }  void GRenderWindow::BackupGeometry() { @@ -351,13 +363,12 @@ QByteArray GRenderWindow::saveGeometry() {      return geometry;  } -qreal GRenderWindow::GetWindowPixelRatio() const { -    // windowHandle() might not be accessible until the window is displayed to screen. -    return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; +qreal GRenderWindow::windowPixelRatio() const { +    return devicePixelRatio();  }  std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { -    const qreal pixel_ratio{GetWindowPixelRatio()}; +    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}))};  } @@ -367,6 +378,47 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {      QWidget::closeEvent(event);  } +void GRenderWindow::keyPressEvent(QKeyEvent* event) { +    InputCommon::GetKeyboard()->PressKey(event->key()); +} + +void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { +    InputCommon::GetKeyboard()->ReleaseKey(event->key()); +} + +void GRenderWindow::mousePressEvent(QMouseEvent* event) { +    if (event->source() == Qt::MouseEventSynthesizedBySystem) +        return; // touch input is handled in TouchBeginEvent + +    auto pos = event->pos(); +    if (event->button() == Qt::LeftButton) { +        const auto [x, y] = ScaleTouch(pos); +        this->TouchPressed(x, y); +    } else if (event->button() == Qt::RightButton) { +        InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); +    } +} + +void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { +    if (event->source() == Qt::MouseEventSynthesizedBySystem) +        return; // touch input is handled in TouchUpdateEvent + +    auto pos = event->pos(); +    const auto [x, y] = ScaleTouch(pos); +    this->TouchMoved(x, y); +    InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); +} + +void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { +    if (event->source() == Qt::MouseEventSynthesizedBySystem) +        return; // touch input is handled in TouchEndEvent + +    if (event->button() == Qt::LeftButton) +        this->TouchReleased(); +    else if (event->button() == Qt::RightButton) +        InputCommon::GetMotionEmu()->EndTilt(); +} +  void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {      // TouchBegin always has exactly one touch point, so take the .first()      const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); @@ -415,26 +467,20 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) {      InputCommon::GetKeyboard()->ReleaseAllKeys();  } -void GRenderWindow::OnClientAreaResized(u32 width, u32 height) { -    NotifyClientAreaSizeChanged(std::make_pair(width, height)); +void GRenderWindow::resizeEvent(QResizeEvent* event) { +    QWidget::resizeEvent(event); +    OnFramebufferSizeChanged();  }  std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { -    return std::make_unique<GGLContext>(context.get()); +    if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { +        return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext()); +    } +    return {};  }  bool GRenderWindow::InitRenderTarget() { -    shared_context.reset(); -    context.reset(); -    if (child) { -        delete child; -    } -    if (container) { -        delete container; -    } -    if (layout()) { -        delete layout(); -    } +    ReleaseRenderTarget();      first_frame = false; @@ -451,13 +497,6 @@ bool GRenderWindow::InitRenderTarget() {          break;      } -    container = QWidget::createWindowContainer(child, this); -    QBoxLayout* layout = new QHBoxLayout(this); - -    layout->addWidget(container); -    layout->setMargin(0); -    setLayout(layout); -      // Reset minimum required size to avoid resizing issues on the main window after restarting.      setMinimumSize(1, 1); @@ -467,14 +506,9 @@ bool GRenderWindow::InitRenderTarget() {      hide();      resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); -    child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); -    container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);      OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); -      OnFramebufferSizeChanged(); -    NotifyClientAreaSizeChanged(child->GetSize()); -      BackupGeometry();      if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { @@ -486,6 +520,14 @@ bool GRenderWindow::InitRenderTarget() {      return true;  } +void GRenderWindow::ReleaseRenderTarget() { +    if (child_widget) { +        layout()->removeWidget(child_widget); +        delete child_widget; +        child_widget = nullptr; +    } +} +  void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {      auto& renderer = Core::System::GetInstance().Renderer(); @@ -521,16 +563,19 @@ bool GRenderWindow::InitializeOpenGL() {      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()); +    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(); +      return true;  } @@ -559,7 +604,14 @@ bool GRenderWindow::InitializeVulkan() {          return false;      } -    child = new GVKWidgetInternal(this, vk_instance.get()); +    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); +      return true;  #else      QMessageBox::critical(this, tr("Vulkan not available!"), @@ -569,7 +621,7 @@ bool GRenderWindow::InitializeVulkan() {  }  bool GRenderWindow::LoadOpenGL() { -    Core::Frontend::ScopeAcquireWindowContext acquire_context{*this}; +    Core::Frontend::ScopeAcquireContext 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 " @@ -621,12 +673,10 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {  void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {      this->emu_thread = emu_thread; -    child->DisablePainting();  }  void GRenderWindow::OnEmulationStopping() {      emu_thread = nullptr; -    child->EnablePainting();  }  void GRenderWindow::showEvent(QShowEvent* event) { diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 71a2fa321..79b030304 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -11,11 +11,13 @@  #include <QImage>  #include <QThread>  #include <QWidget> +#include <QWindow>  #include "common/thread.h"  #include "core/core.h"  #include "core/frontend/emu_window.h" +class GRenderWindow;  class QKeyEvent;  class QScreen;  class QTouchEvent; @@ -26,14 +28,6 @@ class QOpenGLContext;  class QVulkanInstance;  #endif -class GWidgetInternal; -class GGLWidgetInternal; -class GVKWidgetInternal; -class GMainWindow; -class GRenderWindow; -class QSurface; -class QOpenGLContext; -  namespace VideoCore {  enum class LoadCallbackStage;  } @@ -42,7 +36,7 @@ class EmuThread final : public QThread {      Q_OBJECT  public: -    explicit EmuThread(GRenderWindow* render_window); +    explicit EmuThread(GRenderWindow& window);      ~EmuThread() override;      /** @@ -96,7 +90,11 @@ private:      std::mutex running_mutex;      std::condition_variable running_cv; -    GRenderWindow* render_window; +    /// 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:      /** @@ -126,11 +124,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {      Q_OBJECT  public: -    GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); +    GRenderWindow(QWidget* parent, EmuThread* emu_thread);      ~GRenderWindow() override; -    // EmuWindow implementation -    void SwapBuffers() override; +    // EmuWindow implementation.      void MakeCurrent() override;      void DoneCurrent() override;      void PollEvents() override; @@ -139,30 +136,36 @@ public:                                  void* surface) const override;      std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; -    void ForwardKeyPressEvent(QKeyEvent* event); -    void ForwardKeyReleaseEvent(QKeyEvent* event); -      void BackupGeometry();      void RestoreGeometry();      void restoreGeometry(const QByteArray& geometry); // overridden      QByteArray saveGeometry();                        // overridden -    qreal GetWindowPixelRatio() const; -    std::pair<u32, u32> ScaleTouch(QPointF pos) const; +    qreal windowPixelRatio() const;      void closeEvent(QCloseEvent* event) override; + +    void resizeEvent(QResizeEvent* event) override; + +    void keyPressEvent(QKeyEvent* event) override; +    void keyReleaseEvent(QKeyEvent* event) override; + +    void mousePressEvent(QMouseEvent* event) override; +    void mouseMoveEvent(QMouseEvent* event) override; +    void mouseReleaseEvent(QMouseEvent* event) override; +      bool event(QEvent* event) override; -    void focusOutEvent(QFocusEvent* event) override; -    void OnClientAreaResized(u32 width, u32 height); +    void focusOutEvent(QFocusEvent* event) override;      bool InitRenderTarget(); +    /// Destroy the previous run's child_widget which should also destroy the child_window +    void ReleaseRenderTarget(); +      void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);  public slots: -    void moveContext(); // overridden -      void OnEmulationStarting(EmuThread* emu_thread);      void OnEmulationStopping();      void OnFramebufferSizeChanged(); @@ -173,6 +176,7 @@ signals:      void FirstFrameDisplayed();  private: +    std::pair<u32, u32> ScaleTouch(QPointF pos) const;      void TouchBeginEvent(const QTouchEvent* event);      void TouchUpdateEvent(const QTouchEvent* event);      void TouchEndEvent(); @@ -184,15 +188,9 @@ private:      bool LoadOpenGL();      QStringList GetUnsupportedGLExtensions() const; -    QWidget* container = nullptr; -    GWidgetInternal* child = nullptr; -      EmuThread* emu_thread; -    // Context that backs the GGLWidgetInternal (and will be used by core to render) -    std::unique_ptr<QOpenGLContext> context; -    // Context that will be shared between all newly created contexts. This should never be made -    // current -    std::unique_ptr<QOpenGLContext> shared_context; + +    std::unique_ptr<GraphicsContext> core_context;  #ifdef HAS_VULKAN      std::unique_ptr<QVulkanInstance> vk_instance; @@ -202,6 +200,15 @@ private:      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;  protected: diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 6209fff75..d0f574147 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -640,6 +640,7 @@ void Config::ReadRendererValues() {          ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool();      Settings::values.use_asynchronous_gpu_emulation =          ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); +    Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool();      Settings::values.force_30fps_mode =          ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); @@ -1074,6 +1075,7 @@ void Config::SaveRendererValues() {                   Settings::values.use_accurate_gpu_emulation, false);      WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"),                   Settings::values.use_asynchronous_gpu_emulation, false); +    WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);      WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false);      // Cast to double because Qt's written float values are not human-readable diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index ea899c080..fe64c7d81 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -103,6 +103,8 @@ void ConfigureGraphics::SetConfiguration() {      ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);      ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);      ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); +    ui->use_vsync->setEnabled(runtime_lock); +    ui->use_vsync->setChecked(Settings::values.use_vsync);      ui->force_30fps_mode->setEnabled(runtime_lock);      ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);      UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, @@ -120,6 +122,7 @@ void ConfigureGraphics::ApplyConfiguration() {      Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();      Settings::values.use_asynchronous_gpu_emulation =          ui->use_asynchronous_gpu_emulation->isChecked(); +    Settings::values.use_vsync = ui->use_vsync->isChecked();      Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();      Settings::values.bg_red = static_cast<float>(bg_color.redF());      Settings::values.bg_green = static_cast<float>(bg_color.greenF()); diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index db60426ab..9acc7dd93 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -85,6 +85,16 @@           </widget>          </item>          <item> +         <widget class="QCheckBox" name="use_vsync"> +          <property name="toolTip"> +           <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string> +          </property> +          <property name="text"> +           <string>Use VSync (OpenGL only)</string> +          </property> +         </widget> +        </item> +        <item>           <widget class="QCheckBox" name="use_accurate_gpu_emulation">            <property name="text">             <string>Use accurate GPU emulation (slow)</string> diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 54ca2dc1d..47615adfe 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -20,7 +20,6 @@  #include "core/file_sys/vfs.h"  #include "core/file_sys/vfs_real.h"  #include "core/frontend/applets/general_frontend.h" -#include "core/frontend/scope_acquire_window_context.h"  #include "core/hle/service/acc/profile_manager.h"  #include "core/hle/service/am/applet_ae.h"  #include "core/hle/service/am/applet_oe.h" @@ -985,11 +984,8 @@ 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>(*render_window);      emit EmulationStarting(emu_thread.get()); -    if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { -        render_window->moveContext(); -    }      emu_thread->start();      connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); @@ -1087,6 +1083,9 @@ void GMainWindow::ShutdownGame() {      emulation_running = false;      game_path.clear(); + +    // When closing the game, destroy the GLWindow to clear the context after the game is closed +    render_window->ReleaseRenderTarget();  }  void GMainWindow::StoreRecentFile(const QString& filename) { @@ -2215,48 +2214,47 @@ void GMainWindow::closeEvent(QCloseEvent* event) {      QWidget::closeEvent(event);  } -void GMainWindow::keyPressEvent(QKeyEvent* event) { -    if (render_window) { -        render_window->ForwardKeyPressEvent(event); -    } +static bool IsSingleFileDropEvent(const QMimeData* mime) { +    return mime->hasUrls() && mime->urls().length() == 1;  } -void GMainWindow::keyReleaseEvent(QKeyEvent* event) { -    if (render_window) { -        render_window->ForwardKeyReleaseEvent(event); +void GMainWindow::AcceptDropEvent(QDropEvent* event) { +    if (IsSingleFileDropEvent(event->mimeData())) { +        event->setDropAction(Qt::DropAction::LinkAction); +        event->accept();      }  } -static bool IsSingleFileDropEvent(QDropEvent* event) { -    const QMimeData* mimeData = event->mimeData(); -    return mimeData->hasUrls() && mimeData->urls().length() == 1; -} - -void GMainWindow::dropEvent(QDropEvent* event) { -    if (!IsSingleFileDropEvent(event)) { -        return; +bool GMainWindow::DropAction(QDropEvent* event) { +    if (!IsSingleFileDropEvent(event->mimeData())) { +        return false;      }      const QMimeData* mime_data = event->mimeData(); -    const QString filename = mime_data->urls().at(0).toLocalFile(); +    const QString& filename = mime_data->urls().at(0).toLocalFile();      if (emulation_running && QFileInfo(filename).suffix() == QStringLiteral("bin")) { +        // Amiibo          LoadAmiibo(filename);      } else { +        // Game          if (ConfirmChangeGame()) {              BootGame(filename);          }      } +    return true; +} + +void GMainWindow::dropEvent(QDropEvent* event) { +    DropAction(event);  }  void GMainWindow::dragEnterEvent(QDragEnterEvent* event) { -    if (IsSingleFileDropEvent(event)) { -        event->acceptProposedAction(); -    } +    AcceptDropEvent(event);  }  void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { -    event->acceptProposedAction(); +    AcceptDropEvent(event);  }  bool GMainWindow::ConfirmChangeGame() { @@ -2377,6 +2375,7 @@ 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 diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 8eba2172c..a67125567 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -78,6 +78,9 @@ public:      std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc; +    bool DropAction(QDropEvent* event); +    void AcceptDropEvent(QDropEvent* event); +  signals:      /** @@ -264,8 +267,4 @@ protected:      void dropEvent(QDropEvent* event) override;      void dragEnterEvent(QDragEnterEvent* event) override;      void dragMoveEvent(QDragMoveEvent* event) override; - -    // Overrides used to forward signals to the render window when the focus moves out. -    void keyPressEvent(QKeyEvent* event) override; -    void keyReleaseEvent(QKeyEvent* event) override;  }; | 
