ATI Stream SDK 2.0 beta と OpenCL

まだ未対応らしく Windows7 にはうまくインストールできませんでした。

ATI Stream Software Development Kit (SDK) v2.0 Beta Program

インストーラが正常終了しても Program Files の ATI Stream フォルダが
空のままです。
とりあえず Vista で展開したのちパスを通せば使えなくはないようです。
よく見ると OpenCL は GPU 未対応で CAL も無くなっています。
Intel CPU (+GeForce) でも動きました。

CL_PLATFORM_PROFILE    = FULL_PROFILE
CL_PLATFORM_VERSION    = OpenCL 1.0 ATI-Stream-v2.0-beta2
CL_PLATFORM_NAME       = ATI Stream
CL_PLATFORM_VENDOR     = Advanced Micro Devices
CL_PLATFORM_EXTENSIONS = 

CL_DEVICE_TYPE = 2
CL_DEVICE_VENDOR_ID = 4098
CL_DEVICE_MAX_COMPUTE_UNITS = 8
CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS = 3
CL_DEVICE_MAX_WORK_ITEM_SIZES = 1024 1024 1024
CL_DEVICE_MAX_WORK_GROUP_SIZE = 1024
CL_DEVICE_EXECUTION_CAPABILITIES = 1

Core i7 の結果です。
CL_DEVICE_TYPE の 2 は CPU (CL_DEVICE_TYPE_CPU) を表しています。
CL_DEVICE_MAX_COMPUTE_UNITS は 8個。
Core2 Duo で走らせると CL_DEVICE_MAX_COMPUTE_UNITS = 2 となります。

Device Type は下記の通り 3種類定義されています。

2 CL_DEVICE_TYPE_CPU
4 CL_DEVICE_TYPE_GPU
8 CL_DEVICE_TYPE_ACCELERATOR

Compute Shader は GPU 上で走るプログラムを共通化するのが目的でした。
OpenCL は CPU/GPU やその他を含めた、より幅広いプロセッサを対象として
作られているようです。

Direct3D の Compute Shader も CPU でエミュレーションできれば同じですが
現状だと WARP (10.1) も対応しておらず、Reference でしか動作しません。
エミュレーションできても GPU, CPU 混在しての利用は想定されていないといって
良いでしょう。

CL_DEVICE_EXECUTION_CAPABILITIES は CL_EXEC_KERNEL のみセットされています。
CL_EXEC_NATIVE_KERNEL が無いので、clEnqueueNativeKernel()
は実行出来ないようです。

バッファの転送や実行は Command Queue を生成して非同期に監視します。
GPU の描画操作に似ています。
異なるのは Out of order で実行可能なこと。
そのままでは実行順の保証が出来ませんが、その代わり各コマンドに依存関係を
設定できます。特定のコマンドの完了を待ってから実行できるわけです。

Direct3D11 でも DeviceContext が分離され、Command List (Queue) に
蓄えられる GPU 命令と、そうでないものを区別できるようになっています。
ただこちらは、複数のスレッドで作成したコマンドを単一のコンテキストで実行する
ための仕組みです。コマンドバッファ内の実行順は決まっているため、
スケジューリングは呼び出す側に委ねられています。

OpenCL の kernel の記述言語は glsl とは別もので、より C言語に近いものです。
long は 64bit。vector も 16個まで扱えるようです。
4コンポーネントを超えた場合、各要素は16進数指定で s0, s1,~ sf といった表記
になっています。

D3D の Compute Shader はデバイスやリソース管理等を D3D のコンポーネントに
委ねています。Direct3D の一部であって言語も hlsl そのまま同じものでした。

OpenCL の場合完全に独立しており、リソース管理も言語も OpenCL の世界で作られています。
コマンドをいくつか拡張してレンダリング向け補助機能を追加すれば、新しい
3D API セットができるのではないかと思うくらい。
その分 ATI CAL とか、独自 SDK と比べると複雑になった印象を受けるかもしれません。
リソースは描画と共有可能で、D3D や OpenGL からも OpenCL のバッファを作成
出来るようになっています。

