Monday, September 23, 2013

Gridiron Solitaire #73: Performance!

Please note that I added an exclamation point this week.

If you'll remember, last week I was discussing how the crowd animation pushed CPU usage up in the 120% range (30% per core with a four-core CPU). I upgraded to Visual Studio 2012 (which has some build in performance profilers that are quite useful), and when I ran the profiler, it confirmed that the CPU load was basically 30% (per core) during the crowd animation.

Otherwise, it was steady between 15-20%.

It looked fine on my desktop, but on the crappy netbook, the crowd moved one time in five seconds. Zombie fans.

My performance issue could be easily traced to the individual rectangles that make up the crowd:


Each one of those little rectangles has its own geometry. They're each separate objects. Combined, they make up one crowd "panel", and there are 20+ crowd panels visible in each stadium.

In other words, that's a ton of animation of individual objects.

DQ VB.NET Advisor Garret Rempel mentioned this in an e-mail:
What I would recommend trying is instead of using an export from Expression design, replace the brush with a single textured element, an image. The texture will be a single image that has all the crowd rectangles.

You're probably thinking "Damn! Why didn't you do it that way to start with?" The answer is that I didn't know how. I could do the XAML export and make it work (and was quite proud of it, too). I had no idea what Garret was suggesting was even possible.

Two years later, though, I could at least vaguely understand: instead of a 3D image (a vector), use a 2D image (a .png file), then analyze the image and change the colors as needed.

Realizing that was possible was very exciting, because I suspected that 2D images would cut the processing load significantly, and maybe then the crowd would animate on the crappy netbook, which would mean it would animate on everything.

Thus began five days of work, trying to understand how the hell this might work. After I did understand, finally, and put the code together, it took only 23 lines.

Which took me about 23 hours to write and debug.

If you don't already know about this stuff, here's an explanation. When the single game window loads, it loads the image I've specified as the crowd "blank." Then, the color information for each pixel in the image is retrieved (alpha, blue, green, red). If the color in the pixel is red, then that pixel is changed to the home team's primary color. If the color in the pixel is blue, then that pixel is changed to the home team's secondary color.

After the image is processed, it's copied to a bitmap, when is then used as the background for a brush, and the brush is used as the background for all the crowd rectangles.

Voodoo.

After I got this working, I tested the performance. Instead of the CPU load shooting up from 20% to 30% per core, it did nothing. The entire crowd was wiggling like crazy, and the CPU load was unchanged. Then I put it on the crappy netbook, and for the first time ever, the crowd was wiggling like it should.

I can't believe it, but there you go. Zero CPU load, essentially, for adding the crowd animation.

Plus, since I'm using a 2D image for the crowd now, we can experiment with different crowd shapes and sizes (Fredrik is already working on this). All I need are two "key" colors that I can use to signal when the primary/secondary colors of the home team should be inserted.

We might even be able to do things like 4X the number of fans in an individual rectangle.

So progress, in short. Better performance and much more flexibility. And a bit less hair on my head.

Site Meter