Convert byte array with offset to a small Color32 array?

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.

  1. Pin the memory of the byte array to get an Int Ptr and ‘lock’ the memory
  2. Get the address of the pinned object and convert it to Int64
  3. 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
  4. 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
  5. 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.

???

Its because your not quite understanding how the array is being setup. When you create a Color32[10] test you are created a space in memory that is 10xptrSize long. Each of which holds a pointer to an actual Color32 structure somewhere in memory. Now normally you would actually create these Color32 structures, but instead we’ll just point our pointers to your byteArray.

So what you would want to do is something like this:

Color32[] colorArray = new Color32[10];
IntPtr test = Marshal.UnsafeAddrOfPinnedArrayElement(Colors,4);
for (int i=0;i<10;++i)
{
    colorArray[i] = (Color32)Marshal.PtrToStructure(test,typeof(Color32));
     test+=SizeOf(Color32);
}
1 Like

Hm. So you’re saying Color32[ ] isn’t a solid array of bytes in a sequence, it’s actually an array of pointers? Are you sure? Because in my code I’m sharing an Int Ptr with a Color32[ ] and did not in any way attempt to get the ‘pointers of individual Color32’s’ and got a correct output, as though all the colors in the array were stored sequentially. I only handed the Color32 array a bunch of byte data, no pointers at all, and it output the correct pixel colors.

Could it be this is just by accident that they seem sequential based on how they were allocated? So a Color32[ ] array is not only the size of all the memory it needs for the colors, PLUS a 4-byte pointer for every single pixel? This to me seems horribly inefficient. I’d have to do this marshaling thing for every single pixel individually instead of a single function call for the whole array?

Also btw I’ve found that using SetPixels32() and Apply() versus using LoadRawTextureData() and Apply() is exactly the same speed, and the raw data is a byte[ ] array, suggesting that there isn’t any overhead at all from indirect pointers to Color32 elements?

Nope, a Color32 is a struct, therefore there are no pointers. An array of 10 Color32s uses 40 bytes, plus a bit of overhead for the array itself (not per entry).

–Eric

3 Likes

I may not be explaining it correctly. A standard way to make a Color32 array would be like this:

Color32[] colorArray = new Color32[10];
for (int i=0;i<10;++i)
     colorArray[i] = new Color32();

So lets say a pointer to a Color32 struct is 4 bytes long. colorArray is now an array that is now sitting in memory location 1000 and is 40 bytes long. Lets say the first color32 I create in the for loop is created and is sitting at memory location 2000, the 2nd one is located in memory 2160, 3rd at 54,330 and so on. Our colorArray would actual look like this:
color[0] = 2000
color[1] = 2160
color[2] = 54330

Then in memory location 2000 would be the actual Color32 structure
So all my code is doing is setting each of those pointers to a pointer in your byte array.

You can test this by trying this code:

Color32[] colorArray = new Color32[10];

colarArray[0].r = 10;

It will throw an exception because you never actually allocated memory for color[0]. you just have a null pointer sitting in there.

Ok that’s what I thought Eric. Since you’re here :wink: … can you shed any light then on whether it’s possible at all to convert a byte[ ] to a Color32[ ] with a base offset? The code that tatatok gave above trawls through individual color entries which is not what I want. I am hoping for a single function call (or a few) to perform the conversion for the whole set of data. Is it possible with PtrToStructure() or is that only designed for individual instances of a type?

But Eric just said the Color32[ ] array is sequential and doesn’t use pointers for each entry.

I know you have to do like colorArray[0]=new Color32(1,2,3,4); … which does suggest it is a separate little chunk of 4 bytes sitting somewhere, with a pointer to it, but… eric is saying that’s not the case. Maybe its just set up to look that way on the surface? And the ‘new’ is just creating a new one and then copying its contents into the array entry, as a value, right? It’s not setting a pointer to the one you created? Cus you can probably do colorArray[0]=(Color32)(1,2,3,4); ?

I could be wrong since its a structure. I’m from an old C/C++ background. So the nuances of structure versus class in c# could be eluding me.

EDIT:
Just did some research. All my posts above are valid but only for a class. not structures. Structures are indeed allocated inline inside the array.

Color32[ ] sausage=new Color32[10];
print (sausage[0].r);

Did not throw an error. It printed 0. Because the array space was allocated.

Ok so… all agree its a struct with sequential memory. Question still is then, this being the case, is it possible to convert a byte[ ] to a Color32[ ]?

If Color32 element is a struct and Color32[ ] is an array of structs… and presumably a Color32[ ] array is ITSELF a struct of some kind? A struct with structs in it?

I am using this currently:

[StructLayout(LayoutKind.Explicit)]
struct Converter
{
[FieldOffset(0)]
public IntPtr myints; //pointer to some memory somewhere, the actual memory is not stored here
[FieldOffset(0)]
public Color32[ ] mycols; //point to the same memory, but in the form of a color32[ ]
}
}

Which is one way to do it, but I have to make the IntPtr be -16 or -32 less than the base pointer of the byte array in order for this to ‘map’ properly. It seems a bit hacky. If there is a marshaling function I think it would be safer?

Nope, Color32[ ] array is a class. If you pass around a Color32[ ] array you’re passing around a pointer. Otherwise, if the Color32[ ] array itself was a struct, and for example you used it as a function argument, you’d be copying the entire array when calling the function. So Color32[ ] array is a class, which contains structs.

–Eric

1 Like

ok good to know thanks.

Is it possible to then somehow share memory between a Color32[ ] array class, and a byte[ ] array, using some kind of marshaling thingy instead of a struct that shares fields?

Good question…C# seems dead set against allowing that sort of thing naturally, so I think any solution will be inherently hacky. The standard ways I know of for converting stuff to/from byte arrays involve copying, so those are out.

–Eric

Yeah you’re probably right. I’m emulating a ‘union’ in C, and shared memory is a pretty fringey thing compared to the normal way of doing things. I guess I will have to go with it and just try to keep tabs on getting the object header size to be right on various platforms, or let the user (other developers, cus this is a tool) determine it themselves.