Vulkan学习笔记二

这次要创建出Vulkan的实例VkInstance,在vulkan中,创建surface和查找物理设备,都要基于这个VkInstance来进行操作。

首先在SceneWidget.h中,添加一个Protected的函数CreateInstance,在这个函数中进行VkInstance的创建。即在Init函数中调用CreateInstance。

将CreateInstance的返回值定义为bool,调用后判断返回值,若创建失败则打印错误信息并返回。

Vulkan学习笔记二_第1张图片

然后在CreateInstance函数,写创建实例的代码,如果是第一次配置开发环境的话,那在包含vulkan的头文件前还要先定义一个” VK_USE_PLATFORM_WIN32_KHR”的宏定义,否在找不到在win32下调用的扩展接口。具体可以看vulkan.h中的内容,其实就是针对不同的系统,包含不同的头文件,调用相应系统下的接口。然后在这个宏定义之后,包含vulkan.h文件。

现在要创建实例,但是不知道该先做什么,那就直接写创建实例的接口。在vulkan中,其方法都是以小写的vk开头,而变量是以大写的V和小写的k开头。所以可以CreateInstance函数中,直接写vkCreateInstance()。我用的是Visual studio编译器,可以自动进行提示。如果提示没有找到这个vkCreateInstance函数,那就需要检查你的开发环境,看是哪里没有配置对。

vkCreateInstance里的参数是什么呢,可以查相关的文档,也可以将鼠标放在函数名上,也可以看到提示。

第一个参数是const VkInstanceCreateInfo* pCreateInfo:那就在vkCreateInstance的上边声明一个VkInstanceCreateInfo的结构体。初始化可以赋值为{},然后再设置每个属性的值。vulkan中一般都是用结构体来表示对象的,比如VkApplicationInfo、VkInstanceCreateInfo还有VkWin32SurfaceCreateInfoKHR等等,其中如果后缀有KHR的话,即为扩展。一般是和运行的平台系统有关联。

第二个参数为自定义的分配器回调函数(是Vulkan编程指南中的说明),在这里不需要特殊管理,设置为nullptr。

第三个参数是记录分配的实例对象。由于之前在查找设备和创建surface是还要用,所以用一个VkInstance类型的成员变量m_instance来保存。

当创建成功是会返回一个VK_SUCCESS,其实是一个VkResult类型的枚举,当失败时返回具体的错误值。可以通过这个错误值来判断是哪里出了问题。

VkInstanceCreateInfo的构建, 定义的解释来源于vkspec文档。

VkStructureType sType的值,在这里,为VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO。在vulkan中,枚举一般是以VK_开始的。

const void* pNext的值为这个结构的扩展或是nullptr。在这里先设置为nullptr。

VkInstanceCreateFlags flags为将来预留的,也不用管。

const VkApplicationInfo* pApplicationInfo 指向VkApplicationInfo结构体的指针。

uint32_t  enabledLayerCount 开启的全局层的个数。

const char* const*   ppEnabledLayerNames创建VkInstance时,需要开启的层的名称数组,其个数为enabledLayerCount。

uint32_t   enabledExtensionCount 需要开启的扩展的个数。

const char* const*  ppEnabledExtensionNames需要开启的扩展的名称数组,个数为enabledExtensionCount。

补齐VkInstanceCreateInfo结构中所需要赋值的属性,还需要创建一个VkApplicationInfo的结构体,支持的层的名称和数目以及支持的扩展的名称和数目。所以在VkInstanceCreateInfo的上面定义一个VkInstanceCreateInfo的结构。

其属性:

VkStructureType sType的值为VK_STRUCTURE_TYPE_APPLICATION_INFO;

const void* pNext 这里一样设置为nullptr。

const char* pApplicationName 设置应用的名称,在这个工程里,可以通过Qt来设置,所以这个地方的pApplicationName 其实并不能显示作用来。

uint32_t  applicationVersion 应用程序的版本,可以根据需要设置其值。做过实验,设置成几,好像没有影响。

const char* pEngineName 可以根据需要设置一个字符串。

uint32_t engineVersion引擎的版本,可以根据需要设置其值。做过实验,设置成几,好像没有影响。

uint32_t apiVersion API的版本,可以根据需要设置其值。做过实验,设置成几,好像没有影响。

实例支持的扩展

因为逻辑上与创建VkInstance是分开的,所以另起一个函数,来获取可支持的扩展。并返回支持的扩展的名称数组。在头文件和cpp文件中增加函数GetRequiredExtensions(),在函数中,两次调用vkEnumerateInstanceExtensionProperties来获取实例支持的扩展。

vkEnumerateInstanceExtensionProperties有三个参数,具体的意义表示如下:

const char*  pLayerName 扩展的名称。

uint32_t*    pPropertyCount, 扩展的个数。

VkExtensionProperties*   pProperties 支持的扩展的名称数组。可以支持多个扩展。

第一次调用 vkEnumerateInstanceExtensionProperties,来获实例支持多少个扩展。即先申请一个uint 类型的变量extensionCount,并初始化为0。然后调用vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr),并返回一个VkResult的枚举,当返回值为VK_SUCCESS时,说明函数执行成功。但是在这里,即使函数执行成功了,但是获取到的扩展数目为0,也就是说,没有可用的扩展。那就不用再向下执行了。如果不是0,说明有支持的扩展。这时再定义一个std::vector extensions(extensionCount)变量,并初始化数组的大小为extensionCount(vector其实就是数组)。

vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data())调用之后,函数会填充extensions数组。

获取到扩展后,检查一下,有没有要用的扩展,其实就是需要支持surface和win32的surface。因为需要在Windows系统里显示,所以必须支持这两个。

最终这个函数的代码如下:

Vulkan学习笔记二_第2张图片

要在异常的地方返回,并且返回之前打一下日志,有助于快速定位异常点。

上图代码中:VK_KHR_WIN32_SURFACE_EXTENSION_NAME是定义在vulkan_win32.h文件中,所以如果编译器不识别这个宏时,需要将这个头文件包含进来。

函数完成后,在CreateInstance函数将扩展的两个字段补完整。

实例支持的层

在这里也可以再写一个函数,其实最主要就是要检查实例是否支持校验层:VK_LAYER_KHRONOS_validation。所以增加一个CheckValidationLayerSupport函数,进行验层。

检验层,不是必须的,它只是有助于查找异常,是一种调试的手段。所以可以根据传入参数来决定是否需要检验层。在开发调试阶段,需要开启校验层,发现更多的异常。而在发布版本时,则需要关闭校验层,保证应用可以执行的更流畅。

获取实例支持的层是调用vkEnumerateInstanceLayerProperties函数,用法与vkEnumerateInstanceExtensionProperties类似,不再重复描述了。其代码如下:

Vulkan学习笔记二_第3张图片

然后补起CreateInstance函数的代码,如下:

Vulkan学习笔记二_第4张图片

在工程里加上vulkan的lib。

最后运行一下看看有没有什么问题

Vulkan学习笔记二_第5张图片

黑窗口中没有报任何的错误信息,说明创建成功了:) 。

你可能感兴趣的:(c++)