(图形学笔记 - Vulkan) 1.1.3 - Validation layers(建立vulkan的验证层)

27 篇文章 0 订阅
8 篇文章 6 订阅

1.1.3 - Validation layers(建立vulkan的验证层)

参考链接

  1. Validation Layers

概述

     在日常开发过程中,会接触到一些图形学的概念和基础,之前使用过一部分的OpenGL和OSG作为开发的基础,后来发现自己日益对图形学的相关框架产生了一定的兴趣,因此就开始选择了解OpenGL那个组织新的一直在维护的一个图形框架的接口,就是vulkan,今天是记录自己学习Drawing a triangle中的validation layers的一个笔记,做笔记能让自己更容易坚持下去。毕竟,不积跬步,无以至千里,不积小流,无以成江海。 —— 《劝学》 荀子。

学习和翻译内容

     下面是我对原文的学习和翻译:

Validation layers(验证层)

What are validation layers? (什么是验证层?)

     The Vulkan API is designed around the idea of minimal driver overhead and one of the manifestations of that goal is that there is very limited error checking in the API by default. Even mistakes as simple as setting enumerations to incorrect values or passing null pointers to required parameters are generally not explicitly handled and will simply result in crashes or undefined behavior. Because Vulkan requires you to be very explicit about everything you’re doing, it’s easy to make many small mistakes like using a new GPU feature and forgetting to request it at logical device creation time.

     Vulkan API 是围绕最小驱动程序开销的想法设计的,该目标的表现之一是默认情况下 API 中的错误检查非常有限。即使是将枚举设置为不正确的值或将空指针传递给所需参数这样简单的错误,通常也不会被明确处理,只会导致崩溃或未定义的行为。因为 Vulkan 要求您对所做的一切都非常明确,所以很容易犯许多小错误,例如使用新的 GPU 功能而忘记在逻辑设备创建时请求它。

     However, that doesn’t mean that these checks can’t be added to the API. Vulkan introduces an elegant system for this known as validation layers. Validation layers are optional components that hook into Vulkan function calls to apply additional operations. Common operations in validation layers are:

     但是,这并不意味着不能将这些检查添加到 API 中。Vulkan 为此引入了一个优雅的系统,称为验证层。验证层是可选组件,可与 Vulkan 函数调用挂钩以应用其他操作。验证层中的常见操作有:

  • Checking the values of parameters against the specification to detect misuse.

  • Tracking creation and destruction of objects to find resource leaks.

  • Checking thread safety by tracking the threads that calls originate from.

  • Logging every call and its parameters to the standard output.

  • Tracing Vulkan calls for profiling and replaying.

  • 根据规范检查参数值以检测误用。

  • 跟踪对象的创建和销毁以发现资源泄漏。

  • 通过跟踪调用源自的线程来检查线程安全。

  • 将每个调用及其参数记录到标准输出。

  • 跟踪 Vulkan 调用以进行分析和重放。

     Here’s an example of what the implementation of a function in a diagnostics validation layer could look like:

     以下是诊断验证层中函数实现的示例:

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

在这里插入图片描述

     These validation layers can be freely stacked to include all the debugging functionality that you’re interested in. You can simply enable validation layers for debug builds and completely disable them for release builds, which gives you the best of both worlds!

     这些验证层可以自由堆叠以包含您感兴趣的所有调试功能。您可以简单地为调试版本启用验证层,并为发布版本完全禁用它们,这让您两全其美!

     Vulkan does not come with any validation layers built-in, but the LunarG Vulkan SDK provides a nice set of layers that check for common errors. They’re also completely open source, so you can check which kind of mistakes they check for and contribute. Using the validation layers is the best way to avoid your application breaking on different drivers by accidentally relying on undefined behavior.

     Vulkan 没有内置任何验证层,但 LunarG Vulkan SDK 提供了一组很好的层来检查常见错误。它们也是完全开源的,因此您可以检查它们检查和贡献的错误类型。使用验证层是避免您的应用程序因意外依赖未定义行为而在不同驱动程序上中断的最佳方法。

     Validation layers can only be used if they have been installed onto the system. For example, the LunarG validation layers are only available on PCs with the Vulkan SDK installed.

     验证层只有在已安装到系统上时才能使用。例如,LunarG 验证层仅在安装了 Vulkan SDK 的 PC 上可用。

     There were formerly two different types of validation layers in Vulkan: instance and device specific. The idea was that instance layers would only check calls related to global Vulkan objects like instances, and device specific layers would only check calls related to a specific GPU. Device specific layers have now been deprecated, which means that instance validation layers apply to all Vulkan calls. The specification document still recommends that you enable validation layers at device level as well for compatibility, which is required by some implementations. We’ll simply specify the same layers as the instance at logical device level, which we’ll see later on.

     Vulkan 中以前有两种不同类型的验证层:实例和设备特定。这个想法是实例层只会检查与全局 Vulkan 对象(如实例)相关的调用,而设备特定层只会检查与特定 GPU 相关的调用。设备特定层现已弃用,这意味着实例验证层适用于所有 Vulkan 调用。规范文档仍然建议您在设备级别启用验证层以及兼容性,这是某些实现所要求的。我们将只需指定相同的层在逻辑设备水平的情况下,我们可以在后续的介绍中看到对应的解释, 就不在这里展开了。

