OpenGL fonts, what is the best way to go?

Started by
28 comments, last by Roots 19 years, 4 months ago
As for that cost of using SDL_TTF and creating each frame new SDL_Surface through SDL_TTF and then converting it to GL texture, there is one good solution for this problem.

Say, you are creating fps counter that can rapidly change from frame to frame. You don't want to generate new texture every frame cause that would kill your performance, so what do you do? During font loading you instruct your FontMgr to generate all of 0-9 digits so that each will be prerendered on *separate* texture (1 will have its own, 2 will have its own etc.), and if you're using this font and use special function for rendering, renderer should automatically glue appropriate textures together.

So if you pass "123" using that font, renderer will render texture that "1" owns, at initial position, then it will add "1" texture width to initial position and render at that point texture for "2" etc. This gonna save you hellovalot fps.

Also, if you're sure that specific string won't change over time (ie. player or level name) then just render it once on texture and later on use that texture, no need to recreate it etc. This gonna save you even more fps.

HTH
Advertisement
A much better way is to make the font one big texture, and then alter the texture coordinates...
Killers don't end up in jailThey end up on a high-score!
Ups, Nife is truly right - it's much better cause your memory won't be fragmented.
However, there is one "little" problem - for some fonts which must be generated with really big characters there is a chance that you will run out of max texture width ie. when trying to render them all horizontally. And there is no easy (and not taking too much cycles and memory) solution to this problem, because in SDL_TTF there is no easy way to tell how much space this particular character will take when rendered without actually *rendering* it, which would allow for rendering characters in rows :-/ (or am I missing something??)
I've never worked with this library, but it seems like you could render each char to get its dimensions, and then create a single texture from the individual ones (or just rerender the bigger texture). You are going to need the dimensions for each character anyway to set up the texture coords. If you did this at startup, or even offline, the speed shouldn't be an issue.

You will absolutely want only one texture for the font however. This way you can put all your text into a single vertex array, bind only one texture, and draw everything in one go.
Quote:
I've never worked with this library, but it seems like you could render each char to get its dimensions, and then create a single texture from the individual ones (or just rerender the bigger texture). You are going to need the dimensions for each character anyway to set up the texture coords. If you did this at startup, or even offline, the speed shouldn't be an issue.


That's why I wrote "and not taking too much cycles and memory". I've thought about it, problem is that creating new texture (ok, in reality SDL_Surface) for every glyph (more then 100 in ASCII?) for every font will take very much memory, which in turn (allocating, merging, deallocating) will be cycle expensive. Also it should be done at startup, so in theory you need to hunt throughout all your game code for font definitions and move them to startup code, which sometimes can turn to serious troubles and hacks.
Though it seems that's one of the best approaches.

Quote:You will absolutely want only one texture for the font however. This way you can put all your text into a single vertex array, bind only one texture, and draw everything in one go.


For some games that would be true, but in my case - no, it can't be done. Not to mention GUI, where each control can have it's own font type, font size, font color, font attributes etc.

Anyway, thx for the info.
FTGL works real nice. Cross platform too!
Quote:Original post by Koshmaar
For some games that would be true, but in my case - no, it can't be done. Not to mention GUI, where each control can have it's own font type, font size, font color, font attributes etc.


