@@ -70,6 +70,12 @@ Segment::Segment(const Segment &orig) {
7070 if (orig.data ) { if (allocateData (orig._dataLen )) memcpy (data, orig.data , orig._dataLen ); }
7171 if (orig.pixels ) {
7272 pixels = static_cast <uint32_t *>(d_malloc (sizeof (uint32_t ) * orig.length ()));
73+
74+ // pixels = static_cast<uint32_t*>(heap_caps_malloc(orig.length()* sizeof(uint32_t), MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL)); // use this for ESP32
75+ // pixels = static_cast<uint32_t*>(heap_caps_malloc(sizeof(uint32_t) * orig.length(), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
76+ // pixels = static_cast<uint32_t*>(heap_caps_malloc(sizeof(uint32_t) * orig.length(), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
77+
78+
7379 if (pixels) memcpy (pixels, orig.pixels , sizeof (uint32_t ) * orig.length ());
7480 else {
7581 DEBUG_PRINTLN (F (" !!! Not enough RAM for pixel buffer !!!" ));
@@ -111,6 +117,10 @@ Segment& Segment::operator= (const Segment &orig) {
111117 if (orig.data ) { if (allocateData (orig._dataLen )) memcpy (data, orig.data , orig._dataLen ); }
112118 if (orig.pixels ) {
113119 pixels = static_cast <uint32_t *>(d_malloc (sizeof (uint32_t ) * orig.length ()));
120+ // TODO: also need to put this in 32bit memory on ESP32, maybe make that a function...
121+ // pixels = static_cast<uint32_t*>(heap_caps_malloc(sizeof(uint32_t) * orig.length(), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
122+ // pixels = static_cast<uint32_t*>(heap_caps_malloc(sizeof(uint32_t) * orig.length(), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
123+
114124 if (pixels) memcpy (pixels, orig.pixels , sizeof (uint32_t ) * orig.length ());
115125 else {
116126 DEBUG_PRINTLN (F (" !!! Not enough RAM for pixel buffer !!!" ));
@@ -143,13 +153,22 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
143153
144154// allocates effect data buffer on heap and initialises (erases) it
145155bool Segment::allocateData (size_t len) {
146- if (len == 0 ) return false ; // nothing to do
147- if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation)
156+ if (len == 0 ) return false ; // nothing to do
157+ if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation)
148158 if (call == 0 ) {
149- // DEBUG_PRINTF_P(PSTR("-- Clearing data (%d): %p\n"), len, this);
150- memset (data, 0 , len); // erase buffer if called during effect initialisation
159+ if (checkHeapHealth ()) {
160+ // DEBUG_PRINTF_P(PSTR("-- Clearing data (%d): %p\n"), len, this);
161+ memset (data, 0 , len); // erase buffer if called during effect initialisation
162+ return true ; // no need to reallocate
163+ }
164+ else {
165+ d_free (data); // free data and try to allocate again
166+ data = nullptr ;
167+ Segment::addUsedSegmentData (-_dataLen); // subtract buffer size
168+ }
151169 }
152- return true ;
170+ else
171+ return true ;
153172 }
154173 // DEBUG_PRINTF_P(PSTR("-- Allocating data (%d): %p\n"), len, this);
155174 if (Segment::getUsedSegmentData () + len - _dataLen > MAX_SEGMENT_DATA) {
@@ -158,23 +177,29 @@ bool Segment::allocateData(size_t len) {
158177 errorFlag = ERR_NORAM;
159178 return false ;
160179 }
161- // prefer DRAM over SPI RAM on ESP32 since it is slow
180+ // prefer DRAM over PSRAM for speed
162181 if (data) {
163182 data = (byte*)d_realloc_malloc (data, len); // realloc with malloc fallback
164- if (!data) {
165- data = nullptr ;
183+ if (data == nullptr ) { // allocation failed
166184 Segment::addUsedSegmentData (-_dataLen); // subtract original buffer size
167185 _dataLen = 0 ; // reset data length
186+ return false ;
168187 }
169188 }
170189 else data = (byte*)d_malloc (len);
171190
172191 if (data) {
173- memset (data, 0 , len); // erase buffer
174- Segment::addUsedSegmentData (len - _dataLen);
175- _dataLen = len;
176- // DEBUG_PRINTF_P(PSTR("--- Allocated data (%p): %d/%d -> %p\n"), this, len, Segment::getUsedSegmentData(), data);
177- return true ;
192+ if (!checkHeapHealth ()) {
193+ d_free (data);
194+ data = nullptr ;
195+ }
196+ else {
197+ memset (data, 0 , len); // erase buffer
198+ Segment::addUsedSegmentData (len);
199+ _dataLen = len;
200+ // DEBUG_PRINTF_P(PSTR("--- Allocated data (%p): %d/%d -> %p\n"), this, len, Segment::getUsedSegmentData(), data);
201+ return true ;
202+ }
178203 }
179204 // allocation failed
180205 DEBUG_PRINTLN (F (" !!! Allocation failed. !!!" ));
@@ -205,7 +230,11 @@ void Segment::deallocateData() {
205230void Segment::resetIfRequired () {
206231 if (!reset || !isActive ()) return ;
207232 // DEBUG_PRINTF_P(PSTR("-- Segment reset: %p\n"), this);
208- if (data && _dataLen > 0 ) memset (data, 0 , _dataLen); // prevent heap fragmentation (just erase buffer instead of deallocateData())
233+ if (data && _dataLen > 0 ) {
234+ if (_dataLen > FAIR_DATA_PER_SEG) deallocateData (); // do not keep large allocations
235+ else memset (data, 0 , _dataLen); // can prevent heap fragmentation
236+ DEBUG_PRINTF_P (PSTR (" -- Segment %p reset, data cleared\n " ), this );
237+ }
209238 if (pixels) for (size_t i = 0 ; i < length (); i++) pixels[i] = BLACK; // clear pixel buffer
210239 next_time = 0 ; step = 0 ; call = 0 ; aux0 = 0 ; aux1 = 0 ;
211240 reset = false ;
@@ -454,16 +483,26 @@ void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, ui
454483 stop = 0 ;
455484 return ;
456485 }
457- // re- allocate FX render buffer
486+ // allocate FX render buffer
458487 if (length () != oldLength) {
459- if (pixels) d_free (pixels); // using realloc on large buffers can cause additional fragmentation instead of reducing it
460- pixels = static_cast <uint32_t *>(d_malloc (sizeof (uint32_t ) * length ()));
488+ if (pixels) free (pixels); // note: using realloc can block larger heap segments
489+ #ifdef ARDUINO_ARCH_ESP32
490+ pixels = static_cast <uint32_t *>(pixelbuffer_malloc (izeof (uint32_t ) * length ());
491+ #else
492+ pixels = static_cast <uint32_t *>(p_malloc (sizeof (uint32_t ) * length ()));
493+ #endif
494+
495+ if (!checkHeapHealth ()) {
496+ d_free (pixels);
497+ pixels = nullptr ;
498+ }
461499 if (!pixels) {
462500 DEBUG_PRINTLN (F (" !!! Not enough RAM for pixel buffer !!!" ));
463501 errorFlag = ERR_NORAM_PX;
464502 stop = 0 ;
465503 return ;
466504 }
505+
467506 }
468507 refreshLightCapabilities ();
469508}
@@ -1198,7 +1237,7 @@ void WS2812FX::finalizeInit() {
11981237 bus->begin ();
11991238 bus->setBrightness (bri);
12001239 }
1201- DEBUG_PRINTF_P (PSTR (" Heap after buses: %d\n " ), ESP. getFreeHeap ());
1240+ DEBUG_PRINTF_P (PSTR (" Heap after buses: %d\n " ), getFreeHeapSize ());
12021241
12031242 Segment::maxWidth = _length;
12041243 Segment::maxHeight = 1 ;
@@ -1210,11 +1249,17 @@ void WS2812FX::finalizeInit() {
12101249 deserializeMap (); // (re)load default ledmap (will also setUpMatrix() if ledmap does not exist)
12111250
12121251 // allocate frame buffer after matrix has been set up (gaps!)
1213- if (_pixels) d_free (_pixels); // using realloc on large buffers can cause additional fragmentation instead of reducing it
1214- _pixels = static_cast <uint32_t *>(d_malloc (getLengthTotal () * sizeof (uint32_t )));
1252+ if (_pixels) d_free (_pixels);
1253+ #ifdef ARDUINO_ARCH_ESP32
1254+ _pixels = static_cast <uint32_t *>(pixelbuffer_malloc (getLengthTotal () * sizeof (uint32_t ), true )); // use 32bit RAM (IRAM) or PSRAM on ESP32
1255+ #elif !defined(ESP8266)
1256+ // use PSRAM on S2 and S3 if available (C3 defaults to DRAM). Note: there is no measurable perfomance impact between PSRAM and DRAM on S2/S3 with QSPI PSRAM
1257+ _pixels = static_cast <uint32_t *>(heap_caps_malloc_prefer (size, 2 , MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); // prefer PSRAM if it exists
1258+ #else
1259+ _pixels = static_cast <uint32_t *>(malloc (getLengthTotal () * sizeof (uint32_t ))); // ESP8266 does not support advanced allocation API
1260+ #endif
12151261 DEBUG_PRINTF_P (PSTR (" strip buffer size: %uB\n " ), getLengthTotal () * sizeof (uint32_t ));
1216-
1217- DEBUG_PRINTF_P (PSTR (" Heap after strip init: %uB\n " ), ESP.getFreeHeap ());
1262+ DEBUG_PRINTF_P (PSTR (" Heap after strip init: %uB\n " ), getFreeHeapSize ());
12181263}
12191264
12201265void WS2812FX::service () {
@@ -1258,7 +1303,7 @@ void WS2812FX::service() {
12581303 // if segment is in transition and no old segment exists we don't need to run the old mode
12591304 // (blendSegments() takes care of On/Off transitions and clipping)
12601305 Segment *segO = seg.getOldSegment ();
1261- if (segO && (seg.mode != segO->mode || blendingStyle != BLEND_STYLE_FADE)) {
1306+ if (segO && (seg.mode != segO->mode || blendingStyle != BLEND_STYLE_FADE) && segO-> isActive () ) {
12621307 Segment::modeBlend (true ); // set semaphore for beginDraw() to blend colors and palette
12631308 segO->beginDraw (prog); // set up palette & colors (also sets draw dimensions), parent segment has transition progress
12641309 _currentSegment = segO; // set current segment
0 commit comments