忍者ブログ

Memeplexes

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

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

バッファ

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

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

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


使用するメソッド

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

バッファの生成には次のコンストラクタを使います:

public ComputeBuffer(ComputeContext context, ComputeMemoryFlags flags, long count);
public ComputeBuffer(ComputeContext context, ComputeMemoryFlags flags, T[] data);
public ComputeBuffer(ComputeContext context, ComputeMemoryFlags flags, long count, IntPtr dataPtr);

contextは生成するコンテキストです。

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

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

countは要素の数です。

dataはComputeMemoryFlags.CopyHostPointerなどを指定した時にバッファの初期化に使われるデータです。

dataPtrもdataと似たような意味でしょう。

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

ComputeCommandQueue.ReadFromBuffer<T>メソッドとComputeCommandQueue.WriteToBuffer<T>メソッドです。:

public void ReadFromBuffer<T>(ComputeBufferBase<T> source, ref T[] destination, bool blocking, IList<ComputeEventBase> events) where T : struct;
public void WriteToBuffer<T>(T[] source, ComputeBufferBase<T> destination, bool blocking, IList<ComputeEventBase> events) where T : struct;

意味の取りにくいものだけ解説します。

blockingはこのメソッドが、この作業が完了するまで戻らないことを表します。たとえばこれがfalseだと非同期モード…のはずなのですが、挙動の違いはこの引数を変えても確認できませんでした。

eventsはこのコマンドが実行される前に完了していなければいけないイベントでのコレクションです。もしeventsがnullでなかったら、新しい、このコマンドのイベントが、コレクションの最後に付け加えられます。

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

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

using Cloo;
using System.Linq;

class Program
{
    static void Main()
    {
        ComputePlatform platform = ComputePlatform.Platforms[0];
        ComputeDevice[] devices = platform
            .Devices
            .Where(d => d.Type == ComputeDeviceTypes.Gpu)
            .ToArray();
        ComputeContext context = new ComputeContext(
            devices,
            new ComputeContextPropertyList(platform),
            null, 
            System.IntPtr.Zero
            );
        ComputeCommandQueue commandQueue = new ComputeCommandQueue(
            context,
            devices[0], 
            ComputeCommandQueueFlags.None
            );

        float[] numbers = new float[] { 1, 2, 3 };
        ComputeBuffer<float> buffer = new ComputeBuffer<float>(
            context,
            ComputeMemoryFlags.CopyHostPointer,
            numbers
            );
        float[] dataFromGpu = new float[numbers.Length];
        commandQueue.ReadFromBuffer(buffer, ref dataFromGpu, true, null);

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

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

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

1
2
3

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

拍手[1回]

PR