© Jan Newmarch 2017

Jan Newmarch, Raspberry Pi GPU Audio Video Programming , 10.1007/978-1-4842-2472-4_5

5. EGL on the Raspberry Pi

Jan Newmarch

(1)Oakleigh, Victoria, Australia

EGL provides a base for graphics programming, common to many systems such as OpenCL, OpenGL, OpenGL ES, and OpenVG. It is system neutral and for the RPi is the layer above Dispmanx.

Building Programs

The following is a Makefileto build these programs:

DMX_INC =  -I/opt/vc/include/ -I /opt/vc/include/interface/vmcs_host/ -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux
EGL_INC =
INCLUDES = $(DMX_INC) $(EGL_INC)


CFLAGS = $(INCLUDES)
CPPFLAGS = -march=armv7-a -mtune=cortex-a7


DMX_LIBS =  -L/opt/vc/lib/ -lbcm_host -lvcos -lvchiq_arm -lpthread
EGL_LIBS = -L/opt/vc/lib -lEGL -lGLESv2


LDFLAGS = $(DMX_LIBS) $(EGL_LIBS)

all: context info window

Overview

According to the EGL specification at www.khronos.org/registry/egl/specs/eglspec.1.5.pdf , “EGL [is] an interface between rendering APIs such as OpenCL, OpenGL, OpenGL ES, or OpenVG (referred to collectively as client APIs) and one or more underlying platforms (typically window systems such as X11).” It is not intended that application programmers write directly to EGL; instead, they should use one of the APIs such as OpenGL.

Each underlying platform will have a means of rendering EGL surfaces. In this chapter, you will look at how EGL surfaces are linked to Dispmanx surfaces and how windows are built. You won’t actually draw into any windows because that is best done using, for example, OpenGL. You will do that in Chapter 6.

Why EGL? The following is from the Wayland FAQ at http://wayland.freedesktop.org/faq.html#heading_toc_j_12 :

EGL is the only GL binding API that lets us avoid dependencies on existing window systems, in particular X. GLX obviously pulls in X dependencies and only lets us set up GL on X drawables. The alternative is to write a Wayland-specific GL binding API, say, WaylandGL.

A more subtle point is that libGL.so includes the GLX symbols, so linking to that library will pull in all the X dependencies. This means that we can’t link to full GL without pulling in the client side of X, so we’re using GLES2 for now. Longer term, we’ll need a way to use full GL under Wayland.

From the RPi viewpoint, EGL forms the link between native Dispmanx windows and the OpenGL ES API.

Initializing EGL

EGL has a display that it writes on. The display is built on a native display and is obtained by the call eglGetDisplay. The EGL platform is then initialized using eglInitialize.

Typically an EGL display will support a number of configurations. For example, a pixel may be 16 bits (5 red, 5 blue, and 6 green), 24 bits (8 red, 8 green, and 8 blue), or 32 bits (8 extra bits for alpha transparency). An application will specify certain parameters, such as the minimum size of a red pixel, and can then access the array of matching configurations using eglChooseConfig. The attributes of a configuration can be queried using eglGetConfigAttrib. One configuration should be chosen before proceeding.

The program info.c lists the possible configurations that are available to EGL.

/*
 * code stolen from openGL-RPi-tutorial-master/encode_OGL/
 */


#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <unistd.h>


#include <EGL/egl.h>
#include <EGL/eglext.h>


typedef struct
{
    uint32_t screen_width;
    uint32_t screen_height;


    EGLDisplay display;
    EGLSurface surface;
    EGLContext context;
    EGLConfig config;
} EGL_STATE_T;


EGL_STATE_T state, *p_state = &state;

void printConfigInfo(int n, EGLDisplay display, EGLConfig *config) {
    int size;


    printf("Configuration %d is ", n);

    eglGetConfigAttrib(display,
                       *config, EGL_RED_SIZE, &size);
    printf("  Red size is %d ", size);
    eglGetConfigAttrib(display,
                       *config, EGL_BLUE_SIZE, &size);
    printf("  Blue size is %d ", size);
    eglGetConfigAttrib(display,
                       *config, EGL_GREEN_SIZE, &size);
    printf("  Green size is %d ", size);
    eglGetConfigAttrib(display,
                       *config, EGL_BUFFER_SIZE, &size);
    printf("  Buffer size is %d ", size);


   eglGetConfigAttrib(display,
                       *config,  EGL_BIND_TO_TEXTURE_RGB , &size);
   if (size == EGL_TRUE)
       printf("  Can be bound to RGB texture ");
   else
       printf("  Can't be bound to RGB texture ");


   eglGetConfigAttrib(display,
                       *config,  EGL_BIND_TO_TEXTURE_RGBA , &size);
   if (size == EGL_TRUE)
       printf("  Can be bound to RGBA texture ");
   else
       printf("  Can't be bound to RGBA texture ");
}


