Fast memory copy between arrays?

I have a byte array and a Color32() array. The byte array contains color component data, but it cannot be in a Color32() format for various reasons.

I want to do a very fast/fastest possible copy of a rectangular area of that byte array into the Color32() array, which later will be uploaded to a texture. The bytearray represents a much larger rectangle of pixels than the Color32() array, so I only need to copy a subsection.

At present I can do this in a loop myself by constructing new Color32()'s from 4 bytes. But I am wondering is there some API function to do like a fast memcopy or to cast the array or something, so that I can copy between them much faster?

System.Array.Copy exists, but i doubt it will convert from byte to color component

I haven’t done any timed tests so can’t speak to the speed of this, but:

using UnityEngine;
using System.Runtime.InteropServices;

public class StructTest : MonoBehaviour {

    // Use this for initialization
    void Start () {

        var colorBytes = new Color32Bytes();
        colorBytes.byteArray = new byte[16] {
            255,0,0,255,    //red
            255,0,0,255,    //red
            0,255,0,255,    //green
            255,0,0,255     //red
        };

        GetComponent<Renderer>().material.SetColor("_Color", colorBytes.colors[2]); // sets color of attached renderer's material to green
    }
}

[StructLayout(LayoutKind.Explicit)]
struct Color32Bytes
{
    [FieldOffset(0)]
    public byte[] byteArray;   //4 bytes per color32

    [FieldOffset(0)]
    public Color32[] colors;
}

What is that code doing? Color32Bytes is some kind of new type which acts both as a byte array and a color32 array, sharing the same memory? I guess that might possibly work, but not much different to Color32(bytearray[ ]).

Wondering mainly if there’s a system function which, at the lower level, is designed for copying memory like this without lots of Unity script being involved, since native functions are faster… even if I could just use to copy one row at a time.

It is the C# equivalent of a C/C++ union. I’ve not used it but essentially it is telling the compiler that you want access to a block of memory in various ways, such as by Color32 objects or by sets of four bytes.

I’m sure C# imposes some additional restrictions on it to keep you within managed code boundaries, but essentially that data layout allows you to access it either way.

None of this speaks to what kind of performance you might see under the hood however. Depending on the types, it may be necessary for some very unoptimized byte-level copying to actually take place.

There’s a blurb about it here:

https://social.msdn.microsoft.com/Forums/en-US/60150e7b-665a-49a2-8e2e-2097986142f3/c-equivalent-to-c-union?forum=csharplanguage

You might also be able to use System.Runtime.InteropServices calls to pin each array and then use Marshal.Copy blast stuff around in memory, and that ought to be pretty close to your basic C/C++ memcpy() style burst transfer mode.

Can this be done in Unity script?

That is outside my knowledge. Sorry. Google is your friend there. I imagine that it depends if UnityScript properly exposes those services in the .NET library.

Which platform are you on? If it’s desktop, you might be able to do this using an ā€˜unsafe’ block in C#.

3 Likes

ā€˜Unsafe’ was the first thing that popped into my mind too. It lets you play with pointers and memory directly.

Be warned, its called ā€˜unsafe’ for a reason. Its pretty easy to mess things up if you are not careful. You are basically taking your memory into your own hands.

Somebody wrote that it’s about the same as Array.Copy() so not sure if this will help or not.

I saw that too… the big problem though is array.copy and blockcopy will not work copying bytes from a byte array to a color32[ ] array. They are different types. You can only blockcopy primitive types. And although array.copy says it will typecast and stuff, it doesn’t work in this case.

Are you sure? Bytes are bytes regardless of data type. As long as it’s all sequential in memory and you compute the number of bytes to copy correctly I’d think that ought to work.

BlockCopy uses a source and destination address and the number of bytes to copy. It doesn’t care what the type is.

Unity throws an error when trying to blockcopy from a bytearray to a color32 array. The error goes away when both arrays are of the same type.

