From ea9ae2c46bc0d3c6d5fcf6c89c1c2cba4fff363d Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 22 Feb 2025 08:43:01 +0100 Subject: [PATCH] Merge pull request #4543 from DedeHai/soap-FX-optimization Soap FX optimization --- wled00/FX.cpp | 143 +++++++++++++++++++++++--------------------------- 1 file changed, 67 insertions(+), 76 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index acedbf86..36c44061 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -8731,20 +8731,72 @@ static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@ //Soap //@Stepko //Idea from https://www.youtube.com/watch?v=DiHBgITrZck&ab_channel=StefanPetrick -// adapted for WLED by @blazoncek +// adapted for WLED by @blazoncek, optimization by @dedehai +static void soapPixels(bool isRow, uint8_t *noise3d, CRGB *pixels) { + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + const auto myXY = [&](int x, int y) { return x + y * cols; }; + const int tRC = isRow ? rows : cols; // transpose if isRow + const int tCR = isRow ? cols : rows; // transpose if isRow + const int amplitude = max(1, (tCR - 8) >> 3) * (1 + (SEGMENT.custom1 >> 5)); + const int shift = 0; //(128 - SEGMENT.custom2)*2; + + CRGB ledsbuff[tCR]; + + for (int i = 0; i < tRC; i++) { + int amount = ((int)noise3d[isRow ? i*cols : i] - 128) * amplitude + shift; // use first row/column: XY(0,i)/XY(i,0) + int delta = abs(amount) >> 8; + int fraction = abs(amount) & 255; + for (int j = 0; j < tCR; j++) { + int zD, zF; + if (amount < 0) { + zD = j - delta; + zF = zD - 1; + } else { + zD = j + delta; + zF = zD + 1; + } + int yA = abs(zD)%tCR; + int yB = abs(zF)%tCR; + int xA = i; + int xB = i; + if (isRow) { + std::swap(xA,yA); + std::swap(xB,yB); + } + const int indxA = myXY(xA,yA); + const int indxB = myXY(xB,yB); + CRGB PixelA; + CRGB PixelB; + if ((zD >= 0) && (zD < tCR)) PixelA = pixels[indxA]; + else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[indxA]*3); + if ((zF >= 0) && (zF < tCR)) PixelB = pixels[indxB]; + else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[indxB]*3); + ledsbuff[j] = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction))); + } + for (int j = 0; j < tCR; j++) { + CRGB c = ledsbuff[j]; + if (isRow) std::swap(j,i); + SEGMENT.setPixelColorXY(i, j, pixels[myXY(i,j)] = c); + if (isRow) std::swap(j,i); + } + } +} + uint16_t mode_2Dsoap() { if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); + const auto myXY = [&](int x, int y) { return x + y * cols; }; - const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(uint8_t); // prevent reallocation if mirrored or grouped - if (!SEGENV.allocateData(dataSize + sizeof(uint32_t)*3)) return mode_oops(); //allocation failed + const size_t segSize = SEGMENT.width() * SEGMENT.height(); // prevent reallocation if mirrored or grouped + const size_t dataSize = segSize * (sizeof(uint8_t) + sizeof(CRGB)); // pixels and noise + if (!SEGENV.allocateData(dataSize + sizeof(uint32_t)*3)) return mode_static(); //allocation failed - uint8_t *noise3d = reinterpret_cast(SEGENV.data); - uint32_t *noise32_x = reinterpret_cast(SEGENV.data + dataSize); - uint32_t *noise32_y = reinterpret_cast(SEGENV.data + dataSize + sizeof(uint32_t)); - uint32_t *noise32_z = reinterpret_cast(SEGENV.data + dataSize + sizeof(uint32_t)*2); + uint8_t *noise3d = reinterpret_cast(SEGENV.data); + CRGB *pixels = reinterpret_cast(SEGENV.data + segSize * sizeof(uint8_t)); + uint32_t *noisecoord = reinterpret_cast(SEGENV.data + dataSize); // x, y, z coordinates const uint32_t scale32_x = 160000U/cols; const uint32_t scale32_y = 160000U/rows; const uint32_t mov = MIN(cols,rows)*(SEGMENT.speed+2)/2; @@ -8755,27 +8807,17 @@ uint16_t mode_2Dsoap() { random16_set_seed(535); //WLEDMM SuperSync SEGENV.setUpLeds(); SEGMENT.fill(BLACK); - *noise32_x = random16(); - *noise32_y = random16(); - *noise32_z = random16(); } - //WLEDMM: changing noise calculation for SuperSync to make it deterministic using strip.now - uint32_t noise32_x_MM = *noise32_x; - uint32_t noise32_y_MM = *noise32_y; - uint32_t noise32_z_MM = *noise32_z; - - //WLEDMM SuperSync - noise32_x_MM = *noise32_x + mov * strip.now / 100; //10 fps (original 20-40 fps, depending on realized fps) - noise32_y_MM = *noise32_y + mov * strip.now / 100; - noise32_z_MM = *noise32_z + mov * strip.now / 100; + if (SEGENV.call == 0) for (int i = 0; i < 3; i++) noisecoord[i] = hw_random(); // init + else for (int i = 0; i < 3; i++) noisecoord[i] += mov; for (int i = 0; i < cols; i++) { int32_t ioffset = scale32_x * (i - cols / 2); for (int j = 0; j < rows; j++) { int32_t joffset = scale32_y * (j - rows / 2); - uint8_t data = perlin16(noise32_x_MM + ioffset, noise32_y_MM + joffset, noise32_z_MM) >> 8; //WLEDMM SuperSync - noise3d[XY(i,j)] = scale8(noise3d[XY(i,j)], smoothness) + scale8(data, 255 - smoothness); + uint8_t data = perlin16(noisecoord[0] + ioffset, noisecoord[1] + joffset, noisecoord[2]) >> 8; + noise3d[myXY(i,j)] = scale8(noise3d[myXY(i,j)], smoothness) + scale8(data, 255 - smoothness); } } // init also if dimensions changed @@ -8784,68 +8826,17 @@ uint16_t mode_2Dsoap() { SEGMENT.aux1 = rows; for (int i = 0; i < cols; i++) { for (int j = 0; j < rows; j++) { - SEGMENT.setPixelColorXY(i, j, ColorFromPalette(SEGPALETTE,~noise3d[XY(i,j)]*3)); + SEGMENT.setPixelColorXY(i, j, ColorFromPalette(SEGPALETTE,~noise3d[myXY(i,j)]*3)); } } } - int zD; - int zF; - int amplitude; - int8_t shiftX = 0; //(SEGMENT.custom1 - 128) / 4; - int8_t shiftY = 0; //(SEGMENT.custom2 - 128) / 4; - - amplitude = (cols >= 16) ? (cols-8)/8 : 1; - for (int y = 0; y < rows; y++) { - int amount = ((int)noise3d[XY(0,y)] - 128) * 2 * amplitude + 256*shiftX; - int delta = abs(amount) >> 8; - int fraction = abs(amount) & 255; - for (int x = 0; x < cols; x++) { - if (amount < 0) { - zD = x - delta; - zF = zD - 1; - } else { - zD = x + delta; - zF = zD + 1; - } - CRGB PixelA = CRGB::Black; - if ((zD >= 0) && (zD < cols)) PixelA = SEGMENT.getPixelColorXY(zD, y); - else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[XY(abs(zD),y)]*3); - CRGB PixelB = CRGB::Black; - if ((zF >= 0) && (zF < cols)) PixelB = SEGMENT.getPixelColorXY(zF, y); - else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(abs(zF),y)]*3); - CRGB pix = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction))); - SEGMENT.setPixelColorXY(x, y, pix); - } - } - - amplitude = (rows >= 16) ? (rows-8)/8 : 1; - for (int x = 0; x < cols; x++) { - int amount = ((int)noise3d[XY(x,0)] - 128) * 2 * amplitude + 256*shiftY; - int delta = abs(amount) >> 8; - int fraction = abs(amount) & 255; - for (int y = 0; y < rows; y++) { - if (amount < 0) { - zD = y - delta; - zF = zD - 1; - } else { - zD = y + delta; - zF = zD + 1; - } - CRGB PixelA = CRGB::Black; - if ((zD >= 0) && (zD < rows)) PixelA = SEGMENT.getPixelColorXY(x, zD); - else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[XY(x,abs(zD))]*3); - CRGB PixelB = CRGB::Black; - if ((zF >= 0) && (zF < rows)) PixelB = SEGMENT.getPixelColorXY(x, zF); - else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(x,abs(zF))]*3); - CRGB pix = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction))); - SEGMENT.setPixelColorXY(x, y, pix); - } - } + soapPixels(true, noise3d, pixels); // rows + soapPixels(false, noise3d, pixels); // cols return FRAMETIME; } -static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2"; +static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness,Density;;!;2;pal=11"; //Idea from https://www.youtube.com/watch?v=HsA-6KIbgto&ab_channel=GreatScott%21