Fences can be used by the host to determine completion of execution of queue operations.
A fence’s status is always either signaled or unsignaled. The host can poll the status of a single fence, or wait for any or all of a group of fences to become signaled.
Fences are represented by VkFence
handles:
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFence)
To create a new fence object, use the command
VkResult vkCreateFence( VkDevice device, const VkFenceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence);
device
is the logical device that creates the fence.
pCreateInfo
points to a VkFenceCreateInfo
structure
specifying the state of the fence object.
pAllocator
controls host memory allocation as described in the
Memory Allocation chapter.
pFence
points to a handle in which the resulting fence object is
returned.
The VkFenceCreateInfo
structure is defined as:
typedef struct VkFenceCreateInfo { VkStructureType sType; const void* pNext; VkFenceCreateFlags flags; } VkFenceCreateInfo;
flags
defines the initial state and behavior of the fence.
Bits which can be set include:
typedef enum VkFenceCreateFlagBits { VK_FENCE_CREATE_SIGNALED_BIT = 0x00000001, } VkFenceCreateFlagBits;
If flags
contains VK_FENCE_CREATE_SIGNALED_BIT
then the fence
object is created in the signaled state.
Otherwise it is created in the unsignaled state.
To destroy a fence, call:
void vkDestroyFence( VkDevice device, VkFence fence, const VkAllocationCallbacks* pAllocator);
device
is the logical device that destroys the fence.
fence
is the handle of the fence to destroy.
pAllocator
controls host memory allocation as described in the
Memory Allocation chapter.
To query the status of a fence from the host, use the command
VkResult vkGetFenceStatus( VkDevice device, VkFence fence);
device
is the logical device that owns the fence.
fence
is the handle of the fence to query.
Upon success, vkGetFenceStatus
returns the status of the fence, which
is one of:
VK_SUCCESS
indicates that the fence is signaled.
VK_NOT_READY
indicates that the fence is unsignaled.
To reset the status of one or more fences to the unsignaled state, use the command:
VkResult vkResetFences( VkDevice device, uint32_t fenceCount, const VkFence* pFences);
device
is the logical device that owns the fences.
fenceCount
is the number of fences to reset.
pFences
is a pointer to an array of fenceCount
fence handles
to reset.
If a fence is already in the unsignaled state, then resetting it has no effect.
Fences can be signaled by including them in a queue submission command, defining a queue operation to signal that fence.
This fence signal operation defines the first half of a memory dependency,
guaranteeing that all memory accesses defined by the queue submission are
made available, and that queue operations described by that submission have
completed execution.
This half of the memory dependency does not include host availability of
memory accesses.
The second half of the dependency can be defined by vkWaitForFences
.
Fence signal operations for vkQueueSubmit
additionally include all
queue operations previously submitted via vkQueueSubmit
in their half
of a memory dependency.
To cause the host to wait until any one or all of a group of fences is signaled, use the command:
VkResult vkWaitForFences( VkDevice device, uint32_t fenceCount, const VkFence* pFences, VkBool32 waitAll, uint64_t timeout);
device
is the logical device that owns the fences.
fenceCount
is the number of fences to wait on.
pFences
is a pointer to an array of fenceCount
fence
handles.
waitAll
is the condition that must be satisfied to successfully
unblock the wait.
If waitAll
is VK_TRUE
, then the condition is that all fences
in pFences
are signaled.
Otherwise, the condition is that at least one fence in pFences
is
signaled.
timeout
is the timeout period in units of nanoseconds.
timeout
is adjusted to the closest value allowed by the
implementation-dependent timeout accuracy, which may be substantially
longer than one nanosecond, and may be longer than the requested
period.
If the condition is satisfied when vkWaitForFences
is called, then
vkWaitForFences
returns immediately.
If the condition is not satisfied at the time vkWaitForFences
is
called, then vkWaitForFences
will block and wait up to timeout
nanoseconds for the condition to become satisfied.
If timeout
is zero, then vkWaitForFences
does not wait, but
simply returns the current state of the fences.
VK_TIMEOUT
will be returned in this case if the condition is not
satisfied, even though no actual wait was performed.
If the specified timeout period expires before the condition is satisfied,
vkWaitForFences
returns VK_TIMEOUT
.
If the condition is satisfied before timeout
nanoseconds has expired,
vkWaitForFences
returns VK_SUCCESS
.
vkWaitForFences
defines the second half of a memory dependency with
the host, for each fence being waited on.
The memory dependency defined by signaling a fence and waiting on the host
does not guarantee that the results of memory accesses will be visible to
the host, or that the memory is available.
To provide that guarantee, the application must insert a memory barrier
between the device writes and the end of the submission that will signal the
fence, with dstAccessMask
having the VK_ACCESS_HOST_READ_BIT
bit
set, with dstStageMask
having the VK_PIPELINE_STAGE_HOST_BIT
bit
set, and with the appropriate srcStageMask
and srcAccessMask
members set to guarantee completion of the writes.
If the memory was allocated without the
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
set, then
vkInvalidateMappedMemoryRanges
must be called after the fence is
signaled in order to ensure the writes are visible to the host, as described
in Host Access to Device Memory Objects.