modules: lvgl: allow offloading rendering process to background thread

Enable offloading of display_write call to background thread for color
displays. This feature is opt-in, as it may offer significant
performance gains for every display pipeline.

When enabled display_write and lv_disp_flush_ready will be called from a
background thread. This means that while the display driver waits on the
hardware to render the framebuffer, the LVGL rendering thread will not
be blocked.

Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
This commit is contained in:
Daniel DeGrasse 2023-08-16 11:01:32 -05:00 committed by Carles Cufí
parent 82bfb26446
commit da59df2905
6 changed files with 131 additions and 27 deletions

View file

@ -1,4 +1,5 @@
# Copyright (c) 2023 Fabian Blatz <fabianblatz@gmail.com>
# Copyright 2023 NXP
# SPDX-License-Identifier: Apache-2.0
config ZEPHYR_LVGL_MODULE
@ -53,6 +54,29 @@ endchoice
config LV_COLOR_16_SWAP
bool
config LV_Z_FLUSH_THREAD
bool "Flush LVGL frames in a separate thread"
help
Flush LVGL frames in a separate thread, while the primary thread
renders the next LVGL frame. Can be disabled if the performance
gain this approach offers is not required
if LV_Z_FLUSH_THREAD
config LV_Z_FLUSH_THREAD_STACK_SIZE
int "Stack size for flushing thread"
default 1024
help
Stack size for LVGL flush thread, which will call display_write
config LV_Z_FLUSH_THREAD_PRIO
int "LVGL flush thread priority"
default 0
help
Cooperative priority of LVGL flush thread.
endif # LV_Z_FLUSH_THREAD
rsource "Kconfig.memory"
rsource "Kconfig.input"
rsource "Kconfig.shell"

View file

