Implement sparse css cache plus dynamic banding for AA

This commit is contained in:
jpirnay
2026-05-11 22:27:13 +02:00
parent fb16db2b48
commit c273e250f2
7 changed files with 758 additions and 202 deletions
+96 -8
View File
@@ -69,6 +69,9 @@ void GfxRenderer::begin() {
panelHeight = display.getDisplayHeight();
panelWidthBytes = display.getDisplayWidthBytes();
frameBufferSize = display.getBufferSize();
bwSnapshotRowStart = 0;
bwSnapshotRowEnd = 0;
bwSnapshotSizeBytes = 0;
bwBufferChunkSize = BW_BUFFER_CHUNK_SIZE;
bwBufferChunks.assign((frameBufferSize + bwBufferChunkSize - 1) / bwBufferChunkSize, nullptr);
}
@@ -2143,9 +2146,75 @@ void GfxRenderer::freeBwBufferChunks() {
* Uses chunked allocation to avoid needing 48KB of contiguous memory.
* Returns true if buffer was stored successfully, false if allocation failed.
*/
bool GfxRenderer::storeBwBuffer() {
bool GfxRenderer::storeBwBuffer() { return storeBwBufferRect(0, 0, getScreenWidth(), getScreenHeight()); }
bool GfxRenderer::storeBwBufferRect(const int x, const int y, const int width, const int height) {
if (width <= 0 || height <= 0) {
LOG_ERR("GFX", "!! BW buffer store rect invalid: x=%d y=%d w=%d h=%d", x, y, width, height);
return false;
}
const int screenWidth = getScreenWidth();
const int screenHeight = getScreenHeight();
if (screenWidth <= 0 || screenHeight <= 0 || panelWidthBytes == 0 || panelHeight == 0 || !frameBuffer) {
LOG_ERR("GFX", "!! BW buffer store unavailable (screen=%dx%d panelHeight=%u rowBytes=%u fb=%p)", screenWidth,
screenHeight, panelHeight, panelWidthBytes, frameBuffer);
return false;
}
const int clampedX0 = std::max(0, x);
const int clampedY0 = std::max(0, y);
const int clampedX1 = std::min(screenWidth - 1, x + width - 1);
const int clampedY1 = std::min(screenHeight - 1, y + height - 1);
if (clampedX0 > clampedX1 || clampedY0 > clampedY1) {
LOG_ERR("GFX", "!! BW buffer store rect outside screen: x=%d y=%d w=%d h=%d", x, y, width, height);
return false;
}
int rowStart = 0;
int rowEnd = 0;
switch (getOrientation()) {
case LandscapeCounterClockwise:
rowStart = clampedY0;
rowEnd = clampedY1;
break;
case LandscapeClockwise:
rowStart = static_cast<int>(panelHeight) - 1 - clampedY1;
rowEnd = static_cast<int>(panelHeight) - 1 - clampedY0;
break;
case Portrait:
rowStart = static_cast<int>(panelHeight) - 1 - clampedX1;
rowEnd = static_cast<int>(panelHeight) - 1 - clampedX0;
break;
case PortraitInverted:
rowStart = clampedX0;
rowEnd = clampedX1;
break;
}
rowStart = std::max(0, rowStart);
rowEnd = std::min(static_cast<int>(panelHeight) - 1, rowEnd);
if (rowStart > rowEnd) {
LOG_ERR("GFX", "!! BW buffer store row-band invalid after orientation mapping: rows=%d..%d", rowStart, rowEnd);
return false;
}
const size_t rows = static_cast<size_t>(rowEnd - rowStart + 1);
const size_t snapshotSizeBytes = rows * panelWidthBytes;
const size_t snapshotBaseOffset = static_cast<size_t>(rowStart) * panelWidthBytes;
if (snapshotSizeBytes == 0 || snapshotBaseOffset + snapshotSizeBytes > frameBufferSize) {
LOG_ERR("GFX", "!! BW buffer store row-band out of bounds: base=%zu size=%zu frame=%u", snapshotBaseOffset,
snapshotSizeBytes, frameBufferSize);
return false;
}
freeBwBufferChunks();
bwSnapshotRowStart = static_cast<uint16_t>(rowStart);
bwSnapshotRowEnd = static_cast<uint16_t>(rowEnd);
bwSnapshotSizeBytes = snapshotSizeBytes;
auto attemptStore = [&](size_t chunkSize) {
bwBufferChunks.assign((frameBufferSize + chunkSize - 1) / chunkSize, nullptr);
bwBufferChunks.assign((bwSnapshotSizeBytes + chunkSize - 1) / chunkSize, nullptr);
for (size_t i = 0; i < bwBufferChunks.size(); i++) {
if (bwBufferChunks[i]) {
LOG_ERR("GFX", "!! BW buffer chunk %zu already stored - this is likely a bug, freeing chunk", i);
@@ -2154,7 +2223,7 @@ bool GfxRenderer::storeBwBuffer() {
}
const size_t offset = i * chunkSize;
const size_t allocSize = std::min(chunkSize, static_cast<size_t>(frameBufferSize - offset));
const size_t allocSize = std::min(chunkSize, bwSnapshotSizeBytes - offset);
bwBufferChunks[i] = static_cast<uint8_t*>(malloc(allocSize));
if (!bwBufferChunks[i]) {
@@ -2166,10 +2235,11 @@ bool GfxRenderer::storeBwBuffer() {
return false;
}
memcpy(bwBufferChunks[i], frameBuffer + offset, allocSize);
memcpy(bwBufferChunks[i], frameBuffer + snapshotBaseOffset + offset, allocSize);
}
bwBufferChunkSize = chunkSize;
LOG_DBG("GFX", "Stored BW buffer in %zu chunks (%zu bytes each)", bwBufferChunks.size(), chunkSize);
LOG_DBG("GFX", "Stored BW buffer rows [%u..%u] (%zu bytes) in %zu chunks (%zu bytes each)", bwSnapshotRowStart,
bwSnapshotRowEnd, bwSnapshotSizeBytes, bwBufferChunks.size(), chunkSize);
return true;
};
@@ -2199,6 +2269,9 @@ bool GfxRenderer::storeBwBuffer() {
}
LOG_ERR("GFX", "!! BW buffer storage failed after retrying smaller chunk sizes");
bwSnapshotSizeBytes = 0;
bwSnapshotRowStart = 0;
bwSnapshotRowEnd = 0;
return false;
}
@@ -2208,6 +2281,13 @@ bool GfxRenderer::storeBwBuffer() {
* Uses chunked restoration to match chunked storage.
*/
void GfxRenderer::restoreBwBuffer() {
if (bwSnapshotSizeBytes == 0) {
display.cleanupGrayscaleBuffers(frameBuffer);
freeBwBufferChunks();
LOG_ERR("GFX", "BW restore skipped: no stored snapshot metadata; cleaned grayscale buffers only");
return;
}
// Check if all chunks are allocated
bool missingChunks = false;
for (const auto& bwBufferChunk : bwBufferChunks) {
@@ -2223,20 +2303,28 @@ void GfxRenderer::restoreBwBuffer() {
// allocations that can later starve TLS handshakes.
display.cleanupGrayscaleBuffers(frameBuffer);
freeBwBufferChunks();
bwSnapshotSizeBytes = 0;
bwSnapshotRowStart = 0;
bwSnapshotRowEnd = 0;
LOG_ERR("GFX", "BW restore skipped due to missing chunks; cleaned grayscale buffers only");
return;
}
const size_t snapshotBaseOffset = static_cast<size_t>(bwSnapshotRowStart) * panelWidthBytes;
for (size_t i = 0; i < bwBufferChunks.size(); i++) {
const size_t offset = i * bwBufferChunkSize;
const size_t chunkSize = std::min(bwBufferChunkSize, static_cast<size_t>(frameBufferSize - offset));
memcpy(frameBuffer + offset, bwBufferChunks[i], chunkSize);
const size_t chunkSize = std::min(bwBufferChunkSize, bwSnapshotSizeBytes - offset);
memcpy(frameBuffer + snapshotBaseOffset + offset, bwBufferChunks[i], chunkSize);
}
display.cleanupGrayscaleBuffers(frameBuffer);
freeBwBufferChunks();
LOG_DBG("GFX", "Restored and freed BW buffer chunks");
LOG_DBG("GFX", "Restored BW buffer rows [%u..%u] (%zu bytes) and freed BW chunks", bwSnapshotRowStart,
bwSnapshotRowEnd, bwSnapshotSizeBytes);
bwSnapshotSizeBytes = 0;
bwSnapshotRowStart = 0;
bwSnapshotRowEnd = 0;
}
/**
+6 -2
View File
@@ -55,6 +55,9 @@ class GfxRenderer {
uint16_t panelHeight = 0; // set in begin()
uint16_t panelWidthBytes = 0; // set in begin()
uint32_t frameBufferSize = 0; // set in begin()
uint16_t bwSnapshotRowStart = 0;
uint16_t bwSnapshotRowEnd = 0;
size_t bwSnapshotSizeBytes = 0;
size_t bwBufferChunkSize = BW_BUFFER_CHUNK_SIZE;
std::vector<uint8_t*> bwBufferChunks;
std::map<int, EpdFontFamily> fontMap;
@@ -206,8 +209,9 @@ class GfxRenderer {
void copyGrayscaleLsbBuffers() const;
void copyGrayscaleMsbBuffers() const;
void displayGrayBuffer() const;
bool storeBwBuffer(); // Returns true if buffer was stored successfully
void restoreBwBuffer(); // Restore and free the stored buffer
bool storeBwBuffer(); // Returns true if buffer was stored successfully
bool storeBwBufferRect(int x, int y, int width, int height); // Store only rows intersecting logical rect
void restoreBwBuffer(); // Restore and free the stored buffer
void cleanupGrayscaleWithFrameBuffer() const;
// Font helpers