Tizen Native API  6.5
Elementary GL Helper functions

Defines

#define ELEMENTARY_GLVIEW_USE(glview)   Evas_GL_API *__evas_gl_glapi = elm_glview_gl_api_get(glview);
 Convenience macro to insert at the beginning of every function calling OpenGL with Elm_GLView.
#define ELEMENTARY_GLVIEW_USE_OR_RETURN(glview, retval)
 Convenience macro to insert at the beginning of every function calling OpenGL with Elm_GLView.
#define ELEMENTARY_GLVIEW_GLOBAL_DECLARE()   extern Evas_GL_API *__evas_gl_glapi;
 Convenience macro to use the GL helpers in simple applications: declare.
#define ELEMENTARY_GLVIEW_GLOBAL_DEFINE()   Evas_GL_API *__evas_gl_glapi = NULL;
 Convenience macro to use the GL helpers in simple applications: define.
#define ELEMENTARY_GLVIEW_GLOBAL_USE(glview)   do { __evas_gl_glapi = elm_glview_gl_api_get(glview); } while (0)
 Convenience macro to use the GL helpers in simple applications: use.
#define ELEMENTARY_GLVIEW_GLES1_API_CHECK()   EVAS_GL_GLES1_API_CHECK()
 Macro to check that the GL APIs are properly set (GLES 1.1)
#define ELEMENTARY_GLVIEW_GLES2_API_CHECK()   EVAS_GL_GLES2_API_CHECK()
 Macro to check that the GL APIs are properly set (GLES 2.0)
#define ELEMENTARY_GLVIEW_GLES3_API_CHECK()   EVAS_GL_GLES3_API_CHECK()
 Macro to check that the GL APIs are properly set (GLES 3.0)

OpenGL with Elementary

Porting a native EGL+OpenGL-ES2 application to EFL

Contents of this section:

Foreword

While Evas and Ecore provide all the required functions to build a whole application based on EFL and using OpenGL, it is recommended to use Elm_GLView instead. Elementary Elm_GLView will create a drawable GL surface for the application, and set up all the required callbacks so that the complexity of Evas GL is hidden.

Convenience functions for OpenGL with EFL

The file Elementary_GL_Helpers.h provides some convenience functions that ease the use of OpenGL within an Elementary application.

Why all the trouble?

Evas GL is an abstraction layer on top of EGL, GLX or WGL that should provide the necessary features for most applications, in a platform-independent way. Since the goal of Evas GL is to abstract the underlying platform, only a subset of the features can be used by applications.

On top of this, an Evas GL surface can be stacked within a GUI layout just like any other widget, and will support transparency, direct rendering, and various other features. For these reasons, it is not possible to directly expose lower level APIs like OpenGL or EGL and interact with Evas as the same time.

The following sections should provide developers guides on how to use OpenGL-ES in an EFL application.

Evas GL initialization with GLView

When using Elm_GLView, EFL will take care of the tedious creation of all the surfaces and contexts. Also, EFL hides the underlying display system so there is no way to get a direct handle to

Here is a demo using EFL with OpenGL:

// gcc `pkg-config --cflags --libs elementary` glview.c -o glview
#include <Elementary_GL_Helpers.h>

static void _draw_gl(Evas_Object *obj)
{
   ELEMENTARY_GLVIEW_USE(obj);

   glClearColor(0.2, 0.2, 0.2, 1.0);
   glClear(GL_COLOR_BUFFER_BIT);
}

static void _resize_gl(Evas_Object *obj)
{
   ELEMENTARY_GLVIEW_USE(obj);
   int w, h;

   elm_glview_size_get(obj, &w, &h);
   glViewport(0, 0, w, h);
}

// This is the GL initialization function
Evas_Object* glview_create(Evas_Object *win)
{
   Evas_Object *glview;

   glview = elm_glview_add(win);
   elm_win_resize_object_add(win, glview);
   elm_glview_mode_set(glview, ELM_GLVIEW_ALPHA | ELM_GLVIEW_DEPTH | ELM_GLVIEW_STENCIL);
   elm_glview_resize_policy_set(glview, ELM_GLVIEW_RESIZE_POLICY_RECREATE);
   elm_glview_render_policy_set(glview, ELM_GLVIEW_RENDER_POLICY_ON_DEMAND);
   //elm_glview_init_func_set(glview, _init_gl);
   //elm_glview_del_func_set(glview, _del_gl);
   elm_glview_render_func_set(glview, _draw_gl);
   elm_glview_resize_func_set(glview, _resize_gl);
   evas_object_size_hint_min_set(glview, 250, 250);
   evas_object_show(glview);

   return glview;
}

