忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。


C#でOpenCL入門 チュートリアルその6 プログラム

 前回はGPU内にバッファと呼ばれるメモリ領域を作り、そこでデータを読み書きしました。
しかしそれだけではいけません。
GPUで計算するというのが本来の目的です。
データを書き込んだり読み込んだりするだけではやりたい事の半分しか終わっていません。
バッファにセットしたデータを使ってなにか計算しなければいけないのです。

さてこの計算は一気にできるものではありません。
2つのオブジェクトが関係しているのです。
プログラムとカーネルがその2つですが、今回はプログラムだけを扱います。
ここでいうプログラムとはC#で書くようなプログラムのことではありません。
OpenCLでプログラムと言ったとき、それはGPUで動くプログラムのことです。
OpenCL Cという、C言語を拡張した言語で書いたプログラムです。

ちなみにカーネルとは、プログラム中に記述された関数のことです。
詳しくは次回以降に。


プログラム

OpenCLでGPUを動かすときには、OpenCL Cという独自の言語を使います。
DirectXで言うとHLSLのような言語です。
この言語によって、GPU特有の命令を実行するのです。
OpenCL Cで書いたプログラムはファイルとして持っておき、それを今回C#側から読み出し、コンパイルします。
(そして実行するのです。今回は実行まではいかず、コンパイルまでですが。)。

OpenCLで書いたプログラムは例えば次のようになります。
拡張子は.clです。

__kernel void myKernelFunction(__global float* numbers)
{
numbers[0] = 3;
numbers[1] = 4;
numbers[2] = 5;
}
このプログラムは実はあまりGPUの並列処理っぽくはない、シングルスレッドなプログラムです。
バッファに対して値をセットしています(シングルスレッドで)。
もちろん、書き方によってはGPUの性能をフルに生かした並列処理なプログラムを書くことは可能です(ちょっと複雑になるのでここには書きませんが)。

さてここで見慣れないキーワードが出てきました。
__kernel__globalです。
これらはC言語にはない、OpenCL C特有のキーワードです。

__kernelは関数が、GPUで実行されるということを意味します(GPUというか、正確にはCPUでも設定次第で実行できるのでデバイスというのが正しいのでしょうが、めんどくさいので以後GPUとします)。
__globalは変数がGPUのメインメモリに確保されるということを意味します。

では変数がGPUのメインメモリに確保されない場合があるのでしょうか?
そうです。
GPUと一口にいっても、変数がどこに確保されるかは全部で次の4つが考えられます。
以下の4つは、基本的に最初のものほど制限が緩く、最後の方に行くほど制限がきつく(しかし高速に)なっていきます。

__globalは変数がGPUのメインメモリにあり、自由に読み書き可能です。
ランダムアクセス可能なのでGPGPUの計算結果はここに格納することになります。
DirectXでいうとUnorderedAccessViewに相当すると言えるでしょうか。

__constantは変数がGPUのメインメモリにある・・・ところまでは__globalと同じなのですが、読み取り専用です。
読み取り専用にして何が嬉しいのかということですが、NVIDIAのGPUではたしかこちらのほうがパフォーマンスが高かったはずです。
読み取り専用なのでキャッシュを有効活用できるわけですね。

__localは、GPUの共有メモリです。
共有メモリとはなんんでしょう?
これが__globalや__constantと何が違うのかというと、これは複数あるということです。(複数あるという意味で、オブジェクト指向言語で言う非staticフィールドに似ています)
GPUには複数の演算ユニットがあり、__globalや__constantはそれら全てで共有されていました。
__localはそれらのうち一部で共有されるのです。

ですから、__localのついた変数は、実際には演算ユニットのグループの数だけ存在することになります。
グループの中で共有されるのです。
計算結果はここにあってもCPUからは読み込めません。(staticなメソッドから非staticなフィールドを直接読み込めないのと同じようなものです)
計算途中のデータをここに格納するといいでしょう。

__privateは、GPUのレジスタです。
これは一つの演算ユニットに特有の変数です。
これはデフォルトです。
なので、変数の前に何も付けなかった場合、その変数は__privateだとみなされます。
普通に関数内でint a;というような宣言をすると、aは__privateとなります。


プログラムの生成

 OpenCL Cで書いたプログラムは、C#中でプログラムオブジェクトになります。
プログラムオブジェクトを作るには、clCreateProgramWithSource関数を使います。
参考

cl_program clCreateProgramWithSource(
    cl_context context,
    cl_uint count,
    const char **strings,
    const size_t *lengths,
    cl_int *errcode_ret
)

contextは生成したプログラムを使うコンテキストです。
countはプログラムのソースコードの数です。stringsはこの個数の長さの配列です。
stringsはプログラムのソースコード(複数可)です。
lengthsはプログラムのソースコードの文字列の長さです。これにNULLを渡せば、stringsに入っているのはNULLで終わる文字列として解釈されます。
errcode_retはこの関数のエラーコードです。


プログラムの破棄

生成したプログラムオブジェクトは最後に破棄しなくてはいけません。
プログラムオブジェクトの破棄には、clReleaseProgram関数を使います。
参考

cl_int clReleaseProgram(cl_program program)

programは破棄するプログラムです。


プログラムのコンパイル

プログラムは、生成したばかりの状態ではまだ使えません。
一度使うGPU向けにコンパイルする必要があります。
コンパイルにはclBuildProgram関数を使います。
参考

cl_int clBuildProgram(
    cl_program program,
    cl_uint num_devices,
    const cl_device_id *device_list,
    const char *options,
    void (CL_CALLBACK *pfn_notify)(cl_program program, void *user_data),
    void *user_data
)

programはビルドするプログラムです。
num_devicesはdevice_listに入っているデバイスの数です。
device_listはprogramと関連付けるデバイスです。NULLでも構いません。NULLにすると、既に関連付けられている全てのデバイスに対してビルドします。
optionsはビルドオプションです。
pfn_notifyは非同期に呼び出すときのコールバック関数です。NULLだと、ビルドが終了してからこの関数は終了します。

pfn_notifyには次の2つの引数があります:
programはビルドしたプログラムです。
user_dataは、clBuildProgramの引数として渡したuser_dataです。

user_dataは、pfn_notifyの引数として渡される、ユーザーのためのデータです。NULLでも構いません。


プログラムを使うコード

MyProgram.cs
using System;

