如何修复Vulkan的VUID-VkDeviceCreateInfo-queueFamilyIndex-02802验证错误
问题:Vulkan队列族重复选取触发VUID-VkDeviceCreateInfo-queueFamilyIndex-02802错误
问题背景
部分显卡队列族数量不足时,代码重复选取同一queueFamilyIndex创建队列,触发Vulkan验证错误VUID-VkDeviceCreateInfo-queueFamilyIndex-02802。
错误输出
Validation Error: [ VUID-VkDeviceCreateInfo-queueFamilyIndex-02802 ] Object 0: handle = 0x1088d50, type = VK_OBJECT_TYPE_PHYSICAL_DEVICE; | MessageID = 0x29498778 | CreateDevice(): pCreateInfo->pQueueCreateInfos[2].queueFamilyIndex (=1) is not unique and was also used in pCreateInfo->pQueueCreateInfos[1]. The Vulkan spec states: The queueFamilyIndex member of each element of pQueueCreateInfos must be unique within pQueueCreateInfos, except that two members can share the same queueFamilyIndex if one describes protected-capable queues and one describes queues that are not protected-capable
系统环境
shadoww@xddz ------------ OS: Debian GNU/Linux 11 (bullseye) x86_64 Host: Aspire A315-21G V1.12 Kernel: 5.10.0-18-amd64 Uptime: 1 day, 13 hours, 37 mins Packages: 2926 (dpkg) Shell: zsh 5.8 Resolution: 1366x768 WM: bspwm Theme: Adwaita-One-Dark [GTK2/3] Icons: hicolor [GTK2/3] Terminal: alacritty Terminal Font: Iosevka CPU: AMD A9-9420 RADEON R5 2C+3G (2) @ 3.000GHz GPU: AMD ATI Radeon R5 M230 / R7 M260DX / Radeon 520 Mobile GPU: AMD ATI Radeon R2/R3/R4/R5 Graphics Memory: 5397MiB / 7410MiB
VkQueueFamilyProperties信息
VkQueueFamilyProperties: ======================== queueProperties[0]: ------------------- minImageTransferGranularity = (1,1,1) queueCount = 1 queueFlags = QUEUE_GRAPHICS | QUEUE_COMPUTE | QUEUE_TRANSFER | QUEUE_SPARSE_BINDING timestampValidBits = 64 present support = true queueProperties[1]: ------------------- minImageTransferGranularity = (1,1,1) queueCount = 4 queueFlags = QUEUE_COMPUTE | QUEUE_TRANSFER | QUEUE_SPARSE_BINDING timestampValidBits = 64 present support = true
相关代码片段(./engine/src/renderer/vulkan/vulkan_device.c)
/* avoid creating additional queues for shared indices */ b8 present_shares_graphics_queue = (context->device.graphics_queue_index == context->device.present_queue_index); b8 transfer_shares_graphics_queue = (context->device.graphics_queue_index == context->device.transfer_queue_index); u32 index_count = 1; if (!present_shares_graphics_queue) { index_count++; } if (!transfer_shares_graphics_queue) { index_count++; } u32 indices[index_count]; u8 index = 0; indices[index++] = context->device.graphics_queue_index; if (!present_shares_graphics_queue) { indices[index++] = context->device.present_queue_index; } if (!transfer_shares_graphics_queue) { indices[index++] = context->device.transfer_queue_index; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * */ VkDeviceQueueCreateInfo queue_create_infos[index_count]; for (u32 i = 0; i < index_count; ++i) { queue_create_infos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_create_infos[i].queueFamilyIndex = indices[i]; queue_create_infos[i].queueCount = 1; /* TODO: Enable this for a future enhancement. */ /* if (indices[i] == context->device.graphics_queue_index) { */ /* queue_create_infos[i].queueCount = 2; */ /* } */ queue_create_infos[i].flags = 0; queue_create_infos[i].pNext = 0; f32 queue_priority = 1.0f; queue_create_infos[i].pQueuePriorities = &queue_priority; } /* request device features */ /* TODO: configuration driven */ VkPhysicalDeviceFeatures device_features = {0}; device_features.samplerAnisotropy = VK_TRUE; /* request anisotropy */ /* * * * * * * * * * * * * */ /* create device */ VkDeviceCreateInfo device_create_info = {VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO}; device_create_info.queueCreateInfoCount = index_count; device_create_info.pQueueCreateInfos = queue_create_infos; device_create_info.pEnabledFeatures = &device_features; device_create_info.enabledExtensionCount = 1; const char * extension_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME; device_create_info.ppEnabledExtensionNames = &extension_names; /* deprecated and ignored */ device_create_info.enabledLayerCount = 0; device_create_info.ppEnabledLayerNames = 0; /* * * * * * * * * * * * * */ /* failure */ VK_CHECK(vkCreateDevice(context->device.physical_device, &device_create_info, context->allocator, &context->device.logical_device)); M_INFO("Successfully created logical device."); /* * * * * * * * */
修复方案
问题根源是代码仅检查了传输/呈现队列是否与图形队列复用,未考虑传输队列和呈现队列之间的复用情况(比如当前场景中,呈现队列和传输队列都使用了index=1,但代码未过滤重复)。
修改思路
- 收集所有需要的队列族索引,确保每个索引只保留一次
- 同一个队列族仅创建一次
VkDeviceQueueCreateInfo,后续直接从该队列族获取不同用途的队列(图形、呈现、传输可共享同一队列族的队列)
具体代码修改
// 收集需要的唯一队列族索引 u32 indices[3]; u32 index_count = 0; // 添加图形队列索引(必选) indices[index_count++] = context->device.graphics_queue_index; // 添加呈现队列索引,仅当未在已收集列表中时加入 b8 present_already_added = false; for (u32 i = 0; i < index_count; ++i) { if (indices[i] == context->device.present_queue_index) { present_already_added = true; break; } } if (!present_already_added) { indices[index_count++] = context->device.present_queue_index; } // 添加传输队列索引,仅当未在已收集列表中时加入 b8 transfer_already_added = false; for (u32 i = 0; i < index_count; ++i) { if (indices[i] == context->device.transfer_queue_index) { transfer_already_added = true; break; } } if (!transfer_already_added) { indices[index_count++] = context->device.transfer_queue_index; } // 创建队列创建信息,每个唯一索引只创建一次 VkDeviceQueueCreateInfo queue_create_infos[index_count]; for (u32 i = 0; i < index_count; ++i) { queue_create_infos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_create_infos[i].queueFamilyIndex = indices[i]; queue_create_infos[i].queueCount = 1; queue_create_infos[i].flags = 0; queue_create_infos[i].pNext = 0; f32 queue_priority = 1.0f; queue_create_infos[i].pQueuePriorities = &queue_priority; } // 后续device_create_info部分保持不变
额外说明
- 创建逻辑设备后,获取队列时直接根据对应的
queueFamilyIndex获取即可,同一队列族可获取多个队列(只要该队列族的queueCount足够) - 比如当前场景中,队列族1有4个队列,可分别获取一个用于呈现、一个用于传输,无需重复创建队列族的创建信息
内容的提问来源于stack exchange,提问作者mango
相关产品推荐
相关产品推荐

