可能的渲染性能优化

Possible Rendering Performance Optimizations

我今天使用 C# 和 OpenTK 进行了一些基准测试,只是为了看看在帧率下降之前我可以实际渲染多少。我得到的数字是天文数字,我对我的测试结果很满意。

在我的项目中,我正在加载 blender monkey,它有 968 个三角形。然后我实例化它并渲染它 100 次。这意味着我每帧渲染 96,800 个三角形。这个数字远远超过我在游戏的任何给定场景中需要渲染的任何东西。在此之后,我进一步推动了它,并在不同的位置渲染了 2000 只猴子。我现在渲染了惊人的 1,936,000 个(每帧近 200 万个三角形)并且帧率仍然锁定在每秒 60 帧!这个数字让我大吃一惊。我把它推得更远,最后帧率开始下降,但这只是意味着限制是每帧大约 400 万个三角形实例化。

虽然我只是想知道,因为我使用的是一些遗留的 OpenGL,是否仍然可以进一步推动它——或者我是否应该费心?

对于我的测试,我加载了 blender monkey 模型,使用已弃用的调用将其存储到显示列表中,例如:

modelMeshID = MeshGenerator.Generate( delegate {
            GL.Begin( PrimitiveType.Triangles );
            foreach( Face f in model.Faces ) {
                foreach( ModelVertex p in f.Points ) {
                    Vector3 v = model.Vertices[ p.Vertex ];
                    Vector3 n = model.Normals[ p.Normal ];
                    Vector2 tc = model.TexCoords[ p.TexCoord ];
                    GL.Normal3( n.X , n.Y , n.Z );
                    GL.TexCoord2( tc.Y , tc.X );
                    GL.Vertex3( v.X , v.Y , v.Z );
                }
            }
            GL.End();
        } );

然后调用该列表 x 次。 但我的问题是,如果我将 VAO(顶点数组对象)而不是旧的 GL.Vertex3 放入显示列表,是否可以加快速度 api? 这会产生影响吗表现如何?或者它会产生与显示列表相同的结果吗?

这是几千的屏幕截图:

我的系统规格:

CPU: AMD Athlon IIx4(quad core) 620 2.60 GHz
Graphics Card: AMD Radeon HD 6800

使用顶点列表,beginend 会导致猴子几何体在每次迭代时通过 PCI-E 发送到 GPU,这是渲染过程中最慢的内存接口。此外,根据您的 GL 实现,对 GL 的每次调用都会有或多或少的开销。如果你使用缓冲对象,所有的开销都会消失,因为你只发送猴子一次,然后你需要的只是每次迭代的绘制调用。

然而,猴子几何体很小(只有几 kb),因此通过 PCI-E 总线发送它(大约 16 GB/s?),加上 [=21 的几百次迭代=], 甚至不需要一毫秒。即使这样也不会影响您的帧率,因为除非您明确 synchronizing,否则它将完全被流水线吸收:复制和绘制调用将 运行 而 GPU 仍在忙于渲染前一帧。此时GPU开始渲染下一帧,数据已经存在

这就是我猜测的原因,假设您有一个相当优化的 GL 实现(良好的驱动程序),使用缓冲区对象不会产生任何加速。请注意,面对更大更复杂的几何图形和渲染操作,缓冲区对象当然会成为性能的关键。小缓冲区甚至可以在绘制调用之间缓存在芯片上。

然而,作为一个严重的速度狂,你肯定想仔细检查和验证这些猜测:)

My question though, is if I could speed this up if I threw VAO's (Vertex Array Objects) into the display list instead of the old GL.Vertex3 api? Would this effect performance at all? Or would it create the same outcome with the display list?

没有

您要 运行 解决的主要问题是,显示列表和顶点数组不能很好地相互配合。使用缓冲区 objects 他们有点工作,但显示列表本身是遗留的,就像即时模式绘图 API.

然而,即使您设法从显示列表中正确获取 VBO 绘图,也会有轻微的改进:在编译显示列表时,OpenGL 驱动程序知道,到达的所有内容都将 "frozen" 最终。这允许进行一些非常积极的内部优化;所有几何数据都将打包到 GPU 上的缓冲区 object 中,合并状态更改。 AMD 在这个游戏上不如 NVidia 好,但也不差;显示列表在 CAD 应用程序中大量使用,在 ATI 涉足娱乐市场之前,他们专注于 CAD,因此他们的显示列表实现一点也不差。如果您将特定绘图调用所需的所有相关状态更改打包到显示列表中,那么在调用显示列表时您可能会进入快速路径。

I pushed it even further and finally the framerate started to drop, but this just means that the limit is roughly 4 million triangles per frame with instancing.

真正限制您的是调用显示列表的开销。我建议您在 DL 中添加更多几何图形,然后重试。

显示列表的效率惊人。它们从现代 OpenGL 中移除主要是因为它们只能与即时模式绘图命令一起有效使用。此外,最近的一些东西,如变换反馈和条件渲染,很难集成到显示列表中。所以他们被移除了;这是理所当然的,因为显示列表使用起来有点笨拙。

现在,如果您查看 Vulkan,其基本思想是在命令缓冲区中预先设置尽可能多的绘图命令(状态更改、资源绑定等),并将这些命令重复用于不同的数据。这就像您可以创建多个显示列表并让它们生孩子一样。