Ios 使用SceneKit通过金属计算管道渲染几何体

Ios 使用SceneKit通过金属计算管道渲染几何体,ios,swift,shader,scenekit,metal,Ios,Swift,Shader,Scenekit,Metal,我是金属新手,有以下问题 我有一个简单的设置,在通过计算管道传递几何体后在屏幕上渲染三角形。我可以单独测试计算管道和渲染管道,但无法让它们一起工作。具体来说,我得到一个GPU挂起错误 这是我的尝试,只是通过一个三角形,计算机除了通过如下所示的渲染命令外,什么都不做 大多数代码都是从样本中的代码改编而来,使用金属视图,并以Objective-C编写。我显示的代码使用Swift,以SceneKit为基础 我画了一个简单的三角形 // my swift variables in the ViewCon

我是金属新手,有以下问题

我有一个简单的设置,在通过计算管道传递几何体后在屏幕上渲染三角形。我可以单独测试计算管道和渲染管道,但无法让它们一起工作。具体来说,我得到一个GPU挂起错误

这是我的尝试,只是通过一个三角形,计算机除了通过如下所示的渲染命令外,什么都不做

大多数代码都是从样本中的代码改编而来,使用金属视图,并以
Objective-C
编写。我显示的代码使用
Swift
,以
SceneKit
为基础

我画了一个简单的三角形

// my swift variables in the ViewController
var vertexBuffer: MTLBuffer!
var renderPipelineState: MTLRenderPipelineState!
let sharedLibrary = sharedMetalRenderingDevice.device.makeDefaultLibrary()!
var _icbArgumentBuffer: MTLBuffer!
var _indirectCommandBuffer: MTLIndirectCommandBuffer!
var computePipelineState: MTLComputePipelineState!

// I call this inside viewDidLoad()
func setupMetalResources() {
    guard let device = sceneView.device else {
        assertionFailure()
        return
    }
    
    struct TriangleVertex {
        var position: vector_float3
    }
    
    let vertices: [TriangleVertex] = [
        TriangleVertex(position: vector_float3( 0.0, 0.5, 1)),
        TriangleVertex(position: vector_float3( -0.5, -0.5, 1)),
        TriangleVertex(position: vector_float3( 0.5, 0.5, 1))
    ]
    
    self.vertexBuffer = device.makeBuffer(
        bytes: vertices,
        length: MemoryLayout<TriangleVertex>.size * vertices.count,
        options: .cpuCacheModeWriteCombined)
    
    let vertexFunc = sharedLibrary.makeFunction(name: "passthrough_vertex")
    let fragmentFunc = sharedLibrary.makeFunction(name: "passthrough_fragment")
    
    let pipelineDescriptor = MTLRenderPipelineDescriptor()
    pipelineDescriptor.vertexFunction = vertexFunc
    pipelineDescriptor.fragmentFunction = fragmentFunc
    
    pipelineDescriptor.colorAttachments[0].pixelFormat = sceneView.colorPixelFormat
    pipelineDescriptor.depthAttachmentPixelFormat = sceneView.depthPixelFormat
    pipelineDescriptor.supportIndirectCommandBuffers = true
    
    guard let pipeline = try? device.makeRenderPipelineState(descriptor: pipelineDescriptor)
    else {
        assertionFailure()
        return
    }
    
    self.renderPipelineState = pipeline
    
    let cullF = sharedLibrary.makeFunction(name: "testCull")
    let ag = cullF?.makeArgumentEncoder(bufferIndex: 1)
    
    let icbDesc = MTLIndirectCommandBufferDescriptor()
    icbDesc.commandTypes = .draw
    icbDesc.inheritBuffers = false
    icbDesc.maxVertexBufferBindCount = 3
    icbDesc.maxFragmentBufferBindCount = 0
    
    _indirectCommandBuffer = sharedMetalRenderingDevice.device.makeIndirectCommandBuffer(descriptor: icbDesc, maxCommandCount: 3, options: .storageModePrivate)
    
    _icbArgumentBuffer = sharedMetalRenderingDevice.device.makeBuffer(length: ag!.encodedLength, options: .storageModeShared)
    ag?.setArgumentBuffer(_icbArgumentBuffer, offset: 0)
    ag?.setIndirectCommandBuffer(_indirectCommandBuffer, index: 0)
    do {
        computePipelineState = try sharedMetalRenderingDevice.device.makeComputePipelineState(function: t!)
    } catch {
        
    }
}

