summaryrefslogtreecommitdiffstats
path: root/video/filter
diff options
context:
space:
mode:
Diffstat (limited to 'video/filter')
-rw-r--r--video/filter/refqueue.c27
-rw-r--r--video/filter/refqueue.h5
-rw-r--r--video/filter/vf_d3d11vpp.c49
-rw-r--r--video/filter/vf_fingerprint.c17
-rw-r--r--video/filter/vf_format.c124
-rw-r--r--video/filter/vf_gpu.c130
-rw-r--r--video/filter/vf_gpu.h35
-rw-r--r--video/filter/vf_gpu_egl.c107
-rw-r--r--video/filter/vf_gpu_vulkan.c115
-rw-r--r--video/filter/vf_sub.c21
-rw-r--r--video/filter/vf_vapoursynth.c143
-rw-r--r--video/filter/vf_vavpp.c52
-rw-r--r--video/filter/vf_vdpaupp.c51
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, &params);
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 =