the Fiddler.'s picture

Progress: Font rendering optimization complete

I spent the past few days implementing and profiling different font rendering methods, and I'm glad to say that OpenTK can now render text at an impressive rate of ~1.25 million characters per second. This is the best-case situtation where all text is rendered in one batch, but the gist is that text rendering has negligible impact on performance.

I've also tried to improve text rendering at small point sizes, but this didn't work out so well. To get pixel-perfect glyph rendering, you have to disable OpenGL texture filtering, however, by doing so you stop benefiting from sub-pixel precision making text look really bad. You can improve quality by enabling grid-fitting and making absolutely sure that every glyph is rendered at whole pixel boundaries. This isn't really practical (scrolling text suddenly becomes a very interesting problem!), so I've compromised by enabling filtering and increasing text contrast to compensate. Grid-fitting is still enabled for small point sizes (16 or less), but disabled for larger text where sub-pixel precision allows us to have better shapes. I'm not 100% content with the looks of the text, but this can be tweaked in the future.

On the implementation side, rendering text involves four steps:

  1. Load a TextureFont with the desired type face and size.
  2. Create a TextPrinter suitable for the script you wish to render. OpenTK will provide a simple TextPrinter with support for word wraping, text alignment and right-to-left rendering, but you will be able to provide your own implementation e.g. for complex scripts or vertical rendering. Such implementations could also be provided as add-in modules in the future.
  3. Use the TextPrinter.Prepare() method to prepare your text for rendering and obtain a TextHandle.
  4. Use TextPrinter.Draw() with the TextHandle to render your text at any time.

The first two should only be done during start up. There will also be a way to render dynamic text without caching, at a (small) performance hit. The current implementation can render up to 8192 characters in one batch (about two full pages of text) - although most games rarely display more than a few hundred characters at once.

I'd love to hear your thoughts on the font renderer. Check out the implementation from SVN (or browse it online) and give it a try. Is it too complex to use?

I hope to have 0.3.13 ready for release by the end of this week. There's still quite some work to do, input is broken on Linux and Win9x, the examples need cleaning up (vertex arrays are totally botched on dual-core machines), and I'd like to have some documentation ready as well. Till next time!

kerning.png110.21 KB


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
objarni's picture

Great news! It increases productivity quite a lot to have font rendering when developing games/tools :)

Anonymous's picture

I've looked through your font rendering code, it looks impressive. There is something I'm wondering about however. What are you doing for over/under hangs and leading space? Kerning? I can't really see anything to indicate that, but I don't know if DrawString()/MeasureString() will take that into account.

Having looked through your code I'm inspired to ditch the dependency of bitmapped fonts in my own stuff.

the Fiddler.'s picture

Thanks. According to MSDN, MeasureString will apply kerning when passed the StringFormat.GenericTypographic parameter. Overhangs/underhangs seem to be treated correctly, too, at least if I understand their significance correctly.

I've uploaded a screenshot that showcases both (see attachment to the opening post).

Anonymous's picture

Awesome, I may have to switch over to your method of dealing with fonts. Seems a lot more flexible than my current bitmap support.