From 65979986a923a8f08019b257c3fe72cd5e8ecf68 Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Thu, 14 Sep 2017 08:04:55 +0200 Subject: 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. --- video/out/opengl/common.h | 4 +- video/out/opengl/context.c | 446 ++-- video/out/opengl/context.h | 152 +- video/out/opengl/context_cocoa.c | 2 +- video/out/opengl/context_drm_egl.c | 194 +- video/out/opengl/context_glx.c | 376 ++++ video/out/opengl/context_mali_fbdev.c | 58 +- video/out/opengl/context_rpi.c | 84 +- video/out/opengl/context_vdpau.c | 202 +- video/out/opengl/context_wayland.c | 74 +- video/out/opengl/context_x11.c | 358 ---- video/out/opengl/context_x11egl.c | 84 +- video/out/opengl/egl_helpers.c | 114 +- video/out/opengl/egl_helpers.h | 19 +- video/out/opengl/formats.h | 1 - video/out/opengl/gl_utils.c | 291 --- video/out/opengl/gl_utils.h | 56 - video/out/opengl/hwdec.c | 239 --- video/out/opengl/hwdec.h | 130 -- video/out/opengl/hwdec_cuda.c | 3 +- video/out/opengl/hwdec_ios.m | 2 +- video/out/opengl/hwdec_osx.c | 2 +- video/out/opengl/hwdec_rpi.c | 2 +- video/out/opengl/hwdec_vaegl.c | 4 +- video/out/opengl/hwdec_vaglx.c | 5 +- video/out/opengl/hwdec_vdpau.c | 2 +- video/out/opengl/lcms.c | 531 ----- video/out/opengl/lcms.h | 43 - video/out/opengl/osd.c | 367 ---- video/out/opengl/osd.h | 25 - video/out/opengl/ra.c | 327 --- video/out/opengl/ra.h | 491 ----- video/out/opengl/ra_gl.c | 7 - video/out/opengl/ra_gl.h | 3 +- video/out/opengl/shader_cache.c | 955 --------- video/out/opengl/shader_cache.h | 56 - video/out/opengl/user_shaders.c | 452 ---- video/out/opengl/user_shaders.h | 98 - video/out/opengl/utils.c | 524 ++--- video/out/opengl/utils.h | 151 +- video/out/opengl/video.c | 3813 --------------------------------- video/out/opengl/video.h | 195 -- video/out/opengl/video_shaders.c | 872 -------- video/out/opengl/video_shaders.h | 56 - 44 files changed, 1397 insertions(+), 10473 deletions(-) create mode 100644 video/out/opengl/context_glx.c delete mode 100644 video/out/opengl/context_x11.c delete mode 100644 video/out/opengl/gl_utils.c delete mode 100644 video/out/opengl/gl_utils.h delete mode 100644 video/out/opengl/hwdec.c delete mode 100644 video/out/opengl/hwdec.h delete mode 100644 video/out/opengl/lcms.c delete mode 100644 video/out/opengl/lcms.h delete mode 100644 video/out/opengl/osd.c delete mode 100644 video/out/opengl/osd.h delete mode 100644 video/out/opengl/ra.c delete mode 100644 video/out/opengl/ra.h delete mode 100644 video/out/opengl/shader_cache.c delete mode 100644 video/out/opengl/shader_cache.h delete mode 100644 video/out/opengl/user_shaders.c delete mode 100644 video/out/opengl/user_shaders.h delete mode 100644 video/out/opengl/video.c delete mode 100644 video/out/opengl/video.h delete mode 100644 video/out/opengl/video_shaders.c delete mode 100644 video/out/opengl/video_shaders.h (limited to 'video/out/opengl') 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 - * 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 . */ -#include -#include -#include -#include -#include -#include -#include - +#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 - * 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 . - */ - -#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 #include -#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_glx.c b/video/out/opengl/context_glx.c new file mode 100644 index 0000000000..462f2cf592 --- /dev/null +++ b/video/out/opengl/context_glx.c @@ -0,0 +1,376 @@ +/* + * 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 . + */ + +#include +#include + +// FreeBSD 10.0-CURRENT lacks the GLX_ARB_create_context extension completely +#ifndef GLX_CONTEXT_MAJOR_VERSION_ARB +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +#ifndef __APPLE__ +// These are respectively 0x00000001 and 0x00000002 on OSX +#define GLX_CONTEXT_DEBUG_BIT_ARB 0x0001 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 +#endif +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#endif +// GLX_EXT_create_context_es2_profile +#ifndef GLX_CONTEXT_ES2_PROFILE_BIT_EXT +#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#endif + +#include "video/out/x11_common.h" +#include "context.h" +#include "utils.h" + +struct priv { + GL gl; + XVisualInfo *vinfo; + GLXContext context; + GLXFBConfig fbc; +}; + +static void glx_uninit(struct ra_ctx *ctx) +{ + 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, p->context); + } + + vo_x11_uninit(ctx->vo); +} + +static bool create_context_x11_old(struct ra_ctx *ctx, GL *gl) +{ + struct priv *p = ctx->priv; + Display *display = ctx->vo->x11->display; + struct vo *vo = ctx->vo; + + if (p->context) + return true; + + if (!p->vinfo) { + MP_FATAL(vo, "Can't create a legacy GLX context without X visual\n"); + return false; + } + + GLXContext new_context = glXCreateContext(display, p->vinfo, NULL, True); + if (!new_context) { + MP_FATAL(vo, "Could not create GLX context!\n"); + return false; + } + + if (!glXMakeCurrent(display, ctx->vo->x11->window, new_context)) { + MP_FATAL(vo, "Could not set GLX context!\n"); + glXDestroyContext(display, new_context); + return false; + } + + const char *glxstr = glXQueryExtensionsString(display, ctx->vo->x11->screen); + + mpgl_load_functions(gl, (void *)glXGetProcAddressARB, glxstr, vo->log); + + p->context = new_context; + + return true; +} + +typedef GLXContext (*glXCreateContextAttribsARBProc) + (Display*, GLXFBConfig, GLXContext, Bool, const int*); + +static bool create_context_x11_gl3(struct ra_ctx *ctx, GL *gl, int gl_version, + bool es) +{ + struct priv *p = ctx->priv; + struct vo *vo = ctx->vo; + + if (p->context) + return true; + + if (!ra_gl_ctx_test_version(ctx, gl_version, es)) + return false; + + glXCreateContextAttribsARBProc glXCreateContextAttribsARB = + (glXCreateContextAttribsARBProc) + glXGetProcAddressARB((const GLubyte *)"glXCreateContextAttribsARB"); + + const char *glxstr = + glXQueryExtensionsString(vo->x11->display, vo->x11->screen); + bool have_ctx_ext = glxstr && !!strstr(glxstr, "GLX_ARB_create_context"); + + if (!(have_ctx_ext && glXCreateContextAttribsARB)) { + return false; + } + + int ctx_flags = ctx->opts.debug ? GLX_CONTEXT_DEBUG_BIT_ARB : 0; + int profile_mask = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + + if (es) { + profile_mask = GLX_CONTEXT_ES2_PROFILE_BIT_EXT; + if (!(glxstr && strstr(glxstr, "GLX_EXT_create_context_es2_profile"))) + return false; + } + + int context_attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, MPGL_VER_GET_MAJOR(gl_version), + GLX_CONTEXT_MINOR_VERSION_ARB, MPGL_VER_GET_MINOR(gl_version), + GLX_CONTEXT_PROFILE_MASK_ARB, profile_mask, + GLX_CONTEXT_FLAGS_ARB, ctx_flags, + None + }; + vo_x11_silence_xlib(1); + GLXContext context = glXCreateContextAttribsARB(vo->x11->display, + p->fbc, 0, True, + context_attribs); + vo_x11_silence_xlib(-1); + if (!context) + return false; + + // set context + if (!glXMakeCurrent(vo->x11->display, vo->x11->window, context)) { + MP_FATAL(vo, "Could not set GLX context!\n"); + glXDestroyContext(vo->x11->display, context); + return false; + } + + p->context = context; + + mpgl_load_functions(gl, (void *)glXGetProcAddressARB, glxstr, vo->log); + + return true; +} + +// The GL3/FBC initialization code roughly follows/copies from: +// http://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX) +// but also uses some of the old code. + +static GLXFBConfig select_fb_config(struct vo *vo, const int *attribs, bool alpha) +{ + int fbcount; + GLXFBConfig *fbc = glXChooseFBConfig(vo->x11->display, vo->x11->screen, + attribs, &fbcount); + if (!fbc) + return NULL; + + // The list in fbc is sorted (so that the first element is the best). + GLXFBConfig fbconfig = fbcount > 0 ? fbc[0] : NULL; + + if (alpha) { + for (int n = 0; n < fbcount; n++) { + XVisualInfo *v = glXGetVisualFromFBConfig(vo->x11->display, fbc[n]); + if (v) { + bool is_rgba = vo_x11_is_rgba_visual(v); + XFree(v); + if (is_rgba) { + fbconfig = fbc[n]; + break; + } + } + } + } + + XFree(fbc); + + return fbconfig; +} + +static void set_glx_attrib(int *attribs, int name, int value) +{ + for (int n = 0; attribs[n * 2 + 0] != None; n++) { + if (attribs[n * 2 + 0] == name) { + attribs[n * 2 + 1] = value; + break; + } + } +} + +static 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; + GL *gl = &p->gl; + + if (!vo_x11_init(ctx->vo)) + goto uninit; + + int glx_major, glx_minor; + + if (!glXQueryVersion(vo->x11->display, &glx_major, &glx_minor)) { + 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(ctx, "GLX version older than 1.3.\n"); + goto uninit; + } + + int glx_attribs[] = { + GLX_X_RENDERABLE, True, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_ALPHA_SIZE, 0, + GLX_DOUBLEBUFFER, True, + None + }; + GLXFBConfig fbc = NULL; + if (ctx->opts.want_alpha) { + set_glx_attrib(glx_attribs, GLX_ALPHA_SIZE, 1); + fbc = select_fb_config(vo, glx_attribs, true); + if (!fbc) + set_glx_attrib(glx_attribs, GLX_ALPHA_SIZE, 0); + } + if (!fbc) + fbc = select_fb_config(vo, glx_attribs, false); + if (!fbc) { + 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(ctx, "GLX chose FB config with ID 0x%x\n", fbid); + + 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(ctx, "Selected GLX FB config has no associated X visual\n"); + } + + if (!vo_x11_create_vo_window(vo, p->vinfo, "gl")) + goto uninit; + + bool success = false; + 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 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; + + 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 false; +} + +static bool glx_init_probe(struct ra_ctx *ctx) +{ + 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 true; +} + +static void resize(struct ra_ctx *ctx) +{ + ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0); +} + +static bool glx_reconfig(struct ra_ctx *ctx) +{ + vo_x11_config_vo_window(ctx->vo); + resize(ctx); + return true; +} + +static int glx_control(struct ra_ctx *ctx, int *events, int request, void *arg) +{ + int ret = vo_x11_control(ctx->vo, events, request, arg); + if (*events & VO_EVENT_RESIZE) + resize(ctx); + return ret; +} + +static void glx_wakeup(struct ra_ctx *ctx) +{ + vo_x11_wakeup(ctx->vo); +} + +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 ra_ctx_fns ra_ctx_glx = { + .type = "opengl", + .name = "x11", + .reconfig = glx_reconfig, + .control = glx_control, + .wakeup = glx_wakeup, + .wait_events = glx_wait_events, + .init = glx_init, + .uninit = glx_uninit, +}; + +const struct ra_ctx_fns ra_ctx_glx_probe = { + .type = "opengl", + .name = "x11probe", + .reconfig = glx_reconfig, + .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 cr