[譯]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提供的標准診斷層。像擴展一樣,驗證層需要通過標明它們的名字來啟用。所有有用的標准驗證都被綁進了這個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".
我們首先添加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_ATTR
和VKAPI_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 messageVK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
: Informational message like the creation of a resourceVK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
: Message about behavior that is not necessarily an error, but very likely a bug in your applicationVK_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 performanceVK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
: Something has happened that violates the specification or indicates a possible mistakeVK_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 stringpObjects
: Array of Vulkan object handles related to the messageobjectCount
: Number of objects in array
參數pCallbackData
指向一個VkDebugUtilsMessengerCallbackDataEXT
結構體,它包含消息的細節,其最重要的成員是:
pMessage
:以\0結尾的字符串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
.
回調函數返回一個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及其層專用的,它需要被顯式地標明為第一個參數。你將在其他子對象上也看到這樣的模式。我們看看它能否工作。運行程序,一旦出現空窗口就關閉窗口。你將看到下述信息打印到命令行:
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配置層,將此文件復制到你項目的Debug
和Release
文件夾下,遵循其指示來設置你想要的行為。然而,在本教程最后,我將假設你在使用默認配置。
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++完整源代碼
- Previous上一章
- Next下一章