void init_egl(EGL_STATE_T *state)
{
    EGLBoolean result;
    EGLint num_configs;


    EGLConfig *configs;

    // get an EGL display connection
    state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);


    // initialize the EGL display connection
    result = eglInitialize(state->display, NULL, NULL);
    if (result == EGL_FALSE) {
        fprintf(stderr, "Can't initialise EGL ");
        exit(1);
    }


    eglGetConfigs(state->display, NULL, 0, &num_configs);
    printf("EGL has %d configs ", num_configs);


    configs = calloc(num_configs, sizeof *configs);
    eglGetConfigs(state->display, configs, num_configs, &num_configs);


    int i;
    for (i = 0; i < num_configs; i++) {
        printConfigInfo(i, state->display, &configs[i]);
    }
}


int
main(int argc, char *argv[])
{
    init_egl(p_state);


    return 0;
}

The program is run by info.

The following is a portion of the output:

EGL has 28 configs
Configuration 0 is
  Red size is 8
  Blue size is 8
  Green size is 8
  Buffer size is 32
  Can't be bound to RGB texture
  Can be bound to RGBA texture
Configuration 1 is
  Red size is 8
  Blue size is 8
  Green size is 8
  Buffer size is 24
  Can be bound to RGB texture
  Can be bound to RGBA texture

Creating a Rendering Context

Each configuration will support one or more client APIs such as OpenGL. The API is usually requested through the configuration attribute EGL_RENDERABLE_TYPE, which should have a value such as EGL_OPENGL_ES2_BIT.

In addition to a configuration, each application needs one or more contexts. Each context defines a level of the API that will be used for rendering. Examples typically use a level of 2, and a context is created using eglCreateContext.

The following is typical code to perform these steps:

init_egl() {
    EGLint major, minor, count, n, size;
    EGLConfig *configs;
    int i;
    EGLint config_attribs[] = {
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_NONE
    };


    static const EGLint context_attribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    };


   ...    

    display->context =
        eglCreateContext(display->display,
                         display->config,
                         EGL_NO_CONTEXT, context_attribs);


}

The program context.c takes the first available configuration and then creates a context.

/*
 * code stolen from openGL-RPi-tutorial-master/encode_OGL/
 */


#include <stdio.h>
#include <assert.h>
#include <math.h>


#include <EGL/egl.h>
#include <EGL/eglext.h>


typedef struct
{
    uint32_t screen_width;
    uint32_t screen_height;


    EGLDisplay display;
    EGLSurface surface;
    EGLContext context;
    EGLConfig config;
} EGL_STATE_T;


EGL_STATE_T state, *p_state = &state;

void init_egl(EGL_STATE_T *state)
{
    EGLBoolean result;
    EGLint num_configs;


    static const EGLint attribute_list[] =
        {
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
            EGL_NONE
        };


    static const EGLint context_attributes[] =
        {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
        };


    EGLConfig *configs;

    // get an EGL display connection
    state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);


    // initialize the EGL display connection
    result = eglInitialize(state->display, NULL, NULL);


    eglGetConfigs(state->display, NULL, 0, &num_configs);
    printf("EGL has %d configs ", num_configs);


    configs = calloc(num_configs, sizeof *configs) ;
    eglGetConfigs(state->display, configs, num_configs, &num_configs);


    // get an appropriate EGL configuration - just use the first available
    result = eglChooseConfig(state->display, attribute_list,
                             &state->config, 1, &num_configs);
    assert(EGL_FALSE != result);


    // Choose the OpenGL ES API
    result = eglBindAPI(EGL_OPENGL_ES_API);
    assert(EGL_FALSE != result);


    // create an EGL rendering context
    state->context = eglCreateContext(state->display,
                                      state->config, EGL_NO_CONTEXT,
                                      context_attributes);
    assert(state->context!=EGL_NO_CONTEXT);
    printf("Got an EGL context ");
}


int
main(int argc, char *argv[])
{
    init_egl(p_state);


    return 0;
}

The program is run by context and has the following output:

EGL has 28 configs
Got an EGL context  

Creating an EGL Drawing Surface with Dispmanx

An EGL window needs to be created from a native window or surface. This is different for each system. For the RPi, you use a Dispmanx window, as created in the previous chapter. This is used to create an EGL drawing surface by eglCreateWindowSurface.

You take the EGL context creation from the previous program and the Dispmanx window creation from the previous chapter and build an EGL window surface with the following:

