Vulkan实战之验证层

news2024/10/5 13:01:37

文章目录

      • 验证层是什么?
      • 使用验证层
      • 消息回调
      • 调试实例的创建和销毁
      • 测试
      • 配置
      • 最终代码

在这里插入图片描述

验证层是什么?

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/482140.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32 系列 DAC的介绍与使用

STM32网上资料多&#xff0c;对自己来说基本的使用也是很简单的&#xff0c; 我的STM32专栏并没有什么系统的基础教学&#xff0c;基本上是某个项目用到了&#xff0c;或者产品使用过程出过问题 才会来记录一下&#xff0c;正好用到了 DAC &#xff0c;一般产品还用得不多&…

【Unity入门】23.简单物理系统

【Unity入门】简单物理系统 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity入门系列博客&#xff0c;所学知识来自B站阿发老师~感谢 &#xff08;一&#xff09;重力系统 &#xff08;1&#xff09;Rigidbody组件 Unity里面有提供符合我们常规认知的物理系统组件Physics&…

GDKOI 2023游记总结

不知觉就咕了1.5个月 在回忆 2021 年那个刚步入初中的懵懂孩童参加 GDOI 的惊喜中感慨初中时光的飞逝。 2021 GDOI 普及组游记 Day - ∞ \infty ∞ 去年因为疫情取消了&#xff0c;今年难得重新举办&#xff0c;珍惜每一次机会吧。 前年的地点订在深圳耀华学校&#xff0c;忘…

老外从神话原型中提取的12个品牌个性

老外从神话原型中提取的12个品牌个性 也是西方视角&#xff0c;需要本土化 参照心理学大师荣格的理论&#xff1a;心理学潜意识派 趣讲大白话&#xff1a;品牌的调调是啥 【趣讲信息科技151期】 **************************** 12种原型又归属于4种人性动机。 1、稳定&#xff0…

Python小姿势 - ## Python中的迭代器与生成器

Python中的迭代器与生成器 在Python中&#xff0c;迭代是一个非常重要的概念&#xff0c;迭代器和生成器是迭代的两种最常见的形式。那么&#xff0c;迭代器与生成器有何不同呢&#xff1f; 首先&#xff0c;我们先来了解一下迭代器。 迭代器是一种对象&#xff0c;它可以记住遍…

第二十八章 React脚手架配置代理

为了更好地理解如何在React应用程序中配置代理&#xff0c;我们需要先了解什么是代理。 代理是一种充当客户端和服务器之间中间人的服务器。当客户端向服务器发送请求时&#xff0c;代理服务器将接收请求并将其转发到服务器。服务器将响应发送回代理服务器&#xff0c;代理服务…

Spring:Bean的实例化(构造方法、静态工厂和实例化工厂)

三种方式&#xff0c;分别为构造方法、静态工厂和实例化工厂 新建Module项目&#xff0c;选择Maven&#xff0c;在pom.xml导入如下依赖&#xff1a; pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.o…

网络编程 总结二

一、TCP TCP模型 1. TCP搭建相关函数&#xff1a; 套接字Socket 1&#xff09;Socket函数&#xff1a; 2&#xff09;bind 3&#xff09;listen 4&#xff09;accept 5&#xff09;recv 注意&#xff1a; 1> TCP中的recv 可以替换成read&#xff1b; 2>TCP中的…

SpringBoot自定义注解

SpringBoot自定义注解 1. 创建一个注解的基本元素 修饰符&#xff1a;访问修饰符必须为public,不写默认为pubic&#xff1b; 关键字&#xff1a;关键字为interface&#xff1b; 注解名称&#xff1a;注解名称为自定义注解的名称 注解类型元素&#xff1a;注解类型元素是注解中…

Python---闭包,装饰器,设计模式之工厂模式

1. 闭包定义 定义双层嵌套函数&#xff0c; 内层函数可以访问外层函数的变量 将内存函数作为外层函数的返回&#xff0c;此内层函数就是闭包函数 2. 闭包的好处和缺点 优点&#xff1a;不定义全局变量&#xff0c;也可以让函数持续访问和修改一个外部变量 优点&#xff1a…

