dolphin/Source/Core/Common/GL/GLInterface/GLX.cpp

202 lines
5.9 KiB
C++

// Copyright 2012 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <string>
#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 int (*PFNGLXSWAPINTERVALSGIPROC)(int interval);
static PFNGLXCREATECONTEXTATTRIBSPROC glXCreateContextAttribs = nullptr;
static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = nullptr;
static bool s_glxError;
static int ctxErrorHandler(Display* dpy, XErrorEvent* ev)
{
s_glxError = true;
return 0;
}
void cInterfaceGLX::SwapInterval(int Interval)
{
if (glXSwapIntervalSGI)
glXSwapIntervalSGI(Interval);
else
ERROR_LOG(VIDEO, "No support for SwapInterval (framerate clamped to monitor refresh rate).");
}
void* cInterfaceGLX::GetFuncAddress(const std::string& name)
{
return (void*)glXGetProcAddress((const GLubyte*)name.c_str());
}
void cInterfaceGLX::Swap()
{
glXSwapBuffers(dpy, win);
}
// Create rendering window.
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
bool cInterfaceGLX::Create(void* window_handle, bool core)
{
dpy = XOpenDisplay(nullptr);
int screen = DefaultScreen(dpy);
// checking glx version
int glxMajorVersion, glxMinorVersion;
glXQueryVersion(dpy, &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,
None};
int fbcount = 0;
GLXFBConfig* fbc = glXChooseFBConfig(dpy, screen, visual_attribs, &fbcount);
if (!fbc || !fbcount)
{
ERROR_LOG(VIDEO, "Failed to retrieve a framebuffer config");
return false;
}
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};
ctx = nullptr;
if (core)
{
ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, context_attribs);
XSync(dpy, False);
}
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};
s_glxError = false;
ctx = glXCreateContextAttribs(dpy, fbconfig, 0, True, context_attribs_33);
XSync(dpy, False);
}
if (!ctx || s_glxError)
{
int 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);
XSync(dpy, False);
}
if (!ctx || s_glxError)
{
ERROR_LOG(VIDEO, "Unable to create GL context.");
return false;
}
XSetErrorHandler(oldHandler);
XWindow.Initialize(dpy);
Window parent = (Window)window_handle;
XWindowAttributes attribs;
if (!XGetWindowAttributes(dpy, parent, &attribs))
{
ERROR_LOG(VIDEO, "Window attribute retrieval failed");
return false;
}
s_backbuffer_width = attribs.width;
s_backbuffer_height = attribs.height;
win = XWindow.CreateXWindow(parent, vi);
XFree(vi);
return true;
}
bool cInterfaceGLX::MakeCurrent()
{
bool success = glXMakeCurrent(dpy, win, ctx);
if (success)
{
// load this function based on the current bound context
glXSwapIntervalSGI =
(PFNGLXSWAPINTERVALSGIPROC)GLInterface->GetFuncAddress("glXSwapIntervalSGI");
}
return success;
}
bool cInterfaceGLX::ClearCurrent()
{
return glXMakeCurrent(dpy, None, nullptr);
}
// Close backend
void cInterfaceGLX::Shutdown()
{
XWindow.DestroyXWindow();
if (ctx)
{
glXDestroyContext(dpy, ctx);
XCloseDisplay(dpy);
ctx = nullptr;
}
}