const int THREAD_SIZE= 32;
cl_int	  status= 0;
cl_uint	  nums;
cl_platform_id	pid= 0;
cl_device_id	device= 0;

status= clGetPlatformIDs( 1, &pid, &nums );
status= clGetDeviceIDs( pid, CL_DEVICE_TYPE_CPU, 1, &device, &nums );

cl_context  context= clCreateContextFromType( NULL, CL_DEVICE_TYPE_CPU, NULL, NULL, &status );
cl_command_queue   command= clCreateCommandQueue( context, device, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &status );

cl_mem	mem= clCreateBuffer( context, CL_MEM_READ_WRITE, THREAD_SIZE * sizeof(cl_float), NULL, &status );

const char*   src=
    "__kernel void main( __global float* op )\n"
    "{\n"
    "   unsigned int   tid= get_global_id(0);\n"
    "   op[tid]= tid * tid;\n"
    "}";
size_t	srcsize= strlen( src );
cl_program  prog= clCreateProgramWithSource( context, 1, &src, &srcsize, &status );

status= clBuildProgram( prog, 0, NULL, NULL, NULL, NULL );

cl_kernel  kernel= clCreateKernel( prog, "main", &status );

status= clSetKernelArg( kernel, 0, sizeof(cl_mem), &mem );

