vulkan学习笔记四

窗口已经创建完成(win32的surface),接下来就是创建逻辑设备了。在SceneWidget的头文件中再添加一个bool CreateDevice()的函数,用来创建逻辑设备。

创建逻辑设备,一般要分两步走。

第一步:查找物理设备,先要找到可用的物理设备也就是显止,才能根据物理设备创建逻辑设备。所以在头文件中再添加一个查找的函数:bool FindPhysicalDevice()。和添加一个物理设备的成员变量,用来记录所选择的物理设备:VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE。

(1)罗列出当前可用的物理设备。

可以通过vkEnumeratePhysicalDevices函数来获取当前的物理设备的信息。其用法与查找实例支持的Layer的特性函数vkEnumerateInstanceLayerProperties类似。

先调用一次vkEnumeratePhysicalDevices,第一个参数传入之前创建好的实例m_instance。第二个参数为返回的当前可用的物理设备的数目,所以传一个uint类型的变量deviceCount的引用。第三个参数传入nullptr。这次的调用只是获取当前可用设备的数目。

查检deviceCount的值是否为0,uint类型不会小于0 。如果deviceCount的值合法,再声明一个用于存放物理设备的列表变量,可以在声明时进行初始化,列表的数目初始化为deviceCount,即: std::vector<VkPhysicalDevice> devices(deviceCount)。

第二次调用vkEnumeratePhysicalDevices,前两个参数与第一次调用时的相同。第三个参数为devices.data()。

其代码如下:

(2)找出符合要求的设备。

对物理设备列表进行遍历,遍历时对每个物理设备进行检查。需要在SceneWidget.h文件中添加一个检查物理设备的函数bool CheckPhysicalDevice(VkPhysicalDevice device),然后遍历物理设备时,通过这个检查函数对每个设备进行检查:

然后在CheckPhysicalDevice(VkPhysicalDevice device)函数里进行检测。如果检测成功就用成员变量记录下这个物理设备。

在CheckPhysicalDevice函数中需要做几件事。在这里先检查队列家族特性(自己直译),所以在SceneWidget.h头文件中,添加bool CheckQueue(VkPhysicalDevice device)。然后在CheckPhysicalDevice函数中进行调用。

在CheckQueue函数中检查队列特性:

先要检查一上物理设备的查询队列家族特性。必须支持图形和在surface上进行显示。查询队列家族特性的函数vkGetPhysicalDeviceQueueFamilyProperties与vkEnumeratePhysicalDevices用法类似。

第一次调用查询特性的数目,可以用uint32_t queueFamilyCount = 0;来记录下这个数目。

vkGetPhysicalDeviceQueueFamilyProperties函数第一个参数为device,要查找的设备指针。第二个参数为queueFamilyCount的引用,返回设备支持的队列的数目。第三个参数这里设为nulllptr,这个参数是要返回支持的队列的数组。

设备动支持的特性在vulkan中对应的是VkQueueFamilyProperties结构。所以可以用一个std::vector<VkQueueFamilyProperties> queueFamilies来保存获取到的队列。

VkQueueFamilyProperties结构的成员:

VkQueueFlags  queueFlags;标志这个队列的特性如:VK_QUEUE_GRAPHICS_BIT或是VK_QUEUE_COMPUTE_BIT等等。

uint32_t  queueCount;在同一特性中可以创建此类队列的个数。

uint32_t  timestampValidBits;时间戳。36到64位,也可以是0,表示不支持时间戳。

VkExtent3D  minImageTransferGranularity;操作队列时,支持图像传输的最小粒度。

第二次调用,根据特性的数目来获取支持的特性数组。所以前两个参数与第一次调用时一致。第三个参数为queueFamilies.data()。

然后对特性再进行遍历,检查是否支持VK_QUEUE_GRAPHICS_BIT特性:

这里的m_index = i是一个int类型的值,是记录下这个特性在特性列表中的索引,这个索引将来还要用,所以先记录一下。

查询是否支持surface显示,要用到vkGetPhysicalDeviceSurfaceSupportKHR函数。其参数也好理解,第一个参数是当前的物理设备device,第二个参数是当前的特性列表的索引,第三个参数是需要支持的surface指针。第四个参数返回是否支持是一个bool变量的引用。

查找特性时,有可能会出现多个设备都支持所需要的特性。这时,需要返回第一个支持的设备就行。除非是有显卡需要显示场景,在这里只需要一个就可以了,所以直接返回。

