Descriptors are grouped together into descriptor set objects. A descriptor set object is an opaque object that contains storage for a set of descriptors, where the types and number of descriptors is defined by a descriptor set layout. The layout object may be used to define the association of each descriptor binding with memory or other hardware resources. The layout is used both for determining the resources that need to be associated with the descriptor set, and determining the interface between shader stages and shader resources.
A descriptor set layout object is defined by an array of zero or more descriptor bindings. Each individual descriptor binding is specified by a descriptor type, a count (array size) of the number of descriptors in the binding, a set of shader stages that can access the binding, and (if using immutable samplers) an array of sampler descriptors.
Descriptor set layout objects are represented by VkDescriptorSetLayout
handles:
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSetLayout)
To create descriptor set layout objects, call:
VkResult vkCreateDescriptorSetLayout( VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorSetLayout* pSetLayout);
device
is the logical device that creates the descriptor set
layout.
pCreateInfo
is a pointer to an instance of the
VkDescriptorSetLayoutCreateInfo
structure specifying the state of
the descriptor set layout object.
pAllocator
controls host memory allocation as described in the
Memory Allocation chapter.
pSetLayout
points to a VkDescriptorSetLayout
handle in which
the resulting descriptor set layout object is returned.
Information about the descriptor set layout is passed in an instance of the
VkDescriptorSetLayoutCreateInfo
structure:
typedef struct VkDescriptorSetLayoutCreateInfo { VkStructureType sType; const void* pNext; VkDescriptorSetLayoutCreateFlags flags; uint32_t bindingCount; const VkDescriptorSetLayoutBinding* pBindings; } VkDescriptorSetLayoutCreateInfo;
sType
is the type of this structure.
pNext
is NULL
or a pointer to an extension-specific structure.
flags
is reserved for future use.
bindingCount
is the number of elements in pBindings
.
pBindings
is a pointer to an array of
VkDescriptorSetLayoutBinding
structures.
The VkDescriptorSetLayoutBinding
structure is defined as:
typedef struct VkDescriptorSetLayoutBinding { uint32_t binding; VkDescriptorType descriptorType; uint32_t descriptorCount; VkShaderStageFlags stageFlags; const VkSampler* pImmutableSamplers; } VkDescriptorSetLayoutBinding;
binding
is the binding number of this entry and corresponds to a
resource of the same binding number in the shader stages.
descriptorType
is a VkDescriptorType
specifying which type
of resource descriptors are used for this binding.
descriptorCount
is the number of descriptors contained in the
binding, accessed in a shader as an array.
If descriptorCount
is zero this binding entry is reserved and the
resource must not be accessed from any stage via this binding within
any pipeline using the set layout.
stageFlags
member is a bitmask of VkShaderStageFlagBits
specifying which pipeline shader stages can access a resource for this
binding.
VK_SHADER_STAGE_ALL
is a shorthand specifying that all defined
shader stages, including any additional stages defined by extensions,
can access the resource.
If a shader stage is not included in stageFlags
, then a resource must
not be accessed from that stage via this binding within any pipeline using
the set layout.
There are no limitations on what combinations of stages can be used by a
descriptor binding, and in particular a binding can be used by both
graphics stages and the compute stage.
pImmutableSamplers
affects initialization of samplers.
If descriptorType
specifies a VK_DESCRIPTOR_TYPE_SAMPLER
or
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
type descriptor, then
pImmutableSamplers
can be used to initialize a set of immutable
samplers.
Immutable samplers are permanently bound into the set layout; later
binding a sampler into an immutable sampler slot in a descriptor set is
not allowed.
If pImmutableSamplers
is not NULL
, then it is considered to be a
pointer to an array of sampler handles that will be consumed by the set
layout and used for the corresponding binding.
If pImmutableSamplers
is NULL
, then the sampler slots are
dynamic and sampler handles must be bound into descriptor sets using
this layout.
If descriptorType
is not one of these descriptor types, then
pImmutableSamplers
is ignored.
The above layout definition allows the descriptor bindings to be specified
sparsely such that not all binding numbers between 0 and the maximum binding
number need to be specified in the pBindings
array.
Bindings that are not specified have a descriptorCount
and
stageFlags
of zero, and the descriptorType
is treated as
undefined.
However, all binding numbers between 0 and the maximum binding number in the
VkDescriptorSetLayoutCreateInfo
::pBindings
array may consume
memory in the descriptor set layout even if not all descriptor bindings are
used, though it should not consume additional memory from the descriptor
pool.
![]() | Note |
---|---|
The maximum binding number specified should be as compact as possible to avoid wasted memory. |
The following examples show a shader snippet using two descriptor sets, and application code that creates corresponding descriptor set layouts.
GLSL example.
// // binding to a single sampled image descriptor in set 0 // layout (set=0, binding=0) uniform texture2D mySampledImage; // // binding to an array of sampled image descriptors in set 0 // layout (set=0, binding=1) uniform texture2D myArrayOfSampledImages[12]; // // binding to a single uniform buffer descriptor in set 1 // layout (set=1, binding=0) uniform myUniformBuffer { vec4 myElement[32]; };
SPIR-V example.
... %1 = OpExtInstImport "GLSL.std.450" ... OpName %9 "mySampledImage" OpName %14 "myArrayOfSampledImages" OpName %18 "myUniformBuffer" OpMemberName %18 0 "myElement" OpName %20 "" OpDecorate %9 DescriptorSet 0 OpDecorate %9 Binding 0 OpDecorate %14 DescriptorSet 0 OpDecorate %14 Binding 1 OpDecorate %17 ArrayStride 16 OpMemberDecorate %18 0 Offset 0 OpDecorate %18 Block OpDecorate %20 DescriptorSet 1 OpDecorate %20 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 %7 = OpTypeImage %6 2D 0 0 0 1 Unknown %8 = OpTypePointer UniformConstant %7 %9 = OpVariable %8 UniformConstant %10 = OpTypeInt 32 0 %11 = OpConstant %10 12 %12 = OpTypeArray %7 %11 %13 = OpTypePointer UniformConstant %12 %14 = OpVariable %13 UniformConstant %15 = OpTypeVector %6 4 %16 = OpConstant %10 32 %17 = OpTypeArray %15 %16 %18 = OpTypeStruct %17 %19 = OpTypePointer Uniform %18 %20 = OpVariable %19 Uniform ...
API example.
VkResult myResult; const VkDescriptorSetLayoutBinding myDescriptorSetLayoutBinding[] = { // binding to a single image descriptor { 0, // binding VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, // descriptorType 1, // descriptorCount VK_SHADER_STAGE_FRAGMENT_BIT, // stageFlags NULL // pImmutableSamplers }, // binding to an array of image descriptors { 1, // binding VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, // descriptorType 12, // descriptorCount VK_SHADER_STAGE_FRAGMENT_BIT, // stageFlags NULL // pImmutableSamplers }, // binding to a single uniform buffer descriptor { 0, // binding VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, // descriptorType 1, // descriptorCount VK_SHADER_STAGE_FRAGMENT_BIT, // stageFlags NULL // pImmutableSamplers } }; const VkDescriptorSetLayoutCreateInfo myDescriptorSetLayoutCreateInfo[] = { // Create info for first descriptor set with two descriptor bindings { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, // sType NULL, // pNext 0, // flags 2, // bindingCount &myDescriptorSetLayoutBinding[0] // pBindings }, // Create info for second descriptor set with one descriptor binding { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, // sType NULL, // pNext 0, // flags 1, // bindingCount &myDescriptorSetLayoutBinding[2] // pBindings } }; VkDescriptorSetLayout myDescriptorSetLayout[2]; // // Create first descriptor set layout // myResult = vkCreateDescriptorSetLayout( myDevice, &myDescriptorSetLayoutCreateInfo[0], NULL, &myDescriptorSetLayout[0]); // // Create second descriptor set layout // myResult = vkCreateDescriptorSetLayout( myDevice, &myDescriptorSetLayoutCreateInfo[1], NULL, &myDescriptorSetLayout[1]);
To destroy a descriptor set layout, call:
void vkDestroyDescriptorSetLayout( VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks* pAllocator);
device
is the logical device that destroys the descriptor set
layout.
descriptorSetLayout
is the descriptor set layout to destroy.
pAllocator
controls host memory allocation as described in the
Memory Allocation chapter.
Access to descriptor sets from a pipeline is accomplished through a pipeline layout. Zero or more descriptor set layouts and zero or more push constant ranges are combined to form a pipeline layout object which describes the complete set of resources that can be accessed by a pipeline. The pipeline layout represents a sequence of descriptor sets with each having a specific layout. This sequence of layouts is used to determine the interface between shader stages and shader resources. Each pipeline is created using a pipeline layout.
Pipeline layout objects are represented by VkPipelineLayout
handles:
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineLayout)
To create a pipeline layout, call:
VkResult vkCreatePipelineLayout( VkDevice device, const VkPipelineLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineLayout* pPipelineLayout);
device
is the logical device that creates the pipeline layout.
pCreateInfo
is a pointer to an instance of the
VkPipelineLayoutCreateInfo
structure specifying the state of the
pipeline layout object.
pAllocator
controls host memory allocation as described in the
Memory Allocation chapter.
pPipelineLayout
points to a VkPipelineLayout
handle in which
the resulting pipeline layout object is returned.
The VkPipelineLayoutCreateInfo
structure is defined as:
typedef struct VkPipelineLayoutCreateInfo { VkStructureType sType; const void* pNext; VkPipelineLayoutCreateFlags flags; uint32_t setLayoutCount; const VkDescriptorSetLayout* pSetLayouts; uint32_t pushConstantRangeCount; const VkPushConstantRange* pPushConstantRanges; } VkPipelineLayoutCreateInfo;
sType
is the type of this structure.
pNext
is NULL
or a pointer to an extension-specific structure.
flags
is reserved for future use.
setLayoutCount
is the number of descriptor sets included in the
pipeline layout.
pSetLayouts
is a pointer to an array of
VkDescriptorSetLayout
objects.
pushConstantRangeCount
is the number of push constant ranges
included in the pipeline layout.
pPushConstantRanges
is a pointer to an array of
VkPushConstantRange
structures defining a set of push constant
ranges for use in a single pipeline layout.
In addition to descriptor set layouts, a pipeline layout also describes
how many push constants can be accessed by each stage of the pipeline.
![]() | Note |
---|---|
Push constants represent a high speed path to modify constant data in pipelines that is expected to outperform memory-backed resource updates. |
The VkPushConstantRange
structure is defined as:
typedef struct VkPushConstantRange { VkShaderStageFlags stageFlags; uint32_t offset; uint32_t size; } VkPushConstantRange;
stageFlags
is a set of stage flags describing the shader stages
that will access a range of push constants.
If a particular stage is not included in the range, then accessing
members of that range of push constants from the corresponding shader
stage will result in undefined data being read.
offset
and size
are the start offset and size, respectively,
consumed by the range.
Both offset
and size
are in units of bytes and must be a
multiple of 4.
The layout of the push constant variables is specified in the shader.
Once created, pipeline layouts are used as part of pipeline creation (see Pipelines), as part of binding descriptor sets (see Descriptor Set Binding), and as part of setting push constants (see Push Constant Updates). Pipeline creation accepts a pipeline layout as input, and the layout may be used to map (set, binding, arrayElement) tuples to hardware resources or memory locations within a descriptor set. The assignment of hardware resources depends only on the bindings defined in the descriptor sets that comprise the pipeline layout, and not on any shader source.
All resource variables statically used in all shaders
in a pipeline must be declared with a (set,binding,arrayElement) that
exists in the corresponding descriptor set layout and is of an appropriate
descriptor type and includes the set of shader stages it is used by in
stageFlags
.
The pipeline layout can include entries that are not used by a particular
pipeline, or that are dead-code eliminated from any of the shaders.
The pipeline layout allows the application to provide a consistent set of
bindings across multiple pipeline compiles, which enables those pipelines to
be compiled in a way that the implementation may cheaply switch pipelines
without reprogramming the bindings.
Similarly, the push constant block declared in each shader (if present)
must only place variables at offsets that are each included in a push
constant range with stageFlags
including the bit corresponding to the
shader stage that uses it.
The pipeline layout can include ranges or portions of ranges that are not
used by a particular pipeline, or for which the variables have been
dead-code eliminated from any of the shaders.
There is a limit on the total number of resources of each type that can be included in bindings in all descriptor set layouts in a pipeline layout as shown in Pipeline Layout Resource Limits. The “Total Resources Available” column gives the limit on the number of each type of resource that can be included in bindings in all descriptor sets in the pipeline layout. Some resource types count against multiple limits. Additionally, there are limits on the total number of each type of resource that can be used in any pipeline stage as described in Shader Resource Limits.
Table 13.1. Pipeline Layout Resource Limits
Total Resources Available | Resource Types |
---|---|
| sampler |
combined image sampler | |
| sampled image |
combined image sampler | |
uniform texel buffer | |
| storage image |
storage texel buffer | |
| uniform buffer |
uniform buffer dynamic | |
| uniform buffer dynamic |
| storage buffer |
storage buffer dynamic | |
| storage buffer dynamic |
| input attachment |
To destroy a pipeline layout, call:
void vkDestroyPipelineLayout( VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks* pAllocator);
device
is the logical device that destroys the pipeline layout.
pipelineLayout
is the pipeline layout to destroy.
pAllocator
controls host memory allocation as described in the
Memory Allocation chapter.
Two pipeline layouts are defined to be “compatible for push constants” if they were created with identical push constant ranges. Two pipeline layouts are defined to be “compatible for set N” if they were created with matching (the same, or identically defined) descriptor set layouts for sets zero through N, and if they were created with identical push constant ranges.
When binding a descriptor set (see Descriptor Set Binding) to set number N, if the previously bound descriptor sets for sets zero through N-1 were all bound using compatible pipeline layouts, then performing this binding does not disturb any of the lower numbered sets. If, additionally, the previous bound descriptor set for set N was bound using a pipeline layout compatible for set N, then the bindings in sets numbered greater than N are also not disturbed.
Similarly, when binding a pipeline, the pipeline can correctly access any previously bound descriptor sets which were bound with compatible pipeline layouts, as long as all lower numbered sets were also bound with compatible layouts.
Layout compatibility means that descriptor sets can be bound to a command buffer for use by any pipeline created with a compatible pipeline layout, and without having bound a particular pipeline first. It also means that descriptor sets can remain valid across a pipeline change, and the same resources will be accessible to the newly bound pipeline.
![]() | Note |
---|---|
Place the least frequently changing descriptor sets near the start of the pipeline layout, and place the descriptor sets representing the most frequently changing resources near the end. When pipelines are switched, only the descriptor set bindings that have been invalidated will need to be updated and the remainder of the descriptor set bindings will remain in place. |
The maximum number of descriptor sets that can be bound to a pipeline
layout is queried from physical device properties (see
maxBoundDescriptorSets
in Limits).
API example.
const VkDescriptorSetLayout layouts[] = { layout1, layout2 }; const VkPushConstantRange ranges[] = { { VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, // stageFlags 0, // offset 4 // size }, { VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // stageFlags 4, // offset 4 // size }, }; const VkPipelineLayoutCreateInfo createInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType NULL, // pNext 0, // flags 2, // setLayoutCount layouts, // pSetLayouts 2, // pushConstantRangeCount ranges // pPushConstantRanges }; VkPipelineLayout myPipelineLayout; myResult = vkCreatePipelineLayout( myDevice, &createInfo, NULL, &myPipelineLayout);
A descriptor pool maintains a pool of descriptors, from which descriptor sets are allocated. Descriptor pools are externally synchronized, meaning that the application must not allocate and/or free descriptor sets from the same pool in multiple threads simultaneously.
Descriptor pools are represented by VkDescriptorPool
handles:
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorPool)
To create a descriptor pool object, call:
VkResult vkCreateDescriptorPool( VkDevice device, const VkDescriptorPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorPool* pDescriptorPool);
device
is the logical device that creates the descriptor pool.
pCreateInfo
is a pointer to an instance of the
VkDescriptorPoolCreateInfo
structure specifying the state of the
descriptor pool object.
pAllocator
controls host memory allocation as described in the
Memory Allocation chapter.
pDescriptorPool
points to a VkDescriptorPool
handle in which
the resulting descriptor pool object is returned.
pAllocator
controls host memory allocation as described in the
Memory Allocation chapter.
The created descriptor pool is returned in pDescriptorPool
.
Additional information about the pool is passed in an instance of the
VkDescriptorPoolCreateInfo
structure:
typedef struct VkDescriptorPoolCreateInfo { VkStructureType sType; const void* pNext; VkDescriptorPoolCreateFlags flags; uint32_t maxSets; uint32_t poolSizeCount; const VkDescriptorPoolSize* pPoolSizes; } VkDescriptorPoolCreateInfo;
sType
is the type of this structure.
pNext
is NULL
or a pointer to an extension-specific structure.
flags
specifies certain supported operations on the pool.
Bits which can be set include:
typedef enum VkDescriptorPoolCreateFlagBits { VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT = 0x00000001, } VkDescriptorPoolCreateFlagBits;
If flags
includes
VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT
, then descriptor
sets can return their individual allocations to the pool, i.e. all of
vkAllocateDescriptorSets
, vkFreeDescriptorSets
, and
vkResetDescriptorPool
are allowed.
Otherwise, descriptor sets allocated from the pool must not be individually
freed back to the pool, i.e. only vkAllocateDescriptorSets
and
vkResetDescriptorPool
are allowed.
maxSets
is the maximum number of descriptor sets that can be
allocated from the pool.
poolSizeCount
is the number of elements in pPoolSizes
.
pPoolSizes
is a pointer to an array of VkDescriptorPoolSize
structures, each containing a descriptor type and number of descriptors
of that type to be allocated in the pool.
If multiple VkDescriptorPoolSize
structures appear in the
pPoolSizes
array then the pool will be created with enough storage for
the total number of descriptors of each type.
Fragmentation of a descriptor pool is possible and may lead to descriptor set allocation failures. A failure due to fragmentation is defined as failing a descriptor set allocation despite the sum of all outstanding descriptor set allocations from the pool plus the requested allocation requiring no more than the total number of descriptors requested at pool creation. Implementations provide certain guarantees of when fragmentation must not cause allocation failure, as described below.
If a descriptor pool has not had any descriptor sets freed since it was
created or most recently reset then fragmentation must not cause an
allocation failure (note that this is always the case for a pool created
without the VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT
bit
set).
Additionally, if all sets allocated from the pool since it was created or
most recently reset use the same number of descriptors (of each type) and
the requested allocation also uses that same number of descriptors (of each
type), then fragmentation must not cause an allocation failure.
If an allocation failure occurs due to fragmentation, an application can create an additional descriptor pool to perform further descriptor set allocations.
The VkDescriptorPoolSize
structure is defined as:
typedef struct VkDescriptorPoolSize { VkDescriptorType type; uint32_t descriptorCount; } VkDescriptorPoolSize;
type
is the type of descriptor.
descriptorCount
is the number of descriptors of that type to
allocate.
To destroy a descriptor pool, call:
void vkDestroyDescriptorPool( VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks* pAllocator);
device
is the logical device that destroys the descriptor pool.
descriptorPool
is the descriptor pool to destroy.
pAllocator
controls host memory allocation as described in the
Memory Allocation chapter.
When a pool is destroyed, all descriptor sets allocated from the pool are implicitly freed and become invalid. Descriptor sets allocated from a given pool do not need to be freed before destroying that descriptor pool.
Descriptor sets are allocated from descriptor pool objects, and are
represented by VkDescriptorSet
handles:
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSet)
To allocate descriptor sets from a descriptor pool, call:
VkResult vkAllocateDescriptorSets( VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets);
device
is the logical device that owns the descriptor pool.
pAllocateInfo
is a pointer to an instance of the
VkDescriptorSetAllocateInfo
structure describing parameters of the
allocation.
pDescriptorSets
is a pointer to an array of VkDescriptorSet
handles in which the resulting descriptor set objects are returned.
The array must be at least the length specified by the
descriptorSetCount
member of pAllocateInfo
.
The allocated descriptor sets are returned in pDescriptorSets
.
When a descriptor set is allocated, the initial state is largely uninitialized and all descriptors are undefined. However, the descriptor set can be bound in a command buffer without causing errors or exceptions. All entries that are statically used by a pipeline in a drawing or dispatching command must have been populated before the descriptor set is bound for use by that command. Entries that are not statically used by a pipeline can have uninitialized descriptors or descriptors of resources that have been destroyed, and executing a draw or dispatch with such a descriptor set bound does not cause undefined behavior. This means applications need not populate unused entries with dummy descriptors.
If an allocation fails due to fragmentation, an indeterminate error is
returned with an unspecified error code.
Any returned error other than VK_ERROR_FRAGMENTED_POOL
does not imply
its usual meaning: applications should assume that the allocation failed
due to fragmentation, and create a new descriptor pool.
![]() | Note |
---|---|
Applications should check for a negative return value when allocating new
descriptor sets, assume that any error effectively means
The reason for this is that |
The VkDescriptorSetAllocateInfo
structure is defined as:
typedef struct VkDescriptorSetAllocateInfo { VkStructureType sType; const void* pNext; VkDescriptorPool descriptorPool; uint32_t descriptorSetCount; const VkDescriptorSetLayout* pSetLayouts; } VkDescriptorSetAllocateInfo;
sType
is the type of this structure.
pNext
is NULL
or a pointer to an extension-specific structure.
descriptorPool
is the pool which the sets will be allocated from.
descriptorSetCount
determines the number of descriptor sets to be
allocated from the pool.
pSetLayouts
is an array of descriptor set layouts, with each
member specifying how the corresponding descriptor set is allocated.
To free allocated descriptor sets, call:
VkResult vkFreeDescriptorSets( VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets);
device
is the logical device that owns the descriptor pool.
descriptorPool
is the descriptor pool from which the descriptor
sets were allocated.
descriptorSetCount
is the number of elements in the
pDescriptorSets
array.
pDescriptorSets
is an array of handles to VkDescriptorSet
objects.
After a successful call to vkFreeDescriptorSets
, all descriptor sets
in pDescriptorSets
are invalid.
To return all descriptor sets allocated from a given pool to the pool, rather than freeing individual descriptor sets, call:
VkResult vkResetDescriptorPool( VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags);
device
is the logical device that owns the descriptor pool.
descriptorPool
is the descriptor pool to be reset.
flags
is reserved for future use.
Resetting a descriptor pool recycles all of the resources from all of the descriptor sets allocated from the descriptor pool back to the descriptor pool, and the descriptor sets are implicitly freed.
Once allocated, descriptor sets can be updated with a combination of write and copy operations. To update descriptor sets, call:
void vkUpdateDescriptorSets( VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet* pDescriptorCopies);
device
is the logical device that updates the descriptor sets.
descriptorWriteCount
is the number of elements in the
pDescriptorWrites
array.
pDescriptorWrites
is a pointer to an array of
VkWriteDescriptorSet
structures describing the descriptor sets to
write to.
descriptorCopyCount
is the number of elements in the
pDescriptorCopies
array.
pDescriptorCopies
is a pointer to an array of
VkCopyDescriptorSet
structures describing the descriptor sets to
copy between.
The operations described by pDescriptorWrites
are performed first,
followed by the operations described by pDescriptorCopies
.
Within each array, the operations are performed in the order they appear in
the array.
Each element in the pDescriptorWrites
array describes an operation
updating the descriptor set using descriptors for resources specified in the
structure.
Each element in the pDescriptorCopies
array is a
VkCopyDescriptorSet
structure describing an operation copying
descriptors between sets.
The VkWriteDescriptorSet
structure is defined as:
typedef struct VkWriteDescriptorSet { VkStructureType sType; const void* pNext; VkDescriptorSet dstSet; uint32_t dstBinding; uint32_t dstArrayElement; uint32_t descriptorCount; VkDescriptorType descriptorType; const VkDescriptorImageInfo* pImageInfo; const VkDescriptorBufferInfo* pBufferInfo; const VkBufferView* pTexelBufferView; } VkWriteDescriptorSet;
sType
is the type of this structure.
pNext
is NULL
or a pointer to an extension-specific structure.
dstSet
is the destination descriptor set to update.
dstBinding
is the descriptor binding within that set.
dstArrayElement
is the starting element in that array.
descriptorCount
is the number of descriptors to update (the number
of elements in pImageInfo
, pBufferInfo
, or
pTexelBufferView
).
descriptorType
is a VkDescriptorType
specifying the type of
each descriptor in pImageInfo
, pBufferInfo
, or
pTexelBufferView
, as described below.
It must be the same type as that specified in
VkDescriptorSetLayoutBinding
for dstSet
at dstBinding
.
The type of the descriptor also controls which array the descriptors are
taken from.
pImageInfo
points to an array of VkDescriptorImageInfo
structures or is ignored, as described below.
pBufferInfo
points to an array of VkDescriptorBufferInfo
structures or is ignored, as described below.
pTexelBufferView
points to an array of VkBufferView
handles
as described in the Buffer Views section or
is ignored, as described below.
Only one of pImageInfo
, pBufferInfo
, or pTexelBufferView
members is used according to the descriptor type specified in the
descriptorType
member of the containing VkWriteDescriptorSet
structure, as specified below.
If the dstBinding
has fewer than descriptorCount
array elements
remaining starting from dstArrayElement
, then the remainder will be
used to update the subsequent binding - dstBinding
+1 starting at array
element zero.
If a binding has a descriptorCount
of zero, it is skipped.
This behavior applies recursively, with the update affecting consecutive
bindings as needed to update all descriptorCount
descriptors.
All consecutive bindings updated via a single VkWriteDescriptorSet
structure, except those with a descriptorCount
of zero, must have
identical descriptorType
and stageFlags
, and must all either
use immutable samplers or must all not use immutable samplers.
The type of descriptors in a descriptor set is specified by
VkWriteDescriptorSet
::descriptorType
, which must be one of the
values:
typedef enum VkDescriptorType { VK_DESCRIPTOR_TYPE_SAMPLER = 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER = 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE = 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE = 3, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER = 4, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER = 5, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER = 6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER = 7, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC = 8, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC = 9, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT = 10, } VkDescriptorType;
If descriptorType
is VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
, or
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC
, the elements of the
VkWriteDescriptorSet
::pBufferInfo
array of
VkDescriptorBufferInfo
structures will be used to update the
descriptors, and other arrays will be ignored.
If descriptorType
is VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
or
VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER
, the
VkWriteDescriptorSet
::pTexelBufferView
array will be used to
update the descriptors, and other arrays will be ignored.
If descriptorType
is VK_DESCRIPTOR_TYPE_SAMPLER
,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
,
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE
,
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
, or
VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT
, the elements of the
VkWriteDescriptorSet
::pImageInfo
array of
VkDescriptorImageInfo
structures will be used to update the
descriptors, and other arrays will be ignored.
The VkDescriptorBufferInfo
structure is defined as:
typedef struct VkDescriptorBufferInfo { VkBuffer buffer; VkDeviceSize offset; VkDeviceSize range; } VkDescriptorBufferInfo;
buffer
is the buffer resource.
offset
is the offset in bytes from the start of buffer
.
Access to buffer memory via this descriptor uses addressing that is
relative to this starting offset.
range
is the size in bytes that is used for this descriptor
update, or VK_WHOLE_SIZE
to use the range from offset
to the
end of the buffer.
![]() | Note |
---|---|
When using |
For VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
and
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC
descriptor types,
offset
is the base offset from which the dynamic offset is applied and
range
is the static size used for all dynamic offsets.
The VkDescriptorImageInfo
structure is defined as:
typedef struct VkDescriptorImageInfo { VkSampler sampler; VkImageView imageView; VkImageLayout imageLayout; } VkDescriptorImageInfo;
sampler
is a sampler handle, and is used in descriptor updates for
types VK_DESCRIPTOR_TYPE_SAMPLER
and
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
if the binding being
updated does not use immutable samplers.
imageView
is an image view handle, and is used in descriptor
updates for types VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE
,
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
, and
VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT
.
imageLayout
is the layout that the image will be in at the time
this descriptor is accessed.
imageLayout
is used in descriptor updates for types
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE
,
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
, and
VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT
.
Members of VkDescriptorImageInfo
that are not used in an update (as
described above) are ignored.
The VkCopyDescriptorSet
structure is defined as:
typedef struct VkCopyDescriptorSet { VkStructureType sType; const void* pNext; VkDescriptorSet srcSet; uint32_t srcBinding; uint32_t srcArrayElement; VkDescriptorSet dstSet; uint32_t dstBinding; uint32_t dstArrayElement; uint32_t descriptorCount; } VkCopyDescriptorSet;
sType
is the type of this structure.
pNext
is NULL
or a pointer to an extension-specific structure.
srcSet
, srcBinding
, and srcArrayElement
are the source
set, binding, and array element, respectively.
dstSet
, dstBinding
, and dstArrayElement
are the
destination set, binding, and array element, respectively.
descriptorCount
is the number of descriptors to copy from the
source to destination.
If descriptorCount
is greater than the number of remaining array
elements in the source or destination binding, those affect consecutive
bindings in a manner similar to VkWriteDescriptorSet
above.
To bind one or more descriptor sets to a command buffer, call:
void vkCmdBindDescriptorSets( VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets);
commandBuffer
is the command buffer that the descriptor sets will
be bound to.
pipelineBindPoint
is a VkPipelineBindPoint
indicating
whether the descriptors will be used by graphics pipelines or compute
pipelines.
There is a separate set of bind points for each of graphics and compute,
so binding one does not disturb the other.
layout
is a VkPipelineLayout
object used to program the
bindings.
firstSet
is the set number of the first descriptor set to be
bound.
descriptorSetCount
is the number of elements in the
pDescriptorSets
array.
pDescriptorSets
is an array of handles to VkDescriptorSet
objects describing the descriptor sets to write to.
dynamicOffsetCount
is the number of dynamic offsets in the
pDynamicOffsets
array.
pDynamicOffsets
is a pointer to an array of uint32_t
values
specifying dynamic offsets.
vkCmdBindDescriptorSets
causes the sets numbered [firstSet
..
firstSet
+descriptorSetCount
-1] to use the bindings stored in
pDescriptorSets
[0..descriptorSetCount
-1] for subsequent
rendering commands (either compute or graphics, according to the
pipelineBindPoint
).
Any bindings that were previously applied via these sets are no longer
valid.
Once bound, a descriptor set affects rendering of subsequent graphics or compute commands in the command buffer until a different set is bound to the same set number, or else until the set is disturbed as described in Pipeline Layout Compatibility.
A compatible descriptor set must be bound for all set numbers that any shaders in a pipeline access, at the time that a draw or dispatch command is recorded to execute using that pipeline. However, if none of the shaders in a pipeline statically use any bindings with a particular set number, then no descriptor set need be bound for that set number, even if the pipeline layout includes a non-trivial descriptor set layout for that set number.
If any of the sets being bound include dynamic uniform or storage buffers,
then pDynamicOffsets
includes one element for each array element in
each dynamic descriptor type binding in each set.
Values are taken from pDynamicOffsets
in an order such that all
entries for set N come before set N+1; within a set, entries are ordered by
the binding numbers in the descriptor set layouts; and within a binding
array, elements are in order.
dynamicOffsetCount
must equal the total number of dynamic descriptors
in the sets being bound.
The effective offset used for dynamic uniform and storage buffer bindings is
the sum of the relative offset taken from pDynamicOffsets
, and the
base address of the buffer plus base offset in the descriptor set.
The length of the dynamic uniform and storage buffer bindings is the buffer
range as specified in the descriptor set.
Each of the pDescriptorSets
must be compatible with the pipeline
layout specified by layout
.
The layout used to program the bindings must also be compatible with the
pipeline used in subsequent graphics or compute commands, as defined in the
Pipeline Layout Compatibility section.
The descriptor set contents bound by a call to vkCmdBindDescriptorSets
may be consumed during host execution of the command, or during shader
execution of the resulting draws, or any time in between.
Thus, the contents must not be altered (overwritten by an update command,
or freed) between when the command is recorded and when the command
completes executing on the queue.
The contents of pDynamicOffsets
are consumed immediately during
execution of vkCmdBindDescriptorSets
.
Once all pending uses have completed, it is legal to update and reuse a
descriptor set.
As described above in section Pipeline Layouts, the pipeline layout defines shader push constants which are updated via Vulkan commands rather than via writes to memory or copy commands.
![]() | Note |
---|---|
Push constants represent a high speed path to modify constant data in pipelines that is expected to outperform memory-backed resource updates. |
The values of push constants are undefined at the start of a command buffer.
To update push constants, call:
void vkCmdPushConstants( VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues);
commandBuffer
is the command buffer in which the push constant
update will be recorded.
layout
is the pipeline layout used to program the push constant
updates.
stageFlags
is a bitmask of VkShaderStageFlagBits
specifying
the shader stages that will use the push constants in the updated range.
offset
is the start offset of the push constant range to update,
in units of bytes.
size
is the size of the push constant range to update, in units of
bytes.
pValues
is an array of size
bytes containing the new push
constant values.