OpenGL Driver

The OpenGL driver renders an LVGL display into an OpenGL texture, for embedding LVGL inside an application that already owns the OpenGL context.

Edit on GitHub

Overview

The OpenGL display driver renders an LVGL display into an OpenGL texture and hands you the texture id. It creates no window and no context: it is the driver to use when something else, a game, a visualization tool or your own engine already owns the OpenGL context and you want LVGL content inside it.

If you do not already have a context, use a driver that creates one for you instead:

  • GLFW on PC-like platforms
  • EGL, or DRM and Wayland with their EGL backends, on embedded targets
  • SDL with its EGL backend

Experimental API

The OpenGL Driver API is experimental. Expect breaking changes.

Configuration

Enable LV_USE_OPENGLES.

There are no libraries for LVGL to resolve, the GL entry points come from the context you already created.

To render on the GPU rather than uploading software-rendered pixels into the texture, also enable a draw unit: LV_USE_DRAW_NANOVG (recommended) or LV_USE_DRAW_OPENGLES. See OpenGL Overview.

Usage

The OpenGL context must already be current when you call lv_opengles_texture_create. It allocates the texture for you; lv_opengles_texture_get_texture_id returns the id to use in your own rendering.

 
#include "lvgl/lvgl.h"

#define WIDTH  640
#define HEIGHT 480

int main(void)
{
    lv_init();
    /* Don't forget the tick callback */

    /* NOTE: the OpenGL context must be created and current before this point */

    lv_display_t * disp = lv_opengles_texture_create(WIDTH, HEIGHT);

    /* The texture id, for use in your application's rendering */
    unsigned int texture_id = lv_opengles_texture_get_texture_id(disp);

    lv_demo_widgets();

    while(1) {
        uint32_t time_until_next = lv_timer_handler();
        if(time_until_next == LV_NO_TIMER_READY) time_until_next = LV_DEF_REFR_PERIOD;
        lv_delay_ms(time_until_next);
        /* Draw texture_id in your own scene, then swap buffers */
    }

    return 0;
}

If you have already allocated a texture yourself, wrap it instead of letting LVGL create one with lv_opengles_texture_create_from_texture_id. lv_opengles_texture_get_from_texture_id looks up the display belonging to a texture id, and lv_opengles_texture_reshape resizes a texture and its display together.

Private API

Resizing a texture requires using LVGL's Private API.

Create as many displays as you need, each gets its own texture.

Drawing the texture yourself

The driver flushes into the texture and stops there; putting it on screen is your job. lv_opengles_render_texture draws a texture into the current framebuffer if you want LVGL to do it, and lv_opengles_render_display_texture does the same for a display's own texture.

To take over presentation entirely, set your own flush callback. This one works in both the full and direct render modes:

 
static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map)
{
    LV_UNUSED(area);
    LV_UNUSED(px_map);

    if(lv_display_flush_is_last(disp)) {
        const int32_t w = lv_display_get_horizontal_resolution(disp);
        const int32_t h = lv_display_get_vertical_resolution(disp);

        /* The texture always covers the full screen, even when `area` does not,
         * which is the case in the direct render mode */
        lv_area_t full_area;
        lv_area_set(&full_area, 0, 0, w, h);

        unsigned int texture_id = lv_opengles_texture_get_texture_id(disp);
        /* Renders into the current context */
        lv_opengles_render_texture(texture_id, &full_area, LV_OPA_COVER, w, h, &full_area, false, true);
    }

    lv_display_flush_ready(disp);
}

Register it after creating the display, together with the render mode you want:

 
lv_display_set_render_mode(disp, LV_DISPLAY_RENDER_MODE_FULL);
lv_display_set_flush_cb(disp, flush_cb);

lv_opengles_viewport sets the viewport LVGL renders through, if your application needs LVGL confined to part of the framebuffer.

OpenGL Texture Caching Renderer

LV_USE_DRAW_OPENGLES enables a renderer that caches software-rendered areas as OpenGL textures and reuses them whenever a later frame asks for the same thing. In most UIs this is a large improvement, because most of the screen does not change between frames.

Known Limitations

  • Performance is the same or slightly worse when drawn areas never hit the cache — widgets whose color or shape changes every frame, such as a label recolored randomly each frame (the "Multiple labels" scene of the benchmark demo).
  • Layers that have both transparent pixels and an overall layer opacity do not blend correctly. Visible in the border corners of the benchmark demo's "Containers with opa_layer" scene.
  • Layers with rotation are not supported. Rotated images are fine.

LV_USE_DRAW_NANOVG avoids these limitations and performs better; prefer it unless you specifically need the texture cache. See NanoVG Draw Unit.

Support

CapabilityValue
RotationHardware (GPU)
Runtime resolution changeWith lv_opengles_texture_reshape (Private API)
Runtime color format changeNo
Color formatsFrom LV_COLOR_FORMAT_DEFAULT (RGB565 or XRGB8888)
Multiple displays / windowsYes. One display per texture. Windows are not this driver's concern
Hardware accelerationOpenGL ES, via LV_USE_DRAW_NANOVG or LV_USE_DRAW_OPENGLES
Render modeDirect by default
InputNone built in. The window belongs to your application, so feed LVGL input yourself

See Also

  • GLFW - creates the window and context for you, and can composite these textures
  • EGL - context creation on embedded targets, including off-screen rendering
  • OpenGL Overview - how the drivers and draw units fit together
  • NanoVG Draw Unit - the recommended GPU renderer
  • OpenGL ES Draw Unit - the texture-caching renderer

Last updated on

On this page