Using validation layers (使用验证层)

     In this section we’ll see how to enable the standard diagnostics layers provided by the Vulkan SDK. Just like extensions, validation layers need to be enabled by specifying their name. All of the useful standard validation is bundled into a layer included in the SDK that is known as VK_LAYER_KHRONOS_validation.

     在本节中,我们将看到如何启用 Vulkan SDK 提供的标准诊断层。就像扩展一样,需要通过指定名称来启用验证层。所有有用的标准验证都捆绑在 SDK 中包含的称为VK_LAYER_KHRONOS_validation

     Let’s first add two configuration variables to the program to specify the layers to enable and whether to enable them or not. I’ve chosen to base that value on whether the program is being compiled in debug mode or not. The NDEBUG macro is part of the C++ standard and means “not debug”.

     让我们首先在程序中添加两个配置变量来指定要启用的层以及是否启用它们。我选择根据程序是否在调试模式下编译该值。所述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

在这里插入图片描述
     We’ll add a new function checkValidationLayerSupportthat checks if all of the requested layers are available. First list all of the available layers using the vkEnumerateInstanceLayerProperties function. Its usage is identical to that of vkEnumerateInstanceExtensionProperties which was discussed in the instance creation chapter.

     我们将添加一个新函数checkValidationLayerSupport来检查是否所有请求的层都可用。首先使用该vkEnumerateInstanceLayerProperties函数列出所有可用层。它的用法与vkEnumerateInstanceExtensionProperties实例创建章节中讨论的相同。

bool checkValidationLayerSupport() {
    uint32_t layerCount;
    vkEnumerateInstanceLayerProperties(&layerCount, nullptr);

    std::vector<VkLayerProperties> availableLayers(layerCount);
    vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());

    return false;
}

在这里插入图片描述
     Next, check if all of the layers in validationLayers exist in the availableLayers list. You may need to include <cstring> for strcmp.

     接下来,检查列表中是否validationLayers存在 所有图层availableLayers。您可能需要包括<cstring>for 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;

在这里插入图片描述
     We can now use this function in createInstance:

     我们现在可以在createInstance

void createInstance() {
    if (enableValidationLayers && !checkValidationLayerSupport()) {
        throw std::runtime_error("validation layers requested, but not available!");
    }

    ...
}

在这里插入图片描述
    Now run the program in debug mode and ensure that the error does not occur. If it does, then have a look at the FAQ.

    Finally, modify the VkInstanceCreateInfo struct instantiation to include the validation layer names if they are enabled:

    现在在调试模式下运行程序并确保不会发生错误。如果是,请查看常见问题解答。

    最后,修改VkInstanceCreateInfo结构体实例化以包含验证层名称(如果已启用):

if (enableValidationLayers) {
    createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
    createInfo.ppEnabledLayerNames = validationLayers.data();
} else {
    createInfo.enabledLayerCount = 0;
}

在这里插入图片描述
     If the check was successful then vkCreateInstance should not ever return a VK_ERROR_LAYER_NOT_PRESENT error, but you should run the program to make sure.

     如果检查成功,则vkCreateInstance不应返回 VK_ERROR_LAYER_NOT_PRESENT错误,但您应该运行该程序以确保。

