20. Fixed-Function Vertex Processing

Vertex fetching is controlled via configurable state, as a logically distinct graphics pipeline stage.

20.1. Vertex Attributes

Vertex shaders can define input variables, which receive vertex attribute data transferred from one or more VkBuffer(s) by drawing commands. Vertex shader input variables are bound to buffers via an indirect binding where the vertex shader associates a vertex input attribute number with each variable, vertex input attributes are associated to vertex input bindings on a per-pipeline basis, and vertex input bindings are associated with specific buffers on a per-draw basis via the vkCmdBindVertexBuffers command. Vertex input attribute and vertex input binding descriptions also contain format information controlling how data is extracted from buffer memory and converted to the format expected by the vertex shader.

There are VkPhysicalDeviceLimits::maxVertexInputAttributes number of vertex input attributes and VkPhysicalDeviceLimits::maxVertexInputBindings number of vertex input bindings (each referred to by zero-based indices), where there are at least as many vertex input attributes as there are vertex input bindings. Applications can store multiple vertex input attributes interleaved in a single buffer, and use a single vertex input binding to access those attributes.

In GLSL, vertex shaders associate input variables with a vertex input attribute number using the location layout qualifier. The component layout qualifier associates components of a vertex shader input variable with components of a vertex input attribute.

GLSL example
// Assign location M to variableName
layout (location=M, component=2) in vec2 variableName;

// Assign locations [N,N+L) to the array elements of variableNameArray
layout (location=N) in vec4 variableNameArray[L];

In SPIR-V, vertex shaders associate input variables with a vertex input attribute number using the Location decoration. The Component decoration associates components of a vertex shader input variable with components of a vertex input attribute. The Location and Component decorations are specified via the OpDecorate instruction.

SPIR-V example
               ...
          %1 = OpExtInstImport "GLSL.std.450"
               ...
               OpName %9 "variableName"
               OpName %15 "variableNameArray"
               OpDecorate %18 BuiltIn VertexIndex
               OpDecorate %19 BuiltIn InstanceIndex
               OpDecorate %9 Location M
               OpDecorate %9 Component 2
               OpDecorate %15 Location N
               ...
          %2 = OpTypeVoid
          %3 = OpTypeFunction %2
          %6 = OpTypeFloat 32
          %7 = OpTypeVector %6 2
          %8 = OpTypePointer Input %7
          %9 = OpVariable %8 Input
         %10 = OpTypeVector %6 4
         %11 = OpTypeInt 32 0
         %12 = OpConstant %11 L
         %13 = OpTypeArray %10 %12
         %14 = OpTypePointer Input %13
         %15 = OpVariable %14 Input
               ...

20.1.1. Attribute Location and Component Assignment

Vertex shaders allow Location and Component decorations on input variable declarations. The Location decoration specifies which vertex input attribute is used to read and interpret the data that a variable will consume. The Component decoration allows the location to be more finely specified for scalars and vectors, down to the individual components within a location that are consumed. The components within a location are 0, 1, 2, and 3. A variable starting at component N will consume components N, N+1, N+2, …​ up through its size. For single precision types, it is invalid if the sequence of components gets larger than 3.

When a vertex shader input variable declared using a scalar or vector 32-bit data type is assigned a location, its value(s) are taken from the components of the input attribute specified with the corresponding VkVertexInputAttributeDescription::location. The components used depend on the type of variable and the Component decoration specified in the variable declaration, as identified in Input attribute components accessed by 32-bit input variables. Any 32-bit scalar or vector input will consume a single location. For 32-bit data types, missing components are filled in with default values as described below.

Table 30. Input attribute components accessed by 32-bit input variables
32-bit data type Component decoration Components consumed

scalar

0 or unspecified

(x, o, o, o)

scalar

1

(o, y, o, o)

scalar

2

(o, o, z, o)

scalar

3

(o, o, o, w)

two-component vector

0 or unspecified

(x, y, o, o)

two-component vector

1

(o, y, z, o)

two-component vector

2

(o, o, z, w)

three-component vector

