Duckstation/src/common/gl/context_glx.cpp

329 lines
8 KiB
C++
Raw Normal View History

#include "context_glx.h"
#include "../assert.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);
2020-07-15 05:50:30 +00:00
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;
}
2020-07-15 05:50:30 +00:00
m_vi = glXGetVisualFromFBConfig(GetDisplay(), m_fb_config);
if (!m_vi)
{
Log_ErrorPrintf("glXGetVisualFromFBConfig() failed");
return false;
}
2020-07-15 05:50:30 +00:00
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