diff options
author | Niklas Haas <git@haasn.xyz> | 2017-09-14 08:04:55 +0200 |
---|---|---|
committer | Niklas Haas <git@haasn.xyz> | 2017-09-21 15:00:55 +0200 |
commit | 65979986a923a8f08019b257c3fe72cd5e8ecf68 (patch) | |
tree | b8f4b8c17d583594aef0ca509064f8b2ff7128d4 /video | |
parent | 20f958c9775652c3213588c2a0824f5353276adc (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.c | 186 | ||||
-rw-r--r-- | video/out/gpu/context.h | 95 | ||||
-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.c | 372 | ||||
-rw-r--r-- | video/out/gpu/utils.h | 120 | ||||
-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.h | 4 | ||||
-rw-r--r-- | video/out/opengl/context.c | 446 | ||||
-rw-r--r-- | video/out/opengl/context.h | 152 | ||||
-rw-r--r-- | video/out/opengl/context_cocoa.c | 2 | ||||
-rw-r--r-- | video/out/opengl/context_drm_egl.c | 194 | ||||
-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.c | 58 | ||||
-rw-r--r-- | video/out/opengl/context_rpi.c | 84 | ||||
-rw-r--r-- | video/out/opengl/context_vdpau.c | 202 | ||||
-rw-r--r-- | video/out/opengl/context_wayland.c | 74 | ||||
-rw-r--r-- | video/out/opengl/context_x11egl.c | 84 | ||||
-rw-r--r-- | video/out/opengl/egl_helpers.c | 114 | ||||
-rw-r--r-- | video/out/opengl/egl_helpers.h | 19 | ||||
-rw-r--r-- | video/out/opengl/formats.h | 1 | ||||
-rw-r--r-- | video/out/opengl/gl_utils.c | 291 | ||||
-rw-r--r-- | video/out/opengl/gl_utils.h | 56 | ||||
-rw-r--r-- | video/out/opengl/hwdec_cuda.c | 3 | ||||
-rw-r--r-- | video/out/opengl/hwdec_ios.m | 2 | ||||
-rw-r--r-- | video/out/opengl/hwdec_osx.c | 2 | ||||
-rw-r--r-- | video/out/opengl/hwdec_rpi.c | 2 | ||||
-rw-r--r-- | video/out/opengl/hwdec_vaegl.c | 4 | ||||
-rw-r--r-- | video/out/opengl/hwdec_vaglx.c | 5 | ||||
-rw-r--r-- | video/out/opengl/hwdec_vdpau.c | 2 | ||||
-rw-r--r-- | video/out/opengl/ra_gl.c | 7 | ||||
-rw-r--r-- | video/out/opengl/ra_gl.h | 3 | ||||
-rw-r--r-- | video/out/opengl/utils.c | 524 | ||||
-rw-r--r-- | video/out/opengl/utils.h | 151 | ||||
-rw-r--r-- | video/out/vo.c | 6 | ||||
-rw-r--r-- | video/out/vo_gpu.c (renamed from video/out/vo_opengl.c) | 301 | ||||
-rw-r--r-- | video/out/vo_opengl_cb.c | 53 | ||||
-rw-r--r-- | video/out/vo_rpi.c | 2 |
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, ¶ms); + + 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, ¶ms); - - 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; |