EAPI int elm_main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
{
   Evas_Object *win;

   // Set the acceleration preference to 3d
   elm_config_accel_preference_set("3d");

   // Create a window
   win = elm_win_util_standard_add("glview", "GLView");
   evas_object_show(win);

   // Setup our GLView
   glview_create(win);

   elm_run();
   elm_shutdown();
   return 0;
}
ELM_MAIN()

Direct rendering with Evas GL

Evas GL can be used to render directly to the back buffer of the Evas window. It is important to note that this is an optimization path and this will present a few limitations. Normally, Evas GL will create an FBO and use it as target surface for all the GL draw operations, and finally blend this FBO's contents to the Evas canvas.

Evas GL will automatically fallback to indirect rendering unless the conditions are right. The flag EVAS_GL_OPTIONS_DIRECT must be set on the target surface in order to enable direct rendering. When using the Elementary GLView widget, the flag ELM_GLVIEW_DIRECT should be set.

Some limitations of direct rendering include the following:

  • If glClear() is called after glClearColor(0,0,0,0), then clear will be skipped for the color buffer. Otherwise this would erase the back buffer in evas, instead of preparing a transparent surface for GL rendering. Opaque colors will be cleared as expected (eg. (0,0,0,1)).
  • Evas GL will fallback to indirect rendering if an Evas Map is applied, or if other conditions force Evas to fallback.
  • The user application is responsible for handling screen rotation when using direct rendering, which is why applications should set the flag EVAS_GL_OPTIONS_CLIENT_SIDE_ROTATION on the GL surface (or ELM_GLVIEW_CLIENT_SIDE_ROTATION for Elm_GLView). Please also see evas_gl_rotation_get.
Note:
Direct rendering is an option that can drastically improve the performance of OpenGL applications, but it can exhibit some side effects.

OpenGL-ES 1.1 support in EFL

Since Tizen 2.3, Evas GL supports the OpenGL-ES 1.1 set of rendering APIs on top of the normal OpenGL-ES 2.0 APIs, if the drivers supports it.

With Elm_GLView, it is easy to create a 1.1 capable surface:

Evas_Object *glview;
glview = elm_glview_version_add(win, EVAS_GL_GLES_1_X);

OpenGL-ES 3.0 support in EFL

Since Tizen 2.4, Evas GL supports the OpenGL-ES 3.0 set of rendering APIs on top of the normal OpenGL-ES 2.0 APIs, if the drivers supports it.

With Elm_GLView, it is easy to create a 3.0 capable surface:

Evas_Object *glview;
glview = elm_glview_version_add(win, EVAS_GL_GLES_3_X);

As usual, the GL API is available using elm_glview_gl_api_get, which can be abstracted with ELEMENTARY_GLVIEW_USE.

When using Evas GL directly, developers must be careful to use evas_gl_context_api_get with the current context in order to get the proper API (1.1 or 2.0 or 3.0). Indeed, evas_gl_api_get will always return a GLES 2.0 API, and the 1.1 and 2.0 APIs are not compatible. Also, the application will then be responsible for calling evas_gl_make_current.

Remarks:
Always use Elm_GLView unless there is a very good reason not to.

Other uses of EGL and their Evas GL equivalents

Of course, Evas GL is not limited to creating fullscreen OpenGL target surfaces, and some developers might want to exploit some of the more advanced features of Evas GL.

These features usually require to use Rendering GL on Evas directly rather than just Elm_GLView.

Evas GL vs. EGL

As explained above, Evas GL is an abstraction layer on top of EGL, but not limited to EGL. While trying to look similar to EGL, Evas GL should also support GLX and other backends (WGL, ...).

As a consequence, only a subset of the EGL features are supported by Evas GL.

Manual work is required in order to transform all calls to EGL into Evas GL calls, but the final code should be much lighter when using EFL. Here is a simple table comparison between EGL and Evas GL calls:

