着色器是处理光线、像素、几何图形和纹理等视觉数据以产生特定渲染效果的 GPU 程序。着色器通过称为资源绑定的过程查找必要的数据。CPU 代码会编排 GPU 资源 (例如纹理和内存缓冲区) 的创建,然后仔细安排着色器代码,以便通过绑定协议访问这些代码。
Vulkan 中的新描述符堆功能从头开始重构了这一过程,解决了用户的长期反馈问题,从而简化了资源绑定,并使其在 Direct3D 12 (D3D12) 中的工作方式更加一致。描述符堆可直接控制描述符内存管理,更适合现代硬件,并可简化资源管理的性能优化。它们对于使用动态纹理索引的渲染器、复杂的光线追踪着色器或支持 D3D12 的共享后端尤为有用。本文重点介绍添加的描述符堆、它们与描述符集的比较,以及如何开始。
描述符堆 API 由 VK_EXT_descriptor_heap 定义,Khronos 为此发布了 参考手册 和 使用指南。EXT 前缀表示扩展程序已获得跨供应商支持。Khronos 正在 积极寻求反馈 关于当前界面的反馈,目标是完善界面并将其推广到 KHR 级别的扩展程序。
NVIDIA 驱动 610 及更高版本为 VK_EXT_descriptor_heap 提供扩展到工具和代码示例的支持。NVIDIA Nsight Graphics 2026.2 通过相同的帧捕获和检查工作流引入了描述符堆支持,以快速缩小与描述符相关的问题。开源 descriptor_heap 示例应用是了解如何在实践中使用扩展程序的一个很好的起点。
如需试用,请下载最新版本的Nsight Graphics,并在“Help”(帮助)>“Samples”(示例)下查看描述符堆示例。请注意,此菜单仅在Windows上可用。在Linux上,您需要下载并构建示例。
图 1. descriptor_heap 示例,在一次调用中绘制 6x6x6 的立方体 (按脸纹理)
从描述符集迁移到堆有哪些好处?
描述符是供应商特定的字节,用于描述缓冲区和纹理等资源。例如,描述符可能由低级指针和一些元数据组成,但对于着色器而言,它只是一个不透明内存块。资源绑定过程主要包括管理描述符以及在 CPU 和着色器代码中访问这些描述符的方法。
Vulkan 最初仅提供描述符集,这些描述符集是可由不同着色器组成和重用的描述符数组。这些操作需要繁琐的描述符集布局、额外的分组和池分配。VK_EXT_descriptor_buffer 扩展程序为描述符引入了应用管理内存,但保留了基于集的绑定模型、布局和其他所需的基础架构层。
相比之下,VK_EXT_descriptor_heap 大大简化了概念,使其只在用户分配的内存“堆”中使用用户编写的描述符 (请勿与二进制堆数据结构混淆) 。一次只能绑定一个资源描述符堆,而且 Vulkan 文档建议在应用程序的整个生命周期中使用相同的堆,因为绑定新的堆会产生高昂的成本并影响性能。单独的堆专门用于采样器,遵循相同的准则。访问着色器中的描述符可以使用多种方法来完成,详情请参阅以下章节。
作为这种灵活性的回报,应用程序负责内存分配和布局。下文简要介绍了 API,包括与描述符集的比较。有关更完整的详细信息,请参阅 Descriptor Heap 指南。
如何使用描述符堆
准备好开始编码了吗?要使用描述符堆和支持的工具,您需要以下版本的软件:
- NVIDIA 驱动 610 或更高版本,适用于本文所述的所有内容。有时,Vulkan 开发者测试版驱动可能会抢先体验新的驱动级描述符堆更新。
- Vulkan 头文件 1.4.340 或更高版本 (包含在 Vulkan SDK 中)
- Nsight Graphics 2026.2
与任何 Vulkan 扩展程序一样,使用之前必须请求 VK_EXT_descriptor_heap 扩展程序。将 VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME 和 VkPhysicalDeviceDescriptorHeapFeaturesEXT 添加到 VkDeviceCreateInfo 扩展程序和 pNext 链中。
要在不进行任何修改的情况下使用现有着色器代码,可以在创建着色器时使用 VkShaderDescriptorSetAndBindingMappingInfoEXT 将描述符映射到设置和绑定索引。有关详细信息,请参阅将堆映射到现有着色器。以下两个示例也在descriptor_heap 示例中演示:
- 推送索引:将映射源设置为
VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_PUSH_INDEX_EXT后,从推送常量数据读取的值会偏移描述符位置。这允许应用为同一着色器的每次绘制选择不同的描述符范围。 - 常量偏移:更简单的是,
VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_CONSTANT_OFFSET_EXT只是为集/ 绑定描述符位置添加了一个常量偏移量。着色器可以始终为该绑定访问相同的描述符,或者如果绑定是数组或具有 bindingCount,则着色器可以动态地从偏移量中选择自己的索引。
| 描述子集 | 描述符堆 |
vkCreateDescriptorPool vkAllocateDescriptorSets |
VkBuffer with VkDeviceMemory 绑定和 VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT |
VkDescriptorSetLayoutBindingvkCreateDescriptorSetLayout |
VkShaderDescriptorSetAndBindingMappingInfoEXT |
vkUpdateDescriptorSetsvkCmdPushDescriptorSet |
vkWriteResourceDescriptorsEXT |
vkCmdPushConstants |
vkCmdPushDataEXT |
vkCmdBindDescriptorSets |
vkCmdBindSamplerHeapEXTvkCmdBindResourceHeapEXT |
在以下 GLSL 和语着色器示例中,整个堆已映射到具有零常数偏移量的绑定。然后,着色器可有效地对堆中的全局描述符进行索引。应用程序映射代码不仅指定了绑定集和位置,还指定了描述符的类型和步长细节,这些描述符必须与相应着色器代码中绑定的类型相匹配。
GLSL:
layout(set = 0, binding = 0)
uniform texture2D heapImages[];
layout(set = 0, binding = 1)
uniform sampler heapSamplers[];
layout(push_constant)
uniform PushConstants {
uint textureIndex;
} push;
layout(location = 0) in vec2 uv;
layout(location = 0) out vec4 outColor;
void main
{
outColor = texture(
sampler2D(
heapTextures[nonuniformEXT(push.textureIndex)],
heapSamplers[0]),
uv);
}
语着色语言:
[[vk::binding(0, 0)]] // (binding, set)
Texture2D<float4> heapImages[];
[[vk::binding(1, 0)]]
SamplerState heapSamplers[];
[[vk::push_constant]]
ConstantBuffer<PushConstants> push;[shader(“fragment”)]
float4 fragmentMain(float2 uv : TEXCOORD0) : SV_Target { return heapImages[NonUniformResourceIndex(push.textureIndex)].Sample( heapSamplers[0], uv); }
此组织可能看起来比较熟悉,特别是对于那些习惯使用 D3D12 描述符堆的人而言:
- 着色器可以动态索引描述符
- 索引由应用程序管理
- 堆是全局绑定状态
但是,Vulkan 与 D3D12 的区别有很多,包括应用程序为堆选择确切的内存位置,以及不同类型的描述符大小不同。
直接描述符堆访问
着色器可以直接从绑定资源和采样器描述符堆中索引描述符数组,而无需将描述符映射到集索引和绑定索引。这种方法意味着指针锯齿,需要仔细编制索引以进行有效访问,并且必须启用 VK_KHR_shader_untyped_pointers 扩展程序。如需了解详情,请参阅 Vulkan 指南的无类型着色器模型部分。
descriptor_heap 应用包含一个使用无类型指针的示例,并且还可与 Nsight Graphics 配合使用。此功能现已可供试用,但该功能是新功能,在着色器语言和工具链中仍处于成熟阶段。请持续关注更新。
检查和调试
在 Nsight Graphics 2026.2 中,描述符堆显示在开发者用于查看描述符集的同一着色器资源视图中。如果您使用 VkShaderDescriptorSetAndBindingMappingInfoEXT,则仅显示映射的描述符。
例如,请参见图 2 中一个立方体的六个纹理。要查看映射值,请单击 API 检查器中链接的工作流或着色器对象,然后在 Object Browser undercreateInfo > pNext > pMappings 中查看 (图 3) 。
图 2. Nsight Graphics:显示使用 vkCmdDrawIndexed 绘制立方体时的六个描述符堆映射图像
图 3. 可以在 Nsight Graphics 的“Object Browser” (对象浏览器) 窗口中检查 NV_skp_3 对象 (和 Vulkan 工作流对象) 的映射
在调试工具中捕获和回放描述符堆需要实现支持设备功能 VkPhysicalDeviceDescriptorHeapFeaturesEXT::descriptorHeapCaptureReplay。NVIDIA 驱动从驱动版本 610 开始增加了对此功能的支持。
如果这是您第一次使用 Nsight Graphics,则需先创建一个 Capture,然后使用图形调试器和实时回放对其进行检查:
- 单击“Start Activity” (开始活动)
- 单击对话框窗口中的“Launch Graphics Capture” (启动图形截取)
- 按 F11 或单击应用程序窗口中的 Capture 按钮以创建 Capture 文件,其中包含用于渲染帧的所有 Vulkan API 调用
- 终止应用
- 返回 Nsight Graphics,您应该会看到捕获文件文档。在“document” (文档) 选项卡中,单击“Start Graphics Debugger” (启动图形调试器)
- 在窗口顶部的工具栏中,单击“Start Live Replay” (开始实时回放)
- 在 Graphics Debugger (图形调试器) 下探索窗口。在左侧列出的“Events” (事件) 的“Filter” (筛选) 文本框中输入“draw” (绘制) ,以隔离绘图调用。然后单击“FS” ( FS) 检查片段着色器 API 状态
开始使用描述符堆
描述符堆可与 NVIDIA 驱动和开发者工具配合使用。了解描述符堆的最佳方法是开始使用它们进行构建。
- 运行并检查示例:最简单的方法是在“Help” (帮助) >“Samples” (示例) 下运行 Nsight Graphics 随附的示例
- 本地构建和调试:有关构建
descriptor_heap示例的说明可在其存储库中找到:NV_skp_1 的说明位于 https://github.com/nvpro-samples/vk_mini_samples/ - 提供反馈: 对于 Nsight Graphics 问题,请使用“Help” (帮助) >“Send Feedback” (发送反馈) 。有关
vk_mini_samples实现的问题,请打开 GitHub 问题。欢迎就实际使用和采用情况提供反馈,您可以在 NVIDIA 开发者论坛 上进行讨论,也可以在 Khronos Vulkan-Docs 资源库中发布反馈。
随着使用模式的成熟和着色语言支持的不断发展,请留意未来的更新。