vulakn教程--Drawing a Triangle--Set up--Validation layers
2016-09-04 10:04
435 查看
原文链接:Vulkan-tutorial
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。
GLFW提供了一个获取extension的简便方法:
获取extension列表,并添加
然后修改
LunarG validation layers 只有在安装了LunarG SDK的PC上才能使用,这里我们不需要所有的layers,LunarG SDK 只需要
然后修改
做了这些以后我们就可验证错误了吗?别急,我们还需要定义一个回调函数(callback),将debug信息带回到我们的程序中,然后显示出来。现在,让我们看看这个回调函数长什么样。定义一个具有
第一个参数VkDebugReportFlagsEXT可以取值如下(可以组合使用):
Msg 参数就是我们需要的debug信息,userData是我们要传给debugCallback的数据。
和创建VkInstance一样,需要填充一个VkDebugReportCallbackCreateInfoEXT结构体:
填充:
接下来会和
创建 callback:
好了,如果你运行程序,会出现两个窗体,一个是用来显示我们的三角形的,现在它是空的,另一个是命令提示窗,它显示debug信息。现在关闭窗口,会发现:
糟糕,有错误!怎么可能? 它提示callbacks没有被清除。 VkDebugReportCallbackEXT 对象需要被vkDestroyDebugReportCallbackEXT清除,现在用VDeleter包裹callbacks,并添加vkDestroyDebugReportCallbackEXT函数:
在Vulkan SDK 的目录下有个Config文件夹,这个文件下有个vk_layer_settings.txt文件,是你对Validation layers进行配置的文件,你可以对这个问文件进行配置。但在后续的教程里,我都认为你采用的是默认配置。
源码 :
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;
}
相关文章推荐
- vulakn教程--Drawing a Triangle--Set up--Logical Device
- vulakn教程--Drawing a Triangle--Set up--Physical Device and Queue Family
- vulakn教程--Drawing a Triangle--Set up--Instance
- vulakn教程--Drawing a Triangle--Pipeline--Introduction
- vulakn教程--Drawing a Triangle--Set up--Base code
- vulakn教程--Drawing a Triangle--Presentation--Window surface
- vulakn教程--Drawing a Triangle--Presentation--SwapChain
- vulakn教程--Drawing a Triangle--Pipeline--Render passes
- vulakn教程--Drawing a Triangle--Draw--Render and presentation
- vulakn教程--Drawing a Triangle--Pipeline--Fixed function
- vulakn教程--Drawing a Triangle--Presentation-- Image views
- vulakn教程--Drawing a Triangle--Pipeline--Shader Module
- vulakn教程--Drawing a Triangle--Draw--CommandBuffer
- vulakn教程--Drawing a Triangle--Draw--Framebuffer
- DirectX 教程: DirectX Tutorial - Direct3D: Drawing a Triangle
- CIRCOS教程翻译 3.9——Drawing on top of data
- "https://open.gl/"教程之Drawing Polygons源码(freeglut版)
- Android OpenGL ES 开发教程(11):绘制三角形Triangle
- Drawing a triangle - Instance
- NPOI 1.2.3教程 -22 画图Drawing