Abysmal Metal 渲染速度
Abysmal Metal rendering speed
我一直在尝试加快我游戏中的渲染速度,但最终还是让它变慢了,我很困惑这是怎么发生的。我正在 2d 游戏中绘制一些地形,所以想象一下各种纹理的屏幕上的长条。
之前的实现会整理各种纹理并遍历这些纹理并以不同的方式绘制与纹理关联的三角形,正如您可以想象的那样,这会导致更多的绘制调用并强制切换多个制服如果我们环绕边缘,整个过程中的时间。
所以现在我所做的是将所有纹理打包在一起,这样我就不必更改它们并且可以一次绘制大片三角形。这将绘制计数从接近 4000 降低到不到 1000(由 Xcode 中的捕获帧工具测量),但极大地减慢了帧的速度。因此,相同的场景正在使用相同数量的三角形进行渲染,但 fps 从大约 40 变为 10。
之前它是一千多个简单的 drawPrimitives(.Triangle ...
,一次只有 5 或 6 个三角形,现在每次调用 100 多个三角形只需几百次调用。
例如 [drawPrimitives:3 vertexStart:1944 vertexCount:348 instanceCount:116] 报告说它需要 1.93 毫秒才能完成。
在帧捕获中,它显示这些调用绘制 100 次左右需要 2+ms!为什么这么久!!这让我觉得超级奇怪,因为它可以在大约 3 微秒内完成一个四边形,所以如果它按比例缩放,我想这个条带只需要 .2ms
两种实现的着色器是相同的,而且非常简单。我分析了准备三角形的代码,它更快。我唯一可以指出的是实际的 drawPrimitives 调用,但无法弄清楚为什么它现在陷入困境。
那么为什么绘制调用减少了 4 倍导致帧速率增加了 3 倍?为什么进展如此缓慢,我错过了什么!我更有效率!!!
编辑
这是着色器代码:
vertex TerrainFragmentIn terrainVertex(const device TerrainVertex* verts [[ buffer(0) ]],
uint v_id [[ vertex_id ]],
constant Constants &mvp [[buffer(1)]],
constant ModelMatrix &modelMat [[buffer(2)]]
) {
TerrainVertex vert = verts[v_id];
TerrainFragmentIn outVertex;
outVertex.position = mvp.viewProjectionMatrix * modelMat.modelMatrix * float4(vert.position.x,vert.position.y,0,1);
outVertex.shadow = vert.shadow;
outVertex.uv = vert.tex;
return outVertex;
}
fragment float4 terrainFragment(TerrainFragmentIn inFrag [[stage_in]],
texture2d<float, access::sample> colorTexture [[ texture(0) ]],
sampler colorSampler [[ sampler(0) ]]) {
float4 color = colorTexture.sample(colorSampler, inFrag.uv);
color *= float4(inFrag.shadow,inFrag.shadow,inFrag.shadow,1.0);
return color * 2;
}
这些结构是这样定义的,并且在 swift 方面是相同的:
struct TerrainVertex {
float2 position [[ attribute(0) ]];
float2 tex [[ attribute(1) ]];
float shadow [[ attribute(2) ]];
};
struct Constants {
float4x4 viewProjectionMatrix;
};
struct ModelMatrix {
float4x4 modelMatrix;
};
90%的调用之间几乎没有状态变化
每个三角形大约为 20x30 像素。在这些电话中,我没有进行任何混合。我实际上希望我在捕获时得到诊断或警告/错误,但没有,遗憾的是没有。我正在 rMPB 运行 10.12.3 上进行测试。我还没有试穿 iOS。
在您的屏幕截图中,它显示实例数为 120。
在调用 drawPrimitives 时,您是否将 instanceCount 参数设置为图元计数(看起来是这样)。如果是,那么每个绘制调用都会渲染 120 * 120 = 14400 个三角形,这可以解释为什么合并绘制调用会降低帧率,因为您每次都在绘制三角形平方图元。
如果您没有使用实例化(并且您的着色器提示您没有使用),那么您应该将 instanceCount 设置为 1。
我一直在尝试加快我游戏中的渲染速度,但最终还是让它变慢了,我很困惑这是怎么发生的。我正在 2d 游戏中绘制一些地形,所以想象一下各种纹理的屏幕上的长条。
之前的实现会整理各种纹理并遍历这些纹理并以不同的方式绘制与纹理关联的三角形,正如您可以想象的那样,这会导致更多的绘制调用并强制切换多个制服如果我们环绕边缘,整个过程中的时间。
所以现在我所做的是将所有纹理打包在一起,这样我就不必更改它们并且可以一次绘制大片三角形。这将绘制计数从接近 4000 降低到不到 1000(由 Xcode 中的捕获帧工具测量),但极大地减慢了帧的速度。因此,相同的场景正在使用相同数量的三角形进行渲染,但 fps 从大约 40 变为 10。
之前它是一千多个简单的 drawPrimitives(.Triangle ...
,一次只有 5 或 6 个三角形,现在每次调用 100 多个三角形只需几百次调用。
例如 [drawPrimitives:3 vertexStart:1944 vertexCount:348 instanceCount:116] 报告说它需要 1.93 毫秒才能完成。
在帧捕获中,它显示这些调用绘制 100 次左右需要 2+ms!为什么这么久!!这让我觉得超级奇怪,因为它可以在大约 3 微秒内完成一个四边形,所以如果它按比例缩放,我想这个条带只需要 .2ms
两种实现的着色器是相同的,而且非常简单。我分析了准备三角形的代码,它更快。我唯一可以指出的是实际的 drawPrimitives 调用,但无法弄清楚为什么它现在陷入困境。
那么为什么绘制调用减少了 4 倍导致帧速率增加了 3 倍?为什么进展如此缓慢,我错过了什么!我更有效率!!!
编辑 这是着色器代码:
vertex TerrainFragmentIn terrainVertex(const device TerrainVertex* verts [[ buffer(0) ]],
uint v_id [[ vertex_id ]],
constant Constants &mvp [[buffer(1)]],
constant ModelMatrix &modelMat [[buffer(2)]]
) {
TerrainVertex vert = verts[v_id];
TerrainFragmentIn outVertex;
outVertex.position = mvp.viewProjectionMatrix * modelMat.modelMatrix * float4(vert.position.x,vert.position.y,0,1);
outVertex.shadow = vert.shadow;
outVertex.uv = vert.tex;
return outVertex;
}
fragment float4 terrainFragment(TerrainFragmentIn inFrag [[stage_in]],
texture2d<float, access::sample> colorTexture [[ texture(0) ]],
sampler colorSampler [[ sampler(0) ]]) {
float4 color = colorTexture.sample(colorSampler, inFrag.uv);
color *= float4(inFrag.shadow,inFrag.shadow,inFrag.shadow,1.0);
return color * 2;
}
这些结构是这样定义的,并且在 swift 方面是相同的:
struct TerrainVertex {
float2 position [[ attribute(0) ]];
float2 tex [[ attribute(1) ]];
float shadow [[ attribute(2) ]];
};
struct Constants {
float4x4 viewProjectionMatrix;
};
struct ModelMatrix {
float4x4 modelMatrix;
};
90%的调用之间几乎没有状态变化
每个三角形大约为 20x30 像素。在这些电话中,我没有进行任何混合。我实际上希望我在捕获时得到诊断或警告/错误,但没有,遗憾的是没有。我正在 rMPB 运行 10.12.3 上进行测试。我还没有试穿 iOS。
在您的屏幕截图中,它显示实例数为 120。
在调用 drawPrimitives 时,您是否将 instanceCount 参数设置为图元计数(看起来是这样)。如果是,那么每个绘制调用都会渲染 120 * 120 = 14400 个三角形,这可以解释为什么合并绘制调用会降低帧率,因为您每次都在绘制三角形平方图元。
如果您没有使用实例化(并且您的着色器提示您没有使用),那么您应该将 instanceCount 设置为 1。