class MyProgram
{
    static void Main()
    {
        IntPtr device = getDevices(getPlatforms()[0], DeviceType.Default)[0];
        Context context = new Context(device);
        CommandQueue commandQueue = new CommandQueue(context, device);

        Program program = new Program(context, System.IO.File.ReadAllText("myKernelProgram.cl"));
        program.Build(device);

        Buffer buffer = Buffer.FromCopiedHostMemory(context, new float[] { 1, 2, 3 });

        float[] readBack = new float[3];
        commandQueue.ReadBuffer(buffer, readBack);

        foreach (var number in readBack)
        {
            Console.WriteLine(number);
        }
    }

    private static IntPtr[] getDevices(IntPtr platform, DeviceType deviceType)
    {
        int deviceCount;
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, 0, null, out deviceCount);
       
        IntPtr[] result = new IntPtr[deviceCount];
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, deviceCount, result, out deviceCount);
        return result;
    }


    private static IntPtr[] getPlatforms()
    {
        int platformCount;
        OpenCLFunctions.clGetPlatformIDs(0, null, out platformCount);

        IntPtr[] result = new IntPtr[platformCount];
        OpenCLFunctions.clGetPlatformIDs(platformCount, result, out platformCount);
        return result;
    }
}
 



OpenCLWrappers.cs
using System;
using System.Runtime.InteropServices;

class Context
{
    public IntPtr InternalPointer { get; private set; }

    public Context(params IntPtr[] devices)
    {
        int error;
        InternalPointer = OpenCLFunctions.clCreateContext(
            null,
            devices.Length,
            devices,
            null,
            IntPtr.Zero,
            out error
            );
    }

    ~Context()
    {
        OpenCLFunctions.clReleaseContext(InternalPointer);
    }
}

class CommandQueue
{
    public IntPtr InternalPointer { get; private set; }

    public CommandQueue(Context context, IntPtr device)
    {
        int error;
        InternalPointer = OpenCLFunctions.clCreateCommandQueue(
            context.InternalPointer,
            device,
            0,
            out error
            );
    }

    ~CommandQueue()
    {
        OpenCLFunctions.clReleaseCommandQueue(InternalPointer);
    }

    public void ReadBuffer<T>(Buffer buffer, T[] systemBuffer) where T : struct
    {
        GCHandle handle = GCHandle.Alloc(systemBuffer, GCHandleType.Pinned);

        OpenCLFunctions.clEnqueueReadBuffer(
            InternalPointer,
            buffer.InternalPointer,
            true,
            0,
            Math.Min(buffer.SizeInBytes, Marshal.SizeOf(typeof(T)) * systemBuffer.Length),
            handle.AddrOfPinnedObject(),
            0,
            IntPtr.Zero,
            IntPtr.Zero
            );

        handle.Free();
    }
}

class Buffer
{
    public IntPtr InternalPointer { get; private set; }
    public int SizeInBytes { get; private set; }

    private Buffer() { }

    ~Buffer()
    {
        OpenCLFunctions.clReleaseMemObject(InternalPointer);
    }

    public static Buffer FromCopiedHostMemory<T>(Context context, T[] initialData) where T : struct
    {
        Buffer result = new Buffer();
        result.SizeInBytes = Marshal.SizeOf(typeof(T)) * initialData.Length;

        int errorCode;
        GCHandle handle = GCHandle.Alloc(initialData, GCHandleType.Pinned);

        result.InternalPointer = OpenCLFunctions.clCreateBuffer(
            context.InternalPointer,
            MemoryFlags.CopyHostMemory,
            result.SizeInBytes,
            handle.AddrOfPinnedObject(),
            out errorCode
            );

        handle.Free();
        return result;
    }
}

class Program
{
    public IntPtr InternalPointer { get; private set; }

    public Program(Context context, params string[] sources)
    {
        int errorCode;

        InternalPointer = OpenCLFunctions.clCreateProgramWithSource(
            context.InternalPointer,
            sources.Length,
            sources,
            null,
            out errorCode
            );
    }

    ~Program()
    {
        OpenCLFunctions.clReleaseProgram(InternalPointer);
    }

    public void Build(params IntPtr[] devices)
    {
        OpenCLFunctions.clBuildProgram(
            InternalPointer,
            devices.Length,
            devices,
            null,
            null,
            IntPtr.Zero
            );
    }
}


OepnCLFunctions.cs
using System;
using System.Runtime.InteropServices;

static class OpenCLFunctions
{
    [DllImport("OpenCL.dll")]
    public static extern int clGetPlatformIDs(int entryCount, IntPtr[] platforms, out int platformCount);

    [DllImport("OpenCL.dll")]
    public static extern int clGetDeviceIDs(
        IntPtr platform,
        DeviceType deviceType,
        int entryCount,
        IntPtr[] devices,
        out int deviceCount
        );

    [DllImport("OpenCL.dll")]
    public static extern IntPtr clCreateContext(
        IntPtr[] properties, 
        int deviceCount,
        IntPtr[] devices,
        NotifyContextCreated pfnNotify,
        IntPtr userData,
        out int errorCode
        );

    [DllImport("OpenCL.dll")]
    public static extern int clReleaseContext(IntPtr context);

    [DllImport("OpenCL.dll")]
    public static extern IntPtr clCreateCommandQueue(
        IntPtr context,
        IntPtr device,
        long properties,
        out int errorCodeReturn
        );

    [DllImport("OpenCL.dll")]
    public static extern int clReleaseCommandQueue(IntPtr commandQueue);

    [DllImport("OpenCL.dll")]
    public static extern IntPtr clCreateBuffer(
        IntPtr context,
        MemoryFlags allocationAndUsage,
        int sizeInBytes,
        IntPtr hostPtr,//
        out int errorCodeReturn
        );

    [DllImport("OpenCL.dll")]
    public static extern int clReleaseMemObject(IntPtr memoryObject);

    [DllImport("OpenCL.dll")]
    public static extern int clEnqueueReadBuffer(
        IntPtr commandQueue,
        IntPtr buffer,
        bool isBlocking,
        int offset,
        int sizeInBytes,
        IntPtr result,
        int numberOfEventsInWaitList,
        IntPtr eventWaitList,
        IntPtr eventObjectOut
        );

    [DllImport("OpenCL.dll")]
    public static extern IntPtr clCreateProgramWithSource(
        IntPtr context,
        int count,
        string[] programSources, 
        int[] sourceLengths, 
        out int errorCode
        );

    [DllImport("OpenCL.dll")]
    public static extern int clBuildProgram(
        IntPtr program,
        int deviceCount, 
        IntPtr[] deviceList,
        string buildOptions,
        NotifyProgramBuilt notify,
        IntPtr userData
        );