// This is the SCNSceneRendererDelegate’s -> didRenderScene of SceneKit (my sceneview)
func renderer(_ renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: TimeInterval) {
    guard let renderEncoder = renderer.currentRenderCommandEncoder else { return }
    let myRange: Range = 0..<65536

    let commandBuffer = renderer.commandQueue?.makeCommandBuffer()
    let blit = commandBuffer?.makeBlitCommandEncoder()
    blit?.resetCommandsInBuffer(_indirectCommandBuffer, range: myRange)
    blit?.endEncoding()

    let computeEncoder = commandBuffer?.makeComputeCommandEncoder()
    computeEncoder!.setComputePipelineState(computePipelineState!)
    computeEncoder!.setBuffer(vertexBuffer, offset: 0, index: 0)
    computeEncoder!.setBuffer(_icbArgumentBuffer, offset: 0, index: 1)
    computeEncoder!.useResource( _indirectCommandBuffer, usage: .write)
    computeEncoder!.dispatchThreads(MTLSize(width: 1, height: 1, depth: 1), threadsPerThreadgroup: MTLSize(width: 1, height: 1, depth: 1))
    computeEncoder!.endEncoding()

    let optimBlit = commandBuffer?.makeBlitCommandEncoder()
    optimBlit?.optimizeIndirectCommandBuffer(_indirectCommandBuffer, range: myRange)
    optimBlit?.endEncoding()

    renderEncoder.setCullMode(.back)
    renderEncoder.setRenderPipelineState(renderPipelineState)
    renderEncoder.useResource(vertexBuffer, usage: .read)
    // If I comment the entire compute encoder and pass the vertex buffer to the render encoder, it works fine
    // The below 2 lines are how I pass the vertex buffer into the render pass 
    //        renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0) 
    //        renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)

    renderEncoder.executeCommandsInBuffer( _indirectCommandBuffer, range: myRange)
    //        renderEncoder.endEncoding() // uncommenting this causes "invalid usage because encoding has ended."
    commandBuffer?.commit() // I get a GPU Hang error 
    //        commandBuffer?.waitUntilCompleted() // uncommenting this causes the screen to go black and nothing shows  
}


// This is the Metal shader code
struct Vertex
{
    float4 position [[position]];
};
struct Vertex1
{
    float3 position;
};


vertex Vertex passthrough_vertex(const device Vertex1 *vertices [[buffer(0)]],
                                 constant simd_float4x4& modelViewProjectionTransform [[buffer(1)]],
                                 uint vid [[vertex_id]])
{
    Vertex out;
    out.position = modelViewProjectionTransform * float4(vertices[vid].position,1);
//    out.position = float4(vertices[vid].position.x, vertices[vid].position.y, vertices[vid].position.z, 1);
    return out;
}

fragment float4 passthrough_fragment(Vertex inVertex [[stage_in]])
{
    return float4(1,0,0,1);
}

typedef struct ICBContainer
{
    command_buffer commandBuffer [[ id(0) ]];
} ICBContainer;


