I have a large byte array. I need to ‘map’ i.e. share the memory of a small portion of it, in Color32[ ] format. It is absolutely VITAL that there is no copying of data in this process, it’s very performance sensitive.
So for example, this byte array represents a 2D image and I want to get a small portion of 1 row of the image represented as a Color32[ ] array, so that I can use it elsewhere where Color32[ ] is required.
I figured out a way to do it, in 5 steps.
- Pin the memory of the byte array to get an Int Ptr and ‘lock’ the memory
- Get the address of the pinned object and convert it to Int64
- Add an offset to the Int64 representing the start of where I want to map the memory within the byte array and cast it back to an IntPtr
- Store the pointer in a custom struct which explicitly maps one IntPtr and one Color32[ ] field to the same offset in memory using [FieldOffset(0)] on both fields
- Read back out the Color32[ ] array from the struct and use it
My concern here though is that in order to get the IntPtr to properly map to the Color32[ ] array, I have to subtract a certain number of bytes from the pointer which represents (i think) the per-object overhead/header of .net by-reference objects. This entirely depends on a) whether you’re on 64-bit or 32-bit and b) whether you’re in the editor. 32-bit has a 8-byte overhead, 64-bit has 16, and the editor doubles both.
Long story short this ‘works’… provided I put compiler directives in for each platform, BUT this sort of feels all kind of a bit hacky and unreliable. I’m worried about what might happen in future if .net changes the overhead sizes or makes them unpredictable, or if some platform/device needs to be configured with a different overhead size based on something I can’t control or know about etc.
I tried instead using these two options e.g.
//Color32[ ] ObjCol32=(Color32[ ])Marshal.PtrToStructure(pinnedBytes.AddrOfPinnedObject(),typeof(Color32[ ]));
//Color32 Col32=(Color32)Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(Colors,4),typeof(Color32[ ]));
Neither of these let me cast to a Color32. The second one is ideal because it lets you have an offset (here I used 4 to test), but it just gives a ‘cant cast to type’ error. I think it’s because I want an array out of it and it doesn’t want to work except for individual pieces of data? Otherwise I’m just not programming the syntax properly or something.
When I break out the second one into two parts:
IntPtr test = Marshal.UnsafeAddrOfPinnedArrayElement(Colors,4);
Color32[ ] test2 = (Color32[ ])Marshal.PtrToStructure(test,typeof(Color32[ ]));
… the first part works but the second part does not. It gives error “InvalidCastException: Cannot cast from source type to destination type.” when the cast is attempted, even though the script has no errors in Unity until I hit play.
Anyway… any help here? I just simply need to have a sort of ‘virtual’ Color32[ ] array mapped onto the memory of a portion of a byte array, with an offset from the base address, and I need to be able to modify that offset many times so it needs to be in a variable.
???