diff options
| author | 2019-04-13 22:08:40 -0400 | |
|---|---|---|
| committer | 2019-04-13 22:08:40 -0400 | |
| commit | 065f83c6c321c9672cda9b89c09bef6c7f3c5472 (patch) | |
| tree | 3efd50ea836f1ab052581108f1fa218a22e73b62 | |
| parent | Merge pull request #2389 from FreddyFunk/rename-gamedir (diff) | |
| parent | bootmanager: Bypass input focus issues (diff) | |
| download | yuzu-065f83c6c321c9672cda9b89c09bef6c7f3c5472.tar.gz yuzu-065f83c6c321c9672cda9b89c09bef6c7f3c5472.tar.xz yuzu-065f83c6c321c9672cda9b89c09bef6c7f3c5472.zip | |
Merge pull request #2017 from jroweboy/glwidget
Frontend: Migrate to QOpenGLWindow and support shared contexts
Diffstat (limited to '')
| -rw-r--r-- | src/core/frontend/emu_window.h | 39 | ||||
| -rw-r--r-- | src/yuzu/bootmanager.cpp | 208 | ||||
| -rw-r--r-- | src/yuzu/bootmanager.h | 29 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 15 | ||||
| -rw-r--r-- | src/yuzu/main.h | 4 | ||||
| -rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | 37 | ||||
| -rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2.h | 2 |
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 @@ | |||
| 13 | namespace Core::Frontend { | 13 | namespace 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 | */ | ||
| 20 | class GraphicsContext { | ||
| 21 | public: | ||
| 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 | */ |
| 33 | class EmuWindow { | 50 | class EmuWindow : public GraphicsContext { |
| 34 | public: | 51 | public: |
| 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 | ||
| 92 | class GGLContext : public Core::Frontend::GraphicsContext { | ||
| 93 | public: | ||
| 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 | |||
| 110 | private: | ||
| 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 |
| 88 | class GGLWidgetInternal : public QGLWidget { | 118 | class GGLWidgetInternal : public QOpenGLWindow { |
| 89 | public: | 119 | public: |
| 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 | ||
| 116 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | 188 | GRenderWindow::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 | ||
| 143 | void GRenderWindow::SwapBuffers() { | 215 | void 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 | ||
| 159 | void GRenderWindow::MakeCurrent() { | 231 | void GRenderWindow::MakeCurrent() { |
| 160 | child->makeCurrent(); | 232 | context->makeCurrent(child); |
| 161 | } | 233 | } |
| 162 | 234 | ||
| 163 | void GRenderWindow::DoneCurrent() { | 235 | void GRenderWindow::DoneCurrent() { |
| 164 | child->doneCurrent(); | 236 | context->doneCurrent(); |
| 165 | } | 237 | } |
| 166 | 238 | ||
| 167 | void GRenderWindow::PollEvents() {} | 239 | void GRenderWindow::PollEvents() {} |
| @@ -174,14 +246,26 @@ void GRenderWindow::PollEvents() {} | |||
| 174 | void GRenderWindow::OnFramebufferSizeChanged() { | 246 | void 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 | ||
| 255 | void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { | ||
| 256 | if (child) { | ||
| 257 | child->keyPressEvent(event); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) { | ||
| 262 | if (child) { | ||
| 263 | child->keyReleaseEvent(event); | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 183 | void GRenderWindow::BackupGeometry() { | 267 | void GRenderWindow::BackupGeometry() { |
| 184 | geometry = ((QGLWidget*)this)->saveGeometry(); | 268 | geometry = ((QWidget*)this)->saveGeometry(); |
| 185 | } | 269 | } |
| 186 | 270 | ||
| 187 | void GRenderWindow::RestoreGeometry() { | 271 | void 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 | ||
| 207 | qreal GRenderWindow::windowPixelRatio() const { | 291 | qreal 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 | ||
| 212 | std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { | 296 | std::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 | ||
| 223 | void GRenderWindow::keyPressEvent(QKeyEvent* event) { | ||
| 224 | InputCommon::GetKeyboard()->PressKey(event->key()); | ||
| 225 | } | ||
| 226 | |||
| 227 | void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 228 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); | ||
| 229 | } | ||
| 230 | |||
| 231 | void 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 | |||
| 244 | void 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 | |||
| 254 | void 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 | |||
| 264 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | 307 | void 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 | ||
| 359 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | ||
| 360 | return std::make_unique<GGLContext>(shared_context.get()); | ||
| 361 | } | ||
| 362 | |||
| 316 | void GRenderWindow::InitRenderTarget() { | 363 | void 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; | |||
| 21 | class GGLWidgetInternal; | 21 | class GGLWidgetInternal; |
| 22 | class GMainWindow; | 22 | class GMainWindow; |
| 23 | class GRenderWindow; | 23 | class GRenderWindow; |
| 24 | class QSurface; | ||
| 25 | class QOpenGLContext; | ||
| 24 | 26 | ||
| 25 | namespace VideoCore { | 27 | namespace VideoCore { |
| 26 | enum class LoadCallbackStage; | 28 | enum 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 | ||
| 163 | private: | 161 | private: |
| 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 | ||
| 2034 | void GMainWindow::keyPressEvent(QKeyEvent* event) { | ||
| 2035 | if (render_window) { | ||
| 2036 | render_window->ForwardKeyPressEvent(event); | ||
| 2037 | } | ||
| 2038 | } | ||
| 2039 | |||
| 2040 | void GMainWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 2041 | if (render_window) { | ||
| 2042 | render_window->ForwardKeyReleaseEvent(event); | ||
| 2043 | } | ||
| 2044 | } | ||
| 2045 | |||
| 2034 | bool GMainWindow::ConfirmChangeGame() { | 2046 | bool 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 | ||
| 22 | class SDLGLContext : public Core::Frontend::GraphicsContext { | ||
| 23 | public: | ||
| 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 | |||
| 48 | private: | ||
| 49 | SDL_Window* window; | ||
| 50 | SDL_GLContext context; | ||
| 51 | }; | ||
| 52 | |||
| 22 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | 53 | void 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 | |||
| 313 | std::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 | ||