-
Notifications
You must be signed in to change notification settings - Fork 455
Description
Environment:
- OS: Windows 11
- GPU and driver version: AMD Radeon RX 7900 XTX - Driver version 25.10.30.02
- SDK or header version if building from repo: Vulkan SDK 1.3.280.0
- Options enabled (synchronization, best practices, etc.): VK_LAYER_KHRONOS_validation (default settings)
Describe the Issue
Validation layer throws VUID-vkCmdDraw-None-04008 error for a pipeline that uses only gl_VertexIndex built-in (no vertex input attributes). The pipeline is correctly configured with vertexBindingDescriptionCount = 0 and vertexAttributeDescriptionCount = 0, and the SPIR-V shader contains no vertex input attribute declarations - only the gl_VertexIndex built-in.
The shader generates vertices procedurally for a fullscreen quad using:
const vec2 positions[4] = vec2[](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);The validation layer incorrectly interprets gl_VertexIndex as requiring a vertex buffer binding, when it is a built-in that does not require vertex buffers.
Expected behavior
No validation error should occur. The shader uses only gl_VertexIndex which is a built-in variable that:
- Does not require vertex buffer bindings
- Is provided automatically by Vulkan for each vertex in the draw call
- Is commonly used for procedural vertex generation, fullscreen quads, and post-processing
The Vulkan specification states that built-in inputs like gl_VertexIndex do not require vertex input bindings.
Valid Usage ID
Validation Error: [ VUID-vkCmdDraw-None-04008 ]
Object 0: handle = 0x2068b6e4740, type = VK_OBJECT_TYPE_COMMAND_BUFFER
Object 1: handle = 0xf0665500000017af, type = VK_OBJECT_TYPE_PIPELINE
MessageID = 0x255241c3
vkCmdDraw(): Vertex binding 0 is VK_NULL_HANDLE.
(Most likely you forgot to call vkCmdBindVertexBuffers).
The Vulkan spec states: If the nullDescriptor feature is not enabled,
all vertex input bindings accessed via vertex input variables declared
in the vertex shader entry point's interface must not be VK_NULL_HANDLE
Additional context
Vertex Shader Source
#version 450
// No vertex input attributes - generates vertices procedurally
layout(location = 0) out vec2 fragUV;
void main() {
// Generate fullscreen quad vertices using gl_VertexIndex
const vec2 positions[4] = vec2[](
vec2(-1.0, -1.0), // bottom-left
vec2( 1.0, -1.0), // bottom-right
vec2(-1.0, 1.0), // top-left
vec2( 1.0, 1.0) // top-right
);
const vec2 uvs[4] = vec2[](
vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(1.0, 1.0)
);
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
fragUV = uvs[gl_VertexIndex];
}Pipeline Configuration
// Vertex input state - explicitly configured with no vertex bindings
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0; // No bindings
vertexInputInfo.vertexAttributeDescriptionCount = 0; // No attributes
vertexInputInfo.pVertexBindingDescriptions = nullptr;
vertexInputInfo.pVertexAttributeDescriptions = nullptr;
// Input assembly
VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo{};
inputAssemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
inputAssemblyInfo.primitiveRestartEnable = VK_FALSE;
// Create pipeline
VkGraphicsPipelineCreateInfo pipelineInfo{};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssemblyInfo;
// ... other pipeline state ...
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);
// Draw call
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdDraw(commandBuffer, 4, 1, 0, 0); // ERROR OCCURS HERESPIR-V Verification (spirv-dis output excerpt)
OpEntryPoint Vertex %main "main" %_ %gl_VertexIndex %fragUV
; Only input is the built-in VertexIndex
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
; fragUV is an OUTPUT, not input
%fragUV = OpVariable %_ptr_Output_v2float Output
OpDecorate %fragUV Location 0
The SPIR-V contains NO vertex input attributes (no OpDecorate with Location on Input variables). Only gl_VertexIndex (a built-in) is present.
Runtime Verification Logs
[CRITICAL] === ABOUT TO CREATE OVERLAY PIPELINE ===
[CRITICAL] vertexInputInfo.vertexBindingDescriptionCount = 0
[CRITICAL] vertexInputInfo.vertexAttributeDescriptionCount = 0
[CRITICAL] vertexInputInfo.pVertexBindingDescriptions = 0
[CRITICAL] vertexInputInfo.pVertexAttributeDescriptions = 0
[CRITICAL] *** OVERLAY PIPELINE: 17322626475215558575 (0xf0665500000017af)
[CRITICAL] OVERLAY VERTEX SHADER LOADED
[CRITICAL] Size: 1440 bytes
[CRITICAL] SPIR-V magic number: 119734787 (0x07230203 - valid)
[DEBUG] VALIDATION LAYER: Validation Error: [ VUID-vkCmdDraw-None-04008 ]
Object 1: handle = 0xf0665500000017af, type = VK_OBJECT_TYPE_PIPELINE
vkCmdDraw(): Vertex binding 0 is VK_NULL_HANDLE.
The pipeline handle in the error (0xf0665500000017af) matches the overlay pipeline that was configured with 0 bindings.
Workaround
Binding a dummy null vertex buffer satisfies the validation layer:
VkBuffer nullBuffer = VK_NULL_HANDLE;
VkDeviceSize offset = 0;
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &nullBuffer, &offset);
vkCmdDraw(commandBuffer, 4, 1, 0, 0); // No error with dummy bindingAdditionally, if a different pipeline binds vertex buffers earlier in the command buffer, those bindings persist and the error does not occur, confirming the validation layer is checking for buffer bindings rather than shader requirements.
Root Cause
The validation layer appears to be incorrectly treating gl_VertexIndex (a built-in) as if it were a vertex input attribute requiring a vertex buffer binding. The VUID-vkCmdDraw-None-04008 check should distinguish between:
- Actual vertex input attributes (OpVariable with Input + Location decoration)
- Built-in inputs like
gl_VertexIndex,gl_InstanceIndex(OpVariable with Input + BuiltIn decoration)
Only the former should require vertex buffer bindings.