Archives

February 2007 の記事

今度こそ GeForce8800 の Vista 対応正式版ドライバがリリースしたようです。
ForceWare 100.65
http://www.nvidia.com/object/winvista_x86_100.65.html
前回から 0.01 だけ番号が増えていますね。


GPU 用の汎用 Computing のための SDK である CUDA に動きがあったようです。
いろいろ新しい情報が公開されています。
CUDA for GPU Computing

NVIDIA CUDA Homepage

年々強化されている GPU 演算能力が、プログラマブル機能の進化によって
グラフィック用途向け以外でも活用できるようになって来ました。
ただ Direct3D や OpenGL などの Graphic 向けに特化されている API セットでは
汎用化が進んでいるとはいえやはり制限はついてしまいます。

もっとプログラマが自由に使えて、それでいて強大な演算能力が欲しい。
そういう要望にこたえたのが、CPU からのアプローチでいえば Cell であって
GPU 側からの回答のひとつがこの G80 + CUDA です。
CPU も GPU もこの両者は近づきつつあるのかもしれません。
(本当にくっついちゃったのが AMD + ATI です)

対応しているのは WindowsXP と RedHat系 Linux。
Vista が含まれていないのが少々残念。
Linux がしっかりサポートされているのは最近の NVIDIA の傾向です。
この流れで PS3 Linux でも動いたりしたら面白いのですが
対応 GPU は GeForce8800 (G80) 系だけです。


D3D10 GeometryShaderでspriteで書いた
1頂点を4点のポリゴンに展開する GeometryShader の例です。
VS_OUTPUT の Size には、xy に texcoord のサイズが、zw に右下の頂点座標が
入ります。この zw の計算は VertexShader で行っています。

VS_OUTPUT は VertexShader の出力、GS_OUTPUT はそのまま PixelShader の入力
になります。

struct VS_OUTPUT {
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD;
float4 Size : SIZE; // .xy=uvsize .zw=right/bottom
float4 Color : COLOR;
};

struct GS_OUTPUT {
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD;
float4 Color : COLOR;
};


[maxvertexcount(6)]
void GS_Main( point VS_OUTPUT In[1], inout TriangleStream<GS_OUTPUT> gsstream )
{
GS_OUTPUT Out;

// 左上三角
Out.Pos= In[0].Pos;
Out.Tex= In[0].Tex;
Out.Color= In[0].Color;
gsstream.Append( Out );

Out.Pos= In[0].Pos; Out.Pos.x= In[0].Size.z;
Out.Tex= In[0].Tex; Out.Tex.x+= In[0].Size.x;
Out.Color= In[0].Color;
gsstream.Append( Out );

Out.Pos= In[0].Pos; Out.Pos.y= In[0].Size.w;
Out.Tex= In[0].Tex; Out.Tex.y+= In[0].Size.y;
Out.Color= In[0].Color;
gsstream.Append( Out );

gsstream.RestartStrip();

// 右下三角
Out.Pos= In[0].Pos; Out.Pos.y= In[0].Size.w;
Out.Tex= In[0].Tex; Out.Tex.y+= In[0].Size.y;
Out.Color= In[0].Color;
gsstream.Append( Out );

Out.Pos= In[0].Pos; Out.Pos.x= In[0].Size.z;
Out.Tex= In[0].Tex; Out.Tex.x+= In[0].Size.x;
Out.Color= In[0].Color;
gsstream.Append( Out );

Out.Pos= In[0].Pos; Out.Pos.xy= In[0].Size.zw;
Out.Tex= In[0].Tex; Out.Tex.xy+= In[0].Size.xy;
Out.Color= In[0].Color;
gsstream.Append( Out );

gsstream.RestartStrip();
}


Direct3D10 では、GeometryShader を使って頂点を増やすことができます。
従来任意の 2D の画像を描画する場合、描画には 4頂点分のデータが必要でした。
例えば x y u v color 等の5要素を 4頂点分使い、TriangleList の 2つの三角
ポリゴンとして描画します。


●頂点
0, 0, 0, 0, 0xffffffff,
32, 0, 1, 0, 0xffffffff,
0, 32, 0, 1, 0xffffffff,
32, 32, 1, 1, 0xffffffff,

●インデックス
0, 1, 2,
2, 1, 3,

そのため CPU 側で4頂点分の計算を行い4頂点分のデータを書き込む必要があります。
D3D10 では、GeometryShader を使うことでもっと簡単に描画できるようになります。

例えば頂点形式を x y w h u v us vs color の 8要素として、PointList を使い

0, 0, 32, 32, 0, 0, 1, 1, 0xfffffff,

