diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 627f7d0eba..c6eeee1021 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -13,7 +13,7 @@ src/draw/nxp/g2d @AndreCostaaa src/draw/nxp/pxp @uLipe src/draw/opengles @AndreCostaaa @kisvegabor src/draw/renesas/dave2d @uLipe -src/draw/sdl @kisvegabor +src/draw/sdl @kisvegabor @AndreCostaaa src/draw/vg_lite @uLipe @FASTSHIFT src/drivers/display/drm @AndreCostaaa src/drivers/display/fb @AndreCostaaa diff --git a/docs/src/integration/embedded_linux/draw_opengl.rst b/docs/src/integration/embedded_linux/draw_opengl.rst new file mode 100644 index 0000000000..8755719028 --- /dev/null +++ b/docs/src/integration/embedded_linux/draw_opengl.rst @@ -0,0 +1,72 @@ +.. _opengles_draw_unit: + +===================== +OpenGL ES Draw Unit +===================== + +************ +Introduction +************ + +Overview +======== + +The OpenGL ES Draw Unit provides a hardware-accelerated rendering backend for LVGL that leverages OpenGL ES capabilities. + +OpenGL ES (OpenGL for Embedded Systems) is a subset of OpenGL designed for embedded devices, mobile phones, and other resource-constrained platforms. +The OpenGL ES Draw Unit brings GPU-accelerated rendering to LVGL applications on these platforms. + +Key Features +------------ + +- **Hardware Acceleration**: Direct GPU acceleration via OpenGL ES 2.0+ +- **Texture Caching**: Rendered elements are cached as OpenGL textures for efficient reuse +- **GPU Blending**: Hardware-accelerated texture composition and blending +- **Embedded-Friendly**: Optimized for resource-constrained embedded systems +- **Wide Platform Support**: Works on mobile, embedded Linux, and other OpenGL ES-compatible platforms + +Performance Characteristics +---------------------------- + +The OpenGL ES Draw Unit provides excellent performance for: + +- **Best Performance**: Static UI elements that benefit from texture caching +- **Good Performance**: UIs with moderate animation and dynamic content +- **Embedded Optimization**: Efficient memory usage suitable for embedded systems + + + +************* +Prerequisites +************* + +- OpenGL ES 2.0 or higher support on your platform +- A driver which supports OpenGL configured (see :ref:`OpenGL Overview `) + + + +************* +Configuration +************* + +Enable in lv_conf.h +=================== + +.. code-block:: c + + #define LV_USE_OPENGLES 1 + + #define LV_USE_DRAW_OPENGLES 1 + + /* Configurable cache count. Bigger cache will improve performance */ + #define LV_DRAW_OPENGLES_TEXTURE_CACHE_COUNT 64 + + + +******** +See Also +******** + +- :ref:`opengl_overview` - Complete OpenGL integration overview +- :ref:`egl_driver` - EGL Display Driver documentation +- :ref:`nanovg_draw_unit` - Vector graphics rendering option diff --git a/docs/src/integration/embedded_linux/draw_sdl.rst b/docs/src/integration/embedded_linux/draw_sdl.rst new file mode 100644 index 0000000000..04952f11d1 --- /dev/null +++ b/docs/src/integration/embedded_linux/draw_sdl.rst @@ -0,0 +1,58 @@ +.. _sdl_draw_unit: + +============== +SDL Draw Unit +============== + +************ +Introduction +************ + +Overview +======== + +The SDL Draw Unit provides a hardware-accelerated rendering backend for LVGL that leverages SDL2's texture system. +It uses software rendering to create SDL textures which are then cached and efficiently blended together by the GPU to compose the final UI. +This approach combines the flexibility of software rendering with the performance benefits of hardware-accelerated texture blending. + +Key Features +------------ + +- **Texture Caching**: Rendered elements are cached as SDL textures, reducing redundant rendering operations +- **Hardware Blending**: SDL's GPU-accelerated texture blending provides smooth compositing +- **Cross-Platform**: Works on any platform that supports SDL2 +- **Easy Integration**: Seamless integration with SDL-based LVGL applications + +Performance Characteristics +---------------------------- + +The SDL Draw Unit excels in scenarios with: + +- **Best Performance**: Static or infrequently changing UI elements that benefit from texture caching +- **Good Performance**: UIs with moderate animation and updates +- **Consider Alternatives**: Heavily dynamic content that changes every frame may not benefit as much from caching + + + +************* +Prerequisites +************* + +- SDL2 library installed (see :ref:`SDL Driver ` for installation instructions) +- LVGL configured with SDL support (``LV_USE_SDL 1``) + + + +************* +Configuration +************* + +Enable in lv_conf.h +=================== + +.. code-block:: c + + #define LV_USE_SDL 1 + #define LV_USE_DRAW_SDL 1 + +The SDL Draw Unit automatically integrates with the SDL display driver when both are enabled. diff --git a/docs/src/integration/embedded_linux/index.rst b/docs/src/integration/embedded_linux/index.rst index 80169c7c9a..8375377e33 100644 --- a/docs/src/integration/embedded_linux/index.rst +++ b/docs/src/integration/embedded_linux/index.rst @@ -7,6 +7,8 @@ Running under Embedded Linux .. toctree:: :maxdepth: 2 + draw_opengl + draw_sdl overview opengl nanovg diff --git a/docs/src/integration/embedded_linux/opengl.rst b/docs/src/integration/embedded_linux/opengl.rst index 6f4ede96c8..d918f87d3c 100644 --- a/docs/src/integration/embedded_linux/opengl.rst +++ b/docs/src/integration/embedded_linux/opengl.rst @@ -19,7 +19,7 @@ OpenGL Support in LVGL The OpenGL integration consists of two main components: - OpenGL Display Drivers: Handle output by showing the LVGL rendered content on the display, window, or texture in OpenGL-based environments -- OpenGL Draw Unit: When enabled, LVGL uses OpenGL for hardware-accelerated rendering operations, including texture caching for improved performance +- OpenGL Draw Unit (see :ref:`OpenGL Draw Unit `): When enabled, LVGL uses OpenGL for hardware-accelerated rendering operations, including texture caching for improved performance OpenGL support addresses several key use cases: @@ -39,34 +39,21 @@ The OpenGL implementation provides significant performance benefits in most scen OpenGL Driver Options ====================== -LVGL provides three OpenGL drivers to suit different application needs and platforms: +The following drivers can be used with OpenGL -1. GLFW Driver --------------- +The following drivers can be used and will automatically create and maintain an OpenGL context. -The GLFW display/input driver offers support for creating -LVGL displays and keyboard/mouse inputs that can be used in an OpenGL context. -The GLFW driver provides automatic window creation and context management with comprehensive input handling for rapid development on PC-like platforms. +- :ref:`SDL driver ` +- :ref:`Wayland driver ` +- :ref:`DRM driver ` +- :ref:`GLFW driver ` -For complete implementation details, see :ref:`GLFW driver `. +All drivers except the GLFW driver use EGL (Embedded-System Graphics Library) under the hood. -2. EGL Driver -------------- - -The EGL display driver offers support for creating -LVGL displays using the EGL (Embedded-System Graphics Library) API that can be used in an OpenGL context. -This driver provides lower-level hardware integration with direct driver access, supporting both DRM-based systems and standalone implementations for embedded platforms. - -For complete implementation details, see :ref:`EGL driver `. - -3. Generic OpenGL Driver ------------------------- - -The generic OpenGL driver offers support for creating -LVGL displays as OpenGL textures that can be embedded in existing OpenGL applications. -This driver assumes an existing OpenGL context and generates textures with hardware acceleration -for integration into custom graphics pipelines. +Additionally, LVGL provides a generic OpenGL driver which the user may use to embed +OpenGL textures in existing OpenGL applications. This driver assumes an existing OpenGL context +and generates textures with hardware acceleration for integration into custom graphics pipelines. For complete implementation details, see :ref:`OpenGL driver `. diff --git a/docs/src/integration/pc/sdl.rst b/docs/src/integration/pc/sdl.rst index e527497abc..50831ae03f 100644 --- a/docs/src/integration/pc/sdl.rst +++ b/docs/src/integration/pc/sdl.rst @@ -82,3 +82,73 @@ When building for 32-bit architecture, add the following workaround at the begin #ifndef WIN32 setenv("DBUS_FATAL_WARNINGS", "0", 1); #endif + + + +****************** +Rendering Backends +****************** + +The SDL driver supports multiple rendering backends that determine how LVGL renders content to the screen. +Each backend offers different performance characteristics and use cases. + + +Standard Software Rendering (Default) +====================================== + +By default, the SDL driver uses pure software rendering with no additional configuration required. + +**Configuration:** + +No additional configuration needed - this is the default when only ``LV_USE_SDL 1`` is enabled. + +SDL Draw Unit +============= + +The SDL Draw Unit uses software rendering to create SDL textures which are then cached and blended together by the GPU, +providing a hybrid approach that combines software rendering flexibility with hardware-accelerated texture composition. + +**Configuration:** + +.. code-block:: c + + #define LV_USE_SDL 1 + #define LV_USE_DRAW_SDL 1 + +For complete details, see :ref:`sdl_draw_unit`. + + +OpenGL-Based Rendering +======================= + +The SDL driver can leverage OpenGL for hardware-accelerated rendering. LVGL provides two OpenGL-based rendering options: + +- :ref:`NanoVG Draw Unit ` +- :ref:`OpenGL Draw Unit ` + + +.. code-block:: c + + #define LV_USE_SDL 1 + #define LV_USE_OPENGLES 1 + + /* For NanoVG Draw Unit*/ + #define LV_USE_DRAW_NANOVG 1 + #define LV_USE_NANOVG 1 + + /* For OpenGL Draw Unit */ + #define LV_USE_DRAW_OPENGLES 1 + +See the :ref:`Complete OpenGL overview ` for more information. + + + +******** +See Also +******** + +- :ref:`sdl_draw_unit` - SDL texture-based rendering +- :ref:`opengl_overview` - Complete OpenGL integration overview +- :ref:`nanovg_draw_unit` - NanoVG vector graphics rendering +- :ref:`opengles_draw_unit` - OpenGL ES for embedded systems + diff --git a/scripts/lv_conf_internal_gen.py b/scripts/lv_conf_internal_gen.py index 24fb05ae5b..d770329e6f 100755 --- a/scripts/lv_conf_internal_gen.py +++ b/scripts/lv_conf_internal_gen.py @@ -291,14 +291,21 @@ LV_EXPORT_CONST_INT(LV_DRAW_BUF_ALIGN); #endif #endif +#if LV_USE_SDL && LV_USE_OPENGLES && (LV_USE_DRAW_OPENGLES || LV_USE_DRAW_NANOVG) + #define LV_SDL_USE_EGL 1 +#else + #define LV_SDL_USE_EGL 0 +#endif + #ifndef LV_USE_EGL - #if LV_LINUX_DRM_USE_EGL || LV_WAYLAND_USE_EGL + #if LV_LINUX_DRM_USE_EGL || LV_WAYLAND_USE_EGL || LV_SDL_USE_EGL #define LV_USE_EGL 1 #else #define LV_USE_EGL 0 #endif #endif /* LV_USE_EGL */ + #if LV_USE_OS #if (LV_USE_FREETYPE || LV_USE_THORVG) && LV_DRAW_THREAD_STACK_SIZE < (32 * 1024) #error "Increase LV_DRAW_THREAD_STACK_SIZE to at least 32KB for FreeType or ThorVG." diff --git a/src/drivers/sdl/lv_sdl_egl.c b/src/drivers/sdl/lv_sdl_egl.c new file mode 100644 index 0000000000..4b4bc947bc --- /dev/null +++ b/src/drivers/sdl/lv_sdl_egl.c @@ -0,0 +1,265 @@ +/** + * @file lv_sdl_egl.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_SDL_USE_EGL + +#include +#include "lv_sdl_private.h" +#include "../opengles/lv_opengles_egl_private.h" +#include "../opengles/lv_opengles_driver.h" +#include "../../draw/lv_draw_buf.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef struct { + lv_opengles_egl_t * egl_ctx; + lv_opengles_texture_t opengles_texture; +} lv_sdl_egl_display_data_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void * create_window_cb(void * driver_data, const lv_egl_native_window_properties_t * props); +static void destroy_window_cb(void * driver_data, void * native_window); +static void flip_cb(void * driver_data, bool vsync); +static size_t select_config_cb(void * driver_data, const lv_egl_config_t * configs, size_t config_count); +static lv_egl_interface_t lv_sdl_get_egl_interface(lv_display_t * display); +static void flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map); + +static lv_result_t init_display(lv_display_t * display); +static lv_result_t resize_display(lv_display_t * display); +static void deinit_display(lv_display_t * display); +static SDL_Renderer * get_renderer(lv_display_t * display); +static lv_result_t redraw(lv_display_t * display); + +const lv_sdl_backend_ops_t lv_sdl_backend_ops = { + .init_display = init_display, + .resize_display = resize_display, + .deinit_display = deinit_display, + .redraw = redraw, + .get_renderer = get_renderer, +}; + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * STATIC FUNCTIONS + **********************/ + +static lv_result_t init_display(lv_display_t * display) +{ + lv_egl_interface_t ifc = lv_sdl_get_egl_interface(display); + lv_sdl_egl_display_data_t * ddata = lv_malloc_zeroed(sizeof(*ddata)); + if(!ddata) { + LV_LOG_WARN("Failed to allocate memory for display data"); + return LV_RESULT_INVALID; + } + ddata->egl_ctx = lv_opengles_egl_context_create(&ifc); + if(!ddata->egl_ctx) { + LV_LOG_ERROR("Failed to initialize EGL context"); + lv_free(ddata); + return LV_RESULT_INVALID; + } + + lv_sdl_backend_set_display_data(display, ddata); + + if(LV_USE_DRAW_NANOVG) { + static lv_draw_buf_t draw_buf; + static uint8_t dummy_buf; + lv_draw_buf_init(&draw_buf, 4096, 4096, LV_COLOR_FORMAT_ARGB8888, 4096 * 4, &dummy_buf, 4096 * 4096 * 4); + + lv_display_set_draw_buffers(display, &draw_buf, NULL); + lv_display_set_render_mode(display, LV_DISPLAY_RENDER_MODE_FULL); + } + else { + lv_result_t res = resize_display(display); + if(res != LV_RESULT_OK) { + LV_LOG_ERROR("Failed to create draw buffers"); + lv_opengles_egl_context_destroy(ddata->egl_ctx); + lv_free(ddata); + lv_sdl_backend_set_display_data(display, NULL); + return LV_RESULT_INVALID; + } + lv_display_set_render_mode(display, LV_DISPLAY_RENDER_MODE_DIRECT); + } + lv_display_set_flush_cb(display, flush_cb); + + return LV_RESULT_OK; + +} +static lv_result_t resize_display(lv_display_t * display) +{ + if(!LV_USE_DRAW_OPENGLES) { + return LV_RESULT_OK; + } + + lv_sdl_egl_display_data_t * ddata = lv_sdl_backend_get_display_data(display); + LV_ASSERT_NULL(ddata); + + int32_t hor_res = lv_sdl_window_get_horizontal_resolution(display); + int32_t ver_res = lv_sdl_window_get_vertical_resolution(display); + ddata->opengles_texture.is_texture_owner = true; + return lv_opengles_texture_reshape(&ddata->opengles_texture, display, hor_res, ver_res); + +} +static void deinit_display(lv_display_t * display) +{ + lv_sdl_egl_display_data_t * ddata = lv_sdl_backend_get_display_data(display); + if(ddata->egl_ctx) { + lv_opengles_egl_context_destroy(ddata->egl_ctx); + ddata->egl_ctx = NULL; + } + + if(LV_USE_DRAW_OPENGLES) { + lv_opengles_texture_deinit(&ddata->opengles_texture); + } + + lv_free(ddata); + lv_sdl_backend_set_display_data(display, NULL); +} + + +static lv_egl_interface_t lv_sdl_get_egl_interface(lv_display_t * display) +{ + return (lv_egl_interface_t) { + .driver_data = display, + .create_window_cb = create_window_cb, + .destroy_window_cb = destroy_window_cb, + .egl_platform = 0, + .native_display = EGL_DEFAULT_DISPLAY, + .flip_cb = flip_cb, + .select_config = select_config_cb, + }; + +} +static void * create_window_cb(void * driver_data, const lv_egl_native_window_properties_t * props) +{ + LV_UNUSED(props); + lv_display_t * display = (lv_display_t *)driver_data; + SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version); + SDL_GetWindowWMInfo(lv_sdl_window_get_window(display), &wmInfo); + + EGLNativeWindowType native_window; +#if defined(SDL_VIDEO_DRIVER_WINDOWS) + native_window = wmInfo.info.win.window; +#elif defined(SDL_VIDEO_DRIVER_X11) + native_window = wmInfo.info.x11.window; +#elif defined(SDL_VIDEO_DRIVER_WAYLAND) + native_window = wmInfo.info.wl.surface; +#else + LV_LOG_ERROR("Unsupported platform for EGL"); + return NULL; +#endif + return (void *)native_window; +} + +static void flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map) +{ + + LV_UNUSED(area); + LV_UNUSED(px_map); + lv_sdl_egl_display_data_t * ddata = lv_sdl_backend_get_display_data(display); + LV_ASSERT_NULL(ddata); + + if(lv_display_flush_is_last(display)) { +#if LV_USE_DRAW_OPENGLES + lv_opengles_viewport(0, 0, + lv_display_get_original_horizontal_resolution(display), + lv_display_get_original_vertical_resolution(display)); + lv_opengles_render_display_texture(display, false, true); +#endif /*LV_USE_DRAW_OPENGLES*/ + lv_opengles_egl_update(ddata->egl_ctx); + } + lv_display_flush_ready(display); + return; +} + +static size_t select_config_cb(void * driver_data, const lv_egl_config_t * configs, size_t config_count) +{ + lv_display_t * display = (lv_display_t *)driver_data; + int32_t target_w = lv_display_get_horizontal_resolution(display); + int32_t target_h = lv_display_get_vertical_resolution(display); + +#if LV_COLOR_DEPTH == 16 + lv_color_format_t target_cf = LV_COLOR_FORMAT_RGB565; +#elif LV_COLOR_DEPTH == 32 + lv_color_format_t target_cf = LV_COLOR_FORMAT_ARGB8888; +#else +#error "Unsupported color format" +#endif + + + for(size_t i = 0; i < config_count; ++i) { + LV_LOG_TRACE("Got config %zu %#x %dx%d %d %d %d %d buffer size %d depth %d samples %d stencil %d surface type %d renderable type %d", + i, configs[i].id, + configs[i].max_width, configs[i].max_height, configs[i].r_bits, configs[i].g_bits, configs[i].b_bits, configs[i].a_bits, + configs[i].buffer_size, configs[i].depth, configs[i].samples, configs[i].stencil, + configs[i].surface_type & EGL_WINDOW_BIT, configs[i].renderable_type & EGL_OPENGL_ES2_BIT); + } + + for(size_t i = 0; i < config_count; ++i) { + lv_color_format_t config_cf = lv_opengles_egl_color_format_from_egl_config(&configs[i]); + const bool resolution_matches = configs[i].max_width >= target_w && + configs[i].max_height >= target_h; + const bool is_nanovg_compatible = (configs[i].renderable_type & EGL_OPENGL_ES2_BIT) != 0 && + configs[i].stencil == 8 && configs[i].samples == 4; + const bool is_window = (configs[i].surface_type & EGL_WINDOW_BIT) != 0; + const bool is_compatible_with_draw_unit = is_nanovg_compatible || !LV_USE_DRAW_NANOVG; + + if(is_window && resolution_matches && config_cf == target_cf && is_compatible_with_draw_unit) { + LV_LOG_INFO("Choosing config %zu", i); + return i; + } + } + return config_count; +} +static void destroy_window_cb(void * driver_data, void * native_window) +{ + LV_UNUSED(driver_data); + LV_UNUSED(native_window); +} + +static void flip_cb(void * driver_data, bool vsync) +{ + + LV_UNUSED(driver_data); + LV_UNUSED(vsync); +} + + +static SDL_Renderer * get_renderer(lv_display_t * display) +{ + LV_UNUSED(display); + return NULL; +} +static lv_result_t redraw(lv_display_t * display) +{ + LV_UNUSED(display); + return LV_RESULT_OK; +} + +#endif /*LV_SDL_USE_EGL*/ diff --git a/src/drivers/sdl/lv_sdl_private.h b/src/drivers/sdl/lv_sdl_private.h index 1fd519bfa7..be5b31fef9 100644 --- a/src/drivers/sdl/lv_sdl_private.h +++ b/src/drivers/sdl/lv_sdl_private.h @@ -14,9 +14,13 @@ extern "C" { * INCLUDES *********************/ #include "../../misc/lv_types.h" +#include "lv_sdl_window.h" #if LV_USE_SDL +#include "../opengles/lv_opengles_egl_private.h" +#include "../opengles/lv_opengles_texture_private.h" + #include LV_SDL_INCLUDE_PATH /********************* @@ -27,6 +31,35 @@ extern "C" { * TYPEDEFS **********************/ +typedef struct { + void * backend_data; + SDL_Window * window; + float zoom; + uint8_t ignore_size_chg; +} lv_sdl_window_t; + +void lv_sdl_backend_set_display_data(lv_display_t * display, void * backend_display_data); +void * lv_sdl_backend_get_display_data(lv_display_t * display); + +int32_t lv_sdl_window_get_horizontal_resolution(lv_display_t * display); +int32_t lv_sdl_window_get_vertical_resolution(lv_display_t * display); + +typedef lv_result_t (*lv_sdl_backend_init_display_t)(lv_display_t * disp); +typedef lv_result_t (*lv_sdl_backend_resize_display_t)(lv_display_t * disp); +typedef lv_result_t (*lv_sdl_backend_redraw_t)(lv_display_t * disp); +typedef SDL_Renderer * (*lv_sdl_backend_get_renderer_t)(lv_display_t * disp); +typedef void (*lv_sdl_backend_deinit_display_t)(lv_display_t * disp); + +typedef struct { + lv_sdl_backend_init_display_t init_display; + lv_sdl_backend_resize_display_t resize_display; + lv_sdl_backend_deinit_display_t deinit_display; + lv_sdl_backend_redraw_t redraw; + lv_sdl_backend_get_renderer_t get_renderer; +} lv_sdl_backend_ops_t; + +extern const lv_sdl_backend_ops_t lv_sdl_backend_ops; + /********************** * GLOBAL PROTOTYPES **********************/ @@ -36,6 +69,21 @@ void lv_sdl_mouse_handler(SDL_Event * event); void lv_sdl_mousewheel_handler(SDL_Event * event); lv_display_t * lv_sdl_get_disp_from_win_id(uint32_t win_id); + +#if LV_SDL_USE_EGL +lv_result_t lv_sdl_egl_init(lv_display_t * disp); +lv_result_t lv_sdl_egl_resize(lv_display_t * disp); +void lv_sdl_egl_deinit(lv_display_t * disp); +#elif LV_USE_DRAW_SDL +lv_result_t lv_sdl_texture_init(lv_display_t * disp); +lv_result_t lv_sdl_texture_resize(lv_display_t * disp); +void lv_sdl_texture_deinit(lv_display_t * disp); +#else +lv_result_t lv_sdl_sw_init(lv_display_t * disp); +lv_result_t lv_sdl_sw_resize(lv_display_t * disp); +void lv_sdl_sw_deinit(lv_display_t * disp); +#endif + /********************** * MACROS **********************/ diff --git a/src/drivers/sdl/lv_sdl_sw.c b/src/drivers/sdl/lv_sdl_sw.c new file mode 100644 index 0000000000..7427739dad --- /dev/null +++ b/src/drivers/sdl/lv_sdl_sw.c @@ -0,0 +1,342 @@ +/** + * @file lv_sdl_sw.c + * + */ + +/********************* + * INCLUDES + *********************/ + +/* for aligned_alloc */ +#ifndef __USE_ISOC11 + #define _ISOC11_SOURCE +#endif + +#include "lv_sdl_private.h" + +#if LV_USE_SDL && !LV_SDL_USE_EGL && !LV_USE_DRAW_SDL + +#ifndef _WIN32 + #include +#else + #include +#endif /* _WIN32 */ + +#include "../../display/lv_display_private.h" +#include "../../misc/lv_types.h" +#include "../../draw/sw/lv_draw_sw_utils.h" + + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + SDL_Texture * texture; + SDL_Renderer * renderer; + uint8_t * fb1; + uint8_t * fb2; + uint8_t * fb_act; + uint8_t * buf1; + uint8_t * buf2; + uint8_t * rotated_buf; + size_t rotated_buf_size; +} lv_sdl_sw_display_data_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void * sdl_draw_buf_realloc_aligned(void * ptr, size_t new_size); +static void sdl_draw_buf_free(void * ptr); +static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map); +static lv_result_t window_update(lv_display_t * disp); + +static inline int sdl_render_mode(void) +{ + return LV_SDL_RENDER_MODE; +} + +static lv_result_t init_display(lv_display_t * display); +static lv_result_t resize_display(lv_display_t * display); +static void deinit_display(lv_display_t * display); +static SDL_Renderer * get_renderer(lv_display_t * display); + +const lv_sdl_backend_ops_t lv_sdl_backend_ops = { + .init_display = init_display, + .resize_display = resize_display, + .deinit_display = deinit_display, + .redraw = window_update, + .get_renderer = get_renderer, +}; + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * STATIC FUNCTIONS + **********************/ + +static lv_result_t init_display(lv_display_t * display) +{ + lv_sdl_sw_display_data_t * ddata = lv_malloc_zeroed(sizeof(*ddata)); + if(!ddata) { + LV_LOG_WARN("No memory for display data"); + return LV_RESULT_INVALID; + } + ddata->renderer = SDL_CreateRenderer(lv_sdl_window_get_window(display), -1, + LV_SDL_ACCELERATED ? SDL_RENDERER_ACCELERATED : SDL_RENDERER_SOFTWARE); + if(!ddata->renderer) { + LV_LOG_ERROR("Failed to create SDL renderer '%s'", SDL_GetError()); + lv_free(ddata); + return LV_RESULT_INVALID; + } + lv_sdl_backend_set_display_data(display, ddata); + + int32_t hor_res = lv_sdl_window_get_horizontal_resolution(display); + int32_t ver_res = lv_sdl_window_get_vertical_resolution(display); + + resize_display(display); + + uint32_t px_size = lv_color_format_get_size(lv_display_get_color_format(display)); + lv_memset(ddata->fb1, 0xff, hor_res * ver_res * px_size); + if(ddata->fb2) lv_memset(ddata->fb2, 0xff, hor_res * ver_res * px_size); + + lv_display_set_flush_cb(display, flush_cb); + + if(LV_SDL_RENDER_MODE == LV_DISPLAY_RENDER_MODE_PARTIAL) { + uint32_t palette_size = LV_COLOR_INDEXED_PALETTE_SIZE(lv_display_get_color_format(display)) * 4; + uint32_t buffer_size_bytes = 32 * 1024 + palette_size; + ddata->buf1 = sdl_draw_buf_realloc_aligned(NULL, buffer_size_bytes); + if(LV_SDL_BUF_COUNT == 2) { + ddata->buf2 = sdl_draw_buf_realloc_aligned(NULL, buffer_size_bytes); + } + lv_display_set_buffers(display, ddata->buf1, ddata->buf2, buffer_size_bytes, LV_SDL_RENDER_MODE); + } + else { + /*LV_DISPLAY_RENDER_MODE_DIRECT or FULL */ + uint32_t stride = lv_draw_buf_width_to_stride(display->hor_res, + lv_display_get_color_format(display)); + lv_display_set_buffers(display, ddata->fb1, ddata->fb2, stride * display->ver_res, + LV_SDL_RENDER_MODE); + } + return LV_RESULT_OK; +} +static lv_result_t resize_display(lv_display_t * display) +{ + lv_color_format_t cf = lv_display_get_color_format(display); + /*In some cases SDL stride might be different than LVGL render stride, like in I1 format. + SDL still uses ARGB8888 as the color format, but LVGL renders in I1, thus causing a mismatch + This ensures correct stride for SDL buffers in this case.*/ + if(cf == LV_COLOR_FORMAT_I1) { + cf = LV_COLOR_FORMAT_ARGB8888; + } + uint32_t stride = lv_draw_buf_width_to_stride(display->hor_res, cf); + lv_sdl_sw_display_data_t * ddata = lv_sdl_backend_get_display_data(display); + + ddata->fb1 = sdl_draw_buf_realloc_aligned(ddata->fb1, stride * display->ver_res); + LV_ASSERT_MALLOC(ddata->fb1); + lv_memzero(ddata->fb1, stride * display->ver_res); + + if(sdl_render_mode() == LV_DISPLAY_RENDER_MODE_PARTIAL) { + ddata->fb_act = ddata->fb1; + } + else { + if(LV_SDL_BUF_COUNT == 2) { + ddata->fb2 = sdl_draw_buf_realloc_aligned(ddata->fb2, stride * display->ver_res); + lv_memset(ddata->fb2, 0x00, stride * display->ver_res); + } + lv_display_set_buffers(display, ddata->fb1, ddata->fb2, stride * display->ver_res, LV_SDL_RENDER_MODE); + } + if(ddata->texture) SDL_DestroyTexture(ddata->texture); + +#if LV_COLOR_DEPTH == 32 || LV_COLOR_DEPTH == 1 + SDL_PixelFormatEnum px_format = + SDL_PIXELFORMAT_RGB888; /*same as SDL_PIXELFORMAT_RGB888, but it's not supported in older versions*/ +#elif LV_COLOR_DEPTH == 24 + SDL_PixelFormatEnum px_format = SDL_PIXELFORMAT_BGR24; +#elif LV_COLOR_DEPTH == 16 + SDL_PixelFormatEnum px_format = SDL_PIXELFORMAT_RGB565; +#else +#error("Unsupported color format") +#endif + + ddata->texture = SDL_CreateTexture(ddata->renderer, px_format, + SDL_TEXTUREACCESS_STATIC, display->hor_res, display->ver_res); + SDL_SetTextureBlendMode(ddata->texture, SDL_BLENDMODE_BLEND); + return LV_RESULT_OK; +} + +static void deinit_display(lv_display_t * display) +{ + + lv_sdl_sw_display_data_t * ddata = lv_sdl_backend_get_display_data(display); + LV_ASSERT_NULL(ddata); + if(ddata->texture) { + SDL_DestroyTexture(ddata->texture); + ddata->texture = NULL; + } + if(ddata->renderer) { + SDL_DestroyRenderer(ddata->renderer); + ddata->renderer = NULL; + } + + if(ddata->fb1) { + sdl_draw_buf_free(ddata->fb1); + ddata->fb1 = NULL; + } + if(ddata->fb2) { + sdl_draw_buf_free(ddata->fb2); + ddata->fb2 = NULL; + } + + if(ddata->buf1) { + sdl_draw_buf_free(ddata->buf1); + ddata->buf1 = NULL; + } + if(ddata->buf2) { + sdl_draw_buf_free(ddata->buf2); + ddata->buf2 = NULL; + } + + lv_free(ddata); + lv_sdl_backend_set_display_data(display, NULL); +} + +static SDL_Renderer * get_renderer(lv_display_t * display) +{ + lv_sdl_sw_display_data_t * ddata = lv_sdl_backend_get_display_data(display); + LV_ASSERT_NULL(ddata); + return ddata->renderer; +} + + +static void * sdl_draw_buf_realloc_aligned(void * ptr, size_t new_size) +{ + if(ptr) { + sdl_draw_buf_free(ptr); + } + + /* No need copy for drawing buffer */ +#ifndef _WIN32 + /* Size must be multiple of align, See: https://en.cppreference.com/w/c/memory/aligned_alloc */ +#define BUF_ALIGN (LV_DRAW_BUF_ALIGN < sizeof(void *) ? sizeof(void *) : LV_DRAW_BUF_ALIGN) + return aligned_alloc(BUF_ALIGN, LV_ALIGN_UP(new_size, BUF_ALIGN)); +#else + return _aligned_malloc(LV_ALIGN_UP(new_size, LV_DRAW_BUF_ALIGN), LV_DRAW_BUF_ALIGN); +#endif /* _WIN32 */ +} + +static void sdl_draw_buf_free(void * ptr) +{ +#ifndef _WIN32 + free(ptr); +#else + _aligned_free(ptr); +#endif /* _WIN32 */ +} + +static void flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map) +{ + lv_color_format_t cf = lv_display_get_color_format(display); + lv_sdl_sw_display_data_t * ddata = lv_sdl_backend_get_display_data(display); + uint32_t * argb_px_map = NULL; + + if(LV_SDL_RENDER_MODE == LV_DISPLAY_RENDER_MODE_PARTIAL) { + if(cf == LV_COLOR_FORMAT_RGB565_SWAPPED) { + uint32_t width = lv_area_get_width(area); + uint32_t height = lv_area_get_height(area); + lv_draw_sw_rgb565_swap(px_map, width * height); + } + /*Update values in a special OLED I1 --> ARGB8888 case + We render everything in I1, but display it in ARGB8888*/ + if(cf == LV_COLOR_FORMAT_I1) { + /*I1 uses 1 bit wide pixels, ARGB8888 uses 4 byte wide pixels*/ + cf = LV_COLOR_FORMAT_ARGB8888; + uint32_t width = lv_area_get_width(area); + uint32_t height = lv_area_get_height(area); + uint32_t argb_px_map_size = width * height * 4; + argb_px_map = malloc(argb_px_map_size); + if(argb_px_map == NULL) { + LV_LOG_ERROR("malloc failed"); + lv_display_flush_ready(display); + return; + } + /* skip the palette */ + px_map += LV_COLOR_INDEXED_PALETTE_SIZE(LV_COLOR_FORMAT_I1) * 4; + const uint32_t i1_stride = lv_draw_buf_width_to_stride(width, LV_COLOR_FORMAT_I1); + const uint32_t argb8888_stride = lv_draw_buf_width_to_stride(width, LV_COLOR_FORMAT_ARGB8888); + lv_draw_sw_i1_to_argb8888(px_map, argb_px_map, width, height, i1_stride, argb8888_stride, 0xFF000000u, 0xFFFFFFFFu); + px_map = (uint8_t *)argb_px_map; + } + + lv_area_t rotated_area = *area; + lv_display_rotate_area(display, &rotated_area); + + int32_t px_map_w = lv_area_get_width(area); + int32_t px_map_h = lv_area_get_height(area); + uint32_t px_map_stride = lv_draw_buf_width_to_stride(lv_area_get_width(area), cf); + uint32_t px_size = lv_color_format_get_size(cf); + + int32_t fb_stride = lv_draw_buf_width_to_stride(display->hor_res, cf); + uint8_t * fb_start = ddata->fb_act; + fb_start += rotated_area.y1 * fb_stride + rotated_area.x1 * px_size; + lv_display_rotation_t rotation = lv_display_get_rotation(display); + + if(rotation == LV_DISPLAY_ROTATION_0) { + uint32_t px_map_line_bytes = lv_area_get_width(area) * px_size; + + int32_t y; + for(y = area->y1; y <= area->y2; y++) { + lv_memcpy(fb_start, px_map, px_map_line_bytes); + px_map += px_map_stride; + fb_start += fb_stride; + } + } + else { + lv_draw_sw_rotate(px_map, fb_start, px_map_w, px_map_h, px_map_stride, fb_stride, rotation, cf); + } + } + + if(lv_display_flush_is_last(display)) { + if(sdl_render_mode() != LV_DISPLAY_RENDER_MODE_PARTIAL) { + ddata->fb_act = px_map; + } + window_update(display); + } + free(argb_px_map); + lv_display_flush_ready(display); +} + +static lv_result_t window_update(lv_display_t * display) +{ + lv_color_format_t cf = lv_display_get_color_format(display); + lv_sdl_sw_display_data_t * ddata = lv_sdl_backend_get_display_data(display); + if(cf == LV_COLOR_FORMAT_I1) { + cf = LV_COLOR_FORMAT_ARGB8888; + } + uint32_t stride = lv_draw_buf_width_to_stride(display->hor_res, cf); + SDL_UpdateTexture(ddata->texture, NULL, ddata->fb_act, stride); + + SDL_RenderClear(ddata->renderer); + + /*Update the renderer with the texture containing the rendered image*/ + SDL_RenderCopy(ddata->renderer, ddata->texture, NULL, NULL); + SDL_RenderPresent(ddata->renderer); + return LV_RESULT_OK; +} + +#endif /*LV_USE_SDL && !LV_SDL_USE_EGL && !LV_USE_DRAW_SDL*/ diff --git a/src/drivers/sdl/lv_sdl_texture.c b/src/drivers/sdl/lv_sdl_texture.c new file mode 100644 index 0000000000..beabb4bd46 --- /dev/null +++ b/src/drivers/sdl/lv_sdl_texture.c @@ -0,0 +1,136 @@ +/** + * @file lv_sdl_texture.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_sdl_private.h" + +#if LV_USE_SDL && !LV_SDL_USE_EGL && LV_USE_DRAW_SDL + +#include "../../draw/lv_draw_buf.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + SDL_Renderer * renderer; +} lv_sdl_texture_display_data_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map); + +static lv_result_t init_display(lv_display_t * display); +static lv_result_t resize(lv_display_t * display); +static void deinit_display(lv_display_t * display); +static SDL_Renderer * get_renderer(lv_display_t * display); +static lv_result_t redraw(lv_display_t * display); + +/********************** + * STATIC VARIABLES + **********************/ + +const lv_sdl_backend_ops_t lv_sdl_backend_ops = { + .init_display = init_display, + .resize_display = resize, + .deinit_display = deinit_display, + .get_renderer = get_renderer, + .redraw = redraw, +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * STATIC FUNCTIONS + **********************/ + +static lv_result_t init_display(lv_display_t * display) +{ + lv_sdl_texture_display_data_t * ddata = lv_malloc_zeroed(sizeof(*ddata)); + if(!ddata) { + LV_LOG_WARN("No memory for display data"); + return LV_RESULT_INVALID; + } + ddata->renderer = SDL_CreateRenderer(lv_sdl_window_get_window(display), -1, + LV_SDL_ACCELERATED ? SDL_RENDERER_ACCELERATED : SDL_RENDERER_SOFTWARE); + if(!ddata->renderer) { + LV_LOG_ERROR("Failed to create SDL renderer '%s'", SDL_GetError()); + lv_free(ddata); + return LV_RESULT_INVALID; + } + lv_sdl_backend_set_display_data(display, ddata); + + /*It will render directly to default Texture, so the buffer is not used, so just set something*/ + static lv_draw_buf_t draw_buf; + static uint8_t dummy_buf; /*It won't be used as it will render to the SDL textures directly*/ + lv_draw_buf_init(&draw_buf, 4096, 4096, LV_COLOR_FORMAT_ARGB8888, 4096 * 4, &dummy_buf, 4096 * 4096 * 4); + + lv_display_set_draw_buffers(display, &draw_buf, NULL); + lv_display_set_render_mode(display, LV_DISPLAY_RENDER_MODE_FULL); + lv_display_set_flush_cb(display, flush_cb); + return LV_RESULT_OK; +} + +static lv_result_t resize(lv_display_t * display) +{ + LV_UNUSED(display); + return LV_RESULT_OK; +} + +static void deinit_display(lv_display_t * display) +{ + lv_sdl_texture_display_data_t * ddata = lv_sdl_backend_get_display_data(display); + LV_ASSERT_NULL(ddata); + + if(ddata->renderer) { + SDL_DestroyRenderer(ddata->renderer); + } + + lv_free(ddata); + lv_sdl_backend_set_display_data(display, NULL); +} + +static void flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map) +{ + LV_UNUSED(area); + LV_UNUSED(px_map); + lv_sdl_texture_display_data_t * ddata = lv_sdl_backend_get_display_data(display); + if(lv_display_flush_is_last(display)) { + SDL_RenderPresent(ddata->renderer); + } + lv_display_flush_ready(display); +} + +static SDL_Renderer * get_renderer(lv_display_t * display) +{ + lv_sdl_texture_display_data_t * ddata = lv_sdl_backend_get_display_data(display); + LV_ASSERT_NULL(ddata); + return ddata->renderer; +} +static lv_result_t redraw(lv_display_t * display) +{ + + lv_sdl_texture_display_data_t * ddata = lv_sdl_backend_get_display_data(display); + LV_ASSERT_NULL(ddata); + SDL_RenderPresent(ddata->renderer); + return LV_RESULT_OK; +} + +#endif /*LV_USE_SDL && !LV_SDL_USE_EGL && LV_USE_DRAW_SDL*/ diff --git a/src/drivers/sdl/lv_sdl_window.c b/src/drivers/sdl/lv_sdl_window.c index fe19304d31..15a87ab121 100644 --- a/src/drivers/sdl/lv_sdl_window.c +++ b/src/drivers/sdl/lv_sdl_window.c @@ -14,21 +14,9 @@ #if LV_USE_SDL #include #include "../../core/lv_refr.h" -#include "../../stdlib/lv_string.h" #include "../../core/lv_global.h" #include "../../display/lv_display_private.h" #include "../../lv_init.h" -#include "../../draw/lv_draw_buf.h" - -/* for aligned_alloc */ -#ifndef __USE_ISOC11 - #define __USE_ISOC11 -#endif -#ifndef _WIN32 - #include -#else - #include -#endif /* _WIN32 */ #define SDL_MAIN_HANDLED /*To fix SDL's "undefined reference to WinMain" issue*/ #include "lv_sdl_private.h" @@ -45,35 +33,12 @@ /********************** * TYPEDEFS **********************/ -typedef struct { - SDL_Window * window; - SDL_Renderer * renderer; -#if LV_USE_DRAW_SDL == 0 - SDL_Texture * texture; - uint8_t * fb1; - uint8_t * fb2; - uint8_t * fb_act; - uint8_t * buf1; - uint8_t * buf2; - uint8_t * rotated_buf; - size_t rotated_buf_size; -#endif - float zoom; - uint8_t ignore_size_chg; -} lv_sdl_window_t; /********************** * STATIC PROTOTYPES **********************/ static inline int sdl_render_mode(void); -static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p); -static void window_create(lv_display_t * disp); -static void window_update(lv_display_t * disp); -#if LV_USE_DRAW_SDL == 0 - static void texture_resize(lv_display_t * disp); - static void * sdl_draw_buf_realloc_aligned(void * ptr, size_t new_size); - static void sdl_draw_buf_free(void * ptr); -#endif +static lv_result_t window_create(lv_display_t * disp); static void sdl_event_handler(lv_timer_t * t); static void release_disp_cb(lv_event_t * e); static void res_chg_event_cb(lv_event_t * e); @@ -95,6 +60,10 @@ static lv_timer_t * event_handler_timer; lv_display_t * lv_sdl_window_create(int32_t hor_res, int32_t ver_res) { if(!inited) { +#if LV_SDL_USE_EGL && defined(SDL_VIDEO_DRIVER_X11) + SDL_SetHintWithPriority("SDL_VIDEODRIVER", "x11", SDL_HINT_OVERRIDE); + SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1"); +#endif SDL_Init(SDL_INIT_VIDEO); SDL_StartTextInput(); event_handler_timer = lv_timer_create(sdl_event_handler, 5, NULL); @@ -113,39 +82,18 @@ lv_display_t * lv_sdl_window_create(int32_t hor_res, int32_t ver_res) lv_free(dsc); return NULL; } - lv_display_add_event_cb(disp, release_disp_cb, LV_EVENT_DELETE, disp); lv_display_set_driver_data(disp, dsc); - window_create(disp); - - lv_display_set_flush_cb(disp, flush_cb); - -#if LV_USE_DRAW_SDL == 0 - if(sdl_render_mode() == LV_DISPLAY_RENDER_MODE_PARTIAL) { - uint32_t palette_size = LV_COLOR_INDEXED_PALETTE_SIZE(lv_display_get_color_format(disp)) * 4; - uint32_t buffer_size_bytes = 32 * 1024 + palette_size; - dsc->buf1 = sdl_draw_buf_realloc_aligned(NULL, buffer_size_bytes); -#if LV_SDL_BUF_COUNT == 2 - dsc->buf2 = sdl_draw_buf_realloc_aligned(NULL, buffer_size_bytes); -#endif - lv_display_set_buffers(disp, dsc->buf1, dsc->buf2, buffer_size_bytes, LV_DISPLAY_RENDER_MODE_PARTIAL); + lv_result_t res = window_create(disp); + if(res != LV_RESULT_OK) { + LV_LOG_ERROR("Failed to initialize window"); + lv_free(dsc); + lv_display_delete(disp); + return NULL; } - /*LV_DISPLAY_RENDER_MODE_DIRECT or FULL */ - else { - uint32_t stride = lv_draw_buf_width_to_stride(disp->hor_res, - lv_display_get_color_format(disp)); - lv_display_set_buffers(disp, dsc->fb1, dsc->fb2, stride * disp->ver_res, - LV_SDL_RENDER_MODE); - } -#else /*LV_USE_DRAW_SDL == 1*/ - /*It will render directly to default Texture, so the buffer is not used, so just set something*/ - static lv_draw_buf_t draw_buf; - static uint8_t dummy_buf; /*It won't be used as it will render to the SDL textures directly*/ - lv_draw_buf_init(&draw_buf, 4096, 4096, LV_COLOR_FORMAT_ARGB8888, 4096 * 4, &dummy_buf, 4096 * 4096 * 4); - lv_display_set_draw_buffers(disp, &draw_buf, NULL); - lv_display_set_render_mode(disp, LV_DISPLAY_RENDER_MODE_DIRECT); -#endif /*LV_USE_DRAW_SDL == 0*/ + lv_display_add_event_cb(disp, release_disp_cb, LV_EVENT_DELETE, disp); lv_display_add_event_cb(disp, res_chg_event_cb, LV_EVENT_RESOLUTION_CHANGED, NULL); + /*Process the initial events*/ sdl_event_handler(NULL); @@ -208,10 +156,12 @@ void lv_sdl_window_set_icon(lv_display_t * disp, void * icon, int32_t width, int SDL_FreeSurface(iconSurface); } -void * lv_sdl_window_get_renderer(lv_display_t * disp) +void * lv_sdl_window_get_renderer(lv_display_t * display) { - lv_sdl_window_t * dsc = lv_display_get_driver_data(disp); - return dsc->renderer; + if(!display) { + return NULL; + } + return lv_sdl_backend_ops.get_renderer(display); } struct SDL_Window * lv_sdl_window_get_window(lv_display_t * disp) @@ -238,6 +188,36 @@ void lv_sdl_quit(void) } } +void lv_sdl_backend_set_display_data(lv_display_t * display, void * backend_display_data) +{ + LV_ASSERT_NULL(display); + lv_sdl_window_t * dsc = lv_display_get_driver_data(display); + dsc->backend_data = backend_display_data; +} +void * lv_sdl_backend_get_display_data(lv_display_t * display) +{ + LV_ASSERT_NULL(display); + lv_sdl_window_t * dsc = lv_display_get_driver_data(display); + return dsc->backend_data; +} + +int32_t lv_sdl_window_get_horizontal_resolution(lv_display_t * display) +{ + /* Private function, fine to assert here*/ + LV_ASSERT_NULL(display); + lv_sdl_window_t * dsc = lv_display_get_driver_data(display); + LV_ASSERT_NULL(dsc); + return (int32_t)((float)(display->hor_res) * dsc->zoom); +} +int32_t lv_sdl_window_get_vertical_resolution(lv_display_t * display) +{ + /* Private function, fine to assert here*/ + LV_ASSERT_NULL(display); + lv_sdl_window_t * dsc = lv_display_get_driver_data(display); + LV_ASSERT_NULL(dsc); + return (int32_t)((float)(display->ver_res) * dsc->zoom); +} + /********************** * STATIC FUNCTIONS **********************/ @@ -247,88 +227,6 @@ static inline int sdl_render_mode(void) return LV_SDL_RENDER_MODE; } -static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map) -{ -#if LV_USE_DRAW_SDL == 0 - lv_sdl_window_t * dsc = lv_display_get_driver_data(disp); - lv_color_format_t cf = lv_display_get_color_format(disp); - uint32_t * argb_px_map = NULL; - - if(sdl_render_mode() == LV_DISPLAY_RENDER_MODE_PARTIAL) { - - if(cf == LV_COLOR_FORMAT_RGB565_SWAPPED) { - uint32_t width = lv_area_get_width(area); - uint32_t height = lv_area_get_height(area); - lv_draw_sw_rgb565_swap(px_map, width * height); - } - /*Update values in a special OLED I1 --> ARGB8888 case - We render everything in I1, but display it in ARGB8888*/ - if(cf == LV_COLOR_FORMAT_I1) { - /*I1 uses 1 bit wide pixels, ARGB8888 uses 4 byte wide pixels*/ - cf = LV_COLOR_FORMAT_ARGB8888; - uint32_t width = lv_area_get_width(area); - uint32_t height = lv_area_get_height(area); - uint32_t argb_px_map_size = width * height * 4; - argb_px_map = malloc(argb_px_map_size); - if(argb_px_map == NULL) { - LV_LOG_ERROR("malloc failed"); - lv_display_flush_ready(disp); - return; - } - /* skip the palette */ - px_map += LV_COLOR_INDEXED_PALETTE_SIZE(LV_COLOR_FORMAT_I1) * 4; - lv_draw_sw_i1_to_argb8888(px_map, argb_px_map, width, height, width / 8, width * 4, 0xFF000000u, 0xFFFFFFFFu); - px_map = (uint8_t *)argb_px_map; - } - - lv_area_t rotated_area = *area; - lv_display_rotate_area(disp, &rotated_area); - - int32_t px_map_w = lv_area_get_width(area); - int32_t px_map_h = lv_area_get_height(area); - uint32_t px_map_stride = lv_draw_buf_width_to_stride(lv_area_get_width(area), cf); - uint32_t px_size = lv_color_format_get_size(cf); - - int32_t fb_stride = lv_draw_buf_width_to_stride(disp->hor_res, cf); - uint8_t * fb_start = dsc->fb_act; - fb_start += rotated_area.y1 * fb_stride + rotated_area.x1 * px_size; - lv_display_rotation_t rotation = lv_display_get_rotation(disp); - - if(rotation == LV_DISPLAY_ROTATION_0) { - uint32_t px_map_line_bytes = lv_area_get_width(area) * px_size; - - int32_t y; - for(y = area->y1; y <= area->y2; y++) { - lv_memcpy(fb_start, px_map, px_map_line_bytes); - px_map += px_map_stride; - fb_start += fb_stride; - } - } - else { - lv_draw_sw_rotate(px_map, fb_start, px_map_w, px_map_h, px_map_stride, fb_stride, rotation, cf); - } - } - - if(lv_display_flush_is_last(disp)) { - if(sdl_render_mode() != LV_DISPLAY_RENDER_MODE_PARTIAL) { - dsc->fb_act = px_map; - } - - window_update(disp); - } - free(argb_px_map); -#else - LV_UNUSED(area); - LV_UNUSED(px_map); - if(lv_display_flush_is_last(disp)) { - window_update(disp); - } -#endif /*LV_USE_DRAW_SDL == 0*/ - - /*IMPORTANT! It must be called to tell the system the flush is ready*/ - lv_display_flush_ready(disp); -} - /** * SDL main thread. All SDL related task have to be handled here! * It initializes SDL, handles drawing and the mouse. @@ -355,7 +253,7 @@ static void sdl_event_handler(lv_timer_t * t) case SDL_WINDOWEVENT_TAKE_FOCUS: #endif case SDL_WINDOWEVENT_EXPOSED: - window_update(disp); + lv_sdl_backend_ops.redraw(disp); break; case SDL_WINDOWEVENT_RESIZED: dsc->ignore_size_chg = 1; @@ -383,12 +281,16 @@ static void sdl_event_handler(lv_timer_t * t) } } -static void window_create(lv_display_t * disp) +static lv_result_t window_create(lv_display_t * disp) { lv_sdl_window_t * dsc = lv_display_get_driver_data(disp); dsc->zoom = 1.0; int flag = 0; +#if LV_SDL_USE_EGL + flag |= SDL_WINDOW_OPENGL; +#endif + #if LV_SDL_FULLSCREEN flag |= SDL_WINDOW_FULLSCREEN; #endif @@ -398,130 +300,35 @@ static void window_create(lv_display_t * disp) dsc->window = SDL_CreateWindow("LVGL Simulator", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, hor_res, ver_res, flag); /*last param. SDL_WINDOW_BORDERLESS to hide borders*/ + if(!dsc->window) { + LV_LOG_ERROR("Failed to create SDL window"); + return LV_RESULT_INVALID; + } + if(lv_sdl_backend_ops.init_display(disp) != LV_RESULT_OK) { + LV_LOG_ERROR("Failed to initialize SDL backend"); + SDL_DestroyWindow(dsc->window); + return LV_RESULT_INVALID; + } - dsc->renderer = SDL_CreateRenderer(dsc->window, -1, - LV_SDL_ACCELERATED ? SDL_RENDERER_ACCELERATED : SDL_RENDERER_SOFTWARE); -#if LV_USE_DRAW_SDL == 0 - texture_resize(disp); - - uint32_t px_size = lv_color_format_get_size(lv_display_get_color_format(disp)); - lv_memset(dsc->fb1, 0xff, hor_res * ver_res * px_size); - if(dsc->fb2) lv_memset(dsc->fb2, 0xff, hor_res * ver_res * px_size); - -#endif /*LV_USE_DRAW_SDL == 0*/ /*Some platforms (e.g. Emscripten) seem to require setting the size again */ SDL_SetWindowSize(dsc->window, hor_res, ver_res); -#if LV_USE_DRAW_SDL == 0 - texture_resize(disp); -#endif /*LV_USE_DRAW_SDL == 0*/ + return LV_RESULT_OK; } -static void window_update(lv_display_t * disp) -{ - lv_sdl_window_t * dsc = lv_display_get_driver_data(disp); -#if LV_USE_DRAW_SDL == 0 - int32_t hor_res = disp->hor_res; - lv_color_format_t cf = lv_display_get_color_format(disp); - if(cf == LV_COLOR_FORMAT_I1) { - cf = LV_COLOR_FORMAT_ARGB8888; - } - uint32_t stride = lv_draw_buf_width_to_stride(hor_res, cf); - SDL_UpdateTexture(dsc->texture, NULL, dsc->fb_act, stride); - - SDL_RenderClear(dsc->renderer); - - /*Update the renderer with the texture containing the rendered image*/ - SDL_RenderCopy(dsc->renderer, dsc->texture, NULL, NULL); -#endif - SDL_RenderPresent(dsc->renderer); -} - -#if LV_USE_DRAW_SDL == 0 -static void texture_resize(lv_display_t * disp) -{ - lv_color_format_t cf = lv_display_get_color_format(disp); - /*In some cases SDL stride might be different than LVGL render stride, like in I1 format. - SDL still uses ARGB8888 as the color format, but LVGL renders in I1, thus causing a mismatch - This ensures correct stride for SDL buffers in this case.*/ - if(cf == LV_COLOR_FORMAT_I1) { - cf = LV_COLOR_FORMAT_ARGB8888; - } - uint32_t stride = lv_draw_buf_width_to_stride(disp->hor_res, cf); - lv_sdl_window_t * dsc = lv_display_get_driver_data(disp); - - dsc->fb1 = sdl_draw_buf_realloc_aligned(dsc->fb1, stride * disp->ver_res); - LV_ASSERT_MALLOC(dsc->fb1); - lv_memzero(dsc->fb1, stride * disp->ver_res); - - if(sdl_render_mode() == LV_DISPLAY_RENDER_MODE_PARTIAL) { - dsc->fb_act = dsc->fb1; - } - else { -#if LV_SDL_BUF_COUNT == 2 - dsc->fb2 = sdl_draw_buf_realloc_aligned(dsc->fb2, stride * disp->ver_res); - memset(dsc->fb2, 0x00, stride * disp->ver_res); -#endif - lv_display_set_buffers(disp, dsc->fb1, dsc->fb2, stride * disp->ver_res, LV_SDL_RENDER_MODE); - } - if(dsc->texture) SDL_DestroyTexture(dsc->texture); - -#if LV_COLOR_DEPTH == 32 || LV_COLOR_DEPTH == 1 - SDL_PixelFormatEnum px_format = - SDL_PIXELFORMAT_RGB888; /*same as SDL_PIXELFORMAT_RGB888, but it's not supported in older versions*/ -#elif LV_COLOR_DEPTH == 24 - SDL_PixelFormatEnum px_format = SDL_PIXELFORMAT_BGR24; -#elif LV_COLOR_DEPTH == 16 - SDL_PixelFormatEnum px_format = SDL_PIXELFORMAT_RGB565; -#else -#error("Unsupported color format") -#endif - - dsc->texture = SDL_CreateTexture(dsc->renderer, px_format, - SDL_TEXTUREACCESS_STATIC, disp->hor_res, disp->ver_res); - SDL_SetTextureBlendMode(dsc->texture, SDL_BLENDMODE_BLEND); -} - -static void * sdl_draw_buf_realloc_aligned(void * ptr, size_t new_size) -{ - if(ptr) { - sdl_draw_buf_free(ptr); - } - - /* No need copy for drawing buffer */ - -#ifndef _WIN32 - /* Size must be multiple of align, See: https://en.cppreference.com/w/c/memory/aligned_alloc */ - -#define BUF_ALIGN (LV_DRAW_BUF_ALIGN < sizeof(void *) ? sizeof(void *) : LV_DRAW_BUF_ALIGN) - return aligned_alloc(BUF_ALIGN, LV_ALIGN_UP(new_size, BUF_ALIGN)); -#else - return _aligned_malloc(LV_ALIGN_UP(new_size, LV_DRAW_BUF_ALIGN), LV_DRAW_BUF_ALIGN); -#endif /* _WIN32 */ -} - -static void sdl_draw_buf_free(void * ptr) -{ -#ifndef _WIN32 - free(ptr); -#else - _aligned_free(ptr); -#endif /* _WIN32 */ -} -#endif - static void res_chg_event_cb(lv_event_t * e) { lv_display_t * disp = lv_event_get_current_target(e); - - lv_sdl_window_t * dsc = lv_display_get_driver_data(disp); - if(dsc->ignore_size_chg == false) { - SDL_SetWindowSize(dsc->window, - (int)((float)(disp->hor_res)*dsc->zoom), (int)((float)(disp->ver_res)*dsc->zoom)); + if(lv_sdl_backend_ops.resize_display(disp) != LV_RESULT_OK) { + LV_LOG_WARN("Failed to resize display"); + return; } -#if LV_USE_DRAW_SDL == 0 - texture_resize(disp); -#endif + lv_sdl_window_t * dsc = lv_display_get_driver_data(disp); + if(!dsc->ignore_size_chg) { + SDL_SetWindowSize(dsc->window, + lv_sdl_window_get_horizontal_resolution(disp), + lv_sdl_window_get_vertical_resolution(disp)); + } } static void release_disp_cb(lv_event_t * e) @@ -529,21 +336,11 @@ static void release_disp_cb(lv_event_t * e) if(lv_deinit_in_progress) { lv_sdl_quit(); } - lv_display_t * disp = (lv_display_t *) lv_event_get_user_data(e); - lv_sdl_window_t * dsc = lv_display_get_driver_data(disp); -#if LV_USE_DRAW_SDL == 0 - SDL_DestroyTexture(dsc->texture); -#endif - SDL_DestroyRenderer(dsc->renderer); + + lv_sdl_backend_ops.deinit_display(disp); SDL_DestroyWindow(dsc->window); -#if LV_USE_DRAW_SDL == 0 - if(dsc->fb1) sdl_draw_buf_free(dsc->fb1); - if(dsc->fb2) sdl_draw_buf_free(dsc->fb2); - if(dsc->buf1) sdl_draw_buf_free(dsc->buf1); - if(dsc->buf2) sdl_draw_buf_free(dsc->buf2); -#endif lv_free(dsc); lv_display_set_driver_data(disp, NULL); } diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 414c3eec4f..b109f66d38 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -4901,14 +4901,21 @@ LV_EXPORT_CONST_INT(LV_DRAW_BUF_ALIGN); #endif #endif +#if LV_USE_SDL && LV_USE_OPENGLES && (LV_USE_DRAW_OPENGLES || LV_USE_DRAW_NANOVG) + #define LV_SDL_USE_EGL 1 +#else + #define LV_SDL_USE_EGL 0 +#endif + #ifndef LV_USE_EGL - #if LV_LINUX_DRM_USE_EGL || LV_WAYLAND_USE_EGL + #if LV_LINUX_DRM_USE_EGL || LV_WAYLAND_USE_EGL || LV_SDL_USE_EGL #define LV_USE_EGL 1 #else #define LV_USE_EGL 0 #endif #endif /* LV_USE_EGL */ + #if LV_USE_OS #if (LV_USE_FREETYPE || LV_USE_THORVG) && LV_DRAW_THREAD_STACK_SIZE < (32 * 1024) #error "Increase LV_DRAW_THREAD_STACK_SIZE to at least 32KB for FreeType or ThorVG."