Vulkan development practice detailed learning notes - verify Layer

1. Drawing

Open bnvulkanex / util / fileutil H file addition

//A structure SpvDataStruct for storing SPIR-V data is declared
//The structure has two members, where size is used to store the total bytes of SPIR-V data, and data is the pointer to the first address of SPIR-V data memory block.
typedef struct SpvDataStruct  //Structure for storing SPIR-V data
{
	int size; //Total bytes of SPIR-V data
	uint32_t* data; //Pointer to the first address of SPIR-V data memory block
}SpvData;

BNVulkanEx/util/FileUtil.cpp

SpvData& FileUtil::loadSPV(string fname) //Load the SPIR-V data file under the folder
{
	size_t size = (getfilesize(fname));//Gets the total number of bytes of the SPIR-V data file
	cout<<"len:"<<size<<endl;
	SpvData spvData;//Building SpvData structure instances
	spvData.size = size;//Sets the total number of bytes of SPIR-V data
	spvData.data = (uint32_t*)(malloc(size)); Allocate the corresponding number of bytes of memory
	char* buf = (char*)spvData.data;//Load data from file into memory
	char c_file[1000];
	strcpy(c_file,fname.c_str());
	FILE *fpSPV;
	fpSPV = fopen(c_file,"rb");
	if(fpSPV == NULL)
	{
		printf("Open file%s fail\n",fname.c_str());
	}
	fread(buf,size,1,fpSPV);
	return spvData;
}

The Asset in the book should be the directory of android

Different GPU s of different models will provide different verification Layer combinations according to different manufacturers and driver versions

Common validation layers

nameexplain
VK_LAYER_GOOGLE_unique_objectsSince non dispatched Vulkan object handles do not require uniqueness, it is possible for Vulkan drivers to return the same handle as long as these different objects are considered equivalent. This makes it difficult to trace objects during debugging. After activating this verification Layer, each Vulkan object will be assigned a unique ID, which makes object tracking during debugging much easier. In addition, it should be noted that this verification Layer must be placed at the end of all active verification Layer sequences, that is, the position closest to the drive
VK_LAYER_LUNARG_api_dumpAfter this verification Layer is opened, all relevant parameter values in all called Vulkan function methods will be printed during program running, which is convenient for developers to debug
VK_LAYER_LUNARG_core_validationAfter this verification Layer is activated, important information such as description set and pipeline status will be verified and printed during program operation. At the same time, after this verification Layer is activated, it will track and verify the display memory, object binding, command buffer, etc., and also verify the graphics pipeline and calculation pipeline
VK_LAYER_LUNARG_imageThis verification Layer is used to verify the texture format, render target format, and so on. For example, you can verify whether the requested format is supported in the corresponding device, and whether the image view creation parameters match the corresponding image
VK_LAYER_LUNARG_object_trackerThis verification Layer is used to track the whole process of objects from creation to use and then to destruction. It can help developers avoid memory leakage. It can also be used to verify whether the objects of interest are properly created and currently effective
VK_LAYER_LUNARG_parameter_validationThis verification Layer is used to verify that the parameters passed to the Vulkan function method are correct
VK_LAYER_LUNARG_swapchainThis verification Layer is used to verify the use of WSI exchange chain extensions. For example, it can verify whether the extension is available before using the WSI exchange chain extension related function method, and can be used to verify whether the given image index is within the allowable range of the exchange chain, etc
VK_LAYER_GOOGLE_threadingThis verification Layer is mainly used to help check the thread safety of Vulkan context. It can check whether the multi-threaded related API is used correctly, and it can also report object access that violates the multi-threaded mutually exclusive access rules. At the same time, it also enables applications to continue to run normally without crashing when threading problems are reported
VK_LAYER_LUNARG_standard_validationThis validation Layer is used to confirm that all validation layers are organized in the correct order

Different types of verification letters

nameInformation type flagexplain
error messageVK_DEBUG_REPORT_ERROR_BIT_EXTGenerally refers to the wrong API use. Such problems may lead to unpredictable program running results, such as program crash
Warning messageVK_DEBUG_REPORT_WARNING_BIT_EXTGenerally, it refers to potential wrong API use or dangerous API use
Message informationVK_DEBUG_REPORT_INFORMATION_BIT_EXTUsed to display user-friendly prompts. Generally, this information is used to describe the activities in the background of the program, such as the details of resources, which is very helpful for debugging
Performance warning informationVK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXTIt is generally used to alert potential non optimal Vulkan calls, which may lead to poor program performance
debug informationVK_DEBUG_REPORT_DEBUG_BIT_EXTUsed to give diagnostic information from the loader or verify the Layer

