Using Cairo graphic library on IMX6Q

Document created by Ali Sarlak on Feb 1, 2018Last modified by Ali Sarlak on Feb 8, 2018
Version 2Show Document
  • View in full screen mode

This document will explain Cairo setup to draw something on screen with hardware accelerates using OpenGL ES 2.0 or OpenVG.

 

Introduction:

 

As you know you can use those libraries that I mentioned (OpenGL ES and OpenVG) to draw on frame buffer with hardware accelerate on imx6q but using those libraries are a little bit hard to deal what I mean is that using OpenGL or OpenVG  is a kind of tough job but why? Let me bring an example here to clarify it, Imagine you want to draw an attitude aircraft symbol, this symbol needs some of elements to be drawn to look like a complete attitude symbol it includes:

1-Circle

2-line

3-Text

4-Triangle

5-some custom shapes for instance two L like lines that draw horizontally

 

If you have an experience with OpenGL specially OpenGL ES you’ll realize that drawing circle, line, triangle and so forth doesn’t a really tough job, of course drawing these primitive in OpenGL needs more lines of code in contrast with Cairo API that you can draw them with just three lines of code but the most hard job is drawing TEXT in OpenGL when you want to draw a simple text you have to deal with extra libraries like freetype,… to fetch the glyph features and then you can using atlas approach to draw text in a bitmap texture then when you need a character in your app  you can access to the character’s position in previous stored glyph in the texture, fetch and use, also you need to work with two specific OpenGL ES shaders in this case.

 

So I think it’s ok to use OpenGL or OpenVG to draw shapes if you are really skilled with those or if you looking for trouble! :D personally I prefer to use a high level API and then focus on other aspect of my application.

 

Compiling Cairo:

 

This document doesn’t intend to configure or compile Cairo, I’m sure that you can easily configure and compile it with OpenGL ES backend with YOCTO, Buildroot or any other embedded Linux distribution builders (YOCTO and Buildroot aren’t an embedded Linux distributions they can make custom one for you) even you can compile it manually.

 

To configure:

./configure --prefix=/home/super/Desktop/ROOTFS/MY_ROOTFS/usr --host=${CROSS_COMPILE} CFLAGS="-I/home/super/Desktop/ROOTFS/MY_ROOTFS/usr/include/ -DLINUX -DEGL_API_FB" LIBS="-L/home/super/Desktop/ROOTFS/MY_ROOTFS/usr/lib/ -lz" --enable-xlib=no --enable-egl --enable-glesv2

 

To compile:

make

 

 

By the way you can find your suitable configuration for your own board; Cairo has a lot of options.

 

 

How to make surface for Cairo:

 

If you have an experience drawing shapes with Cairo you know that you need a surface from cairo_t* type to drawing function API can work on and shapes appear on the screen.

To create a Cairo surface that uses OpenGL ES you have to configure EGL (EGL is an interface between Khronos rendering APIs (such as OpenGL, OpenGL ES or OpenVG) and the underlying native platform windowing system)[1] correctly and then make a Cairo surface from it.

 

                 EGLint config_attributes[] =

                {

                                               EGL_RENDERABLE_TYPE,

                                               EGL_OPENGL_ES2_BIT,

                                               EGL_RED_SIZE, 8,

                                               EGL_GREEN_SIZE, 8,

                                               EGL_BLUE_SIZE, 8,

                                               EGL_ALPHA_SIZE,EGL_DONT_CARE,

                                               EGL_SURFACE_TYPE,EGL_WINDOW_BIT,

                                               EGL_DEPTH_SIZE, 16,

                                               EGL_SAMPLES,      4,

                                               EGL_NONE

                };

 

When you want to change OpenGL ES v 2.0 with OpenVG it’s enough that change the parameter of EGL_RENDERABLE_TYPE (that is EGL_OPENGL_ES2_BIT) to EGL_OPENVG_BIT.

 

The below code will appear Figure 1 on screen:

 

 Simple drawing by Cairo on IMX6Q

Figure 1:Simple drawing by Cairo on IMX6Q

 

 

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

//========================================================================

// Name        : testCairo.cpp

// Author      : Ali Sarlak

// Version     : 1.0

// Copyright   : GPL

// Description : EGL+Cairo GLIB

//========================================================================

 

#include <iostream>

#include <stdio.h>

#include <EGL/egl.h>

#include <EGL/eglext.h>

#include <EGL/eglplatform.h>

#include <cairo/cairo-gl.h>

#include <EGL/eglvivante.h>

#include <stdlib.h>

 

 

#define DISPLAY_WIDTH 640

#define DISPLAY_HEIGHT 480

using namespace std;

 

int main()

