[vlc-devel] [PATCH 5/7] vout: add new vulkan/libplacebo vout
Hugo Beauzée-Luyssen
hugo at beauzee.fr
Tue Oct 2 16:37:09 CEST 2018
Hi,
On Mon, Oct 1, 2018, at 4:51 PM, Niklas Haas wrote:
> From: Niklas Haas <git at haasn.xyz>
>
> This uses libplacebo's rendering helpers for all video output, on top of
> the vulkan graphics API. Some notes:
>
> - The existing fourcc/chroma helpers don't really line up with what the
> libplacebo API expects, or in some cases return values that just don't
> seem to make sense. I was advised against touching them for fear of
> breaking the rest of VLC - so we add our own helpers that give us the
> information in the format we need for libplacebo.
>
> - Not all libplacebo options are mapped. There's no ability to create
> custom filter functions (which libplacebo/mpv support), and there's
> also no support for ICC profiles / 3DLUTs (which libplacebo supports)
> nor for the new color blindness simulation parameters in libplacebo
> v0.6. We also don't map the VLC brightness/hue/gamma/etc. options to
> the libplacebo structs - we could do it for free as part of the video
> decode matrix, rather than needing to insert a CPU filter for it.
>
> - How to create the vulkan surface will depend on the platform (much
> like in opengl), so we move context, surface and device creation into
> a single module (`vulkan/surface.c`) which will be conditionally
> compiled depending on the platform in order to provide support for
> multiple surfaces side-by-side (e.g. x11 and wayland). This does mean
> that the context/device-related options end up being separate per
> platform, but OTOH this is not that bad since different platforms
> might want different e.g. swapchain modes (an example being wayland,
> which can make better use of mailbox rather than fifo).
>
> - libplacebo doesn't have a "configure" step, instead all rendering
> parameters are fully dynamic. So we could call UpdateParams() in our
> module at any point in time when the config values change.
> Unfortunately, there's no easy way for us to find out when this is the
> case, so right now changing the vulkan module options requires a
> module reinit to take effect. In theory we could change this. (As an
> aside: calling var_Inherit* per frame does work to get us the changes
> in "realtime", as soon as the user clicks "save", but this may block
> for arbitrary amounts of time so I was advised against doing it)
>
> Due to the new functions, structs and enum members used, the minimum
> libplacebo version has been bumped up to v0.5.0. In theory we could also
> try and support v0.4.0 with some #ifdefs, but v0.5.0 has been out for
> several months now so it should be a safe requirement.
> ---
> configure.ac | 60 +-
> modules/video_output/Makefile.am | 24 +
> modules/video_output/placebo_utils.c | 364 +++++++++++
> modules/video_output/placebo_utils.h | 295 +++++++++
> modules/video_output/vulkan/display.c | 762 ++++++++++++++++++++++
> modules/video_output/vulkan/surface.c | 220 +++++++
> modules/video_output/vulkan/vk_instance.c | 77 +++
> modules/video_output/vulkan/vk_instance.h | 58 ++
> 8 files changed, 1841 insertions(+), 19 deletions(-)
> create mode 100644 modules/video_output/vulkan/display.c
> create mode 100644 modules/video_output/vulkan/surface.c
> create mode 100644 modules/video_output/vulkan/vk_instance.c
> create mode 100644 modules/video_output/vulkan/vk_instance.h
>
> diff --git a/configure.ac b/configure.ac
> index 09992925b7..0a5b0b9171 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -2983,6 +2983,25 @@ dnl
>
> EXTEND_HELP_STRING([Video plugins:])
>
> +dnl
> +dnl libplacebo support
> +dnl
> +AC_ARG_ENABLE([libplacebo],
> + AS_HELP_STRING([--disable-libplacebo],
> + [disable libplacebo support (default auto)]))
> +
> +AS_IF([test "$enable_libplacebo" != "no"], [
> + PKG_CHECK_MODULES([LIBPLACEBO], [libplacebo >= 0.5], [
> + AC_DEFINE([HAVE_LIBPLACEBO], [1], [Define to 1 if libplacebo is
> enabled.])
Unrelated to the patch itself since this is just moving, but couldn't this be defined in the .am file instead of config.h? It would prevent a full rebuilt of all files when adding/removing libplacebo as config.h wouldn't change
> + ], [
> + AS_IF([test -n "${enable_libplacebo}"], [
> + AC_MSG_ERROR([${LIBPLACEBO_PKG_ERRORS}.])
> + ])
> + enable_libplacebo="no"
> + ])
> +])
> +AM_CONDITIONAL([HAVE_LIBPLACEBO], [test "$enable_libplacebo" != "no"])
> +
> dnl
> dnl OpenGL
> dnl
> @@ -3024,6 +3043,28 @@ AS_IF([test "${have_gl}" = "yes"], [
> dnl OpenGL ES 2: depends on EGL 1.1
> PKG_ENABLE_MODULES_VLC([GLES2], [], [glesv2], [OpenGL ES v2 support],
> [disabled])
>
> +dnl
> +dnl Vulkan
> +dnl
> +AC_ARG_ENABLE(vulkan,
> + [AS_HELP_STRING([--disable-vulkan],
> + [disable Vulkan support (default auto)])])
> +
> +AS_IF([test "$enable_vulkan" != "no"], [
> + AS_IF([test "$enable_libplacebo" = "no"], [
> + AC_MSG_ERROR([Vulkan support requires libplacebo as well. Do not
> use --disable-libplacebo.])
> + ])
> + PKG_CHECK_MODULES([VULKAN], [vulkan >= 1.0.26], [
> + AC_DEFINE([HAVE_VULKAN], [1], [Define to 1 if vulkan is enabled.])
> + ], [
> + AS_IF([test -n "${enable_vulkan}"], [
> + AC_MSG_ERROR([${VULKAN_PKG_ERRORS}.])
> + ])
> + enable_vulkan="no"
> + ])
> +])
> +AM_CONDITIONAL(HAVE_VULKAN, [test "$enable_vulkan" != "no"])
> +
> dnl
> dnl Xlib
> dnl
> @@ -4132,25 +4173,6 @@ dnl Libnotify notification plugin
> dnl
> PKG_ENABLE_MODULES_VLC([NOTIFY], [], [libnotify gtk+-3.0], [libnotify
> notification], [auto])
>
> -dnl
> -dnl libplacebo support
> -dnl
> -AC_ARG_ENABLE([libplacebo],
> - AS_HELP_STRING([--disable-libplacebo],
> - [disable libplacebo support (default auto)]))
> -
> -AS_IF([test "$enable_libplacebo" != "no"], [
> - PKG_CHECK_MODULES([LIBPLACEBO], [libplacebo >= 0.2.1], [
> - AC_DEFINE([HAVE_LIBPLACEBO], [1], [Define to 1 if libplacebo is
> enabled.])
> - ], [
> - AS_IF([test -n "${enable_libplacebo}"], [
> - AC_MSG_ERROR([${LIBPLACEBO_PKG_ERRORS}.])
> - ])
> - enable_libplacebo="no"
> - ])
> -])
> -AM_CONDITIONAL([HAVE_LIBPLACEBO], [test "$enable_libplacebo" != "no"])
> -
> PKG_ENABLE_MODULES_VLC([MEDIALIBRARY], [medialibrary], [medialibrary],
> (medialibrary support), [auto])
>
> dnl
> diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
> index ea52adade5..bd6c4456a4 100644
> --- a/modules/video_output/Makefile.am
> +++ b/modules/video_output/Makefile.am
> @@ -122,6 +122,22 @@ vout_LTLIBRARIES += libglconv_vdpau_plugin.la
> endif
> endif # HAVE_GL
>
> +### Vulkan ###
> +
> +VULKAN_COMMONSOURCES = video_output/vulkan/vk_instance.c \
> + video_output/placebo_utils.c
> +
> +VULKAN_COMMONCLFAGS = $(VULKAN_CFLAGS) $(LIBPLACEBO_CFLAGS)
> +VULKAN_COMMONLIBS = $(VULKAN_LIBS) $(LIBPLACEBO_LIBS)
> +
> +libvk_plugin_la_SOURCES = $(VULKAN_COMMONSOURCES) video_output/vulkan/display.c
> +libvk_plugin_la_CFLAGS = $(AM_CFLAGS) $(VULKAN_COMMONCFLAGS)
> +libvk_plugin_la_LIBADD = $(VULKAN_COMMONLIBS)
> +
> +if HAVE_VULKAN
> +vout_LTLIBRARIES += libvk_plugin.la
> +endif
> +
> ### XCB ###
> libvlc_xcb_events_la_SOURCES = \
> video_output/xcb/events.c video_output/xcb/events.h
> @@ -165,6 +181,11 @@ libglx_plugin_la_SOURCES = video_output/glx.c
> libglx_plugin_la_CFLAGS = $(AM_CFLAGS) $(GL_CFLAGS)
> libglx_plugin_la_LIBADD = $(GL_LIBS) $(X_LIBS) $(X_PRE_LIBS) -lX11
>
> +libvk_x11_plugin_la_SOURCES = $(VULKAN_COMMONSOURCES) video_output/
> vulkan/surface.c
> +libvk_x11_plugin_la_CFLAGS = $(AM_CFLAGS) $(VULKAN_COMMONCFLAGS) \
> + -DVK_USE_PLATFORM_XLIB_KHR
> +libvk_x11_plugin_la_LIBADD = $(VULKAN_COMMONLIBS) $(X_LIBS) $
> (X_PRE_LIBS) -lX11
> +
> if HAVE_XCB
> pkglib_LTLIBRARIES += libvlc_xcb_events.la
> vout_LTLIBRARIES += libxcb_x11_plugin.la libxcb_window_plugin.la
> @@ -181,6 +202,9 @@ endif
> if HAVE_GL
> vout_LTLIBRARIES += libglx_plugin.la
> endif
> +if HAVE_VULKAN
> +vout_LTLIBRARIES += libvk_x11_plugin.la
> +endif
> endif
>
>
> diff --git a/modules/video_output/placebo_utils.c b/modules/
> video_output/placebo_utils.c
> index 2515dc0bef..90edf77be1 100644
> --- a/modules/video_output/placebo_utils.c
> +++ b/modules/video_output/placebo_utils.c
> @@ -52,6 +52,318 @@ struct pl_context *vlc_placebo_Create(vlc_object_t
> *obj)
> });
> }
>
> +struct plane_desc {
> + int components;
> + size_t pixel_size;
> + int comp_bits[4];
> + int comp_map[4];
> + int w_denom;
> + int h_denom;
> +};
> +
> +struct fmt_desc {
> + enum pl_fmt_type type;
> + struct plane_desc planes[4];
> + int num_planes;
> + int color_bits; // relevant bits, or 0 if the same as the texture
> depth
> +};
> +
> +#define SIZE(n, bits, pad) (((n) * (bits) + (pad) + 7) / 8)
> +#define COMPS(...) {__VA_ARGS__}
IIRC this is not valid ANSI C. Most compilers we use probably support it anyway, but you're only using this macro in 2 locations so it might be worth removing it plainly.
> +
> +#define PLANE(n, bits, map, wd, hd, pad) \
> + { .components = n, \
> + .pixel_size = SIZE(n, bits, pad), \
> + .comp_bits = {bits, bits, bits, bits}, \
> + .comp_map = map, \
> + .w_denom = wd, \
> + .h_denom = hd, \
> + }
> +
> +#define _PLANAR(n, bits, wd, hd) \
> + .type = PL_FMT_UNORM, \
> + .num_planes = n, \
> + .planes = { \
> + PLANE(1, bits, {0}, 1, 1, 0), \
> + PLANE(1, bits, {1}, wd, hd, 0), \
> + PLANE(1, bits, {2}, wd, hd, 0), \
> + PLANE(1, bits, {3}, 1, 1, 0), \
> + }
> +
> +#define _SEMIPLANAR(n, bits, wd, hd) \
> + .type = PL_FMT_UNORM, \
> + .num_planes = n, \
> + .planes = { \
> + PLANE(1, bits, {0}, 1, 1, 0), \
> + PLANE(2, bits, COMPS(1, 2), wd, hd, 0), \
> + PLANE(1, bits, {3}, 1, 1, 0), \
> + }
> +
> +#define PACKED(n, bits, pad) \
> + .type = PL_FMT_UNORM, \
> + .num_planes = 1, \
> + .planes = { \
> + PLANE(n, bits, COMPS(0, 1, 2, 3), 1, 1, pad), \
> + }
> +
> +#define SINGLE(t, bits) \
> + .type = PL_FMT_##t, \
> + .num_planes = 1, \
> + .planes = { \
> + PLANE(1, bits, {0}, 1, 1, 0), \
> + }
> +
> +#define PLANAR(...) _PLANAR(__VA_ARGS__)
> +#define SEMIPLANAR(...) _SEMIPLANAR(__VA_ARGS__)
> +
> +#define _410 4, 2
> +#define _411 4, 1
> +#define _420 2, 2
> +#define _422 2, 1
> +#define _440 1, 2
> +#define _444 1, 1
> +
> +// NOTE: This list contains some special formats that don't follow the
> normal
> +// rules, but which are included regardless. The corrections for these
> +// exceptions happen below, in the function vlc_placebo_PlaneFormat!
> +static const struct { vlc_fourcc_t fcc; struct fmt_desc desc; }
> formats[] = {
> + { VLC_CODEC_YV9, {PLANAR(3, 8, _410)} },
> + { VLC_CODEC_I410, {PLANAR(3, 8, _410)} },
> + { VLC_CODEC_I411, {PLANAR(3, 8, _411)} },
> + { VLC_CODEC_I440, {PLANAR(3, 8, _440)} },
> + { VLC_CODEC_J440, {PLANAR(3, 8, _440)} },
> + { VLC_CODEC_GREY, {PLANAR(1, 8, _444)} },
> +
> + { VLC_CODEC_I420, {PLANAR(3, 8, _420)} },
> + { VLC_CODEC_J420, {PLANAR(3, 8, _420)} },
> +#ifdef WORDS_BIGENDIAN
> + { VLC_CODEC_I420_9B, {PLANAR(3, 16, _420), .color_bits =
> 9} },
> + { VLC_CODEC_I420_10B, {PLANAR(3, 16, _420), .color_bits =
> 10} },
> + { VLC_CODEC_I420_12B, {PLANAR(3, 16, _420), .color_bits =
> 12} },
> + { VLC_CODEC_I420_16B, {PLANAR(3, 16, _420), .color_bits =
> 16} },
> +#else
> + { VLC_CODEC_I420_9L, {PLANAR(3, 16, _420), .color_bits =
> 9} },
> + { VLC_CODEC_I420_10L, {PLANAR(3, 16, _420), .color_bits =
> 10} },
> + { VLC_CODEC_I420_12L, {PLANAR(3, 16, _420), .color_bits =
> 12} },
> + { VLC_CODEC_I420_16L, {PLANAR(3, 16, _420), .color_bits =
> 16} },
> +#endif
> +
> + { VLC_CODEC_I422, {PLANAR(3, 8, _422)} },
> + { VLC_CODEC_J422, {PLANAR(3, 8, _422)} },
> +#ifdef WORDS_BIGENDIAN
> + { VLC_CODEC_I422_9B, {PLANAR(3, 16, _422), .color_bits =
> 9} },
> + { VLC_CODEC_I422_10B, {PLANAR(3, 16, _422), .color_bits =
> 10} },
> + { VLC_CODEC_I422_12B, {PLANAR(3, 16, _422), .color_bits =
> 12} },
> +#else
> + { VLC_CODEC_I422_9L, {PLANAR(3, 16, _422), .color_bits =
> 9} },
> + { VLC_CODEC_I422_10L, {PLANAR(3, 16, _422), .color_bits =
> 10} },
> + { VLC_CODEC_I422_12L, {PLANAR(3, 16, _422), .color_bits =
> 12} },
> +#endif
> +
> + { VLC_CODEC_I444, {PLANAR(3, 8, _444)} },
> + { VLC_CODEC_J444, {PLANAR(3, 8, _444)} },
> +#ifdef WORDS_BIGENDIAN
> + { VLC_CODEC_I444_9B, {PLANAR(3, 16, _444), .color_bits =
> 9} },
> + { VLC_CODEC_I444_10B, {PLANAR(3, 16, _444), .color_bits =
> 10} },
> + { VLC_CODEC_I444_12B, {PLANAR(3, 16, _444), .color_bits =
> 12} },
> + { VLC_CODEC_I444_16B, {PLANAR(3, 16, _444), .color_bits =
> 16} },
> +#else
> + { VLC_CODEC_I444_9L, {PLANAR(3, 16, _444), .color_bits =
> 9} },
> + { VLC_CODEC_I444_10L, {PLANAR(3, 16, _444), .color_bits =
> 10} },
> + { VLC_CODEC_I444_12L, {PLANAR(3, 16, _444), .color_bits =
> 12} },
> + { VLC_CODEC_I444_16L, {PLANAR(3, 16, _444), .color_bits =
> 16} },
> +#endif
> +
> + { VLC_CODEC_YUVA, {PLANAR(4, 8, _444)} },
> + { VLC_CODEC_YUV422A, {PLANAR(4, 8, _422)} },
> +#ifdef WORDS_BIGENDIAN
> + { VLC_CODEC_YUVA_444_10B, {PLANAR(4, 16, _444), .color_bits =
> 10} },
> +#else
> + { VLC_CODEC_YUVA_444_10L, {PLANAR(4, 16, _444), .color_bits =
> 10} },
> +#endif
> +
> + { VLC_CODEC_NV12, {SEMIPLANAR(2, 8, _420)} },
> + { VLC_CODEC_NV21, {SEMIPLANAR(2, 8, _420)} },
> + { VLC_CODEC_P010, {SEMIPLANAR(2, 16, _420)} },
> + { VLC_CODEC_NV16, {SEMIPLANAR(2, 8, _422)} },
> + { VLC_CODEC_NV61, {SEMIPLANAR(2, 8, _422)} },
> + { VLC_CODEC_NV24, {SEMIPLANAR(2, 8, _444)} },
> + { VLC_CODEC_NV42, {SEMIPLANAR(2, 8, _444)} },
> +
> + { VLC_CODEC_RGB8, {PACKED(3, 2, 2)} },
> + { VLC_CODEC_RGB12, {PACKED(3, 4, 4)} },
> + { VLC_CODEC_RGB15, {PACKED(3, 5, 1)} },
> + { VLC_CODEC_RGB16, {PACKED(3, 5, 1)} },
> + { VLC_CODEC_RGB24, {PACKED(3, 8, 0)} },
> + { VLC_CODEC_RGB32, {PACKED(3, 8, 8)} },
> + { VLC_CODEC_RGBA, {PACKED(4, 8, 0)} },
> + { VLC_CODEC_BGRA, {PACKED(4, 8, 0)} },
> +
> + { VLC_CODEC_GBR_PLANAR, {PLANAR(3, 8, _444)} },
> +#ifdef WORDS_BIGENDIAN
> + { VLC_CODEC_GBR_PLANAR_9B, {PLANAR(3, 16, _444), .color_bits =
> 9} },
> + { VLC_CODEC_GBR_PLANAR_10B, {PLANAR(3, 16, _444), .color_bits =
> 10} },
> + { VLC_CODEC_GBR_PLANAR_16B, {PLANAR(3, 16, _444), .color_bits =
> 16} },
> +#else
> + { VLC_CODEC_GBR_PLANAR_9L, {PLANAR(3, 16, _444), .color_bits =
> 9} },
> + { VLC_CODEC_GBR_PLANAR_10L, {PLANAR(3, 16, _444), .color_bits =
> 10} },
> + { VLC_CODEC_GBR_PLANAR_16L, {PLANAR(3, 16, _444), .color_bits =
> 16} },
> +#endif
> +
> + { VLC_CODEC_U8, {SINGLE(UNORM, 8)} },
> + { VLC_CODEC_S8, {SINGLE(SNORM, 8)} },
> + { VLC_CODEC_U16N, {SINGLE(UNORM, 16)} },
> + { VLC_CODEC_S16N, {SINGLE(SNORM, 16)} },
> + { VLC_CODEC_U24N, {SINGLE(UNORM, 24)} },
> + { VLC_CODEC_S24N, {SINGLE(SNORM, 24)} },
> + { VLC_CODEC_U32N, {SINGLE(UNORM, 32)} },
> + { VLC_CODEC_S32N, {SINGLE(SNORM, 32)} },
> + { VLC_CODEC_FL32, {SINGLE(FLOAT, 32)} },
> + { VLC_CODEC_FL64, {SINGLE(FLOAT, 64)} },
> +
> + { 0 },
> +};
> +
> +static const struct fmt_desc *FindDesc(vlc_fourcc_t fcc)
> +{
> + for (int i = 0; formats[i].fcc; i++) {
> + if (formats[i].fcc == fcc) {
> + return &formats[i].desc;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +// This fills everything except width/height, which are left as 1
> +static void FillDesc(vlc_fourcc_t fcc, const struct fmt_desc *desc,
> + struct pl_plane_data data[4])
> +{
> + assert(desc->num_planes <= 4);
> + for (int i = 0; i < desc->num_planes; i++) {
> + const struct plane_desc *p = &desc->planes[i];
> +
> + data[i] = (struct pl_plane_data) {
> + .type = desc->type,
> + .width = 1,
> + .height = 1,
> + .pixel_stride = p->pixel_size,
> + };
> +
> + for (int c = 0; c < p->components; c++) {
> + data[i].component_size[c] = p->comp_bits[c];
> + data[i].component_map[c] = p->comp_map[c];
> + }
> + }
> +
> + // Exceptions to the rule
> + switch (fcc) {
> + case VLC_CODEC_YV9:
> + case VLC_CODEC_YV12:
> + // Planar Y:V:U
> + data[1].component_map[0] = 2;
> + data[2].component_map[0] = 1;
> + break;
> +
> + case VLC_CODEC_RGB32:
> + // XRGB instead of RGBX
> + data[0].component_map[0] = -1;
> + data[1].component_map[0] = 0;
> + data[2].component_map[0] = 1;
> + data[3].component_map[0] = 2;
> + break;
> +
> + case VLC_CODEC_BGRA:
> + // Packed BGR
> + data[0].component_map[0] = 2;
> + data[0].component_map[1] = 1;
> + data[0].component_map[2] = 0;
> + break;
> +
> + case VLC_CODEC_GBR_PLANAR:
> + case VLC_CODEC_GBR_PLANAR_9L:
> + case VLC_CODEC_GBR_PLANAR_10L:
> + case VLC_CODEC_GBR_PLANAR_16L:
> + case VLC_CODEC_GBR_PLANAR_9B:
> + case VLC_CODEC_GBR_PLANAR_10B:
> + case VLC_CODEC_GBR_PLANAR_16B:
> + // Planar GBR
> + data[0].component_map[0] = 1;
> + data[1].component_map[0] = 2;
> + data[2].component_map[0] = 0;
> + break;
> +
> + case VLC_CODEC_RGB16:
> + // 5:6:5 instead of 5:5:5
> + data[0].component_size[1] += 1;
> + break;
> +
> + case VLC_CODEC_RGB8:
> + // 3:3:2 instead of 2:2:2
> + data[0].component_size[0] += 1;
> + data[0].component_size[1] += 1;
> + break;
> +
> + default: break;
> + }
> +}
> +
> +int vlc_placebo_PlaneFormat(const video_format_t *fmt, struct
> pl_plane_data data[4])
> +{
> + const struct fmt_desc *desc = FindDesc(fmt->i_chroma);
> + if (!desc)
> + return 0;
> +
> + FillDesc(fmt->i_chroma, desc, data);
> + for (int i = 0; i < desc->num_planes; i++) {
> + const struct plane_desc *p = &desc->planes[i];
> + data[i].width = (fmt->i_visible_width + p->w_denom - 1) / p-
> >w_denom;
> + data[i].height = (fmt->i_visible_height + p->h_denom - 1) / p-
> >h_denom;
> + }
> +
> + return desc->num_planes;
> +}
> +
> +int vlc_placebo_PlaneData(const picture_t *pic, struct pl_plane_data
> data[4],
> + const struct pl_buf *buf)
> +{
> + int planes = vlc_placebo_PlaneFormat(&pic->format, data);
> + if (!planes)
> + return 0;
> +
> + assert(planes == pic->i_planes);
> + for (int i = 0; i < planes; i++) {
> + assert(data[i].height == pic->p[i].i_visible_lines);
> + data[i].row_stride = pic->p[i].i_pitch;
> + if (buf) {
> + assert(buf->data);
> + assert(pic->p[i].p_pixels <= buf->data + buf->params.size);
> + data[i].buf = buf;
> + data[i].buf_offset = (uintptr_t) pic->p[i].p_pixels -
> (ptrdiff_t) buf->data;
> + } else {
> + data[i].pixels = pic->p[i].p_pixels;
> + }
> + }
> +
> + return planes;
> +}
> +
> +bool vlc_placebo_FormatSupported(const struct pl_gpu *gpu, vlc_fourcc_t
> fcc)
> +{
> + const struct fmt_desc *desc = FindDesc(fcc);
> + if (!desc)
> + return false;
> +
> + struct pl_plane_data data[4];
> + FillDesc(fcc, desc, data);
> + for (int i = 0; i < desc->num_planes; i++) {
> + if (!pl_plane_find_fmt(gpu, NULL, &data[i]))
> + return false;
> + }
> +
> + return true;
> +}
> +
> struct pl_color_space vlc_placebo_ColorSpace(const video_format_t *fmt)
> {
> static const enum pl_color_primaries primaries[COLOR_PRIMARIES_MAX
> +1] = {
> @@ -100,3 +412,55 @@ struct pl_color_space vlc_placebo_ColorSpace(const
> video_format_t *fmt)
> .sig_avg = sig_avg,
> };
> }
> +
> +struct pl_color_repr vlc_placebo_ColorRepr(const video_format_t *fmt)
> +{
> + static const enum pl_color_system yuv_systems[COLOR_SPACE_MAX+1] =
> {
> + [COLOR_SPACE_UNDEF] = PL_COLOR_SYSTEM_BT_709, // _UNKNOWN
> is RGB
> + [COLOR_SPACE_BT601] = PL_COLOR_SYSTEM_BT_601,
> + [COLOR_SPACE_BT709] = PL_COLOR_SYSTEM_BT_709,
> + [COLOR_SPACE_BT2020] = PL_COLOR_SYSTEM_BT_2020_NC,
> + };
> +
> + // fmt->space describes the YCbCr type only, it does not
> distinguish
> + // between YUV, XYZ, RGB and the likes!
> + enum pl_color_system sys;
> + if (likely(vlc_fourcc_IsYUV(fmt->i_chroma))) {
> + sys = yuv_systems[fmt->space];
> + } else if (unlikely(fmt->i_chroma == VLC_CODEC_XYZ12)) {
> + sys = PL_COLOR_SYSTEM_XYZ;
> + } else {
> + sys = PL_COLOR_SYSTEM_RGB;
> + }
> +
> + const struct fmt_desc *desc = FindDesc(fmt->i_chroma);
> + int sample_depth = desc->planes[0].comp_bits[0]; // just use first
> component
> +
> + return (struct pl_color_repr) {
> + .sys = sys,
> + .alpha = PL_ALPHA_PREMULTIPLIED,
> + .levels = unlikely(fmt->b_color_range_full)
> + ? PL_COLOR_LEVELS_PC
> + : PL_COLOR_LEVELS_TV,
> + .bits = {
> + .sample_depth = sample_depth,
> + .color_depth = desc->color_bits ? desc->color_bits :
> sample_depth,
> + .bit_shift = 0,
> + },
> + };
> +}
> +
> +enum pl_chroma_location vlc_placebo_ChromaLoc(const video_format_t
> *fmt)
> +{
> + static const enum pl_chroma_location locs[CHROMA_LOCATION_MAX+1] =
> {
> + [CHROMA_LOCATION_UNDEF] = PL_CHROMA_UNKNOWN,
> + [CHROMA_LOCATION_LEFT] = PL_CHROMA_LEFT,
> + [CHROMA_LOCATION_CENTER] = PL_CHROMA_CENTER,
> + [CHROMA_LOCATION_TOP_LEFT] = PL_CHROMA_TOP_LEFT,
> + [CHROMA_LOCATION_TOP_CENTER] = PL_CHROMA_TOP_CENTER,
> + [CHROMA_LOCATION_BOTTOM_LEFT] = PL_CHROMA_BOTTOM_LEFT,
> + [CHROMA_LOCATION_BOTTOM_CENTER] = PL_CHROMA_BOTTOM_CENTER,
> + };
> +
> + return locs[fmt->chroma_location];
> +}
> diff --git a/modules/video_output/placebo_utils.h b/modules/
> video_output/placebo_utils.h
> index c54e4a579e..37a7ed2c3e 100644
> --- a/modules/video_output/placebo_utils.h
> +++ b/modules/video_output/placebo_utils.h
> @@ -26,12 +26,25 @@
>
> #include <libplacebo/colorspace.h>
> #include <libplacebo/shaders/colorspace.h>
> +#include <libplacebo/utils/upload.h>
>
> // Create a libplacebo context, hooked up to the log system; or NULL on OOM
> VLC_API struct pl_context *vlc_placebo_Create(vlc_object_t *);
>
> // Turn a video_format_t into the equivalent libplacebo values
> VLC_API struct pl_color_space vlc_placebo_ColorSpace(const
> video_format_t *);
> +VLC_API struct pl_color_repr vlc_placebo_ColorRepr(const video_format_t
> *);
> +VLC_API enum pl_chroma_location vlc_placebo_ChromaLoc(const
> video_format_t *);
> +
> +// Fill a pl_plane_data array with various data. Returns the number of
> planes,
> +// or 0 if the format is unsupported by the libplacebo API. If `buf` is
> set,
> +// then all addresses of the picture_t must lie within `buf`'s mapped
> memory.
> +VLC_API int vlc_placebo_PlaneFormat(const video_format_t *, struct
> pl_plane_data[4]);
> +VLC_API int vlc_placebo_PlaneData(const picture_t *, struct
> pl_plane_data[4],
> + const struct pl_buf *buf);
> +
> +// See if a given FourCC is physically supported by a given GPU
> +VLC_API bool vlc_placebo_FormatSupported(const struct pl_gpu *,
> vlc_fourcc_t);
>
> // Shared options strings/structs for libplacebo options
>
> @@ -153,6 +166,15 @@ static const char * const tone_text[] = {
> #define GAMUT_WARN_TEXT "Highlight clipped pixels"
> #define GAMUT_WARN_LONGTEXT "Debugging tool to indicate which pixels
> were clipped as part of the tone mapping process."
>
> +#define PEAK_FRAMES_TEXT "HDR peak detection buffer size"
> +#define PEAK_FRAMES_LONGTEXT "How many input frames to consider when
> determining the brightness of HDR signals. Higher values result in a
> slower/smoother response to brightness level changes. Setting this to 0
> disables peak detection entirely."
> +
> +#define TARGET_AVG_TEXT "Target peak brightness average"
> +#define TARGET_AVG_LONGTEXT "If the source frame has an average
> brightness exceeding this number, the frame will be automatically
> darkened to compensate. This feature only works when peak detection is
> enabled."
> +
> +#define SCENE_THRESHOLD_TEXT "HDR peak scene change threshold"
> +#define SCENE_THRESHOLD_LONGTEXT "When using HDR peak detection, this
> sets a threshold for sudden brightness changes that should be considered
> as scene changes. This will result in the detected peak being
> immediately updated to the new value, rather than gradually being
> adjusted. Setting this to 0 disables this feature."
> +
> #define DITHER_TEXT N_("Dithering algorithm")
> #define DITHER_LONGTEXT N_("The algorithm to use when dithering to a
> lower bit depth.")
>
> @@ -172,7 +194,280 @@ static const char * const dither_text[] = {
> "White noise (fast but low quality)",
> };
>
> +#define DITHER_SIZE_TEXT "Dither LUT size (log 2)"
> +#define DITHER_SIZE_LONGTEXT "Controls the size of the dither matrix,
> as a power of two (e.g. the default of 6 corresponds to a 64x64 matrix).
> Does not affect all algorithms."
> +
> +#define TEMPORAL_DITHER_TEXT "Temporal dithering"
> +#define TEMPORAL_DITHER_LONGTEXT "Enables perturbing the dither matrix
> across frames. This reduces the persistence of dithering artifacts, but
> can cause flickering on some (cheap) LCD screens."
> +
> #define DITHER_DEPTH_TEXT "Dither depth override (0 = auto)"
> #define DITHER_DEPTH_LONGTEXT "Overrides the detected framebuffer
> depth. Useful to dither to lower bit depths than otherwise required."
>
> +enum {
> + SCALE_BUILTIN = 0,
> + SCALE_SPLINE16,
> + SCALE_SPLINE36,
> + SCALE_SPLINE64,
> + SCALE_MITCHELL,
> + SCALE_BICUBIC,
> + SCALE_EWA_LANCZOS,
> + SCALE_NEAREST,
> + SCALE_BILINEAR,
> + SCALE_GAUSSIAN,
> + SCALE_LANCZOS,
> + SCALE_GINSENG,
> + SCALE_EWA_GINSENG,
> + SCALE_EWA_HANN,
> + SCALE_HAASNSOFT,
> + SCALE_CATMULL_ROM,
> + SCALE_ROBIDOUX,
> + SCALE_ROBIDOUXSHARP,
> + SCALE_EWA_ROBIDOUX,
> + SCALE_EWA_ROBIDOUXSHARP,
> + SCALE_SINC,
> + SCALE_EWA_JINC,
> + SCALE_CUSTOM,
> +};
> +
> +static const int scale_values[] = {
> + SCALE_BUILTIN,
> + SCALE_SPLINE16,
> + SCALE_SPLINE36,
> + SCALE_SPLINE64,
> + SCALE_MITCHELL,
> + SCALE_BICUBIC,
> + SCALE_EWA_LANCZOS,
> + SCALE_NEAREST,
> + SCALE_BILINEAR,
> + SCALE_GAUSSIAN,
> + SCALE_LANCZOS,
> + SCALE_GINSENG,
> + SCALE_EWA_GINSENG,
> + SCALE_EWA_HANN,
> + SCALE_HAASNSOFT,
> + SCALE_CATMULL_ROM,
> + SCALE_ROBIDOUX,
> + SCALE_ROBIDOUXSHARP,
> + SCALE_EWA_ROBIDOUX,
> + SCALE_EWA_ROBIDOUXSHARP,
> + SCALE_SINC,
> + SCALE_EWA_JINC,
> + SCALE_CUSTOM,
> +};
> +
> +static const char * const scale_text[] = {
> + "Built-in / fixed function (fast)",
> + "Spline 2 taps",
> + "Spline 3 taps (recommended upscaler)",
> + "Spline 4 taps",
> + "Mitchell-Netravali (recommended downscaler)",
> + "Bicubic",
> + "Jinc / EWA Lanczos 3 taps (high quality, slow)",
> + "Nearest neighbour",
> + "Bilinear",
> + "Gaussian",
> + "Lanczos 3 taps",
> + "Ginseng 3 taps",
> + "EWA Ginseng",
> + "EWA Hann",
> + "HaasnSoft (blurred EWA Hann)",
> + "Catmull-Rom",
> + "Robidoux",
> + "RobidouxSharp",
> + "EWA Robidoux",
> + "EWA RobidouxSharp",
> + "Unwindowed sinc (clipped)",
> + "Unwindowed EWA Jinc (clipped)",
> + "Custom (see below)",
> +};
> +
> +static const struct pl_filter_config *scale_config[] = {
> + [SCALE_BUILTIN] = NULL,
> + [SCALE_SPLINE16] = &pl_filter_spline16,
> + [SCALE_SPLINE36] = &pl_filter_spline36,
> + [SCALE_SPLINE64] = &pl_filter_spline64,
> + [SCALE_NEAREST] = &pl_filter_box,
> + [SCALE_BILINEAR] = &pl_filter_triangle,
> + [SCALE_GAUSSIAN] = &pl_filter_gaussian,
> + [SCALE_SINC] = &pl_filter_sinc,
> + [SCALE_LANCZOS] = &pl_filter_lanczos,
> + [SCALE_GINSENG] = &pl_filter_ginseng,
> + [SCALE_EWA_JINC] = &pl_filter_ewa_jinc,
> + [SCALE_EWA_LANCZOS] = &pl_filter_ewa_lanczos,
> + [SCALE_EWA_GINSENG] = &pl_filter_ewa_ginseng,
> + [SCALE_EWA_HANN] = &pl_filter_ewa_hann,
> + [SCALE_HAASNSOFT] = &pl_filter_haasnsoft,
> + [SCALE_BICUBIC] = &pl_filter_bicubic,
> + [SCALE_CATMULL_ROM] = &pl_filter_catmull_rom,
> + [SCALE_MITCHELL] = &pl_filter_mitchell,
> + [SCALE_ROBIDOUX] = &pl_filter_robidoux,
> + [SCALE_ROBIDOUXSHARP] = &pl_filter_robidouxsharp,
> + [SCALE_EWA_ROBIDOUX] = &pl_filter_robidoux,
> + [SCALE_EWA_ROBIDOUXSHARP] = &pl_filter_robidouxsharp,
> + [SCALE_CUSTOM] = NULL,
> +};
> +
> +#define UPSCALER_PRESET_TEXT "Upscaler preset"
> +#define DOWNSCALER_PRESET_TEXT "Downscaler preset"
> +#define SCALER_PRESET_LONGTEXT "Choose from one of the built-in scaler
> presets. If set to custom, you can choose your own combination of
> kernel/window functions."
> +
> +#define LUT_ENTRIES_TEXT "Scaler LUT size"
> +#define LUT_ENTRIES_LONGTEXT "Size of the LUT texture used for up/
> downscalers that require one. Reducing this may boost performance at the
> cost of quality."
> +
> +#define ANTIRING_TEXT "Anti-ringing strength"
> +#define ANTIRING_LONGTEXT "Enables anti-ringing for non-polar filters.
> A value of 1.0 completely removes ringing, a value of 0.0 is a no-op."
> +
> +enum {
> + FILTER_NONE = 0,
> + FILTER_BOX,
> + FILTER_TRIANGLE,
> + FILTER_HANN,
> + FILTER_HAMMING,
> + FILTER_WELCH,
> + FILTER_KAISER,
> + FILTER_BLACKMAN,
> + FILTER_GAUSSIAN,
> + FILTER_SINC,
> + FILTER_JINC,
> + FILTER_SPHINX,
> + FILTER_BCSPLINE,
> + FILTER_CATMULL_ROM,
> + FILTER_MITCHELL,
> + FILTER_ROBIDOUX,
> + FILTER_ROBIDOUXSHARP,
> + FILTER_BICUBIC,
> + FILTER_SPLINE16,
> + FILTER_SPLINE36,
> + FILTER_SPLINE64,
> +};
> +
> +static const int filter_values[] = {
> + FILTER_NONE,
> + FILTER_BOX,
> + FILTER_TRIANGLE,
> + FILTER_HANN,
> + FILTER_HAMMING,
> + FILTER_WELCH,
> + FILTER_KAISER,
> + FILTER_BLACKMAN,
> + FILTER_GAUSSIAN,
> + FILTER_SINC,
> + FILTER_JINC,
> + FILTER_SPHINX,
> + FILTER_BCSPLINE,
> + FILTER_CATMULL_ROM,
> + FILTER_MITCHELL,
> + FILTER_ROBIDOUX,
> + FILTER_ROBIDOUXSHARP,
> + FILTER_BICUBIC,
> + FILTER_SPLINE16,
> + FILTER_SPLINE36,
> + FILTER_SPLINE64,
> +};
> +
> +static const char * const filter_text[] = {
> + "None",
> + "Box / Nearest",
> + "Triangle / Linear",
> + "Hann",
> + "Hamming",
> + "Welch",
> + "Kaiser",
> + "Blackman",
> + "Gaussian",
> + "Sinc",
> + "Jinc",
> + "Sphinx",
> + "BC spline",
> + "Catmull-Rom",
> + "Mitchell-Netravali",
> + "Robidoux",
> + "RobidouxSharp",
> + "Bicubic",
> + "Spline16",
> + "Spline36",
> + "Spline64",
> +};
> +
> +static const struct pl_filter_function *filter_fun[] = {
> + [FILTER_NONE] = NULL,
> + [FILTER_BOX] = &pl_filter_function_box,
> + [FILTER_TRIANGLE] = &pl_filter_function_triangle,
> + [FILTER_HANN] = &pl_filter_function_hann,
> + [FILTER_HAMMING] = &pl_filter_function_hamming,
> + [FILTER_WELCH] = &pl_filter_function_welch,
> + [FILTER_KAISER] = &pl_filter_function_kaiser,
> + [FILTER_BLACKMAN] = &pl_filter_function_blackman,
> + [FILTER_GAUSSIAN] = &pl_filter_function_gaussian,
> + [FILTER_SINC] = &pl_filter_function_sinc,
> + [FILTER_JINC] = &pl_filter_function_jinc,
> + [FILTER_SPHINX] = &pl_filter_function_sphinx,
> + [FILTER_BCSPLINE] = &pl_filter_function_bcspline,
> + [FILTER_CATMULL_ROM] = &pl_filter_function_catmull_rom,
> + [FILTER_MITCHELL] = &pl_filter_function_mitchell,
> + [FILTER_ROBIDOUX] = &pl_filter_function_robidoux,
> + [FILTER_ROBIDOUXSHARP] = &pl_filter_function_robidouxsharp,
> + [FILTER_BICUBIC] = &pl_filter_function_bicubic,
> + [FILTER_SPLINE16] = &pl_filter_function_spline16,
> + [FILTER_SPLINE36] = &pl_filter_function_spline36,
> + [FILTER_SPLINE64] = &pl_filter_function_spline64,
> +};
> +
> +#define KERNEL_TEXT "Kernel function"
> +#define KERNEL_LONGTEXT "Main function defining the filter kernel."
> +
> +#define WINDOW_TEXT "Window function"
> +#define WINDOW_LONGTEXT "Window the kernel by an additional function.
> (Optional)"
> +
> +#define CLAMP_TEXT "Clamping coefficient"
> +#define CLAMP_LONGTEXT "If 1.0, clamp the kernel to only allow non-
> negative coefficients. If 0.0, no clamping is performed. Values in
> between are linear."
> +
> +#define BLUR_TEXT "Blur/Sharpen coefficient"
> +#define BLUR_LONGTEXT "If 1.0, no change is performed. Values below 1.0
> sharpen/narrow the kernel, values above 1.0 blur/widen the kernel. Avoid
> setting too low values!"
> +
> +#define TAPER_TEXT "Taper width"
> +#define TAPER_LONGTEXT "Taper the kernel - all inputs within the range
> [0, taper] will return 1.0, and the rest of the kernel is squished into
> (taper, radius]."
> +
> +#define POLAR_TEXT "Use as EWA / Polar filter"
> +#define POLAR_LONGTEXT "EWA/Polar filters are much slower but higher
> quality. Not all functions are good candidates. It's recommended to use
> jinc as the kernel."
> +
> +#define DEBAND_TEXT "Enable debanding"
> +#define DEBAND_LONGTEXT "Turns on the debanding step. This algorithm
> can be further tuned with the iterations and grain options."
> +
> +#define DEBAND_ITER_TEXT "Debanding iterations"
> +#define DEBAND_ITER_LONGTEXT "The number of debanding steps to perform
> per sample. Each step reduces a bit more banding, but takes time to
> compute. Note that the strength of each step falls off very quickly, so
> high numbers (>4) are practically useless. A value of 0 is a no-op."
> +
> +#define DEBAND_THRESH_TEXT "Gradient threshold"
> +#define DEBAND_THRESH_LONGTEXT "The debanding filter's cut-off
> threshold. Higher numbers increase the debanding strength dramatically,
> but progressively diminish image details."
> +
> +#define DEBAND_RADIUS_TEXT "Search radius"
> +#define DEBAND_RADIUS_LONGTEXT "The debanding filter's initial radius.
> The radius increases linearly for each iteration. A higher radius will
> find more gradients, but a lower radius will smooth more aggressively."
> +
> +#define DEBAND_GRAIN_TEXT "Grain strength"
> +#define DEBAND_GRAIN_LONGTEXT "Add some extra noise to the image. This
> significantly helps cover up remaining quantization artifacts. Higher
> numbers add more noise."
> +
> +#define SIGMOID_TEXT "Use sigmoidization when upscaling"
> +#define SIGMOID_LONGTEXT "If true, sigmoidizes the signal before
> upscaling. This helps prevent ringing artifacts. Not always in effect,
> even if enabled."
> +
> +#define SIGMOID_CENTER_TEXT "Sigmoid center"
> +#define SIGMOID_CENTER_LONGTEXT "The center (bias) of the sigmoid
> curve."
> +
> +#define SIGMOID_SLOPE_TEXT "Sigmoid slope"
> +#define SIGMOID_SLOPE_LONGTEXT "The slope (steepness) of the sigmoid
> curve."
> +
> +#define POLAR_CUTOFF_TEXT "Cut-off value for polar samplers"
> +#define POLAR_CUTOFF_LONGTEXT "As a micro-optimization, all samples
> with a weight below this value will be ignored. This reduces the need to
> perform unnecessary work that doesn't noticeably change the resulting
> image. Setting it to a value of 0.0 disables this optimization."
> +
> +#define SKIP_AA_TEXT "Disable anti-aliasing when downscaling"
> +#define SKIP_AA_LONGTEXT "This will result in moiré artifacts and
> nasty, jagged pixels when downscaling, except for some very limited
> special cases (e.g. bilinear downsampling to exactly 0.5x).
> Significantly speeds up downscaling with high downscaling ratios."
> +
> +#define OVERLAY_DIRECT_TEXT "Force GPU built-in sampling for overlay
> textures"
> +#define OVERLAY_DIRECT_LONGTEXT "Normally, the configured up/
> downscalers will be used when overlay textures (such as subtitles) need
> to be scaled up or down. Enabling this option overrides this behavior
> and forces overlay textures to go through the GPU's built-in sampling
> instead (typically bilinear)."
> +
> +#define DISABLE_LINEAR_TEXT "Don't linearize before scaling"
> +#define DISABLE_LINEAR_LONGTEXT "Normally, the image is converted to
> linear light before scaling (under certain conditions). Enabling this
> option disables this behavior."
> +
> +#define FORCE_GENERAL_TEXT "Force the use of general-purpose scalers"
> +#define FORCE_GENERAL_LONGTEXT "Normally, certain special scalers will
> be replaced by faster versions instead of going through the general
> scaler architecture. Enabling this option disables these optimizations."
> +
> #endif // VLC_PLACEBO_UTILS_H
> diff --git a/modules/video_output/vulkan/display.c b/modules/
> video_output/vulkan/display.c
> new file mode 100644
> index 0000000000..db3b127561
> --- /dev/null
> +++ b/modules/video_output/vulkan/display.c
> @@ -0,0 +1,762 @@
> +/**
> + * @file display.c
> + * @brief Vulkan video output module
> + */
> +/
> *****************************************************************************
> + * Copyright © 2018 Niklas Haas
> + *
> + * This program 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.
> + *
> + * This program 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 this program; if not, write to the Free Software
> Foundation,
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
> +
> *****************************************************************************/
> +
> +#ifdef HAVE_CONFIG_H
> +# include <config.h>
> +#endif
> +
> +
> +#ifdef HAVE_CONFIG_H
> +# include <config.h>
> +#endif
> +
That's one too many :)
> +#include <stdlib.h>
> +#include <assert.h>
> +
> +#include <vlc_common.h>
> +#include <vlc_plugin.h>
> +#include <vlc_vout_display.h>
> +
> +#include "../placebo_utils.h"
> +#include "vk_instance.h"
> +
> +#include <libplacebo/renderer.h>
> +#include <libplacebo/utils/upload.h>
> +#include <libplacebo/swapchain.h>
> +#include <libplacebo/vulkan.h>
> +
> +#define VLCVK_MAX_BUFFERS 128
> +
> +struct vout_display_sys_t
> +{
> + vlc_vk_t *vk;
> + const struct pl_tex *plane_tex[4];
> + struct pl_renderer *renderer;
> + picture_pool_t *pool;
> +
> + // Pool of textures for the subpictures
> + struct pl_overlay *overlays;
> + const struct pl_tex **overlay_tex;
> + int num_overlays;
> +
> + // Dynamic during rendering
> + vout_display_place_t place;
> + uint64_t counter;
> +
> + // Mapped buffers
> + picture_t *pics[VLCVK_MAX_BUFFERS];
> + unsigned long long list; // bitset of available pictures
> +
> + // Storage for rendering parameters
> + struct pl_filter_config upscaler;
> + struct pl_filter_config downscaler;
> + struct pl_deband_params deband;
> + struct pl_sigmoid_params sigmoid;
> + struct pl_color_map_params color_map;
> + struct pl_dither_params dither;
> + struct pl_render_params params;
> + struct pl_color_space target;
> + int dither_depth;
> +};
> +
> +struct picture_sys
> +{
> + vlc_vk_t *vk;
> + unsigned index;
> + const struct pl_buf *buf;
> +};
> +
> +// Display callbacks
> +static picture_pool_t *Pool(vout_display_t *, unsigned);
> +static void PictureRender(vout_display_t *, picture_t *, subpicture_t
> *, mtime_t);
> +static void PictureDisplay(vout_display_t *, picture_t *, subpicture_t
> *);
> +static int Control(vout_display_t *, int, va_list);
> +static void PollBuffers(vout_display_t *);
> +static void UpdateParams(vout_display_t *);
> +
> +// Allocates a Vulkan surface and instance for video output.
> +static int Open(vlc_object_t *obj)
> +{
> + vout_display_t *vd = (vout_display_t *) obj;
> + vout_display_sys_t *sys = vd->sys = vlc_obj_calloc(obj, 1, sizeof
> (*sys));
> + if (unlikely(sys == NULL))
> + return VLC_ENOMEM;
> +
> + vout_window_t *window = vd->cfg->window;
> + if (window == NULL)
> + {
> + msg_Err(vd, "parent window not available");
> + goto error;
> + }
> +
> + sys->vk = vlc_vk_Create(window, NULL);
> + if (sys->vk == NULL)
> + goto error;
> +
> + const struct pl_gpu *gpu = sys->vk->vulkan->gpu;
> + sys->renderer = pl_renderer_create(sys->vk->ctx, gpu);
> + if (!sys->renderer)
> + goto error;
> +
> + // Attempt using the input format as the display format
> + if (vlc_placebo_FormatSupported(gpu, vd->source.i_chroma)) {
> + vd->fmt.i_chroma = vd->source.i_chroma;
> + } else {
> + const vlc_fourcc_t *fcc;
> + for (fcc = vlc_fourcc_GetFallback(vd->source.i_chroma); *fcc;
> fcc++) {
> + if (vlc_placebo_FormatSupported(gpu, *fcc)) {
> + vd->fmt.i_chroma = *fcc;
> + break;
> + }
> + }
> +
> + if (!vd->fmt.i_chroma) {
> + vd->fmt.i_chroma = VLC_CODEC_RGBA;
> + msg_Warn(vd, "Failed picking any suitable input format,
> falling "
> + "back to RGBA for sanity!");
> + }
> + }
> +
> + // Hard-coded list of supported subtitle chromas (non-planar only!)
> + static const vlc_fourcc_t subfmts[] = {
> + VLC_CODEC_RGBA,
> + VLC_CODEC_BGRA,
> + VLC_CODEC_RGB8,
> + VLC_CODEC_RGB12,
> + VLC_CODEC_RGB15,
> + VLC_CODEC_RGB16,
> + VLC_CODEC_RGB24,
> + VLC_CODEC_RGB32,
> + VLC_CODEC_GREY,
> + 0
> + };
> +
> + vd->info.subpicture_chromas = subfmts;
> +
> + vd->pool = Pool;
> + vd->prepare = PictureRender;
> + vd->display = PictureDisplay;
> + vd->control = Control;
> +
> + UpdateParams(vd);
> + return VLC_SUCCESS;
> +
> +error:
> + pl_renderer_destroy(&sys->renderer);
> + if (sys->vk != NULL)
> + vlc_vk_Release(sys->vk);
> + vlc_obj_free(obj, sys);
I believe this is done automatically
> + return VLC_EGENERIC;
> +}
> +
> +static void Close(vlc_object_t *obj)
> +{
> + vout_display_t *vd = (vout_display_t *)obj;
> + vout_display_sys_t *sys = vd->sys;
> + const struct pl_gpu *gpu = sys->vk->vulkan->gpu;
> +
> + for (int i = 0; i < 4; i++)
> + pl_tex_destroy(gpu, &sys->plane_tex[i]);
> +
> + for (int i = 0; i < sys->num_overlays; i++)
> + pl_tex_destroy(gpu, &sys->overlay_tex[i]);
> +
> + if (sys->overlays) {
> + free(sys->overlays);
> + free(sys->overlay_tex);
> + }
> +
> + pl_renderer_destroy(&sys->renderer);
> +
> + PollBuffers(vd);
> + if (sys->pool)
> + picture_pool_Release(sys->pool);
> +
> + vlc_vk_Release(sys->vk);
> + vlc_obj_free(obj, sys);
Same here
> +}
> +
> +static void DestroyPicture(picture_t *pic)
> +{
> + struct picture_sys *picsys = pic->p_sys;
> + const struct pl_gpu *gpu = picsys->vk->vulkan->gpu;
> +
> + pl_buf_destroy(gpu, &picsys->buf);
> + vlc_vk_Release(picsys->vk);
> +}
> +
> +static picture_t *CreatePicture(vout_display_t *vd)
> +{
> + vout_display_sys_t *sys = vd->sys;
> + const struct pl_gpu *gpu = sys->vk->vulkan->gpu;
> +
> + struct picture_sys *picsys = calloc(1, sizeof(*picsys));
> + if (unlikely(picsys == NULL))
> + return NULL;
> +
> + picture_t *pic = picture_NewFromResource(&vd->fmt,
> &(picture_resource_t) {
> + .p_sys = picsys,
> + .pf_destroy = DestroyPicture,
> + });
> +
> + if (!pic) {
> + free(picsys);
> + return NULL;
> + }
> +
> + picsys->vk = sys->vk;
> + vlc_vk_Hold(picsys->vk);
> +
> + // XXX: needed since picture_NewFromResource override pic planes
> + // cf. opengl display.c
> + if (picture_Setup(pic, &vd->fmt) != VLC_SUCCESS) {
> + picture_Release(pic);
> + return NULL;
> + }
> +
> + size_t buf_size = 0;
> + size_t offsets[PICTURE_PLANE_MAX];
> + for (int i = 0; i < pic->i_planes; i++)
> + {
> + const plane_t *p = &pic->p[i];
> +
> + if (p->i_pitch < 0 || p->i_lines <= 0 ||
> + (size_t) p->i_pitch > SIZE_MAX/p->i_lines)
> + {
> + picture_Release(pic);
> + return NULL;
> + }
> + offsets[i] = buf_size;
> + buf_size += p->i_pitch * p->i_lines;
> + }
> +
> + // Round up for alignment
> + buf_size = buf_size + 15 / 16 * 16;
> +
> + picsys->buf = pl_buf_create(gpu, &(struct pl_buf_params) {
> + .type = PL_BUF_TEX_TRANSFER,
> + .size = buf_size,
> + .host_mapped = true,
> + });
> +
> + if (!picsys->buf) {
> + picture_Release(pic);
> + return NULL;
> + }
> +
> + for (int i = 0; i < pic->i_planes; ++i)
> + pic->p[i].p_pixels = (void *) &picsys->buf->data[offsets[i]];
> +
> + return pic;
> +}
> +
> +static picture_pool_t *Pool(vout_display_t *vd, unsigned
> requested_count)
> +{
> + assert(requested_count <= VLCVK_MAX_BUFFERS);
> + vout_display_sys_t *sys = vd->sys;
> + if (sys->pool)
> + return sys->pool;
> +
> + unsigned count;
> + picture_t *pictures[requested_count];
I'm not sure we want more VLAs
> + for (count = 0; count < requested_count; count++)
> + {
> + pictures[count] = CreatePicture(vd);
> + if (!pictures[count])
> + break;
> +
> + struct picture_sys *picsys = pictures[count]->p_sys;
> + picsys->index = count;
> + }
> +
> + if (count <= 1)
> + goto error;
> +
> + sys->pool = picture_pool_New(count, pictures);
> + if (!sys->pool)
> + goto error;
> +
> + return sys->pool;
> +
> +error:
> + for (unsigned i = 0; i < count; i++) {
> + picture_Release(pictures[i]);
> + sys->pics[i] = NULL;
> + }
> +
> + // Fallback to a regular memory pool
> + sys->pool = picture_pool_NewFromFormat(&vd->fmt, requested_count);
> + return sys->pool;
> +}
> +
> +// Garbage collect all buffers that can be re-used
> +static void PollBuffers(vout_display_t *vd)
> +{
> + vout_display_sys_t *sys = vd->sys;
> + const struct pl_gpu *gpu = sys->vk->vulkan->gpu;
> + unsigned long long list = sys->list;
> +
> + // Release all pictures that are not used by the GPU anymore
> + while (list != 0) {
> + int i = ctz(list);
> + picture_t *pic = sys->pics[i];
> + assert(pic);
> + struct picture_sys *picsys = pic->p_sys;
> + assert(picsys);
> +
> + if (!pl_buf_poll(gpu, picsys->buf, 0)) {
> + sys->list &= ~(1ULL << i);
> + sys->pics[i] = NULL;
> + picture_Release(pic);
> + }
> +
> + list &= ~(1ULL << i);
> + }
> +}
> +
> +static void PictureRender(vout_display_t *vd, picture_t *pic,
> + subpicture_t *subpicture, mtime_t date)
> +{
> + VLC_UNUSED(date);
> + vout_display_sys_t *sys = vd->sys;
> + const struct pl_gpu *gpu = sys->vk->vulkan->gpu;
> + bool failed = false;
> +
> + struct pl_swapchain_frame frame;
> + if (!pl_swapchain_start_frame(sys->vk->swapchain, &frame))
> + return; // Probably benign error, ignore it
> +
> + struct pl_image img = {
> + .signature = sys->counter++,
> + .num_planes = pic->i_planes,
> + .width = pic->format.i_visible_width,
> + .height = pic->format.i_visible_height,
> + .color = vlc_placebo_ColorSpace(&vd->fmt),
> + .repr = vlc_placebo_ColorRepr(&vd->fmt),
> + .src_rect = {
> + .x0 = pic->format.i_x_offset,
> + .y0 = pic->format.i_y_offset,
> + .x1 = pic->format.i_x_offset + pic->format.i_visible_width,
> + .y1 = pic->format.i_y_offset + pic-
> >format.i_visible_height,
> + },
> + };
> +
> + // Upload the image data for each plane
> + struct pl_plane_data data[4];
> + struct picture_sys *picsys = pic->p_sys;
> + if (!vlc_placebo_PlaneData(pic, data, picsys ? picsys->buf : NULL))
> {
> + // This should never happen, in theory
> + assert(!"Failed processing the picture_t into
> pl_plane_data!?");
> + }
> +
> + for (int i = 0; i < pic->i_planes; i++) {
> + struct pl_plane *plane = &img.planes[i];
> + if (!pl_upload_plane(gpu, plane, &sys->plane_tex[i], &data[i]))
> {
> + msg_Err(vd, "Failed uploading image data!");
> + failed = true;
> + goto done;
> + }
> +
> + // Matches only the chroma planes, never luma or alpha
> + if (vlc_fourcc_IsYUV(vd->fmt.i_chroma) && i != 0 && i != 3) {
> + enum pl_chroma_location loc = vlc_placebo_ChromaLoc(&vd-
> >fmt);
> + pl_chroma_location_offset(loc, &plane->shift_x, &plane-
> >shift_y);
> + }
> + }
> +
> + // If this was a mapped buffer, mark it as in use by the GPU
> + if (picsys) {
> + unsigned index = picsys->index;
> + if (sys->pics[index] == NULL) {
> + sys->list |= 1ULL << index;
> + sys->pics[index] = pic;
> + picture_Hold(pic);
> + }
> + }
> +
> + // Garbage collect all previously used mapped buffers
> + PollBuffers(vd);
> +
> + struct pl_render_target target;
> + pl_render_target_from_swapchain(&target, &frame);
> + target.dst_rect = (struct pl_rect2d) {
> + .x0 = sys->place.x,
> + .y0 = sys->place.y,
> + .x1 = sys->place.x + sys->place.width,
> + .y1 = sys->place.y + sys->place.height,
> + };
> +
> + // Override the target colorimetry only if the user requests it
> + if (sys->target.primaries)
> + target.color.primaries = sys->target.primaries;
> + if (sys->target.transfer) {
> + target.color.transfer = sys->target.transfer;
> + target.color.light = PL_COLOR_LIGHT_UNKNOWN; // re-infer
> + }
> + if (sys->target.sig_avg > 0.0)
> + target.color.sig_avg = sys->target.sig_avg;
> + if (sys->dither_depth > 0) {
> + // override the sample depth without affecting the color
> encoding
> + struct pl_bit_encoding *bits = &target.repr.bits;
> + float scale = bits->color_depth / bits->sample_depth;
> + bits->sample_depth = sys->dither_depth;
> + bits->color_depth = scale * sys->dither_depth;
> + }
> +
> + if (subpicture) {
> + int num_regions = 0;
> + for (subpicture_region_t *r = subpicture->p_region; r; r = r-
> >p_next)
> + num_regions++;
> +
> + // Grow the overlays array if needed
> + if (num_regions > sys->num_overlays) {
> + sys->overlays = realloc(sys->overlays, num_regions *
> sizeof(struct pl_overlay));
> + sys->overlay_tex = realloc(sys->overlay_tex, num_regions *
> sizeof(struct pl_tex *));
> + if (!sys->overlays || !sys->overlay_tex) {
> + // Unlikely OOM, just do whatever
> + sys->num_overlays = 0;
> + failed = true;
> + goto done;
> + }
> + // Clear the newly added texture pointers for
> pl_upload_plane
> + for (int i = sys->num_overlays; i < num_regions; i++)
> + sys->overlay_tex[i] = NULL;
> + sys->num_overlays = num_regions;
> + }
> +
> + // Upload all of the regions
> + subpicture_region_t *r = subpicture->p_region;
> + for (int i = 0; i < num_regions; i++) {
> + assert(r->p_picture->i_planes == 1);
> + struct pl_plane_data subdata;
> + if (!vlc_placebo_PlaneData(r->p_picture, &subdata, NULL))
> + assert(!"Failed processing the subpicture_t into
> pl_plane_data!?");
> +
> + struct pl_overlay *overlay = &sys->overlays[i];
> + *overlay = (struct pl_overlay) {
> + .rect = {
> + .x0 = target.dst_rect.x0 + r->i_x,
> + .y0 = target.dst_rect.y0 + r->i_y,
> + .x1 = target.dst_rect.x0 + r->i_x + r-
> >fmt.i_visible_width,
> + .y1 = target.dst_rect.y0 + r->i_y + r-
> >fmt.i_visible_height,
> + },
> + .mode = PL_OVERLAY_NORMAL,
> + .color = vlc_placebo_ColorSpace(&r->fmt),
> + .repr = vlc_placebo_ColorRepr(&r->fmt),
> + };
> +
> + if (!pl_upload_plane(gpu, &overlay->plane, &sys-
> >overlay_tex[i], &subdata)) {
> + msg_Err(vd, "Failed uploading subpicture region!");
> + num_regions = i; // stop here
> + break;
> + }
> + }
> +
> + // Update the target information to reference the subpictures
> + target.overlays = sys->overlays;
> + target.num_overlays = num_regions;
> + }
> +
> + // If we don't cover the entire output, clear it first
> + struct pl_rect2d full = {0, 0, frame.fbo->params.w, frame.fbo-
> >params.h };
> + if (!pl_rect2d_eq(target.dst_rect, full)) {
> + // TODO: make background color configurable?
> + pl_tex_clear(gpu, frame.fbo, (float[4]){ 0.0, 0.0, 0.0, 0.0 });
> + }
> +
> + // Dispatch the actual image rendering with the pre-configured
> parameters
> + if (!pl_render_image(sys->renderer, &img, &target, &sys->params)) {
> + msg_Err(vd, "Failed rendering frame!");
> + failed = true;
> + goto done;
> + }
> +
> +done:
> +
> + if (failed)
> + pl_tex_clear(gpu, frame.fbo, (float[4]){ 1.0, 0.0, 0.0, 1.0 });
> +
> + if (!pl_swapchain_submit_frame(sys->vk->swapchain)) {
> + msg_Err(vd, "Failed rendering frame!");
> + return;
> + }
> +}
> +
> +static void PictureDisplay(vout_display_t *vd, picture_t *pic,
> subpicture_t *subpicture)
> +{
> + picture_Release(pic);
> + if (subpicture)
> + subpicture_Delete(subpicture);
> +
> + vout_display_sys_t *sys = vd->sys;
> + pl_swapchain_swap_buffers(sys->vk->swapchain);
> +}
> +
> +static int Control(vout_display_t *vd, int query, va_list ap)
> +{
> + vout_display_sys_t *sys = vd->sys;
> +
> + switch (query)
> + {
> + case VOUT_DISPLAY_RESET_PICTURES:
> + abort();
assert(false) would be less extreme :)
> +
> + case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
> + case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
> + case VOUT_DISPLAY_CHANGE_ZOOM: {
> + vout_display_cfg_t cfg = *va_arg (ap, const vout_display_cfg_t
> *);
> + vout_display_PlacePicture(&sys->place, &vd->source, &cfg,
> false);
> + return VLC_SUCCESS;
> + }
> +
> + case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
> + case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
> + vout_display_PlacePicture(&sys->place, &vd->source, vd->cfg,
> false);
> + return VLC_SUCCESS;
> +
> + default:
> + msg_Err (vd, "Unknown request %d", query);
> + }
> +
> + return VLC_EGENERIC;
> +}
> +
> +// Options
> +
> +#define VK_TEXT N_("Vulkan surface extension")
> +#define PROVIDER_LONGTEXT N_( \
> + "Extension which provides the Vulkan surface to use.")
> +
> +vlc_module_begin () set_shortname (N_("Vulkan"))
> + set_description (N_("Vulkan video output"))
> + set_category (CAT_VIDEO)
> + set_subcategory (SUBCAT_VIDEO_VOUT)
> + set_capability ("vout display", 300)
> + set_callbacks (Open, Close)
> + add_shortcut ("vulkan", "vk")
> + add_module ("vk", "vulkan", NULL,
> + VK_TEXT, PROVIDER_LONGTEXT)
> +
> + set_section(N_("Scaling"), NULL)
> + add_integer("upscaler-preset", SCALE_BUILTIN,
> + UPSCALER_PRESET_TEXT, SCALER_PRESET_LONGTEXT, false)
> + change_integer_list(scale_values, scale_text)
> + add_integer("downscaler-preset", SCALE_BUILTIN,
> + DOWNSCALER_PRESET_TEXT, SCALER_PRESET_LONGTEXT, false)
> + change_integer_list(scale_values, scale_text)
> + add_integer_with_range("lut-entries", 64, 16, 256,
> + LUT_ENTRIES_TEXT, LUT_ENTRIES_LONGTEXT, false)
> + add_float_with_range("antiringing", 0.0,
> + 0.0, 1.0, ANTIRING_TEXT, ANTIRING_LONGTEXT, false)
> + add_bool("sigmoid", !!pl_render_default_params.sigmoid_params,
> + SIGMOID_TEXT, SIGMOID_LONGTEXT, true)
> + add_float_with_range("sigmoid-center",
> pl_sigmoid_default_params.center,
> + 0., 1., SIGMOID_CENTER_TEXT, SIGMOID_CENTER_LONGTEXT, true)
> + add_float_with_range("sigmoid-slope",
> pl_sigmoid_default_params.slope,
> + 1., 20., SIGMOID_SLOPE_TEXT, SIGMOID_SLOPE_LONGTEXT, true)
> +
> + set_section(N_("Debanding"), NULL)
> + add_bool("debanding", false, DEBAND_TEXT, DEBAND_LONGTEXT, false)
> + add_integer("iterations", pl_deband_default_params.iterations,
> + DEBAND_ITER_TEXT, DEBAND_ITER_LONGTEXT, false)
> + add_float("threshold", pl_deband_default_params.threshold,
> + DEBAND_THRESH_TEXT, DEBAND_THRESH_LONGTEXT, false)
> + add_float("radius", pl_deband_default_params.radius,
> + DEBAND_RADIUS_TEXT, DEBAND_RADIUS_LONGTEXT, false)
> + add_float("grain", pl_deband_default_params.grain,
> + DEBAND_GRAIN_TEXT, DEBAND_GRAIN_LONGTEXT, false)
> +
> + set_section(N_("Colorspace conversion"), NULL)
> + add_integer("intent", pl_color_map_default_params.intent,
> + RENDER_INTENT_TEXT, RENDER_INTENT_LONGTEXT, false)
> + change_integer_list(intent_values, intent_text)
> + add_integer("target-prim", PL_COLOR_PRIM_UNKNOWN, PRIM_TEXT,
> PRIM_LONGTEXT, false) \
> + change_integer_list(prim_values, prim_text) \
> + add_integer("target-trc", PL_COLOR_TRC_UNKNOWN, TRC_TEXT,
> TRC_LONGTEXT, false) \
> + change_integer_list(trc_values, trc_text) \
> +
> + // TODO: support for ICC profiles / 3DLUTs.. we will need some way
> of loading
> + // this from the operating system / user
> +
> + set_section(N_("Tone mapping"), NULL)
> + add_integer("tone-mapping",
> pl_color_map_default_params.tone_mapping_algo,
> + TONEMAPPING_TEXT, TONEMAPPING_LONGTEXT, false)
> + change_integer_list(tone_values, tone_text)
> + add_float("tone-mapping-param",
> pl_color_map_default_params.tone_mapping_param,
> + TONEMAP_PARAM_TEXT, TONEMAP_PARAM_LONGTEXT, true)
> + add_float("tone-mapping-desat",
> pl_color_map_default_params.tone_mapping_desaturate,
> + TONEMAP_DESAT_TEXT, TONEMAP_DESAT_LONGTEXT, false)
> + add_bool("gamut-warning", false, GAMUT_WARN_TEXT,
> GAMUT_WARN_LONGTEXT, true)
> + add_integer_with_range("peak-frames",
> pl_color_map_default_params.peak_detect_frames,
> + 0, 255, PEAK_FRAMES_TEXT, PEAK_FRAMES_LONGTEXT, false)
> + add_float_with_range("target-avg", 0.25,
> + 0.0, 1.0, TARGET_AVG_TEXT, TARGET_AVG_LONGTEXT, false)
> + add_float_with_range("scene-threshold",
> pl_color_map_default_params.scene_threshold,
> + 0., 10., SCENE_THRESHOLD_TEXT, SCENE_THRESHOLD_LONGTEXT,
> false)
> +
> + set_section(N_("Dithering"), NULL)
> + add_integer("dither", -1,
> + DITHER_TEXT, DITHER_LONGTEXT, false)
> + change_integer_list(dither_values, dither_text)
> + add_integer_with_range("dither-size",
> pl_dither_default_params.lut_size,
> + 1, 8, DITHER_SIZE_TEXT, DITHER_SIZE_LONGTEXT, false)
> + add_bool("temporal-dither", pl_dither_default_params.temporal,
> + TEMPORAL_DITHER_TEXT, TEMPORAL_DITHER_LONGTEXT, false)
> + add_integer_with_range("dither-depth", 0,
> + 0, 16, DITHER_DEPTH_TEXT, DITHER_DEPTH_LONGTEXT, false)
> +
> + set_section(N_("Custom upscaler (when preset = custom)"), NULL)
> + add_integer("upscaler-kernel", FILTER_BOX,
> + KERNEL_TEXT, KERNEL_LONGTEXT, true)
> + change_integer_list(filter_values, filter_text)
> + add_integer("upscaler-window", FILTER_NONE,
> + WINDOW_TEXT, WINDOW_LONGTEXT, true)
> + change_integer_list(filter_values, filter_text)
> + add_bool("upscaler-polar", false, POLAR_TEXT, POLAR_LONGTEXT, true)
> + add_float_with_range("upscaler-clamp", 0.0,
> + 0.0, 1.0, CLAMP_TEXT, CLAMP_LONGTEXT, true)
> + add_float_with_range("upscaler-blur", 1.0,
> + 0.0, 100.0, BLUR_TEXT, BLUR_LONGTEXT, true)
> + add_float_with_range("upscaler-taper", 0.0,
> + 0.0, 10.0, TAPER_TEXT, TAPER_LONGTEXT, true)
> +
> + set_section(N_("Custom downscaler (when preset = custom)"), NULL)
> + add_integer("downscaler-kernel", FILTER_BOX,
> + KERNEL_TEXT, KERNEL_LONGTEXT, true)
> + change_integer_list(filter_values, filter_text)
> + add_integer("downscaler-window", FILTER_NONE,
> + WINDOW_TEXT, WINDOW_LONGTEXT, true)
> + change_integer_list(filter_values, filter_text)
> + add_bool("downscaler-polar", false, POLAR_TEXT, POLAR_LONGTEXT,
> true)
> + add_float_with_range("downscaler-clamp", 0.0,
> + 0.0, 1.0, CLAMP_TEXT, CLAMP_LONGTEXT, true)
> + add_float_with_range("downscaler-blur", 1.0,
> + 0.0, 100.0, BLUR_TEXT, BLUR_LONGTEXT, true)
> + add_float_with_range("downscaler-taper", 0.0,
> + 0.0, 10.0, TAPER_TEXT, TAPER_LONGTEXT, true)
> +
> + set_section(N_("Performance tweaks / debugging"), NULL)
> + add_bool("skip-aa", false, SKIP_AA_TEXT, SKIP_AA_LONGTEXT, false)
> + add_float_with_range("polar-cutoff", 0.001,
> + 0., 1., POLAR_CUTOFF_TEXT, POLAR_CUTOFF_LONGTEXT, false)
> + add_bool("overlay-direct", false, OVERLAY_DIRECT_TEXT,
> OVERLAY_DIRECT_LONGTEXT, false)
> + add_bool("disable-linear", false, DISABLE_LINEAR_TEXT,
> DISABLE_LINEAR_LONGTEXT, false)
> + add_bool("force-general", false, FORCE_GENERAL_TEXT,
> FORCE_GENERAL_LONGTEXT, false)
> +
> +vlc_module_end ()
> +
> +// Update the renderer settings based on the current configuration.
> +//
> +// XXX: This could be called every time the parameters change, but
> currently
> +// VLC does not allow that - so we're stuck with doing it once on
> Open().
> +// Should be changed as soon as it's possible!
> +static void UpdateParams(vout_display_t *vd)
> +{
> + vout_display_sys_t *sys = vd->sys;
> +
> + sys->deband = pl_deband_default_params;
> + sys->deband.iterations = var_InheritInteger(vd, "iterations");
> + sys->deband.threshold = var_InheritFloat(vd, "threshold");
> + sys->deband.radius = var_InheritFloat(vd, "radius");
> + sys->deband.grain = var_InheritFloat(vd, "grain");
> + bool use_deband = sys->deband.iterations > 0 || sys->deband.grain >
> 0;
> + use_deband &= var_InheritBool(vd, "debanding");
> +
> + sys->sigmoid = pl_sigmoid_default_params;
> + sys->sigmoid.center = var_InheritFloat(vd, "sigmoid-center");
> + sys->sigmoid.slope = var_InheritFloat(vd, "sigmoid-slope");
> + bool use_sigmoid = var_InheritBool(vd, "sigmoid");
> +
> + sys->color_map = pl_color_map_default_params;
> + sys->color_map.intent = var_InheritInteger(vd, "intent");
> + sys->color_map.tone_mapping_algo = var_InheritInteger(vd, "tone-
> mapping");
> + sys->color_map.tone_mapping_param = var_InheritFloat(vd, "tone-
> mapping-param");
> + sys->color_map.tone_mapping_desaturate = var_InheritFloat(vd,
> "tone-mapping-desat");
> + sys->color_map.gamut_warning = var_InheritBool(vd, "gamut-
> warning");
> + sys->color_map.peak_detect_frames = var_InheritInteger(vd, "peak-
> frames");
> + sys->color_map.scene_threshold = var_InheritFloat(vd, "scene-
> threshold");
> +
> + sys->dither = pl_dither_default_params;
> + int method = var_InheritInteger(vd, "dither");
> + bool use_dither = method >= 0;
> + sys->dither.method = use_dither ? method : 0;
> + sys->dither.lut_size = var_InheritInteger(vd, "dither-size");
> + sys->dither.temporal = var_InheritBool(vd, "temporal-dither");
> +
> + sys->params = pl_render_default_params;
> + sys->params.deband_params = use_deband ? &sys->deband : NULL;
> + sys->params.sigmoid_params = use_sigmoid ? &sys->sigmoid : NULL;
> + sys->params.color_map_params = &sys->color_map;
> + sys->params.dither_params = use_dither ? &sys->dither : NULL;
> + sys->params.lut_entries = var_InheritInteger(vd, "lut-entries");
> + sys->params.antiringing_strength = var_InheritFloat(vd,
> "antiringing");
> + sys->params.skip_anti_aliasing = var_InheritBool(vd, "skip-aa");
> + sys->params.polar_cutoff = var_InheritFloat(vd, "polar-cutoff");
> + sys->params.disable_overlay_sampling = var_InheritBool(vd,
> "overlay-direct");
> + sys->params.disable_linear_scaling = var_InheritBool(vd, "disable-
> linear");
> + sys->params.disable_builtin_scalers = var_InheritBool(vd, "force-
> general");
> +
> + int preset = var_InheritInteger(vd, "upscaler-preset");
> + sys->params.upscaler = scale_config[preset];
> + if (preset == SCALE_CUSTOM) {
> + sys->params.upscaler = &sys->upscaler;
> + sys->upscaler = (struct pl_filter_config) {
> + .kernel = filter_fun[var_InheritInteger(vd, "upscaler-
> kernel")],
> + .window = filter_fun[var_InheritInteger(vd, "upscaler-
> window")],
> + .clamp = var_InheritFloat(vd, "upscaler-clamp"),
> + .blur = var_InheritFloat(vd, "upscaler-blur"),
> + .taper = var_InheritFloat(vd, "upscaler-taper"),
> + .polar = var_InheritBool(vd, "upscaler-polar"),
> + };
> +
> + if (!sys->upscaler.kernel) {
> + msg_Err(vd, "Tried specifying a custom upscaler with no
> kernel!");
> + sys->params.upscaler = NULL;
> + }
> + };
> +
> + preset = var_InheritInteger(vd, "downscaler-preset");
> + sys->params.downscaler = scale_config[preset];
> + if (preset == SCALE_CUSTOM) {
> + sys->params.downscaler = &sys->downscaler;
> + sys->downscaler = (struct pl_filter_config) {
> + .kernel = filter_fun[var_InheritInteger(vd, "downscaler-
> kernel")],
> + .window = filter_fun[var_InheritInteger(vd, "downscaler-
> window")],
> + .clamp = var_InheritFloat(vd, "downscaler-clamp"),
> + .blur = var_InheritFloat(vd, "downscaler-blur"),
> + .taper = var_InheritFloat(vd, "downscaler-taper"),
> + .polar = var_InheritBool(vd, "downscaler-polar"),
> + };
> +
> + if (!sys->downscaler.kernel) {
> + msg_Err(vd, "Tried specifying a custom downscaler with no
> kernel!");
> + sys->params.downscaler = NULL;
> + }
> + };
> +
> + sys->dither_depth = var_InheritInteger(vd, "dither-depth");
> + sys->target = (struct pl_color_space) {
> + .primaries = var_InheritInteger(vd, "target-prim"),
> + .transfer = var_InheritInteger(vd, "target-trc"),
> + .sig_avg = var_InheritFloat(vd, "target-avg"),
> + };
> +}
> diff --git a/modules/video_output/vulkan/surface.c b/modules/
> video_output/vulkan/surface.c
> new file mode 100644
> index 0000000000..31865bd951
> --- /dev/null
> +++ b/modules/video_output/vulkan/surface.c
> @@ -0,0 +1,220 @@
> +/**
> + * @file surface.c
> + * @brief Vulkan platform-specific surface extension module
> + */
> +/
> *****************************************************************************
> + * Copyright © 2018 Niklas Haas, Marvin Scholz
> + *
> + * This program 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.
> + *
> + * This program 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 this program; if not, write to the Free Software
> Foundation,
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
> +
> *****************************************************************************/
> +
> +#ifdef HAVE_CONFIG_H
> +# include <config.h>
> +#endif
> +
> +#include <stdlib.h>
> +#include <assert.h>
> +
> +#include <vlc_common.h>
> +#include <vlc_plugin.h>
> +#include <vlc_vout_window.h>
> +
> +#ifdef VK_USE_PLATFORM_XLIB_KHR
> +
> +#include <vlc_xlib.h>
> +#define MODULE_NAME N_("Vulkan context (Xlib)")
> +
> +#else
> +#error Trying to build vulkan/surface.c without any platform defined!
> +#endif
> +
> +#include "../placebo_utils.h"
> +#include "vk_instance.h"
> +
> +static int Open (vlc_object_t *obj)
> +{
> + vlc_vk_t *vk = (vlc_vk_t *) obj;
> + const char *surf_extension;
> +
> +#ifdef VK_USE_PLATFORM_XLIB_KHR
> +
> + if (vk->window->type != VOUT_WINDOW_TYPE_XID || !
> vlc_xlib_init(obj))
> + return VLC_EGENERIC;
> +
> + // Initialize X11 display
> + Display *dpy = vk->sys = XOpenDisplay(vk->window->display.x11);
> + if (dpy == NULL)
> + return VLC_EGENERIC;
> +
> + surf_extension = VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
> +
> +#endif
> +
> + // Initialize Vulkan instance
> + vk->ctx = vlc_placebo_Create(VLC_OBJECT(vk));
> + if (!vk->ctx)
> + goto error;
> +
> + vk->instance = pl_vk_inst_create(vk->ctx, &(struct
> pl_vk_inst_params) {
> + .debug = var_InheritBool(vk, "vk-debug"),
> + .extensions = (const char *[]) {
> + VK_KHR_SURFACE_EXTENSION_NAME,
> + surf_extension,
> + },
> + .num_extensions = 2,
> + });
> + if (!vk->instance)
> + goto error;
> +
> + // Create the platform-specific surface object
> + const VkInstance vkinst = vk->instance->instance;
> +#ifdef VK_USE_PLATFORM_XLIB_KHR
> +
> + VkXlibSurfaceCreateInfoKHR xinfo = {
> + .sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
> + .dpy = dpy,
> + .window = (Window) vk->window->handle.xid,
> + };
> +
> + VkResult res = vkCreateXlibSurfaceKHR(vkinst, &xinfo, NULL, &vk-
> >surface);
> +
> +#endif
> +
> + if (res != VK_SUCCESS)
> + goto error;
> +
> + // Create vulkan device
> + vk->vulkan = pl_vulkan_create(vk->ctx, &(struct pl_vulkan_params) {
> + .instance = vkinst,
> + .surface = vk->surface,
> + .device_name = var_InheritString(vk, "vk-device"),
This seems likely to leak in case of error
> + .allow_software = var_InheritBool(vk, "allow-sw"),
> + .async_transfer = var_InheritBool(vk, "async-xfer"),
> + .async_compute = var_InheritBool(vk, "async-comp"),
> + .queue_count = var_InheritInteger(vk, "queue-count"),
> + });
> + if (!vk->vulkan)
> + goto error;
> +
> + // Create swapchain for this surface
> + struct pl_vulkan_swapchain_params swap_params = {
> + .surface = vk->surface,
> + .present_mode = var_InheritInteger(vk, "present-mode"),
> + .swapchain_depth = var_InheritInteger(vk, "queue-depth"),
> + };
> +
> + vk->swapchain = pl_vulkan_create_swapchain(vk->vulkan,
> &swap_params);
> + if (!vk->swapchain)
> + goto error;
> +
> + return VLC_SUCCESS;
> +
> +error:
> + pl_swapchain_destroy(&vk->swapchain);
> + if (vk->surface)
> + vkDestroySurfaceKHR(vk->instance->instance, vk->surface, NULL);
> +
> + pl_vulkan_destroy(&vk->vulkan);
> + pl_vk_inst_destroy(&vk->instance);
> + pl_context_destroy(&vk->ctx);
> +
> +#ifdef VK_USE_PLATFORM_XLIB_KHR
> + if (dpy)
> + XCloseDisplay(dpy);
> +#endif
> +
> + return VLC_EGENERIC;
> +}
> +
> +static void Close (vlc_object_t *obj)
> +{
> + vlc_vk_t *vk = (vlc_vk_t *) obj;
> +
> + vkDestroySurfaceKHR(vk->instance->instance, vk->surface, NULL);
> + pl_vk_inst_destroy(&vk->instance);
> + pl_context_destroy(&vk->ctx);
> +
> +#ifdef VK_USE_PLATFORM_XLIB_KHR
> + Display *dpy = vk->sys;
> + XCloseDisplay(dpy);
> +#endif
> +}
> +
> +#define DEBUG_TEXT "Enable API debugging"
> +#define DEBUG_LONGTEXT "This loads the vulkan standard validation
> layers, which can help catch API usage errors. Comes at a small
> performance penalty."
> +
> +#define DEVICE_TEXT "Device name override"
> +#define DEVICE_LONGTEXT "If set to something non-empty, only a device
> with this exact name will be used. To see a list of devices and their
> names, run vlc -v with this module active."
> +
> +#define ALLOWSW_TEXT "Allow software devices"
> +#define ALLOWSW_LONGTEXT "If enabled, allow the use of software
> emulation devices, which are not real devices and therefore typically
> very slow. (This option has no effect if forcing a specific device
> name)"
> +
> +#define ASYNC_XFER_TEXT "Allow asynchronous transfer"
> +#define ASYNC_XFER_LONGTEXT "Allows the use of an asynchronous transfer
> queue if the device has one. Typically this maps to a DMA engine, which
> can perform texture uploads/downloads without blocking the GPU's compute
> units. Highly recommended for 4K and above."
> +
> +#define ASYNC_COMP_TEXT "Allow asynchronous compute"
> +#define ASYNC_COMP_LONGTEXT "Allows the use of dedicated compute queue
> families if the device has one. Sometimes these will schedule concurrent
> compute work better than the main graphics queue. Turn this off if you
> have any issues."
> +
> +#define QUEUE_COUNT_TEXT "Queue count"
> +#define QUEUE_COUNT_LONGTEXT "How many queues to use on the device.
> Increasing this might improve rendering throughput for GPUs capable of
> concurrent scheduling. Increasing this past the driver's limit has no
> effect."
> +
> +#define QUEUE_DEPTH_TEXT "Maximum frame latency"
> +#define QUEUE_DEPTH_LONGTEXT "Affects how many frames to render/present
> in advance. Increasing this can improve performance at the cost of
> latency, by allowing better pipelining between frames. May have no
> effect, depending on the VLC clock settings."
> +
> +static const int present_values[] = {
> + VK_PRESENT_MODE_IMMEDIATE_KHR,
> + VK_PRESENT_MODE_MAILBOX_KHR,
> + VK_PRESENT_MODE_FIFO_KHR,
> + VK_PRESENT_MODE_FIFO_RELAXED_KHR,
> +};
> +
> +static const char * const present_text[] = {
> + "Immediate (non-blocking, tearing)",
> + "Mailbox (non-blocking, non-tearing)",
> + "FIFO (blocking, non-tearing)",
> + "Relaxed FIFO (blocking, tearing)",
> +};
> +
> +#define PRESENT_MODE_TEXT "Preferred present mode"
> +#define PRESENT_MODE_LONGTEXT "Which present mode to use when creating
> the swapchain. If the chosen mode is not supported, VLC will fall back
> to using FIFO."
> +
> +vlc_module_begin ()
> + set_shortname (MODULE_NAME)
> + set_description (MODULE_NAME)
> + set_category (CAT_VIDEO)
> + set_subcategory (SUBCAT_VIDEO_VOUT)
> + set_capability ("vulkan", 10)
> + set_callbacks (Open, Close)
> +
> + set_section(N_("Device selection"), NULL)
> + add_bool("vk-debug", false, DEBUG_TEXT, DEBUG_LONGTEXT, false)
> + add_string("vk-device", "", DEVICE_TEXT, DEVICE_LONGTEXT, false)
> + add_bool("allow-sw", pl_vulkan_default_params.allow_software,
> + ALLOWSW_TEXT, ALLOWSW_LONGTEXT, false)
> +
> + set_section(N_("Performance tuning"), NULL)
> + add_bool("async-xfer", pl_vulkan_default_params.async_transfer,
> + ASYNC_XFER_TEXT, ASYNC_XFER_LONGTEXT, false)
> + add_bool("async-comp", pl_vulkan_default_params.async_compute,
> + ASYNC_COMP_TEXT, ASYNC_COMP_LONGTEXT, false)
> + add_integer_with_range("queue-count",
> pl_vulkan_default_params.queue_count,
> + 1, 8, QUEUE_COUNT_TEXT, QUEUE_COUNT_LONGTEXT, false)
> + add_integer_with_range("queue-depth", 3,
> + 1, 8, QUEUE_DEPTH_TEXT, QUEUE_DEPTH_LONGTEXT, false)
> + add_integer("present-mode", VK_PRESENT_MODE_FIFO_KHR,
> + PRESENT_MODE_TEXT, PRESENT_MODE_LONGTEXT, false)
> + change_integer_list(present_values, present_text)
> +
> +vlc_module_end ()
> diff --git a/modules/video_output/vulkan/vk_instance.c b/modules/
> video_output/vulkan/vk_instance.c
> new file mode 100644
> index 0000000000..7b17df3aee
> --- /dev/null
> +++ b/modules/video_output/vulkan/vk_instance.c
> @@ -0,0 +1,77 @@
> +/
> *****************************************************************************
> + * vk_instance.c: Vulkan instance abstraction
> +
> *****************************************************************************
> + * Copyright (C) 2018 Niklas Haas
> + *
> + * This program 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.
> + *
> + * This program 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 this program; if not, write to the Free Software
> Foundation,
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
> +
> *****************************************************************************/
> +
> +#ifdef HAVE_CONFIG_H
> +# include <config.h>
> +#endif
Please don't include config.h from header files
> +
> +#include <assert.h>
> +#include <stdatomic.h>
This should be vlc_atomics instead
> +#include <stdlib.h>
> +
> +#include <vlc_common.h>
> +#include <vlc_modules.h>
> +
> +#include "vk_instance.h"
> +
> +/**
> + * Creates a Vulkan surface (and its underlying instance).
> + *
> + * @param wnd window to use as Vulkan surface
> + * @param name module name (or NULL for auto)
> + * @return a new context, or NULL on failure
> + */
> +vlc_vk_t *vlc_vk_Create(struct vout_window_t *wnd, const char *name)
> +{
> + vlc_object_t *parent = (vlc_object_t *) wnd;
> + struct vlc_vk_t *vk;
> +
> + vk = vlc_object_create(parent, sizeof (*vk));
> + if (unlikely(vk == NULL))
> + return NULL;
> +
> + vk->ctx = NULL;
> + vk->instance = NULL;
> + vk->surface = (VkSurfaceKHR) NULL;
> +
> + vk->window = wnd;
> + vk->module = module_need(vk, "vulkan", name, true);
> + if (vk->module == NULL)
> + {
> + vlc_object_release(vk);
> + return NULL;
> + }
> + atomic_init(&vk->ref_count, 1);
> +
> + return vk;
> +}
> +
> +void vlc_vk_Hold(vlc_vk_t *vk)
> +{
> + atomic_fetch_add(&vk->ref_count, 1);
> +}
> +
> +void vlc_vk_Release(vlc_vk_t *vk)
> +{
> + if (atomic_fetch_sub(&vk->ref_count, 1) != 1)
> + return;
> + module_unneed(vk, vk->module);
> + vlc_object_release(vk);
> +}
> diff --git a/modules/video_output/vulkan/vk_instance.h b/modules/
> video_output/vulkan/vk_instance.h
> new file mode 100644
> index 0000000000..0385b38428
> --- /dev/null
> +++ b/modules/video_output/vulkan/vk_instance.h
> @@ -0,0 +1,58 @@
> +/
> *****************************************************************************
> + * vk_instance.h: Vulkan instance abstraction
> +
> *****************************************************************************
> + * Copyright (C) 2018 Niklas Haas
> + *
> + * This program 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.
> + *
> + * This program 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 this program; if not, write to the Free Software
> Foundation,
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
> +
> *****************************************************************************/
> +
> +#ifndef VLC_VULKAN_INSTANCE_H
> +#define VLC_VULKAN_INSTANCE_H
> +
> +#ifdef HAVE_CONFIG_H
> +# include <config.h>
> +#endif
> +
> +#include <stdatomic.h>
> +
> +#include <vulkan/vulkan.h>
> +#include <libplacebo/vulkan.h>
> +
> +struct vout_window_t;
> +struct vout_window_cfg_t;
> +
> +// Shared struct for vulkan instance / surface / device state
> +typedef struct vlc_vk_t
> +{
> + // fields internal to vk_instance.c, should not be touched
> + struct vlc_common_members obj;
> + module_t *module;
> + atomic_uint ref_count;
> + void *sys;
> +
> + // these should be initialized by the surface module (i.e.
> surface.c)
> + struct pl_context *ctx;
> + const struct pl_vk_inst *instance;
> + const struct pl_vulkan *vulkan;
> + const struct pl_swapchain *swapchain;
> + VkSurfaceKHR surface;
> + struct vout_window_t *window;
> +} vlc_vk_t;
> +
> +vlc_vk_t *vlc_vk_Create(struct vout_window_t *, const char *) VLC_USED;
> +void vlc_vk_Release(vlc_vk_t *);
> +void vlc_vk_Hold(vlc_vk_t *);
> +
> +#endif // VLC_VULKAN_INSTANCE_H
> --
> 2.19.0
>
This is also missing some POTFILES.in inclusion
Regards,
--
Hugo Beauzée-Luyssen
hugo at beauzee.fr
More information about the vlc-devel
mailing list