Por que Descriptor Sets sempre foram um pé no saco

Se você já mexeu com Vulkan de verdade, conhece o ritual: cria VkDescriptorPool, aloca VkDescriptorSet, escreve VkDescriptorSetLayout, decora mentalmente qual set index mapeia pra qual binding, e chama vkCmdBindDescriptorSets em cada draw. Funciona — mas é muita cerimônia pra dizer "aqui estão uns ponteiros pras minhas texturas".

O VK_EXT_descriptor_heap ataca exatamente isso. Em vez de sets, layouts e pools, você tem um único buffer alocado pela aplicação que guarda descritores crus. Os shaders indexam direto nele, e o lado da CPU só escreve bytes. Se você já programou D3D12, vai sentir um déjà vu — e é essa a ideia.

A extensão é definida pela Khronos como EXT (cross-vendor), com manual de referência e guia de uso já publicados. Drivers NVIDIA 610+ já suportam, e o Nsight Graphics 2026.2 adiciona inspeção de heaps no fluxo de captura que você já conhece. Se você curte esse tipo de automação declarativa, dá uma olhada no nosso case sobre como o Honk do Spotify automatizou 240 migrações de dataset — o mesmo princípio de trocar boilerplate imperativo por binding declarativo.

근거자료: NVIDIA Developer Blog — Streamlining Resource Binding with End-to-End Support for Vulkan Descriptor Heaps

Developer workstation with GPU debugger showing Vulkan descriptor heap memory layout for shader resource binding System Abstract Visual

A mudança de API: Sets → Heaps

Olha só o resumo da mudança mental em uma tabela:

Descriptor SetsDescriptor Heaps
vkCreateDescriptorPool + vkAllocateDescriptorSetsVkBuffer com VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT
VkDescriptorSetLayoutBinding + vkCreateDescriptorSetLayoutVkShaderDescriptorSetAndBindingMappingInfoEXT
vkUpdateDescriptorSets / vkCmdPushDescriptorSetvkWriteResourceDescriptorsEXT
vkCmdPushConstantsvkCmdPushDataEXT
vkCmdBindDescriptorSetsvkCmdBindSamplerHeapEXT / vkCmdBindResourceHeapEXT

um resource heap e um sampler heap podem estar bound ao mesmo tempo. A doc do Vulkan recomenda bindar uma vez e manter durante toda a vida da aplicação — rebindar é caro.

Mapeando o heap pros shaders existentes

Você não precisa reescrever seu GLSL pra usar heaps. O VkShaderDescriptorSetAndBindingMappingInfoEXT permite mapear regiões do heap nos slots tradicionais de set/binding. Dois padrões comuns:

  • Push Index (VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_PUSH_INDEX_EXT): a posição do descritor é deslocada por um valor lido dos push constants. Perfeito pra ranges de descritores por-draw.
  • Constant Offset (VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_CONSTANT_OFFSET_EXT): só adiciona um offset fixo. O shader escolhe seu próprio índice se o binding for um array.

Um fragment shader GLSL mínimo indexando texturas mapeadas no heap:

// Heap mapeado em set=0, binding=0 (texturas) e binding=1 (samplers)
// com offset constante zero. O shader indexa o 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 pela aplicação a cada draw
} push;

layout(location = 0) in vec2 uv;
layout(location = 0) out vec4 outColor;

void main() {
    // nonuniformEXT avisa o driver que o índice pode variar por pixel
    outColor = texture(
        sampler2D(
            heapTextures[nonuniformEXT(push.textureIndex)],
            heapSamplers[0]),
        uv);
}

Em Slang é praticamente igual:

[[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);
}

Acesso direto ao heap (untyped pointers)

Um caminho mais agressivo: pular o mapeamento set/binding e indexar o heap direto via untyped pointers. Isso exige VK_KHR_shader_untyped_pointers e cuidado com bounds — é aliasing de ponteiro, então índice errado = comportamento indefinido. O sample descriptor_heap no repo da NVIDIA demonstra isso e o Nsight consegue inspecionar. Já dá pra usar, mas ainda está amadurecendo entre as linguagens de shader.

