我正在构建一个在Metal中渲染2D几何体的应用程序.
现在,顶点的位置从顶点函数内求解.我想要的是将解决的位置从相同的顶点函数内部写回缓冲区.
我的印象是这是可能的,尽管在我第一次尝试这样做时,即:
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;
}
Run Code Online (Sandbox Code Playgroud)
我遇到了这个编译时错误:
好吧,所以我想到了一些解决方案 - 我可以解决第一个顶点位置 - 非光栅化 - 通过一个声明如下的顶点函数:
vertex void solve_vertex(device VertexIn *unsolved [[ buffer(0) ]],
device VertexOut *solved [[ buffer(1) ]],
vid [[ vertex_id ]])
{
solved[vid] = ...
}
Run Code Online (Sandbox Code Playgroud)
然后将那些已解决的顶点传输到一个更简单的光栅化顶点函数中.
另一个可行但似乎不太吸引人的解决方案可能是在计算功能中解决它们.
那么,在这样的情况下,最好的前进方向是什么?从我的一点点研究中,我可以追踪到在转换反馈中完成了同样的程序但是我没有运气(除了问题的乞讨之外的链接)在Apple的文档/示例代码中找到示例或者网络上的其他地方,在面对这类问题时提供最佳实践.
好吧,事实证明使用非光栅化顶点函数是要走的路。然而,有一些事情需要注意以供其他人将来参考:
一个非光栅化顶点功能仅仅是一个顶点函数返回void,即:
vertex void non_rasterizing_vertex(...) { }
Run Code Online (Sandbox Code Playgroud)
在执行非光栅化“渲染”过程时,MTLRenderPassDescriptor仍然需要设置纹理 - 例如在MTLRenderPassDescriptor's colorAttachments[0].texture- 出于我不知道的原因(我认为这只是由于 GPU 编程的固定性质)。
该MTLRenderPipelineState需求有它的rasterizationEnabled属性设置为false,那么你就可以在非光栅化顶点功能分配给它的vertexFunction属性。该fragmentFunction属性可以按预期保持为零。
当实际执行传球,的一个drawPrimitives:方法(其可以被命名误导要在所配置的调用仍然需要)MTLRenderCommandEncoder。我最终调用了 render MTLPrimitiveType.Points,因为这似乎是最有意义的。
完成所有这些设置“渲染”逻辑准备从顶点函数写回顶点缓冲区 - 只要它们在device地址空间中:
vertex void non_rasterizing_vertex(device float *writeableBuffer [[ buffer(0) ]],
uint vid [[ vertex_id ]])
{
writeableBuffer[vid] = 42; // Write away!
}
Run Code Online (Sandbox Code Playgroud)
这个“答案”最终更像是一篇博客文章,但我希望它对未来的参考仍然有用。
我仍然想研究在计算管道中执行这样的计算工作与在上面的渲染管道中执行这样的计算工作之间的性能权衡。一旦我有更多时间这样做,我将更新此答案。