Pass an array within uniforms struct to Metal shader - swift

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 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.

Related

How to deal with the lack of `simd_packed_float3` in Swift

There is no simd_packed_float3 type in Swift.
Why it's a problem?
Consider this Metal struct:
struct Test{
packed_float3 x;
float y;
};
First of all, you can't calculate a buffer pointer to address the memory of y, since you can't do this:
MemoryLayout<simd_packed_float3>.size
(Not sure if stride makes sense with packed types, but anyway with simd types it always gives the same length as size on my devices)
You can't use MemoryLayout<simd_float3>.size either, since it will return 16 and not 12 like in architectures available to me for testing.
Second, if you need to write a packed_float3 value of x to the buffer you will need to write the three consecutive floats, but not a single simd type. Again, simd_float3 is not usable since it will write 0 into the forth word corrupting the memory of the next property in the struct (y).
So I've done this:
struct Float_3{
var x: Float
var y: Float
var z: Float
}
typealias simd_packed_float3 = Float_3
It seems to be a functioning solution, but I'm not sure it's not a nasty thing to do...
What problems may I encounter with this approach, and how could I be sure that it won't break on some device that I don't have?
You can define a packed struct in your bridging header:
struct __attribute__((packed)) PackedFloat3 {
float x;
float y;
float z;
};
MemoryLayout<PackedFloat3>.size == 12
MemoryLayout<PackedFloat3>.stride == 12
By the way, simd_float3 is 16 bytes everywhere, simd types have stricter alignment requirements.
You can also typedef it to packed_float3 under #ifndef #ifdef __METAL_VERSION__ to have the same spelling in Swift and MSL.
The reason to do it in bridging header instead of Swift is that you can use the same structs with same spelling in both shaders and Swift.
I'm answering this following the answers I received on the Swift forum.
Turns out that someone in the Metal team at Apple has already thought of this problem and created the MTLPacked types exactly for the types that would have irregular sizes:
MTLPackedFloat3
MTLPackedFloat4x3

Using MemoryLayout on a struct gives the incorrect size

Memory layout behaves a little strange when trying to find the size of a struct.
I can just maintain a function that adds the size of each. But I was wondering if there was a better way.
enum Mode: UInt8 {
case tings
}
// this should be 5 - UInt8 + UInt16 + UInt16
struct Stuff {
let mode: Mode
let sessionID: UInt16
let sessionCount: UInt16
}
print(MemoryLayout<Stuff>.size) // 4 ???
print(MemoryLayout<UInt16>.size) // 2
print(MemoryLayout<Mode>.size) // 0 !?!?!?!?
print(MemoryLayout<Mode.RawValue>.size) // 1
You cannot rely on simply adding together the sizes of the individual fields of a structure to get the struct's size.
Swift can add padding into the fields of a struct to align fields on various byte boundaries to improve the efficiency in accessing the data at runtime.
If you want to allocate one item, you can simply use the size of the memory layout. If you want a contiguous block of n instances then you should allocate blocks based on the stride of the layout.

float arrays in objective C

