1 /**
2  * @file lv_draw_vglite.c
3  *
4  */
5 
6 /**
7  * Copyright 2023-2024 NXP
8  *
9  * SPDX-License-Identifier: MIT
10  */
11 
12 /*********************
13  *      INCLUDES
14  *********************/
15 
16 #include "lv_draw_vglite.h"
17 
18 #if LV_USE_DRAW_VGLITE
19 #include "lv_vglite_buf.h"
20 #include "lv_vglite_utils.h"
21 
22 #include "../../../core/lv_global.h"
23 
24 /*********************
25  *      DEFINES
26  *********************/
27 
28 #define DRAW_UNIT_ID_VGLITE 2
29 
30 #if LV_USE_VGLITE_DRAW_ASYNC
31     #define VGLITE_TASK_BUF_SIZE 100
32 #endif
33 
34 /**********************
35  *      TYPEDEFS
36  **********************/
37 
38 #if LV_USE_VGLITE_DRAW_ASYNC
39 /**
40  * Structure of pending vglite draw task
41  */
42 typedef struct _vglite_draw_task_t {
43     lv_draw_task_t * task;
44     bool flushed;
45 } vglite_draw_tasks_t;
46 #endif
47 
48 /**********************
49  *  STATIC PROTOTYPES
50  **********************/
51 
52 /*
53  * Evaluate a task and set the score and preferred VGLite draw unit.
54  * Return 1 if task is preferred, 0 otherwise (task is not supported).
55  */
56 static int32_t _vglite_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
57 
58 /*
59  * Dispatch (assign) a task to VGLite draw unit (itself).
60  * Return 1 if task was dispatched, 0 otherwise (task not supported).
61  */
62 static int32_t _vglite_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
63 
64 /*
65  * Wait for VG-Lite draw unit to finish.
66  */
67 #if LV_USE_VGLITE_DRAW_ASYNC
68     static int32_t _vglite_wait_for_finish(lv_draw_unit_t * draw_unit);
69 #endif
70 
71 /*
72  * Delete the VGLite draw unit.
73  */
74 static int32_t _vglite_delete(lv_draw_unit_t * draw_unit);
75 
76 #if LV_USE_VGLITE_DRAW_THREAD
77     static void _vglite_render_thread_cb(void * ptr);
78 #endif
79 
80 static void _vglite_execute_drawing(lv_draw_vglite_unit_t * u);
81 
82 /**********************
83  *  STATIC VARIABLES
84  **********************/
85 
86 #define _draw_info LV_GLOBAL_DEFAULT()->draw_info
87 
88 #if LV_USE_VGLITE_DRAW_ASYNC
89     /*
90     * Circular buffer to hold the queued and the flushed tasks.
91     * Two indexes, _head and _tail, are used to signal the beginning
92     * and the end of the valid tasks that are pending.
93     */
94     static vglite_draw_tasks_t _draw_task_buf[VGLITE_TASK_BUF_SIZE];
95     static volatile int _head = 0;
96     static volatile int _tail = 0;
97 #endif
98 
99 /**********************
100  *      MACROS
101  **********************/
102 
103 /**********************
104  *   GLOBAL FUNCTIONS
105  **********************/
106 
lv_draw_vglite_init(void)107 void lv_draw_vglite_init(void)
108 {
109     lv_draw_buf_vglite_init_handlers();
110 
111     lv_draw_vglite_unit_t * draw_vglite_unit = lv_draw_create_unit(sizeof(lv_draw_vglite_unit_t));
112     draw_vglite_unit->base_unit.evaluate_cb = _vglite_evaluate;
113     draw_vglite_unit->base_unit.dispatch_cb = _vglite_dispatch;
114 #if LV_USE_VGLITE_DRAW_ASYNC
115     draw_vglite_unit->base_unit.wait_for_finish_cb = _vglite_wait_for_finish;
116 #endif
117     draw_vglite_unit->base_unit.delete_cb = _vglite_delete;
118     draw_vglite_unit->base_unit.name = "NXP_VGLITE";
119 
120 #if LV_USE_VGLITE_DRAW_THREAD
121     lv_thread_init(&draw_vglite_unit->thread, "vglitedraw", LV_THREAD_PRIO_HIGHEST, _vglite_render_thread_cb, 2 * 1024,
122                    draw_vglite_unit);
123 #endif
124 }
125 
lv_draw_vglite_deinit(void)126 void lv_draw_vglite_deinit(void)
127 {
128 }
129 
130 /**********************
131  *   STATIC FUNCTIONS
132  **********************/
133 
_vglite_src_cf_supported(lv_color_format_t cf)134 static inline bool _vglite_src_cf_supported(lv_color_format_t cf)
135 {
136     bool is_cf_supported = false;
137 
138     switch(cf) {
139 #if CHIPID == 0x255 || CHIPID == 0x555
140         case LV_COLOR_FORMAT_I1:
141         case LV_COLOR_FORMAT_I2:
142         case LV_COLOR_FORMAT_I4:
143         case LV_COLOR_FORMAT_I8:
144 #endif
145         case LV_COLOR_FORMAT_A4:
146         case LV_COLOR_FORMAT_A8:
147         case LV_COLOR_FORMAT_L8:
148         case LV_COLOR_FORMAT_RGB565:
149 #if CHIPID == 0x555
150         case LV_COLOR_FORMAT_RGB565A8:
151         case LV_COLOR_FORMAT_RGB888:
152 #endif
153         case LV_COLOR_FORMAT_ARGB8888:
154         case LV_COLOR_FORMAT_XRGB8888:
155             is_cf_supported = true;
156             break;
157         default:
158             break;
159     }
160 
161     return is_cf_supported;
162 }
163 
_vglite_dest_cf_supported(lv_color_format_t cf)164 static inline bool _vglite_dest_cf_supported(lv_color_format_t cf)
165 {
166     bool is_cf_supported = false;
167 
168     switch(cf) {
169         case LV_COLOR_FORMAT_A8:
170 #if CHIPID == 0x255 || CHIPID == 0x555
171         case LV_COLOR_FORMAT_L8:
172 #endif
173         case LV_COLOR_FORMAT_RGB565:
174 #if CHIPID == 0x555
175         case LV_COLOR_FORMAT_RGB565A8:
176         case LV_COLOR_FORMAT_RGB888:
177 #endif
178         case LV_COLOR_FORMAT_ARGB8888:
179         case LV_COLOR_FORMAT_XRGB8888:
180             is_cf_supported = true;
181             break;
182         default:
183             break;
184     }
185 
186     return is_cf_supported;
187 }
188 
_vglite_evaluate(lv_draw_unit_t * u,lv_draw_task_t * t)189 static int32_t _vglite_evaluate(lv_draw_unit_t * u, lv_draw_task_t * t)
190 {
191     LV_UNUSED(u);
192 
193     const lv_draw_dsc_base_t * draw_dsc_base = (lv_draw_dsc_base_t *) t->draw_dsc;
194 
195     if(!_vglite_dest_cf_supported(draw_dsc_base->layer->color_format))
196         return 0;
197 
198     switch(t->type) {
199         case LV_DRAW_TASK_TYPE_FILL:
200             if(t->preference_score > 80) {
201                 t->preference_score = 80;
202                 t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE;
203             }
204             return 1;
205 
206         case LV_DRAW_TASK_TYPE_LINE:
207         case LV_DRAW_TASK_TYPE_ARC:
208         case LV_DRAW_TASK_TYPE_TRIANGLE:
209             if(t->preference_score > 90) {
210                 t->preference_score = 90;
211                 t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE;
212             }
213             return 1;
214 
215         case LV_DRAW_TASK_TYPE_LABEL:
216             if(t->preference_score > 95) {
217                 t->preference_score = 95;
218                 t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE;
219             }
220             return 1;
221 
222         case LV_DRAW_TASK_TYPE_BORDER: {
223                 if(t->preference_score > 90) {
224                     t->preference_score = 90;
225                     t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE;
226                 }
227                 return 1;
228             }
229 
230         case LV_DRAW_TASK_TYPE_LAYER: {
231                 const lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc;
232                 lv_layer_t * layer_to_draw = (lv_layer_t *)draw_dsc->src;
233 
234 #if LV_USE_VGLITE_BLIT_SPLIT
235                 bool has_transform = (draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE ||
236                                       draw_dsc->scale_y != LV_SCALE_NONE);
237 #endif
238                 if(!_vglite_src_cf_supported(layer_to_draw->color_format)
239 #if LV_USE_VGLITE_BLIT_SPLIT
240                    || has_transform
241 #endif
242                   )
243                     return 0;
244 
245                 if(t->preference_score > 80) {
246                     t->preference_score = 80;
247                     t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE;
248                 }
249                 return 1;
250             }
251 
252         case LV_DRAW_TASK_TYPE_IMAGE: {
253                 lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc;
254                 const lv_image_dsc_t * img_dsc = draw_dsc->src;
255 
256                 if(img_dsc->header.cf >= LV_COLOR_FORMAT_PROPRIETARY_START) {
257                     return 0;
258                 }
259 
260 #if LV_USE_VGLITE_BLIT_SPLIT
261                 bool has_transform = (draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE ||
262                                       draw_dsc->scale_y != LV_SCALE_NONE);
263 #endif
264 
265                 if((!_vglite_src_cf_supported(img_dsc->header.cf))
266 #if LV_USE_VGLITE_BLIT_SPLIT
267                    || has_transform
268 #endif
269                    || (!vglite_src_buf_aligned(img_dsc->data, img_dsc->header.stride, img_dsc->header.cf))
270                   )
271                     return 0;
272 
273                 if(t->preference_score > 80) {
274                     t->preference_score = 80;
275                     t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE;
276                 }
277                 return 1;
278             }
279         default:
280             return 0;
281     }
282 
283     return 0;
284 }
285 
_vglite_dispatch(lv_draw_unit_t * draw_unit,lv_layer_t * layer)286 static int32_t _vglite_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
287 {
288     lv_draw_vglite_unit_t * draw_vglite_unit = (lv_draw_vglite_unit_t *) draw_unit;
289 
290     /* Return immediately if it's busy with draw task. */
291     if(draw_vglite_unit->task_act)
292         return 0;
293 
294     /* Try to get an ready to draw. */
295     lv_draw_task_t * t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_VGLITE);
296 
297     if(t == NULL)
298         return LV_DRAW_UNIT_IDLE;
299 
300     if(t->preferred_draw_unit_id != DRAW_UNIT_ID_VGLITE) {
301         /* Let the preferred known unit to draw this task. */
302         if(t->preferred_draw_unit_id != LV_DRAW_UNIT_NONE) {
303             return LV_DRAW_UNIT_IDLE;
304         }
305         else {
306             /* Fake unsupported tasks as ready. */
307             t->state = LV_DRAW_TASK_STATE_READY;
308             /* Request a new dispatching as it can get a new task. */
309             lv_draw_dispatch_request();
310 
311             return 1;
312         }
313     }
314 
315     if(lv_draw_layer_alloc_buf(layer) == NULL)
316         return LV_DRAW_UNIT_IDLE;
317 
318     t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
319     draw_vglite_unit->base_unit.target_layer = layer;
320     draw_vglite_unit->base_unit.clip_area = &t->clip_area;
321     draw_vglite_unit->task_act = t;
322 
323 #if LV_USE_VGLITE_DRAW_THREAD
324     /* Let the render thread work. */
325     if(draw_vglite_unit->inited)
326         lv_thread_sync_signal(&draw_vglite_unit->sync);
327 #else
328     _vglite_execute_drawing(draw_vglite_unit);
329 
330     draw_vglite_unit->task_act->state = LV_DRAW_TASK_STATE_READY;
331     draw_vglite_unit->task_act = NULL;
332 
333     /* The draw unit is free now. Request a new dispatching as it can get a new task. */
334     lv_draw_dispatch_request();
335 #endif
336 
337     return 1;
338 }
339 
340 #if LV_USE_VGLITE_DRAW_ASYNC
_vglite_wait_for_finish(lv_draw_unit_t * draw_unit)341 static int32_t _vglite_wait_for_finish(lv_draw_unit_t * draw_unit)
342 {
343     lv_draw_vglite_unit_t * draw_vglite_unit = (lv_draw_vglite_unit_t *) draw_unit;
344     draw_vglite_unit->wait_for_finish = true;
345 
346     /* Signal draw unit to finish its tasks and return READY state after completion. */
347     if(draw_vglite_unit->inited)
348         lv_thread_sync_signal(&draw_vglite_unit->sync);
349 
350     /* Wait for finish now. */
351     lv_draw_dispatch_wait_for_request();
352 
353     return 1;
354 }
355 #endif
356 
_vglite_delete(lv_draw_unit_t * draw_unit)357 static int32_t _vglite_delete(lv_draw_unit_t * draw_unit)
358 {
359 #if LV_USE_VGLITE_DRAW_THREAD
360     lv_draw_vglite_unit_t * draw_vglite_unit = (lv_draw_vglite_unit_t *) draw_unit;
361 
362     LV_LOG_INFO("Cancel VGLite draw thread.");
363     draw_vglite_unit->exit_status = true;
364 
365     if(draw_vglite_unit->inited)
366         lv_thread_sync_signal(&draw_vglite_unit->sync);
367 
368     lv_result_t res = lv_thread_delete(&draw_vglite_unit->thread);
369 
370     return res;
371 #else
372     LV_UNUSED(draw_unit);
373 
374     return 1;
375 #endif
376 }
377 
_vglite_execute_drawing(lv_draw_vglite_unit_t * u)378 static void _vglite_execute_drawing(lv_draw_vglite_unit_t * u)
379 {
380     lv_draw_task_t * t = u->task_act;
381     lv_draw_unit_t * draw_unit = (lv_draw_unit_t *)u;
382     lv_layer_t * layer = draw_unit->target_layer;
383     lv_draw_buf_t * draw_buf = layer->draw_buf;
384 
385     /* Set target buffer */
386     vglite_set_dest_buf(draw_buf->data, draw_buf->header.w, draw_buf->header.h, draw_buf->header.stride,
387                         draw_buf->header.cf);
388 
389     lv_area_t clip_area;
390     lv_area_copy(&clip_area, draw_unit->clip_area);
391     lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
392 
393     lv_area_t draw_area;
394     lv_area_copy(&draw_area, &t->area);
395     lv_area_move(&draw_area, -layer->buf_area.x1, -layer->buf_area.y1);
396 
397     if(!lv_area_intersect(&draw_area, &draw_area, &clip_area))
398         return; /*Fully clipped, nothing to do*/
399 
400     if(_draw_info.unit_cnt > 1)
401         lv_draw_buf_invalidate_cache(draw_buf, &draw_area);
402 
403     /* Set scissor area, excluding the split blit case */
404 #if LV_USE_VGLITE_BLIT_SPLIT
405     if(t->type != LV_DRAW_TASK_TYPE_IMAGE || t->type != LV_DRAW_TASK_TYPE_LAYER)
406 #endif
407         vglite_set_scissor(&clip_area);
408 
409     switch(t->type) {
410         case LV_DRAW_TASK_TYPE_LABEL:
411             lv_draw_vglite_label(draw_unit, t->draw_dsc, &t->area);
412             break;
413         case LV_DRAW_TASK_TYPE_FILL:
414             lv_draw_vglite_fill(draw_unit, t->draw_dsc, &t->area);
415             break;
416         case LV_DRAW_TASK_TYPE_BORDER:
417             lv_draw_vglite_border(draw_unit, t->draw_dsc, &t->area);
418             break;
419         case LV_DRAW_TASK_TYPE_IMAGE:
420             lv_draw_vglite_img(draw_unit, t->draw_dsc, &t->area);
421             break;
422         case LV_DRAW_TASK_TYPE_ARC:
423             lv_draw_vglite_arc(draw_unit, t->draw_dsc, &t->area);
424             break;
425         case LV_DRAW_TASK_TYPE_LINE:
426             lv_draw_vglite_line(draw_unit, t->draw_dsc);
427             break;
428         case LV_DRAW_TASK_TYPE_LAYER:
429             lv_draw_vglite_layer(draw_unit, t->draw_dsc, &t->area);
430             break;
431         case LV_DRAW_TASK_TYPE_TRIANGLE:
432             lv_draw_vglite_triangle(draw_unit, t->draw_dsc);
433             break;
434         default:
435             break;
436     }
437 
438     /* Disable scissor */
439     vglite_set_scissor(&layer->buf_area);
440 
441 #if LV_USE_PARALLEL_DRAW_DEBUG
442     /*Layers manage it for themselves*/
443     if(t->type != LV_DRAW_TASK_TYPE_LAYER) {
444         lv_area_t draw_area;
445         if(!lv_area_intersect(&draw_area, &t->area, u->base_unit.clip_area))
446             return;
447 
448         int32_t idx = 0;
449         lv_draw_unit_t * draw_unit_tmp = _draw_info.unit_head;
450         while(draw_unit_tmp != (lv_draw_unit_t *)u) {
451             draw_unit_tmp = draw_unit_tmp->next;
452             idx++;
453         }
454         lv_draw_rect_dsc_t rect_dsc;
455         lv_draw_rect_dsc_init(&rect_dsc);
456         rect_dsc.bg_color = lv_palette_main(idx % LV_PALETTE_LAST);
457         rect_dsc.border_color = rect_dsc.bg_color;
458         rect_dsc.bg_opa = LV_OPA_10;
459         rect_dsc.border_opa = LV_OPA_80;
460         rect_dsc.border_width = 1;
461         lv_draw_sw_fill((lv_draw_unit_t *)u, &rect_dsc, &draw_area);
462 
463         lv_point_t txt_size;
464         lv_text_get_size(&txt_size, "W", LV_FONT_DEFAULT, 0, 0, 100, LV_TEXT_FLAG_NONE);
465 
466         lv_area_t txt_area;
467         txt_area.x1 = draw_area.x1;
468         txt_area.y1 = draw_area.y1;
469         txt_area.x2 = draw_area.x1 + txt_size.x - 1;
470         txt_area.y2 = draw_area.y1 + txt_size.y - 1;
471 
472         lv_draw_rect_dsc_init(&rect_dsc);
473         rect_dsc.bg_color = lv_color_white();
474         lv_draw_sw_fill((lv_draw_unit_t *)u, &rect_dsc, &txt_area);
475 
476         char buf[8];
477         lv_snprintf(buf, sizeof(buf), "%d", idx);
478         lv_draw_label_dsc_t label_dsc;
479         lv_draw_label_dsc_init(&label_dsc);
480         label_dsc.color = lv_color_black();
481         label_dsc.text = buf;
482         lv_draw_sw_label((lv_draw_unit_t *)u, &label_dsc, &txt_area);
483     }
484 #endif
485 }
486 
487 #if LV_USE_VGLITE_DRAW_ASYNC
_vglite_queue_task(lv_draw_task_t * task)488 static inline void _vglite_queue_task(lv_draw_task_t * task)
489 {
490     VGLITE_ASSERT_MSG(((_tail + 1) % VGLITE_TASK_BUF_SIZE) != _head, "VGLite task buffer full.");
491 
492     _draw_task_buf[_tail].task = task;
493     _draw_task_buf[_tail].flushed = false;
494     _tail = (_tail + 1) % VGLITE_TASK_BUF_SIZE;
495 }
496 
_vglite_signal_task_ready(lv_draw_task_t * task)497 static inline void _vglite_signal_task_ready(lv_draw_task_t * task)
498 {
499     /* Signal the ready state to dispatcher. */
500     task->state = LV_DRAW_TASK_STATE_READY;
501     _head = (_head + 1) % VGLITE_TASK_BUF_SIZE;
502 
503     /* No need to cleanup the tasks in buffer as we advance with the _head. */
504 }
505 
_vglite_signal_all_task_ready(void)506 static inline void _vglite_signal_all_task_ready(void)
507 {
508     int end = (_head <= _tail) ? _tail : _tail + VGLITE_TASK_BUF_SIZE;
509 
510     for(int i = _head; i < end; i++) {
511         lv_draw_task_t * task = _draw_task_buf[i % VGLITE_TASK_BUF_SIZE].task;
512 
513         _vglite_signal_task_ready(task);
514     }
515 }
516 
_vglite_signal_flushed_task_ready(void)517 static inline void _vglite_signal_flushed_task_ready(void)
518 {
519     if(vglite_cmd_buf_is_flushed()) {
520         int end = (_head <= _tail) ? _tail : _tail + VGLITE_TASK_BUF_SIZE;
521 
522         for(int i = _head; i < end; i++) {
523             if(_draw_task_buf[i % VGLITE_TASK_BUF_SIZE].flushed) {
524                 lv_draw_task_t * task = _draw_task_buf[i % VGLITE_TASK_BUF_SIZE].task;
525 
526                 _vglite_signal_task_ready(task);
527 
528             }
529             else {
530                 /* Those tasks have been flushed now. */
531                 _draw_task_buf[i % VGLITE_TASK_BUF_SIZE].flushed = true;
532             }
533         }
534     }
535 }
536 #endif
537 
538 #if LV_USE_VGLITE_DRAW_THREAD
_vglite_render_thread_cb(void * ptr)539 static void _vglite_render_thread_cb(void * ptr)
540 {
541     lv_draw_vglite_unit_t * u = ptr;
542 
543     lv_thread_sync_init(&u->sync);
544     u->inited = true;
545 
546     while(1) {
547         /* Wait for sync if there is no task set. */
548         while(u->task_act == NULL
549 #if LV_USE_VGLITE_DRAW_ASYNC
550               /*
551                * Wait for sync if _draw_task_buf is empty.
552                * The thread will have to run to complete any pending tasks.
553                */
554               && !u->wait_for_finish
555 #endif
556              ) {
557             if(u->exit_status)
558                 break;
559 
560             lv_thread_sync_wait(&u->sync);
561         }
562 
563         if(u->exit_status) {
564             LV_LOG_INFO("Ready to exit VGLite draw thread.");
565             break;
566         }
567 
568         if(u->task_act) {
569 #if LV_USE_VGLITE_DRAW_ASYNC
570             _vglite_queue_task((void *)u->task_act);
571 #endif
572             _vglite_execute_drawing(u);
573         }
574 #if LV_USE_VGLITE_DRAW_ASYNC
575         if(u->wait_for_finish) {
576             u->wait_for_finish = false;
577             vglite_wait_for_finish();
578             _vglite_signal_all_task_ready();
579         }
580         else {   /* u->task_act */
581             _vglite_signal_flushed_task_ready();
582         }
583 #else
584         /* Signal the ready state to dispatcher. */
585         u->task_act->state = LV_DRAW_TASK_STATE_READY;
586 #endif
587         /* Cleanup. */
588         u->task_act = NULL;
589 
590         /* The draw unit is free now. Request a new dispatching as it can get a new task. */
591         lv_draw_dispatch_request();
592     }
593 
594     u->inited = false;
595     lv_thread_sync_delete(&u->sync);
596     LV_LOG_INFO("Exit VGLite draw thread.");
597 }
598 #endif
599 
600 #endif /*LV_USE_DRAW_VGLITE*/
601