Message callback(消息回调)

     The validation layers will print debug messages to the standard output by default, but we can also handle them ourselves by providing an explicit callback in our program. This will also allow you to decide which kind of messages you would like to see, because not all are necessarily (fatal) errors. If you don’t want to do that right now then you may skip to the last section in this chapter.

     默认情况下,验证层会将调试消息打印到标准输出,但我们也可以通过在我们的程序中提供显式回调来自己处理它们。这也将允许您决定要查看哪种消息,因为并非所有消息都必然是(致命的)错误。如果您现在不想这样做,那么您可以跳到本章的最后一节。

     To set up a callback in the program to handle messages and the associated details, we have to set up a debug messenger with a callback using the VK_EXT_debug_utils extension.

     要在程序中设置回调以处理消息和相关详细信息,我们必须使用VK_EXT_debug_utils扩展设置带有回调的调试信使。

     We’ll first create a getRequiredExtensions function that will return the required list of extensions based on whether validation layers are enabled or not:

     我们将首先创建一个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;
}

在这里插入图片描述
     The extensions specified by GLFW are always required, but the debug messenger extension is conditionally added. Note that I’ve used the VK_EXT_DEBUG_UTILS_EXTENSION_NAME macro here which is equal to the literal string “VK_EXT_debug_utils”. Using this macro lets you avoid typos.

     始终需要 GLFW 指定的扩展,但有条件地添加调试信使扩展。请注意,我在VK_EXT_DEBUG_UTILS_EXTENSION_NAME这里使用了等于文字字符串“VK_EXT_debug_utils”的 宏。使用这个宏可以避免打字错误。

     We can now use this function in createInstance:

我们现在可以在createInstance

auto extensions = getRequiredExtensions();
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();

     Run the program to make sure you don’t receive a VK_ERROR_EXTENSION_NOT_PRESENT error. We don’t really need to check for the existence of this extension, because it should be implied by the availability of the validation layers.

     运行程序以确保您没有收到 VK_ERROR_EXTENSION_NOT_PRESENT 错误消息。我们真的不需要检查这个扩展是否存在,因为它应该被验证层的可用性所暗示。

     Now let’s see what a debug callback function looks like. Add a new static member function called debugCallback with the PFN_vkDebugUtilsMessengerCallbackEXT prototype. The VKAPI_ATTR and VKAPI_CALL ensure that the function has the right signature for Vulkan to call it.

     现在让我们看看调试回调函数是什么样的。添加一个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;
}

在这里插入图片描述
     The first parameter specifies the severity of the message, which is one of the following flags:

  • VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: Diagnostic message
  • VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: Informational message like the creation of a resource
  • VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: Message about behavior that is not necessarily an error, but very likely a bug in your application
  • VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: Message about behavior that is invalid and may cause crashes

     The values of this enumeration are set up in such a way that you can use a comparison operation to check if a message is equal or worse compared to some level of severity, for example:

     第一个参数指定消息的严重性,它是以下标志之一:

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

在这里插入图片描述

     The messageType parameter can have the following values:

  • VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: Some event has happened that is unrelated to the specification or performance.
  • VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: Something has happened that violates the specification or indicates a possible mistake.
  • VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: Potential non-optimal use of Vulkan.

     该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 的潜在非最佳使用。

     The pCallbackData parameter refers to a VkDebugUtilsMessengerCallbackDataEXT struct containing the details of the message itself, with the most important members being:

  • pMessage: The debug message as a null-terminated string.
  • pObjects: Array of Vulkan object handles related to the message.
  • objectCount: Number of objects in array.

     该pCallbackData参数指的是一个VkDebugUtilsMessengerCallbackDataEXT包含消息本身详细信息的结构体,其中最重要的成员是:

  • pMessage: 作为以空字符结尾的字符串的调试消息。
  • pObjects:与消息相关的 Vulkan 对象句柄数组。
  • objectCount: 数组中的对象数。

     Finally, the pUserData parameter contains a pointer that was specified during the setup of the callback and allows you to pass your own data to it.

     最后,该pUserData参数包含一个在回调设置期间指定的指针,并允许您将自己的数据传递给它。

     The callback returns a boolean that indicates if the Vulkan call that triggered the validation layer message should be aborted. If the callback returns true, then the call is aborted with the VK_ERROR_VALIDATION_FAILED_EXT error. This is normally only used to test the validation layers themselves, so you should always return VK_FALSE.

     回调返回一个布尔值,指示是否应中止触发验证层消息的 Vulkan 调用。如果回调返回 true,则调用将因VK_ERROR_VALIDATION_FAILED_EXT错误而中止。这通常仅用于测试验证层本身,因此您应该始终返回VK_FALSE.

     All that remains now is telling Vulkan about the callback function. Perhaps somewhat surprisingly, even the debug callback in Vulkan is managed with a handle that needs to be explicitly created and destroyed. Such a callback is part of a debug messenger and you can have as many of them as you want. Add a class member for this handle right under instance:

     现在剩下的就是告诉 Vulkan 回调函数。也许有点令人惊讶,甚至 Vulkan 中的调试回调也是通过一个需要显式创建和销毁的句柄来管理的。这样的回调是调试信使的一部分,您可以根据需要拥有任意数量的回调。在下面为这个句柄添加一个类成员instance

