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:
parent
82bfb26446
commit
da59df2905
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue