A lot of my work I’m doing on Masteroid right now is not “flashy”. I’ve worked a bunch on tools over the last week and, as I wrap that up, my attention is turning to performance.
I see many developers engaging in complex discussions on array optimization and similar academic/mathematical topics. But, in my practical experience after years of gamedev, I rarely find that to be an impactful performance problem. Instead, these are the three top performance problems in every game I’ve worked on:
- Collision, number and complexity of tests
- Object instantiation/destruction (allocation and garbage collection)
- Render breaks
In this post, we’re primarily talking about render breaks. First of all, render break is not used much as a term outside of the FlatRedBall (FRB from now on) game engine community. But the concept applies to any engine. A render break is when the GPU has to stop what it’s doing and switch states before it can continue rendering. Render breaks most often occur when the GPU has to switch textures or blend states.
For example, if you have a platforming level with a blue sky background, a cloud, a brick platform and a character, the GPU will render the scene something like this:
- Load the blue sky texture
- Render the blue sky background
- Unload the sky texture
- Load the cloud texture
- Render clouds
- Unload the cloud texture
- Load the brick texture
- Render platforms
- Unload the brick texture
- Load the character texture
- Render the character
- Unload the character texture
The text in red above highlights unnecessary render breaks. The GPU had to stop drawing pixels to the canvas and swap textures. This is relatively slow. In the example above, if clouds can exist both in front and behind the platforms, you could have MANY more render breaks to swap back and forth.
Since the dawn of videogames, gamedevs have dealt with this by “spritesheeting” or “texture packing” their sprites – essentially packing multiple sprites into a single texture and defining bounds for each sprite. With spritesheeting, the GPU no longer has to swap textures. It is now drawing a small part of the texture in memory to the screen.
As both the developer and the artist on most of my projects, I draw all of my sprites on a single sheet from the beginning of the project. I either use texture coordinates or animation chains in FRB to define the individual sprites in the spritesheet. So I eliminate render breaks due to texture swaps from day one.
Except for fonts.
I use Angelcode’s popular Bmfont tool to create bitmapped fonts. This tool generates a fnt file that describes each character. It also generates a png. FlatRedBall makes it super easy to use bitmap font tools natively in the engine itself, in Glue (FRB’s project layout tool), and in Gum (cross-platform UI tool).
The problem with this is that the png files for those fonts are a separate texture. UI has a lot of layers with button and panel backgrounds, fonts, icons, etc. Those layers result in huge numbers of render breaks as the GPU swaps back and forth between font textures and sprite textures.
One quick and easy hack for this is to simply drop the png into the top left corner of your spritesheet! The fnt file specifies character coordinates from the top left so as long as the font is the first thing in your spritesheet it will work fine. Then you can edit the png filename in the fnt file to point at your spritesheet and font rendering will not require a texture swap.
But what if you need more than one font in your game? That’s where things get tricky. If you want a font that is not top-left-aligned you’ll have to edit the X and Y value for every character in the fnt file to adjust for the offset (see example above). I knew this would work conceptually but I had never actually tried it because, as far as I know, no tooling exists to help edit all of those offsets. So I made one.
It turns out that actually inflating a fnt file into a complete runtime object is complicated and has a chain of dependencies. But, if you look at the file above, all we really need to do is add to the x or y value of every line based on our offset.
I already had a little console app that does things like pack game data into a .dat file. I added a crude menu with some prompts that allow you to add x and y offsets to all of the values in a font file using Regex. Then I copy/pasted the png files (very precisely, this is important!) into photoshop, measured the offsets and updated the fnt files.
I can’t publish my whole tool because it does a bunch of game and context-specific stuff that would probably just be confusing. The meat of the whole thing is this crude, but effective, regex:
I haven’t done an exact count but this will likely remove 30+ render breaks from my sector screens when the station menu is up and maybe as many as 8 when it’s not.
Commit 498c5a3.
UPDATE: When I originally wrote this article, my font offset tool was kludged into a bunch of other build tools. Based on a lot of requests from the FlatRedBall community, I broke this out into a separate tool and posted it on Github