将Metal缓冲区传递给SceneKit着色器

时间:2015-11-05 10:16:28

标签: scenekit metal

我想使用Metal计算着色器来计算一些位置,然后将这些位置输入到金属着色器中。听起来很直接,但我在将MTLBuffer数据输入基于金属的SCNProgram时遇到了麻烦。

计算内核如下所示,在这个设计的例子中,它采用了三个3D向量(在两个缓冲区中)。

kernel void doSimple(const device float3 *inVector [[ buffer(0) ]],
                    device float3 *outVector [[ buffer(1) ]],
                    uint id [[ thread_position_in_grid ]]) {

    float yDisplacement = 0;
    . . . .

    outVector[id] = float3(
        inVector[id].x, 
        inVector[id].y + yDisplacement, 
        inVector[id].z);
}

此核心函数在- renderer:willRenderScene:atTime:的{​​{1}}方法的每一帧中运行。有两个缓冲区,它们在每帧之后切换。

缓冲区创建如下;

SCNSceneRendererDelegate

使用以下(func setupBuffers() { positions = [vector_float3(0,0,0), vector_float3(1,0,0), vector_float3(2,0,0)] let bufferSize = sizeof(vector_float3) * positions.count //copy same data into two different buffers for initialisation buffer1 = device.newBufferWithBytes(&positions, length: bufferSize, options: .OptionCPUCacheModeDefault) buffer2 = device.newBufferWithBytes(&positions, length: bufferSize, options: .OptionCPUCacheModeDefault) } func);

运行计算着色器
willRenderScene

这很有效,我可以看到我的计算着色器正在更新数组中每个向量的y坐标。

此场景由三个均匀间隔的球体组成。顶点着色器只是获取计算着色器中计算的位置,并将其添加到每个顶点位置(无论如何都是y组件)。我给每个球体一个索引,顶点着色器使用这个索引从我的计算数组中拉出适当的位置。

金属顶点函数如下所示,它由 let computeCommandBuffer = commandQueue.commandBuffer() let computeCommandEncoder = computeCommandBuffer.computeCommandEncoder() computeCommandEncoder.setComputePipelineState(pipelineState) computeCommandEncoder.setBuffer(buffer1, offset: 0, atIndex: 0) computeCommandEncoder.setBuffer(buffer2, offset: 0, atIndex: 1) computeCommandEncoder.dispatchThreadgroups(numThreadgroups, threadsPerThreadgroup: threadsPerGroup) computeCommandEncoder.endEncoding() computeCommandBuffer.commit() computeCommandBuffer.waitUntilCompleted() let bufferSize = positions.count*sizeof(vector_float3) var data = NSData(bytesNoCopy: buffer2.contents(), length: bufferSize, freeWhenDone: false) var resultArray = [vector_float3](count: positions.count, repeatedValue: vector_float3(0,0,0)) data.getBytes(&resultArray, length:bufferSize) for outPos in resultArray { print(outPos.x, ", ", outPos.y, ", ", outPos.z) } 引用,并设置为每个球体的材质。

SCNProgram

vertex SimpleVertex simpleVertex(SimpleVertexInput in [[ stage_in ]], constant SCNSceneBuffer& scn_frame [[buffer(0)]], constant MyNodeBuffer& scn_node [[buffer(1)]], constant MyPositions &myPos [[buffer(2)]], constant uint &index [[buffer(3)]] ) { SimpleVertex vert; float3 posOffset = myPos.positions[index]; float3 pos = float3(in.position.x, in.position.y + posOffset.y, in.position.z); vert.position = scn_node.modelViewProjectionTransform * float4(pos,1.0); return vert; } 是一个包含float3s数组的简单结构。

MyPositions

使用每个球体材质的struct MyPositions { float3 positions[3]; }; 方法将数据传递到顶点着色器没有问题,如下所示(也在setValue方法中完成)。一切都按预期工作(三个球体向上移动)。

willRenderScene

但是这需要将数据从GPU复制到CPU再到GPU,这是我宁愿避免的。所以我的问题是...... 如何将MTL缓冲区传递给SCNProgram?

var i0:UInt32 = 0 let index0 = NSData(bytes: &i0, length: sizeof(UInt32)) sphere1Mat.setValue(index0, forKey: "index") sphere1Mat.setValue(data, forKey: "myPos") 中尝试了以下内容,但除了willRenderScene

之外什么都没有
EXEC_BAD...

完成example is over on GitHub

感谢阅读,一直在努力解决这个问题。解决方法是使用MTLTexture代替MTLBuffer,因为我已经能够通过漫射垫道具将这些传递到SCN程序中。

1 个答案:

答案 0 :(得分:-1)

只需逐步切换缓冲区的绑定。

步骤1 computeCommandEncoder.setBuffer(buffer1,offset:0,atIndex: 0 ) computeCommandEncoder.setBuffer(buffer2,offset:0,atIndex: 1

第二步 computeCommandEncoder.setBuffer(buffer1,offset:0,atIndex: 1 ) computeCommandEncoder.setBuffer(buffer2,offset:0,atIndex: 0

步骤3 computeCommandEncoder.setBuffer(buffer1,offset:0,atIndex: 0 ) computeCommandEncoder.setBuffer(buffer2,offset:0,atIndex: 1

依旧......

out缓冲区成为缓冲区中的新缓冲区,反之亦然...