    [DllImport("OpenCL.dll")]
    public static extern int clReleaseProgram(IntPtr program);
}

delegate void NotifyContextCreated(string errorInfo, IntPtr privateInfoSize, int cb, IntPtr userData);
delegate void NotifyProgramBuilt(IntPtr program, IntPtr userData);

enum DeviceType : long
{
    Default = (1 << 0),
    Cpu = (1 << 1),
    Gpu = (1 << 2),
    Accelerator = (1 << 3),
    All = 0xFFFFFFFF
}

enum MemoryFlags : long
{
    ReadWrite = (1 << 0),
    WriteOnly = (1 << 1),
    ReadOnly = (1 << 2),
    UseHostMemory = (1 << 3),
    HostAccessible = (1 << 4),
    CopyHostMemory = (1 << 5)
}



myKernelProgram.cl
__kernel void myKernelFunction(__global float* numbers)
{
    numbers[0] = 3;
numbers[1] = 4;
numbers[2] = 5;
}


このプログラムは、OpenCLプログラムのソースコードをロードし、コンパイルし、終了します。
残念ながら出力になにか面白い結果を表示したりはしません。





拍手[0回]

PR

C#でOpenCL入門 チュートリアルその5 バッファ

 前回までは初期化作業が中心でした。
GPUに対して命令するオブジェクトの生成をしたのです。

今回はGPU内にメモリ領域、バッファを生成し、そこに書き込みと読み込みをしてみます。
(GPGPUではそれに付け加えてバッファの値を使った計算もしなければいけませんが、それは次回以降で)


バッファの生成

バッファの生成にはclCreateBuffer関数を使います。
この関数は、デバイス(GPU)内にメモリの領域を確保します。
CPU側で言うとnew[]のようなものですね。
参考

cl_mem clCreateBuffer(
    cl_context context,
    cl_mem_flags flags,
    size_t size,
    void *host_ptr,
    cl_int *errcode_ret
)

contextはこのバッファを作るGPUに関連付けられたコンテキスト。
flagsは生成するバッファの性質を表したビットフィールドで、以下の値を組み合わせて使います。

cl_mem_flags 解説
CL_MEM_READ_WRITE 1 << 0 メモリがGPUによって読み込まれもするし書きこまれもすることを表します。これがデフォルトです。
CL_MEM_WRITE_ONLY 1 << 1 メモリがGPUによって書きこまれはするけれど読み込まれることはないということを表します。これを指定してデータを読み込もうとしたらどうなるかは未定義です。
CL_MEM_READ_ONLY 1 << 2 メモリが読み込み専用であることを表します。これを指定してデータを書きこもうとしたらどうなるかは未定義です。
CL_MEM_USE_HOST_PTR 1 << 3 このフラグはhost_ptrがNULL出ないときに限り有効です。もしこの値が指定されたら、アプリケーションはOpenCLの実装に対してhost_ptrで指定されたメモリをこのメモリオブジェクト(バッファ)のストレージとして使用して欲しいということを意味します。
CL_MEM_ALLOC_HOST_PTR 1 << 4 CPUからアクセス可能なメモリとしてメモリを生成してほしいことを表します。
この値とCL_MEM_USE_HOST_PTRは同時に指定できません。
CL_MEM_COPY_HOST_PTR 1 << 5 このフラグはhost_ptrがNULL出ないときに限って有効です。この値が指定されたら、host_ptrに書かれたデータをコピーしてメモリがGPUに確保されます。
この値とCL_MEM_USE_HOST_PTRは同時に指定できません。
この値はCL_MEM_ALLOC_HOST_PTRと一緒に使うことができます。

sizeは確保されるバッファーのサイズです(バイト単位)。
host_ptrはバッファの初期データです。ここで確保されているデータの大きさは、sizeと同じか、それよりも大きくなければいけません。
errcode_retはこの関数のエラーコードです。NULLでも構いません。

戻り値は生成されたバッファです。
型はcl_memとあります(cl_bufferというような名前ではありません!)。
これはバッファをテクスチャなど他のメモリオブジェクトと同じように扱うための型です。
clCreateImage2D()という2次元イメージを作る関数があるのですが、その戻り値もcl_memなのです。


バッファの破棄

使い終わったバッファは最後に破棄しなくてはいけません。
バッファの破棄にはclReleaseMemObject()という関数を使います。
名前に注意してください。
clReleaseBufferではありません。
この関数はデバイスが確保したメモリ一般を解放する関数なのです。
参考

cl_int clReleaseMemObject(cl_mem memobj)

memobjは破棄するメモリオブジェクトです(バッファやイメージ2Dなど)。


バッファからの読み込み

バッファへデータをセットするは初期化時に行えます。
後からデータを読み込むにはclEnqueueReadBuffer関数を使います。
この関数はバッファだけでなくコマンドキューも必要とします。
バッファから読み込むというより、コマンドキューに「バッファから読み込め」というコマンドを放り込むイメージです。
参考

cl_int clEnqueueReadBuffer(
    cl_command_queue command_queue,
    cl_mem buffer,
    cl_bool blocking_read,
    size_t offset,
    size_t cb,
    void *ptr,
    cl_uint num_events_in_wait_list,
    const cl_event *event_wait_list,
    cl_event *event
)

command_queueは読み込みを行うコマンドキュー。
bufferは読み込みを行うバッファ。
blocking_readはこの関数がブロックするかどうかを指定します。TRUEなら、読み込みが完了するまでこの関数は終わりません。FALSEなら、この関数は非同期に実行します。その場合ptrに入っているデータは読み込まれたものだとは限りません。読み込みが完了したかどうかを確かめるにはeventで帰ってくるイベントオブジェクトを使います。
offsetはバッファオブジェクトから読み込みを始めるオフセットです(バイト単位)。
cbは読み込むサイズです(バイト単位)。
ptrは読み込み先のメモリです。
event_wait_listnum_events_in_wait_listはこの命令を実行する前に行わなければいけない命令のリストです。NULLでも構いません。
eventはこの命令のイベントを返します(イベントは、ほかの命令が実行されるタイミングの調節に使えます)。



バッファを初期化してその内容を読み取る

では以上の3つの関数を使ったプログラムを書いてみましょう。

Program.cs
using System;

class Program
{
    static void Main()
    {
        IntPtr device = getDevices(getPlatforms()[0], DeviceType.Default)[0];
        Context context = new Context(device);
        CommandQueue commandQueue = new CommandQueue(context, device);

        Buffer buffer = Buffer.FromCopiedHostMemory(context, new float[] { 1, 2, 3 });
        
        float[] readBack = new float[3];
        commandQueue.ReadBuffer(buffer, readBack);

        foreach (var number in readBack)
        {
            Console.WriteLine(number);
        }
    }