BNVulkanEx/main_ Add bnvalidateutil. Under task h BNValidateUtil. cpp

CMAKELISTS.txt add bnvalidateutil h BNValidateUtil. cpp

#ifndef VULKANEXBASE_BNValidateUtil_H
#define VULKANEXBASE_BNValidateUtil_H
#include <vector>
#include <vulkan/vulkan.h>
#include <string>
//A structure with the name BNDeviceLayerAndExtensionType is declared, which contains two members
//One is the verification Layer name list layerNames that can be supported after deletion
//The other is a list of device extension names required to support these validation layers
typedef struct BNDeviceLayerAndExtensionType
{
    std::vector<std::string *> layerNames;     //List of supported validation Layer names
    std::vector<std::string *> extensionNames; //Supported list of names of extensions required to validate Layer
} BNDeviceLayerAndExtension;

class BNValidateUtil
{
public:
    //The obtained verification Layer attribute list layerList, two function pointers dbgCreateDebug ReportCallback and dbgDestroyDebugReportCallback, and the debug report object debugReportCallback are declared respectively
    static std::vector<VkLayerProperties> layerList; //Gets the list of validation Layer properties for the
    static PFN_vkCreateDebugReportCallbackEXT dbgCreateDebugReportCallback;
    static PFN_vkDestroyDebugReportCallbackEXT dbgDestroyDebugReportCallback;
    static VkDebugReportCallbackEXT debugReportCallback; Debugging report callback
    //The getLayerProperties method is declared, and the parameter of this method is the list of validation Layer names expected to be started.
    //Its function is to find the intersection between the expected validation Layer name list and the actually supported validation Layer name list, and then return the supported validation Layer name list and the required instance extension name list.
    static BNDeviceLayerAndExtension getLayerProperties(std::vector<const char *> exceptedLayerNames);
    //Getlayerdevice extension method is declared. The first parameter of this method is the specified physical device;
    //The second parameter is the specified verification Layer name list. The function is to obtain the logical device extension name list required by the specified verification Layer name list.
    static std::vector<std::string *> getLayerDeviceExtension(VkPhysicalDevice &gpu, std::vector<std::string *> supportedlayerNames);
    //First, the method of creating a debug report callback createDebugReportCallbackSelf is declared,
    static void createDebugReportCallbackSelf(VkInstance &instance);  //Method for creating debug report callback
	//Then it declares the method of destroying the debugging report callback destroyDebugReportCallbackSelf,
    //dbgDestroyDebug ReportCallback points to the dynamically loaded method used to destroy the debug report callback.
    static void destroyDebugReportCallbackSelf(VkInstance &instance); //Method of destroying debugging report callback
	//Finally, the method debugFunction for being called back to print verification information is declared
    //The createDebugReportCallbackSelf and destroyDebugReportCallbackSelf methods will call the corresponding methods of dynamic loading described earlier to complete the target task.
    static VKAPI_ATTR VkBool32 VKAPI_CALL debugFunction(              //Method for being called back to print verification information
        VkFlags msgFlags,                                             //Flag of the debug event type that triggered the execution of this callback
        VkDebugReportObjectTypeEXT objType,                           //The type of object processed by this callback
        uint64_t srcObject,                                           //Handle to the object created or processed by this callback
        size_t location,                                              //Describes the location of the corresponding debug event code
        int32_t msgCode,                                              //Message code
        const char *layerPrefix,                                      //Validation Layer that triggers this callback
        const char *msg,                                              //Message string
        void *pUserData                                               //User defined data
    );
};

#endif

Implementation of verification code