size_t	gthread= THREAD_SIZE;
size_t	lthread= 1;
cl_event   event;
status= clEnqueueNDRangeKernel( command, kernel, 1, NULL, >hread, 

HP TouchSmart IQ800 に GeForce 9600M GS ドライバを入れる

HP TouchSmart IQ800 で Multi touch を試すには Windows7 が必要ですが
ノート用 GPU を使っているせいかグラフィックドライバがうまく入らない
ことがあります。

HP TouchSmart PC IQ800

GeForce 9600M GS に対応しているはずの 186.03 もそのままでは install できません。
Vista 用ドライバも使えるので、もともと付属している純正ドライバは入れられます。
プリインストールされている Vista のドライブパーティションを残してあるなら
\hp\drivers\nVidia_Graphics がそれです。

IQ827jp の場合付属しているのは WindowsVista x86 (32bit) なので、上記ドライバも
x86 のみです。Windows7 x64 を入れる場合は使えませんでした。
HP のサイトから IQ817jp 用の Vista x64 ドライバを落とせますがこれもだめです。

Windows7 beta のときはこんな感じでドライバを入れました。

一応 186.03 も inf を書き換えれば何とかなりそうです。
もともと GeForce 9600M GS に対応していると書かれているので
inf ファイルからそれらしき記述を探します。
まず nvac.inf を調べてみます。

NVIDIA_DEV.0648.** というシンボルが 9600M GS のようです。

デバイスの ID が一致しているかどうか確認します。

デバイスマネージャー → Display adapters
   → Standard VGA Graphics Adapter → プロパティ
details タブ Hardware Ids を見ると ID がわかります。& が区切り。

VEN = 10DE
DEV= 0648
SUBSYS= 900F1043

DEV が 0648 なので上で調べたシンボルと一致しています。
対応する SUBSYS が無いためそのままインストールできなかったのだと思われます。
9600M GS 相当の定義エントリ「%NVIDIA_DEV.0648.**% = ~」の行を複製し、
「**」の番号を別の重複しない値 (今回は18) に書き換えます。
SUBSYS_ の後ろを 900F1043 に置き換えることで一応インストールできる
ようになりました。

%NVIDIA_DEV.0648.18% = Section005, PCI\VEN_10DE&DEV_0648&SUBSYS_900F1043

行を複製した部分は 3カ所です。そのうち SUBSYS を書き換えたのは 2カ所。
nvac.inf 以外にも inf ファイルは多数存在し、他にも 9600M GS の記述がみられます。
本当に nvac で良いのか根拠は全くないです。
何らかの問題が生じる可能性もあるので、もし試す場合は必ず自己責任でお願いします。
早く公式な Windows7 ドライバが出てくれると良いのですが。

Aero にするにはドライバインストール後にシステム評価の再計測が必要です。

WDDM 1.1

CPU: 6.0
RAM: 6.0
AERO: 6.3
GAME: 6.3
HDD: 5.9

関連エントリ
Windows7 Multitouch API (3)
Windows7 とマルチタッチ / HP TouchSmart PC IQ800

Direct3D11 Windows7 RTM と DebugLayer

Windows7 を RTM 版に入れ替えたら D3D11(beta) のプログラムが動かなくなって
いました。DirectX SDK March2009 + D3D11 を使用しており、RC まではきちんと
動いていたものです。

止まっているのが CommandList まわりの呼び出しで関係ない関数に飛んでいます。
lpVtbl がずれているような感じ。
単に beta と互換性がなくなっているだけかもしれません。
新しい Windows7 SDK には D3D 関連のヘッダや lib も含まれているため、
こちらを使えば RTM 版 dll が用いられます。
d3dx11 や d3dcompiler は無いので、この辺を使っている場合は DirectX SDK も
併用することになります。core ではないためおそらく大丈夫だと思います。

でも結局 Windows7 SDK の d3d11.lib に切り替えてもだめ。
コンソールをみると d3d11.dll だけでなく最後に d3d11_beta.dll も読み込まれて
いました。この両者が混在しているのはあまりよい感じではありません。

DirectX SDK サンプルは動くしサンプルをビルドしても大丈夫です。
サンプルとの違いは DXGI1.1 を使っていることくらいでしょうか。
試しに DXGI1 に戻してもやっぱり動きません。
DXGI1.1 は Windows SDK 側の dxgi.lib で使用できるはずなので、問題ないと
思います。

lib の検索順番や、リンクする lib の組み合わせをいろいろテスト。
気になるのは Windows SDK 側の d3d11.lib のみ使うようにしても、必ず最後に
d3d11_beta.dll が読み込まれてしまうことです。
誰が読み込んでいるのだろうと調べているうちに DebugLayer が原因ではないかと
思いつきました。

D3D11Create~() で D3D11_CREATE_DEVICE_DEBUG を指定していると、
Debug でも Release build でも最後に d3d11_beta.dll が読み込まれています。
とりあえずこのフラグ指定を消すと Windows7 RTM で動作しました。
やはり d3d11_beta.dll との互換性が問題だと思われます。

DirectX SDK がリリースされるまで待った方が良さそうです。

OpenGLES2.0 の頂点

OpenGLES の頂点データは各頂点要素毎の配列を登録します。
頂点座標や法線など、頂点の構成要素は Direct3D では element、OpenGL では
attribute と呼ばれているようです。

glVertexAttribPointer( vloc, 3, GL_FLOAT, GL_FALSE, sizeof(vec3), v_data );
glVertexAttribPointer( nloc, 3, GL_FLOAT, GL_FALSE, sizeof(vec3), n_data );
glVertexAttribPointer( tloc, 2, GL_FLOAT, GL_FALSE, sizeof(vec2), t_data );

Direct3D の場合は基本的にパックされた頂点データを用います。
いわゆる AOS で、対する OpenGL はベクター単位の SOA になります。
とはいえ実際には Direct3D でも複数の頂点ストリームを与えることが出来るので、
OpenGL のように要素毎に配列を分離することが可能です。

また OpenGL の場合も index は共有なので頂点の位置や配列の大きさも同数です。
stride を指定すれば、Direct3D のようなパックした頂点データのポインタを登録
することができます。
どちらも出来ることや使い方にほとんど差が無くなっています。

struct vtype {
    float x, y, z;
    float nx, ny, nz;
    float tu, tv;
};
glVertexAttribPointer( vloc, 3, GL_FLOAT, GL_FALSE, sizeof(vtype), &vp->x );
glVertexAttribPointer( nloc, 3, GL_FLOAT, GL_FALSE, sizeof(vtype), &vp->nx );
glVertexAttribPointer( cloc, 2, GL_FLOAT, GL_FALSE, sizeof(vtype), &vp->tu );

普段 Direct3D 向けにデータを出力している関係上、座標系と同じように D3D と同じ
データが使えれば楽出来ます。
問題はどちらが効率がよいのかと言うこと。
D3D 形式の頂点の持ち方で速度が落ちないかどうかが心配な点です。

キャッシュの利用効率を考えると、一見インターリーブされた D3D タイプの方が
良さそうに見えます。
一度にアクセスするであろうデータが適切な局所性を持つからです。
頂点は index によるランダムアクセスになる可能性があるので、SOA の配列の効率が
必ずしも最善とは限りません。でも GPU は 1頂点分まとめてデータを読み込む
ことがわかっているので、一カ所に集まっていた方がキャッシュを活用できます。

以前そう考えて最適化のつもりで試したものの、逆に遅くなってしまうハードウエア
がありました。インデックスも個別に持てたので、頂点ストリーム毎にキャッシュを
持つなどの特殊な仕組みだったのかもしれません。もしそうなら D3D タイプの
インターリーブは同じデータが重複してキャッシュに乗ることになってしまいます。

ただこれは特殊なケースかもしれません。いろいろ見てみると、やはり最適化手法
としてインターリーブを推奨している場合もあるようです。
普通は D3D と同形式でデータを扱って問題ないと思われます。
これでデータもライブラリコードも D3D と全く同じように扱う準備が出来ました。

関連エントリ
OpenGLES2.0 D3D座標系
OpenGLES2.0 シェーダー管理

Direct3D Matrix の並びと SSE 命令 (2)

先日の SSE 命令の実行速度を試してみました。
あまり厳密なものではなくて、掲載したコード 3種類をそのままループで回しただけです。

● Core2 Duo P8600 2.4GHz Windows7 x86

x86  SSE1       17581 (msec)  mulps+addps
x86  SSE3       20623         haddps
x86  SSE4.1     17799         dpps

● Core i7 940 2.93GHz Windows7 RC x64

x86  SSE1       14320         mulps+addps
x86  SSE3       11716         haddps
x86  SSE4.1     13682         dpps

x64  SSE1       17269         mulps+addps
x64  SSE3       11076         haddps
x64  SSE4.1     13681         dpps

単位は時間で値が小さい方が高速です。

Core2 実行時、SSE3 hadd で遅くなってしまい少々焦りました。
昨日これで良いって書いたばかりなのに。結局元のコードの方が速いという結果に。
Core i7 の場合は正反対で hadd が最速になっています。

おかしなことに x64 のみ SSE1 で極端に速度が落ちています。
実際に x64+SSE1 の展開されたコードを見てみると、x64 で増えたレジスタを最大限
使おうとしてループの先頭で全部レジスタにロードしていました。
その分転送などの命令が他のケースよりも増えています。

このテストは局所的な小さいループかつ、使用したデータもストリーム入出力ではなく
同じ領域へのたたみ込みでした。
それゆえすべてのデータがキャッシュに乗った状態だと考えられます。
レジスタ間の転送命令が増えるよりもおそらくキャッシュに乗ったメモリから
読み直した方が速いのでしょう。

実際に SSE 命令が使われるケースだとストリーム処理が多いため、必ずしも
このような結果にはならないと思われます。

haddps , dpps の実行性能は CPU によって逆転することがわかりました。
この両者は比較的似たようなコードに展開されています。
ループ内の命令数も同数でした。
プロセッサによって異なるため、やはり指針としては命令数を減らすことを考えて
書くのが良さそうです。

関数は intrinsics 命令で記述しためコンパイラは inline 展開しており、
複数の関数に渡って並べ替えなどの最適化が行われています。
intrinsic 命令がどのように展開されるかだけ把握しておけば asm を使わない方が
良いかもしれません。x64 だと使えないし。
_mm_setr_ps とかは結構命令数食います。

関連エントリ
Direct3D Matrix の並びと SSE 命令
Intel AVX その3 命令
D3D10 row_major column_major
SSE についてのメモ(2) SSE4など