这适用于Android上的OpenGL ES 2.0游戏,但我怀疑正确答案对于任何opengl情况都是通用的。
TL; DR - 最好将N个数据发送到gpu一次,然后用它进行K绘制调用;或者将K * N数据发送到gpu一次,并进行1次绘制调用?
更多细节我想知道我的情况的最佳做法。我有一个动态网格,其顶点我重新计算每一帧 - 把它想象成一个水面 - 我需要在游戏中将这些顶点投影到K个不同的四边形上。 (在每种情况下,投影略有不同;保留细节,您可以将它们想象为围绕网格的K个不同的镜子。)K大约为10-25;我还在搞清楚。
我可以想到两个广泛的选择:
按原样绑定网格,并调用绘制K不同的时间 为着色器更改制服或弄乱固定功能 状态渲染到正确的四边形(在屏幕上)或不同的 纹理的片段(我可以在渲染四边形时使用它 同样的效果)。
将网格中的所有顶点复制K次,基本上是一个 单个顶点流,其中包含K个网格,并添加一个属性(或 很少)指出每个网格克隆应该投射哪个四边形 到(以及如何到达那里),并使用顶点着色器进行投影。一世 会打一次电话,但要发送K次的数据。
问题:在这两个选项中,通常表现更好吗?
(另外:有更好的方法吗?
我考虑了第三个选项,在那里我将网格细节呈现给纹理,并创建了我的K-clone几何体作为一种虚拟流,我可以一劳永逸地绑定,在顶点着色器中查找进入每个顶点的纹理,找出它真正代表的顶点;但我被告知顶点着色器中的纹理支持在OpenGL ES 2.0中很差或被禁止,并且更愿意避免使用该路径。)
答案 0 :(得分:1)
这个问题没有完美的答案,但我建议你考虑一下实时计算机图形和OpenGL管道的本质。虽然“GL”需要产生与有序执行一致的结果,但实际情况是GPU是高度并行的动物。如果您实际上同时进行许多不相关的任务(有些甚至将整个管道分成不连续的区块),它们会使用大量最有效的技巧。例如,GDDR内存实际上是高延迟,因此为了提高效率,GPU需要能够安排其他作业以保持流处理器(着色器单元)忙,同时为刚启动的作业提取内存。
如果您每帧重新计算网格的一部分,那么您几乎肯定希望在每帧的大量CPU-> GPU数据传输上支持更多的绘制调用。使用不必要的数据传输使总线饱和甚至困扰PCI Express硬件(它比几个额外的绘制调用所增加的开销慢得多),它只能在嵌入式OpenGL ES系统上变得更糟。话虽如此,没有理由你不能简单地glBufferSubData (...)
只在网格的受影响部分进行流式处理,并继续在一次绘制调用中绘制整个网格。
如果您在缓冲区中拆分(或分区数据)和/或绘制调用,您可能会获得更好的缓存一致性,具体取决于您的实际用例场景。在您的情况下,果断地判断哪个更好的方法的唯一方法是在目标硬件上分析您的软件。但所有这些都未能看到更大的图景,即: “我为什么要在CPU上执行此操作?”
<小时/> 这听起来像真正想要的只是顶点实例化。如果您可以通过传递实例ID重新使用算法在顶点着色器中完全工作,那么您应该看到我迄今为止所提出的所有解决方案的大量改进(真正的实例化实际上介于两者之间)您在解决方案1和2中所描述的内容:)
实例化的实际概念非常简单,无论您的特定版本的OpenGL API是否支持API级别,您都可以获益(您可以使用顶点属性和额外的顶点缓冲区数据手动实现它)。问题是,如果正确实现实例化,则根本不需要复制数据。识别每个顶点所需的额外数据是静态的,您可以随时更改着色器统一并进行额外的绘制调用(这可能与OpenGL ES 2.0有关,因为它不提供{{1} })不接触任何顶点数据。
您当然不必复制顶点 K * N 次,您的缓冲区空间复杂度将更像O( K + K * M < / i>),其中M是您必须添加的新组件的数量,以唯一标识每个顶点,以便您可以计算GPU上的所有内容。对于“实例”,您可能需要对四元组1-4中的每个顶点进行编号,并根据您正在处理的顶点在着色器中以不同方式处理顶点。在这种情况下,M系数为1,无论您需要动态计算每个帧需要多少个实例,它都不会改变; N将决定OpenGL ES 2.0中的绘制调用数,而不是数据的大小。如果支持gl_VertexID,则OpenGL ES 2.0中不需要这些额外的存储空间:(
实例化是有效利用高度并行GPU并避免CPU / GPU同步和减慢总线传输的最佳方式。即使OpenGL ES 2.0不支持API意义上的实例化,使用相同顶点缓冲区的多个绘制调用,其中您在调用之间唯一改变的是几个着色器制服通常比在CPU上计算顶点和上传新顶点更可取每帧的数据或顶点缓冲区的大小直接取决于您打算绘制的实例数( yuck )。你必须尝试一下,看看你的硬件是什么样的。
答案 1 :(得分:0)
实例化将是您正在寻找的,但不幸的是它不适用于OpenGL ES 2.0。如果您的所有资产都适合GPU,我会支持将所有顶点发送到GPU并进行一次绘制调用。我有将绘制调用从100+减少到1的经验,性能从15 fps降到60 fps。