Chapter 3. An Introduction to EGL

In Chapter 2, “Hello, Triangle: An OpenGL ES 2.0 Example,” we drew a triangle into a window using OpenGL ES 2.0, but we used some custom functions of our own design to open and manage the window. Although that simplifies our examples, it obscures how you might need to work with OpenGL ES 2.0 on your own systems.

As part of the family of APIs provided by the Khronos Group for developing content, a (mostly) platform-independent API, EGL, is available for managing drawing surfaces (windows are just one type; we’ll talk about others later). EGL provides the mechanisms for the following:

  • Communicating with the native windowing system of your system.

  • Querying the available types and configurations of drawing surfaces.

  • Creating drawing surfaces.

  • Synchronizing rendering between OpenGL ES 2.0 and other graphics-rendering APIs (like OpenVG, or the native drawing commands of your windowing system).

  • Managing rendering resources such as texture maps.

We introduce the fundamentals required to open a window in this chapter. As we describe other operations, such as creating a texture map, we discuss the necessary EGL commands.

Communicating with the Windowing System

EGL provides a “glue” layer between OpenGL ES 2.0 (and other Khronos graphics APIs) and the native windowing system running on your computer, like the X Window System common on GNU/Linux systems, Microsoft Windows, or Mac OS X’s Quartz. Before EGL can determine what types of drawing surfaces, or any other characteristics of the underlying system for that matter, it needs to open a communications channel with the windowing system.

Because every windowing system has different semantics, EGL provides a basic opaque type—the EGLDisplay—that encapsulates all of the system dependencies for interfacing with the native windowing system. The first operation that any application using EGL will need to do is create and initialize a connection with the local EGL display. This is done in a two-call sequence, as shown in Example 3-1.

Example 3-1. Initializing EGL

EGLint  majorVersion;
EGLint  minorVersion;
EGLDisplay  display;

display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if(display == EGL_NO_DISPLAY)
{
   // Unable to open connection to local windowing system
}
if(!eglInitialize(display, &majorVersion, &minorVersion))
{
   // Unable to initialize EGL. Handle and recover
}

To open a connection to the EGL display server, call

EGLDisplay

eglGetDisplay(EGLNativeDisplayType display_id);

EGLNativeDisplayType is defined to match the native window system’s display type. On Microsoft Windows, for example, an EGLNativeDisplayType would be defined to be an HDC—a handle to the Microsoft Windows device context. However, to make it easy to move your code to different operating systems and platforms, the token EGL_DEFAULT_DISPLAY is accepted and will return a connection to the default native display, as we did.

If a display connection isn’t available, eglGetDisplay will return EGL_NO_DISPLAY. This error indicates that EGL isn’t available, and you won’t be able to use OpenGL ES 2.0.

Before we continue discussing more EGL operation, we need to briefly describe how EGL processes and reports errors to your application.

Checking for Errors

Most functions in EGL return EGL_TRUE when successful and EGL_FALSE otherwise. However, EGL will do more than just tell you if the call failed, it will record an error to indicate the reason for failure. However, that error code isn’t returned to you directly; you need to query EGL explicitly for the error code, which you can do by calling

EGLint

eglGetError();

You might wonder why this is a prudent approach, as compared to directly returning the error code when the call completes. Although we never encourage ignoring function return codes, allowing optional error code recovery reduces redundant code in applications verified to work properly. You should certainly check for errors during development and debugging, and all the time in critical applications, but once you’re convinced your application is working as expected, you can likely reduce your error checking.

Initializing EGL

Once you’ve successfully opened a connection, EGL needs to be initialized, which is done by calling

EGLBoolean

eglInitialize(EGLDisplay display, EGLint *majorVersion, EGLint *minorVersion);

This initializes EGL’s internal data structures and returns the major and minor version numbers of the EGL implementation. If EGL is unable to be initialized, this call will return EGL_FALSE, and set EGL’s error code to:

  • EGL_BAD_DISPLAY if display doesn’t specify a valid EGLDisplay.

  • EGL_NOT_INITIALIZED if the EGL cannot be initialized.

