我有一个非常慢的工作金属应用程序,需要运行得更快。我相信问题是我创建了太多的MTLCommandBuffer对象。
我创建这么多MTLCommandBuffer对象的原因是我需要向像素着色器发送不同的统一值。我贴了一串代码来说明下面的问题。
for (int obj_i = 0 ; obj_i < n ; ++obj_i)
{
// I create one render command buffer per object I draw so I can use different uniforms
id <MTLCommandBuffer> mtlCommandBuffer = [metal_info.g_commandQueue commandBuffer];
id <MTLRenderCommandEncoder> renderCommand = [mtlCommandBuffer renderCommandEncoderWithDescriptor:<#(MTLRenderPassDescriptor *)#>]
// glossing over details, but this call has per object specific data
memcpy([global_uniform_buffer contents], per_object_data, sizeof(per_data_object));
[renderCommand setVertexBuffer:object_vertices offset:0 atIndex:0];
// I am reusing a single buffer for all shader calls
// this is killing performance
[renderCommand setVertexBuffer:global_uniform_buffer offset:0 atIndex:1];
[renderCommand drawIndexedPrimitives:MTLPrimitiveTypeTriangle
indexCount:per_object_index_count
indexType:MTLIndexTypeUInt32
indexBuffer:indicies
indexBufferOffset:0];
[renderCommand endEncoding];
[mtlCommandBuffer presentDrawable:frameDrawable];
[mtlCommandBuffer commit];
}
上面的代码按预期绘制,但非常慢。我猜测是因为有一种更好的方法来强制像素着色器评估,而不是为每个对象创建一个MTLCommandBuffer。
我考虑过简单地分配比单个着色器传递所需的缓冲区大得多的缓冲区,只需使用offset在一个渲染命令编码器中排队几个调用然后执行它们。这种方法看起来非常不正统,我想确保我解决了以金属友好的方式为每个对象发送自定义数据所需的问题。
使用每个调用自定义统一数据的同一像素/顶点着色器的多次传递渲染的最快方法是什么?
答案 0 :(得分:6)
不要为每个对象重复使用相同的统一缓冲区。这样做会破坏CPU和GPU之间的所有并行性,并导致定期同步点。
相反,为要在帧中渲染的每个对象创建单独的统一缓冲区。实际上,你应该为每个对象创建2个并在每个帧之间交替,以便GPU在准备CPU上的下一帧时可以渲染最后一帧。
执行此操作后,您只需重构循环,以便每帧执行一次命令缓冲区和渲染命令工作。你的循环应该只包括复制统一数据,设置顶点缓冲区和调用绘图原语。