【数据分析之道-Matplotlib(一)】Matplotlib Pyplot

系列文章目录 作者&#xff1a;i阿极 作者简介&#xff1a;Python领域新星作者、多项比赛获奖者&#xff1a;博主个人首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞&#x1f44d;收藏&#x1f4c1;评论&#x1f4d2;关注…

Jetson nano之ROS入门- -ROS集成开发搭建与ROS通信学习笔记

文章目录 一、ROS集成开发环境搭建二、ROS通信机制和命令1. 话题通信2. 服务通信3. 参数服务4. ROS常用命令 三、Python实现ROS通信- - 控制和读取小乌龟状态1. 配置package.xml文件2. 配置CMakeLists.txt文件3. 编写Python代码4. 配置launch文件 总结 一、ROS集成开发环境搭建…

反射、枚举

反射的定义&#xff1a;Java的反射机制是在运行状态中&#xff0c;都能对任意的类拿到这个类的所有属性&#xff0c;从而对其进行相应的修改&#xff1b;用途&#xff1a;在日常第三方应用开发中&#xff0c;可以通过反射机制来获取某个类的私有成员变量或是方法&#xff1b;主…

研究生,但是一直摆烂——想办法解决

原因剖析 孤独因为没有朋友&#xff0c; 之前自己思维误区&#xff0c;总觉得好好学习好好锻炼变得优秀就会有朋友&#xff0c;其实不是这个样子的&#xff0c;即使自己一直内卷&#xff0c;但还是很孤独。 现在重新反思自己。 没有朋友因为&#xff1a; 1 外貌 2 聊天的情商…

Which programming language do you choose

NO.1&#xff1a;JavaScript JavaScript&#xff0c;简称JS语言&#xff0c;是一种具有函数优先的轻量级&#xff0c;解释型或即时编译型的高级编程语言。虽然它是作为开发Web页面的脚本语言而出名的&#xff0c;但是它也被用到了很多非浏览器环境中&#xff0c;JavaScript 基…

老王的自动驾驶决策和规划第一章

文章目录 自动驾驶决策规划算法序章第一章&#xff08;1&#xff09; 细说五次多项式&#xff08;2&#xff09; 凸优化与非凸优化(3) 直角坐标与自然坐标转换(上, 下) 自动驾驶决策规划算法 序章 课程链接&#xff1a;序章 第一章 &#xff08;1&#xff09; 细说五次多项…

关于SpringBoot整合Websocket实现简易对话聊天窗

前言 官网链接&#xff1a;Websocket Websocket 是什么&#xff1f;它可以将两个独立的浏览器窗口作为通信的两端。 这种形式的通信与传统的 HTTP、TCP 所不同。传统的 HTTP 请求—响应协议是无法实现实时通信的&#xff0c;也就是说&#xff0c;只能由客户端向服务端发送请求…

前端Web开发,HTML,css,JavaScript

web浏览器响应流程&#xff0c;及技术不同的浏览器&#xff0c;内核不同&#xff0c;对于相同的前端代码解析的效果会存在差异web标准&#xff0c;三个组成部分 HTML&#xff1a;负责网页的结构&#xff08;页面元素和内容&#xff09;CSS&#xff1a;负责页面的表现&#xff0…

VC调试方法大全

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

代码随想录算法训练营第四十八天| 198.打家劫舍、213.打家劫舍II、337.打家劫舍III

文章目录 198.打家劫舍213.打家劫舍II337.打家劫舍III 198.打家劫舍 题目链接&#xff1a;代码随想录 解题思路&#xff1a; 1.dp[i]&#xff1a;考虑下标i&#xff08;包括i&#xff09;以内的房屋&#xff0c;最多可以偷窃的金额为dp[i] 只是考虑&#xff0c;不一定偷 2.递推…