Tier 2 Argument Buffers in Swift?

Looking to use Tier 2 AB in Swift. Since the struct can't be shared properly between header and Swift file like in Obj-C I have 2 structs:

In Swift:

struct VertexShaderArguments {
  var uniforms: MTLBuffer
  var materials: MTLBuffer
}

In Header:

struct VertexShaderArguments {
  device Uniforms &uniforms;
  device Material *materials;
};

And I construct and populate the argument buffer like so:

let vertexShaderArgumentBuffer = Renderer.device.makeBuffer(length: MemoryLayout<VertexShaderArguments>.stride)!
vertexShaderArgumentBuffer.label = "Vertex Shader Argument Buffer"
self.vertexShaderArgumentBuffer = vertexShaderArgumentBuffer

let vertexShaderArgumentBufferContents = vertexShaderArgumentBuffer.contents().assumingMemoryBound(to: VertexShaderArguments.self)
vertexShaderArgumentBufferContents.pointee.uniforms = uniformsBuffer
vertexShaderArgumentBufferContents.pointee.materials = scene.materialsBuffer

I've followed this example closely (https://developer.apple.com/documentation/metal/buffers/managing_groups_of_resources_with_argument_buffers) and consulted other resources.

Examining the VertexShaderArgumentBuffer in the Metal debugger reveals the error: "Not a valid buffer" for both members.

I would appreciate any assistance, and ask that Apple please try to provide more examples in Swift in the future. The fact that virtually all Metal examples are only Objective-C is baffling.

What's the type of uniformsBuffer and scene.materialsBuffer?

Actually, never mind. This is not going to work the way you wrote it.

Accepted Answer

To populate an argument buffer, you need any device and constant pointers to be a valid GPU address. One way to get a valid GPU address is to take a gpuAddress property from an existing MTLBuffer and add an offset to it up to the buffer length. And gpuAddress is a uint64_t. The code you wrote tries to turn an id of an MTLBuffer into a GPU address, and the Metal debugger correctly indicates that it's not a valid buffer.

To fix your snippet,

struct VertexShaderArguments {
  var uniforms: UInt64
  var materials: UInt64
}

let vertexShaderArgumentBuffer = Renderer.device.makeBuffer(length: MemoryLayout<VertexShaderArguments>.stride)!
vertexShaderArgumentBuffer.label = "Vertex Shader Argument Buffer"
self.vertexShaderArgumentBuffer = vertexShaderArgumentBuffer

let vertexShaderArgumentBufferContents = vertexShaderArgumentBuffer.contents().assumingMemoryBound(to: VertexShaderArguments.self)
vertexShaderArgumentBufferContents.pointee.uniforms = uniformsBuffer.gpuAddress
vertexShaderArgumentBufferContents.pointee.materials = scene.materialsBuffer.gpuAddress

Also, I would advice declaring a struct common header, and then including that header in a bridging header and including it in the shaders. That way, you can be sure that they are layout compatible.

To do that, you can use some macros that are defined to different things depending on whether __METAL_VERSION_ is defined or not.

There's an example at around 6:53 in this talk from last years WWDC: https://developer.apple.com/videos/play/wwdc2022/10101/

Hi. I confirmed that this works. Thank you very much!

One note: it does not seem possible to share ArgumentBuffer struct in the way you recommend. Example:

Say you have 3 files:

  1. Renderer.swift
  2. ShaderTypes.h (BRIDGING HEADER)
  3. ArgumentBuffers.h

Using the example struct

struct VertexShaderArguments {
  device Uniforms &uniforms;
  device Material *materials;
};

Whether this is declared directly in the bridging header, or in the other header which the bridging header then imports, it will never be visible or able to be referenced in the Swift file.

My guess was that this has to do with the fact that the struct specifies address space, while other structs that I share successfully between Metal Shader Language and Swift do not. In either case, using your explanation I can have one Swift struct and one C struct for any Tier 2 argument buffer. Although sharing a struct would indeed be more convenient.

I really, really appreciate the explanation!

Tier 2 Argument Buffers in Swift?
 
 
Q