From e3e3eeaf8c1593d384c6537244a301cdc1abd3d9 Mon Sep 17 00:00:00 2001 From: Jozef Bastek Date: Fri, 4 Dec 2020 10:02:22 +0100 Subject: [PATCH] fix(vglite): update for v8 includes updates coming from NXP MCU SDK 2.10 with adaptation for lvgl v8: Fixed BLIT offset computation Previous implementation didn't take into account a possibility of non-zero offset in source image, so output was wrong if offset was used. With this fix, CPU and VG-Lite output is the same even with offsets. Signed-off-by: Jozef Bastek gpu: vglite: Buffer sync with BLITs Added buffer synchronisation with BLITs instead of memcpy for VG-Lite. In LVGL v8, buffer synchronisation acceleration is moved to littlevgl_support.c. Signed-off-by: Jozef Bastek gpu: vglite: Fixed BLIT boundary Fixed artifact issue revealed by buffer sync with BLIT feature. Caused by wrong BLIT boundary. Signed-off-by: Jozef Bastek gpu: vglite: BLIT quality degradation workaround for RT595 Limitation in RT595 causes BLIT image quality degradation when coordinates are above 368 px. This patch implements workaround that will break the BLIT into multiple smaller BLITs, so the quality is not affected for higher resolutions. Signed-off-by: Jozef Bastek gpu: vglite: Fix address alignment and stride requirements Fixed multiple issues: - The VGLite alignment requirement checks for the pixel destination buffer are not applicable in our case of Linear (non-tiled) format. - Some VGLite stride requirement requirement should be expressed in bytes, not in pixels. - Fix the Y alignment function to ensure that line starts at an address that the respects the alignment requirement of VG-Lite. Such mistakes do not break application, but cause a fallback to non-accelerated Blit by CPU. See MGG-741. Signed-off-by: Seb Fagard gpu: vglite: fix some MISRA C 2012 violations fixed 56 violations: mainly implicit format casts and unchecked returned values. see JIRA MCUX-43327 Signed-off-by: Seb Fagard gpu: vglite: fix stride requirement in _init_vg_buf() fix unit: need to convert the alignment requirement into bytes when checking stride parameter. fix condition: stride requirement applies only on source buffers. Signed-off-by: Seb Fagard gpu: vglite: updates for v8 cache callback type changed in display driver struct Signed-off-by: Seb Fagard gpu: vglite: update parameter type for v8 display driver is now a pointer in structure. Signed-off-by: Seb Fagard --- src/draw/lv_draw_blend.c | 16 +- src/gpu/lv_gpu_nxp_vglite.c | 570 ++++++++++++++++++++++++++++++++---- src/gpu/lv_gpu_nxp_vglite.h | 12 +- 3 files changed, 533 insertions(+), 65 deletions(-) diff --git a/src/draw/lv_draw_blend.c b/src/draw/lv_draw_blend.c index 31ca6ba0d7..46ba49891f 100644 --- a/src/draw/lv_draw_blend.c +++ b/src/draw/lv_draw_blend.c @@ -701,8 +701,8 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t * disp_area, lv_col blit.src_stride = lv_area_get_width(map_area) * sizeof(lv_color_t); blit.src_area.x1 = (draw_area->x1 - (map_area->x1 - disp_area->x1)); blit.src_area.y1 = (draw_area->y1 - (map_area->y1 - disp_area->y1)); - blit.src_area.x2 = blit.src_area.x1 + draw_area_w; - blit.src_area.y2 = blit.src_area.y1 + draw_area_h; + blit.src_area.x2 = blit.src_area.x1 + draw_area_w - 1; + blit.src_area.y2 = blit.src_area.y1 + draw_area_h - 1; blit.dst = disp_buf; blit.dst_width = lv_area_get_width(disp_area); @@ -710,8 +710,8 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t * disp_area, lv_col blit.dst_stride = lv_area_get_width(disp_area) * sizeof(lv_color_t); blit.dst_area.x1 = draw_area->x1; blit.dst_area.y1 = draw_area->y1; - blit.dst_area.x2 = blit.dst_area.x1 + draw_area_w; - blit.dst_area.y2 = blit.dst_area.y1 + draw_area_h; + blit.dst_area.x2 = blit.dst_area.x1 + draw_area_w - 1; + blit.dst_area.y2 = blit.dst_area.y1 + draw_area_h - 1; blit.opa = opa; @@ -751,8 +751,8 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t * disp_area, lv_col blit.src_stride = lv_area_get_width(map_area) * sizeof(lv_color_t); blit.src_area.x1 = (draw_area->x1 - (map_area->x1 - disp_area->x1)); blit.src_area.y1 = (draw_area->y1 - (map_area->y1 - disp_area->y1)); - blit.src_area.x2 = blit.src_area.x1 + draw_area_w; - blit.src_area.y2 = blit.src_area.y1 + draw_area_h; + blit.src_area.x2 = blit.src_area.x1 + draw_area_w - 1; + blit.src_area.y2 = blit.src_area.y1 + draw_area_h - 1; blit.dst = disp_buf; blit.dst_width = lv_area_get_width(disp_area); @@ -760,8 +760,8 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t * disp_area, lv_col blit.dst_stride = lv_area_get_width(disp_area) * sizeof(lv_color_t); blit.dst_area.x1 = draw_area->x1; blit.dst_area.y1 = draw_area->y1; - blit.dst_area.x2 = blit.dst_area.x1 + draw_area_w; - blit.dst_area.y2 = blit.dst_area.y1 + draw_area_h; + blit.dst_area.x2 = blit.dst_area.x1 + draw_area_w - 1; + blit.dst_area.y2 = blit.dst_area.y1 + draw_area_h - 1; blit.opa = opa; diff --git a/src/gpu/lv_gpu_nxp_vglite.c b/src/gpu/lv_gpu_nxp_vglite.c index 94ab5ca9a9..6021300e78 100644 --- a/src/gpu/lv_gpu_nxp_vglite.c +++ b/src/gpu/lv_gpu_nxp_vglite.c @@ -40,27 +40,78 @@ #include "../misc/lv_log.h" #include "fsl_cache.h" #include "vg_lite.h" +#include "fsl_debug_console.h" /********************* * DEFINES *********************/ -/********************** - * TYPEDEFS - **********************/ - #if LV_COLOR_DEPTH==16 #define VGLITE_PX_FMT VG_LITE_RGB565 #else #error Only 16bit color depth is supported. Set LV_COLOR_DEPTH to 16. #endif +/* Enable BLIT quality degradation workaround for RT595 */ +#define RT595_BLIT_WRKRND_ENABLED 1 + +/* If LV_HOR_RES_MAX/LV_VER_RES_MAX is higher than this value, workaround will be enabled */ +#define RT595_BLIT_WRKRND_THR 352 + +/* Print detailed info to SDK console (NOT to LVGL log system) */ +#define BLIT_DBG_VERBOSE 0 + +/* Draw rectangles around BLIT tiles */ +#define BLIT_DBG_AREAS 0 + +/* Redirect PRINT to SDK PRINTF */ +#define PRINT PRINTF + +/* Verbose debug print */ +#if BLIT_DBG_VERBOSE + #define PRINT_BLT PRINTF +#else + #define PRINT_BLT(...) +#endif + +/* Internal compound symbol */ +#if (defined(CPU_MIMXRT595SFFOB) || defined(CPU_MIMXRT595SFFOB_cm33) || \ + defined(CPU_MIMXRT595SFFOC) || defined(CPU_MIMXRT595SFFOC_cm33)) && \ + ((LV_HOR_RES_MAX > RT595_BLIT_WRKRND_THR) || (LV_VER_RES_MAX > RT595_BLIT_WRKRND_THR)) && \ + RT595_BLIT_WRKRND_ENABLED + #define _BLIT_SPLIT_ENABLED 1 +#else + #define _BLIT_SPLIT_ENABLED 0 +#endif + +/* BLIT split threshold - BLITs with width or height higher than this value will be done + * in multiple steps. Value must be 16-aligned. Don't change. + * */ +#define LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR 352 + + +/********************** + * TYPEDEFS + **********************/ + /********************** * STATIC PROTOTYPES **********************/ -static lv_res_t init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t height, uint32_t stride, - const lv_color_t * ptr); +static lv_res_t _init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t height, uint32_t stride, + const lv_color_t * ptr, bool source); + +static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit); +#if _BLIT_SPLIT_ENABLED +static void _align_x(lv_area_t * area, lv_color_t ** buf); +static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx); +static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit); +#if BLIT_DBG_AREAS +static void _draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height, + lv_area_t * fill_area, lv_color_t color); +#endif +static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit); +#endif /********************** * STATIC VARIABLES @@ -70,6 +121,14 @@ static lv_res_t init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t hei * MACROS **********************/ +#define CHECK(cond, txt) \ + do { \ + if (cond) { \ + PRINT("%s. STOP!\n", txt); \ + for ( ; ; ); \ + } \ + } while(0) + /********************** * GLOBAL FUNCTIONS **********************/ @@ -94,25 +153,37 @@ lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv lv_color32_t col32 = {.full = lv_color_to32(color)}; /*Convert color to RGBA8888*/ lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - if(init_vg_buf(&rt, dest_width, dest_height, dest_width * sizeof(lv_color_t), dest_buf) != LV_RES_OK) { + if(_init_vg_buf(&rt, (uint32_t) dest_width, (uint32_t) dest_height, (uint32_t) dest_width * sizeof(lv_color_t), (const lv_color_t *) dest_buf, false) != LV_RES_OK) { #if LV_GPU_NXP_VG_LITE_LOG_ERRORS LV_LOG_ERROR("init_vg_buf reported error. Fill failed."); #endif return LV_RES_INV; } - if(opa >= LV_OPA_MAX) { /*Opaque fill*/ + if(opa >= (lv_opa_t) LV_OPA_MAX) { /*Opaque fill*/ rect.x = fill_area->x1; rect.y = fill_area->y1; - rect.width = (fill_area->x2 - fill_area->x1) + 1; - rect.height = (fill_area->y2 - fill_area->y1) + 1; + rect.width = (int32_t) fill_area->x2 - (int32_t) fill_area->x1 + 1; + rect.height = (int32_t) fill_area->y2 - (int32_t) fill_area->y1 + 1; - if(disp && disp->driver->clean_dcache_cb) { /*Clean & invalidate cache*/ + if(disp != NULL && disp->driver->clean_dcache_cb != NULL) { /*Clean & invalidate cache*/ disp->driver->clean_dcache_cb(disp->driver); } - err |= vg_lite_clear(&rt, &rect, col32.full); - err |= vg_lite_finish(); + err = vg_lite_clear(&rt, &rect, col32.full); + if (err != VG_LITE_SUCCESS) { + #if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("vg_lite_clear reported error. Fill failed."); + #endif + return LV_RES_INV; + } + err = vg_lite_finish(); + if (err != VG_LITE_SUCCESS) { + #if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("vg_lite_finish reported error. Fill failed."); + #endif + return LV_RES_INV; + } } else { /*fill with transparency*/ @@ -127,8 +198,8 @@ lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv VLC_OP_END }; - err |= vg_lite_init_path(&path, VG_LITE_S16, VG_LITE_LOW, sizeof(path_data), path_data, - fill_area->x1, fill_area->y1, fill_area->x2 + 1, fill_area->y2 + 1); + err = vg_lite_init_path(&path, VG_LITE_S16, VG_LITE_LOW, sizeof(path_data), path_data, + (vg_lite_float_t) fill_area->x1, (vg_lite_float_t) fill_area->y1, ((vg_lite_float_t) fill_area->x2) + 1.0f, ((vg_lite_float_t) fill_area->y2) + 1.0f); if(err != VG_LITE_SUCCESS) { #if LV_GPU_NXP_VG_LITE_LOG_ERRORS LV_LOG_ERROR("vg_lite_init_path() failed."); @@ -136,12 +207,12 @@ lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv return LV_RES_INV; } - colMix.ch.red = ((uint16_t)col32.ch.red * opa) >> 8; /*Pre-multiply color*/ - colMix.ch.green = ((uint16_t)col32.ch.green * opa) >> 8; - colMix.ch.blue = ((uint16_t)col32.ch.blue * opa) >> 8; + colMix.ch.red = (uint8_t)(((uint16_t)col32.ch.red * opa) >> 8); /*Pre-multiply color*/ + colMix.ch.green = (uint8_t)(((uint16_t)col32.ch.green * opa) >> 8); + colMix.ch.blue = (uint8_t)(((uint16_t)col32.ch.blue * opa) >> 8); colMix.ch.alpha = opa; - if(disp && disp->driver->clean_dcache_cb) { /*Clean & invalidate cache*/ + if((disp != NULL) && (disp->driver->clean_dcache_cb != NULL)) { /*Clean & invalidate cache*/ disp->driver->clean_dcache_cb(disp->driver); } @@ -149,8 +220,8 @@ lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv vg_lite_identity(&matrix); /*Draw rectangle*/ - err |= vg_lite_draw(&rt, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, colMix.full); - if(err) { + err = vg_lite_draw(&rt, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, colMix.full); + if(err != VG_LITE_SUCCESS) { #if LV_GPU_NXP_VG_LITE_LOG_ERRORS LV_LOG_ERROR("vg_lite_draw() failed."); #endif @@ -158,8 +229,21 @@ lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv return LV_RES_INV; } - err |= vg_lite_finish(); - err |= vg_lite_clear_path(&path); + err = vg_lite_finish(); + if(err != VG_LITE_SUCCESS) { +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("vg_lite_finish() failed."); +#endif + return LV_RES_INV; + } + + err = vg_lite_clear_path(&path); + if(err != VG_LITE_SUCCESS) { +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("vg_lite_clear_path() failed."); +#endif + return LV_RES_INV; + } } if(err == VG_LITE_SUCCESS) { @@ -180,63 +264,255 @@ lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS) */ lv_res_t lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit) +{ +#if _BLIT_SPLIT_ENABLED + + lv_res_t rv = LV_RES_INV; + + if(_lv_gpu_nxp_vglite_check_blit(blit) != LV_RES_OK) { + PRINT_BLT("Blit check failed\n"); + return LV_RES_INV; + } + + PRINT_BLT("BLIT from: " + "Area: %03d,%03d - %03d,%03d " + "Addr: %d\n\n", + blit->src_area.x1, blit->src_area.y1, + blit->src_area.x2, blit->src_area.y2, + (uintptr_t) blit->src); + + PRINT_BLT("BLIT to: " + "Area: %03d,%03d - %03d,%03d " + "Addr: %d\n\n", + blit->dst_area.x1, blit->dst_area.y1, + blit->dst_area.x2, blit->dst_area.y2, + (uintptr_t) blit->src); + + /* Stage 1: Move starting pointers as close as possible to [x1, y1], so coordinates are as small as possible. */ + _align_x(&blit->src_area, (lv_color_t **)&blit->src); + _align_y(&blit->src_area, (lv_color_t **)&blit->src, blit->src_stride / sizeof(lv_color_t)); + _align_x(&blit->dst_area, (lv_color_t **)&blit->dst); + _align_y(&blit->dst_area, (lv_color_t **)&blit->dst, blit->dst_stride / sizeof(lv_color_t)); + + /* Stage 2: If we're in limit, do a single BLIT */ + if((blit->src_area.x2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) && + (blit->src_area.y2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR)) { + PRINT_BLT("Simple blit!\n"); + return _lv_gpu_nxp_vglite_blit_single(blit); + }; + + /* Stage 3: Split the BLIT into multiple tiles */ + PRINT_BLT("Split blit!\n"); + + PRINT_BLT("Blit " + "([%03d,%03d], [%03d,%03d]) -> " + "([%03d,%03d], [%03d,%03d]) | " + "([%03dx%03d] -> [%03dx%03d]) | " + "A:(%d -> %d)\n", + blit->src_area.x1, blit->src_area.y1, blit->src_area.x2, blit->src_area.y2, + blit->dst_area.x1, blit->dst_area.y1, blit->dst_area.x2, blit->dst_area.y2, + lv_area_get_width(&blit->src_area), lv_area_get_height(&blit->src_area), + lv_area_get_width(&blit->dst_area), lv_area_get_height(&blit->dst_area), + (uintptr_t) blit->src, (uintptr_t) blit->dst); + + + uint32_t totalWidth = lv_area_get_width(&blit->src_area); + uint32_t totalHeight = lv_area_get_height(&blit->src_area); + + lv_gpu_nxp_vglite_blit_info_t tileBlit; + + /* Number of tiles needed */ + int totalTilesX = (blit->src_area.x1 + totalWidth + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) / + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR; + int totalTilesY = (blit->src_area.y1 + totalHeight + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) / + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR; + + /* src and dst buffer shift against each other. Src buffer real data [0,0] may start actually at [3,0] in buffer, as + * the buffer pointer has to be aligned, while dst buffer real data [0,0] may start at [1,0] in buffer. alignment may be + * different */ + int shiftSrcX = (blit->src_area.x1 > blit->dst_area.x1) ? (blit->src_area.x1 - blit->dst_area.x1) : 0; + int shiftDstX = (blit->src_area.x1 < blit->dst_area.x1) ? (blit->dst_area.x1 - blit->src_area.x1) : 0; + + PRINT_BLT("\n"); + PRINT_BLT("Align shift: src: %d, dst: %d\n", shiftSrcX, shiftDstX); + + tileBlit = *blit; + + for(int tileY = 0; tileY < totalTilesY; tileY++) { + + tileBlit.src_area.y1 = 0; /* no vertical alignment, always start from 0 */ + tileBlit.src_area.y2 = totalHeight - tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; + if(tileBlit.src_area.y2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) { + tileBlit.src_area.y2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; /* Should never happen */ + } + tileBlit.src = blit->src + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->src_stride / sizeof( + lv_color_t); /* stride in px! */ + + tileBlit.dst_area.y1 = tileBlit.src_area.y1; /* y has no alignment, always in sync with src */ + tileBlit.dst_area.y2 = tileBlit.src_area.y2; + + tileBlit.dst = blit->dst + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->dst_stride / sizeof( + lv_color_t); /* stride in px! */ + + for(int tileX = 0; tileX < totalTilesX; tileX++) { + + if(tileX == 0) { + /* 1st tile is special - there may be a gap between buffer start pointer + * and area.x1 value, as the pointer has to be aligned. + * tileBlit.src pointer - keep init value from Y-loop. + * Also, 1st tile start is not shifted! shift is applied from 2nd tile */ + tileBlit.src_area.x1 = blit->src_area.x1; + tileBlit.dst_area.x1 = blit->dst_area.x1; + } + else { + /* subsequent tiles always starts from 0, but shifted*/ + tileBlit.src_area.x1 = 0 + shiftSrcX; + tileBlit.dst_area.x1 = 0 + shiftDstX; + /* and advance start pointer + 1 tile size */ + tileBlit.src += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR; + tileBlit.dst += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR; + } + + /* Clip tile end coordinates */ + tileBlit.src_area.x2 = totalWidth + blit->src_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; + if(tileBlit.src_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) { + tileBlit.src_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; + } + + tileBlit.dst_area.x2 = totalWidth + blit->dst_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; + if(tileBlit.dst_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) { + tileBlit.dst_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; + } + + if(tileX < (totalTilesX - 1)) { + /* And adjust end coords if shifted, but not for last tile! */ + tileBlit.src_area.x2 += shiftSrcX; + tileBlit.dst_area.x2 += shiftDstX; + } + + rv = _lv_gpu_nxp_vglite_blit_single(&tileBlit); + +#if BLIT_DBG_AREAS + _draw_rectangle((lv_color_t *) tileBlit.dst, tileBlit.dst_width, tileBlit.dst_height, &tileBlit.dst_area, LV_COLOR_RED); + _draw_rectangle((lv_color_t *) tileBlit.src, tileBlit.src_width, tileBlit.src_height, &tileBlit.src_area, + LV_COLOR_GREEN); +#endif + + PRINT_BLT("Tile [%d, %d]: " + "([%d,%d], [%d,%d]) -> " + "([%d,%d], [%d,%d]) | " + "([%dx%d] -> [%dx%d]) | " + "A:(0x%8X -> 0x%8X) %s\n", + tileX, tileY, + tileBlit.src_area.x1, tileBlit.src_area.y1, tileBlit.src_area.x2, tileBlit.src_area.y2, + tileBlit.dst_area.x1, tileBlit.dst_area.y1, tileBlit.dst_area.x2, tileBlit.dst_area.y2, + lv_area_get_width(&tileBlit.src_area), lv_area_get_height(&tileBlit.src_area), + lv_area_get_width(&tileBlit.dst_area), lv_area_get_height(&tileBlit.dst_area), + (uintptr_t) tileBlit.src, (uintptr_t) tileBlit.dst, + rv == LV_RES_OK ? "OK!" : "!!! FAILED !!!"); + + if(rv != LV_RES_OK) { /* if anything goes wrong... */ +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("Split BLIT failed. Trying SW BLIT instead."); +#endif + _sw_blit(&tileBlit); + rv = LV_RES_OK; /* Don't report error, as SW BLIT was performed */ + } + + } + PRINT_BLT(" \n"); + } + + return rv; /* should never fail */ + +#else /* non RT595 */ + /* Just pass down */ + return _lv_gpu_nxp_vglite_blit_single(blit); +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/*** + * BLock Image Transfer - single direct BLIT. + * @param[in] blit Description of the transfer + * @retval LV_RES_OK Transfer complete + * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS) + */ +static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit) { vg_lite_buffer_t src_vgbuf, dst_vgbuf; vg_lite_error_t err = VG_LITE_SUCCESS; uint32_t rect[4]; lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - if(blit->opa < LV_OPA_MIN) { + if(blit == NULL) { + /*Wrong parameter*/ + return LV_RES_INV; + } + + if(blit->opa < (lv_opa_t) LV_OPA_MIN) { return LV_RES_OK; /*Nothing to BLIT*/ } - if(!blit) { - /*Wrong parameter*/ - return LV_RES_INV; - } - /*Wrap src/dst buffer into VG-Lite buffer*/ - if(init_vg_buf(&src_vgbuf, blit->src_width, blit->src_height, blit->src_stride, blit->src) != LV_RES_OK) { + if(_init_vg_buf(&src_vgbuf, (uint32_t) blit->src_width, (uint32_t) blit->src_height, (uint32_t) blit->src_stride, blit->src, true) != LV_RES_OK) { #if LV_GPU_NXP_VG_LITE_LOG_ERRORS LV_LOG_ERROR("init_vg_buf reported error. BLIT failed."); #endif return LV_RES_INV; } - if(init_vg_buf(&dst_vgbuf, blit->dst_width, blit->dst_height, blit->dst_stride, blit->dst) != LV_RES_OK) { + if(_init_vg_buf(&dst_vgbuf, (uint32_t) blit->dst_width, (uint32_t) blit->dst_height, (uint32_t) blit->dst_stride, blit->dst, false) != LV_RES_OK) { #if LV_GPU_NXP_VG_LITE_LOG_ERRORS LV_LOG_ERROR("init_vg_buf reported error. BLIT failed."); #endif return LV_RES_INV; } - rect[0] = 0; /*Crop*/ - rect[1] = 0; - rect[2] = blit->src_width; - rect[3] = blit->src_height; + rect[0] = (uint32_t) blit->src_area.x1; /* start x */ + rect[1] = (uint32_t) blit->src_area.y1; /* start y */ + rect[2] = (uint32_t) blit->src_area.x2 - (uint32_t) blit->src_area.x1 + 1U; /* width */ + rect[3] = (uint32_t) blit->src_area.y2 - (uint32_t) blit->src_area.y1 + 1U; /* height */ vg_lite_matrix_t matrix; vg_lite_identity(&matrix); - vg_lite_translate(blit->dst_area.x1, blit->dst_area.y1, &matrix); + vg_lite_translate((vg_lite_float_t)blit->dst_area.x1, (vg_lite_float_t)blit->dst_area.y1, &matrix); - if(disp && disp->driver->clean_dcache_cb) { /*Clean & invalidate cache*/ + if((disp != NULL) && (disp->driver->clean_dcache_cb != NULL)) { /*Clean & invalidate cache*/ disp->driver->clean_dcache_cb(disp->driver); } uint32_t color; vg_lite_blend_t blend; - if(blit->opa >= LV_OPA_MAX) { + if(blit->opa >= (uint8_t) LV_OPA_MAX) { color = 0x0; blend = VG_LITE_BLEND_NONE; } else { - color = ((blit->opa) << 24) | ((blit->opa) << 16) | ((blit->opa) << 8) | (blit->opa); + uint32_t opa = (uint32_t) blit->opa; + color = (opa << 24) | (opa << 16) | (opa << 8) | opa; blend = VG_LITE_BLEND_SRC_OVER; src_vgbuf.image_mode = VG_LITE_MULTIPLY_IMAGE_MODE; } - err |= vg_lite_blit_rect(&dst_vgbuf, &src_vgbuf, rect, &matrix, blend, color, VG_LITE_FILTER_POINT); - err |= vg_lite_finish(); + err = vg_lite_blit_rect(&dst_vgbuf, &src_vgbuf, rect, &matrix, blend, color, VG_LITE_FILTER_POINT); + if(err != VG_LITE_SUCCESS) { +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("vg_lite_blit_rect() failed."); +#endif + return LV_RES_INV; + } + + err = vg_lite_finish(); + if(err != VG_LITE_SUCCESS) { +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("vg_lite_finish() failed."); +#endif + return LV_RES_INV; + } if(err == VG_LITE_SUCCESS) { return LV_RES_OK; @@ -249,10 +525,6 @@ lv_res_t lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit) } } -/********************** - * STATIC FUNCTIONS - **********************/ - /*** * Fills vg_lite_buffer_t structure according given parameters. * @param[out] dst Buffer structure to be filled @@ -261,19 +533,19 @@ lv_res_t lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit) * @param[in] stride Stride of the buffer in bytes * @param[in] ptr Pointer to the buffer (must be aligned according VG-Lite requirements) */ -static lv_res_t init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t height, uint32_t stride, - const lv_color_t * ptr) +static lv_res_t _init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t height, uint32_t stride, + const lv_color_t * ptr, bool source) { - if((((uintptr_t)ptr) % LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0) { /*Test for alignment*/ + if((((uintptr_t)ptr) % (uintptr_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0U) { /*Test for alignment*/ #if LV_GPU_NXP_VG_LITE_LOG_ERRORS LV_LOG_ERROR("ptr (0x%X) not aligned to %d.", (size_t) ptr, LV_ATTRIBUTE_MEM_ALIGN_SIZE); #endif return LV_RES_INV; } - if((stride % LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) != 0x0) { /*Test for stride alignment*/ + if(source && (stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t))) != 0x0U) { /*Test for stride alignment*/ #if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("Buffer stride (%d px) not aligned to %d px.", stride, LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); + LV_LOG_ERROR("Buffer stride (%d px) not aligned to %d bytes.", stride, LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t)); #endif return LV_RES_INV; } @@ -283,17 +555,205 @@ static lv_res_t init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t hei dst->image_mode = VG_LITE_NORMAL_IMAGE_MODE; dst->transparency_mode = VG_LITE_IMAGE_OPAQUE; - dst->width = width; - dst->height = height; - dst->stride = stride; + dst->width = (int32_t) width; + dst->height = (int32_t) height; + dst->stride = (int32_t) stride; - memset(&dst->yuv, 0, sizeof(dst->yuv)); + void *r_ptr = memset(&dst->yuv, 0, sizeof(dst->yuv)); + if (r_ptr == NULL) { + return LV_RES_INV; + } dst->memory = (void *)ptr; dst->address = (uint32_t) dst->memory; - dst->handle = 0x0; + dst->handle = NULL; return LV_RES_OK; } +#if _BLIT_SPLIT_ENABLED + +/** + * Software BLIT as a fall-back scenario + * @param[in] blit BLIT configuration + */ +static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit) +{ + int x, y; + + lv_coord_t w = lv_area_get_width(&blit->src_area); + lv_coord_t h = lv_area_get_height(&blit->src_area); + + uint32_t srcStridePx = blit->src_stride / sizeof(lv_color_t); + uint32_t dstStridePx = blit->dst_stride / sizeof(lv_color_t); + + lv_color_t * src = (lv_color_t *)blit->src + blit->src_area.y1 * srcStridePx + blit->src_area.x1; + lv_color_t * dst = (lv_color_t *)blit->dst + blit->dst_area.y1 * dstStridePx + blit->dst_area.x1; + + if(blit->opa >= LV_OPA_MAX) { + /* simple copy */ + for(y = 0; y < h; y++) { + _lv_memcpy(dst, src, w * sizeof(lv_color_t)); + src += srcStridePx; + dst += dstStridePx; + } + } + else if(blit->opa >= LV_OPA_MIN) { + /* alpha blending */ + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + dst[x] = lv_color_mix(src[x], dst[x], blit->opa); + } + src += srcStridePx; + dst += dstStridePx; + } + } +} + +/** + * Verify BLIT structure - widths, stride, pointer alignment + * @param[in] blit + * @return + */ +static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit) +{ + + if(lv_area_get_width(&blit->src_area) < LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) { /* Test for minimal width */ +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("source area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->src_area), + LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); +#endif + return LV_RES_INV; + } + + if(lv_area_get_width(&blit->dst_area) < LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) { /* Test for minimal width */ +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("destination area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->dst_area), + LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); +#endif + return LV_RES_INV; + } + + if((((uintptr_t) blit->src) % LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0) { /* Test for pointer alignment */ +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("source buffer ptr (0x%X) not aligned to %d.", (size_t) blit->src, LV_ATTRIBUTE_MEM_ALIGN_SIZE); +#endif + return LV_RES_INV; + } + /* No alignment requirement for destination pixel buffer when using mode VG_LITE_LINEAR */ + + if((blit->src_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) != 0x0) { /* Test for stride alignment */ +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("source buffer stride (%d px) not aligned to %d px.", blit->src_stride, LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); +#endif + return LV_RES_INV; + } + + if((blit->dst_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) != 0x0) { /* Test for stride alignment */ +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("destination buffer stride (%d px) not aligned to %d px.", blit->dst_stride, LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); +#endif + return LV_RES_INV; + } + + if((lv_area_get_width(&blit->src_area) != lv_area_get_width(&blit->dst_area)) || + (lv_area_get_height(&blit->src_area) != lv_area_get_height(&blit->dst_area))) { +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("source and destination buffer areas are not equal."); +#endif + return LV_RES_INV; + } + + return LV_RES_OK; +} + +/*** + * Move buffer pointer as close as possible to area, but with respect to alignment requirements. X-axis only. + * @param[in,out] area Area to be updated + * @param[in,out] buf Pointer to be updated + */ +static void _align_x(lv_area_t * area, lv_color_t ** buf) +{ + + int alignedAreaStartPx = area->x1 - (area->x1 % (LV_ATTRIBUTE_MEM_ALIGN_SIZE * 8 / LV_COLOR_DEPTH) ); + CHECK(alignedAreaStartPx < 0, "Should never happen."); + + area->x1 -= alignedAreaStartPx; + area->x2 -= alignedAreaStartPx; + *buf += alignedAreaStartPx; +} + +/*** + * Move buffer pointer to the area start and update variables, Y-axis only. + * @param[in,out] area Area to be updated + * @param[in,out] buf Pointer to be updated + * @param[in] stridePx Buffer stride in pixels + */ +static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx) +{ + int LineToAlignMem; + int alignedAreaStartPy; + /* find how many lines of pixels will respect memory alignment requirement */ + if(stridePx % LV_ATTRIBUTE_MEM_ALIGN_SIZE == 0) { + alignedAreaStartPy = area->y1; + } else { + LineToAlignMem = LV_ATTRIBUTE_MEM_ALIGN_SIZE / (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); + CHECK(LV_ATTRIBUTE_MEM_ALIGN_SIZE % (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) != 0, "Complex case: need gcd function."); + alignedAreaStartPy = area->y1 - (area->y1 % LineToAlignMem); + CHECK(alignedAreaStartPy < 0, "Should never happen."); + } + + area->y1 -= alignedAreaStartPy; + area->y2 -= alignedAreaStartPy; + *buf += alignedAreaStartPy * stridePx; +} + +#if BLIT_DBG_AREAS +/*** + * Draws a simple rectangle, 1 px line width. + * @param dest_buf Destination buffer + * @param dest_width Destination buffer width (must be aligned on 16px) + * @param dest_height Destination buffer height + * @param fill_area Rectangle coordinates + * @param color Rectangle color + */ +static void _draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height, + lv_area_t * fill_area, lv_color_t color) +{ + + lv_area_t a; + + /* top line */ + a.x1 = fill_area->x1; + a.x2 = fill_area->x2; + a.y1 = fill_area->y1; + a.y2 = fill_area->y1; + lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER); + + + /* bottom line */ + a.x1 = fill_area->x1; + a.x2 = fill_area->x2; + a.y1 = fill_area->y2; + a.y2 = fill_area->y2; + lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER); + + /* left line */ + a.x1 = fill_area->x1; + a.x2 = fill_area->x1; + a.y1 = fill_area->y1; + a.y2 = fill_area->y2; + lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER); + + /* right line */ + a.x1 = fill_area->x2; + a.x2 = fill_area->x2; + a.y1 = fill_area->y1; + a.y2 = fill_area->y2; + lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER); +} +#endif /* BLIT_DBG_AREAS */ + +#endif /* _BLIT_SPLIT_ENABLED */ + #endif /*LV_USE_GPU_NXP_VG_LITE*/ diff --git a/src/gpu/lv_gpu_nxp_vglite.h b/src/gpu/lv_gpu_nxp_vglite.h index 3f8b717346..31416598ea 100644 --- a/src/gpu/lv_gpu_nxp_vglite.h +++ b/src/gpu/lv_gpu_nxp_vglite.h @@ -43,8 +43,11 @@ extern "C" { * DEFINES *********************/ -/** Stride in px required by VG-Lite HW. Don't change this.*/ -#define LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX 16 +/** Use this symbol as limit to disable feature (value has to be larger than supported resolution) */ +#define LV_GPU_NXP_VG_LITE_FEATURE_DISABLED (1920*1080+1) + +/** Stride in px required by VG-Lite HW. Don't change this. */ +#define LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX 16U #ifndef LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT /** Minimum area (in pixels) to be filled by VG-Lite with 100% opacity*/ @@ -61,6 +64,11 @@ extern "C" { #define LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT 32 #endif +#ifndef LV_GPU_NXP_VG_LITE_BUFF_SYNC_BLIT_SIZE_LIMIT +/** Minimum invalidated area (in pixels) to be synchronized by VG-Lite during buffer sync */ +#define LV_GPU_NXP_VG_LITE_BUFF_SYNC_BLIT_SIZE_LIMIT 32 +#endif + #ifndef LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT /** Minimum area (in pixels) for image copy with transparency to be handled by VG-Lite*/ #define LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT 32