Determining the Available Surface Configurations

Once we’ve initialized EGL, we’re able to determine what types and configurations of rendering surfaces are available to us. There are two ways to go about this:

  1. Query every surface configuration and find the best choice ourselves.

  2. Specify a set of requirements and let EGL make a recommendation for the best match.

In many situations, the second option is simpler to implement, and most likely yields what you would have found using the first option. In either case, EGL will return an EGLConfig, which is an identifier to an EGL-internal data structure that contains information about a particular surface and its characteristics, such as number of bits for each color component, or if there’s a depth buffer associated with that EGLConfig. You can query any of the attributes of an EGLConfig, using the eglGetConfigAttribute function, which we describe later.

To query all EGL surface configurations supported by the underlying windowing system, call

EGLBoolean

eglGetConfigs(EGLDisplay display, EGLConfig *configs, EGLint maxReturnConfigs, EGLint *numConfigs);

which returns EGL_TRUE if the call succeeded.

There are two ways to call eglGetConfigs: First, if you specify NULL for the value of configs, the system will return EGL_TRUE and set numConfigs to the number of available EGLConfigs. No additional information about any of the EGLConfigs in the system is returned, but knowing the number of available configurations allows you to allocate enough memory to get the entire set of EGLConfigs, should you care to.

Alternatively, and perhaps more useful, is that you can allocate an array of uninitialized EGLConfig values, and pass those into eglGetConfigs as the configs parameter. Set maxReturnConfigs to the size of the array you allocated, which will also specify the maximum number of configs that will be returned. When the call completes, numConfigs will be updated with the number of entries in configs that were modified. You can then begin processing the list of returns, querying the characteristics of the configurations to determine which one matches our needs the best.

Querying EGLConfig Attributes

We now describe the values that EGL associates with an EGLConfig, and how you can retrieve those values.

An EGLConfig contains all of the information about a surface made available by EGL. This includes information about the number of available colors, additional buffers associated with the configuration (like depth and stencil buffers, which we discuss later), the type of surfaces, and numerous other characteristics. What follows is a list of all of the attributes that can be queried from an EGLConfig. We only discuss a subset of these in this chapter, but we provide the entire list in Table 3-1 as a reference.

Table 3-1. EGLConfig Attributes

Attribute

Description

Default Value

EGL_BUFFER_SIZE

Number of bits for all color components in the color buffer

0

EGL_RED_SIZE

Number of red bits in the color buffer

0

EGL_GREEN_SIZE

Number of green bits in the color buffer

0

EGL_BLUE_SIZE

Number of blue bits in the color buffer

0

EGL_LUMINANCE_SIZE

Number of luminance bits in the color buffer

0

EGL_ALPHA_SIZE

Number of alpha bits in the color buffer

0

EGL_ALPHA_MASK_SIZE

Number of alpha-mask bits in the mask buffer

0

EGL_BIND_TO_TEXTURE_RGB

True if bindable to RGB textures

EGL_DONT_CARE

EGL_BIND_TO_TEXTURE_RGBA

True if bindable to RGBA textures

EGL_DONT_CARE

EGL_COLOR_BUFFER_TYPE

Type of the color buffer: either EGL_RGB_BUFFER, or EGL_LUMINANCE_BUFFER

EGL_RGB_BUFFER

EGL_CONFIG_CAVEAT

Any caveats associated with the configuration

EGL_DONT_CARE

EGL_CONFIG_ID

The unique EGLConfig identifier value

EGL_DONT_CARE

EGL_CONFORMANT

True if contexts created with this EGLConfig are conformant

EGL_DEPTH_SIZE

Number of bits in the depth buffer

0

EGL_LEVEL

Frame buffer level

0

EGL_MAX_PBUFFER_WIDTH

Maximum width for a PBuffer created with this EGLConfig

EGL_MAX_PBUFFER_HEIGHT

Maximum height for a PBuffer created with this EGLConfig

EGL_MAX_PBUFFER_PIXELS

Maximum size of a PBuffer created with this EGLConfig

