1 /**
2 * @file lv_draw_pxp.c
3 *
4 */
5
6 /**
7 * Copyright 2022-2024 NXP
8 *
9 * SPDX-License-Identifier: MIT
10 */
11
12 /*********************
13 * INCLUDES
14 *********************/
15
16 #include "lv_draw_pxp.h"
17
18 #if LV_USE_PXP
19 #if LV_USE_DRAW_PXP || LV_USE_ROTATE_PXP
20 #include "lv_pxp_cfg.h"
21 #include "lv_pxp_utils.h"
22
23 #if LV_USE_PARALLEL_DRAW_DEBUG
24 #include "../../../core/lv_global.h"
25 #endif
26
27 /*********************
28 * DEFINES
29 *********************/
30
31 #define DRAW_UNIT_ID_PXP 3
32
33 /**********************
34 * TYPEDEFS
35 **********************/
36
37 /**********************
38 * STATIC PROTOTYPES
39 **********************/
40
41 /*
42 * Evaluate a task and set the score and preferred PXP unit.
43 * Return 1 if task is preferred, 0 otherwise (task is not supported).
44 */
45 static int32_t _pxp_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
46
47 /*
48 * Dispatch a task to the PXP unit.
49 * Return 1 if task was dispatched, 0 otherwise (task not supported).
50 */
51 static int32_t _pxp_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
52
53 /*
54 * Delete the PXP draw unit.
55 */
56 static int32_t _pxp_delete(lv_draw_unit_t * draw_unit);
57
58 #if LV_USE_PXP_DRAW_THREAD
59 static void _pxp_render_thread_cb(void * ptr);
60 #endif
61
62 static void _pxp_execute_drawing(lv_draw_pxp_unit_t * u);
63
64 /**********************
65 * STATIC PROTOTYPES
66 **********************/
67
68 /**********************
69 * STATIC VARIABLES
70 **********************/
71
72 #if LV_USE_PARALLEL_DRAW_DEBUG
73 #define _draw_info LV_GLOBAL_DEFAULT()->draw_info
74 #endif
75
76 /**********************
77 * MACROS
78 **********************/
79
80 /**********************
81 * GLOBAL FUNCTIONS
82 **********************/
83
lv_draw_pxp_init(void)84 void lv_draw_pxp_init(void)
85 {
86 lv_pxp_init();
87
88 #if LV_USE_DRAW_PXP
89 lv_draw_buf_pxp_init_handlers();
90
91 lv_draw_pxp_unit_t * draw_pxp_unit = lv_draw_create_unit(sizeof(lv_draw_pxp_unit_t));
92 draw_pxp_unit->base_unit.evaluate_cb = _pxp_evaluate;
93 draw_pxp_unit->base_unit.dispatch_cb = _pxp_dispatch;
94 draw_pxp_unit->base_unit.delete_cb = _pxp_delete;
95 draw_pxp_unit->base_unit.name = "NXP_PXP";
96
97 #if LV_USE_PXP_DRAW_THREAD
98 lv_thread_init(&draw_pxp_unit->thread, "pxpdraw", LV_THREAD_PRIO_HIGH, _pxp_render_thread_cb, 2 * 1024, draw_pxp_unit);
99 #endif
100 #endif /*LV_USE_DRAW_PXP*/
101 }
102
lv_draw_pxp_deinit(void)103 void lv_draw_pxp_deinit(void)
104 {
105 lv_pxp_deinit();
106 }
107
lv_draw_pxp_rotate(const void * src_buf,void * dest_buf,int32_t src_width,int32_t src_height,int32_t src_stride,int32_t dest_stride,lv_display_rotation_t rotation,lv_color_format_t cf)108 void lv_draw_pxp_rotate(const void * src_buf, void * dest_buf, int32_t src_width, int32_t src_height,
109 int32_t src_stride, int32_t dest_stride, lv_display_rotation_t rotation,
110 lv_color_format_t cf)
111 {
112 lv_pxp_reset();
113
114 /* convert rotation angle */
115 pxp_rotate_degree_t pxp_rotation;
116 switch(rotation) {
117 case LV_DISPLAY_ROTATION_0:
118 pxp_rotation = kPXP_Rotate0;
119 break;
120 case LV_DISPLAY_ROTATION_90:
121 pxp_rotation = kPXP_Rotate90;
122 break;
123 case LV_DISPLAY_ROTATION_180:
124 pxp_rotation = kPXP_Rotate180;
125 break;
126 case LV_DISPLAY_ROTATION_270:
127 pxp_rotation = kPXP_Rotate270;
128 break;
129 default:
130 pxp_rotation = kPXP_Rotate0;
131 break;
132 }
133 PXP_SetRotateConfig(PXP_ID, kPXP_RotateOutputBuffer, pxp_rotation, kPXP_FlipDisable);
134
135 /*Simple blit, no effect - Disable PS buffer*/
136 PXP_SetProcessSurfacePosition(PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
137
138 /*AS buffer - source image*/
139 pxp_as_buffer_config_t asBufferConfig = {
140 .pixelFormat = pxp_get_as_px_format(cf),
141 .bufferAddr = (uint32_t)src_buf,
142 .pitchBytes = src_stride
143 };
144 PXP_SetAlphaSurfaceBufferConfig(PXP_ID, &asBufferConfig);
145 PXP_SetAlphaSurfacePosition(PXP_ID, 0U, 0U, src_width - 1U, src_height - 1U);
146 PXP_EnableAlphaSurfaceOverlayColorKey(PXP_ID, false);
147
148 /*Output buffer.*/
149 pxp_output_buffer_config_t outputBufferConfig = {
150 .pixelFormat = pxp_get_out_px_format(cf),
151 .interlacedMode = kPXP_OutputProgressive,
152 .buffer0Addr = (uint32_t)dest_buf,
153 .buffer1Addr = (uint32_t)0U,
154 .pitchBytes = dest_stride,
155 .width = src_width,
156 .height = src_height
157 };
158 PXP_SetOutputBufferConfig(PXP_ID, &outputBufferConfig);
159
160 lv_pxp_run();
161 }
162
163 /**********************
164 * STATIC FUNCTIONS
165 **********************/
166 #if LV_USE_DRAW_PXP
_pxp_src_cf_supported(lv_color_format_t cf)167 static inline bool _pxp_src_cf_supported(lv_color_format_t cf)
168 {
169 bool is_cf_supported = false;
170
171 switch(cf) {
172 case LV_COLOR_FORMAT_RGB565:
173 case LV_COLOR_FORMAT_ARGB8888:
174 case LV_COLOR_FORMAT_XRGB8888:
175 is_cf_supported = true;
176 break;
177 default:
178 break;
179 }
180
181 return is_cf_supported;
182 }
183
_pxp_dest_cf_supported(lv_color_format_t cf)184 static inline bool _pxp_dest_cf_supported(lv_color_format_t cf)
185 {
186 bool is_cf_supported = false;
187
188 switch(cf) {
189 case LV_COLOR_FORMAT_RGB565:
190 case LV_COLOR_FORMAT_RGB888:
191 case LV_COLOR_FORMAT_ARGB8888:
192 case LV_COLOR_FORMAT_XRGB8888:
193 is_cf_supported = true;
194 break;
195 default:
196 break;
197 }
198
199 return is_cf_supported;
200 }
201
_pxp_draw_img_supported(const lv_draw_image_dsc_t * draw_dsc)202 static bool _pxp_draw_img_supported(const lv_draw_image_dsc_t * draw_dsc)
203 {
204 const lv_image_dsc_t * img_dsc = draw_dsc->src;
205
206 bool has_recolor = (draw_dsc->recolor_opa > LV_OPA_MIN);
207 bool has_transform = (draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE ||
208 draw_dsc->scale_y != LV_SCALE_NONE);
209
210 /* Recolor and transformation are not supported at the same time. */
211 if(has_recolor && has_transform)
212 return false;
213
214 bool has_opa = (draw_dsc->opa < (lv_opa_t)LV_OPA_MAX);
215 bool src_has_alpha = (img_dsc->header.cf == LV_COLOR_FORMAT_ARGB8888);
216
217 /*
218 * Recolor or transformation for images w/ opa or alpha channel can't
219 * be obtained in a single PXP configuration. Two steps are required.
220 */
221 if((has_recolor || has_transform) && (has_opa || src_has_alpha))
222 return false;
223
224 /* PXP can only rotate at 90x angles. */
225 if(draw_dsc->rotation % 900)
226 return false;
227
228 /*
229 * PXP is set to process 16x16 blocks to optimize the system for memory
230 * bandwidth and image processing time.
231 * The output engine essentially truncates any output pixels after the
232 * desired number of pixels has been written.
233 * When rotating a source image and the output is not divisible by the block
234 * size, the incorrect pixels could be truncated and the final output image
235 * can look shifted.
236 *
237 * No combination of rotate with flip, scaling or decimation is possible
238 * if buffer is unaligned.
239 */
240 if(has_transform && (img_dsc->header.w % 16 || img_dsc->header.h % 16))
241 return false;
242
243 return true;
244 }
245
_pxp_evaluate(lv_draw_unit_t * u,lv_draw_task_t * t)246 static int32_t _pxp_evaluate(lv_draw_unit_t * u, lv_draw_task_t * t)
247 {
248 LV_UNUSED(u);
249
250 const lv_draw_dsc_base_t * draw_dsc_base = (lv_draw_dsc_base_t *) t->draw_dsc;
251
252 if(!_pxp_dest_cf_supported(draw_dsc_base->layer->color_format))
253 return 0;
254
255 switch(t->type) {
256 case LV_DRAW_TASK_TYPE_FILL: {
257 const lv_draw_fill_dsc_t * draw_dsc = (lv_draw_fill_dsc_t *) t->draw_dsc;
258
259 /* Most simple case: just a plain rectangle (no radius, no gradient). */
260 if((draw_dsc->radius != 0) || (draw_dsc->grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE))
261 return 0;
262
263 if(t->preference_score > 70) {
264 t->preference_score = 70;
265 t->preferred_draw_unit_id = DRAW_UNIT_ID_PXP;
266 }
267 return 1;
268 }
269
270 case LV_DRAW_TASK_TYPE_LAYER: {
271 const lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc;
272 lv_layer_t * layer_to_draw = (lv_layer_t *)draw_dsc->src;
273
274 if(!_pxp_src_cf_supported(layer_to_draw->color_format))
275 return 0;
276
277 if(!_pxp_draw_img_supported(draw_dsc))
278 return 0;
279
280 if(t->preference_score > 70) {
281 t->preference_score = 70;
282 t->preferred_draw_unit_id = DRAW_UNIT_ID_PXP;
283 }
284 return 1;
285 }
286
287 case LV_DRAW_TASK_TYPE_IMAGE: {
288 lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc;
289 const lv_image_dsc_t * img_dsc = draw_dsc->src;
290
291 if(img_dsc->header.cf >= LV_COLOR_FORMAT_PROPRIETARY_START)
292 return 0;
293
294 if(draw_dsc->tile)
295 return 0;
296
297 if((!_pxp_src_cf_supported(img_dsc->header.cf)) ||
298 (!pxp_buf_aligned(img_dsc->data, img_dsc->header.stride)))
299 return 0;
300
301 if(!_pxp_draw_img_supported(draw_dsc))
302 return 0;
303
304 if(t->preference_score > 70) {
305 t->preference_score = 70;
306 t->preferred_draw_unit_id = DRAW_UNIT_ID_PXP;
307 }
308 return 1;
309 }
310 default:
311 return 0;
312 }
313
314 return 0;
315 }
316
_pxp_dispatch(lv_draw_unit_t * draw_unit,lv_layer_t * layer)317 static int32_t _pxp_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
318 {
319 lv_draw_pxp_unit_t * draw_pxp_unit = (lv_draw_pxp_unit_t *) draw_unit;
320
321 /* Return immediately if it's busy with draw task. */
322 if(draw_pxp_unit->task_act)
323 return 0;
324
325 /* Try to get an ready to draw. */
326 lv_draw_task_t * t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_PXP);
327
328 if(t == NULL || t->preferred_draw_unit_id != DRAW_UNIT_ID_PXP)
329 return LV_DRAW_UNIT_IDLE;
330
331 if(lv_draw_layer_alloc_buf(layer) == NULL)
332 return LV_DRAW_UNIT_IDLE;
333
334 t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
335 draw_pxp_unit->base_unit.target_layer = layer;
336 draw_pxp_unit->base_unit.clip_area = &t->clip_area;
337 draw_pxp_unit->task_act = t;
338
339 #if LV_USE_PXP_DRAW_THREAD
340 /* Let the render thread work. */
341 if(draw_pxp_unit->inited)
342 lv_thread_sync_signal(&draw_pxp_unit->sync);
343 #else
344 _pxp_execute_drawing(draw_pxp_unit);
345
346 draw_pxp_unit->task_act->state = LV_DRAW_TASK_STATE_READY;
347 draw_pxp_unit->task_act = NULL;
348
349 /* The draw unit is free now. Request a new dispatching as it can get a new task. */
350 lv_draw_dispatch_request();
351 #endif
352
353 return 1;
354 }
355
_pxp_delete(lv_draw_unit_t * draw_unit)356 static int32_t _pxp_delete(lv_draw_unit_t * draw_unit)
357 {
358 #if LV_USE_PXP_DRAW_THREAD
359 lv_draw_pxp_unit_t * draw_pxp_unit = (lv_draw_pxp_unit_t *) draw_unit;
360
361 LV_LOG_INFO("Cancel PXP draw thread.");
362 draw_pxp_unit->exit_status = true;
363
364 if(draw_pxp_unit->inited)
365 lv_thread_sync_signal(&draw_pxp_unit->sync);
366
367 lv_result_t res = lv_thread_delete(&draw_pxp_unit->thread);
368
369 return res;
370 #else
371 LV_UNUSED(draw_unit);
372
373 return 0;
374 #endif
375 }
376
_pxp_execute_drawing(lv_draw_pxp_unit_t * u)377 static void _pxp_execute_drawing(lv_draw_pxp_unit_t * u)
378 {
379 lv_draw_task_t * t = u->task_act;
380 lv_draw_unit_t * draw_unit = (lv_draw_unit_t *)u;
381 lv_layer_t * layer = draw_unit->target_layer;
382 lv_draw_buf_t * draw_buf = layer->draw_buf;
383
384 lv_area_t draw_area;
385 if(!lv_area_intersect(&draw_area, &t->area, draw_unit->clip_area))
386 return; /*Fully clipped, nothing to do*/
387
388 /* Make area relative to the buffer */
389 lv_area_move(&draw_area, -layer->buf_area.x1, -layer->buf_area.y1);
390
391 /* Invalidate only the drawing area */
392 lv_draw_buf_invalidate_cache(draw_buf, &draw_area);
393
394 switch(t->type) {
395 case LV_DRAW_TASK_TYPE_FILL:
396 lv_draw_pxp_fill(draw_unit, t->draw_dsc, &t->area);
397 break;
398 case LV_DRAW_TASK_TYPE_IMAGE:
399 lv_draw_pxp_img(draw_unit, t->draw_dsc, &t->area);
400 break;
401 case LV_DRAW_TASK_TYPE_LAYER:
402 lv_draw_pxp_layer(draw_unit, t->draw_dsc, &t->area);
403 break;
404 default:
405 break;
406 }
407
408 #if LV_USE_PARALLEL_DRAW_DEBUG
409 /*Layers manage it for themselves*/
410 if(t->type != LV_DRAW_TASK_TYPE_LAYER) {
411 lv_area_t draw_area;
412 if(!lv_area_intersect(&draw_area, &t->area, u->base_unit.clip_area))
413 return;
414
415 int32_t idx = 0;
416 lv_draw_unit_t * draw_unit_tmp = _draw_info.unit_head;
417 while(draw_unit_tmp != (lv_draw_unit_t *)u) {
418 draw_unit_tmp = draw_unit_tmp->next;
419 idx++;
420 }
421 lv_draw_rect_dsc_t rect_dsc;
422 lv_draw_rect_dsc_init(&rect_dsc);
423 rect_dsc.bg_color = lv_palette_main(idx % LV_PALETTE_LAST);
424 rect_dsc.border_color = rect_dsc.bg_color;
425 rect_dsc.bg_opa = LV_OPA_10;
426 rect_dsc.border_opa = LV_OPA_80;
427 rect_dsc.border_width = 1;
428 lv_draw_sw_fill((lv_draw_unit_t *)u, &rect_dsc, &draw_area);
429
430 lv_point_t txt_size;
431 lv_text_get_size(&txt_size, "W", LV_FONT_DEFAULT, 0, 0, 100, LV_TEXT_FLAG_NONE);
432
433 lv_area_t txt_area;
434 txt_area.x1 = draw_area.x1;
435 txt_area.y1 = draw_area.y1;
436 txt_area.x2 = draw_area.x1 + txt_size.x - 1;
437 txt_area.y2 = draw_area.y1 + txt_size.y - 1;
438
439 lv_draw_rect_dsc_init(&rect_dsc);
440 rect_dsc.bg_color = lv_color_white();
441 lv_draw_sw_fill((lv_draw_unit_t *)u, &rect_dsc, &txt_area);
442
443 char buf[8];
444 lv_snprintf(buf, sizeof(buf), "%d", idx);
445 lv_draw_label_dsc_t label_dsc;
446 lv_draw_label_dsc_init(&label_dsc);
447 label_dsc.color = lv_color_black();
448 label_dsc.text = buf;
449 lv_draw_sw_label((lv_draw_unit_t *)u, &label_dsc, &txt_area);
450 }
451 #endif
452 }
453
454 #if LV_USE_PXP_DRAW_THREAD
_pxp_render_thread_cb(void * ptr)455 static void _pxp_render_thread_cb(void * ptr)
456 {
457 lv_draw_pxp_unit_t * u = ptr;
458
459 lv_thread_sync_init(&u->sync);
460 u->inited = true;
461
462 while(1) {
463 /* Wait for sync if there is no task set. */
464 while(u->task_act == NULL) {
465 if(u->exit_status)
466 break;
467
468 lv_thread_sync_wait(&u->sync);
469 }
470
471 if(u->exit_status) {
472 LV_LOG_INFO("Ready to exit PXP draw thread.");
473 break;
474 }
475
476 _pxp_execute_drawing(u);
477
478 /* Signal the ready state to dispatcher. */
479 u->task_act->state = LV_DRAW_TASK_STATE_READY;
480
481 /* Cleanup. */
482 u->task_act = NULL;
483
484 /* The draw unit is free now. Request a new dispatching as it can get a new task. */
485 lv_draw_dispatch_request();
486 }
487
488 u->inited = false;
489 lv_thread_sync_delete(&u->sync);
490 LV_LOG_INFO("Exit PXP draw thread.");
491 }
492 #endif
493 #endif /*LV_USE_DRAW_PXP*/
494 #endif /*LV_USE_DRAW_PXP || LV_USE_ROTATE_PXP*/
495 #endif /*LV_USE_PXP*/
496