#include "BNValidateUtil.h"
#include <iostream>
#include <cstring>
#define LOGE printf
std::vector<VkLayerProperties> BNValidateUtil::layerList;
PFN_vkCreateDebugReportCallbackEXT BNValidateUtil::dbgCreateDebugReportCallback;
PFN_vkDestroyDebugReportCallbackEXT BNValidateUtil::dbgDestroyDebugReportCallback;
VkDebugReportCallbackEXT BNValidateUtil::debugReportCallback;
//Two overloaded versions of the isContain method
//The two isContain methods have different entry parameter types and basically the same functions. They are used to judge whether the specified string is included in the specified string list.
bool isContain(std::vector<const char *> inNames, char *inName)
{ //Method to determine whether the string is in the list
    for (auto s : inNames)
    { //Traversal string list
        if (strcmp(s, inName) == 0)
        {                //If the given string is the same as the current string
            return true; //Returns true, indicating that the specified string is in the list
        }
    }
    return false; //Returns false, indicating that the specified string is not in the list
}
bool isContain(std::vector<std::string *> inNames, char *inName)
{ //Method to determine whether the string is in the list
    for (auto s : inNames)
    { //Traversal string list
        if (strcmp((*s).c_str(), inName) == 0)
        {                //If the given string is the same as the current string
            return true; //Returns true, indicating that the specified string is in the list
        }
    }
    return false; //Returns false, indicating that the specified string is not in the list
}
//getLayerProperties method. This method receives the list of verified Layer names that are expected to start, then calls the vkEnumerateInstanceLayerProperties method to get the list of validation Layer attributes that can be supported at present.
//Then we call the vkEnumerateInstanceLayerProperties method to get the list of Layer attributes that can be supported at present.
BNDeviceLayerAndExtension BNValidateUtil::getLayerProperties(std::vector<const char *> exceptedLayerNames)
{
    BNDeviceLayerAndExtension result;                                  //Return result structure instance
    uint32_t layerCount;                                               //Total number of validation layers
    vkEnumerateInstanceLayerProperties(&layerCount, NULL);             //Gets the total number of validation layers
    LOGE("Layer The quantity of is %d\n", layerCount);                            //Print the total number of validation layers
    layerList.resize(layerCount);                                      //Change list length
    vkEnumerateInstanceLayerProperties(&layerCount, layerList.data()); //Get the total list of validation Layer properties
    //Then traverse the obtained verification Layer attribute list to investigate each verification Layer attribute.
    for (int i = 0; i < layerList.size(); i++)
    {                                                            //Traverse and verify the Layer attribute list
        VkLayerProperties lp = layerList[i];                     //Gets the current validation Layer property
        LOGE("----------------Layer %d----------------\n", i);   //Print verification Layer serial number
        LOGE("layer name %s\n", lp.layerName);                    //Print verification Layer name
        LOGE("layer describe %s\n", lp.description);                  //Print verification Layer description information
        bool flag = isContain(exceptedLayerNames, lp.layerName); //Whether the current verification Layer needs
        if (flag)
        { //If necessary, record the current verification Layer name in the verification Layer name result list
            //Record the name of this verification Layer in the result list (list of supported verification Layer names),
            result.layerNames.push_back(new std::string(lp.layerName));
        }
        uint32_t propertyCount; //Number of extended attributes corresponding to this validation Layer
        vkEnumerateInstanceExtensionProperties(lp.layerName, &propertyCount, NULL);
        std::vector<VkExtensionProperties> propertiesList; //Extended attribute list
        propertiesList.resize(propertyCount);              //Adjust list length
        vkEnumerateInstanceExtensionProperties(lp.layerName, &propertyCount, propertiesList.data());
        for (auto ep : propertiesList)
        {                                              //Traverse the list of extended properties corresponding to this validation Layer
            LOGE("  Required extensions:%s\n", ep.extensionName); //Print extension name
            if (flag)
            { //If the current verification Layer is required
                if (!isContain(result.extensionNames, ep.extensionName))
                {
                    //At the same time, the instance extension name required for this verification Layer is also recorded in the result list (the list of supported extension names required for verification Layer),
                    result.extensionNames.push_back(new std::string(ep.extensionName));
                }
            }
        }
    }
    //Finally, the structure instance containing two result lists is returned
    return result; //Return results
}
//The getlayerdevice extension method receives the specified physical device and a list of validation Layer names that need to be supported
std::vector<std::string *> BNValidateUtil::getLayerDeviceExtension(VkPhysicalDevice &gpu, std::vector<std::string *> supportedlayerNames)
{
//Then traverse all the current verification Layer attribute lists to obtain the device extension attribute list required for each verification Layer.
    std::vector<std::string *> result; //Required device extension name result list
    for (int i = 0; i < layerList.size(); i++)
    { 
        //Get the list of device extended attributes required for each validation Layer.
        //Traverse the attribute list of all validation layers
        VkLayerProperties lp = layerList[i];                                                            //Gets the current validation Layer property
        LOGE("----------------Layer %d----------------\n", i);                                          //Print verification Layer serial number
        LOGE("layer name %s\n", lp.layerName);                                                           //Print verification Layer name
        LOGE("layer describe %s\n", lp.description);                                                         //Print verification Layer description information
        uint32_t propertyCount;                                                                         //Number of device extended attributes
        vkEnumerateDeviceExtensionProperties(gpu, lp.layerName, &propertyCount, NULL);                  //Gets the number of device extended attributes corresponding to the current verification Layer
        std::vector<VkExtensionProperties> propertiesList;                                              //Device extended attribute list
        propertiesList.resize(propertyCount);                                                           //Adjust list length
        vkEnumerateDeviceExtensionProperties(gpu, lp.layerName, &propertyCount, propertiesList.data()); //Fill in the device extended attribute list corresponding to the current verification Layer
        for (auto ep : propertiesList)
        { //Traverse device extended attribute list
            LOGE("  Required device extensions:%s\n", ep.extensionName);
            if (isContain(supportedlayerNames, lp.layerName))
            { //Judge whether the current verification Layer is required
                if (!isContain(result, ep.extensionName))
                {                                                        //Judge whether the current device extension is already in the list
                    result.push_back(new std::string(ep.extensionName)); //Adds the current device extension name to the list
                }
            }
        }
    }
    return result; //Returns the result list of the required device extension name
}
//If the return value of this method is VK_SUCCESS means that after the callback, the subsequent verification Layer will continue to execute. If the return value is VK_FALSE indicates that the subsequent verification Layer will not continue.
//
VKAPI_ATTR VkBool32 VKAPI_CALL BNValidateUtil::debugFunction(
    VkFlags msgFlags,                   //Flag of the debug event type that triggered the execution of this callback
    VkDebugReportObjectTypeEXT objType, //The type of object processed by this callback
    uint64_t srcObject,                 //Handle to the object created or processed by this callback
    size_t location,                    //Describes the location of the corresponding debug event code
    int32_t msgCode,                    //Message code
    const char *layerPrefix,            //The validation Layer that triggers this callback, such as loader or validation Layer
    const char *msg,                    //Message string
    void *pUserData)
{ //User defined data
    //According to the message code, the message is divided into error information, warning information, message information, performance warning information and debugging information, and printed respectively.
	//In actual development, if readers have other special needs, they can further customize the output debugging information.
    //The name of this callback method can be customized, but its entry parameter sequence is specified in Vulkan and cannot be changed at will.
    if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
    { //error message
        LOGE("[VK_DEBUG_REPORT] ERROR: [%s]Code%d:%s\n", layerPrefix, msgCode, msg);
    }
    else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
    { //Warning message
        LOGE("[VK_DEBUG_REPORT] WARNING: [%s]Code%d:%s\n", layerPrefix, msgCode, msg);
    }
    else if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT)
    { //Message information
        LOGE("[VK_DEBUG_REPORT] INFORMATION:[%s]Code%d:%s\n", layerPrefix, msgCode, msg);
    }
    else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
    { //Performance warning information
        LOGE("[VK_DEBUG_REPORT] PERFORMANCE: [%s]Code%d:%s\n", layerPrefix, msgCode, msg);
    }
    else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT)
    { //debug information
        LOGE("[VK_DEBUG_REPORT] DEBUG: [%s]Code%d:%s\n", layerPrefix, msgCode, msg);
    }
    else
    {
        return VK_FALSE; //Other unknown situations
    }
    return VK_SUCCESS;
}

