summaryrefslogtreecommitdiffstats
path: root/video/out
diff options
context:
space:
mode:
Diffstat (limited to 'video/out')
-rw-r--r--video/out/drm_common.c250
-rw-r--r--video/out/drm_common.h19
-rw-r--r--video/out/opengl/common.c47
-rw-r--r--video/out/opengl/common.h8
-rw-r--r--video/out/opengl/drm_egl.c439
-rw-r--r--video/out/opengl/hwdec.c2
-rw-r--r--video/out/opengl/hwdec.h2
-rw-r--r--video/out/opengl/hwdec_vaegl.c2
-rw-r--r--video/out/opengl/hwdec_vaglx.c8
-rw-r--r--video/out/opengl/hwdec_vdpau.c2
-rw-r--r--video/out/opengl/nnedi3.c219
-rw-r--r--video/out/opengl/nnedi3.h47
-rw-r--r--video/out/opengl/nnedi3_weights.binbin0 -> 161280 bytes
-rw-r--r--video/out/opengl/superxbr.c234
-rw-r--r--video/out/opengl/superxbr.h36
-rw-r--r--video/out/opengl/utils.c59
-rw-r--r--video/out/opengl/utils.h5
-rw-r--r--video/out/opengl/video.c358
-rw-r--r--video/out/opengl/video.h7
-rw-r--r--video/out/opengl/w32.c58
-rw-r--r--video/out/opengl/x11.c4
-rw-r--r--video/out/opengl/x11egl.c2
-rw-r--r--video/out/vo.c17
-rw-r--r--video/out/vo.h5
-rw-r--r--video/out/vo_direct3d.c8
-rw-r--r--video/out/vo_drm.c365
-rw-r--r--video/out/vo_opengl.c56
-rw-r--r--video/out/vo_opengl_cb.c203
-rw-r--r--video/out/vo_vdpau.c14
-rw-r--r--video/out/w32_common.c154
-rw-r--r--video/out/w32_common.h2
-rw-r--r--video/out/win32/displayconfig.c236
-rw-r--r--video/out/win32/displayconfig.h27
-rw-r--r--video/out/x11_common.c18
-rw-r--r--video/out/x11_common.h2
35 files changed, 2306 insertions, 609 deletions
diff --git a/video/out/drm_common.c b/video/out/drm_common.c
index 1ea0e45ae8..3462a5d4de 100644
--- a/video/out/drm_common.c
+++ b/video/out/drm_common.c
@@ -1,8 +1,6 @@
/*
* This file is part of mpv.
*
- * by rr- <rr-@sakuya.pl>
- *
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -15,6 +13,11 @@
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * You can alternatively redistribute this file 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.
*/
#include <errno.h>
@@ -35,16 +38,216 @@
#define EVT_INTERRUPT 255
#define HANDLER_ACQUIRE 0
#define HANDLER_RELEASE 1
+#define RELEASE_SIGNAL SIGUSR1
+#define ACQUIRE_SIGNAL SIGUSR2
static int vt_switcher_pipe[2];
+// KMS ------------------------------------------------------------------------
+
+static bool is_connector_valid(struct kms *kms, int connector_id,
+ drmModeConnector *connector, bool silent)
+{
+ if (!connector) {
+ if (!silent) {
+ MP_ERR(kms, "Cannot get connector %d: %s\n", connector_id,
+ mp_strerror(errno));
+ }
+ return false;
+ }
+
+ if (connector->connection != DRM_MODE_CONNECTED) {
+ if (!silent) {
+ MP_ERR(kms, "Connector %d is disconnected\n", connector_id);
+ }
+ return false;
+ }
+
+ if (connector->count_modes == 0) {
+ if (!silent) {
+ MP_ERR(kms, "Connector %d has no valid modes\n", connector_id);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+static bool setup_connector(
+ struct kms *kms, const drmModeRes *res, int connector_id)
+{
+ drmModeConnector *connector = NULL;
+ if (connector_id == -1) {
+ // get the first connected connector
+ for (int i = 0; i < res->count_connectors; i++) {
+ connector = drmModeGetConnector(kms->fd, res->connectors[i]);
+ if (is_connector_valid(kms, i, connector, true)) {
+ connector_id = i;
+ break;
+ }
+ if (connector) {
+ drmModeFreeConnector(connector);
+ connector = NULL;
+ }
+ }
+ if (connector_id == -1) {
+ MP_ERR(kms, "No connected connectors found\n");
+ return false;
+ }
+ }
+
+ if (connector_id < 0 || connector_id >= res->count_connectors) {
+ MP_ERR(kms, "Bad connector ID. Max valid connector ID = %u\n",
+ res->count_connectors);
+ return false;
+ }
+
+ connector = drmModeGetConnector(kms->fd, res->connectors[connector_id]);
+ if (!is_connector_valid(kms, connector_id, connector, false))
+ return false;
+
+ kms->connector = connector;
+ return true;
+}
+
+static bool setup_crtc(struct kms *kms, const drmModeRes *res)
+{
+ for (unsigned int i = 0; i < kms->connector->count_encoders; ++i) {
+ drmModeEncoder *encoder
+ = drmModeGetEncoder(kms->fd, kms->connector->encoders[i]);
+ if (!encoder) {
+ MP_WARN(kms, "Cannot retrieve encoder %u:%u: %s\n",
+ i, kms->connector->encoders[i], mp_strerror(errno));
+ continue;
+ }
+
+ // iterate all global CRTCs
+ for (unsigned int j = 0; j < res->count_crtcs; ++j) {
+ // check whether this CRTC works with the encoder
+ if (!(encoder->possible_crtcs & (1 << j)))
+ continue;
+
+ kms->encoder = encoder;
+ kms->crtc_id = encoder->crtc_id;
+ return true;
+ }
+
+ drmModeFreeEncoder(encoder);
+ }
+
+ MP_ERR(kms,
+ "Connector %u has no suitable CRTC\n",
+ kms->connector->connector_id);
+ return false;
+}
+
+static bool setup_mode(struct kms *kms, int mode_id)
+{
+ if (mode_id < 0 || mode_id >= kms->connector->count_modes) {
+ MP_ERR(
+ kms,
+ "Bad mode ID (max = %d).\n",
+ kms->connector->count_modes - 1);
+
+ MP_INFO(kms, "Available modes:\n");
+ for (unsigned int i = 0; i < kms->connector->count_modes; i++) {
+ MP_INFO(kms,
+ "Mode %d: %s (%dx%d)\n",
+ i,
+ kms->connector->modes[i].name,
+ kms->connector->modes[i].hdisplay,
+ kms->connector->modes[i].vdisplay);
+ }
+ return false;
+ }
+
+ kms->mode = kms->connector->modes[mode_id];
+ return true;
+}
+
+
+struct kms *kms_create(struct mp_log *log)
+{
+ struct kms *ret = talloc(NULL, struct kms);
+ *ret = (struct kms) {
+ .log = mp_log_new(ret, log, "kms"),
+ .fd = -1,
+ .connector = NULL,
+ .encoder = NULL,
+ .mode = { 0 },
+ .crtc_id = -1,
+ };
+ return ret;
+}
+
+bool kms_setup(struct kms *kms, const char *device_path, int connector_id, int mode_id)
+{
+ kms->fd = open(device_path, O_RDWR | O_CLOEXEC);
+ if (kms->fd < 0) {
+ MP_ERR(kms, "Cannot open \"%s\": %s.\n", device_path, mp_strerror(errno));
+ return false;
+ }
+
+ drmModeRes *res = drmModeGetResources(kms->fd);
+ if (!res) {
+ MP_ERR(kms, "Cannot retrieve DRM resources: %s\n", mp_strerror(errno));
+ return false;
+ }
+
+ if (!setup_connector(kms, res, mode_id))
+ return false;
+ if (!setup_crtc(kms, res))
+ return false;
+ if (!setup_mode(kms, mode_id))
+ return false;
+
+ return true;
+}
+
+void kms_destroy(struct kms *kms)
+{
+ if (!kms)
+ return;
+ if (kms->connector) {
+ drmModeFreeConnector(kms->connector);
+ kms->connector = NULL;
+ }
+ if (kms->encoder) {
+ drmModeFreeEncoder(kms->encoder);
+ kms->encoder = NULL;
+ }
+ close(kms->fd);
+ talloc_free(kms);
+}
+
+
+
+// VT switcher ----------------------------------------------------------------
+
static void vt_switcher_sighandler(int sig)
{
- unsigned char event = sig == SIGUSR1 ? EVT_RELEASE : EVT_ACQUIRE;
+ unsigned char event = sig == RELEASE_SIGNAL ? EVT_RELEASE : EVT_ACQUIRE;
write(vt_switcher_pipe[1], &event, sizeof(event));
}
-int vt_switcher_init(struct vt_switcher *s, struct mp_log *log)
+static bool has_signal_installed(int signo)
+{
+ struct sigaction act = { 0 };
+ sigaction(signo, 0, &act);
+ return act.sa_handler != 0;
+}
+
+static int install_signal(int signo, void (*handler)(int))
+{
+ struct sigaction act = { 0 };
+ act.sa_handler = handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ return sigaction(signo, &act, NULL);
+}
+
+
+bool vt_switcher_init(struct vt_switcher *s, struct mp_log *log)
{
s->log = log;
s->tty_fd = -1;
@@ -53,37 +256,48 @@ int vt_switcher_init(struct vt_switcher *s, struct mp_log *log)
if (mp_make_cloexec_pipe(vt_switcher_pipe)) {
MP_ERR(s, "Creating pipe failed: %s\n", mp_strerror(errno));
- return -1;
+ return false;
}
s->tty_fd = open("/dev/tty", O_RDWR | O_CLOEXEC);
if (s->tty_fd < 0) {
MP_ERR(s, "Can't open TTY for VT control: %s\n", mp_strerror(errno));
- return -1;
+ return false;
}
- struct sigaction act;
- act.sa_handler = vt_switcher_sighandler;
- act.sa_flags = SA_RESTART;
- sigemptyset(&act.sa_mask);
- sigaction(SIGUSR1, &act, 0);
- sigaction(SIGUSR2, &act, 0);
+ if (has_signal_installed(RELEASE_SIGNAL)) {
+ MP_ERR(s, "Can't handle VT release - signal already used\n");
+ return false;
+ }
+ if (has_signal_installed(ACQUIRE_SIGNAL)) {
+ MP_ERR(s, "Can't handle VT acquire - signal already used\n");
+ return false;
+ }
+
+ if (install_signal(RELEASE_SIGNAL, vt_switcher_sighandler)) {
+ MP_ERR(s, "Failed to install release signal: %s\n", mp_strerror(errno));
+ return false;
+ }
+ if (install_signal(ACQUIRE_SIGNAL, vt_switcher_sighandler)) {
+ MP_ERR(s, "Failed to install acquire signal: %s\n", mp_strerror(errno));
+ return false;
+ }
struct vt_mode vt_mode;
if (ioctl(s->tty_fd, VT_GETMODE, &vt_mode) < 0) {
MP_ERR(s, "VT_GETMODE failed: %s\n", mp_strerror(errno));
- return -1;
+ return false;
}
vt_mode.mode = VT_PROCESS;
- vt_mode.relsig = SIGUSR1;
- vt_mode.acqsig = SIGUSR2;
+ vt_mode.relsig = RELEASE_SIGNAL;
+ vt_mode.acqsig = ACQUIRE_SIGNAL;
if (ioctl(s->tty_fd, VT_SETMODE, &vt_mode) < 0) {
MP_ERR(s, "VT_SETMODE failed: %s\n", mp_strerror(errno));
- return -1;
+ return false;
}
- return 0;
+ return true;
}
void vt_switcher_acquire(struct vt_switcher *s,
@@ -108,6 +322,8 @@ void vt_switcher_interrupt_poll(struct vt_switcher *s)
void vt_switcher_destroy(struct vt_switcher *s)
{
+ install_signal(RELEASE_SIGNAL, SIG_DFL);
+ install_signal(ACQUIRE_SIGNAL, SIG_DFL);
close(s->tty_fd);
close(vt_switcher_pipe[0]);
close(vt_switcher_pipe[1]);
diff --git a/video/out/drm_common.h b/video/out/drm_common.h
index 5e6c1915ba..98a4bad04d 100644
--- a/video/out/drm_common.h
+++ b/video/out/drm_common.h
@@ -18,6 +18,19 @@
#ifndef MP_VT_SWITCHER_H
#define MP_VT_SWITCHER_H
+#include <stdbool.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+struct kms {
+ struct mp_log *log;
+ int fd;
+ drmModeConnector *connector;
+ drmModeEncoder *encoder;
+ drmModeModeInfo mode;
+ uint32_t crtc_id;
+};
+
struct vt_switcher {
int tty_fd;
struct mp_log *log;
@@ -25,7 +38,7 @@ struct vt_switcher {
void *handler_data[2];
};
-int vt_switcher_init(struct vt_switcher *s, struct mp_log *log);
+bool vt_switcher_init(struct vt_switcher *s, struct mp_log *log);
void vt_switcher_destroy(struct vt_switcher *s);
void vt_switcher_poll(struct vt_switcher *s, int timeout_ms);
void vt_switcher_interrupt_poll(struct vt_switcher *s);
@@ -33,4 +46,8 @@ void vt_switcher_interrupt_poll(struct vt_switcher *s);
void vt_switcher_acquire(struct vt_switcher *s, void (*handler)(void*), void *user_data);
void vt_switcher_release(struct vt_switcher *s, void (*handler)(void*), void *user_data);
+struct kms *kms_create(struct mp_log *log);
+bool kms_setup(struct kms *kms, const char *device_path, int conn_id, int mode_id);
+void kms_destroy(struct kms *kms);
+
#endif
diff --git a/video/out/opengl/common.c b/video/out/opengl/common.c
index f045184373..54389e15cf 100644
--- a/video/out/opengl/common.c
+++ b/video/out/opengl/common.c
@@ -83,9 +83,7 @@ struct gl_functions {
const char *extension; // introduced with this extension in any version
int provides; // bitfield of MPGL_CAP_* constants
int ver_core; // introduced as required function
- int ver_removed; // removed as required function (no replacement)
int ver_es_core; // introduced as required GL ES function
- int ver_es_removed; // removed as required function (no replacement)
const struct gl_function *functions;
};
@@ -173,6 +171,7 @@ static const struct gl_functions gl_functions[] = {
.ver_core = 300,
.ver_es_core = 300,
.functions = (const struct gl_function[]) {
+ DEF_FN(BindBufferBase),
DEF_FN(GetStringi),
// for ES 3.0
DEF_FN(GetTexLevelParameteriv),
@@ -230,6 +229,16 @@ static const struct gl_functions gl_functions[] = {
.extension = "GL_ARB_texture_rg",
.provides = MPGL_CAP_TEX_RG,
},
+ {
+ .ver_core = 320,
+ .extension = "GL_ARB_sync",
+ .functions = (const struct gl_function[]) {
+ DEF_FN(FenceSync),
+ DEF_FN(ClientWaitSync),
+ DEF_FN(DeleteSync),
+ {0}
+ },
+ },
// Swap control, always an OS specific extension
// The OSX code loads this manually.
{
@@ -305,6 +314,16 @@ static const struct gl_functions gl_functions[] = {
{0}
},
},
+ // uniform buffer object extensions, requires OpenGL 3.1.
+ {
+ .ver_core = 310,
+ .extension = "GL_ARB_uniform_buffer_object",
+ .functions = (const struct gl_function[]) {
+ DEF_FN(GetUniformBlockIndex),
+ DEF_FN(UniformBlockBinding),
+ {0}
+ },
+ },
};
#undef FN_OFFS
@@ -389,10 +408,6 @@ void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n),
const struct gl_functions *section = &gl_functions[n];
int version = gl->es ? gl->es : gl->version;
int ver_core = gl->es ? section->ver_es_core : section->ver_core;
- int ver_removed = gl->es ? section->ver_es_removed : section->ver_removed;
-
- if (ver_removed && version >= ver_removed)
- continue;
// NOTE: Function entrypoints can exist, even if they do not work.
// We must always check extension strings and versions.
@@ -450,16 +465,12 @@ void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n),
if (gl->es >= 300)
gl->glsl_version = 300;
} else {
- if (gl->version >= 200)
- gl->glsl_version = 110;
- if (gl->version >= 210)
- gl->glsl_version = 120;
- if (gl->version >= 300)
- gl->glsl_version = 130;
- // Specifically needed for OSX (normally we request 3.0 contexts only, but
- // OSX always creates 3.2 contexts when requesting a core context).
- if (gl->version >= 320)
- gl->glsl_version = 150;
+ gl->glsl_version = 110;
+ int glsl_major = 0, glsl_minor = 0;
+ if (sscanf(shader, "%d.%d", &glsl_major, &glsl_minor) == 2)
+ gl->glsl_version = glsl_major * 100 + glsl_minor;
+ // GLSL 400 defines "sample" as keyword - breaks custom shaders.
+ gl->glsl_version = MPMIN(gl->glsl_version, 330);
}
if (is_software_gl(gl)) {
@@ -492,6 +503,7 @@ void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *),
extern const struct mpgl_driver mpgl_driver_x11;
extern const struct mpgl_driver mpgl_driver_x11egl;
+extern const struct mpgl_driver mpgl_driver_drm_egl;
extern const struct mpgl_driver mpgl_driver_cocoa;
extern const struct mpgl_driver mpgl_driver_wayland;
extern const struct mpgl_driver mpgl_driver_w32;
@@ -513,6 +525,9 @@ static const struct mpgl_driver *const backends[] = {
#if HAVE_EGL_X11
&mpgl_driver_x11egl,
#endif
+#if HAVE_EGL_DRM
+ &mpgl_driver_drm_egl,
+#endif
#if HAVE_GL_X11
&mpgl_driver_x11,
#endif
diff --git a/video/out/opengl/common.h b/video/out/opengl/common.h
index 35d303e96c..d87be595ba 100644
--- a/video/out/opengl/common.h
+++ b/video/out/opengl/common.h
@@ -192,6 +192,7 @@ struct GL {
void (GLAPIENTRY *GenBuffers)(GLsizei, GLuint *);
void (GLAPIENTRY *DeleteBuffers)(GLsizei, const GLuint *);
void (GLAPIENTRY *BindBuffer)(GLenum, GLuint);
+ void (GLAPIENTRY *BindBufferBase)(GLenum, GLuint, GLuint);
GLvoid * (GLAPIENTRY * MapBuffer)(GLenum, GLenum);
GLboolean (GLAPIENTRY *UnmapBuffer)(GLenum);
void (GLAPIENTRY *BufferData)(GLenum, intptr_t, const GLvoid *, GLenum);
@@ -244,6 +245,10 @@ struct GL {
void (GLAPIENTRY *UniformMatrix3fv)(GLint, GLsizei, GLboolean,
const GLfloat *);
+ GLsync (GLAPIENTRY *FenceSync)(GLenum, GLbitfield);
+ GLenum (GLAPIENTRY *ClientWaitSync)(GLsync, GLbitfield, GLuint64);
+ void (GLAPIENTRY *DeleteSync)(GLsync sync);
+
void (GLAPIENTRY *VDPAUInitNV)(const GLvoid *, const GLvoid *);
void (GLAPIENTRY *VDPAUFiniNV)(void);
GLvdpauSurfaceNV (GLAPIENTRY *VDPAURegisterOutputSurfaceNV)
@@ -256,6 +261,9 @@ struct GL {
GLint (GLAPIENTRY *GetVideoSync)(GLuint *);
GLint (GLAPIENTRY *WaitVideoSync)(GLint, GLint, unsigned int *);
+ GLuint (GLAPIENTRY *GetUniformBlockIndex)(GLuint, const GLchar *);
+ void (GLAPIENTRY *UniformBlockBinding)(GLuint, GLuint, GLuint);
+
void (GLAPIENTRY *DebugMessageCallback)(MP_GLDEBUGPROC callback,
const void *userParam);
diff --git a/video/out/opengl/drm_egl.c b/video/out/opengl/drm_egl.c
new file mode 100644
index 0000000000..f8e528201a
--- /dev/null
+++ b/video/out/opengl/drm_egl.c
@@ -0,0 +1,439 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * You can alternatively redistribute this file 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.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/poll.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <gbm.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GL/gl.h>
+
+#include "common.h"
+#include "common/common.h"
+#include "video/out/drm_common.h"
+
+#define USE_MASTER 0
+
+struct framebuffer
+{
+ struct gbm_bo *bo;
+ int width, height;
+ int fd;
+ int id;
+};
+
+struct gbm
+{
+ struct gbm_surface *surface;
+ struct gbm_device *device;
+ struct gbm_bo *bo;
+ struct gbm_bo *next_bo;
+};
+
+struct egl
+{
+ EGLDisplay display;
+ EGLContext context;
+ EGLSurface surface;
+};
+
+struct priv {
+ struct kms *kms;
+
+ drmEventContext ev;
+ drmModeCrtc *old_crtc;
+
+ struct egl egl;
+ struct gbm gbm;
+ struct framebuffer fb;
+
+ bool active;
+ bool waiting_for_flip;
+
+ bool vt_switcher_active;
+ struct vt_switcher vt_switcher;
+};
+
+static EGLConfig select_fb_config_egl(struct MPGLContext *ctx, bool es)
+{
+ struct priv *p = ctx->priv;
+ const EGLint attributes[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 0,
+ EGL_DEPTH_SIZE, 1,
+ EGL_RENDERABLE_TYPE, es ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT,
+ EGL_NONE
+ };
+ EGLint config_count;
+ EGLConfig config;
+ if (!eglChooseConfig(p->egl.display, attributes, &config, 1, &config_count)) {
+ MP_FATAL(ctx->vo, "Failed to configure EGL.\n");
+ return NULL;
+ }
+ if (!config_count) {
+ MP_FATAL(ctx->vo, "Could not find EGL configuration!\n");
+ return NULL;
+ }
+ return config;
+}
+
+static bool init_egl(struct MPGLContext *ctx, bool es)
+{
+ struct priv *p = ctx->priv;
+ MP_VERBOSE(ctx->vo, "Initializing EGL\n");
+ p->egl.display = eglGetDisplay(p->gbm.device);
+ if (p->egl.display == EGL_NO_DISPLAY) {
+ MP_ERR(ctx->vo, "Failed to get EGL display.\n");
+ return false;
+ }
+ if (!eglInitialize(p->egl.display, NULL, NULL)) {
+ MP_ERR(ctx->vo, "Failed to initialize EGL.\n");
+ return false;
+ }
+ if (!eglBindAPI(es ? EGL_OPENGL_ES_API : EGL_OPENGL_API)) {
+ MP_ERR(ctx->vo, "Failed to set EGL API version.\n");
+ return false;
+ }
+ EGLConfig config = select_fb_config_egl(ctx, es);
+ if (!config) {
+ MP_ERR(ctx->vo, "Failed to configure EGL.\n");
+ return false;
+ }
+ p->egl.context = eglCreateContext(p->egl.display, config, EGL_NO_CONTEXT, NULL);
+ if (!p->egl.context) {
+ MP_ERR(ctx->vo, "Failed to create EGL context.\n");
+ return false;
+ }
+ MP_VERBOSE(ctx->vo, "Initializing EGL surface\n");
+ p->egl.surface = eglCreateWindowSurface(p->egl.display, config, p->gbm.surface, NULL);
+ if (p->egl.surface == EGL_NO_SURFACE) {
+ MP_ERR(ctx->vo, "Failed to create EGL surface.\n");
+ return false;
+ }
+ return true;
+}
+
+static bool init_gbm(struct MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ MP_VERBOSE(ctx->vo, "Creating GBM device\n");
+ p->gbm.device = gbm_create_device(p->kms->fd);
+ if (!p->gbm.device) {
+ MP_ERR(ctx->vo, "Failed to create GBM device.\n");
+ return false;
+ }
+
+ MP_VERBOSE(ctx->vo, "Initializing GBM surface (%d x %d)\n",
+ p->kms->mode.hdisplay, p->kms->mode.vdisplay);
+ p->gbm.surface = gbm_surface_create(
+ p->gbm.device,
+ p->kms->mode.hdisplay,
+ p->kms->mode.vdisplay,
+ GBM_BO_FORMAT_XRGB8888,
+ GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+ if (!p->gbm.surface) {
+ MP_ERR(ctx->vo, "Failed to create GBM surface.\n");
+ return false;
+ }
+ return true;
+}
+
+static void framebuffer_destroy_callback(struct gbm_bo *bo, void *data)
+{
+ struct framebuffer *fb = data;
+ if (fb) {
+ drmModeRmFB(fb->fd, fb->id);
+ }
+}
+
+static void update_framebuffer_from_bo(
+ const struct MPGLContext *ctx, struct gbm_bo *bo)
+{
+ struct priv *p = ctx->priv;
+ p->fb.bo = bo;
+ p->fb.fd = p->kms->fd;
+ p->fb.width = gbm_bo_get_width(bo);
+ p->fb.height = gbm_bo_get_height(bo);
+ int stride = gbm_bo_get_stride(bo);
+ int handle = gbm_bo_get_handle(bo).u32;
+
+ int ret = drmModeAddFB(p->kms->fd, p->fb.width, p->fb.height,
+ 24, 32, stride, handle, &p->fb.id);
+ if (ret) {
+ MP_ERR(ctx->vo, "Failed to create framebuffer: %s\n", mp_strerror(errno));
+ }
+ gbm_bo_set_user_data(bo, &p->fb, framebuffer_destroy_callback);
+}
+
+static void page_flipped(int fd, unsigned int frame, unsigned int sec,
+ unsigned int usec, void *data)
+{
+ struct priv *p = data;
+ p->waiting_for_flip = false;
+}
+
+static bool crtc_setup(struct MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ if (p->active)
+ return true;
+ p->old_crtc = drmModeGetCrtc(p->kms->fd, p->kms->crtc_id);
+ int ret = drmModeSetCrtc(p->kms->fd, p->kms->crtc_id,
+ p->fb.id,
+ 0,
+ 0,
+ &p->kms->connector->connector_id,
+ 1,
+ &p->kms->mode);
+ p->active = true;
+ return ret == 0;
+}
+
+static void crtc_release(struct MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ if (!p->active)
+ return;
+ p->active = false;
+
+ // wait for current page flip
+ while (p->waiting_for_flip) {
+ int ret = drmHandleEvent(p->kms->fd, &p->ev);
+ if (ret) {
+ MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret);
+ break;
+ }
+ }
+
+ if (p->old_crtc) {
+ drmModeSetCrtc(p->kms->fd,
+ p->old_crtc->crtc_id,
+ p->old_crtc->buffer_id,
+ p->old_crtc->x,
+ p->old_crtc->y,
+ &p->kms->connector->connector_id,
+ 1,
+ &p->old_crtc->mode);
+ drmModeFreeCrtc(p->old_crtc);
+ p->old_crtc = NULL;
+ }
+}
+
+static void release_vt(void *data)
+{
+ struct MPGLContext *ctx = data;
+ MP_VERBOSE(ctx->vo, "Releasing VT");
+ crtc_release(ctx);
+ if (USE_MASTER) {
+ //this function enables support for switching to x, weston etc.
+ //however, for whatever reason, it can be called only by root users.
+ //until things change, this is commented.
+ struct priv *p = ctx->priv;
+ if (drmDropMaster(p->kms->fd)) {
+ MP_WARN(ctx->vo, "Failed to drop DRM master: %s\n", mp_strerror(errno));
+ }
+ }
+}
+
+static void acquire_vt(void *data)
+{
+ struct MPGLContext *ctx = data;
+ MP_VERBOSE(ctx->vo, "Acquiring VT");
+ if (USE_MASTER) {
+ struct priv *p = ctx->priv;
+ if (drmSetMaster(p->kms->fd)) {
+ MP_WARN(ctx->vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno));
+ }
+ }
+
+ crtc_setup(ctx);
+}
+
+static void drm_egl_uninit(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ crtc_release(ctx);
+
+ if (p->vt_switcher_active)
+ vt_switcher_destroy(&p->vt_switcher);
+
+ eglMakeCurrent(p->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(p->egl.display, p->egl.context);
+ eglDestroySurface(p->egl.display, p->egl.surface);
+ gbm_surface_destroy(p->gbm.surface);
+ eglTerminate(p->egl.display);
+ gbm_device_destroy(p->gbm.device);
+ p->egl.context = EGL_NO_CONTEXT;
+ eglDestroyContext(p->egl.display, p->egl.context);
+
+ if (p->kms) {
+ kms_destroy(p->kms);
+ p->kms = 0;
+ }
+}
+
+static int drm_egl_init(struct MPGLContext *ctx, int flags)
+{
+ if (ctx->vo->probing) {
+ MP_VERBOSE(ctx->vo, "DRM EGL backend can be activated only manually.\n");
+ return -1;
+