EGL_MAX_SWAP_INTERVAL

Maximum buffer swap interval

EGL_DONT_CARE

EGL_MIN_SWAP_INTERVAL

Minimum buffer swap interval

EGL_DONT_CARE

EGL_NATIVE_RENDERABLE

True if native rendering libraries can render into a surface created with EGLConfig

EGL_DONT_CARE

EGL_NATIVE_VISUAL_ID

Handle of corresponding native window system visual ID

EGL_DONT_CARE

EGL_NATIVE_VISUAL_TYPE

Type of corresponding native window system visual

EGL_DONT_CARE

EGL_RENDERABLE_TYPE

A bitmask composed of the tokens EGL_OPENGL_ES_BIT, EGL_OPENGL_ES2_BIT, or EGL_OPENVG_BIT that represent the rendering interfaces supported with the config

EGL_OPENGL_ES_BIT

EGL_SAMPLE_BUFFERS

Number of available multisample buffers

0

EGL_SAMPLES

Number of samples per pixel

0

EGL_STENCIL_SIZE

Number of bits in the stencil buffer

0

EGL_SURFACE_TYPE

Type of EGL surfaces supported. Can be any of EGL_WINDOW_BIT, EGL_PIXMAP_BIT, or EGL_PBUFFER_BIT

EGL_WINDOW_BIT

EGL_TRANSPARENT_TYPE

Type of transparency supported

EGL_NONE

EGL_TRANSPARENT_RED_VALUE

Red color value interpreted as transparent

EGL_DONT_CARE

EGL_TRANSPARENT_GREEN_VALUE

Green color value interpreted as transparent

EGL_DONT_CARE

EGL_TRANSPARENT_BLUE_VALUE

Blue color value interpreted as transparent.

EGL_DONT_CARE

Note: Various tokens do not have a default value mandated in the EGL specification, which are indicated by—for their default value.

To query a particular attribute associated with an EGLConfig, use

EGLBoolean

eglGetConfigAttrib(EGLDisplay display, EGLConfig config, EGLint attribute, EGLint *value);

which will return the value for the specific attribute of the associated EGLConfig. This allows you total control over which configuration you choose for ultimately creating rendering surfaces. However, looking at Table 3.1, you might be somewhat intimidated given the number of options. EGL provides another routine, eglChooseConfig, that allows you to specify what’s important for your application, and will return the best matching configuration to your requests.

Letting EGL Choose the Config

To have EGL make the choice of matching EGLConfigs, use

EGLBoolean

eglChooseChofig(EGLDispay display, const EGLint *attribList, EGLConfig *config, EGLint maxReturnConfigs, ELGint *numConfigs );

You need to provide a list of the attributes, with associated preferred values for all the attributes that are important for the correct operation of your application. For example, if you need an EGLConfig that supports a rendering surface having five bits red and blue, six bits green (the common “RGB 565” format), a depth buffer, and supporting OpenGL ES 2.0, you might declare the array shown in Example 3-2.

Example 3-2. Specifying EGL Attributes

EGLint attribList[] =
{
   EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
   EGL_RED_SIZE, 5,
   EGL_GREEN_SIZE, 6,
   EGL_BLUE_SIZE, 5,
   EGL_DEPTH_SIZE, 1,
   EGL_NONE
};

For values that aren’t explicitly specified in the attribute list, EGL will use their default value as specified in Table 3-1. Additionally, when specifying a numeric value for an attribute, EGL will guarantee the returned configuration will have at least that value as a minimum if there’s a matching EGLConfig available.

To use this set of attributes as a selection criteria, follow Example 3-3.

Example 3-3. Querying EGL Surface Configurations

const EGLint MaxConfigs = 10;
EGLConfig  configs[MaxConfigs];  // We'll only accept 10 configs

EGLint  numConfigs;
if(!eglChooseConfig(dpy, attribList, configs, MaxConfigs,
   &numConfigs))
{
   // Something didn't work ... handle error situation
}
else
{
   // Everything's okay. Continue to create a rendering surface
}

