【发布时间】:2015-03-14 01:36:15
【问题描述】:
我正在尝试使用 Metal API 在 Apple A7 GPU 上尽可能快地渲染大量非常小的二维四边形。研究 GPU 的三角形吞吐量数,例如here,根据 Apple 在他们的主题演示期间在屏幕上引用 >1M 三角形,我希望能够以 60fps 的速度每帧渲染 500,000 个这样的四边形。考虑到所有这些都是可见的(在屏幕上,而不是被 z 缓冲区隐藏)和微小的(对于光栅化器来说很棘手),可能会少一些,所以这可能不是 GPU 超级优化的用例。也许那个 Apple 演示以 30fps 运行,所以假设大约 200,000 应该是可行的。肯定有 100,000……对吧?
但是,在我的测试应用程序中,最大值仅为 20,000 左右——不止于此,而且 iPad Air 上的帧速率降至 60 以下。它有 100,000 个四边形,以 14 fps 的速度运行,即吞吐量为 280 万三角形/秒(与 AnandTech 文章中引用的 6810 万个屏幕三角形相比!)。
即使我将四边形缩小为单个像素,使用微不足道的片段着色器,性能也不会提高。所以我们可以假设这是顶点绑定,并且 Xcode 中的 GPU 报告同意(“Tiler”为 100%)。顶点着色器也很简单,只做一点缩放和平移数学,所以我假设瓶颈是某个固定功能阶段...?
只是为了获得更多背景信息,我使用单个实例绘制调用渲染所有几何图形,每个实例一个四边形,即每个实例 4 个顶点。四边形的位置是从一个单独的缓冲区应用的,该缓冲区由顶点着色器中的实例 id 索引。我也尝试了其他一些方法(非实例化,所有顶点都预先转换,实例化+索引等),但这没有帮助。没有复杂的顶点属性、缓冲区/表面格式或我能想到的任何其他东西似乎可能会在驱动程序/GPU 中遇到缓慢的路径(尽管我当然不能确定)。混合已关闭。几乎所有其他东西都处于默认状态(视口、剪刀、ztest、剔除等)。
该应用程序是用 Swift 编写的,但希望这并不重要 ;)
我想了解的是,在渲染这样的四边形时(与“适当的”3d 场景相反),我所看到的性能是否符合预期,或者是否需要一些更高级的技术才能接近广告的三角形吞吐量。人们认为这里的限制瓶颈可能是什么?
另外,如果有人知道为什么这在 OpenGL 中可能比在 Metal 中更快(我没有尝试过,也想不出任何原因),那么我也很想听听。
谢谢
编辑:添加着色器代码。
vertex float4 vertex_shader(
const constant float2* vertex_array [[ buffer(0) ]],
const device QuadState* quads [[ buffer(1) ]],
constant const Parms& parms [[ buffer(2) ]],
unsigned int vid [[ vertex_id ]],
unsigned int iid [[ instance_id ]] )
{
float2 v = vertex_array[vid]*0.5f;
v += quads[iid].position;
// ortho cam and projection transform
v += parms.cam.position;
v *= parms.cam.zoom * parms.proj.scaling;
return float4(v, 0, 1.0);
}
fragment half4 fragment_shader()
{
return half4(0.773,0.439,0.278,0.4);
}
【问题讨论】:
-
你能告诉我们你的顶点布局/描述符和你的着色器代码吗?在我在这里的示例应用程序中,我可以在 iPad mini 2 上达到每帧 150ktris,在 iPhone 6 上达到 >300ktris/帧。我的三角形的平均覆盖率为 2 个像素。
-
当然,我在上面添加了着色器代码。我没有明确设置顶点布局。我还注意到对于 Tiler 来说,有多少屏幕被四边形覆盖很重要(我预计这对片段阶段很重要,但很惊讶地看到它对顶点阶段的影响如此之大......猜猜这是一个平铺缓存效果)。也就是说,将所有四边形集中在屏幕的一个小区域中,而不是将它们均匀地分布在整个地方,可以大大提高性能,然后我也可以打出大于 100k 的三角形。也许这就是它们达到 >1M 的方式:具有非常高 tri 计数的小物体..
-
是的,瓦工与它有很大关系。这些小三角形中的大多数只会碰到单个图块,并且必须移动到 GPU 上的图块越少,拼贴器的开销就越少。 FWIW,我看不出你的着色器有什么明显的错误。
标签: ios swift opengl-es gpu metal