Reputation: 111
I'm creating a Vulkan Renderer and setting up a Vulkan device and it's corresponding queues. Previously I haven't had any problems creating queues since I was only creating one, but now that I am creating several of them (one for graphics, one for compute, and one for transfer) the validation layer is throwing this error after/during device creation:
VUID-VkDeviceCreateInfo-queueFamilyIndex-00372(ERROR / SPEC): msgNum: 0 - vkCreateDevice: pCreateInfo->pQueueCreateInfos[1].queueFamilyIndex (=0) is not unique within pCreateInfo->pQueueCreateInfos array. The Vulkan spec states: (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkDeviceCreateInfo-queueFamilyIndex-00372)
Objects: 1
[0] 0x16933778a10, type: 2, name: NULL
VUID-VkDeviceCreateInfo-queueFamilyIndex-00372(ERROR / SPEC): msgNum: 0 - vkCreateDevice: pCreateInfo->pQueueCreateInfos[2].queueFamilyIndex (=0) is not unique within pCreateInfo->pQueueCreateInfos array. The Vulkan spec states: (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkDeviceCreateInfo-queueFamilyIndex-00372)
Objects: 1
[0] 0x16933778a10, type: 2, name: NULL
VUID-VkDeviceCreateInfo-queueFamilyIndex-00372(ERROR / SPEC): msgNum: 0 - CreateDevice(): pCreateInfo->pQueueCreateInfos[1].queueFamilyIndex (=0) is not unique within pQueueCreateInfos. The Vulkan spec states: (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkDeviceCreateInfo-queueFamilyIndex-00372)
Objects: 1
[0] 0x16933778a10, type: 3, name: NULL
VUID-VkDeviceCreateInfo-queueFamilyIndex-00372(ERROR / SPEC): msgNum: 0 - CreateDevice(): pCreateInfo->pQueueCreateInfos[2].queueFamilyIndex (=0) is not unique within pQueueCreateInfos. The Vulkan spec states: (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkDeviceCreateInfo-queueFamilyIndex-00372)
Objects: 1
[0] 0x16933778a10, type: 3, name: NULL
And the VkResult from vkCreateDevice is VK_ERROR_INITIALIZATION_FAILED.
My three queues are created like this(this is non standard code, it's wrapped in my own structure):
GraphicsQueueInfo.QueueFlag = VK_QUEUE_GRAPHICS_BIT;
GraphicsQueueInfo.QueuePriority = 1.0f;
ComputeQueueInfo.QueueFlag = VK_QUEUE_COMPUTE_BIT;
ComputeQueueInfo.QueuePriority = 1.0f;
TransferQueueInfo.QueueFlag = VK_QUEUE_TRANSFER_BIT;
TransferQueueInfo.QueuePriority = 1.0f;
The QueueFlag member is used to determine the type of queue we want to make from it. This is later used in the queue selection function(here's a snippet)
uint8 i = 0;
while (true)
{
if ((queueFamilies[i].queueCount > 0) && (queueFamilies[i].queueFlags & _QI.QueueFlag))
{
break;
}
i++;
}
QueueCreateInfo.queueFamilyIndex = i;
It seems all queues end up having the same queueFamilyIndex(which is set from i) and that causes the error, but I don't know if I'm doing something wrong.
The vulkan-1.dll also crashes when vkGetDeviceQueue is called after the failed device cretion happens.
Upvotes: 3
Views: 1732
Reputation: 29240
In your second block you're populating QueueCreateInfo.queueFamilyIndex
(I assume of type VkDeviceQueueCreateInfo) with i, based on the first queue you find with _QI.QueueFlag
.
I also assume you're calling this block in some kind of loop in order to get queues for graphics, compute and transfer. So let's suppose your block up there is in a function called findQueueFamilyIndex(...)
and that you're calling it something like this....
std::vector<VkDeviceQueueCreateInfo> deviceQueueInfos;
deviceQueueInfos.push_back({});
findQueueFamilyIndex(VK_QUEUE_GRAPHICS_BIT, deviceQueueInfos.back());
deviceQueueInfos.push_back({});
findQueueFamilyIndex(VK_QUEUE_COMPUTE_BIT, deviceQueueInfos.back());
deviceQueueInfos.push_back({});
findQueueFamilyIndex(VK_QUEUE_TRANSFER_BIT, deviceQueueInfos.back());
The problem here is that you will almost certainly get the same queue family index for all three queues here, and that's illegal to request. Every graphics queue must support compute and transfer operations, so your loop of
if ((queueFamilies[i].queueCount > 0) && (queueFamilies[i].queueFlags & _QI.QueueFlag))
{
break;
}
is a bad way to pick a queue family index. What you want is the queue family index of the queue that has a given flag, and as few other flags as possible. Something like this:
uint32_t targetIndex = UINT32_MAX;
uint32_t targetFlags = 0xFFFFFFFF;
for (uint32_t i = 0; i < queueFamilyCount; ++i) {
// doesn't have the flag? ignore this
if (0 == (queueFamilies[i].queueFlags & _QI.QueueFlag)) {
continue;
}
// first matching queue? use it and continue
if (targetIndex == UINT32_MAX) {
targetIndex = i;
targetFlags = queueFamilies[i].queueFlags;
continue;
}
// Matching queue, but with fewer flags than the current best? Use it.
if (countBits(queueFamilies[i].queueFlags) < countBits(targetFlags)) {
targetIndex = i;
targetFlags = queueFamilies[i].queueFlags;
continue;
}
}
If you want N queues from a given queue family, you must specify the family ONCE, and say you want N queues. So if you want multiple queues for different kinds of work, the best way to approach it is to first find the best queue family index for a given kind of work. This will be the one with the least number of VkQueueFlagBits
other than the one you're requesting. For instance, an nVidia RTX 2080 has 3 queue families. One is dedicated to compute, one is dedicated to transfer, and one supports all 3.
So lets assume you write a function that takes a list of queue families and returns the best family index for a given queue:
uint32_t findBestQeueue(
VkQueueFlags desiredFlags,
const std::vector<VkQueueFamilyProperties>& queueFamilies)
{ ... }
Then what you can do is something like this:
std::vector<VkQueueFamilyProperties> qfps;
... populate qfps using vkGetPhysicalDeviceQueueFamilyProperties ...
std::map<uint32_t, uint32_t> queueFamilyToQueueCount;
auto qfi = findBestQeueue(VK_QUEUE_TRANSFER_BIT, qfps);
queueFamilyToQueueCount[qfi] += 1;
qfi = findBestQeueue(VK_QUEUE_COMPUTE_BIT, qfps);
queueFamilyToQueueCount[qfi] += 1;
qfi = findBestQeueue(VK_QUEUE_TRANSFER_BIT, qfps);
queueFamilyToQueueCount[qfi] += 1;
Now you have a map of queue family indices to the count of queues you need for them. You can then turn that into a std::vector<VkDeviceQueueCreateInfo>
which can then be used to populate the appropriate members of VkDeviceCreateInfo
.
Note this is not a perfect way of grabbing queues. For instance it's possible that there will only be one single queue family with one queue... in which case this code would fail because it's requesting 3 queues from a family with only one available, but for most hardware this should get your past this particular failure.
Upvotes: 2