@ -1,18 +1,59 @@
/*
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <errno.h>
#include "lvgl_display.h"
#ifdef CONFIG_LV_Z_FLUSH_THREAD
K_SEM_DEFINE(flush_complete, 0, 1);
/* Message queue will only ever need to queue one message */
K_MSGQ_DEFINE(flush_queue, sizeof(struct lvgl_display_flush), 1, 1);
void lvgl_flush_thread_entry(void *arg1, void *arg2, void *arg3)
{
struct lvgl_display_flush flush;
struct lvgl_disp_data *data;
while (1) {
k_msgq_get(&flush_queue, &flush, K_FOREVER);
data = (struct lvgl_disp_data *)flush.disp_drv->user_data;
display_write(data->display_dev, flush.x, flush.y, &flush.desc,
flush.buf);
lv_disp_flush_ready(flush.disp_drv);
k_sem_give(&flush_complete);
}
}
K_THREAD_DEFINE(lvgl_flush_thread, CONFIG_LV_Z_FLUSH_THREAD_STACK_SIZE,
lvgl_flush_thread_entry, NULL, NULL, NULL,
K_PRIO_COOP(CONFIG_LV_Z_FLUSH_THREAD_PRIO), 0, 0);
void lvgl_wait_cb(lv_disp_drv_t *disp_drv)
{
k_sem_take(&flush_complete, K_FOREVER);
}
#endif /* CONFIG_LV_Z_FLUSH_THREAD */
int set_lvgl_rendering_cb(lv_disp_drv_t *disp_drv)
{
int err = 0;
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_drv->user_data;
#ifdef CONFIG_LV_Z_FLUSH_THREAD
disp_drv->wait_cb = lvgl_wait_cb;
#endif
switch (data->cap.current_pixel_format) {
case PIXEL_FORMAT_ARGB_8888:
disp_drv->flush_cb = lvgl_flush_cb_32bit;
@ -54,3 +95,25 @@ int set_lvgl_rendering_cb(lv_disp_drv_t *disp_drv)
return err;
}
void lvgl_flush_display(struct lvgl_display_flush *request)
{
#ifdef CONFIG_LV_Z_FLUSH_THREAD
/*
* LVGL will only start a flush once the previous one is complete,
* so we can reset the flush state semaphore here.
*/
k_sem_reset(&flush_complete);
k_msgq_put(&flush_queue, request, K_FOREVER);
/* Explicitly yield, in case the calling thread is a cooperative one */
k_yield();
#else
/* Write directly to the display */
struct lvgl_disp_data *data =
(struct lvgl_disp_data *)request->disp_drv->user_data;
display_write(data->display_dev, request->x, request->y,
&request->desc, request->buf);
lv_disp_flush_ready(request->disp_drv);
#endif
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -20,6 +21,14 @@ struct lvgl_disp_data {
bool blanking_on;
};
struct lvgl_display_flush {
lv_disp_drv_t *disp_drv;
uint16_t x;
uint16_t y;
struct display_buffer_descriptor desc;
void *buf;
};
void lvgl_flush_cb_mono(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
void lvgl_flush_cb_16bit(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
void lvgl_flush_cb_24bit(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
@ -38,6 +47,8 @@ void lvgl_rounder_cb_mono(lv_disp_drv_t *disp_drv, lv_area_t *area);
int set_lvgl_rendering_cb(lv_disp_drv_t *disp_drv);
void lvgl_flush_display(struct lvgl_display_flush *request);
#ifdef __cplusplus
}
#endif

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -10,18 +11,19 @@
void lvgl_flush_cb_16bit(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_drv->user_data;
uint16_t w = area->x2 - area->x1 + 1;
uint16_t h = area->y2 - area->y1 + 1;
struct display_buffer_descriptor desc;
struct lvgl_display_flush flush;
desc.buf_size = w * 2U * h;
desc.width = w;
desc.pitch = w;
desc.height = h;
display_write(data->display_dev, area->x1, area->y1, &desc, (void *)color_p);
lv_disp_flush_ready(disp_drv);
flush.disp_drv = disp_drv;
flush.x = area->x1;
flush.y = area->y1;
flush.desc.buf_size = w * 2U * h;
flush.desc.width = w;
flush.desc.pitch = w;
flush.desc.height = h;
flush.buf = (void *)color_p;
lvgl_flush_display(&flush);
}
#ifndef CONFIG_LV_COLOR_DEPTH_16

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -10,18 +11,19 @@
void lvgl_flush_cb_24bit(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_drv->user_data;
uint16_t w = area->x2 - area->x1 + 1;
uint16_t h = area->y2 - area->y1 + 1;
struct display_buffer_descriptor desc;
struct lvgl_display_flush flush;
desc.buf_size = w * 3U * h;
desc.width = w;
desc.pitch = w;
desc.height = h;
display_write(data->display_dev, area->x1, area->y1, &desc, (void *)color_p);
lv_disp_flush_ready(disp_drv);
flush.disp_drv = disp_drv;
flush.x = area->x1;
flush.y = area->y1;
flush.desc.buf_size = w * 3U * h;
flush.desc.width = w;
flush.desc.pitch = w;
flush.desc.height = h;
flush.buf = (void *)color_p;
lvgl_flush_display(&flush);
}
void lvgl_set_px_cb_24bit(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x,

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -10,18 +11,19 @@
void lvgl_flush_cb_32bit(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_drv->user_data;
uint16_t w = area->x2 - area->x1 + 1;
uint16_t h = area->y2 - area->y1 + 1;
struct display_buffer_descriptor desc;
struct lvgl_display_flush flush;
desc.buf_size = w * 4U * h;
desc.width = w;
desc.pitch = w;
desc.height = h;
display_write(data->display_dev, area->x1, area->y1, &desc, (void *)color_p);
lv_disp_flush_ready(disp_drv);
flush.disp_drv = disp_drv;
flush.x = area->x1;
flush.y = area->y1;
flush.desc.buf_size = w * 4U * h;
flush.desc.width = w;
flush.desc.pitch = w;
flush.desc.height = h;
flush.buf = (void *)color_p;
lvgl_flush_display(&flush);
}
#ifndef CONFIG_LV_COLOR_DEPTH_32