Quick answer: Bind all descriptor sets declared by the pipeline layout before draw. Use one bind call per set, matching the set indices.

A custom Vulkan renderer prints VUID-vkCmdDraw-None-08600: descriptor set was not bound. Draw is rejected on debug builds. The shader expects three sets; bind code only covers two.

Pipeline Layout Defines Required Sets

VkDescriptorSetLayout layouts[3] = { camera, material, lighting };
VkPipelineLayoutCreateInfo plInfo = {};
plInfo.setLayoutCount = 3;
plInfo.pSetLayouts = layouts;
vkCreatePipelineLayout(device, &plInfo, nullptr, &pipelineLayout);

This pipeline expects three sets: 0=camera, 1=material, 2=lighting.

Bind All Required Sets

VkDescriptorSet sets[3] = { cameraSet, materialSet, lightingSet };
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 3, sets, 0, nullptr);

vkCmdDraw(cmd, vertexCount, 1, 0, 0);

Or bind in groups:

vkCmdBindDescriptorSets(cmd, ..., pipelineLayout, 0, 2, perFrameSets, 0, nullptr);
vkCmdBindDescriptorSets(cmd, ..., pipelineLayout, 2, 1, &lightingSet, 0, nullptr);

firstSet parameter (3rd arg) lets you bind ranges starting from any set index. Useful for set 0 = per-frame, set 1 = per-pass, set 2 = per-object hierarchies.

Watch for Pipeline Switches

Switching to a different pipeline that uses fewer sets doesn’t unbind previous; switching to one with more sets requires rebinding the extra. Validation: if your incompatibility level dropped, sets above that level are invalidated.

Don't Free Mid-Frame

If you vkResetDescriptorPool while a recorded command buffer still has those sets bound, replay will crash. Use frame-in-flight indexing — one pool per frame, reset only when GPU has finished that frame.

Debug with Validation Layers

VK_LAYER_KHRONOS_validation = enabled
VK_EXT_debug_utils for message callback

Run with validation layers in dev. Errors print to your debug callback with set/binding context. Promote validation errors to debug breaks during development.

Verifying

RenderDoc capture: inspect each draw’s “descriptor sets” tab. All slots required by pipeline layout filled. No “not bound” warnings. Game runs without validation messages.

“Vulkan demands explicitness. Bind every set, every frame, until you build a smarter caching layer.”

For shipping, build a descriptor manager that tracks dirty state and rebinds only on change — saves CPU time per draw call without sacrificing correctness.