Thursday, 31 May 2012

Getting Started with EGL on the Raspberry pi

So i finally go my raspberry pi and my plan is to port my NGL library to it, the main difference is that NGL is using Qt and OpenGL 3.2 core profile and the pi will use OpenGL ES and EGL. Having never used EGL I decides to do a bit of rtfm and read the spec as well as some of the demo programs that come with the pi. The following is a basic introduction to getting started with EGL and using the pi in general. All the code for this post can be found here

EGL getting started

EGL is used as an interface between OpenGL (and other Khronos API's) and the base system (in this case the pi). It is responsible for accessing the display hardware and other synchronisation of the display / graphics context. In the case of the pi we use it to access the display hardware and use OpenGL or OpenVG with it.

All of the functions for this are stored in the header file  egl.h as shown below
#include <EGL/egl.h>
On the debian "squeeze" image of the OS these headers can be found in /opt/vc/include, we also need to add the EGL library to our build using the flags -L/opt/vc/lib -lEGL (more on this later in the Makefile section).

Accessing the display

Almost all the EGL functions require a valid display pointer to do their work, this is stored using the EGLDisplay typedef (it's actually a void * ).

We can get one of these using the following code
EGLDisplay display;
// get an EGL display connection
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
assert(display !=EGL_NO_DISPLAY);
If this is successful it should return a valid display, else the EGL_NO_DISPLAY value will be returned.

EGL initialisation 

Now we have a valid display connection we can initialise EGL and query what version we have, this is done with the following code.
// now lets initialise EGL and get the versions
int major;
int minor;
EGLBoolean result;

result = eglInitialize(display, &major, &minor);
assert(result != EGL_FALSE );
std::cout<<"Major version "<<major<<" minor "<<minor<<"\n";
On my version of the pi it gives the following output
Major version 1 minor 4


Now we have initialised EGL we can query the different configurations available to use. This is done using the eglGetConfigs function which works in two different modes. The first mode will allow us to get how many configs there are, and the second will fill a buffer with all of the different configs. This is done in the the following code
EGLint numConfigs;
// first we call getConfigs with a NULL to see how many configs we have
assert(result != EGL_FALSE );
std::cout<< "number of configs found "<<numConfigs<<"\n";
// now we create a buffer to store all our configs
EGLConfig *configs = new EGLConfig[numConfigs];
// and copy them into our buffer (don't forget to delete once done)
assert(result != EGL_FALSE );


// don't forget to delete once done
delete [] configs;

We can now gather the information from each of the configs using the eglGetConfigAttrib function. This requires you to pass in the attribute you wish to query and will return the value if set. The following code queries the attributes available on the pi (some that are in the spec are not on the pi)

for(int i=0; i<numConfigs; ++i)
 std::cout<<"Config #"<<i<<"\n";
 std::cout<<"Buffer Size "<<value<<"\n";
 std::cout<<"Red Size "<<value<<"\n";
 std::cout<<"Green Size "<<value<<"\n";
 std::cout<<"Blue Size "<<value<<"\n";
 std::cout<<"Alpha Size "<<value<<"\n";
  case  EGL_NONE : std::cout<<"EGL_CONFIG_CAVEAT EGL_NONE\n"; break;
  case  EGL_SLOW_CONFIG : std::cout<<"EGL_CONFIG_CAVEAT EGL_SLOW_CONFIG\n"; break;
 std::cout<<"Config ID "<<value<<"\n";

 std::cout<<"Depth size "<<value<<"\n";

 std::cout<<"Max pbuffer width "<<value<<"\n";
 std::cout<<"Max pbuffer height "<<value<<"\n";
 std::cout<<"Max pbuffer pixels "<<value<<"\n";
 std::cout<<"Native renderable "<<std::string(value ? "true" : "false")<<"\n";
 std::cout<<"Native visual ID "<<value<<"\n";
 std::cout<<"Native visual type "<<value<<"\n";
 std::cout<<"Sample Buffers "<<value<<"\n";
 std::cout<<"Samples "<<value<<"\n";
 std::cout<<"Surface type "<<value<<"\n";

This give a sample output like this (a full listing can be seen here or in the file output.txt in the code bundle)
Config #0
Buffer Size 32
Red Size 8
Green Size 8
Blue Size 8
Alpha Size 8
Config ID 1
Depth size 24
Max pbuffer width 2048
Max pbuffer height 2048
Max pbuffer pixels 4194304
Native renderable true
Native visual ID 37928
Native visual type 12344
Sample Buffers 0
Samples 0
Surface type 1639


To build the program the following makefile was used, it includes the correct paths and libs
CFLAGS=-c -Wall -O3 -I/usr/local/include -I/opt/vc/include -Iinclude/ngl -Isrc/ngl -Isrc/shaders -DNGL_DEBUG
LDFLAGS=-L/opt/vc/lib -lEGL
SOURCES=$(shell find ./ -name *.cpp)


 $(CC) $(LDFLAGS) $(OBJECTS) -o $@

 $(CC) $(CFLAGS) $< -o $@

clean :
 rm -f *.o $(EXECUTABLE)

That it for now but here is a sneak preview of NGL almost working