pragma's picture

CLOO: copy two dimensional array

Hi there,

I'm experimenting a bit with Cloo (very cool project) and ran into a problem:

I'm trying to copy a 2 dimensional float array onto the device something like

float[,] weights = new float[count, count];

obviously I can't copy it via:

ComputeBuffer<float> weightsBuffer = new ComputeBuffer<float>(context, ComputeMemoryFlags.CopyHostPointer, weights);

How can I copy this array to the device?

I could manually flatten the array :

 
float[] weightsFlat = new float[count* count];
int x = 0;
for (int i=0; i<count; i++)
   for (int j=0; j<count; j++)
      weightsFlat[x++] = weights[i,j];

but I would really want to avoid that.

greetings pragma


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
nythrix's picture

The following seems to work:

ComputePlatform platform = ComputePlatform.Platforms[0];
ComputeContextPropertyList cpl = new ComputeContextPropertyList(platform);
ComputeContext context = new ComputeContext(ComputeDeviceTypes.Default, cpl, null, IntPtr.Zero);
float[,] arr = new float[,]
    {
        {1,2,3},
        {4,5,6},
        {7,8,9}
    };
IntPtr arrayPtr = Marshal.UnsafeAddrOfPinnedArrayElement(arr, 0);
ComputeBuffer<float> clBuffer = new ComputeBuffer<float>(context, ComputeMemoryFlags.CopyHostPointer, 9*sizeof(float), arrayPtr);
ComputeCommandQueue commands = new ComputeCommandQueue(context, context.Devices[0], ComputeCommandQueueFlags.None);
float[] flat = commands.Read(clBuffer, null);

Two things to keep in mind:
1) I'm not sure if the input array has to be pinned or rather I don't know if UnsafeAddrOfPinnedArrayElement does it and
2) coming back from the ComputeBuffer it might be hard to reconstruct the original 2D array.

Edit: I'm a total stick. You CAN reconstruct the 2D (and probably 3D, 4D, 5D, ...) array using the same pointer mechanism and this call:

commands.Read(clBuffer, trueOrFalse, 0, 9, arrayPtr, null);
the Fiddler's picture

Of course, another solution is to provide additional overloads for 2d and 3d arrays in Cloo. That's what OpenTK does for such functions, e.g. GL.BufferData:

BufferData(..., IntPtr data);                   // 1 item or nd-arrays (manual pin)
BufferData<T>(..., ref T data) where T struct; // 1 item or nd-arrays
BufferData<T>(..., T[] data) where T struct;   // 1d arrays
BufferData<T>(..., T[,] data) where T struct;  // 2d arrays
BufferData<T>(..., T[,,] data) where T struct; // 3d arrays
 
// Usage example. All methods below are equivalent.
float[,,] data = new float[1,2,3];
 
BufferData(..., ref data[0]); // ref T overload
BufferData(..., data);        // T[,,] overload
 
unsafe
{
    fixed (float*  data_ptr = data)
    {
        BufferData(..., (IntPtr)data_ptr); // IntPtr overload
    }
}
 
GCHandle data_ptr = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
    BufferData(..., data_ptr.AddrOfPinnedObject()); // IntPtr overload, slower than above
}
finally
{
    data_ptr.Free();
}
nythrix's picture

Of course, another solution is to provide additional overloads for 2d and 3d arrays in Cloo.
That's even a better idea.

pragma's picture

Ah thank you.

i tried that Marshal.UnsafeAddrOfPinnedArrayElement thing and it seems to work :)

btw: it seems like you do have to pin it before retrieving the address

gamingdrake's picture

I know this is probably wayyy late for this. .NET has a method in the LINQ namespace called SelectMany. It works like this.

int[][] myArray = new int[50][];
for(int  i = 0; i < 50; i++)
{
      myArray[i] = new int[i];
}
 
//now we get to the good part
IEnumerable<int> m_En = myArray.SelectMany(x => x);
int[] flattened = m_En.ToArray();

you can do this for any dimensional arrays. I currently have it working on a 3d jagged array, taking the 0th index and casting the rest to an array. Works nicely and is relatively fast.