kernel void
testCull(uint                      objectIndex   [[ thread_position_in_grid ]],
         device Vertex1            *vertices      [[ buffer(0) ]],
         device ICBContainer       *icb_container [[ buffer(1) ]])
{
    render_command cmd(icb_container->commandBuffer, objectIndex);
    
    cmd.set_vertex_buffer(vertices, 0);
    cmd.draw_primitives(primitive_type::triangle, 0, 3, 1, 1);
}
//ViewController中的我的swift变量
var vertexBuffer:MTLBuffer!
var renderPipelineState:MTLRenderPipelineState!
让sharedLibrary=sharedMetalRenderingDevice.device.makeDefaultLibrary()!
变量icbArgumentBuffer:MTLBuffer!
var_indirectCommandBuffer:MTLIndirectCommandBuffer!
var computePipelineState:MTLComputePipelineState!
//我称之为内部视图didload()
func setupMetalResources(){
guard let device=sceneView.device else{
断言失败()
返回
}
struct TriangleVertex{
变量位置:向量_浮动3
}
让顶点:[TriangalLeverTex]=[
TriangalLeverTex(位置:向量_float3(0.0,0.5,1)),
TriangalLeverTex(位置:向量_float3(-0.5,-0.5,1)),
TriangalLeverTex(位置:向量_float3(0.5,0.5,1))
]
self.vertexBuffer=device.makeBuffer(
字节:顶点,
长度:MemoryLayout.size*顶点数,
选项:.cpuCacheModeWriteCombined)
让vertexFunc=sharedLibrary.makeFunction(名称:“passthrough_vertex”)
let fragmentFunc=sharedLibrary.makeFunction(名称:“passthrough_fragment”)
让pipelineDescriptor=MTLRenderPipelineDescriptor()
pipelineDescriptor.vertexFunction=vertexFunc
pipelineDescriptor.fragmentFunction=fragmentFunc
pipelineDescriptor.colorAttachments[0]。pixelFormat=sceneView.colorPixelFormat
pipelineDescriptor.depthAttachmentPixelFormat=sceneView.depthPixelFormat
pipelineDescriptor.supportIndirectCommandBuffers=true
guard let pipeline=try?device.makeRenderPipelineState(描述符:pipelineDescriptor)
否则{
断言失败()
返回
}
self.renderPipelineState=管道
让cullF=sharedLibrary.makeFunction(名称:“testCull”)
设ag=culf?.makeArgumentEncoder(缓冲索引:1)
让icbDesc=MTLIndirectCommandBufferDescriptor()
icbDesc.commandTypes=.draw
icbDesc.inheritBuffers=false
icbDesc.maxVertexBufferBindCount=3
icbDesc.maxFragmentBufferBindCount=0
_indirectCommandBuffer=sharedMetalRenderingDevice.device.makeIndirectCommandBuffer(描述符:icbDesc,maxCommandCount:3,选项:.storageModePrivate)
_icbArgumentBuffer=sharedMetalRenderingDevice.device.makeBuffer(长度:ag!.encodedLength,选项:.storageModeShared)
ag?.setArgumentBuffer(_icbArgumentBuffer,偏移量:0)
ag?.setIndirectCommandBuffer(_indirectCommandBuffer,索引:0)
做{
computePipelineState=尝试sharedMetalRenderingDevice.device.MakeCumutePipelineState(函数:t!)
}抓住{
}
}
//这是ScenerEnderDelegate的->didRenderScene(我的sceneview)
func渲染器(渲染器:SCNSceneRenderer,didRenderScene场景:SCNScene,时间:时间间隔){
guard let renderCoder=renderer.currentRenderCommandEncoder else{return}
让myRange:Range=0..commandBuffer,objectIndex);
cmd.set_vertex_buffer(顶点,0);
cmd.draw_基元(基元类型::三角形,0,3,1,1);
}

有人能指出错误或给我指出正确的方向来解决这个渲染问题吗?

我有一个类似的问题,通过这个链接得到了帮助:


我正在尝试使用我的特定应用程序来实现这一点,但我是一名金属新手,仍然在努力使它能够使用一系列不同的三角形…

我遇到了一个类似的问题/问题,该链接帮助了我:

我正试图使这项工作与我的具体应用,但我是一个新手与金属,仍然努力使它与一堆不同的三角形工作