缓存OpenGL纹理

4

我正在开发一个使用SDL的2D游戏。由于某些系统具有弱的CPU和强大的GPU,因此我有一个后端渲染器,除了纯粹的SDL /软件外还使用OpenGL。

简化版本的渲染器接口如下:

class Renderer {
public:
    virtual void render_surface(SDL_Surface* surface) = 0;
    virtual void render_text(const std::string& text, const Font& font) = 0;
};

但这里有一个问题:当我用OpenGL绘制表面时,我需要反复调用glBindTexture,这样会浪费很多时间。目前,我有一个基于表面内存地址的愚蠢缓存,但它显然无法处理动态生成的表面,例如在render_text中。
我能想到的唯一正确的解决方案是完全改变接口,并让调用者明智地缓存纹理:
class Renderer {
public:
    virtual Texture load_surface(SDL_Surface* surface) = 0;
    virtual Texture load_text(const std::string& text, const Font& font) = 0;
    virtual void render_texture(const Texture& texture) = 0;
};

但是在我看来,这种方法有些丑陋,并且对于软件渲染器来说必须进行伪造。

还有其他什么我可以做的吗?

1个回答

7
这实际上听起来像是两个不同的问题(至少你提出的解决方案是如此)。我将就这两个问题提供一些指针,因为您尝试完成的任务并不完全清楚。

1. 冗余状态更改/绘制调用

您可以始终排队您的渲染命令,然后按纹理/着色器/其他昂贵状态进行排序(不要担心,排序使其听起来比实际复杂/昂贵得多)然后才进行绘制。

您真正要做的是根据所需纹理、是否为半透明或不透明等创建不同类别的绘制命令,并在收到完成帧所需的所有绘制命令后,以系统化方式通过类别运行。唯一的真正排序将发生在插入时,并且由于存储桶相对较小,所以会比在绘制时尝试对随机命令进行排序要便宜得多。

从Quake诞生之初以来,高性能游戏引擎一直以这种方式工作。其想法是尽可能减少纹理更改和绘制调用。在旧时代,绘制调用本身具有很大的开销(需要将顶点数组内存从CPU复制到GPU并在某些API中进行内核模式上下文切换),这些天它们仍然很昂贵,但原因不同。如果您可以将尽可能多的独立于顺序的绘图操作合并为单个调用,则通常可以大幅提高性能。

实际上,PowerVR在硬件级别上也执行类似的操作。它等待所有绘图命令,然后将屏幕分割成可以确定哪些命令是冗余的(例如隐藏表面)并在必须光栅化任何内容之前对其进行裁剪。只要绘制操作不依赖于顺序(例如混合的阿尔法),它就会减少存储器/功耗消耗。


2. GPU存储使用效率低/非持久性

在最坏的情况下,您始终可以考虑将纹理打包到图集中。这样,您就不必打破绘制调用以交换绑定的纹理,只需更智能地计算您的纹理坐标即可。

出于此目的,您经常在GUI中跨多个帧打印相同的文本。您可以轻松编写软件,将呈现的字符串/格式化段落/等作为纹理进行缓存。如果您足够聪明,还可以将其扩展到整个GUI窗口,并且只在必须重新绘制其中某些内容时重新打包存储呈现的窗口部分的纹理。


我认为目前我没有问题1,每帧只绘制大约十几个纹理,全部都是相同比例且很少重复。所以对于问题2,归结为:“更巧妙地缓存动态生成的纹理”,对吗?至于draw_surface(),你有什么想法可以正确地缓存它吗? - futlib
1
再次阅读您的第二部分,似乎您建议我上面概述的内容,将那些load_surface和load_text函数放在上面。我理解得对吗? - futlib
@futlib:实际上是这样的,但你选择的名称让我感到有些不舒服。在我的工作中,当我需要开发GUI时,我会重载我的字体渲染函数,以返回/可以写入纹理区域而不是默认帧缓冲区;名称没有改变,因为它们仍然执行相同的操作,唯一的区别是结果存储方式不同。 - Andon M. Coleman
@futlib:您最大的效率收益将来自于开发更复杂一些的东西,可以将多个渲染字符串打包成一个纹理。在实际场景中,您可能会遇到具有不同格式的段落、句子和单词,如果每次格式更改都必须制作不同的纹理来呈现多色、部分加粗等文本字符串,那么性能上将会受到重大影响。相反,您应该缓存组成句子、段落等的所有格式化子字符串的组合。 - Andon M. Coleman
阅读了更多关于这方面的内容后,我现在非常确定将事物渲染成纹理并绘制它们是正确的方法。试图保留类似于SDL的API实际上是不可能的,也没有任何帮助。 - futlib

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接