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.