0 or unspecified

(x, y, z, o)

three-component vector

1

(o, y, z, w)

four-component vector

0 or unspecified

(x, y, z, w)

Components indicated by “o” are available for use by other input variables which are sourced from the same attribute, and if used, are either filled with the corresponding component from the input format (if present), or the default value.

When a vertex shader input variable declared using a 32-bit floating point matrix type is assigned a location i, its values are taken from consecutive input attributes starting with the corresponding VkVertexInputAttributeDescription::location. Such matrices are treated as an array of column vectors with values taken from the input attributes identified in Input attributes accessed by 32-bit input matrix variables. The VkVertexInputAttributeDescription::format must be specified with a VkFormat that corresponds to the appropriate type of column vector. The Component decoration must not be used with matrix types.

Table 31. Input attributes accessed by 32-bit input matrix variables
Data type Column vector type Locations consumed Components consumed

mat2

two-component vector

i, i+1

(x, y, o, o), (x, y, o, o)

mat2x3

three-component vector

i, i+1

(x, y, z, o), (x, y, z, o)

mat2x4

four-component vector

i, i+1

(x, y, z, w), (x, y, z, w)

mat3x2

two-component vector

i, i+1, i+2

(x, y, o, o), (x, y, o, o), (x, y, o, o)

mat3

three-component vector

i, i+1, i+2

(x, y, z, o), (x, y, z, o), (x, y, z, o)

mat3x4

four-component vector

i, i+1, i+2

(x, y, z, w), (x, y, z, w), (x, y, z, w)

mat4x2

two-component vector

i, i+1, i+2, i+3

(x, y, o, o), (x, y, o, o), (x, y, o, o), (x, y, o, o)

mat4x3

three-component vector

i, i+1, i+2, i+3

(x, y, z, o), (x, y, z, o), (x, y, z, o), (x, y, z, o)

mat4

four-component vector

i, i+1, i+2, i+3

(x, y, z, w), (x, y, z, w), (x, y, z, w), (x, y, z, w)

Components indicated by “o” are available for use by other input variables which are sourced from the same attribute, and if used, are either filled with the corresponding component from the input (if present), or the default value.

When a vertex shader input variable declared using a scalar or vector 64-bit data type is assigned a location i, its values are taken from consecutive input attributes starting with the corresponding VkVertexInputAttributeDescription::location. The locations and components used depend on the type of variable and the Component decoration specified in the variable declaration, as identified in Input attribute locations and components accessed by 64-bit input variables. For 64-bit data types, no default attribute values are provided. Input variables must not use more components than provided by the attribute. Input attributes which have one- or two-component 64-bit formats will consume a single location. Input attributes which have three- or four-component 64-bit formats will consume two consecutive locations. A 64-bit scalar data type will consume two components, and a 64-bit two-component vector data type will consume all four components available within a location. A three- or four-component 64-bit data type must not specify a component. A three-component 64-bit data type will consume all four components of the first location and components 0 and 1 of the second location. This leaves components 2 and 3 available for other component-qualified declarations. A four-component 64-bit data type will consume all four components of the first location and all four components of the second location. It is invalid for a scalar or two-component 64-bit data type to specify a component of 1 or 3.

Table 32. Input attribute locations and components accessed by 64-bit input variables
Input format Locations consumed 64-bit data type Location decoration Component decoration 32-bit components consumed

R64

i

scalar

i

0 or unspecified

(x, y, -, -)

R64G64

i

scalar

i

0 or unspecified

(x, y, o, o)

scalar

i

2

(o, o, z, w)

two-component vector

i

0 or unspecified

(x, y, z, w)

R64G64B64

i, i+1

scalar

i

0 or unspecified

(x, y, o, o), (o, o, -, -)

scalar

i

2

(o, o, z, w), (o, o, -, -)

scalar

i+1

0 or unspecified

(o, o, o, o), (x, y, -, -)

two-component vector

i

0 or unspecified

(x, y, z, w), (o, o, -, -)

three-component vector

i

unspecified

