Use single buffer mode for EInkDisplay

Frees up 48kB of staticly allocated RAM in exchange for 48kB just when grayscale rendering is needed
This commit is contained in:
Dave Allie 2025-12-16 22:47:16 +11:00
parent c262f222de
commit a2954578cc
No known key found for this signature in database
GPG Key ID: F2FDDB3AD8D0276F
5 changed files with 46 additions and 6 deletions

View File

@ -164,7 +164,9 @@ int GfxRenderer::getLineHeight(const int fontId) const {
uint8_t* GfxRenderer::getFrameBuffer() const { return einkDisplay.getFrameBuffer(); } uint8_t* GfxRenderer::getFrameBuffer() const { return einkDisplay.getFrameBuffer(); }
void GfxRenderer::swapBuffers() const { einkDisplay.swapBuffers(); } size_t GfxRenderer::getBufferSize() {
return EInkDisplay::BUFFER_SIZE;
}
void GfxRenderer::grayscaleRevert() const { einkDisplay.grayscaleRevert(); } void GfxRenderer::grayscaleRevert() const { einkDisplay.grayscaleRevert(); }
@ -174,6 +176,35 @@ void GfxRenderer::copyGrayscaleMsbBuffers() const { einkDisplay.copyGrayscaleMsb
void GfxRenderer::displayGrayBuffer() const { einkDisplay.displayGrayBuffer(); } void GfxRenderer::displayGrayBuffer() const { einkDisplay.displayGrayBuffer(); }
/**
* This should be called before grayscale buffers are populated.
* A `restoreBwBuffer` call should always follow the grayscale render if this method was called.
*/
void GfxRenderer::storeBwBuffer() {
if (bwBuffer) {
Serial.printf("[%lu] [GFX] !! BW buffer already stored - this is likely a bug, freeing it\n", millis());
free(bwBuffer);
}
bwBuffer = static_cast<uint8_t *>(malloc(EInkDisplay::BUFFER_SIZE));
memcpy(bwBuffer, einkDisplay.getFrameBuffer(), EInkDisplay::BUFFER_SIZE);
}
/**
* This can only be called if `storeBwBuffer` was called prior to the grayscale render.
* It should be called to restore the BW buffer state after grayscale rendering is complete.
*/
void GfxRenderer::restoreBwBuffer() {
if (!bwBuffer) {
Serial.printf("[%lu] [GFX] !! BW buffer not stored - this is likely a bug\n", millis());
return;
}
einkDisplay.cleanupGrayscaleBuffers(bwBuffer);
free(bwBuffer);
bwBuffer = nullptr;
}
void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp, int* x, const int* y, void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp, int* x, const int* y,
const bool pixelState, const EpdFontStyle style) const { const bool pixelState, const EpdFontStyle style) const {
const EpdGlyph* glyph = fontFamily.getGlyph(cp, style); const EpdGlyph* glyph = fontFamily.getGlyph(cp, style);

View File

@ -12,6 +12,7 @@ class GfxRenderer {
private: private:
EInkDisplay& einkDisplay; EInkDisplay& einkDisplay;
RenderMode renderMode; RenderMode renderMode;
uint8_t* bwBuffer = nullptr;
std::map<int, EpdFontFamily> fontMap; std::map<int, EpdFontFamily> fontMap;
void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState, void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState,
EpdFontStyle style) const; EpdFontStyle style) const;
@ -49,9 +50,11 @@ class GfxRenderer {
void copyGrayscaleLsbBuffers() const; void copyGrayscaleLsbBuffers() const;
void copyGrayscaleMsbBuffers() const; void copyGrayscaleMsbBuffers() const;
void displayGrayBuffer() const; void displayGrayBuffer() const;
void storeBwBuffer();
void restoreBwBuffer();
// Low level functions // Low level functions
uint8_t* getFrameBuffer() const; uint8_t* getFrameBuffer() const;
void swapBuffers() const; static size_t getBufferSize();
void grayscaleRevert() const; void grayscaleRevert() const;
}; };

@ -1 +1 @@
Subproject commit 4d0dcd5ff87fcd86eb2966a123e85b03284a03db Subproject commit af965a074b99bdbbf0670d2c284bb34d978f73ed

View File

@ -20,6 +20,7 @@ build_flags =
-DARDUINO_USB_MODE=1 -DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_CDC_ON_BOOT=1
-DMINIZ_NO_ZLIB_COMPATIBLE_NAMES=1 -DMINIZ_NO_ZLIB_COMPATIBLE_NAMES=1
-DEINK_DISPLAY_SINGLE_BUFFER_MODE=1
# https://libexpat.github.io/doc/api/latest/#XML_GE # https://libexpat.github.io/doc/api/latest/#XML_GE
-DXML_GE=0 -DXML_GE=0
-DXML_CONTEXT_BYTES=1024 -DXML_CONTEXT_BYTES=1024

View File

@ -217,9 +217,8 @@ void EpubReaderScreen::renderScreen() {
const int w = textWidth + margin * 2; const int w = textWidth + margin * 2;
const int h = renderer.getLineHeight(READER_FONT_ID) + margin * 2; const int h = renderer.getLineHeight(READER_FONT_ID) + margin * 2;
renderer.grayscaleRevert(); renderer.grayscaleRevert();
uint8_t* fb1 = renderer.getFrameBuffer();
renderer.swapBuffers(); // TODO: This looks like garbage again, need to implement windowed updates
memcpy(fb1, renderer.getFrameBuffer(), EInkDisplay::BUFFER_SIZE);
renderer.fillRect(x, y, w, h, 0); renderer.fillRect(x, y, w, h, 0);
renderer.drawText(READER_FONT_ID, x + margin, y + margin, "Indexing..."); renderer.drawText(READER_FONT_ID, x + margin, y + margin, "Indexing...");
renderer.drawRect(x + 5, y + 5, w - 10, h - 10); renderer.drawRect(x + 5, y + 5, w - 10, h - 10);
@ -297,6 +296,9 @@ void EpubReaderScreen::renderContents(std::unique_ptr<Page> page) {
pagesUntilFullRefresh--; pagesUntilFullRefresh--;
} }
// Save bw buffer to reset buffer state after grayscale data sync
renderer.storeBwBuffer();
// grayscale rendering // grayscale rendering
// TODO: Only do this if font supports it // TODO: Only do this if font supports it
{ {
@ -315,6 +317,9 @@ void EpubReaderScreen::renderContents(std::unique_ptr<Page> page) {
renderer.displayGrayBuffer(); renderer.displayGrayBuffer();
renderer.setRenderMode(GfxRenderer::BW); renderer.setRenderMode(GfxRenderer::BW);
} }
// restore the bw data
renderer.restoreBwBuffer();
} }
void EpubReaderScreen::renderStatusBar() const { void EpubReaderScreen::renderStatusBar() const {