diff options
Diffstat (limited to 'src/frontend/duckstation/gl/context_glx.cpp')
-rw-r--r-- | src/frontend/duckstation/gl/context_glx.cpp | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/src/frontend/duckstation/gl/context_glx.cpp b/src/frontend/duckstation/gl/context_glx.cpp new file mode 100644 index 0000000..bd32039 --- /dev/null +++ b/src/frontend/duckstation/gl/context_glx.cpp @@ -0,0 +1,328 @@ +#include "context_glx.h" +#include "../duckstation_compat.h" +#include "../log.h" +#include <dlfcn.h> +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<Context> ContextGLX::Create(const WindowInfo& wi, const Version* versions_to_try, + size_t num_versions_to_try) +{ + std::unique_ptr<ContextGLX> context = std::make_unique<ContextGLX>(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<void*>(glXGetProcAddress(reinterpret_cast<const GLubyte*>(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<u32>(std::max(interval, 0))) != 0); + } + else if (GLAD_GLX_SGI_swap_control) + { + return (glXSwapIntervalSGI(interval) != 0); + } + else + { + return false; + } +} + +std::unique_ptr<Context> ContextGLX::CreateSharedContext(const WindowInfo& wi) +{ + std::unique_ptr<ContextGLX> context = std::make_unique<ContextGLX>(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<Window>(reinterpret_cast<uintptr_t>(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 |