From 2156236f52119cb1fce7e701df535d54054050ff Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Thu, 7 May 2020 22:49:04 +1000 Subject: [PATCH] Frontends: Use common GL context wrapper --- src/core/gpu_hw_opengl.cpp | 9 - src/duckstation-qt/CMakeLists.txt | 6 +- src/duckstation-qt/mainwindow.cpp | 9 +- src/duckstation-qt/openglhostdisplay.cpp | 227 ++++++-------------- src/duckstation-qt/openglhostdisplay.h | 12 +- src/duckstation-qt/qthostdisplay.cpp | 4 +- src/duckstation-qt/qthostdisplay.h | 4 +- src/duckstation-qt/qthostinterface.cpp | 6 +- src/duckstation-sdl/opengl_host_display.cpp | 137 ++++++------ src/duckstation-sdl/opengl_host_display.h | 5 +- src/duckstation-sdl/sdl_host_interface.cpp | 7 +- 11 files changed, 162 insertions(+), 264 deletions(-) diff --git a/src/core/gpu_hw_opengl.cpp b/src/core/gpu_hw_opengl.cpp index 4ade9d2dd..ee955a201 100644 --- a/src/core/gpu_hw_opengl.cpp +++ b/src/core/gpu_hw_opengl.cpp @@ -155,15 +155,6 @@ std::tuple GPU_HW_OpenGL::ConvertToFramebufferCoordinates(s32 x, s32 y void GPU_HW_OpenGL::SetCapabilities(HostDisplay* host_display) { - Log_InfoPrintf("Context Type: %s", IsGLES() ? "OpenGL ES" : "OpenGL"); - - const char* gl_vendor = reinterpret_cast(glGetString(GL_VENDOR)); - const char* gl_renderer = reinterpret_cast(glGetString(GL_RENDERER)); - const char* gl_version = reinterpret_cast(glGetString(GL_VERSION)); - Log_InfoPrintf("GL_VENDOR: %s", gl_vendor); - Log_InfoPrintf("GL_RENDERER: %s", gl_renderer); - Log_InfoPrintf("GL_VERSION: %s", gl_version); - GLint max_texture_size = VRAM_WIDTH; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); Log_InfoPrintf("Max texture size: %dx%d", max_texture_size, max_texture_size); diff --git a/src/duckstation-qt/CMakeLists.txt b/src/duckstation-qt/CMakeLists.txt index a0187b38f..02105ba4a 100644 --- a/src/duckstation-qt/CMakeLists.txt +++ b/src/duckstation-qt/CMakeLists.txt @@ -58,6 +58,7 @@ add_executable(duckstation-qt settingsdialog.ui ) +target_include_directories(duckstation-qt PRIVATE "${Qt5Gui_PRIVATE_INCLUDE_DIRS}") target_link_libraries(duckstation-qt PRIVATE frontend-common core common imgui glad minizip scmversion Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network) if(WIN32) @@ -89,9 +90,4 @@ if(WIN32) add_custom_command(TARGET duckstation-qt POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/qt.conf.win" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/qt.conf" ) -else() - if(OpenGL_GLX_FOUND) - target_compile_definitions(duckstation-qt PRIVATE "HAS_GLX") - target_link_libraries(duckstation-qt PRIVATE OpenGL::GLX) - endif() endif() diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 661120071..9e0c95c4b 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -102,14 +102,14 @@ void MainWindow::createDisplay(QThread* worker_thread, bool use_debug_device, bo return; } - if (!m_host_display->createSurface() || !m_host_display->makeDeviceContextCurrent()) + if (!m_host_display->createSurface()) { reportError(tr("Failed to create host display surface.")); m_host_display->destroyDeviceContext(); return; } - m_host_display->moveContextToThread(worker_thread); + m_host_display->deactivateDeviceContext(); } void MainWindow::updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main) @@ -117,10 +117,7 @@ void MainWindow::updateDisplay(QThread* worker_thread, bool fullscreen, bool ren const bool is_fullscreen = m_display_widget->isFullScreen(); const bool is_rendering_to_main = (!is_fullscreen && m_display_widget->parent()); if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main) - { - m_host_display->moveContextToThread(worker_thread); return; - } m_host_display->destroySurface(); @@ -160,8 +157,6 @@ void MainWindow::updateDisplay(QThread* worker_thread, bool fullscreen, bool ren QSignalBlocker blocker(m_ui.actionFullscreen); m_ui.actionFullscreen->setChecked(fullscreen); - - m_host_display->moveContextToThread(worker_thread); } void MainWindow::destroyDisplay() diff --git a/src/duckstation-qt/openglhostdisplay.cpp b/src/duckstation-qt/openglhostdisplay.cpp index 1ceb1e21e..78a12b0c3 100644 --- a/src/duckstation-qt/openglhostdisplay.cpp +++ b/src/duckstation-qt/openglhostdisplay.cpp @@ -10,87 +10,12 @@ #include #include #include +#if !defined(WIN32) && !defined(APPLE) +#include +#endif #include Log_SetChannel(OpenGLHostDisplay); -static thread_local QOpenGLContext* s_thread_gl_context; - -static void* GetProcAddressCallback(const char* name) -{ - QOpenGLContext* ctx = s_thread_gl_context; - if (!ctx) - return nullptr; - - return (void*)ctx->getProcAddress(name); -} - -#if defined(WIN32) -#include "common/windows_headers.h" -#elif defined(HAS_GLX) -#include -#endif - -/// Changes the swap interval on a window. Since Qt doesn't expose this functionality, we need to change it manually -/// ourselves it by calling system-specific functions. Assumes the context is current. -static void SetSwapInterval(QOpenGLContext* context, int interval) -{ - static QOpenGLContext* last_context = nullptr; - -#ifdef WIN32 - static void(WINAPI * wgl_swap_interval_ext)(int) = nullptr; - - if (last_context != context) - { - wgl_swap_interval_ext = nullptr; - last_context = context; - - HMODULE gl_module = GetModuleHandleA("opengl32.dll"); - if (!gl_module) - return; - - const auto wgl_get_proc_address = - reinterpret_cast(GetProcAddress(gl_module, "wglGetProcAddress")); - if (!wgl_get_proc_address) - return; - - wgl_swap_interval_ext = - reinterpret_cast(wgl_get_proc_address("wglSwapIntervalEXT")); - } - - if (wgl_swap_interval_ext) - wgl_swap_interval_ext(interval); -#elif __linux__ - const QString platform_name(QGuiApplication::platformName()); - if (platform_name == QStringLiteral("xcb")) - { - static void (*glx_swap_interval_ext)(Display*, GLXDrawable, int) = nullptr; - - if (last_context != context) - { - glx_swap_interval_ext = nullptr; - last_context = context; - - glx_swap_interval_ext = reinterpret_cast( - glXGetProcAddress(reinterpret_cast("glXSwapIntervalEXT"))); - if (!glx_swap_interval_ext) - return; - } - - if (!glx_swap_interval_ext) - return; - - Display* dpy = glXGetCurrentDisplay(); - GLXDrawable drawable = glXGetCurrentDrawable(); - if (dpy && drawable != GLX_NONE) - glx_swap_interval_ext(dpy, drawable, interval); - } - else - { - qCritical() << "Unknown platform: " << platform_name; - } -#endif -} - class OpenGLDisplayWidgetTexture : public HostDisplayTexture { public: @@ -148,7 +73,7 @@ QtDisplayWidget* OpenGLHostDisplay::createWidget(QWidget* parent) HostDisplay::RenderAPI OpenGLHostDisplay::GetRenderAPI() const { - return m_gl_context->isOpenGLES() ? HostDisplay::RenderAPI::OpenGLES : HostDisplay::RenderAPI::OpenGL; + return m_gl_context->IsGLES() ? HostDisplay::RenderAPI::OpenGLES : HostDisplay::RenderAPI::OpenGL; } void* OpenGLHostDisplay::GetRenderDevice() const @@ -161,6 +86,12 @@ void* OpenGLHostDisplay::GetRenderContext() const return m_gl_context.get(); } +void OpenGLHostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) +{ + QtHostDisplay::WindowResized(new_window_width, new_window_height); + m_gl_context->ResizeSurface(static_cast(new_window_width), static_cast(new_window_height)); +} + std::unique_ptr OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* initial_data, u32 initial_data_stride, bool dynamic) { @@ -213,13 +144,13 @@ void OpenGLHostDisplay::SetVSync(bool enabled) GLint current_fbo = 0; glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - SetSwapInterval(m_gl_context.get(), enabled ? 1 : 0); + m_gl_context->SetSwapInterval(enabled ? 1 : 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); } const char* OpenGLHostDisplay::GetGLSLVersionString() const { - if (m_gl_context->isOpenGLES()) + if (m_gl_context->IsGLES()) { if (GLAD_GL_ES_VERSION_3_0) return "#version 300 es"; @@ -239,7 +170,7 @@ std::string OpenGLHostDisplay::GetGLSLVersionHeader() const { std::string header = GetGLSLVersionString(); header += "\n\n"; - if (m_gl_context->isOpenGLES()) + if (m_gl_context->IsGLES()) { header += "precision highp float;\n"; header += "precision highp int;\n\n"; @@ -273,80 +204,60 @@ bool OpenGLHostDisplay::hasDeviceContext() const return static_cast(m_gl_context); } +WindowInfo OpenGLHostDisplay::getWindowInfo() const +{ + WindowInfo wi; + + // Windows and Apple are easy here since there's no display connection. +#if defined(WIN32) + wi.type = WindowInfo::Type::Win32; + wi.window_handle = reinterpret_cast(m_widget->winId()); +#elif defined(__APPLE__) + wi.type = WindowInfo::Type::MacOS; + wi.window_handle = reinterpret_cast(m_widget->winId()); +#else + QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); + const QString platform_name = QGuiApplication::platformName(); + if (platform_name == QStringLiteral("xcb")) + { + wi.type = WindowInfo::Type::X11; + wi.display_connection = pni->nativeResourceForWindow("display", m_widget->windowHandle()); + wi.window_handle = reinterpret_cast(m_widget->winId()); + } + else if (platform_name == QStringLiteral("wayland")) + { + wi.type = WindowInfo::Type::Wayland; + wi.display_connection = pni->nativeResourceForWindow("display", m_widget->windowHandle()); + wi.window_handle = pni->nativeResourceForWindow("surface", m_widget->windowHandle()); + } + else + { + qCritical() << "Unknown PNI platform " << platform_name; + return wi; + } +#endif + + wi.surface_width = m_widget->width(); + wi.surface_height = m_widget->height(); + wi.surface_format = WindowInfo::SurfaceFormat::RGB8; + + return wi; +} + bool OpenGLHostDisplay::createDeviceContext(bool debug_device) { - m_gl_context = std::make_unique(); - - // Prefer a desktop OpenGL context where possible. If we can't get this, try OpenGL ES. - static constexpr std::array, 11> desktop_versions_to_try = { - {{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}, {3, 2}, {3, 1}, {3, 0}}}; - static constexpr std::array, 4> es_versions_to_try = {{{3, 2}, {3, 1}, {3, 0}}}; - - QSurfaceFormat surface_format; // = requestedFormat(); - surface_format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); - surface_format.setSwapInterval(0); - surface_format.setRenderableType(QSurfaceFormat::OpenGL); - surface_format.setProfile(QSurfaceFormat::CoreProfile); - if (debug_device) - surface_format.setOption(QSurfaceFormat::DebugContext); - - for (const auto [major, minor] : desktop_versions_to_try) - { - surface_format.setVersion(major, minor); - m_gl_context->setFormat(surface_format); - if (m_gl_context->create()) - break; - } - - if (!m_gl_context->isValid()) - { - // try forcing ES - surface_format.setRenderableType(QSurfaceFormat::OpenGLES); - surface_format.setProfile(QSurfaceFormat::NoProfile); - if (debug_device) - surface_format.setOption(QSurfaceFormat::DebugContext, false); - - for (const auto [major, minor] : es_versions_to_try) - { - surface_format.setVersion(major, minor); - m_gl_context->setFormat(surface_format); - if (m_gl_context->create()) - break; - } - } - - if (!m_gl_context->isValid()) + m_gl_context = GL::Context::Create(getWindowInfo()); + if (!m_gl_context) { Log_ErrorPrintf("Failed to create any GL context"); - m_gl_context.reset(); return false; } - surface_format = m_gl_context->format(); - Log_InfoPrintf("Got a %s %d.%d context", (m_gl_context->isOpenGLES() ? "OpenGL ES" : "desktop OpenGL"), - surface_format.majorVersion(), surface_format.minorVersion()); - return true; } bool OpenGLHostDisplay::initializeDeviceContext(bool debug_device) { - if (!m_gl_context->makeCurrent(m_widget->windowHandle())) - return false; - - s_thread_gl_context = m_gl_context.get(); - - // Load GLAD. - const auto load_result = - m_gl_context->isOpenGLES() ? gladLoadGLES2Loader(GetProcAddressCallback) : gladLoadGLLoader(GetProcAddressCallback); - if (!load_result) - { - Log_ErrorPrintf("Failed to load GL functions"); - s_thread_gl_context = nullptr; - m_gl_context->doneCurrent(); - return false; - } - if (debug_device && GLAD_GL_KHR_debug) { glad_glDebugMessageCallbackKHR(GLDebugCallback, nullptr); @@ -356,17 +267,16 @@ bool OpenGLHostDisplay::initializeDeviceContext(bool debug_device) if (!QtHostDisplay::initializeDeviceContext(debug_device)) { - s_thread_gl_context = nullptr; - m_gl_context->doneCurrent(); + m_gl_context->DoneCurrent(); return false; } return true; } -bool OpenGLHostDisplay::makeDeviceContextCurrent() +bool OpenGLHostDisplay::activateDeviceContext() { - if (!m_gl_context->makeCurrent(m_widget->windowHandle())) + if (!m_gl_context->MakeCurrent()) { Log_ErrorPrintf("Failed to make GL context current"); return false; @@ -375,20 +285,15 @@ bool OpenGLHostDisplay::makeDeviceContextCurrent() return true; } -void OpenGLHostDisplay::moveContextToThread(QThread* new_thread) +void OpenGLHostDisplay::deactivateDeviceContext() { - m_gl_context->doneCurrent(); - m_gl_context->moveToThread(new_thread); + m_gl_context->DoneCurrent(); } void OpenGLHostDisplay::destroyDeviceContext() { - Assert(m_gl_context && s_thread_gl_context == m_gl_context.get()); - QtHostDisplay::destroyDeviceContext(); - - s_thread_gl_context = nullptr; - m_gl_context->doneCurrent(); + m_gl_context->DoneCurrent(); m_gl_context.reset(); } @@ -397,6 +302,10 @@ bool OpenGLHostDisplay::createSurface() m_window_width = m_widget->scaledWindowWidth(); m_window_height = m_widget->scaledWindowHeight(); emit m_widget->windowResizedEvent(m_window_width, m_window_height); + + if (m_gl_context) + m_gl_context->ChangeSurface(getWindowInfo()); + return true; } @@ -455,7 +364,7 @@ void main() return false; } - if (!m_gl_context->isOpenGLES()) + if (!m_gl_context->IsGLES()) m_display_program.BindFragData(0, "o_col0"); if (!m_display_program.Link()) @@ -508,9 +417,7 @@ void OpenGLHostDisplay::Render() ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - QWindow* window_handle = m_widget->windowHandle(); - m_gl_context->makeCurrent(window_handle); - m_gl_context->swapBuffers(window_handle); + m_gl_context->SwapBuffers(); ImGui::NewFrame(); ImGui_ImplOpenGL3_NewFrame(); diff --git a/src/duckstation-qt/openglhostdisplay.h b/src/duckstation-qt/openglhostdisplay.h index f8728d2ef..7a3eaac41 100644 --- a/src/duckstation-qt/openglhostdisplay.h +++ b/src/duckstation-qt/openglhostdisplay.h @@ -8,12 +8,13 @@ #define __glext_h_ #endif +#include "common/gl/context.h" #include "common/gl/program.h" #include "common/gl/texture.h" +#include "common/window_info.h" #include "core/host_display.h" #include "qtdisplaywidget.h" #include "qthostdisplay.h" -#include #include class QtHostInterface; @@ -29,8 +30,8 @@ public: bool hasDeviceContext() const override; bool createDeviceContext(bool debug_device) override; bool initializeDeviceContext(bool debug_device) override; - bool makeDeviceContextCurrent() override; - void moveContextToThread(QThread* new_thread) override; + bool activateDeviceContext() override; + void deactivateDeviceContext() override; void destroyDeviceContext() override; bool createSurface() override; void destroySurface(); @@ -38,6 +39,7 @@ public: RenderAPI GetRenderAPI() const override; void* GetRenderDevice() const override; void* GetRenderContext() const override; + void WindowResized(s32 new_window_width, s32 new_window_height) override; std::unique_ptr CreateTexture(u32 width, u32 height, const void* initial_data, u32 initial_data_stride, bool dynamic) override; @@ -54,6 +56,8 @@ private: const char* GetGLSLVersionString() const; std::string GetGLSLVersionHeader() const; + WindowInfo getWindowInfo() const; + bool createImGuiContext() override; void destroyImGuiContext() override; bool createDeviceResources() override; @@ -61,7 +65,7 @@ private: void renderDisplay(); - std::unique_ptr m_gl_context = nullptr; + std::unique_ptr m_gl_context = nullptr; GL::Program m_display_program; GLuint m_display_vao = 0; diff --git a/src/duckstation-qt/qthostdisplay.cpp b/src/duckstation-qt/qthostdisplay.cpp index 82b455838..c343611df 100644 --- a/src/duckstation-qt/qthostdisplay.cpp +++ b/src/duckstation-qt/qthostdisplay.cpp @@ -50,12 +50,12 @@ bool QtHostDisplay::initializeDeviceContext(bool debug_device) return true; } -bool QtHostDisplay::makeDeviceContextCurrent() +bool QtHostDisplay::activateDeviceContext() { return true; } -void QtHostDisplay::moveContextToThread(QThread* new_thread) {} +void QtHostDisplay::deactivateDeviceContext() {} void QtHostDisplay::destroyDeviceContext() { diff --git a/src/duckstation-qt/qthostdisplay.h b/src/duckstation-qt/qthostdisplay.h index 3a303f463..285291ad9 100644 --- a/src/duckstation-qt/qthostdisplay.h +++ b/src/duckstation-qt/qthostdisplay.h @@ -23,8 +23,8 @@ public: virtual bool hasDeviceContext() const; virtual bool createDeviceContext(bool debug_device); virtual bool initializeDeviceContext(bool debug_device); - virtual bool makeDeviceContextCurrent(); - virtual void moveContextToThread(QThread* new_thread); + virtual bool activateDeviceContext(); + virtual void deactivateDeviceContext(); virtual void destroyDeviceContext(); virtual bool createSurface(); virtual void destroySurface(); diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp index 22718173c..b8b2da063 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -317,7 +317,7 @@ bool QtHostInterface::AcquireHostDisplay() return false; } - if (!getHostDisplay()->makeDeviceContextCurrent() || + if (!getHostDisplay()->activateDeviceContext() || !getHostDisplay()->initializeDeviceContext(m_settings.gpu_use_debug_device)) { getHostDisplay()->destroyDeviceContext(); @@ -366,9 +366,9 @@ void QtHostInterface::disconnectDisplaySignals() void QtHostInterface::updateDisplayState() { // this expects the context to get moved back to us afterwards - getHostDisplay()->moveContextToThread(m_original_thread); + getHostDisplay()->deactivateDeviceContext(); emit updateDisplayRequested(m_worker_thread, m_is_fullscreen, m_is_rendering_to_main); - if (!getHostDisplay()->makeDeviceContextCurrent()) + if (!getHostDisplay()->activateDeviceContext()) Panic("Failed to make device context current after updating"); getHostDisplay()->updateImGuiDisplaySize(); diff --git a/src/duckstation-sdl/opengl_host_display.cpp b/src/duckstation-sdl/opengl_host_display.cpp index 935a44c51..bb5cb4ceb 100644 --- a/src/duckstation-sdl/opengl_host_display.cpp +++ b/src/duckstation-sdl/opengl_host_display.cpp @@ -2,12 +2,24 @@ #include "common/assert.h" #include "common/log.h" #include "imgui_impl_sdl.h" +#include #include #include #include #include Log_SetChannel(OpenGLHostDisplay); +#ifdef __APPLE__ +#include +struct NSView; + +static NSView* GetContentViewFromWindow(NSWindow* window) +{ + // window.contentView + return reinterpret_cast(objc_msgSend)(reinterpret_cast(window), sel_getUid("contentView")); +} +#endif + class OpenGLDisplayWidgetTexture : public HostDisplayTexture { public: @@ -67,8 +79,7 @@ OpenGLHostDisplay::~OpenGLHostDisplay() m_display_program.Destroy(); ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplSDL2_Shutdown(); - SDL_GL_MakeCurrent(nullptr, nullptr); - SDL_GL_DeleteContext(m_gl_context); + m_gl_context.reset(); } if (m_window) @@ -77,7 +88,7 @@ OpenGLHostDisplay::~OpenGLHostDisplay() HostDisplay::RenderAPI OpenGLHostDisplay::GetRenderAPI() const { - return m_is_gles ? HostDisplay::RenderAPI::OpenGLES : HostDisplay::RenderAPI::OpenGL; + return m_gl_context->IsGLES() ? HostDisplay::RenderAPI::OpenGLES : HostDisplay::RenderAPI::OpenGL; } void* OpenGLHostDisplay::GetRenderDevice() const @@ -87,13 +98,15 @@ void* OpenGLHostDisplay::GetRenderDevice() const void* OpenGLHostDisplay::GetRenderContext() const { - return m_gl_context; + return m_gl_context.get(); } void OpenGLHostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) { HostDisplay::WindowResized(new_window_width, new_window_height); - SDL_GL_GetDrawableSize(m_window, &m_window_width, &m_window_height); + m_gl_context->ResizeSurface(static_cast(new_window_width), static_cast(new_window_height)); + m_window_width = static_cast(m_gl_context->GetSurfaceWidth()); + m_window_height = static_cast(m_gl_context->GetSurfaceHeight()); ImGui::GetIO().DisplaySize.x = static_cast(m_window_width); ImGui::GetIO().DisplaySize.y = static_cast(m_window_height); } @@ -150,13 +163,13 @@ void OpenGLHostDisplay::SetVSync(bool enabled) GLint current_fbo = 0; glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - SDL_GL_SetSwapInterval(enabled ? 1 : 0); + m_gl_context->SetSwapInterval(enabled ? 1 : 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); } const char* OpenGLHostDisplay::GetGLSLVersionString() const { - if (m_is_gles) + if (m_gl_context->IsGLES()) { if (GLAD_GL_ES_VERSION_3_0) return "#version 300 es"; @@ -176,7 +189,7 @@ std::string OpenGLHostDisplay::GetGLSLVersionHeader() const { std::string header = GetGLSLVersionString(); header += "\n\n"; - if (m_is_gles) + if (m_gl_context->IsGLES()) { header += "precision highp float;\n"; header += "precision highp int;\n\n"; @@ -207,64 +220,55 @@ static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLen bool OpenGLHostDisplay::CreateGLContext(bool debug_device) { - // Prefer a desktop OpenGL context where possible. If we can't get this, try OpenGL ES. - static constexpr std::array, 11> desktop_versions_to_try = { - {{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}, {3, 2}, {3, 1}, {3, 0}}}; - static constexpr std::array, 4> es_versions_to_try = {{{3, 2}, {3, 1}, {3, 0}}}; - - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - if (debug_device) - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); - - for (const auto [major, minor] : desktop_versions_to_try) + SDL_SysWMinfo syswm = {}; + SDL_VERSION(&syswm.version); + if (!SDL_GetWindowWMInfo(m_window, &syswm)) { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor); - - Log_InfoPrintf("Trying a Desktop OpenGL %d.%d context", major, minor); - m_gl_context = SDL_GL_CreateContext(m_window); - if (m_gl_context) - { - Log_InfoPrintf("Got a desktop OpenGL %d.%d context", major, minor); - break; - } - } - - if (!m_gl_context) - { - // try es - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); - - for (const auto [major, minor] : es_versions_to_try) - { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor); - - Log_InfoPrintf("Trying a OpenGL ES %d.%d context", major, minor); - m_gl_context = SDL_GL_CreateContext(m_window); - if (m_gl_context) - { - Log_InfoPrintf("Got a OpenGL ES %d.%d context", major, minor); - m_is_gles = true; - break; - } - } - } - - if (!m_gl_context || SDL_GL_MakeCurrent(m_window, m_gl_context) != 0) - { - Log_ErrorPrintf("Failed to create any GL context"); + Log_ErrorPrintf("SDL_GetWindowWMInfo failed"); return false; } - // Load GLAD. - const auto load_result = - m_is_gles ? gladLoadGLES2Loader(SDL_GL_GetProcAddress) : gladLoadGLLoader(SDL_GL_GetProcAddress); - if (!load_result) + int window_width, window_height; + SDL_GetWindowSize(m_window, &window_width, &window_height); + + WindowInfo wi; + wi.surface_width = static_cast(window_width); + wi.surface_height = static_cast(window_height); + wi.surface_format = WindowInfo::SurfaceFormat::RGB8; + + switch (syswm.subsystem) { - Log_ErrorPrintf("Failed to load GL functions"); +#ifdef SDL_VIDEO_DRIVER_WINDOWS + case SDL_SYSWM_WINDOWS: + wi.type = WindowInfo::Type::Win32; + wi.window_handle = syswm.info.win.window; + break; +#endif + +#ifdef SDL_VIDEO_DRIVER_COCOA + case SDL_SYSWM_COCOA: + wi.type = WindowInfo::Type::MacOS; + wi.window_handle = GetContentViewFromWindow(syswm.info.cocoa.window); + break; +#endif + +#ifdef SDL_VIDEO_DRIVER_X11 + case SDL_SYSWM_X11: + wi.type = WindowInfo::Type::X11; + wi.window_handle = reinterpret_cast(static_cast(syswm.info.x11.window)); + wi.display_connection = syswm.info.x11.display; + break; +#endif + + default: + Log_ErrorPrintf("Unhandled syswm subsystem %u", static_cast(syswm.subsystem)); + return false; + } + + m_gl_context = GL::Context::Create(wi); + if (!m_gl_context) + { + Log_ErrorPrintf("Failed to create a GL context of any kind."); return false; } @@ -276,10 +280,11 @@ bool OpenGLHostDisplay::CreateGLContext(bool debug_device) } // this can change due to retina scaling on macos? - SDL_GL_GetDrawableSize(m_window, &m_window_width, &m_window_height); + m_window_width = static_cast(m_gl_context->GetSurfaceWidth()); + m_window_height = static_cast(m_gl_context->GetSurfaceHeight()); // start with vsync on - SDL_GL_SetSwapInterval(1); + m_gl_context->SetSwapInterval(1); return true; } @@ -288,7 +293,7 @@ bool OpenGLHostDisplay::CreateImGuiContext() ImGui::GetIO().DisplaySize.x = static_cast(m_window_width); ImGui::GetIO().DisplaySize.y = static_cast(m_window_height); - if (!ImGui_ImplSDL2_InitForOpenGL(m_window, m_gl_context) || !ImGui_ImplOpenGL3_Init(GetGLSLVersionString())) + if (!ImGui_ImplSDL2_InitForOpenGL(m_window, nullptr) || !ImGui_ImplOpenGL3_Init(GetGLSLVersionString())) return false; ImGui_ImplOpenGL3_NewFrame(); @@ -329,7 +334,7 @@ void main() return false; } - if (!m_is_gles) + if (!m_gl_context->IsGLES()) m_display_program.BindFragData(0, "o_col0"); if (!m_display_program.Link()) @@ -377,7 +382,7 @@ void OpenGLHostDisplay::Render() ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - SDL_GL_SwapWindow(m_window); + m_gl_context->SwapBuffers(); ImGui::NewFrame(); ImGui_ImplSDL2_NewFrame(m_window); diff --git a/src/duckstation-sdl/opengl_host_display.h b/src/duckstation-sdl/opengl_host_display.h index 42d97d9fa..954989192 100644 --- a/src/duckstation-sdl/opengl_host_display.h +++ b/src/duckstation-sdl/opengl_host_display.h @@ -1,4 +1,5 @@ #pragma once +#include "common/gl/context.h" #include "common/gl/program.h" #include "common/gl/texture.h" #include "core/host_display.h" @@ -41,12 +42,10 @@ private: void RenderDisplay(); SDL_Window* m_window = nullptr; - SDL_GLContext m_gl_context = nullptr; + std::unique_ptr m_gl_context; GL::Program m_display_program; GLuint m_display_vao = 0; GLuint m_display_nearest_sampler = 0; GLuint m_display_linear_sampler = 0; - - bool m_is_gles = false; }; diff --git a/src/duckstation-sdl/sdl_host_interface.cpp b/src/duckstation-sdl/sdl_host_interface.cpp index 7a6053b73..9ceb6a3af 100644 --- a/src/duckstation-sdl/sdl_host_interface.cpp +++ b/src/duckstation-sdl/sdl_host_interface.cpp @@ -81,8 +81,7 @@ bool SDLHostInterface::CreateSDLWindow() static constexpr u32 DEFAULT_WINDOW_HEIGHT = 700; // Create window. - const u32 window_flags = - SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | (UseOpenGLRenderer() ? SDL_WINDOW_OPENGL : 0); + const u32 window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; u32 window_width = DEFAULT_WINDOW_WIDTH; u32 window_height = DEFAULT_WINDOW_HEIGHT; @@ -116,6 +115,8 @@ bool SDLHostInterface::CreateSDLWindow() if (m_fullscreen) SDL_SetWindowFullscreen(m_window, SDL_WINDOW_FULLSCREEN_DESKTOP); + // Process events so that we have everything sorted out before creating a child window for the GL context (X11). + SDL_PumpEvents(); return true; } @@ -818,7 +819,7 @@ void SDLHostInterface::DrawDebugMenu() for (u32 i = LOGLEVEL_NONE; i < LOGLEVEL_COUNT; i++) { if (ImGui::MenuItem(Settings::GetLogLevelDisplayName(static_cast(i)), nullptr, - m_settings.log_level == static_cast(i))) + m_settings.log_level == static_cast(i))) { m_settings_copy.log_level = static_cast(i); settings_changed = true;