# OpenGL Driver (/integration/embedded_linux/drivers/opengl_driver)



Overview [#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](/integration/embedded_linux/drivers/glfw) on PC-like platforms
* [EGL](/integration/embedded_linux/drivers/egl), or [DRM](/integration/embedded_linux/drivers/drm) and
  [Wayland](/integration/embedded_linux/drivers/wayland) with their EGL backends, on embedded targets
* [SDL](/integration/pc/sdl) with its EGL backend

<Callout type="info" title="Experimental API">
  The OpenGL Driver API is experimental. Expect breaking changes.
</Callout>

Configuration [#configuration]

Enable <ApiLink name="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: <ApiLink name="LV_USE_DRAW_NANOVG" /> (recommended) or <ApiLink name="LV_USE_DRAW_OPENGLES" />. See
[OpenGL Overview](/integration/embedded_linux/opengl).

Usage [#usage]

The OpenGL context must already be current when you call <ApiLink name="lv_opengles_texture_create" />. It
allocates the texture for you; <ApiLink name="lv_opengles_texture_get_texture_id" /> returns the id to use in
your own rendering.

```c title=" " lineNumbers=1
#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
<ApiLink name="lv_opengles_texture_create_from_texture_id" />.
<ApiLink name="lv_opengles_texture_get_from_texture_id" /> looks up the display belonging to a texture id,
and <ApiLink name="lv_opengles_texture_reshape" /> resizes a texture and its display together.

<Callout type="info" title="Private API">
  Resizing a texture requires using LVGL's Private API.
</Callout>

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

Drawing the texture yourself [#drawing-the-texture-yourself]

The driver flushes into the texture and stops there; putting it on screen is your job.
<ApiLink name="lv_opengles_render_texture" /> draws a texture into the current framebuffer if you want LVGL
to do it, and <ApiLink name="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:

```c title=" " lineNumbers=1
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:

```c title=" " lineNumbers=1
lv_display_set_render_mode(disp, LV_DISPLAY_RENDER_MODE_FULL);
lv_display_set_flush_cb(disp, flush_cb);
```

<ApiLink name="lv_opengles_viewport" /> sets the viewport LVGL renders through, if your application needs
LVGL confined to part of the framebuffer.

OpenGL Texture Caching Renderer [#opengl-texture-caching-renderer]

<ApiLink name="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 [#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.

<ApiLink name="LV_USE_DRAW_NANOVG" /> avoids these limitations and performs better; prefer it unless you
specifically need the texture cache. See
[NanoVG Draw Unit](/integration/embedded_linux/draw_units/draw_nanovg).

Support [#support]

| Capability                  | Value                                                                                           |
| --------------------------- | ----------------------------------------------------------------------------------------------- |
| Rotation                    | Hardware (GPU)                                                                                  |
| Runtime resolution change   | With <ApiLink name="lv_opengles_texture_reshape" />  (Private API)                              |
| Runtime color format change | No                                                                                              |
| Color formats               | From <ApiLink name="LV_COLOR_FORMAT_DEFAULT" /> (RGB565 or XRGB8888)                            |
| Multiple displays / windows | Yes. One display per texture. Windows are not this driver's concern                             |
| Hardware acceleration       | OpenGL ES, via <ApiLink name="LV_USE_DRAW_NANOVG" /> or <ApiLink name="LV_USE_DRAW_OPENGLES" /> |
| Render mode                 | Direct by default                                                                               |
| Input                       | None built in. The window belongs to your application, so feed LVGL input yourself              |

See Also [#see-also]

* [GLFW](/integration/embedded_linux/drivers/glfw) - creates the window and context for you, and can composite these textures
* [EGL](/integration/embedded_linux/drivers/egl) - context creation on embedded targets, including off-screen rendering
* [OpenGL Overview](/integration/embedded_linux/opengl) - how the drivers and draw units fit together
* [NanoVG Draw Unit](/integration/embedded_linux/draw_units/draw_nanovg) - the recommended GPU renderer
* [OpenGL ES Draw Unit](/integration/embedded_linux/draw_units/draw_opengl) - the texture-caching renderer