void egl_from_dispmanx(EGL_STATE_T *state,
                       EGL_DISPMANX_WINDOW_T *nativewindow) {
    EGLBoolean result;


    state->surface = eglCreateWindowSurface(state->display,
                                            state->config,
                                            nativewindow, NULL );
    assert(state->surface != EGL_NO_SURFACE);


    // connect the context to the surface
    result = eglMakeCurrent(state->display, state->surface, state->surface, state->context);
    assert(EGL_FALSE != result);
}

Drawing into this surface is generally done by an API such as OpenGL, and the choice of this has been set in the EGL context. This is set for the drawing surfaces by eglMakeCurrent.

Following this, drawing can take place. For now, you will draw only a big red rectangle occupying the screen. Once the drawing is complete, the EGL layer renders the surface by swapping the drawing buffer using eglSwapBuffers.

The program is window.c.

/*
 * code stolen from openGL-RPi-tutorial-master/encode_OGL/
 */


#include <stdio.h>
#include <assert.h>
#include <math.h>


#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>


#include <bcm_host.h>

typedef struct
{
    EGLDisplay display;
    EGLSurface surface;
    EGLContext context;
    EGLConfig config;


    EGL_DISPMANX_WINDOW_T nativewindow;
    DISPMANX_DISPLAY_HANDLE_T dispman_display;
} EGL_STATE_T;


EGL_STATE_T state = {
    .display = EGL_NO_DISPLAY,
    .surface = EGL_NO_SURFACE,
    .context = EGL_NO_CONTEXT
};
EGL_STATE_T *p_state = &state;


void init_egl(EGL_STATE_T *state)
{
    EGLint num_configs;
    EGLBoolean result;


    bcm_host_init();

    static const EGLint attribute_list[] =
        {
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
            EGL_NONE
        };


    static const EGLint context_attributes[] =
        {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
        };


    // get an EGL display connection
    state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);


    // initialize the EGL display connection
    result = eglInitialize(state->display, NULL, NULL);


    // get an appropriate EGL frame buffer configuration
    result = eglChooseConfig(state->display, attribute_list, &state->config, 1, &num_configs);
    assert(EGL_FALSE != result);


    // Choose the OpenGL ES API
    result = eglBindAPI(EGL_OPENGL_ES_API);
    assert(EGL_FALSE != result);


    // create an EGL rendering context
    state->context = eglCreateContext(state->display,
                                      state->config, EGL_NO_CONTEXT,
                                      context_attributes);
    assert(state->context!=EGL_NO_CONTEXT) ;
}


void init_dispmanx(EGL_STATE_T *state) {
    EGL_DISPMANX_WINDOW_T *nativewindow = &p_state->nativewindow;  
    int32_t success = 0;  
    uint32_t screen_width;
    uint32_t screen_height;


    DISPMANX_ELEMENT_HANDLE_T dispman_element;
    DISPMANX_DISPLAY_HANDLE_T dispman_display;
    DISPMANX_UPDATE_HANDLE_T dispman_update;
    VC_RECT_T dst_rect;
    VC_RECT_T src_rect;


    bcm_host_init();

    // create an EGL window surface
    success = graphics_get_display_size(0 /* LCD */,
                                        &screen_width,
                                        &screen_height);
    assert( success >= 0 );


    dst_rect.x = 0;
    dst_rect.y = 0;
    dst_rect.width = screen_width;
    dst_rect.height = screen_height;


    src_rect.x = 0;
    src_rect.y = 0;
    src_rect.width = screen_width << 16;
    src_rect.height = screen_height << 16;        


    dispman_display = vc_dispmanx_display_open( 0 /* LCD */);
    dispman_update = vc_dispmanx_update_start( 0 );
    state->dispman_display = dispman_display;


    dispman_element =
        vc_dispmanx_element_add(dispman_update, dispman_display,
                                0/*layer*/, &dst_rect, 0/*src*/,
                                &src_rect, DISPMANX_PROTECTION_NONE,
                                0 /*alpha*/, 0/*clamp*/, 0/*transform*/);


    // Build an EGL_DISPMANX_WINDOW_T from the Dispmanx window
    nativewindow->element = dispman_element;
    nativewindow->width = screen_width;
    nativewindow->height = screen_height;
    vc_dispmanx_update_submit_sync(dispman_update);
    assert(vc_dispmanx_element_remove(dispman_update, dispman_element) == 0);


    printf("Got a Dispmanx window ");
}


void egl_from_dispmanx(EGL_STATE_T *state) {
    EGLBoolean result;


    state->surface = eglCreateWindowSurface(state->display,
                                            state->config,
                                            &p_state->nativewindow, NULL );
    assert(state->surface != EGL_NO_SURFACE);


    // connect the context to the surface
    result = eglMakeCurrent(state->display, state->surface, state->surface, state->context);
    assert(EGL_FALSE != result) ;
}


