1.绘制
打开BNVulkanEx/util/FileUtil.h文件添加
//声明了一个用于存储SPIR-V数据的结构体SpvDataStruct
//该结构体有两个成员,其中size用于存储SPIR-V数据的总字节数,data为指向SPIR-V数据内存块首地址的指针。
typedef struct SpvDataStruct //存储SPIR-V 数据的结构体
{
int size; //SPIR-V 数据总字节数
uint32_t* data; //指向SPIR-V 数据内存块首地址的指针
}SpvData;
BNVulkanEx/util/FileUtil.cpp
SpvData& FileUtil::loadSPV(string fname) //加载文件夹下的SPIR-V 数据文件
{
size_t size = (getfilesize(fname));//获取SPIR-V 数据文件的总字节数
cout<<"len:"<<size<<endl;
SpvData spvData;//构建SpvData 结构体实例
spvData.size = size;//设置SPIR-V 数据总字节数
spvData.data = (uint32_t*)(malloc(size)); 分配相应字节数的内存
char* buf = (char*)spvData.data;//从文件中加载数据进入内存
char c_file[1000];
strcpy(c_file,fname.c_str());
FILE *fpSPV;
fpSPV = fopen(c_file,"rb");
if(fpSPV == NULL)
{
printf("打开文件%s失败\n",fname.c_str());
}
fread(buf,size,1,fpSPV);
return spvData;
}
书上的Asset应该是android的目录
不同型号的GPU随厂商、驱动版本的不同会提供不同的验证Layer组合
常用的验证Layer
名称 | 说明 |
---|---|
VK_LAYER_GOOGLE_unique_objects | 由于非分派的Vulkan对象句柄不要求具有唯一性,因此只要这些不同的对象被认为是等价的,Vulkan驱动就有可能返回相同的句柄。这就使得调试时的对象追踪变得比较困难。激活此验证Layer后,每个Vulkan对象都会被分配唯一的标识,这就使得调试时的对象追踪变得容易很多。另外要注意的是,这一验证Layer必须被放到所有激活验证Layer序列的最后,也就是离驱动最近的位置 |
VK_LAYER_LUNARG_api_dump | 打开此验证Layer后,程序运行过程中将打印所有被调用Vulkan功能方法中所有的相关参数值,便于开发人员调试 |
VK_LAYER_LUNARG_core_validation | 此验证Layer激活后程序运行过程中将验证并打印描述集、管线状态等方面的重要信息。同时此验证Layer激活后还会追踪与验证显存、对象绑定、命令缓冲等,也会对图形管线和计算管线进行验证 |
VK_LAYER_LUNARG_image | 此验证Layer用于验证纹理格式、渲染目标格式等。例如可以验证请求使用的格式在对应的设备中是否支持,还可以验证图像视图创建参数与对应的图像是否匹配等 |
VK_LAYER_LUNARG_object_tracker | 此验证Layer用于追踪对象从创建到使用再到销毁的全过程,可以帮助开发人员避免内存泄露,还可以用于验证关注的对象是否恰当地被创建以及目前是否有效等 |
VK_LAYER_LUNARG_parameter_validation | 此验证Layer用于验证传递给Vulkan功能方法的参数是否正确 |
VK_LAYER_LUNARG_swapchain | 此验证Layer用于验证WSI交换链扩展的使用。例如,其可以在使用WSI交换链扩展相关功能方法前验证此扩展是否可用,可用于验证给出的图像索引是否在交换链允许的范围内等 |
VK_LAYER_GOOGLE_threading | 此验证Layer主要用于帮助检查Vulkan上下文的线程安全性。其可以检查多线程相关的API是否被正确地使用,其还可以报告违反多线程互斥访问规则的对象访问。同时,其还可以使应用程序在报告了线程问题的情况下持续正常运行而不崩溃 |
VK_LAYER_LUNARG_standard_validation | 此验证Layer用于确认所有的验证Layer以正确的顺序组织 |
不同类型的验证信
名称 | 信息类型标志 | 说明 |
---|---|---|
错误信息 | VK_DEBUG_REPORT_ERROR_BIT_EXT | 一般指错误的API使用。这类问题可能导致不可预知的程序运行结果,比如程序崩溃 |
警告信息 | VK_DEBUG_REPORT_WARNING_BIT_EXT | 一般是指有潜在错误的API使用或危险的API使用 |
消息信息 | VK_DEBUG_REPORT_INFORMATION_BIT_EXT | 用于显示用户友好的提示信息。一般这些信息用于描述程序后台的活动,比如资源的明细等对调试工作很有帮助的信息 |
性能警告信息 | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | 一般用于提醒潜在的非最优Vulkan调用,这些调用可能导致程序的性能变差 |
调试信息 | VK_DEBUG_REPORT_DEBUG_BIT_EXT | 用于给出来自加载器或验证Layer的诊断信息 |
BNVulkanEx/main_task 下添加BNValidateUtil.h BNValidateUtil.cpp
CMAKELISTS.txt添加BNValidateUtil.h BNValidateUtil.cpp
#ifndef VULKANEXBASE_BNValidateUtil_H
#define VULKANEXBASE_BNValidateUtil_H
#include <vector>
#include <vulkan/vulkan.h>
#include <string>
//声明了名称为BNDeviceLayerAndExtensionType的结构体,其包含两个成员
//一个是删选后能支持的验证Layer名称列表layerNames
//另一个是支持这些验证Layer所需的设备扩展名称列表
typedef struct BNDeviceLayerAndExtensionType
{
std::vector<std::string *> layerNames; //支持的验证Layer名称列表
std::vector<std::string *> extensionNames; //支持的验证Layer所需扩展的名称列表
} BNDeviceLayerAndExtension;
class BNValidateUtil
{
public:
//分别声明了获取的验证Layer属性列表layerList、两个函数指针dbgCreateDebug ReportCallback和dbgDestroyDebugReportCallback以及调试报告对象debugReportCallback
static std::vector<VkLayerProperties> layerList; //获取的验证Layer属性列表
static PFN_vkCreateDebugReportCallbackEXT dbgCreateDebugReportCallback;
static PFN_vkDestroyDebugReportCallbackEXT dbgDestroyDebugReportCallback;
static VkDebugReportCallbackEXT debugReportCallback; 调试报告回调
//声明了getLayerProperties方法,此方法的参数为期望启动的验证Layer名称列表。
//其功能为根据输入的期望启动的验证Layer名称列表和实际能支持的验证Layer名称列表,求交集后返回能支持的验证Layer名称列表及所需的实例扩展名称列表。
static BNDeviceLayerAndExtension getLayerProperties(std::vector<const char *> exceptedLayerNames);
//声明了getLayerDeviceExtension方法,此方法的第一个参数为指定的物理设备;
//第二个参数为指定的验证Layer名称列表,功能为获取指定的验证Layer名称列表所需的逻辑设备扩展名称列表。
static std::vector<std::string *> getLayerDeviceExtension(VkPhysicalDevice &gpu, std::vector<std::string *> supportedlayerNames);
//首先声明了创建调试报告回调的方法createDebugReportCallbackSelf,
static void createDebugReportCallbackSelf(VkInstance &instance); //创建调试报告回调的方法
//接着声明了销毁调试报告回调的方法destroyDebugReportCallbackSelf,
//dbgDestroyDebug ReportCallback指向动态加载的用于销毁调试报告回调的方法。
static void destroyDebugReportCallbackSelf(VkInstance &instance); //销毁调试报告回调的方法
//最后声明了用于被回调以打印验证信息的方法debugFunction
//createDebugReportCallbackSelf和destroyDebugReportCallbackSelf方法将调用前面介绍的动态加载的对应方法来完成目标任务。
static VKAPI_ATTR VkBool32 VKAPI_CALL debugFunction( //用于被回调以打印验证信息的方法
VkFlags msgFlags, //触发此回调执行的调试事件类型标志
VkDebugReportObjectTypeEXT objType, //由此回调处理的对象类型
uint64_t srcObject, //此回调创建或处理的对象的句柄
size_t location, //描述对应调试事件代码的位置
int32_t msgCode, //消息代码
const char *layerPrefix, //触发此回调的验证Layer
const char *msg, //消息字符串
void *pUserData //用户自定义数据
);
};
#endif
验证代码的实现
#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;
//两个重载版本的isContain方法
//这两个isContain方法入口参数类型不同,功能基本一致,都是用于判断指定的字符串是否包含在指定的字符串列表中。
bool isContain(std::vector<const char *> inNames, char *inName)
{ //判断字符串是否在列表中的方法
for (auto s : inNames)
{ //遍历字符串列表
if (strcmp(s, inName) == 0)
{ //若给定字符串与当前字符串相同
return true; //返回true,表示指定字符串在列表中
}
}
return false; //返回false,表示指定字符串不在列表中
}
bool isContain(std::vector<std::string *> inNames, char *inName)
{ //判断字符串是否在列表中的方法
for (auto s : inNames)
{ //遍历字符串列表
if (strcmp((*s).c_str(), inName) == 0)
{ //若给定字符串与当前字符串相同
return true; //返回true,表示指定字符串在列表中
}
}
return false; //返回false,表示指定字符串不在列表中
}
//getLayerProperties方法。此方法接收期望启动的验证Layer名称列表,然后调用vkEnumerateInstanceLayerProperties方法获取目前能支持的验证Layer属性列表,
//然后调用vkEnumerateInstanceLayerProperties方法获取目前能支持的验证Layer属性列表,
BNDeviceLayerAndExtension BNValidateUtil::getLayerProperties(std::vector<const char *> exceptedLayerNames)
{
BNDeviceLayerAndExtension result; //返回结果结构体实例
uint32_t layerCount; //总的验证Layer的数量
vkEnumerateInstanceLayerProperties(&layerCount, NULL); //获取总的验证Layer数量
LOGE("Layer的数量为 %d\n", layerCount); //打印总的验证Layer数量
layerList.resize(layerCount); //更改列表长度
vkEnumerateInstanceLayerProperties(&layerCount, layerList.data()); //获取总的验证Layer属性列表
//接着对获取的验证Layer属性列表进行遍历,考察其中的每个验证Layer属性。
for (int i = 0; i < layerList.size(); i++)
{ //遍历验证Layer属性列表
VkLayerProperties lp = layerList[i]; //获取当前验证Layer属性
LOGE("----------------Layer %d----------------\n", i); //打印验证Layer序号
LOGE("layer名称 %s\n", lp.layerName); //打印验证Layer名称
LOGE("layer描述 %s\n", lp.description); //打印验证Layer描述信息
bool flag = isContain(exceptedLayerNames, lp.layerName); //当前验证Layer是否需要
if (flag)
{ //若需要,则将当前验证Layer名称记录到验证Layer名称结果列表
//则将此验证Layer的名称记录到结果列表(支持的验证Layer名称列表)中,
result.layerNames.push_back(new std::string(lp.layerName));
}
uint32_t propertyCount; //此验证Layer对应的扩展属性数量
vkEnumerateInstanceExtensionProperties(lp.layerName, &propertyCount, NULL);
std::vector<VkExtensionProperties> propertiesList; //扩展属性列表
propertiesList.resize(propertyCount); //调整列表长度
vkEnumerateInstanceExtensionProperties(lp.layerName, &propertyCount, propertiesList.data());
for (auto ep : propertiesList)
{ //遍历此验证Layer对应的扩展属性列表
LOGE(" 所需扩展:%s\n", ep.extensionName); //打印扩展名称
if (flag)
{ //若当前验证Layer是需要的
if (!isContain(result.extensionNames, ep.extensionName))
{
//,同时也将此验证Layer所需的实例扩展名称记录到结果列表(支持的验证Layer所需扩展的名称列表)中,
result.extensionNames.push_back(new std::string(ep.extensionName));
}
}
}
}
//最后将包含两个结果列表的结构体实例返回
return result; //返回结果
}
//getLayerDeviceExtension方法接收指定的物理设备和需要支持的验证Layer名称列表
std::vector<std::string *> BNValidateUtil::getLayerDeviceExtension(VkPhysicalDevice &gpu, std::vector<std::string *> supportedlayerNames)
{
//然后遍历目前所有的验证Layer属性列表,获取每个验证Layer所需的设备扩展属性列表。
std::vector<std::string *> result; //所需设备扩展名称结果列表
for (int i = 0; i < layerList.size(); i++)
{
//获取每个验证Layer所需的设备扩展属性列表。
//遍历所有验证Layer的属性列表
VkLayerProperties lp = layerList[i]; //获取当前验证Layer属性
LOGE("----------------Layer %d----------------\n", i); //打印验证Layer序号
LOGE("layer名称 %s\n", lp.layerName); //打印验证Layer名称
LOGE("layer描述 %s\n", lp.description); //打印验证Layer描述信息
uint32_t propertyCount; //设备扩展属性数量
vkEnumerateDeviceExtensionProperties(gpu, lp.layerName, &propertyCount, NULL); //获取当前验证Layer对应设备扩展属性数量
std::vector<VkExtensionProperties> propertiesList; //设备扩展属性列表
propertiesList.resize(propertyCount); //调整列表长度
vkEnumerateDeviceExtensionProperties(gpu, lp.layerName, &propertyCount, propertiesList.data()); //填充当前验证Layer对应设备扩展属性列表
for (auto ep : propertiesList)
{ //遍历设备扩展属性列表
LOGE(" 所需设备扩展:%s\n", ep.extensionName);
if (isContain(supportedlayerNames, lp.layerName))
{ //判断当前验证Layer是否需要
if (!isContain(result, ep.extensionName))
{ //判断当前设备扩展是否已在列表中
result.push_back(new std::string(ep.extensionName)); //将当前设备扩展名称添加进列表
}
}
}
}
return result; //返回所需设备扩展名称结果列表
}
//若此方法的返回值为VK_SUCCESS表示此回调结束后,后继的验证Layer继续执行,若返回值为VK_FALSE表示后面的验证Layer不再继续执行。
//
VKAPI_ATTR VkBool32 VKAPI_CALL BNValidateUtil::debugFunction(
VkFlags msgFlags, //触发此回调执行的调试事件类型标志
VkDebugReportObjectTypeEXT objType, //由此回调处理的对象类型
uint64_t srcObject, //此回调创建或处理的对象的句柄
size_t location, //描述对应调试事件代码的位置
int32_t msgCode, //消息代码
const char *layerPrefix, //触发此回调的验证Layer,比如是加载器还是验证Layer
const char *msg, //消息字符串
void *pUserData)
{ //用户自定义数据
//其根据消息代码将消息分为错误信息、警告信息、消息信息、性能警告信息、调试信息,并分别进行打印。
//实际开发中若读者还有其他特殊需要,还可以进一步自定义输出的调试信息。
//此回调方法的名称可以自定义,但其入口参数序列是Vulkan中规定的,不能进行随意改动。
if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
{ //错误信息
LOGE("[VK_DEBUG_REPORT] ERROR: [%s]Code%d:%s\n", layerPrefix, msgCode, msg);
}
else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
{ //警告信息
LOGE("[VK_DEBUG_REPORT] WARNING: [%s]Code%d:%s\n", layerPrefix, msgCode, msg);
}
else if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT)
{ //消息信息
LOGE("[VK_DEBUG_REPORT] INFORMATION:[%s]Code%d:%s\n", layerPrefix, msgCode, msg);
}
else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
{ //性能警告信息
LOGE("[VK_DEBUG_REPORT] PERFORMANCE: [%s]Code%d:%s\n", layerPrefix, msgCode, msg);
}
else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT)
{ //调试信息
LOGE("[VK_DEBUG_REPORT] DEBUG: [%s]Code%d:%s\n", layerPrefix, msgCode, msg);
}
else
{
return VK_FALSE; //其他未知情况
}
return VK_SUCCESS;
}
//创建调试报告回调的方法createDebugReportCallbackSelf
void BNValidateUtil::createDebugReportCallbackSelf(VkInstance &instance)
{ //创建调试报告回调相关
//其中首先动态加载vkCreateDebugReportCallbackEXT方法,
dbgCreateDebugReportCallback = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
//然后构建调试报告回调创建用信息结构体实例
VkDebugReportCallbackCreateInfoEXT dbgReportCreateInfo = {}; //构建调试报告回调创建用信息结构体实例
//指定回调方法为debugFunction,确定回调方法由警告信息、性能警告信息、错误信息、调试信息触发
//,接着使用动态加载的vkCreateDebugReportCallbackEXT方法创建调试报告回调实
dbgReportCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
dbgReportCreateInfo.pfnCallback = debugFunction; //指定回调方法
dbgReportCreateInfo.pUserData = NULL; //传递给回调的用户自定义数据
dbgReportCreateInfo.pNext = NULL; //指向自定义数据的指针
dbgReportCreateInfo.flags = //所需的触发消息回调的事件类型
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); //创建调试报告回调实例
if (result == VK_SUCCESS)
{
LOGE("调试报告回调对象创建成功!\n");
}
}
//销毁调试报告回调的方法destroyDebugReportCallbackSelf,此方法中首先动态加载vkDestroyDebugReportCallbackEXT方法,然后调用vkDestroyDebugReportCallbackEXT方法销毁指定的调试报告回调。
void BNValidateUtil::destroyDebugReportCallbackSelf(VkInstance &instance) //销毁调试报告回调相关
{
dbgDestroyDebugReportCallback = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
dbgDestroyDebugReportCallback(instance, debugReportCallback, NULL);
}
MyVulkanManager.h中添加
//为了使用验证Layer,增加了两个成员
#include "BNValidateUtil.h"
class MyVulkanManager
{
public:
//窗口辅助结构体
static struct WindowInfo info;
//vulkan绘制的循环标志
static bool loopDrawFlag;
//一个是期望启动的验证Layer名称列表
//另一个是能够支持的验证Layer与所需实例扩展名称列表的组合结构体
static std::vector<const char* >exceptionLayerNames;
static BNDeviceLayerAndExtension bdlae;
下面的工作为在MyVulkanManager类的init_vulkan_instance方法、create_vulkan_devices方法和destroy_vulkan_instance方法中添加相关的调用代码
init_vulkan_instance
//改动后的init_vulkan_instance方法,其中主要的变化有两点。第一点是在所需的实例扩展名称列表中增加了“VK_EXT_DEBUG_REPORT_EXTENSION_NAME”
//启用此实例扩展后使得vkCreateDebugReportCallbackEXT方法和vkDestroyDebugReportCallbackEXT方法的动态加载能够成功
instanceExtensionNames.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
//增加了组织期望启动的验证Layer名称列表以及基于当前环境获取能支持的验证Layer名称列表和对应所需实例扩展名称列表的代码。
//在构建实例创建信息结构体实例时使用了获取的两个列表,并在方法最后增加了创建调试报告回调的相关代码。
std::vector<const char*>exceptedLayerNames;//期望启动的验证Layer名称列表
exceptedLayerNames.push_back("VK_LAYER_LUNARG_core_validation");
exceptedLayerNames.push_back("VK_LAYER_LUNARG_parameter_validation");
exceptedLayerNames.push_back("VK_LAYER_LUNARG_standard_validation");
//在构建实例创建信息结构体实例时使用了获取的两个列表
bdlae = BNValidateUtil::getLayerProperties(exceptedLayerNames);//获取支持情况
for(auto s: bdlae.extensionNames)//将所需的扩展加入扩展名称列表
{
instanceExtensionNames.push_back((*s).c_str());
}
exceptedLayerNames.clear();//清空验证Layer名称列表
for(auto s : bdlae.layerNames) 将能支持的验证Layer名称加入Layer名称列表
{
exceptedLayerNames.push_back((*s).c_str());
}
create_vulkan_devices
//改动后的create_vulkan_devices方法,其中主要是增加了获取需要启动的验证Layer所需设备扩展名称列表的相关代码。
std::vector<std::string*> needsDeviceExtensions = BNValidateUtil::getLayerDeviceExtension(gpus[USED_GPU_INDEX], bdlae.layerNames);//获取验证Layer所需设备扩展
for(auto s : needsDeviceExtensions)//将所需设备扩展加入列表
{
deviceExtensionNames.push_back((*s).c_str());
}
destroy_vulkan_instance
//其中只是增加了销毁调试报告回调的相关代码。
if(exceptedLayerNames.size() > 0)//销毁调试报告回调
{
BNValidateUtil::destroyDebugReportCallbackSelf(instance);
}