VkDebugUtilsMessengerEXT debugMessenger;

     Now add a functionsetupDebugMessenger to be called from initVulkan right after createInstance:

现在添加一个setupDebugMessenger要从initVulkan后面调用的函数createInstance

void initVulkan() {
    createInstance();
    setupDebugMessenger();
}

void setupDebugMessenger() {
    if (!enableValidationLayers) return;

}

在这里插入图片描述
     We’ll need to fill in a structure with details about the messenger and its callback:
     我们需要用有关信使及其回调的详细信息填充结构:

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

在这里插入图片描述
     The messageSeverity field allows you to specify all the types of severities you would like your callback to be called for. I’ve specified all types except for VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT here to receive notifications about possible problems while leaving out verbose general debug info.

     该messageSeverity字段允许您指定您希望回调被调用的所有类型的严重性。我已经指定了除VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT此处之外的所有类型,以接收有关可能出现的问题的通知,同时省略详细的一般调试信息。

     Similarly the messageType field lets you filter which types of messages your callback is notified about. I’ve simply enabled all types here. You can always disable some if they’re not useful to you.

     同样,该messageType字段可让您过滤通知您的回调的消息类型。我只是在这里启用了所有类型。如果它们对您没有用,您可以随时禁用它们。

     Finally, the pfnUserCallback field specifies the pointer to the callback function. You can optionally pass a pointer to the pUserData field which will be passed along to the callback function via the pUserData parameter. You could use this to pass a pointer to the HelloTriangleApplication class, for example.

     最后,该pfnUserCallback字段指定指向回调函数的指针。您可以选择将指针pUserData传递给将通过pUserData参数传递给回调函数的字段。例如,您可以使用它来传递指向HelloTriangleApplication类的指针。

     Note that there are many more ways to configure validation layer messages and debug callbacks, but this is a good setup to get started with for this tutorial. See the extension specification for more info about the possibilities.

     请注意,还有更多方法可以配置验证层消息和调试回调,但这是本教程开始时的一个很好的设置。有关可能性的更多信息,请参阅扩展规范。

     This struct should be passed to the vkCreateDebugUtilsMessengerEXT function to create the VkDebugUtilsMessengerEXT object. Unfortunately, because this function is an extension function, it is not automatically loaded. We have to look up its address ourselves using vkGetInstanceProcAddr. We’re going to create our own proxy function that handles this in the background. I’ve added it right above the HelloTriangleApplication class definition.

     这个结构应该传递给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;
    }
}

在这里插入图片描述
     The vkGetInstanceProcAddr function will return nullptr if the function couldn’t be loaded. We can now call this function to create the extension object if it’s available:

     如果无法加载该vkGetInstanceProcAddr函数,则该函数将返回nullptr。我们现在可以调用此函数来创建扩展对象(如果可用):

if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
    throw std::runtime_error("failed to set up debug messenger!");
}

在这里插入图片描述
     The second to last parameter is again the optional allocator callback that we set to nullptr, other than that the parameters are fairly straightforward. Since the debug messenger is specific to our Vulkan instance and its layers, it needs to be explicitly specified as first argument. You will also see this pattern with other child objects later on.

     倒数第二个参数再次是我们设置为 的可选分配器回调,除此之外nullptr参数相当简单。由于调试信使特定于我们的 Vulkan 实例及其层,因此需要将其明确指定为第一个参数。稍后您还将在其他子对象中看到这种模式。

     The VkDebugUtilsMessengerEXT object also needs to be cleaned up with a call to vkDestroyDebugUtilsMessengerEXT. Similarly tovkCreateDebugUtilsMessengerEXT the function needs to be explicitly loaded.

     该VkDebugUtilsMessengerEXT对象还需要通过调用来清理 vkDestroyDebugUtilsMessengerEXT。类似于vkCreateDebugUtilsMessengerEXT 需要显式加载的函数。

     Create another proxy function right below CreateDebugUtilsMessengerEXT:

     在下面创建另一个代理函数CreateDebugUtilsMessengerEXT

