aboutsummaryrefslogtreecommitdiffhomepage
path: root/video
diff options
context:
space:
mode:
authorGravatar Niklas Haas <git@haasn.xyz>2017-09-14 08:04:55 +0200
committerGravatar Niklas Haas <git@haasn.xyz>2017-09-21 15:00:55 +0200
commit65979986a923a8f08019b257c3fe72cd5e8ecf68 (patch)
treeb8f4b8c17d583594aef0ca509064f8b2ff7128d4 /video
parent20f958c9775652c3213588c2a0824f5353276adc (diff)
vo_opengl: refactor into vo_gpu
This is done in several steps: 1. refactor MPGLContext -> struct ra_ctx 2. move GL-specific stuff in vo_opengl into opengl/context.c 3. generalize context creation to support other APIs, and add --gpu-api 4. rename all of the --opengl- options that are no longer opengl-specific 5. move all of the stuff from opengl/* that isn't GL-specific into gpu/ (note: opengl/gl_utils.h became opengl/utils.h) 6. rename vo_opengl to vo_gpu 7. to handle window screenshots, the short-term approach was to just add it to ra_swchain_fns. Long term (and for vulkan) this has to be moved to ra itself (and vo_gpu altered to compensate), but this was a stop-gap measure to prevent this commit from getting too big 8. move ra->fns->flush to ra_gl_ctx instead 9. some other minor changes that I've probably already forgotten Note: This is one half of a major refactor, the other half of which is provided by rossy's following commit. This commit enables support for all linux platforms, while his version enables support for all non-linux platforms. Note 2: vo_opengl_cb.c also re-uses ra_gl_ctx so it benefits from the --opengl- options like --opengl-early-flush, --opengl-finish etc. Should be a strict superset of the old functionality. Disclaimer: Since I have no way of compiling mpv on all platforms, some of these ports were done blindly. Specifically, the blind ports included context_mali_fbdev.c and context_rpi.c. Since they're both based on egl_helpers, the port should have gone smoothly without any major changes required. But if somebody complains about a compile error on those platforms (assuming anybody actually uses them), you know where to complain.
Diffstat (limited to 'video')
-rw-r--r--video/out/gpu/context.c186
-rw-r--r--video/out/gpu/context.h95
-rw-r--r--video/out/gpu/hwdec.c (renamed from video/out/opengl/hwdec.c)0
-rw-r--r--video/out/gpu/hwdec.h (renamed from video/out/opengl/hwdec.h)0
-rw-r--r--video/out/gpu/lcms.c (renamed from video/out/opengl/lcms.c)0
-rw-r--r--video/out/gpu/lcms.h (renamed from video/out/opengl/lcms.h)0
-rw-r--r--video/out/gpu/osd.c (renamed from video/out/opengl/osd.c)0
-rw-r--r--video/out/gpu/osd.h (renamed from video/out/opengl/osd.h)0
-rw-r--r--video/out/gpu/ra.c (renamed from video/out/opengl/ra.c)0
-rw-r--r--video/out/gpu/ra.h (renamed from video/out/opengl/ra.h)3
-rw-r--r--video/out/gpu/shader_cache.c (renamed from video/out/opengl/shader_cache.c)7
-rw-r--r--video/out/gpu/shader_cache.h (renamed from video/out/opengl/shader_cache.h)0
-rw-r--r--video/out/gpu/user_shaders.c (renamed from video/out/opengl/user_shaders.c)2
-rw-r--r--video/out/gpu/user_shaders.h (renamed from video/out/opengl/user_shaders.h)0
-rw-r--r--video/out/gpu/utils.c372
-rw-r--r--video/out/gpu/utils.h120
-rw-r--r--video/out/gpu/video.c (renamed from video/out/opengl/video.c)36
-rw-r--r--video/out/gpu/video.h (renamed from video/out/opengl/video.h)1
-rw-r--r--video/out/gpu/video_shaders.c (renamed from video/out/opengl/video_shaders.c)0
-rw-r--r--video/out/gpu/video_shaders.h (renamed from video/out/opengl/video_shaders.h)0
-rw-r--r--video/out/opengl/common.h4
-rw-r--r--video/out/opengl/context.c446
-rw-r--r--video/out/opengl/context.h152
-rw-r--r--video/out/opengl/context_cocoa.c2
-rw-r--r--video/out/opengl/context_drm_egl.c194
-rw-r--r--video/out/opengl/context_glx.c (renamed from video/out/opengl/context_x11.c)196
-rw-r--r--video/out/opengl/context_mali_fbdev.c58
-rw-r--r--video/out/opengl/context_rpi.c84
-rw-r--r--video/out/opengl/context_vdpau.c202
-rw-r--r--video/out/opengl/context_wayland.c74
-rw-r--r--video/out/opengl/context_x11egl.c84
-rw-r--r--video/out/opengl/egl_helpers.c114
-rw-r--r--video/out/opengl/egl_helpers.h19
-rw-r--r--video/out/opengl/formats.h1
-rw-r--r--video/out/opengl/gl_utils.c291
-rw-r--r--video/out/opengl/gl_utils.h56
-rw-r--r--video/out/opengl/hwdec_cuda.c3
-rw-r--r--video/out/opengl/hwdec_ios.m2
-rw-r--r--video/out/opengl/hwdec_osx.c2
-rw-r--r--video/out/opengl/hwdec_rpi.c2
-rw-r--r--video/out/opengl/hwdec_vaegl.c4
-rw-r--r--video/out/opengl/hwdec_vaglx.c5
-rw-r--r--video/out/opengl/hwdec_vdpau.c2
-rw-r--r--video/out/opengl/ra_gl.c7
-rw-r--r--video/out/opengl/ra_gl.h3
-rw-r--r--video/out/opengl/utils.c524
-rw-r--r--video/out/opengl/utils.h151
-rw-r--r--video/out/vo.c6
-rw-r--r--video/out/vo_gpu.c (renamed from video/out/vo_opengl.c)301
-rw-r--r--video/out/vo_opengl_cb.c53
-rw-r--r--video/out/vo_rpi.c2
51 files changed, 2071 insertions, 1795 deletions
diff --git a/video/out/gpu/context.c b/video/out/gpu/context.c
new file mode 100644
index 0000000000..dbabba8b3b
--- /dev/null
+++ b/video/out/gpu/context.c
@@ -0,0 +1,186 @@
+/*
+ * 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 <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <math.h>
+#include <assert.h>
+
+#include "config.h"
+#include "common/common.h"
+#include "common/msg.h"
+#include "options/options.h"
+#include "options/m_option.h"
+#include "video/out/vo.h"
+
+#include "context.h"
+
+extern const struct ra_ctx_fns ra_ctx_glx;
+extern const struct ra_ctx_fns ra_ctx_glx_probe;
+extern const struct ra_ctx_fns ra_ctx_x11_egl;
+extern const struct ra_ctx_fns ra_ctx_drm_egl;
+extern const struct ra_ctx_fns ra_ctx_cocoa;
+extern const struct ra_ctx_fns ra_ctx_wayland_egl;
+extern const struct ra_ctx_fns ra_ctx_wgl;
+extern const struct ra_ctx_fns ra_ctx_angle;
+extern const struct ra_ctx_fns ra_ctx_dxinterop;
+extern const struct ra_ctx_fns ra_ctx_rpi;
+extern const struct ra_ctx_fns ra_ctx_mali;
+extern const struct ra_ctx_fns ra_ctx_vdpauglx;
+
+static const struct ra_ctx_fns *contexts[] = {
+// OpenGL contexts:
+#if HAVE_RPI
+ &ra_ctx_rpi,
+#endif
+/*
+#if HAVE_GL_COCOA
+ &ra_ctx_cocoa,
+#endif
+#if HAVE_EGL_ANGLE_WIN32
+ &ra_ctx_angle,
+#endif
+#if HAVE_GL_WIN32
+ &ra_ctx_wgl,
+#endif
+#if HAVE_GL_DXINTEROP
+ &ra_ctx_dxinterop,
+#endif
+*/
+#if HAVE_GL_X11
+ &ra_ctx_glx_probe,
+#endif
+#if HAVE_EGL_X11
+ &ra_ctx_x11_egl,
+#endif
+#if HAVE_GL_X11
+ &ra_ctx_glx,
+#endif
+#if HAVE_GL_WAYLAND
+ &ra_ctx_wayland_egl,
+#endif
+#if HAVE_EGL_DRM
+ &ra_ctx_drm_egl,
+#endif
+#if HAVE_MALI_FBDEV
+ &ra_ctx_mali,
+#endif
+#if HAVE_VDPAU_GL_X11
+ &ra_ctx_vdpauglx,
+#endif
+};
+
+static bool get_help(struct mp_log *log, struct bstr param)
+{
+ if (bstr_equals0(param, "help")) {
+ mp_info(log, "GPU contexts / APIs:\n");
+ mp_info(log, " auto (autodetect)\n");
+ for (int n = 0; n < MP_ARRAY_SIZE(contexts); n++)
+ mp_info(log, " %s (%s)\n", contexts[n]->name, contexts[n]->type);
+ return true;
+ }
+
+ return false;
+}
+
+int ra_ctx_validate_api(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, struct bstr param)
+{
+ if (get_help(log, param))
+ return M_OPT_EXIT;
+ if (bstr_equals0(param, "auto"))
+ return 1;
+ for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) {
+ if (bstr_equals0(param, contexts[i]->type))
+ return 1;
+ }
+ return M_OPT_INVALID;
+}
+
+int ra_ctx_validate_context(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, struct bstr param)
+{
+ if (get_help(log, param))
+ return M_OPT_EXIT;
+ if (bstr_equals0(param, "auto"))
+ return 1;
+ for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) {
+ if (bstr_equals0(param, contexts[i]->name))
+ return 1;
+ }
+ return M_OPT_INVALID;
+}
+
+// Create a VO window and create a RA context on it.
+// vo_flags: passed to the backend's create window function
+struct ra_ctx *ra_ctx_create(struct vo *vo, const char *context_type,
+ const char *context_name, struct ra_ctx_opts opts)
+{
+ bool api_auto = !context_type || strcmp(context_type, "auto") == 0;
+ bool ctx_auto = !context_name || strcmp(context_name, "auto") == 0;
+
+ if (ctx_auto) {
+ MP_VERBOSE(vo, "Probing for best GPU context.\n");
+ opts.probing = true;
+ }
+
+ // Hack to silence backend (X11/Wayland/etc.) errors. Kill it once backends
+ // are separate from `struct vo`
+ bool old_probing = vo->probing;
+ vo->probing = opts.probing;
+
+ for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) {
+ if (!opts.probing && strcmp(contexts[i]->name, context_name) != 0)
+ continue;
+ if (!api_auto && strcmp(contexts[i]->type, context_type) != 0)
+ continue;
+
+ struct ra_ctx *ctx = talloc_ptrtype(NULL, ctx);
+ *ctx = (struct ra_ctx) {
+ .vo = vo,
+ .global = vo->global,
+ .log = mp_log_new(ctx, vo->log, contexts[i]->type),
+ .opts = opts,
+ .fns = contexts[i],
+ };
+
+ MP_VERBOSE(ctx, "Initializing GPU context '%s'\n", ctx->fns->name);
+ if (contexts[i]->init(ctx)) {
+ vo->probing = old_probing;
+ return ctx;
+ }
+
+ talloc_free(ctx);
+ }
+
+ // If we've reached this point, then none of the contexts matched the name
+ // requested, or the backend creation failed for all of them.
+ MP_ERR(vo, "Failed initializing any suitable GPU context!\n");
+ vo->probing = old_probing;
+ return NULL;
+}
+
+void ra_ctx_destroy(struct ra_ctx **ctx)
+{
+ if (*ctx)
+ (*ctx)->fns->uninit(*ctx);
+ talloc_free(*ctx);
+ *ctx = NULL;
+}
diff --git a/video/out/gpu/context.h b/video/out/gpu/context.h
new file mode 100644
index 0000000000..42de59b75f
--- /dev/null
+++ b/video/out/gpu/context.h
@@ -0,0 +1,95 @@
+#pragma once
+
+#include "video/out/vo.h"
+
+#include "config.h"
+#include "ra.h"
+
+struct ra_ctx_opts {
+ int allow_sw; // allow software renderers
+ int want_alpha; // create an alpha framebuffer if possible
+ int debug; // enable debugging layers/callbacks etc.
+ bool probing; // the backend was auto-probed
+ int swapchain_depth; // max number of images to render ahead
+};
+
+struct ra_ctx {
+ struct vo *vo;
+ struct ra *ra;
+ struct mpv_global *global;
+ struct mp_log *log;
+
+ struct ra_ctx_opts opts;
+ const struct ra_ctx_fns *fns;
+ struct ra_swapchain *swapchain;
+
+ void *priv;
+};
+
+// The functions that make up a ra_ctx.
+struct ra_ctx_fns {
+ const char *type; // API type (for --gpu-api)
+ const char *name; // name (for --gpu-context)
+
+ // Resize the window, or create a new window if there isn't one yet.
+ // Currently, there is an unfortunate interaction with ctx->vo, and
+ // display size etc. are determined by it.
+ bool (*reconfig)(struct ra_ctx *ctx);
+
+ // This behaves exactly like vo_driver.control().
+ int (*control)(struct ra_ctx *ctx, int *events, int request, void *arg);
+
+ // These behave exactly like vo_driver.wakeup/wait_events. They are
+ // optional.
+ void (*wakeup)(struct ra_ctx *ctx);
+ void (*wait_events)(struct ra_ctx *ctx, int64_t until_time_us);
+
+ // Initialize/destroy the 'struct ra' and possibly the underlying VO backend.
+ // Not normally called by the user of the ra_ctx.
+ bool (*init)(struct ra_ctx *ctx);
+ void (*uninit)(struct ra_ctx *ctx);
+};
+
+// Extra struct for the swapchain-related functions so they can be easily
+// inherited from helpers.
+struct ra_swapchain {
+ struct ra_ctx *ctx;
+ struct priv *priv;
+ const struct ra_swapchain_fns *fns;
+
+ bool flip_v; // flip the rendered image vertically (set by the swapchain)
+};
+
+struct ra_swapchain_fns {
+ // Gets the current framebuffer depth in bits (0 if unknown). Optional.
+ int (*color_depth)(struct ra_swapchain *sw);
+
+ // Retrieves a screenshot of the framebuffer. These are always the right
+ // side up, regardless of ra_swapchain->flip_v. Optional.
+ struct mp_image *(*screenshot)(struct ra_swapchain *sw);
+
+ // Called when rendering starts. Returns NULL on failure. This must be
+ // followed by submit_frame, to submit the rendered frame.
+ struct ra_tex *(*start_frame)(struct ra_swapchain *sw);
+
+ // Present the frame. Issued in lockstep with start_frame, with rendering
+ // commands in between. The `frame` is just there for timing data, for
+ // swapchains smart enough to do something with it.
+ bool (*submit_frame)(struct ra_swapchain *sw, const struct vo_frame *frame);
+
+ // Performs a buffer swap. This blocks for as long as necessary to meet
+ // params.swapchain_depth, or until the next vblank (for vsynced contexts)
+ void (*swap_buffers)(struct ra_swapchain *sw);
+};
+
+// Create and destroy a ra_ctx. This also takes care of creating and destroying
+// the underlying `struct ra`, and perhaps the underlying VO backend.
+struct ra_ctx *ra_ctx_create(struct vo *vo, const char *context_type,
+ const char *context_name, struct ra_ctx_opts opts);
+void ra_ctx_destroy(struct ra_ctx **ctx);
+
+struct m_option;
+int ra_ctx_validate_api(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, struct bstr param);
+int ra_ctx_validate_context(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, struct bstr param);
diff --git a/video/out/opengl/hwdec.c b/video/out/gpu/hwdec.c
index 5fbc1aa4a9..5fbc1aa4a9 100644
--- a/video/out/opengl/hwdec.c
+++ b/video/out/gpu/hwdec.c
diff --git a/video/out/opengl/hwdec.h b/video/out/gpu/hwdec.h
index 20bbaae9eb..20bbaae9eb 100644
--- a/video/out/opengl/hwdec.h
+++ b/video/out/gpu/hwdec.h
diff --git a/video/out/opengl/lcms.c b/video/out/gpu/lcms.c
index 8747ae6aa6..8747ae6aa6 100644
--- a/video/out/opengl/lcms.c
+++ b/video/out/gpu/lcms.c
diff --git a/video/out/opengl/lcms.h b/video/out/gpu/lcms.h
index 35bbd61fe0..35bbd61fe0 100644
--- a/video/out/opengl/lcms.h
+++ b/video/out/gpu/lcms.h
diff --git a/video/out/opengl/osd.c b/video/out/gpu/osd.c
index f7c325d1db..f7c325d1db 100644
--- a/video/out/opengl/osd.c
+++ b/video/out/gpu/osd.c
diff --git a/video/out/opengl/osd.h b/video/out/gpu/osd.h
index 6c2b886de3..6c2b886de3 100644
--- a/video/out/opengl/osd.h
+++ b/video/out/gpu/osd.h
diff --git a/video/out/opengl/ra.c b/video/out/gpu/ra.c
index ef1de54d1a..ef1de54d1a 100644
--- a/video/out/opengl/ra.c
+++ b/video/out/gpu/ra.c
diff --git a/video/out/opengl/ra.h b/video/out/gpu/ra.h
index ae7fb9aea7..76f98397f8 100644
--- a/video/out/opengl/ra.h
+++ b/video/out/gpu/ra.h
@@ -436,9 +436,6 @@ struct ra_fns {
// delayed by a few frames. When no value is available, this returns 0.
uint64_t (*timer_stop)(struct ra *ra, ra_timer *timer);
- // Hint that possibly queued up commands should be sent to the GPU. Optional.
- void (*flush)(struct ra *ra);
-
// Associates a marker with any past error messages, for debugging
// purposes. Optional.
void (*debug_marker)(struct ra *ra, const char *msg);
diff --git a/video/out/opengl/shader_cache.c b/video/out/gpu/shader_cache.c
index 90a757617b..afda9cc036 100644
--- a/video/out/opengl/shader_cache.c
+++ b/video/out/gpu/shader_cache.c
@@ -14,7 +14,6 @@
#include "options/path.h"
#include "stream/stream.h"
#include "shader_cache.h"
-#include "formats.h"
#include "utils.h"
// Force cache flush if more than this number of shaders is created.
@@ -361,7 +360,7 @@ void gl_sc_uniform_vec2(struct gl_shader_cache *sc, char *name, float f[2])
u->v.f[1] = f[1];
}
-void gl_sc_uniform_vec3(struct gl_shader_cache *sc, char *name, GLfloat f[3])
+void gl_sc_uniform_vec3(struct gl_shader_cache *sc, char *name, float f[3])
{
struct sc_uniform *u = find_uniform(sc, name);
u->input.type = RA_VARTYPE_FLOAT;
@@ -379,7 +378,7 @@ static void transpose2x2(float r[2 * 2])
}
void gl_sc_uniform_mat2(struct gl_shader_cache *sc, char *name,
- bool transpose, GLfloat *v)
+ bool transpose, float *v)
{
struct sc_uniform *u = find_uniform(sc, name);
u->input.type = RA_VARTYPE_FLOAT;
@@ -401,7 +400,7 @@ static void transpose3x3(float r[3 * 3])
}
void gl_sc_uniform_mat3(struct gl_shader_cache *sc, char *name,
- bool transpose, GLfloat *v)
+ bool transpose, float *v)
{
struct sc_uniform *u = find_uniform(sc, name);
u->input.type = RA_VARTYPE_FLOAT;
diff --git a/video/out/opengl/shader_cache.h b/video/out/gpu/shader_cache.h
index 82a078079b..82a078079b 100644
--- a/video/out/opengl/shader_cache.h
+++ b/video/out/gpu/shader_cache.h
diff --git a/video/out/opengl/user_shaders.c b/video/out/gpu/user_shaders.c
index 58a1ac9e64..446941b03f 100644
--- a/video/out/opengl/user_shaders.c
+++ b/video/out/gpu/user_shaders.c
@@ -17,9 +17,9 @@
#include <assert.h>
+#include "common/msg.h"
#include "misc/ctype.h"
#include "user_shaders.h"
-#include "formats.h"
static bool parse_rpn_szexpr(struct bstr line, struct szexp out[MAX_SZEXP_SIZE])
{
diff --git a/video/out/opengl/user_shaders.h b/video/out/gpu/user_shaders.h
index 94a070c8e2..94a070c8e2 100644
--- a/video/out/opengl/user_shaders.h
+++ b/video/out/gpu/user_shaders.h
diff --git a/video/out/gpu/utils.c b/video/out/gpu/utils.c
new file mode 100644
index 0000000000..f8dcbaac60
--- /dev/null
+++ b/video/out/gpu/utils.c
@@ -0,0 +1,372 @@
+#include "common/msg.h"
+#include "video/out/vo.h"
+#include "utils.h"
+
+// Standard parallel 2D projection, except y1 < y0 means that the coordinate
+// system is flipped, not the projection.
+void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
+ float y0, float y1)
+{
+ if (y1 < y0) {
+ float tmp = y0;
+ y0 = tmp - y1;
+ y1 = tmp;
+ }
+
+ t->m[0][0] = 2.0f / (x1 - x0);
+ t->m[0][1] = 0.0f;
+ t->m[1][0] = 0.0f;
+ t->m[1][1] = 2.0f / (y1 - y0);
+ t->t[0] = -(x1 + x0) / (x1 - x0);
+ t->t[1] = -(y1 + y0) / (y1 - y0);
+}
+
+// Apply the effects of one transformation to another, transforming it in the
+// process. In other words: post-composes t onto x
+void gl_transform_trans(struct gl_transform t, struct gl_transform *x)
+{
+ struct gl_transform xt = *x;
+ x->m[0][0] = t.m[0][0] * xt.m[0][0] + t.m[0][1] * xt.m[1][0];
+ x->m[1][0] = t.m[1][0] * xt.m[0][0] + t.m[1][1] * xt.m[1][0];
+ x->m[0][1] = t.m[0][0] * xt.m[0][1] + t.m[0][1] * xt.m[1][1];
+ x->m[1][1] = t.m[1][0] * xt.m[0][1] + t.m[1][1] * xt.m[1][1];
+ gl_transform_vec(t, &x->t[0], &x->t[1]);
+}
+
+void gl_transform_ortho_fbodst(struct gl_transform *t, struct fbodst fbo)
+{
+ int y_dir = fbo.flip ? -1 : 1;
+ gl_transform_ortho(t, 0, fbo.tex->params.w, 0, fbo.tex->params.h * y_dir);
+}
+
+void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool)
+{
+ for (int i = 0; i < pool->num_buffers; i++)
+ ra_buf_free(ra, &pool->buffers[i]);
+
+ talloc_free(pool->buffers);
+ *pool = (struct ra_buf_pool){0};
+}
+
+static bool ra_buf_params_compatible(const struct ra_buf_params *new,
+ const struct ra_buf_params *old)
+{
+ return new->type == old->type &&
+ new->size <= old->size &&
+ new->host_mapped == old->host_mapped &&
+ new->host_mutable == old->host_mutable;
+}
+
+static bool ra_buf_pool_grow(struct ra *ra, struct ra_buf_pool *pool)
+{
+ struct ra_buf *buf = ra_buf_create(ra, &pool->current_params);
+ if (!buf)
+ return false;
+
+ MP_TARRAY_INSERT_AT(NULL, pool->buffers, pool->num_buffers, pool->index, buf);
+ MP_VERBOSE(ra, "Resized buffer pool of type %u to size %d\n",
+ pool->current_params.type, pool->num_buffers);
+ return true;
+}
+
+struct ra_buf *ra_buf_pool_get(struct ra *ra, struct ra_buf_pool *pool,
+ const struct ra_buf_params *params)
+{
+ assert(!params->initial_data);
+
+ if (!ra_buf_params_compatible(params, &pool->current_params)) {
+ ra_buf_pool_uninit(ra, pool);
+ pool->current_params = *params;
+ }
+
+ // Make sure we have at least one buffer available
+ if (!pool->buffers && !ra_buf_pool_grow(ra, pool))
+ return NULL;
+
+ // Make sure the next buffer is available for use
+ if (!ra->fns->buf_poll(ra, pool->buffers[pool->index]) &&
+ !ra_buf_pool_grow(ra, pool))
+ {
+ return NULL;
+ }
+
+ struct ra_buf *buf = pool->buffers[pool->index++];
+ pool->index %= pool->num_buffers;
+
+ return buf;
+}
+
+bool ra_tex_upload_pbo(struct ra *ra, struct ra_buf_pool *pbo,
+ const struct ra_tex_upload_params *params)
+{
+ if (params->buf)
+ return ra->fns->tex_upload(ra, params);
+
+ struct ra_tex *tex = params->tex;
+ size_t row_size = tex->params.dimensions == 2 ? params->stride :
+ tex->params.w * tex->params.format->pixel_size;
+
+ struct ra_buf_params bufparams = {
+ .type = RA_BUF_TYPE_TEX_UPLOAD,
+ .size = row_size * tex->params.h * tex->params.d,
+ .host_mutable = true,
+ };
+
+ struct ra_buf *buf = ra_buf_pool_get(ra, pbo, &bufparams);
+ if (!buf)
+ return false;
+
+ ra->fns->buf_update(ra, buf, 0, params->src, bufparams.size);
+
+ struct ra_tex_upload_params newparams = *params;
+ newparams.buf = buf;
+ newparams.src = NULL;
+
+ return ra->fns->tex_upload(ra, &newparams);
+}
+
+struct ra_layout std140_layout(struct ra_renderpass_input *inp)
+{
+ size_t el_size = ra_vartype_size(inp->type);
+
+ // std140 packing rules:
+ // 1. The alignment of generic values is their size in bytes
+ // 2. The alignment of vectors is the vector length * the base count, with
+ // the exception of vec3 which is always aligned like vec4
+ // 3. The alignment of arrays is that of the element size rounded up to
+ // the nearest multiple of vec4
+ // 4. Matrices are treated like arrays of vectors
+ // 5. Arrays/matrices are laid out with a stride equal to the alignment
+ size_t size = el_size * inp->dim_v;
+ if (inp->dim_v == 3)
+ size += el_size;
+ if (inp->dim_m > 1)
+ size = MP_ALIGN_UP(size, sizeof(float[4]));
+
+ return (struct ra_layout) {
+ .align = size,
+ .stride = size,
+ .size = size * inp->dim_m,
+ };
+}
+
+struct ra_layout std430_layout(struct ra_renderpass_input *inp)
+{
+ size_t el_size = ra_vartype_size(inp->type);
+
+ // std430 packing rules: like std140, except arrays/matrices are always
+ // "tightly" packed, even arrays/matrices of vec3s
+ size_t size = el_size * inp->dim_v;
+ if (inp->dim_v == 3 && inp->dim_m == 1)
+ size += el_size;
+
+ return (struct ra_layout) {
+ .align = size,
+ .stride = size,
+ .size = size * inp->dim_m,
+ };
+}
+
+// Create a texture and a FBO using the texture as color attachments.
+// fmt: texture internal format
+// If the parameters are the same as the previous call, do not touch it.
+// flags can be 0, or a combination of FBOTEX_FUZZY_W and FBOTEX_FUZZY_H.
+// Enabling FUZZY for W or H means the w or h does not need to be exact.
+bool fbotex_change(struct fbotex *fbo, struct ra *ra, struct mp_log *log,
+ int w, int h, const struct ra_format *fmt, int flags)
+{
+ int lw = w, lh = h;
+
+ if (fbo->tex) {
+ int cw = w, ch = h;
+ int rw = fbo->tex->params.w, rh = fbo->tex->params.h;
+
+ if ((flags & FBOTEX_FUZZY_W) && cw < rw)
+ cw = rw;
+ if ((flags & FBOTEX_FUZZY_H) && ch < rh)
+ ch = rh;
+
+ if (rw == cw && rh == ch && fbo->tex->params.format == fmt)
+ goto done;
+ }
+
+ if (flags & FBOTEX_FUZZY_W)
+ w = MP_ALIGN_UP(w, 256);
+ if (flags & FBOTEX_FUZZY_H)
+ h = MP_ALIGN_UP(h, 256);
+
+ mp_verbose(log, "Create FBO: %dx%d (%dx%d)\n", lw, lh, w, h);
+
+ if (!fmt || !fmt->renderable || !fmt->linear_filter) {
+ mp_err(log, "Format %s not supported.\n", fmt ? fmt->name : "(unset)");
+ return false;
+ }
+
+ fbotex_uninit(fbo);
+
+ *fbo = (struct fbotex) {
+ .ra = ra,
+ };
+
+ struct ra_tex_params params = {
+ .dimensions = 2,
+ .w = w,
+ .h = h,
+ .d = 1,
+ .format = fmt,
+ .src_linear = true,
+ .render_src = true,
+ .render_dst = true,
+ .storage_dst = true,
+ .blit_src = true,
+ };
+
+ fbo->tex = ra_tex_create(fbo->ra, &params);
+
+ if (!fbo->tex) {
+ mp_err(log, "Error: framebuffer could not be created.\n");
+ fbotex_uninit(fbo);
+ return false;
+ }
+
+done:
+
+ fbo->lw = lw;
+ fbo->lh = lh;
+
+ fbo->fbo = (struct fbodst){
+ .tex = fbo->tex,
+ };
+
+ return true;
+}
+
+void fbotex_uninit(struct fbotex *fbo)
+{
+ if (fbo->ra) {
+ ra_tex_free(fbo->ra, &fbo->tex);
+ *fbo = (struct fbotex) {0};
+ }
+}
+
+struct timer_pool {
+ struct ra *ra;
+ ra_timer *timer;
+ bool running; // detect invalid usage
+
+ uint64_t samples[VO_PERF_SAMPLE_COUNT];
+ int sample_idx;
+ int sample_count;
+
+ uint64_t sum;
+ uint64_t peak;
+};
+
+struct timer_pool *timer_pool_create(struct ra *ra)
+{
+ if (!ra->fns->timer_create)
+ return NULL;
+
+ ra_timer *timer = ra->fns->timer_create(ra);
+ if (!timer)
+ return NULL;
+
+ struct timer_pool *pool = talloc(NULL, struct timer_pool);
+ if (!pool) {
+ ra->fns->timer_destroy(ra, timer);
+ return NULL;
+ }
+
+ *pool = (struct timer_pool){ .ra = ra, .timer = timer };
+ return pool;
+}
+
+void timer_pool_destroy(struct timer_pool *pool)
+{
+ if (!pool)
+ return;
+
+ pool->ra->fns->timer_destroy(pool->ra, pool->timer);
+ talloc_free(pool);
+}
+
+void timer_pool_start(struct timer_pool *pool)
+{
+ if (!pool)
+ return;
+
+ assert(!pool->running);
+ pool->ra->fns->timer_start(pool->ra, pool->timer);
+ pool->running = true;
+}
+
+void timer_pool_stop(struct timer_pool *pool)
+{
+ if (!pool)
+ return;
+
+ assert(pool->running);
+ uint64_t res = pool->ra->fns->timer_stop(pool->ra, pool->timer);
+ pool->running = false;
+
+ if (res) {
+ // Input res into the buffer and grab the previous value
+ uint64_t old = pool->samples[pool->sample_idx];
+ pool->sample_count = MPMIN(pool->sample_count + 1, VO_PERF_SAMPLE_COUNT);
+ pool->samples[pool->sample_idx++] = res;
+ pool->sample_idx %= VO_PERF_SAMPLE_COUNT;
+ pool->sum = pool->sum + res - old;
+
+ // Update peak if necessary
+ if (res >= pool->peak) {
+ pool->peak = res;
+ } else if (pool->peak == old) {
+ // It's possible that the last peak was the value we just removed,
+ // if so we need to scan for the new peak
+ uint64_t peak = res;
+ for (int i = 0; i < VO_PERF_SAMPLE_COUNT; i++)
+ peak = MPMAX(peak, pool->samples[i]);
+ pool->peak = peak;
+ }
+ }
+}
+
+struct mp_pass_perf timer_pool_measure(struct timer_pool *pool)
+{
+ if (!pool)
+ return (struct mp_pass_perf){0};
+
+ struct mp_pass_perf res = {
+ .peak = pool->peak,
+ .count = pool->sample_count,
+ };
+
+ int idx = pool->sample_idx - pool->sample_count + VO_PERF_SAMPLE_COUNT;
+ for (int i = 0; i < res.count; i++) {
+ idx %= VO_PERF_SAMPLE_COUNT;
+ res.samples[i] = pool->samples[idx++];
+ }
+
+ if (res.count > 0) {
+ res.last = res.samples[res.count - 1];
+ res.avg = pool->sum / res.count;
+ }
+
+ return res;
+}
+
+void mp_log_source(struct mp_log *log, int lev, const char *src)
+{
+ int line = 1;
+ if (!src)
+ return;
+ while (*src) {
+ const char *end = strchr(src, '\n');
+ const char *next = end + 1;
+ if (!end)
+ next = end = src + strlen(src);
+ mp_msg(log, lev, "[%3d] %.*s\n", line, (int)(end - src), src);
+ line++;
+ src = next;
+ }
+}
diff --git a/video/out/gpu/utils.h b/video/out/gpu/utils.h
new file mode 100644
index 0000000000..04695f8085
--- /dev/null
+++ b/video/out/gpu/utils.h
@@ -0,0 +1,120 @@
+#pragma once
+
+#include <stdbool.h>
+#include <math.h>
+
+#include "ra.h"
+
+// A 3x2 matrix, with the translation part separate.
+struct gl_transform {
+ // row-major, e.g. in mathematical notation:
+ // | m[0][0] m[0][1] |
+ // | m[1][0] m[1][1] |
+ float m[2][2];
+ float t[2];
+};
+
+static const struct gl_transform identity_trans = {
+ .m = {{1.0, 0.0}, {0.0, 1.0}},
+ .t = {0.0, 0.0},
+};
+
+void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
+ float y0, float y1);
+
+// This treats m as an affine transformation, in other words m[2][n] gets
+// added to the output.
+static inline void gl_transform_vec(struct gl_transform t, float *x, float *y)
+{
+ float vx = *x, vy = *y;
+ *x = vx * t.m[0][0] + vy * t.m[0][1] + t.t[0];
+ *y = vx * t.m[1][0] + vy * t.m[1][1] + t.t[1];
+}
+
+struct mp_rect_f {
+ float x0, y0, x1, y1;
+};
+
+// Semantic equality (fuzzy comparison)
+static inline bool mp_rect_f_seq(struct mp_rect_f a, struct mp_rect_f b)
+{
+ return fabs(a.x0 - b.x0) < 1e-6 && fabs(a.x1 - b.x1) < 1e-6 &&
+ fabs(a.y0 - b.y0) < 1e-6 && fabs(a.y1 - b.y1) < 1e-6;
+}
+
+static inline void gl_transform_rect(struct gl_transform t, struct mp_rect_f *r)
+{
+ gl_transform_vec(t, &r->x0, &r->y0);
+ gl_transform_vec(t, &r->x1, &r->y1);
+}
+
+static inline bool gl_transform_eq(struct gl_transform a, struct gl_transform b)
+{
+ for (int x = 0; x < 2; x++) {
+ for (int y = 0; y < 2; y++) {
+ if (a.m[x][y] != b.m[x][y])
+ return false;
+ }
+ }
+
+ return a.t[0] == b.t[0] && a.t[1] == b.t[1];
+}
+
+void gl_transform_trans(struct gl_transform t, struct gl_transform *x);
+
+struct fbodst {
+ struct ra_tex *tex;
+ bool flip; // mirror vertically
+};
+
+void gl_transform_ortho_fbodst(struct gl_transform *t, struct fbodst fbo);
+
+// A pool of buffers, which can grow as needed
+struct ra_buf_pool {
+ struct ra_buf_params current_params;
+ struct ra_buf **buffers;
+ int num_buffers;
+ int index;
+};
+
+void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool);
+
+// Note: params->initial_data is *not* supported
+struct ra_buf *ra_buf_pool_get(struct ra *ra, struct ra_buf_pool *pool,
+ const struct ra_buf_params *params);
+
+// Helper that wraps ra_tex_upload using texture upload buffers to ensure that
+// params->buf is always set. This is intended for RA-internal usage.
+bool ra_tex_upload_pbo(struct ra *ra, struct ra_buf_pool *pbo,
+ const struct ra_tex_upload_params *params);
+
+// Layout rules for GLSL's packing modes
+struct ra_layout std140_layout(struct ra_renderpass_input *inp);
+struct ra_layout std430_layout(struct ra_renderpass_input *inp);
+
+struct fbotex {
+ struct ra *ra;
+ struct ra_tex *tex;
+ int lw, lh; // logical (configured) size, <= than texture size
+ struct fbodst fbo;
+};
+
+void fbotex_uninit(struct fbotex *fbo);
+bool fbotex_change(struct fbotex *fbo, struct ra *ra, struct mp_log *log,
+ int w, int h, const struct ra_format *fmt, int flags);
+#define FBOTEX_FUZZY_W 1
+#define FBOTEX_FUZZY_H 2
+#define FBOTEX_FUZZY (FBOTEX_FUZZY_W | FBOTEX_FUZZY_H)
+
+// A wrapper around ra_timer that does result pooling, averaging etc.
+struct timer_pool;
+
+struct timer_pool *timer_pool_create(struct ra *ra);
+void timer_pool_destroy(struct timer_pool *pool);
+void timer_pool_start(struct timer_pool *pool);
+void timer_pool_stop(struct timer_pool *pool);
+struct mp_pass_perf timer_pool_measure(struct timer_pool *pool);
+
+// print a multi line string with line numbers (e.g. for shader sources)
+// log, lev: module and log level, as in mp_msg()
+void mp_log_source(struct mp_log *log, int lev, const char *src);
diff --git a/video/out/opengl/video.c b/video/out/gpu/video.c
index 3362381eff..e36fde60e8 100644
--- a/video/out/opengl/video.c
+++ b/video/out/gpu/video.c
@@ -347,9 +347,9 @@ static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
const struct m_sub_options gl_video_conf = {
.opts = (const m_option_t[]) {
- OPT_CHOICE("opengl-dumb-mode", dumb_mode, 0,
+ OPT_CHOICE("gpu-dumb-mode", dumb_mode, 0,
({"auto", 0}, {"yes", 1}, {"no", -1})),
- OPT_FLOATRANGE("opengl-gamma", gamma, 0, 0.1, 2.0),
+ OPT_FLOATRANGE("gamma-factor", gamma, 0, 0.1, 2.0),
OPT_FLAG("gamma-auto", gamma_auto, 0),
OPT_CHOICE_C("target-prim", target_prim, 0, mp_csp_prim_names),
OPT_CHOICE_C("target-trc", target_trc, 0, mp_csp_trc_names),
@@ -376,7 +376,7 @@ const struct m_sub_options gl_video_conf = {
OPT_FLAG("sigmoid-upscaling", sigmoid_upscaling, 0),
OPT_FLOATRANGE("sigmoid-center", sigmoid_center, 0, 0.0, 1.0),
OPT_FLOATRANGE("sigmoid-slope", sigmoid_slope, 0, 1.0, 20.0),
- OPT_STRING("opengl-fbo-format", fbo_format, 0),
+ OPT_STRING("fbo-format", fbo_format, 0),
OPT_CHOICE_OR_INT("dither-depth", dither_depth, 0, -1, 16,
({"no", -1}, {"auto", 0})),
OPT_CHOICE("dither", dither_algo, 0,
@@ -399,18 +399,24 @@ const struct m_sub_options gl_video_conf = {
({"no", BLEND_SUBS_NO},
{"yes", BLEND_SUBS_YES},
{"video", BLEND_SUBS_VIDEO})),
- OPT_PATHLIST("opengl-shaders", user_shaders, 0),
- OPT_CLI_ALIAS("opengl-shader", "opengl-shaders-append"),
+ OPT_PATHLIST("glsl-shaders", user_shaders, 0),
+ OPT_CLI_ALIAS("glsl-shader", "glsl-shaders-append"),
OPT_FLAG("deband", deband, 0),
OPT_SUBSTRUCT("deband", deband_opts, deband_conf, 0),
OPT_FLOAT("sharpen", unsharp, 0),
- OPT_INTRANGE("opengl-tex-pad-x", tex_pad_x, 0, 0, 4096),
- OPT_INTRANGE("opengl-tex-pad-y", tex_pad_y, 0, 0, 4096),
+ OPT_INTRANGE("gpu-tex-pad-x", tex_pad_x, 0, 0, 4096),
+ OPT_INTRANGE("gpu-tex-pad-y", tex_pad_y, 0, 0, 4096),
OPT_SUBSTRUCT("", icc_opts, mp_icc_conf, 0),
- OPT_CHOICE("opengl-early-flush", early_flush, 0,
- ({"no", 0}, {"yes", 1}, {"auto", -1})),
- OPT_STRING("opengl-shader-cache-dir", shader_cache_dir, 0),
+ OPT_STRING("gpu-shader-cache-dir", shader_cache_dir, 0),
OPT_REPLACED("hdr-tone-mapping", "tone-mapping"),
+ OPT_REPLACED("opengl-shaders", "glsl-shaders"),
+ OPT_CLI_ALIAS("opengl-shader", "glsl-shaders-append"),
+ OPT_REPLACED("opengl-shader-cache-dir", "gpu-shader-cache-dir"),
+ OPT_REPLACED("opengl-tex-pad-x", "gpu-tex-pad-x"),
+ OPT_REPLACED("opengl-tex-pad-y", "gpu-tex-pad-y"),
+ OPT_REPLACED("opengl-fbo-format", "fbo-format"),
+ OPT_REPLACED("opengl-dumb-mode", "gpu-dumb-mode"),
+ OPT_REPLACED("opengl-gamma", "gpu-gamma"),
{0}
},
.size = sizeof(struct gl_video_opts),
@@ -3095,16 +3101,6 @@ done:
p->ra->fns->clear(p->ra, target.tex, color, &target_rc);
}
- // The playloop calls this last before waiting some time until it decides
- // to call flip_page(). Tell OpenGL to start execution of the GPU commands
- // while we sleep (this happens asynchronously).
- if ((p->opts.early_flush == -1 && !frame->display_synced) ||
- p->opts.early_flush == 1)
- {
- if (p->ra->fns->flush)
- p->ra->fns->flush(p->ra);
- }
-
p->frames_rendered++;
pass_report_performance(p);
}
diff --git a/video/out/opengl/video.h b/video/out/gpu/video.h
index d163bc8405..884f5914fd 100644
--- a/video/out/opengl/video.h
+++ b/video/out/gpu/video.h
@@ -27,7 +27,6 @@
#include "shader_cache.h"
#include "video/csputils.h"
#include "video/out/filter_kernels.h"
-#include "video/out/vo.h"
// Assume we have this many texture units for sourcing additional passes.
// The actual texture unit assignment is dynamic.
diff --git a/video/out/opengl/video_shaders.c b/video/out/gpu/video_shaders.c
index 60c5ce82ac..60c5ce82ac 100644
--- a/video/out/opengl/video_shaders.c
+++ b/video/out/gpu/video_shaders.c
diff --git a/video/out/opengl/video_shaders.h b/video/out/gpu/video_shaders.h
index 8345e4c598..8345e4c598 100644
--- a/video/out/opengl/video_shaders.h
+++ b/video/out/gpu/video_shaders.h
diff --git a/video/out/opengl/common.h b/video/out/opengl/common.h
index 7b2e3ed497..b9f582b79f 100644
--- a/video/out/opengl/common.h
+++ b/video/out/opengl/common.h
@@ -26,10 +26,10 @@
#include "common/msg.h"
#include "misc/bstr.h"
-#include "video/out/vo.h"
#include "video/csputils.h"
-
#include "video/mp_image.h"
+#include "video/out/vo.h"
+#include "video/out/gpu/ra.h"
#include "gl_headers.h"
diff --git a/video/out/opengl/context.c b/video/out/opengl/context.c
index fe454e9741..d3cdcac3b7 100644
--- a/video/out/opengl/context.c
+++ b/video/out/opengl/context.c
@@ -1,10 +1,4 @@
/*
- * common OpenGL routines
- *
- * copyleft (C) 2005-2010 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
- * Special thanks go to the xine team and Matthias Hopf, whose video_out_opengl.c
- * gave me lots of good ideas.
- *
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
@@ -21,73 +15,10 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-#include <math.h>
-#include <assert.h>
-
+#include "options/m_config.h"
#include "context.h"
-#include "common/common.h"
-#include "options/options.h"
-#include "options/m_option.h"
-
-extern const struct mpgl_driver mpgl_driver_x11;
-extern const struct mpgl_driver mpgl_driver_x11egl;
-extern const struct mpgl_driver mpgl_driver_x11_probe;
-extern const struct mpgl_driver mpgl_driver_drm_egl;
-extern const struct mpgl_driver mpgl_driver_drm;
-extern const struct mpgl_driver mpgl_driver_cocoa;
-extern const struct mpgl_driver mpgl_driver_wayland;
-extern const struct mpgl_driver mpgl_driver_w32;
-extern const struct mpgl_driver mpgl_driver_angle;
-extern const struct mpgl_driver mpgl_driver_angle_es2;
-extern const struct mpgl_driver mpgl_driver_dxinterop;
-extern const struct mpgl_driver mpgl_driver_rpi;
-extern const struct mpgl_driver mpgl_driver_mali;
-extern const struct mpgl_driver mpgl_driver_vdpauglx;
-
-static const struct mpgl_driver *const backends[] = {
-#if HAVE_RPI
- &mpgl_driver_rpi,
-#endif
-#if HAVE_GL_COCOA
- &mpgl_driver_cocoa,
-#endif
-#if HAVE_EGL_ANGLE_WIN32
- &mpgl_driver_angle,
-#endif
-#if HAVE_GL_WIN32
- &mpgl_driver_w32,
-#endif
-#if HAVE_GL_DXINTEROP
- &mpgl_driver_dxinterop,
-#endif
-#if HAVE_GL_X11
- &mpgl_driver_x11_probe,
-#endif
-#if HAVE_EGL_X11
- &mpgl_driver_x11egl,
-#endif
-#if HAVE_GL_X11
- &mpgl_driver_x11,
-#endif
-#if HAVE_GL_WAYLAND
- &mpgl_driver_wayland,
-#endif
-#if HAVE_EGL_DRM
- &mpgl_driver_drm,
- &mpgl_driver_drm_egl,
-#endif
-#if HAVE_MALI_FBDEV
- &mpgl_driver_mali,
-#endif
-#if HAVE_VDPAU_GL_X11
- &mpgl_driver_vdpauglx,
-#endif
-};
+#include "ra_gl.h"
+#include "utils.h"
// 0-terminated list of desktop GL versions a backend should try to
// initialize. The first entry is the most preferred version.
@@ -103,140 +34,319 @@ const int mpgl_preferred_gl_versions[] = {
0
};
-int mpgl_find_backend(const char *name)
+enum {
+ FLUSH_NO = 0,
+ FLUSH_YES,
+ FLUSH_AUTO,
+};
+
+enum {
+ GLES_AUTO = 0,
+ GLES_YES,
+ GLES_NO,
+};
+
+struct opengl_opts {
+ int use_glfinish;
+ int waitvsync;
+ int vsync_pattern[2];
+ int swapinterval;
+ int early_flush;
+ int restrict_version;
+ int gles_mode;
+};
+
+#define OPT_BASE_STRUCT struct opengl_opts
+const struct m_sub_options opengl_conf = {
+ .opts = (const struct m_option[]) {
+ OPT_FLAG("opengl-glfinish", use_glfinish, 0),
+ OPT_FLAG("opengl-waitvsync", waitvsync, 0),
+ OPT_INT("opengl-swapinterval", swapinterval, 0),
+ OPT_INTPAIR("opengl-check-pattern", vsync_pattern, 0),
+ OPT_INT("opengl-restrict", restrict_version, 0),
+ OPT_CHOICE("opengl-es", gles_mode, 0,
+ ({"auto", GLES_AUTO}, {"yes", GLES_YES}, {"no", GLES_NO})),
+ OPT_CHOICE("opengl-early-flush", early_flush, 0,
+ ({"no", FLUSH_NO}, {"yes", FLUSH_YES}, {"auto", FLUSH_AUTO})),
+
+ OPT_REPLACED("opengl-debug", "gpu-debug"),
+ OPT_REPLACED("opengl-sw", "gpu-sw"),
+ OPT_REPLACED("opengl-vsync-fences", "swapchain-depth"),
+ OPT_REPLACED("opengl-backend", "gpu-context"),
+ {0},
+ },
+ .defaults = &(const struct opengl_opts) {
+ .swapinterval = 1,
+ },
+ .size = sizeof(struct opengl_opts),
+};
+
+struct priv {
+ GL *gl;
+ struct mp_log *log;
+ struct ra_gl_ctx_params params;
+ struct opengl_opts *opts;
+ struct ra_swapchain_fns fns;
+ GLuint main_fb;
+ struct ra_tex *wrapped_fb; // corresponds to main_fb
+ // for debugging:
+ int frames_rendered;
+ unsigned int prev_sgi_sync_count;
+ // for gl_vsync_pattern
+ int last_pattern;
+ int matches, mismatches;
+ // for swapchain_depth simulation
+ GLsync *vsync_fences;
+ int num_vsync_fences;
+};
+
+bool ra_gl_ctx_test_version(struct ra_ctx *ctx, int version, bool es)
{
- if (name == NULL || strcmp(name, "auto") == 0)
- return -1;
- for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
- if (strcmp(backends[n]->name, name) == 0)
- return n;
+ bool ret;
+ struct opengl_opts *opts;
+ void *tmp = talloc_new(NULL);
+ opts = mp_get_config_group(tmp, ctx->global, &opengl_conf);
+
+ // Version too high
+ if (opts->restrict_version && version >= opts->restrict_version) {
+ ret = false;
+ goto done;
}
- return -2;
-}
-int mpgl_validate_backend_opt(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param)
-{
- if (bstr_equals0(param, "help")) {
- mp_info(log, "OpenGL windowing backends:\n");
- mp_info(log, " auto (autodetect)\n");
- for (int n = 0; n < MP_ARRAY_SIZE(backends); n++)
- mp_info(log, " %s\n", backends[n]->name);
- return M_OPT_EXIT;
+ switch (opts->gles_mode) {
+ case GLES_YES: ret = es; goto done;
+ case GLES_NO: ret = !es; goto done;
+ case GLES_AUTO: ret = true; goto done;
+ default: abort();
}
- char s[20];
- snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
- return mpgl_find_backend(s) >= -1 ? 1 : M_OPT_INVALID;
+
+done:
+ talloc_free(tmp);
+ return ret;
}
-static void *get_native_display(void *pctx, const char *name)
+static void *get_native_display(void *priv, const char *name)
{
- MPGLContext *ctx = pctx;
- if (!ctx->native_display_type || !name)
+ struct priv *p = priv;
+ if (!p->params.native_display_type || !name)
+ return NULL;
+ if (strcmp(p->params.native_display_type, name) != 0)
return NULL;
- return strcmp(ctx->native_display_type, name) == 0 ? ctx->native_display : NULL;
+
+ return p->params.native_display;
}
-static MPGLContext *init_backend(struct vo *vo, const struct mpgl_driver *driver,
- bool probing, int vo_flags)
+void ra_gl_ctx_uninit(struct ra_ctx *ctx)
{
- MPGLContext *ctx = talloc_ptrtype(NULL, ctx);
- *ctx = (MPGLContext) {
- .gl = talloc_zero(ctx, GL),
- .vo = vo,
- .global = vo->global,
- .driver = driver,
- .log = vo->log,
+ if (ctx->ra)
+ ctx->ra->fns->destroy(ctx->ra);
+ if (ctx->swapchain) {
+ talloc_free(ctx->swapchain);
+ ctx->swapchain = NULL;
+ }
+}
+
+static const struct ra_swapchain_fns ra_gl_swapchain_fns;
+
+bool ra_gl_ctx_init(struct ra_ctx *ctx, GL *gl, struct ra_gl_ctx_params params)
+{
+ struct ra_swapchain *sw = ctx->swapchain = talloc_ptrtype(NULL, sw);
+ *sw = (struct ra_swapchain) {
+ .ctx = ctx,
+ .flip_v = !params.flipped, // OpenGL framebuffers are normally inverted
};
- if (probing)
- vo_flags |= VOFLAG_PROBING;
- bool old_probing = vo->probing;
- vo->probing = probing; // hack; kill it once backends are separate
- MP_VERBOSE(vo, "Initializing OpenGL backend '%s'\n", ctx->driver->name);
- ctx->priv = talloc_zero_size(ctx, ctx->driver->priv_size);
- if (ctx->driver->init(ctx, vo_flags) < 0) {
- vo->probing = old_probing;
- talloc_free(ctx);
- return NULL;
+
+ struct priv *p = sw->priv = talloc_ptrtype(sw, p);
+ *p = (struct priv) {
+ .gl = gl,
+ .log = ctx->log,
+ .params = params,
+ .opts = mp_get_config_group(p, ctx->global, &opengl_conf),
+ .fns = ra_gl_swapchain_fns,
+ };
+
+ sw->fns = &p->fns;
+
+ const struct ra_swapchain_fns *ext = p->params.external_swapchain;
+ if (ext) {
+ if (ext->color_depth)
+ p->fns.color_depth = ext->color_depth;
+ if (ext->screenshot)
+ p->fns.screenshot = ext->screenshot;
+ if (ext->start_frame)
+ p->fns.start_frame = ext->start_frame;
+ if (ext->submit_frame)
+ p->fns.submit_frame = ext->submit_frame;
+ if (ext->swap_buffers)
+ p->fns.swap_buffers = ext->swap_buffers;
}
- vo->probing = old_probing;
- if (!ctx->gl->version && !ctx->gl->es)
- goto cleanup;
+ if (!gl->version && !gl->es)
+ return false;
- if (probing && ctx->gl->es && (vo_flags & VOFLAG_NO_GLES)) {
- MP_VERBOSE(ctx->vo, "Skipping GLES backend.\n");
- goto cleanup;
+ if (gl->mpgl_caps & MPGL_CAP_SW) {
+ MP_WARN(p, "Suspected software renderer or indirect context.\n");
+ if (ctx->opts.probing && !ctx->opts.allow_sw)
+ return false;
}
- if (ctx->gl->mpgl_caps & MPGL_CAP_SW) {
- MP_WARN(ctx->vo, "Suspected software renderer or indirect context.\n");
- if (vo->probing && !(vo_flags & VOFLAG_SW))
- goto cleanup;
+ gl->debug_context = ctx->opts.debug;
+ gl->get_native_display_ctx = p;
+ gl->get_native_display = get_native_display;
+
+ if (gl->SwapInterval) {
+ gl->SwapInterval(p->opts->swapinterval);
+ } else {
+ MP_VERBOSE(p, "GL_*_swap_control extension missing.\n");
}
- ctx->gl->debug_context = !!(vo_flags & VOFLAG_GL_DEBUG);
+ ctx->ra = ra_create_gl(p->gl, ctx->log);
+ return !!ctx->ra;
+}
- ctx->gl->get_native_display_ctx = ctx;
- ctx->gl->get_native_display = get_native_display;
+void ra_gl_ctx_resize(struct ra_swapchain *sw, int w, int h, int fbo)
+{
+ struct priv *p = sw->priv;
+ if (p->main_fb == fbo && p->wrapped_fb && p->wrapped_fb->params.w == w
+ && p->wrapped_fb->params.h == h)
+ return;
- return ctx;
+ if (p->wrapped_fb)
+ ra_tex_free(sw->ctx->ra, &p->wrapped_fb);
-cleanup:
- mpgl_uninit(ctx);
- return NULL;
+ p->main_fb = fbo;
+ p->wrapped_fb = ra_create_wrapped_fb(sw->ctx->ra, fbo, w, h);
}
-// Create a VO window and create a GL context on it.
-// vo_flags: passed to the backend's create window function
-MPGLContext *mpgl_init(struct vo *vo, const char *backend_name, int vo_flags)
+int ra_gl_ctx_color_depth(struct ra_swapchain *sw)
{
- MPGLContext *ctx = NULL;
- int index = mpgl_find_backend(backend_name);
- if (index == -1) {
- for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
- ctx = init_backend(vo, backends[n], true, vo_flags);
- if (ctx)
- break;
- }
- // VO forced, but no backend is ok => force the first that works at all
- if (!ctx && !vo->probing) {
- for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
- ctx = init_backend(vo, backends[n], false, vo_flags);
- if (ctx)
- break;
- }
- }
- } else if (index >= 0) {
- ctx = init_backend(vo, backends[index], false, vo_flags);
- }
- return ctx;
+ struct priv *p = sw->priv;
+ GL *gl = p->gl;
+
+ if (!p->wrapped_fb)
+ return 0;
+
+ if ((gl->es < 300 && !gl->version) || !(gl->mpgl_caps & MPGL_CAP_FB))
+ return 0;
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, p->main_fb);
+
+ GLenum obj = gl->version ? GL_BACK_LEFT : GL_BACK;
+ if (p->main_fb)
+ obj = GL_COLOR_ATTACHMENT0;
+
+ GLint depth_g = 0;
+
+ gl->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, obj,
+ GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, &depth_g);
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ return depth_g;
}
-int mpgl_reconfig_window(struct MPGLContext *ctx)
+struct mp_image *ra_gl_ctx_screenshot(struct ra_swapchain *sw)
{
- return ctx->driver->reconfig(ctx);
+ struct priv *p = sw->priv;
+
+ assert(p->wrapped_fb);
+ struct mp_image *screen = gl_read_fbo_contents(p->gl, p->main_fb,
+ p->wrapped_fb->params.w,
+ p->wrapped_fb->params.h);
+
+ // OpenGL FB is also read in flipped order, so we need to flip when the
+ // rendering is *not* flipped, which in our case is whenever
+ // p->params.flipped is true. I hope that made sense
+ if (p->params.flipped)
+ mp_image_vflip(screen);
+
+ return screen;
}
-int mpgl_control(struct MPGLContext *ctx, int *events, int request, void *arg)
+struct ra_tex *ra_gl_ctx_start_frame(struct ra_swapchain *sw)
{
- return ctx->driver->control(ctx, events, request, arg);
+ struct priv *p = sw->priv;
+
+ return p->wrapped_fb;
}
-void mpgl_start_frame(struct MPGLContext *ctx)
+bool ra_gl_ctx_submit_frame(struct ra_swapchain *sw, const struct vo_frame *frame)
{
- if (ctx->driver->start_frame)
- ctx->driver->start_frame(ctx);
+ struct priv *p = sw->priv;
+ GL *gl = p->gl;
+
+ if (p->opts->use_glfinish)
+ gl->Finish();
+
+ if (gl->FenceSync && !p->params.external_swapchain) {
+ GLsync fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ if (fence)
+ MP_TARRAY_APPEND(p, p->vsync_fences, p->num_vsync_fences, fence);
+ }
+
+ switch (p->opts->early_flush) {
+ case FLUSH_AUTO:
+ if (frame->display_synced)
+ break;
+ // fall through
+ case FLUSH_YES:
+ gl->Flush();
+ }
+
+ return true;
}
-void mpgl_swap_buffers(struct MPGLContext *ctx)
+static void check_pattern(struct priv *p, int item)
{
- ctx->driver->swap_buffers(ctx);
+ int expected = p->opts->vsync_pattern[p->last_pattern];
+ if (item == expected) {
+ p->last_pattern++;
+ if (p->last_pattern >= 2)
+ p->last_pattern = 0;
+ p->matches++;
+ } else {
+ p->mismatches++;
+ MP_WARN(p, "wrong pattern, expected %d got %d (hit: %d, mis: %d)\n",
+ expected, item, p->matches, p->mismatches);
+ }
}
-void mpgl_uninit(MPGLContext *ctx)
+void ra_gl_ctx_swap_buffers(struct ra_swapchain *sw)
{
- if (ctx)
- ctx->driver->uninit(ctx);
- talloc_free(ctx);
+ struct priv *p = sw->priv;
+ GL *gl = p->gl;
+
+ p->params.swap_buffers(sw->ctx);
+ p->frames_rendered++;
+
+ if (p->frames_rendered > 5 && !sw->ctx->opts.debug)
+ ra_gl_set_debug(sw->ctx->ra, false);
+
+ if ((p->opts->waitvsync || p->opts->vsync_pattern[0])
+ && gl->GetVideoSync)
+ {
+ unsigned int n1 = 0, n2 = 0;
+ gl->GetVideoSync(&n1);
+ if (p->opts->waitvsync)
+ gl->WaitVideoSync(2, (n1 + 1) % 2, &n2);
+ int step = n1 - p->prev_sgi_sync_count;
+ p->prev_sgi_sync_count = n1;
+ MP_DBG(p, "Flip counts: %u->%u, step=%d\n", n1, n2, step);
+ if (p->opts->vsync_pattern[0])
+ check_pattern(p, step);
+ }
+
+ while (p->num_vsync_fences >= sw->ctx->opts.swapchain_depth) {
+ gl->ClientWaitSync(p->vsync_fences[0], GL_SYNC_FLUSH_COMMANDS_BIT, 1e9);
+ gl->DeleteSync(p->vsync_fences[0]);
+ MP_TARRAY_REMOVE_AT(p->vsync_fences, p->num_vsync_fences, 0);
+ }
}
+
+static const struct ra_swapchain_fns ra_gl_swapchain_fns = {
+ .color_depth = ra_gl_ctx_color_depth,
+ .screenshot = ra_gl_ctx_screenshot,
+ .start_frame = ra_gl_ctx_start_frame,
+ .submit_frame = ra_gl_ctx_submit_frame,
+ .swap_buffers = ra_gl_ctx_swap_buffers,
+};
diff --git a/video/out/opengl/context.h b/video/out/opengl/context.h
index 229c5ef54f..bdf426b9b4 100644
--- a/video/out/opengl/context.h
+++ b/video/out/opengl/context.h
@@ -1,116 +1,56 @@
-/*
- * common OpenGL routines
- *
- * copyleft (C) 2005-2010 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
- * Special thanks go to the xine team and Matthias Hopf, whose video_out_opengl.c
- * gave me lots of good ideas.
- *
- * 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/>.
- */
-
-#ifndef MP_GL_CONTEXT_H_
-#define MP_GL_CONTEXT_H_
+#pragma once
+#include "common/global.h"
+#include "video/out/gpu/context.h"
#include "common.h"
-enum {
- VOFLAG_GLES = 1 << 0, // Hint to create a GLES context
- VOFLAG_NO_GLES = 1 << 1, // Hint to create a desktop GL context
- VOFLAG_GL_DEBUG = 1 << 2, // Hint to request debug OpenGL context
- VOFLAG_ALPHA = 1 << 3, // Hint to request alpha framebuffer
- VOFLAG_SW = 1 << 4, // Hint to accept a software GL renderer
- VOFLAG_PROBING = 1 << 6, // The backend is being auto-probed.
- VOFLAG_GLES2 = 1 << 7, // Hint for GLESv2 (needs VOFLAG_GLES)
-};
-
extern const int mpgl_preferred_gl_versions[];
-struct MPGLContext;
-
-// A windowing backend (like X11, win32, ...), which provides OpenGL rendering.
-struct mpgl_driver {
- const char *name;
-
- // Size of the struct allocated for MPGLContext.priv
- int priv_size;
-
- // Init the GL context and possibly the underlying VO backend.
- // The created context should be compatible to GL 3.2 core profile, but
- // some other GL versions are supported as well (e.g. GL 2.1 or GLES 2).
- // Return 0 on success, negative value (-1) on error.
- int (*init)(struct MPGLContext *ctx, int vo_flags);
-
- // Resize the window, or create a new window if there isn't one yet.
- // Currently, there is an unfortunate interaction with ctx->vo, and
- // display size etc. are determined by it.
- // Return 0 on success, negative value (-1) on error.
- int (*reconfig)(struct MPGLContext *ctx);
-
- // Called when rendering starts. The backend can map or resize the
- // framebuffer, or update GL.main_fb. swap_buffers() ends the frame.
- // Optional.
- void (*start_frame)(struct MPGLContext *ctx);
-
- // Present the frame.
- void (*swap_buffers)(struct MPGLContext *ctx);
-
- // This behaves exactly like vo_driver.control().
- int (*control)(struct MPGLContext *ctx, int *events, int request, void *arg);
-
- // These behave exactly like vo_driver.wakeup/wait_events. They are
- // optional.
- void (*wakeup)(struct MPGLContext *ctx);
- void (*wait_events)(struct MPGLContext *ctx, int64_t until_time_us);
-
- // Destroy the GL context and possibly the underlying VO backend.
- void (*uninit)(struct MPGLContext *ctx);
-};
-
-typedef struct MPGLContext {
- GL *gl;
- struct vo *vo;
- const struct mpgl_driver *driver;
- struct mpv_global *global;
- struct mp_log *log;
-
- // For hwdec_vaegl.c.
+// Returns whether or not a candidate GL version should be accepted or not
+// (based on the --opengl opts). Implementations may call this before
+// ra_gl_ctx_init if they wish to probe for multiple possible GL versions.
+bool ra_gl_ctx_test_version(struct ra_ctx *ctx, int version, bool es);
+
+// These are a set of helpers for ra_ctx providers based on ra_gl.
+// The init function also initializes ctx->ra and ctx->swapchain, so the user
+// doesn't have to do this manually. (Similarly, the uninit function will
+// clean them up)
+
+struct ra_gl_ctx_params {
+ // Set to the platform-specific function to swap buffers, like
+ // glXSwapBuffers, eglSwapBuffers etc. This will be called by
+ // ra_gl_ctx_swap_buffers. Required unless you either never call that
+ // function or if you override it yourself.
+ void (*swap_buffers)(struct ra_ctx *ctx);
+
+ // Set to false if the implementation follows normal GL semantics, which is
+ // upside down. Set to true if it does *not*, i.e. if rendering is right
+ // side up
+ bool flipped;
+
+ // If this is set to non-NULL, then the ra_gl_ctx will consider the GL
+ // implementation to be using an external swapchain, which disables the
+ // software simulation of --swapchain-depth. Any functions defined by this
+ // ra_swapchain_fns structs will entirely replace the equivalent ra_gl_ctx
+ // functions in the resulting ra_swapchain.
+ const struct ra_swapchain_fns *external_swapchain;
+
+ // For hwdec_vaegl.c:
const char *native_display_type;
void *native_display;
+};
- // Flip the rendered image vertically. This is useful for dxinterop.
- bool flip_v;
-
- // framebuffer to render to (normally 0)
- GLuint main_fb;
-
- // For free use by the mpgl_driver.
- void *priv;
-} MPGLContext;
-
-MPGLContext *mpgl_init(struct vo *vo, const char *backend_name, int vo_flags);
-void mpgl_uninit(MPGLContext *ctx);
-int mpgl_reconfig_window(struct MPGLContext *ctx);
-int mpgl_control(struct MPGLContext *ctx, int *events, int request, void *arg);
-void mpgl_start_frame(struct MPGLContext *ctx);
-void mpgl_swap_buffers(struct MPGLContext *ctx);
-
-int mpgl_find_backend(const char *name);
+void ra_gl_ctx_uninit(struct ra_ctx *ctx);
+bool ra_gl_ctx_init(struct ra_ctx *ctx, GL *gl, struct ra_gl_ctx_params params);
-struct m_option;
-int mpgl_validate_backend_opt(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param);
+// Call this any time the window size or main framebuffer changes
+void ra_gl_ctx_resize(struct ra_swapchain *sw, int w, int h, int fbo);
-#endif
+// These functions are normally set in the ra_swapchain->fns, but if an
+// implementation has a need to override this fns struct with custom functions
+// for whatever reason, these can be used to inherit the original behavior.
+int ra_gl_ctx_color_depth(struct ra_swapchain *sw);
+struct mp_image *ra_gl_ctx_screenshot(struct ra_swapchain *sw);
+struct ra_tex *ra_gl_ctx_start_frame(struct ra_swapchain *sw);
+bool ra_gl_ctx_submit_frame(struct ra_swapchain *sw, const struct vo_frame *frame);
+void ra_gl_ctx_swap_buffers(struct ra_swapchain *sw);
diff --git a/video/out/opengl/context_cocoa.c b/video/out/opengl/context_cocoa.c
index 1d9a10cf38..cdf6faffcd 100644
--- a/video/out/opengl/context_cocoa.c
+++ b/video/out/opengl/context_cocoa.c
@@ -188,4 +188,4 @@ const struct mpgl_driver mpgl_driver_cocoa = {
.swap_buffers = cocoa_swap_buffers,
.control = cocoa_control,
.uninit = cocoa_uninit,
-}; \ No newline at end of file
+};
diff --git a/video/out/opengl/context_drm_egl.c b/video/out/opengl/context_drm_egl.c
index e52fec451b..21b16a52d5 100644
--- a/video/out/opengl/context_drm_egl.c
+++ b/video/out/opengl/context_drm_egl.c
@@ -28,10 +28,12 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include "context.h"
-#include "egl_helpers.h"
-#include "common/common.h"
#include "video/out/drm_common.h"
+#include "common/common.h"
+
+#include "egl_helpers.h"
+#include "common.h"
+#include "context.h"
#define USE_MASTER 0
@@ -59,6 +61,7 @@ struct egl
};
struct priv {
+ GL gl;
struct kms *kms;
drmEventContext ev;
@@ -75,34 +78,33 @@ struct priv {
struct vt_switcher vt_switcher;
};
-static bool init_egl(struct MPGLContext *ctx, int flags)
+static bool init_egl(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
- MP_VERBOSE(ctx->vo, "Initializing EGL\n");
+ MP_VERBOSE(ctx, "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");
+ MP_ERR(ctx, "Failed to get EGL display.\n");
return false;
}
if (!eglInitialize(p->egl.display, NULL, NULL)) {
- MP_ERR(ctx->vo, "Failed to initialize EGL.\n");
+ MP_ERR(ctx, "Failed to initialize EGL.\n");
return false;
}
EGLConfig config;
- if (!mpegl_create_context(p->egl.display, ctx->vo->log, flags,
- &p->egl.context, &config))
- return -1;
- MP_VERBOSE(ctx->vo, "Initializing EGL surface\n");
+ if (!mpegl_create_context(ctx, p->egl.display, &p->egl.context, &config))
+ return false;
+ MP_VERBOSE(ctx, "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");
+ MP_ERR(ctx, "Failed to create EGL surface.\n");
return false;
}
return true;
}
-static bool init_gbm(struct MPGLContext *ctx)
+static bool init_gbm(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
MP_VERBOSE(ctx->vo, "Creating GBM device\n");
@@ -136,7 +138,7 @@ static void framebuffer_destroy_callback(struct gbm_bo *bo, void *data)
}
static void update_framebuffer_from_bo(
- const struct MPGLContext *ctx, struct gbm_bo *bo)
+ const struct ra_ctx *ctx, struct gbm_bo *bo)
{
struct priv *p = ctx->priv;
p->fb.bo = bo;
@@ -161,7 +163,7 @@ static void page_flipped(int fd, unsigned int frame, unsigned int sec,
p->waiting_for_flip = false;
}
-static bool crtc_setup(struct MPGLContext *ctx)
+static bool crtc_setup(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
if (p->active)
@@ -174,7 +176,7 @@ static bool crtc_setup(struct MPGLContext *ctx)
return ret == 0;
}
-static void crtc_release(struct MPGLContext *ctx)
+static void crtc_release(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
@@ -204,7 +206,7 @@ static void crtc_release(struct MPGLContext *ctx)
static void release_vt(void *data)
{
- struct MPGLContext *ctx = data;
+ struct ra_ctx *ctx = data;
MP_VERBOSE(ctx->vo, "Releasing VT");
crtc_release(ctx);
if (USE_MASTER) {
@@ -221,7 +223,7 @@ static void release_vt(void *data)
static void acquire_vt(void *data)
{
- struct MPGLContext *ctx = data;
+ struct ra_ctx *ctx = data;
MP_VERBOSE(ctx->vo, "Acquiring VT");
if (USE_MASTER) {
struct priv *p = ctx->priv;
@@ -234,11 +236,41 @@ static void acquire_vt(void *data)
crtc_setup(ctx);
}
-static void drm_egl_uninit(MPGLContext *ctx)
+static void drm_egl_swap_buffers(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
- crtc_release(ctx);
+ eglSwapBuffers(p->egl.display, p->egl.surface);
+ p->gbm.next_bo = gbm_surface_lock_front_buffer(p->gbm.surface);
+ p->waiting_for_flip = true;
+ update_framebuffer_from_bo(ctx, p->gbm.next_bo);
+ int ret = drmModePageFlip(p->kms->fd, p->kms->crtc_id, p->fb.id,
+ DRM_MODE_PAGE_FLIP_EVENT, p);
+ if (ret) {
+ MP_WARN(ctx->vo, "Failed to queue page flip: %s\n", mp_strerror(errno));
+ }
+
+ // poll page flip finish event
+ const int timeout_ms = 3000;
+ struct pollfd fds[1] = { { .events = POLLIN, .fd = p->kms->fd } };
+ poll(fds, 1, timeout_ms);
+ if (fds[0].revents & POLLIN) {
+ ret = drmHandleEvent(p->kms->fd, &p->ev);
+ if (ret != 0) {
+ MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret);
+ return;
+ }
+ }
+
+ gbm_surface_release_buffer(p->gbm.surface, p->gbm.bo);
+ p->gbm.bo = p->gbm.next_bo;
+}
+static void drm_egl_uninit(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
+
+ crtc_release(ctx);
if (p->vt_switcher_active)
vt_switcher_destroy(&p->vt_switcher);
@@ -258,19 +290,14 @@ static void drm_egl_uninit(MPGLContext *ctx)
}
}
-static int drm_egl_init(struct MPGLContext *ctx, int flags)
+static bool drm_egl_init(struct ra_ctx *ctx)
{
- if (ctx->vo->probing) {
- MP_VERBOSE(ctx->vo, "DRM EGL backend can be activated only manually.\n");
- return -1;
+ if (ctx->opts.probing) {
+ MP_VERBOSE(ctx, "DRM EGL backend can be activated only manually.\n");
+ return false;
}
- struct priv *p = ctx->priv;
- p->kms = NULL;
- p->old_crtc = NULL;
- p->gbm.surface = NULL;
- p->gbm.device = NULL;
- p->active = false;
- p->waiting_for_flip = false;
+
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
p->ev.version = DRM_EVENT_CONTEXT_VERSION;
p->ev.page_flip_handler = page_flipped;
@@ -279,79 +306,76 @@ static int drm_egl_init(struct MPGLContext *ctx, int flags)
vt_switcher_acquire(&p->vt_switcher, acquire_vt, ctx);
vt_switcher_release(&p->vt_switcher, release_vt, ctx);
} else {
- MP_WARN(ctx->vo, "Failed to set up VT switcher. Terminal switching will be unavailable.\n");
+ MP_WARN(ctx, "Failed to set up VT switcher. Terminal switching will be unavailable.\n");
}
- MP_VERBOSE(ctx->vo, "Initializing KMS\n");
- p->kms = kms_create(ctx->vo->log, ctx->vo->opts->drm_connector_spec,
+ MP_VERBOSE(ctx, "Initializing KMS\n");
+ p->kms = kms_create(ctx->log, ctx->vo->opts->drm_connector_spec,
ctx->vo->opts->drm_mode_id);
if (!p->kms) {
MP_ERR(ctx->vo, "Failed to create KMS.\n");
- return -1;
+ return false;
}
if (!init_gbm(ctx)) {
MP_ERR(ctx->vo, "Failed to setup GBM.\n");
- return -1;
+ return false;
}
- if (!init_egl(ctx, flags)) {
+ if (!init_egl(ctx)) {
MP_ERR(ctx->vo, "Failed to setup EGL.\n");
- return -1;
+ return false;
}
if (!eglMakeCurrent(p->egl.display, p->egl.surface, p->egl.surface,
p->egl.context)) {
MP_ERR(ctx->vo, "Failed to make context current.\n");
- return -1;
+ return false;
}
- mpegl_load_functions(ctx->gl, ctx->vo->log);
-
- ctx->native_display_type = "drm";
- ctx->native_display = (void *)(intptr_t)p->kms->fd;
-
+ mpegl_load_functions(&p->gl, ctx->vo->log);
// required by gbm_surface_lock_front_buffer
eglSwapBuffers(p->egl.display, p->egl.surface);
- MP_VERBOSE(ctx->vo, "Preparing framebuffer\n");
+ MP_VERBOSE(ctx, "Preparing framebuffer\n");
p->gbm.bo = gbm_surface_lock_front_buffer(p->gbm.surface);
if (!p->gbm.bo) {
- MP_ERR(ctx->vo, "Failed to lock GBM surface.\n");
- return -1;
+ MP_ERR(ctx, "Failed to lock GBM surface.\n");
+ return false;
}
update_framebuffer_from_bo(ctx, p->gbm.bo);
if (!p->fb.id) {
- MP_ERR(ctx->vo, "Failed to create framebuffer.\n");
- return -1;
+ MP_ERR(ctx, "Failed to create framebuffer.\n");
+ return false;
}
if (!crtc_setup(ctx)) {
- MP_ERR(ctx->vo, "Failed to set CRTC for connector %u: %s\n",
+ MP_ERR(ctx, "Failed to set CRTC for connector %u: %s\n",
p->kms->connector->connector_id, mp_strerror(errno));
- return -1;
+ return false;
}
- return 0;
-}
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = drm_egl_swap_buffers,
+ .native_display_type = "drm",
+ .native_display = (void *)(intptr_t)p->kms->fd,
+ };
+ if (!ra_gl_ctx_init(ctx, &p->gl, params))
+ return false;
-static int drm_egl_init_deprecated(struct MPGLContext *ctx, int flags)
-{
- if (ctx->vo->probing)
- return -1;
- MP_WARN(ctx->vo, "'drm-egl' is deprecated, use 'drm' instead.\n");
- return drm_egl_init(ctx, flags);
+ return true;
}
-static int drm_egl_reconfig(struct MPGLContext *ctx)
+static bool drm_egl_reconfig(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
ctx->vo->dwidth = p->fb.width;
ctx->vo->dheight = p->fb.height;
- return 0;
+ ra_gl_ctx_resize(ctx->swapchain, p->fb.width, p->fb.height, 0);
+ return true;
}
-static int drm_egl_control(struct MPGLContext *ctx, int *events, int request,
+static int drm_egl_control(struct ra_ctx *ctx, int *events, int request,
void *arg)
{
struct priv *p = ctx->priv;
@@ -367,51 +391,11 @@ static int drm_egl_control(struct MPGLContext *ctx, int *events, int request,
return VO_NOTIMPL;
}
-static void drm_egl_swap_buffers(MPGLContext *ctx)
-{
- struct priv *p = ctx->priv;
- eglSwapBuffers(p->egl.display, p->egl.surface);
- p->gbm.next_bo = gbm_surface_lock_front_buffer(p->gbm.surface);
- p->waiting_for_flip = true;
- update_framebuffer_from_bo(ctx, p->gbm.next_bo);
- int ret = drmModePageFlip(p->kms->fd, p->kms->crtc_id, p->fb.id,
- DRM_MODE_PAGE_FLIP_EVENT, p);
- if (ret) {
- MP_WARN(ctx->vo, "Failed to queue page flip: %s\n", mp_strerror(errno));
- }
-
- // poll page flip finish event
- const int timeout_ms = 3000;
- struct pollfd fds[1] = { { .events = POLLIN, .fd = p->kms->fd } };
- poll(fds, 1, timeout_ms);
- if (fds[0].revents & POLLIN) {
- ret = drmHandleEvent(p->kms->fd, &p->ev);
- if (ret != 0) {
- MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret);
- return;
- }
- }
-
- gbm_surface_release_buffer(p->gbm.surface, p->gbm.bo);
- p->gbm.bo = p->gbm.next_bo;
-}
-
-const struct mpgl_driver mpgl_driver_drm = {
+const struct ra_ctx_fns ra_ctx_drm_egl = {
+ .type = "opengl",
.name = "drm",
- .priv_size = sizeof(struct priv),
- .init = drm_egl_init,
.reconfig = drm_egl_reconfig,
- .swap_buffers = drm_egl_swap_buffers,
- .control = drm_egl_control,
- .uninit = drm_egl_uninit,
-};
-
-const struct mpgl_driver mpgl_driver_drm_egl = {
- .name = "drm-egl",
- .priv_size = sizeof(struct priv),
- .init = drm_egl_init_deprecated,
- .reconfig = drm_egl_reconfig,
- .swap_buffers = drm_egl_swap_buffers,
.control = drm_egl_control,
+ .init = drm_egl_init,
.uninit = drm_egl_uninit,
};
diff --git a/video/out/opengl/context_x11.c b/video/out/opengl/context_glx.c
index 4d8dac1ea5..462f2cf592 100644
--- a/video/out/opengl/context_x11.c
+++ b/video/out/opengl/context_glx.c
@@ -39,43 +39,46 @@
#include "video/out/x11_common.h"
#include "context.h"
+#include "utils.h"
-struct glx_context {
+struct priv {
+ GL gl;
XVisualInfo *vinfo;
GLXContext context;
GLXFBConfig fbc;
};
-static void glx_uninit(MPGLContext *ctx)
+static void glx_uninit(struct ra_ctx *ctx)
{
- struct glx_context *glx_ctx = ctx->priv;
- if (glx_ctx->vinfo)
- XFree(glx_ctx->vinfo);
- if (glx_ctx->context) {
+ struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
+
+ if (p->vinfo)
+ XFree(p->vinfo);
+ if (p->context) {
Display *display = ctx->vo->x11->display;
glXMakeCurrent(display, None, NULL);
- glXDestroyContext(display, glx_ctx->context);
+ glXDestroyContext(display, p->context);
}
+
vo_x11_uninit(ctx->vo);
}
-static bool create_context_x11_old(struct MPGLContext *ctx)
+static bool create_context_x11_old(struct ra_ctx *ctx, GL *gl)
{
- struct glx_context *glx_ctx = ctx->priv;
+ struct priv *p = ctx->priv;
Display *display = ctx->vo->x11->display;
struct vo *vo = ctx->vo;
- GL *gl = ctx->gl;
- if (glx_ctx->context)
+ if (p->context)
return true;
- if (!glx_ctx->vinfo) {
+ if (!p->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);
+ GLXContext new_context = glXCreateContext(display, p->vinfo, NULL, True);
if (!new_context) {
MP_FATAL(vo, "Could not create GLX context!\n");
return false;
@@ -91,7 +94,7 @@ static bool create_context_x11_old(struct MPGLContext *ctx)
mpgl_load_functions(gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
- glx_ctx->context = new_context;
+ p->context = new_context;
return true;
}
@@ -99,15 +102,18 @@ static bool create_context_x11_old(struct MPGLContext *ctx)
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)
+static bool create_context_x11_gl3(struct ra_ctx *ctx, GL *gl, int gl_version,
+ bool es)
{
- struct glx_context *glx_ctx = ctx->priv;
+ struct priv *p = ctx->priv;
struct vo *vo = ctx->vo;
- if (glx_ctx->context)
+ if (p->context)
return true;
+ if (!ra_gl_ctx_test_version(ctx, gl_version, es))
+ return false;
+
glXCreateContextAttribsARBProc glXCreateContextAttribsARB =
(glXCreateContextAttribsARBProc)
glXGetProcAddressARB((const GLubyte *)"glXCreateContextAttribsARB");
@@ -120,7 +126,7 @@ static bool create_context_x11_gl3(struct MPGLContext *ctx, int vo_flags,
return false;
}
- int ctx_flags = vo_flags & VOFLAG_GL_DEBUG ? GLX_CONTEXT_DEBUG_BIT_ARB : 0;
+ int ctx_flags = ctx->opts.debug ? GLX_CONTEXT_DEBUG_BIT_ARB : 0;
int profile_mask = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
if (es) {
@@ -138,7 +144,7 @@ static bool create_context_x11_gl3(struct MPGLContext *ctx, int vo_flags,
};
vo_x11_silence_xlib(1);
GLXContext context = glXCreateContextAttribsARB(vo->x11->display,
- glx_ctx->fbc, 0, True,
+ p->fbc, 0, True,
context_attribs);
vo_x11_silence_xlib(-1);
if (!context)
@@ -151,9 +157,9 @@ static bool create_context_x11_gl3(struct MPGLContext *ctx, int vo_flags,
return false;
}
- glx_ctx->context = context;
+ p->context = context;
- mpgl_load_functions(ctx->gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
+ mpgl_load_functions(gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
return true;
}
@@ -162,7 +168,7 @@ static bool create_context_x11_gl3(struct MPGLContext *ctx, int vo_flags,
// 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)
+static GLXFBConfig select_fb_config(struct vo *vo, const int *attribs, bool alpha)
{
int fbcount;
GLXFBConfig *fbc = glXChooseFBConfig(vo->x11->display, vo->x11->screen,
@@ -173,7 +179,7 @@ static GLXFBConfig select_fb_config(struct vo *vo, const int *attribs, int flags
// 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) {
+ if (alpha) {
for (int n = 0; n < fbcount; n++) {
XVisualInfo *v = glXGetVisualFromFBConfig(vo->x11->display, fbc[n]);
if (v) {
@@ -202,10 +208,16 @@ static void set_glx_attrib(int *attribs, int name, int value)
}
}
-static int glx_init(struct MPGLContext *ctx, int flags)
+static void glx_swap_buffers(struct ra_ctx *ctx)
+{
+ glXSwapBuffers(ctx->vo->x11->display, ctx->vo->x11->window);
+}
+
+static bool glx_init(struct ra_ctx *ctx)
{
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
struct vo *vo = ctx->vo;
- struct glx_context *glx_ctx = ctx->priv;
+ GL *gl = &p->gl;
if (!vo_x11_init(ctx->vo))
goto uninit;
@@ -213,12 +225,12 @@ static int glx_init(struct MPGLContext *ctx, int flags)
int glx_major, glx_minor;
if (!glXQueryVersion(vo->x11->display, &glx_major, &glx_minor)) {
- MP_ERR(vo, "GLX not found.\n");
+ MP_ERR(ctx, "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)) {
- MP_ERR(vo, "GLX version older than 1.3.\n");
+ MP_ERR(ctx, "GLX version older than 1.3.\n");
goto uninit;
}
@@ -233,126 +245,132 @@ static int glx_init(struct MPGLContext *ctx, int flags)
None
};
GLXFBConfig fbc = NULL;
- if (flags & VOFLAG_ALPHA) {
+ if (ctx->opts.want_alpha) {
set_glx_attrib(glx_attribs, GLX_ALPHA_SIZE, 1);
- fbc = select_fb_config(vo, glx_attribs, flags);
- if (!fbc) {
+ fbc = select_fb_config(vo, glx_attribs, true);
+ if (!fbc)
set_glx_attrib(glx_attribs, GLX_ALPHA_SIZE, 0);
- flags &= ~VOFLAG_ALPHA;
- }
}
if (!fbc)
- fbc = select_fb_config(vo, glx_attribs, flags);
+ fbc = select_fb_config(vo, glx_attribs, false);
if (!fbc) {
- MP_ERR(vo, "no GLX support present\n");
+ MP_ERR(ctx, "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);
+ MP_VERBOSE(ctx, "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);
+ p->fbc = fbc;
+ p->vinfo = glXGetVisualFromFBConfig(vo->x11->display, fbc);
+ if (p->vinfo) {
+ MP_VERBOSE(ctx, "GLX chose visual with ID 0x%x\n",
+ (int)p->vinfo->visualid);
} else {
- MP_WARN(vo, "Selected GLX FB config has no associated X visual\n");
+ MP_WARN(ctx, "Selected GLX FB config has no associated X visual\n");
}
- if (!vo_x11_create_vo_window(vo, glx_ctx->vinfo, "gl"))
+ if (!vo_x11_create_vo_window(vo, p->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;
+ for (int n = 0; mpgl_preferred_gl_versions[n]; n++) {
+ int version = mpgl_preferred_gl_versions[n];
+ MP_VERBOSE(ctx, "Creating OpenGL %d.%d context...\n",
+ MPGL_VER_P(version));
+ if (version >= 300) {
+ success = create_context_x11_gl3(ctx, gl, version, false);
+ } else {
+ success = create_context_x11_old(ctx, gl);
}
+ 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) // try again for GLES
+ success = create_context_x11_gl3(ctx, gl, 200, true);
+ if (success && !glXIsDirect(vo->x11->display, p->context))
+ gl->mpgl_caps |= MPGL_CAP_SW;
if (!success)
goto uninit;
- return 0;
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = glx_swap_buffers,
+ };
+
+ if (!ra_gl_ctx_init(ctx, gl, params))
+ goto uninit;
+
+ return true;
uninit:
glx_uninit(ctx);
- return -1;
+ return false;
}
-static int glx_init_probe(struct MPGLContext *ctx, int flags)
+static bool glx_init_probe(struct ra_ctx *ctx)
{
- 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;
- }
+ if (!glx_init(ctx))
+ return false;
+
+ struct priv *p = ctx->priv;
+ if (!(p->gl.mpgl_caps & MPGL_CAP_VDPAU)) {
+ MP_VERBOSE(ctx, "No vdpau support found - probing more things.\n");
+ glx_uninit(ctx);
+ return false;
}
- return r;
+
+ return true;
}
-static int glx_reconfig(struct MPGLContext *ctx)
+static void resize(struct ra_ctx *ctx)
{
- vo_x11_config_vo_window(ctx->vo);
- return 0;
+ ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0);
}
-static int glx_control(struct MPGLContext *ctx, int *events, int request,
- void *arg)
+static bool glx_reconfig(struct ra_ctx *ctx)
{
- return vo_x11_control(ctx->vo, events, request, arg);
+ vo_x11_config_vo_window(ctx->vo);
+ resize(ctx);
+ return true;
}
-static void glx_swap_buffers(struct MPGLContext *ctx)
+static int glx_control(struct ra_ctx *ctx, int *events, int request, void *arg)
{
- glXSwapBuffers(ctx->vo->x11->display, ctx->vo->x11->window);
+ int ret = vo_x11_control(ctx->vo, events, request, arg);
+ if (*events & VO_EVENT_RESIZE)
+ resize(ctx);
+ return ret;
}
-static void glx_wakeup(struct MPGLContext *ctx)
+static void glx_wakeup(struct ra_ctx *ctx)
{
vo_x11_wakeup(ctx->vo);
}
-static void glx_wait_events(struct MPGLContext *ctx, int64_t until_time_us)
+static void glx_wait_events(struct ra_ctx *ctx, int64_t until_time_us)
{
vo_x11_wait_events(ctx->vo, until_time_us);
}
-const struct mpgl_driver mpgl_driver_x11 = {
+const struct ra_ctx_fns ra_ctx_glx = {
+ .type = "opengl",
.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,
+ .init = glx_init,
.uninit = glx_uninit,
};
-const struct mpgl_driver mpgl_driver_x11_probe = {
+const struct ra_ctx_fns ra_ctx_glx_probe = {
+ .type = "opengl",
.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,
+ .init = glx_init_probe,
.uninit = glx_uninit,
};
diff --git a/video/out/opengl/context_mali_fbdev.c b/video/out/opengl/context_mali_fbdev.c
index 66daa7f9ee..8576e536d3 100644
--- a/video/out/opengl/context_mali_fbdev.c
+++ b/video/out/opengl/context_mali_fbdev.c
@@ -50,8 +50,7 @@ static bool get_fbdev_size(int *w, int *h)
}
struct priv {
- struct mp_log *log;
- struct GL *gl;
+ struct GL gl;
EGLDisplay egl_display;
EGLConfig egl_config;
EGLContext egl_context;
@@ -60,9 +59,10 @@ struct priv {
int w, h;
};
-static void mali_uninit(struct MPGLContext *ctx)
+static void mali_uninit(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
if (p->egl_surface) {
eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
@@ -74,25 +74,29 @@ static void mali_uninit(struct MPGLContext *ctx)
eglReleaseThread();
}
-static int mali_init(struct MPGLContext *ctx, int flags)
+static void mali_swap_buffers(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
- p->log = ctx->vo->log;
+ eglSwapBuffers(p->egl_display, p->egl_surface);
+}
+
+static bool mali_init(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
if (!get_fbdev_size(&p->w, &p->h)) {
- MP_FATAL(p, "Could not get fbdev size.\n");
+ MP_FATAL(ctx, "Could not get fbdev size.\n");
goto fail;
}
p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!eglInitialize(p->egl_display, NULL, NULL)) {
- MP_FATAL(p, "EGL failed to initialize.\n");
+ MP_FATAL(ctx, "EGL failed to initialize.\n");
goto fail;
}
EGLConfig config;
- if (!mpegl_create_context(p->egl_display, p->log, flags, &p->egl_context,
- &config))
+ if (!mpegl_create_context(ctx, p->egl_display, &p->egl_context, &config))
goto fail;
p->egl_window = (struct fbdev_window){
@@ -104,53 +108,51 @@ static int mali_init(struct MPGLContext *ctx, int flags)
(EGLNativeWindowType)&p->egl_window, NULL);
if (p->egl_surface == EGL_NO_SURFACE) {
- MP_FATAL(p, "Could not create EGL surface!\n");
+ MP_FATAL(ctx, "Could not create EGL surface!\n");
goto fail;
}
if (!eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
p->egl_context))
{
- MP_FATAL(p, "Failed to set context!\n");
+ MP_FATAL(ctx, "Failed to set context!\n");
goto fail;
}
- ctx->gl = talloc_zero(ctx, GL);
+ mpegl_load_functions(&p->gl, ctx->log);
- mpegl_load_functions(ctx->gl, p->log);
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = mali_swap_buffers,
+ };
+
+ if (!ra_gl_ctx_init(ctx, &p->gl, params))
+ goto fail;
- return 0;
+ return true;
fail:
mali_uninit(ctx);
- return -1;
+ return false;
}
-static int mali_reconfig(struct MPGLContext *ctx)
+static bool mali_reconfig(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
ctx->vo->dwidth = p->w;
ctx->vo->dheight = p->h;
- return 0;
+ ra_gl_ctx_resize(ctx->swapchain, p->w, p->h, 0);
}
-static void mali_swap_buffers(MPGLContext *ctx)
-{
- struct priv *p = ctx->priv;
- eglSwapBuffers(p->egl_display, p->egl_surface);
-}
-
-static int mali_control(MPGLContext *ctx, int *events, int request, void *arg)
+static int mali_control(struct ra_ctx *ctx, int *events, int request, void *arg)
{
return VO_NOTIMPL;
}
-const struct mpgl_driver mpgl_driver_mali = {
+const struct ra_ctx_fns ra_ctx_mali_fbdev = {
+ .type = "opengl",
.name = "mali-fbdev",
- .priv_size = sizeof(struct priv),
- .init = mali_init,
.reconfig = mali_reconfig,
- .swap_buffers = mali_swap_buffers,
.control = mali_control,
+ .init = mali_init,
.uninit = mali_uninit,
};
diff --git a/video/out/opengl/context_rpi.c b/video/out/opengl/context_rpi.c
index e79622be5d..8b447d0bfc 100644
--- a/video/out/opengl/context_rpi.c
+++ b/video/out/opengl/context_rpi.c
@@ -30,7 +30,7 @@
#include "egl_helpers.h"
struct priv {
- struct mp_log *log;
+ struct GL gl;
DISPMANX_DISPLAY_HANDLE_T display;
DISPMANX_ELEMENT_HANDLE_T window;
DISPMANX_UPDATE_HANDLE_T update;
@@ -49,13 +49,13 @@ struct priv {
static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1,
uint32_t param2)
{
- struct MPGLContext *ctx = callback_data;
+ struct ra_ctx *ctx = callback_data;
struct priv *p = ctx->priv;
atomic_store(&p->reload_display, true);
vo_wakeup(ctx->vo);
}
-static void destroy_dispmanx(struct MPGLContext *ctx)
+static void destroy_dispmanx(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
@@ -77,9 +77,10 @@ static void destroy_dispmanx(struct MPGLContext *ctx)
p->update = 0;
}
-static void rpi_uninit(MPGLContext *ctx)
+static void rpi_uninit(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
vc_tv_unregister_callback_full(tv_callback, ctx);
@@ -92,26 +93,26 @@ static void rpi_uninit(MPGLContext *ctx)
p->egl_display = EGL_NO_DISPLAY;
}
-static int recreate_dispmanx(struct MPGLContext *ctx)
+static bool recreate_dispmanx(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
int display_nr = 0;
int layer = 0;
- MP_VERBOSE(ctx->vo, "Recreating DISPMANX state...\n");
+ MP_VERBOSE(ctx, "Recreating DISPMANX state...\n");
destroy_dispmanx(ctx);
p->display = vc_dispmanx_display_open(display_nr);
p->update = vc_dispmanx_update_start(0);
if (!p->display || !p->update) {
- MP_FATAL(ctx->vo, "Could not get DISPMANX objects.\n");
+ MP_FATAL(ctx, "Could not get DISPMANX objects.\n");
goto fail;
}
uint32_t dispw, disph;
if (graphics_get_display_size(0, &dispw, &disph) < 0) {
- MP_FATAL(ctx->vo, "Could not get display size.\n");
+ MP_FATAL(ctx, "Could not get display size.\n");
goto fail;
}
p->w = dispw;
@@ -145,7 +146,7 @@ static int recreate_dispmanx(struct MPGLContext *ctx)
&src, DISPMANX_PROTECTION_NONE, &alpha,
0, 0);
if (!p->window) {
- MP_FATAL(ctx->vo, "Could not add DISPMANX element.\n");
+ MP_FATAL(ctx, "Could not add DISPMANX element.\n");
goto fail;
}
@@ -161,14 +162,14 @@ static int recreate_dispmanx(struct MPGLContext *ctx)
&p->egl_window, NULL);
if (p->egl_surface == EGL_NO_SURFACE) {
- MP_FATAL(p, "Could not create EGL surface!\n");
+ MP_FATAL(ctx, "Could not create EGL surface!\n");
goto fail;
}
if (!eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
p->egl_context))
{
- MP_FATAL(p, "Failed to set context!\n");
+ MP_FATAL(ctx, "Failed to set context!\n");
goto fail;
}
@@ -197,21 +198,27 @@ static int recreate_dispmanx(struct MPGLContext *ctx)
ctx->vo->dwidth = p->w;
ctx->vo->dheight = p->h;
+ ra_gl_ctx_resize(ctx->swapchain, p->w, p->h, 0);
ctx->vo->want_redraw = true;
vo_event(ctx->vo, VO_EVENT_WIN_STATE);
- return 0;
+ return true;
fail:
destroy_dispmanx(ctx);
- return -1;
+ return false;
}
-static int rpi_init(struct MPGLContext *ctx, int flags)
+static void rpi_swap_buffers(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
- p->log = ctx->vo->log;
+ eglSwapBuffers(p->egl_display, p->egl_surface);
+}
+
+static bool rpi_init(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
bcm_host_init();
@@ -219,43 +226,40 @@ static int rpi_init(struct MPGLContext *ctx, int flags)
p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!eglInitialize(p->egl_display, NULL, NULL)) {
- MP_FATAL(p, "EGL failed to initialize.\n");
+ MP_FATAL(ctx, "EGL failed to initialize.\n");
goto fail;
}
- if (!mpegl_create_context(p->egl_display, p->log, 0, &p->egl_context,
- &p->egl_config))
+ if (!mpegl_create_context(ctx, p->egl_display, &p->egl_context, &p->egl_config))
goto fail;
if (recreate_dispmanx(ctx) < 0)
goto fail;
- ctx->gl = talloc_zero(ctx, GL);
+ mpegl_load_functions(&p->gl, ctx->log);
- mpegl_load_functions(ctx->gl, p->log);
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = rpi_swap_buffers,
+ .native_display_type = "MPV_RPI_WINDOW",
+ .native_display = p->win_params,
+ };
- ctx->native_display_type = "MPV_RPI_WINDOW";
- ctx->native_display = p->win_params;
+ if (!ra_gl_ctx_init(ctx, &p->gl, params))
+ goto fail;
- return 0;
+ return true;
fail:
rpi_uninit(ctx);
- return -1;
+ return false;
}
-static int rpi_reconfig(struct MPGLContext *ctx)
+static bool rpi_reconfig(struct ra_ctx *ctx)
{
return recreate_dispmanx(ctx);
}
-static void rpi_swap_buffers(MPGLContext *ctx)
-{
- struct priv *p = ctx->priv;
- eglSwapBuffers(p->egl_display, p->egl_surface);
-}
-
-static struct mp_image *take_screenshot(struct MPGLContext *ctx)
+static struct mp_image *take_screenshot(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
@@ -289,21 +293,20 @@ fail:
return NULL;
}
-
-static int rpi_control(MPGLContext *ctx, int *events, int request, void *arg)
+static int rpi_control(struct ra_ctx *ctx, int *events, int request, void *arg)
{
struct priv *p = ctx->priv;
switch (request) {
case VOCTRL_SCREENSHOT_WIN:
*(struct mp_image **)arg = take_screenshot(ctx);
- return true;
+ return VO_TRUE;
case VOCTRL_FULLSCREEN:
recreate_dispmanx(ctx);
return VO_TRUE;
case VOCTRL_CHECK_EVENTS:
if (atomic_fetch_and(&p->reload_display, 0)) {
- MP_WARN(ctx->vo, "Recovering from display mode switch...\n");
+ MP_WARN(ctx, "Recovering from display mode switch...\n");
recreate_dispmanx(ctx);
}
return VO_TRUE;
@@ -315,12 +318,11 @@ static int rpi_control(MPGLContext *ctx, int *events, int request, void *arg)
return VO_NOTIMPL;
}
-const struct mpgl_driver mpgl_driver_rpi = {
+const struct ra_ctx_fns ra_ctx_rpi = {
+ .type = "opengl",
.name = "rpi",
- .priv_size = sizeof(struct priv),
- .init = rpi_init,
.reconfig = rpi_reconfig,
- .swap_buffers = rpi_swap_buffers,
.control = rpi_control,
+ .init = rpi_init,
.uninit = rpi_uninit,
-}; \ No newline at end of file
+};
diff --git a/video/out/opengl/context_vdpau.c b/video/out/opengl/context_vdpau.c
index 40d21ab65c..a2321f78dd 100644
--- a/video/out/opengl/context_vdpau.c
+++ b/video/out/opengl/context_vdpau.c
@@ -26,8 +26,6 @@
// follow it. I'm not sure about the original nvidia headers.
#define BRAINDEATH(x) ((void *)(uintptr_t)(x))
-#define NUM_SURFACES 4
-
struct surface {
int w, h;
VdpOutputSurface surface;
@@ -39,21 +37,22 @@ struct surface {
};
struct priv {
+ GL gl;
GLXContext context;
struct mp_vdpau_ctx *vdp;
VdpPresentationQueueTarget vdp_target;
VdpPresentationQueue vdp_queue;
+ struct surface *surfaces;
int num_surfaces;
- struct surface surfaces[NUM_SURFACES];
- int current_surface;
+ int idx_surfaces;
};
typedef GLXContext (*glXCreateContextAttribsARBProc)
(Display*, GLXFBConfig, GLXContext, Bool, const int*);
-static bool create_context_x11(struct MPGLContext *ctx, int vo_flags)
+static bool create_context_x11(struct ra_ctx *ctx)
{
- struct priv *glx_ctx = ctx->priv;
+ struct priv *p = ctx->priv;
struct vo *vo = ctx->vo;
int glx_major, glx_minor;
@@ -62,6 +61,9 @@ static bool create_context_x11(struct MPGLContext *ctx, int vo_flags)
return false;
}
+ if (!ra_gl_ctx_test_version(ctx, MPGL_VER(glx_major, glx_minor), false))
+ return false;
+
int glx_attribs[] = {
GLX_X_RENDERABLE, True,
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
@@ -96,7 +98,7 @@ static bool create_context_x11(struct MPGLContext *ctx, int vo_flags)
return false;
}
- int ctx_flags = vo_flags & VOFLAG_GL_DEBUG ? GLX_CONTEXT_DEBUG_BIT_ARB : 0;
+ int ctx_flags = ctx->opts.debug ? GLX_CONTEXT_DEBUG_BIT_ARB : 0;
int context_attribs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
GLX_CONTEXT_MINOR_VERSION_ARB, 0,
@@ -117,19 +119,20 @@ static bool create_context_x11(struct MPGLContext *ctx, int vo_flags)
return false;
}
- glx_ctx->context = context;
- mpgl_load_functions(ctx->gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
+ p->context = context;
+ mpgl_load_functions(&p->gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
return true;
}
-static int create_vdpau_objects(struct MPGLContext *ctx)
+static int create_vdpau_objects(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
+ struct GL *gl = &p->gl;
VdpDevice dev = p->vdp->vdp_device;
struct vdp_functions *vdp = &p->vdp->vdp;
VdpStatus vdp_st;
- ctx->gl->VDPAUInitNV(BRAINDEATH(dev), p->vdp->get_proc_address);
+ gl->VDPAUInitNV(BRAINDEATH(dev), p->vdp->get_proc_address);
vdp_st = vdp->presentation_queue_target_create_x11(dev, ctx->vo->x11->window,
&p->vdp_target);
@@ -141,13 +144,13 @@ static int create_vdpau_objects(struct MPGLContext *ctx)
return 0;
}
-static void destroy_vdpau_surface(struct MPGLContext *ctx,
+static void destroy_vdpau_surface(struct ra_ctx *ctx,
struct surface *surface)
{
struct priv *p = ctx->priv;
struct vdp_functions *vdp = &p->vdp->vdp;
VdpStatus vdp_st;
- GL *gl = ctx->gl;
+ GL *gl = &p->gl;
if (surface->mapped)
gl->VDPAUUnmapSurfacesNV(1, &surface->registered);
@@ -168,14 +171,14 @@ static void destroy_vdpau_surface(struct MPGLContext *ctx,
};
}
-static int recreate_vdpau_surface(struct MPGLContext *ctx,
- struct surface *surface)
+static bool recreate_vdpau_surface(struct ra_ctx *ctx,
+ struct surface *surface)
{
struct priv *p = ctx->priv;
VdpDevice dev = p->vdp->vdp_device;
struct vdp_functions *vdp = &p->vdp->vdp;
VdpStatus vdp_st;
- GL *gl = ctx->gl;
+ GL *gl = &p->gl;
destroy_vdpau_surface(ctx, surface);
@@ -219,16 +222,37 @@ static int recreate_vdpau_surface(struct MPGLContext *ctx,
gl->VDPAUUnmapSurfacesNV(1, &surface->registered);
surface->mapped = false;
- return 0;
+ return true;
error:
destroy_vdpau_surface(ctx, surface);
- return -1;
+ return false;
+}
+
+static void vdpau_swap_buffers(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+ struct vdp_functions *vdp = &p->vdp->vdp;
+ VdpStatus vdp_st;
+
+ // This is the *next* surface we will be rendering to. By delaying the
+ // block_until_idle, we're essentially allowing p->num_surfaces - 1
+ // in-flight surfaces, plus the one currently visible surface.
+ struct surface *surf = &p->surfaces[p->idx_surfaces];
+ if (surf->surface == VDP_INVALID_HANDLE)
+ return;
+
+ VdpTime prev_vsync_time;
+ vdp_st = vdp->presentation_queue_block_until_surface_idle(p->vdp_queue,
+ surf->surface,
+ &prev_vsync_time);
+ CHECK_VDP_WARNING(ctx, "waiting for surface failed");
}
-static void glx_uninit(MPGLContext *ctx)
+static void vdpau_uninit(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
if (p->vdp) {
struct vdp_functions *vdp = &p->vdp->vdp;
@@ -259,10 +283,12 @@ static void glx_uninit(MPGLContext *ctx)
vo_x11_uninit(ctx->vo);
}
-static int glx_init(struct MPGLContext *ctx, int flags)
+static const struct ra_swapchain_fns vdpau_swapchain;
+
+static bool vdpau_init(struct ra_ctx *ctx)
{
struct vo *vo = ctx->vo;
- struct priv *p = ctx->priv;
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
p->vdp_queue = VDP_INVALID_HANDLE;
p->vdp_target = VDP_INVALID_HANDLE;
@@ -280,110 +306,112 @@ static int glx_init(struct MPGLContext *ctx, int flags)
if (!vo_x11_create_vo_window(vo, NULL, "vdpauglx"))
goto uninit;
- if (!create_context_x11(ctx, flags))
+ if (!create_context_x11(ctx))
goto uninit;
- if (!(ctx->gl->mpgl_caps & MPGL_CAP_VDPAU))
+ if (!(p->gl.mpgl_caps & MPGL_CAP_VDPAU))
goto uninit;
if (create_vdpau_objects(ctx) < 0)
goto uninit;
- p->num_surfaces = NUM_SURFACES;
+ p->num_surfaces = ctx->opts.swapchain_depth + 1; // +1 for the visible image
+ p->surfaces = talloc_zero_array(p, struct surface, p->num_surfaces);
for (int n = 0; n < p->num_surfaces; n++)
p->surfaces[n].surface = VDP_INVALID_HANDLE;
- ctx->flip_v = true;
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = vdpau_swap_buffers,
+ .external_swapchain = &vdpau_swapchain,
+ .flipped = true,
+ };
- return 0;
+ if (!ra_gl_ctx_init(ctx, &p->gl, params))
+ goto uninit;
+
+ return true;
uninit:
- glx_uninit(ctx);
- return -1;
+ vdpau_uninit(ctx);
+ return false;
}
-static int glx_reconfig(struct MPGLContext *ctx)
+static struct ra_tex *vdpau_start_frame(struct ra_swapchain *sw)
{
- vo_x11_config_vo_window(ctx->vo);
- return 0;
-}
+ struct priv *p = sw->ctx->priv;
+ struct vo *vo = sw->ctx->vo;
+ GL *gl = &p->gl;
+
+ struct surface *surf = &p->surfaces[p->idx_surfaces];
+ if (surf->w != vo->dwidth || surf->h != vo->dheight ||
+ surf->surface == VDP_INVALID_HANDLE)
+ {
+ if (!recreate_vdpau_surface(sw->ctx, surf))
+ return NULL;
+ }
-static int glx_control(struct MPGLContext *ctx, int *events, int request,
- void *arg)
-{
- return vo_x11_control(ctx->vo, events, request, arg);
+ assert(!surf->mapped);
+ gl->VDPAUMapSurfacesNV(1, &surf->registered);
+ surf->mapped = true;
+
+ ra_gl_ctx_resize(sw, surf->w, surf->h, surf->fbo);
+ return ra_gl_ctx_start_frame(sw);
}
-static void glx_start_frame(struct MPGLContext *ctx)
+static bool vdpau_submit_frame(struct ra_swapchain *sw,
+ const struct vo_frame *frame)
{
- struct priv *p = ctx->priv;
+ struct priv *p = sw->ctx->priv;
+ GL *gl = &p->gl;
struct vdp_functions *vdp = &p->vdp->vdp;
VdpStatus vdp_st;
- GL *gl = ctx->gl;
-
- struct surface *surface = &p->surfaces[p->current_surface];
-
- if (surface->surface != VDP_INVALID_HANDLE) {
- VdpTime prev_vsync_time;
- vdp_st = vdp->presentation_queue_block_until_surface_idle(p->vdp_queue,
- surface->surface,
- &prev_vsync_time);
- CHECK_VDP_WARNING(ctx, "waiting for surface failed");
- }
- if (surface->w != ctx->vo->dwidth || surface->h != ctx->vo->dheight)
- recreate_vdpau_surface(ctx, surface);
+ struct surface *surf = &p->surfaces[p->idx_surfaces];
+ assert(surf->surface != VDP_INVALID_HANDLE);
+ assert(surf->mapped);
+ gl->VDPAUUnmapSurfacesNV(1, &surf->registered);
+ surf->mapped = false;
+ vdp_st = vdp->presentation_queue_display(p->vdp_queue, surf->surface, 0, 0, 0);
+ CHECK_VDP_WARNING(sw->ctx, "trying to present vdp surface");
- ctx->main_fb = surface->fbo; // 0 if creating the surface failed
-
- if (surface->surface != VDP_INVALID_HANDLE) {
- gl->VDPAUMapSurfacesNV(1, &surface->registered);
- surface->mapped = true;
- }
+ p->idx_surfaces = (p->idx_surfaces + 1) % p->num_surfaces;
+ return ra_gl_ctx_submit_frame(sw, frame) && vdp_st == VDP_STATUS_OK;
}
-static void glx_swap_buffers(struct MPGLContext *ctx)
+static bool vdpau_reconfig(struct ra_ctx *ctx)
{
- struct priv *p = ctx->priv;
- struct vdp_functions *vdp = &p->vdp->vdp;
- VdpStatus vdp_st;
- GL *gl = ctx->gl;
-
- struct surface *surface = &p->surfaces[p->current_surface];
- if (surface->surface == VDP_INVALID_HANDLE)
- return; // surface alloc probably failed before
-
- if (surface->mapped)
- gl->VDPAUUnmapSurfacesNV(1, &surface->registered);
- surface->mapped = false;
-
- vdp_st = vdp->presentation_queue_display(p->vdp_queue, surface->surface,
- 0, 0, 0);
- CHECK_VDP_WARNING(ctx, "trying to present vdp surface");
+ vo_x11_config_vo_window(ctx->vo);
+ return true;
+}
- p->current_surface = (p->current_surface + 1) % p->num_surfaces;
+static int vdpau_control(struct ra_ctx *ctx, int *events, int request, void *arg)
+{
+ return vo_x11_control(ctx->vo, events, request, arg);
}
-static void glx_wakeup(struct MPGLContext *ctx)
+static void vdpau_wakeup(struct ra_ctx *ctx)
{
vo_x11_wakeup(ctx->vo);
}
-static void glx_wait_events(struct MPGLContext *ctx, int64_t until_time_us)
+static void vdpau_wait_events(struct ra_ctx *ctx, int64_t until_time_us)
{
vo_x11_wait_events(ctx->vo, until_time_us);
}
-const struct mpgl_driver mpgl_driver_vdpauglx = {
+static const struct ra_swapchain_fns vdpau_swapchain = {
+ .start_frame = vdpau_start_frame,
+ .submit_frame = vdpau_submit_frame,
+};
+
+const struct ra_ctx_fns ra_ctx_vdpauglx = {
+ .type = "opengl",
.name = "vdpauglx",
- .priv_size = sizeof(struct priv),
- .init = glx_init,
- .reconfig = glx_reconfig,
- .start_frame = glx_start_frame,
- .swap_buffers = glx_swap_buffers,
- .control = glx_control,
- .wakeup = glx_wakeup,
- .wait_events = glx_wait_events,
- .uninit = glx_uninit,
+ .reconfig = vdpau_reconfig,
+ .control = vdpau_control,
+ .wakeup = vdpau_wakeup,
+ .wait_events = vdpau_wait_events,
+ .init = vdpau_init,
+ .uninit = vdpau_uninit,
};
diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c
index 87e98cd64f..6ddc550306 100644
--- a/video/out/opengl/context_wayland.c
+++ b/video/out/opengl/context_wayland.c
@@ -19,6 +19,7 @@
#include "video/out/wayland_common.h"
#include "context.h"
#include "egl_helpers.h"
+#include "utils.h"
static void egl_resize(struct vo_wayland_state *wl)
{
@@ -63,30 +64,42 @@ static void egl_resize(struct vo_wayland_state *wl)
wl->vo->want_redraw = true;
}
-static int egl_create_context(struct vo_wayland_state *wl, MPGLContext *ctx,
- int flags)
+static void waylandgl_swap_buffers(struct ra_ctx *ctx)
{
- GL *gl = ctx->gl;
+ struct vo_wayland_state *wl = ctx->vo->wayland;
+ vo_wayland_wait_events(ctx->vo, 0);
+ eglSwapBuffers(wl->egl_context.egl.dpy, wl->egl_context.egl_surface);
+}
+
+static bool egl_create_context(struct ra_ctx *ctx, struct vo_wayland_state *wl)
+{
+ GL *gl = ctx->priv = talloc_zero(ctx, GL);
if (!(wl->egl_context.egl.dpy = eglGetDisplay(wl->display.display)))
- return -1;
+ return false;
if (eglInitialize(wl->egl_context.egl.dpy, NULL, NULL) != EGL_TRUE)
- return -1;
+ return false;
- if (!mpegl_create_context(wl->egl_context.egl.dpy, wl->log, flags,
+ if (!mpegl_create_context(ctx, wl->egl_context.egl.dpy,
&wl->egl_context.egl.ctx,
&wl->egl_context.egl.conf))
- return -1;
+ return false;
eglMakeCurrent(wl->egl_context.egl.dpy, NULL, NULL, wl->egl_context.egl.ctx);
mpegl_load_functions(gl, wl->log);
- ctx->native_display_type = "wl";
- ctx->native_display = wl->display.display;
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = waylandgl_swap_buffers,
+ .native_display_type = "wl",
+ .native_display = wl->display.display,
+ };
+
+ if (!ra_gl_ctx_init(ctx, gl, params))
+ return false;
- return 0;
+ return true;
}
static void egl_create_window(struct vo_wayland_state *wl)
@@ -122,23 +135,25 @@ static void egl_create_window(struct vo_wayland_state *wl)
eglSwapInterval(wl->egl_context.egl.dpy, 0);
}
-static int waylandgl_reconfig(struct MPGLContext *ctx)
+static bool waylandgl_reconfig(struct ra_ctx *ctx)
{
struct vo_wayland_state * wl = ctx->vo->wayland;
if (!vo_wayland_config(ctx->vo))
- return -1;
+ return false;
if (!wl->egl_context.egl_window)
egl_create_window(wl);
- return 0;
+ return true;
}
-static void waylandgl_uninit(MPGLContext *ctx)
+static void waylandgl_uninit(struct ra_ctx *ctx)
{
struct vo_wayland_state *wl = ctx->vo->wayland;
+ ra_gl_ctx_uninit(ctx);
+
if (wl->egl_context.egl.ctx) {
eglReleaseThread();
if (wl->egl_context.egl_window)
@@ -153,52 +168,45 @@ static void waylandgl_uninit(MPGLContext *ctx)
vo_wayland_uninit(ctx->vo);
}
-static void waylandgl_swap_buffers(MPGLContext *ctx)
-{
- struct vo_wayland_state *wl = ctx->vo->wayland;
-
- vo_wayland_wait_events(ctx->vo, 0);
-
- eglSwapBuffers(wl->egl_context.egl.dpy, wl->egl_context.egl_surface);
-}
-
-static int waylandgl_control(MPGLContext *ctx, int *events, int request,
+static int waylandgl_control(struct ra_ctx *ctx, int *events, int request,
void *data)
{
struct vo_wayland_state *wl = ctx->vo->wayland;
int r = vo_wayland_control(ctx->vo, events, request, data);
- if (*events & VO_EVENT_RESIZE)
+ if (*events & VO_EVENT_RESIZE) {
egl_resize(wl);
+ ra_gl_ctx_resize(ctx->swapchain, wl->vo->dwidth, wl->vo->dheight, 0);
+ }
return r;
}
-static void wayland_wakeup(struct MPGLContext *ctx)
+static void wayland_wakeup(struct ra_ctx *ctx)
{
vo_wayland_wakeup(ctx->vo);
}
-static void wayland_wait_events(struct MPGLContext *ctx, int64_t until_time_us)
+static void wayland_wait_events(struct ra_ctx *ctx, int64_t until_time_us)
{
vo_wayland_wait_events(ctx->vo, until_time_us);
}
-static int waylandgl_init(struct MPGLContext *ctx, int flags)
+static bool waylandgl_init(struct ra_ctx *ctx)
{
if (!vo_wayland_init(ctx->vo))
- return -1;
+ return false;
- return egl_create_context(ctx->vo->wayland, ctx, flags);
+ return egl_create_context(ctx, ctx->vo->wayland);
}
-const struct mpgl_driver mpgl_driver_wayland = {
+const struct ra_ctx_fns ra_ctx_wayland_egl = {
+ .type = "opengl",
.name = "wayland",
- .init = waylandgl_init,
.reconfig = waylandgl_reconfig,
- .swap_buffers = waylandgl_swap_buffers,
.control = waylandgl_control,
.wakeup = wayland_wakeup,
.wait_events = wayland_wait_events,
+ .init = waylandgl_init,
.uninit = waylandgl_uninit,
};
diff --git a/video/out/opengl/context_x11egl.c b/video/out/opengl/context_x11egl.c
index 2b68007a33..7ab4fe0579 100644
--- a/video/out/opengl/context_x11egl.c
+++ b/video/out/opengl/context_x11egl.c
@@ -32,14 +32,17 @@
#include "egl_helpers.h"
struct priv {
+ GL gl;
EGLDisplay egl_display;
EGLContext egl_context;
EGLSurface egl_surface;
};
-static void mpegl_uninit(MPGLContext *ctx)
+static void mpegl_uninit(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
+
if (p->egl_context) {
eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
@@ -51,7 +54,7 @@ static void mpegl_uninit(MPGLContext *ctx)
static int pick_xrgba_config(void *user_data, EGLConfig *configs, int num_configs)
{
- struct MPGLContext *ctx = user_data;
+ struct ra_ctx *ctx = user_data;
struct priv *p = ctx->priv;
struct vo *vo = ctx->vo;
@@ -72,40 +75,44 @@ static int pick_xrgba_config(void *user_data, EGLConfig *configs, int num_config
return 0;
}
-static int mpegl_init(struct MPGLContext *ctx, int flags)
+static void mpegl_swap_buffers(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
+ eglSwapBuffers(p->egl_display, p->egl_surface);
+}
+
+static bool mpegl_init(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
struct vo *vo = ctx->vo;
- int msgl = vo->probing ? MSGL_V : MSGL_FATAL;
+ int msgl = ctx->opts.probing ? MSGL_V : MSGL_FATAL;
if (!vo_x11_init(vo))
goto uninit;
p->egl_display = eglGetDisplay(vo->x11->display);
if (!eglInitialize(p->egl_display, NULL, NULL)) {
- mp_msg(vo->log, msgl, "Could not initialize EGL.\n");
+ MP_MSG(ctx, msgl, "Could not initialize EGL.\n");
goto uninit;
}
- struct mpegl_opts opts = {
- .vo_flags = flags,
+ struct mpegl_cb cb = {
.user_data = ctx,
- .refine_config = (flags & VOFLAG_ALPHA) ? pick_xrgba_config : NULL,
+ .refine_config = ctx->opts.want_alpha ? pick_xrgba_config : NULL,
};
EGLConfig config;
- if (!mpegl_create_context_opts(p->egl_display, vo->log, &opts,
- &p->egl_context, &config))
+ if (!mpegl_create_context_cb(ctx, p->egl_display, cb, &p->egl_context, &config))
goto uninit;
int vID, n;
eglGetConfigAttrib(p->egl_display, config, EGL_NATIVE_VISUAL_ID, &vID);
- MP_VERBOSE(vo, "chose visual 0x%x\n", vID);
+ MP_VERBOSE(ctx, "chose visual 0x%x\n", vID);
XVisualInfo template = {.visualid = vID};
XVisualInfo *vi = XGetVisualInfo(vo->x11->display, VisualIDMask, &template, &n);
if (!vi) {
- MP_FATAL(vo, "Getting X visual failed!\n");
+ MP_FATAL(ctx, "Getting X visual failed!\n");
goto uninit;
}
@@ -120,64 +127,73 @@ static int mpegl_init(struct MPGLContext *ctx, int flags)
(EGLNativeWindowType)vo->x11->window, NULL);
if (p->egl_surface == EGL_NO_SURFACE) {
- MP_FATAL(ctx->vo, "Could not create EGL surface!\n");
+ MP_FATAL(ctx, "Could not create EGL surface!\n");
goto uninit;
}
if (!eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
p->egl_context))
{
- MP_FATAL(ctx->vo, "Could not make context current!\n");
+ MP_FATAL(ctx, "Could not make context current!\n");
goto uninit;
}
- mpegl_load_functions(ctx->gl, vo->log);
+ mpegl_load_functions(&p->gl, ctx->log);
- ctx->native_display_type = "x11";
- ctx->native_display = vo->x11->display;
- return 0;
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = mpegl_swap_buffers,
+ .native_display_type = "x11",
+ .native_display = vo->x11->display,
+ };
+
+ if (!ra_gl_ctx_init(ctx, &p->gl, params))
+ goto uninit;
+
+ return true;
uninit:
mpegl_uninit(ctx);
- return -1;
+ return false;
}
-static int mpegl_reconfig(struct MPGLContext *ctx)
+static void resize(struct ra_ctx *ctx)
{
- vo_x11_config_vo_window(ctx->vo);
- return 0;
+ ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0);
}
-static int mpegl_control(struct MPGLContext *ctx, int *events, int request,
- void *arg)
+static bool mpegl_reconfig(struct ra_ctx *ctx)
{
- return vo_x11_control(ctx->vo, events, request, arg);
+ vo_x11_config_vo_window(ctx->vo);
+ resize(ctx);
+ return true;
}
-static void mpegl_swap_buffers(MPGLContext *ctx)
+static int mpegl_control(struct ra_ctx *ctx, int *events, int request,
+ void *arg)
{
- struct priv *p = ctx->priv;
- eglSwapBuffers(p->egl_display, p->egl_surface);
+ int ret = vo_x11_control(ctx->vo, events, request, arg);
+ if (*events & VO_EVENT_RESIZE)
+ resize(ctx);
+ return ret;
}
-static void mpegl_wakeup(struct MPGLContext *ctx)
+static void mpegl_wakeup(struct ra_ctx *ctx)
{
vo_x11_wakeup(ctx->vo);
}
-static void mpegl_wait_events(struct MPGLContext *ctx, int64_t until_time_us)
+static void mpegl_wait_events(struct ra_ctx *ctx, int64_t until_time_us)
{
vo_x11_wait_events(ctx->vo, until_time_us);
}
-const struct mpgl_driver mpgl_driver_x11egl = {
+const struct ra_ctx_fns ra_ctx_x11_egl = {
+ .type = "opengl",
.name = "x11egl",
- .priv_size = sizeof(struct priv),
- .init = mpegl_init,
.reconfig = mpegl_reconfig,
- .swap_buffers = mpegl_swap_buffers,
.control = mpegl_control,
.wakeup = mpegl_wakeup,
.wait_events = mpegl_wait_events,
+ .init = mpegl_init,
.uninit = mpegl_uninit,
};
diff --git a/video/out/opengl/egl_helpers.c b/video/out/opengl/egl_helpers.c
index ac152df06a..0033bf1e33 100644
--- a/video/out/opengl/egl_helpers.c
+++ b/video/out/opengl/egl_helpers.c
@@ -25,6 +25,7 @@
#include "egl_helpers.h"
#include "common.h"
+#include "utils.h"
#include "context.h"
#if HAVE_EGL_ANGLE
@@ -43,41 +44,49 @@
#define EGL_OPENGL_ES3_BIT 0x00000040
#endif
-// es_version = 0 (desktop), 2/3 (ES major version)
-static bool create_context(EGLDisplay display, struct mp_log *log, bool probing,
- int es_version, struct mpegl_opts *opts,
+// es_version: 0 (core), 2 or 3
+static bool create_context(struct ra_ctx *ctx, EGLDisplay display,
+ int es_version, struct mpegl_cb cb,
EGLContext *out_context, EGLConfig *out_config)
{
- int msgl = probing ? MSGL_V : MSGL_FATAL;
-
- EGLenum api = EGL_OPENGL_API;
- EGLint rend = EGL_OPENGL_BIT;
- const char *name = "Desktop OpenGL";
- if (es_version == 2) {
+ int msgl = ctx->opts.probing ? MSGL_V : MSGL_FATAL;
+
+ EGLenum api;
+ EGLint rend;
+ const char *name;
+
+ switch (es_version) {
+ case 0:
+ api = EGL_OPENGL_API;
+ rend = EGL_OPENGL_BIT;
+ name = "Desktop OpenGL";
+ break;
+ case 2:
api = EGL_OPENGL_ES_API;
rend = EGL_OPENGL_ES2_BIT;
- name = "GLES 2.0";
- }
- if (es_version == 3) {
+ name = "GLES 2.x";
+ break;
+ case 3:
api = EGL_OPENGL_ES_API;
rend = EGL_OPENGL_ES3_BIT;
name = "GLES 3.x";
+ break;
+ default: abort();
}
- mp_msg(log, MSGL_V, "Trying to create %s context.\n", name);
+ MP_VERBOSE(ctx, "Trying to create %s context.\n", name);
if (!eglBindAPI(api)) {
- mp_msg(log, MSGL_V, "Could not bind API!\n");
+ MP_VERBOSE(ctx, "Could not bind API!\n");
return false;
}
-
EGLint attributes[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
- EGL_ALPHA_SIZE, (opts->vo_flags & VOFLAG_ALPHA ) ? 1 : 0,
+ EGL_ALPHA_SIZE, ctx->opts.want_alpha ? 1 : 0,
EGL_RENDERABLE_TYPE, rend,
EGL_NONE
};
@@ -92,29 +101,34 @@ static bool create_context(EGLDisplay display, struct mp_log *log, bool probing,
if (!num_configs) {
talloc_free(configs);
- mp_msg(log, msgl, "Could not choose EGLConfig!\n");
+ MP_MSG(ctx, msgl, "Could not choose EGLConfig!\n");
return false;
}
int chosen = 0;
- if (opts->refine_config)
- chosen = opts->refine_config(opts->user_data, configs, num_configs);
+ if (cb.refine_config)
+ chosen = cb.refine_config(cb.user_data, configs, num_configs);
EGLConfig config = configs[chosen];
talloc_free(configs);
- EGLContext *ctx = NULL;
+ EGLContext *egl_ctx = NULL;
if (es_version) {
+ if (!ra_gl_ctx_test_version(ctx, MPGL_VER(es_version, 0), true))
+ return false;
+
EGLint attrs[] = {
EGL_CONTEXT_CLIENT_VERSION, es_version,
EGL_NONE
};
- ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
+ egl_ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
} else {
for (int n = 0; mpgl_preferred_gl_versions[n]; n++) {
int ver = mpgl_preferred_gl_versions[n];
+ if (!ra_gl_ctx_test_version(ctx, ver, false))
+ continue;
EGLint attrs[] = {
EGL_CONTEXT_MAJOR_VERSION, MPGL_VER_GET_MAJOR(ver),
@@ -124,25 +138,25 @@ static bool create_context(EGLDisplay display, struct mp_log *log, bool probing,
EGL_NONE
};
- ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
- if (ctx)
+ egl_ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
+ if (egl_ctx)
break;
}
- if (!ctx) {
+ if (!egl_ctx && ra_gl_ctx_test_version(ctx, 140, false)) {
// Fallback for EGL 1.4 without EGL_KHR_create_context.
EGLint attrs[] = { EGL_NONE };
- ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
+ egl_ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
}
}
- if (!ctx) {
- mp_msg(log, msgl, "Could not create EGL context!\n");
+ if (!egl_ctx) {
+ MP_MSG(ctx, msgl, "Could not create EGL context!\n");
return false;
}
- *out_context = ctx;
+ *out_context = egl_ctx;
*out_config = config;
return true;
}
@@ -152,56 +166,36 @@ static bool create_context(EGLDisplay display, struct mp_log *log, bool probing,
// Create a context and return it and the config it was created with. If it
// returns false, the out_* pointers are set to NULL.
// vo_flags is a combination of VOFLAG_* values.
-bool mpegl_create_context(EGLDisplay display, struct mp_log *log, int vo_flags,
+bool mpegl_create_context(struct ra_ctx *ctx, EGLDisplay display,
EGLContext *out_context, EGLConfig *out_config)
{
- return mpegl_create_context_opts(display, log,
- &(struct mpegl_opts){.vo_flags = vo_flags}, out_context, out_config);
+ return mpegl_create_context_cb(ctx, display, (struct mpegl_cb){0},
+ out_context, out_config);
}
// Create a context and return it and the config it was created with. If it
// returns false, the out_* pointers are set to NULL.
-bool mpegl_create_context_opts(EGLDisplay display, struct mp_log *log,
- struct mpegl_opts *opts,
- EGLContext *out_context, EGLConfig *out_config)
+bool mpegl_create_context_cb(struct ra_ctx *ctx, EGLDisplay display,
+ struct mpegl_cb cb, EGLContext *out_context,
+ EGLConfig *out_config)
{
- assert(opts);
-
*out_context = NULL;
*out_config = NULL;
const char *version = eglQueryString(display, EGL_VERSION);
const char *vendor = eglQueryString(display, EGL_VENDOR);
const char *apis = eglQueryString(display, EGL_CLIENT_APIS);
- mp_verbose(log, "EGL_VERSION=%s\nEGL_VENDOR=%s\nEGL_CLIENT_APIS=%s\n",
+ MP_VERBOSE(ctx, "EGL_VERSION=%s\nEGL_VENDOR=%s\nEGL_CLIENT_APIS=%s\n",
STR_OR_ERR(version), STR_OR_ERR(vendor), STR_OR_ERR(apis));
- bool probing = opts->vo_flags & VOFLAG_PROBING;
- int msgl = probing ? MSGL_V : MSGL_FATAL;
- bool try_gles = !(opts->vo_flags & VOFLAG_NO_GLES);
-
- if (!(opts->vo_flags & VOFLAG_GLES)) {
- // Desktop OpenGL
- if (create_context(display, log, try_gles | probing, 0, opts,
- out_context, out_config))
- return true;
- }
-
- if (try_gles && !(opts->vo_flags & VOFLAG_GLES2)) {
- // ES 3.x
- if (create_context(display, log, true, 3, opts,
- out_context, out_config))
- return true;
- }
-
- if (try_gles) {
- // ES 2.0
- if (create_context(display, log, probing, 2, opts,
- out_context, out_config))
+ int es[] = {0, 3, 2}; // preference order
+ for (int i = 0; i < MP_ARRAY_SIZE(es); i++) {
+ if (create_context(ctx, display, es[i], cb, out_context, out_config))
return true;
}
- mp_msg(log, msgl, "Could not create a GL context.\n");
+ int msgl = ctx->opts.probing ? MSGL_V : MSGL_ERR;
+ MP_MSG(ctx, msgl, "Could not create a GL context.\n");
return false;
}
diff --git a/video/out/opengl/egl_helpers.h b/video/out/opengl/egl_helpers.h
index 05f9dccb70..eaaf9d7a48 100644
--- a/video/out/opengl/egl_helpers.h
+++ b/video/out/opengl/egl_helpers.h
@@ -6,26 +6,23 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include "video/out/gpu/context.h"
+
struct mp_log;
-bool mpegl_create_context(EGLDisplay display, struct mp_log *log, int vo_flags,
+bool mpegl_create_context(struct ra_ctx *ctx, EGLDisplay display,
EGLContext *out_context, EGLConfig *out_config);
-struct mpegl_opts {
- // combination of VOFLAG_* values.
- int vo_flags;
-
- // for callbacks
- void *user_data;
-
+struct mpegl_cb {
// if set, pick the desired config from the given list and return its index
// defaults to 0 (they are sorted by eglChooseConfig)
int (*refine_config)(void *user_data, EGLConfig *configs, int num_configs);
+ void *user_data;
};
-bool mpegl_create_context_opts(EGLDisplay display, struct mp_log *log,
- struct mpegl_opts *opts,
- EGLContext *out_context, EGLConfig *out_config);
+bool mpegl_create_context_cb(struct ra_ctx *ctx, EGLDisplay display,
+ struct mpegl_cb cb, EGLContext *out_context,
+ EGLConfig *out_config);
struct GL;
void mpegl_load_functions(struct GL *gl, struct mp_log *log);
diff --git a/video/out/opengl/formats.h b/video/out/opengl/formats.h
index 3da6ede82a..f727a3b6ef 100644
--- a/video/out/opengl/formats.h
+++ b/video/out/opengl/formats.h
@@ -2,7 +2,6 @@
#define MPGL_FORMATS_H_
#include "common.h"
-#include "ra.h"
struct gl_format {
const char *name; // symbolic name for user interaction/debugging
diff --git a/video/out/opengl/gl_utils.c b/video/out/opengl/gl_utils.c
deleted file mode 100644
index bce2dabe5d..0000000000
--- a/video/out/opengl/gl_utils.c
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * This file is part of mpv.
- * Parts based on MPlayer code by Reimar Döffinger.
- *
- * 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 <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <assert.h>
-
-#include <libavutil/sha.h>
-#include <libavutil/intreadwrite.h>
-#include <libavutil/mem.h>
-
-#include "osdep/io.h"
-
-#include "common/common.h"
-#include "options/path.h"
-#include "stream/stream.h"
-#include "formats.h"
-#include "ra_gl.h"
-#include "gl_utils.h"
-
-// GLU has this as gluErrorString (we don't use GLU, as it is legacy-OpenGL)
-static const char *gl_error_to_string(GLenum error)
-{
- switch (error) {
- case GL_INVALID_ENUM: return "INVALID_ENUM";
- case GL_INVALID_VALUE: return "INVALID_VALUE";
- case GL_INVALID_OPERATION: return "INVALID_OPERATION";
- case GL_INVALID_FRAMEBUFFER_OPERATION: return "INVALID_FRAMEBUFFER_OPERATION";
- case GL_OUT_OF_MEMORY: return "OUT_OF_MEMORY";
- default: return "unknown";
- }
-}
-
-void gl_check_error(GL *gl, struct mp_log *log, const char *info)
-{
- for (;;) {
- GLenum error = gl->GetError();
- if (error == GL_NO_ERROR)
- break;
- mp_msg(log, MSGL_ERR, "%s: OpenGL error %s.\n", info,
- gl_error_to_string(error));
- }
-}
-
-static int get_alignment(int stride)
-{
- if (stride % 8 == 0)
- return 8;
- if (stride % 4 == 0)
- return 4;
- if (stride % 2 == 0)
- return 2;
- return 1;
-}
-
-// upload a texture, handling things like stride and slices
-// target: texture target, usually GL_TEXTURE_2D
-// format, type: texture parameters
-// dataptr, stride: image data
-// x, y, width, height: part of the image to upload
-void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type,
- const void *dataptr, int stride,
- int x, int y, int w, int h)
-{
- int bpp = gl_bytes_per_pixel(format, type);
- const uint8_t *data = dataptr;
- int y_max = y + h;
- if (w <= 0 || h <= 0 || !bpp)
- return;
- if (stride < 0) {
- data += (h - 1) * stride;
- stride = -stride;
- }
- gl->PixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(stride));
- int slice = h;
- if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH) {
- // this is not always correct, but should work for MPlayer
- gl->PixelStorei(GL_UNPACK_ROW_LENGTH, stride / bpp);
- } else {
- if (stride != bpp * w)
- slice = 1; // very inefficient, but at least it works
- }
- for (; y + slice <= y_max; y += slice) {
- gl->TexSubImage2D(target, 0, x, y, w, slice, format, type, data);
- data += stride * slice;
- }
- if (y < y_max)
- gl->TexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data);
- if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH)
- gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
-}
-
-mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h)
-{
- if (gl->es)
- return NULL; // ES can't read from front buffer
- mp_image_t *image = mp_image_alloc(IMGFMT_RGB24, w, h);
- if (!image)
- return NULL;
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
- GLenum obj = fbo ? GL_COLOR_ATTACHMENT0 : GL_FRONT;
- gl->PixelStorei(GL_PACK_ALIGNMENT, 1);
- gl->ReadBuffer(obj);
- //flip image while reading (and also avoid stride-related trouble)
- for (int y = 0; y < h; y++) {
- gl->ReadPixels(0, h - y - 1, w, 1, GL_RGB, GL_UNSIGNED_BYTE,
- image->planes[0] + y * image->stride[0]);
- }
- gl->PixelStorei(GL_PACK_ALIGNMENT, 4);
- gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
- return image;
-}
-
-static void gl_vao_enable_attribs(struct gl_vao *vao)
-{
- GL *gl = vao->gl;
-
- for (int n = 0; n < vao->num_entries; n++) {
- const struct ra_renderpass_input *e = &vao->entries[n];
- GLenum type = 0;
- bool normalized = false;
- switch (e->type) {
- case RA_VARTYPE_INT:
- type = GL_INT;
- break;
- case RA_VARTYPE_FLOAT:
- type = GL_FLOAT;
- break;
- case RA_VARTYPE_BYTE_UNORM:
- type = GL_UNSIGNED_BYTE;
- normalized = true;
- break;
- default:
- abort();
- }
- assert(e->dim_m == 1);
-
- gl->EnableVertexAttribArray(n);
- gl->VertexAttribPointer(n, e->dim_v, type, normalized,
- vao->stride, (void *)(intptr_t)e->offset);
- }
-}
-
-void gl_vao_init(struct gl_vao *vao, GL *gl, int stride,
- const struct ra_renderpass_input *entries,
- int num_entries)
-{
- assert(!vao->vao);
- assert(!vao->buffer);
-
- *vao = (struct gl_vao){
- .gl = gl,
- .stride = stride,
- .entries = entries,
- .num_entries = num_entries,
- };
-
- gl->GenBuffers(1, &vao->buffer);
-
- if (gl->BindVertexArray) {
- gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
-
- gl->GenVertexArrays(1, &vao->vao);
- gl->BindVertexArray(vao->vao);
- gl_vao_enable_attribs(vao);
- gl->BindVertexArray(0);
-
- gl->BindBuffer(GL_ARRAY_BUFFER, 0);
- }
-}
-
-void gl_vao_uninit(struct gl_vao *vao)
-{
- GL *gl = vao->gl;
- if (!gl)
- return;
-
- if (gl->DeleteVertexArrays)
- gl->DeleteVertexArrays(1, &vao->vao);
- gl->DeleteBuffers(1, &vao->buffer);
-
- *vao = (struct gl_vao){0};
-}
-
-static void gl_vao_bind(struct gl_vao *vao)
-{
- GL *gl = vao->gl;
-
- if (gl->BindVertexArray) {
- gl->BindVertexArray(vao->vao);
- } else {
- gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
- gl_vao_enable_attribs(vao);
- gl->BindBuffer(GL_ARRAY_BUFFER, 0);
- }
-}
-
-static void gl_vao_unbind(struct gl_vao *vao)
-{
- GL *gl = vao->gl;
-
- if (gl->BindVertexArray) {
- gl->BindVertexArray(0);
- } else {
- for (int n = 0; n < vao->num_entries; n++)
- gl->DisableVertexAttribArray(n);
- }
-}
-
-// Draw the vertex data (as described by the gl_vao_entry entries) in ptr
-// to the screen. num is the number of vertexes. prim is usually GL_TRIANGLES.
-// If ptr is NULL, then skip the upload, and use the data uploaded with the
-// previous call.
-void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num)
-{
- GL *gl = vao->gl;
-
- if (ptr) {
- gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
- gl->BufferData(GL_ARRAY_BUFFER, num * vao->stride, ptr, GL_STREAM_DRAW);
- gl->BindBuffer(GL_ARRAY_BUFFER, 0);
- }
-
- gl_vao_bind(vao);
-
- gl->DrawArrays(prim, 0, num);
-
- gl_vao_unbind(vao);
-}
-
-static void GLAPIENTRY gl_debug_cb(GLenum source, GLenum type, GLuint id,
- GLenum severity, GLsizei length,
- const GLchar *message, const void *userParam)
-{
- // keep in mind that the debug callback can be asynchronous
- struct mp_log *log = (void *)userParam;
- int level = MSGL_ERR;
- switch (severity) {
- case GL_DEBUG_SEVERITY_NOTIFICATION:level = MSGL_V; break;
- case GL_DEBUG_SEVERITY_LOW: level = MSGL_INFO; break;
- case GL_DEBUG_SEVERITY_MEDIUM: level = MSGL_WARN; break;
- case GL_DEBUG_SEVERITY_HIGH: level = MSGL_ERR; break;
- }
- mp_msg(log, level, "GL: %s\n", message);
-}
-
-void gl_set_debug_logger(GL *gl, struct mp_log *log)
-{
- if (gl->DebugMessageCallback)
- gl->DebugMessageCallback(log ? gl_debug_cb : NULL, log);
-}
-
-int gl_get_fb_depth(GL *gl, int fbo)
-{
- if ((gl->es < 300 && !gl->version) || !(gl->mpgl_caps & MPGL_CAP_FB))
- return -1;
-
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
-
- GLenum obj = gl->version ? GL_BACK_LEFT : GL_BACK;
- if (fbo)
- obj = GL_COLOR_ATTACHMENT0;
-
- GLint depth_g = -1;
-
- gl->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, obj,
- GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, &depth_g);
-
- gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
-
- return depth_g > 0 ? depth_g : -1;
-}
diff --git a/video/out/opengl/gl_utils.h b/video/out/opengl/gl_utils.h
deleted file mode 100644
index 306ee23f65..0000000000
--- a/video/out/opengl/gl_utils.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * This file is part of mpv.
- * Parts based on MPlayer code by Reimar Döffinger.
- *
- * 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/>.
- */
-
-#ifndef MP_GL_UTILS_
-#define MP_GL_UTILS_
-
-#include <math.h>
-
-#include "common.h"
-#include "ra.h"
-
-struct mp_log;
-
-void gl_check_error(GL *gl, struct mp_log *log, const char *info);
-
-void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type,
- const void *dataptr, int stride,
- int x, int y, int w, int h);
-
-mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h);
-
-struct gl_vao {
- GL *gl;
- GLuint vao; // the VAO object, or 0 if unsupported by driver
- GLuint buffer; // GL_ARRAY_BUFFER used for the data
- int stride; // size of each element (interleaved elements are assumed)
- const struct ra_renderpass_input *entries;
- int num_entries;
-};
-
-void gl_vao_init(struct gl_vao *vao, GL *gl, int stride,
- const struct ra_renderpass_input *entries,
- int num_entries);
-void gl_vao_uninit(struct gl_vao *vao);
-void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num);
-
-void gl_set_debug_logger(GL *gl, struct mp_log *log);
-
-int gl_get_fb_depth(GL *gl, int fbo);
-
-#endif
diff --git a/video/out/opengl/hwdec_cuda.c b/video/out/opengl/hwdec_cuda.c
index d40bafee24..d9c4c199f1 100644
--- a/video/out/opengl/hwdec_cuda.c
+++ b/video/out/opengl/hwdec_cuda.c
@@ -32,11 +32,10 @@
#include <libavutil/hwcontext.h>
#include <libavutil/hwcontext_cuda.h>
+#include "video/out/gpu/hwdec.h"
#include "formats.h"
-#include "hwdec.h"
#include "options/m_config.h"
#include "ra_gl.h"
-#include "video.h"
struct priv_owner {
struct mp_hwdec_ctx hwctx;
diff --git a/video/out/opengl/hwdec_ios.m b/video/out/opengl/hwdec_ios.m
index 8e020ded63..71b205b583 100644
--- a/video/out/opengl/hwdec_ios.m
+++ b/video/out/opengl/hwdec_ios.m
@@ -27,10 +27,10 @@
#include "config.h"
+#include "video/out/gpu/hwdec.h"
#include "video/mp_image_pool.h"
#include "video/vt.h"
#include "ra_gl.h"
-#include "hwdec.h"
struct priv_owner {
struct mp_hwdec_ctx hwctx;
diff --git a/video/out/opengl/hwdec_osx.c b/video/out/opengl/hwdec_osx.c
index 348a5e19c5..cfd5f52e7b 100644
--- a/video/out/opengl/hwdec_osx.c
+++ b/video/out/opengl/hwdec_osx.c
@@ -29,9 +29,9 @@
#include "config.h"
#include "video/mp_image_pool.h"
+#include "video/out/gpu/hwdec.h"
#include "video/vt.h"
#include "ra_gl.h"
-#include "hwdec.h"
struct priv_owner {
struct mp_hwdec_ctx hwctx;
diff --git a/video/out/opengl/hwdec_rpi.c b/video/out/opengl/hwdec_rpi.c
index 6f39c3e330..ea8312a179 100644
--- a/video/out/opengl/hwdec_rpi.c
+++ b/video/out/opengl/hwdec_rpi.c
@@ -33,8 +33,8 @@
#include "common/common.h"
#include "common/msg.h"
#include "video/mp_image.h"
+#include "video/out/gpu/hwdec.h"
-#include "hwdec.h"
#include "common.h"
#include "ra_gl.h"
diff --git a/video/out/opengl/hwdec_vaegl.c b/video/out/opengl/hwdec_vaegl.c
index a0e3222cfc..6078222bd5 100644
--- a/video/out/opengl/hwdec_vaegl.c
+++ b/video/out/opengl/hwdec_vaegl.c
@@ -30,9 +30,9 @@
#include "config.h"
-#include "hwdec.h"
-#include "video/vaapi.h"
+#include "video/out/gpu/hwdec.h"
#include "video/mp_image_pool.h"
+#include "video/vaapi.h"
#include "common.h"
#include "ra_gl.h"
diff --git a/video/out/opengl/hwdec_vaglx.c b/video/out/opengl/hwdec_vaglx.c
index 8db15c4468..d5bc0b6ee7 100644
--- a/video/out/opengl/hwdec_vaglx.c
+++ b/video/out/opengl/hwdec_vaglx.c
@@ -25,10 +25,11 @@
#include <va/va_x11.h>
#include "video/out/x11_common.h"
-#include "ra_gl.h"
-#include "hwdec.h"
+#include "video/out/gpu/hwdec.h"
#include "video/vaapi.h"
+#include "ra_gl.h"
+
struct priv_owner {
struct mp_vaapi_ctx *ctx;
VADisplay *display;
diff --git a/video/out/opengl/hwdec_vdpau.c b/video/out/opengl/hwdec_vdpau.c
index d733650328..e0618e425e 100644
--- a/video/out/opengl/hwdec_vdpau.c
+++ b/video/out/opengl/hwdec_vdpau.c
@@ -20,7 +20,7 @@
#include <GL/glx.h>
-#include "hwdec.h"
+#include "video/out/gpu/hwdec.h"
#include "ra_gl.h"
#include "video/vdpau.h"
#include "video/vdpau_mixer.h"
diff --git a/video/out/opengl/ra_gl.c b/video/out/opengl/ra_gl.c
index 0d99877a9e..ccb8755ba6 100644
--- a/video/out/opengl/ra_gl.c
+++ b/video/out/opengl/ra_gl.c
@@ -1097,12 +1097,6 @@ static uint64_t gl_timer_stop(struct ra *ra, ra_timer *ratimer)
return timer->result;
}
-static void gl_flush(struct ra *ra)
-{
- GL *gl = ra_gl_get(ra);
- gl->Flush();
-}
-
static void gl_debug_marker(struct ra *ra, const char *msg)
{
struct ra_gl *p = ra->priv;
@@ -1130,6 +1124,5 @@ static struct ra_fns ra_fns_gl = {
.timer_destroy = gl_timer_destroy,
.timer_start = gl_timer_start,
.timer_stop = gl_timer_stop,
- .flush = gl_flush,
.debug_marker = gl_debug_marker,
};
diff --git a/video/out/opengl/ra_gl.h b/video/out/opengl/ra_gl.h
index e5e09a0197..9844977801 100644
--- a/video/out/opengl/ra_gl.h
+++ b/video/out/opengl/ra_gl.h
@@ -1,8 +1,7 @@
#pragma once
#include "common.h"
-#include "ra.h"
-#include "gl_utils.h"
+#include "utils.h"
struct ra *ra_create_gl(GL *gl, struct mp_log *log);
struct ra_tex *ra_create_wrapped_tex(struct ra *ra,
diff --git a/video/out/opengl/utils.c b/video/out/opengl/utils.c
index b8fc24a52e..3b296d52de 100644
--- a/video/out/opengl/utils.c
+++ b/video/out/opengl/utils.c
@@ -1,371 +1,269 @@
-#include "common/msg.h"
-#include "video/out/vo.h"
+/*
+ * This file is part of mpv.
+ * Parts based on MPlayer code by Reimar Döffinger.
+ *
+ * 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 <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include <libavutil/sha.h>
+#include <libavutil/intreadwrite.h>
+#include <libavutil/mem.h>
+
+#include "osdep/io.h"
+
+#include "common/common.h"
+#include "options/path.h"
+#include "stream/stream.h"
+#include "formats.h"
#include "utils.h"
-// Standard parallel 2D projection, except y1 < y0 means that the coordinate
-// system is flipped, not the projection.
-void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
- float y0, float y1)
+// GLU has this as gluErrorString (we don't use GLU, as it is legacy-OpenGL)
+static const char *gl_error_to_string(GLenum error)
{
- if (y1 < y0) {
- float tmp = y0;
- y0 = tmp - y1;
- y1 = tmp;
+ switch (error) {
+ case GL_INVALID_ENUM: return "INVALID_ENUM";
+ case GL_INVALID_VALUE: return "INVALID_VALUE";
+ case GL_INVALID_OPERATION: return "INVALID_OPERATION";
+ case GL_INVALID_FRAMEBUFFER_OPERATION: return "INVALID_FRAMEBUFFER_OPERATION";
+ case GL_OUT_OF_MEMORY: return "OUT_OF_MEMORY";
+ default: return "unknown";
}
-
- t->m[0][0] = 2.0f / (x1 - x0);
- t->m[0][1] = 0.0f;
- t->m[1][0] = 0.0f;
- t->m[1][1] = 2.0f / (y1 - y0);
- t->t[0] = -(x1 + x0) / (x1 - x0);
- t->t[1] = -(y1 + y0) / (y1 - y0);
-}
-
-// Apply the effects of one transformation to another, transforming it in the
-// process. In other words: post-composes t onto x
-void gl_transform_trans(struct gl_transform t, struct gl_transform *x)
-{
- struct gl_transform xt = *x;
- x->m[0][0] = t.m[0][0] * xt.m[0][0] + t.m[0][1] * xt.m[1][0];
- x->m[1][0] = t.m[1][0] * xt.m[0][0] + t.m[1][1] * xt.m[1][0];
- x->m[0][1] = t.m[0][0] * xt.m[0][1] + t.m[0][1] * xt.m[1][1];
- x->m[1][1] = t.m[1][0] * xt.m[0][1] + t.m[1][1] * xt.m[1][1];
- gl_transform_vec(t, &x->t[0], &x->t[1]);
-}
-
-void gl_transform_ortho_fbodst(struct gl_transform *t, struct fbodst fbo)
-{
- int y_dir = fbo.flip ? -1 : 1;
- gl_transform_ortho(t, 0, fbo.tex->params.w, 0, fbo.tex->params.h * y_dir);
}
-void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool)
+void gl_check_error(GL *gl, struct mp_log *log, const char *info)
{
- for (int i = 0; i < pool->num_buffers; i++)
- ra_buf_free(ra, &pool->buffers[i]);
-
- talloc_free(pool->buffers);
- *pool = (struct ra_buf_pool){0};
+ for (;;) {
+ GLenum error = gl->GetError();
+ if (error == GL_NO_ERROR)
+ break;
+ mp_msg(log, MSGL_ERR, "%s: OpenGL error %s.\n", info,
+ gl_error_to_string(error));
+ }
}
-static bool ra_buf_params_compatible(const struct ra_buf_params *new,
- const struct ra_buf_params *old)
+static int get_alignment(int stride)
{
- return new->type == old->type &&
- new->size <= old->size &&
- new->host_mapped == old->host_mapped &&
- new->host_mutable == old->host_mutable;
+ if (stride % 8 == 0)
+ return 8;
+ if (stride % 4 == 0)
+ return 4;
+ if (stride % 2 == 0)
+ return 2;
+ return 1;
}
-static bool ra_buf_pool_grow(struct ra *ra, struct ra_buf_pool *pool)
+// upload a texture, handling things like stride and slices
+// target: texture target, usually GL_TEXTURE_2D
+// format, type: texture parameters
+// dataptr, stride: image data
+// x, y, width, height: part of the image to upload
+void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type,
+ const void *dataptr, int stride,
+ int x, int y, int w, int h)
{
- struct ra_buf *buf = ra_buf_create(ra, &pool->current_params);
- if (!buf)
- return false;
-
- MP_TARRAY_INSERT_AT(NULL, pool->buffers, pool->num_buffers, pool->index, buf);
- MP_VERBOSE(ra, "Resized buffer pool to size %d\n", pool->num_buffers);
- return true;
+ int bpp = gl_bytes_per_pixel(format, type);
+ const uint8_t *data = dataptr;
+ int y_max = y + h;
+ if (w <= 0 || h <= 0 || !bpp)
+ return;
+ if (stride < 0) {
+ data += (h - 1) * stride;
+ stride = -stride;
+ }
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(stride));
+ int slice = h;
+ if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH) {
+ // this is not always correct, but should work for MPlayer
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, stride / bpp);
+ } else {
+ if (stride != bpp * w)
+ slice = 1; // very inefficient, but at least it works
+ }
+ for (; y + slice <= y_max; y += slice) {
+ gl->TexSubImage2D(target, 0, x, y, w, slice, format, type, data);
+ data += stride * slice;
+ }
+ if (y < y_max)
+ gl->TexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data);
+ if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH)
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
}
-struct ra_buf *ra_buf_pool_get(struct ra *ra, struct ra_buf_pool *pool,
- const struct ra_buf_params *params)
+mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h)
{
- assert(!params->initial_data);
-
- if (!ra_buf_params_compatible(params, &pool->current_params)) {
- ra_buf_pool_uninit(ra, pool);
- pool->current_params = *params;
- }
-
- // Make sure we have at least one buffer available
- if (!pool->buffers && !ra_buf_pool_grow(ra, pool))
- return NULL;
-
- // Make sure the next buffer is available for use
- if (!ra->fns->buf_poll(ra, pool->buffers[pool->index]) &&
- !ra_buf_pool_grow(ra, pool))
- {
+ if (gl->es)
+ return NULL; // ES can't read from front buffer
+ mp_image_t *image = mp_image_alloc(IMGFMT_RGB24, w, h);
+ if (!image)
return NULL;
+ gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
+ GLenum obj = fbo ? GL_COLOR_ATTACHMENT0 : GL_FRONT;
+ gl->PixelStorei(GL_PACK_ALIGNMENT, 1);
+ gl->ReadBuffer(obj);
+ //flip image while reading (and also avoid stride-related trouble)
+ for (int y = 0; y < h; y++) {
+ gl->ReadPixels(0, h - y - 1, w, 1, GL_RGB, GL_UNSIGNED_BYTE,
+ image->planes[0] + y * image->stride[0]);
}
-
- struct ra_buf *buf = pool->buffers[pool->index++];
- pool->index %= pool->num_buffers;
-
- return buf;
+ gl->PixelStorei(GL_PACK_ALIGNMENT, 4);
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+ return image;
}
-bool ra_tex_upload_pbo(struct ra *ra, struct ra_buf_pool *pbo,
- const struct ra_tex_upload_params *params)
+static void gl_vao_enable_attribs(struct gl_vao *vao)
{
- if (params->buf)
- return ra->fns->tex_upload(ra, params);
-
- struct ra_tex *tex = params->tex;
- size_t row_size = tex->params.dimensions == 2 ? params->stride :
- tex->params.w * tex->params.format->pixel_size;
-
- struct ra_buf_params bufparams = {
- .type = RA_BUF_TYPE_TEX_UPLOAD,
- .size = row_size * tex->params.h * tex->params.d,
- .host_mutable = true,
- };
-
- struct ra_buf *buf = ra_buf_pool_get(ra, pbo, &bufparams);
- if (!buf)
- return false;
-
- ra->fns->buf_update(ra, buf, 0, params->src, bufparams.size);
-
- struct ra_tex_upload_params newparams = *params;
- newparams.buf = buf;
- newparams.src = NULL;
-
- return ra->fns->tex_upload(ra, &newparams);
-}
+ GL *gl = vao->gl;
+
+ for (int n = 0; n < vao->num_entries; n++) {
+ const struct ra_renderpass_input *e = &vao->entries[n];
+ GLenum type = 0;
+ bool normalized = false;
+ switch (e->type) {
+ case RA_VARTYPE_INT:
+ type = GL_INT;
+ break;
+ case RA_VARTYPE_FLOAT:
+ type = GL_FLOAT;
+ break;
+ case RA_VARTYPE_BYTE_UNORM:
+ type = GL_UNSIGNED_BYTE;
+ normalized = true;
+ break;
+ default:
+ abort();
+ }
+ assert(e->dim_m == 1);
-struct ra_layout std140_layout(struct ra_renderpass_input *inp)
-{
- size_t el_size = ra_vartype_size(inp->type);
-
- // std140 packing rules:
- // 1. The alignment of generic values is their size in bytes
- // 2. The alignment of vectors is the vector length * the base count, with
- // the exception of vec3 which is always aligned like vec4
- // 3. The alignment of arrays is that of the element size rounded up to
- // the nearest multiple of vec4
- // 4. Matrices are treated like arrays of vectors
- // 5. Arrays/matrices are laid out with a stride equal to the alignment
- size_t size = el_size * inp->dim_v;
- if (inp->dim_v == 3)
- size += el_size;
- if (inp->dim_m > 1)
- size = MP_ALIGN_UP(size, sizeof(float[4]));
-
- return (struct ra_layout) {
- .align = size,
- .stride = size,
- .size = size * inp->dim_m,
- };
+ gl->EnableVertexAttribArray(n);
+ gl->VertexAttribPointer(n, e->dim_v, type, normalized,
+ vao->stride, (void *)(intptr_t)e->offset);
+ }
}
-struct ra_layout std430_layout(struct ra_renderpass_input *inp)
+void gl_vao_init(struct gl_vao *vao, GL *gl, int stride,
+ const struct ra_renderpass_input *entries,
+ int num_entries)
{
- size_t el_size = ra_vartype_size(inp->type);
-
- // std430 packing rules: like std140, except arrays/matrices are always
- // "tightly" packed, even arrays/matrices of vec3s
- size_t align = el_size * inp->dim_v;
- if (inp->dim_v == 3 && inp->dim_m == 1)
- align += el_size;
-
- return (struct ra_layout) {
- .align = align,
- .stride = align,
- .size = align * inp->dim_m,
+ assert(!vao->vao);
+ assert(!vao->buffer);
+
+ *vao = (struct gl_vao){
+ .gl = gl,
+ .stride = stride,
+ .entries = entries,
+ .num_entries = num_entries,
};
-}
-
-// Create a texture and a FBO using the texture as color attachments.
-// fmt: texture internal format
-// If the parameters are the same as the previous call, do not touch it.
-// flags can be 0, or a combination of FBOTEX_FUZZY_W and FBOTEX_FUZZY_H.
-// Enabling FUZZY for W or H means the w or h does not need to be exact.
-bool fbotex_change(struct fbotex *fbo, struct ra *ra, struct mp_log *log,
- int w, int h, const struct ra_format *fmt, int flags)
-{
- int lw = w, lh = h;
-
- if (fbo->tex) {
- int cw = w, ch = h;
- int rw = fbo->tex->params.w, rh = fbo->tex->params.h;
- if ((flags & FBOTEX_FUZZY_W) && cw < rw)
- cw = rw;
- if ((flags & FBOTEX_FUZZY_H) && ch < rh)
- ch = rh;
+ gl->GenBuffers(1, &vao->buffer);
- if (rw == cw && rh == ch && fbo->tex->params.format == fmt)
- goto done;
- }
+ if (gl->BindVertexArray) {
+ gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
- if (flags & FBOTEX_FUZZY_W)
- w = MP_ALIGN_UP(w, 256);
- if (flags & FBOTEX_FUZZY_H)
- h = MP_ALIGN_UP(h, 256);
+ gl->GenVertexArrays(1, &vao->vao);
+ gl->BindVertexArray(vao->vao);
+ gl_vao_enable_attribs(vao);
+ gl->BindVertexArray(0);
- mp_verbose(log, "Create FBO: %dx%d (%dx%d)\n", lw, lh, w, h);
-
- if (!fmt || !fmt->renderable || !fmt->linear_filter) {
- mp_err(log, "Format %s not supported.\n", fmt ? fmt->name : "(unset)");
- return false;
+ gl->BindBuffer(GL_ARRAY_BUFFER, 0);
}
+}
- fbotex_uninit(fbo);
-
- *fbo = (struct fbotex) {
- .ra = ra,
- };
-
- struct ra_tex_params params = {
- .dimensions = 2,
- .w = w,
- .h = h,
- .d = 1,
- .format = fmt,
- .src_linear = true,
- .render_src = true,
- .render_dst = true,
- .storage_dst = true,
- .blit_src = true,
- };
-
- fbo->tex = ra_tex_create(fbo->ra, &params);
-
- if (!fbo->tex) {
- mp_err(log, "Error: framebuffer could not be created.\n");
- fbotex_uninit(fbo);
- return false;
- }
-
-done:
-
- fbo->lw = lw;
- fbo->lh = lh;
+void gl_vao_uninit(struct gl_vao *vao)
+{
+ GL *gl = vao->gl;
+ if (!gl)
+ return;
- fbo->fbo = (struct fbodst){
- .tex = fbo->tex,
- };
+ if (gl->DeleteVertexArrays)
+ gl->DeleteVertexArrays(1, &vao->vao);
+ gl->DeleteBuffers(1, &vao->buffer);
- return true;
+ *vao = (struct gl_vao){0};
}
-void fbotex_uninit(struct fbotex *fbo)
+static void gl_vao_bind(struct gl_vao *vao)
{
- if (fbo->ra) {
- ra_tex_free(fbo->ra, &fbo->tex);
- *fbo = (struct fbotex) {0};
+ GL *gl = vao->gl;
+
+ if (gl->BindVertexArray) {
+ gl->BindVertexArray(vao->vao);
+ } else {
+ gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
+ gl_vao_enable_attribs(vao);
+ gl->BindBuffer(GL_ARRAY_BUFFER, 0);
}
}
-struct timer_pool {
- struct ra *ra;
- ra_timer *timer;
- bool running; // detect invalid usage
-
- uint64_t samples[VO_PERF_SAMPLE_COUNT];
- int sample_idx;
- int sample_count;
-
- uint64_t sum;
- uint64_t peak;
-};
-
-struct timer_pool *timer_pool_create(struct ra *ra)
+static void gl_vao_unbind(struct gl_vao *vao)
{
- if (!ra->fns->timer_create)
- return NULL;
-
- ra_timer *timer = ra->fns->timer_create(ra);
- if (!timer)
- return NULL;
+ GL *gl = vao->gl;
- struct timer_pool *pool = talloc(NULL, struct timer_pool);
- if (!pool) {
- ra->fns->timer_destroy(ra, timer);
- return NULL;
+ if (gl->BindVertexArray) {
+ gl->BindVertexArray(0);
+ } else {
+ for (int n = 0; n < vao->num_entries; n++)
+ gl->DisableVertexAttribArray(n);
}
-
- *pool = (struct timer_pool){ .ra = ra, .timer = timer };
- return pool;
}
-void timer_pool_destroy(struct timer_pool *pool)
+// Draw the vertex data (as described by the gl_vao_entry entries) in ptr
+// to the screen. num is the number of vertexes. prim is usually GL_TRIANGLES.
+// If ptr is NULL, then skip the upload, and use the data uploaded with the
+// previous call.
+void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num)
{
- if (!pool)
- return;
-
- pool->ra->fns->timer_destroy(pool->ra, pool->timer);
- talloc_free(pool);
-}
+ GL *gl = vao->gl;
-void timer_pool_start(struct timer_pool *pool)
-{
- if (!pool)
- return;
+ if (ptr) {
+ gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
+ gl->BufferData(GL_ARRAY_BUFFER, num * vao->stride, ptr, GL_STREAM_DRAW);
+ gl->BindBuffer(GL_ARRAY_BUFFER, 0);
+ }
- assert(!pool->running);
- pool->ra->fns->timer_start(pool->ra, pool->timer);
- pool->running = true;
-}
+ gl_vao_bind(vao);
-void timer_pool_stop(struct timer_pool *pool)
-{
- if (!pool)
- return;
+ gl->DrawArrays(prim, 0, num);
- assert(pool->running);
- uint64_t res = pool->ra->fns->timer_stop(pool->ra, pool->timer);
- pool->running = false;
-
- if (res) {
- // Input res into the buffer and grab the previous value
- uint64_t old = pool->samples[pool->sample_idx];
- pool->sample_count = MPMIN(pool->sample_count + 1, VO_PERF_SAMPLE_COUNT);
- pool->samples[pool->sample_idx++] = res;
- pool->sample_idx %= VO_PERF_SAMPLE_COUNT;
- pool->sum = pool->sum + res - old;
-
- // Update peak if necessary
- if (res >= pool->peak) {
- pool->peak = res;
- } else if (pool->peak == old) {
- // It's possible that the last peak was the value we just removed,
- // if so we need to scan for the new peak
- uint64_t peak = res;
- for (int i = 0; i < VO_PERF_SAMPLE_COUNT; i++)
- peak = MPMAX(peak, pool->samples[i]);
- pool->peak = peak;
- }
- }
+ gl_vao_unbind(vao);
}
-struct mp_pass_perf timer_pool_measure(struct timer_pool *pool)
+static void GLAPIENTRY gl_debug_cb(GLenum source, GLenum type, GLuint id,
+ GLenum severity, GLsizei length,
+ const GLchar *message, const void *userParam)
{
- if (!pool)
- return (struct mp_pass_perf){0};
-
- struct mp_pass_perf res = {
- .peak = pool->peak,
- .count = pool->sample_count,
- };
-
- int idx = pool->sample_idx - pool->sample_count + VO_PERF_SAMPLE_COUNT;
- for (int i = 0; i < res.count; i++) {
- idx %= VO_PERF_SAMPLE_COUNT;
- res.samples[i] = pool->samples[idx++];
+ // keep in mind that the debug callback can be asynchronous
+ struct mp_log *log = (void *)userParam;
+ int level = MSGL_ERR;
+ switch (severity) {
+ case GL_DEBUG_SEVERITY_NOTIFICATION:level = MSGL_V; break;
+ case GL_DEBUG_SEVERITY_LOW: level = MSGL_INFO; break;
+ case GL_DEBUG_SEVERITY_MEDIUM: level = MSGL_WARN; break;
+ case GL_DEBUG_SEVERITY_HIGH: level = MSGL_ERR; break;
}
-
- if (res.count > 0) {
- res.last = res.samples[res.count - 1];
- res.avg = pool->sum / res.count;
- }
-
- return res;
+ mp_msg(log, level, "GL: %s\n", message);
}
-void mp_log_source(struct mp_log *log, int lev, const char *src)
+void gl_set_debug_logger(GL *gl, struct mp_log *log)
{
- int line = 1;
- if (!src)
- return;
- while (*src) {
- const char *end = strchr(src, '\n');
- const char *next = end + 1;
- if (!end)
- next = end = src + strlen(src);
- mp_msg(log, lev, "[%3d] %.*s\n", line, (int)(end - src), src);
- line++;
- src = next;
- }
+ if (gl->DebugMessageCallback)
+ gl->DebugMessageCallback(log ? gl_debug_cb : NULL, log);
}
diff --git a/video/out/opengl/utils.h b/video/out/opengl/utils.h
index 7d00d26cf5..18cab476ed 100644
--- a/video/out/opengl/utils.h
+++ b/video/out/opengl/utils.h
@@ -1,121 +1,54 @@
-#pragma once
+/*
+ * This file is part of mpv.
+ * Parts based on MPlayer code by Reimar Döffinger.
+ *
+ * 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/>.
+ */
+
+#ifndef MP_GL_UTILS_
+#define MP_GL_UTILS_
-#include <stdbool.h>
#include <math.h>
-#include "video/out/vo.h"
-#include "ra.h"
+#include "video/out/gpu/utils.h"
+#include "common.h"
-// A 3x2 matrix, with the translation part separate.
-struct gl_transform {
- // row-major, e.g. in mathematical notation:
- // | m[0][0] m[0][1] |
- // | m[1][0] m[1][1] |
- float m[2][2];
- float t[2];
-};
-
-static const struct gl_transform identity_trans = {
- .m = {{1.0, 0.0}, {0.0, 1.0}},
- .t = {0.0, 0.0},
-};
-
-void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
- float y0, float y1);
-
-// This treats m as an affine transformation, in other words m[2][n] gets
-// added to the output.
-static inline void gl_transform_vec(struct gl_transform t, float *x, float *y)
-{
- float vx = *x, vy = *y;
- *x = vx * t.m[0][0] + vy * t.m[0][1] + t.t[0];
- *y = vx * t.m[1][0] + vy * t.m[1][1] + t.t[1];
-}
+struct mp_log;
-struct mp_rect_f {
- float x0, y0, x1, y1;
-};
-
-// Semantic equality (fuzzy comparison)
-static inline bool mp_rect_f_seq(struct mp_rect_f a, struct mp_rect_f b)
-{
- return fabs(a.x0 - b.x0) < 1e-6 && fabs(a.x1 - b.x1) < 1e-6 &&
- fabs(a.y0 - b.y0) < 1e-6 && fabs(a.y1 - b.y1) < 1e-6;
-}
-
-static inline void gl_transform_rect(struct gl_transform t, struct mp_rect_f *r)
-{
- gl_transform_vec(t, &r->x0, &r->y0);
- gl_transform_vec(t, &r->x1, &r->y1);
-}
+void gl_check_error(GL *gl, struct mp_log *log, const char *info);
-static inline bool gl_transform_eq(struct gl_transform a, struct gl_transform b)
-{
- for (int x = 0; x < 2; x++) {
- for (int y = 0; y < 2; y++) {
- if (a.m[x][y] != b.m[x][y])
- return false;
- }
- }
+void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type,
+ const void *dataptr, int stride,
+ int x, int y, int w, int h);
- return a.t[0] == b.t[0] && a.t[1] == b.t[1];
-}
+mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h);
-void gl_transform_trans(struct gl_transform t, struct gl_transform *x);
-
-struct fbodst {
- struct ra_tex *tex;
- bool flip; // mirror vertically
+struct gl_vao {
+ GL *gl;
+ GLuint vao; // the VAO object, or 0 if unsupported by driver
+ GLuint buffer; // GL_ARRAY_BUFFER used for the data
+ int stride; // size of each element (interleaved elements are assumed)
+ const struct ra_renderpass_input *entries;
+ int num_entries;
};
-void gl_transform_ortho_fbodst(struct gl_transform *t, struct fbodst fbo);
-
-// A pool of buffers, which can grow as needed
-struct ra_buf_pool {
- struct ra_buf_params current_params;
- struct ra_buf **buffers;
- int num_buffers;
- int index;
-};
-
-void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool);
-
-// Note: params->initial_data is *not* supported
-struct ra_buf *ra_buf_pool_get(struct ra *ra, struct ra_buf_pool *pool,
- const struct ra_buf_params *params);
-
-// Helper that wraps ra_tex_upload using texture upload buffers to ensure that
-// params->buf is always set. This is intended for RA-internal usage.
-bool ra_tex_upload_pbo(struct ra *ra, struct ra_buf_pool *pbo,
- const struct ra_tex_upload_params *params);
-
-// Layout rules for GLSL's packing modes
-struct ra_layout std140_layout(struct ra_renderpass_input *inp);
-struct ra_layout std430_layout(struct ra_renderpass_input *inp);
-
-struct fbotex {
- struct ra *ra;
- struct ra_tex *tex;
- int lw, lh; // logical (configured) size, <= than texture size
- struct fbodst fbo;
-};
-
-void fbotex_uninit(struct fbotex *fbo);
-bool fbotex_change(struct fbotex *fbo, struct ra *ra, struct mp_log *log,
- int w, int h, const struct ra_format *fmt, int flags);
-#define FBOTEX_FUZZY_W 1
-#define FBOTEX_FUZZY_H 2
-#define FBOTEX_FUZZY (FBOTEX_FUZZY_W | FBOTEX_FUZZY_H)
-
-// A wrapper around ra_timer that does result pooling, averaging etc.
-struct timer_pool;
+void gl_vao_init(struct gl_vao *vao, GL *gl, int stride,
+ const struct ra_renderpass_input *entries,
+ int num_entries);
+void gl_vao_uninit(struct gl_vao *vao);
+void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num);
-struct timer_pool *timer_pool_create(struct ra *ra);
-void timer_pool_destroy(struct timer_pool *pool);
-void timer_pool_start(struct timer_pool *pool);
-void timer_pool_stop(struct timer_pool *pool);
-struct mp_pass_perf timer_pool_measure(struct timer_pool *pool);
+void gl_set_debug_logger(GL *gl, struct mp_log *log);
-// print a multi line string with line numbers (e.g. for shader sources)
-// log, lev: module and log level, as in mp_msg()
-void mp_log_source(struct mp_log *log, int lev, const char *src);
+#endif
diff --git a/video/out/vo.c b/video/out/vo.c
index f9c5d04e24..a40360b188 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -50,6 +50,7 @@
extern const struct vo_driver video_out_x11;
extern const struct vo_driver video_out_vdpau;
extern const struct vo_driver video_out_xv;
+extern const struct vo_driver video_out_gpu;
extern const struct vo_driver video_out_opengl;
extern const struct vo_driver video_out_opengl_cb;
extern const struct vo_driver video_out_null;
@@ -69,8 +70,8 @@ const struct vo_driver *const video_out_drivers[] =
#if HAVE_RPI
&video_out_rpi,
#endif
-#if HAVE_GL
- &video_out_opengl,
+#if HAVE_GPU
+ &video_out_gpu,
#endif
#if HAVE_VDPAU
&video_out_vdpau,
@@ -107,6 +108,7 @@ const struct vo_driver *const video_out_drivers[] =
&video_out_lavc,
#endif
#if HAVE_GL
+ &video_out_opengl,
&video_out_opengl_cb,
#endif
NULL
diff --git a/video/out/vo_opengl.c b/video/out/vo_gpu.c
index 72691e56c2..5df9e06f47 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_gpu.c
@@ -38,56 +38,25 @@
#include "video/mp_image.h"
#include "sub/osd.h"
-#include "opengl/context.h"
-#include "opengl/utils.h"
-#include "opengl/hwdec.h"
-#include "opengl/osd.h"
-#include "filter_kernels.h"
-#include "video/hwdec.h"
-#include "opengl/video.h"
-#include "opengl/ra_gl.h"
-
-#define NUM_VSYNC_FENCES 10
-
-struct vo_opengl_opts {
- int use_glFinish;
- int waitvsync;
- int use_gl_debug;
- int allow_sw;
- int swap_interval;
- int vsync_fences;
- char *backend;
- int es;
- int pattern[2];
-};
+#include "gpu/context.h"
+#include "gpu/hwdec.h"
+#include "gpu/video.h"
-struct gl_priv {
+struct gpu_priv {
struct vo *vo;
struct mp_log *log;
- MPGLContext *glctx;
- GL *gl;
- struct ra *ra;
-
- struct vo_opengl_opts opts;
+ struct ra_ctx *ctx;
+ char *context_name;
+ char *context_type;
+ struct ra_ctx_opts opts;
struct gl_video *renderer;
-
struct ra_hwdec *hwdec;
int events;
-
- int frames_rendered;
- unsigned int prev_sgi_sync_count;
-
- // check-pattern sub-option; for testing/debugging
- int last_pattern;
- int matches, mismatches;
-
- GLsync vsync_fences[NUM_VSYNC_FENCES];
- int num_vsync_fences;
};
-static void resize(struct gl_priv *p)
+static void resize(struct gpu_priv *p)
{
struct vo *vo = p->vo;
@@ -102,88 +71,39 @@ static void resize(struct gl_priv *p)
vo->want_redraw = true;
}
-static void check_pattern(struct vo *vo, int item)
-{
- struct gl_priv *p = vo->priv;
- int expected = p->opts.pattern[p->last_pattern];
- if (item == expected) {
- p->last_pattern++;
- if (p->last_pattern >= 2)
- p->last_pattern = 0;
- p->matches++;
- } else {
- p->mismatches++;
- MP_WARN(vo, "wrong pattern, expected %d got %d (hit: %d, mis: %d)\n",
- expected, item, p->matches, p->mismatches);
- }
-}
-
static void draw_frame(struct vo *vo, struct vo_frame *frame)
{
- struct gl_priv *p = vo->priv;
- GL *gl = p->gl;
-
- mpgl_start_frame(p->glctx);
+ struct gpu_priv *p = vo->priv;
+ struct ra_swapchain *sw = p->ctx->swapchain;
- if (gl->FenceSync && p->num_vsync_fences < p->opts.vsync_fences) {
- GLsync fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);;
- if (fence)
- p->vsync_fences[p->num_vsync_fences++] = fence;
+ struct ra_tex *tex = sw->fns->start_frame(sw);
+ if (!tex) {
+ MP_ERR(vo, "Failed starting frame!\n");
+ return;
}
- struct fbodst target = {
- .tex = ra_create_wrapped_fb(p->ra, p->glctx->main_fb,
- vo->dwidth, vo->dheight),
- .flip = !p->glctx->flip_v,
+ struct fbodst dst = {
+ .tex = tex,
+ .flip = sw->flip_v,
};
- gl_video_render_frame(p->renderer, frame, target);
- ra_tex_free(p->ra, &target.tex);
- if (p->opts.use_glFinish)
- gl->Finish();
+ gl_video_render_frame(p->renderer, frame, dst);
+ if (!sw->fns->submit_frame(sw, frame)) {
+ MP_ERR(vo, "Failed presenting frame!\n");
+ return;
+ }
}
static void flip_page(struct vo *vo)
{
- struct gl_priv *p = vo->priv;
- GL *gl = p->gl;
-
- mpgl_swap_buffers(p->glctx);
-
- p->frames_rendered++;
- if (p->frames_rendered > 5 && !p->opts.use_gl_debug)
- ra_gl_set_debug(p->ra, false);
-
- if (p->opts.use_glFinish)
- gl->Finish();
-
- if (p->opts.waitvsync || p->opts.pattern[0]) {
- if (gl->GetVideoSync) {
- unsigned int n1 = 0, n2 = 0;
- gl->GetVideoSync(&n1);
- if (p->opts.waitvsync)
- gl->WaitVideoSync(2, (n1 + 1) % 2, &n2);
- int step = n1 - p->prev_sgi_sync_count;
- p->prev_sgi_sync_count = n1;
- MP_DBG(vo, "Flip counts: %u->%u, step=%d\n", n1, n2, step);
- if (p->opts.pattern[0])
- check_pattern(vo, step);
- } else {
- MP_WARN(vo, "GLX_SGI_video_sync not available, disabling.\n");
- p->opts.waitvsync = 0;
- p->opts.pattern[0] = 0;
- }
- }
- while (p->opts.vsync_fences > 0 && p->num_vsync_fences >= p->opts.vsync_fences) {
- gl->ClientWaitSync(p->vsync_fences[0], GL_SYNC_FLUSH_COMMANDS_BIT, 1e9);
- gl->DeleteSync(p->vsync_fences[0]);
- MP_TARRAY_REMOVE_AT(p->vsync_fences, p->num_vsync_fences, 0);
- }
+ struct gpu_priv *p = vo->priv;
+ struct ra_swapchain *sw = p->ctx->swapchain;
+ sw->fns->swap_buffers(sw);
}
static int query_format(struct vo *vo, int format)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
if (!gl_video_check_format(p->renderer, format))
return 0;
return 1;
@@ -191,13 +111,12 @@ static int query_format(struct vo *vo, int format)
static int reconfig(struct vo *vo, struct mp_image_params *params)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
- if (mpgl_reconfig_window(p->glctx) < 0)
+ if (!p->ctx->fns->reconfig(p->ctx))
return -1;
resize(p);
-
gl_video_config(p->renderer, params);
return 0;
@@ -205,12 +124,12 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
static void request_hwdec_api(struct vo *vo, void *api)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
if (p->hwdec)
return;
- p->hwdec = ra_hwdec_load_api(p->vo->log, p->ra, p->vo->global,
+ p->hwdec = ra_hwdec_load_api(p->vo->log, p->ctx->ra, p->vo->global,
vo->hwdec_devs, (intptr_t)api);
gl_video_set_hwdec(p->renderer, p->hwdec);
}
@@ -222,12 +141,12 @@ static void call_request_hwdec_api(void *ctx, enum hwdec_type type)
vo_control(ctx, VOCTRL_LOAD_HWDEC_API, (void *)(intptr_t)type);
}
-static void get_and_update_icc_profile(struct gl_priv *p)
+static void get_and_update_icc_profile(struct gpu_priv *p)
{
if (gl_video_icc_auto_enabled(p->renderer)) {
MP_VERBOSE(p, "Querying ICC profile...\n");
bstr icc = bstr0(NULL);
- int r = mpgl_control(p->glctx, &p->events, VOCTRL_GET_ICC_PROFILE, &icc);
+ int r = p->ctx->fns->control(p->ctx, &p->events, VOCTRL_GET_ICC_PROFILE, &icc);
if (r != VO_NOTAVAIL) {
if (r == VO_FALSE) {
@@ -241,10 +160,10 @@ static void get_and_update_icc_profile(struct gl_priv *p)
}
}
-static void get_and_update_ambient_lighting(struct gl_priv *p)
+static void get_and_update_ambient_lighting(struct gpu_priv *p)
{
int lux;
- int r = mpgl_control(p->glctx, &p->events, VOCTRL_GET_AMBIENT_LUX, &lux);
+ int r = p->ctx->fns->control(p->ctx, &p->events, VOCTRL_GET_AMBIENT_LUX, &lux);
if (r == VO_TRUE) {
gl_video_set_ambient_lux(p->renderer, lux);
}
@@ -256,7 +175,8 @@ static void get_and_update_ambient_lighting(struct gl_priv *p)
static int control(struct vo *vo, uint32_t request, void *data)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
+ struct ra_swapchain *sw = p->ctx->swapchain;
switch (request) {
case VOCTRL_SET_PANSCAN:
@@ -266,14 +186,13 @@ static int control(struct vo *vo, uint32_t request, void *data)
vo->want_redraw = true;
return VO_TRUE;
case VOCTRL_SCREENSHOT_WIN: {
- struct mp_image *screen = gl_read_fbo_contents(p->gl, p->glctx->main_fb,
- vo->dwidth, vo->dheight);
+ struct mp_image *screen = NULL;
+ if (sw->fns->screenshot)
+ screen = sw->fns->screenshot(sw);
if (!screen)
break; // redirect to backend
// set image parameters according to the display, if possible
screen->params.color = gl_video_get_output_colorspace(p->renderer);
- if (p->glctx->flip_v)
- mp_image_vflip(screen);
*(struct mp_image **)data = screen;
return true;
}
@@ -300,7 +219,7 @@ static int control(struct vo *vo, uint32_t request, void *data)
}
int events = 0;
- int r = mpgl_control(p->glctx, &events, request, data);
+ int r = p->ctx->fns->control(p->ctx, &events, request, data);
if (events & VO_EVENT_ICC_PROFILE_CHANGED) {
get_and_update_icc_profile(p);
vo->want_redraw = true;
@@ -322,16 +241,16 @@ static int control(struct vo *vo, uint32_t request, void *data)
static void wakeup(struct vo *vo)
{
- struct gl_priv *p = vo->priv;
- if (p->glctx && p->glctx->driver->wakeup)
- p->glctx->driver->wakeup(p->glctx);
+ struct gpu_priv *p = vo->priv;
+ if (p->ctx && p->ctx->fns->wakeup)
+ p->ctx->fns->wakeup(p->ctx);
}
static void wait_events(struct vo *vo, int64_t until_time_us)
{
- struct gl_priv *p = vo->priv;
- if (p->glctx->driver->wait_events) {
- p->glctx->driver->wait_events(p->glctx, until_time_us);
+ struct gpu_priv *p = vo->priv;
+ if (p->ctx && p->ctx->fns->wait_events) {
+ p->ctx->fns->wait_events(p->ctx, until_time_us);
} else {
vo_wait_default(vo, until_time_us);
}
@@ -340,14 +259,14 @@ static void wait_events(struct vo *vo, int64_t until_time_us)
static struct mp_image *get_image(struct vo *vo, int imgfmt, int w, int h,
int stride_align)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
return gl_video_get_image(p->renderer, imgfmt, w, h, stride_align);
}
static void uninit(struct vo *vo)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
gl_video_uninit(p->renderer);
ra_hwdec_uninit(p->hwdec);
@@ -355,53 +274,29 @@ static void uninit(struct vo *vo)
hwdec_devices_set_loader(vo->hwdec_devs, NULL, NULL);
hwdec_devices_destroy(vo->hwdec_devs);
}
- ra_free(&p->ra);
- mpgl_uninit(p->glctx);
+ ra_ctx_destroy(&p->ctx);
}
static int preinit(struct vo *vo)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
p->vo = vo;
p->log = vo->log;
- int vo_flags = 0;
-
int alpha_mode;
mp_read_option_raw(vo->global, "alpha", &m_option_type_choice, &alpha_mode);
- if (alpha_mode == 1)
- vo_flags |= VOFLAG_ALPHA;
-
- if (p->opts.use_gl_debug)
- vo_flags |= VOFLAG_GL_DEBUG;
-
- if (p->opts.es == 1)
- vo_flags |= VOFLAG_GLES;
- if (p->opts.es == 2)
- vo_flags |= VOFLAG_GLES | VOFLAG_GLES2;
- if (p->opts.es == -1)
- vo_flags |= VOFLAG_NO_GLES;
-
- if (p->opts.allow_sw)
- vo_flags |= VOFLAG_SW;
-
- p->glctx = mpgl_init(vo, p->opts.backend, vo_flags);
- if (!p->glctx)
- goto err_out;
- p->gl = p->glctx->gl;
-
- if (p->gl->SwapInterval) {
- p->gl->SwapInterval(p->opts.swap_interval);
- } else {
- MP_VERBOSE(vo, "swap_control extension missing.\n");
- }
+ struct ra_ctx_opts opts = p->opts;
+ opts.want_alpha = alpha_mode == 1;
- p->ra = ra_create_gl(p->gl, vo->log);
- if (!p->ra)
+ p->ctx = ra_ctx_create(vo, p->context_type, p->context_name, opts);
+ if (!p->ctx)
goto err_out;
+ assert(p->ctx->ra);
+ assert(p->ctx->swapchain);
+ struct ra_swapchain *sw = p->ctx->swapchain;
- p->renderer = gl_video_init(p->ra, vo->log, vo->global);
+ p->renderer = gl_video_init(p->ctx->ra, vo->log, vo->global);
gl_video_set_osd_source(p->renderer, vo->osd);
gl_video_configure_queue(p->renderer, vo);
@@ -411,13 +306,11 @@ static int preinit(struct vo *vo)
hwdec_devices_set_loader(vo->hwdec_devs, call_request_hwdec_api, vo);
- p->hwdec = ra_hwdec_load(p->vo->log, p->ra, vo->global,
+ p->hwdec = ra_hwdec_load(p->vo->log, p->ctx->ra, vo->global,
vo->hwdec_devs, vo->opts->gl_hwdec_interop);
gl_video_set_hwdec(p->renderer, p->hwdec);
- gl_check_error(p->gl, p->log, "before retrieving framebuffer depth");
- int fb_depth = gl_get_fb_depth(p->gl, p->glctx->main_fb);
- gl_check_error(p->gl, p->log, "retrieving framebuffer depth");
+ int fb_depth = sw->fns->color_depth ? sw->fns->color_depth(sw) : 0;
if (fb_depth)
MP_VERBOSE(p, "Reported display depth: %d\n", fb_depth);
gl_video_set_fb_depth(p->renderer, fb_depth);
@@ -429,13 +322,54 @@ err_out:
return -1;
}
-#define OPT_BASE_STRUCT struct gl_priv
+#define OPT_BASE_STRUCT struct gpu_priv
+static const m_option_t options[] = {
+ OPT_STRING_VALIDATE("gpu-context", context_name, 0, ra_ctx_validate_context),
+ OPT_STRING_VALIDATE("gpu-api", context_type, 0, ra_ctx_validate_api),
+ OPT_FLAG("gpu-debug", opts.debug, 0),
+ OPT_FLAG("gpu-sw", opts.allow_sw, 0),
+ OPT_INTRANGE("swapchain-depth", opts.swapchain_depth, 0, 1, 8),
+ {0}
+};
+
+static const struct gpu_priv defaults = { .opts = {
+ .swapchain_depth = 3,
+}};
+
+const struct vo_driver video_out_gpu = {
+ .description = "Shader-based GPU Renderer",
+ .name = "gpu",
+ .caps = VO_CAP_ROTATE90,
+ .preinit = preinit,
+ .query_format = query_format,
+ .reconfig = reconfig,
+ .control = control,
+ .get_image = get_image,
+ .draw_frame = draw_frame,
+ .flip_page = flip_page,
+ .wait_events = wait_events,
+ .wakeup = wakeup,
+ .uninit = uninit,
+ .priv_size = sizeof(struct gpu_priv),
+ .priv_defaults = &defaults,
+ .options = options,
+};
+
+static int preinit_opengl(struct vo *vo)
+{
+ MP_WARN(vo, "--vo=opengl was replaced by --vo=gpu --gpu-api=opengl, and will"
+ " be removed in the future!\n");
+
+ struct gpu_priv *p = vo->priv;
+ p->context_type = "opengl";
+ return preinit(vo);
+}
const struct vo_driver video_out_opengl = {
- .description = "Extended OpenGL Renderer",
+ .description = "Shader-based GPU Renderer",
.name = "opengl",
.caps = VO_CAP_ROTATE90,
- .preinit = preinit,
+ .preinit = preinit_opengl,
.query_format = query_format,
.reconfig = reconfig,
.control = control,
@@ -445,26 +379,7 @@ const struct vo_driver video_out_opengl = {
.wait_events = wait_events,
.wakeup = wakeup,
.uninit = uninit,
- .priv_size = sizeof(struct gl_priv),
- .options = (const m_option_t[]) {
- OPT_FLAG("opengl-glfinish", opts.use_glFinish, 0),
- OPT_FLAG("opengl-waitvsync", opts.waitvsync, 0),
- OPT_INT("opengl-swapinterval", opts.swap_interval, 0),
- OPT_FLAG("opengl-debug", opts.use_gl_debug, 0),
- OPT_STRING_VALIDATE("opengl-backend", opts.backend, 0,
- mpgl_validate_backend_opt),
- OPT_FLAG("opengl-sw", opts.allow_sw, 0),
- OPT_CHOICE("opengl-es", opts.es, 0, ({"no", -1}, {"auto", 0},
- {"yes", 1}, {"force2", 2})),
- OPT_INTPAIR("opengl-check-pattern", opts.pattern, 0),
- OPT_INTRANGE("opengl-vsync-fences", opts.vsync_fences, 0,
- 0, NUM_VSYNC_FENCES),
-
- {0}
- },
- .priv_defaults = &(const struct gl_priv){
- .opts = {
- .swap_interval = 1,
- },
- },
+ .priv_size = sizeof(struct gpu_priv),
+ .priv_defaults = &defaults,
+ .options = options,
};
diff --git a/video/out/vo_opengl_cb.c b/video/out/vo_opengl_cb.c
index ea6aaa9048..7e95e8bd31 100644
--- a/video/out/vo_opengl_cb.c
+++ b/video/out/vo_opengl_cb.c
@@ -24,9 +24,10 @@
#include "common/global.h"
#include "player/client.h"
+#include "gpu/video.h"
+#include "gpu/hwdec.h"
#include "opengl/common.h"
-#include "opengl/video.h"
-#include "opengl/hwdec.h"
+#include "opengl/context.h"
#include "opengl/ra_gl.h"
#include "libmpv/opengl_cb.h"
@@ -86,7 +87,7 @@ struct mpv_opengl_cb_context {
// application's OpenGL context is current - i.e. only while the
// host application is calling certain mpv_opengl_cb_* APIs.
GL *gl;
- struct ra *ra;
+ struct ra_ctx *ra_ctx;
struct gl_video *renderer;
struct ra_hwdec *hwdec;
struct m_config_cache *vo_opts_cache;
@@ -171,16 +172,36 @@ int mpv_opengl_cb_init_gl(struct mpv_opengl_cb_context *ctx, const char *exts,
return MPV_ERROR_UNSUPPORTED;
}
- ctx->ra = ra_create_gl(ctx->gl, ctx->log);
- if (!ctx->ra)
+ // initialize a blank ra_ctx to reuse ra_gl_ctx
+ ctx->ra_ctx = talloc_zero(ctx, struct ra_ctx);
+ ctx->ra_ctx->log = ctx->log;
+ ctx->ra_ctx->global = ctx->global;
+ ctx->ra_ctx->opts = (struct ra_ctx_opts) {
+ .probing = false,
+ .allow_sw = true,
+ };
+
+ static const struct ra_swapchain_fns empty_swapchain_fns = {0};
+ struct ra_gl_ctx_params gl_params = {
+ // vo_opengl_cb is essentially like a gigantic external swapchain where
+ // the user is in charge of presentation / swapping etc. But we don't
+ // actually need to provide any of these functions, since we can just
+ // not call them to begin with - so just set it to an empty object to
+ // signal to ra_gl_ctx that we don't care about its latency emulation
+ // functionality
+ .external_swapchain = &empty_swapchain_fns
+ };
+
+ ctx->gl->SwapInterval = NULL; // we shouldn't randomly change this, so lock it
+ if (!ra_gl_ctx_init(ctx->ra_ctx, ctx->gl, gl_params))
return MPV_ERROR_UNSUPPORTED;
- ctx->renderer = gl_video_init(ctx->ra, ctx->log, ctx->global);
+ ctx->renderer = gl_video_init(ctx->ra_ctx->ra, ctx->log, ctx->global);
m_config_cache_update(ctx->vo_opts_cache);
ctx->hwdec_devs = hwdec_devices_create();
- ctx->hwdec = ra_hwdec_load(ctx->log, ctx->ra, ctx->global,
+ ctx->hwdec = ra_hwdec_load(ctx->log, ctx->ra_ctx->ra, ctx->global,
ctx->hwdec_devs, ctx->vo_opts->gl_hwdec_interop);
gl_video_set_hwdec(ctx->renderer, ctx->hwdec);
@@ -221,7 +242,7 @@ int mpv_opengl_cb_uninit_gl(struct mpv_opengl_cb_context *ctx)
ctx->hwdec = NULL;
hwdec_devices_destroy(ctx->hwdec_devs);
ctx->hwdec_devs = NULL;
- ra_free(&ctx->ra);
+ ra_ctx_destroy(&ctx->ra_ctx);
talloc_free(ctx->gl);
ctx->gl = NULL;
return 0;
@@ -236,11 +257,6 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
return MPV_ERROR_UNSUPPORTED;
}
- struct fbodst target = {
- .tex = ra_create_wrapped_fb(ctx->ra, fbo, vp_w, abs(vp_h)),
- .flip = vp_h < 0,
- };
-
reset_gl_state(ctx->gl);
pthread_mutex_lock(&ctx->lock);
@@ -280,7 +296,7 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
mp_read_option_raw(ctx->global, "opengl-debug", &m_option_type_flag,
&debug);
ctx->gl->debug_context = debug;
- ra_gl_set_debug(ctx->ra, debug);
+ ra_gl_set_debug(ctx->ra_ctx->ra, debug);
if (gl_video_icc_auto_enabled(ctx->renderer))
MP_ERR(ctx, "icc-profile-auto is not available with opengl-cb\n");
}
@@ -316,7 +332,14 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
pthread_mutex_unlock(&ctx->lock);
MP_STATS(ctx, "glcb-render");
+ struct ra_swapchain *sw = ctx->ra_ctx->swapchain;
+ ra_gl_ctx_resize(sw, vp_w, abs(vp_h), fbo);
+ struct fbodst target = {
+ .tex = ra_gl_ctx_start_frame(sw),
+ .flip = vp_h < 0,
+ };
gl_video_render_frame(ctx->renderer, frame, target);
+ ra_gl_ctx_submit_frame(sw, frame);
reset_gl_state(ctx->gl);
@@ -328,8 +351,6 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
pthread_cond_wait(&ctx->wakeup, &ctx->lock);
pthread_mutex_unlock(&ctx->lock);
- ra_tex_free(ctx->ra, &target.tex);
-
return 0;
}
diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c
index 5b5d62c78f..8b819af163 100644
--- a/video/out/vo_rpi.c
+++ b/video/out/vo_rpi.c
@@ -44,7 +44,7 @@
#include "sub/osd.h"
#include "opengl/ra_gl.h"
-#include "opengl/video.h"
+#include "gpu/video.h"
struct mp_egl_rpi {
struct mp_log *log;