Files
lvgl/src/draw/nxp/vglite/lv_draw_vglite.c
nicusorcitu 2b33f8b0a7 perf(nxp): v9.1.0 upstream vglite optimization (#6646)
Signed-off-by: Nicușor Cîțu <nicusor.citu@nxp.com>
Signed-off-by: Ana Grad <ana.grad@nxp.com>
Signed-off-by: Cosmin-Daniel Radu <cosmin.radu_1@nxp.com>
Signed-off-by: Cristian Stoica <cristianmarian.stoica@nxp.com>
Co-authored-by: Ana Grad <ana.grad@nxp.com>
Co-authored-by: Cristian Stoica <cristianmarian.stoica@nxp.com>
2024-08-26 20:11:26 +02:00

594 lines
17 KiB
C

/**
* @file lv_draw_vglite.c
*
*/
/**
* Copyright 2023-2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_vglite.h"
#if LV_USE_DRAW_VGLITE
#include "lv_vglite_buf.h"
#include "lv_vglite_utils.h"
#include "../../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define DRAW_UNIT_ID_VGLITE 2
#if LV_USE_VGLITE_DRAW_ASYNC
#define VGLITE_TASK_BUF_SIZE 100
#endif
/**********************
* TYPEDEFS
**********************/
#if LV_USE_VGLITE_DRAW_ASYNC
/**
* Structure of pending vglite draw task
*/
typedef struct _vglite_draw_task_t {
lv_draw_task_t * task;
bool flushed;
} vglite_draw_tasks_t;
#endif
/**********************
* STATIC PROTOTYPES
**********************/
/*
* Evaluate a task and set the score and preferred VGLite draw unit.
* Return 1 if task is preferred, 0 otherwise (task is not supported).
*/
static int32_t _vglite_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
/*
* Dispatch (assign) a task to VGLite draw unit (itself).
* Return 1 if task was dispatched, 0 otherwise (task not supported).
*/
static int32_t _vglite_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
/*
* Wait for VG-Lite draw unit to finish.
*/
#if LV_USE_VGLITE_DRAW_ASYNC
static int32_t _vglite_wait_for_finish(lv_draw_unit_t * draw_unit);
#endif
/*
* Delete the VGLite draw unit.
*/
static int32_t _vglite_delete(lv_draw_unit_t * draw_unit);
#if LV_USE_VGLITE_DRAW_THREAD
static void _vglite_render_thread_cb(void * ptr);
#endif
static void _vglite_execute_drawing(lv_draw_vglite_unit_t * u);
/**********************
* STATIC VARIABLES
**********************/
#define _draw_info LV_GLOBAL_DEFAULT()->draw_info
#if LV_USE_VGLITE_DRAW_ASYNC
/*
* Circular buffer to hold the queued and the flushed tasks.
* Two indexes, _head and _tail, are used to signal the beginning
* and the end of the valid tasks that are pending.
*/
static vglite_draw_tasks_t _draw_task_buf[VGLITE_TASK_BUF_SIZE];
static volatile int _head = 0;
static volatile int _tail = 0;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_vglite_init(void)
{
lv_draw_buf_vglite_init_handlers();
lv_draw_vglite_unit_t * draw_vglite_unit = lv_draw_create_unit(sizeof(lv_draw_vglite_unit_t));
draw_vglite_unit->base_unit.evaluate_cb = _vglite_evaluate;
draw_vglite_unit->base_unit.dispatch_cb = _vglite_dispatch;
#if LV_USE_VGLITE_DRAW_ASYNC
draw_vglite_unit->base_unit.wait_for_finish_cb = _vglite_wait_for_finish;
#endif
draw_vglite_unit->base_unit.delete_cb = _vglite_delete;
#if LV_USE_VGLITE_DRAW_THREAD
lv_thread_init(&draw_vglite_unit->thread, LV_THREAD_PRIO_HIGH, _vglite_render_thread_cb, 2 * 1024, draw_vglite_unit);
#endif
}
void lv_draw_vglite_deinit(void)
{
}
/**********************
* STATIC FUNCTIONS
**********************/
static inline bool _vglite_src_cf_supported(lv_color_format_t cf)
{
bool is_cf_supported = false;
switch(cf) {
#if CHIPID == 0x255 || CHIPID == 0x555
case LV_COLOR_FORMAT_I1:
case LV_COLOR_FORMAT_I2:
case LV_COLOR_FORMAT_I4:
case LV_COLOR_FORMAT_I8:
#endif
case LV_COLOR_FORMAT_A4:
case LV_COLOR_FORMAT_A8:
case LV_COLOR_FORMAT_L8:
case LV_COLOR_FORMAT_RGB565:
#if CHIPID == 0x555
case LV_COLOR_FORMAT_RGB565A8:
case LV_COLOR_FORMAT_RGB888:
#endif
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
is_cf_supported = true;
break;
default:
break;
}
return is_cf_supported;
}
static inline bool _vglite_dest_cf_supported(lv_color_format_t cf)
{
bool is_cf_supported = false;
switch(cf) {
case LV_COLOR_FORMAT_A8:
#if CHIPID == 0x255 || CHIPID == 0x555
case LV_COLOR_FORMAT_L8:
#endif
case LV_COLOR_FORMAT_RGB565:
#if CHIPID == 0x555
case LV_COLOR_FORMAT_RGB565A8:
case LV_COLOR_FORMAT_RGB888:
#endif
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
is_cf_supported = true;
break;
default:
break;
}
return is_cf_supported;
}
static int32_t _vglite_evaluate(lv_draw_unit_t * u, lv_draw_task_t * t)
{
LV_UNUSED(u);
const lv_draw_dsc_base_t * draw_dsc_base = (lv_draw_dsc_base_t *) t->draw_dsc;
if(!_vglite_dest_cf_supported(draw_dsc_base->layer->color_format))
return 0;
switch(t->type) {
case LV_DRAW_TASK_TYPE_FILL:
if(t->preference_score > 80) {
t->preference_score = 80;
t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE;
}
return 1;
case LV_DRAW_TASK_TYPE_LINE:
case LV_DRAW_TASK_TYPE_ARC:
case LV_DRAW_TASK_TYPE_TRIANGLE:
if(t->preference_score > 90) {
t->preference_score = 90;
t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE;
}
return 1;
case LV_DRAW_TASK_TYPE_LABEL:
if(t->preference_score > 95) {
t->preference_score = 95;
t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE;
}
return 1;
case LV_DRAW_TASK_TYPE_BORDER: {
if(t->preference_score > 90) {
t->preference_score = 90;
t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE;
}
return 1;
}
case LV_DRAW_TASK_TYPE_LAYER: {
const lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc;
lv_layer_t * layer_to_draw = (lv_layer_t *)draw_dsc->src;
#if LV_USE_VGLITE_BLIT_SPLIT
bool has_transform = (draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE ||
draw_dsc->scale_y != LV_SCALE_NONE);
#endif
if(!_vglite_src_cf_supported(layer_to_draw->color_format)
#if LV_USE_VGLITE_BLIT_SPLIT
|| has_transform
#endif
)
return 0;
if(t->preference_score > 80) {
t->preference_score = 80;
t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE;
}
return 1;
}
case LV_DRAW_TASK_TYPE_IMAGE: {
lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc;
const lv_image_dsc_t * img_dsc = draw_dsc->src;
#if LV_USE_VGLITE_BLIT_SPLIT
bool has_transform = (draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE ||
draw_dsc->scale_y != LV_SCALE_NONE);
#endif
if((!_vglite_src_cf_supported(img_dsc->header.cf))
#if LV_USE_VGLITE_BLIT_SPLIT
|| has_transform
#endif
|| (!vglite_src_buf_aligned(img_dsc->data, img_dsc->header.stride, img_dsc->header.cf))
)
return 0;
if(t->preference_score > 80) {
t->preference_score = 80;
t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE;
}
return 1;
}
default:
return 0;
}
return 0;
}
static int32_t _vglite_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
{
lv_draw_vglite_unit_t * draw_vglite_unit = (lv_draw_vglite_unit_t *) draw_unit;
/* Return immediately if it's busy with draw task. */
if(draw_vglite_unit->task_act)
return 0;
/* Try to get an ready to draw. */
lv_draw_task_t * t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_VGLITE);
if(t == NULL)
return LV_DRAW_UNIT_IDLE;
if(lv_draw_get_unit_count() > 1) {
/* Let the SW unit to draw this task. */
if(t->preferred_draw_unit_id != DRAW_UNIT_ID_VGLITE)
return LV_DRAW_UNIT_IDLE;
}
else {
/* Fake unsupported tasks as ready. */
if(t->preferred_draw_unit_id != DRAW_UNIT_ID_VGLITE) {
t->state = LV_DRAW_TASK_STATE_READY;
/* Request a new dispatching as it can get a new task. */
lv_draw_dispatch_request();
return 1;
}
}
void * buf = lv_draw_layer_alloc_buf(layer);
if(buf == NULL)
return LV_DRAW_UNIT_IDLE;
t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
draw_vglite_unit->base_unit.target_layer = layer;
draw_vglite_unit->base_unit.clip_area = &t->clip_area;
draw_vglite_unit->task_act = t;
#if LV_USE_VGLITE_DRAW_THREAD
/* Let the render thread work. */
if(draw_vglite_unit->inited)
lv_thread_sync_signal(&draw_vglite_unit->sync);
#else
_vglite_execute_drawing(draw_vglite_unit);
draw_vglite_unit->task_act->state = LV_DRAW_TASK_STATE_READY;
draw_vglite_unit->task_act = NULL;
/* The draw unit is free now. Request a new dispatching as it can get a new task. */
lv_draw_dispatch_request();
#endif
return 1;
}
#if LV_USE_VGLITE_DRAW_ASYNC
static int32_t _vglite_wait_for_finish(lv_draw_unit_t * draw_unit)
{
lv_draw_vglite_unit_t * draw_vglite_unit = (lv_draw_vglite_unit_t *) draw_unit;
draw_vglite_unit->wait_for_finish = true;
if(draw_vglite_unit->inited)
lv_thread_sync_signal(&draw_vglite_unit->sync);
return 1;
}
#endif
static int32_t _vglite_delete(lv_draw_unit_t * draw_unit)
{
#if LV_USE_VGLITE_DRAW_THREAD
lv_draw_vglite_unit_t * draw_vglite_unit = (lv_draw_vglite_unit_t *) draw_unit;
LV_LOG_INFO("Cancel VGLite draw thread.");
draw_vglite_unit->exit_status = true;
if(draw_vglite_unit->inited)
lv_thread_sync_signal(&draw_vglite_unit->sync);
lv_result_t res = lv_thread_delete(&draw_vglite_unit->thread);
return res;
#else
LV_UNUSED(draw_unit);
return 1;
#endif
}
static void _vglite_execute_drawing(lv_draw_vglite_unit_t * u)
{
lv_draw_task_t * t = u->task_act;
lv_draw_unit_t * draw_unit = (lv_draw_unit_t *)u;
lv_layer_t * layer = draw_unit->target_layer;
lv_draw_buf_t * draw_buf = layer->draw_buf;
/* Set target buffer */
vglite_set_dest_buf(draw_buf->data, draw_buf->header.w, draw_buf->header.h, draw_buf->header.stride,
draw_buf->header.cf);
lv_area_t clip_area;
lv_area_copy(&clip_area, draw_unit->clip_area);
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t draw_area;
lv_area_copy(&draw_area, &t->area);
lv_area_move(&draw_area, -layer->buf_area.x1, -layer->buf_area.y1);
if(!lv_area_intersect(&draw_area, &draw_area, &clip_area))
return; /*Fully clipped, nothing to do*/
if(_draw_info.unit_cnt > 1)
lv_draw_buf_invalidate_cache(draw_buf, &draw_area);
/* Set scissor area, excluding the split blit case */
#if LV_USE_VGLITE_BLIT_SPLIT
if(t->type != LV_DRAW_TASK_TYPE_IMAGE || t->type != LV_DRAW_TASK_TYPE_LAYER)
#endif
vglite_set_scissor(&clip_area);
switch(t->type) {
case LV_DRAW_TASK_TYPE_LABEL:
lv_draw_vglite_label(draw_unit, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_FILL:
lv_draw_vglite_fill(draw_unit, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_BORDER:
lv_draw_vglite_border(draw_unit, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_IMAGE:
lv_draw_vglite_img(draw_unit, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_ARC:
lv_draw_vglite_arc(draw_unit, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_LINE:
lv_draw_vglite_line(draw_unit, t->draw_dsc);
break;
case LV_DRAW_TASK_TYPE_LAYER:
lv_draw_vglite_layer(draw_unit, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_TRIANGLE:
lv_draw_vglite_triangle(draw_unit, t->draw_dsc);
break;
default:
break;
}
/* Disable scissor */
vglite_set_scissor(&layer->buf_area);
#if LV_USE_PARALLEL_DRAW_DEBUG
/*Layers manage it for themselves*/
if(t->type != LV_DRAW_TASK_TYPE_LAYER) {
lv_area_t draw_area;
if(!lv_area_intersect(&draw_area, &t->area, u->base_unit.clip_area))
return;
int32_t idx = 0;
lv_draw_unit_t * draw_unit_tmp = _draw_info.unit_head;
while(draw_unit_tmp != (lv_draw_unit_t *)u) {
draw_unit_tmp = draw_unit_tmp->next;
idx++;
}
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_color = lv_palette_main(idx % LV_PALETTE_LAST);
rect_dsc.border_color = rect_dsc.bg_color;
rect_dsc.bg_opa = LV_OPA_10;
rect_dsc.border_opa = LV_OPA_80;
rect_dsc.border_width = 1;
lv_draw_sw_fill((lv_draw_unit_t *)u, &rect_dsc, &draw_area);
lv_point_t txt_size;
lv_text_get_size(&txt_size, "W", LV_FONT_DEFAULT, 0, 0, 100, LV_TEXT_FLAG_NONE);
lv_area_t txt_area;
txt_area.x1 = draw_area.x1;
txt_area.y1 = draw_area.y1;
txt_area.x2 = draw_area.x1 + txt_size.x - 1;
txt_area.y2 = draw_area.y1 + txt_size.y - 1;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_color = lv_color_white();
lv_draw_sw_fill((lv_draw_unit_t *)u, &rect_dsc, &txt_area);
char buf[8];
lv_snprintf(buf, sizeof(buf), "%d", idx);
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
label_dsc.color = lv_color_black();
label_dsc.text = buf;
lv_draw_sw_label((lv_draw_unit_t *)u, &label_dsc, &txt_area);
}
#endif
}
#if LV_USE_VGLITE_DRAW_ASYNC
static inline void _vglite_queue_task(lv_draw_task_t * task)
{
VGLITE_ASSERT_MSG(((_tail + 1) % VGLITE_TASK_BUF_SIZE) != _head, "VGLite task buffer full.");
_draw_task_buf[_tail].task = task;
_draw_task_buf[_tail].flushed = false;
_tail = (_tail + 1) % VGLITE_TASK_BUF_SIZE;
}
static inline void _vglite_signal_task_ready(lv_draw_task_t * task)
{
/* Signal the ready state to dispatcher. */
task->state = LV_DRAW_TASK_STATE_READY;
_head = (_head + 1) % VGLITE_TASK_BUF_SIZE;
/* No need to cleanup the tasks in buffer as we advance with the _head. */
}
static inline void _vglite_signal_all_task_ready(void)
{
int end = (_head <= _tail) ? _tail : _tail + VGLITE_TASK_BUF_SIZE;
for(int i = _head; i < end; i++) {
lv_draw_task_t * task = _draw_task_buf[i % VGLITE_TASK_BUF_SIZE].task;
_vglite_signal_task_ready(task);
}
}
static inline void _vglite_signal_flushed_task_ready(void)
{
if(vglite_cmd_buf_is_flushed()) {
int end = (_head <= _tail) ? _tail : _tail + VGLITE_TASK_BUF_SIZE;
for(int i = _head; i < end; i++) {
if(_draw_task_buf[i % VGLITE_TASK_BUF_SIZE].flushed) {
lv_draw_task_t * task = _draw_task_buf[i % VGLITE_TASK_BUF_SIZE].task;
_vglite_signal_task_ready(task);
}
else {
/* Those tasks have been flushed now. */
_draw_task_buf[i % VGLITE_TASK_BUF_SIZE].flushed = true;
}
}
}
}
#endif
#if LV_USE_VGLITE_DRAW_THREAD
static void _vglite_render_thread_cb(void * ptr)
{
lv_draw_vglite_unit_t * u = ptr;
lv_thread_sync_init(&u->sync);
u->inited = true;
while(1) {
/* Wait for sync if there is no task set. */
while(u->task_act == NULL
#if LV_USE_VGLITE_DRAW_ASYNC
/*
* Wait for sync if _draw_task_buf is empty.
* The thread will have to run to complete any pending tasks.
*/
&& !u->wait_for_finish
#endif
) {
if(u->exit_status)
break;
lv_thread_sync_wait(&u->sync);
}
if(u->exit_status) {
LV_LOG_INFO("Ready to exit VGLite draw thread.");
break;
}
if(u->task_act) {
#if LV_USE_VGLITE_DRAW_ASYNC
_vglite_queue_task((void *)u->task_act);
#endif
_vglite_execute_drawing(u);
}
#if LV_USE_VGLITE_DRAW_ASYNC
if(u->wait_for_finish) {
u->wait_for_finish = false;
vglite_wait_for_finish();
_vglite_signal_all_task_ready();
}
else { /* u->task_act */
_vglite_signal_flushed_task_ready();
}
#else
/* Signal the ready state to dispatcher. */
u->task_act->state = LV_DRAW_TASK_STATE_READY;
#endif
/* Cleanup. */
u->task_act = NULL;
/* The draw unit is free now. Request a new dispatching as it can get a new task. */
lv_draw_dispatch_request();
}
u->inited = false;
lv_thread_sync_delete(&u->sync);
LV_LOG_INFO("Exit VGLite draw thread.");
}
#endif
#endif /*LV_USE_DRAW_VGLITE*/