void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) {
    auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
    if (func != nullptr) {
        func(instance, debugMessenger, pAllocator);
    }
}

在这里插入图片描述
     Make sure that this function is either a static class function or a function outside the class. We can then call it in the cleanup function:

     确保此函数是静态类函数或类外的函数。然后我们可以在cleanup函数中调用它:

void cleanup() {
    if (enableValidationLayers) {
        DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
    }

    vkDestroyInstance(instance, nullptr);

    glfwDestroyWindow(window);

    glfwTerminate();
}

在这里插入图片描述

Debugging instance creation and destruction(调试实例创建和销毁)

     Although we’ve now added debugging with validation layers to the program we’re not covering everything quite yet. The vkCreateDebugUtilsMessengerEXT call requires a valid instance to have been created and vkDestroyDebugUtilsMessengerEXT must be called before the instance is destroyed. This currently leaves us unable to debug any issues in the vkCreateInstance and vkDestroyInstance calls.

     虽然我们现在已经在程序中添加了带有验证层的调试,但我们还没有完全涵盖所有内容。该vkCreateDebugUtilsMessengerEXT调用需要创建一个有效的实例,并且vkDestroyDebugUtilsMessengerEXT必须在实例被销毁之前调用。这使我们目前无法调试vkCreateInstancevkDestroyInstance调用中的任何问题。

     However, if you closely read the extension documentation, you’ll see that there is a way to create a separate debug utils messenger specifically for those two function calls. It requires you to simply pass a pointer to a VkDebugUtilsMessengerCreateInfoEXT struct in the pNext extension field of VkInstanceCreateInfo. First extract population of the messenger create info into a separate function:

     但是,如果您仔细阅读扩展文档,您会发现有一种方法可以专门为这两个函数调用创建单独的调试工具信使。它要求您简单地将指针传递给VkDebugUtilsMessengerCreateInfoEXT. 的pNext扩展字段中的结构体VkInstanceCreateInfo。首先将信使创建信息的人口提取到一个单独的函数中:

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!");
    }
}

在这里插入图片描述
     We can now re-use this in the createInstance function:
     我们现在可以在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!");
    }
}

在这里插入图片描述
     The debugCreateInfo variable is placed outside the if statement to ensure that it is not destroyed before the vkCreateInstance call. By creating an additional debug messenger this way it will automatically be used during vkCreateInstance and vkDestroyInstance and cleaned up after that.

     该debugCreateInfo变量放置在 if 语句之外,以确保它在vkCreateInstance调用之前不会被销毁。通过以这种方式创建额外的调试信使,它将在此期间自动使用vkCreateInstancevkDestroyInstance在此之后进行清理。

Testing

     Now let’s intentionally make a mistake to see the validation layers in action. Temporarily remove the call to DestroyDebugUtilsMessengerEXT in the cleanup function and run your program. Once it exits you should see something like this:

    现在让我们故意犯一个错误来查看正在运行的验证层。暂时删除函数DestroyDebugUtilsMessengerEXT中的cleanup调用并运行您的程序。一旦退出,您应该会看到如下内容:
在这里插入图片描述

	If you don't see any messages then check your installation.

     If you want to see which call triggered a message, you can add a breakpoint to the message callback and look at the stack trace.

     如果要查看哪个调用触发了消息,可以在消息回调中添加断点并查看堆栈跟踪。

Configuration(配置)

     There are a lot more settings for the behavior of validation layers than just the flags specified in the VkDebugUtilsMessengerCreateInfoEXT struct. Browse to the Vulkan SDK and go to the Config directory. There you will find a vk_layer_settings.txt file that explains how to configure the layers.

     验证层的行为有很多设置,而不仅仅是VkDebugUtilsMessengerCreateInfoEXT结构中指定的标志。浏览到 Vulkan SDK 并转到Config目录。在那里你会找到一个 vk_layer_settings.txt文件,解释如何配置层

     To configure the layer settings for your own application, copy the file to the Debug and Releasedirectories of your project and follow the instructions to set the desired behavior. However, for the remainder of this tutorial I’ll assume that you’re using the default settings.

     要为您自己的应用程序配置层设置,请将文件复制到项目的 DebugRelease目录,然后按照说明设置所需的行为。但是,对于本教程的其余部分,我将假设您使用的是默认设置。

     Throughout this tutorial I’ll be making a couple of intentional mistakes to show you how helpful the validation layers are with catching them and to teach you how important it is to know exactly what you’re doing with Vulkan. Now it’s time to look at Vulkan devices in the system.

     在本教程中,我将故意犯一些错误,以向您展示验证层在捕获它们方面的帮助,并告诉您准确了解您正在使用 Vulkan 做什么是多么重要。现在是时候查看系统中的Vulkan 设备了。

