fut*_*lib 3 c++ opengl optimization caching
我正在研究一款使用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;
};
Run Code Online (Sandbox Code Playgroud)
但是这有一个问题:glBindTexture每当我使用OpenGL绘制表面时,我都会失去很多时间来重复调用.现在,我有一个基于表面内存地址的傻瓜缓存,但它显然不适用于动态生成的表面,例如内部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;
};
Run Code Online (Sandbox Code Playgroud)
但这是IMO使用起来有点难看,并且必须为软件渲染器伪造.
还有什么我可以做的吗?
这实际上听起来像两个单独的问题(至少你提出的解决方案).我将就这两个问题提出几点建议,因为你要完成的是什么并不完全清楚.
在实际绘制之前,你总是可以通过纹理/着色器/其他昂贵的状态对渲染命令进行排队,然后对它们进行排序(不要担心,排序使它听起来比实际上更复杂/更昂贵).
您真正要做的是创建不同的类别,根据所需的纹理,无论是半透明还是不透明等,将绘图命令放入其中,然后在收到所有必需的绘图命令后以系统方式运行类别完成你的框架.唯一真正的排序将在插入时发生,因为桶的数量相对较小,所以比在你绘制时尝试对随机混乱的命令进行排序要便宜得多.
这是高性能游戏引擎自Quake曙光开始以来的工作方式.我们的想法是尽量减少纹理变化并尽可能地绘制调用.在过去,绘制调用本身有很多费用(需要将顶点数组内存从CPU复制到GPU以及某些API中的内核模式上下文切换),这些天它们仍然很昂贵但出于不同的原因.如果您可以将尽可能多的与订单无关的绘制操作组合到单个调用中,则通常可以显着提高性能.
事实上,PowerVR在硬件层面做了类似的事情.它等待所有绘制命令,然后将屏幕划分为平铺,在那里它可以确定哪些命令是多余的(例如隐藏的表面)并在它必须光栅化任何东西之前剔除它们.只要绘制操作不依赖于顺序(例如alpha混合),它就会降低内存/功耗.
在最坏的情况下,您始终可以考虑将纹理打包到地图集中.这样,为了交换绑定纹理,您不需要断开绘制调用,您只需要更智能地计算纹理坐标.
为此,您经常在GUI中的多个帧中打印相同的文本.您可以轻松编写软件以将呈现的字符串/格式化的段落/等缓存为纹理.如果你聪明的话,可以将它扩展到整个GUI窗口,并且只有在重新绘制其中的某些内容时才重新打包存储渲染窗口的纹理部分.