(x, y, z, w), (x, y, -, -)

R64G64B64A64

i, i+1

scalar

i

0 or unspecified

(x, y, o, o), (o, o, o, o)

scalar

i

2

(o, o, z, w), (o, o, o, o)

scalar

i+1

0 or unspecified

(o, o, o, o), (x, y, o, o)

scalar

i+1

2

(o, o, o, o), (o, o, z, w)

two-component vector

i

0 or unspecified

(x, y, z, w), (o, o, o, o)

two-component vector

i+1

0 or unspecified

(o, o, o, o), (x, y, z, w)

three-component vector

i

unspecified

(x, y, z, w), (x, y, o, o)

four-component vector

i

unspecified

(x, y, z, w), (x, y, z, w)

Components indicated by “o” are available for use by other input variables which are sourced from the same attribute. Components indicated by “-” are not available for input variables as there are no default values provided for 64-bit data types, and there is no data provided by the input format.

When a vertex shader input variable declared using a 64-bit floating-point matrix type is assigned a location i, its values are taken from consecutive input attribute locations. Such matrices are treated as an array of column vectors with values taken from the input attributes as shown in Input attribute locations and components accessed by 64-bit input variables. Each column vector starts at the location immediately following the last location of the previous column vector. The number of attributes and components assigned to each matrix is determined by the matrix dimensions and ranges from two to eight locations.

When a vertex shader input variable declared using an array type is assigned a location, its values are taken from consecutive input attributes starting with the corresponding VkVertexInputAttributeDescription::location. The number of attributes and components assigned to each element are determined according to the data type of the array elements and Component decoration (if any) specified in the declaration of the array, as described above. Each element of the array, in order, is assigned to consecutive locations, but all at the same specified component within each location.

Only input variables declared with the data types and component decorations as specified above are supported. Location aliasing is causing two variables to have the same location number. Component aliasing is assigning the same (or overlapping) component number for two location aliases. Location aliasing is allowed only if it does not cause component aliasing. Further, when location aliasing, the aliases sharing the location must all have the same SPIR-V floating-point component type or all have the same width integer-type components.

20.2. Vertex Input Description

Applications specify vertex input attribute and vertex input binding descriptions as part of graphics pipeline creation. The VkGraphicsPipelineCreateInfo::pVertexInputState points to a structure of type VkPipelineVertexInputStateCreateInfo.

The VkPipelineVertexInputStateCreateInfo structure is defined as:

typedef struct VkPipelineVertexInputStateCreateInfo {
    VkStructureType                             sType;
    const void*                                 pNext;
    VkPipelineVertexInputStateCreateFlags       flags;
    uint32_t                                    vertexBindingDescriptionCount;
    const VkVertexInputBindingDescription*      pVertexBindingDescriptions;
    uint32_t                                    vertexAttributeDescriptionCount;
    const VkVertexInputAttributeDescription*    pVertexAttributeDescriptions;
} VkPipelineVertexInputStateCreateInfo;
  • sType is the type of this structure.

  • pNext is NULL or a pointer to an extension-specific structure.

  • flags is reserved for future use.

  • vertexBindingDescriptionCount is the number of vertex binding descriptions provided in pVertexBindingDescriptions.

  • pVertexBindingDescriptions is a pointer to an array of VkVertexInputBindingDescription structures.

  • vertexAttributeDescriptionCount is the number of vertex attribute descriptions provided in pVertexAttributeDescriptions.

  • pVertexAttributeDescriptions is a pointer to an array of VkVertexInputAttributeDescription structures.

Valid Usage
  • vertexBindingDescriptionCount must be less than or equal to VkPhysicalDeviceLimits::maxVertexInputBindings

  • vertexAttributeDescriptionCount must be less than or equal to VkPhysicalDeviceLimits::maxVertexInputAttributes

  • For every binding specified by each element of pVertexAttributeDescriptions, a VkVertexInputBindingDescription must exist in pVertexBindingDescriptions with the same value of binding

  • All elements of pVertexBindingDescriptions must describe distinct binding numbers

  • All elements of pVertexAttributeDescriptions must describe distinct attribute locations