再检查物理设备对扩展的支持,在SceneWidget.h中添加bool CheckDeviceExtensionSupport(VkPhysicalDevice device)。

可以通过函数vkEnumerateDeviceExtensionProperties来获取物理设备支持的扩展。也是需要调用两次。一次是获取支持的扩展的数目,一闪根据数目获取支持的扩展的名称。

其用法就不再细述。在获取到支持的扩展列表后,通过名称来找有没有与VK_KHR_SWAPCHAIN_EXTENSION_NAME宏定义相同的字符串。如果有则说明支持,如果没有则不支持。VK_KHR_SWAPCHAIN_EXTENSION_NAME在vulkan_core.h文件中有定义,其实就是"VK_KHR_swapchain"。

再检查一下物理设备对Swapchain(交换链)的支持。

获取物理设备对surface相关的支持,在SceneWidget.h中添加 void CheckDeviceSupportSurface(VkPhysicalDevice device)。在这个函数里执行以下的操作:

可以通过函数vkGetPhysicalDeviceSurfaceCapabilitiesKHR来获取,其参数:

第一个参数为物理设备指针。第二个参数为之前创建的m_surface。第三个参数为VkSurfaceCapabilitiesKHR结构的引用,将查询的结构保存在这个结构中。结构中的数据为:

uint32_t  minImageCount; 交换链中最小的图像数据。

uint32_t  maxImageCount; 交换链中最大的图像数据。

VkExtent2D  currentExtent; 当前的宽高。

VkExtent2D  minImageExtent; 最小图像的宽高。

VkExtent2D  maxImageExtent; 最大图像的宽高。

uint32_t  maxImageArrayLayers; 最大的图层数。

VkSurfaceTransformFlagsKHR  supportedTransforms; 所支持的显示时的转换,是VkCompositeAlphaFlagBitsKHR的掩码,如:                  VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR、K_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR等

VkSurfaceTransformFlagBitsKHR  currentTransform;表示当前的转换

VkCompositeAlphaFlagsKHR  supportedCompositeAlpha;表示surface上的合成模式,可以通过alpha的值实现不透明的组合,如果图像没有alpha,则默认为1.0。

VkImageUsageFlags  supportedUsageFlags;支持的显示模式,是VkPresentModeKHR的掩码。

m_capabilities记录了支持的surface的能力。

然后再取物理设备支持的surface的像素格式,通过vkGetPhysicalDeviceSurfaceFormatsKHR来获取,同样需要调用两次,一次取支持的像素模式的数目,一次取支持的像素的格式内容。将获取的内容记录在VkSurfaceFormatKHR的列表m_formats里。

还要再取一下支持的surface的显示方式:

在CheckPhysicalDevice函数里,调用CheckQueue之后,调用上面添加的两个函数,并在最后检查物理设备是否支持采样的向异性。

最终CheckPhysicalDevice函数的样子如下:

在FindPhysicalDevice函数里,作完之前的检查后需要判断一下m_physicalDevice这个成员是否为空。

在创建RenderPass和pipeline时,还要用到最大采样样本数,正好在这里查这句询一下,并记录下来,之后再用时,可以直接用就行。在这里用一个成员变量来记录这个值VkSampleCountFlagBits m_msaa = VK_SAMPLE_COUNT_1_BIT。

在头文件中再添加一个获取最大采样样本数的函数:

VkSampleCountFlagBits GetMaxUsableSampleCount()。查询选中的物理设备支持的最大采样样本数。

VkSampleCountFlagBits是定义在vulkan_core.h中的。所以还要在头文件中包含vulkan_core.h头文件。

最大采样样本数可以在vulkan的VkPhysicalDeviceProperties结构中有保存,可以通过vkGetPhysicalDeviceProperties函数去查询。

vkGetPhysicalDeviceProperties的第一个参数为要查询的物理设备,此时已经保存在m_physicalDevice成员中。第二个参数为VkPhysicalDeviceProperties结构的指针(对象的引用)。

VkPhysicalDeviceProperties结构的成员

uint32_t  apiVersion;vulkan设备支持的vulkan的版本。

uint32_t  driverVersion;设备的驱动版本

uint32_t  vendorID;物理设备的供应商的唯一标识

uint32_t  deviceID;供应商提供的物理设备的唯一标识

VkPhysicalDeviceType  deviceType;指定设备类型

char  deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];设备名称

