Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 4 of 4

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

  1. #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;
    
        }
      }
    
      adjust_gamma();
      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;
    }
    
    void adjust_gamma()
    {
      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.
    Last edited by StefanPetrick; 03-11-2023 at 04:36 PM.

  2. #2
    Frames per second over time

    Click image for larger version. 

Name:	Bildschirmfoto 2023-03-11 um 17.40.34.jpg 
Views:	13 
Size:	104.9 KB 
ID:	30585

    I posted a video of the animation here on Reddit.
    Last edited by StefanPetrick; 03-11-2023 at 04:38 PM.

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

    Will explain the solution and final result when it's done.
    Last edited by StefanPetrick; 03-11-2023 at 07:12 PM.

  4. #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.



    Last edited by StefanPetrick; 03-12-2023 at 12:19 PM.

Posting Permissions

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