OpenGL状态冗余消除树,呈现状态优先级

时间:2014-08-26 12:36:23

标签: opengl opengl-es rendering directx-11 batching

我正在我的游戏引擎中使用自动OpenGL批处理方法,以减少绘制调用和冗余调用。

我的批处理树设计从最昂贵的状态开始,并为每个较便宜的状态添加叶子。

实施例: 树根:着色器/程序 兄弟姐妹:混合状态...... a.s.o。

所以我的问题是这个列表中最有可能是最昂贵的电话:

  • 绑定程序
  • 绑定纹理
  • 绑定缓冲区
  • 缓冲纹理,顶点数据
  • 绑定渲染目标
  • glEnable / glDisable
  • 混合状态方程,颜色,函数,colorWriteMask
  • 深度模板状态depthFunc,stencilOperations,stencilFunction,writeMasks

还想知道哪种方法会更快:
- 将所有可处理的绘制命令收集到单个顶点缓冲区并仅调用1个绘制调用(此方法也会强制更新cpu侧每个顶点的矩阵变换)
- 不要批处理并渲染许多小的绘制调用,只有批处理粒子系统...

PS:渲染目标将始终更改为Pre或Post,具体取决于使用情况。

到目前为止的进展:

  • Andon M. Coleman:最便宜的制服&顶点数组绑定,昂贵的FBO,纹理绑定
  • datenwolf:程序使状态缓存无效

1:帧缓冲状态
2:程序
3:纹理绑定
...
N:顶点阵列绑定,统一绑定

WebGL中的当前执行树:

  • 程序
  • 属性指针
  • 纹理
  • Blend State
  • 深度状态
  • 模具正面/背面状态
  • 光栅化器状态
  • 采样器状态
  • Bind Buffer
  • 绘制阵列

每个步骤都是一个兄弟哈希树,以避免再次检查主渲染队列中的状态缓存

加载纹理/程序/着色器/缓冲区在渲染到额外队列之前发生,以便将来进行多线程处理,并确保在对其执行任何操作之前初始化上下文。

自渲染对象的最大问题是你无法控制什么时候发生的事情,例如如果开发人员在gl初始化之前调用这些方法,他就不知道为什么但是他会有一些错误或问题... < / p>

2 个答案:

答案 0 :(得分:7)

此类操作的相对成本当然取决于使用模式和您的一般情况。但您可能会发现Nvidia's "Beoynd Porting" presentation slides作为有用的指南。让我在这里重现幻灯片48:

  

状态变化的相对成本

     
      
  • 降低成本...
  •   
  • 渲染目标~60K / s
  •   
  • 程序~300K / s
  •   
  • ROP
  •   
  • 纹理绑定~1.5M / s
  •   
  • 顶点格式
  •   
  • UBO Bindings
  •   
  • 统一更新~10M / s
  •   

这不会直接匹配列表中的所有项目符号。例如。 glEnable/glDisable可能会影响任何事情。 GL的缓冲区绑定也不是GPU直接看到的。当然,缓冲区绑定主要是客户端状态,具体取决于目标。混合状态的改变将是ROP状态改变,等等。

答案 1 :(得分:0)

这往往是高度平台/供应商依赖。您可能找到的任何数字都适用于特定的GPU,平台和驱动程序版本。互联网上有很多关于这个主题的神话。如果您真的想知道,您需要编写一些基准测试,并在各种平台上运行它们。

所有这些警告:

  • 渲染目标(FBO)切换往往非常昂贵。但是,高度平台和架构依赖。例如,如果您有某种形式的基于区块的体系结构,那么理想情况下可以延迟到帧结束的待处理渲染可能必须完成并刷新。或者更多&#34;经典&#34;在体系结构中,可能存在用于早期深度测试的压缩颜色缓冲区或缓冲区,在切换渲染目标时需要考虑这些缓冲区。

  • 无法一般性地评估更新纹理或缓冲区数据。它显然在很大程度上取决于正在更新的数据量。与互联网上的某些声明相反,glBufferSubData()glTexSubImage2D()之类的呼叫通常会导致同步。但它们涉及数据副本。

  • 绑定程序不应该非常昂贵,但通常比下面的状态更改更重要。

  • 纹理绑定大多相对便宜。但这实际上取决于具体情况。例如,如果您使用具有VRAM的GPU,并且此刻纹理不在VRAM中,则可能会触发从系统内存到VRAM的纹理数据副本。

  • 统一更新。据推测,这在某些平台上非常快。但实际上,其他人的成本实在太高了。所以这里有很多变化。

  • 顶点状态设置(包括VBO和VAO绑定)通常很快。它必须是,因为大多数应用程序经常这样做,它很快就会成为瓶颈。但是对于纹理有类似的考虑,如果最近没有使用缓冲存储器,则可能必须复制/映射。

  • 一般状态更新,如混合状态,模板状态或写掩码,通常非常快。但是可能存在非常多的例外情况。

只是一个典型的例子,说明为什么特性在体系结构之间可能如此不同:如果您更改混合状态,那么可能会在一个体系结构上发送几个命令字,而开销最小。在其他体系结构中,混合是作为片段着色器的一部分完成的。因此,如果更改混合状态,则必须修改着色器程序以修补新混合计算的代码。