OpenCL
OpenCL を用いたGPUおよびCPUでの並列計算。
■ 1. LUX.GPU.OpenCL
ライブラリ
TOpenCL
:TCLSystem のシングルトン
┃
TCLSystem
:システム
┗TCLPlatfos
:プラットフォームリスト
┗TCLPlatfo
:プラットフォーム
┣TCLExtenss
:拡張機能リスト
┣TCLDevices
:デバイスリスト
┃ ┗TCLDevice
:デバイス
┗TCLContexs
:コンテキストリスト
┗TCLContex
:コンテキスト
┣TCLQueuers
:コマンドキューリスト
┃ ┗TCLQueuer
:コマンドキュー
┣TCLArgumes
:実引数リスト
┃ ┣TCLBuffer
:バッファー
┃ ┣TCLImager
:イメージ
┃ ┗TCLSamplr
:サンプラー
┣TCLLibrars
:ライブラリリスト
┃ ┗TCLLibrar
:ライブラリ
┗TCLExecuts
:実行プログラムリスト
┗TCLExecut
:実行プログラム
┣TCLBuildrs
:ビルドリスト
┃ ┗TCLBuildr
:ビルド
┗TCLKernels
:カーネルリスト
┗TCLKernel
:カーネル
┗TCLParames
:仮引数リスト
┗TCLParame
:仮引数
■ 2. ライブラリの利用方法
TOpenCL
クラスは、TCLSystem
クラスのシングルトンです。
TCLSystem
クラスは、実行マシンにおけるすべての計算用デバイスを自動的に検出します。
⬤ 2.1. プラットフォーム
“プラットフォーム”オブジェクト (TCLPlatfo
) は、各デバイスベンダーが定義する環境を表します。
TCLSystem
クラスは、すべてのプラットフォームを自動的に検出し、Platfos[]
プロパティに列挙されます。
Object Pascal
TOpenCL.Platfos.Count :Integer // 全プラットフォームの数 TOpenCL.Platfos[*] :TCLPlatfo // 全プラットフォームの配列
特定のプラットフォームに関する情報は、TCLPlatfo
クラスのプロパティから取得できます。
Object Pascal
_Platfo := TOpenCL.Platfos[0]; // 特定プラットフォームの選択 _Platfo.Handle :T_cl_platform_id // ポインタ _Platfo.Profile :String // プロファイル _Platfo.Version :String // バージョン _Platfo.Name :String // 名前 _Platfo.Vendor :String // ベンダー名 _Platfo.Extenss.Count :Integer // 拡張機能の数 _Platfo.Extenss[*] :String // 拡張機能の配列
⬤ 2.2. デバイス
デバイスオブジェクト (TCLDevice
) は、物理的なGPUやCPUを表します。
TCLPlatfo
クラスは、特定のプラットフォーム内のすべてのデバイスを自動的に検出し、Devices[]
プロパティに列挙されます。
Object Pascal
_Platfo.Devices.Count :Integer // デバイスの数 _Platfo.Devices[*] :TCLDevice // デバイスの配列
特定のデバイスに関する情報は、TCLDevice
クラスのプロパティから取得できます。
Object Pascal
_Device := _Platfo.Devices[0]; // 特定デバイスの選択 _Device.Handle :T_cl_device_id // ポインタ _Device.DEVICE_TYPE :T_cl_device_type // タイプ _Device.DEVICE_VENDOR_ID :T_cl_uint // ベンダーID _Device.DEVICE_NAME :String // 名前 _Device.DEVICE_VENDOR :String // ベンダー _Device.DRIVER_VERSION :String // ドライバのバージョン _Device.DEVICE_PROFILE :String // プロファイル _Device.DEVICE_VERSION :String // バージョン
⬤ 2.3. コンテキスト
“コンテキスト” (TCLContex
) は、関連するデータやプログラムを管理および保持します。
TCLContex
クラスは、TCLPlatfo
クラスを引数として生成できます。
Object Pascal
_Contex := TCLContex.Create( _Platfo );
生成されたTCLContex
クラスは、TCLPlatfo
クラスのContexs[]
プロパティへ登録されます。
Object Pascal
_Platfo.Contexs.Count :Integer // コンテキストの数 _Platfo.Contexs[*] :TCLContex // コンテキストの配列
⬤ 2.4. コマンドキュー
“コマンドキュー”オブジェクト (TCLQueuer
) は、デバイスに送られる命令を管理します。
つまり、コンテキストとデバイス間での命令のやり取りを管理します。
TCLQueuer
クラスは、TCLContex
クラスとTCLDevice
クラスを引数として生成できます。
Object Pascal
_Queuer := TCLQueuer.Create( _Contex, _Device ); {or} _Queuer := _Contex.Queuers[ _Device ];
生成されたTCLQueuer
クラスは、TCLContex
クラスのQueuers[]
プロパティへ登録されます。
Object Pascal
_Contex.Queuers.Count :Integer // コマンドキューの数 _Contex.Queuers[*] :TCLQueuer // コマンドキューの配列
なお、異なるプラットフォームのコンテキストとデバイスを接続するコマンドキューは生成できません。
Object Pascal
P0 := TOpenCL.Platfos[0]; P1 := TOpenCL.Platfos[1]; P2 := TOpenCL.Platfos[2]; D00 := P0.Devices[0]; D01 := P0.Devices[1]; D02 := P0.Devices[2]; D10 := P1.Devices[0]; D20 := P2.Devices[0]; C0 := TCLContex.Create( P0 ); C1 := TCLContex.Create( P1 ); C2 := TCLContex.Create( P2 ); Q00 := TCLQueuer.Create( C0, D00 ); // OK Q01 := TCLQueuer.Create( C0, D01 ); // OK Q02 := TCLQueuer.Create( C0, D02 ); // OK Q10 := TCLQueuer.Create( C1, D00 ); // エラー Q11 := TCLQueuer.Create( C1, D01 ); // エラー Q12 := TCLQueuer.Create( C1, D02 ); // エラー Q20 := TCLQueuer.Create( C2, D00 ); // エラー Q21 := TCLQueuer.Create( C2, D10 ); // エラー Q22 := TCLQueuer.Create( C2, D20 ); // OK
⬤ 2.5. 実引数
▼ 2.5.1. メモリー
“メモリー”オブジェクト (TCLMemory
) は、さまざまなデータを保存し、デバイスと共有します。
TCLMemory
クラスは、TCLContex
クラスとTCLQueuer
クラスを引数として生成できます。
TCLMemory
クラスは抽象クラスであり、TCLBuffer
クラスとTCLImager
クラスを派生させます。
▽ 2.5.1.1. バッファー
TCLBuffer
クラスは、任意の“単純型”や“レコード型”の配列を格納します。
デバイスへ以下のような構造体型の配列を送りたい場合、
OpenCL C
typedef struct { int A; double B; } TItem; kernel void Main( global TItem* Buffer ) { ・・・ }
以下のようにTCLBuffer
クラスを生成します。
Object Pascal
TItem = record A :Integer; B :Double; end; _Buffer := TCLBuffer<TItem>.Create( _Contex, _Queuer );
配列データは、Data[]
プロパティを通して読み書きします。
ホストとデバイスの同期のため、配列データを使用する前に“マップ”し、終了後に“アンマップ”する必要があります。
Object Pascal
_Buffer.Count := 3; // 要素数の設定 _Buffer.Data.Map; // メモリ領域を展開 _Buffer.Data[0] := TItem.Create( 1, 2.34 ); // 書き込み _Buffer.Data[1] := TItem.Create( 5, 6.78 ); // 書き込み _Buffer.Data[2] := TItem.Create( 9, 0.12 ); // 書き込み _Buffer.Data.Unmap; // メモリ領域を同期
▽ 2.5.1.2. イメージ
“イメージ”オブジェクト (TCLImager
) は、1D~3Dにおけるピクセル配列を格納します。
3Dのボクセルデータもイメージの一種と見なされます。
TCLImager
クラスは抽象クラスであり、カラーチャンネルのレイアウトやビット数に応じて、様々なクラスが派生します。
TCLImager
┣TCLImager1D
┃ ┣TCLImager1DxBGRAxUInt8
┃ ┣TCLImager1DxBGRAxUFix8
┃ ┣TCLImager1DxRGBAxUInt32
┃ ┗TCLImager1DxRGBAxSFlo32
┣TCLImager2D
┃ ┣TCLImager2DxBGRAxUInt8
┃ ┣TCLImager2DxBGRAxUFix8
┃ ┣TCLImager2DxRGBAxUInt32
┃ ┗TCLImager2DxRGBAxSFlo32
┗TCLImager3D
┣TCLImager3DxBGRAxUInt8
┣TCLImager3DxBGRAxUFix8
┣TCLImager3DxRGBAxUInt32
┗TCLImager3DxRGBAxSFlo32
クラス名の1番目の部分は、画像の次元を表しています。
- TCLImager
1D
x*
x*
- 次元:
1D
- TCLImager
2D
x*
x*
- 次元:
2D
- TCLImager
3D
x*
x*
- 次元:
3D
クラス名の2番目の部分は、カラーチャンネルの順番を表しています。
- TCLImager
*
xBGRA
x*
- カラーチャンネル順:
BGRA
- TCLImager
*
xRGBA
x*
- カラーチャンネル順:
RGBA
クラス名の3番目の部分は、色のデータ型を表しています。
- TCLImager
*
x*
xUInt8
- デバイス側データ型:
uint8
@ OpenCL C- ホスト側データ型:
UInt8 (Byte)
@ Delphi- TCLImager
*
x*
xUFix8
- デバイス側データ型:
float
@ OpenCL C- ホスト側データ型:
UInt8 (Byte)
@ Delphi- TCLImager
*
x*
xUInt32
- デバイス側データ型:
uint
@ OpenCL C- ホスト側データ型:
UInt32 (Cardinal)
@ Delphi- TCLImager
*
x*
xSFlo32
- デバイス側データ型:
float
@ OpenCL C- ホスト側データ型:
Single
@ Delphi
X/Y/Z方向のピクセル数は、’CountX’/’Y’/’Z’ プロパティで設定できます。
Object Pascal
_Imager := TCLDevIma3DxBGRAxUInt8.Create( _Contex, _Queuer ); _Imager.CountX := 100; // X方向ピクセル数 _Imager.CountY := 200; // Y方向ピクセル数 _Imager.CountZ := 300; // Z方向ピクセル数
▼ 2.5.2. サンプラー
サンプラーオブジェクト (TCLSamplr
) は、ピクセル色を実数座標で得るための補間方法を定義します。
TCLSamplr
クラスは、’TCLContex’クラスを引数として生成できます。
Object Pascal
_Samplr := TCLSamplr.Create( _Contex );
⬤ 2.6. プログラム
“プログラム”オブジェクト (TCLProgra
) は、ソースコードを読み込んで、実行可能なバイナリへビルドします。
TCLProgra
クラスは、’TCLContex’クラスを引数として生成できます。
TCLProgra
クラスは抽象クラスであり、ソースコードの種類に応じて、TCLLibrar
クラスまたはTCLExecut
クラスの基底クラスとして機能します。
▼ 2.6.1. ライブラリ
TCLLibrar
クラスは、直接実行する関数を含まないプログラムです。
Object Pascal
_Librar := TCLLibrar.Create( _Contex ); _Librar.Source.LoadFromFile( 'Librar.cl' ); // ソースコードのロード
▼ 2.6.2. エグゼキュータブル
TCLExecut
クラスは、直接実行する関数(カーネル)を含んだプログラムです。
Object Pascal
_Execut := TCLExecut.Create( _Contex ); _Execut.Source.LoadFromFile( 'Execut.cl' ); // ソースコードのロード
⬤ 2.7. ビルド
ビルド (TCLBuildr
) はプログラムが行う“行為”ですが、このライブラリでは、ビルドはクラスとして明確に定義されています。
TCLBuildr
クラスは、’TCLExecut’クラスと’TCLDevice’クラスを引数として生成できます。
Object Pascal
_Buildr := TCLBuildr.Create( _Execut, _Device ); {or} _Buildr := _Execut.Buildrs[ _Device ]; {or} _Buildr := _Execut.BuildTo( _Device );
カーネルオブジェクト(2.8.章参照)は、実行時にTCLBuildr
クラスを自動的に生成します。
しかし、カーネルの実行前にTCLBuildr
クラスを作成することで、コンパイルとリンクのエラーを事前に確認することができます。
Object Pascal
_Buildr.Handle; // ビルドの実行(ハンドルの生成) _Buildr.CompileStatus :T_cl_build_status // コンパイルのスタータス _Buildr.CompileLog :String // コンパイルのログ _Buildr.LinkStatus :T_cl_build_status // リンクのスタータス _Buildr.LinkLog :String // リンクのログ
⬤ 2.8. カーネル
カーネル”オブジェクト (TCLKernel
) は、プログラム内の実行可能な関数を指します。
OpenCL C
kernel void Main( ・・・ ) { ・・・ }
TCLKernel
クラスは、TCLExecut
クラスとTCLQueuer
クラスを引数にして生成できます。
Object Pascal
_Kernel := TCLKernel.Create( _Execut, 'Main', _Queuer ); {or} _Kernel := _Execut.Kernels.Add( 'Main', _Queuer );
▼ 2.8.1. 仮引数
メモリオブジェクトは、TCLKernel
クラスのParames[]
プロパティを介して、ソースコード内の引数に関連付けられます。
Object Pascal
_Kernel.Parames['Buffer'] := _Buffer; // バッファーの接続 _Kernel.Parames['Imager'] := _Imager; // イメージ の接続 _Kernel.Parames['Samplr'] := _Samplr; // サンプラーの接続
▼ 2.8.2. 反復回数
OpenCL のプログラムは、3重のループ構文のように繰り返し実行されます。
Object Pascal
_Kernel.GloSizX := 100; // X方向のループ回数 _Kernel.GloSizY := 200; // Y方向のループ回数 _Kernel.GloSizZ := 300; // Z方向のループ回数
ループの最小および最大インデックスを指定することも可能です。
Object Pascal
_Kernel.GloMinX := 0; // X方向の開始インデックス _Kernel.GloMinY := 0; // Y方向の開始インデックス _Kernel.GloMinZ := 0; // Z方向の開始インデックス _Kernel.GloMaxX := 100-1; // X方向の終了インデックス _Kernel.GloMaxY := 200-1; // Y方向の終了インデックス _Kernel.GloMaxZ := 300-1; // Z方向の終了インデックス
▼ 2.8.3. 実行
Object Pascal
_Kernel.Run; // 実行
■ 3. 参考文献
⬤ 3.1. The Khronos Group Inc
⬤ 3.2. GitHub
- Delphi IDE @ Embarcadero
https://www.embarcadero.com/jp/products/delphi/starter