1

I have a Uniforms struct defined in Swift as:

struct Uniforms {
    var t = Float(0.0)
    var arr = [0.2, 0.2, 0.2, 0.2, 0.2]
}

However, I cannot allocate a proper MTLBuffer for it because MemoryLayout<Uniforms>.stride returns 16. This contradicts the statement in Swift specification that the Array is a value-type. It is in fact treated as a reference-type by MemoryLayout.

Long story short, how can I pass a Uniforms structure that contains an array to a shader (I use constant namespace to pass it, all good there). Do I need to pass the array separately through a separate [[buffer(n)]] argument, into which I would copy the memory from the array? Any easier options?

Hamid Yusifli
  • 9,688
  • 2
  • 24
  • 48
akuz
  • 607
  • 7
  • 14

1 Answers1

2

Since Swift makes no guarantees about struct layout, it would be dangerous to copy the contents of such a struct into a Metal buffer directly (also, as written, the array contains Doubles, which are not supported by Metal currently anyway). There are a few different approaches that could work, depending on the shape of the real problem.

If you know the maximum number of elements in the array, you could add a struct member indicating the actual count, and make the last element of the struct expected by your shader a fixed-length array:

#define MAX_VALUE_COUNT 1024
struct ShaderUniforms {
    float t;
    uint32_t valueCount;
    float values[MAX_VALUE_COUNT];
};

Then, in Swift, you could allocate a Metal buffer of the maximum size (4104 bytes, in this contrived case) and copy however many array elements you need into the buffer (preceded, of course, by the other struct members).

Alternately, yes, you could use a separate buffer parameter of pointer type (e.g., constant float *values [[buffer(1)]]). That would allow you to have a value count that isn't bounded by anything explicitly coded into the shader.

warrenm
  • 31,094
  • 6
  • 92
  • 116
  • That’s sad (about Swift). Regarding your comment about ‘constant float *values [[buffer(1)]]’ - isn’t that supposed to be taken by reference in the shader? Thanks – akuz May 27 '20 at 19:37
  • Also according to this question/answer seems that Swift *currently* is ok with making correct layout for structs: https://stackoverflow.com/questions/39302834/does-swift-guarantee-the-storage-order-of-fields-in-classes-and-structs — does that mean that my only problem is that in Swift, array is not actually a value type — could I somehow *make* all the array values stored in the struct in Swift? If there was no array, I think it should be fine to use structs *currently*. – akuz May 27 '20 at 19:41
  • If you just take a reference to float, you can't index into it, and I don't know how you'd write that type as a reference to a pointer. You could conceivably type it as a reference to a fixed-length array, maybe even a variable-length array, though the syntax for that is gross. It seems pretty clean to me to just write it as a pointer. – warrenm May 27 '20 at 20:29
  • I don't know why you'd choose to rely on unspecified behavior on Swift's part. Although the ordering is assured, nothing I'm aware of in the spec says that Swift won't insert arbitrary padding before the array, nor that the first element of the array resides at the address of the array itself. If you want assurances about layout, write the type in (Objective-)C, where you have control over layout and alignment, and bridge it in. – warrenm May 27 '20 at 20:31
  • Maybe ‘constant array &values’? – akuz May 27 '20 at 20:31
  • Thanks for your help! I’ll accept your answer, just maybe add a commend that the struct layout in Swift is *currently* ok. Thanks again! – akuz May 27 '20 at 20:34