You're right, my method is only good if you're using 1 size and 1 attribute for each font (which is what I'm using). The color can always achieved by real-time coloring..
Killers don't end up in jailThey end up on a high-score!
If you want an idea of the cost of SDL_TTF, I tested my app,

with a fps counter (updated every frame) with SDL_TTF (an ostream I feed the fps into, an some other data, which totals 8 calls to SDL_TTF rendering function), with glTexSubImage2D and no mipmaps I get 1.80984ms per frame

with no fps counter at all, I get 1.5713133 ms per frame.

So the fps counter cost me about 0.23852 ms. or the difference between 60 FPS and 59.153 FPS. But it took me a while to tweak it (I use SDL bliting to combine the separate text pieces so I only call glTexSubImage2D once a frame).
Hmm, for my font rendering, i spent quite alot of time tweaking/optimising on writing a font rendering solution.

Basically, the user defines a font like this:

Font myFont;
myFont.load("myFont.jpg","myFontWidths.dat",Color);

Basically the first parameter is the texture containing the font, the second stores the font widths and the last is self explanatory.

Each Font Object has a dynamic buffer/mesh which grows/shrinks based on what is required to be rendered for any one frame.

In my main loop i just need to do this

myFont.draw("Hello World!", 100, 100);
myFont.draw("Another line of text", 200, 200);

What happens is that everytime a draw call is made, the dynamic buffer is appended with quads/UV coordinates based on XY positions. The font does not immediately render upon executing the draw call.

When it's time to draw the frame, the font simply sets the font texture once and sends in 1 large vertex array. I use a vector for the buffer and do not deallocate the buffer at the end of the frame, i simply reuse it, this means 0 allocation/deallocation of memory. The exception is when the total characters for any 1 frame is larger than the previous.

Advantages:

1 draw call per font. (Remember draw calls are extremely expensive)
Texture fonts (I believe they are the fastest way to draw text)
Flexibility (Just change the texture for a new font, use a bigger texture for bigger fonts)
Extremely fast


Lately I've read interesting approach to font rendering, here's quote from mail of Bob Pendelton from SDL mailing list:

Quote:
(...) For outline fonts I usually render each glyph/size/color once when it is
first used by the program and convert it to a texture at that point.
Then I draw text by texture mapping the rectangles for each individual
glyph in a string. Doing that spreads the time cost of rendering strings
out so you don't have the long pause caused by rendering every glyph in
a font. It also means you only have a texture for each glyph that has
been used. It is surprising how few glyphs are ever used. Then to, it
saves time by never rendering a glyph/size/color more than one.
Typically, if you are rendering a number of strings and you render all
of each string using SDL_ttf you will render the same glyph/size/color
many times. It seems to be a good way to render antialiased text. (...)


Later on I've asked about that memory fragmenation issue that have been discussed here, and Bob replied:

Quote:
The answer is "that depends". The way I describe doesn't have any
visible effect on runtime performance and only loads glyphs that you
actually use. But, as you point out, it might cause memory fragmentation
problems. I have never seen that problem, and there are several reasons
for that. 1) I always set a video ram budget and stick to it. The budget
is set at a percentage, usually 70% of the size of the memory on the
video cards I'm targeting. 2) Programs follow a fixed pattern in
allocating memory, first allocate all rendering buffers, second allocate
textures and such, then allocate glyphs as they are needed. By
allocating the big stuff first you avoid memory fragmentation. You only
get into problems when you free a bunch of textures and load new ones.
Not usually a problem. 3) I keep a copy of the glyph in ram so I can
free all the texture glyphs and reload them without having to rerender
them.

OTOH, It is not hard to render all of the glyphs of a font into a set
(not one, but several) textures in advance. Just write, or find, a
utility to do it for you. Then keep font data around that has the size,
texture number, and location within a texture for each glyph in the
font. Then you load the textures and use the font. This approach has
very little run time overhead, but it can use a *lot* of memory for
large fonts (serious problem for CJK fonts) and loads lots of glyphs
that you will never use. But, it might be the best way in memory tight
situations because the memory usage is always 100% predictable in
advance. Which make sticking to a memory budget a lot easier even if it
does use up memory you may never need to use.

You also have to take into account the kind of application you are
writing. If you are writing a typical, you don't have a lot of text and
don't use a lot of glyphs. If you are writing a GUI for a word processor
you are going to use a lot of glyphs.

As I said, it depends, use your best judgment based on the application
you are currently building. There is no one answer that is always better
than all the others. And, when using your judgment remember that
tomorrow memory will be cheaper and video cards will be faster.



Now that's what I call good explanation :-)

This topic is closed to new replies.

Advertisement