// Copyright 2012 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #include #include #include "Common/GL/GLInterface/GLX.h" #include "Common/Logging/Log.h" #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSPROC)(Display*, GLXFBConfig, GLXContext, Bool, const int*); typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*, GLXDrawable, int); typedef int (*PFNGLXSWAPINTERVALMESAPROC)(unsigned int); static PFNGLXCREATECONTEXTATTRIBSPROC glXCreateContextAttribs = nullptr; static PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXTPtr = nullptr; static PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESAPtr = nullptr; static PFNGLXCREATEGLXPBUFFERSGIXPROC glXCreateGLXPbufferSGIX = nullptr; static PFNGLXDESTROYGLXPBUFFERSGIXPROC glXDestroyGLXPbufferSGIX = nullptr; static bool s_glxError; static int ctxErrorHandler(Display* dpy, XErrorEvent* ev) { s_glxError = true; return 0; } void GLContextGLX::SwapInterval(int Interval) { if (!m_drawable) return; // Try EXT_swap_control, then MESA_swap_control. if (glXSwapIntervalEXTPtr) glXSwapIntervalEXTPtr(m_display, m_drawable, Interval); else if (glXSwapIntervalMESAPtr) glXSwapIntervalMESAPtr(static_cast(Interval)); else ERROR_LOG(VIDEO, "No support for SwapInterval (framerate clamped to monitor refresh rate)."); } void* GLContextGLX::GetFuncAddress(const std::string& name) { return reinterpret_cast(glXGetProcAddress(reinterpret_cast(name.c_str()))); } void GLContextGLX::Swap() { glXSwapBuffers(m_display, m_drawable); } // Create rendering window. // Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() bool GLContextGLX::Initialize(void* display_handle, void* window_handle, bool stereo, bool core) { m_display = static_cast(display_handle); int screen = DefaultScreen(m_display); // checking glx version int glxMajorVersion, glxMinorVersion; glXQueryVersion(m_display, &glxMajorVersion, &glxMinorVersion); if (glxMajorVersion < 1 || (glxMajorVersion == 1 && glxMinorVersion < 4)) { ERROR_LOG(VIDEO, "glX-Version %d.%d detected, but need at least 1.4", glxMajorVersion, glxMinorVersion); return false; } // loading core context creation function glXCreateContextAttribs = (PFNGLXCREATECONTEXTATTRIBSPROC)GetFuncAddress("glXCreateContextAttribsARB"); if (!glXCreateContextAttribs) { ERROR_LOG(VIDEO, "glXCreateContextAttribsARB not found, do you support GLX_ARB_create_context?"); return false; } // choosing framebuffer int visual_attribs[] = {GLX_X_RENDERABLE, True, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_DEPTH_SIZE, 0, GLX_STENCIL_SIZE, 0, GLX_DOUBLEBUFFER, True, GLX_STEREO, stereo ? True : False, None}; int fbcount = 0; GLXFBConfig* fbc = glXChooseFBConfig(m_display, screen, visual_attribs, &fbcount); if (!fbc || !fbcount) { ERROR_LOG(VIDEO, "Failed to retrieve a framebuffer config"); return false; } m_fbconfig = *fbc; XFree(fbc); 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. 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}}; m_context = nullptr; if (core) { m_context = glXCreateContextAttribs(m_display, m_fbconfig, 0, True, &context_attribs[0]); XSync(m_display, False); m_attribs.insert(m_attribs.end(), context_attribs.begin(), context_attribs.end()); } if (core && (!m_context || s_glxError)) { 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; m_context = glXCreateContextAttribs(m_display, m_fbconfig, 0, True, &context_attribs_33[0]); XSync(m_display, False); m_attribs.clear(); m_attribs.insert(m_attribs.end(), context_attribs_33.begin(), context_attribs_33.end()); } if (!m_context || s_glxError) { std::array context_attribs_legacy = { {GLX_CONTEXT_MAJOR_VERSION_ARB, 1, GLX_CONTEXT_MINOR_VERSION_ARB, 0, None}}; s_glxError = false; m_context = glXCreateContextAttribs(m_display, m_fbconfig, 0, True, &context_attribs_legacy[0]); XSync(m_display, False); m_attribs.clear(); m_attribs.insert(m_attribs.end(), context_attribs_legacy.begin(), context_attribs_legacy.end()); } if (!m_context || s_glxError) { ERROR_LOG(VIDEO, "Unable to create GL context."); XSetErrorHandler(oldHandler); return false; } glXSwapIntervalEXTPtr = nullptr; glXSwapIntervalMESAPtr = nullptr; glXCreateGLXPbufferSGIX = nullptr; glXDestroyGLXPbufferSGIX = nullptr; m_supports_pbuffer = false; std::string tmp; std::istringstream buffer(glXQueryExtensionsString(m_display, screen)); while (buffer >> tmp) { if (tmp == "GLX_SGIX_pbuffer") { glXCreateGLXPbufferSGIX = reinterpret_cast( GetFuncAddress("glXCreateGLXPbufferSGIX")); glXDestroyGLXPbufferSGIX = reinterpret_cast( GetFuncAddress("glXDestroyGLXPbufferSGIX")); m_supports_pbuffer = glXCreateGLXPbufferSGIX && glXDestroyGLXPbufferSGIX; } else if (tmp == "GLX_EXT_swap_control") { glXSwapIntervalEXTPtr = reinterpret_cast(GetFuncAddress("glXSwapIntervalEXT")); } else if (tmp == "GLX_MESA_swap_control") { glXSwapIntervalMESAPtr = reinterpret_cast(GetFuncAddress("glXSwapIntervalMESA")); } } if (!CreateWindowSurface(reinterpret_cast(window_handle))) { ERROR_LOG(VIDEO, "Error: CreateWindowSurface failed\n"); XSetErrorHandler(oldHandler); return false; } XSetErrorHandler(oldHandler); m_opengl_mode = Mode::OpenGL; return true; } bool GLContextGLX::Initialize(GLContext* main_context) { GLContextGLX* glx_context = static_cast(main_context); m_opengl_mode = glx_context->m_opengl_mode; m_supports_pbuffer = glx_context->m_supports_pbuffer; m_display = glx_context->m_display; m_fbconfig = glx_context->m_fbconfig; s_glxError = false; XErrorHandler oldHandler = XSetErrorHandler(&ctxErrorHandler); m_context = glXCreateContextAttribs(m_display, m_fbconfig, glx_context->m_context, True, &glx_context->m_attribs[0]); XSync(m_display, False); if (!m_context || s_glxError) { ERROR_LOG(VIDEO, "Unable to create GL context."); XSetErrorHandler(oldHandler); return false; } if (m_supports_pbuffer && !CreateWindowSurface(None)) { ERROR_LOG(VIDEO, "Error: CreateWindowSurface failed\n"); XSetErrorHandler(oldHandler); return false; } XSetErrorHandler(oldHandler); return true; } bool GLContextGLX::IsHeadless() const { return m_render_window == nullptr; } std::unique_ptr GLContextGLX::CreateSharedContext() { std::unique_ptr context = std::make_unique(); if (!context->Initialize(this)) return nullptr; return context; } bool GLContextGLX::CreateWindowSurface(Window window_handle) { if (window_handle) { // Get an appropriate visual XVisualInfo* vi = glXGetVisualFromFBConfig(m_display, m_fbconfig); m_render_window = GLX11Window::Create(m_display, window_handle, vi); if (!m_render_window) return false; m_backbuffer_width = m_render_window->GetWidth(); m_backbuffer_height = m_render_window->GetHeight(); m_drawable = static_cast(m_render_window->GetWindow()); XFree(vi); } else if (m_supports_pbuffer) { m_pbuffer = glXCreateGLXPbufferSGIX(m_display, m_fbconfig, 1, 1, nullptr); if (!m_pbuffer) return false; m_drawable = static_cast(m_pbuffer); } return true; } void GLContextGLX::DestroyWindowSurface() { m_render_window.reset(); if (m_supports_pbuffer && m_pbuffer) { glXDestroyGLXPbufferSGIX(m_display, m_pbuffer); m_pbuffer = 0; } } bool GLContextGLX::MakeCurrent() { return glXMakeCurrent(m_display, m_drawable, m_context); } bool GLContextGLX::ClearCurrent() { return glXMakeCurrent(m_display, None, nullptr); } // Close backend void GLContextGLX::Shutdown() { DestroyWindowSurface(); if (m_context) glXDestroyContext(m_display, m_context); } void GLContextGLX::Update() { m_render_window->UpdateDimensions(); m_backbuffer_width = m_render_window->GetWidth(); m_backbuffer_height = m_render_window->GetHeight(); }