Valid Usage (Implicit)
  • sType must be VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO

  • pNext must be NULL or a pointer to a valid instance of VkPipelineVertexInputDivisorStateCreateInfoEXT

  • flags must be 0

  • If vertexBindingDescriptionCount is not 0, pVertexBindingDescriptions must be a valid pointer to an array of vertexBindingDescriptionCount valid VkVertexInputBindingDescription structures

  • If vertexAttributeDescriptionCount is not 0, pVertexAttributeDescriptions must be a valid pointer to an array of vertexAttributeDescriptionCount valid VkVertexInputAttributeDescription structures

typedef VkFlags VkPipelineVertexInputStateCreateFlags;

VkPipelineVertexInputStateCreateFlags is a bitmask type for setting a mask, but is currently reserved for future use.

Each vertex input binding is specified by an instance of the VkVertexInputBindingDescription structure.

The VkVertexInputBindingDescription structure is defined as:

typedef struct VkVertexInputBindingDescription {
    uint32_t             binding;
    uint32_t             stride;
    VkVertexInputRate    inputRate;
} VkVertexInputBindingDescription;
  • binding is the binding number that this structure describes.

  • stride is the distance in bytes between two consecutive elements within the buffer.

  • inputRate is a VkVertexInputRate value specifying whether vertex attribute addressing is a function of the vertex index or of the instance index.

Valid Usage
  • binding must be less than VkPhysicalDeviceLimits::maxVertexInputBindings

  • stride must be less than or equal to VkPhysicalDeviceLimits::maxVertexInputBindingStride

Valid Usage (Implicit)

Possible values of VkVertexInputBindingDescription::inputRate, specifying the rate at which vertex attributes are pulled from buffers, are:

typedef enum VkVertexInputRate {
    VK_VERTEX_INPUT_RATE_VERTEX = 0,
    VK_VERTEX_INPUT_RATE_INSTANCE = 1,
} VkVertexInputRate;
  • VK_VERTEX_INPUT_RATE_VERTEX specifies that vertex attribute addressing is a function of the vertex index.

  • VK_VERTEX_INPUT_RATE_INSTANCE specifies that vertex attribute addressing is a function of the instance index.

Each vertex input attribute is specified by an instance of the VkVertexInputAttributeDescription structure.

The VkVertexInputAttributeDescription structure is defined as:

typedef struct VkVertexInputAttributeDescription {
    uint32_t    location;
    uint32_t    binding;
    VkFormat    format;
    uint32_t    offset;
} VkVertexInputAttributeDescription;
  • location is the shader binding location number for this attribute.

  • binding is the binding number which this attribute takes its data from.

  • format is the size and type of the vertex attribute data.

  • offset is a byte offset of this attribute relative to the start of an element in the vertex input binding.

Valid Usage
  • location must be less than VkPhysicalDeviceLimits::maxVertexInputAttributes

  • binding must be less than VkPhysicalDeviceLimits::maxVertexInputBindings

  • offset must be less than or equal to VkPhysicalDeviceLimits::maxVertexInputAttributeOffset

  • format must be allowed as a vertex buffer format, as specified by the VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT flag in VkFormatProperties::bufferFeatures returned by vkGetPhysicalDeviceFormatProperties

Valid Usage (Implicit)

To bind vertex buffers to a command buffer for use in subsequent draw commands, call:

void vkCmdBindVertexBuffers(
    VkCommandBuffer                             commandBuffer,
    uint32_t                                    firstBinding,
    uint32_t                                    bindingCount,
    const VkBuffer*                             pBuffers,
    const VkDeviceSize*                         pOffsets);
  • commandBuffer is the command buffer into which the command is recorded.

  • firstBinding is the index of the first vertex input binding whose state is updated by the command.

  • bindingCount is the number of vertex input bindings whose state is updated by the command.

  • pBuffers is a pointer to an array of buffer handles.

  • pOffsets is a pointer to an array of buffer offsets.

