Using OpenGL ES in Cascades

This section describes how to set up a Screen window, as well as which flags you should set for OpenGL ES use if you are developing an app using Cascades. You learn how to join the Cascades window group and how to set up EGL. You also learn how to set up a Cascades project and overlay or underlay a Screen window on top or below a Cascades window. For more information about Screen, see Screen Graphics Subsystem. For more information about using EGL, see Using EGL to connect a native window and OpenGL ES.

Choosing an OpenGL ES layout

Cascades renders windows using its own UI framework. OpenGL ES can't be bound directly to a window in a Cascades app. However, you can embed foreign windows into the UI of your Cascades app.

There are two layout methods to choose from when you use OpenGL ES in your Cascades app. The following diagram shows the two layout methods that are available: overlay and underlay.

Illustration showing the OpenGL overlay and underlay options in a Cascades app.

Overlays

An overlay is a Screen window that is placed on top of the Cascades window—that is, the Screen window has a higher Z depth than the Cascades window. Overlays are ideal for transparent 3-D controls and content; however, using transparent Screen windows might degrade performance due to window blending.

You can switch between the Screen window with the OpenGL ES model on it and the Cascades window by altering each window's Z depth. However, when the user changes the orientation of the device, you must manually adjust the size of the Screen window and EGL surface to correspond to the changes. When the Screen window is in the foreground window, BlackBerry Platform Services events must be used for event handling. If you put the Screen window to the background, the Cascades signals and slots must be used for incoming events.

To use overlays in your app, see Using overlays.

Underlays

An underlay is a Screen window that is placed under windows in the Cascades app and gains visibility by using a ForeignWindowControl. Underlays are ideal for opaque 3-D controls and content. The ForeignWindowControl API is designed to allow you to embed Screen windows in the Cascades UI. The visibility of the Screen window can be altered directly from the ForeignWindowControl through the visible property. When you're using an underlay, your app handles events using signals and slots, unless you specify (using ForeignWindowControl::keyInputForwardingEnabled) that events should be forwarded to the Screen window.

To use underlays in your app, see Using underlays.

Threading

In Cascades, you should use a QThread for your OpenGL ES rendering code. Cascades renders using its own UI framework. If threading isn't used, your OpenGL ES rendering code can be blocked while the Cascades UI framework processes. Blocking the rendering of OpenGL ES code can cause UI lag or flickering, which leads to a bad user experience. You can avoid this UI lag by using threads to run independently from the Cascades UI framework, which runs on the main thread. Threads should be used for both overlays and underlays because the same principle applies to both. You can see how threads can be used in Using threads.

Prerequisites

To add EGL and Screen APIs to your Cascades app, add the following line to your .pro file:

LIBS += -lEGL -lscreen

You must also add one of the following OpenGL ES libraries to your .pro file:

  • For OpenGL ES 1.1, add the following: -lGLESv1_CM
  • For OpenGL ES 2.0 and 3.0, add the following: -lGLESv2

Set up a Screen window

Before you can overlay or underlay a Screen window on top or below a Cascades window, you need to set up a Screen window. To learn more about setting up a Screen window, see Screen API. The code sample below shows the basic setup of a Screen window. First, you include the libraries that you need. Then, you set up the Screen window:

Not applicable

#include <QObject>
#include <bb/cascades/Application>
#include <screen/screen.h>
bool yourClass::setupWindow(int width, int height){
    screen_context_t screen_ctx;
    screen_window_t screen_win;
    
    // Create a context
	if ( screen_create_context( &screen_ctx, 0 ) != EXIT_SUCCESS ){
		return false;
	}
    // Create a child window
	if ( screen_create_window_type( &screen_win,
                                    screen_ctx, 
                                    SCREEN_CHILD_WINDOW ) != 0 ){
		return false;
	}
    // Set window format to RGBA 8-bit per channel
    int format = SCREEN_FORMAT_RGBA8888;
	if ( screen_set_window_property_iv( screen_win, 
                                        SCREEN_PROPERTY_FORMAT, 
                                        &format) != 0 ){
		return false;
	}
    // Set the pixel position of the window's top left corner
	int position[2] = { 0, 0 };
	if ( screen_set_window_property_iv( screen_win, 
                                        SCREEN_PROPERTY_POSITION, 
                                        position ) != 0 ){
		return false;
	}
    // Set window size
	int size[2] = { width, height };
	if ( screen_set_window_property_iv( screen_win, 
                                        SCREEN_PROPERTY_SIZE, 
                                        size) != 0 ){
		return false;
	}
    // Set the usage flag
	int usage = SCREEN_USAGE_OPENGL_ES1;
	if ( screen_set_window_property_iv( screen_win, 
                                        SCREEN_PROPERTY_USAGE, 
                                        &usage ) != 0 ){
		return false;
	}
    // Set the window group
	QString group = Application::instance()->
                            mainWindow()->groupId();
	if ( screen_join_window_group( screen_win,
                                   group.toAscii() ) != 0  ){
		return false;
	}
    // Set Z depth
	int z = 1;
	if ( screen_set_window_property_iv( screen_win, 
                                        SCREEN_PROPERTY_ZORDER, 
                                        &z ) != 0 ){
		return false;
	}
    // Set window buffer size
	int bufferSize[2] = { width, height };
	if ( screen_set_window_property_iv( screen_win, 
                                        SCREEN_PROPERTY_BUFFER_SIZE, 
                                        bufferSize ) != 0){
		return false;
	}
    // Set the window ID
	QString id = "screenWindowID";
	if ( screen_set_window_property_cv( screen_win, 
                                        SCREEN_PROPERTY_ID_STRING, 
                                        id.toAscii().length(), 
                                        id.toAscii() ) != 0 ){
		return false;
	}
	// Create window buffers
	if ( screen_create_window_buffers( screen_win, 2 ) != 0 ){
		return false;
	}
    return true;
}