If eglChooseConfig returns successfully, a set of EGLConfigs matching your criteria will be returned. If more than one EGLConfig matches (with at most the maximum number of configurations you specify), eglChooseConfig will sort the configurations using the following ordering:

  1. By the value of EGL_CONFIG_CAVEAT. Precedence is given to configurations where there are no configuration caveats (when the value of EGL_CONFIG_CAVEAT is GL_NONE), then slow rendering configurations (EGL_SLOW_CONFIG), and finally nonconformant configurations (EGL_NON_CONFORMANT_CONFIG).

  2. By the type of buffer as specified by EGL_COLOR_BUFFER_TYPE.

  3. By the number of bits in the color buffer in descending sizes. The number of bits in a buffer depends on the EGL_COLOR_BUFFER_TYPE, and will be at least the value specified for a particular color channel. When the buffer type is EGL_RGB_BUFFER, the number of bits is computed as the total of EGL_RED_SIZE, EGL_GREEN_SIZE, and EGL_BLUE_SIZE. When the color buffer type is EGL_LUMINANCE_BUFFER, the number of bits is the sum of EGL_LUMINANCE_SIZE and EGL_ALPHA_SIZE.

  4. By the EGL_BUFFER_SIZE in ascending order.

  5. By the value of EGL_SAMPLE_BUFFERS in ascending order.

  6. By the number of EGL_SAMPLES in ascending order.

  7. By the value of EGL_DEPTH_SIZE in ascending order.

  8. By the value of the EGL_STENCIL_SIZE in ascending order.

  9. By the value of the EGL_ALHPA_MASK_SIZE (which is applicable only to OpenVG surfaces).

  10. By the EGL_NATIVE_VISUAL_TYPE in an implementation-dependent manner.

  11. By the value of the EGL_CONFIG_ID in ascending order.

Parameters not mentioned in this list are not used in the sorting process.

As mentioned in the example, if eglChooseConfig returns successfully, we have enough information to continue to create something to draw into. By default, if you don’t specify what type of rendering surface type you would like (by specifying the EGL_SURFACE_TYPE attribute), EGL assumes you want an on-screen window.

Creating an On-Screen Rendering Area: The EGL Window

Once we have a suitable EGLConfig that meets our requirements for rendering, we’re set to create our window. To create a window, call

EGLSurface

eglCreateWindowSurface(EGLDisplay display, EGLConfig config, EGLNatvieWindowType window, const EGLint *attribList);

This function takes our connection to the native display manager, and the EGLConfig that we obtained in the previous step. Additionally, it requires a window from the native windowing system that was created previously. Because EGL is a software layer between many different windowing systems and OpenGL ES 2.0, demonstrating how to create a native window is outside the scope of this guide. Please reference the documentation for your native windowing system to determine what’s required to create a window in that environment.

Finally, this call also takes a list of attributes; however this list differs from those shown in Table 3-1. Because EGL supports other rendering APIs (notably OpenVG), there are attributes accepted by eglCreateWindowSurface that don’t apply when working with OpenGL ES 2.0 (see Table 3-2). For our purposes, there is a single attribute that’s accepted by eglCreateWindowSurface, and it’s used to specify which buffer of the front- or back-buffer we’d like to render into.

Table 3-2. Attributes for Window Creating Using eglCreateWindowSurface

Token

Description

Default Value

EGL_RENDER_BUFFER

Specifies which buffer should be used for rendering (using the EGL_FRONT_BUFFER value), or back (EGL_BACK_BUFFER).

EGL_BACK_BUFFER

Note

For OpenGL ES 2.0 window rendering surfaces, only double-buffered windows are supported.

The attribute list might be empty (i.e., passing a NULL pointer as the value for attribList), or it might be a list populated with an EGL_NONE token as the first element. In such cases, all of the relevant attributes use their default values.

There are a number of ways that eglCreateWindowSurface could fail, and if any of them occur, EGL_NO_SURFACE is returned from the call, and the particular error is set. If this situation occurs, we can determine the reason for the failure by calling eglGetError, which will return one of the following reasons shown in Table 3-3.