Do I need to null-terminate a basic float array in objective C?
I have a basic float array:
float data[] = {0.5, 0.1, 1};
and when I do a sizeof(data) I get "12".
You don't need to null terminate it to create one, no. And in general a method taking a float[] would also take a size parameter to indicate how many elements there are.
You get sizeof(data) = 12 because a float is 4-bytes on your architecture and there's 3 of them.
sizeof return the amount of memory (in bytes) occupied by the parameter. In your case, every float occupies 4 bytes, thus 4*3=12.
As Hot Licks said in the comment of mattjgalloway's answer, there is not a standard way to retrieve the number of elements in a C array.
Using size = sizeof(data) / sizeof(float) works, but you must be careful in using this approach, since if you pass the array as a parameter it won't work.
A common approach is to store the size in a variable and use it as upper bound in your for loop (often functions that expect an array have an additional parameter to get the size of the array).
Using a null-terminated array is useful because you can iterate through your array and stop when the i-esim element is null (that's the approach of methods like strcmp).
Values of type float can never be null, so it's not possible to terminate an array of type float with null. For one thing, variables of any primitive type always have a numeric value, and the various null constants (in Objective-C nil, Nil, NULL, and '\0') have the literal value 0, which is obviously a valid value in range of a float.
So even if you can compile the following line without a warning,
float x = NULL;
...it would have the same consequence as this:
float x = 0;
Inserting a null constant in an array of type float would be indistinguishable from inserting 0.0 or any other constant zero value.

Initializing structure in Matlab

I am trying to initialize a structure in MATLAB similar to how C code does
typedef struct{
float x;
float y;
} Data
Data datapts[100];
From matlab, I know this is how to create a structure:
Data = structure('x',0,'y',0)
but how do you create 100 instances of it?
Or is this not usually done in MATLAB? Does MATLAB prefer dynamic allocation whenever there is new data to add?
Thanks for all your help..
I don't know C, so I don't know how your code initializes the structure. However, consider these two possibilities:
1. A struct array data with 100 elements, each of which has two fields x and y
You can initialize an empty struct with
data = struct('x', cell(100,1), 'y', cell(100,1));
and you access each element of the struct array as data(1) and each of these is a struct. Typically, these are used when you have several equivalent "things" with the same set of properties, but different values for each.
Example:
elements = struct(...
'name', {'Hydrogen', 'Helium', 'Lithium'},...
'atomicWeight', {1, 4, 7}, ...
'symbol', {'H', 'He', 'Li'});
elements(1)
ans =
name: 'Hydrogen'
atomicWeight: 1
symbol: 'H'
So you can access each individual struct to get to its properties. Now if you wanted to append a struct array with the next 10 elements to this list, you can use cat, just like you would for matrices.
2. A struct data with two fields x and y, each with 100 elements
You can initialize this as
data = struct('x',zeros(100,1),'y',zeros(100,1));
and you access each element of the field as data.x(1). This is typically used when you have one "thing" with several properties that can possibly hold different values.
Example:
weather=struct('time',{{'6:00','12:00','18:00','24:00'}},...
'temperature',[23,28,25,21]);
Once you understand structs and struct arrays and how they're used and indexed, you can use them in more complicated ways than in the simple illustration above.
repmat(Data,100,1);
You can assign data to it with:
Data(1).x = 10;
Data(1).y = 20;
In addition to the other methods described by #yoda and #Jacob, you can use cell2struct.

What is a good way to store 100,000+ ints (representing image delta data)?

I've searched the forum and seen some possible partial solutions to this question, but I'd like help putting it all together.
I'm getting the frames from the camera and doing image processing on the difference between the current frame and previous frame. In addition to the RGB values from the camera, I'm also calculating Hue and Saturation for each pixel, each of which is also an int. So my 2 questions are:
What is the best way to store all of these values from each call to didOutputSampleBuffer? From what I've been reading, it seems like with this many values, the overhead from NSNumber will be noticable so least memory would be spent using a classic c-style array of ints w/ length 144 x 192 x 5(R,G,B,H,S) = 138,240. Does that make sense?
How do I put this array in the scope of my didOutputSampleBuffer method, because I'm initializing the array upon app launch, not in the didOutputSampleBuffer method. Someone on the forum mentioned perhaps I could wrap the array in NSMutableData and then i could just store it as a property?
Thank you for your advice,
Don
Given that the size of an image won't change, you should be able to create a buffer to store these components as interleaved bytes or a few buffers for each color component plane. You could do this by manually using malloc() and free() to create this buffer and destroy it when done.
If you'd prefer to use reference counting, you could wrap these bytes in an NSData instance, which won't add much overhead to your processing. Either a pointer to your processed buffer bytes or an NSData instance could be used as properties.
Note that you'll probably want to use unsigned char types for each component, because you're only getting back individual bytes for each of the color components. Why waste memory with unnecessary precision?
Yes, that is a good way to store the data. Alternatively, you could use a c array of structures (see my example).
You could use a global variable or a property, containing either a NSMutableData object or the pointer to the array. Since you want access to the data as integers and not raw data, storing the pointer to the data would probably be easier than a NSData object.
Example:
// header file
struct PixelData {
int r, g, b, h, s;
};
#interface TheClass : TheSuperclass {
struct PixelData *dataPointer;
}
#property struct PixelData *dataPointer;
#end
// implementation file
#implementation TheClass
#synthesize dataPointer;
- (void)didOutputSampleBuffer { // Yes, I know this isn't the full name.
// parse data
// store data for pixel at index i:
struct PixelData *dp = self.dataPointer;
dp[i].r = r;
dp[i].g = g;
dp[i].b = b;
dp[i].h = h;
dp[i].s = s;
}
#end
When I was dealing with a similar problem I simply made the c style array an ivar on an object.
This way I could attach additional properties to it like metadata, etc.
#interface MyObject : NSObject {
int *arrayOfInts;
}
#property (readwrite) int *arrayOfInts;
#end
You still have to explicitly manage the memory in this case.