Merge pull request #2017 from jroweboy/glwidget
Frontend: Migrate to QOpenGLWindow and support shared contexts
This commit is contained in:
commit
065f83c6c3
|
@ -12,6 +12,23 @@
|
||||||
|
|
||||||
namespace Core::Frontend {
|
namespace Core::Frontend {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a graphics context that can be used for background computation or drawing. If the
|
||||||
|
* graphics backend doesn't require the context, then the implementation of these methods can be
|
||||||
|
* stubs
|
||||||
|
*/
|
||||||
|
class GraphicsContext {
|
||||||
|
public:
|
||||||
|
/// Makes the graphics context current for the caller thread
|
||||||
|
virtual void MakeCurrent() = 0;
|
||||||
|
|
||||||
|
/// Releases (dunno if this is the "right" word) the context from the caller thread
|
||||||
|
virtual void DoneCurrent() = 0;
|
||||||
|
|
||||||
|
/// Swap buffers to display the next frame
|
||||||
|
virtual void SwapBuffers() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction class used to provide an interface between emulation code and the frontend
|
* Abstraction class used to provide an interface between emulation code and the frontend
|
||||||
* (e.g. SDL, QGLWidget, GLFW, etc...).
|
* (e.g. SDL, QGLWidget, GLFW, etc...).
|
||||||
|
@ -30,7 +47,7 @@ namespace Core::Frontend {
|
||||||
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
|
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
|
||||||
* re-read the upper points again and think about it if you don't see this.
|
* re-read the upper points again and think about it if you don't see this.
|
||||||
*/
|
*/
|
||||||
class EmuWindow {
|
class EmuWindow : public GraphicsContext {
|
||||||
public:
|
public:
|
||||||
/// Data structure to store emuwindow configuration
|
/// Data structure to store emuwindow configuration
|
||||||
struct WindowConfig {
|
struct WindowConfig {
|
||||||
|
@ -40,17 +57,21 @@ public:
|
||||||
std::pair<unsigned, unsigned> min_client_area_size;
|
std::pair<unsigned, unsigned> min_client_area_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Swap buffers to display the next frame
|
|
||||||
virtual void SwapBuffers() = 0;
|
|
||||||
|
|
||||||
/// Polls window events
|
/// Polls window events
|
||||||
virtual void PollEvents() = 0;
|
virtual void PollEvents() = 0;
|
||||||
|
|
||||||
/// Makes the graphics context current for the caller thread
|
/**
|
||||||
virtual void MakeCurrent() = 0;
|
* Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
|
||||||
|
* context can be used from other threads for background graphics computation. If the frontend
|
||||||
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
|
* is using a graphics backend that doesn't need anything specific to run on a different thread,
|
||||||
virtual void DoneCurrent() = 0;
|
* then it can use a stubbed implemenation for GraphicsContext.
|
||||||
|
*
|
||||||
|
* If the return value is null, then the core should assume that the frontend cannot provide a
|
||||||
|
* Shared Context
|
||||||
|
*/
|
||||||
|
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
#include <QOffscreenSurface>
|
||||||
|
#include <QOpenGLWindow>
|
||||||
|
#include <QPainter>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
@ -82,13 +89,36 @@ void EmuThread::run() {
|
||||||
render_window->moveContext();
|
render_window->moveContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GGLContext : public Core::Frontend::GraphicsContext {
|
||||||
|
public:
|
||||||
|
explicit GGLContext(QOpenGLContext* shared_context) : surface() {
|
||||||
|
context = std::make_unique<QOpenGLContext>(shared_context);
|
||||||
|
surface.setFormat(shared_context->format());
|
||||||
|
surface.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MakeCurrent() override {
|
||||||
|
context->makeCurrent(&surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoneCurrent() override {
|
||||||
|
context->doneCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapBuffers() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<QOpenGLContext> context;
|
||||||
|
QOffscreenSurface surface;
|
||||||
|
};
|
||||||
|
|
||||||
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
|
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
|
||||||
// context.
|
// context.
|
||||||
// The corresponding functionality is handled in EmuThread instead
|
// The corresponding functionality is handled in EmuThread instead
|
||||||
class GGLWidgetInternal : public QGLWidget {
|
class GGLWidgetInternal : public QOpenGLWindow {
|
||||||
public:
|
public:
|
||||||
GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent)
|
GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
|
||||||
: QGLWidget(fmt, parent), parent(parent) {}
|
: QOpenGLWindow(shared_context), parent(parent) {}
|
||||||
|
|
||||||
void paintEvent(QPaintEvent* ev) override {
|
void paintEvent(QPaintEvent* ev) override {
|
||||||
if (do_painting) {
|
if (do_painting) {
|
||||||
|
@ -101,9 +131,51 @@ public:
|
||||||
parent->OnFramebufferSizeChanged();
|
parent->OnFramebufferSizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void keyPressEvent(QKeyEvent* event) override {
|
||||||
|
InputCommon::GetKeyboard()->PressKey(event->key());
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyReleaseEvent(QKeyEvent* event) override {
|
||||||
|
InputCommon::GetKeyboard()->ReleaseKey(event->key());
|
||||||
|
}
|
||||||
|
|
||||||
|
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 mouseMoveEvent(QMouseEvent* event) override {
|
||||||
|
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||||
|
return; // touch input is handled in TouchUpdateEvent
|
||||||
|
|
||||||
|
const auto pos{event->pos()};
|
||||||
|
const auto [x, y] = parent->ScaleTouch(pos);
|
||||||
|
parent->TouchMoved(x, y);
|
||||||
|
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseReleaseEvent(QMouseEvent* event) override {
|
||||||
|
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||||
|
return; // touch input is handled in TouchEndEvent
|
||||||
|
|
||||||
|
if (event->button() == Qt::LeftButton)
|
||||||
|
parent->TouchReleased();
|
||||||
|
else if (event->button() == Qt::RightButton)
|
||||||
|
InputCommon::GetMotionEmu()->EndTilt();
|
||||||
|
}
|
||||||
|
|
||||||
void DisablePainting() {
|
void DisablePainting() {
|
||||||
do_painting = false;
|
do_painting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnablePainting() {
|
void EnablePainting() {
|
||||||
do_painting = true;
|
do_painting = true;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +186,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
|
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
|
||||||
: QWidget(parent), child(nullptr), emu_thread(emu_thread) {
|
: QWidget(parent), child(nullptr), context(nullptr), emu_thread(emu_thread) {
|
||||||
|
|
||||||
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
|
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
|
||||||
.arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
|
.arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
|
||||||
|
@ -137,19 +209,19 @@ void GRenderWindow::moveContext() {
|
||||||
auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
|
auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
|
||||||
? emu_thread
|
? emu_thread
|
||||||
: qApp->thread();
|
: qApp->thread();
|
||||||
child->context()->moveToThread(thread);
|
context->moveToThread(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::SwapBuffers() {
|
void GRenderWindow::SwapBuffers() {
|
||||||
// In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`,
|
// In our multi-threaded QWidget use case we shouldn't need to call `makeCurrent`,
|
||||||
// since we never call `doneCurrent` in this thread.
|
// since we never call `doneCurrent` in this thread.
|
||||||
// However:
|
// However:
|
||||||
// - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called
|
// - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called
|
||||||
// since the last time `swapBuffers` was executed;
|
// since the last time `swapBuffers` was executed;
|
||||||
// - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks.
|
// - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks.
|
||||||
child->makeCurrent();
|
context->makeCurrent(child);
|
||||||
|
|
||||||
child->swapBuffers();
|
context->swapBuffers(child);
|
||||||
if (!first_frame) {
|
if (!first_frame) {
|
||||||
emit FirstFrameDisplayed();
|
emit FirstFrameDisplayed();
|
||||||
first_frame = true;
|
first_frame = true;
|
||||||
|
@ -157,11 +229,11 @@ void GRenderWindow::SwapBuffers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::MakeCurrent() {
|
void GRenderWindow::MakeCurrent() {
|
||||||
child->makeCurrent();
|
context->makeCurrent(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::DoneCurrent() {
|
void GRenderWindow::DoneCurrent() {
|
||||||
child->doneCurrent();
|
context->doneCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::PollEvents() {}
|
void GRenderWindow::PollEvents() {}
|
||||||
|
@ -174,14 +246,26 @@ void GRenderWindow::PollEvents() {}
|
||||||
void GRenderWindow::OnFramebufferSizeChanged() {
|
void GRenderWindow::OnFramebufferSizeChanged() {
|
||||||
// Screen changes potentially incur a change in screen DPI, hence we should update the
|
// Screen changes potentially incur a change in screen DPI, hence we should update the
|
||||||
// framebuffer size
|
// framebuffer size
|
||||||
qreal pixelRatio = windowPixelRatio();
|
qreal pixelRatio = GetWindowPixelRatio();
|
||||||
unsigned width = child->QPaintDevice::width() * pixelRatio;
|
unsigned width = child->QPaintDevice::width() * pixelRatio;
|
||||||
unsigned height = child->QPaintDevice::height() * pixelRatio;
|
unsigned height = child->QPaintDevice::height() * pixelRatio;
|
||||||
UpdateCurrentFramebufferLayout(width, height);
|
UpdateCurrentFramebufferLayout(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
|
||||||
|
if (child) {
|
||||||
|
child->keyPressEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) {
|
||||||
|
if (child) {
|
||||||
|
child->keyReleaseEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GRenderWindow::BackupGeometry() {
|
void GRenderWindow::BackupGeometry() {
|
||||||
geometry = ((QGLWidget*)this)->saveGeometry();
|
geometry = ((QWidget*)this)->saveGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::RestoreGeometry() {
|
void GRenderWindow::RestoreGeometry() {
|
||||||
|
@ -199,18 +283,18 @@ QByteArray GRenderWindow::saveGeometry() {
|
||||||
// If we are a top-level widget, store the current geometry
|
// If we are a top-level widget, store the current geometry
|
||||||
// otherwise, store the last backup
|
// otherwise, store the last backup
|
||||||
if (parent() == nullptr)
|
if (parent() == nullptr)
|
||||||
return ((QGLWidget*)this)->saveGeometry();
|
return ((QWidget*)this)->saveGeometry();
|
||||||
else
|
else
|
||||||
return geometry;
|
return geometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal GRenderWindow::windowPixelRatio() const {
|
qreal GRenderWindow::GetWindowPixelRatio() const {
|
||||||
// windowHandle() might not be accessible until the window is displayed to screen.
|
// windowHandle() might not be accessible until the window is displayed to screen.
|
||||||
return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
|
return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const {
|
std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const {
|
||||||
const qreal pixel_ratio = windowPixelRatio();
|
const qreal pixel_ratio = GetWindowPixelRatio();
|
||||||
return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
|
return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
|
||||||
static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
|
static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
|
||||||
}
|
}
|
||||||
|
@ -220,47 +304,6 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
|
||||||
QWidget::closeEvent(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) {
|
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
|
||||||
// TouchBegin always has exactly one touch point, so take the .first()
|
// TouchBegin always has exactly one touch point, so take the .first()
|
||||||
const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
|
const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
|
||||||
|
@ -313,35 +356,60 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
|
||||||
NotifyClientAreaSizeChanged(std::make_pair(width, height));
|
NotifyClientAreaSizeChanged(std::make_pair(width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::InitRenderTarget() {
|
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
|
||||||
if (child) {
|
return std::make_unique<GGLContext>(shared_context.get());
|
||||||
delete child;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (layout()) {
|
void GRenderWindow::InitRenderTarget() {
|
||||||
delete layout();
|
shared_context.reset();
|
||||||
}
|
context.reset();
|
||||||
|
|
||||||
|
delete child;
|
||||||
|
child = nullptr;
|
||||||
|
|
||||||
|
delete container;
|
||||||
|
container = nullptr;
|
||||||
|
|
||||||
|
delete layout();
|
||||||
|
|
||||||
first_frame = false;
|
first_frame = false;
|
||||||
|
|
||||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||||
QGLFormat fmt;
|
QSurfaceFormat fmt;
|
||||||
fmt.setVersion(4, 3);
|
fmt.setVersion(4, 3);
|
||||||
fmt.setProfile(QGLFormat::CoreProfile);
|
fmt.setProfile(QSurfaceFormat::CoreProfile);
|
||||||
|
// 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);
|
fmt.setSwapInterval(false);
|
||||||
|
|
||||||
// Requests a forward-compatible context, which is required to get a 3.2+ context on OS X
|
child = new GGLWidgetInternal(this, shared_context.get());
|
||||||
fmt.setOption(QGL::NoDeprecatedFunctions);
|
container = QWidget::createWindowContainer(child, this);
|
||||||
|
|
||||||
child = new GGLWidgetInternal(fmt, this);
|
|
||||||
QBoxLayout* layout = new QHBoxLayout(this);
|
QBoxLayout* layout = new QHBoxLayout(this);
|
||||||
|
layout->addWidget(container);
|
||||||
resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
|
||||||
layout->addWidget(child);
|
|
||||||
layout->setMargin(0);
|
layout->setMargin(0);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
|
// Reset minimum size to avoid unwanted resizes when this function is called for a second time.
|
||||||
|
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();
|
||||||
|
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);
|
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||||
|
|
||||||
OnFramebufferSizeChanged();
|
OnFramebufferSizeChanged();
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <QGLWidget>
|
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QWidget>
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
|
@ -21,6 +21,8 @@ class QTouchEvent;
|
||||||
class GGLWidgetInternal;
|
class GGLWidgetInternal;
|
||||||
class GMainWindow;
|
class GMainWindow;
|
||||||
class GRenderWindow;
|
class GRenderWindow;
|
||||||
|
class QSurface;
|
||||||
|
class QOpenGLContext;
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
enum class LoadCallbackStage;
|
enum class LoadCallbackStage;
|
||||||
|
@ -121,25 +123,21 @@ public:
|
||||||
void MakeCurrent() override;
|
void MakeCurrent() override;
|
||||||
void DoneCurrent() override;
|
void DoneCurrent() override;
|
||||||
void PollEvents() override;
|
void PollEvents() override;
|
||||||
|
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||||
|
|
||||||
|
void ForwardKeyPressEvent(QKeyEvent* event);
|
||||||
|
void ForwardKeyReleaseEvent(QKeyEvent* event);
|
||||||
|
|
||||||
void BackupGeometry();
|
void BackupGeometry();
|
||||||
void RestoreGeometry();
|
void RestoreGeometry();
|
||||||
void restoreGeometry(const QByteArray& geometry); // overridden
|
void restoreGeometry(const QByteArray& geometry); // overridden
|
||||||
QByteArray saveGeometry(); // overridden
|
QByteArray saveGeometry(); // overridden
|
||||||
|
|
||||||
qreal windowPixelRatio() const;
|
qreal GetWindowPixelRatio() const;
|
||||||
|
std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
|
||||||
|
|
||||||
void closeEvent(QCloseEvent* event) override;
|
void closeEvent(QCloseEvent* 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;
|
bool event(QEvent* event) override;
|
||||||
|
|
||||||
void focusOutEvent(QFocusEvent* event) override;
|
void focusOutEvent(QFocusEvent* event) override;
|
||||||
|
|
||||||
void OnClientAreaResized(unsigned width, unsigned height);
|
void OnClientAreaResized(unsigned width, unsigned height);
|
||||||
|
@ -161,7 +159,6 @@ signals:
|
||||||
void FirstFrameDisplayed();
|
void FirstFrameDisplayed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
|
|
||||||
void TouchBeginEvent(const QTouchEvent* event);
|
void TouchBeginEvent(const QTouchEvent* event);
|
||||||
void TouchUpdateEvent(const QTouchEvent* event);
|
void TouchUpdateEvent(const QTouchEvent* event);
|
||||||
void TouchEndEvent();
|
void TouchEndEvent();
|
||||||
|
@ -169,11 +166,17 @@ private:
|
||||||
void OnMinimalClientAreaChangeRequest(
|
void OnMinimalClientAreaChangeRequest(
|
||||||
const std::pair<unsigned, unsigned>& minimal_size) override;
|
const std::pair<unsigned, unsigned>& minimal_size) override;
|
||||||
|
|
||||||
GGLWidgetInternal* child;
|
QWidget* container = nullptr;
|
||||||
|
GGLWidgetInternal* child = nullptr;
|
||||||
|
|
||||||
QByteArray geometry;
|
QByteArray geometry;
|
||||||
|
|
||||||
EmuThread* emu_thread;
|
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;
|
||||||
|
|
||||||
/// Temporary storage of the screenshot taken
|
/// Temporary storage of the screenshot taken
|
||||||
QImage screenshot_image;
|
QImage screenshot_image;
|
||||||
|
|
|
@ -2031,6 +2031,18 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
|
||||||
event->acceptProposedAction();
|
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() {
|
bool GMainWindow::ConfirmChangeGame() {
|
||||||
if (emu_thread == nullptr)
|
if (emu_thread == nullptr)
|
||||||
return true;
|
return true;
|
||||||
|
@ -2098,7 +2110,8 @@ int main(int argc, char* argv[]) {
|
||||||
QCoreApplication::setOrganizationName("yuzu team");
|
QCoreApplication::setOrganizationName("yuzu team");
|
||||||
QCoreApplication::setApplicationName("yuzu");
|
QCoreApplication::setApplicationName("yuzu");
|
||||||
|
|
||||||
QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
|
// Enables the core to make the qt created contexts current on std::threads
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
// Qt changes the locale and causes issues in float conversion using std::to_string() when
|
// Qt changes the locale and causes issues in float conversion using std::to_string() when
|
||||||
|
|
|
@ -252,4 +252,8 @@ protected:
|
||||||
void dropEvent(QDropEvent* event) override;
|
void dropEvent(QDropEvent* event) override;
|
||||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||||
void dragMoveEvent(QDragMoveEvent* 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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,6 +19,37 @@
|
||||||
#include "input_common/sdl/sdl.h"
|
#include "input_common/sdl/sdl.h"
|
||||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||||
|
|
||||||
|
class SDLGLContext : public Core::Frontend::GraphicsContext {
|
||||||
|
public:
|
||||||
|
explicit SDLGLContext() {
|
||||||
|
// create a hidden window to make the shared context against
|
||||||
|
window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position
|
||||||
|
SDL_WINDOWPOS_UNDEFINED, // y position
|
||||||
|
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
|
||||||
|
SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
|
||||||
|
context = SDL_GL_CreateContext(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SDLGLContext() {
|
||||||
|
SDL_GL_DeleteContext(context);
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MakeCurrent() override {
|
||||||
|
SDL_GL_MakeCurrent(window, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoneCurrent() override {
|
||||||
|
SDL_GL_MakeCurrent(window, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapBuffers() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SDL_Window* window;
|
||||||
|
SDL_GLContext context;
|
||||||
|
};
|
||||||
|
|
||||||
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
|
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
|
||||||
TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
|
TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
|
||||||
InputCommon::GetMotionEmu()->Tilt(x, y);
|
InputCommon::GetMotionEmu()->Tilt(x, y);
|
||||||
|
@ -153,6 +184,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
|
||||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
|
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
|
||||||
|
|
||||||
std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
|
std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
|
||||||
Common::g_scm_branch, Common::g_scm_desc);
|
Common::g_scm_branch, Common::g_scm_desc);
|
||||||
|
@ -171,7 +203,6 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
Fullscreen();
|
Fullscreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
gl_context = SDL_GL_CreateContext(render_window);
|
gl_context = SDL_GL_CreateContext(render_window);
|
||||||
|
|
||||||
if (gl_context == nullptr) {
|
if (gl_context == nullptr) {
|
||||||
|
@ -278,3 +309,7 @@ void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
|
||||||
|
|
||||||
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
|
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const {
|
||||||
|
return std::make_unique<SDLGLContext>();
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ public:
|
||||||
/// Releases the GL context from the caller thread
|
/// Releases the GL context from the caller thread
|
||||||
void DoneCurrent() override;
|
void DoneCurrent() override;
|
||||||
|
|
||||||
|
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||||
|
|
||||||
/// Whether the window is still open, and a close request hasn't yet been sent
|
/// Whether the window is still open, and a close request hasn't yet been sent
|
||||||
bool IsOpen() const;
|
bool IsOpen() const;
|
||||||
|
|
||||||
|
|
Reference in New Issue