|Core in version||4.5|
|Core since version||3.2|
|Core ARB extension||ARB_sync|
Sync Objects are objects that are used to synchronize the activity between the GPU and the application.
glFinish is a start to synchronization, but sync objects allow for much finer grained control.
Sync Object Conventions
Sync objects do not follow the standard conventions for OpenGL Objects. Regular OpenGL objects use GLuint names; Sync objects are defined as a pointer to an opaque type. This is defined as:
typedef struct __GLsync *GLsync;
Sync objects have a type. Each sync object type has its own unique object creation function, though they all create GLsync objects. Thus, these objects are not created with the usual pair of
glGen*/glDelete* functions. There is a generic
glDeleteSync function to delete any kind of sync object.
Sync objects are never bound to the context, nor do they encapsulate state the way normal GL objects do. These are not OpenGL objects.
The purpose of sync objects is to synchronize the CPU with the GPU's actions. To do this, sync objects have the concept of a current status. The status of a sync object can be signaled or unsignaled; this state represents some condition of the GPU, depending on the particular type of sync object and how it was used. This is similar to how mutual exclusives are used to synchronize behavior between threads; when a mutex becomes signaled, it allows other threads that are waiting on it to activate.
To block all CPU operations until a sync object is signaled, you call this function:
enum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
This function will not return until one of two things happens: the
sync object parameter becomes signaled, or a number of nanoseconds greater than or equal to the
timeout parameter passes. If
timeout is zero, the function will simply check to see if the sync object is signaled and return immediately. Note that the fact that
timeout is in nanoseconds does not imply that this function has true nanosecond granularity in its timeout; you are only guaranteed that at least that much time will pass.
The return value explains why
glClientWaitSync returned. If it returns GL_ALREADY_SIGNALED, then the sync object was signaled before the function was called. If it returns GL_TIMEOUT_EXPIRED, then the sync object did not signal within the given timeout period. If GL_CONDITION_SATISFIED is returned, then the sync object was signaled within the given timeout period. If a
glError occurred, then GL_WAIT_FAILED will be returned in addition to raising an error.
flags parameter controls how OpenGL's command queue is flushed. If you pass GL_SYNC_FLUSH_COMMANDS_BIT, then the equivalent of a
glFlush will be issued before blocking on the sync object. This is done to prevent a certain kind of infinite loop due to the GPU's command queue being currently too full to accept the sync object. You only need to pass the flag the first time.
There is another function for waiting on sync objects:
void glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
Recall the discussion in the article on Synchronization about the difference between the GPU's command queue and the driver's internal command buffer. What
glWaitSync does is prevent the driver from adding any commands to the GPU's command queue until this sync object is signaled. This function does not wait for this to happen.
The driver will still put commands in its internal buffer. But none of them will be seen by the GPU until this sync object is signaled.
You need to perform a
glFlush before calling this, to ensure that the sync object is in the GPU's command queue. If you don't, then you may create an infinite loop. Since glWaitSync prevents the driver from adding any commands to the GPU command queue, this would include the sync object itself if it has not yet been added to the queue. This function does not take the GL_SYNC_FLUSH_COMMANDS_BIT, so you have to do it manually.
Sync Object Types
As previously mentioned, sync objects have a specific type, which defines their signaling behavior. Currently, there is only one type: fences.
A fence is a sync object that is added to the OpenGL command stream. It starts unsignaled, and becomes signaled when the GPU executes and completes the fence. Because OpenGL must execute and complete commands in order, when a fence becomes signaled, you can be certain that the GPU has completed all OpenGL commands issued before the fence was created.
A fence is created with this function:
GLsync glFenceSync(GLenum condition, GLbitfield flags)
This function not only creates a fence, but it adds it to the command stream. So only call it in the location you want to place the fence.
The only available value for
condition is GL_SYNC_GPU_COMMANDS_COMPLETE. This causes the fence to be signaled when the GPU has completed all previously issued commands. Currently, the
flags field has no possible parameters; it should be 0. The field exists in case of future extensions to this functionality.
The fact that fences are signaled after the commands have completed, not just are started, means that it allows you to know when the GPU has finished using certain resources. For example, if you want to access a buffer object, but do not want to block the CPU until the GPU has finished using it, then you can set a fence after the last command that used the buffer object. It is also useful for knowing when a pixel buffer or feedback buffer has the data you requested.
NVIDIA hardware has had the NV_fence extension for a long time, since the GeForce 256 days. This extension provides the effect of fence sync objects, though with a different API.