void cleanup(int s) {
    if (p_state->surface != EGL_NO_SURFACE &&
        eglDestroySurface(p_state->display, p_state->surface)) {
        printf("Surface destroyed ok ");
    }
    if (p_state->context !=  EGL_NO_CONTEXT &&
        eglDestroyContext(p_state->display, p_state->context)) {
        printf("Main context destroyed ok ");
    }
    if (p_state->display != EGL_NO_DISPLAY &&
        eglTerminate(p_state->display)) {
        printf("Display terminated ok ");
    }
    if (eglReleaseThread()) {
        printf("EGL thread resources released ok ");
    }
    if (vc_dispmanx_display_close(p_state->dispman_display) == 0) {
        printf("Dispmanx display rleased ok ");
    }
    bcm_host_deinit();
    exit(s);
}


int
main(int argc, char *argv[])
{
    signal(SIGINT, cleanup);


    init_egl(p_state);

    init_dispmanx(p_state);

    egl_from_dispmanx(p_state);

    glClearColor(1.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glFlush();


    eglSwapBuffers(p_state->display, p_state->surface);

    sleep(4);

    cleanup(0);
    return 0 ;
}

The program is run by window and has the following output:

Got a Dispmanx window
Surface destroyed ok
Main context destroyed ok
Display terminated ok
EGL thread resources released ok
Dispmanx display rleased ok

Garbage Collection

C does not have automatic garbage collection like Java or other languages have. You have to do it yourself. The function cleanup manages this. It is also attached to a SIG_INT interrupt handler to clean up after Ctrl-C interrupts.

void cleanup(int s) {
    if (p_state->surface != EGL_NO_SURFACE &&
        eglDestroySurface(p_state->display, p_state->surface)) {
        printf("Surface destroyed ok ");
    }
    if (p_state->context !=  EGL_NO_CONTEXT &&
        eglDestroyContext(p_state->display, p_state->context)) {
        printf("Main context destroyed ok ");
    }
    if (p_state->display != EGL_NO_DISPLAY &&
        eglTerminate(p_state->display)) {
        printf("Display terminated ok ");
    }
    if (eglReleaseThread()) {
        printf("EGL thread resources released ok ");
    }
    if (vc_dispmanx_display_close(p_state->dispman_display) == 0) {
        printf("Dispmanx display released ok ");
    }
    bcm_host_deinit();
    exit(s);
}

Garbage collection is not complete; there seems to be a number of Khronos calls that don’t go away.

$/opt/vc/bin/vcdbg reloc

Relocatable heap version 4 found at 0x1f000000
total space allocated is 492M, with 490M relocatable, 0 legacy and 2.3M offline
0 legacy blocks of size 2359296


free list at 0x3bf78d20
461M free memory in 1 free block(s)
largest free block is 461M bytes


0x1f000000: offline 2.3M
0x1f240000: free 461M
[  31] 0x3bf78d40: used  96K (refcount 1 lock count 1, size    98304, align  256, data 0x3bf78e00, D1rual) 'khrn_hw_bin_mem'
[  30] 0x3bf90e60: used  96K (refcount 1 lock count 1, size    98304, align  256, data 0x3bf90f00, D1rual) 'khrn_hw_bin_mem'
0x3bf90e60: corrupt trailer (space 98592 != 98593)
[  29] 0x3bfa8f80: used  96K (refcount 0 lock count 0, size    98304, align  256, data 0x3bfa9000, D1ruAl) 'khrn_hw_bin_mem'
0x3bfc10a0: free 25M
[  11] 0x3d885700: used   96 (refcount 0 lock count 0, size       22, align    1, data 0x3d885720, d1rual) 'khrn_hw_null_render'
0x3d885760: free 3.5K
[   4] 0x3d886540: used  576 (refcount 1 lock count 0, size      512, align    4, data 0x3d886560, d0rual) 'ILCS VC buffer pool'
[   3] 0x3d886780: used 3.5M (refcount 1 lock count 8, size  3618816, align 4096, data 0x3d887000, d1rual) 'ARM FB'
[   2] 0x3dbfafa0: used  16K (refcount 1 lock count 0, size    16384, align   32, data 0x3dbfafc0, d0ruAl) 'audioplus_tmp_buf'
[   1] 0x3dbfefe0: used 4.0K (refcount 1 lock count 0, size        0, align 4096, data 0x3dbff000, d1rual) 'camera fast alloc arena'
heap corruption detected
small allocs not requested

This appears to be a fixed allocation that does not increase over multiple runs of programs.

Conclusion

This chapter has looked at EGL, which is the layer above Dispmanx and the layer below the major APIs of OpenGL ES, OpenVG, and so on.

Resources

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset