Vulkan 学习(18)---- 使用 ValidationLayer
目录
- ValidationLayer 简介
- 启用 ValidationLayer
- Enable ValidationLayer
- 检查扩展支持
- 创建回调函数
- 注册回调函数
- Android ValidationLayer
ValidationLayer 简介
Vulkan API
的设计是按照最小化驱动程序的开销进行的,所以默认情况下 Vulkan API
提供的错误检测的功能非常有限,很多基本的错误都没有被 Vulkan
显式进行处理,遇到错误也是直接错误崩溃,或者直接发生未定义行为,比如使用了一个新的特性,但是在逻辑设备创建的时候没有添加这个拓展
Vulkan
引入验证层,验证层是一个可选组件,它会 hook
到 Vulkan
的函数调用中,验证层的常见功能为:
- 对照规范检查参数的值是否合法
- 跟踪对象的销毁和创建,找出潜在的资源泄露
- 跟踪调用来源的线程来检查线程的安全性
- 将每个调用及其参数记录到标准输出
- 跟踪
Vulkan
调用进行分析和重放
这些验证层可以自由的开始或者关闭,可以在调试时开启验证层,在发布的时候禁用
Vulkan
本身实现不包含验证层的代码,但是 LunarG Vulkan SDK
提供了标准验证层的实现,用于检查常见的错误,它完全开源,只有安装了验证层才可以正常使用
启用 ValidationLayer
Enable ValidationLayer
添加一个新的函数 checkValidationLayerSupport
,使用函数 vkEnumrateInstanceLayerProPerties
可以枚举出所有支持的 Layer
bool checkValidationLayerSupport() {uint32_t layerCount;vkEnumerateInstanceLayerProperties(&layerCount, nullptr);std::vector<VkLayerProperties> availableLayers(layerCount);vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());for (const char* layerName : validationLayers) {bool layerFound = false;for (const auto& layerProperties : availableLayers) {if (strcmp(layerName, layerProperties.layerName) == 0) {layerFound = true;break;}}if (!layerFound) {return false;}
}
}
确定系统可以支持验证层之后,在 create_instance
的时候就可以指定参数开启验证层:
if (enableValidationLayers) {createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());createInfo.ppEnabledLayerNames = validationLayers.data();
} else {createInfo.enabledLayerCount = 0;
}
如果验证层在 sdk
中没有安装,vkCreateInstace
会提示 VK_ERROR_LAYER_NOT_PRESENT
错误
检查扩展支持
验证层会将消息打印到标准输出,但是我们也可以在程序中提供回调函数来处理验证层消息,还可以自己决定要看的消息类型,因为不是所有的消息都是错误或者致命错误
要在程序中回调处理相关的消息,需要使用 VK_EXT_debug_utils
扩展,然后再添加一个回调函数
这里使用 getRequiredExtensions
来获取需要的所有拓展,当然也包括添加 VK_EXT_DEBUG_UTILS_EXTENSION_NAME
拓展
std::vector<const char*> getRequiredExtensions() {uint32_t glfwExtensionCount = 0;const char** glfwExtensions;glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);if (enableValidationLayers) {extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);}return extensions;
}
创建回调函数
debugcallback
回调函数的原型如下:
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) {std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;return VK_FALSE;
}
- 第一个参数表示消息的严重程度,可以是下面的值之一
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT
:诊断消息VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
:信息性消息,例如资源的创建VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
:关于不一定是错误的行为的消息,但很可能是应用程序中的错误VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
:关于无效且可能导致崩溃的行为的消息
- 第二个参数
messageType
可以是下面的值
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
:发生了一些与规范或性能无关的事件VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
:发生了一些违反规范或指示可能错误的事情VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT
:Vulkan
的潜在非最佳使用
pCallbackData
参数是指一个VkDebugUtilsMessengerCallbackDataEXT
结构,其中包含消息本身的详细信息
pMessage
:调试消息pObjects
:与消息相关的Vulkan
对象句柄数组objectCount
:数组中的对象数
pUserData
参数包含一个在回调设置期间指定的指针,允许您将自己的数据传递给它
返回值表示是否中止验证层的消息的 Vulkan
调用,如果回调返回 true
,则调用将以 VK_ERROR_VALIDATION_FAILED_EXT
错误中止
注册回调函数
我们需要使用 vkCreateDebugUtilsMessengerEXT
函数用来创建 VkDebugUtilsMessengerEXT
对象
vkCreateDebugUtilsMessengerEXT
的 pCreateInfo
参数传入了 debugCallback
的函数指针
需要注意的是,CreateDebugUtilsMessengerEXT
是扩展函数,因此不会自动加载,我们必须使用 vkGetInstanceProcAddr
自己查找其地址
PFN_vkCreateDebugUtilsMessengerEXT
的函数原型是:
typedef VkResult (VKAPI_PTR *PFN_vkCreateDebugUtilsMessengerEXT)(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pMessenger);
参考的 CreateInfo
创建流程如下:
void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {createInfo = {};createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;createInfo.pfnUserCallback = debugCallback;
}
-
messageSeverity
字段允许您指定希望调用回调的所有严重性类型
在此处指定了除VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
之外的所有类型,以接收有关可能问题的通知,同时排除详细的常规调试信息 -
messageType
字段允许您筛选通知回调的消息类型,这里启用了所有类型 -
pfnUserCallback
字段指定回调函数的指针,可以选择性地将指针传递给pUserData
字段,该指针将通过pUserData
参数传递给回调函数
参考代码如下:
void setupDebugMessenger() {if (!enableValidationLayers) return;VkDebugUtilsMessengerCreateInfoEXT createInfo;populateDebugMessengerCreateInfo(createInfo);if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {throw std::runtime_error("failed to set up debug messenger!");}
}VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) {auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");if (func != nullptr) {return func(instance, pCreateInfo, pAllocator, pDebugMessenger);} else {return VK_ERROR_EXTENSION_NOT_PRESENT;}
}void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) {auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");if (func != nullptr) {func(instance, debugMessenger, pAllocator);}
}
Android ValidationLayer
Android
系统中使用 Vulkan loader
对接厂商的 vulkan api
的不同实现,vulkan loader
中也可以开启验证层
ValidationLayer
的实现可以从 khronos
的开源代码编译,生成对应的so
,放到 /data/local/debug/vulkan
下 Vulkan loader
会自动查找此目录,并且对接到 ValidationLayer