Initializing and binding EGL

EGL stands for Embedded Graphics Library. It's an API that sits between standard graphics libraries like OpenGL and OpenVG and proprietary windowing systems. In this tutorial, we are trying to create a native BlackBerry PlayBook window and prepare it in such a way that you can use OpenGL to draw graphics on it. We've got the native window set up. Now, we need to configure EGL and bind it to our native window.

Get and initialize EGLDisplay

EGL has to know which display it should use. EGL is designed to be used on lots of different hardware - some of which might have multiple displays. In the case of the BlackBerry PlayBook, we have a single, beautiful display. But we still have to explicitly tell EGL to use it.

The following snippet of code, in bbutil_init_egl(), declares an EGLDisplay variable, egl_disp, and tells EGL to store a reference to the default display in it. It then initializes that default display.

EGLDisplay egl_disp;

egl_disp = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(egl_disp, NULL, NULL);

Get the required EGLConfig

We are going to create an EGLSurface to render our OpenGL ES graphics on. That surface will be associated with a particular EGL buffer configuration. So we need to set up that EGLConfig. We need a buffer configuration that can support OpenGL ES rendering on a window.

We start by using eglGetConfigs() to return all of the available buffer configurations. Then we loop through all of the returned configurations checking to ensure they have the attributes we need. For example, this snippet of code in bbutil_choose_config() checks to see if an EGLConfig has the EGL_BUFFER_SIZE attribute set to 32.

eglGetConfigs(egl_disp, egl_configs, egl_num_configs, &egl_num_configs);

for (i = 0; i < egl_num_configs; i++) {
    eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_BUFFER_SIZE, &val);
    if (val != 32)) {
        continue;
    }

...

This is one of the key attributes we need in our configuration. Examples of other attributes include: EGL_SURFACE_TYPE, EGL_RENDERABLE_TYPE, and EGL_DEPTH_SIZE.

Create the EGL context

An EGL context is the combination of an EGLDisplay and an associated EGLConfig. We can use the resulting context to render to any compatible EGLSurface. The following snippet creates an EGLContext. We could pass another EGLContext into the third parameter if we wanted to share textures with that context. The final parameter is reserved for environment-specific attributes.

egl_ctx = eglCreateContext(egl_disp, egl_conf, EGL_NO_CONTEXT, NULL);

Bind EGL to the native window

We created a native window and we set up EGL. Now we have to tell them to work together. We begin by setting a few window properties at the beginning of bbutil_init_egl(). We need to ensure that the format and usage of the window are compatible with OpenGL ES.

int format = SCREEN_FORMAT_RGBX8888;

if (api == GL_ES_1) {
    	usage = SCREEN_USAGE_OPENGL_ES1 | SCREEN_USAGE_ROTATION;
    } else if (api == GL_ES_2) {
    	usage = SCREEN_USAGE_OPENGL_ES2 | SCREEN_USAGE_ROTATION;
    } else if (api == VG) {
    	usage = SCREEN_USAGE_OPENVG | SCREEN_USAGE_ROTATION;
    } else {
    	fprintf(stderr, "invalid api setting\n");
    	return EXIT_FAILURE;
    }

screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_FORMAT, &format);

screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_USAGE, &usage);

Next, the happy couple are brought together and the result is a shiny new EGLSurface, eager to render OpenGL ES graphics.

EGLSurface egl_surf;
egl_surf = 
  eglCreateWindowSurface(egl_disp, egl_conf, screen_win, NULL);

There is a little bit of house keeping left to do. We have to call eglMakeCurrent() to make our combination of EGLDisplay, EGLSurface, and EGLContext the current context for the current thread. In other words, the calling thread has to declare that it's going to use the EGL setup we prepared.

eglMakeCurrent(egl_disp, egl_surf, egl_surf, egl_ctx);

Oh, and since OpenGL ES works by drawing to an off-screen buffer and then swapping that buffer to the screen buffer at periodic intervals, we can configure the interval at which that swapping process takes place. The interval parameter here specifies the number of frames that are displayed before a swap occurs.

EGLint interval = 1;
eglSwapInterval(egl_disp, interval);