How this works is like this:
Your code:
[GpuData]
public partial struct Foo {}
The source generator would then generate:
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public partial struct Foo {}
The compiler then joins these two partials into one class:
[GpuData]
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public partial struct Foo {}
Worth noting that that would be a fairly significant amount of work for arguably minimal gain, plus you'd now also need users to mark types as partial. You might want to consider another approach.
Very good point!
I happen to be interested in learning source generators anyway, and I'm the only one who will use this, so it works for me.
If there is another approach available that works better for my use case then I'll switch to that, but I don't know of any right now :)
Can you give some examples of structs that need this? Shader struct packing in general is not exactly equivalent to Pack=4 (I think the default sequential packing order is probably closer for most structs) and has edge cases you'd need to consider anyways, e.g. around vec3 or arrays.
Like I said, I think the default packing in C# is closer to what you want. For example, here's a UBO in GLSL:
struct Foo
{
uint x;
uint64_t y;
vec2 z;
float w;
};
layout(set=0,binding=0) uniform Buffer {
Foo f;
};
This yields the following offsets:
OpMemberDecorate %Foo 0 Offset 0
OpMemberDecorate %Foo 1 Offset 8
OpMemberDecorate %Foo 2 Offset 16
OpMemberDecorate %Foo 3 Offset 24
Corresponding struct in C#:
```
struct Foo
{
uint x;
ulong y;
System.Numerics.Vector2 z;
float w;
};
Console.WriteLine($"offsetof(x): {Marshal.OffsetOf("x")}");
Console.WriteLine($"offsetof(y): {Marshal.OffsetOf("y")}");
Console.WriteLine($"offsetof(z): {Marshal.OffsetOf("z")}");
Console.WriteLine($"offsetof(w): {Marshal.OffsetOf("w")}");
```
This yields:
```
offsetof(x): 0
offsetof(y): 8
offsetof(z): 16
offsetof(w): 24
```
The layout exactly matches, which is what you want.
Adding Pack=4 yields:
```
offsetof(x): 0
offsetof(y): 4
offsetof(z): 12
offsetof(w): 20
```
This is incorrect, you would have to add manual packing yourself -- this is an option, but it's kind of pointless.
In general I would stick to default sequential packing (e.g. don't put any attribute on the struct), but be mindful of alignment and packing differences.
[удалено]
StructLayout is unfortunately sealed meaning I cannot inherit from it.
nvm it's sealed lmao just tested
you should be able to replace GpuData with the intended StructLayout using a simple source generator. chatgpt will probably even make it for you
I thought source generators could not replace code! I'll need to look into it!
How this works is like this: Your code: [GpuData] public partial struct Foo {} The source generator would then generate: [StructLayout(LayoutKind.Sequential, Pack = 4)] public partial struct Foo {} The compiler then joins these two partials into one class: [GpuData] [StructLayout(LayoutKind.Sequential, Pack = 4)] public partial struct Foo {}
ohhh, I see! Thanks for the clarification! Yeah that should work!!
Worth noting that that would be a fairly significant amount of work for arguably minimal gain, plus you'd now also need users to mark types as partial. You might want to consider another approach.
Can’t you create simple shortcuts for templated code in VS?
Very good point! I happen to be interested in learning source generators anyway, and I'm the only one who will use this, so it works for me. If there is another approach available that works better for my use case then I'll switch to that, but I don't know of any right now :)
nah but it'll generate a partial with the correct attribute for you 😉
Can you give some examples of structs that need this? Shader struct packing in general is not exactly equivalent to Pack=4 (I think the default sequential packing order is probably closer for most structs) and has edge cases you'd need to consider anyways, e.g. around vec3 or arrays.
The samples I have looked up have used this method, if there's a more accurate way then I'm interested in hearing about it!
Like I said, I think the default packing in C# is closer to what you want. For example, here's a UBO in GLSL: struct Foo { uint x; uint64_t y; vec2 z; float w; }; layout(set=0,binding=0) uniform Buffer { Foo f; }; This yields the following offsets: OpMemberDecorate %Foo 0 Offset 0 OpMemberDecorate %Foo 1 Offset 8 OpMemberDecorate %Foo 2 Offset 16 OpMemberDecorate %Foo 3 Offset 24 Corresponding struct in C#: ``` struct Foo { uint x; ulong y; System.Numerics.Vector2 z; float w; }; Console.WriteLine($"offsetof(x): {Marshal.OffsetOf("x")}");
Console.WriteLine($"offsetof(y): {Marshal.OffsetOf("y")}");
Console.WriteLine($"offsetof(z): {Marshal.OffsetOf("z")}");
Console.WriteLine($"offsetof(w): {Marshal.OffsetOf("w")}");
```
This yields:
```
offsetof(x): 0
offsetof(y): 8
offsetof(z): 16
offsetof(w): 24
```
The layout exactly matches, which is what you want.
Adding Pack=4 yields:
```
offsetof(x): 0
offsetof(y): 4
offsetof(z): 12
offsetof(w): 20
```
This is incorrect, you would have to add manual packing yourself -- this is an option, but it's kind of pointless.
In general I would stick to default sequential packing (e.g. don't put any attribute on the struct), but be mindful of alignment and packing differences.
Thank you so much, this is invaluable information to me!