Vulkan Timeline Semaphores are a synchronization primitive accessible both from the device and the host. A timeline semaphore represents a monotonically increasing 64-bit unsigned value. Whereas binary Vulkan semaphores are waited on just to become signaled, timeline semaphores are waited on to reach a specific value. Once a timeline semaphore reaches a certain value, it is considered signaled for every value less than or equal to that value. vkWaitSemaphores
is used to wait for semaphores on the host. It can operate in one of two modes: “wait for all” and “wait for any”.
In SwiftShader, Vulkan Timeline Semaphores are implemented as an unsigned 64-bit integer protected by a mutex with changes signaled by a condition variable. Waiting for all timeline semaphores in a set is implemented by simply waiting for each of the semaphores in turn. Waiting for any semaphore in a set is a bit more complex.
A “wait for any” of a set of semaphores is represented by a TimelineSemaphore::WaitForAny
object. Additionally, TimelineSemaphore
contains an internal list of all WaitForAny
objects that wait for it, as well as for which values they wait. When signaled, the timeline semaphore looks through this list and, in turn, signals any WaitForAny
objects that are waiting for a value less than or equal to the timeline semaphore's new value.
A WaitForAny
object is created from a VkSemaphoreWaitInfo
. During construction, it checks the value of each timeline semaphore provided against the value for which it is waiting. If it has not yet been reached, the wait object registers itself with the timeline semaphore. If it has been reached, the wait object is immediately signaled and no further timeline semaphores are checked.
Once a WaitForAny
object is signaled, it remains signaled. There is no way to change what semaphores or values to wait for after construction. Any subsequent calls to wait()
will return VK_SUCCESS
immediately.
When a WaitForAny
object is destroyed, it unregisters itself from every TimelineSemaphore
it was waiting for. It is expected that the number of concurrent waits are few, and that the wait objects are short-lived, so there should not be a build-up of wait objects in any timeline semaphore.