Not applicable

Set the usage flag

You need to set a usage flag on a Screen window so that the Screen window is set for OpenGL ES use. OpenGL ES has different usage flags depending on the version you want to use:

  • OpenGL ES 1.1 - SCREEN_USAGE_OPENGL_ES1
  • OpenGL ES 2.0 - SCREEN_USAGE_OPENGL_ES2
  • OpenGL ES 3.0 - SCREEN_USAGE_OPENGL_ES3

To set the usage flag for OpenGL ES 1.1, add the following code to your Screen window setup code:

Not applicable

	int usage = SCREEN_USAGE_OPENGL_ES1;
	if ( screen_set_window_property_iv( screen_win, 
                                        SCREEN_PROPERTY_USAGE, 
                                        &usage ) != 0 ){
		return false;
	}

Not applicable

Join the Cascades window group

You need the Screen window to join the Cascades window group, so that they are both part of the Cascades Screen context. You can get the window group name by using Application::instance(). When you have the window group name, you can set the window group on the Screen window. You must convert the group from QString to ASCII format.

Not applicable

	QString group = 
        Application::instance()->mainWindow()->groupId();

	if ( screen_join_window_group( screen_win, 
                                   group.toAscii() ) != 0  ){
		return false;
	}

Not applicable

Set up EGL

EGL acts as an interface between the OpenGL ES API and the Screen API. You must set up EGL and the Screen window to get your OpenGL ES code to render to the device's screen. To learn more about EGL, see Using EGL to connect a native window and OpenGL ES. The following code sample provides basic EGL setup and termination code. First, include the EGL library. Next, set up and initialize EGL:

Not applicable

#include <EGL/egl.h>
bool yourClass::setupEGL(){
    EGLConfig egl_conf;
    EGLContext egl_ctx;
    EGLSurface egl_surf;
    EGLDisplay egl_disp;
    
    int num_configs;

    // Define our configuration: 
    // RGB 8-bit per channel using a window surface
    EGLint attrib_list[]= { EGL_RED_SIZE,        8,
                            EGL_GREEN_SIZE,      8,
                            EGL_BLUE_SIZE,       8,
                            EGL_SURFACE_TYPE,    EGL_WINDOW_BIT,
                            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
                            EGL_NONE};

    // Get the EGL display
    egl_disp = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if ( egl_disp == EGL_NO_DISPLAY ){
    	terminateEGL();
    	return false;
    }
    // Initialize EGL
    if ( eglInitialize(egl_disp, NULL, NULL) != EGL_TRUE ){
    	terminateEGL();
    	return false;
    }
    // Bind EGL to the OpenGL ES API
    if ( eglBindAPI(EGL_OPENGL_ES_API) != EGL_TRUE ){
    	terminateEGL();
    	return false;
    }
    // Set EGL configuration that was created up top
    if ( ! eglChooseConfig( egl_disp, 
                            attrib_list, 
                            &egl_conf, 1, 
                            &num_configs) ){
    	terminateEGL();
    	return false;
    }
    // Create a EGL window surface, 
    // note passing in the Screen API window

	egl_surf = eglCreateWindowSurface( egl_disp, 
                                       egl_conf, 
                                       screen_win, NULL );
	if ( egl_surf == EGL_NO_SURFACE ){
		return false;
	}
    // Set the window surface to 
    // be the current read and write surface

	if ( eglMakeCurrent( egl_disp, egl_surf,
                         egl_surf, egl_ctx ) != EGL_TRUE ){
		return false;
	}
    // Set the swap interval to at least 1 frame per buffer swap
	EGLint interval = 1;
	if ( eglSwapInterval( egl_disp, interval ) != EGL_TRUE ){
		return false;
	}
    return true;
}

Not applicable

The EGL termination code looks like the following code sample:

Not applicable

void yourClass::terminateEGL(){
    if (egl_disp != EGL_NO_DISPLAY) {

        // Set current surface to nothing
        eglMakeCurrent( egl_disp, EGL_NO_SURFACE,
                        EGL_NO_SURFACE, EGL_NO_CONTEXT);

        // Destroy the surface if necessary
        if (egl_surf != EGL_NO_SURFACE) {
            eglDestroySurface(egl_disp, egl_surf);
            egl_surf = EGL_NO_SURFACE;
        }
        // Destroy the context if necessary
        if (egl_ctx != EGL_NO_CONTEXT) {
            eglDestroyContext(egl_disp, egl_ctx);
            egl_ctx = EGL_NO_CONTEXT;
        }
    }
}

Not applicable

Last modified: 2015-05-07



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus