窗口已经创建完成win32的surface接下来就是创建逻辑设备了。在SceneWidget的头文件中再添加一个bool CreateDevice()的函数用来创建逻辑设备。创建逻辑设备一般要分两步走。第一步查找物理设备先要找到可用的物理设备也就是显卡才能根据物理设备创建逻辑设备。所以在头文件中再添加一个查找的函数bool FindPhysicalDevice()。和添加一个物理设备的成员变量用来记录所选择的物理设备VkPhysicalDevice m_physicalDevice VK_NULL_HANDLE。1罗列出当前可用的物理设备。可以通过vkEnumeratePhysicalDevices函数来获取当前的物理设备的信息。其用法与查找实例支持的Layer的特性函数vkEnumerateInstanceLayerProperties类似。先调用一次vkEnumeratePhysicalDevices第一个参数传入之前创建好的实例m_instance。第二个参数为返回的当前可用的物理设备的数目所以传一个uint类型的变量deviceCount的引用。第三个参数传入nullptr。这次的调用只是获取当前可用设备的数目。查检deviceCount的值是否为0uint类型不会小于0 。如果deviceCount的值合法再声明一个用于存放物理设备的列表变量可以在声明时进行初始化列表的数目初始化为deviceCount即 std::vectorVkPhysicalDevice 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::vectorVkQueueFamilyProperties 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的uuidVkPhysicalDeviceLimits 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_INFOconst 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_INFOconst 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中调用一下看看有没有报错。执行没有报错 。
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的值是否为0uint类型不会小于0 。如果deviceCount的值合法再声明一个用于存放物理设备的列表变量可以在声明时进行初始化列表的数目初始化为deviceCount即 std::vectorVkPhysicalDevice 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::vectorVkQueueFamilyProperties 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的uuidVkPhysicalDeviceLimits 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_INFOconst 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_INFOconst 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中调用一下看看有没有报错。执行没有报错 。