Quote:
but I'm concerned about the overhead costs in converting the SDL font surfaces to OpenGL textures. (I don't have an extensive graphics background btw).
If you are doing it every frame, then even for small texts it's gonna bite you for no good! Ie. I've coded little application using my engine, just black screen and nothing more. It run at 70 fps (with vsync enabled), stats were displayed at upper window border. Ok, I've added 2 strings that would display coordinates of mouse cursor, ie. "122 243" etc. It was done with theese functions:
void SC :: Renderer :: RenderText(const char * _text, const char * _fontName, usint _size, const TScrPoint & _position, uchar _r, uchar _g, uchar _b, ulint _style, eTextPosition _justify, eTextRenderMode _rMode) { TextObjHandle renderedText = CreateTextTexture(_text, _fontName, _size, _r, _g, _b, _style, _rMode); TScrPoint pos (_position); if (_justify == tp_Centered) // center text pos.x = pos.x - (renderedText.width)/2; if (_justify == tp_Right) // justify to right pos.x = pos.x - renderedText.width; boundTexture.id = renderedText.id; boundTexture.width = PowerOfTwo(renderedText.width); boundTexture.height = PowerOfTwo(renderedText.height); RenderTexture(pos, renderedText.width, renderedText.height, 1, TScrPnt(0,0), renderedText.width, renderedText.height); FreeTextObj(renderedText); }SC :: TextObjHandle SC :: Renderer :: CreateTextTexture(const char * _text, const char * _fontName, usint _size, uchar _r, uchar _g, uchar _b, ulint _style, eTextRenderMode _rMode){ TTF_Font * font = getFontMgr.GetFont(_fontName, _size); TextObjHandle textObj; textObj.id = 0; if (!font) { logError2("Renderer", "Couldn't find font to output text:") return textObj; } TTF_SetFontStyle(font, _style); SDL_Color clrFg = {_r, _g, _b, 0}; // color of text SDL_Surface * tmp; if (_rMode == trm_Solid) tmp = TTF_RenderText_Solid( font, _text, clrFg); else if (_rMode == trm_Blended) tmp = TTF_RenderText_Blended( font, _text, clrFg); if (tmp == 0) { logError2("Renderer", "Couldn't create texture for rendered text") return textObj; } // ------------------------------------------------- // OpenGL need to use the surface with width and height expanded to powers of 2 usint w = PowerOfTwo(tmp->w); usint h = PowerOfTwo(tmp->h); SDL_Surface * image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); if ( image == 0 ) { logError2("Renderer", "Couldn't create texture for rendered text") SC_ASSERT(0) return textObj; } // copy the tmp into the GL texture image SDL_Rect area; area.x = 0; area.y = 0; area.w = tmp->w; area.h = tmp->h; SDL_BlitSurface(tmp, &area, image, &area); // create an OpenGL texture for the image glGenTextures(1, &textObj.id); glBindTexture(GL_TEXTURE_2D, textObj.id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); // set texture for use textObj.width = tmp->w; textObj.height = tmp->h; SDL_FreeSurface(tmp); SDL_FreeSurface(image); return textObj;}
As you can see, it's SDL_TTF & OpenGL version (sure, it's crappy and not optimized, but IMHO nothing great can be achieved by optimizing theese, because slowliness is caused by external code and hardware limitations). So every frame RenderText was called twice. And FPS fall to 65 - that's 2,5 frames for every rapidly changing text. As you can see, cost is really high :-/
Also, I've tested whether it was fault of dynamic texture creation or some screwed OpenGL code in RenderFont, so I've used this:
struct TextObjHandle { GLuint id; ulint width, height; }; bool SC :: Renderer :: CreateTextObject(TextObjHandle & _handle, const char * _text, const char * _fontName, usint _size, uchar _r, uchar _g, uchar _b, ulint _style, eTextRenderMode _rMode) { _handle = CreateTextTexture(_text, _fontName, _size, _r, _g, _b, _style, _rMode); return (_handle.id != 0); } void SC :: Renderer :: FreeTextObj(TextObjHandle & _handle) { glDeleteTextures(1, &_handle.id); _handle.id = 0; } void SC :: Renderer :: RenderText(const TextObjHandle & _handle, const TScrPoint & _position, eTextPosition _justify) { TScrPoint pos (_position); if (_justify == tp_Centered) // center text pos.x = pos.x - (_handle.width)/2; if (_justify == tp_Right) // justify to right pos.x = pos.x - _handle.width; boundTexture.id = _handle.id; boundTexture.width = PowerOfTwo(_handle.width); boundTexture.height = PowerOfTwo(_handle.height); glBindTexture(GL_TEXTURE_2D, _handle.id); RenderTexture(pos, _handle.width, _handle.height, 1, TScrPnt(0,0), _handle.width, _handle.height); }
Created 2 static (non changing) strings, erased old ones and tested again. And you know what? It was 70 FPS :-/
In this situation you'll realize that for any serious project that uses fast changing texts with SDL_TTF & OpenGL, Bob Pendelton's solution that I've posted here before is really neccessary (obviously I will use it in my engine).