    private static IntPtr[] getDevices(IntPtr platform, DeviceType deviceType)
    {
        int deviceCount;
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, 0, null, out deviceCount);
       
        IntPtr[] result = new IntPtr[deviceCount];
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, deviceCount, result, out deviceCount);
        return result;
    }


    private static IntPtr[] getPlatforms()
    {
        int platformCount;
        OpenCLFunctions.clGetPlatformIDs(0, null, out platformCount);

        IntPtr[] result = new IntPtr[platformCount];
        OpenCLFunctions.clGetPlatformIDs(platformCount, result, out platformCount);
        return result;
    }
}
 


OpenCLWrappers.cs
using System;
using System.Runtime.InteropServices;

class Context
{
    public IntPtr InternalPointer { get; private set; }

    public Context(params IntPtr[] devices)
    {
        int error;
        InternalPointer = OpenCLFunctions.clCreateContext(
            null,
            devices.Length,
            devices,
            null,
            IntPtr.Zero,
            out error
            );
    }

    ~Context()
    {
        OpenCLFunctions.clReleaseContext(InternalPointer);
    }
}

class CommandQueue
{
    public IntPtr InternalPointer { get; private set; }

    public CommandQueue(Context context, IntPtr device)
    {
        int error;
        InternalPointer = OpenCLFunctions.clCreateCommandQueue(
            context.InternalPointer,
            device,
            0,
            out error
            );
    }

    ~CommandQueue()
    {
        OpenCLFunctions.clReleaseCommandQueue(InternalPointer);
    }

    public void ReadBuffer<T>(Buffer buffer, T[] systemBuffer) where T : struct
    {
        GCHandle handle = GCHandle.Alloc(systemBuffer, GCHandleType.Pinned);

        OpenCLFunctions.clEnqueueReadBuffer(
            InternalPointer,
            buffer.InternalPointer,
            true,
            0,
            Math.Min(buffer.SizeInBytes, Marshal.SizeOf(typeof(T)) * systemBuffer.Length),
            handle.AddrOfPinnedObject(),
            0,
            IntPtr.Zero,
            IntPtr.Zero
            );

        handle.Free();
    }
}

class Buffer
{
    public IntPtr InternalPointer { get; private set; }
    public int SizeInBytes { get; private set; }

    private Buffer() { }

    ~Buffer()
    {
        OpenCLFunctions.clReleaseMemObject(InternalPointer);
    }

    public static Buffer FromCopiedHostMemory<T>(Context context, T[] initialData) where T : struct
    {
        Buffer result = new Buffer();
        result.SizeInBytes = Marshal.SizeOf(typeof(T)) * initialData.Length;

        int errorCode;
        GCHandle handle = GCHandle.Alloc(initialData, GCHandleType.Pinned);

        result.InternalPointer = OpenCLFunctions.clCreateBuffer(
            context.InternalPointer,
            MemoryFlags.CopyHostMemory,
            result.SizeInBytes,
            handle.AddrOfPinnedObject(),
            out errorCode
            );

        handle.Free();
        return result;
    }
}


OpenCLFunctions.cs
using System;
using System.Runtime.InteropServices;

static class OpenCLFunctions
{
    [DllImport("OpenCL.dll")]
    public static extern int clGetPlatformIDs(int entryCount, IntPtr[] platforms, out int platformCount);

    [DllImport("OpenCL.dll")]
    public static extern int clGetDeviceIDs(
        IntPtr platform,
        DeviceType deviceType,
        int entryCount,
        IntPtr[] devices,
        out int deviceCount
        );

    [DllImport("OpenCL.dll")]
    public static extern IntPtr clCreateContext(
        IntPtr[] properties, 
        int deviceCount,
        IntPtr[] devices,
        NotifyCallback pfnNotify,
        IntPtr userData,
        out int errorCode
        );

    [DllImport("OpenCL.dll")]
    public static extern int clReleaseContext(IntPtr context);

    [DllImport("OpenCL.dll")]
    public static extern IntPtr clCreateCommandQueue(
        IntPtr context,
        IntPtr device,
        long properties,
        out int errorCodeReturn
        );

    [DllImport("OpenCL.dll")]
    public static extern int clReleaseCommandQueue(IntPtr commandQueue);

    [DllImport("OpenCL.dll")]
    public static extern IntPtr clCreateBuffer(
        IntPtr context,
        MemoryFlags allocationAndUsage,
        int sizeInBytes,
        IntPtr hostPtr,
        out int errorCodeReturn
        );

    [DllImport("OpenCL.dll")]
    public static extern int clReleaseMemObject(IntPtr memoryObject);

    [DllImport("OpenCL.dll")]
    public static extern int clEnqueueReadBuffer(
        IntPtr commandQueue,
        IntPtr buffer,
        bool isBlocking,
        int offset,
        int sizeInBytes,
        IntPtr result,
        int numberOfEventsInWaitList,
        IntPtr eventWaitList,
        IntPtr eventObjectOut
        );
}

delegate void NotifyCallback(string errorInfo, IntPtr privateInfoSize, int cb, IntPtr userData);

enum DeviceType : long
{
    Default = (1 << 0),
    Cpu = (1 << 1),
    Gpu = (1 << 2),
    Accelerator = (1 << 3),
    All = 0xFFFFFFFF
}

enum MemoryFlags : long
{
    ReadWrite = (1 << 0),
    WriteOnly = (1 << 1),
    ReadOnly = (1 << 2),
    UseHostMemory = (1 << 3),
    HostAccessible = (1 << 4),
    CopyHostMemory = (1 << 5)
}

このプログラムはGPU内に{1, 2, 3}という値を持ったバッファを作り、それを読み込み表示しています。
実行結果はこうなります。
 
1
2
3

















拍手[0回]


C#でOpenCL入門 チュートリアルその4 コマンドキュー

 
今回はコマンドキューの生成と破棄を行います。
残念ながら前回に引き続き今回もサンプルプログラムは画面に何も出力しません。

コマンドキューとは、GPUに対する操作をカプセル化したオブジェクトです。
GPU内のバッファにデータを書き込んだり読み込んだり、データに対して計算を行ったり。
このような操作のほとんどすべてが非同期の関数です。
(今回は生成と破棄の2つしかしませんが)



コマンドキューの生成

コマンドキューの生成にはclCreateCommandQueue関数を使います。
この関数はコンテキストとデバイスから、コマンドキューを生成します。
参考