System.Buffer.BlockCopy(LevelPixels,CacheLevelRow+LevelScrollX,VisiblePixels,CacheYRow,CacheTextureSize);

(Copying 1 row of data, LevelPixels is a byte array, VisiblePixels is a Color32[ ] array)

produces:

ā€œArgumentException: Object must be an array of primitives.
System.Buffer.ByteLength (System.Array array) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/Buffer.cs:64)
System.Buffer.BlockCopy (System.Array src, Int32 srcOffset, System.Array dst, Int32 dstOffset, Int32 count) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/Buffer.cs:ā€

How though can you get the pointer to a Color32[ ] array and a byte array, to be able to then pass this into the unsafe copy?

This is a sample snippet to get a System.IntPtr out of a Color32[ ] array.

This particular code just passes it to a native function, but you can do whatever you want with it.

    public static void PinTextureAndCallNativeRenderColor( Texture2D t2d, int span)
    {
        if (pixels == null || span != pixelSpan)
        {
            pixelSpan = span;
            pixels = new Color32[ pixelSpan * pixelSpan];
        }

        GCHandle handle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
        try
        {
            dispatcher1_rendercolor( handle.AddrOfPinnedObject(), span, 0);
        }
        finally
        {
            if (handle.IsAllocated)
            {
                handle.Free ();
            }
        }
       
        t2d.SetPixels32( pixels);
        t2d.Apply();
    }

is the try/finally block really needed, or is that just part of something else you’re doing?

Well yes, if you don’t want to be leaking little GCHandle-sized blocks of memory every time you sail through here.

Take a look at some of the docs on the functions involved because before using them ā€œin anger,ā€ you might want to develop a sense of what you are getting into with it all.

Based on above code and similar code found elsewhere, I came up with this which works in Unity and is legal code (currently, anyway).

You need a ā€œusing System.Runtime.InteropServices;ā€ at the top of your script. C#

    // Cast a byte array as a Color32 array to allow unity to directly upload byte-array data using SetColors32() function to an RGBA texture without having to 'convert' all the byte data from byte to Color32 first.
//You can then just SetPixels32() using the byte array, while still retaining the speed ability of reading/writing bytes instead of Color32's.
    void Start () {
        byte[] mybytearray = new byte[1000];
        object inputObj = mybytearray;
        mybytearray[0]=10;
        mybytearray[1]=20;
        mybytearray[2]=30;
        mybytearray[3]=40;
        Color32[] mycolors = Convert(mybytearray);    //'Cast' byte array as Color32 array
        Debug.Log(mycolors[0]);    //Should show 10,20,30,40 without having ever copied any data to a separate Color32 array
    }

    static Color32[] Convert(byte[] input){
        //This function is called in order to pass in a byte array and convert it to a Color32 array.
        //The 'Converter' type defined below in the struct, is basically just a custom type with a byte array field
        //Except that also in that structure, the [FieldOffset] is used to say at what memory location to locate the data for a given field
        //Then within that custom type, we have a Color32 field (at this point unused), which happens to map to the same memory location as the byte array, so they share memory
        //So if we simply set the byte array to that custom type's byte array, the type's Color32 array will also be pointing to that same memory reference, with no 'copy' of any memory bytes
        //So the reference in it to the byte array points to the same mybytearray[] memory as the Color32[] array.
        //We then simply return the Color32[] array and hey presto, the byte array has been interpreted as a Color32 array with almost zero overhead.
        Converter converter = new Converter();
        converter.mybytes = input;
        return converter.mycols;
    }
    [StructLayout(LayoutKind.Explicit)]
    struct Converter
    {
        [FieldOffset(0)]
        public byte[] mybytes;
        [FieldOffset(0)]
        public Color32[] mycols;
    }
    //We’re using a Converter-struct that uses some interop-magic to define two fields that map to the same location in memory. Now we can access the same actual memory but using different variable types
3 Likes