Close-up of code editor displaying GLSL shader with descriptor heap indexing and nonuniformEXT texture access Dev Environment Setup

Limitações e coisas pra ficar de olho

Descriptor heaps não é almoço grátis. Algumas arestas afiadas antes de refatorar seu renderer:

  • A memória é sua responsabilidade. Sem pool, sem allocator, sem rede de segurança. Se dois tipos de descritor têm tamanhos diferentes (e têm, em alguns vendors), você cuida do stride.
  • Um heap por vez. Rebindar é caro. Se sua engine troca de contexto de descritor várias vezes por frame, heaps podem não valer a pena.
  • Untyped pointers ainda amadurecendo. Suporte de toolchain varia entre GLSL, HLSL e Slang. Verifica antes de apostar.
  • Capture/replay exige opt-in do driver. VkPhysicalDeviceDescriptorHeapFeaturesEXT::descriptorHeapCaptureReplay precisa ser suportado — NVIDIA 610+ suporta, mas drivers antigos e outros vendors talvez ainda não.
  • Vulkan ≠ D3D12. O modelo mental é mais próximo, mas o posicionamento de memória é controlado pela app e os tamanhos de descritor não são uniformes. Não porta código D3D12 no automático.

Debugando no Nsight Graphics 2026.2

Os heaps aparecem no mesmo painel de Shader Resource Views que você já usa pra descriptor sets. Se você está usando VkShaderDescriptorSetAndBindingMappingInfoEXT, só os descritores mapeados aparecem. Pra ver os valores de mapeamento: clica no pipeline ou shader object no API Inspector, e olha em createInfo > pNext > pMappings no Object Browser.

Fluxo típico de captura:

  1. Start ActivityLaunch Graphics Capture
  2. F11 no app pra capturar um frame
  3. Fecha o app, clica em Start Graphics Debugger no documento de captura
  4. Start Live Replay, filtra eventos digitando "draw", clica em FS pra inspecionar o fragment shader

Se você já curte escrever testes automatizados pra código gráfico, a mesma disciplina vale aqui — dá uma olhada no nosso guia de testes end-to-end com Claude Code e Playwright pra padrões que transferem bem pra testes de regressão em GPU.

Nsight Graphics frame capture window inspecting Vulkan descriptor heap bindings during live replay of a rendered scene IT Technology Image

Próximos passos

Descriptor heaps já são usáveis em produção hoje em hardware NVIDIA com driver 610+, Vulkan Headers 1.4.340+ e Nsight Graphics 2026.2. O jeito mais rápido de criar intuição é rodar o sample:

  • Rodar pelo Nsight Graphics: Help > Samples > descriptor heap (só no menu do Windows; no Linux, builda do source).
  • Buildar local: o sample descriptor_heap está em nvpro-samples/vk_mini_samples.
  • Dar feedback: usa Help > Send Feedback no Nsight, abre uma issue no repo dos samples, ou posta no repositório Khronos Vulkan-Docs.

Pra onde ir daqui:

  1. Porta um draw path de descriptor sets pra heap e mede. O ganho aparece mais rápido em indexação dinâmica de texturas e shaders de ray tracing.
  2. Se você mantém um backend D3D12, unifica a camada de abstração em cima de semântica de heap — a paridade é o payoff de longo prazo.
  3. Fica de olho na promoção EXTKHR. A Khronos está ativamente buscando feedback, então é agora a hora de moldar a API final.

A era dos descriptor sets não acabou, mas pra renderers novos mirando hardware moderno, heaps são a direção que o ecossistema está tomando. Começa pequeno, mede, e deixa o boilerplate morrer. 🚀

Este conteúdo foi elaborado com o auxílio de ferramentas de IA, com base em fontes confiáveis, e revisado pela nossa equipe editorial antes da publicação. Não substitui o aconselhamento de um profissional especializado.