Add Rgb565PixelBE for big-endian SPI displays#10882
Add Rgb565PixelBE for big-endian SPI displays#10882okhsunrog wants to merge 2 commits intoslint-ui:masterfrom
Conversation
New TargetPixel type that stores RGB565 in big-endian byte order, matching the format expected by SPI LCD controllers (ILI9341, ILI9342C, ST7789, etc.) without any post-render byte swapping. The bits are packed directly in swapped order — from_rgb() encodes into the BE layout and blend() expands to the same u32 representation used by Rgb565Pixel, reuses identical multiply+color math, then extracts directly into BE positions. No swap_bytes() anywhere.
Replace Rgb565Pixel + post-render byte-swap loops with Rgb565PixelBE in the three Pico ST7789 board support examples. The renderer now produces pixels directly in big-endian byte order, eliminating the per-line .to_be() conversion before DMA transfer. Ported examples: - pico_st7789 - pico2_st7789 - pico2_touch_lcd_2_8
|
That's an awesome idea. Thanks! I'd love to get @ogoffart 's input on this when he gets back. This can take 1-2 weeks. Thanks for your patience :) |
|
It would be very nice if anyone with Raspberry Pi Pico and a suitable display could test how this PR affects FPS of the official demos |
|
@tronical can this me merged now? |
|
I've tried this on a pico2 with a 2.8 inch waveshare screen and I get consistently lower overall frame times (~2-3 FPS less). Measured in a release build with defmt and I don't really have a great explanation here. My best guess is that the individual pixel blending operations are slightly more complex, but since they're run many more times than the single le to be pass at the "end", I suspect that they add up to more - at least for line-by-line rendering. I suggest that we offer this data type, but I'm unsure about using it by default in the there pico platform implementations. What do you think? |
|
@tronical I think I'll dig a bit into this and try to investigate what exactly goes differently. Overall I don't mind removing the second commit, so no changes to demos are made. Just want to test it a bit myself |
|
Sounds good :) |
Summary
Rgb565PixelBE, a newTargetPixelimplementation that stores RGB565 pixels in big-endian byte order at render time.to_be()swap loopsMotivation
Most SPI LCD controllers expect pixel data in big-endian byte order. The existing
Rgb565Pixelstores data in native (little-endian) order, forcing every application to byte-swap the entire framebuffer after rendering. On memory-constrained MCUs with external RAM (e.g., ESP32 with PSRAM), this extra pass over the buffer is expensive — in my case it accounted for ~50ms per frame due to slow PSRAM bus bandwidth.Three of the existing examples in
examples/mcu-board-support(pico_st7789.rs,pico2_st7789.rs,pico2_touch_lcd_2_8.rs) already work around this with a.to_be()loop inprocess_line, confirming this is a common need.Approach
Rgb565PixelBEpacks R/G/B bits directly into big-endian-on-little-endian layout infrom_rgb(), so no byte swap is ever needed. Theblend()implementation expands the BE pixel into the same u32 intermediate representation used byRgb565Pixel::blend(), reusing the identical single-multiply alpha blending math, then extracts directly back into BE positions. This means there is no per-pixel overhead compared toRgb565Pixel— the cost is the same, just with different bit shuffling at the edges.Results
On an ESP32 + PSRAM + ILI9342C (320×240) setup, switching from
Rgb565Pixel+ post-render swap toRgb565PixelBEreduced frame time from 118ms to 67ms (8 → 15 FPS), with render time unchanged at ~4ms. The entire gain comes from eliminating the framebuffer copy/swap pass.Real hardware demo project using this type: https://github.com/okhsunrog/m5core2v1-1_demo_rust
Test plan
Rgb565PixelBEproduces byte-swapped equivalents ofRgb565Pixelfor allfrom_rgbinputsblend()matchesRgb565Pixel::blend()results (after byte swap) across multiple color/alpha combinations