【问题标题】:Caching OpenGL textures缓存 OpenGL 纹理
【发布时间】:2014-01-03 00:12:12
【问题描述】:

我正在开发一个使用 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;
};

但这在 IMO 上使用起来有些难看,并且必须为软件渲染器伪造。

对此我还能做些什么吗?

【问题讨论】:

    标签: c++ opengl caching optimization


    【解决方案1】:

    这实际上听起来像是两个独立的问题(至少您提出的解决方案是这样)。我将就这两个问题给出一些指示,因为您要完成的工作并不完全清楚。


    1。冗余状态更改/绘图调用

    您始终可以将渲染命令排队,然后在实际进行绘图之前按纹理/着色器/其他昂贵的状态对它们进行排序(不用担心,排序听起来比实际更复杂/昂贵)。

    你真正要做的是创建不同的类别来根据它需要什么纹理,它是半透明还是不透明等来放置绘图命令,然后在你收到所有的之后以系统的方式运行这些类别。完成框架所需的绘图命令。唯一真正的排序将发生在插入时,并且由于桶相对较小,因此比在绘制时尝试对随机混乱的命令进行排序要便宜得多。

    这就是自 Quake 诞生以来高性能游戏引擎的实际运行方式。这个想法是尽可能减少纹理变化和绘制调用。在过去,draw call 本身有很多费用(需要将顶点数组内存从 CPU 复制到 GPU 和某些 API 中的内核模式上下文切换),现在它们仍然很昂贵,但原因不同。如果您可以将尽可能多的与顺序无关的绘制操作组合到单个调用中,您通常会显着提高性能。

    事实上,PowerVR 在硬件层面做了类似的事情。它等待所有绘制命令,然后将屏幕划分为块,在那里它可以确定哪些命令是多余的(例如隐藏的表面)并在它必须光栅化任何东西之前剔除它们。只要绘制操作不依赖于顺序(例如 alpha 混合),它就可以减少内存/功耗。


    2。 GPU 存储的低效/非持久使用

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

    为此,您经常在 GUI 的多个框架中打印相同的文本。您可以轻松编写软件以将渲染的字符串/格式化的段落/等缓存为纹理。如果您很聪明,您可以将其扩展到整个 GUI 窗口,并且仅在必须重新绘制其中的某些内容时重新打包存储渲染窗口的纹理部分。

    【讨论】:

    • 我认为此时我没有问题 1,我每帧只绘制了大约十几个纹理,所有纹理都以相同的比例绘制,而且几乎没有两次相同的纹理。所以对于 2,它归结为:“更巧妙地缓存动态生成的纹理”,对吗? draw_surface() 怎么样,知道如何正确缓存它吗?
    • 再次阅读您的第二部分,您似乎建议了我上面概述的内容,上面有那些 load_surface 和 load_text 函数。我说对了吗?
    • @futlib:实际上,是的...但是出于某种原因,您选择的名称确实让我感到不适。在我自己的工作中,当我不得不开发 GUI 时,我开发了我的字体渲染函数的重载,它们返回 / 可以写入纹理区域而不是默认帧缓冲区;名称没有改变,因为它们仍然做同样的事情,唯一的区别是结果的存储方式。
    • @futlib:您最大的效率提升将来自开发一些更复杂的东西,可以将多个渲染字符串打包到一个纹理中。在现实世界的场景中,您可能有不同格式的段落、句子和单个单词,如果每次格式更改时都必须制作不同的纹理以呈现多色、部分粗体等文本字符串,您会进入一个伤害性能的世界。相反,您可能希望缓存构成句子、段落等的所有格式化子字符串的组合。
    • 在阅读了更多关于这一切的内容之后,我现在很确定将事物渲染为纹理并绘制它们是正确的方法。试图保留类似 SDL 的 API 是不可能的,而且无论如何也没有帮助。
    猜你喜欢
    • 1970-01-01
    • 2013-09-03
    • 2012-03-04
    • 1970-01-01
    • 1970-01-01
    • 2015-11-14
    • 2016-09-27
    • 2011-11-24
    • 2015-11-15
    相关资源
    最近更新 更多