Command Buffers
Recorded commands include commands to bind pipelines and descriptor sets to the command buffer, commands to modify dynamic state, commands to draw (for graphics rendering), commands to dispatch (for compute), commands to execute secondary command buffers (for primary command buffers only), commands to copy buffers and images, and other commands.
Each command buffer manages state independently of other command buffers. There is no inheritance of state across primary and secondary command buffers, or between secondary command buffers. When a command buffer begins recording, all state in that command buffer is undefined:. When secondary command buffer(s) are recorded to execute on a primary command buffer, the secondary command buffer inherits no state from the primary command buffer, and all state of the primary command buffer is undefined: after an execute secondary command buffer command is recorded. There is one exception to this rule - if the primary command buffer is inside a render pass instance, then the render pass and subpass state is not disturbed by executing secondary command buffers. For state dependent commands (such as draws and dispatches), any state consumed by those commands must not be undefined:.
VkCommandBufferInheritanceViewportScissorInfoNV defines an exception allowing limited inheritance of dynamic viewport and scissor state.
Unless otherwise specified, and without explicit synchronization, the various commands submitted to a queue via command buffers may execute in arbitrary order relative to each other, and/or concurrently. Also, the memory side effects of those commands may not be directly visible to other commands without explicit memory dependencies. This is true within a command buffer, and across command buffers submitted to a given queue. See the synchronization chapter for information on implicit and explicit synchronization between commands.
Command Buffer Lifecycle
Each command buffer is always in one of the following states:
Initial
When a command buffer is allocated, it is in the initial state. Some commands are able to reset a command buffer (or a set of command buffers) back to this state from any of the executable, recording or invalid state. Command buffers in the initial state can only be moved to the recording state, or freed.
Recording
vkBeginCommandBuffer changes the state of a command buffer from
the initial state to the recording state.
Once a command buffer is in the recording state, vkCmd*
commands
can be used to record to the command buffer.
Executable
vkEndCommandBuffer ends the recording of a command buffer, and moves it from the recording state to the executable state. Executable command buffers can be submitted, reset, or recorded to another command buffer.
Pending
Queue submission of a command buffer
changes the state of a command buffer from the executable state to the
pending state.
Whilst in the pending state, applications must not attempt to modify
the command buffer in any way - as the device may be processing the
commands recorded to it.
Once execution of a command buffer completes, the command buffer either
reverts back to the executable state, or if it was recorded with
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
, it moves to the
invalid state.
A synchronization command should be used to detect
when this occurs.
Invalid
Some operations, such as modifying or deleting a resource that was used in a command recorded to a command buffer, will transition the state of that command buffer into the invalid state. Command buffers in the invalid state can only be reset or freed.
Any given command that operates on a command buffer has its own requirements on what state a command buffer must be in, which are detailed in the valid usage constraints for that command.
Resetting a command buffer is an operation that discards any previously recorded commands and puts a command buffer in the initial state. Resetting occurs as a result of vkResetCommandBuffer or vkResetCommandPool, or as part of vkBeginCommandBuffer (which additionally puts the command buffer in the recording state).
Secondary command buffers can be recorded to a primary command buffer via vkCmdExecuteCommands. This partially ties the lifecycle of the two command buffers together - if the primary is submitted to a queue, both the primary and any secondaries recorded to it move to the pending state. Once execution of the primary completes, so it does for any secondary recorded within it. After all executions of each command buffer complete, they each move to their appropriate completion state (either to the executable state or the invalid state, as specified above).
If a secondary moves to the invalid state or the initial state, then all primary buffers it is recorded in move to the invalid state. A primary moving to any other state does not affect the state of a secondary recorded in it.
Resetting or freeing a primary command buffer removes the lifecycle linkage to all secondary command buffers that were recorded into it.
Command Pools
Command Buffer Allocation and Management
Command Buffer Recording
On some implementations, not using the
VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT
bit enables command
buffers to be patched in-place if needed, rather than creating a copy of the
command buffer.
If a command buffer is in the invalid, or
executable state, and the command buffer was allocated from a command pool
with the VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
flag set,
then vkBeginCommandBuffer
implicitly resets the command buffer,
behaving as if vkResetCommandBuffer
had been called with
VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT
not set.
After the implicit reset, commandBuffer
is moved to the
recording state.
If the commandBufferInheritance
feature is enabled, all graphics and compute state including bound pipeline
state, bound shader objects, bound vertex and index buffers, bound
descriptor sets and push constants, and all previously set dynamic state is
inherited by the secondary command buffer from the primary or secondary
command buffer that executes it.
Furthermore, all of the state set by this secondary command buffer is
inherited back to the primary or secondard command buffer that executes it.
If the commandBufferInheritance
feature is not enabled there is a limited amount of inheritance of state
into the secondary command buffer as specified below.
Once recording starts, an application records a sequence of commands
(vkCmd*
) to set state in the command buffer, draw, dispatch, and other
commands.
Several commands can also be recorded indirectly from VkBuffer
content, see Device-Generated Commands.
When a command buffer is in the executable state, it can be submitted to a queue for execution.
Command Buffer Submission
Submission can be a high overhead operation, and applications should
attempt to batch work together into as few calls to vkQueueSubmit
or vkQueueSubmit2
as possible.
If the pNext
chain of VkSubmitInfo includes a
VkPerformanceQuerySubmitInfoKHR structure, then the structure
indicates which counter pass is active for the batch in that submit.
Queue Forward Progress
When using binary semaphores, the application must ensure that command
buffer submissions will be able to complete without any subsequent
operations by the application on any queue.
After any call to vkQueueSubmit
(or other queue operation), for every
queued wait on a semaphore
created with a VkSemaphoreType of VK_SEMAPHORE_TYPE_BINARY
there must be a prior signal of that semaphore that will not be consumed by
a different wait on the semaphore.
When using timeline semaphores, wait-before-signal behavior is well-defined
and applications can submit work via vkQueueSubmit
defining a
timeline semaphore wait operation
before submitting a corresponding semaphore signal operation.
For each timeline semaphore wait
operation defined by a call to vkQueueSubmit
, the application must
ensure that a corresponding semaphore signal operation is executed before forward progress can be
made.
If a command buffer submission waits for any events to be signaled, the application must ensure that command buffer submissions will be able to complete without any subsequent operations by the application. Events signaled by the host must be signaled before the command buffer waits on those events.
The ability for commands to wait on the host to set an events was originally added to allow low-latency updates to resources between host and device. However, to ensure quality of service, implementations would necessarily detect extended stalls in execution and timeout after a short period. As this period is not defined in the Vulkan specification, it is impossible to correctly validate any application with any wait period. Since the original users of this functionality were highly limited and platform-specific, this functionality is now considered defunct and should not be used.
Secondary Command Buffer Execution
Nested Command Buffers
In addition to secondary command buffer execution from primary command
buffers, an implementation may support nested command
buffers, which enable secondary command buffers to be executed from other
secondary command buffers.
If the nestedCommandBuffer
feature
is enabled, the implementation supports nested command
buffers.
Nested command buffer execution works the same as primary-to-secondary execution, except that it is subject to some additional implementation-defined limits.
Each secondary command buffer has a command buffer nesting
level, which is determined at vkEndCommandBuffer time and evaluated
at vkCmdExecuteCommands time.
A secondary command buffer that executes no other secondary command buffers
has a command buffer nesting level of zero.
Otherwise, the command buffer nesting level of a secondary
command buffer is equal to the maximum nesting level of all secondary
command buffers executed by that command buffer plus one.
Some implementations may have a limit on the maximum nesting level of
secondary command buffers that can be recorded.
This limit is advertised in maxCommandBufferNestingLevel
.
If the nestedCommandBufferRendering
feature is enabled, the implementation
supports calling vkCmdExecuteCommands inside secondary command buffers
recorded with VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT
.
If the nestedCommandBufferSimultaneousUse
feature is enabled, the
implementation supports calling vkCmdExecuteCommands with secondary
command buffers recorded with
VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT
.
Whenever vkCmdExecuteCommands is recorded inside a secondary command
buffer recorded with VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT
,
each member of pCommandBuffers
must have been recorded with a
VkCommandBufferBeginInfo with VkCommandBufferInheritanceInfo
compatible with the VkCommandBufferInheritanceInfo of the command
buffer into which the vkCmdExecuteCommands call is being recorded.
The VkCommandBufferInheritanceRenderingInfo structures are compatible
when the VkCommandBufferInheritanceRenderingInfo
::renderpass
are
compatible, or if they are
VK_NULL_HANDLE then the VkCommandBufferInheritanceRenderingInfo
members match, and all other members of
VkCommandBufferInheritanceRenderingInfo
match.
This requirement applies recursively, down to the most nested command buffer
and up to the command buffer where the render pass was originally begun.
Command Buffer Device Mask
Each command buffer has a piece of state storing the current device mask of the command buffer. This mask controls which physical devices within the logical device all subsequent commands will execute on, including state-setting commands, action commands, and synchronization commands.
Scissor, exclusive scissor, and viewport state (excluding the count of each) can be set to different values on each physical device (only when set as dynamic state), and each physical device will render using its local copy of the state. Other state is shared between physical devices, such that all physical devices use the most recently set values for the state. However, when recording an action command that uses a piece of state, the most recent command that set that state must have included all physical devices that execute the action command in its current device mask.
The command buffer’s device mask is orthogonal to the
pCommandBufferDeviceMasks
member of VkDeviceGroupSubmitInfo.
Commands only execute on a physical device if the device index is set in
both device masks.