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 //我们需要的layer
const
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的Instance
kResult result = vkCreateInstance(&inst_info,nullptr,&instance);
做了这些以后我们就可验证错误了吗?别急,我们还需要定义一个回调函数(callback),将debug信息带回到我们的程序中,然后显示出来。现在,让我们看看这个回调函数长什么样。定义一个具有PFN_vkDebugReportCallbackEXT
原型(prototype)的静态(static)成员函数debugCallbak:
12345678 //注意这里只是windows下的实现。
#ifndef WIN32
#define __stdcall
#endif
static
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 NDEBUG
const
bool
enableValidationLayers =
false
;
#else
const
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;
}