关于opengl:渲染字体时使用texelFetch更快吗?

Is it faster to use texelFetch when rendering fonts?

我正在OpenGL 3.3中编写一些字体绘图着色器。我将字体渲染为纹理图集,然后为要绘制的某些文本生成一些显示列表。我希望渲染文本消耗最少的资源(CPU,GPU内存,GPU时间)。我该怎么做?

在查看Freetype-gl时,我注意到作者为每个字符生成6个索引和4个顶点。

由于我使用的是OpenGL 3.3,因此我有一些额外的自由。我的计划是为每个字符生成1个顶点,再为每个字符生成一个整数"代码"。可以在texelFetch操作中使用字符代码来检索纹理编码和字符大小信息。几何着色器将尺寸信息和顶点变成三角形带。

texelFetch会比发送更多的顶点/纹理坐标慢吗?这值得吗?或者是有原因为什么我查看的字体库中没有这样做?

最终代码:

顶点着色器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#version 330

uniform sampler2D font_atlas;
uniform sampler1D code_to_texture;
uniform mat4 projection;
uniform vec2 vertex_offset;  // in view space.
uniform vec4 color;
uniform float gamma;

in vec2 vertex;  // vertex in view space of each character adjusted for kerning, etc.
in int code;

out vec4 v_uv;

void main()
{
    v_uv = texelFetch(
            code_to_texture,
            code,
            0);
    gl_Position = projection * vec4(vertex_offset + vertex, 0.0, 1.0);
}

几何着色器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#version 330

layout (points) in;
layout (triangle_strip, max_vertices = 4) out;

uniform sampler2D font_atlas;
uniform mat4 projection;

in vec4 v_uv[];

out vec2 g_uv;

void main()
{
    vec4 pos = gl_in[0].gl_Position;
    vec4 uv = v_uv[0];
    vec2 size = vec2(textureSize(font_atlas, 0)) * (uv.zw - uv.xy);
    vec2 pos_opposite = pos.xy + (mat2(projection) * size);

    gl_Position = vec4(pos.xy, 0, 1);
    g_uv = uv.xy;
    EmitVertex();

    gl_Position = vec4(pos.x, pos_opposite.y, 0, 1);
    g_uv = uv.xw;
    EmitVertex();

    gl_Position = vec4(pos_opposite.x, pos.y, 0, 1);
    g_uv = uv.zy;
    EmitVertex();

    gl_Position = vec4(pos_opposite.xy, 0, 1);
    g_uv = uv.zw;
    EmitVertex();

    EndPrimitive();
}

片段着色器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#version 330

uniform sampler2D font_atlas;
uniform vec4 color;
uniform float gamma;

in vec2 g_uv;

layout (location = 0) out vec4 fragment_color;

void main()
{
    float a = texture(font_atlas, g_uv).r;
    fragment_color.rgb = color.rgb;
    fragment_color.a = color.a * pow(a, 1.0 / gamma);
}

我不希望您提出的方法与在顶点缓冲区中存储四边形顶点位置和纹理坐标之间存在明显的性能差异。一方面,您的方法需要较小的顶点缓冲区和较少的CPU工作量。另一方面,texelFetch调用在随机位置或多或少会发生,并且不会充分利用缓存。最后一点可能不是很重要,因为我想纹理不会很大。同样,几何着色器的执行模型意味着它们可以迅速成为流水线的瓶颈。

回答"这值得吗?" -我怀疑不是出于性能原因。不幸的是,直到您实施它并衡量性能,您才能知道。我认为这是一个很酷的主意,所以我认为您不会在浪费时间尝试一下。


也许您可以使用Atomic Counter来处理文本中的当前位置。

这是有关内存带宽的有趣论文
GPU性能

您可以将结果缓存在fbo中。

如您所说,要实现真正的快速渲染,您可以构建一个以点作为输入并输出四边形的geom着色器,并采样纹理以获取更多字形信息。

这似乎是最好的解决方案...