summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/frontend/emu_window.h39
-rw-r--r--src/yuzu/bootmanager.cpp91
-rw-r--r--src/yuzu/bootmanager.h10
-rw-r--r--src/yuzu/main.cpp3
4 files changed, 113 insertions, 30 deletions
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 7006a37b3..75c2be4ae 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -13,6 +13,23 @@
13namespace Core::Frontend { 13namespace Core::Frontend {
14 14
15/** 15/**
16 * Represents a graphics context that can be used for background computation or drawing. If the
17 * graphics backend doesn't require the context, then the implementation of these methods can be
18 * stubs
19 */
20class GraphicsContext {
21public:
22 /// Makes the graphics context current for the caller thread
23 virtual void MakeCurrent() = 0;
24
25 /// Releases (dunno if this is the "right" word) the context from the caller thread
26 virtual void DoneCurrent() = 0;
27
28 /// Swap buffers to display the next frame
29 virtual void SwapBuffers() = 0;
30};
31
32/**
16 * Abstraction class used to provide an interface between emulation code and the frontend 33 * Abstraction class used to provide an interface between emulation code and the frontend
17 * (e.g. SDL, QGLWidget, GLFW, etc...). 34 * (e.g. SDL, QGLWidget, GLFW, etc...).
18 * 35 *
@@ -30,7 +47,7 @@ namespace Core::Frontend {
30 * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please 47 * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
31 * re-read the upper points again and think about it if you don't see this. 48 * re-read the upper points again and think about it if you don't see this.
32 */ 49 */
33class EmuWindow { 50class EmuWindow : public GraphicsContext {
34public: 51public:
35 /// Data structure to store emuwindow configuration 52 /// Data structure to store emuwindow configuration
36 struct WindowConfig { 53 struct WindowConfig {
@@ -40,17 +57,21 @@ public:
40 std::pair<unsigned, unsigned> min_client_area_size; 57 std::pair<unsigned, unsigned> min_client_area_size;
41 }; 58 };
42 59
43 /// Swap buffers to display the next frame
44 virtual void SwapBuffers() = 0;
45
46 /// Polls window events 60 /// Polls window events
47 virtual void PollEvents() = 0; 61 virtual void PollEvents() = 0;
48 62
49 /// Makes the graphics context current for the caller thread 63 /**
50 virtual void MakeCurrent() = 0; 64 * Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
51 65 * context can be used from other threads for background graphics computation. If the frontend
52 /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread 66 * is using a graphics backend that doesn't need anything specific to run on a different thread,
53 virtual void DoneCurrent() = 0; 67 * then it can use a stubbed implemenation for GraphicsContext.
68 *
69 * If the return value is null, then the core should assume that the frontend cannot provide a
70 * Shared Context
71 */
72 virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
73 return nullptr;
74 }
54 75
55 /** 76 /**
56 * Signal that a touch pressed event has occurred (e.g. mouse click pressed) 77 * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index f74cb693a..087ee8f93 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -1,6 +1,13 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
1#include <QApplication> 5#include <QApplication>
2#include <QHBoxLayout> 6#include <QHBoxLayout>
3#include <QKeyEvent> 7#include <QKeyEvent>
8#include <QOffscreenSurface>
9#include <QOpenGLWindow>
10#include <QPainter>
4#include <QScreen> 11#include <QScreen>
5#include <QWindow> 12#include <QWindow>
6#include <fmt/format.h> 13#include <fmt/format.h>
@@ -73,13 +80,36 @@ void EmuThread::run() {
73 render_window->moveContext(); 80 render_window->moveContext();
74} 81}
75 82
83class GGLContext : public Core::Frontend::GraphicsContext {
84public:
85 explicit GGLContext(QOpenGLContext* shared_context) : surface() {
86 context = std::make_unique<QOpenGLContext>(shared_context);
87 surface.setFormat(shared_context->format());
88 surface.create();
89 }
90
91 void MakeCurrent() override {
92 context->makeCurrent(&surface);
93 }
94
95 void DoneCurrent() override {
96 context->doneCurrent();
97 }
98
99 void SwapBuffers() override {}
100
101private:
102 std::unique_ptr<QOpenGLContext> context;
103 QOffscreenSurface surface;
104};
105
76// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL 106// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
77// context. 107// context.
78// The corresponding functionality is handled in EmuThread instead 108// The corresponding functionality is handled in EmuThread instead
79class GGLWidgetInternal : public QGLWidget { 109class GGLWidgetInternal : public QOpenGLWindow {
80public: 110public:
81 GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) 111 GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
82 : QGLWidget(fmt, parent), parent(parent) {} 112 : QOpenGLWindow(shared_context), parent(parent) {}
83 113
84 void paintEvent(QPaintEvent* ev) override { 114 void paintEvent(QPaintEvent* ev) override {
85 if (do_painting) { 115 if (do_painting) {
@@ -105,7 +135,7 @@ private:
105}; 135};
106 136
107GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) 137GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
108 : QWidget(parent), child(nullptr), emu_thread(emu_thread) { 138 : QWidget(parent), child(nullptr), context(nullptr), emu_thread(emu_thread) {
109 139
110 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") 140 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
111 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); 141 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
@@ -129,19 +159,19 @@ void GRenderWindow::moveContext() {
129 auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) 159 auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
130 ? emu_thread 160 ? emu_thread
131 : qApp->thread(); 161 : qApp->thread();
132 child->context()->moveToThread(thread); 162 context->moveToThread(thread);
133} 163}
134 164
135void GRenderWindow::SwapBuffers() { 165void GRenderWindow::SwapBuffers() {
136 // In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`, 166 // In our multi-threaded QWidget use case we shouldn't need to call `makeCurrent`,
137 // since we never call `doneCurrent` in this thread. 167 // since we never call `doneCurrent` in this thread.
138 // However: 168 // However:
139 // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called 169 // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called
140 // since the last time `swapBuffers` was executed; 170 // since the last time `swapBuffers` was executed;
141 // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. 171 // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks.
142 child->makeCurrent(); 172 context->makeCurrent(child);
143 173
144 child->swapBuffers(); 174 context->swapBuffers(child);
145 if (!first_frame) { 175 if (!first_frame) {
146 emit FirstFrameDisplayed(); 176 emit FirstFrameDisplayed();
147 first_frame = true; 177 first_frame = true;
@@ -149,11 +179,11 @@ void GRenderWindow::SwapBuffers() {
149} 179}
150 180
151void GRenderWindow::MakeCurrent() { 181void GRenderWindow::MakeCurrent() {
152 child->makeCurrent(); 182 context->makeCurrent(child);
153} 183}
154 184
155void GRenderWindow::DoneCurrent() { 185void GRenderWindow::DoneCurrent() {
156 child->doneCurrent(); 186 context->doneCurrent();
157} 187}
158 188
159void GRenderWindow::PollEvents() {} 189void GRenderWindow::PollEvents() {}
@@ -173,7 +203,7 @@ void GRenderWindow::OnFramebufferSizeChanged() {
173} 203}
174 204
175void GRenderWindow::BackupGeometry() { 205void GRenderWindow::BackupGeometry() {
176 geometry = ((QGLWidget*)this)->saveGeometry(); 206 geometry = ((QWidget*)this)->saveGeometry();
177} 207}
178 208
179void GRenderWindow::RestoreGeometry() { 209void GRenderWindow::RestoreGeometry() {
@@ -191,7 +221,7 @@ QByteArray GRenderWindow::saveGeometry() {
191 // If we are a top-level widget, store the current geometry 221 // If we are a top-level widget, store the current geometry
192 // otherwise, store the last backup 222 // otherwise, store the last backup
193 if (parent() == nullptr) 223 if (parent() == nullptr)
194 return ((QGLWidget*)this)->saveGeometry(); 224 return ((QWidget*)this)->saveGeometry();
195 else 225 else
196 return geometry; 226 return geometry;
197} 227}
@@ -305,7 +335,19 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
305 NotifyClientAreaSizeChanged(std::make_pair(width, height)); 335 NotifyClientAreaSizeChanged(std::make_pair(width, height));
306} 336}
307 337
338std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
339 return std::make_unique<GGLContext>(shared_context.get());
340}
341
308void GRenderWindow::InitRenderTarget() { 342void GRenderWindow::InitRenderTarget() {
343 if (shared_context) {
344 shared_context.reset();
345 }
346
347 if (context) {
348 context.reset();
349 }
350
309 if (child) { 351 if (child) {
310 delete child; 352 delete child;
311 } 353 }
@@ -318,19 +360,28 @@ void GRenderWindow::InitRenderTarget() {
318 360
319 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, 361 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
320 // WA_DontShowOnScreen, WA_DeleteOnClose 362 // WA_DontShowOnScreen, WA_DeleteOnClose
321 QGLFormat fmt; 363 QSurfaceFormat fmt;
322 fmt.setVersion(4, 3); 364 fmt.setVersion(4, 3);
323 fmt.setProfile(QGLFormat::CoreProfile); 365 fmt.setProfile(QSurfaceFormat::CoreProfile);
366 // TODO: expose a setting for buffer value (ie default/single/double/triple)
367 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
368 shared_context = std::make_unique<QOpenGLContext>();
369 shared_context->setFormat(fmt);
370 shared_context->create();
371 context = std::make_unique<QOpenGLContext>();
372 context->setShareContext(shared_context.get());
373 context->setFormat(fmt);
374 context->create();
324 fmt.setSwapInterval(false); 375 fmt.setSwapInterval(false);
325 376
326 // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X 377 child = new GGLWidgetInternal(this, shared_context.get());
327 fmt.setOption(QGL::NoDeprecatedFunctions); 378 QWidget* container = QWidget::createWindowContainer(child, this);
328
329 child = new GGLWidgetInternal(fmt, this);
330 QBoxLayout* layout = new QHBoxLayout(this); 379 QBoxLayout* layout = new QHBoxLayout(this);
331 380
332 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); 381 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
333 layout->addWidget(child); 382 child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
383 container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
384 layout->addWidget(container);
334 layout->setMargin(0); 385 layout->setMargin(0);
335 setLayout(layout); 386 setLayout(layout);
336 387
@@ -340,6 +391,8 @@ void GRenderWindow::InitRenderTarget() {
340 NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(child->width(), child->height())); 391 NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(child->width(), child->height()));
341 392
342 BackupGeometry(); 393 BackupGeometry();
394 // show causes the window to actually be created and the gl context as well
395 show();
343} 396}
344 397
345void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) { 398void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index d1f37e503..d2a440d0d 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -7,9 +7,9 @@
7#include <atomic> 7#include <atomic>
8#include <condition_variable> 8#include <condition_variable>
9#include <mutex> 9#include <mutex>
10#include <QGLWidget>
11#include <QImage> 10#include <QImage>
12#include <QThread> 11#include <QThread>
12#include <QWidget>
13#include "common/thread.h" 13#include "common/thread.h"
14#include "core/core.h" 14#include "core/core.h"
15#include "core/frontend/emu_window.h" 15#include "core/frontend/emu_window.h"
@@ -21,6 +21,8 @@ class QTouchEvent;
21class GGLWidgetInternal; 21class GGLWidgetInternal;
22class GMainWindow; 22class GMainWindow;
23class GRenderWindow; 23class GRenderWindow;
24class QSurface;
25class QOpenGLContext;
24 26
25class EmuThread : public QThread { 27class EmuThread : public QThread {
26 Q_OBJECT 28 Q_OBJECT
@@ -115,6 +117,7 @@ public:
115 void MakeCurrent() override; 117 void MakeCurrent() override;
116 void DoneCurrent() override; 118 void DoneCurrent() override;
117 void PollEvents() override; 119 void PollEvents() override;
120 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
118 121
119 void BackupGeometry(); 122 void BackupGeometry();
120 void RestoreGeometry(); 123 void RestoreGeometry();
@@ -168,6 +171,11 @@ private:
168 QByteArray geometry; 171 QByteArray geometry;
169 172
170 EmuThread* emu_thread; 173 EmuThread* emu_thread;
174 // Context that backs the GGLWidgetInternal (and will be used by core to render)
175 std::unique_ptr<QOpenGLContext> context;
176 // Context that will be shared between all newly created contexts. This should never be made
177 // current
178 std::unique_ptr<QOpenGLContext> shared_context;
171 179
172 /// Temporary storage of the screenshot taken 180 /// Temporary storage of the screenshot taken
173 QImage screenshot_image; 181 QImage screenshot_image;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 2c3e27c2e..c8db7b907 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2032,7 +2032,8 @@ int main(int argc, char* argv[]) {
2032 QCoreApplication::setOrganizationName("yuzu team"); 2032 QCoreApplication::setOrganizationName("yuzu team");
2033 QCoreApplication::setApplicationName("yuzu"); 2033 QCoreApplication::setApplicationName("yuzu");
2034 2034
2035 QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 2035 // Enables the core to make the qt created contexts current on std::threads
2036 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
2036 QApplication app(argc, argv); 2037 QApplication app(argc, argv);
2037 2038
2038 // Qt changes the locale and causes issues in float conversion using std::to_string() when 2039 // Qt changes the locale and causes issues in float conversion using std::to_string() when