From 7c0ecdf7c44a115eeca7cd6624af7139368f0b06 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:51:12 +0100 Subject: [PATCH 1/6] make "target FPS" work --- wled00/FX.h | 8 +++++--- wled00/FX_fcn.cpp | 37 ++++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index cec8e5d294..c8a9d539ef 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -43,13 +43,14 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented #endif /* Not used in all effects yet */ +#define FPS_UNLIMITED 249 #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) // WLEDMM go faster on ESP32 #define WLED_FPS 120 #define FRAMETIME_FIXED (strip.getFrameTime() < 10 ? 12 : 24) #define WLED_FPS_SLOW 60 #define FRAMETIME_FIXED_SLOW (15) // = 66 FPS => 1000/66 -//#define FRAMETIME _frametime #define FRAMETIME strip.getFrameTime() +#define MIN_SHOW_DELAY (max(2, (_frametime*5)/8)) // WLEDMM support higher framerates (up to 250fps) -- 5/8 = 62% #else #define WLED_FPS 42 #define FRAMETIME_FIXED (1000/WLED_FPS) @@ -57,6 +58,7 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented #define FRAMETIME_FIXED_SLOW (1000/WLED_FPS_SLOW) //#define FRAMETIME _frametime #define FRAMETIME strip.getFrameTime() +#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15) #endif /* each segment uses 52 bytes of SRAM memory, so if you're application fails because of @@ -82,8 +84,6 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */ #define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / strip.getMaxSegments()) -#define MIN_SHOW_DELAY (_frametime < 16 ? (_frametime <8? (_frametime <7? (_frametime <6 ? 2 :3) :4) : 8) : 15) // WLEDMM support higher framerates (up to 250fps) - #define NUM_COLORS 3 /* number of colors per segment */ #define SEGMENT strip._segments[strip.getCurrSegmentId()] #define SEGENV strip._segments[strip.getCurrSegmentId()] @@ -852,6 +852,7 @@ class WS2812FX { // 96 bytes customMappingTableSize(0), //WLEDMM customMappingSize(0), _lastShow(0), + _lastServiceShow(0), _segment_index(0), _mainSegment(0) { @@ -1097,6 +1098,7 @@ class WS2812FX { // 96 bytes uint16_t customMappingSize; /*uint32_t*/ unsigned long _lastShow; // WLEDMM avoid losing precision + unsigned long _lastServiceShow; // WLEDMM last call of strip.show (timestamp) uint8_t _segment_index; uint8_t _mainSegment; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 54c21b63dc..6760c2c2ee 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1873,13 +1873,19 @@ void WS2812FX::service() { if (OTAisRunning) return; // WLEDMM avoid flickering during OTA now = nowUp + timebase; - #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) - if ((_frametime > 2) && (_frametime < 32) && (nowUp - _lastShow) < (_frametime/2)) return; // WLEDMM experimental - stabilizes frametimes but increases CPU load - else if (nowUp - _lastShow < MIN_SHOW_DELAY) return; // WLEDMM fallback - #else - if (nowUp - _lastShow < MIN_SHOW_DELAY) return; + unsigned long elapsed = nowUp - _lastServiceShow; + #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) // WLEDMM go faster on ESP32 + //if (_suspend) return; + if (elapsed < 2) return; // keep wifi alive + if ( !_triggered && (_targetFps < FPS_UNLIMITED) && (_targetFps > 0)) { + if (elapsed < MIN_SHOW_DELAY) return; // WLEDMM too early for service + } + #else // legacy + if (elapsed < MIN_SHOW_DELAY) return; #endif + bool doShow = false; + unsigned speedLimit = (_targetFps < FPS_UNLIMITED) ? (0.85f * FRAMETIME) : 1; // WLEDMM lower limit for effect frametime _isServicing = true; _segment_index = 0; @@ -1914,6 +1920,8 @@ void WS2812FX::service() { // actual code may be a bit more involved as effects have runtime data including allocated memory //if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress()); frameDelay = (*_mode[seg.currentMode(seg.mode)])(); + + if (frameDelay < speedLimit) frameDelay = FRAMETIME; // WLEDMM limit effects that want to go faster than target FPS if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; if (seg.transitional && frameDelay > FRAMETIME) frameDelay = FRAMETIME; // force faster updates during transition @@ -1929,6 +1937,7 @@ void WS2812FX::service() { if(doShow) { yield(); show(); + _lastServiceShow = nowUp; // WLEDMM use correct timestamp } _triggered = false; _isServicing = false; @@ -2053,25 +2062,30 @@ void WS2812FX::show(void) { estimateCurrentAndLimitBri(); #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) - unsigned long b4show = millis(); // WLEDMM the time before calling "show" + unsigned long now = millis(); + #ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32 + uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500 + #endif #endif + // some buses send asynchronously and this method will return before // all of the data has been sent. // See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods busses.show(); unsigned long now = millis(); + #ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32 + uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500 + #endif + #endif + unsigned long diff = now - _lastShow; uint16_t fpsCurr = 200; if (diff > 0) fpsCurr = 1000 / diff; _cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5) - #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) - _lastShow = b4show; // WLEDMM this is more accurate, however it also increases CPU load - strip.service will run more frequently - #else _lastShow = now; - #endif + _lastServiceShow = now; #ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32 - uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500 int64_t diff500 = now500 - _lastShow500; if ((diff500 > 300) && (diff500 < 800000)) { // exclude stupid values (timer rollover, major hickups) float fpcCurr500 = 500000.0f / float(diff500); @@ -2107,6 +2121,7 @@ void WS2812FX::setTargetFps(uint8_t fps) { if (fps > 0 && fps <= 251) _targetFps = fps; // WLEDMM allow higher framerates _frametime = 1000 / _targetFps; if (_frametime < 1) _frametime = 1; // WLEDMM better safe than sorry + if (fps >= FPS_UNLIMITED) _frametime = 3; // WLEDMM unlimited mode } void WS2812FX::setMode(uint8_t segid, uint8_t m) { From 1c782aa3690b61bca553e2cbf4969b6e8f37f7a7 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:58:12 +0100 Subject: [PATCH 2/6] improvement for non-fastpath --- wled00/FX.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wled00/FX.h b/wled00/FX.h index c8a9d539ef..af79c514f1 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -58,7 +58,8 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented #define FRAMETIME_FIXED_SLOW (1000/WLED_FPS_SLOW) //#define FRAMETIME _frametime #define FRAMETIME strip.getFrameTime() -#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15) +//#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15) // Upstream legacy +#define MIN_SHOW_DELAY (_frametime < 16 ? (_frametime <8? (_frametime <7? (_frametime <6 ? 2 :3) :4) : 8) : 15) // WLEDMM support higher framerates (up to 250fps) #endif /* each segment uses 52 bytes of SRAM memory, so if you're application fails because of From bf5fb9ccaa735ca8bce2e1224974ae0b224cb38e Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 1 Nov 2024 15:12:07 +0100 Subject: [PATCH 3/6] fix compile errors --- wled00/FX_fcn.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 6760c2c2ee..c23bb183e3 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -2062,7 +2062,7 @@ void WS2812FX::show(void) { estimateCurrentAndLimitBri(); #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) - unsigned long now = millis(); + unsigned long nowUp = millis(); #ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32 uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500 #endif @@ -2072,18 +2072,20 @@ void WS2812FX::show(void) { // all of the data has been sent. // See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods busses.show(); - unsigned long now = millis(); + + #if !defined(ARDUINO_ARCH_ESP32) || !defined(WLEDMM_FASTPATH) // upstream legacy + unsigned long nowUp = millis(); #ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32 uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500 #endif #endif - unsigned long diff = now - _lastShow; + unsigned long diff = nowUp - _lastShow; uint16_t fpsCurr = 200; if (diff > 0) fpsCurr = 1000 / diff; _cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5) - _lastShow = now; - _lastServiceShow = now; + _lastShow = nowUp; + _lastServiceShow = nowUp; #ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32 int64_t diff500 = now500 - _lastShow500; From 6bae3567890be082e692b51537a913bc03c8f72a Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:17:54 +0100 Subject: [PATCH 4/6] renaming upstream has renamed "now" to "showNow" - so we follow --- wled00/FX_fcn.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index c23bb183e3..44642689fd 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -2062,7 +2062,7 @@ void WS2812FX::show(void) { estimateCurrentAndLimitBri(); #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) - unsigned long nowUp = millis(); + unsigned long showNow = millis(); #ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32 uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500 #endif @@ -2074,18 +2074,18 @@ void WS2812FX::show(void) { busses.show(); #if !defined(ARDUINO_ARCH_ESP32) || !defined(WLEDMM_FASTPATH) // upstream legacy - unsigned long nowUp = millis(); + unsigned long showNow = millis(); #ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32 uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500 #endif #endif - unsigned long diff = nowUp - _lastShow; + unsigned long diff = showNow - _lastShow; uint16_t fpsCurr = 200; if (diff > 0) fpsCurr = 1000 / diff; _cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5) - _lastShow = nowUp; - _lastServiceShow = nowUp; + _lastShow = showNow; + _lastServiceShow = showNow; #ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32 int64_t diff500 = now500 - _lastShow500; From dc4964a491bbdec3985bbb044965d4b7985e77b9 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:47:05 +0100 Subject: [PATCH 5/6] small update based on feedback from the PR to upstream * support "0 FPS" meaning "unlimited" * keep FRAMETIME_FIXED fixed --- wled00/FX.h | 15 ++++++++------- wled00/FX_fcn.cpp | 14 +++++++------- wled00/data/settings_leds.htm | 6 ++++-- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index af79c514f1..f20b1caf90 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -43,20 +43,21 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented #endif /* Not used in all effects yet */ -#define FPS_UNLIMITED 249 +#define FPS_UNLIMITED 250 +#define FPS_UNLIMITED_AC 0 // WLEDMM upstream uses "0 fps" for unlimited. We support both ways #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) // WLEDMM go faster on ESP32 -#define WLED_FPS 120 -#define FRAMETIME_FIXED (strip.getFrameTime() < 10 ? 12 : 24) -#define WLED_FPS_SLOW 60 -#define FRAMETIME_FIXED_SLOW (15) // = 66 FPS => 1000/66 #define FRAMETIME strip.getFrameTime() #define MIN_SHOW_DELAY (max(2, (_frametime*5)/8)) // WLEDMM support higher framerates (up to 250fps) -- 5/8 = 62% +#define WLED_FPS 120 +#define WLED_FPS_SLOW 60 +#define FRAMETIME_FIXED 24 // used in Blurz, Freqmap, Scrolling text +//#define FRAMETIME_FIXED (strip.getFrameTime() < 10 ? 12 : 24) +#define FRAMETIME_FIXED_SLOW (15) // = 66 FPS => 1000/66 // used in Solid, Colortwinkles, Candle #else #define WLED_FPS 42 #define FRAMETIME_FIXED (1000/WLED_FPS) -#define WLED_FPS_SLOW 42 +#define WLED_FPS_SLOW 42 #define FRAMETIME_FIXED_SLOW (1000/WLED_FPS_SLOW) -//#define FRAMETIME _frametime #define FRAMETIME strip.getFrameTime() //#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15) // Upstream legacy #define MIN_SHOW_DELAY (_frametime < 16 ? (_frametime <8? (_frametime <7? (_frametime <6 ? 2 :3) :4) : 8) : 15) // WLEDMM support higher framerates (up to 250fps) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 44642689fd..5ad6583c62 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1877,15 +1877,15 @@ void WS2812FX::service() { #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) // WLEDMM go faster on ESP32 //if (_suspend) return; if (elapsed < 2) return; // keep wifi alive - if ( !_triggered && (_targetFps < FPS_UNLIMITED) && (_targetFps > 0)) { + if ( !_triggered && (_targetFps != FPS_UNLIMITED) && (_targetFps != FPS_UNLIMITED_AC)) { if (elapsed < MIN_SHOW_DELAY) return; // WLEDMM too early for service } #else // legacy - if (elapsed < MIN_SHOW_DELAY) return; + if (nowUp - _lastShow < MIN_SHOW_DELAY) return; #endif bool doShow = false; - unsigned speedLimit = (_targetFps < FPS_UNLIMITED) ? (0.85f * FRAMETIME) : 1; // WLEDMM lower limit for effect frametime + unsigned speedLimit = (_targetFps != FPS_UNLIMITED) && (_targetFps != FPS_UNLIMITED_AC) ? (0.85f * FRAMETIME) : 1; // WLEDMM minimum for effect frametime _isServicing = true; _segment_index = 0; @@ -2120,10 +2120,10 @@ uint16_t WS2812FX::getFps() const { } void WS2812FX::setTargetFps(uint8_t fps) { - if (fps > 0 && fps <= 251) _targetFps = fps; // WLEDMM allow higher framerates - _frametime = 1000 / _targetFps; - if (_frametime < 1) _frametime = 1; // WLEDMM better safe than sorry - if (fps >= FPS_UNLIMITED) _frametime = 3; // WLEDMM unlimited mode + if (fps <= 251) _targetFps = fps; // WLEDMM allow higher framerates + if (fps > 0) _frametime = 1000 / _targetFps; + else _frametime = 2; // AC WLED compatibility + if (fps >= FPS_UNLIMITED) _frametime = 2; // WLEDMM unlimited mode } void WS2812FX::setMode(uint8_t segid, uint8_t m) { diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 35d32321f0..7deff3a475 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -166,7 +166,8 @@ gId('ampwarning').style.display = (d.Sf.MA.value > 7200) ? 'inline':'none'; - gId('fpswarning').style.display = (d.Sf.FR.value > 69) ? 'block':'none'; // WLEDMM + gId('fpsNone').style.display = ((d.Sf.FR.value == 0) || (d.Sf.FR.value > 249))? 'block':'none'; // WLEDMM + gId('fpswarning').style.display = (d.Sf.FR.value > 69) || (d.Sf.FR.value == 0) ? 'block':'none'; // WLEDMM gId('fpshelp1').style.display = ((d.Sf.FR.value > 80) && (d.Sf.FR.value < 132)) ? 'block':'none'; // WLEDMM gId('fpshelp2').style.display = ((d.Sf.FR.value > 132) && (d.Sf.FR.value < 196)) ? 'block':'none'; // WLEDMM gId('fpshelp3').style.display = (d.Sf.FR.value > 196) ? 'block':'none'; // WLEDMM @@ -732,7 +733,8 @@