diff --git a/docs/src/details/integration/embedded_linux/drivers/drm.rst b/docs/src/details/integration/embedded_linux/drivers/drm.rst index 22e33017c2..33ea82bbb7 100644 --- a/docs/src/details/integration/embedded_linux/drivers/drm.rst +++ b/docs/src/details/integration/embedded_linux/drivers/drm.rst @@ -1,3 +1,141 @@ +.. _linux_drm: + === DRM -=== \ No newline at end of file +=== + +Overview +-------- + +The **DRM** (Direct Rendering Manager) display driver provides support for rendering +LVGL directly to Linux framebuffer devices through the DRM/KMS subsystem. +It enables running LVGL without a windowing system such as X11 or Wayland, +making it suitable for embedded devices, single-board computers, and direct-to-display +applications. + +The DRM driver interacts directly with the GPU or display controller through +``/dev/dri/cardX`` nodes. + +Getting Started with DRM +------------------------ + +Prerequisites +~~~~~~~~~~~~~ + +The DRM driver requires: + +- A Linux system with DRM/KMS support enabled in the kernel. +- Access to a DRM device node, typically ``/dev/dri/card0``. +- Proper permissions to access DRM devices (e.g. running as root or adding the user + to the ``video`` group). + +On Debian/Ubuntu-based systems: + +.. code-block:: shell + + sudo apt-get install libdrm-dev + +Configure DRM Driver +~~~~~~~~~~~~~~~~~~~~ + +1. Enable the DRM driver support in ``lv_conf.h``, by CMake compiler define, or by KConfig: + + .. code-block:: c + + #define LV_USE_LINUX_DRM 1 + +2. Link against ``libdrm`` when building. + +.. _linux_drm_basic_usage: + +Basic Usage +~~~~~~~~~~~ + +.. code-block:: c + + #include "lvgl/lvgl.h" + #include "lvgl/demos/lv_demos.h" + #include "lvgl/drivers/drm/lv_linux_drm.h" + + int main(void) + { + /* Initialize LVGL */ + lv_init(); + + /* DRM device node */ + const char *device = "/dev/dri/card0"; + + /* Create a DRM display */ + lv_display_t *disp = lv_linux_drm_create(); + + /* Set DRM device file and connector */ + /* The 2nd argument is the DRM device path */ + /* The 3rd argument is the connector_id (-1 = auto-select first available) */ + lv_linux_drm_set_file(disp, device, -1); + + /* Create demo widgets */ + lv_demo_widgets(); + + /* Handle LVGL tasks */ + 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); + } + + return 0; + } + +Notes +~~~~~ + +- ``connector_id`` specifies which display output (HDMI, eDP, DP, etc.) should be used. + If ``-1`` is passed, the DRM driver will try to automatically pick the first available connector. +- DRM requires proper modesetting. By default, LVGL will select a preferred display mode. + + + +Using DRM with GBM +------------------ + +The DRM driver can optionally use **GBM** (Generic Buffer Management) for buffer allocation. +This allows the driver to use GPU-friendly buffer objects instead of simple dumb framebuffers. + +1. Enable the following option in your ``lv_conf.h`` (or via Kconfig/CMake): + +.. code-block:: c + + #define LV_USE_LINUX_DRM_GBM_BUFFERS 1 + +2. Link against ``libgbm`` when building. + +When this option is enabled: + +- Buffers will be allocated using GBM. +- This can improve performance and compatibility on platforms where GBM is supported. + + + +Using DRM with EGL +------------------ + +The DRM driver can also be combined with :ref:`egl_driver` for hardware-accelerated +rendering via EGL/GLES. + +To enable this, set the following options in your ``lv_conf.h`` (or via Kconfig/CMake): + +.. code-block:: c + + #define LV_USE_LINUX_DRM 1 + #define LV_USE_LINUX_DRM_GBM_BUFFERS 1 + #define LV_LINUX_DRM_USE_EGL 1 + #define LV_USE_OPENGLES 1 + #define LV_USE_DRAW_OPENGLES 1 /* optional but recommended for performance */ + +When ``LV_LINUX_DRM_USE_EGL`` is enabled, the DRM driver will automatically initialize EGL. +No special setup is required beyond the basic DRM initialization shown in :ref:`linux_drm_basic_usage`. + +For a detailed overview of EGL usage and configuration, see :ref:`egl_driver`. + diff --git a/docs/src/details/integration/embedded_linux/drivers/egl.rst b/docs/src/details/integration/embedded_linux/drivers/egl.rst new file mode 100644 index 0000000000..04081cf618 --- /dev/null +++ b/docs/src/details/integration/embedded_linux/drivers/egl.rst @@ -0,0 +1,63 @@ +.. _egl_driver: + +=== +EGL +=== + +Overview +-------- + +The **EGL** driver provides support for creating LVGL displays using the EGL (Embedded-System Graphics Library) API. +EGL is a lower-level API that is more closely tied to the underlying drivers of the platform. +The OpenGL support in LVGL is intended to be portable between different APIs. Currently, there is support for GLFW and EGL. +Using EGL requires some additional platform integration. + +:cpp:func:`lv_opengles_egl_window_create` can be used to create a :cpp:type:`lv_opengles_window_t` +which can be used with the same generic LVGL OpenGL APIs as a GLFW window. + +EGL with DRM +------------ + +EGL can be used together with the DRM driver for hardware-accelerated rendering. + +When ``LV_LINUX_DRM_USE_EGL`` is enabled, the DRM driver will automatically set up EGL. +No additional initialization is required beyond the normal DRM setup. + +See :ref:`linux_drm` for configuration and a basic usage example. + +EGL without DRM (Experimental) +------------------------------ + +.. warning:: + + This feature is experimental and the API is private. Expect breaking changes. + +If you want to use EGL without being tied to DRM, you can enable ``LV_USE_EGL`` using a compiler definition. +This API is currently private and experimental, and people should expect breaking changes. + +.. code-block:: bash + + # Enable standalone EGL (experimental) + -DLV_USE_EGL=1 + +This allows you to use EGL with your own context management or other platforms, but the API may change +without notice in future versions. + +Render Direct to Window +----------------------- + +.. warning:: + + This feature is incomplete and has bugs. + +Performance can be improved if the LVGL OpenGL driver renders its cached textures directly to the window +(and :c:macro:`LV_USE_DRAW_OPENGLES` is enabled). This can be done by creating the display with +:cpp:func:`lv_opengles_window_display_create` instead of :cpp:func:`lv_opengles_texture_create` + +:cpp:func:`lv_opengles_texture_get_texture_id` + :cpp:func:`lv_opengles_window_add_texture`. +Performance should be better with GLFW than EGL. EGL currently has issues when used this way. + +Improving Performance +--------------------- + +There is a renderer in LVGL which caches software-rendered areas as OpenGL textures. +See :ref:`opengl_texture_caching_renderer` to learn more about it. diff --git a/docs/src/details/integration/embedded_linux/drivers/glfw.rst b/docs/src/details/integration/embedded_linux/drivers/glfw.rst index bc02c66eb2..8a58e76af6 100644 --- a/docs/src/details/integration/embedded_linux/drivers/glfw.rst +++ b/docs/src/details/integration/embedded_linux/drivers/glfw.rst @@ -1,4 +1,4 @@ -.. _opengl_es_driver: +.. _glfw_driver: ==== GLFW @@ -7,34 +7,34 @@ GLFW Overview -------- -The **OpenGL ES** display/input -`driver `__ -offers support for simulating the LVGL display and keyboard/mouse inputs in an desktop -window created via GLFW. +The **GLFW** display/input driver offers support for creating +LVGL displays and keyboard/mouse inputs that can be used in an OpenGL context. +It can be used like **Wayland**, **XCB**, **SDL** or **Qt** or it can be used for more embedded applications. -It is an alternative to **Wayland**, **XCB**, **SDL** or **Qt**. +The GLFW driver is a quick way to get started on PC-like platforms. -The main purpose for this driver is for testing/debugging the LVGL application in -an **OpenGL** simulation window. +Getting Started with GLFW +------------------------- Prerequisites -------------- +~~~~~~~~~~~~~ -The OpenGL driver uses GLEW GLFW to access the OpenGL window manager. +The GLFW driver uses GLEW GLFW to access the OpenGL window manager. 1. Install GLEW and GLFW: ``sudo apt-get install libglew-dev libglfw3-dev`` -Configure OpenGL Driver ------------------------ +Configure GLFW Driver +~~~~~~~~~~~~~~~~~~~~~ 1. Required linked libraries: -lGL -lGLEW -lglfw 2. Enable the OpenGL driver support in lv_conf.h, by cmake compiler define or by KConfig + .. code-block:: c #define LV_USE_OPENGLES 1 Basic Usage ------------ +~~~~~~~~~~~ .. code-block:: c @@ -51,7 +51,7 @@ Basic Usage lv_init(); /* create a window and initialize OpenGL */ - lv_glfw_window_t * window = lv_glfw_window_create(WIDTH, HEIGHT, true); + lv_opengles_window_t * window = lv_opengles_glfw_window_create(WIDTH, HEIGHT, true); /* create a display that flushes to a texture */ lv_display_t * texture = lv_opengles_texture_create(WIDTH, HEIGHT); @@ -59,10 +59,10 @@ Basic Usage /* add the texture to the window */ unsigned int texture_id = lv_opengles_texture_get_texture_id(texture); - lv_glfw_texture_t * window_texture = lv_glfw_window_add_texture(window, texture_id, WIDTH, HEIGHT); + lv_opengles_window_texture_t * window_texture = lv_opengles_window_add_texture(window, texture_id, WIDTH, HEIGHT); /* get the mouse indev of the window texture */ - lv_indev_t * mouse = lv_glfw_texture_get_mouse_indev(window_texture); + lv_indev_t * mouse = lv_opengles_window_texture_get_mouse_indev(window_texture); /* add a cursor to the mouse indev */ LV_IMAGE_DECLARE(mouse_cursor_icon); @@ -84,9 +84,9 @@ Basic Usage } Advanced Usage --------------- +~~~~~~~~~~~~~~ -The OpenGL driver can draw textures from the user. A third-party library could be +The GLFW driver can draw textures from the user. A third-party library could be used to add content to a texture and the driver will draw the texture in the window. .. code-block:: c @@ -106,7 +106,7 @@ used to add content to a texture and the driver will draw the texture in the win /* create a window and initialize OpenGL */ /* multiple windows can be created */ - lv_glfw_window_t * window = lv_glfw_window_create(WIDTH, HEIGHT, true); + lv_opengles_window_t * window = lv_opengles_glfw_window_create(WIDTH, HEIGHT, true); /**************************** * OPTIONAL MAIN TEXTURE @@ -118,10 +118,10 @@ used to add content to a texture and the driver will draw the texture in the win /* add the main texture to the window */ unsigned int main_texture_id = lv_opengles_texture_get_texture_id(main_texture); - lv_glfw_texture_t * window_main_texture = lv_glfw_window_add_texture(window, main_texture_id, WIDTH, HEIGHT); + lv_opengles_window_texture_t * window_main_texture = lv_opengles_window_add_texture(window, main_texture_id, WIDTH, HEIGHT); /* get the mouse indev of this main texture */ - lv_indev_t * main_texture_mouse = lv_glfw_texture_get_mouse_indev(window_main_texture); + lv_indev_t * main_texture_mouse = lv_opengles_window_texture_get_mouse_indev(window_main_texture); /* add a cursor to the mouse indev */ LV_IMAGE_DECLARE(mouse_cursor_icon); @@ -143,7 +143,7 @@ used to add content to a texture and the driver will draw the texture in the win /* add the sub texture to the window */ unsigned int sub_texture_id = lv_opengles_texture_get_texture_id(sub_texture); - lv_glfw_texture_t * window_sub_texture = lv_glfw_window_add_texture(window, sub_texture_id, sub_texture_w, sub_texture_h); + lv_opengles_window_texture_t * window_sub_texture = lv_opengles_window_add_texture(window, sub_texture_id, sub_texture_w, sub_texture_h); /* create Widgets on the screen of the sub texture */ lv_display_set_default(sub_texture); @@ -151,11 +151,11 @@ used to add content to a texture and the driver will draw the texture in the win lv_display_set_default(main_texture); /* position the sub texture within the window */ - lv_glfw_texture_set_x(window_sub_texture, 250); - lv_glfw_texture_set_y(window_sub_texture, 150); + lv_opengles_window_texture_set_x(window_sub_texture, 250); + lv_opengles_window_texture_set_y(window_sub_texture, 150); /* optionally change the opacity of the sub texture */ - lv_glfw_texture_set_opa(window_sub_texture, LV_OPA_80); + lv_opengles_window_texture_set_opa(window_sub_texture, LV_OPA_80); /********************************************* * USE AN EXTERNAL OPENGL TEXTURE IN LVGL @@ -186,12 +186,12 @@ used to add content to a texture and the driver will draw the texture in the win glBindTexture(GL_TEXTURE_2D, 0); /* add the external texture to the window */ - lv_glfw_texture_t * window_external_texture = lv_glfw_window_add_texture(window, external_texture_id, img_cogwheel_argb.header.w, img_cogwheel_argb.header.h); + lv_opengles_window_texture_t * window_external_texture = lv_opengles_window_add_texture(window, external_texture_id, img_cogwheel_argb.header.w, img_cogwheel_argb.header.h); /* set the position and opacity of the external texture within the window */ - lv_glfw_texture_set_x(window_external_texture, 20); - lv_glfw_texture_set_y(window_external_texture, 20); - lv_glfw_texture_set_opa(window_external_texture, LV_OPA_70); + lv_opengles_window_texture_set_x(window_external_texture, 20); + lv_opengles_window_texture_set_y(window_external_texture, 20); + lv_opengles_window_texture_set_opa(window_external_texture, LV_OPA_70); /********************************************* * USE AN LVGL TEXTURE IN ANOTHER LIBRARY @@ -203,28 +203,9 @@ used to add content to a texture and the driver will draw the texture in the win third_party_lib_use_texture(sub_texture_id); } -OpenGL Texture Caching Renderer -------------------------------- + +Improving Performance +--------------------- There is a renderer in LVGL which caches software-rendered areas as OpenGL textures. -The textures are retrieved from the cache and reused when there is a match. -The performance will be drastically improved in most cases. - -.. code-block:: c - - #define LV_USE_DRAW_OPENGLES 1 - -Known Limitations -~~~~~~~~~~~~~~~~~ - -- Performance will be the same or slightly worse if the drawn areas are never found in the cache - due to Widgets with continuously varying colors or shapes. One example is a label whose color - is set to a random value every frame, as in the "Multiple labels" scene of the benchmark demo. -- Layers with transparent pixels and an overall layer transparency will not blend correctly. - The effect can be observed in the "Containers with opa_layer" scene of the benchmark demo - in the border corners. -- Layers with rotation are not currently supported. Images with rotation are fine. - - -.. Comment: The above blank line is necessary for Sphinx to not complain, - since it looks for the blank line after a bullet list. +See :ref:`opengl_texture_caching_renderer` to learn more about it. diff --git a/docs/src/details/integration/embedded_linux/drivers/index.rst b/docs/src/details/integration/embedded_linux/drivers/index.rst index f5674e09ef..4fe937a3fe 100644 --- a/docs/src/details/integration/embedded_linux/drivers/index.rst +++ b/docs/src/details/integration/embedded_linux/drivers/index.rst @@ -9,7 +9,9 @@ Drivers fbdev drm + opengl glfw + egl wayland X11 evdev diff --git a/docs/src/details/integration/embedded_linux/drivers/opengl.rst b/docs/src/details/integration/embedded_linux/drivers/opengl.rst new file mode 100644 index 0000000000..bba9730053 --- /dev/null +++ b/docs/src/details/integration/embedded_linux/drivers/opengl.rst @@ -0,0 +1,101 @@ +.. _opengl_driver: + +============== +OpenGL Driver +============== + +Overview +-------- + +The **OpenGL** display driver is a generic driver that creates textures for embedding +LVGL content in other applications. The goal is to create textures that people can +embed in other applications. The OpenGL context must be created by the user or they +can use GLFW or EGL as backends. + +Getting Started with OpenGL +--------------------------- + +Prerequisites +~~~~~~~~~~~~~ + +An OpenGL context must be created before using the OpenGL driver. You can create this using: + +- GLFW (see :ref:`GLFW driver `) +- EGL (see :ref:`EGL driver `) +- Your own OpenGL context management + +Configure OpenGL Driver +~~~~~~~~~~~~~~~~~~~~~~~ + +1. Enable the OpenGL driver support in lv_conf.h, by cmake compiler define or by KConfig + + .. code-block:: c + + #define LV_USE_OPENGLES 1 + +Basic Usage +~~~~~~~~~~~ + +.. code-block:: c + + #include "lvgl/lvgl.h" + + #define WIDTH 640 + #define HEIGHT 480 + + int main() + { + /* initialize lvgl */ + lv_init(); + + /* NOTE: OpenGL context must be created before this point */ + + /* create a display that flushes to a texture */ + lv_display_t * texture = lv_opengles_texture_create(WIDTH, HEIGHT); + lv_display_set_default(texture); + + /* get the texture ID for use in your application */ + unsigned int texture_id = lv_opengles_texture_get_texture_id(texture); + + /* create Widgets on the screen */ + 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); + + /* use texture_id in your OpenGL rendering */ + your_opengl_render_function(texture_id); + } + + return 0; + } + + +.. _opengl_texture_caching_renderer: + +OpenGL Texture Caching Renderer +------------------------------- + +There is a renderer in LVGL which caches software-rendered areas as OpenGL textures. +The textures are retrieved from the cache and reused when there is a match. +The performance will be drastically improved in most cases. + +.. code-block:: c + + #define LV_USE_DRAW_OPENGLES 1 + +Known Limitations +~~~~~~~~~~~~~~~~~ + +- Performance will be the same or slightly worse if the drawn areas are never found in the cache + due to Widgets with continuously varying colors or shapes. One example is a label whose color + is set to a random value every frame, as in the "Multiple labels" scene of the benchmark demo. +- Layers with transparent pixels and an overall layer transparency will not blend correctly. + The effect can be observed in the "Containers with opa_layer" scene of the benchmark demo + in the border corners. +- Layers with rotation are not currently supported. Images with rotation are fine. + + diff --git a/docs/src/details/libs/gltf.rst b/docs/src/details/libs/gltf.rst index 7fb274f0a5..a59f91ff90 100644 --- a/docs/src/details/libs/gltf.rst +++ b/docs/src/details/libs/gltf.rst @@ -207,7 +207,7 @@ Setup 3. **Setup OpenGL ES Driver** - Follow the OpenGL ES driver setup documentation (:ref:``opengl_es_driver``) to configure GLFW and OpenGL ES support for your platform. + Follow the OpenGL ES driver setup documentation (:ref:`opengl_driver`) to configure GLFW and OpenGL ES support for your platform. 4. **Basic Setup Example** @@ -219,10 +219,10 @@ Setup lv_init(); /* GLFW setup */ - lv_glfw_window_t *window = lv_glfw_window_create(WINDOW_WIDTH, WINDOW_HEIGHT, true); + lv_opengles_window_t *window = lv_opengles_glfw_window_create(WINDOW_WIDTH, WINDOW_HEIGHT, true); lv_display_t *display = lv_opengles_texture_create(WINDOW_WIDTH, WINDOW_HEIGHT); unsigned int texture_id = lv_opengles_texture_get_texture_id(display); - lv_glfw_window_add_texture(window, texture_id, WINDOW_WIDTH, WINDOW_HEIGHT); + lv_opengles_window_add_texture(window, texture_id, WINDOW_WIDTH, WINDOW_HEIGHT); /* Load and display glTF demo */ lv_demo_gltf("A:"); diff --git a/docs/src/details/widgets/3dtexture.rst b/docs/src/details/widgets/3dtexture.rst index 300cfea48f..96155ff0ee 100644 --- a/docs/src/details/widgets/3dtexture.rst +++ b/docs/src/details/widgets/3dtexture.rst @@ -34,7 +34,7 @@ OpenGL is the first supported 3D graphics back-end. The following must be enable - :c:macro:`LV_USE_OPENGLES` - :c:macro:`LV_USE_DRAW_OPENGLES` -See :ref:`LVGL's OpenGLES driver docs ` to create a window and a +See :ref:`LVGL's OpenGLES driver docs ` to create a window and a display texture. The `lv_example_3dtexture ` repository is a diff --git a/env_support/cmsis-pack/lv_conf_cmsis.h b/env_support/cmsis-pack/lv_conf_cmsis.h index af84eea468..30b59b96aa 100644 --- a/env_support/cmsis-pack/lv_conf_cmsis.h +++ b/env_support/cmsis-pack/lv_conf_cmsis.h @@ -1164,7 +1164,7 @@ * shared across sub-systems and libraries using the Linux DMA-BUF API. * The GBM library aims to provide a platform independent memory management system * it supports the major GPU vendors - This option requires linking with libgbm */ - #define LV_LINUX_DRM_GBM_BUFFERS 0 + #define LV_USE_LINUX_DRM_GBM_BUFFERS 0 #endif diff --git a/lv_conf_template.h b/lv_conf_template.h index 5e42d86850..fe371d8603 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -360,9 +360,13 @@ #define LV_USE_DRAW_DMA2D_INTERRUPT 0 #endif -/** Draw using cached OpenGLES textures */ +/** Draw using cached OpenGLES textures. Requires LV_USE_OPENGLES */ #define LV_USE_DRAW_OPENGLES 0 +#if LV_USE_DRAW_OPENGLES + #define LV_DRAW_OPENGLES_TEXTURE_CACHE_COUNT 64 +#endif + /** Draw using espressif PPA accelerator */ #define LV_USE_PPA 0 #if LV_USE_PPA @@ -1309,6 +1313,8 @@ * The GBM library aims to provide a platform independent memory management system * it supports the major GPU vendors - This option requires linking with libgbm */ #define LV_USE_LINUX_DRM_GBM_BUFFERS 0 + + #define LV_LINUX_DRM_USE_EGL 0 #endif /** Interface for TFT_eSPI */ @@ -1375,12 +1381,16 @@ #define LV_UEFI_USE_MEMORY_SERVICES 0 /**< Use the memory functions from the boot services table */ #endif -/** Use OpenGL to open window on PC and handle mouse and keyboard */ +/** Use a generic OpenGL driver that can be used to embed in other applications or used with GLFW/EGL */ #define LV_USE_OPENGLES 0 #if LV_USE_OPENGLES #define LV_USE_OPENGLES_DEBUG 1 /**< Enable or disable debug for opengles */ #endif +/** Use GLFW to open window on PC and handle mouse and keyboard. Requires*/ +#define LV_USE_GLFW 0 + + /** QNX Screen display and input drivers */ #define LV_USE_QNX 0 #if LV_USE_QNX diff --git a/scripts/lv_conf_internal_gen.py b/scripts/lv_conf_internal_gen.py index 55a292b517..998d638709 100755 --- a/scripts/lv_conf_internal_gen.py +++ b/scripts/lv_conf_internal_gen.py @@ -215,6 +215,10 @@ LV_EXPORT_CONST_INT(LV_DRAW_BUF_ALIGN); #define LV_WAYLAND_WL_SHELL 0 #endif /* LV_USE_WAYLAND */ +#if LV_USE_LINUX_DRM == 0 + #define LV_LINUX_DRM_USE_EGL 0 +#endif /*LV_USE_LINUX_DRM*/ + #if LV_USE_SYSMON == 0 #define LV_USE_PERF_MONITOR 0 #define LV_USE_MEM_MONITOR 0 @@ -257,6 +261,10 @@ LV_EXPORT_CONST_INT(LV_DRAW_BUF_ALIGN); #endif #endif +#ifndef LV_USE_EGL + #define LV_USE_EGL LV_LINUX_DRM_USE_EGL +#endif /* LV_USE_EGL */ + #if LV_USE_OS #if (LV_USE_FREETYPE || LV_USE_THORVG) && LV_DRAW_THREAD_STACK_SIZE < (32 * 1024) #warning "Increase LV_DRAW_THREAD_STACK_SIZE to at least 32KB for FreeType or ThorVG." diff --git a/src/draw/opengles/lv_draw_opengles.c b/src/draw/opengles/lv_draw_opengles.c index 112dbfdac9..875b8f6bb5 100644 --- a/src/draw/opengles/lv_draw_opengles.c +++ b/src/draw/opengles/lv_draw_opengles.c @@ -11,11 +11,10 @@ #if LV_USE_DRAW_OPENGLES #include "../lv_draw_private.h" #include "../../misc/cache/lv_cache_entry_private.h" -#include "../../drivers/glfw/lv_opengles_debug.h" -#include "../../drivers/glfw/lv_opengles_texture.h" -#include "../../drivers/glfw/lv_opengles_driver.h" -#include -#include +#include "../../drivers/opengles/lv_opengles_debug.h" +#include "../../drivers/opengles/lv_opengles_texture.h" +#include "../../drivers/opengles/lv_opengles_driver.h" +#include "../../drivers/opengles/lv_opengles_private.h" #include "../../draw/lv_draw_label.h" #include "../../draw/lv_draw_rect.h" #include "../../draw/lv_draw_arc.h" @@ -101,7 +100,7 @@ void lv_draw_opengles_init(void) draw_opengles_unit->base_unit.evaluate_cb = evaluate; draw_opengles_unit->base_unit.name = "OPENGLES"; draw_opengles_unit->texture_cache = lv_cache_create(&lv_cache_class_lru_rb_count, - sizeof(cache_data_t), 128, (lv_cache_ops_t) { + sizeof(cache_data_t), LV_DRAW_OPENGLES_TEXTURE_CACHE_COUNT, (lv_cache_ops_t) { .compare_cb = (lv_cache_compare_cb_t)opengles_texture_cache_compare_cb, .create_cb = (lv_cache_create_cb_t)opengles_texture_cache_create_cb, .free_cb = (lv_cache_free_cb_t)opengles_texture_cache_free_cb, @@ -195,9 +194,6 @@ static int32_t dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer) if(texture == 0) { lv_display_t * disp = lv_refr_get_disp_refreshing(); if(layer != disp->layer_head) { - void * buf = lv_draw_layer_alloc_buf(layer); - if(buf == NULL) return -1; - int32_t w = lv_area_get_width(&layer->buf_area); int32_t h = lv_area_get_height(&layer->buf_area); @@ -413,32 +409,33 @@ static void blend_texture_layer(lv_draw_task_t * t) lv_layer_t * dest_layer = t->target_layer; unsigned int target_texture = layer_get_texture(dest_layer); - LV_ASSERT(target_texture != 0); int32_t targ_tex_w = lv_area_get_width(&dest_layer->buf_area); int32_t targ_tex_h = lv_area_get_height(&dest_layer->buf_area); - GL_CALL(glBindTexture(GL_TEXTURE_2D, target_texture)); - - unsigned int framebuffer = get_framebuffer(u); - GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); - GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0)); + if(target_texture) { + unsigned int framebuffer = get_framebuffer(u); + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); + GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0)); + } lv_opengles_viewport(0, 0, targ_tex_w, targ_tex_h); // TODO rotation bool h_flip = false; bool v_flip = false; +#if LV_USE_3DTEXTURE if(t->type == LV_DRAW_TASK_TYPE_3D) { lv_draw_3d_dsc_t * _3d_dsc = (lv_draw_3d_dsc_t *)t->draw_dsc; h_flip = _3d_dsc->h_flip; v_flip = _3d_dsc->v_flip; } +#endif lv_opengles_render_texture(src_texture, &area, draw_dsc->opa, targ_tex_w, targ_tex_h, &t->clip_area, h_flip, - v_flip); + !v_flip); - GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + if(target_texture) { + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + } - - GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); GL_CALL(glDeleteTextures(1, &src_texture)); LV_PROFILER_DRAW_END; } @@ -451,11 +448,13 @@ static void draw_from_cached_texture(lv_draw_task_t * t) data_to_find.draw_dsc = (lv_draw_dsc_base_t *)t->draw_dsc; bool h_flip = false; bool v_flip = false; +#if LV_USE_3DTEXTURE if(t->type == LV_DRAW_TASK_TYPE_3D) { lv_draw_3d_dsc_t * _3d_dsc = (lv_draw_3d_dsc_t *)t->draw_dsc; h_flip = _3d_dsc->h_flip; v_flip = _3d_dsc->v_flip; } +#endif data_to_find.w = lv_area_get_width(&t->_real_area); data_to_find.h = lv_area_get_height(&t->_real_area); data_to_find.texture = 0; @@ -515,15 +514,14 @@ static void draw_from_cached_texture(lv_draw_task_t * t) lv_layer_t * dest_layer = t->target_layer; unsigned int target_texture = layer_get_texture(dest_layer); - LV_ASSERT(target_texture != 0); int32_t targ_tex_w = lv_area_get_width(&dest_layer->buf_area); int32_t targ_tex_h = lv_area_get_height(&dest_layer->buf_area); - GL_CALL(glBindTexture(GL_TEXTURE_2D, target_texture)); - - unsigned int framebuffer = get_framebuffer(u); - GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); - GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0)); + if(target_texture) { + unsigned int framebuffer = get_framebuffer(u); + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); + GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0)); + } lv_opengles_viewport(0, 0, targ_tex_w, targ_tex_h); lv_area_move(&t->clip_area, -dest_layer->buf_area.x1, -dest_layer->buf_area.y1); @@ -531,7 +529,9 @@ static void draw_from_cached_texture(lv_draw_task_t * t) lv_area_move(&render_area, -dest_layer->buf_area.x1, -dest_layer->buf_area.y1); lv_opengles_render_texture(texture, &render_area, 0xff, targ_tex_w, targ_tex_h, &t->clip_area, h_flip, v_flip); - GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + if(target_texture) { + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + } lv_cache_release(u->texture_cache, entry_cached, u); @@ -551,6 +551,8 @@ static void execute_drawing(lv_draw_opengles_unit_t * u) lv_draw_task_t * t = u->task_act; t->draw_unit = (lv_draw_unit_t *)u; + /* the shader-based fill is not working reliably with EGL. */ +#if !LV_USE_EGL if(t->type == LV_DRAW_TASK_TYPE_FILL) { lv_draw_fill_dsc_t * fill_dsc = t->draw_dsc; if(fill_dsc->radius == 0 && fill_dsc->grad.dir == LV_GRAD_DIR_NONE) { @@ -560,22 +562,26 @@ static void execute_drawing(lv_draw_opengles_unit_t * u) lv_area_move(&fill_area, -layer->buf_area.x1, -layer->buf_area.y1); unsigned int target_texture = layer_get_texture(layer); - LV_ASSERT(target_texture != 0); int32_t targ_tex_w = lv_area_get_width(&layer->buf_area); int32_t targ_tex_h = lv_area_get_height(&layer->buf_area); - unsigned int framebuffer = get_framebuffer(u); - GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); - GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0)); + if(target_texture) { + unsigned int framebuffer = get_framebuffer(u); + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); + GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0)); + } lv_opengles_viewport(0, 0, targ_tex_w, targ_tex_h); lv_opengles_render_fill(fill_dsc->color, &fill_area, fill_dsc->opa, targ_tex_w, targ_tex_h); - GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + if(target_texture) { + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + } return; } } +#endif /*!LV_USE_EGL*/ if(t->type == LV_DRAW_TASK_TYPE_LAYER) { blend_texture_layer(t); @@ -609,11 +615,8 @@ static unsigned int create_texture(int32_t w, int32_t h, const void * data) { LV_PROFILER_DRAW_BEGIN; unsigned int texture; - GL_CALL(glGenTextures(1, &texture)); GL_CALL(glBindTexture(GL_TEXTURE_2D, texture)); - GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); - GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); @@ -622,11 +625,11 @@ static unsigned int create_texture(int32_t w, int32_t h, const void * data) * have full ARGB pixels since the alpha channel is required for blending. */ GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data)); - - glGenerateMipmap(GL_TEXTURE_2D); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 20); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + GL_CALL(glGenerateMipmap(GL_TEXTURE_2D)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 20)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); LV_PROFILER_DRAW_END; return texture; @@ -640,15 +643,14 @@ static void lv_draw_opengles_3d(lv_draw_task_t * t, const lv_draw_3d_dsc_t * dsc lv_layer_t * dest_layer = t->target_layer; unsigned int target_texture = layer_get_texture(dest_layer); - LV_ASSERT(target_texture != 0); int32_t targ_tex_w = lv_area_get_width(&dest_layer->buf_area); int32_t targ_tex_h = lv_area_get_height(&dest_layer->buf_area); - GL_CALL(glBindTexture(GL_TEXTURE_2D, target_texture)); - - unsigned int framebuffer = get_framebuffer(u); - GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); - GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0)); + if(target_texture) { + unsigned int framebuffer = get_framebuffer(u); + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); + GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0)); + } lv_opengles_viewport(0, 0, targ_tex_w, targ_tex_h); lv_area_t clip_area = t->clip_area; @@ -657,8 +659,9 @@ static void lv_draw_opengles_3d(lv_draw_task_t * t, const lv_draw_3d_dsc_t * dsc lv_opengles_render_texture(dsc->tex_id, coords, dsc->opa, targ_tex_w, targ_tex_h, &clip_area, dsc->h_flip, !dsc->v_flip); - GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); - GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); + if(target_texture) { + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + } LV_PROFILER_DRAW_END; } #endif /*LV_USE_3DTEXTURE*/ diff --git a/src/drivers/display/drm/lv_linux_drm.c b/src/drivers/display/drm/lv_linux_drm.c index 4d2f3f8707..dd9820e1db 100644 --- a/src/drivers/display/drm/lv_linux_drm.c +++ b/src/drivers/display/drm/lv_linux_drm.c @@ -25,6 +25,10 @@ #include "../../../stdlib/lv_sprintf.h" #include "../../../draw/lv_draw_buf.h" +#if LV_LINUX_DRM_USE_EGL && !LV_USE_LINUX_DRM_GBM_BUFFERS + #error LV_USE_LINUX_DRM_GBM_BUFFERS is required to use LV_LINUX_DRM_USE_EGL +#endif + #if LV_USE_LINUX_DRM_GBM_BUFFERS #include @@ -33,6 +37,17 @@ #endif +#if LV_LINUX_DRM_USE_EGL + + #include "../../opengles/lv_opengles_egl.h" + #include "../../opengles/lv_opengles_texture.h" + #include "../../opengles/lv_opengles_window.h" + #include "../../opengles/lv_opengles_private.h" + #include + #include + +#endif + /********************* * DEFINES *********************/ @@ -46,6 +61,8 @@ #define BUFFER_CNT 2 +#define USE_EGL_EXPERIMENTAL_DIRECT_WINDOW_RENDER 0 + /********************** * TYPEDEFS **********************/ @@ -56,8 +73,19 @@ typedef struct { unsigned long int size; uint8_t * map; uint32_t fb_handle; +#if LV_LINUX_DRM_USE_EGL + uint32_t fb_id; +#endif } drm_buffer_t; +#if LV_LINUX_DRM_USE_EGL + typedef EGLint(*egl_wait_sync_khr_t)(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags); + typedef EGLint(*egl_dup_native_fence_fd_android_t)(EGLDisplay dpy, EGLSyncKHR sync); + typedef EGLSyncKHR(*egl_create_sync_khr_t)(EGLDisplay dpy, EGLenum type, const EGLint * attrib_list); + typedef EGLBoolean(*egl_destroy_sync_khr_t)(EGLDisplay dpy, EGLSyncKHR sync); + typedef EGLint(*egl_client_wait_sync_khr_t)(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout); +#endif + typedef struct { int fd; uint32_t conn_id, enc_id, crtc_id, plane_id, crtc_idx; @@ -80,6 +108,23 @@ typedef struct { drmModePropertyPtr conn_props[128]; drm_buffer_t drm_bufs[BUFFER_CNT]; drm_buffer_t * act_buf; +#if LV_USE_LINUX_DRM_GBM_BUFFERS + struct gbm_device * gbm_device; +#endif +#if LV_LINUX_DRM_USE_EGL + struct gbm_surface * surface; + int kms_in_fence_fd; + int kms_out_fence_fd; + EGLSyncKHR kms_fence; + EGLSyncKHR gpu_fence; + struct gbm_bo * bo; + + egl_wait_sync_khr_t egl_wait_sync_khr; + egl_dup_native_fence_fd_android_t egl_dup_native_fence_fd_android; + egl_create_sync_khr_t egl_create_sync_khr; + egl_destroy_sync_khr_t egl_destroy_sync_khr; + egl_client_wait_sync_khr_t egl_client_wait_sync_khr; +#endif } drm_dev_t; /********************** @@ -96,36 +141,41 @@ static int drm_get_conn_props(drm_dev_t * drm_dev); static int drm_add_plane_property(drm_dev_t * drm_dev, const char * name, uint64_t value); static int drm_add_crtc_property(drm_dev_t * drm_dev, const char * name, uint64_t value); static int drm_add_conn_property(drm_dev_t * drm_dev, const char * name, uint64_t value); -static int drm_dmabuf_set_plane(drm_dev_t * drm_dev, drm_buffer_t * buf); static int find_plane(drm_dev_t * drm_dev, unsigned int fourcc, uint32_t * plane_id, uint32_t crtc_id, uint32_t crtc_idx); static int drm_find_connector(drm_dev_t * drm_dev, int64_t connector_id); static int drm_open(const char * path); static int drm_setup(drm_dev_t * drm_dev, const char * device_path, int64_t connector_id, unsigned int fourcc); -static int drm_allocate_dumb(drm_dev_t * drm_dev, drm_buffer_t * buf); -static int drm_setup_buffers(drm_dev_t * drm_dev); -static void drm_flush_wait(lv_display_t * drm_dev); -static void drm_flush(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map); -static void drm_dmabuf_set_active_buf(lv_event_t * event); static uint32_t tick_get_cb(void); -#if LV_USE_LINUX_DRM_GBM_BUFFERS - +#if !LV_USE_LINUX_DRM_GBM_BUFFERS + static int drm_allocate_dumb(drm_dev_t * drm_dev, drm_buffer_t * buf); +#elif LV_USE_LINUX_DRM_GBM_BUFFERS && !LV_LINUX_DRM_USE_EGL static int create_gbm_buffer(drm_dev_t * drm_dev, drm_buffer_t * buf); - #endif +#if LV_LINUX_DRM_USE_EGL + static drm_buffer_t * drm_fb_get_from_bo(struct gbm_bo * bo); + static void drm_fb_destroy_callback(struct gbm_bo * bo, void * data); + static void drm_gbm_egl_pre(lv_opengles_window_t * window); + static void drm_gbm_egl_post1(lv_opengles_window_t * window); + static void drm_gbm_egl_post2(lv_opengles_window_t * window); + static int drm_atomic_commit(drm_dev_t * drm_dev, uint32_t fb_id, uint32_t flags); + static EGLSyncKHR create_fence(drm_dev_t * drm_dev, EGLDisplay display, int fd); +#else + static int drm_setup_buffers(drm_dev_t * drm_dev); + static int drm_dmabuf_set_plane(drm_dev_t * drm_dev, drm_buffer_t * buf); + static void drm_flush_wait(lv_display_t * drm_dev); + static void drm_flush(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map); + static void drm_dmabuf_set_active_buf(lv_event_t * event); +#endif + + /********************** * STATIC VARIABLES **********************/ -#if LV_USE_LINUX_DRM_GBM_BUFFERS - - static struct gbm_device * gbm_device; - -#endif - /********************** * MACROS **********************/ @@ -139,26 +189,37 @@ static uint32_t tick_get_cb(void); lv_display_t * lv_linux_drm_create(void) { + lv_display_t * disp; + lv_tick_set_cb(tick_get_cb); drm_dev_t * drm_dev = lv_malloc_zeroed(sizeof(drm_dev_t)); LV_ASSERT_MALLOC(drm_dev); if(drm_dev == NULL) return NULL; - lv_display_t * disp = lv_display_create(800, 480); + drm_dev->fd = -1; +#if !LV_LINUX_DRM_USE_EGL + disp = lv_display_create(800, 480); if(disp == NULL) { lv_free(drm_dev); return NULL; } - drm_dev->fd = -1; lv_display_set_driver_data(disp, drm_dev); lv_display_set_flush_wait_cb(disp, drm_flush_wait); lv_display_set_flush_cb(disp, drm_flush); +#else /*LV_LINUX_DRM_USE_EGL*/ + disp = (lv_display_t *) drm_dev; + + drm_dev->kms_in_fence_fd = -1; + drm_dev->kms_out_fence_fd = -1; + +#endif return disp; } +#if !LV_LINUX_DRM_USE_EGL /* Called by LVGL when there is something that needs redrawing * it sets the active buffer. if GBM buffers are used, it issues a DMA_BUF_SYNC * ioctl call to lock the buffer for CPU access, the buffer is unlocked just @@ -204,19 +265,27 @@ static void drm_dmabuf_set_active_buf(lv_event_t * event) } } +#endif /*!LV_LINUX_DRM_USE_EGL*/ void lv_linux_drm_set_file(lv_display_t * disp, const char * file, int64_t connector_id) { - drm_dev_t * drm_dev = lv_display_get_driver_data(disp); int ret; +#if !LV_LINUX_DRM_USE_EGL + drm_dev_t * drm_dev = lv_display_get_driver_data(disp); +#else + drm_dev_t * drm_dev = (drm_dev_t *) disp; +#endif + ret = drm_setup(drm_dev, file, connector_id, DRM_FOURCC); if(ret) { - close(drm_dev->fd); - drm_dev->fd = -1; return; } + int32_t hor_res = drm_dev->width; + int32_t ver_res = drm_dev->height; + +#if !LV_LINUX_DRM_USE_EGL ret = drm_setup_buffers(drm_dev); if(ret) { LV_LOG_ERROR("DRM buffer allocation failed"); @@ -227,8 +296,6 @@ void lv_linux_drm_set_file(lv_display_t * disp, const char * file, int64_t conne LV_LOG_INFO("DRM subsystem and buffer mapped successfully"); - int32_t hor_res = drm_dev->width; - int32_t ver_res = drm_dev->height; int32_t width = drm_dev->mmWidth; size_t buf_size = LV_MIN(drm_dev->drm_bufs[1].size, drm_dev->drm_bufs[0].size); @@ -250,6 +317,32 @@ void lv_linux_drm_set_file(lv_display_t * disp, const char * file, int64_t conne LV_LOG_INFO("Resolution is set to %" LV_PRId32 "x%" LV_PRId32 " at %" LV_PRId32 "dpi", hor_res, ver_res, lv_display_get_dpi(disp)); + +#else /*LV_LINUX_DRM_USE_EGL*/ + lv_opengles_window_t * window = lv_opengles_egl_window_create(hor_res, ver_res, drm_dev->surface, drm_dev->gbm_device, + drm_gbm_egl_pre, drm_gbm_egl_post1, drm_gbm_egl_post2); + lv_opengles_egl_window_set_user_data(window, drm_dev); + +#if !USE_EGL_EXPERIMENTAL_DIRECT_WINDOW_RENDER + /* create a display that flushes to a texture */ + lv_display_t * texture = lv_opengles_texture_create(hor_res, ver_res); + lv_display_set_default(texture); + + /* add the texture to the window */ + unsigned int texture_id = lv_opengles_texture_get_texture_id(texture); + lv_opengles_window_add_texture(window, texture_id, hor_res, ver_res); +#else + /* render directly to the window */ + lv_opengles_window_display_create(window, hor_res, ver_res); +#endif + + drm_dev->egl_wait_sync_khr = (egl_wait_sync_khr_t) eglGetProcAddress("eglWaitSyncKHR"); + drm_dev->egl_dup_native_fence_fd_android = (egl_dup_native_fence_fd_android_t) + eglGetProcAddress("eglDupNativeFenceFDANDROID"); + drm_dev->egl_create_sync_khr = (egl_create_sync_khr_t) eglGetProcAddress("eglCreateSyncKHR"); + drm_dev->egl_destroy_sync_khr = (egl_destroy_sync_khr_t) eglGetProcAddress("eglDestroySyncKHR"); + drm_dev->egl_client_wait_sync_khr = (egl_client_wait_sync_khr_t) eglGetProcAddress("eglClientWaitSyncKHR"); +#endif } /********************** @@ -436,13 +529,14 @@ static int drm_add_conn_property(drm_dev_t * drm_dev, const char * name, uint64_ return 0; } +#if !LV_LINUX_DRM_USE_EGL static int drm_dmabuf_set_plane(drm_dev_t * drm_dev, drm_buffer_t * buf) { int ret; static int first = 1; uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK; -#if LV_USE_LINUX_DRM_GBM_BUFFERS +#if LV_USE_LINUX_DRM_GBM_BUFFERS && !LV_LINUX_DRM_USE_EGL struct dma_buf_sync sync_req; @@ -487,6 +581,7 @@ static int drm_dmabuf_set_plane(drm_dev_t * drm_dev, drm_buffer_t * buf) return 0; } +#endif /*!LV_LINUX_DRM_USE_EGL*/ static int find_plane(drm_dev_t * drm_dev, unsigned int fourcc, uint32_t * plane_id, uint32_t crtc_id, uint32_t crtc_idx) @@ -810,14 +905,27 @@ static int drm_setup(drm_dev_t * drm_dev, const char * device_path, int64_t conn #if LV_USE_LINUX_DRM_GBM_BUFFERS /* Create GBM device and buffer */ - gbm_device = gbm_create_device(drm_dev->fd); + drm_dev->gbm_device = gbm_create_device(drm_dev->fd); - if(gbm_device == NULL) { + if(drm_dev->gbm_device == NULL) { LV_LOG_ERROR("Failed to create GBM device"); goto err; } - LV_LOG_INFO("GBM device backend: %s", gbm_device_get_backend_name(gbm_device)); + LV_LOG_INFO("GBM device backend: %s", gbm_device_get_backend_name(drm_dev->gbm_device)); +#endif + +#if LV_LINUX_DRM_USE_EGL + + /* Add support to create a surface with modifiers */ + drm_dev->surface = gbm_surface_create(drm_dev->gbm_device, + drm_dev->width, drm_dev->height, GBM_BO_FORMAT_ARGB8888, + GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + if(!drm_dev->surface) { + LV_LOG_ERROR("failed to create gbm surface"); + gbm_device_destroy(drm_dev->gbm_device); + goto err; + } #endif @@ -828,6 +936,7 @@ err: return -1; } +#if !LV_USE_LINUX_DRM_GBM_BUFFERS static int drm_allocate_dumb(drm_dev_t * drm_dev, drm_buffer_t * buf) { struct drm_mode_create_dumb creq; @@ -885,8 +994,9 @@ static int drm_allocate_dumb(drm_dev_t * drm_dev, drm_buffer_t * buf) return 0; } +#endif /*!LV_USE_LINUX_DRM_GBM_BUFFERS*/ -#if LV_USE_LINUX_DRM_GBM_BUFFERS +#if LV_USE_LINUX_DRM_GBM_BUFFERS && !LV_LINUX_DRM_USE_EGL static int create_gbm_buffer(drm_dev_t * drm_dev, drm_buffer_t * buf) { @@ -894,9 +1004,7 @@ static int create_gbm_buffer(drm_dev_t * drm_dev, drm_buffer_t * buf) struct gbm_bo * gbm_bo; int prime_fd; uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; - uint32_t w, h, format; uint32_t n_planes; - char * drm_format_name; int res; /* gbm_bo_format does not define anything other than ARGB8888 or XRGB8888 */ @@ -906,7 +1014,7 @@ static int create_gbm_buffer(drm_dev_t * drm_dev, drm_buffer_t * buf) } /* Create a linear GBM buffer object - best practice when modifiers are not used */ - if(!(gbm_bo = gbm_bo_create(gbm_device, + if(!(gbm_bo = gbm_bo_create(drm_dev->gbm_device, drm_dev->width, drm_dev->height, GBM_BO_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR))) { @@ -925,16 +1033,13 @@ static int create_gbm_buffer(drm_dev_t * drm_dev, drm_buffer_t * buf) return -1; } - w = gbm_bo_get_width(gbm_bo); - h = gbm_bo_get_height(gbm_bo); - format = gbm_bo_get_format(gbm_bo); + uint32_t h = gbm_bo_get_height(gbm_bo); pitches[0] = buf->pitch = gbm_bo_get_stride_for_plane(gbm_bo, 0); offsets[0] = buf->offset = gbm_bo_get_offset(gbm_bo, 0); buf->size = h * buf->pitch; - drm_format_name = drmGetFormatName(format); LV_LOG_INFO("Created GBM BO of size: %lu pitch: %u offset: %u format: %s", - buf->size, buf->pitch, buf->offset, drm_format_name); + buf->size, buf->pitch, buf->offset, drmGetFormatName(format)); prime_fd = gbm_bo_get_fd_for_plane(gbm_bo, 0); @@ -975,9 +1080,9 @@ static int create_gbm_buffer(drm_dev_t * drm_dev, drm_buffer_t * buf) } -#endif /* END LV_USE_LINUX_DRM_GBM_BUFFERS */ - +#endif /* LV_USE_LINUX_DRM_GBM_BUFFERS && !LV_LINUX_DRM_USE_EGL*/ +#if !LV_LINUX_DRM_USE_EGL static int drm_setup_buffers(drm_dev_t * drm_dev) { int ret; @@ -1052,6 +1157,204 @@ static void drm_flush(lv_display_t * disp, const lv_area_t * area, uint8_t * px_ drm_dev->act_buf = NULL; } +#endif /*!LV_LINUX_DRM_USE_EGL*/ + +#if LV_LINUX_DRM_USE_EGL + +static void drm_gbm_egl_pre(lv_opengles_window_t * window) +{ + drm_dev_t * drm_dev = lv_opengles_egl_window_get_user_data(window); + if(drm_dev->kms_out_fence_fd != -1) { + EGLDisplay display = lv_opengles_egl_window_get_display(window); + drm_dev->kms_fence = create_fence(drm_dev, display, drm_dev->kms_out_fence_fd); + LV_ASSERT_NULL(drm_dev->kms_fence); + + drm_dev->kms_out_fence_fd = -1; + + int result = drm_dev->egl_wait_sync_khr(display, drm_dev->kms_fence, 0); + LV_ASSERT(result == 1); + } +} + +static void drm_gbm_egl_post1(lv_opengles_window_t * window) +{ + drm_dev_t * drm_dev = lv_opengles_egl_window_get_user_data(window); + EGLDisplay display = lv_opengles_egl_window_get_display(window); + drm_dev->gpu_fence = create_fence(drm_dev, display, EGL_NO_NATIVE_FENCE_FD_ANDROID); + LV_ASSERT_NULL(drm_dev->gpu_fence); +} + +static void drm_gbm_egl_post2(lv_opengles_window_t * window) +{ + drm_dev_t * drm_dev = lv_opengles_egl_window_get_user_data(window); + EGLDisplay display = lv_opengles_egl_window_get_display(window); + + drm_dev->kms_in_fence_fd = drm_dev->egl_dup_native_fence_fd_android(display, drm_dev->gpu_fence); + bool res = drm_dev->egl_destroy_sync_khr(display, drm_dev->gpu_fence); + LV_ASSERT(res); + drm_dev->gpu_fence = NULL; + LV_ASSERT(drm_dev->kms_in_fence_fd != -1); + + drm_buffer_t * fb; + uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK; + int ret; + + /* Get the next bo to display */ + struct gbm_bo * next_bo = gbm_surface_lock_front_buffer(drm_dev->surface); + if(!next_bo) { + LV_LOG_ERROR("Failed to lock frontbuffer"); + } + + fb = drm_fb_get_from_bo(next_bo); + if(!fb) { + LV_LOG_ERROR("Failed to get a new framebuffer BO"); + } + + if(drm_dev->kms_fence) { + EGLint status; + + do { + status = drm_dev->egl_client_wait_sync_khr(display, + drm_dev->kms_fence, + 0, + EGL_FOREVER_KHR); + } while(status != EGL_CONDITION_SATISFIED_KHR); + + bool res = drm_dev->egl_destroy_sync_khr(display, drm_dev->kms_fence); + LV_ASSERT(res); + drm_dev->kms_fence = NULL; + } + + ret = drm_atomic_commit(drm_dev, fb->fb_id, flags); + if(ret) { + LV_LOG_ERROR("failed to commit: %s", strerror(errno)); + } + + /* release last buffer to render on again: */ + if(drm_dev->bo) + gbm_surface_release_buffer(drm_dev->surface, drm_dev->bo); + drm_dev->bo = next_bo; + +} + + +static drm_buffer_t * drm_fb_get_from_bo(struct gbm_bo * bo) +{ + int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo)); + drm_buffer_t * fb = gbm_bo_get_user_data(bo); + uint32_t width, height, format, strides[4] = {0}, handles[4] = {0}, offsets[4] = {0}; + int ret = -1; + + if(fb) + return fb; + + fb = lv_malloc_zeroed(sizeof(drm_buffer_t)); + + width = gbm_bo_get_width(bo); + height = gbm_bo_get_height(bo); + format = gbm_bo_get_format(bo); + const int num_planes = gbm_bo_get_plane_count(bo); + for(int i = 0; i < num_planes; i++) { + handles[i] = gbm_bo_get_handle_for_plane(bo, i).u32; + strides[i] = gbm_bo_get_stride_for_plane(bo, i); + offsets[i] = gbm_bo_get_offset(bo, i); + } + + memcpy(handles, (uint32_t [4]) { + gbm_bo_get_handle(bo).u32, 0, 0, 0 + }, 16); + memcpy(strides, (uint32_t [4]) { + gbm_bo_get_stride(bo), 0, 0, 0 + }, 16); + memset(offsets, 0, 16); + ret = drmModeAddFB2(drm_fd, width, height, format, + handles, strides, offsets, &fb->fb_id, 0); + fb->fb_handle = fb->fb_id; + + if(ret) { + LV_LOG_ERROR("failed to create fb: %s", strerror(errno)); + lv_free(fb); + return NULL; + } + + gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback); + + return fb; +} + +static void drm_fb_destroy_callback(struct gbm_bo * bo, void * data) +{ + int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo)); + drm_buffer_t * fb = data; + + if(fb->fb_id) + drmModeRmFB(drm_fd, fb->fb_id); + + lv_free(fb); +} + +static int drm_atomic_commit(drm_dev_t * drm_dev, uint32_t fb_id, uint32_t flags) +{ + int ret; + static int first = 1; + + drm_dev->req = drmModeAtomicAlloc(); + + /* On first Atomic commit, do a modeset */ + if(first) { + drm_add_conn_property(drm_dev, "CRTC_ID", drm_dev->crtc_id); + + drm_add_crtc_property(drm_dev, "MODE_ID", drm_dev->blob_id); + drm_add_crtc_property(drm_dev, "ACTIVE", 1); + + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + + first = 0; + } + + drm_add_plane_property(drm_dev, "FB_ID", fb_id); + drm_add_plane_property(drm_dev, "CRTC_ID", drm_dev->crtc_id); + drm_add_plane_property(drm_dev, "SRC_X", 0); + drm_add_plane_property(drm_dev, "SRC_Y", 0); + drm_add_plane_property(drm_dev, "SRC_W", drm_dev->width << 16); + drm_add_plane_property(drm_dev, "SRC_H", drm_dev->height << 16); + drm_add_plane_property(drm_dev, "CRTC_X", 0); + drm_add_plane_property(drm_dev, "CRTC_Y", 0); + drm_add_plane_property(drm_dev, "CRTC_W", drm_dev->width); + drm_add_plane_property(drm_dev, "CRTC_H", drm_dev->height); + + if(drm_dev->kms_in_fence_fd != -1) { + drm_add_crtc_property(drm_dev, "OUT_FENCE_PTR", (uintptr_t) &drm_dev->kms_out_fence_fd); + drm_add_plane_property(drm_dev, "IN_FENCE_FD", drm_dev->kms_in_fence_fd); + } + + ret = drmModeAtomicCommit(drm_dev->fd, drm_dev->req, flags, NULL); + if(ret) { + LV_LOG_ERROR("drmModeAtomicCommit failed: %s (%d)", strerror(errno), errno); + drmModeAtomicFree(drm_dev->req); + return ret; + } + + if(drm_dev->kms_in_fence_fd != -1) { + close(drm_dev->kms_in_fence_fd); + drm_dev->kms_in_fence_fd = -1; + } + + return 0; +} + +static EGLSyncKHR create_fence(drm_dev_t * drm_dev, EGLDisplay display, int fd) +{ + EGLint attrib_list[] = { + EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fd, + EGL_NONE, + }; + EGLSyncKHR fence = drm_dev->egl_create_sync_khr(display, EGL_SYNC_NATIVE_FENCE_ANDROID, attrib_list); + LV_ASSERT_NULL(fence); + return fence; +} + +#endif /*LV_LINUX_DRM_USE_EGL*/ static uint32_t tick_get_cb(void) { diff --git a/src/drivers/glfw/lv_glfw_window.c b/src/drivers/glfw/lv_glfw_window.c deleted file mode 100644 index bbe3145ad3..0000000000 --- a/src/drivers/glfw/lv_glfw_window.c +++ /dev/null @@ -1,441 +0,0 @@ -/** - * @file lv_glfw_window.c - * - */ - -/********************* - * INCLUDES - *********************/ -#include "lv_glfw_window_private.h" -#if LV_USE_OPENGLES -#include -#include "../../core/lv_refr.h" -#include "../../stdlib/lv_sprintf.h" -#include "../../stdlib/lv_string.h" -#include "../../core/lv_global.h" -#include "../../display/lv_display_private.h" -#include "../../indev/lv_indev.h" -#include "../../lv_init.h" -#include "../../misc/lv_area_private.h" - -#include -#include - -#include "lv_opengles_driver.h" -#include "lv_opengles_texture.h" - -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ -static void window_update_handler(lv_timer_t * t); -static uint32_t lv_glfw_tick_count_callback(void); -static lv_glfw_window_t * lv_glfw_get_lv_window_from_window(GLFWwindow * window); -static void glfw_error_cb(int error, const char * description); -static int lv_glfw_init(void); -static int lv_glew_init(void); -static void lv_glfw_timer_init(void); -static void lv_glfw_window_config(GLFWwindow * window, bool use_mouse_indev); -static void lv_glfw_window_quit(void); -static void window_close_callback(GLFWwindow * window); -static void key_callback(GLFWwindow * window, int key, int scancode, int action, int mods); -static void mouse_button_callback(GLFWwindow * window, int button, int action, int mods); -static void mouse_move_callback(GLFWwindow * window, double xpos, double ypos); -static void proc_mouse(lv_glfw_window_t * window); -static void indev_read_cb(lv_indev_t * indev, lv_indev_data_t * data); -static void framebuffer_size_callback(GLFWwindow * window, int width, int height); - -/********************** - * STATIC VARIABLES - **********************/ -static bool glfw_inited; -static bool glew_inited; -static lv_timer_t * update_handler_timer; -static lv_ll_t glfw_window_ll; - -/********************** - * MACROS - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ - -lv_glfw_window_t * lv_glfw_window_create_ex(int32_t hor_res, int32_t ver_res, bool use_mouse_indev, bool h_flip, - bool v_flip, const char * title) -{ - LV_ASSERT_NULL(title); - if(lv_glfw_init() != 0) { - LV_LOG_ERROR("Failed to init glfw"); - return NULL; - } - - lv_glfw_window_t * window = lv_ll_ins_tail(&glfw_window_ll); - LV_ASSERT_MALLOC(window); - if(window == NULL) { - LV_LOG_ERROR("Failed to create glfw window"); - return NULL; - } - lv_memzero(window, sizeof(*window)); - - /* Create window with graphics context */ - lv_glfw_window_t * existing_window = lv_ll_get_head(&glfw_window_ll); - window->window = glfwCreateWindow(hor_res, ver_res, title, NULL, - existing_window ? existing_window->window : NULL); - if(window->window == NULL) { - LV_LOG_ERROR("glfwCreateWindow fail"); - lv_ll_remove(&glfw_window_ll, window); - lv_free(window); - return NULL; - } - - glfwSetWindowTitle(window->window, title); - - window->h_flip = h_flip; - window->v_flip = v_flip; - window->hor_res = hor_res; - window->ver_res = ver_res; - - lv_ll_init(&window->textures, sizeof(lv_glfw_texture_t)); - window->use_indev = use_mouse_indev; - - glfwSetWindowUserPointer(window->window, window); - lv_glfw_timer_init(); - lv_glfw_window_config(window->window, use_mouse_indev); - lv_glew_init(); - glfwMakeContextCurrent(window->window); - lv_opengles_init(); - - return window; -} - -lv_glfw_window_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res, bool use_mouse_indev) -{ - return lv_glfw_window_create_ex(hor_res, ver_res, use_mouse_indev, false, false, "LVGL Simulator"); -} - -void lv_glfw_window_set_title(lv_glfw_window_t * window, const char * new_title) -{ - glfwSetWindowTitle(window->window, new_title); -} - -void lv_glfw_window_delete(lv_glfw_window_t * window) -{ - glfwDestroyWindow(window->window); - if(window->use_indev) { - lv_glfw_texture_t * texture; - LV_LL_READ(&window->textures, texture) { - if(texture->indev != NULL) lv_indev_delete(texture->indev); - } - } - lv_ll_clear(&window->textures); - lv_ll_remove(&glfw_window_ll, window); - lv_free(window); - - if(lv_ll_is_empty(&glfw_window_ll)) { - lv_glfw_window_quit(); - } -} - -void * lv_glfw_window_get_glfw_window(lv_glfw_window_t * window) -{ - return (void *)(window->window); -} - -void lv_glfw_window_set_flip(lv_glfw_window_t * window, bool h_flip, bool v_flip) -{ - window->h_flip = h_flip; - window->v_flip = v_flip; -} - -lv_glfw_texture_t * lv_glfw_window_add_texture(lv_glfw_window_t * window, unsigned int texture_id, int32_t w, int32_t h) -{ - lv_glfw_texture_t * texture = lv_ll_ins_tail(&window->textures); - LV_ASSERT_MALLOC(texture); - if(texture == NULL) return NULL; - lv_memzero(texture, sizeof(*texture)); - texture->window = window; - texture->texture_id = texture_id; - lv_area_set(&texture->area, 0, 0, w - 1, h - 1); - texture->opa = LV_OPA_COVER; - - if(window->use_indev) { - lv_display_t * texture_disp = lv_opengles_texture_get_from_texture_id(texture_id); - if(texture_disp != NULL) { - lv_indev_t * indev = lv_indev_create(); - if(indev == NULL) { - lv_ll_remove(&window->textures, texture); - lv_free(texture); - return NULL; - } - texture->indev = indev; - lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); - lv_indev_set_read_cb(indev, indev_read_cb); - lv_indev_set_driver_data(indev, texture); - lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT); - lv_indev_set_display(indev, texture_disp); - } - } - - return texture; -} - -void lv_glfw_texture_remove(lv_glfw_texture_t * texture) -{ - if(texture->indev != NULL) { - lv_indev_delete(texture->indev); - } - lv_ll_remove(&texture->window->textures, texture); - lv_free(texture); -} - -void lv_glfw_texture_set_x(lv_glfw_texture_t * texture, int32_t x) -{ - lv_area_set_pos(&texture->area, x, texture->area.y1); -} - -void lv_glfw_texture_set_y(lv_glfw_texture_t * texture, int32_t y) -{ - lv_area_set_pos(&texture->area, texture->area.x1, y); -} - -void lv_glfw_texture_set_opa(lv_glfw_texture_t * texture, lv_opa_t opa) -{ - texture->opa = opa; -} - -lv_indev_t * lv_glfw_texture_get_mouse_indev(lv_glfw_texture_t * texture) -{ - return texture->indev; -} - -/********************** - * STATIC FUNCTIONS - **********************/ - -static int lv_glfw_init(void) -{ - if(glfw_inited) { - return 0; - } - - glfwSetErrorCallback(glfw_error_cb); - - int ret = glfwInit(); - if(ret == 0) { - LV_LOG_ERROR("glfwInit fail."); - return 1; - } - - lv_ll_init(&glfw_window_ll, sizeof(lv_glfw_window_t)); - - glfw_inited = true; - return 0; -} - -static int lv_glew_init(void) -{ - if(glew_inited) { - return 0; - } - - GLenum ret = glewInit(); - if(ret != GLEW_OK) { - LV_LOG_ERROR("glewInit fail: %d.", ret); - return ret; - } - - LV_LOG_INFO("GL version: %s", glGetString(GL_VERSION)); - LV_LOG_INFO("GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); - - glew_inited = true; - - return 0; -} - -static void lv_glfw_timer_init(void) -{ - if(update_handler_timer == NULL) { - update_handler_timer = lv_timer_create(window_update_handler, LV_DEF_REFR_PERIOD, NULL); - lv_tick_set_cb(lv_glfw_tick_count_callback); - } -} - -static void lv_glfw_window_config(GLFWwindow * window, bool use_mouse_indev) -{ - glfwMakeContextCurrent(window); - - glfwSwapInterval(1); - - glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); - - if(use_mouse_indev) { - glfwSetMouseButtonCallback(window, mouse_button_callback); - glfwSetCursorPosCallback(window, mouse_move_callback); - } - - glfwSetKeyCallback(window, key_callback); - - glfwSetWindowCloseCallback(window, window_close_callback); -} - -static void lv_glfw_window_quit(void) -{ - lv_timer_delete(update_handler_timer); - update_handler_timer = NULL; - - glfwTerminate(); - glfw_inited = false; - - lv_deinit(); - - exit(0); -} - -static void window_update_handler(lv_timer_t * t) -{ - LV_UNUSED(t); - - lv_glfw_window_t * window; - - glfwPollEvents(); - - /* delete windows that are ready to close */ - window = lv_ll_get_head(&glfw_window_ll); - while(window) { - lv_glfw_window_t * window_to_delete = window->closing ? window : NULL; - window = lv_ll_get_next(&glfw_window_ll, window); - if(window_to_delete) { - glfwSetWindowShouldClose(window_to_delete->window, GLFW_TRUE); - lv_glfw_window_delete(window_to_delete); - } - } - - /* render each window */ - LV_LL_READ(&glfw_window_ll, window) { - glfwMakeContextCurrent(window->window); - lv_opengles_viewport(0, 0, window->hor_res, window->ver_res); - lv_opengles_render_clear(); - - /* render each texture in the window */ - lv_glfw_texture_t * texture; - LV_LL_READ(&window->textures, texture) { - /* if the added texture is an LVGL opengles texture display, refresh it before rendering it */ - lv_display_t * texture_disp = lv_opengles_texture_get_from_texture_id(texture->texture_id); - if(texture_disp != NULL) { - lv_refr_now(texture_disp); - } - - lv_area_t clip_area = texture->area; -#if LV_USE_DRAW_OPENGLES - lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res, - &clip_area, window->h_flip, texture_disp == NULL ? window->v_flip : !window->v_flip); -#else - lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res, - &clip_area, window->h_flip, window->v_flip); -#endif - } - - /* Swap front and back buffers */ - glfwSwapBuffers(window->window); - } -} - -static void glfw_error_cb(int error, const char * description) -{ - LV_LOG_ERROR("GLFW Error %d: %s", error, description); -} - -static lv_glfw_window_t * lv_glfw_get_lv_window_from_window(GLFWwindow * window) -{ - return glfwGetWindowUserPointer(window); -} - -static void window_close_callback(GLFWwindow * window) -{ - lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); - lv_window->closing = 1; -} - -static void key_callback(GLFWwindow * window, int key, int scancode, int action, int mods) -{ - LV_UNUSED(scancode); - LV_UNUSED(mods); - if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { - lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); - lv_window->closing = 1; - } -} - -static void mouse_button_callback(GLFWwindow * window, int button, int action, int mods) -{ - LV_UNUSED(mods); - if(button == GLFW_MOUSE_BUTTON_LEFT) { - lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); - lv_window->mouse_last_state = action == GLFW_PRESS ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; - proc_mouse(lv_window); - } -} - -static void mouse_move_callback(GLFWwindow * window, double xpos, double ypos) -{ - lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); - lv_window->mouse_last_point.x = (int32_t)xpos; - lv_window->mouse_last_point.y = (int32_t)ypos; - proc_mouse(lv_window); -} - -static void proc_mouse(lv_glfw_window_t * window) -{ - /* mouse activity will affect the topmost LVGL display texture */ - lv_glfw_texture_t * texture; - LV_LL_READ_BACK(&window->textures, texture) { - if(lv_area_is_point_on(&texture->area, &window->mouse_last_point, 0)) { - /* adjust the mouse pointer coordinates so that they are relative to the texture */ - if(window->h_flip) { - texture->indev_last_point.x = texture->area.x2 - window->mouse_last_point.x; - } - else { - texture->indev_last_point.x = window->mouse_last_point.x - texture->area.x1; - } - if(window->v_flip) { - texture->indev_last_point.y = (texture->area.y2 - window->mouse_last_point.y); - } - else { - texture->indev_last_point.y = (window->mouse_last_point.y - texture->area.y1); - } - texture->indev_last_state = window->mouse_last_state; - lv_indev_read(texture->indev); - break; - } - } -} - -static void indev_read_cb(lv_indev_t * indev, lv_indev_data_t * data) -{ - lv_glfw_texture_t * texture = lv_indev_get_driver_data(indev); - data->point = texture->indev_last_point; - data->state = texture->indev_last_state; -} - -static void framebuffer_size_callback(GLFWwindow * window, int width, int height) -{ - lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); - lv_window->hor_res = width; - lv_window->ver_res = height; -} - -static uint32_t lv_glfw_tick_count_callback(void) -{ - double tick = glfwGetTime() * 1000.0; - return (uint32_t)tick; -} - -#endif /*LV_USE_OPENGLES*/ diff --git a/src/drivers/glfw/lv_glfw_window.h b/src/drivers/glfw/lv_glfw_window.h deleted file mode 100644 index e1388c2b9c..0000000000 --- a/src/drivers/glfw/lv_glfw_window.h +++ /dev/null @@ -1,143 +0,0 @@ -/** - * @file lv_glfw_window.h - * - */ - -#ifndef LV_GLFW_WINDOW_H -#define LV_GLFW_WINDOW_H - -#ifdef __cplusplus -extern "C" { -#endif - -/********************* - * INCLUDES - *********************/ - -#include "../../lv_conf_internal.h" -#if LV_USE_OPENGLES - -#include "../../misc/lv_types.h" -#include "../../display/lv_display.h" - -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * GLOBAL PROTOTYPES - **********************/ - -/** - * Create a GLFW window with no textures and initialize OpenGL - * @param hor_res width in pixels of the window - * @param ver_res height in pixels of the window - * @param use_mouse_indev send pointer indev input to LVGL display textures - * @return the new GLFW window handle - */ -lv_glfw_window_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res, bool use_mouse_indev); - -/** - * Create a GLFW window with no textures and initialize OpenGL - * @param hor_res width in pixels of the window - * @param ver_res height in pixels of the window - * @param use_mouse_indev send pointer indev input to LVGL display textures - * @param h_flip Should the window contents be horizontally mirrored? - * @param v_flip Should the window contents be vertically mirrored? - * @param title The window title - * @return the new GLFW window handle - */ -lv_glfw_window_t * lv_glfw_window_create_ex(int32_t hor_res, int32_t ver_res, bool use_mouse_indev, bool h_flip, - bool v_flip, const char * title); - -/** - * Set the window's title text - * @param window GLFW window to configure - * @param new_title The new title text - */ -void lv_glfw_window_set_title(lv_glfw_window_t * window, const char * new_title); - -/** - * Delete a GLFW window. If it is the last one, the process will exit - * @param window GLFW window to delete - */ -void lv_glfw_window_delete(lv_glfw_window_t * window); - -/** - * Set the horizontal / vertical flipping of a GLFW window - * @param window GLFW window to configure - * @param h_flip Should the window contents be horizontally mirrored? - * @param v_flip Should the window contents be vertically mirrored? - */ -void lv_glfw_window_set_flip(lv_glfw_window_t * window, bool h_flip, bool v_flip); - -/** - * Get the GLFW window handle for an lv_glfw_window - * @param window GLFW window to return the handle of - * @return the GLFW window handle - */ -void * lv_glfw_window_get_glfw_window(lv_glfw_window_t * window); - -/** - * Add a texture to the GLFW window. It can be an LVGL display texture, or any OpenGL texture - * @param window GLFW window - * @param texture_id OpenGL texture ID - * @param w width in pixels of the texture - * @param h height in pixels of the texture - * @return the new texture handle - */ -lv_glfw_texture_t * lv_glfw_window_add_texture(lv_glfw_window_t * window, unsigned int texture_id, int32_t w, - int32_t h); - -/** - * Remove a texture from its GLFW window and delete it - * @param texture handle of a GLFW window texture - */ -void lv_glfw_texture_remove(lv_glfw_texture_t * texture); - -/** - * Set the x position of a texture within its GLFW window - * @param texture handle of a GLFW window texture - * @param x new x position of the texture - */ -void lv_glfw_texture_set_x(lv_glfw_texture_t * texture, int32_t x); - -/** - * Set the y position of a texture within its GLFW window - * @param texture handle of a GLFW window texture - * @param y new y position of the texture - */ -void lv_glfw_texture_set_y(lv_glfw_texture_t * texture, int32_t y); - -/** - * Set the opacity of a texture in a GLFW window - * @param texture handle of a GLFW window texture - * @param opa new opacity of the texture - */ -void lv_glfw_texture_set_opa(lv_glfw_texture_t * texture, lv_opa_t opa); - -/** - * Get the mouse indev associated with a texture in a GLFW window, if it exists - * @param texture handle of a GLFW window texture - * @return the indev or `NULL` - * @note there will only be an indev if the texture is based on an - * LVGL display texture and the window was created with - * `use_mouse_indev` as `true` - */ -lv_indev_t * lv_glfw_texture_get_mouse_indev(lv_glfw_texture_t * texture); - -/********************** - * MACROS - **********************/ - -#endif /* LV_USE_OPENGLES */ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* LV_GLFW_WINDOW_H */ diff --git a/src/drivers/glfw/lv_glfw_window_private.h b/src/drivers/glfw/lv_glfw_window_private.h deleted file mode 100644 index 1a769a1ef0..0000000000 --- a/src/drivers/glfw/lv_glfw_window_private.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @file lv_glfw_window_private.h - * - */ - -#ifndef LV_GLFW_WINDOW_PRIVATE_H -#define LV_GLFW_WINDOW_PRIVATE_H - -#ifdef __cplusplus -extern "C" { -#endif - -/********************* - * INCLUDES - *********************/ - -#include "lv_glfw_window.h" -#if LV_USE_OPENGLES - -#include -#include - -#include "../../misc/lv_area.h" -#include "../../display/lv_display.h" -#include "../../indev/lv_indev.h" - -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ - -struct _lv_glfw_window_t { - GLFWwindow * window; - int32_t hor_res; - int32_t ver_res; - bool h_flip; - bool v_flip; - lv_ll_t textures; - lv_point_t mouse_last_point; - lv_indev_state_t mouse_last_state; - uint8_t use_indev : 1; - uint8_t closing : 1; -}; - -struct _lv_glfw_texture_t { - lv_glfw_window_t * window; - unsigned int texture_id; - lv_area_t area; - lv_opa_t opa; - lv_indev_t * indev; - lv_point_t indev_last_point; - lv_indev_state_t indev_last_state; -}; - -/********************** - * GLOBAL PROTOTYPES - **********************/ - -/********************** - * MACROS - **********************/ - -#endif /*LV_USE_OPENGLES*/ - -#ifdef __cplusplus -} /*extern "C"*/ -#endif - -#endif /*LV_GLFW_WINDOW_PRIVATE_H*/ diff --git a/src/drivers/lv_drivers.h b/src/drivers/lv_drivers.h index dc3377fff6..25a38f4baf 100644 --- a/src/drivers/lv_drivers.h +++ b/src/drivers/lv_drivers.h @@ -51,9 +51,11 @@ extern "C" { #include "windows/lv_windows_input.h" #include "windows/lv_windows_display.h" -#include "glfw/lv_glfw_window.h" -#include "glfw/lv_opengles_texture.h" -#include "glfw/lv_opengles_driver.h" +#include "opengles/lv_opengles_window.h" +#include "opengles/lv_opengles_texture.h" +#include "opengles/lv_opengles_driver.h" +#include "opengles/lv_opengles_glfw.h" +#include "opengles/lv_opengles_egl.h" #include "qnx/lv_qnx.h" diff --git a/src/drivers/glfw/lv_opengles_debug.c b/src/drivers/opengles/lv_opengles_debug.c similarity index 93% rename from src/drivers/glfw/lv_opengles_debug.c rename to src/drivers/opengles/lv_opengles_debug.c index 1b098be6b0..c0d7ecf4c8 100644 --- a/src/drivers/glfw/lv_opengles_debug.c +++ b/src/drivers/opengles/lv_opengles_debug.c @@ -10,6 +10,8 @@ #include "lv_opengles_debug.h" #if LV_USE_OPENGLES +#include "lv_opengles_private.h" + #include "../../misc/lv_log.h" /********************* @@ -36,6 +38,7 @@ * GLOBAL FUNCTIONS **********************/ +#if LV_USE_OPENGLES_DEBUG void GLClearError() { while(glGetError() != GL_NO_ERROR); @@ -48,6 +51,7 @@ void GLLogCall(const char * function, const char * file, int line) LV_LOG_ERROR("[OpenGL Error] (%d) %s %s:%d", error, function, file, line); } } +#endif /********************** * STATIC FUNCTIONS diff --git a/src/drivers/glfw/lv_opengles_debug.h b/src/drivers/opengles/lv_opengles_debug.h similarity index 61% rename from src/drivers/glfw/lv_opengles_debug.h rename to src/drivers/opengles/lv_opengles_debug.h index aa8bbebe93..3e26302a9c 100644 --- a/src/drivers/glfw/lv_opengles_debug.h +++ b/src/drivers/opengles/lv_opengles_debug.h @@ -10,17 +10,37 @@ extern "C" { #endif +/********************* + * INCLUDES + *********************/ + #include "../../lv_conf_internal.h" #if LV_USE_OPENGLES -#include -#include -#include +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +#if LV_USE_OPENGLES_DEBUG void GLClearError(void); void GLLogCall(const char * function, const char * file, int line); +#endif + +/********************** + * MACROS + **********************/ + #if LV_USE_OPENGLES_DEBUG #define GL_CALL(x) do {\ GLClearError();\ diff --git a/src/drivers/glfw/lv_opengles_driver.c b/src/drivers/opengles/lv_opengles_driver.c similarity index 93% rename from src/drivers/glfw/lv_opengles_driver.c rename to src/drivers/opengles/lv_opengles_driver.c index 076e748b9e..bc9de6dee1 100644 --- a/src/drivers/glfw/lv_opengles_driver.c +++ b/src/drivers/opengles/lv_opengles_driver.c @@ -6,15 +6,17 @@ /********************* * INCLUDES *********************/ -#include "../../display/lv_display.h" -#include "../../misc/lv_area_private.h" +#include "lv_opengles_driver.h" #if LV_USE_OPENGLES #include "../../misc/lv_types.h" #include "../../misc/lv_profiler.h" #include "lv_opengles_debug.h" -#include "lv_opengles_driver.h" +#include "lv_opengles_private.h" + +#include "../../display/lv_display.h" +#include "../../misc/lv_area_private.h" /********************* * DEFINES @@ -85,6 +87,8 @@ static int shader_location[] = { 0, 0, 0, 0, 0, 0 }; static const char * vertex_shader = "#version 300 es\n" "\n" + "precision mediump float;\n" + "\n" "in vec4 position;\n" "in vec2 texCoord;\n" "\n" @@ -96,19 +100,19 @@ static const char * vertex_shader = "{\n" " gl_Position = vec4((u_VertexTransform * vec3(position.xy, 1)).xy, position.zw);\n" " v_TexCoord = texCoord;\n" - "};\n"; + "}\n"; static const char * fragment_shader = "#version 300 es\n" "\n" "precision lowp float;\n" "\n" - "layout(location = 0) out vec4 color;\n" + "out vec4 color;\n" "\n" "in vec2 v_TexCoord;\n" "\n" "uniform sampler2D u_Texture;\n" - "uniform int u_ColorDepth;\n" + "uniform float u_ColorDepth;\n" "uniform float u_Opa;\n" "uniform bool u_IsFill;\n" "uniform vec3 u_FillColor;\n" @@ -117,18 +121,18 @@ static const char * fragment_shader = "{\n" " vec4 texColor;\n" " if (u_IsFill) {\n" - " texColor = vec4(u_FillColor, 1.0f);\n" + " texColor = vec4(u_FillColor, 1.0);\n" " } else {\n" " texColor = texture(u_Texture, v_TexCoord);\n" " }\n" - " if (u_ColorDepth == 8) {\n" + " if (abs(u_ColorDepth - 8.0) < 0.1) {\n" " float gray = texColor.r;\n" " color = vec4(gray, gray, gray, u_Opa);\n" " } else {\n" " float combinedAlpha = texColor.a * u_Opa;\n" " color = vec4(texColor.rgb * combinedAlpha, combinedAlpha);\n" " }\n" - "};\n"; + "}\n"; /********************** * MACROS @@ -206,7 +210,7 @@ void lv_opengles_render_clear(void) void lv_opengles_viewport(int32_t x, int32_t y, int32_t w, int32_t h) { LV_PROFILER_DRAW_BEGIN; - glViewport(x, y, w, h); + GL_CALL(glViewport(x, y, w, h)); LV_PROFILER_DRAW_END; } @@ -235,7 +239,7 @@ static void lv_opengles_render_internal(unsigned int texture, const lv_area_t * float hor_translate = (float)intersection.x1 / (float)disp_w * 2.0f - (1.0f - hor_scale); float ver_translate = -((float)intersection.y1 / (float)disp_h * 2.0f - (1.0f - ver_scale)); hor_scale = h_flip ? -hor_scale : hor_scale; - ver_scale = v_flip ? -ver_scale : ver_scale; + ver_scale = v_flip ? ver_scale : -ver_scale; float matrix[9] = { hor_scale, 0.0f, hor_translate, 0.0f, ver_scale, ver_translate, @@ -243,18 +247,14 @@ static void lv_opengles_render_internal(unsigned int texture, const lv_area_t * }; if(texture != 0) { - float x_coef = 1.0f / (float)(2 * lv_area_get_width(texture_area)); - float y_coef = 1.0f / (float)(2 * lv_area_get_height(texture_area)); - float ix_co = 1.0f - x_coef; - float iy_co = 1.0f - y_coef; - float clip_x1 = h_flip ? lv_opengles_map_float(texture_clip_area->x2, texture_area->x2, texture_area->x1, x_coef, ix_co) - : lv_opengles_map_float(texture_clip_area->x1, texture_area->x1, texture_area->x2, x_coef, ix_co); - float clip_x2 = h_flip ? lv_opengles_map_float(texture_clip_area->x1, texture_area->x2, texture_area->x1, x_coef, ix_co) - : lv_opengles_map_float(texture_clip_area->x2, texture_area->x1, texture_area->x2, x_coef, ix_co); - float clip_y1 = v_flip ? lv_opengles_map_float(texture_clip_area->y1, texture_area->y1, texture_area->y2, y_coef, iy_co) - : lv_opengles_map_float(texture_clip_area->y2, texture_area->y2, texture_area->y1, y_coef, iy_co); - float clip_y2 = v_flip ? lv_opengles_map_float(texture_clip_area->y2, texture_area->y1, texture_area->y2, y_coef, iy_co) - : lv_opengles_map_float(texture_clip_area->y1, texture_area->y2, texture_area->y1, y_coef, iy_co); + float clip_x1 = h_flip ? lv_opengles_map_float(texture_clip_area->x2, texture_area->x2, texture_area->x1, 0.f, 1.f) + : lv_opengles_map_float(texture_clip_area->x1, texture_area->x1, texture_area->x2, 0.f, 1.f); + float clip_x2 = h_flip ? lv_opengles_map_float(texture_clip_area->x1, texture_area->x2, texture_area->x1, 0.f, 1.f) + : lv_opengles_map_float(texture_clip_area->x2, texture_area->x1, texture_area->x2, 0.f, 1.f); + float clip_y1 = v_flip ? lv_opengles_map_float(texture_clip_area->y2, texture_area->y2, texture_area->y1, 0.f, 1.f) + : lv_opengles_map_float(texture_clip_area->y1, texture_area->y1, texture_area->y2, 0.f, 1.f); + float clip_y2 = v_flip ? lv_opengles_map_float(texture_clip_area->y1, texture_area->y2, texture_area->y1, 0.f, 1.f) + : lv_opengles_map_float(texture_clip_area->y2, texture_area->y1, texture_area->y2, 0.f, 1.f); float positions[LV_OPENGLES_VERTEX_BUFFER_LEN] = { -1.f, 1.0f, clip_x1, clip_y2, @@ -266,7 +266,7 @@ static void lv_opengles_render_internal(unsigned int texture, const lv_area_t * } lv_opengles_shader_bind(); - lv_opengles_shader_set_uniform1i("u_ColorDepth", LV_COLOR_DEPTH); + lv_opengles_shader_set_uniform1f("u_ColorDepth", LV_COLOR_DEPTH); lv_opengles_shader_set_uniform1i("u_Texture", 0); lv_opengles_shader_set_uniformmatrix3fv("u_VertexTransform", 1, true, matrix); lv_opengles_shader_set_uniform1f("u_Opa", (float)opa / (float)LV_OPA_100); @@ -279,8 +279,8 @@ static void lv_opengles_render_internal(unsigned int texture, const lv_area_t * static void lv_opengles_enable_blending(void) { - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + GL_CALL(glEnable(GL_BLEND)); + GL_CALL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); } static void lv_opengles_vertex_buffer_init(const void * data, unsigned int size) diff --git a/src/drivers/glfw/lv_opengles_driver.h b/src/drivers/opengles/lv_opengles_driver.h similarity index 92% rename from src/drivers/glfw/lv_opengles_driver.h rename to src/drivers/opengles/lv_opengles_driver.h index 1340797681..8769ccda64 100644 --- a/src/drivers/glfw/lv_opengles_driver.h +++ b/src/drivers/opengles/lv_opengles_driver.h @@ -34,13 +34,13 @@ extern "C" { /** * Initialize OpenGL - * @note it is not necessary to call this if you use `lv_glfw_window_create` + * @note it is not necessary to call this if you use `lv_opengles_glfw_window_create` */ void lv_opengles_init(void); /** * Deinitialize OpenGL - * @note it is not necessary to call this if you use `lv_glfw_window_create` + * @note it is not necessary to call this if you use `lv_opengles_glfw_window_create` */ void lv_opengles_deinit(void); diff --git a/src/drivers/opengles/lv_opengles_egl.c b/src/drivers/opengles/lv_opengles_egl.c new file mode 100644 index 0000000000..bef392d452 --- /dev/null +++ b/src/drivers/opengles/lv_opengles_egl.c @@ -0,0 +1,557 @@ +/** + * @file lv_opengles_egl.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_opengles_egl.h" +#if LV_USE_EGL + +#include "lv_opengles_window.h" +#include "lv_opengles_driver.h" +#include "lv_opengles_texture.h" +#include "lv_opengles_private.h" +#include "lv_opengles_debug.h" + +#include "../../core/lv_refr_private.h" +#include "../../stdlib/lv_string.h" +#include "../../core/lv_global.h" +#include "../../display/lv_display_private.h" +#include "../../indev/lv_indev.h" +#include "../../lv_init.h" +#include "../../misc/lv_area_private.h" + +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +struct _lv_opengles_window_t { + EGLSurface surface; + int32_t hor_res; + int32_t ver_res; + lv_ll_t textures; + lv_opengles_egl_window_cb_t pre; + lv_opengles_egl_window_cb_t post1; + lv_opengles_egl_window_cb_t post2; + void * user_data; +#if LV_USE_DRAW_OPENGLES + uint8_t direct_render_invalidated: 1; +#endif +}; + +struct _lv_opengles_window_texture_t { + lv_opengles_window_t * window; + unsigned int texture_id; /* 0 if it's a window display */ + lv_display_t * disp; /* non-NULL if it's a display texture or a window display */ + uint8_t * fb; /* non-NULL if it's a window display and !DRAW_OPENGLES */ + lv_area_t area; + lv_opa_t opa; +}; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static lv_result_t lv_egl_init(void); +static void lv_egl_timer_init(void); +static void window_update_handler(lv_timer_t * t); +static void window_display_delete_cb(lv_event_t * e); +static void window_display_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map); +#if !LV_USE_DRAW_OPENGLES + static void ensure_init_window_display_texture(void); +#endif + +/********************** + * STATIC VARIABLES + **********************/ + +static bool egl_inited; +static lv_timer_t * update_handler_timer; +static lv_ll_t egl_window_ll; +static EGLDisplay egl_display; +static EGLContext egl_config; +static EGLContext egl_context; +static void * backend_device; +#if !LV_USE_DRAW_OPENGLES + static unsigned int window_display_texture; +#endif + +static EGLint const attribute_list[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_NONE +}; + +static EGLint const context_attribute_list[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_opengles_window_t * lv_opengles_egl_window_create(int32_t hor_res, int32_t ver_res, void * native_window_handle, + void * device, + lv_opengles_egl_window_cb_t pre, + lv_opengles_egl_window_cb_t post1, + lv_opengles_egl_window_cb_t post2) +{ + backend_device = device; + if(lv_egl_init() == LV_RESULT_INVALID) { + return NULL; + } + + lv_opengles_window_t * window = lv_ll_ins_tail(&egl_window_ll); + LV_ASSERT_MALLOC(window); + if(window == NULL) return NULL; + lv_memzero(window, sizeof(*window)); + + /* Create window with graphics context */ + window->surface = eglCreateWindowSurface(egl_display, egl_config, (EGLNativeWindowType)(uintptr_t)native_window_handle, + NULL); + if(window->surface == EGL_NO_SURFACE) { + LV_LOG_ERROR("eglCreateWindowSurface failed."); + lv_ll_remove(&egl_window_ll, window); + lv_free(window); + return NULL; + } + + window->hor_res = hor_res; + window->ver_res = ver_res; + lv_ll_init(&window->textures, sizeof(lv_opengles_window_texture_t)); + + window->pre = pre; + window->post1 = post1; + window->post2 = post2; + +#if LV_USE_DRAW_OPENGLES + window->direct_render_invalidated = 1; +#endif + + lv_egl_timer_init(); + + EGLBoolean res = eglMakeCurrent(egl_display, window->surface, window->surface, egl_context); + if(res == EGL_FALSE) { + LV_LOG_ERROR("eglMakeCurrent failed."); + return NULL; + } + + res = eglSwapInterval(egl_display, 0); + if(res == EGL_FALSE) { + LV_LOG_ERROR("eglSwapInterval failed."); + return NULL; + } + + lv_opengles_init(); + + return window; +} + +void * lv_opengles_egl_window_get_surface(lv_opengles_window_t * window) +{ + return (void *)(uintptr_t)window->surface; +} + +void * lv_opengles_egl_window_get_display(lv_opengles_window_t * window) +{ + LV_UNUSED(window); + return (void *)(uintptr_t)egl_display; +} + +void lv_opengles_egl_window_set_user_data(lv_opengles_window_t * window, void * user_data) +{ + window->user_data = user_data; +} + +void * lv_opengles_egl_window_get_user_data(lv_opengles_window_t * window) +{ + return window->user_data; +} + +void lv_opengles_window_delete(lv_opengles_window_t * window) +{ + EGLBoolean res = eglDestroySurface(egl_display, window->surface); + if(res == EGL_FALSE) { + LV_LOG_ERROR("eglDestroySurface failed."); + return; + } + + lv_opengles_window_texture_t * texture; + while((texture = lv_ll_get_head(&window->textures))) { + if(texture->texture_id) { + lv_opengles_window_texture_remove(texture); + } + else { + lv_display_delete(texture->disp); + } + } + + lv_ll_remove(&egl_window_ll, window); + lv_free(window); + +#if !LV_USE_DRAW_OPENGLES + if(lv_ll_is_empty(&egl_window_ll)) { + GL_CALL(glDeleteTextures(1, &window_display_texture)); + window_display_texture = 0; + } +#endif +} + +lv_opengles_window_texture_t * lv_opengles_window_add_texture(lv_opengles_window_t * window, unsigned int texture_id, + int32_t w, int32_t h) +{ + lv_opengles_window_texture_t * texture = lv_ll_ins_tail(&window->textures); + LV_ASSERT_MALLOC(texture); + if(texture == NULL) return NULL; + lv_memzero(texture, sizeof(*texture)); + texture->window = window; + texture->texture_id = texture_id; + texture->disp = lv_opengles_texture_get_from_texture_id(texture_id); + lv_area_set(&texture->area, 0, 0, w - 1, h - 1); + texture->opa = LV_OPA_COVER; + +#if LV_USE_DRAW_OPENGLES + window->direct_render_invalidated = 1; +#endif + + return texture; +} + +void lv_opengles_window_texture_remove(lv_opengles_window_texture_t * texture) +{ + if(texture->texture_id == 0) { + LV_LOG_WARN("window displays should be deleted with `lv_display_delete`"); + return; + } + +#if LV_USE_DRAW_OPENGLES + texture->window->direct_render_invalidated = 1; +#endif + + lv_ll_remove(&texture->window->textures, texture); + lv_free(texture); +} + +lv_display_t * lv_opengles_window_display_create(lv_opengles_window_t * window, int32_t w, int32_t h) +{ + lv_display_t * disp = lv_display_create(w, h); + if(disp == NULL) { + return NULL; + } + + lv_opengles_window_texture_t * dsc = lv_ll_ins_tail(&window->textures); + LV_ASSERT_MALLOC(dsc); + if(dsc == NULL) { + lv_display_delete(disp); + return NULL; + } + lv_memzero(dsc, sizeof(*dsc)); + dsc->window = window; + dsc->disp = disp; + lv_area_set(&dsc->area, 0, 0, w - 1, h - 1); + dsc->opa = LV_OPA_COVER; + +#if LV_USE_DRAW_OPENGLES + static size_t LV_ATTRIBUTE_MEM_ALIGN dummy_buf; + lv_display_set_buffers(disp, &dummy_buf, NULL, h * lv_draw_buf_width_to_stride(w, LV_COLOR_FORMAT_ARGB8888), + LV_DISPLAY_RENDER_MODE_FULL); +#else + uint32_t stride = lv_draw_buf_width_to_stride(w, lv_display_get_color_format(disp)); + uint32_t buf_size = stride * h; + dsc->fb = malloc(buf_size); + LV_ASSERT_MALLOC(dsc->fb); + if(dsc->fb == NULL) { + lv_display_delete(disp); + lv_ll_remove(&window->textures, dsc); + lv_free(dsc); + return NULL; + } + lv_display_set_buffers(disp, dsc->fb, NULL, buf_size, LV_DISPLAY_RENDER_MODE_DIRECT); +#endif + + lv_display_set_driver_data(disp, dsc); + lv_display_delete_refr_timer(disp); + lv_display_set_flush_cb(disp, window_display_flush_cb); + lv_display_add_event_cb(disp, window_display_delete_cb, LV_EVENT_DELETE, disp); + +#if LV_USE_DRAW_OPENGLES + window->direct_render_invalidated = 1; +#endif + + return disp; +} + +lv_opengles_window_texture_t * lv_opengles_window_display_get_window_texture(lv_display_t * window_display) +{ + return lv_display_get_driver_data(window_display); +} + +void lv_opengles_window_texture_set_x(lv_opengles_window_texture_t * texture, int32_t x) +{ + lv_area_set_pos(&texture->area, x, texture->area.y1); + +#if LV_USE_DRAW_OPENGLES + texture->window->direct_render_invalidated = 1; +#endif +} + +void lv_opengles_window_texture_set_y(lv_opengles_window_texture_t * texture, int32_t y) +{ + lv_area_set_pos(&texture->area, texture->area.x1, y); + +#if LV_USE_DRAW_OPENGLES + texture->window->direct_render_invalidated = 1; +#endif +} + +void lv_opengles_window_texture_set_opa(lv_opengles_window_texture_t * texture, lv_opa_t opa) +{ + texture->opa = opa; + +#if LV_USE_DRAW_OPENGLES + texture->window->direct_render_invalidated = 1; +#endif +} + +lv_indev_t * lv_opengles_window_texture_get_mouse_indev(lv_opengles_window_texture_t * texture) +{ + LV_UNUSED(texture); + LV_LOG_WARN("EGL does not create indevs. Returning NULL."); + return NULL; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static lv_result_t lv_egl_init(void) +{ + if(egl_inited) { + return LV_RESULT_OK; + } + + /* get an EGL display connection */ + if(backend_device) { + egl_display = eglGetDisplay(backend_device); + } + else { + egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + } + + if(egl_display == EGL_NO_DISPLAY) { + LV_LOG_ERROR("eglGetDisplay failed."); + return LV_RESULT_INVALID; + } + + /* initialize the EGL display connection */ + EGLBoolean res = eglInitialize(egl_display, NULL, NULL); + if(res == EGL_FALSE) { + LV_LOG_ERROR("eglInitialize failed."); + return LV_RESULT_INVALID; + } + + /* get an appropriate EGL frame buffer configuration */ + EGLint num_config; + res = eglChooseConfig(egl_display, attribute_list, &egl_config, 1, &num_config); + if(res == EGL_FALSE) { + LV_LOG_ERROR("eglChooseConfig failed."); + eglTerminate(egl_display); + return LV_RESULT_INVALID; + } + + /* create an EGL rendering context */ + egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, context_attribute_list); + if(egl_context == EGL_NO_CONTEXT) { + LV_LOG_ERROR("eglCreateContext failed."); + eglTerminate(egl_display); + return LV_RESULT_INVALID; + } + + lv_ll_init(&egl_window_ll, sizeof(lv_opengles_window_t)); + + egl_inited = true; + return LV_RESULT_OK; +} + +static void lv_egl_timer_init(void) +{ + if(update_handler_timer == NULL) { + update_handler_timer = lv_timer_create(window_update_handler, LV_DEF_REFR_PERIOD, NULL); + } +} + +static void window_update_handler(lv_timer_t * t) +{ + LV_UNUSED(t); + lv_opengles_window_t * window; + + /* render each window */ + LV_LL_READ(&egl_window_ll, window) { + if(window->pre) window->pre(window); + + EGLBoolean res = eglMakeCurrent(egl_display, window->surface, window->surface, egl_context); + if(res == EGL_FALSE) { + LV_LOG_ERROR("eglMakeCurrent failed."); + return; + } + + lv_opengles_viewport(0, 0, window->hor_res, window->ver_res); + +#if LV_USE_DRAW_OPENGLES + lv_opengles_window_texture_t * textures_head; + bool window_display_direct_render = + !window->direct_render_invalidated + && (textures_head = lv_ll_get_head(&window->textures)) + && textures_head->texture_id == 0 /* it's a window display */ + && lv_ll_get_next(&window->textures, textures_head) == NULL /* it's the only one */ + && textures_head->opa == LV_OPA_COVER + && textures_head->area.x1 == 0 + && textures_head->area.y1 == 0 + && textures_head->area.x2 == window->hor_res - 1 + && textures_head->area.y2 == window->ver_res - 1 + ; + window->direct_render_invalidated = 0; + if(!window_display_direct_render) { + lv_opengles_render_clear(); + } +#else + lv_opengles_render_clear(); +#endif + + /* render each texture in the window */ + lv_opengles_window_texture_t * texture; + LV_LL_READ(&window->textures, texture) { + if(texture->texture_id == 0) { /* it's a window display */ +#if LV_USE_DRAW_OPENGLES + lv_display_set_render_mode(texture->disp, + window_display_direct_render ? LV_DISPLAY_RENDER_MODE_DIRECT : LV_DISPLAY_RENDER_MODE_FULL); +#endif + + lv_display_t * default_save = lv_display_get_default(); + lv_display_set_default(texture->disp); + lv_display_refr_timer(NULL); + lv_display_set_default(default_save); + +#if !LV_USE_DRAW_OPENGLES + ensure_init_window_display_texture(); + + GL_CALL(glBindTexture(GL_TEXTURE_2D, window_display_texture)); + + /* set the dimensions and format to complete the texture */ + /* Color depth: 8 (L8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888) */ +#if LV_COLOR_DEPTH == 8 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, lv_area_get_width(&texture->area), lv_area_get_height(&texture->area), 0, + GL_RED, GL_UNSIGNED_BYTE, texture->fb)); +#elif LV_COLOR_DEPTH == 16 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, lv_area_get_width(&texture->area), lv_area_get_height(&texture->area), + 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + texture->fb)); +#elif LV_COLOR_DEPTH == 24 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, lv_area_get_width(&texture->area), lv_area_get_height(&texture->area), 0, + GL_BGR, GL_UNSIGNED_BYTE, texture->fb)); +#elif LV_COLOR_DEPTH == 32 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lv_area_get_width(&texture->area), lv_area_get_height(&texture->area), + 0, GL_BGRA, GL_UNSIGNED_BYTE, texture->fb)); +#else +#error("Unsupported color format") +#endif + + GL_CALL(glGenerateMipmap(GL_TEXTURE_2D)); + + GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); + + lv_opengles_render_texture(window_display_texture, &texture->area, texture->opa, window->hor_res, window->ver_res, + &texture->area, false, false); +#endif + } + else { + /* if the added texture is an LVGL opengles texture display, refresh it before rendering it */ + if(texture->disp != NULL) { +#if LV_USE_DRAW_OPENGLES + lv_display_t * default_save = lv_display_get_default(); + lv_display_set_default(texture->disp); + lv_display_refr_timer(NULL); + lv_display_set_default(default_save); +#else + lv_refr_now(texture->disp); +#endif + } + +#if LV_USE_DRAW_OPENGLES + lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res, + &texture->area, false, texture->disp != NULL); +#else + lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res, + &texture->area, false, false); +#endif + } + } + + if(window->post1) window->post1(window); + + /* Swap front and back buffers */ + res = eglSwapBuffers(egl_display, window->surface); + if(res == EGL_FALSE) { + LV_LOG_ERROR("eglSwapBuffers failed."); + return; + } + + if(window->post2) window->post2(window); + } +} + +static void window_display_delete_cb(lv_event_t * e) +{ + lv_display_t * disp = lv_event_get_target(e); + lv_opengles_window_texture_t * dsc = lv_display_get_driver_data(disp); + free(dsc->fb); + lv_opengles_window_texture_remove(dsc); +} + +static void window_display_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map) +{ + LV_UNUSED(area); + LV_UNUSED(px_map); + lv_display_flush_ready(disp); +} + +#if !LV_USE_DRAW_OPENGLES +static void ensure_init_window_display_texture(void) +{ + if(window_display_texture) { + return; + } + + GL_CALL(glGenTextures(1, &window_display_texture)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, window_display_texture)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 20)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + + GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); +} +#endif + +#endif /*LV_USE_EGL*/ diff --git a/src/drivers/opengles/lv_opengles_egl.h b/src/drivers/opengles/lv_opengles_egl.h new file mode 100644 index 0000000000..5d4055ff86 --- /dev/null +++ b/src/drivers/opengles/lv_opengles_egl.h @@ -0,0 +1,70 @@ +/** + * @file lv_opengles_egl.h + * + */ + +#ifndef LV_OPENGLES_EGL_H +#define LV_OPENGLES_EGL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../../lv_conf_internal.h" + + +#if LV_USE_EGL + +#include "../../misc/lv_types.h" + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef void (*lv_opengles_egl_window_cb_t)(lv_opengles_window_t * window); + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create an EGL OpenGL window with no textures. OpenGL should be initialized beforehand. + * @param hor_res width in pixels of the window + * @param ver_res height in pixels of the window + * @param native_window_handle a handle to a native window for the platform + * @param device a `EGLNativeDisplayType` + * @param pre called before rendering + * @param post1 called after rendering, before swapping + * @param post2 called after swapping + * @return the new EGL OpenGL window handle + */ +lv_opengles_window_t * lv_opengles_egl_window_create(int32_t hor_res, int32_t ver_res, void * native_window_handle, + void * device, + lv_opengles_egl_window_cb_t pre, + lv_opengles_egl_window_cb_t post1, + lv_opengles_egl_window_cb_t post2); + +void * lv_opengles_egl_window_get_surface(lv_opengles_window_t * window); +void * lv_opengles_egl_window_get_display(lv_opengles_window_t * window); +void lv_opengles_egl_window_set_user_data(lv_opengles_window_t * window, void * user_data); +void * lv_opengles_egl_window_get_user_data(lv_opengles_window_t * window); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_EGL*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_OPENGLES_EGL_H*/ diff --git a/src/drivers/opengles/lv_opengles_glfw.c b/src/drivers/opengles/lv_opengles_glfw.c new file mode 100644 index 0000000000..4323cfbc57 --- /dev/null +++ b/src/drivers/opengles/lv_opengles_glfw.c @@ -0,0 +1,696 @@ +/** + * @file lv_opengles_glfw.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_opengles_glfw.h" +#if LV_USE_GLFW + +#include "lv_opengles_window.h" +#include "lv_opengles_driver.h" +#include "lv_opengles_texture.h" +#include "lv_opengles_private.h" +#include "lv_opengles_debug.h" + +#include "../../core/lv_refr.h" +#include "../../stdlib/lv_sprintf.h" +#include "../../stdlib/lv_string.h" +#include "../../core/lv_global.h" +#include "../../display/lv_display_private.h" +#include "../../indev/lv_indev.h" +#include "../../lv_init.h" +#include "../../misc/lv_area_private.h" + +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +struct _lv_opengles_window_t { + GLFWwindow * window; + int32_t hor_res; + int32_t ver_res; + bool h_flip; + bool v_flip; + lv_ll_t textures; + lv_point_t mouse_last_point; + lv_indev_state_t mouse_last_state; + uint8_t use_indev : 1; + uint8_t closing : 1; +#if LV_USE_DRAW_OPENGLES + uint8_t direct_render_invalidated: 1; +#endif +}; + +struct _lv_opengles_window_texture_t { + lv_opengles_window_t * window; + unsigned int texture_id; /* 0 if it's a window display */ + lv_display_t * disp; /* non-NULL if it's a display texture or a window display */ + uint8_t * fb; /* non-NULL if it's a window display and !DRAW_OPENGLES */ + lv_area_t area; + lv_opa_t opa; + lv_indev_t * indev; + lv_point_t indev_last_point; + lv_indev_state_t indev_last_state; +}; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void window_update_handler(lv_timer_t * t); +static uint32_t lv_glfw_tick_count_callback(void); +static lv_opengles_window_t * lv_glfw_get_lv_window_from_window(GLFWwindow * window); +static void glfw_error_cb(int error, const char * description); +static int lv_glfw_init(void); +static int lv_glew_init(void); +static void lv_glfw_timer_init(void); +static void lv_glfw_window_config(GLFWwindow * window, bool use_mouse_indev); +static void lv_glfw_window_quit(void); +static void window_close_callback(GLFWwindow * window); +static void key_callback(GLFWwindow * window, int key, int scancode, int action, int mods); +static void mouse_button_callback(GLFWwindow * window, int button, int action, int mods); +static void mouse_move_callback(GLFWwindow * window, double xpos, double ypos); +static void proc_mouse(lv_opengles_window_t * window); +static void indev_read_cb(lv_indev_t * indev, lv_indev_data_t * data); +static void framebuffer_size_callback(GLFWwindow * window, int width, int height); +static void window_display_delete_cb(lv_event_t * e); +static void window_display_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map); +#if !LV_USE_DRAW_OPENGLES + static void ensure_init_window_display_texture(void); +#endif + +/********************** + * STATIC VARIABLES + **********************/ +static bool glfw_inited; +static bool glew_inited; +static lv_timer_t * update_handler_timer; +static lv_ll_t glfw_window_ll; +#if !LV_USE_DRAW_OPENGLES + static unsigned int window_display_texture; +#endif + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_opengles_window_t * lv_opengles_glfw_window_create_ex(int32_t hor_res, int32_t ver_res, bool use_mouse_indev, + bool h_flip, bool v_flip, const char * title) +{ + LV_ASSERT_NULL(title); + if(lv_glfw_init() != 0) { + LV_LOG_ERROR("Failed to init glfw"); + return NULL; + } + + lv_opengles_window_t * window = lv_ll_ins_tail(&glfw_window_ll); + LV_ASSERT_MALLOC(window); + if(window == NULL) return NULL; + if(window == NULL) { + LV_LOG_ERROR("Failed to create glfw window"); + return NULL; + } + lv_memzero(window, sizeof(*window)); + + /* Create window with graphics context */ + lv_opengles_window_t * existing_window = lv_ll_get_head(&glfw_window_ll); + window->window = glfwCreateWindow(hor_res, ver_res, title, NULL, + existing_window ? existing_window->window : NULL); + if(window->window == NULL) { + LV_LOG_ERROR("glfwCreateWindow fail"); + lv_ll_remove(&glfw_window_ll, window); + lv_free(window); + return NULL; + } + + glfwSetWindowTitle(window->window, title); + + window->h_flip = h_flip; + window->v_flip = v_flip; + window->hor_res = hor_res; + window->ver_res = ver_res; + + lv_ll_init(&window->textures, sizeof(lv_opengles_window_texture_t)); + window->use_indev = use_mouse_indev; +#if LV_USE_DRAW_OPENGLES + window->direct_render_invalidated = 1; +#endif + + glfwSetWindowUserPointer(window->window, window); + lv_glfw_timer_init(); + lv_glfw_window_config(window->window, use_mouse_indev); + lv_glew_init(); + glfwMakeContextCurrent(window->window); + lv_opengles_init(); + + return window; +} + +lv_opengles_window_t * lv_opengles_glfw_window_create(int32_t hor_res, int32_t ver_res, bool use_mouse_indev) +{ + return lv_opengles_glfw_window_create_ex(hor_res, ver_res, use_mouse_indev, false, false, "LVGL Simulator"); +} + +void lv_opengles_glfw_window_set_title(lv_opengles_window_t * window, const char * new_title) +{ + glfwSetWindowTitle(window->window, new_title); +} + +void lv_opengles_window_delete(lv_opengles_window_t * window) +{ + glfwDestroyWindow(window->window); + + lv_opengles_window_texture_t * texture; + while((texture = lv_ll_get_head(&window->textures))) { + if(texture->texture_id) { + lv_opengles_window_texture_remove(texture); + } + else { + lv_display_delete(texture->disp); + } + } + + lv_ll_remove(&glfw_window_ll, window); + lv_free(window); + + if(lv_ll_is_empty(&glfw_window_ll)) { + lv_glfw_window_quit(); +#if !LV_USE_DRAW_OPENGLES + if(window_display_texture) { + GL_CALL(glDeleteTextures(1, &window_display_texture)); + window_display_texture = 0; + } +#endif + } +} + +void * lv_opengles_glfw_window_get_glfw_window(lv_opengles_window_t * window) +{ + return (void *)(window->window); +} + +void lv_opengles_glfw_window_set_flip(lv_opengles_window_t * window, bool h_flip, bool v_flip) +{ + window->h_flip = h_flip; + window->v_flip = v_flip; +} + +lv_opengles_window_texture_t * lv_opengles_window_add_texture(lv_opengles_window_t * window, unsigned int texture_id, + int32_t w, int32_t h) +{ + lv_opengles_window_texture_t * texture = lv_ll_ins_tail(&window->textures); + LV_ASSERT_MALLOC(texture); + if(texture == NULL) return NULL; + lv_memzero(texture, sizeof(*texture)); + texture->window = window; + texture->texture_id = texture_id; + texture->disp = lv_opengles_texture_get_from_texture_id(texture_id); + lv_area_set(&texture->area, 0, 0, w - 1, h - 1); + texture->opa = LV_OPA_COVER; + + if(window->use_indev && texture->disp) { + lv_indev_t * indev = lv_indev_create(); + if(indev == NULL) { + lv_ll_remove(&window->textures, texture); + lv_free(texture); + return NULL; + } + texture->indev = indev; + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_read_cb(indev, indev_read_cb); + lv_indev_set_driver_data(indev, texture); + lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT); + lv_indev_set_display(indev, texture->disp); + } + +#if LV_USE_DRAW_OPENGLES + window->direct_render_invalidated = 1; +#endif + + return texture; +} + +void lv_opengles_window_texture_remove(lv_opengles_window_texture_t * texture) +{ + if(texture->texture_id == 0) { + LV_LOG_WARN("window displays should be deleted with `lv_display_delete`"); + return; + } + if(texture->indev != NULL) { + lv_indev_delete(texture->indev); + } + +#if LV_USE_DRAW_OPENGLES + texture->window->direct_render_invalidated = 1; +#endif + + lv_ll_remove(&texture->window->textures, texture); + lv_free(texture); +} + +lv_display_t * lv_opengles_window_display_create(lv_opengles_window_t * window, int32_t w, int32_t h) +{ + lv_display_t * disp = lv_display_create(w, h); + if(disp == NULL) { + return NULL; + } + + lv_opengles_window_texture_t * dsc = lv_ll_ins_tail(&window->textures); + LV_ASSERT_MALLOC(dsc); + if(dsc == NULL) { + lv_display_delete(disp); + return NULL; + } + lv_memzero(dsc, sizeof(*dsc)); + dsc->window = window; + dsc->disp = disp; + lv_area_set(&dsc->area, 0, 0, w - 1, h - 1); + dsc->opa = LV_OPA_COVER; + +#if LV_USE_DRAW_OPENGLES + static size_t LV_ATTRIBUTE_MEM_ALIGN dummy_buf; + lv_display_set_buffers(disp, &dummy_buf, NULL, h * lv_draw_buf_width_to_stride(w, LV_COLOR_FORMAT_ARGB8888), + LV_DISPLAY_RENDER_MODE_FULL); +#else + uint32_t stride = lv_draw_buf_width_to_stride(w, lv_display_get_color_format(disp)); + uint32_t buf_size = stride * h; + dsc->fb = malloc(buf_size); + LV_ASSERT_MALLOC(dsc->fb); + if(dsc->fb == NULL) { + lv_display_delete(disp); + lv_ll_remove(&window->textures, dsc); + lv_free(dsc); + return NULL; + } + lv_display_set_buffers(disp, dsc->fb, NULL, buf_size, LV_DISPLAY_RENDER_MODE_DIRECT); +#endif + + if(window->use_indev) { + lv_indev_t * indev = lv_indev_create(); + if(indev == NULL) { + lv_display_delete(disp); + lv_ll_remove(&window->textures, dsc); + lv_free(dsc); + return NULL; + } + dsc->indev = indev; + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_read_cb(indev, indev_read_cb); + lv_indev_set_driver_data(indev, dsc); + lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT); + lv_indev_set_display(indev, disp); + } + + lv_display_set_driver_data(disp, dsc); + lv_display_delete_refr_timer(disp); + lv_display_set_flush_cb(disp, window_display_flush_cb); + lv_display_add_event_cb(disp, window_display_delete_cb, LV_EVENT_DELETE, disp); + +#if LV_USE_DRAW_OPENGLES + window->direct_render_invalidated = 1; +#endif + + return disp; +} + +lv_opengles_window_texture_t * lv_opengles_window_display_get_window_texture(lv_display_t * window_display) +{ + return lv_display_get_driver_data(window_display); +} + +void lv_opengles_window_texture_set_x(lv_opengles_window_texture_t * texture, int32_t x) +{ + lv_area_set_pos(&texture->area, x, texture->area.y1); + +#if LV_USE_DRAW_OPENGLES + texture->window->direct_render_invalidated = 1; +#endif +} + +void lv_opengles_window_texture_set_y(lv_opengles_window_texture_t * texture, int32_t y) +{ + lv_area_set_pos(&texture->area, texture->area.x1, y); + +#if LV_USE_DRAW_OPENGLES + texture->window->direct_render_invalidated = 1; +#endif +} + +void lv_opengles_window_texture_set_opa(lv_opengles_window_texture_t * texture, lv_opa_t opa) +{ + texture->opa = opa; + +#if LV_USE_DRAW_OPENGLES + texture->window->direct_render_invalidated = 1; +#endif +} + +lv_indev_t * lv_opengles_window_texture_get_mouse_indev(lv_opengles_window_texture_t * texture) +{ + return texture->indev; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static int lv_glfw_init(void) +{ + if(glfw_inited) { + return 0; + } + + glfwSetErrorCallback(glfw_error_cb); + + int ret = glfwInit(); + if(ret == 0) { + LV_LOG_ERROR("glfwInit fail."); + return 1; + } + + lv_ll_init(&glfw_window_ll, sizeof(lv_opengles_window_t)); + + glfw_inited = true; + return 0; +} + +static int lv_glew_init(void) +{ + if(glew_inited) { + return 0; + } + + GLenum ret = glewInit(); + if(ret != GLEW_OK) { + LV_LOG_ERROR("glewInit fail: %d.", ret); + return ret; + } + + LV_LOG_INFO("GL version: %s", glGetString(GL_VERSION)); + LV_LOG_INFO("GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + + glew_inited = true; + + return 0; +} + +static void lv_glfw_timer_init(void) +{ + if(update_handler_timer == NULL) { + update_handler_timer = lv_timer_create(window_update_handler, LV_DEF_REFR_PERIOD, NULL); + lv_tick_set_cb(lv_glfw_tick_count_callback); + } +} + +static void lv_glfw_window_config(GLFWwindow * window, bool use_mouse_indev) +{ + glfwMakeContextCurrent(window); + + glfwSwapInterval(1); + + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + if(use_mouse_indev) { + glfwSetMouseButtonCallback(window, mouse_button_callback); + glfwSetCursorPosCallback(window, mouse_move_callback); + } + + glfwSetKeyCallback(window, key_callback); + + glfwSetWindowCloseCallback(window, window_close_callback); +} + +static void lv_glfw_window_quit(void) +{ + lv_timer_delete(update_handler_timer); + update_handler_timer = NULL; + + glfwTerminate(); + glfw_inited = false; + + lv_deinit(); + + exit(0); +} + +static void window_update_handler(lv_timer_t * t) +{ + LV_UNUSED(t); + + lv_opengles_window_t * window; + + glfwPollEvents(); + + /* delete windows that are ready to close */ + window = lv_ll_get_head(&glfw_window_ll); + while(window) { + lv_opengles_window_t * window_to_delete = window->closing ? window : NULL; + window = lv_ll_get_next(&glfw_window_ll, window); + if(window_to_delete) { + glfwSetWindowShouldClose(window_to_delete->window, GLFW_TRUE); + lv_opengles_window_delete(window_to_delete); + } + } + + /* render each window */ + LV_LL_READ(&glfw_window_ll, window) { + glfwMakeContextCurrent(window->window); + lv_opengles_viewport(0, 0, window->hor_res, window->ver_res); + +#if LV_USE_DRAW_OPENGLES + lv_opengles_window_texture_t * textures_head; + bool window_display_direct_render = + !window->direct_render_invalidated + && (textures_head = lv_ll_get_head(&window->textures)) + && textures_head->texture_id == 0 /* it's a window display */ + && lv_ll_get_next(&window->textures, textures_head) == NULL /* it's the only one */ + && textures_head->opa == LV_OPA_COVER + && textures_head->area.x1 == 0 + && textures_head->area.y1 == 0 + && textures_head->area.x2 == window->hor_res - 1 + && textures_head->area.y2 == window->ver_res - 1 + ; + window->direct_render_invalidated = 0; + if(!window_display_direct_render) { + lv_opengles_render_clear(); + } +#else + lv_opengles_render_clear(); +#endif + + /* render each texture in the window */ + lv_opengles_window_texture_t * texture; + LV_LL_READ(&window->textures, texture) { + if(texture->texture_id == 0) { /* it's a window display */ +#if LV_USE_DRAW_OPENGLES + lv_display_set_render_mode(texture->disp, + window_display_direct_render ? LV_DISPLAY_RENDER_MODE_DIRECT : LV_DISPLAY_RENDER_MODE_FULL); +#endif + + lv_display_t * default_save = lv_display_get_default(); + lv_display_set_default(texture->disp); + lv_display_refr_timer(NULL); + lv_display_set_default(default_save); + +#if !LV_USE_DRAW_OPENGLES + ensure_init_window_display_texture(); + + GL_CALL(glBindTexture(GL_TEXTURE_2D, window_display_texture)); + + /* set the dimensions and format to complete the texture */ + /* Color depth: 8 (L8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888) */ +#if LV_COLOR_DEPTH == 8 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, lv_area_get_width(&texture->area), lv_area_get_height(&texture->area), 0, + GL_RED, GL_UNSIGNED_BYTE, texture->fb)); +#elif LV_COLOR_DEPTH == 16 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, lv_area_get_width(&texture->area), lv_area_get_height(&texture->area), + 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + texture->fb)); +#elif LV_COLOR_DEPTH == 24 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, lv_area_get_width(&texture->area), lv_area_get_height(&texture->area), 0, + GL_BGR, GL_UNSIGNED_BYTE, texture->fb)); +#elif LV_COLOR_DEPTH == 32 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lv_area_get_width(&texture->area), lv_area_get_height(&texture->area), + 0, GL_BGRA, GL_UNSIGNED_BYTE, texture->fb)); +#else +#error("Unsupported color format") +#endif + + GL_CALL(glGenerateMipmap(GL_TEXTURE_2D)); + + GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); + + lv_opengles_render_texture(window_display_texture, &texture->area, texture->opa, window->hor_res, window->ver_res, + &texture->area, window->h_flip, window->v_flip); +#endif + } + else { + /* if the added texture is an LVGL opengles texture display, refresh it before rendering it */ + if(texture->disp != NULL) { +#if LV_USE_DRAW_OPENGLES + lv_display_t * default_save = lv_display_get_default(); + lv_display_set_default(texture->disp); + lv_display_refr_timer(NULL); + lv_display_set_default(default_save); +#else + lv_refr_now(texture->disp); +#endif + } + +#if LV_USE_DRAW_OPENGLES + lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res, + &texture->area, window->h_flip, texture->disp == NULL ? window->v_flip : !window->v_flip); +#else + lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res, + &texture->area, window->h_flip, window->v_flip); +#endif + } + } + + /* Swap front and back buffers */ + glfwSwapBuffers(window->window); + } +} + +static void glfw_error_cb(int error, const char * description) +{ + LV_LOG_ERROR("GLFW Error %d: %s", error, description); +} + +static lv_opengles_window_t * lv_glfw_get_lv_window_from_window(GLFWwindow * window) +{ + return glfwGetWindowUserPointer(window); +} + +static void window_close_callback(GLFWwindow * window) +{ + lv_opengles_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); + lv_window->closing = 1; +} + +static void key_callback(GLFWwindow * window, int key, int scancode, int action, int mods) +{ + LV_UNUSED(scancode); + LV_UNUSED(mods); + if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { + lv_opengles_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); + lv_window->closing = 1; + } +} + +static void mouse_button_callback(GLFWwindow * window, int button, int action, int mods) +{ + LV_UNUSED(mods); + if(button == GLFW_MOUSE_BUTTON_LEFT) { + lv_opengles_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); + lv_window->mouse_last_state = action == GLFW_PRESS ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; + proc_mouse(lv_window); + } +} + +static void mouse_move_callback(GLFWwindow * window, double xpos, double ypos) +{ + lv_opengles_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); + lv_window->mouse_last_point.x = (int32_t)xpos; + lv_window->mouse_last_point.y = (int32_t)ypos; + proc_mouse(lv_window); +} + +static void proc_mouse(lv_opengles_window_t * window) +{ + /* mouse activity will affect the topmost LVGL display texture */ + lv_opengles_window_texture_t * texture; + LV_LL_READ_BACK(&window->textures, texture) { + if(lv_area_is_point_on(&texture->area, &window->mouse_last_point, 0)) { + /* adjust the mouse pointer coordinates so that they are relative to the texture */ + if(window->h_flip) { + texture->indev_last_point.x = texture->area.x2 - window->mouse_last_point.x; + } + else { + texture->indev_last_point.x = window->mouse_last_point.x - texture->area.x1; + } + if(window->v_flip) { + texture->indev_last_point.y = (texture->area.y2 - window->mouse_last_point.y); + } + else { + texture->indev_last_point.y = (window->mouse_last_point.y - texture->area.y1); + } + texture->indev_last_state = window->mouse_last_state; + lv_indev_read(texture->indev); + break; + } + } +} + +static void indev_read_cb(lv_indev_t * indev, lv_indev_data_t * data) +{ + lv_opengles_window_texture_t * texture = lv_indev_get_driver_data(indev); + data->point = texture->indev_last_point; + data->state = texture->indev_last_state; +} + +static void framebuffer_size_callback(GLFWwindow * window, int width, int height) +{ + lv_opengles_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); + lv_window->hor_res = width; + lv_window->ver_res = height; +} + +static uint32_t lv_glfw_tick_count_callback(void) +{ + double tick = glfwGetTime() * 1000.0; + return (uint32_t)tick; +} + +static void window_display_delete_cb(lv_event_t * e) +{ + lv_display_t * disp = lv_event_get_target(e); + lv_opengles_window_texture_t * dsc = lv_display_get_driver_data(disp); + free(dsc->fb); + lv_opengles_window_texture_remove(dsc); +} + +static void window_display_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map) +{ + LV_UNUSED(area); + LV_UNUSED(px_map); + lv_display_flush_ready(disp); +} + +#if !LV_USE_DRAW_OPENGLES +static void ensure_init_window_display_texture(void) +{ + if(window_display_texture) { + return; + } + + GL_CALL(glGenTextures(1, &window_display_texture)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, window_display_texture)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 20)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + + GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); +} +#endif + +#endif /*LV_USE_GLFW*/ diff --git a/src/drivers/opengles/lv_opengles_glfw.h b/src/drivers/opengles/lv_opengles_glfw.h new file mode 100644 index 0000000000..39d2d51e10 --- /dev/null +++ b/src/drivers/opengles/lv_opengles_glfw.h @@ -0,0 +1,88 @@ +/** + * @file lv_opengles_glfw.h + * + */ + +#ifndef LV_OPENGLES_GLFW_H +#define LV_OPENGLES_GLFW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../../lv_conf_internal.h" +#if LV_USE_GLFW + +#include "../../misc/lv_types.h" + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a GLFW OpenGL window with no textures and initialize OpenGL + * @param hor_res width in pixels of the window + * @param ver_res height in pixels of the window + * @param use_mouse_indev send pointer indev input to LVGL display textures + * @return the new GLFW OpenGL window handle + */ +lv_opengles_window_t * lv_opengles_glfw_window_create(int32_t hor_res, int32_t ver_res, bool use_mouse_indev); + +/** + * Create a GLFW window with no textures and initialize OpenGL + * @param hor_res width in pixels of the window + * @param ver_res height in pixels of the window + * @param use_mouse_indev send pointer indev input to LVGL display textures + * @param h_flip Should the window contents be horizontally mirrored? + * @param v_flip Should the window contents be vertically mirrored? + * @param title The window title + * @return the new GLFW window handle + */ +lv_opengles_window_t * lv_opengles_glfw_window_create_ex(int32_t hor_res, int32_t ver_res, bool use_mouse_indev, + bool h_flip, bool v_flip, const char * title); + +/** + * Set the window's title text + * @param window GLFW window to configure + * @param new_title The new title text + */ +void lv_opengles_glfw_window_set_title(lv_opengles_window_t * window, const char * new_title); + +/** + * Set the horizontal / vertical flipping of a GLFW window + * @param window GLFW window to configure + * @param h_flip Should the window contents be horizontally mirrored? + * @param v_flip Should the window contents be vertically mirrored? + */ +void lv_opengles_glfw_window_set_flip(lv_opengles_window_t * window, bool h_flip, bool v_flip); + +/** + * Get the GLFW window handle for a GLFW lv_opengles_window_t + * @param window GLFW window to return the handle of + * @return the GLFW window handle + */ +void * lv_opengles_glfw_window_get_glfw_window(lv_opengles_window_t * window); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GLFW*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_OPENGLES_GLFW_H*/ diff --git a/src/drivers/opengles/lv_opengles_private.h b/src/drivers/opengles/lv_opengles_private.h new file mode 100644 index 0000000000..ae5c291189 --- /dev/null +++ b/src/drivers/opengles/lv_opengles_private.h @@ -0,0 +1,64 @@ +/** + * @file lv_opengles_private.h + * + */ + +#ifndef LV_OPENGLES_PRIVATE_H +#define LV_OPENGLES_PRIVATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" +#if LV_USE_OPENGLES + +#if LV_USE_EGL +#include +#include +#include +#include +#else +/* For now, by default we add glew and glfw. + In the future we need to consider adding a config for setting these includes*/ +#include +#include +#endif /*LV_USE_EGL*/ + +/********************* + * DEFINES + *********************/ + +/* In desktop GL () these symbols are defined but for EGL + * they are defined as extensions with the _EXT suffix */ +#ifndef GL_BGRA +#define GL_BGRA GL_BGRA_EXT +#endif /*GL_BGRA*/ + +#ifndef GL_BGR +#define GL_BGR GL_BGR_EXT +#endif /*GL_BGR*/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_OPENGLES*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_OPENGLES_PRIVATE_H*/ diff --git a/src/drivers/glfw/lv_opengles_texture.c b/src/drivers/opengles/lv_opengles_texture.c similarity index 89% rename from src/drivers/glfw/lv_opengles_texture.c rename to src/drivers/opengles/lv_opengles_texture.c index a719a7846d..e12bb0468d 100644 --- a/src/drivers/glfw/lv_opengles_texture.c +++ b/src/drivers/opengles/lv_opengles_texture.c @@ -11,10 +11,11 @@ #if LV_USE_OPENGLES #include "lv_opengles_debug.h" +#include "lv_opengles_private.h" + #include "../../display/lv_display_private.h" + #include -#include -#include /********************* * DEFINES @@ -60,14 +61,22 @@ lv_display_t * lv_opengles_texture_create(int32_t w, int32_t h) lv_display_delete(disp); return NULL; } + +#if LV_USE_DRAW_OPENGLES + static size_t LV_ATTRIBUTE_MEM_ALIGN dummy_buf; + lv_display_set_buffers(disp, &dummy_buf, NULL, w * h * 4, LV_DISPLAY_RENDER_MODE_DIRECT); +#else uint32_t stride = lv_draw_buf_width_to_stride(w, lv_display_get_color_format(disp)); uint32_t buf_size = stride * h; dsc->fb1 = malloc(buf_size); + LV_ASSERT_MALLOC(dsc->fb1); if(dsc->fb1 == NULL) { lv_free(dsc); lv_display_delete(disp); return NULL; } + lv_display_set_buffers(disp, dsc->fb1, NULL, buf_size, LV_DISPLAY_RENDER_MODE_DIRECT); +#endif GL_CALL(glGenTextures(1, &dsc->texture_id)); GL_CALL(glBindTexture(GL_TEXTURE_2D, dsc->texture_id)); @@ -92,16 +101,19 @@ lv_display_t * lv_opengles_texture_create(int32_t w, int32_t h) #error("Unsupported color format") #endif - glGenerateMipmap(GL_TEXTURE_2D); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 20); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + GL_CALL(glGenerateMipmap(GL_TEXTURE_2D)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 20)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); - lv_display_set_buffers(disp, dsc->fb1, NULL, buf_size, LV_DISPLAY_RENDER_MODE_DIRECT); lv_display_set_flush_cb(disp, flush_cb); lv_display_set_driver_data(disp, dsc); lv_display_add_event_cb(disp, release_disp_cb, LV_EVENT_DELETE, disp); +#if LV_USE_DRAW_OPENGLES + lv_display_delete_refr_timer(disp); +#endif + return disp; } diff --git a/src/drivers/glfw/lv_opengles_texture.h b/src/drivers/opengles/lv_opengles_texture.h similarity index 100% rename from src/drivers/glfw/lv_opengles_texture.h rename to src/drivers/opengles/lv_opengles_texture.h diff --git a/src/drivers/opengles/lv_opengles_window.h b/src/drivers/opengles/lv_opengles_window.h new file mode 100644 index 0000000000..9f22af2c55 --- /dev/null +++ b/src/drivers/opengles/lv_opengles_window.h @@ -0,0 +1,102 @@ +/** + * @file lv_opengles_window.h + * + */ + +#ifndef LV_OPENGLES_WINDOW_H +#define LV_OPENGLES_WINDOW_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" +#if LV_USE_OPENGLES + +#include "../../misc/lv_types.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Delete an OpenGL window. If it is the last one, the process will exit + * @param window OpenGL window to delete + */ +void lv_opengles_window_delete(lv_opengles_window_t * window); + +/** + * Add a texture to the OpenGL window. It can be an LVGL display texture, or any OpenGL texture + * @param window OpenGL window + * @param texture_id OpenGL texture ID + * @param w width in pixels of the texture + * @param h height in pixels of the texture + * @return the new texture handle + */ +lv_opengles_window_texture_t * lv_opengles_window_add_texture(lv_opengles_window_t * window, unsigned int texture_id, + int32_t w, int32_t h); + +lv_display_t * lv_opengles_window_display_create(lv_opengles_window_t * window, int32_t w, int32_t h); + +lv_opengles_window_texture_t * lv_opengles_window_display_get_window_texture(lv_display_t * window_display); + +/** + * Remove a texture from its OpenGL window and delete it + * @param texture handle of an OpenGL window texture + */ +void lv_opengles_window_texture_remove(lv_opengles_window_texture_t * texture); + +/** + * Set the x position of a texture within its OpenGL window + * @param texture handle of an OpenGL window texture + * @param x new x position of the texture + */ +void lv_opengles_window_texture_set_x(lv_opengles_window_texture_t * texture, int32_t x); + +/** + * Set the y position of a texture within its OpenGL window + * @param texture handle of an OpenGL window texture + * @param y new y position of the texture + */ +void lv_opengles_window_texture_set_y(lv_opengles_window_texture_t * texture, int32_t y); + +/** + * Set the opacity of a texture in an OpenGL window + * @param texture handle of an OpenGL window texture + * @param opa new opacity of the texture + */ +void lv_opengles_window_texture_set_opa(lv_opengles_window_texture_t * texture, lv_opa_t opa); + +/** + * Get the mouse indev associated with a texture in an OpenGL window, if it exists + * @param texture handle of an OpenGL window texture + * @return the indev or `NULL` + * @note there will only be an indev if the texture is based on an + * LVGL display texture and the window was created with + * `use_mouse_indev` as `true` + */ +lv_indev_t * lv_opengles_window_texture_get_mouse_indev(lv_opengles_window_texture_t * texture); + +/********************** + * MACROS + **********************/ + +#endif /* LV_USE_OPENGLES */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV_OPENGLES_WINDOW_H */ diff --git a/src/libs/gltf/gltf_view/ibl/lv_gltf_ibl_sampler.c b/src/libs/gltf/gltf_view/ibl/lv_gltf_ibl_sampler.c index 3c9ad87976..d7354fb357 100644 --- a/src/libs/gltf/gltf_view/ibl/lv_gltf_ibl_sampler.c +++ b/src/libs/gltf/gltf_view/ibl/lv_gltf_ibl_sampler.c @@ -14,7 +14,8 @@ #include "../../../../misc/lv_math.h" #include "../../../../misc/lv_log.h" #include "../../../../stdlib/lv_string.h" -#include "../../../../drivers/glfw/lv_opengles_debug.h" +#include "../../../../drivers/opengles/lv_opengles_private.h" +#include "../../../../drivers/opengles/lv_opengles_debug.h" #include "../../opengl_shader/lv_opengl_shader_internal.h" #include "../lv_gltf_view_internal.h" diff --git a/src/libs/gltf/gltf_view/lv_gltf_view_render.cpp b/src/libs/gltf/gltf_view/lv_gltf_view_render.cpp index 1179dd86c1..8ee9b918c1 100644 --- a/src/libs/gltf/gltf_view/lv_gltf_view_render.cpp +++ b/src/libs/gltf/gltf_view/lv_gltf_view_render.cpp @@ -18,7 +18,8 @@ #include "../fastgltf/lv_fastgltf.hpp" #include "../../../misc/lv_types.h" #include "../../../stdlib/lv_sprintf.h" -#include "../../../drivers/glfw/lv_opengles_debug.h" +#include "../../../drivers/opengles/lv_opengles_private.h" +#include "../../../drivers/opengles/lv_opengles_debug.h" #include "../math/lv_gltf_math.hpp" #include diff --git a/src/libs/gltf/opengl_shader/lv_opengl_shader_internal.h b/src/libs/gltf/opengl_shader/lv_opengl_shader_internal.h index f5cf75ee36..3f8a56523c 100644 --- a/src/libs/gltf/opengl_shader/lv_opengl_shader_internal.h +++ b/src/libs/gltf/opengl_shader/lv_opengl_shader_internal.h @@ -9,7 +9,8 @@ #include "../../../lv_conf_internal.h" #if LV_USE_GLTF -#include "../../../drivers/glfw/lv_opengles_debug.h" +#include "../../../drivers/opengles/lv_opengles_private.h" +#include "../../../drivers/opengles/lv_opengles_debug.h" #include "../../../misc/lv_types.h" #include "../../../misc/lv_rb_private.h" diff --git a/src/libs/gltf/opengl_shader/lv_opengl_shader_manager.c b/src/libs/gltf/opengl_shader/lv_opengl_shader_manager.c index 0fd26391b7..c4a1427e09 100644 --- a/src/libs/gltf/opengl_shader/lv_opengl_shader_manager.c +++ b/src/libs/gltf/opengl_shader/lv_opengl_shader_manager.c @@ -17,7 +17,8 @@ #include "../../../misc/lv_types.h" #include "../../../stdlib/lv_mem.h" #include "../../../stdlib/lv_sprintf.h" -#include "../../../drivers/glfw/lv_opengles_debug.h" +#include "../../../drivers/opengles/lv_opengles_private.h" +#include "../../../drivers/opengles/lv_opengles_debug.h" #include "../../../stdlib/lv_string.h" #include diff --git a/src/libs/gltf/opengl_shader/lv_opengl_shader_program.c b/src/libs/gltf/opengl_shader/lv_opengl_shader_program.c index ab627293d7..96fe48780b 100644 --- a/src/libs/gltf/opengl_shader/lv_opengl_shader_program.c +++ b/src/libs/gltf/opengl_shader/lv_opengl_shader_program.c @@ -11,7 +11,8 @@ #if LV_USE_GLTF -#include "../../../drivers/glfw/lv_opengles_debug.h" +#include "../../../drivers/opengles/lv_opengles_private.h" +#include "../../../drivers/opengles/lv_opengles_debug.h" #include "../../../misc/lv_assert.h" #include "../../../stdlib/lv_mem.h" /********************* diff --git a/src/lv_api_map_v9_3.h b/src/lv_api_map_v9_3.h index 534f7addd7..834cc8dd6a 100644 --- a/src/lv_api_map_v9_3.h +++ b/src/lv_api_map_v9_3.h @@ -38,6 +38,21 @@ extern "C" { #define lv_draw_buf_cache_operation_cb lv_draw_buf_cache_operation_cb_t #define lv_draw_buf_width_to_stride_cb lv_draw_buf_width_to_stride_cb_t +#define lv_glfw_window_t lv_opengles_window_t +#define lv_glfw_texture_t lv_opengles_window_texture_t +#define lv_glfw_window_create lv_opengles_glfw_window_create +#define lv_glfw_window_create_ex lv_opengles_glfw_window_create_ex +#define lv_glfw_window_set_title lv_opengles_glfw_window_set_title +#define lv_glfw_window_delete lv_opengles_window_delete +#define lv_glfw_window_set_flip lv_opengles_glfw_window_set_flip +#define lv_glfw_window_add_texture lv_opengles_window_add_texture +#define lv_glfw_texture_remove lv_opengles_window_texture_remove +#define lv_glfw_texture_set_x lv_opengles_window_texture_set_x +#define lv_glfw_texture_set_y lv_opengles_window_texture_set_y +#define lv_glfw_texture_set_opa lv_opengles_window_texture_set_opa +#define lv_glfw_texture_get_mouse_indev lv_opengles_window_texture_get_mouse_indev +#define lv_glfw_window_get_glfw_window lv_opengles_glfw_window_get_glfw_window + #ifdef __cplusplus } /*extern "C"*/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 9c7438f287..63cd681498 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -1009,7 +1009,7 @@ #endif #endif -/** Draw using cached OpenGLES textures */ +/** Draw using cached OpenGLES textures. Requires LV_USE_OPENGLES */ #ifndef LV_USE_DRAW_OPENGLES #ifdef CONFIG_LV_USE_DRAW_OPENGLES #define LV_USE_DRAW_OPENGLES CONFIG_LV_USE_DRAW_OPENGLES @@ -1018,6 +1018,16 @@ #endif #endif +#if LV_USE_DRAW_OPENGLES + #ifndef LV_DRAW_OPENGLES_TEXTURE_CACHE_COUNT + #ifdef CONFIG_LV_DRAW_OPENGLES_TEXTURE_CACHE_COUNT + #define LV_DRAW_OPENGLES_TEXTURE_CACHE_COUNT CONFIG_LV_DRAW_OPENGLES_TEXTURE_CACHE_COUNT + #else + #define LV_DRAW_OPENGLES_TEXTURE_CACHE_COUNT 64 + #endif + #endif +#endif + /** Draw using espressif PPA accelerator */ #ifndef LV_USE_PPA #ifdef CONFIG_LV_USE_PPA @@ -4208,6 +4218,14 @@ #define LV_USE_LINUX_DRM_GBM_BUFFERS 0 #endif #endif + + #ifndef LV_LINUX_DRM_USE_EGL + #ifdef CONFIG_LV_LINUX_DRM_USE_EGL + #define LV_LINUX_DRM_USE_EGL CONFIG_LV_LINUX_DRM_USE_EGL + #else + #define LV_LINUX_DRM_USE_EGL 0 + #endif + #endif #endif /** Interface for TFT_eSPI */ @@ -4416,7 +4434,7 @@ #endif #endif -/** Use OpenGL to open window on PC and handle mouse and keyboard */ +/** Use a generic OpenGL driver that can be used to embed in other applications or used with GLFW/EGL */ #ifndef LV_USE_OPENGLES #ifdef CONFIG_LV_USE_OPENGLES #define LV_USE_OPENGLES CONFIG_LV_USE_OPENGLES @@ -4438,6 +4456,16 @@ #endif #endif +/** Use GLFW to open window on PC and handle mouse and keyboard. Requires*/ +#ifndef LV_USE_GLFW + #ifdef CONFIG_LV_USE_GLFW + #define LV_USE_GLFW CONFIG_LV_USE_GLFW + #else + #define LV_USE_GLFW 0 + #endif +#endif + + /** QNX Screen display and input drivers */ #ifndef LV_USE_QNX #ifdef CONFIG_LV_USE_QNX @@ -4729,6 +4757,10 @@ LV_EXPORT_CONST_INT(LV_DRAW_BUF_ALIGN); #define LV_WAYLAND_WL_SHELL 0 #endif /* LV_USE_WAYLAND */ +#if LV_USE_LINUX_DRM == 0 + #define LV_LINUX_DRM_USE_EGL 0 +#endif /*LV_USE_LINUX_DRM*/ + #if LV_USE_SYSMON == 0 #define LV_USE_PERF_MONITOR 0 #define LV_USE_MEM_MONITOR 0 @@ -4771,6 +4803,10 @@ LV_EXPORT_CONST_INT(LV_DRAW_BUF_ALIGN); #endif #endif +#ifndef LV_USE_EGL + #define LV_USE_EGL LV_LINUX_DRM_USE_EGL +#endif /* LV_USE_EGL */ + #if LV_USE_OS #if (LV_USE_FREETYPE || LV_USE_THORVG) && LV_DRAW_THREAD_STACK_SIZE < (32 * 1024) #warning "Increase LV_DRAW_THREAD_STACK_SIZE to at least 32KB for FreeType or ThorVG." diff --git a/src/misc/lv_types.h b/src/misc/lv_types.h index b1ff7d91d3..a7a03d0f70 100644 --- a/src/misc/lv_types.h +++ b/src/misc/lv_types.h @@ -350,8 +350,8 @@ typedef struct _lv_rlottie_t lv_rlottie_t; typedef struct _lv_ffmpeg_player_t lv_ffmpeg_player_t; -typedef struct _lv_glfw_window_t lv_glfw_window_t; -typedef struct _lv_glfw_texture_t lv_glfw_texture_t; +typedef struct _lv_opengles_window_t lv_opengles_window_t; +typedef struct _lv_opengles_window_texture_t lv_opengles_window_texture_t; typedef uint32_t lv_prop_id_t; diff --git a/tests/src/lv_test_perf_conf.h b/tests/src/lv_test_perf_conf.h index 0a5f21596b..96424f924c 100644 --- a/tests/src/lv_test_perf_conf.h +++ b/tests/src/lv_test_perf_conf.h @@ -1183,7 +1183,7 @@ * shared across sub-systems and libraries using the Linux DMA-BUF API. * The GBM library aims to provide a platform independent memory management system * it supports the major GPU vendors - This option requires linking with libgbm */ - #define LV_LINUX_DRM_GBM_BUFFERS 0 + #define LV_USE_LINUX_DRM_GBM_BUFFERS 0 #endif /** Interface for TFT_eSPI */