Hub75: Performance improvements and stacked mode.

Inline and simplify the pixel flip for a 13.2ms -> 3.8ms speedup at 128x128 on RP2350 stock.

Drop RGB565 mode.

Add the ability to stack some panels, eg: 2x128x64 in a 128x127 configuration.
patch-img-open_file
Phil Howard 2024-11-21 20:58:23 +00:00
rodzic 515521c16c
commit 44ffeeed3f
3 zmienionych plików z 133 dodań i 42 usunięć

Wyświetl plik

@ -37,10 +37,48 @@ Hub75::Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool
} }
if (brightness == 0) { if (brightness == 0) {
#if PICO_RP2350
brightness = 6;
#else
if (width >= 64) brightness = 6; if (width >= 64) brightness = 6;
if (width >= 96) brightness = 3; if (width >= 96) brightness = 3;
if (width >= 128) brightness = 2; if (width >= 128) brightness = 2;
if (width >= 160) brightness = 1; if (width >= 160) brightness = 1;
#endif
}
switch (color_order) {
case COLOR_ORDER::RGB:
r_shift = 0;
g_shift = 10;
b_shift = 20;
break;
case COLOR_ORDER::RBG:
r_shift = 0;
g_shift = 20;
b_shift = 10;
break;
case COLOR_ORDER::GRB:
r_shift = 20;
g_shift = 0;
b_shift = 10;
break;
case COLOR_ORDER::GBR:
r_shift = 10;
g_shift = 20;
b_shift = 0;
break;
case COLOR_ORDER::BRG:
r_shift = 10;
g_shift = 00;
b_shift = 20;
break;
case COLOR_ORDER::BGR:
r_shift = 20;
g_shift = 10;
b_shift = 0;
break;
} }
} }
@ -58,26 +96,16 @@ void Hub75::set_color(uint x, uint y, Pixel c) {
} }
void Hub75::set_pixel(uint x, uint y, uint8_t r, uint8_t g, uint8_t b) { void Hub75::set_pixel(uint x, uint y, uint8_t r, uint8_t g, uint8_t b) {
switch(color_order) { int offset = 0;
case COLOR_ORDER::RGB: if(x >= width || y >= height) return;
set_color(x, y, Pixel(r, g, b)); if(y >= height / 2) {
break; y -= height / 2;
case COLOR_ORDER::RBG: offset = (y * width + x) * 2;
set_color(x, y, Pixel(r, b, g)); offset += 1;
break; } else {
case COLOR_ORDER::GRB: offset = (y * width + x) * 2;
set_color(x, y, Pixel(g, r, b));
break;
case COLOR_ORDER::GBR:
set_color(x, y, Pixel(g, b, r));
break;
case COLOR_ORDER::BRG:
set_color(x, y, Pixel(b, r, g));
break;
case COLOR_ORDER::BGR:
set_color(x, y, Pixel(b, g, r));
break;
} }
back_buffer[offset] = (GAMMA_10BIT[b] << b_shift) | (GAMMA_10BIT[g] << g_shift) | (GAMMA_10BIT[r] << r_shift);
} }
void Hub75::FM6126A_write_register(uint16_t value, uint8_t position) { void Hub75::FM6126A_write_register(uint16_t value, uint8_t position) {
@ -247,28 +275,79 @@ void Hub75::dma_complete() {
void Hub75::update(PicoGraphics *graphics) { void Hub75::update(PicoGraphics *graphics) {
if(graphics->pen_type == PicoGraphics::PEN_RGB888) { if(graphics->pen_type == PicoGraphics::PEN_RGB888) {
uint32_t *p = (uint32_t *)graphics->frame_buffer; uint8_t *p = (uint8_t *)graphics->frame_buffer;
for(uint y = 0; y < height; y++) { if(graphics->bounds.w == int32_t(width / 2) && graphics->bounds.h == int32_t(height * 2)) {
for(uint x = 0; x < width; x++) { for(int y = 0; y < graphics->bounds.h; y++) {
uint32_t col = *p; for(int x = 0; x < graphics->bounds.w; x++) {
uint8_t r = (col & 0xff0000) >> 16; int offset = 0;
uint8_t g = (col & 0x00ff00) >> 8; int sy = y;
uint8_t b = (col & 0x0000ff) >> 0; int sx = x;
set_pixel(x, y, r, g, b); uint8_t b = *p++;
p++; uint8_t g = *p++;
uint8_t r = *p++;
// Assuming our canvas is 128x128 and our display is 256x64,
// consisting of 2x128x64 panels, remap the bottom half
// of the canvas to the right-half of the display,
// This gives us an optional square arrangement.
if (sy >= int(height)) {
sy -= height;
sx += width / 2;
} else {
// Awkward hack to *TEMPORARILY* rotate the top panel
sy = height - 1 - sy;
sx = (width / 2) - 1 - sx;
}
// Interlace the top and bottom halves of the panel.
// Since these are scanned out simultaneously to two chains
// of shift registers we need each pair of rows
// (N and N + height / 2) to be adjacent in the buffer.
offset = width * 2;
if(sy >= int(height / 2)) {
sy -= height / 2;
offset *= sy;
offset += 1;
} else {
offset *= sy;
}
offset += sx * 2;
back_buffer[offset] = (GAMMA_10BIT[b] << b_shift) | (GAMMA_10BIT[g] << g_shift) | (GAMMA_10BIT[r] << r_shift);
// Skip the empty byte in out 32-bit aligned 24-bit colour.
p++;
}
} }
} } else {
} for(uint y = 0; y < height; y++) {
else if(graphics->pen_type == PicoGraphics::PEN_RGB565) { for(uint x = 0; x < width; x++) {
uint16_t *p = (uint16_t *)graphics->frame_buffer; int offset = 0;
for(uint y = 0; y < height; y++) { int sy = y;
for(uint x = 0; x < width; x++) { int sx = x;
uint16_t col = __builtin_bswap16(*p); uint8_t b = *p++;
uint8_t r = (col & 0b1111100000000000) >> 8; uint8_t g = *p++;
uint8_t g = (col & 0b0000011111100000) >> 3; uint8_t r = *p++;
uint8_t b = (col & 0b0000000000011111) << 3;
set_pixel(x, y, r, g, b); // Interlace the top and bottom halves of the panel.
p++; // Since these are scanned out simultaneously to two chains
// of shift registers we need each pair of rows
// (N and N + height / 2) to be adjacent in the buffer.
offset = width * 2;
if(sy >= int(height / 2)) {
sy -= height / 2;
offset *= sy;
offset += 1;
} else {
offset *= sy;
}
offset += sx * 2;
back_buffer[offset] = (GAMMA_10BIT[b] << b_shift) | (GAMMA_10BIT[g] << g_shift) | (GAMMA_10BIT[r] << r_shift);
// Skip the empty byte in out 32-bit aligned 24-bit colour.
p++;
}
} }
} }
} }

Wyświetl plik

@ -65,6 +65,9 @@ class Hub75 {
}; };
uint width; uint width;
uint height; uint height;
uint r_shift = 0;
uint g_shift = 10;
uint b_shift = 20;
Pixel *back_buffer; Pixel *back_buffer;
bool managed_buffer = false; bool managed_buffer = false;
PanelType panel_type; PanelType panel_type;

Wyświetl plik

@ -1,5 +1,5 @@
from pimoroni import RGBLED, Button from pimoroni import RGBLED, Button
from picographics import PicoGraphics, DISPLAY_INTERSTATE75_32X32, DISPLAY_INTERSTATE75_64X32, DISPLAY_INTERSTATE75_96X32, DISPLAY_INTERSTATE75_96X48, DISPLAY_INTERSTATE75_128X32, DISPLAY_INTERSTATE75_64X64, DISPLAY_INTERSTATE75_128X64, DISPLAY_INTERSTATE75_192X64, DISPLAY_INTERSTATE75_256X64 from picographics import PicoGraphics, DISPLAY_INTERSTATE75_32X32, DISPLAY_INTERSTATE75_64X32, DISPLAY_INTERSTATE75_96X32, DISPLAY_INTERSTATE75_96X48, DISPLAY_INTERSTATE75_128X32, DISPLAY_INTERSTATE75_64X64, DISPLAY_INTERSTATE75_128X64, DISPLAY_INTERSTATE75_192X64, DISPLAY_INTERSTATE75_256X64, DISPLAY_INTERSTATE75_128X128
from pimoroni_i2c import PimoroniI2C from pimoroni_i2c import PimoroniI2C
import hub75 import hub75
import sys import sys
@ -29,6 +29,7 @@ class Interstate75:
DISPLAY_INTERSTATE75_128X64 = DISPLAY_INTERSTATE75_128X64 DISPLAY_INTERSTATE75_128X64 = DISPLAY_INTERSTATE75_128X64
DISPLAY_INTERSTATE75_192X64 = DISPLAY_INTERSTATE75_192X64 DISPLAY_INTERSTATE75_192X64 = DISPLAY_INTERSTATE75_192X64
DISPLAY_INTERSTATE75_256X64 = DISPLAY_INTERSTATE75_256X64 DISPLAY_INTERSTATE75_256X64 = DISPLAY_INTERSTATE75_256X64
DISPLAY_INTERSTATE75_128X128 = DISPLAY_INTERSTATE75_128X128
PANEL_GENERIC = hub75.PANEL_GENERIC PANEL_GENERIC = hub75.PANEL_GENERIC
PANEL_FM6126A = hub75.PANEL_FM6126A PANEL_FM6126A = hub75.PANEL_FM6126A
@ -46,7 +47,15 @@ class Interstate75:
self.interstate75w = "Pico W" in sys.implementation._machine self.interstate75w = "Pico W" in sys.implementation._machine
self.display = PicoGraphics(display=display) self.display = PicoGraphics(display=display)
self.width, self.height = self.display.get_bounds() self.width, self.height = self.display.get_bounds()
self.hub75 = hub75.Hub75(self.width, self.height, panel_type=panel_type, stb_invert=stb_invert, color_order=color_order)
out_width = self.width
out_height = self.height
if display == DISPLAY_INTERSTATE75_128X128:
out_width = 256
out_height = 64
self.hub75 = hub75.Hub75(out_width, out_height, panel_type=panel_type, stb_invert=stb_invert, color_order=color_order)
self.hub75.start() self.hub75.start()
if self.interstate75w: if self.interstate75w:
self._switch_pins = self.SWITCH_PINS_W self._switch_pins = self.SWITCH_PINS_W