您的位置:首页 > 其它

vulakn教程--Drawing a Triangle--Set up--Validation layers

2016-09-04 10:04 435 查看
原文链接: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
中有定义:

#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME  "VK_EXT_debug_report"  //这是验证所需支持的扩展(extension)。


GLFW提供了一个获取extension的简便方法:

const char** glfwGetRequiredInstanceExtensions(uint32_t* count);


获取extension列表,并添加
VK_EXT_DEBUG_REPORT_EXTENSION_NAME
(我觉得extension 列表里应该已有此扩展):

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:

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


VkResult vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties);


vkEnumerateInstanceLayerProperties()
可以获取所有支持的
VkLayerProperties
,当
pProperties
nullptr
时,pPropertyCount将被赋予所被支持的
VkLayerProperties
数量;
pProperties
不为
nullptr
时,
pPropertyCount
将被赋予所被支持的
VkLayerProperties
数量的同时,
pProperties
指向
pPropertyCount
VkLayerProperties
的数组。

//我们需要的layer
const std::vector<const char*> validationLayers = {
"VK_LAYER_LUNARG_standard_validation"
};


//检查是否支持
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;
}
}
return true;
}


然后修改
createInfo
, 为它添加layers:

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:

//注意这里只是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可以取值如下(可以组合使用):

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的数据。

VkDebugReportCallbackEXT callback; //声明


和创建VkInstance一样,需要填充一个VkDebugReportCallbackCreateInfoEXT结构体:

typedef struct VkDebugReportCallbackCreateInfoEXT {
VkStructureType sType;
const void* pNext;
VkDebugReportFlagsEXT flags;
PFN_vkDebugReportCallbackEXT pfnCallback;
void* pUserData;
} VkDebugReportCallbackCreateInfoEXT;


填充:

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(...)
的地址:

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:

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函数:

VDeleter<VkDebugReportCallbackEXT> callback{instance, DestroyDebugReportCallbackEXT};


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进行配置的文件,你可以对这个问文件进行配置。但在后续的教程里,我都认为你采用的是默认配置。

源码 :

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

#include <iostream>
#include <stdexcept>
#include <functional>
#include <vector>
#include <cstring>

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 <typename T>
class VDeleter {
public:
VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {}

VDeleter(std::function<void(T, VkAllocationCallbacks*)> deletef) {
this->deleter = [=](T obj) {
deletef(obj, nullptr);
};
}

VDeleter(const VDeleter<VkInstance>& instance, std::function<void(VkInstance, T, VkAllocationCallbacks*)> deletef) {
this->deleter = [&instance, deletef](T obj) {
deletef(instance, obj, nullptr);
};
}

VDeleter(const VDeleter<VkDevice>& 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<VkInstance> instance {vkDestroyInstance};
VDeleter<VkDebugReportCallbackEXT> 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<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;
}
}

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Vulkan-验证层