Forum Rule: Always post complete source code & details to reproduce any issue!

# Thread: LED animation rendering: framerate drops over time significantly - what causes this?

1. ## LED animation rendering: framerate drops over time significantly - what causes this?

Hi,

I'm rewriting my Processing simulation code for Arduino + FastLED. The animation runs fine and looks as expected - but is way slower than I'd expect on a small 16x16 matrix. I guess I made a silly mistake and I#m too blind to see it.

My guess was: Is it possible, that sinf() gets really slow with (very) large angles? I tryed to avoid it by using fmodf() before, but no change.

The (in my eyes) critical part of the code is here, it gets called for every single pixel:

Code:
```// calculate distance and angle of the point relative to
// the origin described by center_x & center_y

void get_polar_values() {

// calculate the cartesian distances (deltas) from polar origin point
// aka visual rotation center
float dx = x - center_x;
float dy = y - center_y;

// calculate distance between current point & polar origin
// it's hypotf(), length of the origin vector, pytharorean theroem
dist = hypotf(dx, dy);

// calculate the angle
// (where around the polar origin is the current point?)
angle = atan2f(dy, dx);

}

// convert polar coordinates back to cartesian
// & render noise value there

uint8_t render_pixel() {

// convert polar coordinates back to cartesian ones
float newx = (offset_x + center_x - (cosf(newangle) * newdist)) * scale_x;
float newy = (offset_y + center_y - (sinf(newangle) * newdist)) * scale_y;

// render noisevalue at this new cartesian point
uint16_t noise_field_value = inoise16(newx, newy, z);

// A) enhance histogram (improve contrast) by setting the black and white point
// B) scale the result to a 0-255 range
// it's the contrast boosting & the "colormapping" (technically brightness mapping)

if (noise_field_value<30000) noise_field_value = 30000;
if (noise_field_value>50000) noise_field_value = 50000;
uint8_t value = map(noise_field_value, 30000, 50000, 0, 255);
return value;

}```
It runs on a Teensy 3.6 with 256 LEDs at the beginning @240fps and drops within minutes down to 45fps with only 3 animation layers. Both numbers feel just not right. I suspect I do something stupid I'm not aware of. I use no lookup table for the polar data, because later I want to move the polar origin during runtime, too.

Here the complete code for context what I'm doing:

