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