@@ -68,10 +68,10 @@ Segment::Segment(const Segment &orig) {
6868 if (!stop) return ; // nothing to do if segment is inactive/invalid
6969 if (orig.pixels ) {
7070 // allocate pixel buffer: prefer IRAM/PSRAM
71- pixels = static_cast <uint32_t *>(d_malloc ( sizeof ( uint32_t ) * orig. length () ));
71+ pixels = static_cast <uint32_t *>(allocate_buffer (orig. length ( ) * sizeof ( uint32_t ), BFRALLOC_PREFER_PSRAM | BFRALLOC_NOBYTEACCESS ));
7272 if (pixels) {
7373 memcpy (pixels, orig.pixels , sizeof (uint32_t ) * orig.length ());
74- if (orig.name ) { name = static_cast <char *>(d_malloc (strlen (orig.name )+1 )); if (name) strcpy (name, orig.name ); }
74+ if (orig.name ) { name = static_cast <char *>(allocate_buffer (strlen (orig.name )+1 , BFRALLOC_PREFER_PSRAM )); if (name) strcpy (name, orig.name ); }
7575 if (orig.data ) { if (allocateData (orig._dataLen )) memcpy (data, orig.data , orig._dataLen ); }
7676 } else {
7777 DEBUGFX_PRINTLN (F (" !!! Not enough RAM for pixel buffer !!!" ));
@@ -97,10 +97,10 @@ Segment& Segment::operator= (const Segment &orig) {
9797 // DEBUG_PRINTF_P(PSTR("-- Copying segment: %p -> %p\n"), &orig, this);
9898 if (this != &orig) {
9999 // clean destination
100- if (name) { d_free (name); name = nullptr ; }
100+ if (name) { p_free (name); name = nullptr ; }
101101 if (_t) stopTransition (); // also erases _t
102102 deallocateData ();
103- d_free (pixels);
103+ p_free (pixels);
104104 // copy source
105105 memcpy ((void *)this , (void *)&orig, sizeof (Segment));
106106 // erase pointers to allocated data
@@ -111,10 +111,10 @@ Segment& Segment::operator= (const Segment &orig) {
111111 // copy source data
112112 if (orig.pixels ) {
113113 // allocate pixel buffer: prefer IRAM/PSRAM
114- pixels = static_cast <uint32_t *>(d_malloc ( sizeof ( uint32_t ) * orig. length () ));
114+ pixels = static_cast <uint32_t *>(allocate_buffer (orig. length ( ) * sizeof ( uint32_t ), BFRALLOC_PREFER_PSRAM | BFRALLOC_NOBYTEACCESS ));
115115 if (pixels) {
116116 memcpy (pixels, orig.pixels , sizeof (uint32_t ) * orig.length ());
117- if (orig.name ) { name = static_cast <char *>(d_malloc (strlen (orig.name )+1 )); if (name) strcpy (name, orig.name ); }
117+ if (orig.name ) { name = static_cast <char *>(allocate_buffer (strlen (orig.name )+1 , BFRALLOC_PREFER_PSRAM )); if (name) strcpy (name, orig.name ); }
118118 if (orig.data ) { if (allocateData (orig._dataLen )) memcpy (data, orig.data , orig._dataLen ); }
119119 } else {
120120 DEBUG_PRINTLN (F (" !!! Not enough RAM for pixel buffer !!!" ));
@@ -130,10 +130,10 @@ Segment& Segment::operator= (const Segment &orig) {
130130Segment& Segment::operator = (Segment &&orig) noexcept {
131131 // DEBUG_PRINTF_P(PSTR("-- Moving segment: %p -> %p\n"), &orig, this);
132132 if (this != &orig) {
133- if (name) { d_free (name); name = nullptr ; } // free old name
133+ if (name) { p_free (name); name = nullptr ; } // free old name
134134 if (_t) stopTransition (); // also erases _t
135135 deallocateData (); // free old runtime data
136- d_free (pixels); // free old pixel buffer
136+ p_free (pixels); // free old pixel buffer
137137 // move source data
138138 memcpy ((void *)this , (void *)&orig, sizeof (Segment));
139139 orig.name = nullptr ;
@@ -147,35 +147,38 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
147147
148148// allocates effect data buffer on heap and initialises (erases) it
149149bool Segment::allocateData (size_t len) {
150- if (len == 0 ) return false ; // nothing to do
151- if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation)
150+ if (len == 0 ) return false ; // nothing to do
151+ if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation)
152152 if (call == 0 ) {
153- // DEBUG_PRINTF_P(PSTR("-- Clearing data (%d): %p\n"), len, this);
154- memset (data, 0 , len); // erase buffer if called during effect initialisation
153+ if (_dataLen < FAIR_DATA_PER_SEG) { // segment data is small
154+ // DEBUG_PRINTF_P(PSTR("-- Clearing data (%d): %p\n"), len, this);
155+ memset (data, 0 , len); // erase buffer if called during effect initialisation
156+ return true ; // no need to reallocate
157+ }
155158 }
156- return true ;
159+ else
160+ return true ;
157161 }
158162 // DEBUG_PRINTF_P(PSTR("-- Allocating data (%d): %p\n"), len, this);
163+ // limit to MAX_SEGMENT_DATA if there is no PSRAM, otherwise prefer functionality over speed
164+ #ifndef BOARD_HAS_PSRAM
159165 if (Segment::getUsedSegmentData () + len - _dataLen > MAX_SEGMENT_DATA) {
160166 // not enough memory
161- DEBUG_PRINTF_P (PSTR (" !!! Not enough RAM : %d/%d !!! \n " ), len, Segment::getUsedSegmentData ());
167+ DEBUG_PRINTF_P (PSTR (" SegmentData limit reached : %d/%d\n " ), len, Segment::getUsedSegmentData ());
162168 errorFlag = ERR_NORAM;
163169 return false ;
164170 }
165- // prefer DRAM over SPI RAM on ESP32 since it is slow
171+ #endif
172+
166173 if (data) {
167- data = (byte*)d_realloc_malloc (data, len); // realloc with malloc fallback
168- if (!data) {
169- data = nullptr ;
170- Segment::addUsedSegmentData (-_dataLen); // subtract original buffer size
171- _dataLen = 0 ; // reset data length
172- }
174+ d_free (data); // free data and try to allocate again (segment buffer may be blocking contiguous heap)
175+ Segment::addUsedSegmentData (-_dataLen); // subtract buffer size
173176 }
174- else data = (byte*)d_malloc (len);
177+
178+ data = static_cast <byte*>(allocate_buffer (len, BFRALLOC_PREFER_DRAM | BFRALLOC_CLEAR)); // prefer DRAM over PSRAM for speed
175179
176180 if (data) {
177- memset (data, 0 , len); // erase buffer
178- Segment::addUsedSegmentData (len - _dataLen);
181+ Segment::addUsedSegmentData (len);
179182 _dataLen = len;
180183 // DEBUG_PRINTF_P(PSTR("--- Allocated data (%p): %d/%d -> %p\n"), this, len, Segment::getUsedSegmentData(), data);
181184 return true ;
@@ -209,7 +212,11 @@ void Segment::deallocateData() {
209212void Segment::resetIfRequired () {
210213 if (!reset || !isActive ()) return ;
211214 // DEBUG_PRINTF_P(PSTR("-- Segment reset: %p\n"), this);
212- if (data && _dataLen > 0 ) memset (data, 0 , _dataLen); // prevent heap fragmentation (just erase buffer instead of deallocateData())
215+ if (data && _dataLen > 0 ) {
216+ if (_dataLen > FAIR_DATA_PER_SEG) deallocateData (); // do not keep large allocations
217+ else memset (data, 0 , _dataLen); // can prevent heap fragmentation
218+ DEBUG_PRINTF_P (PSTR (" -- Segment %p reset, data cleared\n " ), this );
219+ }
213220 if (pixels) for (size_t i = 0 ; i < length (); i++) pixels[i] = BLACK; // clear pixel buffer
214221 next_time = 0 ; step = 0 ; call = 0 ; aux0 = 0 ; aux1 = 0 ;
215222 reset = false ;
@@ -466,7 +473,7 @@ void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, ui
466473 if (length () != oldLength) {
467474 // allocate render buffer (always entire segment), prefer IRAM/PSRAM. Note: impact on FPS with PSRAM buffer is low (<2% with QSPI PSRAM) on S2/S3
468475 p_free (pixels);
469- pixels = static_cast <uint32_t *>(d_malloc ( sizeof ( uint32_t ) * length () ));
476+ pixels = static_cast <uint32_t *>(allocate_buffer ( length ( ) * sizeof ( uint32_t ), BFRALLOC_PREFER_PSRAM | BFRALLOC_NOBYTEACCESS ));
470477 if (!pixels) {
471478 DEBUGFX_PRINTLN (F (" !!! Not enough RAM for pixel buffer !!!" ));
472479 deallocateData ();
@@ -581,8 +588,8 @@ Segment &Segment::setName(const char *newName) {
581588 if (newName) {
582589 const int newLen = min (strlen (newName), (size_t )WLED_MAX_SEGNAME_LEN);
583590 if (newLen) {
584- if (name) d_free (name); // free old name
585- name = static_cast <char *>(d_malloc (newLen+1 ));
591+ if (name) p_free (name); // free old name
592+ name = static_cast <char *>(allocate_buffer (newLen+1 , BFRALLOC_PREFER_PSRAM ));
586593 if (mode == FX_MODE_2DSCROLLTEXT) startTransition (strip.getTransition (), true ); // if the name changes in scrolling text mode, we need to copy the segment for blending
587594 if (name) strlcpy (name, newName, newLen+1 );
588595 return *this ;
@@ -1215,10 +1222,11 @@ void WS2812FX::finalizeInit() {
12151222 deserializeMap (); // (re)load default ledmap (will also setUpMatrix() if ledmap does not exist)
12161223
12171224 // allocate frame buffer after matrix has been set up (gaps!)
1218- d_free (_pixels); // using realloc on large buffers can cause additional fragmentation instead of reducing it
1219- _pixels = static_cast <uint32_t *>(d_malloc (getLengthTotal () * sizeof (uint32_t )));
1225+ p_free (_pixels); // using realloc on large buffers can cause additional fragmentation instead of reducing it
1226+ // use PSRAM if available: there is no measurable perfomance impact between PSRAM and DRAM on S2/S3 with QSPI PSRAM for this buffer
1227+ _pixels = static_cast <uint32_t *>(allocate_buffer (getLengthTotal () * sizeof (uint32_t ), BFRALLOC_ENFORCE_PSRAM | BFRALLOC_NOBYTEACCESS | BFRALLOC_CLEAR));
12201228 DEBUG_PRINTF_P (PSTR (" strip buffer size: %uB\n " ), getLengthTotal () * sizeof (uint32_t ));
1221- DEBUG_PRINTF_P (PSTR (" Heap after strip init: %uB\n " ), ESP. getFreeHeap ());
1229+ DEBUG_PRINTF_P (PSTR (" Heap after strip init: %uB\n " ), getFreeHeapSize ());
12221230}
12231231
12241232void WS2812FX::service () {
@@ -1558,7 +1566,11 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
15581566}
15591567
15601568void WS2812FX::show () {
1561- if (!_pixels) return ; // no pixels allocated, nothing to show
1569+ if (!_pixels) {
1570+ DEBUGFX_PRINTLN (F (" Error: no _pixels!" ));
1571+ errorFlag = ERR_NORAM;
1572+ return ; // no pixels allocated, nothing to show
1573+ }
15621574
15631575 unsigned long showNow = millis ();
15641576 size_t diff = showNow - _lastShow;
@@ -1568,7 +1580,7 @@ void WS2812FX::show() {
15681580 // we need to keep track of each pixel's CCT when blending segments (if CCT is present)
15691581 // and then set appropriate CCT from that pixel during paint (see below).
15701582 if ((hasCCTBus () || correctWB) && !cctFromRgb)
1571- _pixelCCT = static_cast <uint8_t *>(d_malloc (totalLen * sizeof (uint8_t ))); // allocate CCT buffer if necessary
1583+ _pixelCCT = static_cast <uint8_t *>(allocate_buffer (totalLen * sizeof (uint8_t ), BFRALLOC_PREFER_PSRAM )); // allocate CCT buffer if necessary, prefer PSRAM
15721584 if (_pixelCCT) memset (_pixelCCT, 127 , totalLen); // set neutral (50:50) CCT
15731585
15741586 if (realtimeMode == REALTIME_MODE_INACTIVE || useMainSegmentOnly || realtimeOverride > REALTIME_OVERRIDE_NONE) {
@@ -1602,7 +1614,7 @@ void WS2812FX::show() {
16021614 }
16031615 Bus::setCCT (oldCCT); // restore old CCT for ABL adjustments
16041616
1605- d_free (_pixelCCT);
1617+ p_free (_pixelCCT);
16061618 _pixelCCT = nullptr ;
16071619
16081620 // some buses send asynchronously and this method will return before
0 commit comments