EGL Evas GL Comment
eglGetDisplay N/A Not required
eglInitialize evas_gl_new -
eglTerminate evas_gl_free -
eglQueryString evas_gl_string_query For extensions: if (glapi->evasglExtFunc) { ... }
eglReleaseThread N/A -
eglGetConfigs N/A Not required
eglGetConfigAttrib N/A -
eglChooseConfig evas_gl_config_new and Evas_GL_Config -
eglCreateWindowSurface Elm_GLView or evas_gl_surface_create Elm_GLView provides elm_glview_add
eglCreatePixmapSurface N/A Not available because it is platform dependent
eglCreatePbufferSurface evas_gl_pbuffer_surface_create -
eglCreatePbufferFromClientBuffer N/A -
eglDestroySurface evas_gl_surface_destroy -
eglSurfaceAttrib N/A Surfaces can't be changed
eglQuerySurface evas_gl_surface_query Subset of features only
eglCreateContext evas_gl_context_create and evas_gl_context_version_create Elm_GLView provides elm_glview_add
eglDestroyContext evas_gl_context_destroy -
eglMakeCurrent evas_gl_make_current -
eglWaitGL N/A Use a fence sync if available
eglWaitNative N/A Use a fence sync if available
eglWaitClient N/A Use a fence sync if available
eglSwapBuffers N/A Transparently done by Evas
eglCopyBuffers N/A Not available because it is platform dependent
eglSwapInterval N/A Transparently done by Ecore and Evas
eglBindTexImage N/A Not available, use FBOs
eglReleaseTexImage N/A Not available, use FBOs
eglGetProcAddress Evas_GL_API: evas_gl_proc_address_get Provides extra Evas GL extensions (not EGL)
eglCreateImageKHR Evas_GL_API: evasglCreateImageForContext Extension
eglDestroyImageKHR Evas_GL_API:evasglDestroyImage Extension
eglCreateSyncKHR Evas_GL_API: evasglCreateSync Extension
eglDestroySyncKHR Evas_GL_API: evasglDestroySync Extension
eglClientWaitSyncKHR Evas_GL_API: evasglClientWaitSync Extension
eglSignalSyncKHR Evas_GL_API: evasglSignalSync Extension
eglGetSyncAttribKHR Evas_GL_API: evasglGetSyncAttrib Extension
eglWaitSyncKHR Evas_GL_API: evasglWaitSync Extension

The extensions above may or may not be available depending on the OpenGL driver and the backend used.

Some EGL definitions have also been imported and transformed for Evas GL. In particular, the EVAS_GL error codes returned by evas_gl_error_get don't start from 0x3000 like EGL but from 0. Also, attribute lists can be terminated by 0 instead of EGL_NONE.

Query surfaces for their properties

When using EGL, it is common to query a surface for its properties. Evas GL supports only a subset of the surface properties:

Refer to evas_gl_surface_query for more information.

PBuffer surfaces for multithread rendering

If an application wants to render offscreen using OpenGL, it can use FBOs. But if the application wants to render in a separate render thread, a surface must be created for that thread in order to call evas_gl_make_current.

In the EGL world, it is common to create a PBuffer surface with eglCreatePBufferSurface() and set its size to 1x1. With Rendering GL on Evas this is possible using evas_gl_pbuffer_surface_create.

Here is how an application could setup a render context in a separate thread:

// In the init function:
Evas_GL_Surface *sfc;
Evas_GL_Config *cfg;
Evas_GL_Context *ctx;
Evas_GL *evasgl;

evasgl = elm_glview_evas_gl_get(glview);

cfg = evas_gl_config_new();
cfg->color_format = EVAS_GL_RGBA_8888;
cfg->depth_bits = EVAS_GL_DEPTH_NONE;
cfg->stencil_bits = EVAS_GL_STENCIL_NONE;
cfg->options_bits = EVAS_GL_OPTIONS_NONE;

sfc = evas_gl_pbuffer_surface_create(evasgl, cfg, WIDTH, HEIGHT, NULL);
ctx = evas_gl_context_create(elm_glview_evas_gl_get(glview), NULL);
evas_gl_config_free(cfg);
// ...

// In the render function:
evas_gl_make_current(evasgl, sfc, ctx);

// Render to a FBO, bind it to a native image and pass it to the main thread
// using an EvasGLImage.

Multithread OpenGL rendering with Evas GL is the topic of another guide.


Define Documentation

Macro to check that the GL APIs are properly set (GLES 1.1)

Since :
2.3

Macro to check that the GL APIs are properly set (GLES 2.0)

Since :
2.3

Macro to check that the GL APIs are properly set (GLES 3.0)

Since :
2.4
#define ELEMENTARY_GLVIEW_GLOBAL_DECLARE ( )    extern Evas_GL_API *__evas_gl_glapi;

Convenience macro to use the GL helpers in simple applications: declare.

This second set of helper macros can be used in simple applications that use OpenGL with a single target surface.

Warning:
Be very careful when using these macros! The only recommended solution is to use ELEMENTARY_GLVIEW_USE in every client function. Here are some situations where you should not use the global helpers:
  • If you are using more than one Evas canvas at a time (eg. multiple windows). The GL API will be different if you are using different rendering engines (software and GL for instance), and this can happen as soon as you have multiple canvases.
  • If you are using multiple GLES APIs i.e OpenGL-ES 1.1, OpenGL-ES 2.0 and OpenGL-ES 3.0 APIs.
  • If you are writing or porting a library that may be used by other applications.
