Sync Object

From OpenGL Wiki
(Redirected from Sync Objects)
Jump to navigation Jump to search
Sync Object
Core in version 4.6
Core since version 3.2
Core ARB extension ARB_sync
Vendor extension NV_fence

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[edit]

Sync objects do not follow the standard OpenGL Object model. 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.

Synchronization[edit]

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 an OpenGL Error occurred, then GL_WAIT_FAILED will be returned in addition to raising an error.

The 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.

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 halt the CPU.

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 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 with a manual glFlush call.

Flushing and contexts[edit]

It is important that syncs are properly flushed into the GPU's command queue. Without proper flushing, the sync object may never be signaled. This is why glClientWaitSync takes a parameter that causes an automatic flush.

But there is another problem. Flushing is ultimately a context-specific action, as it flushes the current context's command queue. Therefore, not only must you perform a flush, you must perform that flush on the same context that created the sync object.

The flush can be performed via glFlush. But a flush can also be performed by passing GL_SYNC_FLUSH_COMMANDS_BIT to glClientWaitSync. In OpenGL 4.5, this flush is made special. If it's the first time that you are waiting on that particular sync object, and the wait is in the same context that created the sync object, the flush will behave as if you had issued it immediately after the sync object. So if you had issued other OpenGL commands after the sync object was created, they will not be flushed.

After the first flush, you do not need to flush the pipeline again to ensure that the sync object becomes signaled. So if you want to wait on a sync object multiple times, you should make sure to not use the flush bit again (unless you're in GL 4.5 in the same thread, where this automatically works).

Sync Object Types[edit]

As previously mentioned, sync objects have a specific type, which defines their signaling behavior. Currently, there is only one type: fences.

Fence[edit]

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.

Legacy Sync[edit]

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.

Reference[edit]