整体代码

///
// Copyright (c)2021, Tom Zhao personal. ("TZOpenTools")
// This software is a personal tools project by Tom Zhao.
// Description:
///

#ifndef _VULKAN_HELLO_TRIANGLE_H_H_
#define _VULKAN_HELLO_TRIANGLE_H_H_

#define GLFW_INCLUDE_VULKAN
#include <vulkan/vulkan.h>

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

#include "GLFW/glfw3.h"

namespace FunnyPrograms
{
	const uint32_t GLFW_WINDOW_WIDTH = 1200;
	const uint32_t GLFW_WINDOW_HEIGHT = 800;

	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 DestoryDebugUtilsMesengerEXT(VkInstance instance,
		VkDebugUtilsMessengerEXT debugMessenger,
		const VkAllocationCallbacks* pAllocator) 
	{
		auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
		if (func != nullptr)
		{
			func(instance, debugMessenger, pAllocator);
		}
	}

	class TzHelloTriangleApplication
	{
	public:
		void run()
		{
			initWindow();
			initVulkan();
			mainLoop();
			cleanup();
		}

	private:
		GLFWwindow* m_window;
		VkInstance m_instance;
		VkDebugUtilsMessengerEXT m_debugMessenger;

		static VKAPI_ATTR VkBool32 VKAPI_CALL m_debugCallback(
			VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, 
			VkDebugUtilsMessageTypeFlagsEXT messageType,
			const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
			void* pUserData)
		{
			std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;

			return VK_FALSE;
		}

	private:
		inline void initWindow()
		{
			glfwInit();

			// Init the glfw whole window hint for all this application.
			glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
			glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

			m_window = glfwCreateWindow(GLFW_WINDOW_WIDTH,
				GLFW_WINDOW_HEIGHT, "Vulkan window", nullptr, nullptr);
		}

		inline void initVulkan()
		{
			createInstance();
			searchAllVulkanExtensions();
			setUpDebugMessenger();
		}

		inline void mainLoop()
		{
			while (!glfwWindowShouldClose(m_window))
			{
				glfwPollEvents();
			}
		}

		inline void cleanup()
		{
			if (enableValidationLayers)
			{
				DestoryDebugUtilsMesengerEXT(m_instance, m_debugMessenger, nullptr);
			}

			vkDestroyInstance(m_instance, nullptr);

			glfwDestroyWindow(m_window);

			glfwTerminate();
		}

		inline 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 = m_debugCallback;
		}

		inline void setUpDebugMessenger()
		{
			if (!enableValidationLayers) return;

			VkDebugUtilsMessengerCreateInfoEXT createInfo;
			populateDebugMessengerCreateInfo(createInfo);

			if (CreateDebugUtilsMessengerEXT(m_instance, &createInfo, nullptr, &m_debugMessenger) != VK_SUCCESS)
			{
				throw std::runtime_error("failed to set up debug messenger!");
			}
		}

		inline 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 (0 == strcmp(layerName, layerProperties.layerName))
					{
						layerFound = true;
						break;
					}
				}

				if (!layerFound)
				{
					return false;
				}
			}

			return true;
		}

		inline void searchAllVulkanExtensions() const
		{
			uint32_t extensionCount = 0;
			vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);

			std::vector<VkExtensionProperties> extensions(extensionCount);

			vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());

			std::cout << "Available extensions: \n";

			for (const auto& extension : extensions)
			{
				std::cout << '\t' << extension.extensionName << '\n';
			}
		}

		inline 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, &m_instance) != VK_SUCCESS) {
				throw std::runtime_error("failed to create instance!");
			}
		}
	};

	inline int TzTriangleAppTestCase_01(void)
	{
		TzHelloTriangleApplication app;

		try
		{
			app.run();
		}
		catch (const std::exception& e)
		{
			std::cerr << e.what() << std::endl;
			return EXIT_FAILURE;
		}

		return EXIT_SUCCESS;
	}
}

#endif // _VULKAN_HELLO_TRIANGLE_H_H_

小结

个人格言

    用心去感受你自己需要坚持的生活,未来慢慢会给你答案的。

在这里插入图片描述

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值