ikerhurtado.com
You're in
Iker Hurtado's pro blog
Developer | Entrepreneur | Investor
Software engineer (entrepreneur and investor at times). These days doing performant frontend and graphics on the web platform at Barcelona Supercomputing Center

EGL use for Android native OpenGL ES applications

27 Mar 2015   |   iker hurtado  
Share on Twitter Share on Google+ Share on Facebook
This post is a brief introduction to Khronos EGL API in order to prepare the surface and the context required for a OpenGL ES Android application. In addition, I show the steps necessary to do it in practice.

EGL is an interface between rendering APIs such as OpenGL ES or OpenVG and the underlying native platform window system.

It handles graphics context management, surface/buffer binding, and rendering synchronization and enables rendering using other standard APIs (of Khronos group). EGL also provides interop capability to enable efficient transfer of data between APIs.

EGL and OpenGL

OpenGL ES commands require a rendering context, that stores the current OpenGL ES state, and a drawing surface where the geometry will be drawn. The drawing surface specifies the types of buffers required for rendering (color buffer, depth buffer, and stencil buffer) and their bit depths.

The OpenGL ES specification does not address these issues. EGL provides an interface to this functionality for OpenGL ES and other graphics APIs.

Any OpenGL ES application will need to do the following using EGL before any rendering can begin:

  • Check the displays that are available on the device and initialize them.
  • Create a rendering surface. There are two types of surfaces: on-screen surfaces, that are attached to the native window system, and off-screen surfaces, that are pixel buffers. These surfaces can be used to render into a texture and can be shared.
  • Create a rendering context. This context needs to be attached to an appropriate surface before rendering can actually begin.

In addition, the EGL API implements more functionality such as power management, support for multiple rendering contexts in a process, sharing objects (such as textures or vertex buffers) across rendering contexts in a process, and a mechanism to get function pointers to EGL or OpenGL ES extension functions supported by a given implementation.

Configuration needed

Header files to include:

EGL/egl.h  Main EGL API definitions
EGL/eglext.h  EGL extension-related definitions

Android.mk build option to dynamically link with android library: LOCAL_LDLIBS += -lEGL

Setting up with EGL

The following steps describe how to set up and manipulate EGL and its integration with OpenGL:

1. Get and initialize the display connection: EGL needs to know where the content should be displayed, therefore it will need to get a display connection and initialize it. This is done using the following two methods:

eglGetDisplay : It obtains the EGL display connection for the native display. If the input argument is EGL_DEFAULT_DISPLAY , a default display connection is returned.

eglInitialize: This call initializes EGL's internal data structures and returns the major and minor version numbers of the EGL implementation.

EGLint majorVersion;
EGLint minorVersion;
EGLDisplay display;

display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

eglInitialize(display, &majorVersion, &minorVersion)

2. Configure EGL: This is done through eglChooseConfig. It returns a list of EGL frame buffer configurations that match the requirements specified by the attrib_list argument. The attribute is an array with pairs of attributes and corresponding desired values, and it is terminated by EGL_NONE.

const EGLint attribs[] = {	
  EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
  EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, // color components - 8 bit
  EGL_NONE };

// The application chooses the configuration it desires
eglChooseConfig(display, attribs, &config, 1, &numConfigs);

3. Create a render surface where the display content will be placed. eglCreateWindowSurface, given the EGL display connection, the EGL frame buffer configuration and native window, returns a new EGL window surface.

surface = eglCreateWindowSurface(display, config, appState->window, NULL);

Before this step we must have obtained the native window and reconfigure its buffers (more about Android native window API):

// As soon as we picked a EGLConfig, we can safely reconfigure 
// the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID
eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);

// Change the format and size of the window buffers. 
// 0,0 -> Buffer dimensions = Screen resolution
ANativeWindow_setBuffersGeometry(appState->window, 0, 0, format);

4. Create the EGL rendering context and make it the current.

eglCreateContext : It creates a new EGL rendering context, which is used to render into the EGL draw surface.

As for the attributes, in this call, there is a single attribute: EGL_CONTEXT_CLIENT_VERSION, that specifies the type of context associated with the version of OpenGL ES we want to use (default value: 1. Hence OpenGL ES 1.X context).

eglMakeCurrent : It attaches an EGL context to the EGL draw and read surfaces. In our code, the created window surface is used as both the read and draw surface.

EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
context = eglCreateContext(display, config, NULL, contextAttribs);
eglMakeCurrent(display, surface, surface, context);

5. OpenGL drawing.

6. Swap the EGL surface internal buffers to display the content. eglSwapBuffers posts the EGL surface color buffer to a native window. This effectively displays the drawing content on the screen.

EGL internally maintains two buffers. The content of the front buffer is displayed, while the drawing can be done on the back buffer. At the time we decided to display the new drawing, we swap the two buffers.

eglSwapBuffers(display, surface);

7. At time we want to stop rendering. Release the EGL context, destroy the EGL surface, and terminate the EGL display connection:

if (display != EGL_NO_DISPLAY)
{
  eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  if (context != EGL_NO_CONTEXT){
    eglDestroyContext(display, context);
  }
  if (surface != EGL_NO_SURFACE){
    eglDestroySurface(display, surface);
  }
  eglTerminate(display);
}
display = EGL_NO_DISPLAY;
context = EGL_NO_CONTEXT;
surface = EGL_NO_SURFACE;


Detailed info on the EGL API in the chapter 3 'An introduction to EGL' of the book OpenGL ES 2.0 Programming Guide

POST A COMMENT: