vulakn教程--Drawing a Triangle--Set up--Validation layers
原文链接:Vulkan-tutorial
Validation layers 验证层
为什么要用Validation layers ?
Vulkan的设计理念是:使驱动(driver)的负担最小化。一个明显的表现就是它有限的错误检测,像设置错误的枚举值或者将必须的函数参数传递为空指针(NULL)这类简单的操作都没有明确的处理,Vulkan只是简单的Crash或者产生一些未定义的行为(undefined behavior)。因为Vulkan要求你必须知道你正在做什么。但这并不意味着我们就无从下手了,相反,Vulakn引入了一个非常优雅的方法:Validation layers.Validation layers是一个可选的组件(optional components) ,它连接Vulkan的方法调用。Validation layers 的基本功能有以下几个:
- 检查参数是否被勿用。
- 追踪对象的创建与销毁,检测是否与有资源泄露。
- 追踪线程(thread)调用的源头,检测线程是否安全。
- 将方法调用的参数打印到标准输出。
- Tracing Vulkan calls for profiling and replaying。
vulkan.h 中有定义:
1 #define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report" //这是验证所需支持的扩展(extension)。
GLFW提供了一个获取extension的简便方法:
1 const char** glfwGetRequiredInstanceExtensions(uint32_t* count);
获取extension列表,并添加 VK_EXT_DEBUG_REPORT_EXTENSION_NAME (我觉得extension 列表里应该已有此扩展):
12345678910111213141516 std::vector<const char*=""> getRequiredExtensions() { std::vector<const char*=""> extensions; unsigned int glfwExtensionCount = 0; const char** glfwExtensions; glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); for (unsigned int i = 0; i < glfwExtensionCount; i++) { extensions.push_back(glfwExtensions[i]); } if (enableValidationLayers) { extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); } return extensions;}
然后修改createInfo,为它添加extensions:
123 auto extensions = getRequiredExtensions();createInfo.enabledExtensionCount = extensions.size();createInfo.ppEnabledExtensionNames=extensions.data();
LunarG validation layers 只有在安装了LunarG SDK的PC上才能使用,这里我们不需要所有的layers,LunarG SDK 只需要VK_LAYER_LUNARG_standard_validation 即可。我们首先获取所有的layers , 然后检查是否支持VK_LAYER_LUNARG_standard_validation 。
1 VkResult vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties);
vkEnumerateInstanceLayerProperties()可以获取所有支持的VkLayerProperties,当pProperties为nullptr时,pPropertyCount将被赋予所被支持的VkLayerProperties数量;pProperties不为nullptr时,pPropertyCount将被赋予所被支持的VkLayerProperties数量的同时,pProperties指向pPropertyCount个VkLayerProperties的数组。
1234 //我们需要的layerconst std::vector<const char*=""> validationLayers = { "VK_LAYER_LUNARG_standard_validation"};
1234567891011121314151617181920 //检查是否支持bool checkValidationLayerSupport() { uint32_t layerCount; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); std::vector 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; } } return true;}
然后修改createInfo, 为它添加layers:
12345678910 bool enableValidationLayers = checkValidationLayerSupport();if (enableValidationLayers) { createInfo.enabledLayerCount = validationLayers.size(); createInfo.ppEnabledLayerNames = validationLayers.data();}else{ createInfo.enabledLayerCount = 0;} //创建具有DEBUG的InstancekResult result = vkCreateInstance(&inst_info,nullptr,&instance);
做了这些以后我们就可验证错误了吗?别急,我们还需要定义一个回调函数(callback),将debug信息带回到我们的程序中,然后显示出来。现在,让我们看看这个回调函数长什么样。定义一个具有PFN_vkDebugReportCallbackEXT原型(prototype)的静态(static)成员函数debugCallbak:
12345678 //注意这里只是windows下的实现。#ifndef WIN32#define __stdcall#endifstatic VkBool32 __stdcall debugCallback( VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) { std::cerr << "validation layer: " << msg << std::endl; return VK_FALSE;}
第一个参数VkDebugReportFlagsEXT可以取值如下(可以组合使用):
1 2 3 4 5 | VK_DEBUG_REPORT_INFORMATION_BIT_EXT VK_DEBUG_REPORT_WARNING_BIT_EXT VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT VK_DEBUG_REPORT_ERROR_BIT_EXT VK_DEBUG_REPORT_DEBUG_BIT_EXT |
Msg 参数就是我们需要的debug信息,userData是我们要传给debugCallback的数据。
1 VkDebugReportCallbackEXT callback; //声明
和创建VkInstance一样,需要填充一个VkDebugReportCallbackCreateInfoEXT结构体:
1234567 typedef struct VkDebugReportCallbackCreateInfoEXT { VkStructureType sType; const void* pNext; VkDebugReportFlagsEXT flags; PFN_vkDebugReportCallbackEXT pfnCallback; void* pUserData;} VkDebugReportCallbackCreateInfoEXT;
填充:
123 VkDebugReportCallbackCreateInfoEXT createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback;//pUserData 也可以指定数据,类似userData
接下来会和VkInstance类似,调用 vkCreateDebugReportCallbackEXT(…)函数来创建我们的callback吗?并不尽然,因为vkCreateDebugReportCallbackEXT(…)是扩展函数,它并不会随vulkan 核心API一同加载,所以需要使用: PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance instance, const char* pName); 函数来加载vkCreateDebugReportCallbackEXT(...)的地址:
12345678 VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); if (func != nullptr) { return func(instance, pCreateInfo, pAllocator, pCallback); } else { return VK_ERROR_EXTENSION_NOT_PRESENT; }}
创建 callback:
123 if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!");}
好了,如果你运行程序,会出现两个窗体,一个是用来显示我们的三角形的,现在它是空的,另一个是命令提示窗,它显示debug信息。现在关闭窗口,会发现:
糟糕,有错误!怎么可能? 它提示callbacks没有被清除。 VkDebugReportCallbackEXT 对象需要被vkDestroyDebugReportCallbackEXT清除,现在用VDeleter包裹callbacks,并添加vkDestroyDebugReportCallbackEXT函数:
1 VDeleter callback{instance, DestroyDebugReportCallbackEXT};
123456 void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); if (func != nullptr) { func(instance, callback, pAllocator); }}
在Vulkan SDK 的目录下有个Config文件夹,这个文件下有个vk_layer_settings.txt文件,是你对Validation layers进行配置的文件,你可以对这个问文件进行配置。但在后续的教程里,我都认为你采用的是默认配置。
源码 :
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 #define GLFW_INCLUDE_VULKAN#include #include #include #include #include #include const int WIDTH = 800;const int HEIGHT = 600; const std::vector<const char*=""> validationLayers = { "VK_LAYER_LUNARG_standard_validation"}; #ifdef NDEBUGconst bool enableValidationLayers = false;#elseconst bool enableValidationLayers = true;#endif VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); if (func != nullptr) { return func(instance, pCreateInfo, pAllocator, pCallback); } else { return VK_ERROR_EXTENSION_NOT_PRESENT; }} void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); if (func != nullptr) { func(instance, callback, pAllocator); }} template "">class VDeleter { public: VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} VDeleter(std::function<void(t, vkallocationcallbacks*)=""> deletef) { this->deleter = [=](T obj) { deletef(obj, nullptr); }; } VDeleter(const VDeleter& instance, std::function< void(vkinstance, t,="" vkallocationcallbacks*)=""> deletef) { this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; } VDeleter(const VDeleter& device, std::function< void(vkdevice, t,="" vkallocationcallbacks*)=""> deletef) { this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; } ~VDeleter() { cleanup(); } T* operator &() { cleanup(); return &object; } operator T() const { return object; } private: T object {VK_NULL_HANDLE}; std::function<void(t)> deleter; void cleanup() { if (object != VK_NULL_HANDLE) { deleter(object); } object = VK_NULL_HANDLE; }}; class HelloTriangleApplication { public: void run() { initWindow(); initVulkan(); mainLoop(); } private: GLFWwindow* window; VDeleter instance {vkDestroyInstance}; VDeleter callback {instance, DestroyDebugReportCallbackEXT}; void initWindow() { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); } void initVulkan() { createInstance(); setupDebugCallback(); } void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } } void createInstance() { if (enableValidationLayers && !checkValidationLayerSupport()) { throw std::runtime_error("validation layers requested, but not available!"); } VkApplicationInfo appInfo = {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = "Hello Triangle"; appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.pEngineName = "No Engine"; appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.apiVersion = VK_API_VERSION_1_0; VkInstanceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); createInfo.enabledExtensionCount = extensions.size(); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { createInfo.enabledLayerCount = validationLayers.size(); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); } } void setupDebugCallback() { if (!enableValidationLayers) return; VkDebugReportCallbackCreateInfoEXT createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug callback!"); } } std::vector<const char*=""> getRequiredExtensions() { std::vector<const char*=""> extensions; unsigned int glfwExtensionCount = 0; const char** glfwExtensions; glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); for (unsigned int i = 0; i < glfwExtensionCount; i++) { extensions.push_back(glfwExtensions[i]); } if (enableValidationLayers) { extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); } return extensions; } bool checkValidationLayerSupport() { uint32_t layerCount; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); std::vector 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; } } return true; } static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) { std::cerr << "validation layer: " << msg << std::endl; return VK_FALSE; }}; int main() { HelloTriangleApplication app; try { app.run(); } catch (const std::runtime_error& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS;}
