If I set up the argument buffer like this:
Code Block struct spvDescriptorSetBuffer0 { /* used by the vertex function only */ constant ArgsTransform* transform [[id(0)]]; /* used by the fragment function only */ constant Args* uniform_buffer [[id(1)]]; };
The shader reads the bound values correctly.
However, I have separate vertex and fragment shader MSL files (because I generate from GLSL using spirv_cross). The MSL for the arg buffer looks like this:
Separate vertex MSL file
Code Block struct spvDescriptorSetBuffer0 { constant ArgsTransform* transform [[id(0)]]; };
Separate fragment MSL file
Code Block struct spvDescriptorSetBuffer0 { constant Args* uniform_buffer [[id(1)]]; };
In this case, when the fragment shader reads uniform_buffer, which is [[id(1)]], it gets the “transform” data that was bound at index 0 (that was set by calling setBuffer(_:offset:index:), passing 0 to index.
Adding constant ArgsTransform* transform [[id(0)]]; to that struct in the fragment MSL file makes uniform_buffer return expected values.
Is this the intended behavior? The documentation for the function is here: https://developer.apple.com/documentation/metal/mtlargumentencoder/2915785-setbuffer
The docs say "either" - it's not clear to me which one is supposed to take effect here. Either way, I think I am setting BOTH the "index ID of a MSL declaration" and the "index field of MTLArgumentDescriptor"The index of the buffer within the argument buffer. This value corresponds to either the index ID of a Metal shading language declaration or the index field of a MTLArgumentDescriptor object.
My argument descriptors for the arg buffer are here:
Code Block <MTLArgumentDescriptorInternal: 0x122670cc0> dataType = MTLDataTypePointer index = 0 arrayLength = 0 access = MTLArgumentAccessReadOnly textureType = MTLTextureType2D constantBlockAlignment = default, <MTLArgumentDescriptorInternal: 0x122670da0> dataType = MTLDataTypePointer index = 1 arrayLength = 0 access = MTLArgumentAccessReadOnly textureType = MTLTextureType2D constantBlockAlignment = default,
I was hoping to make this setup work because if it did, then I could treat argument buffers like vulkan descriptor sets.
Using a MTLArgumentDescriptor to specify types and indices in an argument buffer is a complementary method to using [[id(n)]] to do the same thing. In general, you shouldn't use both.
You can specify types and indices using the [[id(n)]] attribute qualifier and create a MTLArgumentEncoder with -[MTLFunction newArgumentEncoderWithBufferIndex:]. This method is more flexible in that it allow you to specify types and indices for complex nested structures. We have a couple of samples showing how to use this method (such as this one)
You can use an MTLArgumentDescriptor to create a MTLArgumentEncoder with -[MTLDevice newArgumentEncoderWithArguments:]. This method is more flexible in that it allows you to specify types and indices in your host code (i.e. your Rust code in this case). However, you can only specify types or indices for flat (non-nested) structures this way. Also, this method is more error prone because you can mistakenly specify a type with a the argument descriptor that doesn't match the type as it's used in your shader.
Another problem is that you're trying you use a single argument encoder for multiple arguments. This will only work if both arguments are the same type, but your spvDescriptorSetBuffer0 structures are different types (that happened to be named the same).
So create separate argument encoders for your vertex and fragment shader buffers.