Tiled grayscale rendering to drop the storeBwBuffer peak largest contiguous free block (the value that actually drives OOM on the C3) from ~114 KB to ~82-90 KB. This renders each grayscale plane band-by-band into a small (~8 KB) scratch and streams each band straight to controller RAM (community-sdk writeGrayscalePlaneStrip), leaving the BW framebuffer intact. No save, no restore; controller RAM is re-synced for the next differential turn directly from the live framebuffer. Three writers honor the active band target so per-band re-rendering stays cheap and correct: - drawPixel (text) redirects writes to the band scratch and clips to it. - renderCharImpl skips glyphs whose physical y-extent is outside the band before the bitmap decode (glyphIntersectsStrip), so the per-band re-render doesn't pay N x glyph decode. - DirectPixelWriter (images) writes the band scratch via getWriteTarget instead of the framebuffer. Without this, image pixels wrote the live BW frame directly and cleanup re-synced that corruption, leaving thin outlines after navigating away from an image. Controller specifics live in the SDK (X4 setRamArea windowing, X3 PTL); the reader checks supportsStripGrayscale() and is otherwise controller-agnostic. Measured on hardware (X4 and X3, text and images, visually correct): - Grayscale scratch ~8 KB vs ~50 KB save; largest contiguous free block held at full size during grayscale instead of dropping ~25-32 KB. - X4 text page about +25 ms/page; X3 page time is dominated by its intrinsic grayscale refresh, not tiling. Depends on community-sdk #13 (the writeGrayscalePlaneStrip API). The submodule bump here points at that branch, so until #13 merges the submodule won't resolve from upstream and CI will fail there; keeping this a draft until then. Will rebase onto master and re-point the submodule to the merged SDK commit once #13 lands. Did you use AI tools to help write this code? partial
74 lines
2.7 KiB
C++
74 lines
2.7 KiB
C++
#pragma once
|
|
#include <Arduino.h>
|
|
#include <EInkDisplay.h>
|
|
|
|
class HalDisplay {
|
|
public:
|
|
// Constructor with pin configuration
|
|
HalDisplay();
|
|
|
|
// Destructor
|
|
~HalDisplay();
|
|
|
|
// Refresh modes
|
|
enum RefreshMode {
|
|
FULL_REFRESH, // Full refresh with complete waveform
|
|
HALF_REFRESH, // Half refresh (1720ms) - balanced quality and speed
|
|
FAST_REFRESH // Fast refresh using custom LUT
|
|
};
|
|
|
|
// Pass seamless=true on any path where the panel already shows the
|
|
// content it should after begin() returns (silent reboot's popup,
|
|
// sleep-wake with a restored buffer). Skips the wakeup-gated
|
|
// requestResync() and defuses the SDK's X3 _x3InitialFullSyncsRemaining
|
|
// counter; otherwise the first two paints get promoted to FULL
|
|
// (~770ms each on X3).
|
|
void begin(bool seamless = false);
|
|
|
|
// Display dimensions
|
|
static constexpr uint16_t DISPLAY_WIDTH = EInkDisplay::DISPLAY_WIDTH;
|
|
static constexpr uint16_t DISPLAY_HEIGHT = EInkDisplay::DISPLAY_HEIGHT;
|
|
static constexpr uint16_t DISPLAY_WIDTH_BYTES = DISPLAY_WIDTH / 8;
|
|
static constexpr uint32_t BUFFER_SIZE = DISPLAY_WIDTH_BYTES * DISPLAY_HEIGHT;
|
|
|
|
// Frame buffer operations
|
|
void clearScreen(uint8_t color = 0xFF) const;
|
|
void drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
|
|
bool fromProgmem = false) const;
|
|
void drawImageTransparent(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
|
|
bool fromProgmem = false) const;
|
|
|
|
void displayBuffer(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false);
|
|
void refreshDisplay(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false);
|
|
|
|
// Power management
|
|
void deepSleep();
|
|
|
|
// Access to frame buffer
|
|
uint8_t* getFrameBuffer() const;
|
|
|
|
void copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer);
|
|
void copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer);
|
|
void copyGrayscaleMsbBuffers(const uint8_t* msbBuffer);
|
|
void cleanupGrayscaleBuffers(const uint8_t* bwBuffer);
|
|
|
|
void displayGrayBuffer(bool turnOffScreen = false);
|
|
|
|
// Tiled grayscale: stream one band of a plane (lsbPlane selects LSB/MSB RAM)
|
|
// straight to the controller; supportsStripGrayscale() gates the path. See
|
|
// EInkDisplay::writeGrayscalePlaneStrip.
|
|
void writeGrayscalePlaneStrip(bool lsbPlane, const uint8_t* rows, uint16_t yStart, uint16_t numRows);
|
|
bool supportsStripGrayscale() const;
|
|
|
|
// Runtime geometry passthrough
|
|
uint16_t getDisplayWidth() const;
|
|
uint16_t getDisplayHeight() const;
|
|
uint16_t getDisplayWidthBytes() const;
|
|
uint32_t getBufferSize() const;
|
|
|
|
private:
|
|
EInkDisplay einkDisplay;
|
|
};
|
|
|
|
extern HalDisplay display;
|