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?
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 Double
s, 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.