From 657259acc2eab54e02464a7668a8b6d1a54c2f03 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 20 Apr 2024 21:57:04 +0200 Subject: [PATCH 1/5] Optimization: stop to constantly search for the bus Adding a caching mechanism to the Bus Manager - up to 30% faster especially when many led pins are used. --- wled00/bus_manager.cpp | 40 ++++++++++++++++++++++++++++++++++++---- wled00/bus_manager.h | 6 +++++- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 3801c534..4f68a0ee 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -175,7 +175,7 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); } -uint32_t BusDigital::getPixelColor(uint16_t pix) { +uint32_t IRAM_ATTR_YN BusDigital::getPixelColor(uint16_t pix) { if (reversed) pix = _len - pix -1; else pix += _skip; uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); @@ -724,6 +724,10 @@ int BusManager::add(BusConfig &bc) { } else { busses[numBusses] = new BusPwm(bc); } + // WLEDMM clear cached Bus info + lastBus = nullptr; + laststart = 0; + lastend = 0; return numBusses++; } @@ -734,6 +738,10 @@ void BusManager::removeAll() { while (!canAllShow()) yield(); for (uint8_t i = 0; i < numBusses; i++) delete busses[i]; numBusses = 0; + // WLEDMM clear cached Bus info + lastBus = nullptr; + laststart = 0; + lastend = 0; } void BusManager::show() { @@ -749,11 +757,24 @@ void BusManager::setStatusPixel(uint32_t c) { } void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct) { + if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr)) { + // WLEDMM same bus as last time - no need to search again + lastBus->setPixelColor(pix - laststart, c); + return; + } + for (uint_fast8_t i = 0; i < numBusses; i++) { // WLEDMM use fast native types Bus* b = busses[i]; uint_fast16_t bstart = b->getStart(); if (pix < bstart || pix >= bstart + b->getLength()) continue; - busses[i]->setPixelColor(pix - bstart, c); + else { + // WLEDMM remember last Bus we took + lastBus = b; + laststart = bstart; + lastend = bstart + b->getLength(); + b->setPixelColor(pix - bstart, c); + break; // WLEDMM found the right Bus -> so we can stop searching + } } } @@ -772,12 +793,23 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { Bus::setCCT(cct); } -uint32_t BusManager::getPixelColor(uint_fast16_t pix) { // WLEDMM use fast native types +uint32_t IRAM_ATTR BusManager::getPixelColor(uint_fast16_t pix) { // WLEDMM use fast native types, IRAM_ATTR + if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr)) { + // WLEDMM same bus as last time - no need to search again + return lastBus->getPixelColor(pix - laststart); + } + for (uint_fast8_t i = 0; i < numBusses; i++) { Bus* b = busses[i]; uint_fast16_t bstart = b->getStart(); if (pix < bstart || pix >= bstart + b->getLength()) continue; - return b->getPixelColor(pix - bstart); + else { + // WLEDMM remember last Bus we took + lastBus = b; + laststart = bstart; + lastend = bstart + b->getLength(); + return b->getPixelColor(pix - bstart); + } } return 0; } diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index f089b529..0bfd3e04 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -427,8 +427,12 @@ class BusManager { private: uint8_t numBusses = 0; - Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; + Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES] = {nullptr}; // WLEDMM init array ColorOrderMap colorOrderMap; + // WLEDMM cache last used Bus -> 20% to 30% speedup when using many LED pins + Bus *lastBus = nullptr; + unsigned laststart = 0; + unsigned lastend = 0; inline uint8_t getNumVirtualBusses() { int j = 0; From e886ece18909fd029792d860f723ad1d1314c2c3 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 20 Apr 2024 22:02:15 +0200 Subject: [PATCH 2/5] Segment::setPixelColorXY optimization use a shortcut when the segment is "simple" and just a single pixel needs to be set on HW level. --- wled00/FX_2Dfcn.cpp | 28 +++++++++++++++++++++++----- wled00/FX_fcn.cpp | 11 +++++++++++ wled00/wled.h | 2 +- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 45e37598..515bbb73 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -216,18 +216,34 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: if (Segment::maxHeight==1) return; // not a matrix set-up if (x<0 || y<0 || x >= virtualWidth() || y >= virtualHeight()) return; // if pixel would fall out of virtual segment just exit - if (ledsrgb) ledsrgb[XY(x,y)] = col; - + unsigned i = UINT_MAX; + bool sameColor = false; + if (ledsrgb) { // WLEDMM small optimization + i = XY(x,y); + if ((i < UINT_MAX) && (ledsrgb[i] == col)) sameColor = true; + else ledsrgb[i] = col; + } uint8_t _bri_t = currentBri(on ? opacity : 0); if (!_bri_t && !transitional) return; if (_bri_t < 255) { col = color_fade(col, _bri_t); } +#if 0 // this is a dangerous optimization + if ((i < UINT_MAX) && sameColor && (ledsrgb[i] == col) && (_globalLeds == nullptr)) return; // WLEDMM looks like nothing to do (but we don't trust globalleds) +#endif + if (reverse ) x = virtualWidth() - x - 1; if (reverse_y) y = virtualHeight() - y - 1; if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed + // WLEDMM shortcut when no grouping/spacing used + bool simpleSegment = !mirror && !mirror_y && (grouping == 1) && (spacing == 0); + if (simpleSegment) { + strip.setPixelColorXY(start + x, startY + y, col); + return; + } + x *= groupLength(); // expand to physical pixels y *= groupLength(); // expand to physical pixels if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit @@ -308,8 +324,10 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast // returns RGBW values of pixel uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) { if (x<0 || y<0 || !isActive()) return 0; // not active or out-of range - int i = XY(x,y); - if (ledsrgb) return RGBW32(ledsrgb[i].r, ledsrgb[i].g, ledsrgb[i].b, 0); + if (ledsrgb) { + int i = XY(x,y); + return RGBW32(ledsrgb[i].r, ledsrgb[i].g, ledsrgb[i].b, 0); + } if (reverse ) x = virtualWidth() - x - 1; if (reverse_y) y = virtualHeight() - y - 1; if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed @@ -325,7 +343,7 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t } // Adds the specified color with the existing pixel color perserving color balance. -void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { +void IRAM_ATTR_YN Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { if (!isActive()) return; // not active if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit uint32_t col = getPixelColorXY(x,y); diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 537dc662..01af8b52 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1044,6 +1044,17 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT } i += start; // starting pixel in a group +#if 0 // needs more testing + // WLEDMM shortcut when no grouping/spacing used + bool simpleSegment = !mirror && (grouping == 1) && (spacing == 0); + if (simpleSegment) { + int indexSet = i + offset; // offset/phase + if (indexSet >= stop) indexSet -= len; // wrap + strip.setPixelColor(indexSet, col); + return; + } +#endif + // set all the pixels in the group for (int j = 0; j < grouping; j++) { uint16_t indexSet = i + ((reverse) ? -j : j); diff --git a/wled00/wled.h b/wled00/wled.h index 647bfb9c..92b121f0 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2404181 +#define VERSION 2404201 // WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED. #define _MoonModules_WLED_ From d0f018586e76ee0da35b35cd82437223b03d0ef2 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 20 Apr 2024 22:42:26 +0200 Subject: [PATCH 3/5] bugfix comparing uint32_t to fastled CRGB does not work. --- wled00/FX_2Dfcn.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 515bbb73..ea61d9ab 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -220,8 +220,9 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: bool sameColor = false; if (ledsrgb) { // WLEDMM small optimization i = XY(x,y); - if ((i < UINT_MAX) && (ledsrgb[i] == col)) sameColor = true; - else ledsrgb[i] = col; + CRGB fastled_col = CRGB(col); + if (ledsrgb[i] == fastled_col) sameColor = true; + else ledsrgb[i] = fastled_col; } uint8_t _bri_t = currentBri(on ? opacity : 0); if (!_bri_t && !transitional) return; @@ -230,7 +231,7 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: } #if 0 // this is a dangerous optimization - if ((i < UINT_MAX) && sameColor && (ledsrgb[i] == col) && (_globalLeds == nullptr)) return; // WLEDMM looks like nothing to do (but we don't trust globalleds) + if ((i < UINT_MAX) && sameColor && (ledsrgb[i] == CRGB(col)) && (_globalLeds == nullptr)) return; // WLEDMM looks like nothing to do (but we don't trust globalleds) #endif if (reverse ) x = virtualWidth() - x - 1; From 6c93250cd8515b36bbe40596078ed5aacfb96e04 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 20 Apr 2024 23:24:27 +0200 Subject: [PATCH 4/5] small improvement addPixelColorXY() is actually the same code as color_add() --- wled00/FX_2Dfcn.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index ea61d9ab..1d217ff0 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -231,7 +231,7 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: } #if 0 // this is a dangerous optimization - if ((i < UINT_MAX) && sameColor && (ledsrgb[i] == CRGB(col)) && (_globalLeds == nullptr)) return; // WLEDMM looks like nothing to do (but we don't trust globalleds) + if ((i < UINT_MAX) && sameColor && (call > 0) && (ledsrgb[i] == CRGB(col)) && (_globalLeds == nullptr)) return; // WLEDMM looks like nothing to do (but we don't trust globalleds) #endif if (reverse ) x = virtualWidth() - x - 1; @@ -348,19 +348,7 @@ void IRAM_ATTR_YN Segment::addPixelColorXY(int x, int y, uint32_t color, bool fa if (!isActive()) return; // not active if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit uint32_t col = getPixelColorXY(x,y); - uint8_t r = R(col); - uint8_t g = G(col); - uint8_t b = B(col); - uint8_t w = W(col); - if (fast) { - r = qadd8(r, R(color)); - g = qadd8(g, G(color)); - b = qadd8(b, B(color)); - w = qadd8(w, W(color)); - col = RGBW32(r,g,b,w); - } else { - col = color_add(col, color); - } + col = color_add(col, color, fast); setPixelColorXY(x, y, col); } From 6e2bd7780826eb3fbfd7ab9e65a4de79ee0c6056 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 22 Apr 2024 18:20:18 +0200 Subject: [PATCH 5/5] bugfix for Ripple effect (1D mode) - solves #130 bounds check on "w" was not working. --- wled00/FX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 075a2ba9..36a40afd 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2561,7 +2561,7 @@ uint16_t ripple_base() if ((v >= 0) && (v < SEGLEN)) // WLEDMM bugfix: v and w can be negative or out-of-range SEGMENT.setPixelColor(v, color_blend(SEGMENT.getPixelColor(v), col, mag)); // TODO int w = left + propI*2 + 3 -(v-left); - if ((v >= 0) && (v < SEGLEN)) // WLEDMM bugfix: v and w can be negative or out-of-range + if ((w >= 0) && (w < SEGLEN)) // WLEDMM bugfix: v and w can be negative or out-of-range SEGMENT.setPixelColor(w, color_blend(SEGMENT.getPixelColor(w), col, mag)); // TODO } }