本章最后简要概述一下 Vulkan API 在较低层次上的结构。
vk::XXXCreateInfo createInfo{};
createInfo.sType = vk::StructureType::eXXXCreateInfo;
createInfo.pNext = nullptr;
createInfo.foo = ...;
createInfo.bar = ...;
vk::XXX object;
try {
object = device.createXXX(createInfo);
} catch (vk::SystemError& err) {
std::cerr << "Failed to create object: " << err.what() << std::endl;
return false;
}
Vulkan 中的许多结构体都要求你在 sType 成员中显式指定结构体的类型。
pNext 成员可以指向一个扩展结构体,在本教程中它将始终为 nullptr。
创建或销毁对象的函数会有一个 VkAllocationCallbacks 参数,允许你为驱动内存使用自定义分配器,在本教程中也将保持为 nullptr。
几乎所有函数都返回一个 vk::Result,要么是 vk::result::eSuccess,要么是一个错误代码。
规范描述了每个函数可能返回哪些错误代码及其含义。
这些调用的失败会通过 C++ 异常来报告。异常会包含有关错误的更多信息,包括一个 vk::Result。这使我们能够从一次调用中检查多个命令,并保持命令语法的简洁。
验证层
如前所述,Vulkan 是为高性能和低驱动开销而设计的。
因此,默认情况下它只包含非常有限的错误检查和调试能力。
如果你做错了什么,驱动程序常常会崩溃而不是返回错误代码,或者更糟的是,它可能在你的显卡上看起来工作正常,但在其他显卡上完全失败。
Vulkan 允许你通过一种称为 验证层(validation layers) 的特性来启用广泛的检查。
验证层是可以插入到 API 和图形驱动之间的代码片段,用于执行诸如对函数参数进行额外检查、跟踪内存管理问题等操作。
一个重要的好处是,你可以在开发期间启用它们,然后在发布应用程序时完全禁用它们,从而实现零开销。
任何人都可以编写自己的验证层,但 LunarG 的 Vulkan SDK 提供了一套标准的验证层,我们将在本教程中使用它们。你还需要注册一个回调函数来接收来自这些层的调试消息。
由于 Vulkan 对每个操作都如此明确,并且验证层如此广泛,实际上比起 OpenGL 和 Direct3D,找出屏幕变黑的原因可能会容易得多!
在我们开始编写代码之前,只剩最后一步了,那就是
设置开发环境。