使用 SceneKit 通过 Metal 计算管道渲染几何体
Rendering geometry through a Metal compute pipeline using SceneKit
我是 Metal 新手,有以下问题。
我有一个简单的设置,可以在通过计算管道传递几何图形后在屏幕上渲染三角形。我可以单独测试计算管道和渲染管道,但无法让它们一起工作。具体来说,我收到 GPU 挂起错误...
这是我尝试通过一个三角形,计算除了通过如下所示的渲染命令外什么都不做
此代码的大部分改编自 Apple's sample 示例中的代码使用 Metal View 并用 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);
}
谁能指出错误或指出正确的方向来解决这个渲染问题?
我有一个类似的 problem/question 得到了帮助link:
https://gist.github.com/0xLeif/bc0d908bd7c5758d2f7766b8458ed4fd
我正在努力使它适用于我的特定应用程序,但我是金属的新手,并且仍在努力使其适用于一堆不同的三角形...
我是 Metal 新手,有以下问题。
我有一个简单的设置,可以在通过计算管道传递几何图形后在屏幕上渲染三角形。我可以单独测试计算管道和渲染管道,但无法让它们一起工作。具体来说,我收到 GPU 挂起错误...
这是我尝试通过一个三角形,计算除了通过如下所示的渲染命令外什么都不做
此代码的大部分改编自 Apple's sample 示例中的代码使用 Metal View 并用 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);
}
谁能指出错误或指出正确的方向来解决这个渲染问题?
我有一个类似的 problem/question 得到了帮助link: https://gist.github.com/0xLeif/bc0d908bd7c5758d2f7766b8458ed4fd
我正在努力使它适用于我的特定应用程序,但我是金属的新手,并且仍在努力使其适用于一堆不同的三角形...