GetData() corrupts data when an element size is not a multiple of 16 bytes
Prerequisites
- [X] I have verified this issue is present in the
developbranch - [X] I have searched open and closed issues to ensure it has not already been reported.
MonoGame Version
MonoGame.Framework.Compute.DesktopGL 3.8.3
Which MonoGame platform are you using?
MonoGame Cross-Platform Desktop Application (mgdesktopgl)
Operating System
Windows
Description
I have a struct of 3 floats. It takes three bytes. When I generate elements {111,222,333} in a shader and get it back through GetData() data becomes corrupted. Looks like it tries to extend stride to 16 bytes. If I add another float to my structure it works well (I think because of a stride becomes a multiple of 16 bytes)
Steps to Reproduce
shader code:
RWStructuredBuffer<particle> buf;
struct particle2
{
float3 position;
// float w;
};
[numthreads(1,1,1)]
void EmitParticles(uint3 id : SV_DispatchThreadID)
{
particle2 p;
p.position = float3(111,222,333);
buf[id.x] = p;
}
c# code:
public struct Particle2
{
public float X;
public float Y;
public float Z;
// public float W;
}
_shader.DispatchIndirect(0, 0, _argsBuffer);
var data = new Particle2[100];
_testBuffer.GetData(data);
If I uncomment a third component in both shader and c# it works as it should.
Minimal Example Repo
No response
Expected Behavior
GetData() should not corrupt data. If it is impossible to operate structs which stride is not a multiple of 16 an exception should be thrown.
Resulting Behavior
I have a struct of 3 floats. It takes three bytes. When I generate elements {111,222,333} in a shader and get it back through GetData() data becomes corrupted. Looks like it tries to extend stride to 16 bytes. If I add another float to my structure it works well (I think because of a stride becomes a multiple of 16 bytes)
Files
No response
Update: my theory about setting element size multiple of 16 bytes turned out to be wrong. When a float3 hits a border of 16-byte blocks data become corrupted.
corrupted:
struct particle
{
float3 pos;
float3 pos2;
float _1;
float _2;
};
works well:
struct particle
{
float3 pos;
float _1;
// -------- 16-byte block
float3 pos2;
float _2;
// -------- 16-byte block
};
Yes, you want to pad your data to be 16-byte aligned. GPU's are vector processors. They can process 4 floats in a single instruction, hence the 16 byte alignment.
But Unity somehow deals with that on my android device... So what's the proper way to pass a struct containing float3-data? Pad them with a single float to get 64 bit?
Yes exactly, you pad it with one float. I guess Unity does that for you.
In this sample I'm padding the particle struct to be 8-byte aligned, which works fine: Particle So I guess 8 byte is the minimum alignement, not 16.
Whenever you bind a structured buffer to a shader in a debug build, a check is performed that verifies that the C# struct and the shader struct are of the same size. You get an exception when they are not. With the standard Nugets this doesn't work though, because they are release builds. I pushed a fix for this now, so the check is also performed in release builds, but I didn't publish new Nugets yet.