Bitmap Fonts performance issue.

Started by
5 comments, last by Geri 12 years, 7 months ago
Hi everyone

I simplified my project in order to work on the performance and I found out that Bitmap Font is responsible for some of the performance issues.
So what I have is a standard cube and some text (you can check the image below to see what i mean).
[attachment=5208:pav2dgl_snapshot.jpeg]
When I render just cube everything responds fast (meaning translation and rotation of the cube), but once I add text it slows down depending from amount of text on screen (meaning if i have one or two its ok, but 15 or above performance drops a lot).

Here is the code from my project which is responsible for the text.



void GLFont::BuildBitmapFont(HDC &hdc)
{
baseBitmap = glGenLists(96); // Storage for 96 characters
fontSize = -MulDiv(fontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
fontBitmap = CreateFont( -fontSize, // Height of font
0, // Width of font
0, // Angle of escapement
0, // Orientation angle
FW_NORMAL, // Font weight
FALSE, // Italic
FALSE, // Underline
FALSE, // Strikeout
ANSI_CHARSET, // Character set identifier
OUT_TT_PRECIS, // Output precision
CLIP_DEFAULT_PRECIS, // Clipping precision
ANTIALIASED_QUALITY, // Output quality
FF_DONTCARE|DEFAULT_PITCH, // Family and pitch
"Courier New"); // Font name

oldfontBitmap = (HFONT)SelectObject(hdc, fontBitmap); // Selects the font we want
wglUseFontBitmaps(hdc, 32, 96, baseBitmap); // Builds 96 characters starting at character 32
SelectObject(hdc, oldfontBitmap); // Selects the font we want
DeleteObject(fontBitmap); // Delete the font
}

void GLFont::glBitmapPrint(const char *fmt, int i, double size, double _x, double _y, double _z)
{
float length=0; // Used To Find The Length Of The Text
float heigth=0; //Use To Find Heigth of the Text

glPushMatrix();

glColor3f(0.0f, 0.0f, 0.0f); // set Color 2 white
char text[256]; // Holds our string
va_list ap; // Pointer to list of arguments
if (fmt == NULL) // If there's no text
return; // Do nothing

glRasterPos3d(_x, _y, _z);
va_start(ap, fmt); // Parses the string for variables
vsprintf(text, fmt, ap); // And converts symbols to actual numbers
va_end(ap); // Results are stored in text

glPushAttrib(GL_LIST_BIT); // Pushes the display list bits
for (unsigned int loop=0;loop<(strlen(text));loop++) // Loop To Find Text Length
{
length+=gmfBitmap[text[loop]].gmfCellIncX; // Increase Length By Each Characters Width
heigth+=gmfBitmap[text[loop]].gmfCellIncY;
}

if (size < 0.1)
glTranslatef(-size/2,-0.01, 0.0); // Center Our Text On The Screen
else
glTranslatef(-length/20,-0.03, 0.0); // Center Our Text On The Screen
glPushAttrib(GL_LIST_BIT); // Pushes The Display List Bits
glListBase(baseBitmap - 32); // Sets the base character to 32
glScalef(size, size, 0.0);
glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); // Draws the display list text
glPopAttrib();
glPopMatrix();
}






So my question: Are theare any better way to render text on the scene without sacrificing performance? Or I am doing something wrong which causes performance drop?

Any comment would be appreciated.
Thanks in advance.
Advertisement
Many games draw text the same way they draw screen-aligned sprites: pack glyph bitmaps into a texture sheet and draw screen-aligned rectangles with appropriate texture coordinates. At a minimum you'll need texture coordinates (u0, v0, u1, v1) for each character, but you'll most likely want a position offset (x, y) so you can cut out empty space around glyph bitmaps and a width (w) so you can compute character positions and string widths more easily.

For text that doesn't change often, you can build a draw list from your text rendering and use that draw list to render text until it changes.

Protip: to ensure texels line up perfectly with pixels, subtract 0.5 from the integer vertex positions. (A half-texel shift compensates for bilinear filtering, but a half-pixel shift compensates for both bilinear filtering and multisampling.)
NeHe strikes again.

Bitmap fonts use display lists internally; each character is a single display list (containing only 4 vertexes) and drawing many characters is going to result in many display list calls - in other words it's going to be not much faster than immediate mode. Additionally display lists will have some small overhead of their own, and your driver might just suck at them (an OpenGL driver is just obliged to support display lists - the spec says nothing about doing so efficiently!). Even assuming that your driver doesn't suck at display lists, in order to get value out of them you really need to be pushing a lot of commands and/or data in each list. And like I said, here we're talking about 4 vertexes tops (and who knows what kind of command wglUseFontBitmaps compiles into it's lists?)

