[Solved] Rendering text hits fps - hard.

Started by
17 comments, last by thomasfn1 14 years ago
So I'm using opengl, wgl and c++ to render text. The code makes a call to glCallLists to render the text - so it shouldn't be too slow, right? Wrong. Has a rotating skybox (6 quads, 6 textures, texture size 1024x1024, linear filter), and 1/2/3 characters of text drawn in colour at the top right (represents fps). At this point, fps is between 70 and 90. Uh oh. Draw some untextured quads and lines (I tried just the console without text, it has no fps hit) and a whole bunch more text, fps down to a persistent 2. Not good. Here is the relevant code: The calls are made from Lua into c++, but I don't think the problem lies there as I'm also making tonnes of other calls (like drawing each line, each quad) that are still being made back when fps is up at 80. Just to put this in perspective, when I render 3x3 segments of terrain at the same time (each segment is 64x64 quads, textured, with lighting), fps is about 20. Surely rendering some simple text can't be more intensive than rendering a full 3D terrain with lighting? Here is the text code:

// Include header
#include "text.h"

// Define functions
int font_create( char* family, int size, int weight ) {
	HFONT font;
	HFONT oldfont;

	HDC hDC = GetHDC();

	int base = glGenLists( 96 );
	font = CreateFont( -size, 0, 0, 0, weight, false, false, false, 
		ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
		FF_DONTCARE | DEFAULT_PITCH, family );
	oldfont = (HFONT)SelectObject( hDC, font );
	wglUseFontBitmaps( hDC, 32, 96, base );
	SelectObject( hDC, oldfont );
	DeleteObject( font );

	return base;
}

void font_destroy( int id ) {
	glDeleteLists( id, 96 );
}

void font_render_noraster( char* text, int id ) {
	glPushAttrib( GL_LIST_BIT );
	glListBase( id-32 );
	glCallLists( strlen( text ), GL_UNSIGNED_BYTE, text );
	glPopAttrib();
}

void font_render( char* text, int id, int x, int y ) {
	glRasterPos2f( float( x ), float( y ) );
	font_render_noraster( text, id );
}

int font_getwidth( char* text, int id ) {

	GLint oldbuffer;
	glGetIntegerv( GL_DRAW_BUFFER, &oldbuffer );
	glDrawBuffer( GL_NONE );

	GLfloat o_rpos[4];
	glGetFloatv( GL_CURRENT_RASTER_POSITION, o_rpos );

	font_render_noraster( text, id );

	GLfloat n_rpos[4];
	glGetFloatv( GL_CURRENT_RASTER_POSITION, n_rpos );

	glDrawBuffer( oldbuffer );

	return int( n_rpos[0] - o_rpos[0] );
}

Here is the lua binding:
static int lbind_r_rendertext( lua_State* L ) {
	char* text = const_cast<char*>( luaL_checkstring( L, 1 ) );
	int base = luaL_checkint( L, 2 );
	int x = luaL_checkint( L, 3 );
	int y = luaL_checkint( L, 4 );
	font_render( text, base, x, y );
	return 0;
}

Perhaps it doesn't like the const_cast much? Any ideas? [Edited by - thomasfn1 on April 1, 2010 2:03:54 PM]
Advertisement
http://www.gamedev.net/community/forums/faq.asp#tags

------------------------------

redwoodpixel.com

Can you try a profiler or something to see if there is something obvious causing that perf hit? I use a similar method to render text and I've never seen any kind of performance hit from it.

I think something else has to be going on, because that shouldn't be that slow, unless you're calling font_create every frame or something.
[size=2]My Projects:
[size=2]Portfolio Map for Android - Free Visual Portfolio Tracker
[size=2]Electron Flux for Android - Free Puzzle/Logic Game
I'll do some more debugging to see if something silly like font_create being called every frame is happening. I'm not sure on the best way of implementing a profiler, I guess I could make something that records time differences between operations and writes it to the log but it isn't practical (especially since what gets written to the log gets written to that console too :P)

I also tried not casting into char* at the lua binding and keeping it as const char* and passing that into glCallLists instead, had no effect.

Edit:
font_create is getting called once, at the beginning of the program, as expected.

And who uses html in forum code anyways -_-
You can use this profiler if you want, it is trivially easy to set up.

Very Sleepy
[size=2]My Projects:
[size=2]Portfolio Map for Android - Free Visual Portfolio Tracker
[size=2]Electron Flux for Android - Free Puzzle/Logic Game
Thanks - suitable name methinks. I'll have a go now - but I'll have to go soon, so I might not get back to you until tommorow.

Edit:

I ran it over a 10 second period, with the console rendering all the text.

Profiler Result

I'm not sure what it all means :/
You could use GetTextExtentExPoint for getting the text width....
Just make sure to set the active font.
Anyone got any more ideas? I replaced the text size calculation code with GetTextExtentPoint, it apparently works, nothing's moved off to weird places. But I'm still having problems with the fps levels.
Where is your code to actually create the display list(glNewList)? I'd guess something odd maybe happening in there...
As soon as I saw "glRasterPos2f" alarm bells started ringing and so I have an idea; don't use wglUseFontBitmaps to generate your text.

A quick look at the MSDN page on it hightlights the problem;
Quote:
Each display list consists of a single call to glBitmap


That is going to be a killer. The function is old and is going to hurt as its probably poorly optimised/implimented in modern systems, not to mention it probably sends a bitmap over the bus to the card for every call, even in a display list its not going to be fast.

The fastest way to render text is to create a texture with each character on (or more than one texture in the case of larger fonts), then build the list of tris or quads which access this texture at the right point to grab the letters and render them to the screen; it will be faster.

<a url="http://www.angelcode.com/products/bmfont/>AngelCode's bitmap font generator can build you the sprite sheet and supply you with all the kerning data and positions so that you can render them sanely.

This topic is closed to new replies.

Advertisement