You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复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,但代码未过滤重复)。

修改思路

  1. 收集所有需要的队列族索引,确保每个索引只保留一次
  2. 同一个队列族仅创建一次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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 04:40:43