vulakn教程--Drawing a Triangle--Set up--Physical Device and Queue Family
原文链接:Vulkan-tutorial
Physical Device and Queue
好了,我们已经用VkInstance初始化了Vulkan API,是时候选择一个具有我们需要的特性的显卡了(graphics card),事实上,我们可以同时使用多个显卡,为了简单起见,我们只选择第一个满足我们要求的显卡。
1 VkPhysicalDevice physicalDevice=Vk_NULL_HANDLE;  //声明
VkPhysicalDevice 将同Instance一同销毁,这里不必使用VDeleter。
首先我们要考虑两个问题:
- 如何获取Physical Devices。
 - 如何从Physical Devices 中挑选我们想要的那个Physical Device.
 
如何获取Physical Devices
Vulkan 提供了枚举(enumerate)出当前平台(platform)可用的所有显卡(graphics card or Physical Device)的简便方法:
1234 VkResult    vkEnumeratePhysicalDevices(    VkInstance                                 instance,    uint32_t*                                   pPhysicalDeviceCount,    VkPhysicalDevice*                           pPhysicalDevices);
这种模式你绝对不会陌生,在前一章节我们寻找Validation layers时就已经领略过,当时是这样的: vkEnumerateInstanceLayerProperties(..)。 现在我们用相似的方法来搜集所有的Physical Devices:
12345678 uint32_t deviceCount = 0;vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); if (deviceCount == 0) {    throw std::runtime_error("failed to find GPUs with Vulkan support!");}std::vector devices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
现在我们已经得到了所有Physical Devices, 接下来我们挑选一个满足我们具体需求的显卡。
如何从Physical Devices 中挑选我们想要的那个Physical Device
首先我们需要引入几个重要的概念: 
(1) VkPhysicalDeviceProperties (显卡的属性)
1 2 3 4 5 6 7 8 9 10 11  | 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 到底是个啥:
1234567 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的方法感到陌生:
1234 void vkGetPhysicalDeviceProperties(    VkPhysicalDevice            physicalDevice,    VkPhysicalDeviceProperties*       pProperties);
(2) VkPhysicalDeviceFeatures ( 特性支持 ):
12345678910111213141516 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的方法:
1234 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 进行筛选。
123456 typedef struct VkQueueFamilyProperties {    VkQueueFlags    queueFlags;  // or VkQueueFlagBits    uint32_t        queueCount;    uint32_t        timestampValidBits;    VkExtent3D      minImageTransferGranularity;} VkQueueFamilyProperties;
同样为了简单,我们只考虑queueFlags和queueCount这两个字段。需要注意的是queueFlags的类型在VkQueueFamilyProperties的定义中是VkQueueFlags,它的值属于VkQueueFlagBits,结构如下:
1234567 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:
1234 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特性的支持:
123456789 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进行打分,然后取得最高分的那个:
12345678910111213141516171819202122232425262728293031323334 #include ...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;}
当然,我们并不打算在这个教程中使用这个方式,我们的目的仅在于为你提供另一条挑选显卡的思路,使你明确条条大路皆通罗马。
下面我们来添加另一条限制条件:队列支持图形处理命令。 
为了更好的说明问题,我们来添加一个便利的结构:
1234567 struct QueueFamilyIndices {    int graphicsFamily = -1;     bool isComplete() {        return graphicsFamily >= 0;    }};
各个字段的含义都非常明确,如果找到这样的队列graphicsFamily就为这种队列的索引(还记得vkGetPhysicalDeviceQueueFamilyProperties(…)传入的pQueueFamilyPropertyCount参数吗,graphicsFamily与此参数关联),否则为-1. 
我们添加findQueueFamilies(…)方法,用来寻找片特定命令的队列:
12345678910111213141516171819 QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {        QueueFamilyIndices indices;        uint32_t queueFamilyCount = 0;        vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);        std::vector 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(…):
12345 bool isDeviceSuitable(VkPhysicalDevice device) {    QueueFamilyIndices indices = findQueueFamilies(device);     return indices.isComplete();}
把它们组合在一起看起来是这样的:
123456789101112131415161718 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 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!");        }    }
源码:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 #define GLFW_INCLUDE_VULKAN#include  #include #include #include #include #include  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;#endif VkResult 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 "">class VDeleter {public:    VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {}     VDeleter(std::function<void(t, vkallocationcallbacks*)=""> deletef) {        this->deleter = [=](T obj) { deletef(obj, nullptr); };    }     VDeleter(const VDeleter& instance, std::function< void(vkinstance, t,="" vkallocationcallbacks*)=""> deletef) {        this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); };    }     VDeleter(const VDeleter& 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 instance{vkDestroyInstance};     VDeleter callback{instance, DestroyDebugReportCallbackEXT};     VDeleter 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 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 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 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;}
                    