Table 3-3. Possible Errors When eglCreateWindowSurface Fails

Error Code

Description

EGL_BAD_MATCH

This situation occurs when:

 
  • the attributes of the native window do not match those of the provided EGLConfig.

  • the provided EGLConfig doesn’t support rendering into a window (i.e., the EGL_SURFACE_TYPE attribute doesn’t have the EGL_WINDOW_BIT set.

EGL_BAD_CONFIG

This error is flagged if the provided EGLConfig is not supported by the system.

EGL_BAD_NATIVE_WINDOW

This error is specified if the provided native window handle is not valid.

EGL_BAD_ALLOC

This error occurs if eglCreateWindowSurface is unable to allocate the resources for the new EGL window, or if there is already an EGLConfig associated with the provided native window.

Putting this all together, our code for creating a window is shown in Example 3-4.

Example 3-4. Creating an EGL Window Surface

EGLRenderSurface  window;
EGLint attribList[] =
{
   EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
   EGL_NONE
);

window = eglCreateWindowSurface(dpy, window, config, attribList);

if(window == EGL_NO_SURFACE)
{
   switch(eglGetError())
   {
      case EGL_BAD_MATCH:
         // Check window and EGLConfig attributes to determine
         // compatibility, or verify that the EGLConfig
         // supports rendering to a window,
         break;

      case EGL_BAD_CONFIG:
         // Verify that provided EGLConfig is valid
         break;

      case EGL_BAD_NATIVE_WINDOW:
         // Verify that provided EGLNativeWindow is valid
         break;

      case EGL_BAD_ALLOC:
         // Not enough resources available. Handle and recover
         break;
   }
}

This creates a place for us to draw into, but we still have two more steps before we’ll be able to successfully use OpenGL ES 2.0 with our window. Windows, however, aren’t the only rendering surfaces that you might find useful. We introduce another type of rendering surface next before completing our discussion.

Creating an Off-Screen Rendering Area: EGL Pbuffers

In addition to being able to render into an on-screen window using OpenGL ES 2.0, you can also render into nonvisible off-screen surfaces called pbuffers (short for pixel buffer). Pbuffers can take full advantage of any hardware acceleration available to OpenGL ES 2.0, just as a window does. Pbuffers are most often used for generating texture maps. If all you want to do is render to a texture, we recommend using framebuffer objects (covered in Chapter 12, “Framebuffer Objects”) instead of pbuffers because they are more efficient. However, pbuffers can still be useful for some cases where framebuffer objects cannot be used, such as when rendering an off-screen surface with OpenGL ES and then using it as a texture in another API such as OpenVG.

Creating a pbuffer is very similar to creating an EGL window, with a few minor differences. To create a pbuffer, we need to find an EGLConfig just as we did for a window, with one modification: We need to augment the value of EGL_SURFACE_TYPE to include EGL_PBUFFER_BIT. Once we have a suitable EGLConfig, we can create a pbuffer using the function

EGLSurface

eglCreatePbufferSurface(EGLDisplay display, EGLConfig config, const EGLint *attribList);

As with window creation, this function takes our connection to the native display manager, and the EGLConfig that we selected.

This call also takes a list of attributes described in Table 3-4.

Table 3-4. EGL Pixel Buffer Attributes

Token

Description

Default Value

EGL_WIDTH

Specifies the desired width (in pixels) of the pbuffer.

0

EGL_HEIGHT

Specifies the desired height (in pixels) of the pbuffer.

0

EGL_LARGEST_PBUFFER

Select the largest available pbuffer if one of the requested size isn’t available. Values can be EGL_TRUE or EGL_FALSE.

EGL_FALSE

EGL_TEXTURE_FORMAT

Specifies the type of texture format (see Chapter 9) if the pbuffer is bound to a texture map. Valid values are EGL_TEXTURE_RGB, EGL_TEXTURE_RGBA, and EGL_NO_TEXTURE (which indicates that the pbuffer isn’t going to be used directly as a texture).

EGL_NO_TEXTURE

EGL_TEXTURE_TARGET

Specifies the associated texture target that the pbuffer should be attached to if used as a texture map (see Chapter 9). Valid values are EGL_TEXTURE_2D or EGL_NO_TEXTURE.

EGL_NO_TEXTURE

EGL_MIPMAP_TEXTURE

Specifies whether storage for texture mipmap levels (see Chapter 9) should be additionally allocated. Valid values are EGL_TRUE and EGL_FALSE.

EGL_FALSE

There are a number of ways that eglCreatePbufferSurface could fail, and just as with window creation, if any of them occur, EGL_NO_SURFACE is returned from the call, and the particular error is set. In this situation, eglGetError will return one of the errors listed in Table 3-5.

Table 3-5. Possible Errors When eglCreatePbufferSurface Fails

Error Code

Description

EGL_BAD_ALLOC

This error occurs when the pbuffer is unable to be allocated due to a lack of resources.

EGL_BAD_CONFIG

This error is flagged if the provided EGLConfig is not a valid EGLConfig supported by the system.

EGL_BAD_PARAMETER

This error is generated if either the EGL_WIDTH or EGL_HEIGHT provided in the attribute list are negative values.

EGL_BAD_MATCH

This error is generated if any of the following situations occur: if the EGLConfig provided doesn’t support pbuffer surfaces; or if the pbuffer is going to be used as a texture map (EGL_TEXTURE_FORMAT is not EGL_NO_TEXTURE), and the specified EGL_WIDTH and EGL_HEIGHT specify an invalid texture size; or if one of EGL_TEXTURE_FORMAT and EGL_TEXTURE_TARGET is EGL_NO_TEXTURE, and the other attributes is not EGL_NO_TEXTURE.

EGL_BAD_ATTRIBUTE

This error occurs if any of EGL_TEXTURE_FORMAT, EGL_TEXTURE_TARGET, or EGL_MIPMAP_TEXTURE are specified, but the provided EGLConfig doesn’t support OpenGL ES rendering (e.g., only OpenVG rendering is supported).

Putting this all together, we would create a pbuffer as shown in Example 3-5.

Example 3-5. Creating an EGL Pixel Buffer

EGLint attribList[] =
{
   EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
   EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
   EGL_RED_SIZE, 5,
   EGL_GREEN_SIZE, 6,
   EGL_BLUE_SIZE, 5,
   EGL_DEPTH_SIZE, 1,
   EGL_NONE
};

const EGLint MaxConfigs = 10;
EGLConfig  configs[MaxConfigs];  // We'll only accept 10 configs
EGLint  numConfigs;
if(!eglChooseConfig(dpy, attribList, configs, MaxConfigs,
   &numConfigs))
{
   // Something didn't work ... handle error situation
}
else
{
   // We've found a pbuffer-capable EGLConfig
}

// Proceed to create a 512 x 512 pbuffer (or the largest available)
EGLRenderSurface  pbuffer;
EGLint attribList[] =
{
   EGL_WIDTH, 512,
   EGL_HEIGHT, 512,
   EGL_LARGEST_PBUFFER, EGL_TRUE,
   EGL_NONE
);

pbuffer = eglCreatePbufferSurface(dpy, config, attribList);

if(pbuffer == EGL_NO_SURFACE)
{
   switch(eglGetError())
   {
      case EGL_BAD_ALLOC:
         // Not enough resources available. Handle and recover
         break;

      case EGL_BAD_CONFIG:
         // Verify that provided EGLConfig is valid
         break;

      case EGL_BAD_PARAMETER:
         // Verify that the EGL_WIDTH and EGL_HEIGHT are
         // non-negative values
         break;

       case EGL_BAD_MATCH:
          // Check window and EGLConfig attributes to determine
          // compatibility and pbuffer-texture parameters
          break;

   }
}
// Check to see what size pbuffer we were allocated
EGLint  width;
EGLint  height;

if(!eglQuerySurface(dpy, pbuffer, EGL_WIDTH, &width) ||
   !eglQuerySurface(dpy, pbuffer, EGL_HEIGHT, &height))
{
   // Unable to query surface information.
}

Pbuffers support all OpenGL ES 2.0 rendering facilities just as windows do. The major difference—aside from you can’t display a pbuffer on the screen—is that instead of swapping buffers when you’re finished rendering as you do with a window, you will either copy the values from a pbuffer to your application, or modify the binding of the pbuffer as a texture.

Creating a Rendering Context

A rendering context is a data structure internal to OpenGL ES 2.0 that contains all of the state required for operation. For example, it contains references to the vertex and fragment shaders and the array of vertex data used in the example program in Chapter 2. Before OpenGL ES 2.0 can draw it needs to have a context available for its use.

To create a context, use

EGLContext

eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, const EGLint* attribList);

