EGL – The initialization interface for OpenGL-ES

EGL is the syntactic sugar between the particular hardware & operating system your program is currently running on and OpenGL. OpenGL is agnostic to the underlying operating system’s windows system. EGL is an interface between Khronos rendering APIs (like OpenGL-ES and OpenVG) and the underlying native platform windowing system. EGL is designed to wrap the graphics context management, surface/buffer binding, rendering synchronization, and hides the underlying OS-specific calls in EGL wrappers. EGL simply provides some decoration about the fact that OpenGL needs to be able to communicate with and get resources from the native operating system that its running on. This basically means the state, context, and buffers that both the OS and OpenGL need to work together.  Specifically EGL is a wrapper over the following subsystems;

  • WGL – Windows GL – the Windows-OpenGL interface (pronounced wiggle)
  • CGL – the Mac OS X-OpenGL interface (the AGL layer sits on top of CGL)
  • GLX – the equivalent X11-OpenGL interface

EGL is a convenience, as there’s nothing preventing you from directly calling the underlying OS-OGL interface layer directly. In general, you will usually not have to do this, as EGL provides the same overlapping subset of functionality found between all of the OS-OGL interface layers. EGL not only provides a convenient binding between the operating system resources and the OpenGL subsystem, but also provides the hooks to the operating system to inform it when you require something, such as;

  1. Iterating, selecting, and initializing an OpenGL context
    • This can be the OGL API level, software vs. hardware rendering, etc.
  2. Requesting a surface or memory resource.
    • The OS services requests for system or video memory
  3. Iterating through the available surface formats (to pick an optimal one)
    • You can find out properties of the video card(s) from the OS – the surfaces presented will resides on the video card(s) or software renderer interface.
  4. Selecting the desired surface format
  5. Informing the OS you are done rendering and it’s time to show the scene.
  6. Informing the OS to use a different OpenGL context
  7. Informing the OS you are done with the resources.

If you are a Windows programmer, you might be familiar with DXGI, which is the relatively newer Windows API that handles a similar function for DirectX programmers. For iOS, Apple has EAGL, which is their own flavor of EGL. Android programmers may or may not be exposed to EGL – you can always make an EGL call if you want to do something special, but if you use the NativeActivity class, the EGL calls are done for you.

The basic usage of EGL and similar API are the following;

  1. (Android) Obtain the EGL interface.
    • So you can make EGL calls
  2. Obtain a display that’s associated with an app or physical display
  3. Initialize the display
  4. Configure the display
  5. Create surfaces
    • Front, back, offscreen buffers, etc.
  6. Create a context associated with the display
    • This holds the “state” for the OpenGL calls
  7. Make the context “current”
    • This selects the active state
  8. Render with OpenGL (OpenGL not EGL calls, the OpenGL state is held by EGL context)
  9. Flush or swap the buffers so EGL tells the OS to display the rendered scene. Repeat rendering till done.
  10. Make the context “not current”
  11. Clean up the EGL resources

After obtaining a display, you initialize it, set the preferred configuration, and create a surface with a back buffer you can draw into.

You kick things off by getting a display connection through a call to eglGetDisplay by passing in either a native display handle or EGL_DEFAULT_DISPLAY.

.
    // Get Display Type - for Windows this is the Window DC
    EGLDisplay eglDisplay = eglGetDisplay( ::GetDC(hWnd) );
    eglInitialize( eglDisplay, NULL, NULL);

    // typical PC attrib list
    EGLint defaultPCAttribList[] = {
	// 32 bit color
	EGL_RED_SIZE, 8,
	EGL_GREEN_SIZE, 8,
	EGL_BLUE_SIZE, 8,
	// at least 24 bit depth
	EGL_DEPTH_SIZE, 24,
	EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
	// want opengl-es 2.x conformant CONTEXT
	EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 
	EGL_NONE
    };

    EGLint numConfigs;
    EGLConfig config;

    // just grab the first configuration
    eglChooseConfig(eglDisplay, defaultPCAttribList,
                    &config, 1, &numConfigs)

    // create a surface - note Windows window handle
    EGLSurface surface =
	eglCreateWindowSurface(display, config, hWnd, NULL);

    // create a context
    EGLContext context =
	eglCreateContext(display, config, NULL, NULL);

    // now make the context current 
    eglMakeCurrent(display, surface, surface, context);

    // the context is now bound to the surfaces, OpenGL is "live"
    //…
    // perform your rendering
    // …

    // when done, unbind the context and surfaces
    eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    // terminate the connection to the display, release all resources
    eglTerminate(display);
.

I’ll say a few comments about the configuration selection. The most important thing this code does is try to choose a set of attributes that matches your needs. It’s up to you to pick a configuration that is an actual good match for the hardware you are running on. For example, if you are running in on fairly capable GPU, you’d want to pick a configuration that support a good, high-quality color and depth buffer. (Here I’m assuming that is what your application needs). So for a PC, you’d want at least a  32 bit color buffer (8 bits for each of the RGBA values) (though you can sometimes get 32 bits per color for a 128 bit per pixel surface). Also choose your depth buffer carefully, you want to pick a natively supported format, which will usually mean 24 or 32 bit over 16 bit for PC graphics. So the attrib list for a PC title might look like this;

.
    // typical attrib list for PC's or modern mobile devices
    EGLint defaultAttribList[] = {
	// at least 32 bit color
	EGL_RED_SIZE,   8,
	EGL_GREEN_SIZE, 8,
	EGL_BLUE_SIZE,  8,
	// at least 24 bit depth
	EGL_DEPTH_SIZE, 24,
	// want opengl-es 2.x conformant CONTEXT
	EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
	EGL_NONE
	};
.

Mobile devices are generally slower, and have smaller sizes, so choose the least acceptable range as a starting point. For supporting older mobile devices you might pick one of the compressed color formats;

.    
    // typical phone/tablet attrib list for older mobile devices
    EGLint defaultolderMobileAttribList[] = {
	// at least 5-6-5 color
	EGL_RED_SIZE, 5,
	EGL_GREEN_SIZE, 6,
	EGL_BLUE_SIZE, 5,
	// at least 8 bit depth
	EGL_DEPTH_SIZE, 8,
	// want opengl-es 2.x conformant CONTEXT
	EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
	EGL_NONE
	};
.

Most of the time you will call eglChooseConfig and just grab the first configuration. Sometimes this is the wrong thing to do. You should at least take a look at what configurations are presented and try to sort by the features that are most important to your app. You’ll typically see the color and depth values changing, and not in the order you might expect. In a future post I’ll post some code that shows how to go about iterating through the list of surface formats and rating them.

This entry was posted in OpenGL. Bookmark the permalink.