CUDAfy.Net / OpenCL, структура, содержащая байтовый массив, приводит к неблизкому исключению

Итак, я использую CUDAfy.Net, и у меня есть следующие 3 структуры:

[Cudafy]
public struct Collider
{
    public int Index;
    public int Type;
    public Sphere Sphere;
    public Plane Plane;
    public Material Material;
}

[Cudafy]
public struct Material
{
    public Color Color;
    public Texture Texture;
    public float Shininess;
}

[Cudafy]
public struct Texture
{
    public int Width, Height;
    public byte[ ] Data;
}

Теперь, как только я отправлю массив объектов Collider в графический процессор, используя

CopyToDevice<GPU.Collider>( ColliderArray );

Я получаю следующую ошибку:

An unhandled exception of type 'System.ArgumentException' occurred in mscorlib.dll
Additional information: Object contains non-primitive or non-blittable data.

Кто-нибудь с опытом работы с CUDAfy.Net или OpenCL (так как он в основном компилируется в OpenCL), знает, как мне это сделать? Вся проблема заключается в байтовом массиве Texture, так как все работало очень хорошо, когда у меня не было структуры Texture, и, насколько я знаю, массив - это незаметная часть. Я нашел несколько вопросов, касающихся одной и той же проблемы, и они исправили ее с помощью массивов фиксированного размера. Однако я не могу этого сделать, так как это текстуры, которые могут иметь очень разные размеры.

РЕДАКТИРОВАТЬ: Прямо сейчас я делаю следующее на процессоре:

    public unsafe static GPU.Texture CreateGPUTexture( Cudafy.Host.GPGPU _GPU, System.Drawing.Bitmap Image )
    {
        GPU.Texture T = new GPU.Texture( );
        T.Width = Image.Width;
        T.Height = Image.Height;
        byte[ ] Data = new byte[ Image.Width * Image.Height * 3 ];


        for ( int X = 0; X < Image.Width; X++ )
            for ( int Y = 0; Y < Image.Height; Y++ )
            {
                System.Drawing.Color C = Image.GetPixel( X, Y );
                int ID = ( X + Y * Image.Width ) * 3;
                Data[ ID ] = C.R;
                Data[ ID + 1 ] = C.G;
                Data[ ID + 2 ] = C.B;
            }

        byte[ ] _Data = _GPU.CopyToDevice<byte>( Data );
        IntPtr Pointer = _GPU.GetDeviceMemory( _Data ).Pointer;
        T.Data = ( byte* )Pointer.ToPointer( );

        return T;
    }

Затем я присоединяю эту текстурную структуру к коллайдерам и отправляю их в графический процессор. Это все идет без каких-либо ошибок. Однако, как только я пытаюсь использовать текстуру на GPU, вот так:

    [Cudafy]
    public static Color GetTextureColor( int X, int Y, Texture Tex )
    {
        int ID = ( X + Y * Tex.Width ) * 3;
        unsafe
        {
            byte R = Tex.Data[ ID ];
            byte G = Tex.Data[ ID + 1 ];
            byte B = Tex.Data[ ID + 2 ];

            return CreateColor( ( float )R / 255f, ( float )G / 255f, ( float )B / 255f );
        }
    }

Я получаю следующую ошибку:

An unhandled exception of type 'Cloo.InvalidCommandQueueComputeException' occurred in Cudafy.NET.dll
Additional information: OpenCL error code detected: InvalidCommandQueue.

Кстати, структура Texture выглядит следующим образом:

    [Cudafy]
    public unsafe struct Texture
    {
        public int Width, Height;
        public byte* Data;
    }

Я снова в растерянности..

1 ответ

Cudafy пока не поддерживает массивы. Таким образом, вы не можете использовать "public byte[] Data" ни в структурах, ни в самих ядрах. Вы можете попробовать это менее объектно-ориентированным. Я имею в виду попытаться удалить массив данных из самой structre и скопировать их отдельно. например, copyToDevice("свойства текстуры"), а затем скопировать соответствующий массив данных copyToDevice("данные текстуры")

РЕДАКТИРОВАТЬ: ОК, я нашел решение, но это не красивый код.

Как вы получите указатель ваших данных, хранящихся в памяти графического процессора. приведите его к целочисленному значению pointer.ToInt64(); и сохраните это значение в вашем объекте Structure просто как длинное значение (не длинный указатель). чем вы можете использовать метод GThread.InsertCode() для прямой вставки кода в ваше ядро ​​без компиляции. Вы не можете использовать указатель непосредственно в коде вашего ядра, потому что они не являются blittable типом данных. Так что хватит говорить здесь - пример моего рабочего кода

class Program
{
    [Cudafy]
    public struct TestStruct
    {
        public double value;
        public long dataPointer; // your data pointer adress
    }

    [Cudafy]
    public static void kernelTest(GThread thread, TestStruct[] structure, int[] intArray)
    {
        // Do something 
        GThread.InsertCode("int* pointer = (int*)structure[0].dataPointer;");
        GThread.InsertCode("structure[0].value = pointer[1];");             // Here you can acces your data using pointer pointer[0], pointer[1] and so on
    }


    private unsafe static void Main(string[] args)
    {

            GPGPU gpuCuda = CudafyHost.GetDevice(eGPUType.Cuda, 0);
            CudafyModule km = CudafyTranslator.Cudafy();
            gpuCuda.LoadModule(km);

            TestStruct[] host_array = new TestStruct[1];
            host_array[0] = new TestStruct();

            int[] host_intArray = new[] {1, 8, 3};
            int[] dev_intArray = gpuCuda.CopyToDevice(host_intArray);

            DevicePtrEx p = gpuCuda.GetDeviceMemory(dev_intArray);
            IntPtr pointer = p.Pointer;

            host_array[0].dataPointer = pointer.ToInt64();


            TestStruct[] dev_array = gpuCuda.Allocate(host_array);
            gpuCuda.CopyToDevice(host_array, dev_array);

            gpuCuda.Launch().kernelTest(dev_array, dev_intArray);

            gpuCuda.CopyFromDevice(dev_array, host_array);

            Console.WriteLine(host_array[0].value);

            Console.ReadKey();
    }
}

"Волшебство" заключается в InsertCode (), где вы приводите свое длинное значение dataPointer в качестве адреса указателя int... но недостатком этого подхода является то, что вы должны записать эти части кода как String.

ИЛИ вы можете разделить ваши данные и структуры, например

[Cudafy]
public struct Texture
{
    public int Width, Height;
}

[Cudafy]
    public static void kernelTest(GThread thread, Texture[] TexStructure, byte[] Data)
    {....}

И просто скопировать

dev_Data = gpu.CopyToDevice(host_Data);
dev_Texture = gpu.CopyToDevice(host_Texture);
gpu.Launch().kernelTest(dev_Texture, dev_Data);

РЕДАКТИРОВАТЬ ВТОРОЕ: забудь про мой код:D

Проверьте это https://cudafy.codeplex.com/discussions/538310 и это решение вашей проблемы https://cudafy.codeplex.com/discussions/283527

Другие вопросы по тегам