cl_command_queue clCreateCommandQueue(
    cl_context context,
    cl_device_id device,
    cl_command_queue_properties properties,
    cl_int *errcode_ret
)

contextはコマンドキューに結び付けられるコンテキスト。
deviceはコマンドキューに結び付けられるデバイス。
propertiesは生成するコマンドキューの属性のリストです。この値はビットフィールドで、以下の2つを組み合わせます。

コマンドキュープロパティ 解説
CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE コマンドキュー中のコマンド実行をout-of-order(並列実行)にします。そうでない場合はin-orderで、これはコマンド実行の順番を入れ替え無いモードです。
CL_QUEUE_PROFILING_ENABLE コマンドキュー内のcommandのプロファイリングを有効にします。詳細はclGetEventProfilingInfo関数を見てください。

errcode_retはこの関数のエラーコードです。NULLでも構いません。

エラーコードには以下の値があります。
名前 解説
CL_SUCCESS 0 関数は成功しました。
CL_INVALID_CONTEXT -34 contextが無効です。
CL_INVALID_DEVICE -33 deviceが無効です。あるいはdeviceがcontextに結び付けられていません。
CL_INVALID_VALUE -30 propertiesが無効です。
CL_INVALID_QUEUE_PROPERTIES -35 propertiesは有効ですが、デバイスにサポートされていません。
CL_OUT_OF_RESOURCES -5 デバイスがリソースを確保するのに失敗しました。
CL_OUT_OF_HOST_MEMORY -6 メモリが足りません。



コマンドキューの破棄

生成したコマンドキューは、使い終わったら最後に破棄しなければいけません。
破棄するにはclReleaseCommanndQueue関数を使います。
参考

例によってこれも正確には破棄する関数ではなく参照カウントを減らす関数です。
参照カウントが0になったときコマンドキューは破棄されます。
C#から使う場合は実質的に破棄する関数と同じですね。

cl_int clReleaseCommandQueue(
    cl_command_queue command_queue
)

command_queueは破棄するコマンドキューです。

名前 解説
CL_SUCCESS 0 関数は成功しました。
CL_INVALID_COMMAND_QUEUE -36 command_queueが無効です。
CL_OUT_OF_RESOURCES -5 デバイスがリソースを確保できませんでした。
CL_OUT_OF_HOST_MEMORY -6 メモリが足りません。


コマンドキューの生成と破棄

以上の2つの関数を使ったプログラムは次のようになります。

Program.cs
using System;
 
class Program
{
    static void Main()
    {
        IntPtr device = getDevices(getPlatforms()[0], DeviceType.Default)[0];
        Context context = new Context(device);
        CommandQueue commandQueue = new CommandQueue(context, device);
    }
 
    private static IntPtr[] getDevices(IntPtr platform, DeviceType deviceType)
    {
        int deviceCount;
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, 0, null, out deviceCount);
       
        IntPtr[] result = new IntPtr[deviceCount];
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, deviceCount, result, out deviceCount);
        return result;
    }
 
 
    private static IntPtr[] getPlatforms()
    {
        int platformCount;
        OpenCLFunctions.clGetPlatformIDs(0, null, out platformCount);
 
        IntPtr[] result = new IntPtr[platformCount];
        OpenCLFunctions.clGetPlatformIDs(platformCount, result, out platformCount);
        return result;
    }
}

OpenCLWrappers.cs
using System;

class Context
{
    public IntPtr InternalPointer { get; private set; }

    public Context(params IntPtr[] devices)
    {
        int error;
        InternalPointer = OpenCLFunctions.clCreateContext(
            null,
            devices.Length,
            devices,
            null,
            IntPtr.Zero,
            out error
            );
    }

    ~Context()
    {
        OpenCLFunctions.clReleaseContext(InternalPointer);
    }
}

class CommandQueue
{
    public IntPtr InternalPointer { get; private set; }

    public CommandQueue(Context context, IntPtr device)
    {
        int error;
        InternalPointer = OpenCLFunctions.clCreateCommandQueue(
            context.InternalPointer,
            device,
            0,
            out error
            );
    }

    ~CommandQueue()
    {
        OpenCLFunctions.clReleaseCommandQueue(InternalPointer);
    }
}


OpenCLFunctions.cs
using System;
using System.Runtime.InteropServices;

static class OpenCLFunctions
{
    [DllImport("OpenCL.dll")]
    public static extern int clGetPlatformIDs(int entryCount, IntPtr[] platforms, out int platformCount);

    [DllImport("OpenCL.dll")]
    public static extern int clGetDeviceIDs(
        IntPtr platform,
        DeviceType deviceType,
        int entryCount,
        IntPtr[] devices,
        out int deviceCount
        );

    [DllImport("OpenCL.dll")]
    public static extern IntPtr clCreateContext(
        IntPtr[] properties, 
        int deviceCount,
        IntPtr[] devices,
        NotifyCallback pfnNotify,
        IntPtr userData,
        out int errorCode
        );

    [DllImport("OpenCL.dll")]
    public static extern int clReleaseContext(IntPtr context);

    [DllImport("OpenCL.dll")]
    public static extern IntPtr clCreateCommandQueue(
        IntPtr context,
        IntPtr device,
        long properties,
        out int errorCodeReturn
        );

    [DllImport("OpenCL.dll")]
    public static extern int clReleaseCommandQueue(IntPtr commandQueue);
}

delegate void NotifyCallback(string errorInfo, IntPtr privateInfoSize, int cb, IntPtr userData);

enum DeviceType:long
{
    Default = (1 << 0),
    Cpu = (1 << 1),
    Gpu = (1 << 2),
    Accelerator = (1 << 3),
    All = 0xFFFFFFFF
}


このプログラムはコンテキストとデバイスから、コマンドキューを生成します。
ここでは単に生成するだけで何もしません。
画面には何も文字が出力されないでしょう。

今後、このコマンドキューによって、デバイスに命令します。
たとえばデバイスのバッファに値を書き込め、バッファから値を読み出せ、値を使って並列計算せよ、といった具合です。















拍手[1回]


C#でOpenCL入門 チュートリアルその3 コンテキスト


 前回はGPUを司るデバイスを扱いました。
しかし実はOpenCLはデバイスではたいしたことはしません。

GPUで計算をするためにはGPU内に計算に使うデータを格納したバッファを用意しなければならないのですが、デバイスオブジェクトではそれは行わないのです。
その作業はコンテキストを通じて行います。

今回はコンテキストの生成と破棄をメモします。


