[译]Vulkan教程(06)验证层

栏目: 后端 · 发布时间: 4年前

内容简介:[译]Vulkan教程(06)验证层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 incorr

[译]Vulkan教程(06)验证层

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的核心设计思想是最小化driver开销,其表现之一就是,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:

  • 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

但是,这不意味着这些检查无法加入到API。Vulkan引入了一个优雅的系统,即 验证层 。验证层是可选组件,他挂钩进Vulkan函数调用,以实施额外操作。验证层的常见操作有:

  • 根据说明书检查参数值,以检测误用
  • 跟踪对象的创建和销毁过程,以查找资源泄漏
  • 通过追踪线程调用源头来检查线程安全性
  • 将所有调用及其参数保存到标准输出
  • 追踪Vulkan调用,用于剖析和重演

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

下面展示了诊断验证层的函数可能的样子:

 1 VkResult vkCreateInstance(
 2     const VkInstanceCreateInfo* pCreateInfo,
 3     const VkAllocationCallbacks* pAllocator,
 4     VkInstance* instance) {
 5  
 6     if (pCreateInfo == nullptr || instance == nullptr) {
 7         log("Null pointer passed to required parameter!");
 8         return VK_ERROR_INITIALIZATION_FAILED;
 9     }
10  
11     return real_vkCreateInstance(pCreateInfo, pAllocator, instance);
12 }

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!

这些验证层可以被栈入进各种你感兴趣的调试功能。你可以简单地在debug阶段启用验证层,在release阶段彻底禁用它们。这对开发和应用世界都是最好的!

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提供了一个不错的验证层集合,它们能够检查常见错误。它们也是完全开源的,所以你可以看看它们检查哪种错误,并贡献自己的才智。你的app在不同的driver上可能因为意外的未定义行为而中止,使用这些验证层是避免这一问题的最好方法。

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.

验证层只能在被安装到系统上后才能使用。例如,只有安装了Vulkan SDK,LunarG的验证层才能在你的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中有两种不同类型的验证层:针对instance的和针对device的。Instance层只检查与全局Vulkan对象(例如instance)相关的调用;device层只检查与特定CPU相关的调用。Device层现在已经被废弃了,这意味着instance验证层对所有Vulkan调用都有用。为了兼容某些实现,说明书里仍旧推荐你启用device水平的验证层。我们将简单地标明一个层同时属于instance水平和逻辑device水平,详见后续。

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提供的标准诊断层。像扩展一样,验证层需要通过标明它们的名字来启用。所有有用的标准验证都被绑进了这个被称为 VK_LAYER_KHRONOS_validation 的SDK。

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".

我们首先添加2个配置变量,标明要启用的层,以及是否启用它们。我已经选择让这个值基于程序的编译模式是不是debug。宏 NDEBUG 是C++标准的一部分,意思是“不是debug”。

 1 const int WIDTH = 800;
 2 const int HEIGHT = 600;
 3  
 4 const std::vector<const char*> validationLayers = {
 5     "VK_LAYER_KHRONOS_validation"
 6 };
 7  
 8 #ifdef NDEBUG
 9     const bool enableValidationLayers = false;
10 #else
11     const bool enableValidationLayers = true;
12 #endif

We'll add a new function  checkValidationLayerSupport that 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 函数列出所有可以用的层,它的用法和之前的创建instance章节的 vkEnumerateInstanceExtensionProperties 函数相同。

1 bool checkValidationLayerSupport() {
2     uint32_t layerCount;
3     vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
4  
5     std::vector<VkLayerProperties> availableLayers(layerCount);
6     vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
7  
8     return false;
9 }

Next, check if all of the layers in  validationLayers exist in the  availableLayers list. You may need to include  <cstring> for  strcmp .

下一步,检查是否 validationLayers 中所有的层都存在于 availableLayers 列表中。你可能需要include一下 <cstring> ,以使用 strcmp 函数。

 1 for (const char* layerName : validationLayers) {
 2     bool layerFound = false;
 3  
 4     for (const auto& layerProperties : availableLayers) {
 5         if (strcmp(layerName, layerProperties.layerName) == 0) {
 6             layerFound = true;
 7             break;
 8         }
 9     }
10  
11     if (!layerFound) {
12         return false;
13     }
14 }
15  
16 return true;

We can now use this function in  createInstance :

现在我们可以在 createInstance 中使用这个函数了:

1 void createInstance() {
2     if (enableValidationLayers && !checkValidationLayerSupport()) {
3         throw std::runtime_error("validation layers requested, but not available!");
4     }
5  
6     ...
7 }

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.

现在在debug模式下运行现在,确保没有错误出现。如果出现错误,就看一下FAQ。

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

最后,修改 VkInstanceCreateInfo 结构体,如果启用了验证层,就将它们的名字包含进来:

