I made an animation from scratch... #331
Replies: 1 comment
-
TIL... Today's hardest lesson was solving a crash after I added the flashing "explosion" upon beat detected. I spent a while contemplating race conditions and thinking that it was like an interrupt handler modifying Something Bad. Nope. I watched for OOM cases. Nope. Memory use was flat right up through the reboot. No real leaks. CPU time was tragically short, so maybe I was running out of something important and deadlocking because at least twice, I did see evidence that a watchdog timer fired. Then I realized that no matter the framerate, the amount of spin loops, the amount of work done, the system is always almost out of idle CPU, like there's some low-priority task that's eating all the clocks, but I didn't profile it to be sure. Mellow/Jazzy albums (Nothing Like The Sun) barely triggered beat detection. Live Genesis (of the Abacab and Duke era) triggered it very quickly. It turns out that beat detection barely triggered on Sting's album, but the harsh leading edges of the canon-like drums, keyboard attacks, and the two guitarists triggered literally dozens of times more often per minute (yes, I measured...it's what engineers do) and that was a clue. I tried changing the pixel color on every draw, no difference. I made the explosions trigger in fixed places so I could see them and the crashes went away. I finally found the problem. I was using drawCircle and later, fillCircle. If you draw through FastLED via graphics(xy(x, y)) xy actually clamps x,y to be inside the leds[] array. You just can't write out of bounds. However, if you short-circuit the convenience of FastLED and try to use the Arduino/Adafruit drawing primitives (ala https://learn.adafruit.com/adafruit-gfx-graphics-library/graphics-primitives) that's crude C code that doesn't know about the "frame buffer" array and will dutifully scribble outside of memory. The epiphany that lead to this was finally getting a register dump instead of a reboot (no idea why) and observing that the $PC register was inside code named something like heap_walker, so I was looking for memory corruption. I found the extreme case is that if you are in the LR corner at (MAX_WIDTH MAX_HEIGHT) and try to draw a circle with a radius of n lines, only one in four of the pixels being written are inside leds[], with the rest being in memory we don't own. Hilarity ensues. I ultimately solved this by doing:
...while that looks unpleasant, the max and mins are able to be reduced to compile-time constants by the optimizer and std::clamp templates are constrained enough that the code emitters can generate a conditional-move (CMOV) style reduction that interleaves pretty well to reduce pipeline stalls. (The templates for std::clamp didn't find the mixing of ints and floats to be amusing...) It's not free (fillCircle will still dwarf it) but it lets the code run for hours at a time on a collection of less elevatory-y sources of tempo than the post-Dream Of The Blue Turtles jazz/pop/rock stuff that was playing when I initially developed it. So, TIL:
It was a good day. The animation itself is pretty simple (it's like table air hockey "angle of reflection equals negative angle of incidence", but with pixels) and something most of us did with VIC-20's or TRS-80 Model III's when we were first learning, but the process of putting fingers on keyboards to produce a non-zero blinkenperbit measure was satisfying. Hopefully, this condensation of the things I learned can help the next blinkenperbitter. I'll clean this up and offer the code if there's interest. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Before I got into deep ones, I wanted to be sure I understood the rules of the game as a starter project.
I started with four rays, but the infra is there to add and remove ways based on something. Dunno what yet. Maybe in response to remote.
https://photos.google.com/share/AF1QipPpN1n2I8GzuvHQ9tK79NpWnsnj7FJSbxo2qcRIuUhxC9S5rjCbk-hCDMlusdvaxQ/photo/AF1QipM1r0TD451wxxOYUfVF5jjtvnTIO4Wih-esS_bv?key=YTU4SDU1c0lqMW12NGF4bXp0cFdPS3d1aXFmZ2VB
It's on the order of a hundred lines of code. I don't know if it meets the bar for inclusion, but if you're interested, I'll submit a PR. If you have ideas how to make it more snazzy, I'm listening.
If you're not, I still spent an afternoon learning good lessons, like using arduino-level drawing from the topside of FastLED has hazards...that can be solved.
More later....
RJL
Beta Was this translation helpful? Give feedback.
All reactions