vulakn教程--Drawing a Triangle--Set up--Physical Device and Queue Family
来源:互联网 发布:微信自媒体 知乎 编辑:程序博客网 时间:2024/05/19 06:37
原文链接:Vulkan-tutorial
Physical Device and Queue
好了,我们已经用VkInstance初始化了Vulkan API,是时候选择一个具有我们需要的特性的显卡了(graphics card),事实上,我们可以同时使用多个显卡,为了简单起见,我们只选择第一个满足我们要求的显卡。
VkPhysicalDevice physicalDevice=Vk_NULL_HANDLE; //声明
VkPhysicalDevice 将同Instance一同销毁,这里不必使用VDeleter。
首先我们要考虑两个问题:
- 如何获取Physical Devices。
- 如何从Physical Devices 中挑选我们想要的那个Physical Device.
如何获取Physical Devices
Vulkan 提供了枚举(enumerate)出当前平台(platform)可用的所有显卡(graphics card or Physical Device)的简便方法:
VkResult vkEnumeratePhysicalDevices( VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);
这种模式你绝对不会陌生,在前一章节我们寻找Validation layers时就已经领略过,当时是这样的: vkEnumerateInstanceLayerProperties(..)
。 现在我们用相似的方法来搜集所有的Physical Devices:
uint32_t deviceCount = 0;vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);if (deviceCount == 0) { throw std::runtime_error("failed to find GPUs with Vulkan support!");}std::vector<VkPhysicalDevice> devices(deviceCount);vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
现在我们已经得到了所有Physical Devices, 接下来我们挑选一个满足我们具体需求的显卡。
如何从Physical Devices 中挑选我们想要的那个Physical Device
首先我们需要引入几个重要的概念:
(1) VkPhysicalDeviceProperties
(显卡的属性)
typedef struct VkPhysicalDeviceProperties { uint32_t apiVersion; 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]; VkPhysicalDeviceLimits limits; VkPhysicalDeviceSparseProperties sparseProperties;} VkPhysicalDeviceProperties;
好复杂的结构,还好目前我们只对它的VkPhysicalDeviceType
deviceType
字段感兴趣。现在让我们看看VkPhysicalDeviceType
到底是个啥:
typedef enum VkPhysicalDeviceType { VK_PHYSICAL_DEVICE_TYPE_OTHER = 0, //other VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1, //集成 VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2, //独立 VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3, //虚拟 VK_PHYSICAL_DEVICE_TYPE_CPU = 4, //running on cpu} VkPhysicalDeviceType
你一定不会对获取vkGetPhysicalDeviceProperties
的方法感到陌生:
void vkGetPhysicalDeviceProperties( VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties);
(2) VkPhysicalDeviceFeatures
( 特性支持 ):
typedef struct VkPhysicalDeviceFeatures { VkBool32 robustBufferAccess; VkBool32 fullDrawIndexUint32; VkBool32 imageCubeArray; VkBool32 independentBlend; VkBool32 geometryShader; VkBool32 tessellationShader; VkBool32 sampleRateShading; VkBool32 dualSrcBlend; VkBool32 logicOp; VkBool32 multiDrawIndirect; VkBool32 drawIndirectFirstInstance; VkBool32 depthClamp; ... ...} VkPhysicalDeviceFeatures;
这是个庞大(我用…表示它还有很多字段)但简单的结构,每个字段都是bool
型,非真(Vk_TRUE
)即假(Vk_FALSE
)。表示是否对此特性的支持,如果你想了解完整的信息请参考相关文档,毕竟这里只是个栗子。
获取vkGetPhysicalDeviceFeatures的方法:
void vkGetPhysicalDeviceFeatures( VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures* pFeatures);
(3) VkQueueFamilyProperties
(队列家族属性)
你会在很多地方看到队列的身影。在Vulkan中,队列有很多种类(感觉family 译成种类好理解),每种队列只支持Vulkan命令的一个子集,比如:一种队列只具有处理计算的命令(processing of compute commands) 或者只具有内存传递的命令(memory transfer related commands)。我们将从Physical Device里枚举出它所拥有的所有队列(VkQueue
)的种类,并从中抽取出我们感兴的那种队列,或者说我们要通过判断Physical Device 是否支持我们感兴趣的队列来对Physical Device 进行筛选。
typedef struct VkQueueFamilyProperties { VkQueueFlags queueFlags; // or VkQueueFlagBits uint32_t queueCount; uint32_t timestampValidBits; VkExtent3D minImageTransferGranularity;} VkQueueFamilyProperties;
同样为了简单,我们只考虑queueFlags
和queueCount
这两个字段。需要注意的是queueFlags
的类型在VkQueueFamilyProperties
的定义中是VkQueueFlags
,它的值属于VkQueueFlagBits
,结构如下:
typedef enum VkQueueFlagBits { VK_QUEUE_GRAPHICS_BIT = 0x00000001, VK_QUEUE_COMPUTE_BIT = 0x00000002, VK_QUEUE_TRANSFER_BIT = 0x00000004, VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008, VK_QUEUE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF} VkQueueFlagBits;
采用同样的模式来遍历制定Physical Device的 VkQueueFamilyProperties
:
void vkGetPhysicalDeviceQueueFamilyProperties( VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties);
挑选Physical Device
所有的准备工作都完成的差不多了,现在开始挑选满足我们需求的Physical Device 流程。
模拟需求:
我们需要 deviceType
为VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
类型的显卡,并且支持geometry shaders(后续会讲到) 特性,即VkPhysicalDeviceFeatures. geometryShader
为Vk_TRUE
,此外我们想队列支持图形处理命令,即VkQueueFamilyProperties . queueFlags
为VK_QUEUE_GRAPHICS_BIT
。
总结如下:
1. 显卡类型为VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
。
2. 特性支持geometry shaders。
3. 队列支持图形处理命令。
举个例子,我们有一个挑选函数,它满足了我们对显卡类型和对geometryShader
特性的支持:
bool isDeviceSuitable(VkPhysicalDevice device) { VkPhysicalDeviceProperties deviceProperties; VkPhysicalDeviceFeatures deviceFeatures; vkGetPhysicalDeviceProperties(device, &deviceProperties); vkGetPhysicalDeviceFeatures(device, &deviceFeatures); return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && deviceFeatures.geometryShader;}
你也可以采用下面一个比较优雅的方法,它对Physical Device进行打分,然后取得最高分的那个:
#include <map>...void pickPhysicalDevice() { ... // Use an ordered map to automatically sort candidates by increasing score std::map<int, VkPhysicalDevice> candidates; for (const auto& device : devices) { int score = rateDeviceSuitability(device); candidates[score] = device; } // Check if the best candidate is suitable at all if (candidates.begin()->first > 0) { physicalDevice = candidates.begin()->second; } else { throw std::runtime_error("failed to find a suitable GPU!"); }}int rateDeviceSuitability(VkPhysicalDevice device) { ... int score = 0; // Discrete GPUs have a significant performance advantage if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { score += 1000; } // Maximum possible size of textures affects graphics quality score += deviceProperties.limits.maxImageDimension2D; // Application can't function without geometry shaders if (!deviceFeatures.geometryShader) { return 0; } return score;}
当然,我们并不打算在这个教程中使用这个方式,我们的目的仅在于为你提供另一条挑选显卡的思路,使你明确条条大路皆通罗马。
下面我们来添加另一条限制条件:队列支持图形处理命令。
为了更好的说明问题,我们来添加一个便利的结构:
struct QueueFamilyIndices { int graphicsFamily = -1; bool isComplete() { return graphicsFamily >= 0; }};
各个字段的含义都非常明确,如果找到这样的队列graphicsFamily
就为这种队列的索引(还记得vkGetPhysicalDeviceQueueFamilyProperties(…)
传入的pQueueFamilyPropertyCount
参数吗,graphicsFamily
与此参数关联),否则为-1.
我们添加findQueueFamilies(…)
方法,用来寻找片特定命令的队列:
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { QueueFamilyIndices indices; uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); int i = 0; for (const auto& queueFamily : queueFamilies) { if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { indices.graphicsFamily = i; } if (indices.isComplete()) { break; } i++; } return indices; }
然后再添加一个验证方法 isDeviceSuitable
(…):
bool isDeviceSuitable(VkPhysicalDevice device) { QueueFamilyIndices indices = findQueueFamilies(device); return indices.isComplete();}
把它们组合在一起看起来是这样的:
void pickPhysicalDevice() { uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); if (deviceCount == 0) { throw std::runtime_error("failed to find GPUs with Vulkan support!"); } std::vector<VkPhysicalDevice> devices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); for (const auto& device : devices) { if (isDeviceSuitable(device)) { physicalDevice = device; break; } } if (physicalDevice == VK_NULL_HANDLE) { throw std::runtime_error("failed to find a suitable GPU!"); } }
源码:
#define GLFW_INCLUDE_VULKAN#include <GLFW/glfw3.h>#include <iostream>#include <stdexcept>#include <functional>#include <vector>#include <cstring>const int WIDTH = 800;const int HEIGHT = 600;const std::vector<const char*> validationLayers = { "VK_LAYER_LUNARG_standard_validation"};#ifdef NDEBUGconst bool enableValidationLayers = false;#elseconst bool enableValidationLayers = true;#endifVkResult 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; }}void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); if (func != nullptr) { func(instance, callback, pAllocator); }}template <typename T>class VDeleter {public: VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} VDeleter(std::function<void(T, VkAllocationCallbacks*)> deletef) { this->deleter = [=](T obj) { deletef(obj, nullptr); }; } VDeleter(const VDeleter<VkInstance>& instance, std::function<void(VkInstance, T, VkAllocationCallbacks*)> deletef) { this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; } VDeleter(const VDeleter<VkDevice>& device, std::function<void(VkDevice, T, VkAllocationCallbacks*)> deletef) { this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; } ~VDeleter() { cleanup(); } T* operator &() { cleanup(); return &object; } operator T() const { return object; }private: T object{VK_NULL_HANDLE}; std::function<void(T)> deleter; void cleanup() { if (object != VK_NULL_HANDLE) { deleter(object); } object = VK_NULL_HANDLE; }};struct QueueFamilyIndices { int graphicsFamily = -1; bool isComplete() { return graphicsFamily >= 0; }};class HelloTriangleApplication {public: void run() { initWindow(); initVulkan(); mainLoop(); }private: GLFWwindow* window; VDeleter<VkInstance> instance{vkDestroyInstance}; VDeleter<VkDebugReportCallbackEXT> callback{instance, DestroyDebugReportCallbackEXT}; VDeleter<VkSurfaceKHR> surface{instance, vkDestroySurfaceKHR}; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; void initWindow() { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); } void initVulkan() { createInstance(); setupDebugCallback(); pickPhysicalDevice(); } void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } } void createInstance() { if (enableValidationLayers && !checkValidationLayerSupport()) { throw std::runtime_error("validation layers requested, but not available!"); } VkApplicationInfo appInfo = {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 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; VkInstanceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pApplicationInfo = &appInfo; auto extensions = getRequiredExtensions(); createInfo.enabledExtensionCount = extensions.size(); createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { createInfo.enabledLayerCount = 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!"); } } 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!"); } } void pickPhysicalDevice() { uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); if (deviceCount == 0) { throw std::runtime_error("failed to find GPUs with Vulkan support!"); } std::vector<VkPhysicalDevice> devices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); for (const auto& device : devices) { if (isDeviceSuitable(device)) { physicalDevice = device; break; } } if (physicalDevice == VK_NULL_HANDLE) { throw std::runtime_error("failed to find a suitable GPU!"); } } bool isDeviceSuitable(VkPhysicalDevice device) { QueueFamilyIndices indices = findQueueFamilies(device); return indices.isComplete(); } QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { QueueFamilyIndices indices; uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); int i = 0; for (const auto& queueFamily : queueFamilies) { if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { indices.graphicsFamily = i; } if (indices.isComplete()) { break; } i++; } return indices; } 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; } 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; } 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() { HelloTriangleApplication app; try { app.run(); } catch (const std::runtime_error& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS;}
- vulakn教程--Drawing a Triangle--Set up--Physical Device and Queue Family
- vulakn教程--Drawing a Triangle--Set up--Logical Device
- vulakn教程--Drawing a Triangle--Set up--Base code
- vulakn教程--Drawing a Triangle--Set up--Instance
- vulakn教程--Drawing a Triangle--Set up--Validation layers
- vulakn教程--Drawing a Triangle--Draw--Render and presentation
- vulakn教程--Drawing a Triangle--Presentation--SwapChain
- vulakn教程--Drawing a Triangle--Pipeline--Introduction
- vulakn教程--Drawing a Triangle--Draw--Framebuffer
- vulakn教程--Drawing a Triangle--Draw--CommandBuffer
- vulakn教程--Drawing a Triangle--Presentation--Window surface
- vulakn教程--Drawing a Triangle--Presentation-- Image views
- vulakn教程--Drawing a Triangle--Pipeline--Shader Module
- vulakn教程--Drawing a Triangle--Pipeline--Fixed function
- vulakn教程--Drawing a Triangle--Pipeline--Render passes
- DirectX 教程: DirectX Tutorial - Direct3D: Drawing a Triangle
- Set up and run a PBS job
- react native 错误:Make sure you have an Android emulator running or a device connected and have set up
- 【数据结构与算法】——插入排序
- 为什么我们要干一行爱一行
- 上拉电阻与下拉电阻的作用总结
- Codeforces #369 (Div. 2) E. ZS and The Birthday Paradox (勒让德定理+逆元)
- hiho1360 凸多边形
- vulakn教程--Drawing a Triangle--Set up--Physical Device and Queue Family
- 色彩构成与搭配——色彩的原理及分类
- 大数据聚类学习整理
- 05-高斯分布
- Archlinux 安装过程(UEFI+GRUB+GNOME)
- SQL简单函数
- Java反射初探
- 【u031】租用游艇
- Nginx虚拟主机及相关设置