From d64a3e61b610e90ec054e7e81b8e791cf4521cb8 Mon Sep 17 00:00:00 2001 From: Gabriel Lima Date: Fri, 3 Apr 2026 19:23:41 -0300 Subject: [PATCH] Implement shadow buffer to stora changes in memory until update() method is called --- linux_fb_display.cpp | 40 +++++++++++++++++++++++++++------------- linux_fb_display.h | 6 +++++- main.cpp | 2 ++ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/linux_fb_display.cpp b/linux_fb_display.cpp index 6292b14..6d31e3f 100644 --- a/linux_fb_display.cpp +++ b/linux_fb_display.cpp @@ -55,10 +55,14 @@ bool LinuxFBDisplay::open(const char* device) { return false; } + shadow_ = new uint8_t[line_len_ * static_cast(rawHeight())](); + return true; } void LinuxFBDisplay::close() { + delete[] shadow_; + shadow_ = nullptr; if (buf_) { munmap(buf_, map_size_); buf_ = nullptr; @@ -69,6 +73,16 @@ void LinuxFBDisplay::close() { } } +// --------------------------------------------------------------------------- +// Update — flush shadow buffer to the mmap'd framebuffer +// --------------------------------------------------------------------------- +void LinuxFBDisplay::update() { + if (!buf_ || !shadow_) return; + std::memcpy(buf_, shadow_, + line_len_ * static_cast(rawHeight())); + fsync(fd_); +} + // --------------------------------------------------------------------------- // Pixel format conversion // --------------------------------------------------------------------------- @@ -98,8 +112,8 @@ void LinuxFBDisplay::writePixel(uint8_t* dst, Color color) const { void LinuxFBDisplay::drawPixelImpl(int16_t x, int16_t y, Color color) { if (!buf_) return; if (bpp_ == 1) { - uint8_t* p = buf_ + static_cast(y) * line_len_ - + static_cast(x) / 8; + uint8_t* p = shadow_ + static_cast(y) * line_len_ + + static_cast(x) / 8; uint8_t bit = static_cast(x) & 7u; if (color) *p |= (1u << bit); else *p &= ~(1u << bit); @@ -107,7 +121,7 @@ void LinuxFBDisplay::drawPixelImpl(int16_t x, int16_t y, Color color) { } uint32_t offset = static_cast(y) * line_len_ + static_cast(x) * (bpp_ / 8); - writePixel(buf_ + offset, color); + writePixel(shadow_ + offset, color); } // --------------------------------------------------------------------------- @@ -118,7 +132,7 @@ void LinuxFBDisplay::drawHLineImpl(int16_t x, int16_t y, if (!buf_ || w <= 0) return; if (bpp_ == 1) { - uint8_t* row = buf_ + static_cast(y) * line_len_; + uint8_t* row = shadow_ + static_cast(y) * line_len_; uint32_t x0 = static_cast(x); uint32_t x1 = x0 + static_cast(w) - 1; uint32_t b0 = x0 >> 3; @@ -147,8 +161,8 @@ void LinuxFBDisplay::drawHLineImpl(int16_t x, int16_t y, } uint32_t bytes_pp = bpp_ / 8; - uint8_t* row = buf_ + static_cast(y) * line_len_ - + static_cast(x) * bytes_pp; + uint8_t* row = shadow_ + static_cast(y) * line_len_ + + static_cast(x) * bytes_pp; if (bpp_ == 32) { // For XRGB8888, use 32-bit fill. @@ -182,8 +196,8 @@ void LinuxFBDisplay::drawVLineImpl(int16_t x, int16_t y, if (bpp_ == 1) { uint8_t bit = static_cast(x) & 7u; - uint8_t* p = buf_ + static_cast(y) * line_len_ - + static_cast(x) / 8; + uint8_t* p = shadow_ + static_cast(y) * line_len_ + + static_cast(x) / 8; for (int16_t i = 0; i < h; ++i) { if (color) *p |= (1u << bit); else *p &= ~(1u << bit); @@ -193,8 +207,8 @@ void LinuxFBDisplay::drawVLineImpl(int16_t x, int16_t y, } uint32_t bytes_pp = bpp_ / 8; - uint8_t* p = buf_ + static_cast(y) * line_len_ - + static_cast(x) * bytes_pp; + uint8_t* p = shadow_ + static_cast(y) * line_len_ + + static_cast(x) * bytes_pp; // Pre-encode the pixel once. uint8_t pixel_buf[4]; @@ -224,7 +238,7 @@ void LinuxFBDisplay::fillRectImpl(int16_t x, int16_t y, if (h == 1) return; - uint8_t* src = buf_ + static_cast(y) * line_len_ + b0; + uint8_t* src = shadow_ + static_cast(y) * line_len_ + b0; uint32_t span = b1 - b0 + 1; if ((x0 & 7u) == 0 && (x1 & 7u) == 7u) { @@ -249,8 +263,8 @@ void LinuxFBDisplay::fillRectImpl(int16_t x, int16_t y, uint32_t bytes_pp = bpp_ / 8; uint32_t row_bytes = static_cast(w) * bytes_pp; - uint8_t* first_row = buf_ + static_cast(y) * line_len_ - + static_cast(x) * bytes_pp; + uint8_t* first_row = shadow_ + static_cast(y) * line_len_ + + static_cast(x) * bytes_pp; // Optimized path: if all bytes of the pixel are the same value, // the entire rect can be filled with memset. diff --git a/linux_fb_display.h b/linux_fb_display.h index 7254841..28b1344 100644 --- a/linux_fb_display.h +++ b/linux_fb_display.h @@ -28,6 +28,9 @@ public: // Is the framebuffer currently open and mapped? bool isOpen() const { return buf_ != nullptr; } + // Copy the internal shadow buffer to the framebuffer, triggering a display refresh. + void update(); + protected: // Mandatory HAL: single pixel write. void drawPixelImpl(int16_t x, int16_t y, Color color) override; @@ -40,7 +43,8 @@ protected: private: int fd_ = -1; - uint8_t* buf_ = nullptr; + uint8_t* buf_ = nullptr; // mmap'd framebuffer + uint8_t* shadow_ = nullptr; // internal draw target size_t map_size_ = 0; uint32_t bpp_ = 0; uint32_t line_len_ = 0; diff --git a/main.cpp b/main.cpp index 5c44d36..8c4c1e9 100644 --- a/main.cpp +++ b/main.cpp @@ -120,6 +120,8 @@ int main(int argc, char* argv[]) { tm->tm_hour, tm->tm_min, tm->tm_sec); draw_centered(display, 41, line_buf); + display.update(); + // --- Sleep aligned to next whole second --- struct timespec ts{}; clock_gettime(CLOCK_REALTIME, &ts);