diff options
Diffstat (limited to 'video/filter')
-rw-r--r-- | video/filter/refqueue.c | 27 | ||||
-rw-r--r-- | video/filter/refqueue.h | 5 | ||||
-rw-r--r-- | video/filter/vf_d3d11vpp.c | 49 | ||||
-rw-r--r-- | video/filter/vf_fingerprint.c | 17 | ||||
-rw-r--r-- | video/filter/vf_format.c | 124 | ||||
-rw-r--r-- | video/filter/vf_gpu.c | 130 | ||||
-rw-r--r-- | video/filter/vf_gpu.h | 35 | ||||
-rw-r--r-- | video/filter/vf_gpu_egl.c | 107 | ||||
-rw-r--r-- | video/filter/vf_gpu_vulkan.c | 115 | ||||
-rw-r--r-- | video/filter/vf_sub.c | 21 | ||||
-rw-r--r-- | video/filter/vf_vapoursynth.c | 143 | ||||
-rw-r--r-- | video/filter/vf_vavpp.c | 52 | ||||
-rw-r--r-- | video/filter/vf_vdpaupp.c | 51 |
13 files changed, 604 insertions, 272 deletions
diff --git a/video/filter/refqueue.c b/video/filter/refqueue.c index 964fa29c08..f5124087c6 100644 --- a/video/filter/refqueue.c +++ b/video/filter/refqueue.c @@ -39,6 +39,7 @@ struct mp_refqueue { int needed_past_frames; int needed_future_frames; int flags; + int field_parity; bool second_field; // current frame has to output a second field yet bool eof; @@ -68,8 +69,7 @@ struct mp_refqueue *mp_refqueue_alloc(struct mp_filter *f) q->filter = f; q->conv = mp_autoconvert_create(f); - if (!q->conv) - abort(); + MP_HANDLE_OOM(q->conv); q->in = q->conv->f->pins[1]; mp_pin_connect(q->conv->f->pins[0], f->ppins[0]); @@ -98,6 +98,11 @@ void mp_refqueue_set_mode(struct mp_refqueue *q, int flags) q->flags = flags; } +void mp_refqueue_set_parity(struct mp_refqueue *q, int parity) +{ + q->field_parity = parity; +} + // Whether the current frame should be deinterlaced. bool mp_refqueue_should_deint(struct mp_refqueue *q) { @@ -114,8 +119,14 @@ bool mp_refqueue_is_top_field(struct mp_refqueue *q) { if (!mp_refqueue_has_output(q)) return false; - - return !!(q->queue[q->pos]->fields & MP_IMGFIELD_TOP_FIRST) ^ q->second_field; + + bool tff = q->field_parity == MP_FIELD_PARITY_TFF; + bool bff = q->field_parity == MP_FIELD_PARITY_BFF; + bool ret = (!!(q->queue[q->pos]->fields & MP_IMGFIELD_TOP_FIRST) ^ q->second_field + && !tff && !bff); // Default parity + ret = ret || (tff && !q->second_field); // Check if top field is forced + ret = ret || (bff && q->second_field); // Check if bottom field is forced + return ret; } // Whether top-field-first mode is enabled. @@ -124,7 +135,9 @@ bool mp_refqueue_top_field_first(struct mp_refqueue *q) if (!mp_refqueue_has_output(q)) return false; - return q->queue[q->pos]->fields & MP_IMGFIELD_TOP_FIRST; + bool tff = q->field_parity == MP_FIELD_PARITY_TFF; + bool bff = q->field_parity == MP_FIELD_PARITY_BFF; + return ((q->queue[q->pos]->fields & MP_IMGFIELD_TOP_FIRST) || tff) && !bff; } // Discard all state. @@ -267,8 +280,6 @@ struct mp_image *mp_refqueue_execute_reinit(struct mp_refqueue *q) mp_refqueue_flush(q); q->in_format = mp_image_new_ref(cur); - if (!q->in_format) - abort(); mp_image_unref_data(q->in_format); mp_refqueue_add_input(q, cur); @@ -322,7 +333,7 @@ bool mp_refqueue_can_output(struct mp_refqueue *q) if (!q->in_format || !!q->in_format->hwctx != !!img->hwctx || (img->hwctx && img->hwctx->data != q->in_format->hwctx->data) || - !mp_image_params_equal(&q->in_format->params, &img->params)) + !mp_image_params_static_equal(&q->in_format->params, &img->params)) { q->next = img; q->eof = true; diff --git a/video/filter/refqueue.h b/video/filter/refqueue.h index 0a8ace0031..d14058d609 100644 --- a/video/filter/refqueue.h +++ b/video/filter/refqueue.h @@ -29,7 +29,12 @@ enum { MP_MODE_INTERLACED_ONLY = (1 << 2), // only deinterlace marked frames }; +#define MP_FIELD_PARITY_AUTO -1 +#define MP_FIELD_PARITY_TFF 0 +#define MP_FIELD_PARITY_BFF 1 + void mp_refqueue_set_mode(struct mp_refqueue *q, int flags); +void mp_refqueue_set_parity(struct mp_refqueue *q, int parity); bool mp_refqueue_should_deint(struct mp_refqueue *q); bool mp_refqueue_is_top_field(struct mp_refqueue *q); bool mp_refqueue_top_field_first(struct mp_refqueue *q); diff --git a/video/filter/vf_d3d11vpp.c b/video/filter/vf_d3d11vpp.c index 504b881f3f..cedb91d857 100644 --- a/video/filter/vf_d3d11vpp.c +++ b/video/filter/vf_d3d11vpp.c @@ -43,9 +43,10 @@ #define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_FRAME_RATE_CONVERSION 0x20 struct opts { - int deint_enabled; - int interlaced_only; + bool deint_enabled; + bool interlaced_only; int mode; + int field_parity; }; struct priv { @@ -102,8 +103,7 @@ static struct mp_image *alloc_pool(void *pctx, int fmt, int w, int h) return NULL; struct mp_image *mpi = mp_image_new_custom_ref(NULL, texture, release_tex); - if (!mpi) - abort(); + MP_HANDLE_OOM(mpi); mp_image_setfmt(mpi, IMGFMT_D3D11); mp_image_set_size(mpi, w, h); @@ -181,7 +181,7 @@ static int recreate_video_proc(struct mp_filter *vf) rindex = 0; } - // TOOD: so, how do we select which rate conversion mode the processor uses? + // TODO: so, how do we select which rate conversion mode the processor uses? hr = ID3D11VideoDevice_CreateVideoProcessor(p->video_dev, p->vp_enum, rindex, &p->video_proc); @@ -211,8 +211,8 @@ static int recreate_video_proc(struct mp_filter *vf) FALSE, 0); D3D11_VIDEO_PROCESSOR_COLOR_SPACE csp = { - .YCbCr_Matrix = p->params.color.space != MP_CSP_BT_601, - .Nominal_Range = p->params.color.levels == MP_CSP_LEVELS_TV ? 1 : 2, + .YCbCr_Matrix = p->params.repr.sys != PL_COLOR_SYSTEM_BT_601, + .Nominal_Range = p->params.repr.levels == PL_COLOR_LEVELS_LIMITED ? 1 : 2, }; ID3D11VideoContext_VideoProcessorSetStreamColorSpace(p->video_ctx, p->video_proc, @@ -428,10 +428,14 @@ static struct mp_filter *vf_d3d11vpp_create(struct mp_filter *parent, if (!info || !info->hwdec_devs) goto fail; - hwdec_devices_request_all(info->hwdec_devs); + struct hwdec_imgfmt_request params = { + .imgfmt = IMGFMT_D3D11, + .probing = false, + }; + hwdec_devices_request_for_img_fmt(info->hwdec_devs, ¶ms); struct mp_hwdec_ctx *hwctx = - hwdec_devices_get_by_lavc(info->hwdec_devs, AV_HWDEVICE_TYPE_D3D11VA); + hwdec_devices_get_by_imgfmt(info->hwdec_devs, IMGFMT_D3D11); if (!hwctx || !hwctx->av_device_ref) goto fail; AVHWDeviceContext *avhwctx = (void *)hwctx->av_device_ref->data; @@ -466,6 +470,7 @@ static struct mp_filter *vf_d3d11vpp_create(struct mp_filter *parent, (p->opts->deint_enabled ? MP_MODE_DEINT : 0) | MP_MODE_OUTPUT_FIELDS | (p->opts->interlaced_only ? MP_MODE_INTERLACED_ONLY : 0)); + mp_refqueue_set_parity(p->queue, p->opts->field_parity); return f; @@ -476,15 +481,19 @@ fail: #define OPT_BASE_STRUCT struct opts static const m_option_t vf_opts_fields[] = { - OPT_FLAG("deint", deint_enabled, 0), - OPT_FLAG("interlaced-only", interlaced_only, 0), - OPT_CHOICE("mode", mode, 0, - ({"blend", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BLEND}, - {"bob", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB}, - {"adaptive", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_ADAPTIVE}, - {"mocomp", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_MOTION_COMPENSATION}, - {"ivctc", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_INVERSE_TELECINE}, - {"none", 0})), + {"deint", OPT_BOOL(deint_enabled)}, + {"interlaced-only", OPT_BOOL(interlaced_only)}, + {"mode", OPT_CHOICE(mode, + {"blend", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BLEND}, + {"bob", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB}, + {"adaptive", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_ADAPTIVE}, + {"mocomp", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_MOTION_COMPENSATION}, + {"ivctc", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_INVERSE_TELECINE}, + {"none", 0})}, + {"parity", OPT_CHOICE(field_parity, + {"tff", MP_FIELD_PARITY_TFF}, + {"bff", MP_FIELD_PARITY_BFF}, + {"auto", MP_FIELD_PARITY_AUTO})}, {0} }; @@ -494,9 +503,9 @@ const struct mp_user_filter_entry vf_d3d11vpp = { .name = "d3d11vpp", .priv_size = sizeof(OPT_BASE_STRUCT), .priv_defaults = &(const OPT_BASE_STRUCT) { - .deint_enabled = 1, - .interlaced_only = 0, + .deint_enabled = true, .mode = D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB, + .field_parity = MP_FIELD_PARITY_AUTO, }, .options = vf_opts_fields, }, diff --git a/video/filter/vf_fingerprint.c b/video/filter/vf_fingerprint.c index 471d995f4f..ea2c31478e 100644 --- a/video/filter/vf_fingerprint.c +++ b/video/filter/vf_fingerprint.c @@ -33,8 +33,8 @@ struct f_opts { int type; - int clear; - int print; + bool clear; + bool print; }; const struct m_opt_choice_alternatives type_names[] = { @@ -45,15 +45,15 @@ const struct m_opt_choice_alternatives type_names[] = { #define OPT_BASE_STRUCT struct f_opts static const struct m_option f_opts_list[] = { - OPT_CHOICE_C("type", type, 0, type_names), - OPT_FLAG("clear-on-query", clear, 0), - OPT_FLAG("print", print, 0), + {"type", OPT_CHOICE_C(type, type_names)}, + {"clear-on-query", OPT_BOOL(clear)}, + {"print", OPT_BOOL(print)}, {0} }; static const struct f_opts f_opts_def = { .type = 16, - .clear = 1, + .clear = true, }; struct print_entry { @@ -102,9 +102,10 @@ static void f_process(struct mp_filter *f) // Try to achieve minimum conversion, even if it makes the fingerprints less // "portable" across source video. + p->scaled->params.repr = mpi->params.repr; p->scaled->params.color = mpi->params.color; // Make output always full range; no reason to lose precision. - p->scaled->params.color.levels = MP_CSP_LEVELS_PC; + p->scaled->params.repr.levels = PL_COLOR_LEVELS_FULL; if (!mp_zimg_convert(p->zimg, p->scaled, mpi)) { if (!p->fallback_warning) { @@ -212,7 +213,7 @@ static struct mp_filter *f_create(struct mp_filter *parent, void *options) .scaler_chroma_params = {NAN, NAN}, .scaler_chroma = ZIMG_RESIZE_BILINEAR, .dither = ZIMG_DITHER_NONE, - .fast = 1, + .fast = true, }; return f; } diff --git a/video/filter/vf_format.c b/video/filter/vf_format.c index 6681467fc3..f226bf22a7 100644 --- a/video/filter/vf_format.c +++ b/video/filter/vf_format.c @@ -22,6 +22,9 @@ #include <math.h> #include <libavutil/rational.h> +#include <libavutil/buffer.h> +#include <libavutil/frame.h> +#include <libplacebo/utils/libav.h> #include "common/msg.h" #include "common/common.h" @@ -50,42 +53,49 @@ struct vf_format_opts { int chroma_location; int stereo_in; int rotate; + int alpha; int w, h; int dw, dh; double dar; - int convert; + bool convert; + int force_scaler; + bool dovi; + bool hdr10plus; + bool film_grain; }; static void set_params(struct vf_format_opts *p, struct mp_image_params *out, bool set_size) { if (p->colormatrix) - out->color.space = p->colormatrix; + out->repr.sys = p->colormatrix; if (p->colorlevels) - out->color.levels = p->colorlevels; + out->repr.levels = p->colorlevels; if (p->primaries) out->color.primaries = p->primaries; if (p->gamma) { - enum mp_csp_trc in_gamma = p->gamma; - out->color.gamma = p->gamma; - if (in_gamma != out->color.gamma) { + enum pl_color_transfer in_gamma = p->gamma; + out->color.transfer = p->gamma; + if (in_gamma != out->color.transfer) { // When changing the gamma function explicitly, also reset stuff // related to the gamma function since that information will almost // surely be false now and have to be re-inferred - out->color.sig_peak = 0.0; - out->color.light = MP_CSP_LIGHT_AUTO; + out->color.hdr = (struct pl_hdr_metadata){0}; + out->light = MP_CSP_LIGHT_AUTO; } } if (p->sig_peak) - out->color.sig_peak = p->sig_peak; + out->color.hdr = (struct pl_hdr_metadata){ .max_luma = p->sig_peak * MP_REF_WHITE }; if (p->light) - out->color.light = p->light; + out->light = p->light; if (p->chroma_location) out->chroma_location = p->chroma_location; if (p->stereo_in) out->stereo3d = p->stereo_in; if (p->rotate >= 0) out->rotate = p->rotate; + if (p->alpha) + out->repr.alpha = p->alpha; if (p->w > 0 && set_size) out->w = p->w; @@ -102,6 +112,16 @@ static void set_params(struct vf_format_opts *p, struct mp_image_params *out, mp_image_params_set_dsize(out, dsize.num, dsize.den); } +static inline void *get_side_data(const struct mp_image *mpi, + enum AVFrameSideDataType type) +{ + for (int i = 0; i < mpi->num_ff_side_data; i++) { + if (mpi->ff_side_data[i].type == type) + return (void *)mpi->ff_side_data[i].buf->data; + } + return NULL; +} + static void vf_format_process(struct mp_filter *f) { struct priv *priv = f->priv; @@ -115,15 +135,15 @@ static void vf_format_process(struct mp_filter *f) int outfmt = priv->opts->fmt; // If we convert from RGB to YUV, default to limited range. - if (mp_imgfmt_get_forced_csp(img->imgfmt) == MP_CSP_RGB && - outfmt && mp_imgfmt_get_forced_csp(outfmt) == MP_CSP_AUTO) + if (mp_imgfmt_get_forced_csp(img->imgfmt) == PL_COLOR_SYSTEM_RGB && + outfmt && mp_imgfmt_get_forced_csp(outfmt) == PL_COLOR_SYSTEM_UNKNOWN) { - par.color.levels = MP_CSP_LEVELS_TV; + par.repr.levels = PL_COLOR_LEVELS_LIMITED; } set_params(priv->opts, &par, true); - if (par.imgfmt != outfmt) { + if (outfmt && par.imgfmt != outfmt) { par.imgfmt = outfmt; par.hw_subfmt = 0; } @@ -137,14 +157,39 @@ static void vf_format_process(struct mp_filter *f) if (mp_pin_can_transfer_data(f->ppins[1], priv->conv->f->pins[1])) { struct mp_frame frame = mp_pin_out_read(priv->conv->f->pins[1]); + struct mp_image *img = frame.data; - if (!priv->opts->convert && frame.type == MP_FRAME_VIDEO) { - struct mp_image *img = frame.data; + if (frame.type != MP_FRAME_VIDEO) + goto write_out; + if (!priv->opts->convert) { set_params(priv->opts, &img->params, false); mp_image_params_guess_csp(&img->params); } + if (!priv->opts->dovi) { + if (img->params.repr.sys == PL_COLOR_SYSTEM_DOLBYVISION) + img->params.repr.sys = PL_COLOR_SYSTEM_BT_2020_NC; + // Map again to strip any DV metadata set to common fields. + img->params.color.hdr = (struct pl_hdr_metadata){0}; + pl_map_hdr_metadata(&img->params.color.hdr, &(struct pl_av_hdr_metadata) { + .mdm = get_side_data(img, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA), + .clm = get_side_data(img, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL), + .dhp = get_side_data(img, AV_FRAME_DATA_DYNAMIC_HDR_PLUS), + }); + } + + if (!priv->opts->hdr10plus) { + memset(img->params.color.hdr.scene_max, 0, + sizeof(img->params.color.hdr.scene_max)); + img->params.color.hdr.scene_avg = 0; + img->params.color.hdr.ootf = (struct pl_hdr_bezier){0}; + } + + if (!priv->opts->film_grain) + av_buffer_unref(&img->film_grain); + +write_out: mp_pin_in_write(f->ppins[1], frame); } } @@ -175,6 +220,8 @@ static struct mp_filter *vf_format_create(struct mp_filter *parent, void *option return NULL; } + priv->conv->force_scaler = priv->opts->force_scaler; + if (priv->opts->fmt) mp_autoconvert_add_imgfmt(priv->conv, priv->opts->fmt, 0); @@ -183,24 +230,30 @@ static struct mp_filter *vf_format_create(struct mp_filter *parent, void *option #define OPT_BASE_STRUCT struct vf_format_opts static const m_option_t vf_opts_fields[] = { - OPT_IMAGEFORMAT("fmt", fmt, 0), - OPT_CHOICE_C("colormatrix", colormatrix, 0, mp_csp_names), - OPT_CHOICE_C("colorlevels", colorlevels, 0, mp_csp_levels_names), - OPT_CHOICE_C("primaries", primaries, 0, mp_csp_prim_names), - OPT_CHOICE_C("gamma", gamma, 0, mp_csp_trc_names), - OPT_FLOAT("sig-peak", sig_peak, 0), - OPT_CHOICE_C("light", light, 0, mp_csp_light_names), - OPT_CHOICE_C("chroma-location", chroma_location, 0, mp_chroma_names), - OPT_CHOICE_C("stereo-in", stereo_in, 0, mp_stereo3d_names), - OPT_INTRANGE("rotate", rotate, 0, -1, 359), - OPT_INT("w", w, 0), - OPT_INT("h", h, 0), - OPT_INT("dw", dw, 0), - OPT_INT("dh", dh, 0), - OPT_DOUBLE("dar", dar, 0), - OPT_FLAG("convert", convert, 0), - OPT_REMOVED("outputlevels", "use the --video-output-levels global option"), - OPT_REMOVED("peak", "use sig-peak instead (changed value scale!)"), + {"fmt", OPT_IMAGEFORMAT(fmt)}, + {"colormatrix", OPT_CHOICE_C(colormatrix, pl_csp_names)}, + {"colorlevels", OPT_CHOICE_C(colorlevels, pl_csp_levels_names)}, + {"primaries", OPT_CHOICE_C(primaries, pl_csp_prim_names)}, + {"gamma", OPT_CHOICE_C(gamma, pl_csp_trc_names)}, + {"sig-peak", OPT_FLOAT(sig_peak)}, + {"light", OPT_CHOICE_C(light, mp_csp_light_names)}, + {"chroma-location", OPT_CHOICE_C(chroma_location, pl_chroma_names)}, + {"stereo-in", OPT_CHOICE_C(stereo_in, mp_stereo3d_names)}, + {"rotate", OPT_INT(rotate), M_RANGE(-1, 359)}, + {"alpha", OPT_CHOICE_C(alpha, pl_alpha_names)}, + {"w", OPT_INT(w)}, + {"h", OPT_INT(h)}, + {"dw", OPT_INT(dw)}, + {"dh", OPT_INT(dh)}, + {"dar", OPT_DOUBLE(dar)}, + {"convert", OPT_BOOL(convert)}, + {"dolbyvision", OPT_BOOL(dovi)}, + {"hdr10plus", OPT_BOOL(hdr10plus)}, + {"film-grain", OPT_BOOL(film_grain)}, + {"force-scaler", OPT_CHOICE(force_scaler, + {"auto", MP_SWS_AUTO}, + {"sws", MP_SWS_SWS}, + {"zimg", MP_SWS_ZIMG})}, {0} }; @@ -211,6 +264,9 @@ const struct mp_user_filter_entry vf_format = { .priv_size = sizeof(OPT_BASE_STRUCT), .priv_defaults = &(const OPT_BASE_STRUCT){ .rotate = -1, + .dovi = true, + .hdr10plus = true, + .film_grain = true, }, .options = vf_opts_fields, }, diff --git a/video/filter/vf_gpu.c b/video/filter/vf_gpu.c index 5528abaa60..d2ba990a00 100644 --- a/video/filter/vf_gpu.c +++ b/video/filter/vf_gpu.c @@ -24,95 +24,49 @@ #include "options/options.h" #include "video/out/aspect.h" #include "video/out/gpu/video.h" -#include "video/out/opengl/egl_helpers.h" -#include "video/out/opengl/ra_gl.h" - -struct offscreen_ctx { - struct mp_log *log; - struct ra *ra; - void *priv; - - void (*set_context)(struct offscreen_ctx *ctx, bool enable); -}; - -struct gl_offscreen_ctx { - GL gl; - EGLDisplay egl_display; - EGLContext egl_context; +#include "video/filter/vf_gpu.h" + +extern const struct offscreen_context offscreen_vk; +extern const struct offscreen_context offscreen_egl; + +static const struct offscreen_context *contexts[] = { +#if HAVE_EGL + &offscreen_egl, +#endif +#if HAVE_VULKAN + &offscreen_vk, +#endif }; -static void gl_ctx_destroy(void *p) +static inline OPT_STRING_VALIDATE_FUNC(offscreen_ctx_validate_api) { - struct offscreen_ctx *ctx = p; - struct gl_offscreen_ctx *gl = ctx->priv; - - ra_free(&ctx->ra); - - if (gl->egl_context) - eglDestroyContext(gl->egl_display, gl->egl_context); + struct bstr param = bstr0(*value); + for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) { + if (bstr_equals0(param, contexts[i]->api)) + return 1; + } + return M_OPT_INVALID; } -static void gl_ctx_set_context(struct offscreen_ctx *ctx, bool enable) +static int offscreen_ctx_api_help(struct mp_log *log, const struct m_option *opt, + struct bstr name) { - struct gl_offscreen_ctx *gl = ctx->priv; - EGLContext c = enable ? gl->egl_context : EGL_NO_CONTEXT; - - if (!eglMakeCurrent(gl->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, c)) - MP_ERR(ctx, "Could not make EGL context current.\n"); + mp_info(log, "GPU APIs (offscreen contexts):\n"); + for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) + mp_info(log, " %s\n", contexts[i]->api); + return M_OPT_EXIT; } -static struct offscreen_ctx *gl_offscreen_ctx_create(struct mpv_global *global, - struct mp_log *log) +static struct offscreen_ctx *offscreen_ctx_create(struct mpv_global *global, + struct mp_log *log, + const char *api) { - struct offscreen_ctx *ctx = talloc_zero(NULL, struct offscreen_ctx); - struct gl_offscreen_ctx *gl = talloc_zero(ctx, struct gl_offscreen_ctx); - talloc_set_destructor(ctx, gl_ctx_destroy); - *ctx = (struct offscreen_ctx){ - .log = log, - .priv = gl, - .set_context = gl_ctx_set_context, - }; - - // This appears to work with Mesa. EGL 1.5 doesn't specify what a "default - // display" is at all. - gl->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (!eglInitialize(gl->egl_display, NULL, NULL)) { - MP_ERR(ctx, "Could not initialize EGL.\n"); - goto error; - } - - // Unfortunately, mpegl_create_context() is entangled with ra_ctx. - // Fortunately, it does not need much, and we can provide a stub. - struct ra_ctx ractx = { - .log = ctx->log, - .global = global, - }; - EGLConfig config; - if (!mpegl_create_context(&ractx, gl->egl_display, &gl->egl_context, &config)) - { - MP_ERR(ctx, "Could not create EGL context.\n"); - goto error; - } - - if (!eglMakeCurrent(gl->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, - gl->egl_context)) - { - MP_ERR(ctx, "Could not make EGL context current.\n"); - goto error; + for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) { + if (api && strcmp(contexts[i]->api, api) != 0) + continue; + mp_info(log, "Creating offscreen GPU context '%s'\n", contexts[i]->api); + return contexts[i]->offscreen_ctx_create(global, log); } - - mpegl_load_functions(&gl->gl, ctx->log); - ctx->ra = ra_create_gl(&gl->gl, ctx->log); - - if (!ctx->ra) - goto error; - - gl_ctx_set_context(ctx, false); - - return ctx; - -error: - talloc_free(ctx); return NULL; } @@ -124,6 +78,7 @@ static void offscreen_ctx_set_current(struct offscreen_ctx *ctx, bool enable) struct gpu_opts { int w, h; + char *api; }; struct priv { @@ -166,12 +121,14 @@ static struct mp_image *gpu_render_frame(struct mp_filter *f, struct mp_image *i bool need_reconfig = m_config_cache_update(priv->vo_opts_cache); - if (!mp_image_params_equal(&priv->img_params, &in->params)) { - priv->img_params = in->params; + if (!mp_image_params_static_equal(&priv->img_params, &in->params)) { gl_video_config(priv->renderer, &in->params); need_reconfig = true; } + if (!mp_image_params_equal(&priv->img_params, &in->params)) + priv->img_params = in->params; + if (need_reconfig) { struct mp_rect src, dst; struct mp_osd_res osd; @@ -196,6 +153,7 @@ static struct mp_image *gpu_render_frame(struct mp_filter *f, struct mp_image *i .downloadable = true, .w = w, .h = h, + .d = 1, .render_dst = true, }; @@ -211,7 +169,7 @@ static struct mp_image *gpu_render_frame(struct mp_filter *f, struct mp_image *i // (it doesn't have access to the OSD though) int flags = RENDER_FRAME_SUBS | RENDER_FRAME_VF_SUBS; - gl_video_render_frame(priv->renderer, &frame, (struct ra_fbo){priv->target}, + gl_video_render_frame(priv->renderer, &frame, &(struct ra_fbo){priv->target}, flags); res = mp_image_alloc(IMGFMT_RGB0, w, h); @@ -324,7 +282,7 @@ static struct mp_filter *gpu_create(struct mp_filter *parent, void *options) priv->vo_opts_cache = m_config_cache_alloc(f, f->global, &vo_sub_opts); priv->vo_opts = priv->vo_opts_cache->opts; - priv->ctx = gl_offscreen_ctx_create(f->global, f->log); + priv->ctx = offscreen_ctx_create(f->global, f->log, priv->opts->api); if (!priv->ctx) { MP_FATAL(f, "Could not create offscreen ra context.\n"); goto error; @@ -363,8 +321,10 @@ const struct mp_user_filter_entry vf_gpu = { .name = "gpu", .priv_size = sizeof(OPT_BASE_STRUCT), .options = (const struct m_option[]){ - OPT_INT("w", w, 0), - OPT_INT("h", h, 0), + {"w", OPT_INT(w)}, + {"h", OPT_INT(h)}, + {"api", OPT_STRING_VALIDATE(api, offscreen_ctx_validate_api), + .help = offscreen_ctx_api_help}, {0} }, }, diff --git a/video/filter/vf_gpu.h b/video/filter/vf_gpu.h new file mode 100644 index 0000000000..2cc9a16eea --- /dev/null +++ b/video/filter/vf_gpu.h @@ -0,0 +1,35 @@ +/* + * 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/>. + */ + +#pragma once + +#include "common/common.h" +#include "common/global.h" + +struct offscreen_ctx { + struct mp_log *log; + struct ra *ra; + void *priv; + + void (*set_context)(struct offscreen_ctx *ctx, bool enable); +}; + +struct offscreen_context { + const char *api; + struct offscreen_ctx *(*offscreen_ctx_create)(struct mpv_global *, + struct mp_log *); +}; diff --git a/video/filter/vf_gpu_egl.c b/video/filter/vf_gpu_egl.c new file mode 100644 index 0000000000..cd90a6e06a --- /dev/null +++ b/video/filter/vf_gpu_egl.c @@ -0,0 +1,107 @@ +/* + * 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 "common/common.h" +#include "video/filter/vf_gpu.h" +#include "video/out/opengl/egl_helpers.h" +#include "video/out/opengl/ra_gl.h" + +struct gl_offscreen_ctx { + GL gl; + EGLDisplay egl_display; + EGLContext egl_context; +}; + +static void gl_ctx_destroy(void *p) +{ + struct offscreen_ctx *ctx = p; + struct gl_offscreen_ctx *gl = ctx->priv; + + ra_free(&ctx->ra); + + if (gl->egl_context) + eglDestroyContext(gl->egl_display, gl->egl_context); +} + +static void gl_ctx_set_context(struct offscreen_ctx *ctx, bool enable) +{ + struct gl_offscreen_ctx *gl = ctx->priv; + EGLContext c = enable ? gl->egl_context : EGL_NO_CONTEXT; + + if (!eglMakeCurrent(gl->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, c)) + MP_ERR(ctx, "Could not make EGL context current.\n"); +} + +static struct offscreen_ctx *gl_offscreen_ctx_create(struct mpv_global *global, + struct mp_log *log) +{ + struct offscreen_ctx *ctx = talloc(NULL, struct offscreen_ctx); + struct gl_offscreen_ctx *gl = talloc_zero(ctx, struct gl_offscreen_ctx); + talloc_set_destructor(ctx, gl_ctx_destroy); + *ctx = (struct offscreen_ctx){ + .log = log, + .priv = gl, + .set_context = gl_ctx_set_context, + }; + + // This appears to work with Mesa. EGL 1.5 doesn't specify what a "default + // display" is at all. + gl->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!eglInitialize(gl->egl_display, NULL, NULL)) { + MP_ERR(ctx, "Could not initialize EGL.\n"); + goto error; + } + + // Unfortunately, mpegl_create_context() is entangled with ra_ctx. + // Fortunately, it does not need much, and we can provide a stub. + struct ra_ctx ractx = { + .log = ctx->log, + .global = global, + }; + EGLConfig config; + if (!mpegl_create_context(&ractx, gl->egl_display, &gl->egl_context, &config)) + { + MP_ERR(ctx, "Could not create EGL context.\n"); + goto error; + } + + if (!eglMakeCurrent(gl->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + gl->egl_context)) + { + MP_ERR(ctx, "Could not make EGL context current.\n"); + goto error; + } + + mpegl_load_functions(&gl->gl, ctx->log); + ctx->ra = ra_create_gl(&gl->gl, ctx->log); + + if (!ctx->ra) + goto error; + + gl_ctx_set_context(ctx, false); + + return ctx; + +error: + talloc_free(ctx); + return NULL; +} + +const struct offscreen_context offscreen_egl = { + .api = "egl", + .offscreen_ctx_create = gl_offscreen_ctx_create +}; diff --git a/video/filter/vf_gpu_vulkan.c b/video/filter/vf_gpu_vulkan.c new file mode 100644 index 0000000000..81a84becfa --- /dev/null +++ b/video/filter/vf_gpu_vulkan.c @@ -0,0 +1,115 @@ +/* + * 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 "options/m_config.h" +#include "video/filter/vf_gpu.h" +#include "video/out/placebo/ra_pl.h" +#include "video/out/placebo/utils.h" +#include "video/out/vulkan/context.h" +#include "video/out/vulkan/utils.h" + +struct vk_offscreen_ctx { + struct ra_ctx *ractx; + struct mpvk_ctx *vk; +}; + +extern const struct m_sub_options vulkan_conf; + +static void vk_ctx_destroy(void *p) +{ + struct offscreen_ctx *ctx = p; + struct vk_offscreen_ctx *vkctx = ctx->priv; + struct ra_ctx *ractx = vkctx->ractx; + struct mpvk_ctx *vk = vkctx->vk; + + if (ractx->ra) { + pl_gpu_finish(vk->gpu); + ractx->ra->fns->destroy(ctx->ra); + ractx->ra = NULL; + ctx->ra = NULL; + } + + vk->gpu = NULL; + pl_vulkan_destroy(&vk->vulkan); + mpvk_uninit(vk); + talloc_free(vk); + talloc_free(ractx); +} + +static struct offscreen_ctx *vk_offscreen_ctx_create(struct mpv_global *global, + struct mp_log *log) +{ + struct offscreen_ctx *ctx = talloc(NULL, struct offscreen_ctx); + talloc_set_destructor(ctx, vk_ctx_destroy); + *ctx = |