Implementing eglSwapBuffers

This topic explains how to implement eglSwapBuffers() on the Symbian platform. It provides information about how to handle a window resize and the preserve buffer option.

Promoting buffers to the screen

EGL composition surfaces typically have two or more buffers, known as the front and back buffers. Call RSurfaceUpdateSession::SubmitUpdate() to initiate the composing of a specified buffer of a composition surface to the screen. This is an asynchronous operation. If you want to be notified when it has finished, call RSurfaceUpdateSession::NotifyWhenAvailable() immediately before you call SubmitUpdate().

RSurfaceUpdateSession::NotifyWhenAvailable() takes a TRequestStatus, which you can wait on in the usual way—for example, by using an active object or User::WaitForRequest().

If a TRequestStatus is waiting for the first buffer to complete composition, you need to submit the second buffer before the first buffer's TRequestStatus is signaled (unless there is an error). This means that there is interleaving between the two buffers.

Synchronization and threads

First let us consider a scenario that illustrates how eglSwapBuffers() works. The composition surface has two buffers. The front buffer (Buffer 0) is in the process of being displayed and the back buffer (Buffer 1) is receiving drawing commands for the next animation frame. The client calls eglSwapBuffers() when all of the drawing commands for the next frame have been issued.

eglSwapBuffers() can promote Buffer 1 to the screen by calling RSurfaceUpdateSession::SubmitUpdate(), passing in its index. This makes Buffer 1 the front buffer and Buffer 0 should become the new back buffer and all drawing commands should be directed to it.

However, suppose the application runs faster than the composition engine. Buffer 0 cannot become the new back buffer until its composition is complete. You therefore need to wait for Buffer 0 to complete composition before allowing eglSwapBuffers() to return. This means that after you submit Buffer 1 for composition, you must wait for Buffer 0 (not Buffer 1) to complete composition before returning.

In practice it may not be possible to use active objects to do the waiting because eglSwapBuffers() must block on the TRequestStatus supplied to RSurfaceUpdateSession::SubmitUpdate(). Using a nested CActiveScheduler is not recommended because it can cause re-entrant issues. A call to User::WaitForRequest() may therefore be required. However, doing this from within a thread that has an active scheduler can cause stray signal panics (because each buffer has a TRequestStatus but we only wait for one of these at a time). EGL has no control over whether the thread has an active scheduler because the thread is owned by the application.

A possible solution is to create a separate worker thread that is owned by the EGL implementation and has no active scheduler. This thread calls RSurfaceUpdateSession::NotifyWhenAvailable() and RSurfaceUpdateSession::SubmitUpdate(). It can safely call User::WaitForRequest() to wait for each TRequestStatus to be signaled, because the thread is guaranteed not to have an active scheduler. Signaling based on semaphores can be used to control interaction between the thread that calls eglSwapBuffers() and the worker thread that owns the RSurfaceUpdateSession.

Handling a window resize event

A change in the size of an RWindow can be detected in eglSwapBuffers(), by calling RWindow::Size() and comparing the result with the previously recorded size. If the values differ, a resize has occurred. A typical approach on a resize is as follows (and is illustrated in the following diagram):

  1. Promote the current back buffer to the front buffer for display as usual.

  2. Create a new composition surface with the new dimensions and with one of its buffers designated as the back buffer to receive the next set of drawing commands.

  3. On the next call to eglSwapBuffers(), register the new composition surface as the window's background surface and promote its back buffer to the front buffer for display. (This step is marked with the number 3 in the following diagram)

  4. Close the previous composition surface.

Figure 1. The buffers in the typical approach to handling a window resize event

Handling preserve buffers

When the preserve buffer option is in use, a typical implementation promotes the current back buffer to the front buffer for display as usual, and copies the back buffer content to the new back buffer. The client application then provides incremental drawing operations on the new back buffer. On the next call to eglSwapBuffers(), the back buffer is again promoted for display and the content is preserved.

The following diagram shows the scenario of a client application drawing A and later B on a surface with the preserve buffers enabled. Notice that eglSwapBuffers() promotes the back buffer to the front buffer and then immediately copies the contents of the newly promoted buffer to the new back buffer.

Figure 2. Handling preserve buffers

Handling a window resize event with the preserve buffer option

If there is a window resize event when the preserve buffer option is in use, eglSwapBuffers() creates a new surface and typically copies into it any pixels that overlap the previous surface. To illustrate how this works, consider the following scenario:

  1. A window is a wide rectangle showing an A.

  2. The application draws a B so that the window shows AB.

  3. The user resizes the window to a tall thin rectangle which can only show A.

  4. After calling eglSwapBuffers(), the application draws a C below the A.

The following diagram illustrates a typical implementation. There are more details below the diagram.

Figure 3. Handling a window resize with the preserve buffer option

The first eglSwapBuffers() call (marked X in the diagram above) does the following:

  1. Promotes the current back buffer (showing AB) to the front buffer for display as usual.

  2. Copies the back buffer content to the new back buffer (showing AB).

  3. Creates a new surface with the new dimensions.

  4. Designates one of the new buffers as the back buffer and copies the overlapping pixels into it. The new back buffer is then ready to receive new drawing operations.

The Window Server continues to use the old surface s1 until the next call to eglSwapBuffers().

The second eglSwapBuffers() call (marked Y in the diagram above) does the following:

  1. Promotes the back buffer (showing A and C) to the front buffer for display as usual.

  2. Copies the back buffer content to the new back buffer (showing A and C).

  3. Destroys the old surface s1.

The Window Server now uses the new surface s2.