mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-02 03:15:37 +00:00
Reduce screenshot peak stack usage with on-demand chunk computation
This commit is contained in:
1 parent
3d15d59ce0
commit
1184618731
1 file changed
+45
-41
+45
-41
@@ -27,6 +27,12 @@
|
|||||||
// never retransmitted from history). A hash collision would skip one
|
// never retransmitted from history). A hash collision would skip one
|
||||||
// stale chunk, which the forcedBlock rotation repairs within at most
|
// stale chunk, which the forcedBlock rotation repairs within at most
|
||||||
// 128 frames anyway.
|
// 128 frames anyway.
|
||||||
|
// Stack optimization: chunks are computed on demand straight from
|
||||||
|
// gStatusLine/gFrameBuffer instead of building the whole 1024-byte
|
||||||
|
// frame on the stack. Changed chunks are tracked in a 16-byte bitmap.
|
||||||
|
// Peak stack drops from ~1.3 KB to ~40 bytes, at the cost of
|
||||||
|
// transforming each transmitted chunk twice (negligible next to the
|
||||||
|
// blocking UART transfer).
|
||||||
static uint8_t previousHash[128];
|
static uint8_t previousHash[128];
|
||||||
static uint8_t forcedBlock = 0;
|
static uint8_t forcedBlock = 0;
|
||||||
static uint8_t keepAlive = 3;
|
static uint8_t keepAlive = 3;
|
||||||
@@ -65,24 +71,35 @@ static void SCREENSHOT_Send(const uint8_t *buf, uint16_t len)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SCREENSHOT_Line(uint8_t *src, uint8_t *dest, uint16_t *idx) {
|
enum {
|
||||||
for (uint8_t b = 0; b < 8; b++) {
|
SCREENSHOT_CHUNK_SIZE = 8,
|
||||||
for (uint8_t i = 0; i < 128; i += 8) {
|
SCREENSHOT_CHUNKS_PER_LINE = 16,
|
||||||
uint8_t acc = 0;
|
SCREENSHOT_HALF_LINE_COLUMNS = LCD_WIDTH / 2,
|
||||||
for (uint8_t k = 0; k < 8; k++) {
|
};
|
||||||
if (src[i + k] & (1 << b)) acc |= (1 << k);
|
|
||||||
}
|
// Compute one 8-byte output chunk directly from the display buffers.
|
||||||
dest[(*idx)++] = gSetting_set_inv ? ~acc : acc;
|
// Frame layout: per line (status + 7 frame lines), 8 bit layers of
|
||||||
|
// 16 bytes each. Each bit layer is split into two 64-column chunks.
|
||||||
|
static void SCREENSHOT_Chunk(uint8_t chunkIdx, uint8_t *dest)
|
||||||
|
{
|
||||||
|
const uint8_t chunkInLine = chunkIdx % SCREENSHOT_CHUNKS_PER_LINE;
|
||||||
|
const uint8_t line = chunkIdx / SCREENSHOT_CHUNKS_PER_LINE;
|
||||||
|
const uint8_t bit = chunkInLine / 2;
|
||||||
|
const uint8_t columnBase = (chunkInLine % 2) * SCREENSHOT_HALF_LINE_COLUMNS;
|
||||||
|
const uint8_t *src = (line == 0 ? gStatusLine : gFrameBuffer[line - 1])
|
||||||
|
+ columnBase;
|
||||||
|
|
||||||
|
for (uint8_t j = 0; j < SCREENSHOT_CHUNK_SIZE; j++) {
|
||||||
|
uint8_t acc = 0;
|
||||||
|
for (uint8_t k = 0; k < SCREENSHOT_CHUNK_SIZE; k++) {
|
||||||
|
if (src[j * SCREENSHOT_CHUNK_SIZE + k] & (1 << bit)) acc |= (1 << k);
|
||||||
}
|
}
|
||||||
|
dest[j] = gSetting_set_inv ? ~acc : acc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SCREENSHOT_Update(bool force)
|
void SCREENSHOT_Update(bool force)
|
||||||
{
|
{
|
||||||
// Build frame in a temporary stack buffer
|
|
||||||
// This is 1024 bytes but it's temporary and gets freed after the function
|
|
||||||
uint8_t frameBuffer[1024];
|
|
||||||
uint16_t index = 0;
|
|
||||||
static bool wasConnected = false;
|
static bool wasConnected = false;
|
||||||
|
|
||||||
if (SCREENSHOT_IsLocked())
|
if (SCREENSHOT_IsLocked())
|
||||||
@@ -104,33 +121,19 @@ void SCREENSHOT_Update(bool force)
|
|||||||
wasConnected = true;
|
wasConnected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==== BUILD FRAME ONCE ====
|
|
||||||
// Status line: 8 bit layers × 128 columns
|
|
||||||
SCREENSHOT_Line(gStatusLine, frameBuffer, &index);
|
|
||||||
|
|
||||||
// Frame buffer: 7 lines × 8 bit layers × 128 columns
|
|
||||||
for (uint8_t l = 0; l < 7; l++) {
|
|
||||||
SCREENSHOT_Line(gFrameBuffer[l], frameBuffer, &index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index != 1024)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// ==== FIRST PASS: Count changed chunks ====
|
// ==== FIRST PASS: Count changed chunks ====
|
||||||
uint16_t deltaLen = 0;
|
uint16_t deltaLen = 0;
|
||||||
uint8_t changedChunks[128]; // List of changed chunk indices
|
uint8_t changedBitmap[16] = {0}; // 1 bit per chunk
|
||||||
uint8_t newHash[128]; // Fingerprints of the current frame
|
uint8_t chunk[9]; // [0] = index, [1..8] = payload
|
||||||
uint8_t changedCount = 0;
|
|
||||||
|
|
||||||
for (uint8_t chunk = 0; chunk < 128; chunk++) {
|
for (uint8_t chunkIdx = 0; chunkIdx < 128; chunkIdx++) {
|
||||||
newHash[chunk] = SCREENSHOT_Hash(&frameBuffer[chunk * 8]);
|
SCREENSHOT_Chunk(chunkIdx, &chunk[1]);
|
||||||
|
|
||||||
bool changed = newHash[chunk] != previousHash[chunk];
|
bool changed = SCREENSHOT_Hash(&chunk[1]) != previousHash[chunkIdx];
|
||||||
bool isForced = (chunk == forcedBlock);
|
bool isForced = (chunkIdx == forcedBlock);
|
||||||
bool fullUpdate = force;
|
|
||||||
|
|
||||||
if (changed || isForced || fullUpdate) {
|
if (changed || isForced || force) {
|
||||||
changedChunks[changedCount++] = chunk;
|
changedBitmap[chunkIdx >> 3] |= 1 << (chunkIdx & 7);
|
||||||
deltaLen += 9;
|
deltaLen += 9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,19 +164,20 @@ void SCREENSHOT_Update(bool force)
|
|||||||
SCREENSHOT_Send(header, 5);
|
SCREENSHOT_Send(header, 5);
|
||||||
|
|
||||||
// ==== SECOND PASS: Send only changed chunks ====
|
// ==== SECOND PASS: Send only changed chunks ====
|
||||||
uint8_t chunk[9];
|
for (uint8_t chunkIdx = 0; chunkIdx < 128; chunkIdx++) {
|
||||||
|
if (!(changedBitmap[chunkIdx >> 3] & (1 << (chunkIdx & 7))))
|
||||||
for (uint8_t i = 0; i < changedCount; i++) {
|
continue;
|
||||||
uint8_t chunkIdx = changedChunks[i];
|
|
||||||
|
|
||||||
chunk[0] = chunkIdx;
|
chunk[0] = chunkIdx;
|
||||||
memcpy(&chunk[1], &frameBuffer[chunkIdx * 8], 8);
|
SCREENSHOT_Chunk(chunkIdx, &chunk[1]);
|
||||||
|
|
||||||
SCREENSHOT_Send(chunk, 9);
|
SCREENSHOT_Send(chunk, 9);
|
||||||
|
|
||||||
// Update the fingerprint only once the chunk is actually sent,
|
// Update the fingerprint only once the chunk is actually sent,
|
||||||
// so chunks skipped by an early return stay marked as changed
|
// so chunks skipped by an early return stay marked as changed.
|
||||||
previousHash[chunkIdx] = newHash[chunkIdx];
|
// Hashing the recomputed payload also keeps the fingerprint in
|
||||||
|
// sync if the display buffer changed between the two passes.
|
||||||
|
previousHash[chunkIdx] = SCREENSHOT_Hash(&chunk[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t end = 0x0A;
|
uint8_t end = 0x0A;
|
||||||
|
|||||||
Reference in new issue
Block a user