简体   繁体   English

使用 SceneKit 通过 Metal 计算管道渲染几何体

[英]Rendering geometry through a Metal compute pipeline using SceneKit

I am new to Metal and have the following question.我是 Metal 的新手,有以下问题。

I have a simple setup to render a triangle on screen after passing the geometry through a compute pipeline.我有一个简单的设置,可以在通过计算管道传递几何图形后在屏幕上渲染三角形。 I can test the compute pipeline and render pipeline individually but am not able to get them working together.我可以单独测试计算管道和渲染管道,但无法让它们一起工作。 Specifically, I get a GPU Hang error...具体来说,我收到 GPU 挂起错误...

Here is my attempt to just pass through a triangle, the compute doesn't do anything except passing through the render commands like shown below这是我尝试通过一个三角形,计算除了通过如下所示的渲染命令外什么都不做

Most of this code has been adapted from Apple's sample The code in the sample is using a Metal View and is written in Objective-C The code I show is using Swift with SceneKit as the base.此代码的大部分改编自Apple 的示例示例中的代码使用 Metal View 并编写在Objective-C我展示的代码使用SwiftSceneKit作为基础。

I contruct a simple triangle like so,我像这样构造一个简单的三角形,

// 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);
}

Could anyone point the mistake or point me in the right direction to solve this rendering issue?谁能指出错误或指出正确的方向来解决这个渲染问题?

I had a similar problem/question that was helped with this link:https://gist.github.com/0xLeif/bc0d908bd7c5758d2f7766b8458ed4fd我有一个类似的问题/问题,这个链接帮助了我:https://gist.github.com/0xLeif/bc0d908bd7c5758d2f7766b8458ed4fd

I'm trying to make this work with my specific application, but I am a novice with metal, and still struggling to make it work with a bunch of different triangles...我正在尝试使它适用于我的特定应用程序,但我是金属的新手,并且仍在努力使其适用于一堆不同的三角形......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM