[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