The values taken from elements i of pBuffers and pOffsets replace the current state for the vertex input binding firstBinding + i, for i in [0, bindingCount). The vertex input binding is updated to start at the offset indicated by pOffsets[i] from the start of the buffer pBuffers[i]. All vertex input attributes that use each of these bindings will use these updated addresses in their address calculations for subsequent draw commands.

Valid Usage
  • firstBinding must be less than VkPhysicalDeviceLimits::maxVertexInputBindings

  • The sum of firstBinding and bindingCount must be less than or equal to VkPhysicalDeviceLimits::maxVertexInputBindings

  • All elements of pOffsets must be less than the size of the corresponding element in pBuffers

  • All elements of pBuffers must have been created with the VK_BUFFER_USAGE_VERTEX_BUFFER_BIT flag

  • Each element of pBuffers that is non-sparse must be bound completely and contiguously to a single VkDeviceMemory object

Valid Usage (Implicit)
  • commandBuffer must be a valid VkCommandBuffer handle

  • pBuffers must be a valid pointer to an array of bindingCount valid VkBuffer handles

  • pOffsets must be a valid pointer to an array of bindingCount VkDeviceSize values

  • commandBuffer must be in the recording state

  • The VkCommandPool that commandBuffer was allocated from must support graphics operations

  • bindingCount must be greater than 0

  • Both of commandBuffer, and the elements of pBuffers must have been created, allocated, or retrieved from the same VkDevice

Host Synchronization
  • Host access to commandBuffer must be externally synchronized

  • Host access to the VkCommandPool that commandBuffer was allocated from must be externally synchronized

Command Properties
Command Buffer Levels Render Pass Scope Supported Queue Types Pipeline Type

Primary
Secondary

Both

Graphics

20.3. Vertex Attribute Divisor in Instanced Rendering

If vertexAttributeInstanceRateDivisor feature is enabled and the pNext chain of VkPipelineVertexInputStateCreateInfo includes a VkPipelineVertexInputDivisorStateCreateInfoEXT structure, then that structure controls how vertex attributes are assigned to an instance when instanced rendering is enabled.

The VkPipelineVertexInputDivisorStateCreateInfoEXT structure is defined as:

typedef struct VkPipelineVertexInputDivisorStateCreateInfoEXT {
    VkStructureType                                     sType;
    const void*                                         pNext;
    uint32_t                                            vertexBindingDivisorCount;
    const VkVertexInputBindingDivisorDescriptionEXT*    pVertexBindingDivisors;
} VkPipelineVertexInputDivisorStateCreateInfoEXT;
  • sType is the type of this structure

  • pNext is NULL or a pointer to an extension-specific structure

  • vertexBindingDivisorCount is the number of elements in the pVertexBindingDivisors array.

  • pVertexBindingDivisors is a pointer to an array of VkVertexInputBindingDivisorDescriptionEXT structures, which specifies the divisor value for each binding.

Valid Usage (Implicit)
  • sType must be VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT

  • pVertexBindingDivisors must be a valid pointer to an array of vertexBindingDivisorCount VkVertexInputBindingDivisorDescriptionEXT structures

  • vertexBindingDivisorCount must be greater than 0

The individual divisor values per binding are specified using the VkVertexInputBindingDivisorDescriptionEXT structure which is defined as:

typedef struct VkVertexInputBindingDivisorDescriptionEXT {
    uint32_t    binding;
    uint32_t    divisor;
} VkVertexInputBindingDivisorDescriptionEXT;
  • binding is the binding number for which the divisor is specified.

  • divisor is the number of successive instances that will use the same value of the vertex attribute when instanced rendering is enabled. For example, if the divisor is N, the same vertex attribute will applied to N successive instances before moving on to the next vertex attribute. The maximum value of divisor is implementation dependent and can be queried using VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT::maxVertexAttribDivisor. A value of 0 can be used for the divisor if the vertexAttributeInstanceRateZeroDivisor feature is enabled. In this case, the same vertex attribute will be applied to all instances.

If this structure is not used to define a divisor value for an attribute then the divisor has a logical default value of 1.

