Monday, September 21, 2015

Lag Spikes! How I hate Thee!

So, throughout the last week of testing Eruptoid on my mobile device, I noticed a rather consistent lag spike every second or so.  Being somewhat noobish when it comes to troubleshooting such things, I took a few wild shots in the dark to try and fix it.

Game's lagging?  Must be my visual effects from my particle generators.  Let me turn those down... no change?  Too many polygons?  I only have about 2000 triangles, so that can't be it.  Let me pull up the profiler.  The unity profiler basically provides a frame by frame glimpse at every function call your game is executing, and just how long, in milliseconds, it takes for that function call to execute.  Through some trial and error in figuring out how to get the profiler to talk to my phone while the phone was playing the game, I finally got a picture of what was going on.  Sure enough, every second you could see a spike in the profile graph.  We'll just pause the profile and examine one of the spikes.

Remember that blog post I had a couple of weeks ago about how awesome Unity's new GUI system was?  How I no longer had to manually calculate the size of fonts to get them to be legible on screen?  I take a good chunk of my enthusiasm for that back, because it is the dynamic font scaling that is causing my game to lag.

Here's how it works (according to my understanding).  You have a font which needs to be scaled by whatever factor it needs scaling by to match your target screen resolution.  Unity does this at runtime.  But it doesn't just do a one time calculation for what font size it needs.  Instead, it scales the font one letter/number at a time, and then writes that font at its correct size out to a texure.  Then, when unity needs to display that same character at that same size, it just references the texture instead of calculating the font all over again.  But textures are of fixed sizes, and chances are that the texture will eventually get filled up with all the different font data.  When that happens, unity creates another texture, twice the size of the first, copies everything from the first texture into this new texture, deletes the old texture, and then continues on filling up the new texture with new scaled font characters.  This can repeat numerous times, every time the texture gets full.

I imagine this isn't usually a problem, except that I have a real time countdown clock which is updating every frame, creating a different scaled texture every frame, and thus filling the font texture very quickly.  The function "Font.CacheFontForText" ends up getting called once or twice a second, and every time it does my framerate dips, momentarily, very low.  Why you end up with is a stuttering experience, where things are smooth as butter, and then hitch, and then smooth again.  Its very distracting and very annoying.

Even more annoying was the suggestions for fixing it.  "Don't use dynamic fonts".  Seriously.  The whole point of the new unity GUI was to make a user interface easily scale-able to any resolution without a lot of manual calculations.  On fonts, this is done by making them dynamic.  So the solution to the problem is to not use the new unity gui.

Unacceptable.

But I had an idea for a work around.  It might not be perfect, and probably won't work on low end devices (sorry guys, optimizations come later).  I still use the dynamic font, but I limit the framerate to 1/2 the refresh rate of the devices screen.  On most mobile's, that means 30 fps.  When I limit it this low, the amount of font changes that need caching drops, and the lag spikes all but disappear.

Its not ideal.  Its a workaround, not a fix.  And until Unity fixes the lag spikes on Android caused by Font.CacheFontForText, its the best I'll be able to do.