Once again, you’ll need the display connection as well as the EGLConfig best representing your application’s requirements. The third parameter, shareContext, allows multiple EGLContexts to share specific types of data, like shader programs and texture maps. Sharing resources among contexts is an advanced concept that we discuss in Chapter 13, “Advanced Programming with OpenGL ES 2.0.” For the time being, we pass EGL_NO_CONTEXT in as the value for shareContext, indicating that we’re not sharing resources with any other contexts.

Finally, as with many EGL calls, a list of attributes specific to eglCreateContext’s operation is specified. In this case, there’s a single attribute that’s accepted, EGL_CONTEXT_CLIENT_VERSION, discussed in Table 3-6.

Table 3-6. Attributes for Context Creation Using eglCreateContext

Token

Description

Default Value

EGL_CONTEXT_CLIENT_VERSION

Specifies the type of context associated with the version of OpenGL ES that you’re using.

1 (which specifies an OpenGL ES 1.X context)

As we want to use OpenGL ES 2.0, we will always have to specify this attribute to obtain the right type of context.

When eglCreateContext succeeds, it returns a handle to the newly created context. If a context is not able to be created, then eglCreateContext returns EGL_NO_CONTEXT, and the reason for the failure is set, and can be obtained by calling eglGetError. With our current knowledge, the only reason that eglCreateContext would fail is if the EGLConfig we provide isn’t valid, in which case the error returned by eglGetError is EGL_BAD_CONFIG.

