我有一个非常慢的工作金属应用程序,需要运行得更快 . 我相信问题是我创建了太多的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在一个渲染命令编码器中排队几个调用然后执行它们 . 这种方法看起来非常不正统,我想确保我解决了以金属友好的方式为每个对象发送自定义数据所需的问题 .
What is the fastest way to render using multiple passes of the same pixel/vertex shader with per call custom uniform data?
1 回答
不要为每个对象重用相同的统一缓冲区 . 这样做会破坏CPU和GPU之间的所有并行性并导致定期同步点 .
相反,为要在帧中呈现的每个对象创建单独的统一缓冲区 . 实际上,您应该为每个对象创建2个并在每个帧之间交替,以便在准备CPU上的下一帧时GPU可以渲染最后一帧 .
执行此操作后,您只需重构循环,以便每帧执行一次命令缓冲区和渲染命令工作 . 你的循环应该只包括复制统一数据,设置顶点缓冲区和调用绘图原语 .