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

#c# #arrays #struct #opencl #cudafy.net

#c# #массивы #структура #opencl #cudafy.net

Вопрос:

Хорошо, поэтому я использую 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), есть какие-нибудь идеи, как я мог бы это сделать? Вся проблема заключается в массиве байтов текстуры, поскольку все работало просто отлично, когда у меня не было структуры текстуры, а массив, насколько я знаю, является недопустимой частью.
Я нашел несколько вопросов, касающихся той же проблемы, и они исправили ее, используя массивы фиксированного размера. Однако я не могу этого сделать, так как это текстуры, которые могут иметь сильно различающиеся размеры.

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

     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;
    }
  

Затем я прикрепляю эту текстурную структуру к коллайдерам и отправляю их на графический процессор. Все это происходит без каких-либо ошибок.
Однако, как только я попытаюсь ИСПОЛЬЗОВАТЬ текстуру на графическом процессоре, например:

     [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.
  

Структура текстуры, кстати, выглядит так:

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

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

Комментарии:

1. Я согласен с вашей оценкой, что это связано byte[ ] Data с тем, что это переменный размер, и, вероятно, хранилище является внешним по отношению к самой Texture структуре. OpenCL 1.x не может отправлять указатели на хост на устройство. Вы могли бы либо иметь объекты текстуры фиксированного размера, либо иметь их набор (например, маленький, средний, большой, возможно, увеличивающийся в 2 раза на каждом уровне) и выбрать наиболее подходящий для каждой текстуры. Или используйте низкоуровневые вызовы OpenCL и выделяйте точно подходящие объекты в качестве собственных буферов или изображений OpenCL.

2. Я думаю, что я бы предпочел использовать низкоуровневое решение, поскольку это сэкономит большую часть памяти. Возможно, у вас есть пример того, как это сделать? Или некоторые условия поиска, которые, скорее всего, приведут меня к правильным ответам?

3. Не навскидку; просто Google clCreateBuffer , и вы должны найти несколько примеров. Этот API может как создавать, так и загружать буфер, или вы можете просто создать его и использовать clEnqueueWriteBuffer для его загрузки.

4. Я обновил исходное сообщение, не могли бы вы взглянуть, правильно ли это? Теперь я получаю другую ошибку, но копирование на графический процессор больше не является проблемой.

5. Извините, я не знаю CUDAfy, поэтому большая часть этого для меня греческая. Однако целое Pointer и ToPointer прочее в GetDeviceMemory объекте кажется мне странным, поскольку вы не используете указатели для связи с памятью устройства в OpenCL 1.x (вы используете дескрипторы буфера). Ошибка, которую вы получаете, заключается InvalidCommandQueue в том, что я бы сначала облаял это дерево — кто отвечает за очереди команд и почему оно недействительно? Возможно, вам потребуется изучить некоторый OpenCL API и перейти к кодовой базе CUDAfy, чтобы решить эту проблему, или найти кого-то, кто знает CUDAfy.

Ответ №1:

Cudafy пока не поддерживает массивы. Таким образом, вы не можете использовать «общедоступные байтовые [] данные» ни в структурах, ни в самих ядрах. вы могли бы попробовать его менее объектно-ориентированным. Я имею в виду попытку удалить массив данных из самой структуры и скопировать их отдельно. например, copyToDevice(«свойства текстуры»), а затем скопировать соответствующий массив данных copyToDevice («данные текстуры»)

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

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

 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(), где вы приводите свое значение long dataPointer в качестве адреса указателя int… но недостатком этого подхода является то, что вы должны записывать эти части кода в виде строки.

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

 [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

Комментарии:

1. Не могли бы вы, возможно, объяснить эту идею более подробно? Мне довольно сложно понять, как я буду отправлять данные на графический процессор без массивов, каким-то образом сохраняя их там (в основном эту часть, которая вызывает у меня проблемы), а затем отправлять массив отдельно, чтобы добавить его к первым данным на графическом процессоре.