Fix heap blowout on TBeams (#10155)
* Fix heap blowout on TBeams * Update src/graphics/draw/MessageRenderer.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Set MESSAGE_HISTORY_LIMIT to 10 for original ESP32 to optimize RAM usage * Optimize message frame allocation to prevent excessive memory usage * Refine message history limits for resource-constrained builds and cap cached lines to prevent heap overflow * Update src/graphics/draw/MessageRenderer.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -21,8 +21,15 @@
|
|||||||
// How many messages are stored (RAM + flash).
|
// How many messages are stored (RAM + flash).
|
||||||
// Define -DMESSAGE_HISTORY_LIMIT=N in build_flags to control memory usage.
|
// Define -DMESSAGE_HISTORY_LIMIT=N in build_flags to control memory usage.
|
||||||
#ifndef MESSAGE_HISTORY_LIMIT
|
#ifndef MESSAGE_HISTORY_LIMIT
|
||||||
|
#if defined(ARCH_ESP32) && \
|
||||||
|
!(defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32S2))
|
||||||
|
// Baseline ESP32 (non-PSRAM variants) has limited heap; reduce message history on resource-constrained builds.
|
||||||
|
// Override with -DMESSAGE_HISTORY_LIMIT=N if needed.
|
||||||
|
#define MESSAGE_HISTORY_LIMIT 10
|
||||||
|
#else
|
||||||
#define MESSAGE_HISTORY_LIMIT 20
|
#define MESSAGE_HISTORY_LIMIT 20
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
// Internal alias used everywhere in code – do NOT redefine elsewhere.
|
// Internal alias used everywhere in code – do NOT redefine elsewhere.
|
||||||
#define MAX_MESSAGES_SAVED MESSAGE_HISTORY_LIMIT
|
#define MAX_MESSAGES_SAVED MESSAGE_HISTORY_LIMIT
|
||||||
|
|||||||
@@ -422,6 +422,17 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
std::vector<bool> isMine; // track alignment
|
std::vector<bool> isMine; // track alignment
|
||||||
std::vector<bool> isHeader; // track header lines
|
std::vector<bool> isHeader; // track header lines
|
||||||
std::vector<AckStatus> ackForLine;
|
std::vector<AckStatus> ackForLine;
|
||||||
|
// Hard limit on total cached lines to prevent unbounded growth from a single long message.
|
||||||
|
// Reserve to the actual cache cap up front, because a single message can expand to many more
|
||||||
|
// wrapped display lines than a small per-message estimate would predict. For a display
|
||||||
|
// rendering only ~5-30 lines at a time, caching more than this limit wastes heap. Stop
|
||||||
|
// appending once we reach MAX_CACHED_LINES to prevent a single message from blowing out the
|
||||||
|
// heap.
|
||||||
|
constexpr size_t MAX_CACHED_LINES = 100U; // ~5-6KB for std::string overhead on 32-bit (if each ~50-60 bytes avg)
|
||||||
|
allLines.reserve(MAX_CACHED_LINES);
|
||||||
|
isMine.reserve(MAX_CACHED_LINES);
|
||||||
|
isHeader.reserve(MAX_CACHED_LINES);
|
||||||
|
ackForLine.reserve(MAX_CACHED_LINES);
|
||||||
|
|
||||||
for (auto it = filtered.rbegin(); it != filtered.rend(); ++it) {
|
for (auto it = filtered.rbegin(); it != filtered.rend(); ++it) {
|
||||||
const auto &m = *it;
|
const auto &m = *it;
|
||||||
@@ -565,16 +576,23 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
|
|
||||||
int wrapWidth = mine ? rightTextWidth : leftTextWidth;
|
int wrapWidth = mine ? rightTextWidth : leftTextWidth;
|
||||||
std::vector<std::string> wrapped = generateLines(display, "", msgText, wrapWidth);
|
std::vector<std::string> wrapped = generateLines(display, "", msgText, wrapWidth);
|
||||||
|
// Per-message wrap-line limit: even if wrapping produces many lines, cap them to prevent
|
||||||
|
// a single long message from consuming most or all of the cache.
|
||||||
|
constexpr size_t MAX_WRAPPED_LINES_PER_MSG = 20U;
|
||||||
|
size_t wrappedCount = 0;
|
||||||
for (auto &ln : wrapped) {
|
for (auto &ln : wrapped) {
|
||||||
allLines.push_back(ln);
|
if (allLines.size() >= MAX_CACHED_LINES || wrappedCount >= MAX_WRAPPED_LINES_PER_MSG)
|
||||||
|
break; // Cache limit or per-message limit reached; stop adding lines from this message
|
||||||
|
allLines.emplace_back(std::move(ln));
|
||||||
isMine.push_back(mine);
|
isMine.push_back(mine);
|
||||||
isHeader.push_back(false);
|
isHeader.push_back(false);
|
||||||
ackForLine.push_back(AckStatus::NONE);
|
ackForLine.push_back(AckStatus::NONE);
|
||||||
|
++wrappedCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache lines and heights
|
// Cache lines and heights
|
||||||
cachedLines = allLines;
|
cachedLines.swap(allLines);
|
||||||
cachedHeights = calculateLineHeights(cachedLines, emotes, isHeader);
|
cachedHeights = calculateLineHeights(cachedLines, emotes, isHeader);
|
||||||
|
|
||||||
std::vector<MessageBlock> blocks = buildMessageBlocks(isHeader, isMine);
|
std::vector<MessageBlock> blocks = buildMessageBlocks(isHeader, isMine);
|
||||||
|
|||||||
Reference in New Issue
Block a user