日別アーカイブ: 2009年2月8日

Direct2D (5) ID2D1Geometry を使う

GPU の活用によりラスタライズはたいしたウエイトを占めておらず、CPU の仕事は
与えるデータの構築だけとなりました。それゆえ Direct2D はただの描画命令の
集まりにはなっていません。
ベクター描画の途中に介入し、ベクトルの情報のままデータを扱うことが可能です。
Direct2D が単なる GDI の置き換えではなく、可能性を秘めているのはこの Geometry
ではないかと思っています。

実際に ID2D1Geometry がどのようなものか興味あるので、機能を 1つ 1つ追いかけて
みました。

● Path の作成

Path の作成は下記の通り。

ID2D1Factory*	iFactory;
ID2D1PathGeometry*	iPath;
iFactory->CreatePathGeometry( &iPath );

ID2D1GeometrySink*	isink;
iPath->Open( &isink );
isink->SetFillMode( D2D1_FILL_MODE_WINDING );
isink->BeginFigure( startpos, D2D1_FIGURE_BEGIN_FILLED ); // = SVG M
isink->AddLine( pos1 );	    // = SVG L
isink->AddBezier( D2D1::BezierSegment( c2, c3, c4 ) );	 // = SVG C
 ...
isink->EndFigure( D2D1_FIGURE_END_CLOSED );   // = SVG Z
isink->Close();

ID2D1GeometrySink::Close() (ID2D1SimplifiedGeometrySink::Close()) は
SVG 等の Z コマンド相当かと思っていたら EndFigure( D2D1_FIGURE_END_CLOSED )
の方でした。

D2D1_FIGURE_END_OPEN     そのまま
D2D1_FIGURE_END_CLOSED   BeginFigure() の位置に直線で接続(閉じたパスにする)

ID2D1GeometrySink::SetFillMode() では塗りつぶしのルールを決定します。
SVG 等の vector graphics にある evenodd / nonzero に相当します。

・D2D1_FILL_MODE_ALTERNATE
 ”evenodd” 相当。外からのレイが交差した回数で決定します。
 交差回数が偶数なら外、奇数は中。パスの回転方向は影響しません。

・D2D1_FILL_MODE_WINDING
 ”nonzero” 相当。外からのレイが交差するとき、交わった線の方向を考慮します。
 たとえば閉じているパスが仮に右回りなら +1、左回りなら -1、結果が 0 なら外。
 つまりパスの回転方向を見ており、逆回転はくり抜き。

● Sink

Sink は値の追加、データをためることだけが出来るバッファです。
Add 系命令のみで Get はありません。
ID2D1Geometry では各オペレーションに対して受け皿である Sink を渡します。

最初にただの box を作ってみます。

ID2D1RectangleGeometry*	iRect;
iFactory->CreateRectangleGeometry(
	D2D1::RectF( 0.0f, 0.0f, 60.0f, 50.0f ), &iRect );

この box を元に、線を太らせる処理を行います。
もともとは描画時に太い幅 (strokeWidth) の線で描画するための変換ですが、
描画前に Path として受け取ることが出来るわけです。

// 変換結果を格納する ID2D1PathGeometry を作る
ID2D1PathGeometry*	iPath2;
iFactory->CreatePathGeometry( &iPath2 );

// ID2D1PathGeometry の sink に変換結果を受け取る
ID2D1GeometrySink*	isink;
iPath2->Open( &isink );
// iRect の変換結果を isink へ
iRect->Widen( 10.0f, NULL, NULL, isink ); // strokeWidth= 10.0f
isink->Close();

実際に描画したのがこちら。

Direct2D sample

↑左から順に
 ・元の Box
 ・元の Box を幅 10 の線で描画したもの
 ・幅 10 の描画を Path で受け取ったもの
 ・幅 10 で Path 化したものをさらに 幅 4 の線で描画したもの

// 描画コード
iRenderTarget->SetTransform( D2D1::Matrix3x2F::Translation( 20.5f, 20.5f ) );
iRenderTarget->DrawGeometry( iRect, iBrush0 );

iRenderTarget->SetTransform( D2D1::Matrix3x2F::Translation( 120.5f, 20.5f ) );
iRenderTarget->DrawGeometry( iRect, iBrush0, 10.0f );

iRenderTarget->SetTransform( D2D1::Matrix3x2F::Translation( 220.5f, 20.5f ) );
iRenderTarget->DrawGeometry( iPath2, iBrush0 );

iRenderTarget->SetTransform( D2D1::Matrix3x2F::Translation( 320.5f, 20.5f ) );
iRenderTarget->DrawGeometry( iPath2, iBrush0, 4.0f );