Example 3-6 shows how to create a context after selecting an appropriate EGLConfig.

Example 3-6. Creating an EGL Context

const EGLint attribList[] = {
   EGL_CONTEXT_CLIENT_VERSION, 2,
   EGL_NONE
};

EGLContext  context;

context = eglCreateContext(dpy, config, EGL_NO_CONTEXT, attribList);

if(context == EGL_NO_CONTEXT)
{
   EGLError error = eglGetError();

   if(error == EGL_BAD_CONFIG)
   {
      // Handle error and recover
   }
}

Other errors may be generated by eglCreateContext, but for the moment, we’ll only check for bad EGLConfig errors.

After successfully creating an EGLContext, we need to complete one final step before we can render.

Making an EGLContext Current

As an application might have created multiple EGLContexts for various purposes, we need a way to associate a particular EGLContext with our rendering surface—a process commonly called “make current.”

To associate a particular EGLContext with an EGLSurface, use the call

EGLBoolean

eglmakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context);

You probably noticed that this call takes two EGLSurfaces. Although this allows flexibility that we exploit in our discussion of advanced EGL usage, we set both read and draw to the same value, the window that we created previously.

Putting All Our EGL Knowledge Together

We conclude this chapter with a complete example showing the entire process starting with the initialization of the EGL through binding an EGLContext to an EGLRenderSurface. We’ll assume that a native window has already been created, and that if any errors occur, the application will terminate.

In fact, Example 3-7 is very similar to what is done in esCreateWindow as shown in Chapter 2, except those routines separate the creation of the window and the context (for reasons that we discuss later).

Example 3-7. A Complete Routine for Creating an EGL Window