Valid Usage
  • binding must be less than VkPhysicalDeviceLimits::maxVertexInputBindings

  • If the vertexAttributeInstanceRateZeroDivisor feature is not enabled, divisor must not be 0

  • If the vertexAttributeInstanceRateDivisor feature is not enabled, divisor must be 1

  • divisor must be a value between 0 and VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT::maxVertexAttribDivisor, inclusive.

  • VkVertexInputBindingDescription::inputRate must be of type VK_VERTEX_INPUT_RATE_INSTANCE for this binding.

The address of each attribute for each vertexIndex and instanceIndex is calculated as follows:

  • Let attribDesc be the member of VkPipelineVertexInputStateCreateInfo::pVertexAttributeDescriptions with VkVertexInputAttributeDescription::location equal to the vertex input attribute number.

  • Let bindingDesc be the member of VkPipelineVertexInputStateCreateInfo::pVertexBindingDescriptions with VkVertexInputAttributeDescription::binding equal to attribDesc.binding.

  • Let vertexIndex be the index of the vertex within the draw (a value between firstVertex and firstVertex+vertexCount for vkCmdDraw, or a value taken from the index buffer for vkCmdDrawIndexed), and let instanceIndex be the instance number of the draw (a value between firstInstance and firstInstance+instanceCount).

  • Let divisor be the member of VkPipelineVertexInputDivisorStateCreateInfoEXT::pVertexBindingDivisors with VkVertexInputBindingDivisorDescriptionEXT::binding equal to attribDesc.binding.

bufferBindingAddress = buffer[binding].baseAddress + offset[binding];

if (bindingDesc.inputRate == VK_VERTEX_INPUT_RATE_VERTEX)
    vertexOffset = vertexIndex * bindingDesc.stride;
else
    if (divisor == 0)
        vertexOffset = firstInstance * bindingDesc.stride;
    else
        vertexOffset = (firstInstance + ((instanceIndex - firstInstance) / divisor)) * bindingDesc.stride;

attribAddress = bufferBindingAddress + vertexOffset + attribDesc.offset;

For each attribute, raw data is extracted starting at attribAddress and is converted from the VkVertexInputAttributeDescription’s format to either to floating-point, unsigned integer, or signed integer based on the base type of the format; the base type of the format must match the base type of the input variable in the shader. If format is a packed format, attribAddress must be a multiple of the size in bytes of the whole attribute data type as described in Packed Formats. Otherwise, attribAddress must be a multiple of the size in bytes of the component type indicated by format (see Formats). If the format does not include G, B, or A components, then those are filled with (0,0,1) as needed (using either 1.0f or integer 1 based on the format) for attributes that are not 64-bit data types. The number of components in the vertex shader input variable need not exactly match the number of components in the format. If the vertex shader has fewer components, the extra components are discarded.

20.4. Example

To create a graphics pipeline that uses the following vertex description:

struct Vertex
{
    float   x, y, z, w;
    uint8_t u, v;
};

The application could use the following set of structures:

const VkVertexInputBindingDescription binding =
{
    0,                                          // binding
    sizeof(Vertex),                             // stride
    VK_VERTEX_INPUT_RATE_VERTEX                 // inputRate
};

const VkVertexInputAttributeDescription attributes[] =
{
    {
        0,                                      // location
        binding.binding,                        // binding
        VK_FORMAT_R32G32B32A32_SFLOAT,          // format
        0                                       // offset
    },
    {
        1,                                      // location
        binding.binding,                        // binding
        VK_FORMAT_R8G8_UNORM,                   // format
        4 * sizeof(float)                       // offset
    }
};

const VkPipelineVertexInputStateCreateInfo viInfo =
{
    VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_CREATE_INFO,    // sType
    NULL,                         // pNext
    0,                            // flags
    1,                            // vertexBindingDescriptionCount
    &binding,                     // pVertexBindingDescriptions
    2,                            // vertexAttributeDescriptionCount
    &attributes[0]                // pVertexAttributeDescriptions
};