{

    printf("START\n");

    printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");

    EGLContext eglContext;

    EGLSurface eglSurface;

    EGLBoolean resultB;

 

    /* Get a display handle and initalize EGL */

    EGLint major, minor;

    EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);

 

    resultB = eglInitialize(eglDisplay, &major, &minor);

 

    EGLint config_attributes[] =

    {

            EGL_RENDERABLE_TYPE,

            EGL_OPENGL_ES2_BIT,

            EGL_RED_SIZE, 8,

            EGL_GREEN_SIZE, 8,

            EGL_BLUE_SIZE, 8,

            EGL_ALPHA_SIZE,EGL_DONT_CARE,

            EGL_SURFACE_TYPE,EGL_WINDOW_BIT,

            EGL_DEPTH_SIZE, 16,

            EGL_SAMPLES,      4,

            EGL_NONE

    };

 

    EGLint numberConfigs = 0;

    EGLConfig* matchingConfigs=NULL;

 

    if (EGL_FALSE

            == eglChooseConfig(eglDisplay, config_attributes, NULL, 0, &numberConfigs))

    {

        printf("eglChooseConfig EROR\n");

    }

    if (numberConfigs == 0)

    {

        printf("eglChooseConfig EROR\n");

    }

 

    printf("number of configs = %d\n", numberConfigs);

    /* Allocate some space to store list of matching configs... */

    matchingConfigs = (EGLConfig*) malloc(numberConfigs * sizeof(EGLConfig));

 

    if (EGL_FALSE  == eglChooseConfig(eglDisplay, config_attributes, matchingConfigs, numberConfigs, &numberConfigs))

    {

        printf("eglChooseConfig EROR\n");

        if(matchingConfigs!=NULL)

        {

            free(matchingConfigs);

            matchingConfigs=NULL;

        }

        return -1;

    }

 

    printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");

 

    EGLint display_attributes[] =

    {

            EGL_WIDTH, DISPLAY_WIDTH,

            EGL_HEIGHT, DISPLAY_HEIGHT,

            EGL_NONE };

 

    /*Window attributes*/

    EGLint window_attribList[] =

    {

            EGL_NONE

    };

 

    EGLNativeDisplayType eglNativeDisplayType = fbGetDisplay(0);

 

    EGLNativeWindowType eglNativeWindow = fbCreateWindow(eglNativeDisplayType,

            0,

            0,

            DISPLAY_WIDTH,

            DISPLAY_HEIGHT);

 

    eglSurface = eglCreateWindowSurface(eglDisplay,matchingConfigs[0],eglNativeWindow,window_attribList);

 

    if (eglSurface == EGL_NO_SURFACE)

    {

        printf("eglSurface = %x\n", eglGetError());

    }

 

    const EGLint attribListCtx[] =

    {

            // EGL_KHR_create_context is required

            EGL_CONTEXT_CLIENT_VERSION, 2,

            EGL_NONE

    };

 

    eglContext = eglCreateContext(eglDisplay, matchingConfigs[0], EGL_NO_CONTEXT,  attribListCtx);

     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    if (eglContext == EGL_NO_CONTEXT)

    {

        printf("eglContext = %x\n", eglGetError());

        return -1;

    }

 

    cairo_device_t* cdt = cairo_egl_device_create(eglDisplay, eglContext);

 

    eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);

 

    cairo_surface_t *surface = cairo_gl_surface_create_for_egl(cdt, eglSurface,

            DISPLAY_WIDTH,DISPLAY_HEIGHT);

   

    cairo_t *cr = nullptr;

    cr = cairo_create(surface);

    if(!cr)

    {

        printf("Wrong cairo_t!\n");

        return -1;

    }

    //*********************************************************************************************

    for (int index = 0; index < 1; ++index) {

        cairo_set_source_rgb (cr, 0, 0, 0);

 

        cairo_move_to (cr, 0, 0);

        cairo_line_to (cr, 200, 200);

        cairo_move_to (cr, 200, 0);

        cairo_line_to (cr, 0, 200);

        cairo_set_line_width (cr, 1);

        cairo_stroke (cr);

 

        cairo_rectangle (cr, 0, 0, 100,100);

        cairo_set_source_rgba (cr, 1, 0, 0, 0.8);

        cairo_fill (cr);

         cairo_rectangle (cr, 0, 100, 100, 100);

        cairo_set_source_rgba (cr, 0, 1, 0, 0.60);

        cairo_fill (cr);

         cairo_rectangle (cr, 100, 0, 100, 100);

        cairo_set_source_rgba (cr, 0, 0, 1, 0.40);

        cairo_fill (cr);

         cairo_rectangle (cr, 100, 100, 100, 100);

        cairo_set_source_rgba (cr, 1, 1, 0, 0.20);

        cairo_fill (cr);

         cairo_surface_flush(surface);

        eglSwapBuffers(eglDisplay,eglSurface);

    }

 

    //to check that cairo can make the photo from the surface, png file created

    cairo_status_t s = cairo_surface_write_to_png(surface, "surface.png");

    //it is a photo that made by cairo [OK]

    cairo_destroy(cr);

     if (CAIRO_STATUS_SUCCESS == s)

    {

        printf("Status = OK \n");

    }

    else

    {

        printf("Status = ERROR <ERROR_CODE->%d>\n", s);

    }

     if(matchingConfigs!=NULL)

    {

        free(matchingConfigs);

        matchingConfigs=NULL;

    }

 

    cairo_surface_destroy(surface);

    printf("END!\n");

    return 0;

}

 

 

How To Be Sure That My Application Using GPU:

 

If you have a look at https://community.nxp.com/thread/324670 you can profile a graphical application and investigate if it uses GPU or not, also you can measure the performance and analyze the application by vAnalyzer.

 

 

 

According to the link I’ve mentioned that’s enough to set galcore.gpuProfiler=1 in uboot and then check the /sys/module/galcore/parameters/gpuProfiler   file (read the file by cat, vi, nano, etc.) if the output is 1 all things is done in a right way the final step is that exporting some environment variables :

 

export VIV_PROFILE=1

export VP_OUTPUT=sample.vpd

export VP_FRAME_NUM=1000

export VP_SYNC_MODE=1

 

VIV_PROFILE[0,1,2,3], VP_OUTPUT[any string], VP_FRAME_NUM[1,N], VP_SYNC_MODE[0,1]

 

Note: VIV_PROFILE[0] Disable vProfiler (default), VIV_PROFILE [1] Enable vProfiler, VIV_PROFILE [2] Control via application call, VIV_PROFILE [3]Allows control over which frames to profile with vProfiler by VP_FRAME_START and VP_FRAME_END.

 

vAnalyzer screenshot

 

 

If application uses GPU smaple.vpd file will create if not there isn't any vpd file.

 

[1] - https://www.khronos.org/egl

Attachments

    Outcomes