Code:
```// (c) by Stefan Petrick 2023

#include <FastLED.h>

#define WIDTH 16
#define HEIGHT 16
#define NUM_LEDS ((WIDTH) * (HEIGHT))

CRGB leds[WIDTH*HEIGHT];

float c, d, e, f, time;                 // some timedependant counters
float newdist, newangle;           // parameters for reconstruction
float z;                                    // 3d dimension for the 3d noise function
float offset_x, offset_y;            // wanna shift the cartesians during runtime?
float scale_x, scale_y;              // cartesian scaling in 2 dimensions
float dist, angle;                      // the actual polar coordinates

int x, y;                                            // the cartesian coordiantes
int num_x = WIDTH;                         // horizontal pixel count
int num_y = HEIGHT;                         // vertical pixel count

float center_x = (num_x / 2) - 0.5;   // the reference point for polar coordinates
float center_y = (num_y / 2) - 0.5;   // (can also be outside of the actual xy matrix)

void setup() {

Serial.begin(115200);
FastLED.addLeds<APA102, 33, 32, BGR, DATA_RATE_MHZ(12)>(leds, 256);

}

void loop() {

// set timers

time = millis();         // save the current running time
time = time * 0.5 ;        // global anaimation speed (higher value = faster)
c = time / 400;        // speeds of the subtimers (smaller value = faster)
d = time / 800;
e = time / 600;

// calculate whats constant in the current frame

center_x = (num_x / 2) - 0.5;   // the reference point for polar coordinates
center_y = (num_y / 2) - 0.5;

for (x = 0; x < num_x; x++) {
for (y = 0; y < num_y; y++) {

// layer No 1: RED-------------------------------------------------------------

// calculate distance and angle of the point relative to
// the origin described by center_x & center_y

get_polar_values();

// set all parameters for the first layer (red)

scale_x = 5000;
scale_y = 5000;
newangle = angle * 3 + c - dist/4;
newdist = dist;
z = c * 10000;
offset_x = 0;
offset_y = c*5;

// convert polar coordinates back to cartesian
// & render noise value there

byte show1 = render_pixel();

// layer 2: Green

scale_x = 6000;
scale_y = 6000;
newangle = angle * 3 + c*1.1 - dist/4;
newdist = dist;
z = c * 12000;
offset_x = 0;
offset_y = d*5;

// convert polar coordinates back to cartesian
// & render noise value there

byte show2 = render_pixel();

// layer 3: blue

scale_x = 7000;
scale_y = 7000;
newangle = angle * 3 + c*1.2 - dist/4;
newdist = dist;
z = c * 13000;
offset_x = 0;
offset_y = e*5;

// convert polar coordinates back to cartesian
// & render noise value there

byte show3 = render_pixel();

CRGB color = CRGB(show1,show2,show3);
leds[XY(x, y)] = color;

}
}

FastLED.show();

EVERY_N_MILLIS(100) Serial.println(FastLED.getFPS());
}

void get_polar_values() {

// calculate the cartesian distances (deltas) from polar origin point
// aka visual rotation center
float dx = x - center_x;
float dy = y - center_y;

// calculate distance between current point & polar origin
// it's hypotf(), length of the origin vector, pytharorean theroem
//dist = sqrt((dx*dx)+(dy*dy));
dist = hypotf(dx, dy);

// calculate the angle
// (where around the polar origin is the current point?)
angle = atan2f(dy, dx);

// done, that's all we need
}

uint8_t render_pixel() {

// convert polar coordinates back to cartesian ones

fmodf(newangle, 2 * PI);  // no solution to the fps decline, but slower to begin with

float newx = (offset_x + center_x - (cosf(newangle) * newdist)) * scale_x;
float newy = (offset_y + center_y - (sinf(newangle) * newdist)) * scale_y;

// render noisevalue at this new cartesian point
uint16_t noise_field_value = inoise16(newx, newy, z);

// A) enhance histogram (improve contrast) by setting the black and white point
// B) scale the result to a 0-255 range
// it's the contrast boosting & the "colormapping" (technically brightness mapping)

// #################### not sure if the following 3 lines are unperformant?!
// #################### do the datatypes make sense?

if (noise_field_value<30000) noise_field_value = 30000;
if (noise_field_value>50000) noise_field_value = 50000;
uint8_t value = map(noise_field_value, 30000, 50000, 0, 255);
return value;

// done, we've just rendered one color value for one single pixel
}

uint16_t XY(uint8_t x, uint8_t y) {
//if (x >= WIDTH) return NUM_LEDS;
//if (y >= HEIGHT) return NUM_LEDS;
if (y & 1)
return (y + 1) * WIDTH - 1 - x;
else
return y * WIDTH + x;
}

{
for (uint16_t i = 0; i < NUM_LEDS; i++)
{
leds[i].r = dim8_video(leds[i].r);
leds[i].g = dim8_video(leds[i].g);
leds[i].b = dim8_video(leds[i].b);

}
}```
Any hint, comment or roast is appreciated! I'm a bit lost.  Reply With Quote

2.  Reply With Quote

3. Found the problem, solved. FPS in the stable 400s now. Good.

Will explain the solution and final result when it's done.  Reply With Quote

4. Just a short video showing the current state. I reconsidered what is really required to calculate for each single pixel and what can be done just once per frame. Also fixed the collapsing framerate - sinf and cosf become really slow when fed with ever increasing angles.

Btw, behind the paper diffusor are only 16x16 LEDs, I'm really looking forward to seeing this stuff on high resolution setups with 2k or more LEDs! And please forgive my underwelming webcam, in reality the dynamic range is better.  Reply With Quote

#### Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•