This set of macros should be used only in the following situation:
  • Only one surface is used for GL rendering,
  • Only one API set (GLES 1.1 or GLES 2.0 or GLES 3.0) is used ELEMENTARY_GLVIEW_GLOBAL_DECLARE should be used in a global header for the application. For example, in a platform-specific compatibility header file.
Example of a global header file main.h:
#include <Elementary_GL_Helpers.h>
// other includes...

ELEMENTARY_GLVIEW_GLOBAL_DECLARE()

// ...
See also:
ELEMENTARY_GLVIEW_GLOBAL_DEFINE
ELEMENTARY_GLVIEW_GLOBAL_USE
Since :
2.3
#define ELEMENTARY_GLVIEW_GLOBAL_DEFINE ( )    Evas_GL_API *__evas_gl_glapi = NULL;

Convenience macro to use the GL helpers in simple applications: define.

ELEMENTARY_GLVIEW_GLOBAL_DEFINE should be used at the top of a file creating the Elm_GLView widget.

Example of a file glview.c:

#include "main.h"
ELEMENTARY_GLVIEW_GLOBAL_DEFINE()

// ...

static Evas_Object *
glview_create(Evas_Object *parent)
{
   Evas_Object *glview;

   glview = elm_glview_version_add(parent, EVAS_GL_GLES_2_X);
   ELEMENTARY_GLVIEW_GLOBAL_USE(glview);

   elm_glview_mode_set(glview, ELM_GLVIEW_ALPHA | ELM_GLVIEW_DEPTH | ELM_GLVIEW_STENCIL);
   elm_glview_resize_policy_set(glview, ELM_GLVIEW_RESIZE_POLICY_RECREATE);
   elm_glview_render_policy_set(glview, ELM_GLVIEW_RENDER_POLICY_ON_DEMAND);

   elm_glview_init_func_set(glview, _init_gl);
   elm_glview_del_func_set(glview, _del_gl);
   elm_glview_resize_func_set(glview, _resize_gl);
   elm_glview_render_func_set(glview, _draw_gl);

   return glview;
}

// ...
See also:
ELEMENTARY_GLVIEW_GLOBAL_DECLARE
ELEMENTARY_GLVIEW_GLOBAL_USE
Since :
2.3
#define ELEMENTARY_GLVIEW_GLOBAL_USE (   glview)    do { __evas_gl_glapi = elm_glview_gl_api_get(glview); } while (0)

Convenience macro to use the GL helpers in simple applications: use.

This macro will set the global variable holding the GL API so that it's available to the application.

It should be used right after setting up the Elm_GLView object.

See also:
ELEMENTARY_GLVIEW_GLOBAL_DECLARE
ELEMENTARY_GLVIEW_GLOBAL_DEFINE
Since :
2.3
#define ELEMENTARY_GLVIEW_USE (   glview)    Evas_GL_API *__evas_gl_glapi = elm_glview_gl_api_get(glview);

Convenience macro to insert at the beginning of every function calling OpenGL with Elm_GLView.

Parameters:
[in]glviewElementary GLView object in use

Here's a very simple code example:

static void _draw_gl(Evas_Object *obj)
{
   ELEMENTARY_GLVIEW_USE(obj);

   glClearColor(0.2, 0.2, 0.2, 1.0);
   glClear(GL_COLOR_BUFFER_BIT);
}

This is the equivalent of:

static void _draw_gl(Evas_Object *obj)
{
   Evas_GL_API *api = elm_glview_gl_api_get(obj);

   api->glClearColor(0.2, 0.2, 0.2, 1.0);
   api->glClear(GL_COLOR_BUFFER_BIT);
}
Note:
This macro should be used in every function that calls OpenGL from the Elementary. This indeed means that each function must have access to the Elm_GLView widget. Although this might require some changes in existing GL codebases, this is the recommended way to use the GL API.
Since :
2.3
#define ELEMENTARY_GLVIEW_USE_OR_RETURN (   glview,
  retval 
)
Value:
Evas_GL_API *__evas_gl_glapi = elm_glview_gl_api_get(glview); \
   if (!__evas_gl_glapi) return retval;

Convenience macro to insert at the beginning of every function calling OpenGL with Elm_GLView.

Parameters:
[in]glviewElementary Elm_GLView object in use
[in]retvalA value to return in case of failure (GL API was not found), can be empty

This is similar to ELEMENTARY_GLVIEW_USE except that it will return from the function if the GL API can not be used.

Since :
2.3