物理设备
通过 VkInstance
初始化 Vulkan 库后,我们需要查找并在系统中选择支持我们所需功能的显卡。可以选择任意数量的显卡并同时使用。这里简单需求就选择第一张显卡。
void CVulkanApp::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& dev : devices)
{
if (isDeviceSuitable(dev))
{
physicalDevice_ = dev;
break;
}
}
if (physicalDevice_ == VK_NULL_HANDLE)
throw std::runtime_error("Failed to find a suitable GPU!");
}
这个函数就是设置物理设备的过程,在initVulkan里初始化完成VkInstance后创建物理设备。增加类成员 VkPhysicalDevice physicalDevice_ = VK_NULL_HANDLE;
当枚举完整设备列表信息后,要选择合适的显卡设备。假设我们认为我们的应用程序只能用于专用支持几何着色器的显卡。如下判断:
bool CVulkanApp::isDeviceSuitable(VkPhysicalDevice dev)
{
VkPhysicalDeviceProperties deviceProperties;
VkPhysicalDeviceFeatures deviceFeatures;
vkGetPhysicalDeviceProperties(dev, &deviceProperties);
vkGetPhysicalDeviceFeatures(dev, &deviceFeatures);
return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
&& deviceFeatures.geometryShader;
}
队列族
几乎在Vulkan的每一次执行, 从绘图到上传纹理,任何事情都需要提交命令到队列。有不同类型的队列源自不同的队列系列,每个队列系列只允许子集的命令。可能有一个队列系列只允许处理计算命令或仅允许内存传输相关命令的命令。我们需要检查设备支持哪些队列系列以及哪些队列系列 中支持我们想要使用的命令。
定义一个结构QueueFamilyIndices 里面两个参数分别是:
graphicsFamily 存储支持图形操作的队列族索引.
presentFamily存储支持呈现到屏幕的队列族索引.
struct QueueFamilyIndices {
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
bool isComplete() {
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
QueueFamilyIndices CVulkanApp::findQueueFamilies(VkPhysicalDevice dev)
{
QueueFamilyIndices indices;
uint32_t count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(dev, &count, nullptr);
std::vector<VkQueueFamilyProperties> families(count);
vkGetPhysicalDeviceQueueFamilyProperties(dev, &count, families.data());
for (uint32_t i=0; i<count; ++i)
{
if (families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
indices.graphicsFamily = i;
}
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, surface_, &presentSupport);
if (presentSupport)
{
indices.presentFamily = i;
}
if (indices.isComplete())
{
break;
}
}
return indices;
}
这样物理设备的选择以及队列族准备好以后,就可以创建逻辑设备了。