Cannot debug Metal shaders with Indirect Command Buffers

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.

Answered by Qwiff in 697317022

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.

Accepted Answer

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.

Thanks @Qwiff for the link.

This is a bit of a bummer. Given ICB's have been out for a number of years, not sure I should be holding my breath on support anytime soon...

I wonder what Apple's guidance is on development/debug workflow without support... I can only think of:

  • Feature flag app code to switch between using/not-using ICBs (huge pain when ICBs are encoded on the GPU)
  • Whatever the GPU equivalent of "put a printf there" is
  • ... Don't use ICBs for non-trivial things that may change/need-debugging in the future
Cannot debug Metal shaders with Indirect Command Buffers
 
 
Q