#include "context_glx.h" #include "../duckstation_compat.h" #include "../log.h" #include Log_SetChannel(GL::ContextGLX); namespace GL { ContextGLX::ContextGLX(const WindowInfo& wi) : Context(wi) {} ContextGLX::~ContextGLX() { if (glXGetCurrentContext() == m_context) glXMakeCurrent(GetDisplay(), None, nullptr); if (m_context) glXDestroyContext(GetDisplay(), m_context); if (m_vi) XFree(m_vi); if (m_libGL_handle) dlclose(m_libGL_handle); } std::unique_ptr ContextGLX::Create(const WindowInfo& wi, const Version* versions_to_try, size_t num_versions_to_try) { std::unique_ptr context = std::make_unique(wi); if (!context->Initialize(versions_to_try, num_versions_to_try)) return nullptr; return context; } bool ContextGLX::Initialize(const Version* versions_to_try, size_t num_versions_to_try) { // We need libGL loaded, because GLAD loads its own, then releases it. m_libGL_handle = dlopen("libGL.so.1", RTLD_NOW | RTLD_GLOBAL); if (!m_libGL_handle) { m_libGL_handle = dlopen("libGL.so", RTLD_NOW | RTLD_GLOBAL); if (!m_libGL_handle) { Log_ErrorPrintf("Failed to load libGL.so: %s", dlerror()); return false; } } const int screen = DefaultScreen(GetDisplay()); if (!gladLoadGLX(GetDisplay(), screen)) { Log_ErrorPrintf("Loading GLAD GLX functions failed"); return false; } if (m_wi.type == WindowInfo::Type::X11) { if (!CreateWindow(screen)) return false; } else { Panic("Create pbuffer"); } for (size_t i = 0; i < num_versions_to_try; i++) { const Version& cv = versions_to_try[i]; if (cv.profile == Profile::NoProfile && CreateAnyContext(nullptr, true)) { m_version = cv; return true; } else if (cv.profile != Profile::NoProfile && CreateVersionContext(cv, nullptr, true)) { m_version = cv; return true; } } return false; } void* ContextGLX::GetProcAddress(const char* name) { return reinterpret_cast(glXGetProcAddress(reinterpret_cast(name))); } bool ContextGLX::ChangeSurface(const WindowInfo& new_wi) { const bool was_current = (glXGetCurrentContext() == m_context); if (was_current) glXMakeCurrent(GetDisplay(), None, nullptr); m_window.Destroy(); m_wi = new_wi; if (new_wi.type == WindowInfo::Type::X11) { const int screen = DefaultScreen(GetDisplay()); if (!CreateWindow(screen)) return false; } if (was_current && !glXMakeCurrent(GetDisplay(), GetDrawable(), m_context)) { Log_ErrorPrintf("Failed to make context current again after surface change"); return false; } return true; } void ContextGLX::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/) { m_window.Resize(new_surface_width, new_surface_height); m_wi.surface_width = m_window.GetWidth(); m_wi.surface_height = m_window.GetHeight(); } bool ContextGLX::SwapBuffers() { glXSwapBuffers(GetDisplay(), GetDrawable()); return true; } bool ContextGLX::MakeCurrent() { return (glXMakeCurrent(GetDisplay(), GetDrawable(), m_context) == True); } bool ContextGLX::DoneCurrent() { return (glXMakeCurrent(GetDisplay(), None, nullptr) == True); } bool ContextGLX::SetSwapInterval(s32 interval) { if (GLAD_GLX_EXT_swap_control) { glXSwapIntervalEXT(GetDisplay(), GetDrawable(), interval); return true; } else if (GLAD_GLX_MESA_swap_control) { return (glXSwapIntervalMESA(static_cast(std::max(interval, 0))) != 0); } else if (GLAD_GLX_SGI_swap_control) { return (glXSwapIntervalSGI(interval) != 0); } else { return false; } } std::unique_ptr ContextGLX::CreateSharedContext(const WindowInfo& wi) { std::unique_ptr context = std::make_unique(wi); if (wi.type == WindowInfo::Type::X11) { const int screen = DefaultScreen(context->GetDisplay()); if (!context->CreateWindow(screen)) return nullptr; } else { Panic("Create pbuffer"); } if (m_version.profile == Profile::NoProfile) { if (!context->CreateAnyContext(m_context, false)) return nullptr; } else { if (!context->CreateVersionContext(m_version, m_context, false)) return nullptr; } context->m_version = m_version; return context; } bool ContextGLX::CreateWindow(int screen) { int attribs[32] = {GLX_X_RENDERABLE, True, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_DOUBLEBUFFER, True}; int nattribs = 8; switch (m_wi.surface_format) { case WindowInfo::SurfaceFormat::RGB8: attribs[nattribs++] = GLX_RED_SIZE; attribs[nattribs++] = 8; attribs[nattribs++] = GLX_GREEN_SIZE; attribs[nattribs++] = 8; attribs[nattribs++] = GLX_BLUE_SIZE; attribs[nattribs++] = 8; break; case WindowInfo::SurfaceFormat::RGBA8: attribs[nattribs++] = GLX_RED_SIZE; attribs[nattribs++] = 8; attribs[nattribs++] = GLX_GREEN_SIZE; attribs[nattribs++] = 8; attribs[nattribs++] = GLX_BLUE_SIZE; attribs[nattribs++] = 8; attribs[nattribs++] = GLX_ALPHA_SIZE; attribs[nattribs++] = 8; break; case WindowInfo::SurfaceFormat::RGB565: attribs[nattribs++] = GLX_RED_SIZE; attribs[nattribs++] = 5; attribs[nattribs++] = GLX_GREEN_SIZE; attribs[nattribs++] = 6; attribs[nattribs++] = GLX_BLUE_SIZE; attribs[nattribs++] = 5; break; case WindowInfo::SurfaceFormat::Auto: break; default: UnreachableCode(); break; } attribs[nattribs++] = None; attribs[nattribs++] = 0; int fbcount = 0; GLXFBConfig* fbc = glXChooseFBConfig(GetDisplay(), screen, attribs, &fbcount); if (!fbc || !fbcount) { Log_ErrorPrintf("glXChooseFBConfig() failed"); return false; } m_fb_config = *fbc; XFree(fbc); if (!GLAD_GLX_VERSION_1_3) { Log_ErrorPrintf("GLX Version 1.3 is required"); return false; } m_vi = glXGetVisualFromFBConfig(GetDisplay(), m_fb_config); if (!m_vi) { Log_ErrorPrintf("glXGetVisualFromFBConfig() failed"); return false; } return m_window.Create(GetDisplay(), static_cast(reinterpret_cast(m_wi.window_handle)), m_vi); } bool ContextGLX::CreateAnyContext(GLXContext share_context, bool make_current) { X11InhibitErrors ie; m_context = glXCreateContext(GetDisplay(), m_vi, share_context, True); if (!m_context || ie.HadError()) { Log_ErrorPrintf("glxCreateContext() failed"); return false; } if (make_current) { if (!glXMakeCurrent(GetDisplay(), GetDrawable(), m_context)) { Log_ErrorPrintf("glXMakeCurrent() failed"); return false; } } return true; } bool ContextGLX::CreateVersionContext(const Version& version, GLXContext share_context, bool make_current) { // we need create context attribs if (!GLAD_GLX_VERSION_1_3) { Log_ErrorPrint("Missing GLX version 1.3."); return false; } int attribs[32]; int nattribs = 0; attribs[nattribs++] = GLX_CONTEXT_PROFILE_MASK_ARB; attribs[nattribs++] = ((version.profile == Profile::ES) ? ((version.major_version >= 2) ? GLX_CONTEXT_ES2_PROFILE_BIT_EXT : GLX_CONTEXT_ES_PROFILE_BIT_EXT) : GLX_CONTEXT_CORE_PROFILE_BIT_ARB); attribs[nattribs++] = GLX_CONTEXT_MAJOR_VERSION_ARB; attribs[nattribs++] = version.major_version; attribs[nattribs++] = GLX_CONTEXT_MINOR_VERSION_ARB; attribs[nattribs++] = version.minor_version; attribs[nattribs++] = None; attribs[nattribs++] = 0; X11InhibitErrors ie; m_context = glXCreateContextAttribsARB(GetDisplay(), m_fb_config, share_context, True, attribs); XSync(GetDisplay(), False); if (ie.HadError()) m_context = nullptr; if (!m_context) return false; if (make_current) { if (!glXMakeCurrent(GetDisplay(), GetDrawable(), m_context)) { Log_ErrorPrint("glXMakeCurrent() failed"); glXDestroyContext(GetDisplay(), m_context); m_context = nullptr; return false; } } return true; } } // namespace GL