6.1. Fences

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.

To create a new fence object, use the command

 

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;

The flags member of the VkFenceCreateInfo structure pointed to by pCreateInfo contains flags defining the initial state and behavior of the fence. The flags are:

 

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);

To query the status of a fence from the host, use the command

 

VkResult vkGetFenceStatus(
    VkDevice                                    device,
    VkFence                                     fence);

Upon success, vkGetFenceStatus returns the status of the fence, which is one of:

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);

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);

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.