//Method for creating a debug report callback createDebugReportCallbackSelf
void BNValidateUtil::createDebugReportCallbackSelf(VkInstance &instance)
{ //Create debug report callback related
    //First, dynamically load the vkCreateDebugReportCallbackEXT method,
    dbgCreateDebugReportCallback = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
    //Then build a debug report callback to create an instance of the information structure
    VkDebugReportCallbackCreateInfoEXT dbgReportCreateInfo = {}; //Build the instance of information structure for debugging report callback creation
    //Specify the callback method as debugFunction, and confirm that the callback method is triggered by warning information, performance warning information, error information and debugging information
    //Next, create a debug report callback instance using the dynamically loaded vkCreateDebugReportCallbackEXT method
    dbgReportCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
    dbgReportCreateInfo.pfnCallback = debugFunction; //Specify callback method
    dbgReportCreateInfo.pUserData = NULL;            //User defined data passed to callback
    dbgReportCreateInfo.pNext = NULL;                //Pointer to custom data
    dbgReportCreateInfo.flags =                      //The type of event required to trigger the message callback
        VK_DEBUG_REPORT_WARNING_BIT_EXT |
        VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT |
        VK_DEBUG_REPORT_ERROR_BIT_EXT |
        VK_DEBUG_REPORT_DEBUG_BIT_EXT;
    VkResult result = dbgCreateDebugReportCallback(instance, &dbgReportCreateInfo, NULL, &debugReportCallback); //Create debug report callback instance
    if (result == VK_SUCCESS)
    {
        LOGE("Debugging report callback object created successfully!\n");
    }
}
//The method of callback of debug report is destroyed. destroyDebugReportCallbackSelf, in this method, the vkDestroyDebugReportCallbackEXT method is first loaded dynamically, then the vkDestroyDebugReportCallbackEXT method is called to destroy the specified debug report callback.
void BNValidateUtil::destroyDebugReportCallbackSelf(VkInstance &instance) //Destroy debugging report callback related
{
    dbgDestroyDebugReportCallback = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
    dbgDestroyDebugReportCallback(instance, debugReportCallback, NULL);
}

