summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/yuzu/bootmanager.cpp404
-rw-r--r--src/yuzu/bootmanager.h72
2 files changed, 254 insertions, 222 deletions
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 4982884f5..99e0ac61e 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -9,6 +9,9 @@
9#include <QKeyEvent> 9#include <QKeyEvent>
10#include <QMessageBox> 10#include <QMessageBox>
11#include <QOffscreenSurface> 11#include <QOffscreenSurface>
12#include <QOpenGLContext>
13#include <QOpenGLFunctions>
14#include <QOpenGLFunctions_4_3_Core>
12#include <QOpenGLWindow> 15#include <QOpenGLWindow>
13#include <QPainter> 16#include <QPainter>
14#include <QScreen> 17#include <QScreen>
@@ -23,6 +26,7 @@
23#include "common/assert.h" 26#include "common/assert.h"
24#include "common/microprofile.h" 27#include "common/microprofile.h"
25#include "common/scm_rev.h" 28#include "common/scm_rev.h"
29#include "common/scope_exit.h"
26#include "core/core.h" 30#include "core/core.h"
27#include "core/frontend/framebuffer_layout.h" 31#include "core/frontend/framebuffer_layout.h"
28#include "core/frontend/scope_acquire_context.h" 32#include "core/frontend/scope_acquire_context.h"
@@ -35,15 +39,32 @@
35#include "yuzu/bootmanager.h" 39#include "yuzu/bootmanager.h"
36#include "yuzu/main.h" 40#include "yuzu/main.h"
37 41
38EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} 42EmuThread::EmuThread(Core::Frontend::GraphicsContext& core_context) : core_context(core_context) {}
39 43
40EmuThread::~EmuThread() = default; 44EmuThread::~EmuThread() = default;
41 45
42void EmuThread::run() { 46static GMainWindow* GetMainWindow() {
43 render_window->MakeCurrent(); 47 for (QWidget* w : qApp->topLevelWidgets()) {
48 if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) {
49 return main;
50 }
51 }
52 return nullptr;
53}
44 54
55void EmuThread::run() {
45 MicroProfileOnThreadCreate("EmuThread"); 56 MicroProfileOnThreadCreate("EmuThread");
46 57
58 // Acquire render context for duration of the thread if this is the rendering thread
59 if (!Settings::values.use_asynchronous_gpu_emulation) {
60 core_context.MakeCurrent();
61 }
62 SCOPE_EXIT({
63 if (!Settings::values.use_asynchronous_gpu_emulation) {
64 core_context.DoneCurrent();
65 }
66 });
67
47 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); 68 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
48 69
49 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( 70 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources(
@@ -53,11 +74,6 @@ void EmuThread::run() {
53 74
54 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); 75 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
55 76
56 if (Settings::values.use_asynchronous_gpu_emulation) {
57 // Release OpenGL context for the GPU thread
58 render_window->DoneCurrent();
59 }
60
61 // Holds whether the cpu was running during the last iteration, 77 // Holds whether the cpu was running during the last iteration,
62 // so that the DebugModeLeft signal can be emitted before the 78 // so that the DebugModeLeft signal can be emitted before the
63 // next execution step 79 // next execution step
@@ -98,190 +114,157 @@ void EmuThread::run() {
98#if MICROPROFILE_ENABLED 114#if MICROPROFILE_ENABLED
99 MicroProfileOnThreadExit(); 115 MicroProfileOnThreadExit();
100#endif 116#endif
101
102 render_window->moveContext();
103} 117}
104 118
105class GGLContext : public Core::Frontend::GraphicsContext { 119class GGLContext : public Core::Frontend::GraphicsContext {
106public: 120public:
107 explicit GGLContext(QOpenGLContext* shared_context) : shared_context{shared_context} { 121 explicit GGLContext(QOpenGLContext* shared_context)
108 context.setFormat(shared_context->format()); 122 : context(new QOpenGLContext(shared_context->parent())),
109 context.setShareContext(shared_context); 123 surface(new QOffscreenSurface(nullptr)) {
110 context.create(); 124
125 // disable vsync for any shared contexts
126 auto format = shared_context->format();
127 format.setSwapInterval(0);
128
129 context->setShareContext(shared_context);
130 context->setFormat(format);
131 context->create();
132 surface->setParent(shared_context->parent());
133 surface->setFormat(format);
134 surface->create();
111 } 135 }
112 136
113 void MakeCurrent() override { 137 void MakeCurrent() override {
114 context.makeCurrent(shared_context->surface()); 138 context->makeCurrent(surface);
115 } 139 }
116 140
117 void DoneCurrent() override { 141 void DoneCurrent() override {
118 context.doneCurrent(); 142 context->doneCurrent();
119 } 143 }
120 144
121 void SwapBuffers() override {}
122
123private: 145private:
124 QOpenGLContext* shared_context; 146 QOpenGLContext* context;
125 QOpenGLContext context; 147 QOffscreenSurface* surface;
126}; 148};
127 149
128class GWidgetInternal : public QWindow { 150OpenGLWindow::OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
129public: 151 : QWindow(parent), event_handler(event_handler),
130 GWidgetInternal(GRenderWindow* parent) : parent(parent) {} 152 context(new QOpenGLContext(shared_context->parent())) {
131 virtual ~GWidgetInternal() = default;
132
133 void resizeEvent(QResizeEvent* ev) override {
134 parent->OnClientAreaResized(ev->size().width(), ev->size().height());
135 parent->OnFramebufferSizeChanged();
136 }
137
138 void keyPressEvent(QKeyEvent* event) override {
139 InputCommon::GetKeyboard()->PressKey(event->key());
140 }
141
142 void keyReleaseEvent(QKeyEvent* event) override {
143 InputCommon::GetKeyboard()->ReleaseKey(event->key());
144 }
145 153
146 void mousePressEvent(QMouseEvent* event) override { 154 // disable vsync for any shared contexts
147 if (event->source() == Qt::MouseEventSynthesizedBySystem) 155 auto format = shared_context->format();
148 return; // touch input is handled in TouchBeginEvent 156 format.setSwapInterval(Settings::values.use_vsync ? 1 : 0);
157 this->setFormat(format);
149 158
150 const auto pos{event->pos()}; 159 context->setShareContext(shared_context);
151 if (event->button() == Qt::LeftButton) { 160 context->setScreen(this->screen());
152 const auto [x, y] = parent->ScaleTouch(pos); 161 context->setFormat(format);
153 parent->TouchPressed(x, y); 162 context->create();
154 } else if (event->button() == Qt::RightButton) {
155 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
156 }
157 }
158
159 void mouseMoveEvent(QMouseEvent* event) override {
160 if (event->source() == Qt::MouseEventSynthesizedBySystem)
161 return; // touch input is handled in TouchUpdateEvent
162
163 const auto pos{event->pos()};
164 const auto [x, y] = parent->ScaleTouch(pos);
165 parent->TouchMoved(x, y);
166 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
167 }
168
169 void mouseReleaseEvent(QMouseEvent* event) override {
170 if (event->source() == Qt::MouseEventSynthesizedBySystem)
171 return; // touch input is handled in TouchEndEvent
172
173 if (event->button() == Qt::LeftButton)
174 parent->TouchReleased();
175 else if (event->button() == Qt::RightButton)
176 InputCommon::GetMotionEmu()->EndTilt();
177 }
178
179 void DisablePainting() {
180 do_painting = false;
181 }
182 163
183 void EnablePainting() { 164 setSurfaceType(QWindow::OpenGLSurface);
184 do_painting = true;
185 }
186 165
187 std::pair<unsigned, unsigned> GetSize() const { 166 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
188 return std::make_pair(width(), height()); 167 // WA_DontShowOnScreen, WA_DeleteOnClose
189 } 168}
190 169
191protected: 170OpenGLWindow::~OpenGLWindow() {
192 bool IsPaintingEnabled() const { 171 context->doneCurrent();
193 return do_painting; 172}
194 }
195 173
196private: 174void OpenGLWindow::Present() {
197 GRenderWindow* parent; 175 if (!isExposed())
198 bool do_painting = false; 176 return;
199};
200 177
201// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL 178 context->makeCurrent(this);
202// context. 179 Core::System::GetInstance().Renderer().TryPresent(100);
203// The corresponding functionality is handled in EmuThread instead 180 context->swapBuffers(this);
204class GGLWidgetInternal final : public GWidgetInternal, public QOpenGLWindow { 181 auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
205public: 182 f->glFinish();
206 GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) 183 QWindow::requestUpdate();
207 : GWidgetInternal(parent), QOpenGLWindow(shared_context) {} 184}
208 ~GGLWidgetInternal() override = default;
209 185
210 void paintEvent(QPaintEvent* ev) override { 186bool OpenGLWindow::event(QEvent* event) {
211 if (IsPaintingEnabled()) { 187 switch (event->type()) {
212 QPainter painter(this); 188 case QEvent::UpdateRequest:
213 } 189 Present();
190 return true;
191 case QEvent::MouseButtonPress:
192 case QEvent::MouseButtonRelease:
193 case QEvent::MouseButtonDblClick:
194 case QEvent::MouseMove:
195 case QEvent::KeyPress:
196 case QEvent::KeyRelease:
197 case QEvent::FocusIn:
198 case QEvent::FocusOut:
199 case QEvent::FocusAboutToChange:
200 case QEvent::Enter:
201 case QEvent::Leave:
202 case QEvent::Wheel:
203 case QEvent::TabletMove:
204 case QEvent::TabletPress:
205 case QEvent::TabletRelease:
206 case QEvent::TabletEnterProximity:
207 case QEvent::TabletLeaveProximity:
208 case QEvent::TouchBegin:
209 case QEvent::TouchUpdate:
210 case QEvent::TouchEnd:
211 case QEvent::InputMethodQuery:
212 case QEvent::TouchCancel:
213 return QCoreApplication::sendEvent(event_handler, event);
214 case QEvent::Drop:
215 GetMainWindow()->DropAction(static_cast<QDropEvent*>(event));
216 return true;
217 case QEvent::DragResponse:
218 case QEvent::DragEnter:
219 case QEvent::DragLeave:
220 case QEvent::DragMove:
221 GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
222 return true;
223 default:
224 return QWindow::event(event);
214 } 225 }
215}; 226}
216 227
217#ifdef HAS_VULKAN 228void OpenGLWindow::exposeEvent(QExposeEvent* event) {
218class GVKWidgetInternal final : public GWidgetInternal { 229 QWindow::requestUpdate();
219public: 230 QWindow::exposeEvent(event);
220 GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) { 231}
221 setSurfaceType(QSurface::SurfaceType::VulkanSurface);
222 setVulkanInstance(instance);
223 }
224 ~GVKWidgetInternal() override = default;
225};
226#endif
227 232
228GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) 233GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
229 : QWidget(parent), emu_thread(emu_thread) { 234 : QWidget(parent_), emu_thread(emu_thread) {
230 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") 235 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
231 .arg(QString::fromUtf8(Common::g_build_name), 236 .arg(QString::fromUtf8(Common::g_build_name),
232 QString::fromUtf8(Common::g_scm_branch), 237 QString::fromUtf8(Common::g_scm_branch),
233 QString::fromUtf8(Common::g_scm_desc))); 238 QString::fromUtf8(Common::g_scm_desc)));
234 setAttribute(Qt::WA_AcceptTouchEvents); 239 setAttribute(Qt::WA_AcceptTouchEvents);
235 240 auto layout = new QHBoxLayout(this);
241 layout->setMargin(0);
242 setLayout(layout);
236 InputCommon::Init(); 243 InputCommon::Init();
244
245 GMainWindow* parent = GetMainWindow();
237 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); 246 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
238} 247}
239 248
240GRenderWindow::~GRenderWindow() { 249GRenderWindow::~GRenderWindow() {
241 InputCommon::Shutdown(); 250 InputCommon::Shutdown();
242
243 // Avoid an unordered destruction that generates a segfault
244 delete child;
245} 251}
246 252
247void GRenderWindow::moveContext() { 253void GRenderWindow::MakeCurrent() {
248 if (!context) { 254 core_context->MakeCurrent();
249 return; 255}
250 }
251 DoneCurrent();
252 256
253 // If the thread started running, move the GL Context to the new thread. Otherwise, move it 257void GRenderWindow::DoneCurrent() {
254 // back. 258 core_context->DoneCurrent();
255 auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
256 ? emu_thread
257 : qApp->thread();
258 context->moveToThread(thread);
259} 259}
260 260
261void GRenderWindow::SwapBuffers() { 261void GRenderWindow::PollEvents() {
262 if (context) {
263 context->swapBuffers(child);
264 }
265 if (!first_frame) { 262 if (!first_frame) {
266 first_frame = true; 263 first_frame = true;
267 emit FirstFrameDisplayed(); 264 emit FirstFrameDisplayed();
268 } 265 }
269} 266}
270 267
271void GRenderWindow::MakeCurrent() {
272 if (context) {
273 context->makeCurrent(child);
274 }
275}
276
277void GRenderWindow::DoneCurrent() {
278 if (context) {
279 context->doneCurrent();
280 }
281}
282
283void GRenderWindow::PollEvents() {}
284
285bool GRenderWindow::IsShown() const { 268bool GRenderWindow::IsShown() const {
286 return !isMinimized(); 269 return !isMinimized();
287} 270}
@@ -309,21 +292,10 @@ void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* i
309void GRenderWindow::OnFramebufferSizeChanged() { 292void GRenderWindow::OnFramebufferSizeChanged() {
310 // Screen changes potentially incur a change in screen DPI, hence we should update the 293 // Screen changes potentially incur a change in screen DPI, hence we should update the
311 // framebuffer size 294 // framebuffer size
312 const qreal pixelRatio{GetWindowPixelRatio()}; 295 const qreal pixel_ratio = windowPixelRatio();
313 const auto size{child->GetSize()}; 296 const u32 width = this->width() * pixel_ratio;
314 UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio); 297 const u32 height = this->height() * pixel_ratio;
315} 298 UpdateCurrentFramebufferLayout(width, height);
316
317void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
318 if (child) {
319 child->keyPressEvent(event);
320 }
321}
322
323void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) {
324 if (child) {
325 child->keyReleaseEvent(event);
326 }
327} 299}
328 300
329void GRenderWindow::BackupGeometry() { 301void GRenderWindow::BackupGeometry() {
@@ -351,13 +323,12 @@ QByteArray GRenderWindow::saveGeometry() {
351 return geometry; 323 return geometry;
352} 324}
353 325
354qreal GRenderWindow::GetWindowPixelRatio() const { 326qreal GRenderWindow::windowPixelRatio() const {
355 // windowHandle() might not be accessible until the window is displayed to screen. 327 return devicePixelRatio();
356 return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
357} 328}
358 329
359std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { 330std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const {
360 const qreal pixel_ratio{GetWindowPixelRatio()}; 331 const qreal pixel_ratio = windowPixelRatio();
361 return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), 332 return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
362 static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; 333 static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
363} 334}
@@ -367,6 +338,47 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
367 QWidget::closeEvent(event); 338 QWidget::closeEvent(event);
368} 339}
369 340
341void GRenderWindow::keyPressEvent(QKeyEvent* event) {
342 InputCommon::GetKeyboard()->PressKey(event->key());
343}
344
345void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
346 InputCommon::GetKeyboard()->ReleaseKey(event->key());
347}
348
349void GRenderWindow::mousePressEvent(QMouseEvent* event) {
350 if (event->source() == Qt::MouseEventSynthesizedBySystem)
351 return; // touch input is handled in TouchBeginEvent
352
353 auto pos = event->pos();
354 if (event->button() == Qt::LeftButton) {
355 const auto [x, y] = ScaleTouch(pos);
356 this->TouchPressed(x, y);
357 } else if (event->button() == Qt::RightButton) {
358 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
359 }
360}
361
362void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
363 if (event->source() == Qt::MouseEventSynthesizedBySystem)
364 return; // touch input is handled in TouchUpdateEvent
365
366 auto pos = event->pos();
367 const auto [x, y] = ScaleTouch(pos);
368 this->TouchMoved(x, y);
369 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
370}
371
372void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
373 if (event->source() == Qt::MouseEventSynthesizedBySystem)
374 return; // touch input is handled in TouchEndEvent
375
376 if (event->button() == Qt::LeftButton)
377 this->TouchReleased();
378 else if (event->button() == Qt::RightButton)
379 InputCommon::GetMotionEmu()->EndTilt();
380}
381
370void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { 382void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
371 // TouchBegin always has exactly one touch point, so take the .first() 383 // TouchBegin always has exactly one touch point, so take the .first()
372 const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); 384 const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
@@ -415,26 +427,20 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) {
415 InputCommon::GetKeyboard()->ReleaseAllKeys(); 427 InputCommon::GetKeyboard()->ReleaseAllKeys();
416} 428}
417 429
418void GRenderWindow::OnClientAreaResized(u32 width, u32 height) { 430void GRenderWindow::resizeEvent(QResizeEvent* event) {
419 NotifyClientAreaSizeChanged(std::make_pair(width, height)); 431 QWidget::resizeEvent(event);
432 OnFramebufferSizeChanged();
420} 433}
421 434
422std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { 435std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
423 return std::make_unique<GGLContext>(context.get()); 436 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
437 return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext());
438 }
439 return {};
424} 440}
425 441
426bool GRenderWindow::InitRenderTarget() { 442bool GRenderWindow::InitRenderTarget() {
427 shared_context.reset(); 443 ReleaseRenderTarget();
428 context.reset();
429 if (child) {
430 delete child;
431 }
432 if (container) {
433 delete container;
434 }
435 if (layout()) {
436 delete layout();
437 }
438 444
439 first_frame = false; 445 first_frame = false;
440 446
@@ -451,13 +457,6 @@ bool GRenderWindow::InitRenderTarget() {
451 break; 457 break;
452 } 458 }
453 459
454 container = QWidget::createWindowContainer(child, this);
455 QBoxLayout* layout = new QHBoxLayout(this);
456
457 layout->addWidget(container);
458 layout->setMargin(0);
459 setLayout(layout);
460
461 // Reset minimum required size to avoid resizing issues on the main window after restarting. 460 // Reset minimum required size to avoid resizing issues on the main window after restarting.
462 setMinimumSize(1, 1); 461 setMinimumSize(1, 1);
463 462
@@ -467,14 +466,9 @@ bool GRenderWindow::InitRenderTarget() {
467 hide(); 466 hide();
468 467
469 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); 468 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
470 child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
471 container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
472 469
473 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 470 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
474
475 OnFramebufferSizeChanged(); 471 OnFramebufferSizeChanged();
476 NotifyClientAreaSizeChanged(child->GetSize());
477
478 BackupGeometry(); 472 BackupGeometry();
479 473
480 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { 474 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
@@ -486,6 +480,14 @@ bool GRenderWindow::InitRenderTarget() {
486 return true; 480 return true;
487} 481}
488 482
483void GRenderWindow::ReleaseRenderTarget() {
484 if (child_widget) {
485 layout()->removeWidget(child_widget);
486 delete child_widget;
487 child_widget = nullptr;
488 }
489}
490
489void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { 491void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
490 auto& renderer = Core::System::GetInstance().Renderer(); 492 auto& renderer = Core::System::GetInstance().Renderer();
491 493
@@ -521,16 +523,20 @@ bool GRenderWindow::InitializeOpenGL() {
521 fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); 523 fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
522 // TODO: expose a setting for buffer value (ie default/single/double/triple) 524 // TODO: expose a setting for buffer value (ie default/single/double/triple)
523 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); 525 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
524 shared_context = std::make_unique<QOpenGLContext>(); 526 fmt.setSwapInterval(0);
525 shared_context->setFormat(fmt); 527 QSurfaceFormat::setDefaultFormat(fmt);
526 shared_context->create(); 528
527 context = std::make_unique<QOpenGLContext>(); 529 GMainWindow* parent = GetMainWindow();
528 context->setShareContext(shared_context.get()); 530 QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
529 context->setFormat(fmt); 531 child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext());
530 context->create(); 532 child_window->create();
531 fmt.setSwapInterval(false); 533 child_widget = createWindowContainer(child_window, this);
534 child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
535 layout()->addWidget(child_widget);
536
537 core_context = CreateSharedContext();
538 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
532 539
533 child = new GGLWidgetInternal(this, shared_context.get());
534 return true; 540 return true;
535} 541}
536 542
@@ -621,12 +627,10 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
621 627
622void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { 628void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
623 this->emu_thread = emu_thread; 629 this->emu_thread = emu_thread;
624 child->DisablePainting();
625} 630}
626 631
627void GRenderWindow::OnEmulationStopping() { 632void GRenderWindow::OnEmulationStopping() {
628 emu_thread = nullptr; 633 emu_thread = nullptr;
629 child->EnablePainting();
630} 634}
631 635
632void GRenderWindow::showEvent(QShowEvent* event) { 636void GRenderWindow::showEvent(QShowEvent* event) {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 71a2fa321..37bc4f043 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -11,6 +11,7 @@
11#include <QImage> 11#include <QImage>
12#include <QThread> 12#include <QThread>
13#include <QWidget> 13#include <QWidget>
14#include <QWindow>
14 15
15#include "common/thread.h" 16#include "common/thread.h"
16#include "core/core.h" 17#include "core/core.h"
@@ -42,7 +43,7 @@ class EmuThread final : public QThread {
42 Q_OBJECT 43 Q_OBJECT
43 44
44public: 45public:
45 explicit EmuThread(GRenderWindow* render_window); 46 explicit EmuThread(Core::Frontend::GraphicsContext& context);
46 ~EmuThread() override; 47 ~EmuThread() override;
47 48
48 /** 49 /**
@@ -96,7 +97,7 @@ private:
96 std::mutex running_mutex; 97 std::mutex running_mutex;
97 std::condition_variable running_cv; 98 std::condition_variable running_cv;
98 99
99 GRenderWindow* render_window; 100 Core::Frontend::GraphicsContext& core_context;
100 101
101signals: 102signals:
102 /** 103 /**
@@ -122,15 +123,32 @@ signals:
122 void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); 123 void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
123}; 124};
124 125
126class OpenGLWindow : public QWindow {
127 Q_OBJECT
128public:
129 explicit OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context);
130
131 ~OpenGLWindow();
132
133 void Present();
134
135protected:
136 bool event(QEvent* event) override;
137 void exposeEvent(QExposeEvent* event) override;
138
139private:
140 QOpenGLContext* context;
141 QWidget* event_handler;
142};
143
125class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { 144class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
126 Q_OBJECT 145 Q_OBJECT
127 146
128public: 147public:
129 GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); 148 GRenderWindow(QWidget* parent, EmuThread* emu_thread);
130 ~GRenderWindow() override; 149 ~GRenderWindow() override;
131 150
132 // EmuWindow implementation 151 // EmuWindow implementation.
133 void SwapBuffers() override;
134 void MakeCurrent() override; 152 void MakeCurrent() override;
135 void DoneCurrent() override; 153 void DoneCurrent() override;
136 void PollEvents() override; 154 void PollEvents() override;
@@ -139,30 +157,36 @@ public:
139 void* surface) const override; 157 void* surface) const override;
140 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 158 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
141 159
142 void ForwardKeyPressEvent(QKeyEvent* event);
143 void ForwardKeyReleaseEvent(QKeyEvent* event);
144
145 void BackupGeometry(); 160 void BackupGeometry();
146 void RestoreGeometry(); 161 void RestoreGeometry();
147 void restoreGeometry(const QByteArray& geometry); // overridden 162 void restoreGeometry(const QByteArray& geometry); // overridden
148 QByteArray saveGeometry(); // overridden 163 QByteArray saveGeometry(); // overridden
149 164
150 qreal GetWindowPixelRatio() const; 165 qreal windowPixelRatio() const;
151 std::pair<u32, u32> ScaleTouch(QPointF pos) const;
152 166
153 void closeEvent(QCloseEvent* event) override; 167 void closeEvent(QCloseEvent* event) override;
168
169 void resizeEvent(QResizeEvent* event) override;
170
171 void keyPressEvent(QKeyEvent* event) override;
172 void keyReleaseEvent(QKeyEvent* event) override;
173
174 void mousePressEvent(QMouseEvent* event) override;
175 void mouseMoveEvent(QMouseEvent* event) override;
176 void mouseReleaseEvent(QMouseEvent* event) override;
177
154 bool event(QEvent* event) override; 178 bool event(QEvent* event) override;
155 void focusOutEvent(QFocusEvent* event) override;
156 179
157 void OnClientAreaResized(u32 width, u32 height); 180 void focusOutEvent(QFocusEvent* event) override;
158 181
159 bool InitRenderTarget(); 182 bool InitRenderTarget();
160 183
184 /// Destroy the previous run's child_widget which should also destroy the child_window
185 void ReleaseRenderTarget();
186
161 void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); 187 void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
162 188
163public slots: 189public slots:
164 void moveContext(); // overridden
165
166 void OnEmulationStarting(EmuThread* emu_thread); 190 void OnEmulationStarting(EmuThread* emu_thread);
167 void OnEmulationStopping(); 191 void OnEmulationStopping();
168 void OnFramebufferSizeChanged(); 192 void OnFramebufferSizeChanged();
@@ -173,6 +197,7 @@ signals:
173 void FirstFrameDisplayed(); 197 void FirstFrameDisplayed();
174 198
175private: 199private:
200 std::pair<u32, u32> ScaleTouch(QPointF pos) const;
176 void TouchBeginEvent(const QTouchEvent* event); 201 void TouchBeginEvent(const QTouchEvent* event);
177 void TouchUpdateEvent(const QTouchEvent* event); 202 void TouchUpdateEvent(const QTouchEvent* event);
178 void TouchEndEvent(); 203 void TouchEndEvent();
@@ -184,15 +209,9 @@ private:
184 bool LoadOpenGL(); 209 bool LoadOpenGL();
185 QStringList GetUnsupportedGLExtensions() const; 210 QStringList GetUnsupportedGLExtensions() const;
186 211
187 QWidget* container = nullptr;
188 GWidgetInternal* child = nullptr;
189
190 EmuThread* emu_thread; 212 EmuThread* emu_thread;
191 // Context that backs the GGLWidgetInternal (and will be used by core to render) 213
192 std::unique_ptr<QOpenGLContext> context; 214 std::unique_ptr<GraphicsContext> core_context;
193 // Context that will be shared between all newly created contexts. This should never be made
194 // current
195 std::unique_ptr<QOpenGLContext> shared_context;
196 215
197#ifdef HAS_VULKAN 216#ifdef HAS_VULKAN
198 std::unique_ptr<QVulkanInstance> vk_instance; 217 std::unique_ptr<QVulkanInstance> vk_instance;
@@ -202,6 +221,15 @@ private:
202 QImage screenshot_image; 221 QImage screenshot_image;
203 222
204 QByteArray geometry; 223 QByteArray geometry;
224
225 /// Native window handle that backs this presentation widget
226 QWindow* child_window = nullptr;
227
228 /// In order to embed the window into GRenderWindow, you need to use createWindowContainer to
229 /// put the child_window into a widget then add it to the layout. This child_widget can be
230 /// parented to GRenderWindow and use Qt's lifetime system
231 QWidget* child_widget = nullptr;
232
205 bool first_frame = false; 233 bool first_frame = false;
206 234
207protected: 235protected: