diff mbox series

[FFmpeg-devel,4/4] avutil/hwcontext_vulkan: fully support customizable validation layers

Message ID 20211123090119.868788-4-jianhua.wu@intel.com
State New
Headers show
Series [FFmpeg-devel,1/4] avutil/vulkan_functions: add EnumerateInstanceLayerProperties
Related show

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished
andriy/make_ppc success Make finished
andriy/make_fate_ppc success Make fate finished

Commit Message

Wu, Jianhua Nov. 23, 2021, 9:01 a.m. UTC
Validation layer is an indispensable part of developing on Vulkan.

The following commands is on how to enable validation layers:

ffmpeg -init_hw_device vulkan=0,debug=1,validation_layers=VK_LAYER_KHRONOS_validation+VK_LAYER_LUNARG_api_dump

Signed-off-by: Wu Jianhua <jianhua.wu@intel.com>
---
 libavutil/hwcontext_vulkan.c | 110 ++++++++++++++++++++++++++++-------
 libavutil/hwcontext_vulkan.h |   7 +++
 2 files changed, 97 insertions(+), 20 deletions(-)

Comments

Lynne Nov. 23, 2021, 9:22 a.m. UTC | #1
23 Nov 2021, 10:01 by jianhua.wu@intel.com:

> Validation layer is an indispensable part of developing on Vulkan.
>
> The following commands is on how to enable validation layers:
>
> ffmpeg -init_hw_device vulkan=0,debug=1,validation_layers=VK_LAYER_KHRONOS_validation+VK_LAYER_LUNARG_api_dump
>
> Signed-off-by: Wu Jianhua <jianhua.wu@intel.com>
> ---
>  libavutil/hwcontext_vulkan.c | 110 ++++++++++++++++++++++++++++-------
>  libavutil/hwcontext_vulkan.h |   7 +++
>  2 files changed, 97 insertions(+), 20 deletions(-)
>
> diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
> index 644ed947f8..b808f8f76c 100644
> --- a/libavutil/hwcontext_vulkan.c
> +++ b/libavutil/hwcontext_vulkan.c
> @@ -146,6 +146,13 @@ typedef struct AVVkFrameInternal {
>  }                                                                      \
>  } while(0)
>  
> +#define RELEASE_PROPS(props, count)                                            \
> +    if (props) {                                                               \
> +        for (int i = 0; i < count; i++)                                        \
> +            av_free((void *)((props)[i]));                                     \
> +        av_free((void *)props);                                                \
> +    }
> +
>  static const struct {
>  enum AVPixelFormat pixfmt;
>  const VkFormat vkfmts[4];
> @@ -511,15 +518,83 @@ static int check_extensions(AVHWDeviceContext *ctx, int dev, AVDictionary *opts,
>  return 0;
>  
>  fail:
> -    if (extension_names)
> -        for (int i = 0; i < extensions_found; i++)
> -            av_free((void *)extension_names[i]);
> -    av_free(extension_names);
> +    RELEASE_PROPS(extension_names, extensions_found);
>  av_free(user_exts_str);
>  av_free(sup_ext);
>  return err;
>  }
>  
> +static int check_validation_layers(AVHWDeviceContext *ctx, AVDictionary *opts,
> +                                   const char * const **dst, uint32_t *num)
> +{
> +    int found, err = 0;
> +    VulkanDevicePriv *priv = ctx->internal->priv;
> +    FFVulkanFunctions *vk = &priv->vkfn;
> +
> +    uint32_t sup_layer_count;
> +    VkLayerProperties *sup_layers;
> +
> +    AVDictionaryEntry *user_layers;
> +    char *user_layers_str, *save, *token;
> +
> +    const char **enabled_layers = NULL;
> +    uint32_t enabled_layers_count = 0;
> +
> +    user_layers = av_dict_get(opts, "validation_layers", NULL, 0);
> +    if (!user_layers)
> +        return 0;
> +
> +    user_layers_str = av_strdup(user_layers->value);
> +    if (!user_layers_str) {
> +        err = AVERROR(EINVAL);
> +        goto fail;
> +    }
> +
> +    vk->EnumerateInstanceLayerProperties(&sup_layer_count, NULL);
> +    sup_layers = av_malloc_array(sup_layer_count, sizeof(VkLayerProperties));
> +    if (!sup_layers)
> +        return AVERROR(ENOMEM);
> +    vk->EnumerateInstanceLayerProperties(&sup_layer_count, sup_layers);
> +
> +    av_log(ctx, AV_LOG_VERBOSE, "Supported Validation layers:\n");
> +    for (int i = 0; i < sup_layer_count; i++)
> +        av_log(ctx, AV_LOG_VERBOSE, "\t%s\n", sup_layers[i].layerName);
> +
> +    token = av_strtok(user_layers_str, "+", &save);
> +    while (token) {
> +        found = 0;
> +        for (int j = 0; j < sup_layer_count; j++) {
> +            if (!strcmp(token, sup_layers[j].layerName)) {
> +                found = 1;
> +                break;
> +            }
> +        }
> +        if (found) {
> +            av_log(ctx, AV_LOG_VERBOSE, "Requested Validation Layer: %s\n", token);
> +            ADD_VAL_TO_LIST(enabled_layers, enabled_layers_count, token);
> +        } else {
> +            av_log(ctx, AV_LOG_ERROR,
> +                   "Validation Layer \"%s\" not support.\n", token);
> +            err = AVERROR(EINVAL);
> +            goto fail;
> +        }
> +        token = av_strtok(NULL, "+", &save);
> +    }
> +
> +    *dst = enabled_layers;
> +    *num = enabled_layers_count;
> +
> +    av_free(sup_layers);
> +    av_free(user_layers_str);
> +    return 0;
> +
> +fail:
> +    RELEASE_PROPS(enabled_layers, enabled_layers_count);
> +    av_free(sup_layers);
> +    av_free(user_layers_str);
> +    return err;
> +}
> +
>  /* Creates a VkInstance */
>  static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
>  {
> @@ -558,13 +633,18 @@ static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
>  /* Check for present/missing extensions */
>  err = check_extensions(ctx, 0, opts, &inst_props.ppEnabledExtensionNames,
>  &inst_props.enabledExtensionCount, debug_mode);
> +    hwctx->enabled_inst_extensions = inst_props.ppEnabledExtensionNames;
> +    hwctx->nb_enabled_inst_extensions = inst_props.enabledExtensionCount;
>  if (err < 0)
>  return err;
>  
>  if (debug_mode) {
> -        static const char *layers[] = { "VK_LAYER_KHRONOS_validation" };
> -        inst_props.ppEnabledLayerNames = layers;
> -        inst_props.enabledLayerCount = FF_ARRAY_ELEMS(layers);
> +        err = check_validation_layers(ctx, opts, &inst_props.ppEnabledLayerNames,
> +                                      &inst_props.enabledLayerCount);
> +        hwctx->enabled_validation_layers = inst_props.ppEnabledLayerNames;
> +        hwctx->nb_enabled_validation_layers = inst_props.enabledLayerCount;
> +        if (err)
> +            return err;
>  }
>  
>  /* Try to create the instance */
> @@ -574,9 +654,6 @@ static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
>  if (ret != VK_SUCCESS) {
>  av_log(ctx, AV_LOG_ERROR, "Instance creation failure: %s\n",
>  vk_ret2str(ret));
> -        for (int i = 0; i < inst_props.enabledExtensionCount; i++)
> -            av_free((void *)inst_props.ppEnabledExtensionNames[i]);
> -        av_free((void *)inst_props.ppEnabledExtensionNames);
>  return AVERROR_EXTERNAL;
>  }
>  
> @@ -604,9 +681,6 @@ static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
>  hwctx->alloc, &p->debug_ctx);
>  }
>  
> -    hwctx->enabled_inst_extensions = inst_props.ppEnabledExtensionNames;
> -    hwctx->nb_enabled_inst_extensions = inst_props.enabledExtensionCount;
> -
>  return 0;
>  }
>  
> @@ -1163,13 +1237,9 @@ static void vulkan_device_free(AVHWDeviceContext *ctx)
>  if (p->libvulkan)
>  dlclose(p->libvulkan);
>  
> -    for (int i = 0; i < hwctx->nb_enabled_inst_extensions; i++)
> -        av_free((void *)hwctx->enabled_inst_extensions[i]);
> -    av_free((void *)hwctx->enabled_inst_extensions);
> -
> -    for (int i = 0; i < hwctx->nb_enabled_dev_extensions; i++)
> -        av_free((void *)hwctx->enabled_dev_extensions[i]);
> -    av_free((void *)hwctx->enabled_dev_extensions);
> +    RELEASE_PROPS(hwctx->enabled_inst_extensions, hwctx->nb_enabled_inst_extensions);
> +    RELEASE_PROPS(hwctx->enabled_dev_extensions, hwctx->nb_enabled_dev_extensions);
> +    RELEASE_PROPS(hwctx->enabled_validation_layers, hwctx->nb_enabled_validation_layers);
>  }
>  
>  static int vulkan_device_create_internal(AVHWDeviceContext *ctx,
> diff --git a/libavutil/hwcontext_vulkan.h b/libavutil/hwcontext_vulkan.h
> index fdf2a60156..80c36d6f26 100644
> --- a/libavutil/hwcontext_vulkan.h
> +++ b/libavutil/hwcontext_vulkan.h
> @@ -85,6 +85,13 @@ typedef struct AVVulkanDeviceContext {
>  const char * const *enabled_inst_extensions;
>  int nb_enabled_inst_extensions;
>  
> +    /**
> +     * Enabled validation layers.
> +     * If no layers are enabled, set these fields to NULL, and 0 respectively.
> +     */
> +    const char * const *enabled_validation_layers;
> +    int nb_enabled_validation_layers;
> + 
>

Why are you exposing them? Do API users really need to know this?
Wu, Jianhua Nov. 23, 2021, 9:48 a.m. UTC | #2
> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Lynne
> Sent: Tuesday, November 23, 2021 5:23 PM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH 4/4] avutil/hwcontext_vulkan: fully
> support customizable validation layers
> 
> 23 Nov 2021, 10:01 by jianhua.wu@intel.com:
> 
> > Validation layer is an indispensable part of developing on Vulkan.
> >
> > The following commands is on how to enable validation layers:
> >
> > ffmpeg -init_hw_device
> >
> vulkan=0,debug=1,validation_layers=VK_LAYER_KHRONOS_validation+VK_L
> AYE
> > R_LUNARG_api_dump
> >
> > Signed-off-by: Wu Jianhua <jianhua.wu@intel.com>
> > ---
> >  libavutil/hwcontext_vulkan.c | 110 ++++++++++++++++++++++++++++---
> ----
> >  libavutil/hwcontext_vulkan.h |   7 +++
> >  2 files changed, 97 insertions(+), 20 deletions(-)
> >
> > diff --git a/libavutil/hwcontext_vulkan.c
> > b/libavutil/hwcontext_vulkan.c index 644ed947f8..b808f8f76c 100644
> > --- a/libavutil/hwcontext_vulkan.c
> > +++ b/libavutil/hwcontext_vulkan.c
> > @@ -146,6 +146,13 @@ typedef struct AVVkFrameInternal {
> >  }                                                                      \
> >  } while(0)
> >
> > +#define RELEASE_PROPS(props, count)                                            \
> > +    if (props) {                                                               \
> > +        for (int i = 0; i < count; i++)                                        \
> > +            av_free((void *)((props)[i]));                                     \
> > +        av_free((void *)props);                                                \
> > +    }
> > +
> >  static const struct {
> >  enum AVPixelFormat pixfmt;
> >  const VkFormat vkfmts[4];
> > @@ -511,15 +518,83 @@ static int check_extensions(AVHWDeviceContext
> > *ctx, int dev, AVDictionary *opts,  return 0;
> >
> >  fail:
> > -    if (extension_names)
> > -        for (int i = 0; i < extensions_found; i++)
> > -            av_free((void *)extension_names[i]);
> > -    av_free(extension_names);
> > +    RELEASE_PROPS(extension_names, extensions_found);
> >  av_free(user_exts_str);
> >  av_free(sup_ext);
> >  return err;
> >  }
> >
> > +static int check_validation_layers(AVHWDeviceContext *ctx, AVDictionary
> *opts,
> > +                                   const char * const **dst, uint32_t
> > +*num) {
> > +    int found, err = 0;
> > +    VulkanDevicePriv *priv = ctx->internal->priv;
> > +    FFVulkanFunctions *vk = &priv->vkfn;
> > +
> > +    uint32_t sup_layer_count;
> > +    VkLayerProperties *sup_layers;
> > +
> > +    AVDictionaryEntry *user_layers;
> > +    char *user_layers_str, *save, *token;
> > +
> > +    const char **enabled_layers = NULL;
> > +    uint32_t enabled_layers_count = 0;
> > +
> > +    user_layers = av_dict_get(opts, "validation_layers", NULL, 0);
> > +    if (!user_layers)
> > +        return 0;
> > +
> > +    user_layers_str = av_strdup(user_layers->value);
> > +    if (!user_layers_str) {
> > +        err = AVERROR(EINVAL);
> > +        goto fail;
> > +    }
> > +
> > +    vk->EnumerateInstanceLayerProperties(&sup_layer_count, NULL);
> > +    sup_layers = av_malloc_array(sup_layer_count,
> sizeof(VkLayerProperties));
> > +    if (!sup_layers)
> > +        return AVERROR(ENOMEM);
> > +    vk->EnumerateInstanceLayerProperties(&sup_layer_count,
> > + sup_layers);
> > +
> > +    av_log(ctx, AV_LOG_VERBOSE, "Supported Validation layers:\n");
> > +    for (int i = 0; i < sup_layer_count; i++)
> > +        av_log(ctx, AV_LOG_VERBOSE, "\t%s\n",
> > + sup_layers[i].layerName);
> > +
> > +    token = av_strtok(user_layers_str, "+", &save);
> > +    while (token) {
> > +        found = 0;
> > +        for (int j = 0; j < sup_layer_count; j++) {
> > +            if (!strcmp(token, sup_layers[j].layerName)) {
> > +                found = 1;
> > +                break;
> > +            }
> > +        }
> > +        if (found) {
> > +            av_log(ctx, AV_LOG_VERBOSE, "Requested Validation Layer: %s\n",
> token);
> > +            ADD_VAL_TO_LIST(enabled_layers, enabled_layers_count, token);
> > +        } else {
> > +            av_log(ctx, AV_LOG_ERROR,
> > +                   "Validation Layer \"%s\" not support.\n", token);
> > +            err = AVERROR(EINVAL);
> > +            goto fail;
> > +        }
> > +        token = av_strtok(NULL, "+", &save);
> > +    }
> > +
> > +    *dst = enabled_layers;
> > +    *num = enabled_layers_count;
> > +
> > +    av_free(sup_layers);
> > +    av_free(user_layers_str);
> > +    return 0;
> > +
> > +fail:
> > +    RELEASE_PROPS(enabled_layers, enabled_layers_count);
> > +    av_free(sup_layers);
> > +    av_free(user_layers_str);
> > +    return err;
> > +}
> > +
> >  /* Creates a VkInstance */
> >  static int create_instance(AVHWDeviceContext *ctx, AVDictionary
> > *opts)  { @@ -558,13 +633,18 @@ static int
> > create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
> >  /* Check for present/missing extensions */  err =
> > check_extensions(ctx, 0, opts, &inst_props.ppEnabledExtensionNames,
> >  &inst_props.enabledExtensionCount, debug_mode);
> > +    hwctx->enabled_inst_extensions =
> inst_props.ppEnabledExtensionNames;
> > +    hwctx->nb_enabled_inst_extensions =
> > + inst_props.enabledExtensionCount;
> >  if (err < 0)
> >  return err;
> >
> >  if (debug_mode) {
> > -        static const char *layers[] = { "VK_LAYER_KHRONOS_validation" };
> > -        inst_props.ppEnabledLayerNames = layers;
> > -        inst_props.enabledLayerCount = FF_ARRAY_ELEMS(layers);
> > +        err = check_validation_layers(ctx, opts,
> &inst_props.ppEnabledLayerNames,
> > +                                      &inst_props.enabledLayerCount);
> > +        hwctx->enabled_validation_layers =
> inst_props.ppEnabledLayerNames;
> > +        hwctx->nb_enabled_validation_layers =
> inst_props.enabledLayerCount;
> > +        if (err)
> > +            return err;
> >  }
> >
> >  /* Try to create the instance */
> > @@ -574,9 +654,6 @@ static int create_instance(AVHWDeviceContext *ctx,
> > AVDictionary *opts)  if (ret != VK_SUCCESS) {  av_log(ctx,
> > AV_LOG_ERROR, "Instance creation failure: %s\n",  vk_ret2str(ret));
> > -        for (int i = 0; i < inst_props.enabledExtensionCount; i++)
> > -            av_free((void *)inst_props.ppEnabledExtensionNames[i]);
> > -        av_free((void *)inst_props.ppEnabledExtensionNames);
> >  return AVERROR_EXTERNAL;
> >  }
> >
> > @@ -604,9 +681,6 @@ static int create_instance(AVHWDeviceContext *ctx,
> > AVDictionary *opts)  hwctx->alloc, &p->debug_ctx);  }
> >
> > -    hwctx->enabled_inst_extensions =
> inst_props.ppEnabledExtensionNames;
> > -    hwctx->nb_enabled_inst_extensions =
> inst_props.enabledExtensionCount;
> > -
> >  return 0;
> >  }
> >
> > @@ -1163,13 +1237,9 @@ static void
> > vulkan_device_free(AVHWDeviceContext *ctx)  if (p->libvulkan)
> > dlclose(p->libvulkan);
> >
> > -    for (int i = 0; i < hwctx->nb_enabled_inst_extensions; i++)
> > -        av_free((void *)hwctx->enabled_inst_extensions[i]);
> > -    av_free((void *)hwctx->enabled_inst_extensions);
> > -
> > -    for (int i = 0; i < hwctx->nb_enabled_dev_extensions; i++)
> > -        av_free((void *)hwctx->enabled_dev_extensions[i]);
> > -    av_free((void *)hwctx->enabled_dev_extensions);
> > +    RELEASE_PROPS(hwctx->enabled_inst_extensions, hwctx-
> >nb_enabled_inst_extensions);
> > +    RELEASE_PROPS(hwctx->enabled_dev_extensions, hwctx-
> >nb_enabled_dev_extensions);
> > +    RELEASE_PROPS(hwctx->enabled_validation_layers,
> > + hwctx->nb_enabled_validation_layers);
> >  }
> >
> >  static int vulkan_device_create_internal(AVHWDeviceContext *ctx, diff
> > --git a/libavutil/hwcontext_vulkan.h b/libavutil/hwcontext_vulkan.h
> > index fdf2a60156..80c36d6f26 100644
> > --- a/libavutil/hwcontext_vulkan.h
> > +++ b/libavutil/hwcontext_vulkan.h
> > @@ -85,6 +85,13 @@ typedef struct AVVulkanDeviceContext {  const char
> > * const *enabled_inst_extensions;  int nb_enabled_inst_extensions;
> >
> > +    /**
> > +     * Enabled validation layers.
> > +     * If no layers are enabled, set these fields to NULL, and 0 respectively.
> > +     */
> > +    const char * const *enabled_validation_layers;
> > +    int nb_enabled_validation_layers;
> > +
> >
> 
> Why are you exposing them? Do API users really need to know this?
> 

It's okay. For it's only integrated in a really small separate function it
could be skipped by the status debug_mode as before. And validation
layers are embed by other specific drivers, platforms(such as those
specific layers in androids) or SDK, the FFmpeg is not need to do anything
more whatever the current is compiled with optimization mode or debug mode.
The use who only want to use filter is simply not able to know how to
enable the debug_mode. For me, as a user also, it is important to me, I
don't want to changed the code then compiling to use the specific validation
layers again and again.  And not only the developer need to use it, those
people who help us test could report a more detailed problem. I think it's
really benefit.

Thanks,
Jianhua
Lynne Nov. 23, 2021, 10:13 a.m. UTC | #3
23 Nov 2021, 10:48 by jianhua.wu@intel.com:

>
>
>> -----Original Message-----
>> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
>> Lynne
>> Sent: Tuesday, November 23, 2021 5:23 PM
>> To: FFmpeg development discussions and patches <ffmpeg-
>> devel@ffmpeg.org>
>> Subject: Re: [FFmpeg-devel] [PATCH 4/4] avutil/hwcontext_vulkan: fully
>> support customizable validation layers
>>
>> 23 Nov 2021, 10:01 by jianhua.wu@intel.com:
>>
>> > Validation layer is an indispensable part of developing on Vulkan.
>> >
>> > The following commands is on how to enable validation layers:
>> >
>> > ffmpeg -init_hw_device
>> >
>> vulkan=0,debug=1,validation_layers=VK_LAYER_KHRONOS_validation+VK_L
>> AYE
>> > R_LUNARG_api_dump
>> >
>> > Signed-off-by: Wu Jianhua <jianhua.wu@intel.com>
>> > ---
>> >  libavutil/hwcontext_vulkan.c | 110 ++++++++++++++++++++++++++++---
>> ----
>> >  libavutil/hwcontext_vulkan.h |   7 +++
>> >  2 files changed, 97 insertions(+), 20 deletions(-)
>> >
>> > diff --git a/libavutil/hwcontext_vulkan.c
>> > b/libavutil/hwcontext_vulkan.c index 644ed947f8..b808f8f76c 100644
>> > --- a/libavutil/hwcontext_vulkan.c
>> > +++ b/libavutil/hwcontext_vulkan.c
>> > @@ -146,6 +146,13 @@ typedef struct AVVkFrameInternal {
>> >  }                                                                      \
>> >  } while(0)
>> >
>> > +#define RELEASE_PROPS(props, count)                                            \
>> > +    if (props) {                                                               \
>> > +        for (int i = 0; i < count; i++)                                        \
>> > +            av_free((void *)((props)[i]));                                     \
>> > +        av_free((void *)props);                                                \
>> > +    }
>> > +
>> >  static const struct {
>> >  enum AVPixelFormat pixfmt;
>> >  const VkFormat vkfmts[4];
>> > @@ -511,15 +518,83 @@ static int check_extensions(AVHWDeviceContext
>> > *ctx, int dev, AVDictionary *opts,  return 0;
>> >
>> >  fail:
>> > -    if (extension_names)
>> > -        for (int i = 0; i < extensions_found; i++)
>> > -            av_free((void *)extension_names[i]);
>> > -    av_free(extension_names);
>> > +    RELEASE_PROPS(extension_names, extensions_found);
>> >  av_free(user_exts_str);
>> >  av_free(sup_ext);
>> >  return err;
>> >  }
>> >
>> > +static int check_validation_layers(AVHWDeviceContext *ctx, AVDictionary
>> *opts,
>> > +                                   const char * const **dst, uint32_t
>> > +*num) {
>> > +    int found, err = 0;
>> > +    VulkanDevicePriv *priv = ctx->internal->priv;
>> > +    FFVulkanFunctions *vk = &priv->vkfn;
>> > +
>> > +    uint32_t sup_layer_count;
>> > +    VkLayerProperties *sup_layers;
>> > +
>> > +    AVDictionaryEntry *user_layers;
>> > +    char *user_layers_str, *save, *token;
>> > +
>> > +    const char **enabled_layers = NULL;
>> > +    uint32_t enabled_layers_count = 0;
>> > +
>> > +    user_layers = av_dict_get(opts, "validation_layers", NULL, 0);
>> > +    if (!user_layers)
>> > +        return 0;
>> > +
>> > +    user_layers_str = av_strdup(user_layers->value);
>> > +    if (!user_layers_str) {
>> > +        err = AVERROR(EINVAL);
>> > +        goto fail;
>> > +    }
>> > +
>> > +    vk->EnumerateInstanceLayerProperties(&sup_layer_count, NULL);
>> > +    sup_layers = av_malloc_array(sup_layer_count,
>> sizeof(VkLayerProperties));
>> > +    if (!sup_layers)
>> > +        return AVERROR(ENOMEM);
>> > +    vk->EnumerateInstanceLayerProperties(&sup_layer_count,
>> > + sup_layers);
>> > +
>> > +    av_log(ctx, AV_LOG_VERBOSE, "Supported Validation layers:\n");
>> > +    for (int i = 0; i < sup_layer_count; i++)
>> > +        av_log(ctx, AV_LOG_VERBOSE, "\t%s\n",
>> > + sup_layers[i].layerName);
>> > +
>> > +    token = av_strtok(user_layers_str, "+", &save);
>> > +    while (token) {
>> > +        found = 0;
>> > +        for (int j = 0; j < sup_layer_count; j++) {
>> > +            if (!strcmp(token, sup_layers[j].layerName)) {
>> > +                found = 1;
>> > +                break;
>> > +            }
>> > +        }
>> > +        if (found) {
>> > +            av_log(ctx, AV_LOG_VERBOSE, "Requested Validation Layer: %s\n",
>> token);
>> > +            ADD_VAL_TO_LIST(enabled_layers, enabled_layers_count, token);
>> > +        } else {
>> > +            av_log(ctx, AV_LOG_ERROR,
>> > +                   "Validation Layer \"%s\" not support.\n", token);
>> > +            err = AVERROR(EINVAL);
>> > +            goto fail;
>> > +        }
>> > +        token = av_strtok(NULL, "+", &save);
>> > +    }
>> > +
>> > +    *dst = enabled_layers;
>> > +    *num = enabled_layers_count;
>> > +
>> > +    av_free(sup_layers);
>> > +    av_free(user_layers_str);
>> > +    return 0;
>> > +
>> > +fail:
>> > +    RELEASE_PROPS(enabled_layers, enabled_layers_count);
>> > +    av_free(sup_layers);
>> > +    av_free(user_layers_str);
>> > +    return err;
>> > +}
>> > +
>> >  /* Creates a VkInstance */
>> >  static int create_instance(AVHWDeviceContext *ctx, AVDictionary
>> > *opts)  { @@ -558,13 +633,18 @@ static int
>> > create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
>> >  /* Check for present/missing extensions */  err =
>> > check_extensions(ctx, 0, opts, &inst_props.ppEnabledExtensionNames,
>> >  &inst_props.enabledExtensionCount, debug_mode);
>> > +    hwctx->enabled_inst_extensions =
>> inst_props.ppEnabledExtensionNames;
>> > +    hwctx->nb_enabled_inst_extensions =
>> > + inst_props.enabledExtensionCount;
>> >  if (err < 0)
>> >  return err;
>> >
>> >  if (debug_mode) {
>> > -        static const char *layers[] = { "VK_LAYER_KHRONOS_validation" };
>> > -        inst_props.ppEnabledLayerNames = layers;
>> > -        inst_props.enabledLayerCount = FF_ARRAY_ELEMS(layers);
>> > +        err = check_validation_layers(ctx, opts,
>> &inst_props.ppEnabledLayerNames,
>> > +                                      &inst_props.enabledLayerCount);
>> > +        hwctx->enabled_validation_layers =
>> inst_props.ppEnabledLayerNames;
>> > +        hwctx->nb_enabled_validation_layers =
>> inst_props.enabledLayerCount;
>> > +        if (err)
>> > +            return err;
>> >  }
>> >
>> >  /* Try to create the instance */
>> > @@ -574,9 +654,6 @@ static int create_instance(AVHWDeviceContext *ctx,
>> > AVDictionary *opts)  if (ret != VK_SUCCESS) {  av_log(ctx,
>> > AV_LOG_ERROR, "Instance creation failure: %s\n",  vk_ret2str(ret));
>> > -        for (int i = 0; i < inst_props.enabledExtensionCount; i++)
>> > -            av_free((void *)inst_props.ppEnabledExtensionNames[i]);
>> > -        av_free((void *)inst_props.ppEnabledExtensionNames);
>> >  return AVERROR_EXTERNAL;
>> >  }
>> >
>> > @@ -604,9 +681,6 @@ static int create_instance(AVHWDeviceContext *ctx,
>> > AVDictionary *opts)  hwctx->alloc, &p->debug_ctx);  }
>> >
>> > -    hwctx->enabled_inst_extensions =
>> inst_props.ppEnabledExtensionNames;
>> > -    hwctx->nb_enabled_inst_extensions =
>> inst_props.enabledExtensionCount;
>> > -
>> >  return 0;
>> >  }
>> >
>> > @@ -1163,13 +1237,9 @@ static void
>> > vulkan_device_free(AVHWDeviceContext *ctx)  if (p->libvulkan)
>> > dlclose(p->libvulkan);
>> >
>> > -    for (int i = 0; i < hwctx->nb_enabled_inst_extensions; i++)
>> > -        av_free((void *)hwctx->enabled_inst_extensions[i]);
>> > -    av_free((void *)hwctx->enabled_inst_extensions);
>> > -
>> > -    for (int i = 0; i < hwctx->nb_enabled_dev_extensions; i++)
>> > -        av_free((void *)hwctx->enabled_dev_extensions[i]);
>> > -    av_free((void *)hwctx->enabled_dev_extensions);
>> > +    RELEASE_PROPS(hwctx->enabled_inst_extensions, hwctx-
>> >nb_enabled_inst_extensions);
>> > +    RELEASE_PROPS(hwctx->enabled_dev_extensions, hwctx-
>> >nb_enabled_dev_extensions);
>> > +    RELEASE_PROPS(hwctx->enabled_validation_layers,
>> > + hwctx->nb_enabled_validation_layers);
>> >  }
>> >
>> >  static int vulkan_device_create_internal(AVHWDeviceContext *ctx, diff
>> > --git a/libavutil/hwcontext_vulkan.h b/libavutil/hwcontext_vulkan.h
>> > index fdf2a60156..80c36d6f26 100644
>> > --- a/libavutil/hwcontext_vulkan.h
>> > +++ b/libavutil/hwcontext_vulkan.h
>> > @@ -85,6 +85,13 @@ typedef struct AVVulkanDeviceContext {  const char
>> > * const *enabled_inst_extensions;  int nb_enabled_inst_extensions;
>> >
>> > +    /**
>> > +     * Enabled validation layers.
>> > +     * If no layers are enabled, set these fields to NULL, and 0 respectively.
>> > +     */
>> > +    const char * const *enabled_validation_layers;
>> > +    int nb_enabled_validation_layers;
>> > +
>> >
>>
>> Why are you exposing them? Do API users really need to know this?
>>
>
> It's okay. For it's only integrated in a really small separate function it
> could be skipped by the status debug_mode as before. And validation
> layers are embed by other specific drivers, platforms(such as those
> specific layers in androids) or SDK, the FFmpeg is not need to do anything
> more whatever the current is compiled with optimization mode or debug mode.
> The use who only want to use filter is simply not able to know how to
> enable the debug_mode. For me, as a user also, it is important to me, I
> don't want to changed the code then compiling to use the specific validation
> layers again and again.  And not only the developer need to use it, those
> people who help us test could report a more detailed problem. I think it's
> really benefit.
>

Sorry, I didn't quite understand that.
I'm not objecting to being able to use custom debug layers and activating
them if they're requested from the user. We already do that for the
standard debug layer anyway. I'm just not sure I understand why filters
would need to know if a debug layer is ran, and which one it is, by exposing
them via the public API.
Wu Jianhua Nov. 23, 2021, 11:03 a.m. UTC | #4
Lynne:
>23 Nov 2021, 10:48 by jianhua.wu@intel.com:
>>Lynne:
>>> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
>>> Lynne
>>> Sent: Tuesday, November 23, 2021 5:23 PM
>>> To: FFmpeg development discussions and patches <ffmpeg-
>>> devel@ffmpeg.org>
>>> Subject: Re: [FFmpeg-devel] [PATCH 4/4] avutil/hwcontext_vulkan: fully
>>> support customizable validation layers
>>>
>>> 23 Nov 2021, 10:01 by jianhua.wu@intel.com:
>>>
>>> > Validation layer is an indispensable part of developing on Vulkan.
>>> >
>>> > The following commands is on how to enable validation layers:
>>> >
>>> > ffmpeg -init_hw_device
>>> >
>>> vulkan=0,debug=1,validation_layers=VK_LAYER_KHRONOS_validation+VK_L
>>> AYE
>>> > R_LUNARG_api_dump
>>> >
>>> > Signed-off-by: Wu Jianhua <jianhua.wu@intel.com>
>>> > ---
>>> >  libavutil/hwcontext_vulkan.c | 110 ++++++++++++++++++++++++++++---
>>> ----
>>> >  libavutil/hwcontext_vulkan.h |   7 +++
>>> >  2 files changed, 97 insertions(+), 20 deletions(-)
>>> >
>>> >
>>> > +    /**
>>> > +     * Enabled validation layers.
>>> > +     * If no layers are enabled, set these fields to NULL, and 0 respectively.
>>> > +     */
>>> > +    const char * const *enabled_validation_layers;
>>> > +    int nb_enabled_validation_layers;
>>> > +
>>> >
>>>
>>> Why are you exposing them? Do API users really need to know this?
>>>
>>
>> It's okay. For it's only integrated in a really small separate function it
>> could be skipped by the status debug_mode as before. And validation
>> layers are embed by other specific drivers, platforms(such as those
>> specific layers in androids) or SDK, the FFmpeg is not need to do anything
>> more whatever the current is compiled with optimization mode or debug mode.
>> The use who only want to use filter is simply not able to know how to
>> enable the debug_mode. For me, as a user also, it is important to me, I
>> don't want to changed the code then compiling to use the specific validation
>> layers again and again.  And not only the developer need to use it, those
>> people who help us test could report a more detailed problem. I think it's
>> really benefit.
>>
>
> Sorry, I didn't quite understand that.
> I'm not objecting to being able to use custom debug layers and activating
> them if they're requested from the user. We already do that for the
> standard debug layer anyway. I'm just not sure I understand why filters
> would need to know if a debug layer is ran, and which one it is, by exposing
> them via the public API.
>

Oh! My bad. I supposed you mean we may be not necessary to expose
layer info to the Device Context, right?
Lynne Nov. 23, 2021, 1:18 p.m. UTC | #5
23 Nov 2021, 12:03 by toqsxw@outlook.com:

> Lynne:
> >23 Nov 2021, 10:48 by jianhua.wu@intel.com:
> >>Lynne:
>
>>>> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
>>>> Lynne
>>>> Sent: Tuesday, November 23, 2021 5:23 PM
>>>> To: FFmpeg development discussions and patches <ffmpeg-
>>>> devel@ffmpeg.org>
>>>> Subject: Re: [FFmpeg-devel] [PATCH 4/4] avutil/hwcontext_vulkan: fully
>>>> support customizable validation layers
>>>>
>>>> 23 Nov 2021, 10:01 by jianhua.wu@intel.com:
>>>>
>>>> > Validation layer is an indispensable part of developing on Vulkan.
>>>> >
>>>> > The following commands is on how to enable validation layers:
>>>> >
>>>> > ffmpeg -init_hw_device
>>>> >
>>>> vulkan=0,debug=1,validation_layers=VK_LAYER_KHRONOS_validation+VK_L
>>>> AYE
>>>> > R_LUNARG_api_dump
>>>> >
>>>> > Signed-off-by: Wu Jianhua <jianhua.wu@intel.com>
>>>> > ---
>>>> >  libavutil/hwcontext_vulkan.c | 110 ++++++++++++++++++++++++++++---
>>>> ----
>>>> >  libavutil/hwcontext_vulkan.h |   7 +++
>>>> >  2 files changed, 97 insertions(+), 20 deletions(-)
>>>> >
>>>> >
>>>> > +    /**
>>>> > +     * Enabled validation layers.
>>>> > +     * If no layers are enabled, set these fields to NULL, and 0 respectively.
>>>> > +     */
>>>> > +    const char * const *enabled_validation_layers;
>>>> > +    int nb_enabled_validation_layers;
>>>> > +
>>>> >
>>>>
>>>> Why are you exposing them? Do API users really need to know this?
>>>>
>>>
>>> It's okay. For it's only integrated in a really small separate function it
>>> could be skipped by the status debug_mode as before. And validation
>>> layers are embed by other specific drivers, platforms(such as those
>>> specific layers in androids) or SDK, the FFmpeg is not need to do anything
>>> more whatever the current is compiled with optimization mode or debug mode.
>>> The use who only want to use filter is simply not able to know how to
>>> enable the debug_mode. For me, as a user also, it is important to me, I
>>> don't want to changed the code then compiling to use the specific validation
>>> layers again and again.  And not only the developer need to use it, those
>>> people who help us test could report a more detailed problem. I think it's
>>> really benefit.
>>>
>>
>> Sorry, I didn't quite understand that.
>> I'm not objecting to being able to use custom debug layers and activating
>> them if they're requested from the user. We already do that for the
>> standard debug layer anyway. I'm just not sure I understand why filters
>> would need to know if a debug layer is ran, and which one it is, by exposing
>> them via the public API.
>>
>
> Oh! My bad. I supposed you mean we may be not necessary to expose
> layer info to the Device Context, right?
>

Yup, that's right. We'd like to keep the API as small as possible, and
it's already quite large compared to other hwcontexts. In this case,
I don't think it's necessary. API users already know, since they can request
layers, and filters shouldn't have to know.

So just remove the public API part of the patch.

As for the rest, could you always include VK_LAYER_KHRONOS_validation
if debug mode is on? This patch removes that. And free the array of
extensions once the instance has been initialized.
Wu Jianhua Nov. 23, 2021, 3:08 p.m. UTC | #6
Lynne:
> 23 Nov 2021, 12:03 by toqsxw@outlook.com:
>
>> Lynne:
>> >23 Nov 2021, 10:48 by jianhua.wu@intel.com:
>> >>Lynne:
>>
>>>>> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
>>>>> Lynne
>>>>> Sent: Tuesday, November 23, 2021 5:23 PM
>>>>> To: FFmpeg development discussions and patches <ffmpeg-
>>>>> devel@ffmpeg.org>
>>>>> Subject: Re: [FFmpeg-devel] [PATCH 4/4] avutil/hwcontext_vulkan: fully
>>>>> support customizable validation layers
>>>>>
>>>>> 23 Nov 2021, 10:01 by jianhua.wu@intel.com:
>>>>>
>>>>> > Validation layer is an indispensable part of developing on Vulkan.
>>>>> >
>>>>> > The following commands is on how to enable validation layers:
>>>>> >
>>>>> > ffmpeg -init_hw_device
>>>>> >
>>>>> vulkan=0,debug=1,validation_layers=VK_LAYER_KHRONOS_validation+VK_L
>>>>> AYE
>>>>> > R_LUNARG_api_dump
>>>>> >
>>>>> > Signed-off-by: Wu Jianhua <jianhua.wu@intel.com>
>>>>> > ---
>>>>> >  libavutil/hwcontext_vulkan.c | 110 ++++++++++++++++++++++++++++---
>>>>> ----
>>>>> >  libavutil/hwcontext_vulkan.h |   7 +++
>>>>> >  2 files changed, 97 insertions(+), 20 deletions(-)
>>>>> >
>>>>> >
>>>>> > +    /**
>>>>> > +     * Enabled validation layers.
>>>>> > +     * If no layers are enabled, set these fields to NULL, and 0 respectively.
>>>>> > +     */
>>>>> > +    const char * const *enabled_validation_layers;
>>>>> > +    int nb_enabled_validation_layers;
>>>>> > +
>>>>> >
>>>>>
>>>>> Why are you exposing them? Do API users really need to know this?
>>>>>
>>>>
>>>> It's okay. For it's only integrated in a really small separate function it
>>>> could be skipped by the status debug_mode as before. And validation
>>>> layers are embed by other specific drivers, platforms(such as those
>>>> specific layers in androids) or SDK, the FFmpeg is not need to do anything
>>>> more whatever the current is compiled with optimization mode or debug mode.
>>>> The use who only want to use filter is simply not able to know how to
>>>> enable the debug_mode. For me, as a user also, it is important to me, I
>>>> don't want to changed the code then compiling to use the specific validation
>>>> layers again and again.  And not only the developer need to use it, those
>>>> people who help us test could report a more detailed problem. I think it's
>>>> really benefit.
>>>>
>>>
>>> Sorry, I didn't quite understand that.
>>> I'm not objecting to being able to use custom debug layers and activating
>>> them if they're requested from the user. We already do that for the
>>> standard debug layer anyway. I'm just not sure I understand why filters
>>> would need to know if a debug layer is ran, and which one it is, by exposing
>>> them via the public API.
>>>
>>
>> Oh! My bad. I supposed you mean we may be not necessary to expose
>> layer info to the Device Context, right?
>>
>
> Yup, that's right. We'd like to keep the API as small as possible, and
> it's already quite large compared to other hwcontexts. In this case,
> I don't think it's necessary. API users already know, since they can request
> layers, and filters shouldn't have to know.
>
> So just remove the public API part of the patch.
>
> As for the rest, could you always include VK_LAYER_KHRONOS_validation
> if debug mode is on? This patch removes that. And free the array of
> extensions once the instance has been initialized.
>

Sure I do. I'll update this patch soon.

Thanks,
Jianhua
diff mbox series

Patch

diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 644ed947f8..b808f8f76c 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -146,6 +146,13 @@  typedef struct AVVkFrameInternal {
         }                                                                      \
     } while(0)
 
+#define RELEASE_PROPS(props, count)                                            \
+    if (props) {                                                               \
+        for (int i = 0; i < count; i++)                                        \
+            av_free((void *)((props)[i]));                                     \
+        av_free((void *)props);                                                \
+    }
+
 static const struct {
     enum AVPixelFormat pixfmt;
     const VkFormat vkfmts[4];
@@ -511,15 +518,83 @@  static int check_extensions(AVHWDeviceContext *ctx, int dev, AVDictionary *opts,
     return 0;
 
 fail:
-    if (extension_names)
-        for (int i = 0; i < extensions_found; i++)
-            av_free((void *)extension_names[i]);
-    av_free(extension_names);
+    RELEASE_PROPS(extension_names, extensions_found);
     av_free(user_exts_str);
     av_free(sup_ext);
     return err;
 }
 
+static int check_validation_layers(AVHWDeviceContext *ctx, AVDictionary *opts,
+                                   const char * const **dst, uint32_t *num)
+{
+    int found, err = 0;
+    VulkanDevicePriv *priv = ctx->internal->priv;
+    FFVulkanFunctions *vk = &priv->vkfn;
+
+    uint32_t sup_layer_count;
+    VkLayerProperties *sup_layers;
+
+    AVDictionaryEntry *user_layers;
+    char *user_layers_str, *save, *token;
+
+    const char **enabled_layers = NULL;
+    uint32_t enabled_layers_count = 0;
+
+    user_layers = av_dict_get(opts, "validation_layers", NULL, 0);
+    if (!user_layers)
+        return 0;
+
+    user_layers_str = av_strdup(user_layers->value);
+    if (!user_layers_str) {
+        err = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    vk->EnumerateInstanceLayerProperties(&sup_layer_count, NULL);
+    sup_layers = av_malloc_array(sup_layer_count, sizeof(VkLayerProperties));
+    if (!sup_layers)
+        return AVERROR(ENOMEM);
+    vk->EnumerateInstanceLayerProperties(&sup_layer_count, sup_layers);
+
+    av_log(ctx, AV_LOG_VERBOSE, "Supported Validation layers:\n");
+    for (int i = 0; i < sup_layer_count; i++)
+        av_log(ctx, AV_LOG_VERBOSE, "\t%s\n", sup_layers[i].layerName);
+
+    token = av_strtok(user_layers_str, "+", &save);
+    while (token) {
+        found = 0;
+        for (int j = 0; j < sup_layer_count; j++) {
+            if (!strcmp(token, sup_layers[j].layerName)) {
+                found = 1;
+                break;
+            }
+        }
+        if (found) {
+            av_log(ctx, AV_LOG_VERBOSE, "Requested Validation Layer: %s\n", token);
+            ADD_VAL_TO_LIST(enabled_layers, enabled_layers_count, token);
+        } else {
+            av_log(ctx, AV_LOG_ERROR,
+                   "Validation Layer \"%s\" not support.\n", token);
+            err = AVERROR(EINVAL);
+            goto fail;
+        }
+        token = av_strtok(NULL, "+", &save);
+    }
+
+    *dst = enabled_layers;
+    *num = enabled_layers_count;
+
+    av_free(sup_layers);
+    av_free(user_layers_str);
+    return 0;
+
+fail:
+    RELEASE_PROPS(enabled_layers, enabled_layers_count);
+    av_free(sup_layers);
+    av_free(user_layers_str);
+    return err;
+}
+
 /* Creates a VkInstance */
 static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
 {
@@ -558,13 +633,18 @@  static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
     /* Check for present/missing extensions */
     err = check_extensions(ctx, 0, opts, &inst_props.ppEnabledExtensionNames,
                            &inst_props.enabledExtensionCount, debug_mode);
+    hwctx->enabled_inst_extensions = inst_props.ppEnabledExtensionNames;
+    hwctx->nb_enabled_inst_extensions = inst_props.enabledExtensionCount;
     if (err < 0)
         return err;
 
     if (debug_mode) {
-        static const char *layers[] = { "VK_LAYER_KHRONOS_validation" };
-        inst_props.ppEnabledLayerNames = layers;
-        inst_props.enabledLayerCount = FF_ARRAY_ELEMS(layers);
+        err = check_validation_layers(ctx, opts, &inst_props.ppEnabledLayerNames,
+                                      &inst_props.enabledLayerCount);
+        hwctx->enabled_validation_layers = inst_props.ppEnabledLayerNames;
+        hwctx->nb_enabled_validation_layers = inst_props.enabledLayerCount;
+        if (err)
+            return err;
     }
 
     /* Try to create the instance */
@@ -574,9 +654,6 @@  static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
     if (ret != VK_SUCCESS) {
         av_log(ctx, AV_LOG_ERROR, "Instance creation failure: %s\n",
                vk_ret2str(ret));
-        for (int i = 0; i < inst_props.enabledExtensionCount; i++)
-            av_free((void *)inst_props.ppEnabledExtensionNames[i]);
-        av_free((void *)inst_props.ppEnabledExtensionNames);
         return AVERROR_EXTERNAL;
     }
 
@@ -604,9 +681,6 @@  static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
                                          hwctx->alloc, &p->debug_ctx);
     }
 
-    hwctx->enabled_inst_extensions = inst_props.ppEnabledExtensionNames;
-    hwctx->nb_enabled_inst_extensions = inst_props.enabledExtensionCount;
-
     return 0;
 }
 
@@ -1163,13 +1237,9 @@  static void vulkan_device_free(AVHWDeviceContext *ctx)
     if (p->libvulkan)
         dlclose(p->libvulkan);
 
-    for (int i = 0; i < hwctx->nb_enabled_inst_extensions; i++)
-        av_free((void *)hwctx->enabled_inst_extensions[i]);
-    av_free((void *)hwctx->enabled_inst_extensions);
-
-    for (int i = 0; i < hwctx->nb_enabled_dev_extensions; i++)
-        av_free((void *)hwctx->enabled_dev_extensions[i]);
-    av_free((void *)hwctx->enabled_dev_extensions);
+    RELEASE_PROPS(hwctx->enabled_inst_extensions, hwctx->nb_enabled_inst_extensions);
+    RELEASE_PROPS(hwctx->enabled_dev_extensions, hwctx->nb_enabled_dev_extensions);
+    RELEASE_PROPS(hwctx->enabled_validation_layers, hwctx->nb_enabled_validation_layers);
 }
 
 static int vulkan_device_create_internal(AVHWDeviceContext *ctx,
diff --git a/libavutil/hwcontext_vulkan.h b/libavutil/hwcontext_vulkan.h
index fdf2a60156..80c36d6f26 100644
--- a/libavutil/hwcontext_vulkan.h
+++ b/libavutil/hwcontext_vulkan.h
@@ -85,6 +85,13 @@  typedef struct AVVulkanDeviceContext {
     const char * const *enabled_inst_extensions;
     int nb_enabled_inst_extensions;
 
+    /**
+     * Enabled validation layers.
+     * If no layers are enabled, set these fields to NULL, and 0 respectively.
+     */
+    const char * const *enabled_validation_layers;
+    int nb_enabled_validation_layers;
+
     /**
      * Enabled device extensions. By default, VK_KHR_external_memory_fd,
      * VK_EXT_external_memory_dma_buf, VK_EXT_image_drm_format_modifier,