vulkan 第三天 validation layers 检错系统?

Vulkan API默认错误检查有限,可能导致崩溃或未定义行为。Validation layers作为可选组件,用于参数检查、资源管理、线程安全及函数调用记录,提升调试效率。本文介绍如何应用Validation layers并提供全代码示例,同时探讨可能影响CPU占用的无限循环问题。

Vulkan API的设计核心是尽量最小化驱动程序的额外开销,所谓额外开销更多的是指向渲染以外的运算。其中一个具体的表现就是默认条件下,Vulkan API的错误检查的支持非常有限。即使遍历不正确的值或者将需要的参数传递为空指针,也不会有明确的处理逻辑,并且直接导致崩溃或者未定义的异常行为。之所以这样,是因为Vulkan要求每一个步骤定义都非常明确,导致很容易造成小错误,例如使用新的GPU功能,但是忘记了逻辑设备创建时请求它。

但是,这并不意味着这些检查不能添加到具体的API中。Vulkan推出了一个优化的系统,这个系统称之为Validation layers。Validation layers是可选组件,可以挂载到Vulkan函数中调用,以回调其他的操作。Validation layers的常见操作情景有:

  1. 根据规范检查参数数值,最终确认是否存与预期不符的情况
  2. 跟踪对象的创建和销毁,以查找是否存在资源的泄漏
  3. 跟踪线程的调用链,确认线程执行过程中的安全性
  4. 将每次函数调用所使用的参数记录到标准的输出中,进行初步的Vulkan概要分析

本节的代码主要是VulkanAPI的错误检查,应用好了可以大幅提升debug的效率。不过感觉这个使用过程有点复杂,实际使用还是需要更多的实践。

代码实例:代码要贴全了,便于大家理解,我本人是不太喜欢那种只贴一个片段的代码,出现了一个错误你就发现无从下手

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/mat4x4.hpp>
#include<vulkan/vulkan.h>

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


const int WIDTH = 800;
const int HEIGHT = 600;
const std::vector<const char*> validationLayers = {
"VK_LAYER_LUNARG_standard_validation" };

#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif

//代理函数 
//该结构体应该传递给vkCreateDebugReportCallbackEXT函数创建VkDebugReportCallbackEXT对象。
//不幸的是,因为这个功能是一个扩展功能,它不会被自动加载。所以必须使用vkGetInstanceProcAddr查找函数地址。
VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const
	VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) {
	auto func = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
	if (func != nullptr)
	{
		return func(instance, pCreateInfo, pAllocator, pCallback);
	}
	else
	{
		return VK_ERROR_EXTENSION_NOT_PRESENT;
	}
}
//通过调用vkDestroyDebugReportCallbackEXT清理VkDebugReportCallbackEXT对象
void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator)
{
	auto func = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
	if (func != nullptr)
	{
		func(instance, callback, pAllocator);
	}
}



class XFutureWorld
{
public:
	XFutureWorld():
		window(NULL)
	{
		
	}
	~XFutureWorld()
	{
		
	}
private:
	

	GLFWwindow* window;

	VkInstance instance;
	//callback 指针
	VkDebugReportCallbackEXT callback;
public:
	void run()
	{
		initWindow();
		initVulkan();
		mainLoop();
		cleanUp();
	}

private:

	void initWindow()
	{
		//调用glfw 初始化
		glfwInit();
		glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
		glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
		//穿件glfw 窗口
	    window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan window", nullptr, nullptr);
		if(window==NULL)
		{
			std::cout << "窗口创建失败";
		}
	}

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

	void mainLoop()
	{
		while (!glfwWindowShouldClose(window)) {
			glfwPollEvents();
		}
	}

	void cleanUp()
	{
		DestroyDebugReportCallbackEXT(instance, callback, nullptr);
		vkDestroyInstance(instance, nullptr);
		glfwDestroyWindow(window);
		glfwTerminate();
	}

	//创建instance 
	void createInstance()
	{
		//检查请求的layer是否可用,如果不可用就抛出异常
		if (enableValidationLayers && !checkValidationLayerSupport())
		{
			throw std::runtime_error("validation layers requested, but not available!");
		}

		//为该数据结构赋予自定义应用程序的信息。这些数据从技术角度是可选择的,
		//但是它可以为驱动程序提供一些有用的信息来优化程序特殊的使用情景,比如驱动程序使用一些图形引擎的特殊行为。
		//可选的
		VkApplicationInfo appInfo = {};
		appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
		appInfo.pNext = nullptr;
		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;

		//Vulkan中的大量信息通过结构体而不是函数参数传递,我们将填充一个结构体以提供足够的信息创建instance。
		//必须存在,它需要告知Vulkan驱动程序我们需要使用哪些全局的 extensions 和 validation layers
		VkInstanceCreateInfo createInfo = {};
		createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
		createInfo.pApplicationInfo = &appInfo;
		
		//全局扩展?
		//unsigned int glfwExtensionCount = 0;
		//const char** glfwExtensions;

		////GLFW有一个方便的内置函数,返回它有关的扩展信息
		//glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

		//createInfo.enabledExtensionCount = glfwExtensionCount;
		//createInfo.ppEnabledExtensionNames = glfwExtensions;
		//createInfo.enabledLayerCount = 0;

	    //VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);

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

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

		if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
			throw std::runtime_error("failed to create instance!");
		}

	}
	
	//将debugcallback赋值给 VkDebugReportCallbackCreateInfoEXT
	//然后通过CreateDebugReportCallbackEXT代理函数 创建VkDebugReportCallbackEXT=callback
	void setupDebugCallback() {
		if (!enableValidationLayers) return;

		VkDebugReportCallbackCreateInfoEXT createInfo = {};
		createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
		createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
		createInfo.pfnCallback = debugCallback;

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

	//将基于是否开启validation layers返回需要的扩展列表
	std::vector<const char*> getRequiredExtensions() {
		std::vector<const char*> extensions;

		unsigned int glfwExtensionCount = 0;
		const char** glfwExtensions;
		glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

		for (unsigned int i = 0; i < glfwExtensionCount; i++) {
			extensions.push_back(glfwExtensions[i]);
		}

		if (enableValidationLayers) {
			extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
		}

		return extensions;
	}


	//我们将添加一个新的函数checkValidationLayerSupport,检测所有请求的layers是否可用。
	//首先使用vkEnumerateInstanceLayerProperties函数列出所有可用的层。
	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 (strcmp(layerName, layerProperties.layerName) == 0) {
					layerFound = true;
					break;
				}
			}

			if (!layerFound) {
				return false;
			}
		}

		return true;
	}

	//debugcallback函数 
	static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) {
		std::cerr << "validation layer: " << msg << std::endl;

		return VK_FALSE;
	}

};




int main() {

	XFutureWorld xFutureWorld;

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

	return EXIT_SUCCESS;






	


	//uint32_t extensionCount = 0;
	//vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);

	//std::cout << extensionCount << " extensions supported" << std::endl;

	//glm::mat4 matrix;
	//glm::vec4 vec;
	//auto test = matrix * vec;

}

 

 

查看了下程序运行后的效率,发现一个很简单的程序会导致cpu占用率很高,我怀疑是

while (!glfwWindowShouldClose(window)) {
            glfwPollEvents();
        }

这种无限循环的方式并不是特别优秀,是否应该用timer去驱动整个三维的循环会更好,给其他的操作以更大的时间片去处理同时能够维持一个三维基本流畅的效果,暂时还是个假设,希望后期能出一个测试的结论试试。

如果已经有测试过的朋友欢迎联系我啊

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值