#ios #swift #shader #scenekit #metal
#iOS #быстрый #шейдер #scenekit #Металлические #swift
Вопрос:
Я новичок в Металле, и у меня есть следующий вопрос.
У меня есть простая настройка для отображения треугольника на экране после прохождения геометрии через вычислительный конвейер. Я могу протестировать вычислительный конвейер и конвейер рендеринга по отдельности, но не могу заставить их работать вместе. В частности, я получаю сообщение об ошибке зависания графического процессора…
Вот моя попытка просто пройти через треугольник, вычисление ничего не делает, кроме прохождения команд рендеринга, как показано ниже
Большая часть этого кода была адаптирована из образца Apple. Код в примере использует вид металла и написан на Objective-C
коде, который я показываю, с использованием Swift
with 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_float4x4amp; 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);
}
Может ли кто-нибудь указать на ошибку или направить меня в правильном направлении, чтобы решить эту проблему с рендерингом?
Ответ №1:
У меня была похожая проблема / вопрос, который был решен по этой ссылке:https://gist.github.com/0xLeif/bc0d908bd7c5758d2f7766b8458ed4fd
Я пытаюсь заставить это работать с моим конкретным приложением, но я новичок в metal и все еще пытаюсь заставить его работать с кучей разных треугольников…