summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/frontend/emu_window.h39
-rw-r--r--src/yuzu/bootmanager.cpp208
-rw-r--r--src/yuzu/bootmanager.h29
-rw-r--r--src/yuzu/main.cpp15
-rw-r--r--src/yuzu/main.h4
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp37
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h2
7 files changed, 240 insertions, 94 deletions
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index d0bcb4660..70a522556 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 7438fbc0a..c29f2d2dc 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>
@@ -82,13 +89,36 @@ void EmuThread::run() {
82 render_window->moveContext(); 89 render_window->moveContext();
83} 90}
84 91
92class GGLContext : public Core::Frontend::GraphicsContext {
93public:
94 explicit GGLContext(QOpenGLContext* shared_context) : surface() {
95 context = std::make_unique<QOpenGLContext>(shared_context);
96 surface.setFormat(shared_context->format());
97 surface.create();
98 }
99
100 void MakeCurrent() override {
101 context->makeCurrent(&surface);
102 }
103
104 void DoneCurrent() override {
105 context->doneCurrent();
106 }
107
108 void SwapBuffers() override {}
109
110private:
111 std::unique_ptr<QOpenGLContext> context;
112 QOffscreenSurface surface;
113};
114
85// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL 115// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
86// context. 116// context.
87// The corresponding functionality is handled in EmuThread instead 117// The corresponding functionality is handled in EmuThread instead
88class GGLWidgetInternal : public QGLWidget { 118class GGLWidgetInternal : public QOpenGLWindow {
89public: 119public:
90 GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) 120 GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
91 : QGLWidget(fmt, parent), parent(parent) {} 121 : QOpenGLWindow(shared_context), parent(parent) {}
92 122
93 void paintEvent(QPaintEvent* ev) override { 123 void paintEvent(QPaintEvent* ev) override {
94 if (do_painting) { 124 if (do_painting) {
@@ -101,9 +131,51 @@ public:
101 parent->OnFramebufferSizeChanged(); 131 parent->OnFramebufferSizeChanged();
102 } 132 }
103 133
134 void keyPressEvent(QKeyEvent* event) override {
135 InputCommon::GetKeyboard()->PressKey(event->key());
136 }
137
138 void keyReleaseEvent(QKeyEvent* event) override {
139 InputCommon::GetKeyboard()->ReleaseKey(event->key());
140 }
141
142 void mousePressEvent(QMouseEvent* event) override {
143 if (event->source() == Qt::MouseEventSynthesizedBySystem)
144 return; // touch input is handled in TouchBeginEvent
145
146 const auto pos{event->pos()};
147 if (event->button() == Qt::LeftButton) {
148 const auto [x, y] = parent->ScaleTouch(pos);
149 parent->TouchPressed(x, y);
150 } else if (event->button() == Qt::RightButton) {
151 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
152 }
153 }
154
155 void mouseMoveEvent(QMouseEvent* event) override {
156 if (event->source() == Qt::MouseEventSynthesizedBySystem)
157 return; // touch input is handled in TouchUpdateEvent
158
159 const auto pos{event->pos()};
160 const auto [x, y] = parent->ScaleTouch(pos);
161 parent->TouchMoved(x, y);
162 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
163 }
164
165 void mouseReleaseEvent(QMouseEvent* event) override {
166 if (event->source() == Qt::MouseEventSynthesizedBySystem)
167 return; // touch input is handled in TouchEndEvent
168
169 if (event->button() == Qt::LeftButton)
170 parent->TouchReleased();
171 else if (event->button() == Qt::RightButton)
172 InputCommon::GetMotionEmu()->EndTilt();
173 }
174
104 void DisablePainting() { 175 void DisablePainting() {
105 do_painting = false; 176 do_painting = false;
106 } 177 }
178
107 void EnablePainting() { 179 void EnablePainting() {
108 do_painting = true; 180 do_painting = true;
109 } 181 }
@@ -114,7 +186,7 @@ private:
114}; 186};
115 187
116GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) 188GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
117 : QWidget(parent), child(nullptr), emu_thread(emu_thread) { 189 : QWidget(parent), child(nullptr), context(nullptr), emu_thread(emu_thread) {
118 190
119 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") 191 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
120 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); 192 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
@@ -137,19 +209,19 @@ void GRenderWindow::moveContext() {
137 auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) 209 auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
138 ? emu_thread 210 ? emu_thread
139 : qApp->thread(); 211 : qApp->thread();
140 child->context()->moveToThread(thread); 212 context->moveToThread(thread);
141} 213}
142 214
143void GRenderWindow::SwapBuffers() { 215void GRenderWindow::SwapBuffers() {
144 // In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`, 216 // In our multi-threaded QWidget use case we shouldn't need to call `makeCurrent`,
145 // since we never call `doneCurrent` in this thread. 217 // since we never call `doneCurrent` in this thread.
146 // However: 218 // However:
147 // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called 219 // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called
148 // since the last time `swapBuffers` was executed; 220 // since the last time `swapBuffers` was executed;
149 // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. 221 // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks.
150 child->makeCurrent(); 222 context->makeCurrent(child);
151 223
152 child->swapBuffers(); 224 context->swapBuffers(child);
153 if (!first_frame) { 225 if (!first_frame) {
154 emit FirstFrameDisplayed(); 226 emit FirstFrameDisplayed();
155 first_frame = true; 227 first_frame = true;
@@ -157,11 +229,11 @@ void GRenderWindow::SwapBuffers() {
157} 229}
158 230
159void GRenderWindow::MakeCurrent() { 231void GRenderWindow::MakeCurrent() {
160 child->makeCurrent(); 232 context->makeCurrent(child);
161} 233}
162 234
163void GRenderWindow::DoneCurrent() { 235void GRenderWindow::DoneCurrent() {
164 child->doneCurrent(); 236 context->doneCurrent();
165} 237}
166 238
167void GRenderWindow::PollEvents() {} 239void GRenderWindow::PollEvents() {}
@@ -174,14 +246,26 @@ void GRenderWindow::PollEvents() {}
174void GRenderWindow::OnFramebufferSizeChanged() { 246void GRenderWindow::OnFramebufferSizeChanged() {
175 // Screen changes potentially incur a change in screen DPI, hence we should update the 247 // Screen changes potentially incur a change in screen DPI, hence we should update the
176 // framebuffer size 248 // framebuffer size
177 qreal pixelRatio = windowPixelRatio(); 249 qreal pixelRatio = GetWindowPixelRatio();
178 unsigned width = child->QPaintDevice::width() * pixelRatio; 250 unsigned width = child->QPaintDevice::width() * pixelRatio;
179 unsigned height = child->QPaintDevice::height() * pixelRatio; 251 unsigned height = child->QPaintDevice::height() * pixelRatio;
180 UpdateCurrentFramebufferLayout(width, height); 252 UpdateCurrentFramebufferLayout(width, height);
181} 253}
182 254
255void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
256 if (child) {
257 child->keyPressEvent(event);
258 }
259}
260
261void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) {
262 if (child) {
263 child->keyReleaseEvent(event);
264 }
265}
266
183void GRenderWindow::BackupGeometry() { 267void GRenderWindow::BackupGeometry() {
184 geometry = ((QGLWidget*)this)->saveGeometry(); 268 geometry = ((QWidget*)this)->saveGeometry();
185} 269}
186 270
187void GRenderWindow::RestoreGeometry() { 271void GRenderWindow::RestoreGeometry() {
@@ -199,18 +283,18 @@ QByteArray GRenderWindow::saveGeometry() {
199 // If we are a top-level widget, store the current geometry 283 // If we are a top-level widget, store the current geometry
200 // otherwise, store the last backup 284 // otherwise, store the last backup
201 if (parent() == nullptr) 285 if (parent() == nullptr)
202 return ((QGLWidget*)this)->saveGeometry(); 286 return ((QWidget*)this)->saveGeometry();
203 else 287 else
204 return geometry; 288 return geometry;
205} 289}
206 290
207qreal GRenderWindow::windowPixelRatio() const { 291qreal GRenderWindow::GetWindowPixelRatio() const {
208 // windowHandle() might not be accessible until the window is displayed to screen. 292 // windowHandle() might not be accessible until the window is displayed to screen.
209 return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; 293 return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
210} 294}
211 295
212std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { 296std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const {
213 const qreal pixel_ratio = windowPixelRatio(); 297 const qreal pixel_ratio = GetWindowPixelRatio();
214 return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), 298 return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
215 static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; 299 static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
216} 300}
@@ -220,47 +304,6 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
220 QWidget::closeEvent(event); 304 QWidget::closeEvent(event);
221} 305}
222 306
223void GRenderWindow::keyPressEvent(QKeyEvent* event) {
224 InputCommon::GetKeyboard()->PressKey(event->key());
225}
226
227void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
228 InputCommon::GetKeyboard()->ReleaseKey(event->key());
229}
230
231void GRenderWindow::mousePressEvent(QMouseEvent* event) {
232 if (event->source() == Qt::MouseEventSynthesizedBySystem)
233 return; // touch input is handled in TouchBeginEvent
234
235 auto pos = event->pos();
236 if (event->button() == Qt::LeftButton) {
237 const auto [x, y] = ScaleTouch(pos);
238 this->TouchPressed(x, y);
239 } else if (event->button() == Qt::RightButton) {
240 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
241 }
242}
243
244void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
245 if (event->source() == Qt::MouseEventSynthesizedBySystem)
246 return; // touch input is handled in TouchUpdateEvent
247
248 auto pos = event->pos();
249 const auto [x, y] = ScaleTouch(pos);
250 this->TouchMoved(x, y);
251 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
252}
253
254void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
255 if (event->source() == Qt::MouseEventSynthesizedBySystem)
256 return; // touch input is handled in TouchEndEvent
257
258 if (event->button() == Qt::LeftButton)
259 this->TouchReleased();
260 else if (event->button() == Qt::RightButton)
261 InputCommon::GetMotionEmu()->EndTilt();
262}
263
264void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { 307void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
265 // TouchBegin always has exactly one touch point, so take the .first() 308 // TouchBegin always has exactly one touch point, so take the .first()
266 const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); 309 const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
@@ -313,35 +356,60 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
313 NotifyClientAreaSizeChanged(std::make_pair(width, height)); 356 NotifyClientAreaSizeChanged(std::make_pair(width, height));
314} 357}
315 358
359std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
360 return std::make_unique<GGLContext>(shared_context.get());
361}
362
316void GRenderWindow::InitRenderTarget() { 363void GRenderWindow::InitRenderTarget() {
317 if (child) { 364 shared_context.reset();
318 delete child; 365 context.reset();
319 }
320 366
321 if (layout()) { 367 delete child;
322 delete layout(); 368 child = nullptr;
323 } 369
370 delete container;
371 container = nullptr;
372
373 delete layout();
324 374
325 first_frame = false; 375 first_frame = false;
326 376
327 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, 377 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
328 // WA_DontShowOnScreen, WA_DeleteOnClose 378 // WA_DontShowOnScreen, WA_DeleteOnClose
329 QGLFormat fmt; 379 QSurfaceFormat fmt;
330 fmt.setVersion(4, 3); 380 fmt.setVersion(4, 3);
331 fmt.setProfile(QGLFormat::CoreProfile); 381 fmt.setProfile(QSurfaceFormat::CoreProfile);
382 // TODO: expose a setting for buffer value (ie default/single/double/triple)
383 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
384 shared_context = std::make_unique<QOpenGLContext>();
385 shared_context->setFormat(fmt);
386 shared_context->create();
387 context = std::make_unique<QOpenGLContext>();
388 context->setShareContext(shared_context.get());
389 context->setFormat(fmt);
390 context->create();
332 fmt.setSwapInterval(false); 391 fmt.setSwapInterval(false);
333 392
334 // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X 393 child = new GGLWidgetInternal(this, shared_context.get());
335 fmt.setOption(QGL::NoDeprecatedFunctions); 394 container = QWidget::createWindowContainer(child, this);
336 395
337 child = new GGLWidgetInternal(fmt, this);
338 QBoxLayout* layout = new QHBoxLayout(this); 396 QBoxLayout* layout = new QHBoxLayout(this);
339 397 layout->addWidget(container);
340 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
341 layout->addWidget(child);
342 layout->setMargin(0); 398 layout->setMargin(0);
343 setLayout(layout); 399 setLayout(layout);
344 400
401 // Reset minimum size to avoid unwanted resizes when this function is called for a second time.
402 setMinimumSize(1, 1);
403
404 // Show causes the window to actually be created and the OpenGL context as well, but we don't
405 // want the widget to be shown yet, so immediately hide it.
406 show();
407 hide();
408
409 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
410 child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
411 container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
412
345 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 413 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
346 414
347 OnFramebufferSizeChanged(); 415 OnFramebufferSizeChanged();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 3183621bc..9608b959f 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
25namespace VideoCore { 27namespace VideoCore {
26enum class LoadCallbackStage; 28enum class LoadCallbackStage;
@@ -121,25 +123,21 @@ public:
121 void MakeCurrent() override; 123 void MakeCurrent() override;
122 void DoneCurrent() override; 124 void DoneCurrent() override;
123 void PollEvents() override; 125 void PollEvents() override;
126 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
127
128 void ForwardKeyPressEvent(QKeyEvent* event);
129 void ForwardKeyReleaseEvent(QKeyEvent* event);
124 130
125 void BackupGeometry(); 131 void BackupGeometry();
126 void RestoreGeometry(); 132 void RestoreGeometry();
127 void restoreGeometry(const QByteArray& geometry); // overridden 133 void restoreGeometry(const QByteArray& geometry); // overridden
128 QByteArray saveGeometry(); // overridden 134 QByteArray saveGeometry(); // overridden
129 135
130 qreal windowPixelRatio() const; 136 qreal GetWindowPixelRatio() const;
137 std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
131 138
132 void closeEvent(QCloseEvent* event) override; 139 void closeEvent(QCloseEvent* event) override;
133
134 void keyPressEvent(QKeyEvent* event) override;
135 void keyReleaseEvent(QKeyEvent* event) override;
136
137 void mousePressEvent(QMouseEvent* event) override;
138 void mouseMoveEvent(QMouseEvent* event) override;
139 void mouseReleaseEvent(QMouseEvent* event) override;
140
141 bool event(QEvent* event) override; 140 bool event(QEvent* event) override;
142
143 void focusOutEvent(QFocusEvent* event) override; 141 void focusOutEvent(QFocusEvent* event) override;
144 142
145 void OnClientAreaResized(unsigned width, unsigned height); 143 void OnClientAreaResized(unsigned width, unsigned height);
@@ -161,7 +159,6 @@ signals:
161 void FirstFrameDisplayed(); 159 void FirstFrameDisplayed();
162 160
163private: 161private:
164 std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
165 void TouchBeginEvent(const QTouchEvent* event); 162 void TouchBeginEvent(const QTouchEvent* event);
166 void TouchUpdateEvent(const QTouchEvent* event); 163 void TouchUpdateEvent(const QTouchEvent* event);
167 void TouchEndEvent(); 164 void TouchEndEvent();
@@ -169,11 +166,17 @@ private:
169 void OnMinimalClientAreaChangeRequest( 166 void OnMinimalClientAreaChangeRequest(
170 const std::pair<unsigned, unsigned>& minimal_size) override; 167 const std::pair<unsigned, unsigned>& minimal_size) override;
171 168
172 GGLWidgetInternal* child; 169 QWidget* container = nullptr;
170 GGLWidgetInternal* child = nullptr;
173 171
174 QByteArray geometry; 172 QByteArray geometry;
175 173
176 EmuThread* emu_thread; 174 EmuThread* emu_thread;
175 // Context that backs the GGLWidgetInternal (and will be used by core to render)
176 std::unique_ptr<QOpenGLContext> context;
177 // Context that will be shared between all newly created contexts. This should never be made
178 // current
179 std::unique_ptr<QOpenGLContext> shared_context;
177 180
178 /// Temporary storage of the screenshot taken 181 /// Temporary storage of the screenshot taken
179 QImage screenshot_image; 182 QImage screenshot_image;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 588f53860..bdee44b04 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2031,6 +2031,18 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
2031 event->acceptProposedAction(); 2031 event->acceptProposedAction();
2032} 2032}
2033 2033
2034void GMainWindow::keyPressEvent(QKeyEvent* event) {
2035 if (render_window) {
2036 render_window->ForwardKeyPressEvent(event);
2037 }
2038}
2039
2040void GMainWindow::keyReleaseEvent(QKeyEvent* event) {
2041 if (render_window) {
2042 render_window->ForwardKeyReleaseEvent(event);
2043 }
2044}
2045
2034bool GMainWindow::ConfirmChangeGame() { 2046bool GMainWindow::ConfirmChangeGame() {
2035 if (emu_thread == nullptr) 2047 if (emu_thread == nullptr)
2036 return true; 2048 return true;
@@ -2098,7 +2110,8 @@ int main(int argc, char* argv[]) {
2098 QCoreApplication::setOrganizationName("yuzu team"); 2110 QCoreApplication::setOrganizationName("yuzu team");
2099 QCoreApplication::setApplicationName("yuzu"); 2111 QCoreApplication::setApplicationName("yuzu");
2100 2112
2101 QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 2113 // Enables the core to make the qt created contexts current on std::threads
2114 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
2102 QApplication app(argc, argv); 2115 QApplication app(argc, argv);
2103 2116
2104 // Qt changes the locale and causes issues in float conversion using std::to_string() when 2117 // 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 85e3810f2..ce5045819 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -252,4 +252,8 @@ protected:
252 void dropEvent(QDropEvent* event) override; 252 void dropEvent(QDropEvent* event) override;
253 void dragEnterEvent(QDragEnterEvent* event) override; 253 void dragEnterEvent(QDragEnterEvent* event) override;
254 void dragMoveEvent(QDragMoveEvent* event) override; 254 void dragMoveEvent(QDragMoveEvent* event) override;
255
256 // Overrides used to forward signals to the render window when the focus moves out.
257 void keyPressEvent(QKeyEvent* event) override;
258 void keyReleaseEvent(QKeyEvent* event) override;
255}; 259};
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index de7a26e14..68a176032 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -19,6 +19,37 @@
19#include "input_common/sdl/sdl.h" 19#include "input_common/sdl/sdl.h"
20#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 20#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
21 21
22class SDLGLContext : public Core::Frontend::GraphicsContext {
23public:
24 explicit SDLGLContext() {
25 // create a hidden window to make the shared context against
26 window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position
27 SDL_WINDOWPOS_UNDEFINED, // y position
28 Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
29 SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
30 context = SDL_GL_CreateContext(window);
31 }
32
33 ~SDLGLContext() {
34 SDL_GL_DeleteContext(context);
35 SDL_DestroyWindow(window);
36 }
37
38 void MakeCurrent() override {
39 SDL_GL_MakeCurrent(window, context);
40 }
41
42 void DoneCurrent() override {
43 SDL_GL_MakeCurrent(window, nullptr);
44 }
45
46 void SwapBuffers() override {}
47
48private:
49 SDL_Window* window;
50 SDL_GLContext context;
51};
52
22void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 53void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
23 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); 54 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
24 InputCommon::GetMotionEmu()->Tilt(x, y); 55 InputCommon::GetMotionEmu()->Tilt(x, y);
@@ -153,6 +184,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
153 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); 184 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
154 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); 185 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
155 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); 186 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
187 SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
156 188
157 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, 189 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
158 Common::g_scm_branch, Common::g_scm_desc); 190 Common::g_scm_branch, Common::g_scm_desc);
@@ -171,7 +203,6 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
171 if (fullscreen) { 203 if (fullscreen) {
172 Fullscreen(); 204 Fullscreen();
173 } 205 }
174
175 gl_context = SDL_GL_CreateContext(render_window); 206 gl_context = SDL_GL_CreateContext(render_window);
176 207
177 if (gl_context == nullptr) { 208 if (gl_context == nullptr) {
@@ -278,3 +309,7 @@ void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
278 309
279 SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); 310 SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
280} 311}
312
313std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const {
314 return std::make_unique<SDLGLContext>();
315}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index b0d4116cc..17e98227f 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -27,6 +27,8 @@ public:
27 /// Releases the GL context from the caller thread 27 /// Releases the GL context from the caller thread
28 void DoneCurrent() override; 28 void DoneCurrent() override;
29 29
30 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
31
30 /// Whether the window is still open, and a close request hasn't yet been sent 32 /// Whether the window is still open, and a close request hasn't yet been sent
31 bool IsOpen() const; 33 bool IsOpen() const;
32 34