忍者ブログ

Memeplexes

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

C#でOpenCL入門 (OpenCLNet版) バッファ

バッファ

こちらも合わせてお読みください。

前回までは初期化作業が中心でした。
計算らしきものはしませんでしたね。

今回はGPU内にメモリ領域であるバッファを生成し、そこに書き込み、そこから読み込みをしてみます。
つまりGPUにデータを送って、また返してもらうのです。
GPGPUではそれに付け加えてバッファの値を使った計算もしなければいけませんが、それは次回以降にしましょう。


使用するメソッド

今回はバッファオブジェクトを生成し、そこから読み込み、そして破棄します。
そういった作業を行うためのメソッドをここでは紹介します。

バッファの生成には次のメソッドを使います:

public Mem CreateBuffer(MemFlags flags, long size);
public Mem CreateBuffer(MemFlags flags, long size, IntPtr pHost);
public Mem CreateBuffer(MemFlags flags, long size, void* pHost);

flagsは生成するバッファの性質を表したビットフィールドで、以下を組み合わせて使います。

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

sizeは生成されるバッファのサイズです(バイト単位)。

バッファの読み込み、書き込みには次のメソッドを使います:

public virtual void Read(CommandQueue cq, long srcOffset, float[] dstData, int dstStartIndex, int count);
public virtual void Write(CommandQueue cq, long dstOffset, float[] srcData, int srcStartIndex, int count);

cqは書き込み読み込みを行うコマンドキューです。なぜコマンドキューが必要なのかというと、「バッファから読み込む」というより、コマンドキューに「バッファから読み込め」というコマンドをほうりこむイメージだからだと思います。

srcOffsetはバッファオブジェクトから読み込みをはじめるオフセットです。
dstDataはデータの転送先の配列です。この配列にバッファの中身が書き込まれます。
dstStartIndexはデータの転送先の配列のインデックス。
countは読み込みを行う要素の個数です。

dstOffsetはバッファオブジェクトに書き込みをはじめるオフセットです。
srcDataはデータの転送元の配列です。この配列の中身がバッファに書き込まれます。
srcStartIndexはデータの転送元の配列のインデックス。
countは書き込みを行う要素の個数です。

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

では以上のメソッドを使ってプログラムを書いてみましょう。
GPUのメモリに一度書き込んで、そこから読み込みます。

using OpenCLNet;

class Program
{
    static void Main()
    {
        var platform = OpenCL.GetPlatform(0);
        var context = platform.CreateDefaultContext();
        
        var commandQueue = context.CreateCommandQueue(
            platform.QueryDevices(DeviceType.DEFAULT)[0]
            );
        float[] numbers = new float[]{1, 2, 3};
        var buffer = context.CreateBuffer(
            MemFlags.READ_WRITE,
            numbers.Length * sizeof(float)
            );
        buffer.Write(commandQueue, 0, numbers, 0, numbers.Length);
        float[] readBack = new float[3];
        buffer.Read(commandQueue, 0, readBack, 0, readBack.Length);

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

        buffer.Dispose();
        commandQueue.Dispose();
        context.Dispose();
    }
}

これを実行すると次のようになります:

1
2
3

このプログラムは普通のメモリにある3つの数字をGPUのメモリに書き込み、そしてまた普通のメモリに呼び戻し表示しています。

拍手[0回]

PR