fix: Add framebuffer release/realloc and improved lazy indexing (#2563)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "GfxRenderer.h"
|
||||
|
||||
#include <BidiUtils.h>
|
||||
#include <BuildScratch.h>
|
||||
#include <FontDecompressor.h>
|
||||
#include <HalGPIO.h>
|
||||
#include <Logging.h>
|
||||
@@ -91,6 +92,47 @@ void GfxRenderer::begin() {
|
||||
bwBufferChunks.assign((frameBufferSize + BW_BUFFER_CHUNK_SIZE - 1) / BW_BUFFER_CHUNK_SIZE, nullptr);
|
||||
}
|
||||
|
||||
void GfxRenderer::releaseFrameBufferForBuild() {
|
||||
// Lend the framebuffer's bytes IN PLACE: the allocation is never freed, so
|
||||
// it cannot move and repeated loans cannot fragment the heap (the previous
|
||||
// free+realloc model measurably decayed the max contiguous block over a
|
||||
// session). The bytes are deposited in the build-scratch registry so
|
||||
// memory-hungry build phases (e.g. InflateStream's tinfl state + window)
|
||||
// can claim them instead of allocating.
|
||||
uint32_t size = 0;
|
||||
uint8_t* scratch = display.lendFrameBufferStorage(&size);
|
||||
frameBuffer = nullptr;
|
||||
if (scratch) {
|
||||
buildscratch::lend(scratch, size);
|
||||
}
|
||||
}
|
||||
|
||||
bool GfxRenderer::restoreFrameBufferAfterBuild() {
|
||||
buildscratch::reclaim();
|
||||
display.returnFrameBufferStorage(); // cannot fail: the allocation was never freed
|
||||
frameBuffer = display.getFrameBuffer();
|
||||
return frameBuffer != nullptr;
|
||||
}
|
||||
|
||||
GfxRenderer::FrameBufferLoan::FrameBufferLoan(GfxRenderer& renderer) : renderer_(renderer) {
|
||||
// Nesting guard: if the framebuffer is already lent out (an outer loan),
|
||||
// stay inert so this end() cannot return storage the outer loan still owns.
|
||||
if (!renderer_.hasFrameBuffer()) return;
|
||||
renderer_.releaseFrameBufferForBuild();
|
||||
active_ = true;
|
||||
}
|
||||
|
||||
void GfxRenderer::FrameBufferLoan::end() {
|
||||
if (!active_) return;
|
||||
active_ = false;
|
||||
if (!renderer_.restoreFrameBufferAfterBuild()) {
|
||||
// Only reachable if the framebuffer never existed, which begin() already
|
||||
// asserts against; kept as a backstop since running blind helps nobody.
|
||||
LOG_ERR("GFX", "Framebuffer restore failed - restarting");
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
bool GfxRenderer::isFontCacheScanning() const { return fontCacheManager_ && fontCacheManager_->isScanning(); }
|
||||
|
||||
void GfxRenderer::insertFont(const int fontId, EpdFontFamily font) {
|
||||
|
||||
@@ -250,6 +250,34 @@ class GfxRenderer {
|
||||
// Font helpers
|
||||
const uint8_t* getGlyphBitmap(const EpdFontData* fontData, const EpdGlyph* glyph) const;
|
||||
|
||||
// Lend the 48 KB framebuffer's bytes to a memory-hungry phase (chapter
|
||||
// builds) WITHOUT freeing the allocation, so it never moves and repeated
|
||||
// loans cannot fragment the heap. Between release and restore NOTHING may
|
||||
// draw or display — the panel keeps showing its last refreshed image. The
|
||||
// lent bytes are published via buildscratch::claim() for consumers like
|
||||
// InflateStream. restore returns the buffer white, so the caller must
|
||||
// redraw the full screen; it cannot fail (no allocation involved).
|
||||
void releaseFrameBufferForBuild();
|
||||
bool restoreFrameBufferAfterBuild();
|
||||
bool hasFrameBuffer() const { return frameBuffer != nullptr; }
|
||||
|
||||
// RAII form of the loan above, for blocking build regions with early-return
|
||||
// error paths: restores on scope exit (or explicitly via end()). Display the
|
||||
// popup/screen the panel should hold BEFORE constructing one. Constructing
|
||||
// while the framebuffer is already lent yields an inert loan (nesting-safe).
|
||||
class FrameBufferLoan {
|
||||
public:
|
||||
explicit FrameBufferLoan(GfxRenderer& renderer);
|
||||
~FrameBufferLoan() { end(); }
|
||||
void end();
|
||||
FrameBufferLoan(const FrameBufferLoan&) = delete;
|
||||
FrameBufferLoan& operator=(const FrameBufferLoan&) = delete;
|
||||
|
||||
private:
|
||||
GfxRenderer& renderer_;
|
||||
bool active_ = false;
|
||||
};
|
||||
|
||||
// Low level functions
|
||||
uint8_t* getFrameBuffer() const;
|
||||
size_t getBufferSize() const;
|
||||
|
||||
Reference in New Issue
Block a user