コンテキストの生成

コンテキストは1つ以上のデバイスをまとめるオブジェクトです。
コマンドキューやメモリ、プログラムなどは全てこれを通して作ります。
(コマンドキュー?メモリって?プログラム?これらは後述します。)

コンテキストの生成にはclCreateContext関数を使います。
参考

cl_context clCreateContext(
    const cl_context_properties *properties,
    cl_uint num_devices,
    const cl_device_id *devices,
    (void CL_CALLBACK *pfn_notify)(
        const char *errinfo,
        const void *private_info,
        size_t cb,
        void *user_data
    ),
    void *user_data,
    cl_int *errcode_ret
)

propertiesはコンテキストを生成するときの情報です。プロパティ名と値のリストからなり、0で終了します。この値はNULLでもよく、その場合の振る舞いは実装が定義します。この引数の中には次のような値が入ります。

cl_context_properties プロパティ値 解説
 CL_CONTEXT_PLATFORM  0x1084 cl_platform_id  使うプラットフォームです。
 CL_CONTEXT_D3D10_DEVICE_KHR  0x4014 ID3D10Device*  cl_khr_d3d10_sharing拡張が有効なときに使えます。Direct3D10との相互運用が可能です。デフォルトではNULLです。
CL_GL_CONTEXT_KHR  0x2008    cl_khr_gl_sharing拡張が有効なときに使えます。
 
 
 
 
CL_EGL_DISPLAY_KHR  0x2009  
CL_GLX_DISPLAY_KHR  0x200A  
CL_WGL_HDC_KHR  0x200B  
CL_CGL_SHAREGROUP_KHR  0x200C  

num_devicesはdevices引数の中に入っているデバイスの個数です。
devicesはこのコンテキストを生成するのに使うデバイスです。コンテキストはこの中に入っているデバイスを使うことになります。重複して同じデバイスが入っている場合は、片方が無視されます。
pfn_notifyはエラー情報を受け取るコールバック関数です。この関数は非同期に呼び出されます。もしこの引数がNULLなら、コールバック関数は登録されません。

このコールバック関数には4つの引数があります。

errinfoはエラー文字列へのポインタです。
private_infocbはデバッグ時に役に立つ情報です。OpenCLの実装がこの値をセットします。
user_dataはユーザー定義の情報です。clCreateContext()のuser_data引数の中身と同じです。

user_dataは、コールバック関数pfn_notifyの引数として渡されるデータです。NULLでも構いません。
errcode_retはエラーコードです。NULLでも構いません。


コンテキストの破棄

生成したコンテキストは使い終わったら破棄しなくてはいけません。
破棄にはclReleaseContext()関数を使います。

正確に言えばこれは破棄のための関数ではなく、参照カウントを減らす関数です。
参照カウントが0になった時そのオブジェクトは破棄されるのです。
が、C#から使うのだったら破棄のための関数も同然ですね。

cl_int clReleaseContext(cl_context context);

contextは破棄するコンテキストです。

戻り値
名前 解説
CL_SUCCESS 0 関数は成功しました。
CL_INVALID_CONTEXT -34 contextが無効な値です。
CL_OUT_OF_RESOURCES -5 デバイスがリソースを作れませんでした。
CL_OUT_OF_HOST_MEMORY -6 メモリが足りません。



コンテキストオブジェクトの生成と破棄

ではコンテキストを作るプログラムを書いてみましょう。
今回はコンテキスト作るだけで何もしません。
真っ暗な画面が一瞬でてそのまま終了するプログラムです。

そういう意味ではサンプルプログラムの意味が無いのですが、ご容赦ください…

Program.cs
using System;

class Program
{
    static void Main()
    {
        Context context = new Context(getDevices(getPlatforms()[0], DeviceType.Default));
        
    }

    private static IntPtr[] getDevices(IntPtr platform, DeviceType deviceType)
    {
        int deviceCount;
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, 0, null, out deviceCount);
       
        IntPtr[] result = new IntPtr[deviceCount];
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, deviceCount, result, out deviceCount);
        return result;
    }


    private static IntPtr[] getPlatforms()
    {
        int platformCount;
        OpenCLFunctions.clGetPlatformIDs(0, null, out platformCount);

        IntPtr[] result = new IntPtr[platformCount];
        OpenCLFunctions.clGetPlatformIDs(platformCount, result, out platformCount);
        return result;
    }
}
 

OpenCLWrappers.cs
using System;
 
class Context
{
    public IntPtr InternalPointer { get; private set; }
 
    public Context(IntPtr[] devices)
    {
        int error;
        InternalPointer = OpenCLFunctions.clCreateContext(
            null,
            devices.Length,
            devices,
            null,
            IntPtr.Zero,
            out error
            );
    }
 
    ~Context()
    {
        OpenCLFunctions.clReleaseContext(InternalPointer);
    }
}

OpenCLFunctions.cs
using System;
using System.Runtime.InteropServices;

static class OpenCLFunctions
{
    [DllImport("OpenCL.dll")]
    public static extern int clGetPlatformIDs(int entryCount, IntPtr[] platforms, out int platformCount);

    [DllImport("OpenCL.dll")]
    public static extern int clGetDeviceIDs(
        IntPtr platform,
        DeviceType deviceType,
        int entryCount,
        IntPtr[] devices,
        out int deviceCount
        );

    [DllImport("OpenCL.dll")]
    public static extern IntPtr clCreateContext(
        IntPtr[] properties, 
        int deviceCount,
        IntPtr[] devices,
        NotifyCallback pfnNotify,
        IntPtr userData,
        out int errorCode
        );

    [DllImport("OpenCL.dll")]
    public static extern int clReleaseContext(IntPtr context);
}

delegate void NotifyCallback(string errorInfo, IntPtr privateInfoSize, int cb, IntPtr userData);

enum DeviceType:long
{
    Default = (1 << 0),
    Cpu = (1 << 1),
    Gpu = (1 << 2),
    Accelerator = (1 << 3),
    All = 0xFFFFFFFF
}


このプログラムはデバイスからコンテキストを作り、そのまま破棄します。
画面には何も出力しません。

















拍手[1回]


C#でOpenCL入門 チュートリアルその2 デバイス

 OpenCLでは並列計算をしてくれるハードウェアのことをデバイスといいます。
例外はあるもののこれはまずGPUのことだと考えて間違い有りません(実はCPUでもいいのですが)。
デバイス=GPUです。

GPUで並列計算するためには、下準備としてPCに付いているGPUを列挙する必要があります。
今回はGPU、OpenCLの用語で言うとデバイスを列挙、情報を表示する方法をメモします。


