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