Sync Objects and Fences - OpenGL ES 3.0: Programming Guide, Second Edition (2014)

OpenGL ES 3.0: Programming Guide, Second Edition (2014)

Chapter 13. Sync Objects and Fences

OpenGL ES 3.0 provides a mechanism for the application to wait until a set of OpenGL ES operations have finished executing on the GPU. You can synchronize GL operations among multiple graphics contexts and threads, which can be important in many advanced graphics applications. For example, you may want to wait for transform feedback results before using those results in your applications.

In this chapter, we discuss the flush command, the finish command, and sync objects and fences, including why they are useful and how to use them to synchronize operations in the graphics pipeline. Finally, we conclude with an example of using sync objects and fences.

Flush and Finish

The OpenGL ES 3.0 API inherits the OpenGL client–server model. The application, or client, issues commands, and these commands are processed by the OpenGL ES implementation or server. In OpenGL, the client and the server can reside on different machines over a network. OpenGL ES also allows the client and server to reside on different machines but because OpenGL ES targets handheld and embedded platforms, the client and server will typically be on the same device.

In the client–server model, the commands issued by the client do not necessarily get sent to the server immediately. If the client and server are operating over a network, it will be very inefficient to send individual commands over the network. Instead, the commands can be buffered on the client side and then issued to the server at a later point in time. To support this approach, a mechanism is needed that lets the client know when the server has completed execution of previously submitted commands. Consider another example where multiple OpenGL ES contexts (each current to a different thread) are sharing objects. To synchronize correctly between these contexts, it is important that commands from context A be issued to the server before commands from context B, which depends on OpenGL ES state modified by context A. The glFlush command is used to flush any pending commands in the current OpenGL ES context and issue them to the server. Note that glFlush only issues the commands to the server; it does not wait for them to complete. If the client requires that the commands be completed, the glFinish command should be used. We do not recommend using glFinish unless absolutely necessary. Because glFinish does not return until all queued commands in the context have been completely processed by the server, calling glFinish can adversely impact performance by forcing the client and the server to synchronize their operations.

Why Use a Sync Object?

OpenGL ES 3.0 introduces a new feature, called a fence, that provides a way for the application to inform the GPU to wait until a set of OpenGL ES operations have finished executing before queuing up more for execution. You can insert a fence command into the GL command stream and associate it with a sync object to be waited on.

If we compare using sync objects to the glFinish command, sync objects are more efficient, as you can wait on partial completions of the GL command stream. By comparison, calling the glFinish command may reduce the performance of your applications, as this command will empty the graphics pipeline.

Creating and Deleting a Sync Object

To insert a fence command to the GL command stream and create a sync object, you can call the following function:

Image

When a sync object is first created, its status is unsignaled. After the specified condition is satisfied by the fence command, then its status becomes signaled. Because sync objects cannot be reused, you must create one sync object for each synchronization operation.

To delete a sync object, you can call the following function:

Image

The deletion operation does not occur immediately, as the sync object will be deleted only when no other operation is waiting for it. Thus you can call the glDeleteSync command right after waiting for the sync object, which is described next.

Waiting for and Signaling a Sync Object

You can block the client and wait for a sync object to be signaled with the following call:

Image

If the sync object is already at a signaled state, the glClientWaitSync command will return immediately. Otherwise, the call will block and wait up to timeout nanoseconds for the sync object to be signaled.

The glClientWaitSync function can return the following values:

• GL_ALREADY_SIGNALED: the sync object was already at the signaled state when the function was called.

• GL_TIMEOUT_EXPIRED: the sync object did not become signaled after timeout nanoseconds passed.

• GL_CONDITION_SATISFIED: the sync object was signaled before the timeout expired.

• GL_WAIT_FAILED: an error occurred.

The glWaitSync function is similar to the glClientWaitSync function, except that the function returns immediately and blocks the GPU until the sync object is signaled.

Image

Example

Example 13-1 shows an example of inserting a fence command after transform feedback buffers are created (see the EmitParticles function implementation) and blocking the GPU to wait on the transform feedback results before drawing them (see the Draw function implementation). The EmitParticles function and Draw function are executed by two separate CPU threads.

This code segment is a part of the particle system with transform feedback example that will be described in more detail in Chapter 14, “Advanced Programming with OpenGL ES 3.0.”

Example 13-1 Inserting a Fence Command and Waiting for Its Result in Transform Feedback Example


void EmitParticles ( ESContext *esContext, float deltaTime )
{
// Many codes skipped . . .

// Emit particles using transform feedback
glBeginTransformFeedback ( GL_POINTS );
glDrawArrays ( GL_POINTS, 0, NUM_PARTICLES );
glEndTransformFeedback ( );

// Create a sync object to ensure transform feedback results
// are completed before the draw that uses them
userData->emitSync =
glFenceSync ( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 );

// Many codes skipped . . .
}

void Draw ( ESContext *esContext )
{
UserData *userData = ( UserData* ) esContext->userData;

// Block the GL server until transform feedback results
// are completed
glWaitSync ( userData->emitSync, 0, GL_TIMEOUT_IGNORED );
glDeleteSync ( userData->emitSync );

// Many codes skipped . . .

glDrawArrays ( GL_POINTS, 0, NUM_PARTICLES );
}


Summary

In this chapter, you learned about efficient primitives for synchronizing within the host application and GPU execution in OpenGL ES 3.0. We discussed how to use the sync objects and fences. In the next chapter, you will see many advanced rendering examples that tie together all the concepts you have learned so far throughout the book.