プラットフォームからデバイスを取得

前回出てきたプラットフォームは、GPUのメーカーを表すようなものでした。
今回扱うデバイスは、そのメーカーの作ったGPUのことです。
プラットフォームから、デバイスを取得できるのです(生産者から製品を)。

それにはclGetDeviceIDs関数を使います。
参考

cl_int clGetDeviceIDs(
    cl_platform_id platform,
    cl_device_type device_type,
    cl_uint num_entries,
    cl_device_id *devices,
    cl_uint *num_devices
)

platformは取得するデバイスのプラットフォームです。
device_typeは取得するデバイスの種類。具体的には以下の5つから選びます。

cl_device_type 解説
CL_DEVICE_TYPE_CPU (1 << 1) CPUです。
CL_DEVICE_TYPE_GPU (1 << 2) GPUです。
CL_DEVICE_TYPE_ACCELERATOR (1 << 3) IBM CELL(PS3に使われている)などです。
CL_DEVICE_TYPE_DEFAULT (1 << 0) デフォルトのデバイスです。
CL_DEVICE_TYPE_ALL 0xFFFFFFFF 使用可能な全てのOpenCLデバイスです。


num_entriesは結果を格納する引数devicesの配列の長さです。もしdevicesがNULLでなかったら、この引数は0より大きくなければいけません。
devicesはこの関数の結果を格納します。もしこの値がNULLなら、この引数は無視されます。
num_devicesは使用可能なデバイスの数を返します。この値がNULLならば、この引数は無視されます。

戻り値
名前 解説
CL_SUCCESS 0 関数が成功しました。
CL_INVALID_PLATFORM -32 platformが無効な値です。
CL_INVALID_DEVICE_TYPE -31 device_typeが無効な値です。
CL_INVALID_VALUE -30 num_entriesが0なのにdevicesがNULLではありません。あるいは、num_devicesとdevicesが両方共NULLです。
CL_DEVICE_NOT_FOUND -1 device_typeで指定されたタイプのデバイスが存在しません。
CL_OUT_OF_RESOURCES -5 デバイスでリソースを確保するのに失敗しました。
CL_OUT_OF_HOST_MEMORY -6 メモリが足りません。


デバイスの列挙

では早速clGetDeviceIDs()を使ってみましょう。
システムにインストールされたGPU(正確にはデバイス)を列挙します。

Program.cs
using System;

class Program
{
    static void Main()
    {
        foreach (var platform in getPlatforms())
        {
            foreach (var device in getDevices(platform, DeviceType.Default))
            {
                Console.WriteLine(device);
            }
        }
    }

    private static IntPtr[] getDevices(IntPtr platform, DeviceType deviceType)
    {
        int deviceCount;
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, 0, null, out deviceCount);
       
        IntPtr[] result = new IntPtr[deviceCount];
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, deviceCount, result, out deviceCount);
        return result;
    }

    private static IntPtr[] getPlatforms()
    {
        int platformCount;
        OpenCLFunctions.clGetPlatformIDs(0, null, out platformCount);

        IntPtr[] result = new IntPtr[platformCount];
        OpenCLFunctions.clGetPlatformIDs(platformCount, result, out platformCount);
        return result;
    }
}
 

OpenCLFunctions.cs
using System;
using System.Runtime.InteropServices;

static class OpenCLFunctions
{
    [DllImport("OpenCL.dll")]
    public static extern int clGetPlatformIDs(int entryCount, IntPtr[] platforms, out int platformCount);

    [DllImport("OpenCL.dll")]
    public static extern int clGetDeviceIDs(
        IntPtr platform,
        DeviceType deviceType,
        int entryCount,
        IntPtr[] devices,
        out int deviceCount
        );
}

enum DeviceType:long
{
    Default = (1 << 0),
    Cpu = (1 << 1),
    Gpu = (1 << 2),
    Accelerator = (1 << 3),
    All = 0xFFFFFFFF
}
 

実行結果は、私の環境ではこうなりました:

82646520
108450576

うーん意味不明ですね。
これが意味するのはシステムにGPUが2つ付いているということです。
他の環境では数字は一つしか表示されないかもしれません。

しかし数字が表示されるだけというのはなんとも無機質です。
GPUの名前が表示されたらいいのですが…


デバイスの情報

デバイスのIDからその名前や各種情報を取得する関数があります。
それがclGetDeviceInfo()関数です。
参考

cl_int clGetDeviceInfo(
    cl_device_id device,
    cl_device_info param_name,
    size_t param_value_size,
    void *param_value,
    size_t *param_value_size_ret
)

deviceは情報を取得するデバイスです。clGetDeviceIDs関数によって返された値を使います。
param_nameは情報のタイプを表す列挙型です。膨大な種類があります。詳細は以下の表に。
param_value_sizeはparam_valueにセットするメモリのサイズです。
param_valueはこの関数の結果を格納するメモリです。
param_value_size_retは情報の実際のサイズです。NULLならば無視されます。