uint8_t  pipelineCacheUUID[VK_UUID_SIZE];pipeline的uuid

VkPhysicalDeviceLimits  limits;物理设备的限制

VkPhysicalDeviceSparseProperties  sparseProperties;设备支持的各种稀疏属性

获取到最大采样样本数后,将color的与depth的比较取小的那个。

现在所有的检查已完毕,可以创建逻辑设备了。在CreateDevice函数中,先调用FindPhysicalDevice,进行检查,再去创建逻辑设备。

和之前一样,不知道该怎么做时,就先写创建逻辑设备的函数,然后补全参数,vkCreateDevice函数,其参数:

VkPhysicalDevice  physicalDevice 物理设备

const VkDeviceCreateInfo*  pCreateInfo 创建逻辑设备所需要的信息,这里需要在vkCreateDevice的上面写VkDeviceCreateInfo的对象,并填充数据。

const VkAllocationCallbacks*  pAllocator 这个参数依然写为nullptr。

VkDevice*  pDevice 生成的逻辑设备的对象指针。所以还需要在SceneWidget.h中添加一个VkDevice类型的m_deivce变量。

添加VkDeviceCreateInfo结构,变量为deviceCreateInfo,并初始化为{}。

VkStructureType  sType; 结构的类型,为VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO

const void*  pNext; 这个结构现不用扩展,所以为nullptr。

VkDeviceCreateFlags  flags; 此标记暂时写0。

uint32_t  queueCreateInfoCount;应该是写VkDeviceQueueCreateInfo数组的个数。

const VkDeviceQueueCreateInfo*  pQueueCreateInfos; VkDeviceQueueCreateInfo数组,所以在VkDeviceCreateInfo的上面再写一个VkDeviceQueueCreateInfo的结构。

uint32_t   enabledLayerCount; 需要打开的Layer的数目

const char* const*  ppEnabledLayerNames; 需要打开的Layer的名称

uint32_t   enabledExtensionCount;需要支持的扩展数目

const char* const*   ppEnabledExtensionNames;需要支持的扩展名称

const VkPhysicalDeviceFeatures*  pEnabledFeatures;要开启的Feature的结构指针,所以还要补一个VkPhysicalDeviceFeatures的结构。

再说VkDeviceQueueCreateInfo数组,之前检测时,检查过VK_QUEUE_GRAPHICS_BIT和对present的支持。所以这里,要创建图形队列和显示队列两个。即:

std::vector< VkDeviceQueueCreateInfo > queueCreateInfos;

对于VkDeviceQueueCreateInfo结构要填充的成员:

VkStructureType  sType; 值为:VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO

const void*  pNext; 值为nullptr;

VkDeviceQueueCreateFlags  flags; 保持默认

uint32_t   queueFamilyIndex; 这个是队列的索引,就是m_index的值。

uint32_t   queueCount; 值为queueCreateInfos.size()

const float*  pQueuePriorities; 是队列的优先级,一般是从0到1。由于只有一个队列,所以取0到1之间的值就行,这里任意取1.0。

由于两个队列的索引是一样的。所以queueCreateInfos中只记录一个队列的创建信息就行。

所以在VkDeviceCreateInfo结构中对应的成员应该是

queueCreateInfoCount = queueCreateInfos.size();

pQueueCreateInfos = queueCreateInfos.data();

再定义一个VkPhysicalDeviceFeatures deviceFeatures;变量,之前有检测对采样的支持,即:samplerAnisotropy属性为true;所以将deviceFeatures. samplerAnisotropy设置为true。

创建逻辑设备,对扩展的支持,其实在物理设备检测时也已经有了,当时只检测了VK_KHR_SWAPCHAIN_EXTENSION_NAME,所以:

最后还有检验层,这个其实也可以不加。只是如果不加的话,调试程序很不方便。所以可以先加上。之前创建VkInstance对象时,用了一个bool的变量来决定加或不加。现在也可以使用一个变量来决定加不加。最主要的是,真正应用时,可以通过配置文件、上层调用传参、宏定义等方式来设置逻辑设备是否开启校验层。

最后判断vkCreateDevice函数的返回值,并在SceneWidget::UnInit函数中添加清理函数:

创建完成后,顺便把那两个队列创建出来,并保存在m_graphicsQueue和m_presentQueue:

然后在SceneWidget::Init中调用一下,看看有没有报错。

执行:

没有报错:) 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值