Skip to content

Commit 8348089

Browse files
authored
speed improvements to Aurora FX (#4926)
* improvements to Aurora FX - converted to integer math, increasing speed on all ESPs, also shrinks code size - caching values to avoid repeated calculations - CRGBW instead or CRGB, adds white channel support when not using palette - fix for new brightness/gamma handling * overflow & unsigned fix
1 parent 4f96886 commit 8348089

File tree

1 file changed

+80
-81
lines changed

1 file changed

+80
-81
lines changed

wled00/FX.cpp

Lines changed: 80 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -4669,7 +4669,8 @@ static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator@!,!;;!;01
46694669

46704670

46714671
/*
4672-
Aurora effect
4672+
Aurora effect by @Mazen
4673+
improved and converted to integer math by @dedehai
46734674
*/
46744675

46754676
//CONFIG
@@ -4681,140 +4682,138 @@ static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator@!,!;;!;01
46814682
#define W_MAX_SPEED 6 //Higher number, higher speed
46824683
#define W_WIDTH_FACTOR 6 //Higher number, smaller waves
46834684

4684-
//24 bytes
4685+
// fixed-point math scaling
4686+
#define AW_SHIFT 16
4687+
#define AW_SCALE (1 << AW_SHIFT) // 65536 representing 1.0
4688+
4689+
// 32 bytes
46854690
class AuroraWave {
46864691
private:
4692+
int32_t center; // scaled by AW_SCALE
4693+
uint32_t ageFactor_cached; // cached age factor scaled by AW_SCALE
46874694
uint16_t ttl;
4688-
CRGB basecolor;
4689-
float basealpha;
46904695
uint16_t age;
46914696
uint16_t width;
4692-
float center;
4697+
uint16_t basealpha; // scaled by AW_SCALE
4698+
uint16_t speed_factor; // scaled by AW_SCALE
4699+
int16_t wave_start; // wave start LED index
4700+
int16_t wave_end; // wave end LED index
46934701
bool goingleft;
4694-
float speed_factor;
46954702
bool alive = true;
4703+
CRGBW basecolor;
46964704

46974705
public:
4698-
void init(uint32_t segment_length, CRGB color) {
4706+
void init(uint32_t segment_length, CRGBW color) {
46994707
ttl = hw_random16(500, 1501);
47004708
basecolor = color;
4701-
basealpha = hw_random8(60, 101) / (float)100;
4709+
basealpha = hw_random8(60, 100) * AW_SCALE / 100; // 0-99% note: if using 100% there is risk of integer overflow
47024710
age = 0;
4703-
width = hw_random16(segment_length / 20, segment_length / W_WIDTH_FACTOR); //half of width to make math easier
4704-
if (!width) width = 1;
4705-
center = hw_random8(101) / (float)100 * segment_length;
4706-
goingleft = hw_random8(0, 2) == 0;
4707-
speed_factor = (hw_random8(10, 31) / (float)100 * W_MAX_SPEED / 255);
4711+
width = hw_random16(segment_length / 20, segment_length / W_WIDTH_FACTOR) + 1;
4712+
center = (((uint32_t)hw_random8(101) << AW_SHIFT) / 100) * segment_length; // 0-100%
4713+
goingleft = hw_random8() & 0x01; // 50/50 chance
4714+
speed_factor = (((uint32_t)hw_random8(10, 31) * W_MAX_SPEED) << AW_SHIFT) / (100 * 255);
47084715
alive = true;
47094716
}
47104717

4711-
CRGB getColorForLED(int ledIndex) {
4712-
if(ledIndex < center - width || ledIndex > center + width) return 0; //Position out of range of this wave
4713-
4714-
CRGB rgb;
4715-
4716-
//Offset of this led from center of wave
4717-
//The further away from the center, the dimmer the LED
4718-
float offset = ledIndex - center;
4719-
if (offset < 0) offset = -offset;
4720-
float offsetFactor = offset / width;
4721-
4722-
//The age of the wave determines it brightness.
4723-
//At half its maximum age it will be the brightest.
4724-
float ageFactor = 0.1;
4725-
if((float)age / ttl < 0.5) {
4726-
ageFactor = (float)age / (ttl / 2);
4718+
void updateCachedValues() {
4719+
uint32_t half_ttl = ttl >> 1;
4720+
if (age < half_ttl) {
4721+
ageFactor_cached = ((uint32_t)age << AW_SHIFT) / half_ttl;
47274722
} else {
4728-
ageFactor = (float)(ttl - age) / ((float)ttl * 0.5);
4723+
ageFactor_cached = ((uint32_t)(ttl - age) << AW_SHIFT) / half_ttl;
47294724
}
4725+
if (ageFactor_cached >= AW_SCALE) ageFactor_cached = AW_SCALE - 1; // prevent overflow
4726+
4727+
uint32_t center_led = center >> AW_SHIFT;
4728+
wave_start = (int16_t)center_led - (int16_t)width;
4729+
wave_end = (int16_t)center_led + (int16_t)width;
4730+
}
47304731

4731-
//Calculate color based on above factors and basealpha value
4732-
float factor = (1 - offsetFactor) * ageFactor * basealpha;
4733-
rgb.r = basecolor.r * factor;
4734-
rgb.g = basecolor.g * factor;
4735-
rgb.b = basecolor.b * factor;
4732+
CRGBW getColorForLED(int ledIndex) {
4733+
// linear brightness falloff from center to edge of wave
4734+
if (ledIndex < wave_start || ledIndex > wave_end) return 0;
4735+
int32_t ledIndex_scaled = (int32_t)ledIndex << AW_SHIFT;
4736+
int32_t offset = ledIndex_scaled - center;
4737+
if (offset < 0) offset = -offset;
4738+
uint32_t offsetFactor = offset / width; // scaled by AW_SCALE
4739+
if (offsetFactor > AW_SCALE) return 0; // outside of wave
4740+
uint32_t brightness_factor = (AW_SCALE - offsetFactor);
4741+
brightness_factor = (brightness_factor * ageFactor_cached) >> AW_SHIFT;
4742+
brightness_factor = (brightness_factor * basealpha) >> AW_SHIFT;
4743+
4744+
CRGBW rgb;
4745+
rgb.r = (basecolor.r * brightness_factor) >> AW_SHIFT;
4746+
rgb.g = (basecolor.g * brightness_factor) >> AW_SHIFT;
4747+
rgb.b = (basecolor.b * brightness_factor) >> AW_SHIFT;
4748+
rgb.w = (basecolor.w * brightness_factor) >> AW_SHIFT;
47364749

47374750
return rgb;
47384751
};
47394752

47404753
//Change position and age of wave
4741-
//Determine if its sill "alive"
4754+
//Determine if its still "alive"
47424755
void update(uint32_t segment_length, uint32_t speed) {
4743-
if(goingleft) {
4744-
center -= speed_factor * speed;
4745-
} else {
4746-
center += speed_factor * speed;
4747-
}
4748-
4756+
int32_t step = speed_factor * speed;
4757+
center += goingleft ? -step : step;
47494758
age++;
47504759

4751-
if(age > ttl) {
4760+
if (age > ttl) {
47524761
alive = false;
47534762
} else {
4754-
if(goingleft) {
4755-
if(center + width < 0) {
4756-
alive = false;
4757-
}
4758-
} else {
4759-
if(center - width > segment_length) {
4760-
alive = false;
4761-
}
4762-
}
4763+
uint32_t width_scaled = (uint32_t)width << AW_SHIFT;
4764+
uint32_t segment_length_scaled = segment_length << AW_SHIFT;
4765+
4766+
if (goingleft) {
4767+
if (center < - (int32_t)width_scaled) {
4768+
alive = false;
4769+
}
4770+
} else {
4771+
if (center > (int32_t)segment_length_scaled + (int32_t)width_scaled) {
4772+
alive = false;
4773+
}
4774+
}
47634775
}
47644776
};
47654777

4766-
bool stillAlive() {
4767-
return alive;
4768-
};
4778+
bool stillAlive() { return alive; }
47694779
};
47704780

47714781
uint16_t mode_aurora(void) {
47724782
AuroraWave* waves;
47734783
SEGENV.aux1 = map(SEGMENT.intensity, 0, 255, 2, W_MAX_COUNT); // aux1 = Wavecount
4774-
if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { // 20 on ESP32, 9 on ESP8266
4775-
return mode_static(); //allocation failed
4784+
if (!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) {
4785+
return mode_static();
47764786
}
47774787
waves = reinterpret_cast<AuroraWave*>(SEGENV.data);
47784788

4779-
if(SEGENV.call == 0) {
4780-
for (int i = 0; i < SEGENV.aux1; i++) {
4781-
waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(hw_random8(), false, false, hw_random8(0, 3))));
4782-
}
4783-
}
4784-
4789+
// note: on first call, SEGENV.data is zero -> all waves are dead and will be initialized
47854790
for (int i = 0; i < SEGENV.aux1; i++) {
4786-
//Update values of wave
47874791
waves[i].update(SEGLEN, SEGMENT.speed);
4788-
4789-
if(!(waves[i].stillAlive())) {
4790-
//If a wave dies, reinitialize it starts over.
4791-
waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(hw_random8(), false, false, hw_random8(0, 3))));
4792+
if (!(waves[i].stillAlive())) {
4793+
waves[i].init(SEGLEN, SEGMENT.color_from_palette(hw_random8(), false, false, hw_random8(0, 3)));
47924794
}
4795+
waves[i].updateCachedValues();
47934796
}
47944797

4795-
uint8_t backlight = 1; //dimmer backlight if less active colors
4798+
uint8_t backlight = 0; // note: original code used 1, with inverse gamma applied background would never be black
47964799
if (SEGCOLOR(0)) backlight++;
47974800
if (SEGCOLOR(1)) backlight++;
47984801
if (SEGCOLOR(2)) backlight++;
4799-
//Loop through LEDs to determine color
4800-
for (unsigned i = 0; i < SEGLEN; i++) {
4801-
CRGB mixedRgb = CRGB(backlight, backlight, backlight);
4802+
backlight = gamma8inv(backlight); // preserve backlight when using gamma correction
48024803

4803-
//For each LED we must check each wave if it is "active" at this position.
4804-
//If there are multiple waves active on a LED we multiply their values.
4805-
for (int j = 0; j < SEGENV.aux1; j++) {
4806-
CRGB rgb = waves[j].getColorForLED(i);
4804+
for (unsigned i = 0; i < SEGLEN; i++) {
4805+
CRGBW mixedRgb = CRGBW(backlight, backlight, backlight);
48074806

4808-
if(rgb != CRGB(0)) {
4809-
mixedRgb += rgb;
4810-
}
4807+
for (int j = 0; j < SEGENV.aux1; j++) {
4808+
CRGBW rgb = waves[j].getColorForLED(i);
4809+
mixedRgb = color_add(mixedRgb, rgb); // sum all waves influencing this pixel
48114810
}
48124811

4813-
SEGMENT.setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2]);
4812+
SEGMENT.setPixelColor(i, mixedRgb);
48144813
}
4815-
48164814
return FRAMETIME;
48174815
}
4816+
48184817
static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;;sx=24,pal=50";
48194818

48204819
// WLED-SR effects

0 commit comments

Comments
 (0)