Perf Tip: Put Your Bitmap Fonts in an Atlas!
Tags: gamedev, performance, opengl, libgdx
Published: 2020-03-04 18:54:34

---
title: Perf Tip: Put Your Bitmap Fonts in an Atlas!
published: true
description: How to reduce OpenGL texture bindings by putting Bitmap Fonts in a Texture Atlas.
tags: gamedev, performance, opengl, libgdx
canonical_url: https://skaterdad.dev/blog/bitmapfont-atlas/
---

Did you know that Bitmap Fonts can be added to the same Texture Atlas as the rest of your UI textures?  I sure didn't.  Read on to see how I measured and optimized my game's UI performance.

<!--more-->
### Outline
* [Background info and example from my game](#background)
* [Packing fonts in your UI Texture Atlas](#packing-fonts-in-your-ui-texture-atlas)
* [LibGDX: Tell your Bitmap Font to use the new Atlas](#tell-your-bitmap-font-to-use-the-new-atlas)

### Background

In my [previous post](https://dev.to/skaterdad/how-to-use-the-libgdx-glprofiler-4dke), we learned about the importance of minimizing texture bindings in OpenGL.  Using Texture Atlases is one of the easiest and most effective ways of optimizing your game rendering.

The LibGDX Wiki and many tutorials do a great job at recommending and explaining the use of atlases for your game world, but it was not obvious to me that you should also pack your [Bitmap Fonts](https://github.com/libgdx/libgdx/wiki/Bitmap-fonts) with your UI textures.

While making [Santa Skate](https://skaterdad.dev/games), I used the [Hiero](https://github.com/libgdx/libgdx/wiki/Hiero) tool to generate the `.fnt` and `.png` files for my fonts.  I generated four fonts, each of which had a 512x512 texture with mostly empty space.

![The generated font textures](https://dev-to-uploads.s3.amazonaws.com/i/uo4to4rfjmxspw3sccve.PNG)

### What's the problem?

In the screenshot below, there is a typical scene from Santa Skate.  There is a mixture of game world sprites and Scene2D UI elements.  I had already gone through efforts to minimize draw calls/texture bindings on the game world, but I hadn't put much thought into the UI layer.

![In-game screenshot. So pretty.](https://dev-to-uploads.s3.amazonaws.com/i/kfrqagcz3dfz3dht6vh3.png)


#### Measure before optimizing
After setting up the GLProfiler, I was surprised to see **13+ texture bindings in the UI layer per frame!**

#### Why so many texture bindings?
The reason this happens is that the Scene2D Stage draws each Actor in the order they were added to the stage.  Some of those actors need to draw both an image and text, which required multiple texture files in my project.  The progress bar and the lower power-up icons (which render one at a time) are clear examples of this.

#### Packing your fonts?
While searching online for tips on optimizing Scene2D performance, I found some forum posts that mentioned packing your fonts with the rest of the UI graphics.  [This post by 'davedes' on *java-gaming.org* was especially useful](http://www.java-gaming.org/index.php?topic=30592.msg282602#msg282602).  In hindsight, that tip made perfect sense and I'm a little annoyed that I didn't think of it!

Following that advice, I packed up all of my UI layer graphics, fonts, and particles into a single texture atlas.

#### The result?
**Only 5 texture bindings/draw calls in the UI layer per frame!**

Why not 1 draw call?  My UI uses multiple `Group` actors, [which can trigger](https://github.com/libgdx/libgdx/blob/2932c93f5fddfd29dd241fee44282cc9e695ab44/gdx/src/com/badlogic/gdx/scenes/scene2d/Group.java#L204) [SpriteBatch flush() calls](https://github.com/libgdx/libgdx/blob/2932c93f5fddfd29dd241fee44282cc9e695ab44/gdx/src/com/badlogic/gdx/graphics/g2d/SpriteBatch.java#L1053) that [force a texture binding](https://github.com/libgdx/libgdx/blob/2932c93f5fddfd29dd241fee44282cc9e695ab44/gdx/src/com/badlogic/gdx/graphics/g2d/SpriteBatch.java#L960).

### Packing fonts in your UI Texture Atlas

1. Open your favorite texture packer tool.  I really like [GDX Texture Packer GUI](https://github.com/crashinvaders/gdx-texture-packer-gui).
2. If you have a UI textures project already, open it.  Otherwise, add all of your UI graphics to the list of files to pack.
3. Add your bitmap font PNG files to the list!
4. Turn on the option "Strip whitespace Y" to get rid of all the empty vertical space in your fonts.
5. Configure the other options to your needs.
7. Pack it!

![Packed and efficient.](https://dev-to-uploads.s3.amazonaws.com/i/k4co52qd8s39o9f35ysc.png)


### Tell your Bitmap Font to use the new atlas

When you create Bitmap fonts with tools like Hiero, the `.fnt` file references the PNG that was generated with it.  We won't be including those PNG files in our assets anymore, so we need to tell the BitmapFont classes where to find the atlas.

If you're using an `AssetManager` to load your resources, it only takes a couple lines of code to update your fonts to use the Texture Atlas.  Create a `BitmapFontParameter` object, and set the `atlasName` property to the internal filepath of your texture atlas definition file (normally a `.atlas` or `.pack` extension).  Pass that parameter as the third argument to the AssetManager `load()` function.

For my game, this is how I updated all 4 fonts.  No other code changes were required!

```java
  // 1. Create a BitmapFontParameter, pointing to your atlas
  BitmapFontLoader.BitmapFontParameter fontParameter = new BitmapFontLoader.BitmapFontParameter();
  fontParameter.atlasName = "ui/gameui.pack";

  // 2. Pass that parameter as the third argument to "load"
  assetManager.load("fonts/red32.fnt", BitmapFont.class, fontParameter);
  assetManager.load("fonts/red64.fnt", BitmapFont.class, fontParameter);
  assetManager.load("fonts/yellow64.fnt",BitmapFont.class, fontParameter);
  assetManager.load("fonts/yellow32.fnt",BitmapFont.class, fontParameter);

```

If you are not using `AssetManager` and are creating your BitmapFonts manually, you will need to [pass in the TextureRegion to the constructor](https://libgdx.badlogicgames.com/ci/nightlies/docs/api/com/badlogic/gdx/graphics/g2d/BitmapFont.html#BitmapFont-com.badlogic.gdx.files.FileHandle-com.badlogic.gdx.graphics.g2d.TextureRegion-).

```java
  // This code has not been tested!
  // Consider it inspiration.

  TextureAtlas atlas = new TextureAtlas(Gdx.files.internal("textures/gui.pack"));

  BitmapFont font = new BitmapFont(
    Gdx.files.internal("fonts/yourFont.fnt"),
    atlas.findRegion("yourFont")
  );
```