6.2. Fences

Fences are a synchronization primitive that can be used to insert a dependency from a queue to the host. Fences have two states - signaled and unsignaled. A fence can be signaled as part of the execution of a queue submission command. Fences can be unsignaled on the host with vkResetFences. Fences can be waited on by the host with the vkWaitForFences command, and the current state can be queried with vkGetFenceStatus.

Fences are represented by VkFence handles:

 

VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFence)

To create a fence, call:

 

VkResult vkCreateFence(
    VkDevice                                    device,
    const VkFenceCreateInfo*                    pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkFence*                                    pFence);

The VkFenceCreateInfo structure is defined as:

 

typedef struct VkFenceCreateInfo {
    VkStructureType       sType;
    const void*           pNext;
    VkFenceCreateFlags    flags;
} VkFenceCreateInfo;

To destroy a fence, call:

 

void vkDestroyFence(
    VkDevice                                    device,
    VkFence                                     fence,
    const VkAllocationCallbacks*                pAllocator);

To query the status of a fence from the host, call:

 

VkResult vkGetFenceStatus(
    VkDevice                                    device,
    VkFence                                     fence);

Upon success, vkGetFenceStatus returns the status of the fence object, with the following return codes:

Table 6.3. Fence Object Status Codes

Status Meaning

VK_SUCCESS

The fence specified by fence is signaled.

VK_NOT_READY

The fence specified by fence is unsignaled.


If a queue submission command is pending execution, then the value returned by this command may immediately be out of date.

To set the state of fences to unsignaled from the host, call:

 

VkResult vkResetFences(
    VkDevice                                    device,
    uint32_t                                    fenceCount,
    const VkFence*                              pFences);

When vkResetFences is executed on the host, it defines a fence unsignal operation for each fence, which resets the fence to the unsignaled state.

If any member of pFences is already in the unsignaled state when vkResetFences is executed, then vkResetFences has no effect on that fence.

When a fence is submitted to a queue as part of a queue submission command, it defines a memory dependency on the batches that were submitted as part of that command, and defines a fence signal operation which sets the fence to the signaled state.

The first synchronization scope includes every batch submitted in the same queue submission command. Fence signal operations that are defined by vkQueueSubmit additionally include all previous queue submissions to the same queue via vkQueueSubmit in the first synchronization scope.

The second synchronization scope only includes the fence signal operation.

The first access scope includes all memory access performed by the device.

The second access scope is empty.

To wait for one or more fences to enter the signaled state on the host, call:

 

VkResult vkWaitForFences(
    VkDevice                                    device,
    uint32_t                                    fenceCount,
    const VkFence*                              pFences,
    VkBool32                                    waitAll,
    uint64_t                                    timeout);

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.

An execution dependency is defined by waiting for a fence to become signaled, either via vkWaitForFences or by polling on vkGetFenceStatus.

The first synchronization scope includes only the fence signal operation.

The second synchronization scope includes the host operations of vkWaitForFences or vkGetFenceStatus indicating that the fence has become signaled.

[Note]Note

Signaling a fence and waiting on the host does not guarantee that the results of memory accesses will be visible to the host. 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.