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/vo_gpu.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 385 insertions(+) create mode 100644 video/out/vo_gpu.c (limited to 'video/out/vo_gpu.c') diff --git a/video/out/vo_gpu.c b/video/out/vo_gpu.c new file mode 100644 index 0000000000..5df9e06f47 --- /dev/null +++ b/video/out/vo_gpu.c @@ -0,0 +1,385 @@ +/* + * Based on vo_gl.c by Reimar Doeffinger. + * + * 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 +#include +#include +#include +#include + +#include + +#include "config.h" + +#include "mpv_talloc.h" +#include "common/common.h" +#include "misc/bstr.h" +#include "common/msg.h" +#include "common/global.h" +#include "options/m_config.h" +#include "vo.h" +#include "video/mp_image.h" +#include "sub/osd.h" + +#include "gpu/context.h" +#include "gpu/hwdec.h" +#include "gpu/video.h" + +struct gpu_priv { + struct vo *vo; + struct mp_log *log; + 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; +}; + +static void resize(struct gpu_priv *p) +{ + struct vo *vo = p->vo; + + MP_VERBOSE(vo, "Resize: %dx%d\n", vo->dwidth, vo->dheight); + + struct mp_rect src, dst; + struct mp_osd_res osd; + vo_get_src_dst_rects(vo, &src, &dst, &osd); + + gl_video_resize(p->renderer, &src, &dst, &osd); + + vo->want_redraw = true; +} + +static void draw_frame(struct vo *vo, struct vo_frame *frame) +{ + struct gpu_priv *p = vo->priv; + struct ra_swapchain *sw = p->ctx->swapchain; + + struct ra_tex *tex = sw->fns->start_frame(sw); + if (!tex) { + MP_ERR(vo, "Failed starting frame!\n"); + return; + } + + struct fbodst dst = { + .tex = tex, + .flip = sw->flip_v, + }; + + 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 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 gpu_priv *p = vo->priv; + if (!gl_video_check_format(p->renderer, format)) + return 0; + return 1; +} + +static int reconfig(struct vo *vo, struct mp_image_params *params) +{ + struct gpu_priv *p = vo->priv; + + if (!p->ctx->fns->reconfig(p->ctx)) + return -1; + + resize(p); + gl_video_config(p->renderer, params); + + return 0; +} + +static void request_hwdec_api(struct vo *vo, void *api) +{ + struct gpu_priv *p = vo->priv; + + if (p->hwdec) + return; + + 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); +} + +static void call_request_hwdec_api(void *ctx, enum hwdec_type type) +{ + // Roundabout way to run hwdec loading on the VO thread. + // Redirects to request_hwdec_api(). + vo_control(ctx, VOCTRL_LOAD_HWDEC_API, (void *)(intptr_t)type); +} + +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 = p->ctx->fns->control(p->ctx, &p->events, VOCTRL_GET_ICC_PROFILE, &icc); + + if (r != VO_NOTAVAIL) { + if (r == VO_FALSE) { + MP_WARN(p, "Could not retrieve an ICC profile.\n"); + } else if (r == VO_NOTIMPL) { + MP_ERR(p, "icc-profile-auto not implemented on this platform.\n"); + } + + gl_video_set_icc_profile(p->renderer, icc); + } + } +} + +static void get_and_update_ambient_lighting(struct gpu_priv *p) +{ + int 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); + } + if (r != VO_TRUE && gl_video_gamma_auto_enabled(p->renderer)) { + MP_ERR(p, "gamma_auto option provided, but querying for ambient" + " lighting is not supported on this platform\n"); + } +} + +static int control(struct vo *vo, uint32_t request, void *data) +{ + struct gpu_priv *p = vo->priv; + struct ra_swapchain *sw = p->ctx->swapchain; + + switch (request) { + case VOCTRL_SET_PANSCAN: + resize(p); + return VO_TRUE; + case VOCTRL_SET_EQUALIZER: + vo->want_redraw = true; + return VO_TRUE; + case VOCTRL_SCREENSHOT_WIN: { + 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); + *(struct mp_image **)data = screen; + return true; + } + case VOCTRL_LOAD_HWDEC_API: + request_hwdec_api(vo, data); + return true; + case VOCTRL_UPDATE_RENDER_OPTS: { + gl_video_update_options(p->renderer); + get_and_update_icc_profile(p); + gl_video_configure_queue(p->renderer, p->vo); + p->vo->want_redraw = true; + return true; + } + case VOCTRL_RESET: + gl_video_reset(p->renderer); + return true; + case VOCTRL_PAUSE: + if (gl_video_showing_interpolated_frame(p->renderer)) + vo->want_redraw = true; + return true; + case VOCTRL_PERFORMANCE_DATA: + gl_video_perfdata(p->renderer, (struct voctrl_performance_data *)data); + return true; + } + + int events = 0; + 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; + } + if (events & VO_EVENT_AMBIENT_LIGHTING_CHANGED) { + get_and_update_ambient_lighting(p); + vo->want_redraw = true; + } + events |= p->events; + p->events = 0; + if (events & VO_EVENT_RESIZE) + resize(p); + if (events & VO_EVENT_EXPOSE) + vo->want_redraw = true; + vo_event(vo, events); + + return r; +} + +static void wakeup(struct vo *vo) +{ + 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 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); + } +} + +static struct mp_image *get_image(struct vo *vo, int imgfmt, int w, int h, + int stride_align) +{ + 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 gpu_priv *p = vo->priv; + + gl_video_uninit(p->renderer); + ra_hwdec_uninit(p->hwdec); + if (vo->hwdec_devs) { + hwdec_devices_set_loader(vo->hwdec_devs, NULL, NULL); + hwdec_devices_destroy(vo->hwdec_devs); + } + ra_ctx_destroy(&p->ctx); +} + +static int preinit(struct vo *vo) +{ + struct gpu_priv *p = vo->priv; + p->vo = vo; + p->log = vo->log; + + int alpha_mode; + mp_read_option_raw(vo->global, "alpha", &m_option_type_choice, &alpha_mode); + + struct ra_ctx_opts opts = p->opts; + opts.want_alpha = alpha_mode == 1; + + 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->ctx->ra, vo->log, vo->global); + gl_video_set_osd_source(p->renderer, vo->osd); + gl_video_configure_queue(p->renderer, vo); + + get_and_update_icc_profile(p); + + vo->hwdec_devs = hwdec_devices_create(); + + hwdec_devices_set_loader(vo->hwdec_devs, call_request_hwdec_api, vo); + + 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); + + 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); + + return 0; + +err_out: + uninit(vo); + return -1; +} + +#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 = "Shader-based GPU Renderer", + .name = "opengl", + .caps = VO_CAP_ROTATE90, + .preinit = preinit_opengl, + .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, +}; -- cgit v1.2.3