Implement 4-quadrant dirty rectangle optimization and 30 FPS limiting for ST7796
Add intelligent partial screen update system using bitwise XOR change detection and 4-quadrant tracking (top-left, top-right, bottom-left, bottom-right). Each changed pixel is routed to its quadrant, with sophisticated merge logic that combines adjacent rectangles when beneficial (<40% overhead). This dramatically reduces SPI bandwidth for UIs with scattered updates (e.g., corners, sidebars). Key changes: - 4-quadrant dirty rectangle tracking with automatic merging - XOR-based change detection for fast byte-level comparison - Expose st7796_set_window() for partial region updates - 30 FPS frame rate limiter (33ms per frame) to prevent excessive refreshes - Smart sleep timing when frame rate limit is active Performance: Up to 99% reduction in SPI traffic for corner-based UIs (e.g., 4 small regions vs full 480x320 screen updates). Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "low_level_display.h"
|
||||
#include "st7796.h"
|
||||
#include <climits>
|
||||
|
||||
class LowLevelDisplayST7796 : public LowLevelDisplay {
|
||||
private:
|
||||
@@ -13,6 +14,56 @@ private:
|
||||
uint16_t* rgb_buffer; // Persistent buffer for 1-bit to RGB565 conversion
|
||||
bool invert_color; // If true, swap black/white
|
||||
|
||||
// Dirty rectangle tracking for partial updates
|
||||
uint8_t* prev_bit_buffer; // Previous frame buffer for change detection
|
||||
bool dirty_rect_enabled; // Enable/disable dirty rectangle optimization
|
||||
|
||||
struct DirtyRect {
|
||||
int x0, y0; // Top-left corner
|
||||
int x1, y1; // Bottom-right corner (inclusive)
|
||||
bool is_valid;
|
||||
|
||||
void reset() {
|
||||
x0 = INT_MAX;
|
||||
y0 = INT_MAX;
|
||||
x1 = -1;
|
||||
y1 = -1;
|
||||
is_valid = false;
|
||||
}
|
||||
|
||||
void expand(int x, int y) {
|
||||
if (x < x0) x0 = x;
|
||||
if (x > x1) x1 = x;
|
||||
if (y < y0) y0 = y;
|
||||
if (y > y1) y1 = y;
|
||||
is_valid = true;
|
||||
}
|
||||
|
||||
int get_width() const { return is_valid ? (x1 - x0 + 1) : 0; }
|
||||
int get_height() const { return is_valid ? (y1 - y0 + 1) : 0; }
|
||||
int get_area() const { return is_valid ? get_width() * get_height() : 0; }
|
||||
|
||||
bool overlaps(const DirtyRect& other) const {
|
||||
if (!is_valid || !other.is_valid) return false;
|
||||
return !(x1 < other.x0 || x0 > other.x1 || y1 < other.y0 || y0 > other.y1);
|
||||
}
|
||||
|
||||
void merge(const DirtyRect& other) {
|
||||
if (!other.is_valid) return;
|
||||
if (!is_valid) {
|
||||
*this = other;
|
||||
return;
|
||||
}
|
||||
x0 = (x0 < other.x0) ? x0 : other.x0;
|
||||
y0 = (y0 < other.y0) ? y0 : other.y0;
|
||||
x1 = (x1 > other.x1) ? x1 : other.x1;
|
||||
y1 = (y1 > other.y1) ? y1 : other.y1;
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr int MAX_DIRTY_RECTS = 4;
|
||||
DirtyRect dirty_rects[MAX_DIRTY_RECTS]; // Support up to 4 dirty rectangles (4 quadrants)
|
||||
|
||||
public:
|
||||
LowLevelDisplayST7796(const st7796_config* cfg, int w, int h, bool invert = false);
|
||||
~LowLevelDisplayST7796() override;
|
||||
@@ -53,6 +104,10 @@ public:
|
||||
void on_idle_10min() override;
|
||||
void on_user_interaction() override;
|
||||
|
||||
// Dirty rectangle optimization control
|
||||
void enable_dirty_rect(bool enabled = true);
|
||||
bool is_dirty_rect_enabled() const { return dirty_rect_enabled; }
|
||||
|
||||
private:
|
||||
uint8_t saved_brightness = 100;
|
||||
bool is_dimmed = false;
|
||||
|
||||
Reference in New Issue
Block a user