MyVulkanManager. Add in H

//In order to use the validation Layer, two members are added
#include "BNValidateUtil.h"
class MyVulkanManager
{
public:
	//Window auxiliary structure
	static struct WindowInfo info;
	//Cycle flag drawn by vulkan
	static bool loopDrawFlag;
	//One is a list of validation Layer names that you want to start
    //The other is the combination structure that can support the verification Layer and the required instance extension name list
    static std::vector<const char* >exceptionLayerNames;
	static BNDeviceLayerAndExtension bdlae;

The following work is init in MyVulkanManager class_ vulkan_ Instance method, create_vulkan_devices methods and destroy_ vulkan_ Add the relevant calling code to the instance method

init_vulkan_instance

	//Modified init_vulkan_instance method, in which there are two main changes. The first point is to add "VK_EXT_DEBUG_REPORT_EXTENSION_NAME" to the required instance extension name list
    //Enabling this instance extension enables the dynamic loading of the vkCreateDebugReportCallbackEXT method and the vkDestroyDebugReportCallbackEXT method to succeed	
    instanceExtensionNames.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
	
	//The verification Layer name list that the organization expects to start and the code to obtain the supported verification Layer name list and the corresponding required instance extension name list based on the current environment are added.
	//When building an instance and creating an information structure instance, the two obtained lists are used, and the relevant code for creating a debugging report callback is added at the end of the method.
	std::vector<const char*>exceptedLayerNames;//List of validation Layer names expected to start
	exceptedLayerNames.push_back("VK_LAYER_LUNARG_core_validation");
	exceptedLayerNames.push_back("VK_LAYER_LUNARG_parameter_validation");
	exceptedLayerNames.push_back("VK_LAYER_LUNARG_standard_validation");
	//The two lists obtained are used when building an instance and creating an information structure instance
	bdlae = BNValidateUtil::getLayerProperties(exceptedLayerNames);//Get support
	for(auto s: bdlae.extensionNames)//Add the required extension to the extension name list
	{
		instanceExtensionNames.push_back((*s).c_str());
	}
	exceptedLayerNames.clear();//Clear validation Layer name list

	for(auto s : bdlae.layerNames) Will be able to support validation Layer Name join Layer Name list
	{
		exceptedLayerNames.push_back((*s).c_str());
	}

create_vulkan_devices

	//Changed create_vulkan_devices method, which mainly adds the relevant code to obtain the list of device extension names required to start the verification Layer.
std::vector<std::string*> needsDeviceExtensions = BNValidateUtil::getLayerDeviceExtension(gpus[USED_GPU_INDEX], bdlae.layerNames);//Gets the device extension required to validate Layer
	for(auto s : needsDeviceExtensions)//Add required device extensions to the list
	{
		deviceExtensionNames.push_back((*s).c_str());
	}

destroy_vulkan_instance

	//Only the code related to destroying the debugging report callback is added.
	if(exceptedLayerNames.size() > 0)//Destroy debug report callback
	{
		BNValidateUtil::destroyDebugReportCallbackSelf(instance);
	}

Keywords: Vulkan

Added by big_c147 on Sat, 01 Jan 2022 07:33:24 +0200