1 if (enableValidationLayers) {
2     createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
3     createInfo.ppEnabledLayerNames = validationLayers.data();
4 } else {
5     createInfo.enabledLayerCount = 0;
6 }

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.

如果if判断成功,那么 vkCreateInstance 应该就不会再返回 VK_ERROR_LAYER_NOT_PRESENT 错误,但是你应该运行程序以确保之。

Message callback 消息回调

Unfortunately just enabling the layers doesn't help much, because they currently have no way to relay the debug messages back to our program. To receive those messages we have to set up a debug messenger with a callback, which requires the  VK_EXT_debug_utils extension.

不幸的是,仅仅启用层,并没有什么帮助,因为它们现在没办法将调试消息转回给程序。为了接收这些消息,我们不得不用回调设置一个debug信使,这就要使用 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 函数,它将依据验证层是否被启用,返回请求的扩展的列表:

 1 std::vector<const char*> getRequiredExtensions() {
 2     uint32_t glfwExtensionCount = 0;
 3     const char** glfwExtensions;
 4     glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
 5  
 6     std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
 7  
 8     if (enableValidationLayers) {
 9         extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
10     }
11  
12     return extensions;
13 }

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标明的扩展,总是要用到,但是debug信使扩展是在一定条件下太会添加的。注意,这里我用 VK_EXT_DEBUG_UTILS_EXTENSION_NAME 宏,等于用字符串"VK_EXT_debug_utils"。用这个宏让你避免打字了。

We can now use this function in  createInstance :

现在我们可以在 createInstance 中用这个函数了:

1 auto extensions = getRequiredExtensions();
2 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
3 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.

现在我们看看debug回调函数长什么样。添加一个新的静态成员函数 debugCallback ,以 PFN_vkDebugUtilsMessengerCallbackEXT 为原型。 VKAPI_ATTRVKAPI_CALL 确保函数有正确的签名,以供Vulkan调用。

 1 static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
 2     VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
 3     VkDebugUtilsMessageTypeFlagsEXT messageType,
 4     const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
 5     void* pUserData) {
 6  
 7     std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;
 8  
 9     return VK_FALSE;
10 }

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

第一个参数标明消息的严重性,具体如下:

  • VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT 诊断信息
  • VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT :例如创建资源这种消息
  • VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT :关于不像是错误,更像是app里的bug的行为的消息
  • VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT :关于无效且可能造成崩溃的行为的消息

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:

这些枚举值是这样设计的:你可以用比较操作来检查一个消息是等于某个严重等级,还是更糟糕。例如:

1 if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
2     // Message is important enough to show
3 }

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

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
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 .

回调函数返回一个bool值,表明触发了验证层消息的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中,即使debug回调函数也受一个句柄管理,这个句柄需要被显式地创建和销毁。这样的回调函数是 debug 信使 的一部分,而且你想要多少都可以。在 instance 之后为这个句柄添加一个成员:

VkDebugUtilsMessengerEXT debugMessenger;

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

现在添加 setupDebugMessenger 函数,在 initVulkan 中继 createInstance 之后调用它:

1 void initVulkan() {
2     createInstance();
3     setupDebugMessenger();
4 }
5  
6 void setupDebugMessenger() {
7     if (!enableValidationLayers) return;
8  
9 }

We'll need to fill in a structure with details about the messenger and its callback:

我们需要给一个struct填入信息(关于信使及其回调函数):

1 VkDebugUtilsMessengerCreateInfoEXT createInfo = {};
2 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
3 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;
4 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;
5 createInfo.pfnUserCallback = debugCallback;
6 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 外的所有类型,以接收可能的问题的提醒,且不接收冗长的debug信息。

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.

注意,还有很多其他方式,可以配置验证层消息和debug回调函数,但是在本教程中用这样的方式是个好的开始。参考扩展说明 可得更多可能的信息。

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.

这个struct应当被传入 vkCreateDebugUtilsMessengerEXT 函数,以创建 VkDebugUtilsMessengerEXT 对象。不幸的是,因为这个函数是个扩展函数,它不是被自动加载的。我们不得不用 vkGetInstanceProcAddr 函数自己查找它的地址。我们要创建自己的代理函数来在后台处理这件事。我已经将它放到 HelloTriangleApplication 类定义的上面。

1 VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) {
2     auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
3     if (func != nullptr) {
4         return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
5     } else {
6         return VK_ERROR_EXTENSION_NOT_PRESENT;
7     }
8 }

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 。我们现在可以调用这个函数来创建扩展对象了(如果可用的话)。

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

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. Let's see if it works... Run the program and close the window once you're fed up with staring at the blank window. You'll see that the following messages are printed to the command prompt:

