Improvements to heap-memory and PSRAM handling (#4791)
* Improved heap and PSRAM handling - Segment `allocateData()` uses more elaborate DRAM checking to reduce fragmentation and allow for larger setups to run on low heap - Segment data allocation fails if minimum contiguous block size runs low to keep the UI working - Increased `MAX_SEGMENT_DATA` to account for better segment data handling - Memory allocation functions try to keep enough DRAM for segment data - Added constant `PSRAM_THRESHOLD` to improve PSARM usage - Increase MIN_HEAP_SIZE to reduce risk of breaking UI due to low memory for JSON response - ESP32 makes use of IRAM (no 8bit access) for pixeluffers, freeing up to 50kB of RAM - Fix to properly get available heap on all platforms: added function `getFreeHeapSize()` - Bugfix for effects that divide by SEGLEN: don't run FX in service() if segment is not active -Syntax fix in AR: calloc() uses (numelements, size) as arguments * Added new functions for allocation and heap checking - added `allocate_buffer()` function that can be used to allocate large buffers: takes parameters to set preferred ram location, including 32bit accessible RAM on ESP32. Returns null if heap runs low or switches to PSRAM - getFreeHeapSize() and getContiguousFreeHeap() helper functions for all platforms to correctly report free useable heap - updated some constants - updated segment data allocation to free the data if it is large - replaced "psramsafe" variable with it's #ifdef: BOARD_HAS_PSRAM and made accomodating changes - added some compile-time checks to handle invalid env. definitions - updated all allocation functions and some of the logic behind them - added use of fast RTC-Memory where available - increased MIN_HEAP_SIZE for all systems (improved stability in tests) - updated memory calculation in web-UI to account for required segment buffer - added UI alerts if buffer allocation fails - made getUsedSegmentData() non-private (used in buffer alloc function) - changed MAX_SEGMENT_DATA - added more detailed memory log to DEBUG output - added debug output to buffer alloc function
This commit is contained in:
33
wled00/FX.h
33
wled00/FX.h
@@ -88,23 +88,26 @@ extern byte realtimeMode; // used in getMappedPixelIndex()
|
||||
#endif
|
||||
#define FPS_CALC_SHIFT 7 // bit shift for fixed point math
|
||||
|
||||
/* each segment uses 82 bytes of SRAM memory, so if you're application fails because of
|
||||
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
|
||||
// heap memory limit for effects data, pixel buffers try to reserve it if PSRAM is available
|
||||
#ifdef ESP8266
|
||||
#define MAX_NUM_SEGMENTS 16
|
||||
/* How much data bytes all segments combined may allocate */
|
||||
#define MAX_SEGMENT_DATA 5120
|
||||
#define MAX_SEGMENT_DATA (6*1024) // 6k by default
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
#define MAX_NUM_SEGMENTS 20
|
||||
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*512) // 10k by default (S2 is short on free RAM)
|
||||
#define MAX_NUM_SEGMENTS 32
|
||||
#define MAX_SEGMENT_DATA (20*1024) // 20k by default (S2 is short on free RAM), limit does not apply if PSRAM is available
|
||||
#else
|
||||
#define MAX_NUM_SEGMENTS 32 // warning: going beyond 32 may consume too much RAM for stable operation
|
||||
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default
|
||||
#ifdef BOARD_HAS_PSRAM
|
||||
#define MAX_NUM_SEGMENTS 64
|
||||
#else
|
||||
#define MAX_NUM_SEGMENTS 32
|
||||
#endif
|
||||
#define MAX_SEGMENT_DATA (64*1024) // 64k by default, limit does not apply if PSRAM is available
|
||||
#endif
|
||||
|
||||
/* How much data bytes each segment should max allocate to leave enough space for other segments,
|
||||
assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */
|
||||
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / WS2812FX::getMaxSegments())
|
||||
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / MAX_NUM_SEGMENTS)
|
||||
|
||||
#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15)
|
||||
|
||||
@@ -533,7 +536,6 @@ class Segment {
|
||||
|
||||
protected:
|
||||
|
||||
inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; }
|
||||
inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; }
|
||||
|
||||
inline uint32_t *getPixels() const { return pixels; }
|
||||
@@ -600,8 +602,8 @@ class Segment {
|
||||
, _t(nullptr)
|
||||
{
|
||||
DEBUGFX_PRINTF_P(PSTR("-- Creating segment: %p [%d,%d:%d,%d]\n"), this, (int)start, (int)stop, (int)startY, (int)stopY);
|
||||
// allocate render buffer (always entire segment)
|
||||
pixels = static_cast<uint32_t*>(d_calloc(sizeof(uint32_t), length())); // error handling is also done in isActive()
|
||||
// allocate render buffer (always entire segment), prefer PSRAM if DRAM is running low. Note: impact on FPS with PSRAM buffer is low (<2% with QSPI PSRAM)
|
||||
pixels = static_cast<uint32_t*>(allocate_buffer(length() * sizeof(uint32_t), BFRALLOC_PREFER_PSRAM | BFRALLOC_NOBYTEACCESS | BFRALLOC_CLEAR));
|
||||
if (!pixels) {
|
||||
DEBUGFX_PRINTLN(F("!!! Not enough RAM for pixel buffer !!!"));
|
||||
extern byte errorFlag;
|
||||
@@ -623,7 +625,7 @@ class Segment {
|
||||
#endif
|
||||
clearName();
|
||||
deallocateData();
|
||||
d_free(pixels);
|
||||
p_free(pixels);
|
||||
}
|
||||
|
||||
Segment& operator= (const Segment &orig); // copy assignment
|
||||
@@ -646,7 +648,7 @@ class Segment {
|
||||
inline uint16_t groupLength() const { return grouping + spacing; }
|
||||
inline uint8_t getLightCapabilities() const { return _capabilities; }
|
||||
inline void deactivate() { setGeometry(0,0); }
|
||||
inline Segment &clearName() { d_free(name); name = nullptr; return *this; }
|
||||
inline Segment &clearName() { p_free(name); name = nullptr; return *this; }
|
||||
inline Segment &setName(const String &name) { return setName(name.c_str()); }
|
||||
|
||||
inline static unsigned vLength() { return Segment::_vLength; }
|
||||
@@ -672,6 +674,7 @@ class Segment {
|
||||
inline uint16_t dataSize() const { return _dataLen; }
|
||||
bool allocateData(size_t len); // allocates effect data buffer in heap and clears it
|
||||
void deallocateData(); // deallocates (frees) effect data buffer from heap
|
||||
inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; }
|
||||
/**
|
||||
* Flags that before the next effect is calculated,
|
||||
* the internal segment state should be reset.
|
||||
@@ -868,8 +871,8 @@ class WS2812FX {
|
||||
}
|
||||
|
||||
~WS2812FX() {
|
||||
d_free(_pixels);
|
||||
d_free(_pixelCCT); // just in case
|
||||
p_free(_pixels);
|
||||
p_free(_pixelCCT); // just in case
|
||||
d_free(customMappingTable);
|
||||
_mode.clear();
|
||||
_modeData.clear();
|
||||
|
||||
Reference in New Issue
Block a user