EGLBoolean initializeWindow(EGLNativeWindow nativeWindow)
{
   const EGLint  configAttribs[] =
   {
      EGL_RENDER_TYPE, EGL_WINDOW_BIT,
      EGL_RED_SIZE, 8,
      EGL_GREEN_SIZE, 8,
      EGL_BLUE_SIZE, 8,
      EGL_DEPTH_SIZE, 24,
      EGL_NONE
    };

    const EGLint  contextAttribs[] =
    {
       EGL_CONTEXT_CLIENT_VERSION, 2,
       EGL_NONE
    };

    EGLDisplay dpy;

    dpy = eglGetNativeDispay(EGL_DEFAULT_DISPLAY);

    if(dpy == EGL_NO_DISPLAY)
    {
       return EGL_FALSE;
    }

    EGLint major, minor;

    if(!eglInitialize(dpy, &major, &minor))
    {
       return EGL_FALSE;
    }

    EGLConfig  config;
    EGLint  numConfigs;
    if(!eglChooseConfig(dpy, configAttribs, &config, 1,
       &numConfigs)) {
          return EGL_FALSE;
    }

    EGLSurface window;
    window = eglCreateWindowSurface(dpy, config, nativeWindow, NULL);

    if(window == EGL_NO_SURFACE)
    {
       return EGL_FALSE;
    }

    EGLContext context;
    context = eglCreateContext(dpy, config, EGL_NO_CONTEXT,
                               contextAttribs);

    if(context == EGL_NO_CONTEXT)
    {
       return EGL_FALSE;
    }
    if(!eglMakeCurrent(dpy, window, window, context))
    {
       return EGL_FALSE;
    }

    return EGL_TRUE;
}

This code would be very similar if an application made the call in Example 3-8 to open a 512 × 512 window.

Example 3-8. Creating a Window Using the ESutil Library

ESContext  esContext;
const char* title = "OpenGL ES Application Window Title";

if(esCreateWindow(&esContext, title, 512, 512,
                  ES_WINDOW_RGB | ES_WINDOW_DEPTH))
{
   // Window creation failed
}

The last parameter to esCreateWindow specifies the characteristics we want in our window, and specified as a bitmask of the following values:

  • ES_WINDOW_RGBSpecify an RGB-based color buffer.

  • ES_WINDOW_ALPHAAllocate a destination alpha buffer.

  • ES_WINDOW_DEPTHAllocate a depth buffer.

  • ES_WINDOW_STENCILAllocate a stencil buffer.

  • ES_WINDOW_MULTISAMPLEAllocate a multisample buffer.

Specifying these values in the window configuration bitmask will add the appropriate tokens and values into the EGLConfig attribute list (i.e., configAttribs in the preceding example).

Synchronizing Rendering

You might find situations in which you need to coordinate the rendering of multiple graphics APIs into a single window. For example, you might find it easier to use OpenVG or the native windowing system’s font rendering functions more suited for drawing characters into a window than OpenGL ES 2.0. In such cases, you’ll need to have your application allow the various libraries to render into the shared window. EGL has a few functions to help with your synchronization tasks.

If your application is only rendering with OpenGL ES 2.0, then you can guarantee that all rendering has occurred by simply calling glFinish.

However, if you’re using more than one Khronos API for rendering (such as OpenVG), and you might not know which API is used before switching to the window-system native rendering API, you can call the following.

EGLBoolean

eglWaitClient()

Delays execution of the client until all rendering through a Khronos API (like OpenGL ES 2.0, or OpenVG) is completed. On success, it will return EGL_TRUE. On failure, EGL_FALSE is returned, and an EGL_BAD_CURRENT_SURFACE error is posted.

Its operation is similar in operation to glFinish, but works regardless of which Khronos API is currently in operation.

Likewise, if you need to guarantee that the native windowing system rendering is completed, call eglWaitNative.

EGLBoolean

eglWaitNative(EGLint engine)

engine

specifies the renderer to wait for rendering completion. EGL_CORE_NATIVE_ENGINE is always accepted, and represents the most common engine supported; other engines are implementation specific, and specified through EGL extensions. EGL_TRUE is returned on success. On failure, EGL_FALSE is returned, and an EGL_BAD_PARAMETER error is posted.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset