feat(sdl): add EGL support (#9462)

This commit is contained in:
André Costa
2026-01-26 18:38:48 +01:00
committed by GitHub
parent bcfba6b442
commit aac49f630b
13 changed files with 1096 additions and 305 deletions
+1 -1
View File
@@ -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
@@ -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 <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
@@ -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 <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.
@@ -7,6 +7,8 @@ Running under Embedded Linux
.. toctree::
:maxdepth: 2
draw_opengl
draw_sdl
overview
opengl
nanovg
+11 -24
View File
@@ -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 <opengles_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 <sdl_driver>`
- :ref:`Wayland driver <wayland_driver>`
- :ref:`DRM driver <linux_drm>`
- :ref:`GLFW driver <glfw_driver>`
For complete implementation details, see :ref:`GLFW driver <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 <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 <opengl_driver>`.
+70
View File
@@ -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 <nanovg_draw_unit>`
- :ref:`OpenGL Draw Unit <opengles_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 <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
+8 -1
View File
@@ -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."
+265
View File
@@ -0,0 +1,265 @@
/**
* @file lv_sdl_egl.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_SDL_USE_EGL
#include <SDL2/SDL_syswm.h>
#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*/
+48
View File
@@ -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
**********************/
+342
View File
@@ -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 <stdlib.h>
#else
#include <malloc.h>
#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*/
+136
View File
@@ -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*/
+75 -278
View File
@@ -14,21 +14,9 @@
#if LV_USE_SDL
#include <stdbool.h>
#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 <stdlib.h>
#else
#include <malloc.h>
#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);
}
+8 -1
View File
@@ -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."