Using SDL_TTF with OpenGL - halves my framerate

Started by
21 comments, last by deadstar 16 years, 1 month ago
Thanks everyone.

The common vote seems to be making use of NeHe's font tutorials.

I'm having trouble finding cross platform alternatives to wglUseFontBitmaps or wglUseFontOutlines. MacOSX seems to have aglUseFontBitmaps with the use of an extra library, and X11's implementation seems completely different.

In the meantime, I've dug into the SDL_TTF headers and found useful functions such as TTF_RenderGlyph, I might sit down and write a custom function to extract each glyph into a texture. I also didn't know you could do a FOR loop using chars:

for ( i = ' '; i <= '~'; i++ ){}


Found at www.objectmix.com. Could come in handy unless someone can pick out any disadvantages.

So I have one question remaining - I plan to create a vector of GLuints to hold every glyph, how could I reference those glyphs by char?

Making a struct containing a GLuint and a char, then looping through every glyph until the char is found seems a little expensive, and inconsistent, to me. Quick for a's and b's, slow for Y's and Z's :(

"The right, man, in the wrong, place, can make all the dif-fer-rence in the world..." - GMan, Half-Life 2

A blog of my SEGA Megadrive development adventures: http://www.bigevilcorporation.co.uk

Advertisement
There are tools that exist to preprocess the font into a suitable format to load as a texture so you don't need to do so at runtime. For example, BMFont.
I much prefer the cross platform FreeType2 based solution here over nehe's.
Quote:Original post by deadstarSo I have one question remaining - I plan to create a vector of GLuints to hold every glyph, how could I reference those glyphs by char?

If you end up doing this rather than using SiCrane or sigsegv42's links, you can index a vector by the value of the character. Iterate over the characters and insert them into the vector, then when you need to look one up just go to index int(chartofind - 'a') (replace 'a' with whatever the first character you put in the vector was) and that should get you the character you're looking for.
Quote:Original post by nemebean
Quote:Original post by deadstarSo I have one question remaining - I plan to create a vector of GLuints to hold every glyph, how could I reference those glyphs by char?

If you end up doing this rather than using SiCrane or sigsegv42's links, you can index a vector by the value of the character. Iterate over the characters and insert them into the vector, then when you need to look one up just go to index int(chartofind - 'a') (replace 'a' with whatever the first character you put in the vector was) and that should get you the character you're looking for.


Thanks, that makes sense.

The only reason I'm not using the above two methods is that they are purely Bitmap font methods (creating a bitmap font image first using an external tool, then loading into the program).

My implementation needs to directly support TTF, and the interface to be as simple as Font.LoadTTF("arial.ttf") then Font.RenderText("Hello", x, y).

I've got as far as loading the TTF file and creating all the glyphs, I'm just starting work on the rendering now.

"The right, man, in the wrong, place, can make all the dif-fer-rence in the world..." - GMan, Half-Life 2

A blog of my SEGA Megadrive development adventures: http://www.bigevilcorporation.co.uk

Well I finally got somewhere, and it's given me an UNBELIEVABLE speed increase!

I can't believe rendering "Hello" with my old code was so damn slow.

Problem is, the text looks pretty ugly, and very inconsistent. Here's a screenshot rendering Arial 28pt:



And some source:

namespace SYM{	SYM_FONT::SYM_FONT()	{		//Set default colour		Colour.r = 255;		Colour.g = 255;		Colour.b = 255;		Colour.unused = 255;	}	bool SYM_FONT::LoadFont(string TTF_Filename, int Size)	{		//Load the font		Font = TTF_OpenFont(TTF_Filename.c_str(), Size);		//Check font loaded        if (Font == NULL)        {			//Return			return false;        }		//Get font attributes		Ascent = TTF_FontAscent(Font);        Descent = TTF_FontDescent(Font);        Height = TTF_FontHeight(Font);        LineSkip = TTF_FontLineSkip(Font);		//Loop through all chars		for (int i = 0; i <= 255; i++)        {			//Clear the surface			TempSurface = NULL;			//Render the glyph onto the SDL surface			TempSurface = TTF_RenderGlyph_Blended(Font, i, Colour);			//Check the glyph rendered			if ( TempSurface == NULL )			{				//Close the font				TTF_CloseFont(Font);				//Return				return false;                        			}			//New temp glyph			TempGlyph = new SYM_GLYPH;			//Get the glyph attributes			TTF_GlyphMetrics(Font, i, &TempGlyph->MinX, &TempGlyph->MaxX, &TempGlyph->MinY, &TempGlyph->MaxY, &TempGlyph->Advance);			//Convert SDL surface into OpenGL GLuint texture			TempGlyph->Texture = SDL_Surface_To_GL_Tex(TempSurface);			//Push back to glyphs vector			Glyphs.push_back(*TempGlyph);			//Delete temp glyph			delete TempGlyph;			//Free surface			SDL_FreeSurface(TempSurface);		}		//Close the font		TTF_CloseFont(Font);		return true;	}	void SYM_FONT::RenderText(string Text, float x, float y)	{		int TexMinX, TexMaxX, TexMinY, TexMaxY, Width, Height, CharID;		//Enable OpenGL texture mapping		glEnable(GL_TEXTURE_2D);		//Enable blending		glEnable(GL_BLEND);		//Loop through characters		for (int i = 0; i < Text.size(); i++)		{			//Get ID of current char			CharID = (int)Text;            Width = Glyphs[CharID].MaxX;            Height = Glyphs[CharID].MaxY;            			//Bind the glyph texture            glBindTexture(GL_TEXTURE_2D, Glyphs[CharID].Texture);			//Set the colour to white (for correct blending)			glColor3f(1.0f, 1.0f, 1.0f);			//Draw the glyph            glBegin(GL_QUADS);                    glTexCoord2f(0, 0); glVertex2f(x,         y  );                    glTexCoord2f(1, 0); glVertex2f(x + Width, y  );                    glTexCoord2f(1, 1); glVertex2f(x + Width, y + Height);                    glTexCoord2f(0, 1); glVertex2f(x,         y + Height);            glEnd();                        x += Glyphs[CharID].Advance;		}		//Disable blending		glDisable(GL_BLEND);		//Disable texture mapping		glDisable(GL_TEXTURE_2D);	}	GLuint SDL_Surface_To_GL_Tex(SDL_Surface *Surface)	{		GLuint Texture;		int Width, Height;		SDL_Surface *Image;		SDL_Rect Area;		//Get the dimentions of the surface		Area.x = 0;		Area.y = 0;		Area.w = Surface->w;		Area.h = Surface->h;		//Make sure the width and height are powers of two		Width = NextPowerOfTwo(Surface->w);		Height = NextPowerOfTwo(Surface->h);		//Create an OpenGL compliant surface		Image = SDL_CreateRGBSurface(SDL_SWSURFACE, Width, Height, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);		//Copy the surface to the OpenGL compatible one		SDL_BlitSurface(Surface, &Area, Image, &Area);		//Generate an OpenGL texture		glGenTextures(1, &Texture);		//Bind the texture ready for use		glBindTexture(GL_TEXTURE_2D, Texture);		//Copy data from the SDL surface to the OpenGL texture		glTexImage2D(GL_TEXTURE_2D, 0, 4, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, Image->pixels);		//Set texture filtering		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);		//Free the SDL surface		SDL_FreeSurface(Image);		//Return the texture		return Texture;	}} //Namespace


I can't pick out the fault. I've tried other texture filters to take off the slight 'blur' too, but to no avail.

"The right, man, in the wrong, place, can make all the dif-fer-rence in the world..." - GMan, Half-Life 2

A blog of my SEGA Megadrive development adventures: http://www.bigevilcorporation.co.uk

Glancing over your screenshot and your code, I think your problem is that you're populating the Glyph.MaxX and Y values with TTF_GlyphMetrics and using that for rendering when the texture is actually NextPowerOf2 in size, and you need to render a quad with that power of 2 size in order for the glyph to end up at the right size. I suspect that the reason your characters are different sizes is that some of them fall just to one side of a power of two and others fall just on the other side, so some end up very close to the right size and others end up half height or width.
I was hoping you hadn't.. but you had... a texture per Glyph is just such a bad bad idea, both speed (texture switching) and resources wise (lots of small textures takes up more space than one larger one).

For the record, I developed a font renderer based on FT2 which used texture updating (instead of destorying and recreating the texture was just updated) and Glyph caching which hardly touched the framerate in the slighest, so it's possible.

That said, currently I'm prefering to use the font generator at Anglecode which SiCrane linked to.
Quote:Original post by nemebean
Glancing over your screenshot and your code, I think your problem is that you're populating the Glyph.MaxX and Y values with TTF_GlyphMetrics and using that for rendering when the texture is actually NextPowerOf2 in size, and you need to render a quad with that power of 2 size in order for the glyph to end up at the right size. I suspect that the reason your characters are different sizes is that some of them fall just to one side of a power of two and others fall just on the other side, so some end up very close to the right size and others end up half height or width.


Good call. I've changed it to draw the quad the same size as the SDL surface used after rendering the glyph. Sizes look fine now, but positions are still off.



A related problem?

Quote:Original post by phantom
I was hoping you hadn't.. but you had... a texture per Glyph is just such a bad bad idea, both speed (texture switching) and resources wise (lots of small textures takes up more space than one larger one).


Yes it had crossed my mind. One step at a time I suppose, once I get the text to render correctly I could modify it have per-string texture capabilities, OR have all the glyphs rendered to a giant sheet and shift around it with texture coords per glyph.

Then again, maybe I won't. The render speed right now is very impressive compared to the first method. Maybe if the game speed starts to become unbearable then I'll give this class a rewrite.



"The right, man, in the wrong, place, can make all the dif-fer-rence in the world..." - GMan, Half-Life 2

A blog of my SEGA Megadrive development adventures: http://www.bigevilcorporation.co.uk

Right off hand I can't say what's going on now, but I do see another problem that may make you reconsider rendering them all to a single texture. The characters that are supposed to hang below the line are shifted up (and the half-height small characters are the ones that seem to be offset too low, so it might be related). If you rendered them all to a single texture I think SDL_TTF would take care of that for you.

This topic is closed to new replies.

Advertisement