Por qué los Descriptor Sets siempre fueron un dolor de cabeza
Si ya tocaste Vulkan en serio, te sabes el ritual de memoria: creas VkDescriptorPool, alocas VkDescriptorSet, escribes VkDescriptorSetLayout, te memorizas qué set index va con qué binding, y llamas vkCmdBindDescriptorSets en cada draw. Funciona — pero es demasiada ceremonia para decir "aquí están unos punteros a mis texturas".
VK_EXT_descriptor_heap ataca esto de frente. En lugar de sets, layouts y pools, tienes un solo buffer alocado por la aplicación que guarda descriptores crudos. Los shaders indexan directo ahí y el lado CPU solo escribe bytes. Si ya programaste D3D12, vas a sentir un déjà vu — y esa es la idea.
La extensión está definida por Khronos como EXT (cross-vendor), con manual de referencia y guía de uso ya publicados. Drivers NVIDIA 610+ ya la soportan, y Nsight Graphics 2026.2 agrega inspección de heaps al flujo de captura que ya conoces. Si te late este rollo de automatización declarativa, checa nuestro caso sobre cómo el sistema Honk de Spotify automatizó 240 migraciones de datasets — el mismo principio de cambiar boilerplate imperativo por binding declarativo.

El cambio de API: Sets → Heaps
Mira el resumen del cambio mental en una tabla:
| Descriptor Sets | Descriptor Heaps |
|---|---|
vkCreateDescriptorPool + vkAllocateDescriptorSets | VkBuffer con VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT |
VkDescriptorSetLayoutBinding + vkCreateDescriptorSetLayout | VkShaderDescriptorSetAndBindingMappingInfoEXT |
vkUpdateDescriptorSets / vkCmdPushDescriptorSet | vkWriteResourceDescriptorsEXT |
vkCmdPushConstants | vkCmdPushDataEXT |
vkCmdBindDescriptorSets | vkCmdBindSamplerHeapEXT / vkCmdBindResourceHeapEXT |
Solo un resource heap y un sampler heap pueden estar bound al mismo tiempo. La doc de Vulkan recomienda bindear una vez y mantenerlo toda la vida de la app — rebindear es caro.
Mapeando el heap a tus shaders existentes
No tienes que reescribir tu GLSL para usar heaps. VkShaderDescriptorSetAndBindingMappingInfoEXT te deja mapear regiones del heap a los slots tradicionales de set/binding. Dos patrones comunes:
- Push Index (
VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_PUSH_INDEX_EXT): la posición del descriptor se desplaza por un valor leído de los push constants. Ideal para rangos de descriptores por-draw. - Constant Offset (
VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_CONSTANT_OFFSET_EXT): solo suma un offset fijo. El shader elige su propio índice si el binding es un array.
Un fragment shader GLSL mínimo indexando texturas mapeadas al heap:
// Heap mapeado a set=0, binding=0 (texturas) y binding=1 (samplers)
// con offset constante cero. El shader indexa el heap globalmente.
layout(set = 0, binding = 0)
uniform texture2D heapTextures[];
layout(set = 0, binding = 1)
uniform sampler heapSamplers[];
layout(push_constant)
uniform PushConstants {
uint textureIndex; // definido por la app en cada draw
} push;
layout(location = 0) in vec2 uv;
layout(location = 0) out vec4 outColor;
void main() {
// nonuniformEXT le avisa al driver que el índice puede variar por pixel
outColor = texture(
sampler2D(
heapTextures[nonuniformEXT(push.textureIndex)],
heapSamplers[0]),
uv);
}
En Slang es casi idéntico:
[[vk::binding(0, 0)]] // (binding, set)
Texture2D 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);
}
Acceso directo al heap (untyped pointers)
Un camino más agresivo: saltarte el mapeo set/binding e indexar el heap directo via untyped pointers. Esto requiere VK_KHR_shader_untyped_pointers y cuidado con los bounds — es aliasing de punteros, así que un índice malo = comportamiento indefinido. El sample descriptor_heap en el repo de NVIDIA lo demuestra y Nsight lo puede inspeccionar. Ya se puede usar, pero todavía está madurando entre los lenguajes de shader.

Limitaciones y cosas a las que poner ojo
Descriptor heaps no es comida gratis. Algunas aristas filosas antes de refactorizar tu renderer:
- La memoria es tu responsabilidad. Sin pool, sin allocator, sin red de seguridad. Si dos tipos de descriptor tienen tamaños distintos (y sí los tienen, en algunos vendors), tú manejas el stride.
- Un heap a la vez. Rebindear es caro. Si tu engine cambia de contexto de descriptor varias veces por frame, los heaps pueden no convenir.
- Untyped pointers todavía madurando. El soporte de toolchain varía entre GLSL, HLSL y Slang. Verifica antes de apostar.
- Capture/replay requiere opt-in del driver.
VkPhysicalDeviceDescriptorHeapFeaturesEXT::descriptorHeapCaptureReplaydebe estar soportado — NVIDIA 610+ sí, pero drivers viejos y otros vendors quizá todavía no. - Vulkan ≠ D3D12. El modelo mental se acerca, pero la colocación de memoria la controla la app y los tamaños de descriptor no son uniformes. No portes código D3D12 en automático.
Debugeando en Nsight Graphics 2026.2
Los heaps aparecen en el mismo panel de Shader Resource Views que ya usas para descriptor sets. Si estás usando VkShaderDescriptorSetAndBindingMappingInfoEXT, solo aparecen los descriptores mapeados. Para ver los valores de mapeo: da clic en el pipeline o shader object en el API Inspector, y mira en createInfo > pNext > pMappings en el Object Browser.
Flujo típico de captura:
- Start Activity → Launch Graphics Capture
- F11 en la app para capturar un frame
- Cierra la app, da clic en Start Graphics Debugger en el documento de captura
- Start Live Replay, filtra eventos escribiendo "draw", da clic en FS para inspeccionar el fragment shader
Si ya le entras a escribir tests automatizados para código gráfico, la misma disciplina aplica — checa nuestra guía de testing end-to-end con Claude Code y Playwright para patrones que se transfieren bien a testing de regresión en GPU.

Próximos pasos
Los descriptor heaps ya son usables en producción hoy en hardware NVIDIA con driver 610+, Vulkan Headers 1.4.340+ y Nsight Graphics 2026.2. La forma más rápida de agarrar la intuición es correr el sample:
- Correrlo desde Nsight Graphics:
Help > Samples > descriptor heap(solo en el menú de Windows; en Linux, buildéalo del source). - Buildear local: el sample
descriptor_heapestá en nvpro-samples/vk_mini_samples. - Dar feedback: usa
Help > Send Feedbacken Nsight, abre un issue en el repo de samples, o postea en el repositorio Khronos Vulkan-Docs.
Para dónde ir desde aquí:
- Porta un draw path de descriptor sets a heap y mide. La ganancia aparece más rápido en indexación dinámica de texturas y shaders de ray tracing.
- Si mantienes un backend D3D12, unifica la capa de abstracción sobre semántica de heap — la paridad es el payoff a largo plazo.
- Ponle ojo a la promoción
EXT→KHR. Khronos está buscando feedback activamente, así que es ahora el momento de moldear la API final.
La era de los descriptor sets no se acabó, pero para renderers nuevos apuntando a hardware moderno, los heaps son la dirección que el ecosistema está tomando. Empieza chico, mide, y deja que el boilerplate muera. 🚀