There are plenty of ways of getting text onscreen in an OpenGL application, and the right answer depends on whether or not you want anything fancy. A simple 256x256 texture containing the characters in a 16x16 grid, with texture coords to use derived from the character to be drawn is probably the most basic; if you need anything beyond that there are other solutions available.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.


Many games draw text the same way they draw screen-aligned sprites: pack glyph bitmaps into a texture sheet and draw screen-aligned rectangles with appropriate texture coordinates. At a minimum you'll need texture coordinates (u0, v0, u1, v1) for each character, but you'll most likely want a position offset (x, y) so you can cut out empty space around glyph bitmaps and a width (w) so you can compute character positions and string widths more easily.

For text that doesn't change often, you can build a draw list from your text rendering and use that draw list to render text until it changes.

Protip: to ensure texels line up perfectly with pixels, subtract 0.5 from the integer vertex positions. (A half-texel shift compensates for bilinear filtering, but a half-pixel shift compensates for both bilinear filtering and multisampling.)


Thank you for your comment.

By using texture map you loose control over the text and text usually ends up mixing with the images on the screen. I need easy way to write text on the screen with ability to change position, size, color and etc.. and preferably not rotate with the object, for which Bitmap Font is perfect but for unknown reason to me it sacrifices performance.

I tried to using draw list but it didn't show any performance improvement, I am not sure either why it didn't.

Is there any other good implementation of the Bitmap Font which would work faster ? Or anything similar to the Bitmap Font that would work fast?

NeHe strikes again.

Bitmap fonts use display lists internally; each character is a single display list (containing only 4 vertexes) and drawing many characters is going to result in many display list calls - in other words it's going to be not much faster than immediate mode. Additionally display lists will have some small overhead of their own, and your driver might just suck at them (an OpenGL driver is just obliged to support display lists - the spec says nothing about doing so efficiently!). Even assuming that your driver doesn't suck at display lists, in order to get value out of them you really need to be pushing a lot of commands and/or data in each list. And like I said, here we're talking about 4 vertexes tops (and who knows what kind of command wglUseFontBitmaps compiles into it's lists?)

There are plenty of ways of getting text onscreen in an OpenGL application, and the right answer depends on whether or not you want anything fancy. A simple 256x256 texture containing the characters in a 16x16 grid, with texture coords to use derived from the character to be drawn is probably the most basic; if you need anything beyond that there are other solutions available.


Thank you for your reply.

I need something simple and fast but preferably to be able to [size=2]change position, size, color, and not rotate with object.
[size=2]

I think it might be that my driver suck at display list. But I am not in control of what hardware or software end user will have on they machine so something stable working on most of the Windows XP and Windows 7 machines would be good enough for me.


I have [font=Arial, Verdana, Helvetica, sans-serif]Intel HD Graphics 3000 with up to 1696MB total graphics memory [/font]
[font=Arial, Verdana, Helvetica, sans-serif]with OpenGL 3.0 if this is any help. [/font]
I suspect the Bitmap font display lists push an orthographic projection matrix, which you'll need to do yourself here. (Check out the OpenGL FAQ item 9.030)

The only thing you lose with a texture-based font is pixel-perfect accuracy when scaling or rotating. Everything else is achievable.
Position: push a translation matrix or simply add an offset when generating vertex positions
Size: build multiple textures from the font; most of the time you want only a few point sizes anyway
Color: use glColor to set the text color; this assumes modulate texture blend mode (the default),

I strongly recommend doing all your screen-space rendering at the same time near the end of the rendering process. You'll only need to push the projection matrix and related render state once instead of for every character. You'll may want to to disable Z-buffering so text always draws on top of everything else. Here's how I do it:


// push projection transform
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, windowWidth, windowHeight, 0, -1, 1);

// use screen coordinates
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// disable depth testing
glDisable(GL_DEPTH_TEST);

// draw screen-space items
// ...

// restore depth testing
glEnable(GL_DEPTH_TEST);

// reset camera transform
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();


This is old-style (1.x) OpenGL and deprecated in the latest versions but shows the general principle.
Do not use this.

I suggest not to use [s]wglusefontbitmaps [/s]at all. Use simple textures, there is a texture generator called [color="#8B0000"]Bitmap Font Builder.exe, you maybee want that.

This topic is closed to new replies.

Advertisement