2026/3/9 17:30:15
网站建设
项目流程
江西省水利水电建设集团招标网站,神马收录提交入口,全国企业信用信息查询,优秀包装设计案例欢迎回来#xff01;上一集我们成功建立了与 Vulkan 驱动的“外交关系”#xff08;Instance#xff09;。今天#xff0c;我们要进入实质性的阶段#xff1a;挑选我们要用的显卡。
在 OpenGL 中#xff0c;你没得选#xff0c;系统给你什么就是什么。但在 Vulkan 中上一集我们成功建立了与 Vulkan 驱动的“外交关系”Instance。今天我们要进入实质性的阶段挑选我们要用的显卡。在 OpenGL 中你没得选系统给你什么就是什么。但在 Vulkan 中你可以遍历电脑上所有的 GPU比如你的笔记本可能同时有 Intel 核显和 NVIDIA 独显然后根据显存大小、特性支持甚至名字来“钦定”一个用来渲染。这一步我们称之为选择Physical Device (物理设备)。1. 物理设备 (VkPhysicalDevice)首先我们在HelloVulkanApp类中添加一个成员变量来存储我们要用的显卡句柄。// 物理显卡句柄注意这个对象会在 Instance 销毁时自动失效不需要我们手动 vkDestroy VkPhysicalDevice physicalDevice VK_NULL_HANDLE;挑选显卡的逻辑我们需要编写一个pickPhysicalDevice函数。逻辑依然是标准的 Vulkan 流程问 Vulkan 有多少个显卡获取显卡列表。遍历列表找到一个符合我们要求的比如支持图形渲染。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::vectorVkPhysicalDevice 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!); } }把这个函数加到initVulkan中setupDebugMessenger的后面。2. 核心难点队列族 (Queue Families)那么什么叫“符合我们要求 (isDeviceSuitable)”的显卡这就要引出 Vulkan 的指挥系统了。在 Vulkan 里所有的命令比如“画个三角形”、“把图片存入显存”都不是直接执行的而是提交给队列 (Queue)执行的。但是不同的队列有不同的特长。显卡内部就像一个大工厂分成了不同的部门队列族Graphics Queue Family:专门负责画图的部门。Compute Queue Family:专门负责通用计算的部门。Transfer Queue Family:专门负责搬运数据的部门。我们需要找的显卡必须至少包含一个支持 Graphics图形操作的队列族。否则这显卡只能拿来挖矿计算不能拿来玩游戏画图。使用std::optional管理索引因为队列族的索引是非负整数为了区分“没找到”和“找到了索引为0的队列”我们引入 C17 的神器std::optional。记得在文件头部添加#include optional定义一个结构体来存放我们要找的队列族索引struct QueueFamilyIndices { // std::optional 就像一个盒子可能里面有值(int)也可能是空的 std::optionaluint32_t graphicsFamily; bool isComplete() { return graphicsFamily.has_value(); } };3. 寻找队列族 (findQueueFamilies)现在我们来写寻找队列族的逻辑。这需要调用vkGetPhysicalDeviceQueueFamilyProperties。QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { QueueFamilyIndices indices; uint32_t queueFamilyCount 0; vkGetPhysicalDeviceQueueFamilyProperties(device, queueFamilyCount, nullptr); std::vectorVkQueueFamilyProperties queueFamilies(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties(device, queueFamilyCount, queueFamilies.data()); int i 0; for (const auto queueFamily : queueFamilies) { // 检查这个队列族是否支持 Graphics 操作 if (queueFamily.queueFlags VK_QUEUE_GRAPHICS_BIT) { indices.graphicsFamily i; } if (indices.isComplete()) { break; } i; } return indices; }4. 完善检查函数 (isDeviceSuitable)回到之前的检查函数现在我们可以利用刚才写的findQueueFamilies来判断显卡是否合格了。bool isDeviceSuitable(VkPhysicalDevice device) { // 1. 这一步是必须的确保显卡有图形队列 QueueFamilyIndices indices findQueueFamilies(device); return indices.isComplete(); // 扩展知识在这里你其实还可以查询显卡的名字、显存大小等 // VkPhysicalDeviceProperties deviceProperties; // vkGetPhysicalDeviceProperties(device, deviceProperties); // std::cout Checking GPU: deviceProperties.deviceName std::endl; }5. 运行测试此时你的main.cpp结构应该大致如下新增了struct QueueFamilyIndices。HelloVulkanApp类中有了pickPhysicalDevice、isDeviceSuitable和findQueueFamilies。initVulkan顺序Instance - DebugMessenger -pickPhysicalDevice。#define GLFW_INCLUDE_VULKAN #include GLFW/glfw3.h #include iostream #include stdexcept #include vector #include cstring #include cstdlib #include optional const uint32_t WIDTH 800; const uint32_t HEIGHT 600; const std::vectorconst char* validationLayers { VK_LAYER_KHRONOS_validation }; #ifdef NDEBUG const bool enableValidationLayers false; #else const bool enableValidationLayers true; #endif VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) { auto func (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, vkCreateDebugUtilsMessengerEXT); if (func ! nullptr) { return func(instance, pCreateInfo, pAllocator, pDebugMessenger); } else { return VK_ERROR_EXTENSION_NOT_PRESENT; } } void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) { auto func (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, vkDestroyDebugUtilsMessengerEXT); if (func ! nullptr) { func(instance, debugMessenger, pAllocator); } } struct QueueFamilyIndices { std::optionaluint32_t graphicsFamily; bool isComplete() { return graphicsFamily.has_value(); } }; class HelloTriangleApplication { public: void run() { initWindow(); initVulkan(); mainLoop(); cleanup(); } private: GLFWwindow* window; VkInstance instance; VkDebugUtilsMessengerEXT debugMessenger; 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(); setupDebugMessenger(); pickPhysicalDevice(); } void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } } void cleanup() { if (enableValidationLayers) { DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr); } vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); glfwTerminate(); } 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 static_castuint32_t(extensions.size()); createInfo.ppEnabledExtensionNames extensions.data(); VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; if (enableValidationLayers) { createInfo.enabledLayerCount static_castuint32_t(validationLayers.size()); createInfo.ppEnabledLayerNames validationLayers.data(); populateDebugMessengerCreateInfo(debugCreateInfo); createInfo.pNext (VkDebugUtilsMessengerCreateInfoEXT*) debugCreateInfo; } else { createInfo.enabledLayerCount 0; createInfo.pNext nullptr; } if (vkCreateInstance(createInfo, nullptr, instance) ! VK_SUCCESS) { throw std::runtime_error(failed to create instance!); } } void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT createInfo) { createInfo {}; createInfo.sType VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; createInfo.messageSeverity VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; createInfo.messageType VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; createInfo.pfnUserCallback debugCallback; } void setupDebugMessenger() { if (!enableValidationLayers) return; VkDebugUtilsMessengerCreateInfoEXT createInfo; populateDebugMessengerCreateInfo(createInfo); if (CreateDebugUtilsMessengerEXT(instance, createInfo, nullptr, debugMessenger) ! VK_SUCCESS) { throw std::runtime_error(failed to set up debug messenger!); } } 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::vectorVkPhysicalDevice 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::vectorVkQueueFamilyProperties queueFamilies(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties(device, queueFamilyCount, queueFamilies.data()); int i 0; for (const auto queueFamily : queueFamilies) { if (queueFamily.queueFlags VK_QUEUE_GRAPHICS_BIT) { indices.graphicsFamily i; } if (indices.isComplete()) { break; } i; } return indices; } std::vectorconst char* getRequiredExtensions() { uint32_t glfwExtensionCount 0; const char** glfwExtensions; glfwExtensions glfwGetRequiredInstanceExtensions(glfwExtensionCount); std::vectorconst char* extensions(glfwExtensions, glfwExtensions glfwExtensionCount); if (enableValidationLayers) { extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } return extensions; } bool checkValidationLayerSupport() { uint32_t layerCount; vkEnumerateInstanceLayerProperties(layerCount, nullptr); std::vectorVkLayerProperties 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(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { std::cerr validation layer: pCallbackData-pMessage std::endl; return VK_FALSE; } }; int main() { HelloTriangleApplication app; try { app.run(); } catch (const std::exception e) { std::cerr e.what() std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; }运行程序依然是黑窗口别急我们在搭地基。但如果程序没有抛出 failed to find a suitable GPU! 异常恭喜你你的代码成功识别了你的显卡并确认了它拥有绘图能力。给 Windows 用户的特别提示如果你是双显卡笔记本Intel 集显 NVIDIA 独显目前的逻辑 break 会导致它选中列表中第一个合格的显卡。通常驱动程序会把独显排在前面或者是集显。在本教程的简单场景下集显和独显都能跑。但如果你想强制选独显可以在 isDeviceSuitable 里加一个判断deviceProperties.deviceType VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU。下一步选好了物理设备我们还不能直接指挥它。我们需要基于这个物理设备创建一个Logical Device (逻辑设备)并从队列族中把那个我们要用的Queue (队列)拿出来。下一篇我们将完成设备初始化的最后一步真正的获得显卡的控制权详见Physical devices and queue families - Vulkan Tutorial