RenderTarget には直接描画可能な DrawRectangle() 命令があります。
Direct2D では内部的には全く同じように

(1) CreateRectangleGeometry() 等でプリミティブを作成
(2) Widen() など与えたオプションによって必要な変換を行う
(3) Tessellate() で ID2D1Mesh 化
(4) 最終的にすべて ID2D1RenderTarget::FillMesh()

という手順で描画しているだけかもしれません。この場合一番低レベルな描画は
FillMesh() になります。
ただし現在 FillMesh() での直接描画が成功しておらず、これらは予想の範囲を
超えておりません。

もし予想が合っているのなら、Geometry を扱うことでより柔軟なデータ管理ができます。
たとえば比較的 Mesh に近い状態のままキャッシュすることで、高速化や再利用が
可能となるでしょう。毎回 stroke を指定して Draw するよりも Geometry か Mesh
化しておいた方が高速だと思います。

●変換形状の読み出し

Geometry の中身は隠蔽されており、パスの座標値などデータを直接さわることが
できなくなっています。
ところが SDK には、テセレートされたアウトラインフォントの形状を取り出して立体化し、
3D ポリゴンとして描画するサンプルプログラム Interactive3dTextSample があります。

その仕組みはこうです。

(1) Sink 互換のインターフェースを自分で定義しておく
(2) Geometry の Tessellate() 等に渡して変換結果を受け取る

たとえば ID2D1Geometry::Tessellate() であれば、変換後の結果を格納するために
内部で ID2D1TessellationSink::AddTriangles() を呼び出しています。
互換インターフェースで AddTriangles() を定義しておけばトラップ可能となるわけです。

このように、Tessellate() など Geometry で変換した座標値をアプリケーションで
使うことができます。

関連エントリ
Direct2D (4) Direct2D の描画
Direct2D (3) 互換性の検証と Vista で Direct2D
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換

DirectX 一覧 や GPU 年表の更新と ATI Imageon

「 Hyperでんち 」 の古くなっていたいくつかの項目を更新しました。

DirectX 一覧
GPU 年表

DirectX 一覧には Direct3D11 を追加 (リリース前なので未完)
GPU 年表は Mobile の項目を分離。

表を見てわかるとおり Mobile はちょうど今が固定機能パイプラインで DirectX7 世代。
その次は Unitifed Shader が当たり前で、一気に Direct3D10/ShaderModel4 世代と
なる可能性があります。

PowerVR SGX はすでに Unified Shader ベースであり、Intel US15W の GMA500 や
OMAP3 をはじめいくつかのデバイスで使われています。
Qualcomm の MSM に搭載されていた ATI Imageon も、新たに Xbox 360 同様の
Unified Shader Architecture を元に開発が行われていたようです。

AMD Licenses 3D Graphics Core Technology to QUALCOMM, Delivering The Ultimate Visual Experience? to Tomorrow’s Phones

GeometryShader が存在するかどうかわかりませんが Shader ベースであることは確かです。
PowerVR SGX 同様、Vertex/ Pixel の演算ユニットを共有できること、
Graphics 以外への応用が容易なこと、設計時に演算能力をスケーラブルに変更できる
ことがメリットであると考えられます。

Wikipedia Imageon

上記 wikipedia のページには Z460 という新しい Imageon core の名前が載っています。
直接 AMD の pdf 資料へのリンクもあります。登場は 2009 年以降とのこと。

これらのデータより、HTC Diamond 等に使われている現行 MSM7201A の 3D core は
ATI Imageon 2380 シリーズをベースにしたものだと考えられます。

2006/1/30 ImageonR 2380 および ImageonR 2388 で驚くべきゲームおよびマルチメディア体験を可能にし、ハンドヘルド 3D グラフィックスの水準を引き上げる ATI

AMD Imageon 2388/2380

公式ページの資料が参考になります。
Direct3DMobile では使えないものの CubeMap など一通り機能がそろっているようです。
D3D8 相当のスキニング用固定 Matrix Palette もありますが、残念ながら D3DM 側に
対応する機能がありません。
頂点演算は float で行われており、やはり NATIVEFLAOT で正解でした。
違いは VRAM のみで、2388 が内蔵 8MB、2380 が外付けで 16MB 以上に対応とのこと。

関連エントリ
Intel GMA500 のスペックについて考える。続き (2)
Intel GMA500 の機能と性能と Aero
Qualcomm と 3D
HTC Touch Diamond で Direct3DMobile その(11) 問題まとめその他