mpv/video/out/opengl/context_x11.c

359 lines
11 KiB
C
Raw Normal View History

/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <X11/Xlib.h>
#include <GL/glx.h>
vo_opengl: add our own copy of OpenGL headers gl_headers.h is basically header_fixes.h done consequently. It contains all OpenGL defines (and some typedefs) we need. We don't include GL headers provided by the system anymore. Some care has to be taken by certain windowing APIs including all of gl.h anyway. Then the definitions could clash. Fortunately, redefining preprocessor symbols to the same content is allowed and ignored. Also, redefining typedefs to the same thing is allowed in C11. Apparently the latter is not allowed in C99, so there is an imperfect attempt to avoid the typedefs if required API symbols are apparently present already. The nost risky part about this are the standard typedefs and GLAPIENTRY. The latter is different only on win32 (and at least consistently so). The typedefs are mostly based on stdint.h typedefs, which khrplatform.h clumsily emulates on platforms which don't have it. The biggest difference is that we define GLsizeiptr directly to ptrdiff_t, instead of checking for the _WIN64 symbol and defining it to long or long long. This also typedefs GLsync to __GLsync, just like the khronos headers. Although symbols prefixed with __ are implementation reserved, khronos also violates this rule, and having the same definition as khronos will avoid problems on duplicate definitions. We can simplify the build scripts too. The ios-gl check seems a bit wrong now (what we really want to test for is EAGLContext), but I can't test and thus can't improve it. cuda_dynamic.h redefined two GL symbols; just include the new headers directly instead.
2017-04-07 12:30:51 +00:00
// FreeBSD 10.0-CURRENT lacks the GLX_ARB_create_context extension completely
#ifndef GLX_CONTEXT_MAJOR_VERSION_ARB
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
#define GLX_CONTEXT_FLAGS_ARB 0x2094
#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
#ifndef __APPLE__
// These are respectively 0x00000001 and 0x00000002 on OSX
#define GLX_CONTEXT_DEBUG_BIT_ARB 0x0001
#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
#endif
#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#endif
// GLX_EXT_create_context_es2_profile
#ifndef GLX_CONTEXT_ES2_PROFILE_BIT_EXT
#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
#endif
#include "video/out/x11_common.h"
#include "context.h"
struct glx_context {
XVisualInfo *vinfo;
GLXContext context;
GLXFBConfig fbc;
};
static void glx_uninit(MPGLContext *ctx)
{
struct glx_context *glx_ctx = ctx->priv;
if (glx_ctx->vinfo)
XFree(glx_ctx->vinfo);
if (glx_ctx->context) {
Display *display = ctx->vo->x11->display;
glXMakeCurrent(display, None, NULL);
glXDestroyContext(display, glx_ctx->context);
}
vo_x11_uninit(ctx->vo);
}
static bool create_context_x11_old(struct MPGLContext *ctx)
{
struct glx_context *glx_ctx = ctx->priv;
Display *display = ctx->vo->x11->display;
struct vo *vo = ctx->vo;
GL *gl = ctx->gl;
if (glx_ctx->context)
return true;
if (!glx_ctx->vinfo) {
MP_FATAL(vo, "Can't create a legacy GLX context without X visual\n");
return false;
}
GLXContext new_context = glXCreateContext(display, glx_ctx->vinfo, NULL,
True);
if (!new_context) {
2013-09-11 19:25:03 +00:00
MP_FATAL(vo, "Could not create GLX context!\n");
return false;
}
if (!glXMakeCurrent(display, ctx->vo->x11->window, new_context)) {
2013-09-11 19:25:03 +00:00
MP_FATAL(vo, "Could not set GLX context!\n");
glXDestroyContext(display, new_context);
return false;
}
const char *glxstr = glXQueryExtensionsString(display, ctx->vo->x11->screen);
mpgl_load_functions(gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
glx_ctx->context = new_context;
return true;
}
typedef GLXContext (*glXCreateContextAttribsARBProc)
(Display*, GLXFBConfig, GLXContext, Bool, const int*);
static bool create_context_x11_gl3(struct MPGLContext *ctx, int vo_flags,
int gl_version, bool es)
{
struct glx_context *glx_ctx = ctx->priv;
struct vo *vo = ctx->vo;
if (glx_ctx->context)
return true;
glXCreateContextAttribsARBProc glXCreateContextAttribsARB =
(glXCreateContextAttribsARBProc)
glXGetProcAddressARB((const GLubyte *)"glXCreateContextAttribsARB");
const char *glxstr =
glXQueryExtensionsString(vo->x11->display, vo->x11->screen);
bool have_ctx_ext = glxstr && !!strstr(glxstr, "GLX_ARB_create_context");
if (!(have_ctx_ext && glXCreateContextAttribsARB)) {
return false;
}
int ctx_flags = vo_flags & VOFLAG_GL_DEBUG ? GLX_CONTEXT_DEBUG_BIT_ARB : 0;
int profile_mask = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
if (es) {
profile_mask = GLX_CONTEXT_ES2_PROFILE_BIT_EXT;
if (!(glxstr && strstr(glxstr, "GLX_EXT_create_context_es2_profile")))
return false;
}
int context_attribs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, MPGL_VER_GET_MAJOR(gl_version),
GLX_CONTEXT_MINOR_VERSION_ARB, MPGL_VER_GET_MINOR(gl_version),
GLX_CONTEXT_PROFILE_MASK_ARB, profile_mask,
GLX_CONTEXT_FLAGS_ARB, ctx_flags,
None
};
vo_x11_silence_xlib(1);
GLXContext context = glXCreateContextAttribsARB(vo->x11->display,
glx_ctx->fbc, 0, True,
context_attribs);
vo_x11_silence_xlib(-1);
if (!context)
return false;
// set context
if (!glXMakeCurrent(vo->x11->display, vo->x11->window, context)) {
2013-09-11 19:25:03 +00:00
MP_FATAL(vo, "Could not set GLX context!\n");
glXDestroyContext(vo->x11->display, context);
return false;
}
glx_ctx->context = context;
mpgl_load_functions(ctx->gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
return true;
}
// The GL3/FBC initialization code roughly follows/copies from:
// http://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX)
// but also uses some of the old code.
static GLXFBConfig select_fb_config(struct vo *vo, const int *attribs, int flags)
{
int fbcount;
GLXFBConfig *fbc = glXChooseFBConfig(vo->x11->display, vo->x11->screen,
attribs, &fbcount);
if (!fbc)
return NULL;
// The list in fbc is sorted (so that the first element is the best).
GLXFBConfig fbconfig = fbcount > 0 ? fbc[0] : NULL;
if (flags & VOFLAG_ALPHA) {
for (int n = 0; n < fbcount; n++) {
XVisualInfo *v = glXGetVisualFromFBConfig(vo->x11->display, fbc[n]);
if (v) {
bool is_rgba = vo_x11_is_rgba_visual(v);
XFree(v);
if (is_rgba) {
fbconfig = fbc[n];
break;
}
}
}
}
XFree(fbc);
return fbconfig;
}
static void set_glx_attrib(int *attribs, int name, int value)
{
for (int n = 0; attribs[n * 2 + 0] != None; n++) {
if (attribs[n * 2 + 0] == name) {
attribs[n * 2 + 1] = value;
break;
}
}
}
static int glx_init(struct MPGLContext *ctx, int flags)
{
struct vo *vo = ctx->vo;
struct glx_context *glx_ctx = ctx->priv;
if (!vo_x11_init(ctx->vo))
goto uninit;
int glx_major, glx_minor;
if (!glXQueryVersion(vo->x11->display, &glx_major, &glx_minor)) {
MP_ERR(vo, "GLX not found.\n");
goto uninit;
}
// FBConfigs were added in GLX version 1.3.
if (MPGL_VER(glx_major, glx_minor) < MPGL_VER(1, 3)) {
2013-09-11 19:25:03 +00:00
MP_ERR(vo, "GLX version older than 1.3.\n");
goto uninit;
}
int glx_attribs[] = {
GLX_X_RENDERABLE, True,
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_ALPHA_SIZE, 0,
GLX_DOUBLEBUFFER, True,
None
};
GLXFBConfig fbc = NULL;
if (flags & VOFLAG_ALPHA) {
set_glx_attrib(glx_attribs, GLX_ALPHA_SIZE, 1);
fbc = select_fb_config(vo, glx_attribs, flags);
if (!fbc) {
set_glx_attrib(glx_attribs, GLX_ALPHA_SIZE, 0);
flags &= ~VOFLAG_ALPHA;
}
}
if (!fbc)
fbc = select_fb_config(vo, glx_attribs, flags);
if (!fbc) {
2013-09-11 19:25:03 +00:00
MP_ERR(vo, "no GLX support present\n");
goto uninit;
}
int fbid = -1;
if (!glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_FBCONFIG_ID, &fbid))
MP_VERBOSE(vo, "GLX chose FB config with ID 0x%x\n", fbid);
glx_ctx->fbc = fbc;
glx_ctx->vinfo = glXGetVisualFromFBConfig(vo->x11->display, fbc);
if (glx_ctx->vinfo) {
MP_VERBOSE(vo, "GLX chose visual with ID 0x%x\n",
(int)glx_ctx->vinfo->visualid);
} else {
MP_WARN(vo, "Selected GLX FB config has no associated X visual\n");
}
if (!vo_x11_create_vo_window(vo, glx_ctx->vinfo, "gl"))
goto uninit;
bool success = false;
if (!(flags & VOFLAG_GLES)) {
for (int n = 0; mpgl_preferred_gl_versions[n]; n++) {
int version = mpgl_preferred_gl_versions[n];
MP_VERBOSE(vo, "Creating OpenGL %d.%d context...\n",
MPGL_VER_P(version));
if (version >= 300) {
success = create_context_x11_gl3(ctx, flags, version, false);
} else {
success = create_context_x11_old(ctx);
}
if (success)
break;
}
}
if (!success) // try ES
success = create_context_x11_gl3(ctx, flags, 200, true);
if (success && !glXIsDirect(vo->x11->display, glx_ctx->context))
ctx->gl->mpgl_caps |= MPGL_CAP_SW;
if (!success)
goto uninit;
return 0;
uninit:
glx_uninit(ctx);
return -1;
}
static int glx_init_probe(struct MPGLContext *ctx, int flags)
2015-05-13 21:55:22 +00:00
{
int r = glx_init(ctx, flags);
if (r >= 0) {
if (!(ctx->gl->mpgl_caps & MPGL_CAP_VDPAU)) {
MP_VERBOSE(ctx->vo, "No vdpau support found - probing more things.\n");
glx_uninit(ctx);
r = -1;
}
}
return r;
2015-05-13 21:55:22 +00:00
}
static int glx_reconfig(struct MPGLContext *ctx)
2015-05-13 21:55:22 +00:00
{
vo_x11_config_vo_window(ctx->vo);
2015-05-13 21:55:22 +00:00
return 0;
}
static int glx_control(struct MPGLContext *ctx, int *events, int request,
void *arg)
{
return vo_x11_control(ctx->vo, events, request, arg);
}
static void glx_swap_buffers(struct MPGLContext *ctx)
{
glXSwapBuffers(ctx->vo->x11->display, ctx->vo->x11->window);
}
static void glx_wakeup(struct MPGLContext *ctx)
{
vo_x11_wakeup(ctx->vo);
}
static void glx_wait_events(struct MPGLContext *ctx, int64_t until_time_us)
{
vo_x11_wait_events(ctx->vo, until_time_us);
}
2015-05-13 21:55:22 +00:00
const struct mpgl_driver mpgl_driver_x11 = {
.name = "x11",
.priv_size = sizeof(struct glx_context),
.init = glx_init,
.reconfig = glx_reconfig,
.swap_buffers = glx_swap_buffers,
.control = glx_control,
.wakeup = glx_wakeup,
.wait_events = glx_wait_events,
2015-05-13 21:55:22 +00:00
.uninit = glx_uninit,
};
const struct mpgl_driver mpgl_driver_x11_probe = {
.name = "x11probe",
.priv_size = sizeof(struct glx_context),
.init = glx_init_probe,
.reconfig = glx_reconfig,
.swap_buffers = glx_swap_buffers,
.control = glx_control,
.wakeup = glx_wakeup,
.wait_events = glx_wait_events,
.uninit = glx_uninit,
};