如何使用 SDL2 高效渲染字体和文本?
How to render fonts and text with SDL2 efficiently?
看到这个 post here 关于使用 SDL_ttf 在游戏中渲染文本。然而,该方法需要调用 SDL_CreateTextureFromSurface(),以及每个帧的 SDL_FreeSurface() 和 SDL_DestroyTexture()。
每帧创建纹理(并且可能随后必须将它们发送到 GPU)是否会显着影响我的性能?
仅使用 SDL_ttf 用我的整个渲染字符集创建纹理,然后我自己从那里逐个字符 blit 是否更明智?
编辑:我希望仅以美式英语(基本 ASCII)呈现简单的等宽字体。
是的,每帧创建纹理都会影响性能。此外,将 TrueType 字体光栅化为 SDL_Surfaces(如 SDL_ttf 那样)每帧都会影响性能。
我推荐 SDL_FontCache(完全披露:我是作者)。它使用 SDL_ttf 并将生成的字形缓存在纹理中,因此您不必自己完成所有操作:
https://github.com/grimfang4/SDL_FontCache
OpenGL 文本方法
您更有可能通过使用 OpenGL 找到高效的实现,因为它比 SDL 使用更广泛,请参阅:
How to draw text using only OpenGL methods?
目前,我会选择 freetype-gl: https://github.com/rougier/freetype-gl which supports texture atlas https://en.wikipedia.org/wiki/Texture_atlas 开箱即用。
SDL 很好地支持 OpenGL,如果您已经在程序中使用 SDL 纹理,您甚至可以在一个程序中同时使用 GL 和 SDL 纹理,例如:
#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;
}
编译并运行:
gcc -std=c99 main.c -lSDL2 -lGL
./a.out
在 Ubuntu 17.10.
中测试
看到这个 post here 关于使用 SDL_ttf 在游戏中渲染文本。然而,该方法需要调用 SDL_CreateTextureFromSurface(),以及每个帧的 SDL_FreeSurface() 和 SDL_DestroyTexture()。
每帧创建纹理(并且可能随后必须将它们发送到 GPU)是否会显着影响我的性能?
仅使用 SDL_ttf 用我的整个渲染字符集创建纹理,然后我自己从那里逐个字符 blit 是否更明智?
编辑:我希望仅以美式英语(基本 ASCII)呈现简单的等宽字体。
是的,每帧创建纹理都会影响性能。此外,将 TrueType 字体光栅化为 SDL_Surfaces(如 SDL_ttf 那样)每帧都会影响性能。
我推荐 SDL_FontCache(完全披露:我是作者)。它使用 SDL_ttf 并将生成的字形缓存在纹理中,因此您不必自己完成所有操作:
https://github.com/grimfang4/SDL_FontCache
OpenGL 文本方法
您更有可能通过使用 OpenGL 找到高效的实现,因为它比 SDL 使用更广泛,请参阅: How to draw text using only OpenGL methods?
目前,我会选择 freetype-gl: https://github.com/rougier/freetype-gl which supports texture atlas https://en.wikipedia.org/wiki/Texture_atlas 开箱即用。
SDL 很好地支持 OpenGL,如果您已经在程序中使用 SDL 纹理,您甚至可以在一个程序中同时使用 GL 和 SDL 纹理,例如:
#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;
}
编译并运行:
gcc -std=c99 main.c -lSDL2 -lGL
./a.out
在 Ubuntu 17.10.
中测试