cl_device_info  値  
 CL_DEVICE_ADDRESS_BITS cl_uint  0x100D  
 CL_DEVICE_AVAILABLE cl_bool  0x1027  
 CL_DEVICE_COMPILER_AVAILABLE cl_bool  0x1028  
 CL_DEVICE_DOUBLE_FP_CONFIG cl_device_fp_config  0x1032  
 CL_DEVICE_ENDIAN_LITTLE cl_bool  0x1026  
 CL_DEVICE_ERROR_CORRECTION_SUPPORT cl_bool  0x1024  
 CL_DEVICE_EXECUTION_CAPABILITIES cl_device_exec_capabilities  0x1029  
 CL_DEVICE_EXTENSIONS char[]  0x1030  
 CL_DEVICE_GLOBAL_MEM_CACHE_SIZE cl_ulong  0x101E  
 CL_DEVICE_GLOBAL_MEM_CACHE_TYPE cl_device_mem_cache_type  0x101C  
 CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE cl_uint  0x101D  
 CL_DEVICE_GLOBAL_MEM_SIZE cl_ulong  0x101F  
 CL_DEVICE_HALF_FP_CONFIG cl_device_fp_config  0x1033  
 CL_DEVICE_HOST_UNIFIED_MEMORY cl_bool  0x1035  
 CL_DEVICE_IMAGE_SUPPORT cl_bool  0x1016  
 CL_DEVICE_IMAGE2D_MAX_HEIGHT size_t  0x1012  
 CL_DEVICE_IMAGE2D_MAX_WIDTH size_t  0x1011  
 CL_DEVICE_IMAGE3D_MAX_DEPTH size_t  0x1015  
 CL_DEVICE_IMAGE3D_MAX_HEIGHT size_t  0x1014  
 CL_DEVICE_IMAGE3D_MAX_WIDTH size_t  0x1013  
 CL_DEVICE_LOCAL_MEM_SIZE cl_ulong  0x1023  
 CL_DEVICE_LOCAL_MEM_TYPE cl_device_local_mem_type  0x1022  
 CL_DEVICE_MAX_CLOCK_FREQUENCY cl_uint  0x100C  
 CL_DEVICE_MAX_COMPUTE_UNITS cl_uint  0x1002  
 CL_DEVICE_MAX_CONSTANT_ARGS cl_uint  0x1021  
 CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE cl_ulong  0x1020  
 CL_DEVICE_MAX_MEM_ALLOC_SIZE cl_ulong  0x1010  
 CL_DEVICE_MAX_PARAMETER_SIZE size_t  0x1017  
 CL_DEVICE_MAX_READ_IMAGE_ARGS cl_uint  0x100E  
 CL_DEVICE_MAX_SAMPLERS cl_uint  0x1018  
 CL_DEVICE_MAX_WORK_GROUP_SIZE size_t  0x1004  
 CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS cl_uint  0x1003  
 CL_DEVICE_MAX_WORK_ITEM_SIZES size_t[]  0x1005  
 CL_DEVICE_MAX_WRITE_IMAGE_ARGS cl_uint  0x100F  
 CL_DEVICE_MEM_BASE_ADDR_ALIGN cl_uint  0x1019  
 CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE cl_uint  0x101A  
 CL_DEVICE_NAME char[]  0x102B  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR cl_uint  0x1036  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT cl_uint  0x1037  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_INT cl_uint  0x1038  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG cl_uint  0x1039  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT cl_uint  0x103A  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE cl_uint  0x103B  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF cl_uint  0x103C  
 CL_DEVICE_OPENCL_C_VERSION char[]  0x103D  
 CL_DEVICE_PLATFORM  cl_platform_id  0x1031  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR  cl_uint  0x1006  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT  cl_uint  0x1007  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT  cl_uint  0x1008  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG  cl_uint  0x1009  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT  cl_uint  0x100A  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE  cl_uint  0x100B  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF cl_uint  0x1034  
 CL_DEVICE_PROFILE char[]   0x102E  
 CL_DEVICE_PROFILING_TIMER_RESOLUTION size_t   0x1025  
 CL_DEVICE_QUEUE_PROPERTIES cl_command_queue_properties   0x102A  
 CL_DEVICE_SINGLE_FP_CONFIG cl_device_fp_config   0x101B  
 CL_DEVICE_TYPE cl_device_type   0x1000  
 CL_DEVICE_VENDOR  char[]  0x102C  
 CL_DEVICE_VENDOR_ID  cl_uint  0x1001  
 CL_DEVICE_VERSION  char[]  0x102F  
 CL_DRIVER_VERSION  char[]  0x102D  

戻り値
名前 解説
CL_SUCCESS 0 関数は成功しました
CL_INVALID_DEVICE -33 deviceが無効な値です。
CL_INVALID_VALUE -30 param_nameが無効な値であるか、param_value_sizeが小さすぎ、param_valueがNULLではありません。
CL_OUT_OF_RESOURCES -5 デバイスでリソースを確保するのに失敗しました。
CL_OUT_OF_HOST_MEMORY -6 メモリが足りません。




デバイス名の列挙

先程はデバイスのIDを列挙しました。
ここではデバイスの名前を列挙します。

Program.cs
using System;

class Program
{
    static void Main()
    {
        foreach (var platform in getPlatforms())
        {
            foreach (var device in getDevices(platform, DeviceType.Default))
            {
                Console.WriteLine(getDeviceInfo(device, DeviceInfoString.Name));
            }
        }
    }

    private static string getDeviceInfo(IntPtr device, DeviceInfoString deviceInfoName)
    {
        int valueSize;
        OpenCLFunctions.clGetDeviceInfo(device, deviceInfoName, 0, null, out valueSize);
        System.Text.StringBuilder result = new System.Text.StringBuilder(valueSize);
        OpenCLFunctions.clGetDeviceInfo(device, deviceInfoName, valueSize, result, out valueSize);
        return result.ToString();
    }

    private static IntPtr[] getDevices(IntPtr platform, DeviceType deviceType)
    {
        int deviceCount;
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, 0, null, out deviceCount);
       
        IntPtr[] result = new IntPtr[deviceCount];
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, deviceCount, result, out deviceCount);
        return result;
    }


    private static IntPtr[] getPlatforms()
    {
        int platformCount;
        OpenCLFunctions.clGetPlatformIDs(0, null, out platformCount);

        IntPtr[] result = new IntPtr[platformCount];
        OpenCLFunctions.clGetPlatformIDs(platformCount, result, out platformCount);
        return result;
    }
}
 


OpenCLFunctions.cs
using System;
using System.Runtime.InteropServices;

static class OpenCLFunctions
{
    [DllImport("OpenCL.dll")]
    public static extern int clGetPlatformIDs(int entryCount, IntPtr[] platforms, out int platformCount);

    [DllImport("OpenCL.dll")]
    public static extern int clGetDeviceIDs(
        IntPtr platform,
        DeviceType deviceType,
        int entryCount,
        IntPtr[] devices,
        out int deviceCount
        );

    [DllImport("OpenCL.dll")]
    public static extern int clGetDeviceInfo(
        IntPtr device, 
        DeviceInfoString paramName, 
        int paramValueSize,
        System.Text.StringBuilder paramValue,
        out int paramValueSizeReturn
        );
    
}

enum DeviceType:long
{
    Default = (1 << 0),
    Cpu = (1 << 1),
    Gpu = (1 << 2),
    Accelerator = (1 << 3),
    All = 0xFFFFFFFF
}


enum DeviceInfoString
{
    Name = 0x102B,
    Vendor = 0x102C,
    DriverVersion = 0x102D,
    Profile = 0x102E,
    Version = 0x102F,
    Extensions = 0x1030
}


このプログラムはPCに付いているGPUの名前を列挙します。
環境によって実行結果は異なります。
私の環境ではこうなりました:
 
GeForce GTX 260
Juniper
 
GeForce GTX 260というのはNVIDIA社製のGPUの名前です。
JuniperというのはAMDのGPU、Radeon HD 5700シリーズの通称です。


拍手[2回]