Using Indirect Command Buffers seems to break XCode's ability to debug Metal shaders.
Attempting to debug a vertex shader either yields errors like Unable to connect to device (6)
or Function argument 'vertexBuffer.0' does not have a valid vertex buffer binding at index '0'
. I've tried the XCode versions 13.1 (13A1030d) and 13.2 beta 2 (13C5081f).
Is this a known problem/limitation of XCode's Metal Shader debugger?
Are there any workarounds for this?
Are there better ways to "debug" Metal shaders in general?
I thought my code was doing something bad/weird (I'm still new to Metal), but found even the Apple Developer sample code Encoding Indirect Command Buffers on the CPU, XCode cannot debug the shaders.
I've reduced a reproduction to a simple, single file Swift MacOS application drawing a triangle (see Github Repo link below).
Setting up Render Pipeline:
let desc = MTLRenderPipelineDescriptor()
desc.label = "RenderPipeline"
desc.vertexFunction = vertexFn
desc.fragmentFunction = fragFn
desc.colorAttachments[0].pixelFormat = COLOR_PIXEL_FORMAT
desc.supportIndirectCommandBuffers = true
desc.inputPrimitiveTopology = .triangle
let vertexDesc = MTLVertexDescriptor()
vertexDesc.attributes[0].bufferIndex = 0
vertexDesc.attributes[0].offset = 0
vertexDesc.attributes[0].format = .float4
vertexDesc.layouts[0].stride = MemoryLayout<VertexPosition>.stride
vertexDesc.layouts[0].stepRate = 1
vertexDesc.layouts[0].stepFunction = .perVertex
desc.vertexDescriptor = vertexDesc
Setting up Indirect Command Buffer:
let desc = MTLIndirectCommandBufferDescriptor()
desc.commandTypes = .draw
desc.inheritBuffers = false
desc.inheritPipelineState = false
desc.maxVertexBufferBindCount = 1
desc.maxFragmentBufferBindCount = 0
let buf = device.makeIndirectCommandBuffer(descriptor: desc, maxCommandCount: 1, options: MTLResourceOptions.storageModeManaged)!
buf.label = "IndirectCommandBuffer"
let renderEncoder = buf.indirectRenderCommandAt(0)
renderEncoder.setRenderPipelineState(pipelineState)
renderEncoder.setVertexBuffer(vertexFnArgBuffer, offset: 0, at: 0)
renderEncoder.drawPrimitives(.triangle, vertexStart: 0, vertexCount: 3, instanceCount: 1, baseInstance: 0)
Issue command:
let commandBuffer = commandQueue.makeCommandBuffer()!
commandBuffer.label = "@CommandBuffer"
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: view.currentRenderPassDescriptor!)!
renderEncoder.label = "@RenderCommandEncoder"
renderEncoder.setViewport(drawableViewport)
renderEncoder.use(vertexFnArgBuffer, usage: .read, stages: .vertex)
renderEncoder.executeCommandsInBuffer(indirectCommandBuffer, range: 0..<1)
renderEncoder.endEncoding()
commandBuffer.present(view.currentDrawable!)
commandBuffer.commit()
The XCode project is pushed to this Github Repo peterwmwong/MetalGPUFrameCaptureDebugRepro and includes a GIF and MOV screen capture of the specific errors I encountered attempting to debug the simple shader.
Any help would be appreciated!
Thanks.
ICB shader debugging is currently not available, AFAIK.
Enabling shader validation might help somewhat:
MTL_SHADER_VALIDATION_GPUOPT_ENABLE_INDIRECT_COMMAND_BUFFERS=1
See this issue for more info.