第二个到最后一个参数,又是可选的分配器回调函数,我们设置为 nullptr 即可,除此之外,其他参数都很直观。由于debug信使是我们的Vulkan instance及其层专用的,它需要被显式地标明为第一个参数。你将在其他子对象上也看到这样的模式。我们看看它能否工作。运行程序,一旦出现空窗口就关闭窗口。你将看到下述信息打印到命令行:

[译]Vulkan教程(06)验证层

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

如果你没有看到任何消息,那么检查你的安装过程。

Oops, it has already spotted a bug in our program! The  VkDebugUtilsMessengerEXT object needs to be cleaned up with a call to  vkDestroyDebugUtilsMessengerEXT . Similarly to  vkCreateDebugUtilsMessengerEXT the function needs to be explicitly loaded. Note that it is normal for this message to be printed multiple times. This happens because multiple validation layers check for the deletion of the debug messenger.

哎呀,它点出了我们程序中的一个bug! VkDebugUtilsMessengerEXT 对象需要被 vkDestroyDebugUtilsMessengerEXT 函数清理。与 vkCreateDebugUtilsMessengerEXT 相似,这个函数需要被显式地加载。注意,如果这个消息被打印很多次,那是很正常的。这是因为多个验证层检查了debug信使的删除操作。

Create another proxy function right below  CreateDebugUtilsMessengerEXT :

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

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

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 函数里调用它:

 1 void cleanup() {
 2     if (enableValidationLayers) {
 3         DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
 4     }
 5  
 6     vkDestroyInstance(instance, nullptr);
 7  
 8     glfwDestroyWindow(window);
 9  
10     glfwTerminate();
11 }

When you run the program again you'll see that the error message has disappeared. 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.

当你再次运行程序时,你会看到错误消息消失了。如果你想看看,是什么触发了消息,你可以添加一个断点到消息回调函数,观察stack trace。

Debugging instance creation and destruction 调试instance的创建过程和销毁过程

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 函数要求一个有效的instance已经创建完毕, vkDestroyDebugUtilsMessengerEXT 必须在instance销毁之前调用。这让我们无法在 vkCreateInstance vkDestroyInstance 的调用中调试任何问题。

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:

但是,如果你仔细阅读扩展文档,你会看到有个办法可以专门为这2个函数创建一个单独的debug工具信使。它要求你向 VkInstanceCreateInfo pNext 扩展字段传入一个 VkDebugUtilsMessengerCreateInfoEXT 结构体。首先提取信使创建信息到一个单独函数:

 1 void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
 2     createInfo = {};
 3     createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
 4     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;
 5     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;
 6     createInfo.pfnUserCallback = debugCallback;
 7 }
 8  
 9 ...
10  
11 void setupDebugMessenger() {
12     if (!enableValidationLayers) return;
13  
14     VkDebugUtilsMessengerCreateInfoEXT createInfo;
15     populateDebugMessengerCreateInfo(createInfo);
16  
17     if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
18         throw std::runtime_error("failed to set up debug messenger!");
19     }
20 }

We can now re-use this in the  createInstance function:

现在我们可以在 createInstance 函数中复用它:

 1 void createInstance() {
 2     ...
 3  
 4     VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
 5     if (enableValidationLayers) {
 6         createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
 7         createInfo.ppEnabledLayerNames = validationLayers.data();
 8  
 9         populateDebugMessengerCreateInfo(debugCreateInfo);
10         createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
11     } else {
12         createInfo.enabledLayerCount = 0;
13         
14         createInfo.pNext = nullptr;
15     }
16  
17     ...
18 }

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 调用之前不会被销毁。通过以这样的方式创建一个额外的debug信使,它会被自动地用在 vkCreateInstance vkDestroyInstance 中,且在那之后被清理掉。

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 结构体中标明的flag外,严重层的行为配置项还有很多。浏览Vulkan SDK,找到 Config 文件夹。在这里你会发现一个 vk_layer_settings.txt 文件,它解释了如何配置层。

To configure the layer settings for your own application, copy the file to the  Debug and  Release directories 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.

为了给你的app配置层,将此文件复制到你项目的 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设备了。

C++ code C++完整源代码


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

30天自制操作系统

30天自制操作系统

[日] 川合秀实 / 周自恒、李黎明、曾祥江、张文旭 / 人民邮电出版社 / 2012-8 / 99.00元

自己编写一个操作系统,是许多程序员的梦想。也许有人曾经挑战过,但因为太难而放弃了。其实你错了,你的失败并不是因为编写操作系统太难,而是因为没有人告诉你那其实是一件很简单的事。那么,你想不想再挑战一次呢? 这是一本兼具趣味性、实用性与学习性的书籍。作者从计算机的构造、汇编语言、C语言开始解说,让你在实践中掌握算法。在这本书的指导下,从零编写所有代码,30天后就可以制作出一个具有窗口系统的32位......一起来看看 《30天自制操作系统》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

随机密码生成器
随机密码生成器

多种字符组合密码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试