文章目录
- 验证层是什么?
- 使用验证层
- 消息回调
- 调试实例的创建和销毁
- 测试
- 配置
- 最终代码
验证层是什么?
Vulkan API是围绕最小化驱动程序开销的想法设计的,该目标的表现之一是默认情况下API中的错误检查非常有限。即使是像将枚举设置为不正确的值或将空指针传递给所需参数这样简单的错误,通常也不会显式处理,只会导致崩溃或未定义的行为。因为Vulkan要求你非常明确你所做的一切,所以很容易犯许多小错误,比如使用新的GPU功能,忘记在逻辑设备创建时请求它。
但是,这并不意味着不能将这些检查添加到API中。Vulkan为此引入了一个优雅的系统,称为验证层。验证层是可选的组件,它与Vulkan函数调用挂钩,以应用额外的操作。验证层中常见的操作有:
- 根据规格检查参数值以检测误用
- 跟踪对象的创建和销毁以查找资源泄漏
- 通过跟踪调用源自的线程来检查线程安全性
- 将每个调用及其参数记录到标准输出
- 跟踪Vulkan需要分析和重放
下面是一个诊断验证层中函数实现的示例:
VkResult vkCreateInstance(
const VkInstanceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkInstance* instance) {
if (pCreateInfo == nullptr || instance == nullptr) {
log("Null pointer passed to required parameter!");
return VK_ERROR_INITIALIZATION_FAILED;
}
return real_vkCreateInstance(pCreateInfo, pAllocator, instance);
}
这些验证层可以自由堆叠,以包含您感兴趣的所有调试功能。您可以简单地在调试版本中启用验证层,而在发布版本中完全禁用验证层,这将为您提供两全其美的效果!
Vulkan没有内置任何验证层,但是LunarG Vulkan SDK提供了一组很好的层来检查常见错误。它们也是完全开源的,因此您可以检查它们检查哪些错误并做出贡献。使用验证层是避免应用程序因意外依赖未定义行为而在不同驱动程序上中断的最佳方法。
只有在系统上安装了验证层后才能使用它们。例如,LunarG验证层仅在安装了Vulkan SDK的pc上可用。
以前在Vulkan中有两种不同类型的验证层:实例和特定于设备的验证层。这个想法是,实例层只检查与全局Vulkan对象(如实例)相关的调用,而设备特定层只检查与特定GPU相关的调用。设备特定层现在已被弃用,这意味着实例验证层适用于所有Vulkan调用。规范文档仍然建议您在设备级别启用验证层,以获得兼容性,这是某些实现所需要的。我们将简单地在逻辑设备级别指定与实例相同的层,稍后我们将看到这一点。
使用验证层
在本节中,我们将看到如何启用Vulkan SDK提供的标准诊断层。就像扩展一样,验证层需要通过指定其名称来启用。所有有用的标准验证都被捆绑到SDK中包含的一个层中,称为VK_LAYER_KHRONOS_validation。
让我们首先向程序添加两个配置变量,以指定要启用的层以及是否启用它们。我选择将该值基于程序是否在调试模式下编译。NDEBUG宏是c++标准的一部分,意思是“不调试”。
const uint32_t WIDTH = 800;
const uint32_t HEIGHT = 600;
const std::vector<const char*> validationLayers = {
"VK_LAYER_KHRONOS_validation"
};
#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif
我们将添加一个新的函数checkValidationLayerSupport来检查是否所有请求的层都可用。首先使用vkEnumerateInstanceLayerProperties函数列出所有可用的层。它的用法与实例创建章节中讨论的vkEnumerateInstanceExtensionProperties相同。
bool checkValidationLayerSupport() {
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
return false;
}
接下来,检查validationLayers中的所有层是否都存在于availableLayers列表中。您可能需要为strcmp包含。
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;
我们现在可以在createInstance中使用这个函数:
void createInstance() {
if (enableValidationLayers && !checkValidationLayerSupport()) {
throw std::runtime_error("validation layers requested, but not available!");
}
...
}
现在在调试模式下运行程序,并确保不会发生错误。如果是这样,请查看常见问题解答。
最后,修改VkInstanceCreateInfo结构实例化,使其包括验证层名称,如果它们是启用的:
if (enableValidationLayers) {
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
} else {
createInfo.enabledLayerCount = 0;
}
如果检查成功,那么vkCreateInstance不应该返回VK_ERROR_LAYER_NOT_PRESENT错误,但您应该运行程序以确保。
消息回调
缺省情况下,验证层将把调试消息打印到标准输出,但是我们也可以通过在程序中提供显式回调来自己处理它们。这还允许您决定希望看到哪种类型的消息,因为并非所有消息都是必要的(致命的)错误。如果你现在不想这样做,那么你可以跳到本章的最后一部分。
要在程序中设置一个回调来处理消息和相关的详细信息,我们必须使用VK_EXT_debug_utils扩展设置一个带有回调的调试信使。
我们将首先创建一个getRequiredExtensions函数,它将根据是否启用验证层返回所需的扩展列表:
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;
}
GLFW指定的扩展总是必需的,但是调试信使扩展是有条件添加的。注意,我在这里使用了VK_EXT_DEBUG_UTILS_EXTENSION_NAME宏,它等于字面值字符串“VK_EXT_debug_utils”。使用这个宏可以避免输入错误。
我们现在可以在createInstance中使用这个函数:
auto extensions = getRequiredExtensions();
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
运行程序以确保没有收到VK_ERROR_EXTENSION_NOT_PRESENT错误。我们实际上不需要检查这个扩展是否存在,因为它应该由验证层的可用性暗示出来。
现在让我们看看调试回调函数是什么样的。添加一个新的静态成员函数名为debugCallback与PFN_vkDebugUtilsMessengerCallbackEXT原型。VKAPI_ATTR和VKAPI_CALL确保函数具有正确的签名,以便Vulkan调用它。
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;
}
第一个参数指定消息的严重性,它是以下标志之一:
- VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:诊断消息
- VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:类似创建资源的信息
- VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:关于行为的消息,不一定是错误,但很可能是应用程序中的错误
- VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:关于无效行为的消息,可能导致崩溃
此枚举值的设置方式使您可以使用比较操作来检查消息与某些严重级别相比是相等还是更差,例如:
if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
// Message is important enough to show
}
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:潜在的非最佳使用Vulkan
pCallbackData参数引用了一个VkDebugUtilsMessengerCallbackDataEXT结构体,其中包含消息本身的详细信息,其中最重要的成员是:
- pMessage:作为以空结尾的字符串的调试消息
- objects:与消息相关的Vulkan对象句柄数组
- objectCount:数组中对象的个数
最后,pUserData参数包含一个指针,该指针是在设置回调期间指定的,并允许您将自己的数据传递给它。
回调返回一个布尔值,该值指示触发验证层消息的Vulkan调用是否应该中止。如果回调返回true,则调用将以VK_ERROR_VALIDATION_FAILED_EXT错误终止。这通常只用于测试验证层本身,因此您应该始终返回VK_FALSE。
现在剩下的就是告诉Vulkan回调函数。也许有些令人惊讶的是,即使是Vulkan中的调试回调也是用一个需要显式创建和销毁的句柄来管理的。这样的回调是调试消息的一部分,您可以根据需要拥有任意多个回调。在instance下为这个句柄添加一个类成员:
VkDebugUtilsMessengerEXT debugMessenger;
现在添加一个函数setupDebugMessenger,在createInstance之后从initVulkan调用:
void initVulkan() {
createInstance();
setupDebugMessenger();
}
void setupDebugMessenger() {
if (!enableValidationLayers) return;
}
我们需要填充一个结构,其中包含有关信使及其回调的详细信息:
VkDebugUtilsMessengerCreateInfoEXT 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;
createInfo.pUserData = nullptr; // Optional
messageSeverity字段允许您指定您希望调用回调的所有严重类型。除了VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT之外,我在这里指定了所有类型来接收有关可能问题的通知,同时省略了详细的一般调试信息。
类似地,messageType字段允许您筛选通知回调的消息类型。这里我只是启用了所有类型。如果它们对你没有用处,你可以禁用它们。
最后,pfnUserCallback字段指定指向回调函数的指针。您可以选择传递一个指向pUserData字段的指针,该指针将通过pUserData参数传递给回调函数。例如,你可以用它来传递一个指向HelloTriangleApplication类的指针。
注意,还有很多方法可以配置验证层消息和调试回调,但是对于本教程来说,这是一个很好的开始。有关可能性的更多信息,请参阅扩展规范。
这个结构应该传递给vkCreateDebugUtilsMessengerEXT函数来创建VkDebugUtilsMessengerEXT对象。不幸的是,因为这个函数是一个扩展函数,所以不会自动加载它。我们必须使用vkGetInstanceProcAddr自己查找它的地址。我们将创建自己的代理函数在后台处理这个。我将它添加到HelloTriangleApplication类定义的正上方。
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;
}
}
如果vkGetInstanceProcAddr函数无法加载,则返回nullptr。我们现在可以调用这个函数来创建扩展对象,如果它可用的话:
if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
throw std::runtime_error("failed to set up debug messenger!");
}
倒数第二个参数也是可选的allocator回调函数,我们将其设置为nullptr,除此之外,参数相当简单。由于调试消息特定于我们的Vulkan实例及其层,因此需要将其显式指定为第一个参数。稍后,您还将在其他子对象中看到这种模式。
VkDebugUtilsMessengerEXT对象也需要通过调用vkDestroyDebugUtilsMessengerEXT来清理。与vkCreateDebugUtilsMessengerEXT类似,该函数需要显式加载。
在CreateDebugUtilsMessengerEXT下面创建另一个代理函数:
void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) {
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if (func != nullptr) {
func(instance, debugMessenger, pAllocator);
}
}
确保此函数是静态类函数或类外函数。然后我们可以在cleanup函数中调用它:
void cleanup() {
if (enableValidationLayers) {
DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
}
vkDestroyInstance(instance, nullptr);
glfwDestroyWindow(window);
glfwTerminate();
}
调试实例的创建和销毁
尽管我们现在已经在程序中添加了带有验证层的调试,但我们还没有涵盖所有内容。vkCreateDebugUtilsMessengerEXT调用要求已经创建了一个有效的实例,并且vkDestroyDebugUtilsMessengerEXT必须在实例销毁之前调用。这使得我们目前无法调试vkCreateInstance和vkDestroyInstance调用中的任何问题。
但是,如果您仔细阅读扩展文档,就会发现有一种方法可以专门为这两个函数调用创建单独的调试utils消息。它需要你简单地传递一个指针到VkInstanceCreateInfo的pNext扩展字段中的VkDebugUtilsMessengerCreateInfoEXT结构体。首先提取人口的信使创建信息到一个单独的函数:
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;
}
...
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!");
}
}
我们现在可以在createInstance函数中重用它:
void createInstance() {
...
VkInstanceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
...
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
if (enableValidationLayers) {
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
populateDebugMessengerCreateInfo(debugCreateInfo);
createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
} else {
createInfo.enabledLayerCount = 0;
createInfo.pNext = nullptr;
}
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
throw std::runtime_error("failed to create instance!");
}
}
debugCreateInfo变量被放置在if语句之外,以确保它在vkCreateInstance调用之前不会被销毁。通过这种方式创建一个额外的调试消息,它将在vkCreateInstance和vkDestroyInstance期间自动使用,并在此之后清理。
测试
现在,让我们故意犯一个错误,看看验证层是如何工作的。暂时删除清理函数中对DestroyDebugUtilsMessengerEXT的调用,然后运行程序。一旦它退出,你应该看到这样的内容:
如果没有看到任何消息,请检查安装。
如果希望查看哪个调用触发了消息,可以向消息回调添加一个断点,并查看堆栈跟踪。
配置
除了VkDebugUtilsMessengerCreateInfoEXT结构体中指定的标志之外,还有很多关于验证层行为的设置。浏览到Vulkan SDK并进入Config目录。在这里,您将找到一个vk_layer_settings.txt文件,该文件解释了如何配置这些层。
要为您自己的应用程序配置层设置,请将文件复制到项目的Debug和Release目录中,并按照说明设置所需的行为。但是,对于本教程的其余部分,我将假设您使用默认设置。
在本教程中,我将故意犯几个错误,向您展示验证层在捕获错误方面的帮助,并告诉您确切地知道使用Vulkan做什么是多么重要。现在是时候看看系统中的Vulkan设备了。
最终代码
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <iostream>
#include <stdexcept>
#include <vector>
#include <cstring>
#include <cstdlib>
const uint32_t WIDTH = 800;
const uint32_t HEIGHT = 600;
const std::vector<const char*> validationLayers = {
"VK_LAYER_KHRONOS_validation"
};
#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif
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);
}
}
class HelloTriangleApplication {
public:
void run() {
initWindow();
initVulkan();
mainLoop();
cleanup();
}
private:
GLFWwindow* window;
VkInstance instance;
VkDebugUtilsMessengerEXT debugMessenger;
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();
setupDebugMessenger();
}
void mainLoop() {
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
}
}
void cleanup() {
if (enableValidationLayers) {
DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
}
vkDestroyInstance(instance, nullptr);
glfwDestroyWindow(window);
glfwTerminate();
}
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 = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
if (enableValidationLayers) {
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
populateDebugMessengerCreateInfo(debugCreateInfo);
createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
} else {
createInfo.enabledLayerCount = 0;
createInfo.pNext = nullptr;
}
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
throw std::runtime_error("failed to create instance!");
}
}
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;
}
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!");
}
}
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;
}
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(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) {
std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;
return VK_FALSE;
}
};
int main() {
HelloTriangleApplication app;
try {
app.run();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}