Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to render fonts and text with SDL2 efficiently?

Tags:

Saw this post here about using SDL_ttf to render text in a game. However that approach requires calling SDL_CreateTextureFromSurface(), along with the SDL_FreeSurface() and SDL_DestroyTexture() every single frame.

Is creating textures (and probably subsequently having to send them to the GPU) every frame something that can significally impact my performance?

would it be wiser to use SDL_ttf only to create a texture with my whole rendered charset and then to blit from there myself, character by character?

Edit: I'm looking to render simple monospace fonts in US-English (basic ASCII) only.

like image 958
introiboad Avatar asked Mar 15 '15 19:03

introiboad


People also ask

How do I render text in SDL?

You create a surface with the text you want and then convert it to a texture that you can render. Some sample code from one of my projects: std::string score_text = "score: " + std::to_string(score); SDL_Color textColor = { 255, 255, 255, 0 }; SDL_Surface* textSurface = TTF_RenderText_Solid(font, score_text.

What is sdl2 TTF?

SDL_ttf is a library for rendering text using TTF, OTF, and FON fonts. It is compatible with Simple DirectMedia Layer version 2 (SDL2), a popular library used in games and other software. The sdl2-ttf egg is designed to be compatible with the sdl2 egg, which provides bindings to SDL 2.

How does text rendering work?

The process of transforming font outlines into pixels is called rasterization. The operating system's text-rendering engine places the outline (ie the shape) of each character at the desired font size on a pixel grid. Next, it colours all the pixels whose centre is inside the outline (see image below).


2 Answers

Yes, creating textures every frame can affect performance. Also, rasterizing TrueType fonts to SDL_Surfaces (as SDL_ttf does) every frame can affect performance.

I recommend SDL_FontCache (full disclosure: I'm the author). It uses SDL_ttf and caches the resulting glyphs in textures so you don't have to do it all yourself:
https://github.com/grimfang4/SDL_FontCache

like image 116
Jonny D Avatar answered Sep 16 '22 16:09

Jonny D


OpenGL text methods

You are more likely to find an efficient implementation by using OpenGL, since it is more widely used than SDL, see: How to draw text using only OpenGL methods?

Currently, I'd go for freetype-gl: https://github.com/rougier/freetype-gl which supports texture atlas https://en.wikipedia.org/wiki/Texture_atlas out of the box.

SDL supports OpenGL well, and you can even use both GL and SDL textures in a single program, if you are already using SDL textures in your program, e.g.:

#include <SDL2/SDL.h> #define GLEW_STATIC #include <GL/glew.h>  int main(void) {     SDL_GLContext gl_context;     SDL_Event event;     SDL_Renderer *renderer = NULL;     SDL_Texture *texture = NULL;     SDL_Window *window = NULL;     Uint8 *base;     const unsigned int         WINDOW_WIDTH = 500,         WINDOW_HEIGHT = WINDOW_WIDTH     ;     int pitch;     unsigned int x, y;     void *pixels = NULL;      /* Window setup. */     SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);     window = SDL_CreateWindow(         __FILE__, 0, 0,         WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL     );     renderer = SDL_CreateRenderer(window, 0, 0);     gl_context = SDL_GL_CreateContext(window);      /* GL drawing. */     glClearColor(1.0, 0.0, 1.0, 1.0);     glClear(GL_COLOR_BUFFER_BIT);      /* Wrapped texture drawing. */     texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,         SDL_TEXTUREACCESS_STREAMING, WINDOW_WIDTH, WINDOW_HEIGHT);     SDL_LockTexture(texture, NULL, &pixels, &pitch);     for (x = 0; x < WINDOW_WIDTH; x++) {         for (y = 0; y < WINDOW_HEIGHT; y++) {             base = ((Uint8 *)pixels) + (4 * (x * WINDOW_WIDTH + y));             base[0] = 0;             base[1] = 0;             base[2] = 255;             base[3] = 255;         }     }     SDL_UnlockTexture(texture);     SDL_Rect rect;     rect.x = 0;     rect.y = 0;     rect.w = WINDOW_WIDTH / 2;     rect.h = WINDOW_HEIGHT / 2;     SDL_RenderCopy(renderer, texture, NULL, &rect);     SDL_GL_SwapWindow(window);      /* Main loop. */     while (1) {         if (SDL_PollEvent(&event) && event.type == SDL_QUIT)             break;     }      /* Cleanup. */     SDL_GL_DeleteContext(gl_context);     SDL_DestroyTexture(texture);     SDL_DestroyRenderer(renderer);     SDL_DestroyWindow(window);     SDL_Quit();      return EXIT_SUCCESS; } 

Compile and run:

gcc -std=c99 main.c -lSDL2 -lGL ./a.out 

Tested in Ubuntu 17.10.

GitHub upstream: https://github.com/cirosantilli/cpp-cheat/blob/d36527fe4977bb9ef4b885b1ec92bd0cd3444a98/sdl/texture_and_opengl.c