繁体   English   中英

CUDAfy.Net / OpenCL,包含字节数组的结构导致不可拆分的异常

[英]CUDAfy.Net / OpenCL, struct containing byte array results in non-blittable exception

好的,所以我正在使用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对象数组发送到GPU,

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结构时,一切工作都很好,而据我所知,该数组是不可着色的部分。 我发现了关于同一问题的几个问题,他们使用固定大小的数组对其进行了修复。 但是,我无法执行此操作,因为这些是纹理,其大小可能有很大差异。

编辑:现在,我在CPU上执行以下操作:

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

然后,我将此Texture结构附加到对撞机,并将它们发送到GPU。 这一切都没有任何错误。 但是,当我尝试在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;
    }

我又完全茫然了。

Cudafy还不支持数组。 因此,既不能在结构中也不能在内核本身中使用“ public byte [] Data”。 您可以尝试减少面向对象的使用。 我的意思是尝试从structre本身删除数据数组并分别复制它们。 例如copyToDevice(“ texture properties”),然后复制适当的数据数组copyToDevice(“ texture data”)

编辑:好的我找到了一个解决方案,但它不是漂亮的代码。

当您获得存储在GPU mem中的数据指针时。 将他强制转换为整数值指标。ToInt64(); 并将此值作为长值(而不是长指针)存储在您的Structure对象中。 您可以使用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()中,您将长的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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM