From cfd777dca4920b82bea01124d450a34a47311c0b Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sat, 17 Feb 2018 15:43:04 +0100 Subject: [PATCH] WIP display doublebuffer --- embed/extmod/modtrezorui/display-stm32.h | 173 ++++++++++++++++++++++- embed/extmod/modtrezorui/display-unix.h | 30 +++- embed/extmod/modtrezorui/display.c | 12 -- src/trezor/ui/__init__.py | 4 +- 4 files changed, 201 insertions(+), 18 deletions(-) diff --git a/embed/extmod/modtrezorui/display-stm32.h b/embed/extmod/modtrezorui/display-stm32.h index a282133e..1dce59a8 100644 --- a/embed/extmod/modtrezorui/display-stm32.h +++ b/embed/extmod/modtrezorui/display-stm32.h @@ -21,9 +21,66 @@ #define CMD(X) (*((__IO uint8_t *)((uint32_t)(DISPLAY_MEMORY_BASE))) = (X)) #define DATA(X) (*((__IO uint8_t *)((uint32_t)(DISPLAY_MEMORY_BASE | (1 << DISPLAY_MEMORY_PIN)))) = (X)) -#define PIXELDATA(X) DATA((X) >> 8); DATA((X) & 0xFF) -#define LED_PWM_TIM_PERIOD (10000) +#define DOUBLE_BUFFER 1 + +#if DOUBLE_BUFFER +__IO uint8_t *DBUF = (__IO uint8_t *)CCMDATARAM_BASE; + +secbool DBUF_DIRTY = sectrue; + +static struct { + struct { + uint16_t x, y; + } start; + struct { + uint16_t x, y; + } end; + struct { + uint16_t x, y; + } pos; +} PIXELWINDOW; + +static void PIXELDATA(uint16_t c) +{ + // convert from 16-bit depth to 8-bit depth + // (RBB: 5-6-5 bits to 3-3-2 bits: rrrrrggg gggbbbbb -> rrrgggbb) + if (PIXELWINDOW.pos.x <= PIXELWINDOW.end.x && PIXELWINDOW.pos.y <= PIXELWINDOW.end.y) { + const int i = PIXELWINDOW.pos.x + PIXELWINDOW.pos.y * DISPLAY_RESX; + if (i < DISPLAY_RESX * DISPLAY_RESY) { + const uint8_t d = ((c & 0xE000) >> 8) | ((c & 0x700) >> 6) | ((c & 0x18) >> 3); + DBUF[i] = d; + DBUF_DIRTY = sectrue; + } + } + PIXELWINDOW.pos.x++; + if (PIXELWINDOW.pos.x > PIXELWINDOW.end.x) { + PIXELWINDOW.pos.x = PIXELWINDOW.start.x; + PIXELWINDOW.pos.y++; + } +} +#else +#define PIXELDATA(X) DATA((X) >> 8); DATA((X) & 0xFF) +#endif + +static void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); + +void display_clear(void) +{ + const int saved_orientation = DISPLAY_ORIENTATION; + display_orientation(0); // set MADCTL first so that we can set the window correctly next + display_set_window(0, 0, MAX_DISPLAY_RESX - 1, MAX_DISPLAY_RESY - 1); // address the complete frame memory + for (uint32_t i = 0; i < MAX_DISPLAY_RESX * MAX_DISPLAY_RESY; i++) { + DATA(0x00); DATA(0x00); // 2 bytes per pixel because we're using RGB 5-6-5 format + } + display_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); // go back to restricted window + display_orientation(saved_orientation); // if valid, go back to the saved orientation +#if DOUBLE_BUFFER + for (int i = 0; i < DISPLAY_RESX * DISPLAY_RESY; i++) { + PIXELDATA(0x0000); + } +#endif +} static void __attribute__((unused)) display_sleep(void) { @@ -51,15 +108,28 @@ static void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y { x0 += BUFFER_OFFSET.x; x1 += BUFFER_OFFSET.x; y0 += BUFFER_OFFSET.y; y1 += BUFFER_OFFSET.y; + +#if DOUBLE_BUFFER + PIXELWINDOW.start.x = x0; PIXELWINDOW.start.y = y0; + PIXELWINDOW.end.x = x1; PIXELWINDOW.end.y = y1; + PIXELWINDOW.pos.x = x0; PIXELWINDOW.pos.y = y0; +#else + #if DISPLAY_ILI9341V || DISPLAY_ST7789V CMD(0x2A); DATA(x0 >> 8); DATA(x0 & 0xFF); DATA(x1 >> 8); DATA(x1 & 0xFF); // column addr set CMD(0x2B); DATA(y0 >> 8); DATA(y0 & 0xFF); DATA(y1 >> 8); DATA(y1 & 0xFF); // row addr set CMD(0x2C); #endif + +#endif } void display_set_orientation(int degrees) { +#if DOUBLE_BUFFER + +#else + #if DISPLAY_ILI9341V || DISPLAY_ST7789V #define MV (1 << 5) #define MX (1 << 6) @@ -88,8 +158,12 @@ void display_set_orientation(int degrees) CMD(0x36); DATA(display_command_parameter); display_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); // reset the column and page extents #endif + +#endif } +#define LED_PWM_TIM_PERIOD (10000) + void display_set_backlight(int val) { TIM1->CCR1 = LED_PWM_TIM_PERIOD * val / 255; @@ -257,6 +331,101 @@ void display_refresh(void) // synchronize with the panel synchronization signal in order to avoid visual tearing effects while (GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_12)) { } while (GPIO_PIN_SET == HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_12)) { } + +#if DOUBLE_BUFFER + + // don't draw if not dirty + if (sectrue != DBUF_DIRTY) return; + + // frame limiter = don't redraw frame if older one is younger than 16 ms = 60 fps + static uint32_t t0 = 0; + uint32_t t1 = HAL_GetTick(); + if (t1 < t0 + 16) { + t0 = t1; + return; + } else { + t0 = t1; + } + + static const uint16_t rgb332to565lut[256] = { + 0x0000, 0x000a, 0x0015, 0x001f, 0x0120, 0x012a, 0x0135, 0x013f, + 0x0240, 0x024a, 0x0255, 0x025f, 0x0360, 0x036a, 0x0375, 0x037f, + 0x0480, 0x048a, 0x0495, 0x049f, 0x05a0, 0x05aa, 0x05b5, 0x05bf, + 0x06c0, 0x06ca, 0x06d5, 0x06df, 0x07e0, 0x07ea, 0x07f5, 0x07ff, + 0x2000, 0x200a, 0x2015, 0x201f, 0x2120, 0x212a, 0x2135, 0x213f, + 0x2240, 0x224a, 0x2255, 0x225f, 0x2360, 0x236a, 0x2375, 0x237f, + 0x2480, 0x248a, 0x2495, 0x249f, 0x25a0, 0x25aa, 0x25b5, 0x25bf, + 0x26c0, 0x26ca, 0x26d5, 0x26df, 0x27e0, 0x27ea, 0x27f5, 0x27ff, + 0x4800, 0x480a, 0x4815, 0x481f, 0x4920, 0x492a, 0x4935, 0x493f, + 0x4a40, 0x4a4a, 0x4a55, 0x4a5f, 0x4b60, 0x4b6a, 0x4b75, 0x4b7f, + 0x4c80, 0x4c8a, 0x4c95, 0x4c9f, 0x4da0, 0x4daa, 0x4db5, 0x4dbf, + 0x4ec0, 0x4eca, 0x4ed5, 0x4edf, 0x4fe0, 0x4fea, 0x4ff5, 0x4fff, + 0x6800, 0x680a, 0x6815, 0x681f, 0x6920, 0x692a, 0x6935, 0x693f, + 0x6a40, 0x6a4a, 0x6a55, 0x6a5f, 0x6b60, 0x6b6a, 0x6b75, 0x6b7f, + 0x6c80, 0x6c8a, 0x6c95, 0x6c9f, 0x6da0, 0x6daa, 0x6db5, 0x6dbf, + 0x6ec0, 0x6eca, 0x6ed5, 0x6edf, 0x6fe0, 0x6fea, 0x6ff5, 0x6fff, + 0x9000, 0x900a, 0x9015, 0x901f, 0x9120, 0x912a, 0x9135, 0x913f, + 0x9240, 0x924a, 0x9255, 0x925f, 0x9360, 0x936a, 0x9375, 0x937f, + 0x9480, 0x948a, 0x9495, 0x949f, 0x95a0, 0x95aa, 0x95b5, 0x95bf, + 0x96c0, 0x96ca, 0x96d5, 0x96df, 0x97e0, 0x97ea, 0x97f5, 0x97ff, + 0xb000, 0xb00a, 0xb015, 0xb01f, 0xb120, 0xb12a, 0xb135, 0xb13f, + 0xb240, 0xb24a, 0xb255, 0xb25f, 0xb360, 0xb36a, 0xb375, 0xb37f, + 0xb480, 0xb48a, 0xb495, 0xb49f, 0xb5a0, 0xb5aa, 0xb5b5, 0xb5bf, + 0xb6c0, 0xb6ca, 0xb6d5, 0xb6df, 0xb7e0, 0xb7ea, 0xb7f5, 0xb7ff, + 0xd800, 0xd80a, 0xd815, 0xd81f, 0xd920, 0xd92a, 0xd935, 0xd93f, + 0xda40, 0xda4a, 0xda55, 0xda5f, 0xdb60, 0xdb6a, 0xdb75, 0xdb7f, + 0xdc80, 0xdc8a, 0xdc95, 0xdc9f, 0xdda0, 0xddaa, 0xddb5, 0xddbf, + 0xdec0, 0xdeca, 0xded5, 0xdedf, 0xdfe0, 0xdfea, 0xdff5, 0xdfff, + 0xf800, 0xf80a, 0xf815, 0xf81f, 0xf920, 0xf92a, 0xf935, 0xf93f, + 0xfa40, 0xfa4a, 0xfa55, 0xfa5f, 0xfb60, 0xfb6a, 0xfb75, 0xfb7f, + 0xfc80, 0xfc8a, 0xfc95, 0xfc9f, 0xfda0, 0xfdaa, 0xfdb5, 0xfdbf, + 0xfec0, 0xfeca, 0xfed5, 0xfedf, 0xffe0, 0xffea, 0xfff5, 0xffff, + }; + // set full window + const uint16_t x0 = 0, y0 = 0, x1 = DISPLAY_RESX - 1, y1 = DISPLAY_RESY - 1; + CMD(0x2A); DATA(x0 >> 8); DATA(x0 & 0xFF); DATA(x1 >> 8); DATA(x1 & 0xFF); // column addr set + CMD(0x2B); DATA(y0 >> 8); DATA(y0 & 0xFF); DATA(y1 >> 8); DATA(y1 & 0xFF); // row addr set + CMD(0x2C); + + // flush double buffer according to orientation + // also convert from 8-bit depth to 16-bit depth + // (RBB: 3-3-2 bits to 5-6-5 bits: rrrgggbb -> rrrrrggg gggbbbbb) + switch (DISPLAY_ORIENTATION) { + case 0: + for (int i = 0; i < DISPLAY_RESX * DISPLAY_RESY; i++) { + const uint16_t d = rgb332to565lut[DBUF[i]]; + DATA(d >> 8); DATA(d & 0xFF); + } + break; + case 90: + for (int j = 0; j < DISPLAY_RESY; j++) { + for (int i = 0; i < DISPLAY_RESX; i++) { + const int o = (DISPLAY_RESX - 1 - i) * DISPLAY_RESY + j; + const uint16_t d = rgb332to565lut[DBUF[o]]; + DATA(d >> 8); DATA(d & 0xFF); + } + } + break; + case 180: + for (int i = 0; i < DISPLAY_RESX * DISPLAY_RESY; i++) { + const int o = DISPLAY_RESX * DISPLAY_RESY - 1 - i; + const uint16_t d = rgb332to565lut[DBUF[o]]; + DATA(d >> 8); DATA(d & 0xFF); + } + break; + case 270: + for (int j = 0; j < DISPLAY_RESY; j++) { + for (int i = 0; i < DISPLAY_RESX; i++) { + const int o = i * DISPLAY_RESY + (DISPLAY_RESY - 1 - j); + const uint16_t d = rgb332to565lut[DBUF[o]]; + DATA(d >> 8); DATA(d & 0xFF); + } + } + break; + } + + DBUF_DIRTY = secfalse; +#endif } void display_save(const char *filename) diff --git a/embed/extmod/modtrezorui/display-unix.h b/embed/extmod/modtrezorui/display-unix.h index de9dd535..3b0613c2 100644 --- a/embed/extmod/modtrezorui/display-unix.h +++ b/embed/extmod/modtrezorui/display-unix.h @@ -30,8 +30,25 @@ void PIXELDATA(uint16_t c) { if (!RENDERER) { display_init(); } + + int r = ((c & 0xF800) >> 11) * 255 / 31; + int g = ((c & 0x07E0) >> 5) * 255 / 63; + int b = (c & 0x1F) * 255 / 31; + +#define BITS_R 3 +#define BITS_G 3 +#define BITS_B 2 + + r >>= (8 - BITS_R); + g >>= (8 - BITS_G); + b >>= (8 - BITS_B); + + r = r * 255 / ((1 << BITS_R) - 1); + g = g * 255 / ((1 << BITS_G) - 1); + b = b * 255 / ((1 << BITS_B) - 1); + if (PIXELWINDOW.pos.x <= PIXELWINDOW.end.x && PIXELWINDOW.pos.y <= PIXELWINDOW.end.y) { - ((uint16_t *)BUFFER->pixels)[PIXELWINDOW.pos.x + PIXELWINDOW.pos.y * BUFFER->pitch / sizeof(uint16_t)] = c; + ((uint16_t *)BUFFER->pixels)[PIXELWINDOW.pos.x + PIXELWINDOW.pos.y * BUFFER->pitch / sizeof(uint16_t)] = RGB16(r, g, b); } PIXELWINDOW.pos.x++; if (PIXELWINDOW.pos.x > PIXELWINDOW.end.x) { @@ -43,6 +60,17 @@ void PIXELDATA(uint16_t c) { #define PIXELDATA(X) (void)(X) #endif +void display_clear(void) +{ +#ifndef TREZOR_NOUI + if (!RENDERER) { + display_init(); + } + SDL_FillRect(BUFFER, NULL, SDL_MapRGB(BUFFER->format, 0, 0, 0)); + SDL_UpdateTexture(TEXTURE, NULL, BUFFER->pixels, BUFFER->pitch); +#endif +} + void display_init(void) { #ifndef TREZOR_NOUI diff --git a/embed/extmod/modtrezorui/display.c b/embed/extmod/modtrezorui/display.c index 7718d2c5..f525f2ed 100644 --- a/embed/extmod/modtrezorui/display.c +++ b/embed/extmod/modtrezorui/display.c @@ -67,18 +67,6 @@ static inline void clamp_coords(int x, int y, int w, int h, int *x0, int *y0, in *y1 = MIN(y + h - 1, DISPLAY_RESY - 1); } -void display_clear(void) -{ - const int saved_orientation = DISPLAY_ORIENTATION; - display_orientation(0); // set MADCTL first so that we can set the window correctly next - display_set_window(0, 0, MAX_DISPLAY_RESX - 1, MAX_DISPLAY_RESY - 1); // address the complete frame memory - for (uint32_t i = 0; i < MAX_DISPLAY_RESX * MAX_DISPLAY_RESY; i++) { - PIXELDATA(0x0000); // 2 bytes per pixel because we're using RGB 5-6-5 format - } - display_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); // go back to restricted window - display_orientation(saved_orientation); // if valid, go back to the saved orientation -} - void display_bar(int x, int y, int w, int h, uint16_t c) { x += DISPLAY_OFFSET.x; diff --git a/src/trezor/ui/__init__.py b/src/trezor/ui/__init__.py index 493287f2..15e413a2 100644 --- a/src/trezor/ui/__init__.py +++ b/src/trezor/ui/__init__.py @@ -13,9 +13,7 @@ from trezor import workflow display = Display() -# for desktop platforms, we need to refresh the display after each frame -if sys.platform != 'trezor': - loop.after_step_hook = display.refresh +loop.after_step_hook = display.refresh # import constants from modtrezorui