我正在构建一个在Metal中渲染2D几何体的应用程序。
现在,顶点的位置是从顶点函数内求解的。我想要的是将解决的位置从相同的顶点函数内部写回缓冲区。
我的印象是这是possible,尽管我第一次尝试这样做,即:
vertex VertexOut basic_vertex(device VertexIn *vertices [[ buffer(0) ]],
device VertexOut *solvedVertices [[ buffer(1) ]],
vid [[ vertex_id ]])
{
VertexIn in vertices[vid];
VertexOut out;
out.position = ... // Solve the position of the vertex
solvedVertices[vid] = out // Write to the buffer later to be read by CPU
return out;
}
我遇到了这个编译时错误:
好的,所以我想到了一些解决方案 - 我可以在第一个 - 非光栅化中解决顶点位置 - 通过一个声明如下的顶点函数:
vertex void solve_vertex(device VertexIn *unsolved [[ buffer(0) ]],
device VertexOut *solved [[ buffer(1) ]],
vid [[ vertex_id ]])
{
solved[vid] = ...
}
然后将这些已解算的顶点传输到一个更简单的 - 栅格化 - 顶点函数。
另一种可行但似乎不太吸引人的解决方案可能是在计算功能中解决它们。
那么,在这样的情况下,前进的最佳方式是什么?从我的一点点研究中,我可以追踪到Transform Feedback中完成了同样的程序,但我没有运气(除了link在问题的乞讨时)找到例子在Apple的文档/示例代码或网络上的其他地方,以便在遇到此类问题时获得最佳实践。
答案 0 :(得分:4)
好的,事实证明使用非光栅化顶点函数是可行的方法。但是有些事情需要注意,以备将来参考:
非栅格化顶点函数只是一个返回void的顶点函数,即:
vertex void non_rasterizing_vertex(...) { }
执行非栅格化“渲染”传递时,MTLRenderPassDescriptor
仍然需要设置纹理 - 例如在MTLRenderPassDescriptor
的{{1}}中 - 原因我不知道知道(我认为这只是因为GPU编程的固定特性)。
colorAttachments[0].texture
需要将MTLRenderPipelineState
属性设置为rasterizationEnabled
,然后您可以将非光栅化顶点函数指定给它的false
属性。 vertexFunction
属性可以按预期保持为零。
实际执行传递时,仍需要在配置的fragmentFunction
上调用其中一个drawPrimitives:
方法(命名可能为misleading)。我最后打电话来渲染MTLRenderCommandEncoder
,因为这似乎是最敏感的。
所有这些都设置了“渲染”逻辑,准备从顶点函数写回顶点缓冲区 - 只要它们在MTLPrimitiveType.Point
地址空间中:
device
这个“答案”最终更像是博客文章,但我希望它对将来的参考仍然有用。
我还是想研究在计算管道中执行像这样的计算工作与上面的渲染管道之间的性能权衡。一旦我有更多时间这样做,我会更新这个答案。
答案 1 :(得分:0)
正确的解决方案是将写入缓冲区的任何代码移动到计算内核。
您将失去大量向顶点函数中的缓冲区写入的性能。它针对光栅化进行了优化,而不是针对计算进行了优化。
您只需要使用计算命令编码器即可。
guard let computeBuffer = commandQueue.makeCommandBuffer() else { return }
guard let computeEncoder = computeBuffer.makeComputeCommandEncoder() else { return }
computeEncoder.setComputePipelineState(solveVertexPipelineState)
kernel void solve_vertex(device VertexIn *unsolved [[ buffer(0) ]],
device VertexOut *solved [[ buffer(1) ]],
vid [[ instance ]])
{
solved[vid] = ...
}