From 01ae02482c527f2193dba3e039194e551df59ce1 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 23 Jan 2016 04:21:53 -0600 Subject: [PATCH] GLInterface: Support shared contexts in GLX --- Source/Core/Common/GL/GLInterface/GLX.cpp | 180 ++++++++++++++++------ Source/Core/Common/GL/GLInterface/GLX.h | 13 ++ 2 files changed, 149 insertions(+), 44 deletions(-) diff --git a/Source/Core/Common/GL/GLInterface/GLX.cpp b/Source/Core/Common/GL/GLInterface/GLX.cpp index f6fea067ef..e6a018e1d3 100644 --- a/Source/Core/Common/GL/GLInterface/GLX.cpp +++ b/Source/Core/Common/GL/GLInterface/GLX.cpp @@ -2,7 +2,8 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include +#include +#include #include "Common/GL/GLInterface/GLX.h" #include "Common/Logging/Log.h" @@ -17,6 +18,9 @@ typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int interval); static PFNGLXCREATECONTEXTATTRIBSPROC glXCreateContextAttribs = nullptr; static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = nullptr; +static PFNGLXCREATEGLXPBUFFERSGIXPROC glXCreateGLXPbufferSGIX = nullptr; +static PFNGLXDESTROYGLXPBUFFERSGIXPROC glXDestroyGLXPbufferSGIX = nullptr; + static bool s_glxError; static int ctxErrorHandler(Display* dpy, XErrorEvent* ev) { @@ -26,7 +30,7 @@ static int ctxErrorHandler(Display* dpy, XErrorEvent* ev) void cInterfaceGLX::SwapInterval(int Interval) { - if (glXSwapIntervalSGI) + if (glXSwapIntervalSGI && m_has_handle) glXSwapIntervalSGI(Interval); else ERROR_LOG(VIDEO, "No support for SwapInterval (framerate clamped to monitor refresh rate)."); @@ -45,6 +49,9 @@ void cInterfaceGLX::Swap() // Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() bool cInterfaceGLX::Create(void* window_handle, bool stereo, bool core) { + m_has_handle = !!window_handle; + m_host_window = (Window)window_handle; + dpy = XOpenDisplay(nullptr); int screen = DefaultScreen(dpy); @@ -100,51 +107,43 @@ bool cInterfaceGLX::Create(void* window_handle, bool stereo, bool core) fbconfig = *fbc; XFree(fbc); - // Get an appropriate visual - XVisualInfo* vi = glXGetVisualFromFBConfig(dpy, fbconfig); - s_glxError = false; XErrorHandler oldHandler = XSetErrorHandler(&ctxErrorHandler); // Create a GLX context. // We try to get a 4.0 core profile, else we try 3.3, else try it with anything we get. - int context_attribs[] = {GLX_CONTEXT_MAJOR_VERSION_ARB, - 4, - GLX_CONTEXT_MINOR_VERSION_ARB, - 0, - GLX_CONTEXT_PROFILE_MASK_ARB, - GLX_CONTEXT_CORE_PROFILE_BIT_ARB, - GLX_CONTEXT_FLAGS_ARB, - GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, - None}; + std::array context_attribs = { + {GLX_CONTEXT_MAJOR_VERSION_ARB, 4, GLX_CONTEXT_MINOR_VERSION_ARB, 0, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_FLAGS_ARB, + GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, None}}; ctx = nullptr; if (core) { - ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, context_attribs); + ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, &context_attribs[0]); XSync(dpy, False); + m_attribs.insert(m_attribs.end(), context_attribs.begin(), context_attribs.end()); } if (core && (!ctx || s_glxError)) { - int context_attribs_33[] = {GLX_CONTEXT_MAJOR_VERSION_ARB, - 3, - GLX_CONTEXT_MINOR_VERSION_ARB, - 3, - GLX_CONTEXT_PROFILE_MASK_ARB, - GLX_CONTEXT_CORE_PROFILE_BIT_ARB, - GLX_CONTEXT_FLAGS_ARB, - GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, - None}; + std::array context_attribs_33 = { + {GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 3, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_FLAGS_ARB, + GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, None}}; s_glxError = false; - ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, context_attribs_33); + ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, &context_attribs_33[0]); XSync(dpy, False); + m_attribs.clear(); + m_attribs.insert(m_attribs.end(), context_attribs_33.begin(), context_attribs_33.end()); } if (!ctx || s_glxError) { - int context_attribs_legacy[] = {GLX_CONTEXT_MAJOR_VERSION_ARB, 1, GLX_CONTEXT_MINOR_VERSION_ARB, - 0, None}; + std::array context_attribs_legacy = { + {GLX_CONTEXT_MAJOR_VERSION_ARB, 1, GLX_CONTEXT_MINOR_VERSION_ARB, 0, None}}; s_glxError = false; - ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, context_attribs_legacy); + ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, &context_attribs_legacy[0]); XSync(dpy, False); + m_attribs.clear(); + m_attribs.insert(m_attribs.end(), context_attribs_legacy.begin(), context_attribs_legacy.end()); } if (!ctx || s_glxError) { @@ -153,30 +152,116 @@ bool cInterfaceGLX::Create(void* window_handle, bool stereo, bool core) } XSetErrorHandler(oldHandler); - XWindow.Initialize(dpy); - - Window parent = (Window)window_handle; - - XWindowAttributes attribs; - if (!XGetWindowAttributes(dpy, parent, &attribs)) + std::string tmp; + std::istringstream buffer(glXQueryExtensionsString(dpy, screen)); + while (buffer >> tmp) { - ERROR_LOG(VIDEO, "Window attribute retrieval failed"); - return false; + if (tmp == "GLX_SGIX_pbuffer") + m_supports_pbuffer = true; } - s_backbuffer_width = attribs.width; - s_backbuffer_height = attribs.height; + if (m_supports_pbuffer) + { + // Get the function pointers we require + glXCreateGLXPbufferSGIX = + (PFNGLXCREATEGLXPBUFFERSGIXPROC)GetFuncAddress("glXCreateGLXPbufferSGIX"); + glXDestroyGLXPbufferSGIX = + (PFNGLXDESTROYGLXPBUFFERSGIXPROC)GetFuncAddress("glXDestroyGLXPbufferSGIX"); + } - win = XWindow.CreateXWindow(parent, vi); - XFree(vi); + if (!CreateWindowSurface()) + { + ERROR_LOG(VIDEO, "Error: CreateWindowSurface failed\n"); + return false; + } + return true; +} + +bool cInterfaceGLX::Create(cInterfaceBase* main_context) +{ + cInterfaceGLX* glx_context = static_cast(main_context); + + m_has_handle = false; + dpy = glx_context->dpy; + fbconfig = glx_context->fbconfig; + s_glxError = false; + XErrorHandler oldHandler = XSetErrorHandler(&ctxErrorHandler); + + ctx = glXCreateContextAttribs(dpy, fbconfig, glx_context->ctx, True, &glx_context->m_attribs[0]); + XSync(dpy, False); + + if (!ctx || s_glxError) + { + ERROR_LOG(VIDEO, "Unable to create GL context."); + return false; + } + XSetErrorHandler(oldHandler); + + if (!CreateWindowSurface()) + { + ERROR_LOG(VIDEO, "Error: CreateWindowSurface failed\n"); + return false; + } + return true; +} + +std::unique_ptr cInterfaceGLX::CreateSharedContext() +{ + std::unique_ptr context = std::make_unique(); + if (!context->Create(this)) + return nullptr; + return context; +} + +bool cInterfaceGLX::CreateWindowSurface() +{ + if (m_has_handle) + { + // Get an appropriate visual + XVisualInfo* vi = glXGetVisualFromFBConfig(dpy, fbconfig); + + XWindow.Initialize(dpy); + + XWindowAttributes attribs; + if (!XGetWindowAttributes(dpy, m_host_window, &attribs)) + { + ERROR_LOG(VIDEO, "Window attribute retrieval failed"); + return false; + } + + s_backbuffer_width = attribs.width; + s_backbuffer_height = attribs.height; + + win = XWindow.CreateXWindow(m_host_window, vi); + XFree(vi); + } + else + { + win = m_pbuffer = glXCreateGLXPbufferSGIX(dpy, fbconfig, 1, 1, nullptr); + if (!m_pbuffer) + return false; + } return true; } +void cInterfaceGLX::DestroyWindowSurface() +{ + if (!m_pbuffer) + { + XWindow.DestroyXWindow(); + } + else + { + glXDestroyGLXPbufferSGIX(dpy, m_pbuffer); + m_pbuffer = 0; + } +} + bool cInterfaceGLX::MakeCurrent() { bool success = glXMakeCurrent(dpy, win, ctx); - if (success) + if (success && !glXSwapIntervalSGI) { // load this function based on the current bound context glXSwapIntervalSGI = @@ -193,11 +278,18 @@ bool cInterfaceGLX::ClearCurrent() // Close backend void cInterfaceGLX::Shutdown() { - XWindow.DestroyXWindow(); + DestroyWindowSurface(); if (ctx) { glXDestroyContext(dpy, ctx); - XCloseDisplay(dpy); - ctx = nullptr; + + // Don't close the display connection if we are a shared context. + // Saves doing reference counting on this object, and the main context will always + // be shut down last anyway. + if (m_has_handle) + { + XCloseDisplay(dpy); + ctx = nullptr; + } } } diff --git a/Source/Core/Common/GL/GLInterface/GLX.h b/Source/Core/Common/GL/GLInterface/GLX.h index ae019ccbf9..724dca5141 100644 --- a/Source/Core/Common/GL/GLInterface/GLX.h +++ b/Source/Core/Common/GL/GLInterface/GLX.h @@ -5,7 +5,10 @@ #pragma once #include +#include +#include #include +#include #include "Common/GL/GLInterface/X11_Util.h" #include "Common/GL/GLInterfaceBase.h" @@ -13,11 +16,19 @@ class cInterfaceGLX : public cInterfaceBase { private: + Window m_host_window; cX11Window XWindow; Display* dpy; Window win; GLXContext ctx; GLXFBConfig fbconfig; + bool m_has_handle; + bool m_supports_pbuffer = false; + GLXPbufferSGIX m_pbuffer = 0; + std::vector m_attribs; + + bool CreateWindowSurface(); + void DestroyWindowSurface(); public: friend class cX11Window; @@ -25,7 +36,9 @@ public: void Swap() override; void* GetFuncAddress(const std::string& name) override; bool Create(void* window_handle, bool stereo, bool core) override; + bool Create(cInterfaceBase* main_context) override; bool MakeCurrent() override; bool ClearCurrent() override; void Shutdown() override; + std::unique_ptr CreateSharedContext() override; };