From b07bd68046033318bd3a20481c7049693d77b660 Mon Sep 17 00:00:00 2001 From: Thomas Reitmayr Date: Sun, 9 Feb 2025 21:58:50 +0100 Subject: [PATCH] Utilize ESP-IDF's LCD driver for pushing pixels to RGB displays with ESP32-S3 (#22970) This commit is an optimization of code and possibly performance for pushing pixel data to an RGB display with an ESP32-S3. It uses ESP-IDF's LCD driver for doing so and replaces multiple code paths in the previous implementation. No code for other ESP32 variants has been changed. Also see the discussion arendst/Tasmota#22553 --- lib/lib_display/UDisplay/uDisplay.cpp | 93 +++++++++------------------ 1 file changed, 29 insertions(+), 64 deletions(-) diff --git a/lib/lib_display/UDisplay/uDisplay.cpp b/lib/lib_display/UDisplay/uDisplay.cpp index 96b59e3dd..c998da0ab 100755 --- a/lib/lib_display/UDisplay/uDisplay.cpp +++ b/lib/lib_display/UDisplay/uDisplay.cpp @@ -1225,12 +1225,19 @@ Renderer *uDisplay::Init(void) { _panel_config->de_gpio_num = de; _panel_config->pclk_gpio_num = pclk; + // assume that byte swapping of 16-bit color is done only upon request + // via display.ini and not by callers of pushColor() + // -> swap bytes by swapping GPIO numbers + int8_t *par_db8 = lvgl_param.swap_color ? par_dbl : par_dbh; for (uint32_t cnt = 0; cnt < 8; cnt ++) { - _panel_config->data_gpio_nums[cnt] = par_dbh[cnt]; + _panel_config->data_gpio_nums[cnt] = par_db8[cnt]; } + par_db8 = lvgl_param.swap_color ? par_dbh : par_dbl; for (uint32_t cnt = 0; cnt < 8; cnt ++) { - _panel_config->data_gpio_nums[cnt + 8] = par_dbl[cnt]; + _panel_config->data_gpio_nums[cnt + 8] = par_db8[cnt]; } + lvgl_param.swap_color = 0; + _panel_config->disp_gpio_num = GPIO_NUM_NC; _panel_config->flags.disp_active_low = 0; @@ -1781,9 +1788,6 @@ void uDisplay::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { if (interface == _UDSP_RGB) { #ifdef USE_ESP32_S3 - if (lvgl_param.swap_color) { - color = color << 8 | color >> 8; - } if (cur_rot > 0) { while (h--) { drawPixel_RGB(x , y , color); @@ -1854,9 +1858,6 @@ void uDisplay::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { if (interface == _UDSP_RGB) { #ifdef USE_ESP32_S3 - if (lvgl_param.swap_color) { - color = color << 8 | color >> 8; - } if (cur_rot > 0) { while (w--) { drawPixel_RGB(x , y , color); @@ -2228,7 +2229,6 @@ void uDisplay::pushColorsMono(uint16_t *data, uint16_t len, bool rgb16_swap) { static inline void lvgl_color_swap(uint16_t *data, uint16_t len) { for (uint32_t i = 0; i < len; i++) (data[i] = data[i] << 8 | data[i] >> 8); } void uDisplay::pushColors(uint16_t *data, uint16_t len, boolean not_swapped) { - uint16_t color; if (lvgl_param.swap_color) { not_swapped = !not_swapped; @@ -2238,63 +2238,21 @@ void uDisplay::pushColors(uint16_t *data, uint16_t len, boolean not_swapped) { // Isolating _UDSP_RGB to increase code sharing // - // LVGL documentation suggest to call the following: - // lv_draw_sw_rgb565_swap() to invert bytes - // esp_lcd_panel_draw_bitmap() to paste bytes - // but it appears to be faster to include the color swap in the copy loop - // because the CPU is much faster than PSRAM (SPI bus), therefore - // swapping bytes on the fly costs zero performance - // - // not_swapped == false : called from LVGL bytes are swapped - // not_swapped == true : called from displaytext, no byte swap, currently no dma here + // Use ESP-IDF LCD driver to push colors and rely on the following assumptions: + // * bytes swapping is already handled in the driver configuration (see uDisplay::Init()), + // * pushColors() is only called with not_swapped equals true, + // * cache flushing is done by the LCD driver. if (interface == _UDSP_RGB) { #ifdef USE_ESP32_S3 + if (!not_swapped) { + // internal error -> write error message but continue (with possibly wrong colors) + AddLog(LOG_LEVEL_ERROR, PSTR("DSP: Unexpected byte-swapping requested in pushColors()")); + } + // check that bytes count matches the size of area, and remove from inner loop if ((seta_yp2 - seta_yp1) * (seta_xp2 - seta_xp2) > len) { return; } - uint16_t lenc = len; - - if (cur_rot > 0) { - for (uint32_t y = seta_yp1; y < seta_yp2; y++) { - seta_yp1++; - for (uint32_t x = seta_xp1; x < seta_xp2; x++) { - uint16_t color = *data++; - if (!not_swapped) { color = color << 8 | color >> 8; } - drawPixel_RGB(x, y, color); - lenc--; - if (!lenc) return; // failsafe - exist if len (pixel number) is exhausted - } - } - } else { - uint16_t *fb_y = rgb_fb + (int32_t)seta_yp1 * _width; - for (uint32_t y = seta_yp1; y < seta_yp2; y++) { - uint16_t * fb_xy = fb_y + seta_xp1; - // we get the 'not_swapped' test outside of the inner loop - if (not_swapped) { - for (uint32_t x = seta_xp1; x < seta_xp2; x++) { - uint16_t color = *data++; - *fb_xy = color; - fb_xy++; - lenc--; - if (!lenc) break; // failsafe - exist if len (pixel number) is exhausted - } - } else { - for (uint32_t x = seta_xp1; x < seta_xp2; x++) { - uint16_t color = *data++; - color = color << 8 | color >> 8; - *fb_xy = color; - fb_xy++; - lenc--; - if (!lenc) break; // failsafe - exist if len (pixel number) is exhausted - } - } - uint16_t * flush_ptr = rgb_fb + (int32_t)seta_yp1 * _width + seta_xp1; - Cache_WriteBack_Addr((uint32_t)flush_ptr, (seta_xp2 - seta_xp1) * 2); - fb_y += _width; - seta_yp1++; - if (!lenc) break; - } - } + esp_lcd_panel_draw_bitmap(_panel_handle, seta_xp1, seta_yp1, seta_xp2, seta_yp2, (void *)data); #endif return; } @@ -2329,6 +2287,7 @@ void uDisplay::pushColors(uint16_t *data, uint16_t len, boolean not_swapped) { uint8_t *line = (uint8_t*)malloc(len * 3); uint8_t *lp = line; if (line) { + uint16_t color; for (uint32_t cnt = 0; cnt < len; cnt++) { color = *data++; color = (color << 8) | (color >> 8); @@ -2463,9 +2422,6 @@ void uDisplay::drawPixel(int16_t x, int16_t y, uint16_t color) { #ifdef USE_ESP32_S3 if (interface == _UDSP_RGB) { - if (lvgl_param.swap_color) { - color = color << 8 | color >> 8; - } drawPixel_RGB(x, y, color); return; } @@ -2548,6 +2504,15 @@ void uDisplay::setRotation(uint8_t rotation) { break; } +#ifdef USE_ESP32_S3 + if (interface == _UDSP_RGB) { + // utilize the ESP-IDF LCD driver's support for display rotation: + // mirror x axis for rotation 1 and 2, mirror y axis for rotation 2 and 3 + esp_lcd_panel_mirror(_panel_handle, rotation == 1 || rotation == 2, rotation & 2); + // swap x/y for rotation 1 and 3 + esp_lcd_panel_swap_xy(_panel_handle, rotation & 1); + } +#endif // USE_ESP32_S3 } void udisp_bpwr(uint8_t on);