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. However,
all binding numbers between 0 and the maximum binding number 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 |
---|---|
maxDescriptorSetSamplers | sampler |
combined image sampler | |
maxDescriptorSetSampledImages | sampled image |
combined image sampler | |
uniform texel buffer | |
maxDescriptorSetStorageImages | storage image |
storage texel buffer | |
maxDescriptorSetUniformBuffers | uniform buffer |
uniform buffer dynamic | |
maxDescriptorSetUniformBuffersDynamic | uniform buffer dynamic |
maxDescriptorSetStorageBuffers | storage buffer |
storage buffer dynamic | |
maxDescriptorSetStorageBuffersDynamic | storage buffer dynamic |
maxDescriptorSetInputAttachments | 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. 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 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.