【问题标题】:Rendering small text with Vulkan?使用 Vulkan 渲染小文本?
【发布时间】:2021-08-25 04:34:53
【问题描述】:

字体渲染库(例如 freetype)提供了一个函数,该函数将获取轮廓字体文件(例如 .ttf)和字符代码,并在主机内存中生成相应字形的位图。

对于小文本(比如最大 30x30 像素的字形),将这些字形渲染到 Vulkan 帧缓冲区的最有效方法是什么?

我考虑过的一些选项可能是:

  1. 每次按需使用字体渲染库渲染字形,用主机代码将它们blit到一个包含整个“文本框”的主机端图像,将文本框的主机端图像传输到本地设备图像,然后使用片段着色器/图像采样器从要绘制的文本框中渲染一个四边形(如普通图像)。

  2. 在程序启动周期通过所有字形主机端,将它们渲染为字形位图。执行与 1 相同的操作,但从缓存的字形位图中进行 blit(大约需要 1 MB 主机内存)。

  3. 将字形位图单独缓存到设备本地图像中。与其在主机端进行咬合,不如在设备端为每个字形渲染一个四边形,并每次将图像采样器设置为相应的字形。 (不确定绘制调用如何工作?每个字形一次绘制调用,每次使用不同的组合图像采样器?)

  4. 将所有字形位图缓存到一个大型设备端图像中(例如,以大网格布局)。使用单个设备端组合图像采样器,并推送参数来描述包含字形图像的子区域。每个字形一次绘制调用,每次更新推送参数。

  5. 与 4 类似,但使用单个实例绘制调用,而不是推送参数,而是使用实例变化的输入属性。

  6. 还有什么?

我的意思是,像 Unreal、Unity 或 Godot 等常见的游戏引擎是如何解决这个问题的?有没有典型的方法或最佳实践?

【问题讨论】:

    标签: vulkan


    【解决方案1】:

    首先,一些注意事项:

    1. 使用 freetype 对大约 30 像素的字形进行光栅化可能需要 on the order of 10μs。这是一个非常小的一次性成本,但渲染例如每帧 100 个字形会严重占用您的帧预算(如果我们假设数学很简单,例如 100 * 10μs == 1ms)。

    2. 状态更改(如描述符更新)相对昂贵。更改您渲染的每个字符的绑定描述符具有不可忽略的成本。这可能会受到批量字符绘制的限制(绘制所有的 As,然后绘制 Bs 等),但使用推送常量通常是 fastest

    3. 具有小网格(例如四边形或单个三角形)的实例化绘图可能在某些 GPU 上非常慢,因为它们不会在单个波前/扭曲上调度多个实例。如果您要渲染一个有 6 个顶点的四边形,而单个执行单元可以处理 64 个顶点,那么您最终可能会浪费 58/64 = 90.6% 的可用顶点着色容量。

    这表明 4 是您的最佳选择(尽管 5 可能具有可比性);您可以通过缓存绘图调用的结果来进一步优化该方法。假设您有一些菜单文本:

    1. 需要的第一帧,将所有文本渲染为中间图像。
    2. 需要的每一帧都使用中间图像进行一次绘制调用。 (如果您不需要透明度,也可以对文本进行 blit。)

    【讨论】:

    • 我将尝试在启动时将所有字形渲染为一个大图像,然后进行一次绘制调用,其中顶点是四边形(每个字形 6 个顶点),UV 输入属性指向那个大图像(即就像通常的网格/输入纹理绘制一样)。看看与主机端 blitting 相比的性能会很有趣。
    • 我用索引调用尝试过,但是没有用,我想是因为gl_VertexIndex没有枚举索引缓冲区,它枚举了顶点缓冲区:stackoverflow.com/q/68946820/1131467所以我改为实例化绘图,它现在似乎工作。我想知道你在 3 中提到的问题,如果我会在旧显卡或其他东西上遇到这个问题 - 关于这个问题的任何进一步信息?你有哪些显卡型号有这个问题?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-20
    • 2016-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多