当前位置: 首页 > news >正文

Vulkan 学习(18)---- 使用 ValidationLayer

目录

      • ValidationLayer 简介
      • 启用 ValidationLayer
        • Enable ValidationLayer
        • 检查扩展支持
        • 创建回调函数
        • 注册回调函数
      • Android ValidationLayer

ValidationLayer 简介

Vulkan API 的设计是按照最小化驱动程序的开销进行的,所以默认情况下 Vulkan API 提供的错误检测的功能非常有限,很多基本的错误都没有被 Vulkan 显式进行处理,遇到错误也是直接错误崩溃,或者直接发生未定义行为,比如使用了一个新的特性,但是在逻辑设备创建的时候没有添加这个拓展

Vulkan 引入验证层,验证层是一个可选组件,它会 hookVulkan 的函数调用中,验证层的常见功能为:

  • 对照规范检查参数的值是否合法
  • 跟踪对象的销毁和创建,找出潜在的资源泄露
  • 跟踪调用来源的线程来检查线程的安全性
  • 将每个调用及其参数记录到标准输出
  • 跟踪 Vulkan 调用进行分析和重放

ValidationLayer
这些验证层可以自由的开始或者关闭,可以在调试时开启验证层,在发布的时候禁用
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;
}
  • 第一个参数表示消息的严重程度,可以是下面的值之一
  1. VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:诊断消息
  2. VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:信息性消息,例如资源的创建
  3. VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:关于不一定是错误的行为的消息,但很可能是应用程序中的错误
  4. VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:关于无效且可能导致崩溃的行为的消息
  • 第二个参数 messageType 可以是下面的值
  1. VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT:发生了一些与规范或性能无关的事件
  2. VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT:发生了一些违反规范或指示可能错误的事情
  3. VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXTVulkan 的潜在非最佳使用
  • pCallbackData 参数是指一个 VkDebugUtilsMessengerCallbackDataEXT 结构,其中包含消息本身的详细信息
  1. pMessage:调试消息
  2. pObjects:与消息相关的 Vulkan 对象句柄数组
  3. objectCount:数组中的对象数
  • pUserData 参数包含一个在回调设置期间指定的指针,允许您将自己的数据传递给它

返回值表示是否中止验证层的消息的 Vulkan 调用,如果回调返回 true,则调用将以 VK_ERROR_VALIDATION_FAILED_EXT 错误中止

注册回调函数

我们需要使用 vkCreateDebugUtilsMessengerEXT 函数用来创建 VkDebugUtilsMessengerEXT 对象
vkCreateDebugUtilsMessengerEXTpCreateInfo 参数传入了 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/vulkanVulkan loader 会自动查找此目录,并且对接到 ValidationLayer

相关文章:

  • Function Calling与MCP的区别
  • python训练day44 预训练模型
  • Windows系统安装鸿蒙模拟器
  • 原型设计Axure RP网盘资源下载与安装教程共享
  • wpf的Binding之UpdateSourceTrigger
  • Go语言--语法基础6--基本数据类型--数组类型(2)
  • MATLAB GUI界面设计 第七章——高级应用
  • 领域驱动设计(DDD)【26】之CQRS模式初探
  • 大语言模型训练阶段
  • Tang Prime 20K板OV2640例程
  • 30套精品论文答辩开题报告PPT模版
  • (八)聚类
  • node js入门,包含express,npm管理
  • 【3.3】Pod详解——容器探针部署第一个pod
  • Python 可迭代的对象、迭代器 和生成器(另一个示例:等差数列生成器)
  • 设计模式 | 组合模式
  • Excel之证件照换底色3
  • Ubuntu无法显示IP地址
  • 【算法设计与分析】(二)什么是递归,以及分治法的基本思想
  • Mac homebrew 安装教程