となります。PrimitiveType が PointList なのでインデックスは不要です。
これを GeometryShader を使って、4頂点の矩形である 2ポリゴンに変換することが
できます。

昔の PointSprite のような感じです。
違いは uv を自由に指定できて大きさも任意であること。
特徴は 1頂点だけで制御できるので CPU 側の演算が不要でバッファも少なくて
済みます。

こんな風に、GeometryShader は工夫次第でハードウエアでカスタマイズできる
2D 向けの Sprite 機能としても活用できるわけです。

Shader でなんでもできるので、頂点に回転角度を入れて回転機能を持たせる
なんてことももちろん可能でしょう。


そういえば Direct3D10 使っていて AlphaTest が見当たりません。
もともと Direct3D9 の ShaderModel3.0 のときも、
Pixel を捨てるために次の2つの方法がありました。

(1) PixelShader の計算結果の値によって texkill する
(2) AlphaTest を Enable にして Alpha 値で出力する

(2) の方法だと、出力 Alpha を Blend に使ったり RenderTarget に
書き込みたい場合は複雑な計算式になってしまうことがあります。
結局 (1) の方法で (2) も実現できるのである意味重複した機能になっていました。


そういう意味では、D3D10 だと複数の方法から悩まなくて済むのでわかりやすく
なりました。
(それとももしかして AlphaToCoverage が AlphaTest 代わりになるのでしょうか?)


シェーダー側を


float4 PS_Main( PS_INPUT In ) : SV_Target
{
 float4 color= diffuse.Sample( samplestate, In.Tex );

 // AlphaTest
 if( color.w <= 0.001 ){
  clip( -1 );
 }

 return color;
}


のように書き換えて完了です。AlphaTest 相当になりました。
0.001 は AlphaRef に相当します。

clip() は texkill の HLSL 命令です。
値が負のときに pixel を捨てることができます。


従来 CPU からのアクセスは、リソース内の範囲を指定して Lock()/Unlock() を
呼び出していました。D3D10 では Map()/Unmap() に変わっていて、特にアクセス
範囲の指定もありません。
CPU と GPU のそれぞれのリソースアクセス制限は、生成時の D3D10_USAGE に
よって区別します。

・D3D10_USAGE_DEFAULT
  GPU が読み書きするリソース。CPU はアクセスできない。

・D3D10_USAGE_IMMUTABLE
  一度作成したら書き換えできない。constant のようなリソース。
  従来のゲームの作り方だと大半がこれに相当します。

・D3D10_USAGE_DYNAMIC
  CPU から GPU へ一方通行転送が可能。これを使うことで動的なオブジェクトも
  スムーズなやり取りができます。

・D3D10_USAGE_STAGING
  CPU 側で読み込めます。つまり GPU → CPU の転送が可能です。


それぞれの読み書き制限をまとめると次のようになります。

GPU-R,GPU-W,CPU-R,CPU-W
 ○  ○  ×  ×   DEFAULT
 ○  ×  ×  ×   IMMUTABLE
 ○  ×  ×  ○   DYNAMIC
 ○  ○  ○  ○   STAGING


特筆すべき点は、CPU 書込み禁止のリソースは Map() すらできないことです。
それじゃどうやって初期データを書き込むのかというと、バッファ作成時に
一緒に初期化用データを渡すことができます。
初期化時の一度きりの転送と Map() による直接アクセスを明確に分離している
わけですね。

また DYNAMIC は GPU が読み込みのみで CPU は書き込み専用です。
これを使えばスムーズな同期転送ができそうなこともわかります。

この辺必要な機能が明確になってかなりわかりやすくなりました。


D3D10 では dual source color blending なる便利機能があることを発見しました。
今まで pixel shader から 1つの render target に対して出力できるカラーは
RGBA の 4ch のみでした。

そのため例えば SrcAlpha + InvSrcAlpha などを使って pixel 毎の blend factor
として alpha を使用してしまうと、
Render Target の alpha にも任意の値を書き込みたいけどできないという
矛盾が生じてしまいます。

この新機能を使えば pixel shader からの出力が 4ch ×2 (SV_TARGET0, SV_TARGET1)
になるので、Render Target の alpha には diffuse や depth といった任意の値を
書き込みつつ、さらに任意の blend factor でブレンドできるといった設定が
可能になります。

これは便利だ


DirectX SDK February 2007 がリリースされています。
DirectX Software Development Kit February 2007
Vista 版 DirectX9 である Direct3D9Ex の説明やサンプルが追加されたようです。
DirectSound も Vista 向けのアップデートが行われており、Vista への移行が
本格的になってきました。
もちろん Direct3D 10 も含まれています。

やっぱり Vista が発売されたのが大きいですね。
DirectX まわりの動きも今後活発になりそうです。