Rendering to an RWindow Tutorial

This tutorial describes the high level steps that you need to take when writing applications using EGL to create a window surface for on-screen rendering. The code snippets use C style conventions. To avoid unnecessary complexity, they do not contain complete error handling.

Required background

This tutorial assumes a background knowledge of the following:

Procedure

  1. You must get and initialize the EGLDisplay prior to calling most other EGL or client API functions. You only need to do this once.

    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(display, NULL, NULL); 
    
  2. EGL has the concept of current rendering API. To make one of the EGL client APIs the current rendering API for the thread, call eglBindAPI() passing EGL_OPENVG_API or EGL_OPENGLES_API as required. For example:

    // Set OpenGL ES as the current rendering API for the thread
    eglBindAPI(EGL_OPENGLES_API); 
    
  3. Use the eglChooseConfig() method to get the EGLConfig that matches a list of attributes. In order to render to an RWindow, these must include the EGL_WINDOW_BIT.

    // List of attributes to pass to eglChooseConfig.
    const EGLint    KColorRGB565AttribList[] =
        {
        EGL_RED_SIZE,         5,
        EGL_GREEN_SIZE,       6,
        EGL_BLUE_SIZE,        5,
        EGL_SURFACE_TYPE,     EGL_WINDOW_BIT,
        EGL_RENDERABLE_TYPE,  EGL_OPENGL_ES_BIT,
        EGL_NONE
        };
        
    EGLint numConfigs;
    EGLConfig chosenConfig = 0;
        
    // Get the config to use.
    eglChooseConfig(display, KColorRGB565AttribList, &chosenConfig, 1, &numConfigs);
        
    if (numConfigs == 0)
        {
        RDebug::Printf("No matching configs found");
        User::Leave(KErrNotSupported);
        }

    Note: By default the EGL_SURFACE_TYPE has the EGL_SWAP_BEHAVIOR_PRESERVED_BIT set for window surfaces in order to support the preserve buffer option. You can set this explicitly like this:

    EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_SWAP_BEHAVIOR_PRESERVED_BIT,
  4. To create an on-screen rendering surface, you first need to create a Symbian Window Server window (RWindow). This window is then passed as the third argument to eglCreateWindowSurface() along with an EGLConfig that has the EGL_WINDOW_BIT set.

    // Create a window surface to draw to.
    winSurface = eglCreateWindowSurface(display, chosenConfig, &iWindow, NULL);
  5. Set the surface attribute EGL_SWAP_BEHAVIOR to EGL_BUFFER_PRESERVED. This step is only required if you want to enable the preserve buffer option.

    EGLBoolean queryResult = eglSurfaceAttrib(display, winSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
  6. To create a rendering context for the current rendering API, call the eglCreateContext() with the share_context parameter set to EGL_NO_CONTEXT. If this method succeeds, it initializes the context to the initial state defined for the current rendering API, and returns a handle to it.

    // Create a context to store the current rendering API settings.
    contextGL = eglCreateContext(display, chosenConfig, EGL_NO_CONTEXT, NULL);
  7. In order to allow the context to render to a given surface, you need to call eglMakeCurrent(). After doing this you can call EGL client functions, which need a current surface and context.

    // Make the context current on the surface and the display
    eglMakeCurrent(display, winSurface, winSurface, contextGL); 
    
  8. You are now ready to perform your OpenGL ES drawing commands.

    The main thing to understand is that the EGL window surface is double-buffered. This means that the drawing commands are drawn into a back buffer and you need to call eglSwapBuffers() in order to make the drawing appear on the screen in the window. This mechanism has the advantage that it stops flicker.

    eglSwapBuffers(display, winSurface);

    Because you enabled the preserve buffer option in step 5, the eglSwapBuffers() promotes the back buffer to the front for display as usual, and also copies the content to the new back buffer. This means the client application can provide incremental drawing commands instead of creating the entire new scene.

  9. To release the current context and surface without assigning a new one, set the context parameter to EGL_NO_CONTEXT and set the draw and read surfaces to EGL_NO_SURFACE. This flushes the current context for the current rendering API and marks it as no longer current. The current rendering API does not have a current context after eglMakeCurrent() returns.

    eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  10. You destroy the rendering context by calling eglDestroyContext(). All resources associated with the context are marked for deletion as soon as possible. If the context is current to any thread, resources are not actually released while the context remains current.

    eglDestroyContext(display, contextGL); 
    
  11. You destroy the EGLSurface by calling eglDestroySurface(). All resources associated with the surface are marked for deletion as soon as possible. If the surface is current to any thread, the resources are not actually released while the surface remains current.

    eglDestroySurface(display, winSurface); 
    
  12. Perform final cleanup by first terminating the EGLDisplay using eglTerminate() and then releasing any resources still held by EGL by calling eglReleaseThread().

    eglTerminate(display);
    eglReleaseThread();

Related concepts