mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-04-14 00:58:38 +02:00
avutil/hwcontext_vulkan: fully support customizable validation layers
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_LUNARG_monitor+VK_LAYER_LUNARG_api_dump Signed-off-by: Wu Jianhua <jianhua.wu@intel.com>
This commit is contained in:
parent
1e5acc1b59
commit
b3624069f0
@ -146,6 +146,13 @@ typedef struct AVVkFrameInternal {
|
|||||||
} \
|
} \
|
||||||
} while(0)
|
} 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 {
|
static const struct {
|
||||||
enum AVPixelFormat pixfmt;
|
enum AVPixelFormat pixfmt;
|
||||||
const VkFormat vkfmts[4];
|
const VkFormat vkfmts[4];
|
||||||
@ -511,25 +518,129 @@ static int check_extensions(AVHWDeviceContext *ctx, int dev, AVDictionary *opts,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
if (extension_names)
|
RELEASE_PROPS(extension_names, extensions_found);
|
||||||
for (int i = 0; i < extensions_found; i++)
|
|
||||||
av_free((void *)extension_names[i]);
|
|
||||||
av_free(extension_names);
|
|
||||||
av_free(user_exts_str);
|
av_free(user_exts_str);
|
||||||
av_free(sup_ext);
|
av_free(sup_ext);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int check_validation_layers(AVHWDeviceContext *ctx, AVDictionary *opts,
|
||||||
|
const char * const **dst, uint32_t *num,
|
||||||
|
int *debug_mode)
|
||||||
|
{
|
||||||
|
static const char default_layer[] = { "VK_LAYER_KHRONOS_validation" };
|
||||||
|
|
||||||
|
int found = 0, 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 = NULL;
|
||||||
|
char *save, *token;
|
||||||
|
|
||||||
|
const char **enabled_layers = NULL;
|
||||||
|
uint32_t enabled_layers_count = 0;
|
||||||
|
|
||||||
|
AVDictionaryEntry *debug_opt = av_dict_get(opts, "debug", NULL, 0);
|
||||||
|
int debug = debug_opt && strtol(debug_opt->value, NULL, 10);
|
||||||
|
|
||||||
|
/* If `debug=0`, enable no layers at all. */
|
||||||
|
if (debug_opt && !debug)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* If `debug=1` is specified, enable the standard validation layer extension */
|
||||||
|
if (debug) {
|
||||||
|
*debug_mode = debug;
|
||||||
|
for (int i = 0; i < sup_layer_count; i++) {
|
||||||
|
if (!strcmp(default_layer, sup_layers[i].layerName)) {
|
||||||
|
found = 1;
|
||||||
|
av_log(ctx, AV_LOG_VERBOSE, "Default validation layer %s is enabled\n",
|
||||||
|
default_layer);
|
||||||
|
ADD_VAL_TO_LIST(enabled_layers, enabled_layers_count, default_layer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user_layers = av_dict_get(opts, "validation_layers", NULL, 0);
|
||||||
|
if (!user_layers)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
user_layers_str = av_strdup(user_layers->value);
|
||||||
|
if (!user_layers_str) {
|
||||||
|
err = AVERROR(EINVAL);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = av_strtok(user_layers_str, "+", &save);
|
||||||
|
while (token) {
|
||||||
|
found = 0;
|
||||||
|
if (!strcmp(default_layer, token)) {
|
||||||
|
if (debug) {
|
||||||
|
/* if the `debug=1`, default_layer is enabled, skip here */
|
||||||
|
token = av_strtok(NULL, "+", &save);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
/* if the `debug=0`, enable debug mode to load its callback properly */
|
||||||
|
*debug_mode = debug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
av_free(user_layers_str);
|
||||||
|
|
||||||
|
end:
|
||||||
|
av_free(sup_layers);
|
||||||
|
|
||||||
|
*dst = enabled_layers;
|
||||||
|
*num = enabled_layers_count;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
RELEASE_PROPS(enabled_layers, enabled_layers_count);
|
||||||
|
av_free(sup_layers);
|
||||||
|
av_free(user_layers_str);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/* Creates a VkInstance */
|
/* Creates a VkInstance */
|
||||||
static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
|
static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0, debug_mode = 0;
|
||||||
VkResult ret;
|
VkResult ret;
|
||||||
VulkanDevicePriv *p = ctx->internal->priv;
|
VulkanDevicePriv *p = ctx->internal->priv;
|
||||||
FFVulkanFunctions *vk = &p->vkfn;
|
FFVulkanFunctions *vk = &p->vkfn;
|
||||||
AVVulkanDeviceContext *hwctx = ctx->hwctx;
|
AVVulkanDeviceContext *hwctx = ctx->hwctx;
|
||||||
AVDictionaryEntry *debug_opt = av_dict_get(opts, "debug", NULL, 0);
|
|
||||||
const int debug_mode = debug_opt && strtol(debug_opt->value, NULL, 10);
|
|
||||||
VkApplicationInfo application_info = {
|
VkApplicationInfo application_info = {
|
||||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||||
.pEngineName = "libavutil",
|
.pEngineName = "libavutil",
|
||||||
@ -555,17 +666,18 @@ static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = check_validation_layers(ctx, opts, &inst_props.ppEnabledLayerNames,
|
||||||
|
&inst_props.enabledLayerCount, &debug_mode);
|
||||||
|
if (err)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
/* Check for present/missing extensions */
|
/* Check for present/missing extensions */
|
||||||
err = check_extensions(ctx, 0, opts, &inst_props.ppEnabledExtensionNames,
|
err = check_extensions(ctx, 0, opts, &inst_props.ppEnabledExtensionNames,
|
||||||
&inst_props.enabledExtensionCount, debug_mode);
|
&inst_props.enabledExtensionCount, debug_mode);
|
||||||
|
hwctx->enabled_inst_extensions = inst_props.ppEnabledExtensionNames;
|
||||||
|
hwctx->nb_enabled_inst_extensions = inst_props.enabledExtensionCount;
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
goto fail;
|
||||||
|
|
||||||
if (debug_mode) {
|
|
||||||
static const char *layers[] = { "VK_LAYER_KHRONOS_validation" };
|
|
||||||
inst_props.ppEnabledLayerNames = layers;
|
|
||||||
inst_props.enabledLayerCount = FF_ARRAY_ELEMS(layers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to create the instance */
|
/* Try to create the instance */
|
||||||
ret = vk->CreateInstance(&inst_props, hwctx->alloc, &hwctx->inst);
|
ret = vk->CreateInstance(&inst_props, hwctx->alloc, &hwctx->inst);
|
||||||
@ -574,16 +686,14 @@ static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
|
|||||||
if (ret != VK_SUCCESS) {
|
if (ret != VK_SUCCESS) {
|
||||||
av_log(ctx, AV_LOG_ERROR, "Instance creation failure: %s\n",
|
av_log(ctx, AV_LOG_ERROR, "Instance creation failure: %s\n",
|
||||||
vk_ret2str(ret));
|
vk_ret2str(ret));
|
||||||
for (int i = 0; i < inst_props.enabledExtensionCount; i++)
|
err = AVERROR_EXTERNAL;
|
||||||
av_free((void *)inst_props.ppEnabledExtensionNames[i]);
|
goto fail;
|
||||||
av_free((void *)inst_props.ppEnabledExtensionNames);
|
|
||||||
return AVERROR_EXTERNAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ff_vk_load_functions(ctx, vk, p->extensions, 1, 0);
|
err = ff_vk_load_functions(ctx, vk, p->extensions, 1, 0);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
av_log(ctx, AV_LOG_ERROR, "Unable to load instance functions!\n");
|
av_log(ctx, AV_LOG_ERROR, "Unable to load instance functions!\n");
|
||||||
return err;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug_mode) {
|
if (debug_mode) {
|
||||||
@ -604,10 +714,11 @@ static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts)
|
|||||||
hwctx->alloc, &p->debug_ctx);
|
hwctx->alloc, &p->debug_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
hwctx->enabled_inst_extensions = inst_props.ppEnabledExtensionNames;
|
err = 0;
|
||||||
hwctx->nb_enabled_inst_extensions = inst_props.enabledExtensionCount;
|
|
||||||
|
|
||||||
return 0;
|
fail:
|
||||||
|
RELEASE_PROPS(inst_props.ppEnabledLayerNames, inst_props.enabledLayerCount);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct VulkanDeviceSelection {
|
typedef struct VulkanDeviceSelection {
|
||||||
@ -1163,13 +1274,8 @@ static void vulkan_device_free(AVHWDeviceContext *ctx)
|
|||||||
if (p->libvulkan)
|
if (p->libvulkan)
|
||||||
dlclose(p->libvulkan);
|
dlclose(p->libvulkan);
|
||||||
|
|
||||||
for (int i = 0; i < hwctx->nb_enabled_inst_extensions; i++)
|
RELEASE_PROPS(hwctx->enabled_inst_extensions, hwctx->nb_enabled_inst_extensions);
|
||||||
av_free((void *)hwctx->enabled_inst_extensions[i]);
|
RELEASE_PROPS(hwctx->enabled_dev_extensions, hwctx->nb_enabled_dev_extensions);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vulkan_device_create_internal(AVHWDeviceContext *ctx,
|
static int vulkan_device_create_internal(AVHWDeviceContext *ctx,
|
||||||
|
@ -45,6 +45,7 @@ typedef enum FFVulkanExtensions {
|
|||||||
#define FN_LIST(MACRO) \
|
#define FN_LIST(MACRO) \
|
||||||
/* Instance */ \
|
/* Instance */ \
|
||||||
MACRO(0, 0, FF_VK_EXT_NO_FLAG, EnumerateInstanceExtensionProperties) \
|
MACRO(0, 0, FF_VK_EXT_NO_FLAG, EnumerateInstanceExtensionProperties) \
|
||||||
|
MACRO(0, 0, FF_VK_EXT_NO_FLAG, EnumerateInstanceLayerProperties) \
|
||||||
MACRO(0, 0, FF_VK_EXT_NO_FLAG, CreateInstance) \
|
MACRO(0, 0, FF_VK_EXT_NO_FLAG, CreateInstance) \
|
||||||
MACRO(1, 0, FF_VK_EXT_NO_FLAG, DestroyInstance) \
|
MACRO(1, 0, FF_VK_EXT_NO_FLAG, DestroyInstance) \
|
||||||
\
|
\
|
||||||
|
Loading…
x
Reference in New Issue
Block a user