Implement shadow buffer to stora changes in memory until update() method is called

This commit is contained in:
2026-04-03 19:23:41 -03:00
parent 89b3ad3899
commit d64a3e61b6
3 changed files with 34 additions and 14 deletions

View File

@@ -55,10 +55,14 @@ bool LinuxFBDisplay::open(const char* device) {
return false;
}
shadow_ = new uint8_t[line_len_ * static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(y) * line_len_
+ static_cast<uint32_t>(x) / 8;
uint8_t* p = shadow_ + static_cast<uint32_t>(y) * line_len_
+ static_cast<uint32_t>(x) / 8;
uint8_t bit = static_cast<uint32_t>(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<uint32_t>(y) * line_len_
+ static_cast<uint32_t>(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<uint32_t>(y) * line_len_;
uint8_t* row = shadow_ + static_cast<uint32_t>(y) * line_len_;
uint32_t x0 = static_cast<uint32_t>(x);
uint32_t x1 = x0 + static_cast<uint32_t>(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<uint32_t>(y) * line_len_
+ static_cast<uint32_t>(x) * bytes_pp;
uint8_t* row = shadow_ + static_cast<uint32_t>(y) * line_len_
+ static_cast<uint32_t>(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<uint32_t>(x) & 7u;
uint8_t* p = buf_ + static_cast<uint32_t>(y) * line_len_
+ static_cast<uint32_t>(x) / 8;
uint8_t* p = shadow_ + static_cast<uint32_t>(y) * line_len_
+ static_cast<uint32_t>(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<uint32_t>(y) * line_len_
+ static_cast<uint32_t>(x) * bytes_pp;
uint8_t* p = shadow_ + static_cast<uint32_t>(y) * line_len_
+ static_cast<uint32_t>(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<uint32_t>(y) * line_len_ + b0;
uint8_t* src = shadow_ + static_cast<uint32_t>(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<uint32_t>(w) * bytes_pp;
uint8_t* first_row = buf_ + static_cast<uint32_t>(y) * line_len_
+ static_cast<uint32_t>(x) * bytes_pp;
uint8_t* first_row = shadow_ + static_cast<uint32_t>(y) * line_len_
+ static_cast<uint32_t>(x) * bytes_pp;
// Optimized path: if all bytes of the pixel are the same value,
// the entire rect can be filled with memset.

View File

@@ -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;

View File

@@ -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);