Archives

February 2009 の記事

昨日の Tessellate 結果を図示したものです。

FillGeometry() 相当の場合
ポリゴン化の状態をわかりやすくするために flatteningTolerance = 4 で Tessellate
しています。

direct2d

iFactory->CreateEllipseGeometry(
	D2D1::Ellipse( D2D1::Point2F( 0,0f, 0.0f ), 80.0f, 80.0f ), &iElli );
DummySink	tsink;
iElli->Tessellate( NULL, 4.0f, &tsink );

DrawGeometry() 相当の場合
strokeWidth = 4.0 で描画した場合のポリゴン分割です。
こちらも見やすいように flatteningTolerance = 4 で分割しています。

direct2d

iFactory->CreatePathGeometry( &iPath );
ID2D1GeometrySink*	isink;
iPath->Open( &isink );
iElli->Widen( 10.0f, NULL, NULL, isink );
isink->Close();
DummySink	tsink;
iPath->Tessellate( NULL, 4.0f, &tsink );

デフォルトのままだとこれくらいの分割です。
水平方向の分割かつ Strip になっているのがわかります。

direct2d

テストに使った DummySink の定義。

class DummySink : public ID2D1TessellationSink {
private:
    static ID2D1PathGeometry*	iPath;
    static ID2D1GeometrySink*	iSink;
public:
    DummySink()
    {
    }
public:
    STDMETHOD_( void, AddTriangles )( const D2D1_TRIANGLE* triangles, UINT trianglecount )
    {
	for( UINT i= 0 ; i< trianglecount ; i++, triangles++ ){
	    iSink->BeginFigure( triangles->point1, D2D1_FIGURE_BEGIN_FILLED );
	    iSink->AddLine( triangles->point2 );
	    iSink->AddLine( triangles->point3 );
	    iSink->EndFigure( D2D1_FIGURE_END_CLOSED );
	}
    }

    STDMETHOD_(ULONG, AddRef )( THIS )
    {
	return	0;
    }
    STDMETHOD_(ULONG, Release )( THIS )
    {
	return	0;
    }
    STDMETHOD( QueryInterface )( THIS_ REFIID riid, void** obj )
    {
	return	E_UNEXPECTED;
    }
    STDMETHOD( Close )()
    {
	return	S_OK;
    }
    static void	Create( ID2D1Factory* factory )
    {
	factory->CreatePathGeometry( &iPath );
	iPath->Open( &iSink );
    }
    static void	ClosePath()
    {
	if( iSink ){
	    iSink->Close();
	    iSink= NULL;
	}
    }
};

ID2D1PathGeometry*	DummySink::iPath= NULL;
ID2D1GeometrySink*	DummySink::iSink= NULL;

そのまま DrawGeometry() で path を描画すると、とがった角にトゲが生えたような
描画になることがあります。折れ線のつなぎ処理が入っているようです。

direct2d

描画用に ID2D1StrokeStyle を作成しておくとトゲが無くなります。


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



FillMesh() はまだ成功していません。
Tessellate() で座標を取ってみました。

iFactory->CreateRectangleGeometry(
		D2D1::RectF( 0.0f, 0.0f, 10.0f, 10.0f ), &iRect );
DummySink	tsink;
iRect->Tessellate( NULL, &tsink );

まずはただの矩形から。
少々勘違いしていたのは、AddTriangles() はまとめて一度だけ呼ばれるのかと思ったら
1 Triangle 毎に何度も呼び出されること。
結果は次の通り。

t0= 10.000000 0.000000, 10.000000 10.000000,  0.000000 10.000000
t1=  0.000000 0.000000,  0.000000 10.000000, 10.000000  0.000000

頂点の並びが少々おかしい気がします。
それぞれ回転方向が逆になっているので、Strip を展開したような形でしょうか。

ID2D1RectangleGeometry ではなく Path で同様の形を作ってみます。

ID2D1GeometrySink*	isink;
iFactory->CreatePathGeometry( &iPath );
iPath->Open( &isink );
isink->BeginFigure( D2D1::Point2F( 0.0f, 0.0f ), D2D1_FIGURE_BEGIN_FILLED );
	isink->AddLine( D2D1::Point2F( 20.0f,  0.0f ) );
	isink->AddLine( D2D1::Point2F( 20.0f, 20.0f ) );
	isink->AddLine( D2D1::Point2F(  0.0f, 20.0f ) );
	isink->EndFigure( D2D1_FIGURE_END_CLOSED );
	isink->Close();
iPath->Tessellate( NULL, &tsink );

全く同じです。

t0= 20.000000 0.000000, 20.000000 20.000000,  0.000000 20.000000
t1=  0.000000 0.000000,  0.000000 20.000000, 20.000000  0.000000

パスの方向を逆にしても同じでした。

isink->BeginFigure( D2D1::Point2F( 0.0f, 0.0f ), D2D1_FIGURE_BEGIN_FILLED );
isink->AddLine( D2D1::Point2F(  0.0f, 20.0f ) );
isink->AddLine( D2D1::Point2F( 20.0f, 20.0f ) );
isink->AddLine( D2D1::Point2F( 20.0f,  0.0f ) );
isink->EndFigure( D2D1_FIGURE_END_CLOSED );

t0= 20.000000 0.000000, 20.000000 20.000000,  0.000000 20.000000
t1=  0.000000 0.000000,  0.000000 20.000000, 20.000000  0.000000

半径 10 の円の場合。

iFactory->CreateEllipseGeometry(
	D2D1::Ellipse( D2D1::Point2F( 0,0f, 0.0f ), 10.0f, 10.0f ), &iElli );
iElli->Tessellate( NULL, &tsink );

22個の Triangle に分割されています。ちなみに半径 30だと 30個。

t0= -2.015625 9.796875, 0.000000 10.000000, 2.015625 9.796875
t1= 3.890625 9.218750, 2.015625 9.796875, -2.015625 9.796875
t2= -3.890625 9.218750, -2.015625 9.796875, 3.890625 9.218750
t3= 7.062500 7.062500, 3.890625 9.218750, -3.890625 9.218750
t4= -7.062500 7.062500, -3.890625 9.218750, 7.062500 7.062500
t5= 9.218750 3.890625, 7.062500 7.062500, -7.062500 7.062500
t6= -9.218750 3.890625, -7.062500 7.062500, 9.218750 3.890625
t7= 9.796875 2.015625, 9.218750 3.890625, -9.218750 3.890625
t8= -9.796875 2.015625, -9.218750 3.890625, 9.796875 2.015625
t9= 10.000000 0.000000, 9.796875 2.015625, -9.796875 2.015625
t10= -10.000000 0.000000, -9.796875 2.015625, 10.000000 0.000000
t11= 9.796875 -2.015625, 10.000000 0.000000, -10.000000 0.000000
t12= -9.796875 -2.015625, -10.000000 0.000000, 9.796875 -2.015625
t13= 9.218750 -3.890625, 9.796875 -2.015625, -9.796875 -2.015625
t14= -9.218750 -3.890625, -9.796875 -2.015625, 9.218750 -3.890625
t15= 7.062500 -7.062500, 9.218750 -3.890625, -9.218750 -3.890625
t16= -7.062500 -7.062500, -9.218750 -3.890625, 7.062500 -7.062500
t17= 3.890625 -9.218750, 7.062500 -7.062500, -7.062500 -7.062500
t18= -3.890625 -9.218750, -7.062500 -7.062500, 3.890625 -9.218750
t19= 2.015625 -9.796875, 3.890625 -9.218750, -3.890625 -9.218750
t20= -2.015625 -9.796875, -3.890625 -9.218750, 2.015625 -9.796875
t21= 0.000000 -10.000000, -2.015625 -9.796875, 2.015625 -9.796875

円の中心に頂点がありません。水平方向にスライスされているので、やはり Strip に
展開されているのだと思われます。

分割数は Tessellate() 2番目のパラメータで指定出来ます。
  Tessellate( NULL, ここ, &tsink );
省略時は D2D1_DEFAULT_FLATTENING_TOLERANCE が渡されており、これは 0.25f です。

値を大きくすると、5.0 くらいで菱形の 4角形になります。
0.0f を与えると 3862個に分割されました。

# 5.0
t0= -10.000000 0.000000, 0.000000 10.000000, 10.000000 0.000000
t1=  0.000000 -10.000000, -10.000000 0.000000, 10.000000 0.000000

RenderTarget 作成時 D2D1_RENDER_TARGET_PROPERTIES dpiX/dpiY に 0 を与えると
デフォルトの 96dpi になります。
96dpi の時、長さは pixel 相当となり、dpi 値を増やすとピクセル密度が高くなります。
相対的に同じサイズの RenderTarget に描かれる図形は大きくなります。

dpi を考慮した実際に描画される大きさと、Tessellate() 2番目の数値によって
生成される Triangle の数が決められています。


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



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 の下位互換



「 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) 問題まとめその他



●描画

Direct2D の描画は下記の 4通りあります。

・線描画
・塗りつぶし
・イメージ描画
・文字

ID2D1RenderTarget に対して適用できるものです。
それぞれグループ分けと、グループごとに指定出来る共通プロパティをまとめます。

◎線描画
・DrawEllipse (楕円)
・DrawGeometry (パス)
・DrawLine (直線)
・DrawRectangle (四角)
・DrawRoundedRectangle (角が丸い四角形)

    ID2D1Brush
        色や模様

    Stroke
        線の太さ (strokeWidth)
        ID2D1StrokeStyle 線のスタイル
	 (端の形状、丸めたりとんがったり。線の形状、点線やダッシュ点線など)


◎塗りつぶし
・FillEllipse
・FillGeometry
・FillMesh (ID2D1Mesh の描画)
・FillOpacityMask (bitmap でマスク付き塗りつぶし)
・FillRectangle
・FillRoundedRectangle

    ID2D1Brush
        色や模様


◎イメージ描画
・DrawBitmap
・(FillOpacityMask)


◎文字
・DrawGlyphRun (IDWriteFontFace)
・DrawText (IDTextFormatIDWriteTextFormat) 2009/02/06 修正
・DrawTextLayout (IDWriteLeyoutIDWriteTextLayout) 2009/02/06 修正

    ID2D1Brush
        色や模様


●形状

形状の操作は Geometry で行います。
その場合も描画同様のプリミティブが使用できます。
多くの操作では同時に 3x2 Matrix (D2D1_MATRIX_3X2_F) を与えることが可能で、
形や座標判定用の命令も用意されています。

・ID2D1EllipseGeometry
・ID2D1PathGeometry
・ID2D1RectangleGeometry
・ID2D1RoundedRectangleGeometry
・ID2D1TransformedGeometry

CombineWithGeometry
 複数の Geometry 同士の合成や演算を行います。

Tessellate
 テセレートし、トライアングル化した状態にアクセスできるようにします。

Widen
 線として描画する場合に、線を太らせた形状を取り出します。


●Mesh

ID2D1Mesh は直接描画データとしてポリゴン (Triangle) を与える場合に使用します。
非常にデバイスに近い低レベルな状態でデータをもてるものと思われます。
おそらく Geometry → Tessellate も Mesh 相当への変換です。


●DirectWrite

DirectWrite はフォント扱うモジュールとして完全に独立しているようです。
描画対象として Direct2D に拘る必要はなく Direct3D など何でも良いとのこと。
Direct3D11 との組み合わせもおそらく出来るでしょう。

当初は Direct2D のデータに変換して出力しているのかと思いましたが、独自の
レンダラを持っているようです。よく考えると ClearType などフォントに依存する
部分が大きいので当然かも。

フォントを扱うためにはデータとして形状を読み込むだけでなく、同時にレイアウト
も必要となります。このあたりのインターフェースは次の通り。

IDWriteFormat → IDWriteLeyout
IDWriteTextFormat → IDWriteTextLayout

Direct2D のテキスト描画も上記 2つの Interface に対応しています。
また文字単位の形状を直接描画することも可能で、それが GlyphRun 系の命令と
なっているようです。


今回はあまりきちんと試しておらず、メモ程度の内容となっています。
間違いなどあったら訂正していきます。


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



こちらで書いたように、Windows7 では Direct3D11 と同じく
Direct3D10.1 でも下位の GPU が新たにサポートされることになりました。

実際にデバイスを作成して試してみました。
今度は Direct2D ではなく、直接 Direct3D10.1 ( D3D10CreateDevice1() ) を
使用しています。

Direct3D10.1
  D3D10_FEATURE_LEVEL_10_0
  D3D10_FEATURE_LEVEL_10_1
  D3D10_FEATURE_LEVEL_9_1
  D3D10_FEATURE_LEVEL_9_2
  D3D10_FEATURE_LEVEL_9_3

すべて成功しました。
これは、今までの Windows Vista + Direct3D10.0 / Direct3D10.1 では
出来なかったことなのです。

まず Windows SDK for Windows 7 BETA を install しなければ、
D3D10_FEATURE_LEVEL_9_1 ~ D3D10_FEATURE_LEVEL_9_3 のシンボルが
定義されていないということ。
Vista 以前で使える現在の DirectX SDK ではコンパイル出来ませんでした。
DirectX SDK Nov2008 の help では、ヘッダファイルにシンボルが無いのに次の
ように書かれていたりします。

D3D10_FEATURE_LEVEL_9_1 
 This value is in the header, but this feature is not yet implemented. 
D3D10_FEATURE_LEVEL_9_2 
 This value is in the header, but this feature is not yet implemented. 
D3D10_FEATURE_LEVEL_9_3 
 This value is in the header, but this feature is not yet implemented. 

この Windows7 用の FEATURE テストプログラムを WindowsVista 上で走らせて
みたところ、全く同じようにデバイスの生成が成功してしまいました。

考えられるのは Windows Vista でも Direct3D SDK の Direct3D11 beta ランタイム
を導入した時点で使えるようになっていたのではないかということ。
Direct3D11 では実際に D3D9_1~3 の下位の FeatureLevel に対応しています。
Direct3D10.1 で同じように利用できてもおかしくないのかもしれません。


過去の DirectX はすべて互換性を保ちながら移行するのが当たり前でした。

 実際に Direct3D 9 は Direct3D 8 のハードウエアでも動作します。

デバイスは作れますし API も呼べますしシェーダーモデル 1 を扱うことが出来ます。
Direct3D9 の特徴であるシェーダーモデル 2~3 は利用できませんが、それは
Direct3D10.1 で Level9 を扱っても全く同じこと。

さらに前、たとえば DirectX7 までは毎年メジャーナンバーがアップデートされる
ことになっていました。わずか 1年で GPU が使えなくなっていたら、とても
ついて行くのが大変ですよね。

DirectX/GPU 年表
DirectX list

互換性を完全に切り捨てたのは Direct3D9 → Direct3D10 のタイミングだけなのです。
ドライバモデルが変わったり OS と融合されたりと、内部的に困難な理由が
いろいろあったのではないかと考えられます。

だから今回 Windows7 と同じタイミングで、Windows7 だけでなく Vista でも
同等の機能がサポートされるのは画期的なことかもしれません。

でも古くから DirectX を触ってるものからすれば画期的でも何でもなくて、
互換性の切り捨てを撤回して、やっぱり以前と同じように使えるようにしました、
といってるだけに見えるのです。


● Windows7 SDK の導入

Windows7 SDK があれば、Vista でも Direct3D10.1 で Direct3D9 GPU 向けの
プログラムを作成できることがわかりました。
DirectX SDK の更新と思って Windows SDK for Windows 7 BETA を早めに導入して
おくのもありかもしれません。
あらかじめ DirectX SDK November 2008 をインストールしておく必要があります。

VisualStudio2008 を起動する前に、スタートメニューから
 Visual Studio Registration → Windows SDK Configuration Tool
を起動して SDK を切り替えておきます。

include パスの順番には注意が必要です。
 Tools → Options → Projects and Solutions → VC++ Directories
 Win32 / Include files

$(DXSDK_DIR)\include よりも上に $(WindowsSdkDir)\include が来るようにします。
DirectX SDK の install で直接 C:\Program Files ~とパスが書き込まれている
ことがあるので、その行は削除して $(DXSDK_DIR)include に置き換えておくことを
お勧めします。
lib も同じようにします。


● Vista で Direct2D が動く

Windows7 SDK beta があれば Direct2D のプログラムを作ることが出来ます。
実際に走らせるには Windows7 beta をインストールした PC が必要です。

Direct2D は結局 Direct3D10.1 を呼び出しているだけのはず。
Direct3D10.1 で下位 GPU もなぜか動くことがわかったので、もしやと思って
Windows7 beta から d2d1.dll だけ vista に持ってきたら拍子抜けするほどあっさり
動作しました。

とりあえず build したプログラムと同じ場所に d2d1.dll と DWrite.dll だけ置いておけば
Vsita でも Direct2D のプログラムを作って走らせることが出来るようです。


関連エントリ
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換



ヘッダを読みながら整理してみました。


● RenderTarget

基本となるのが ID2D1RenderTarget です。いわゆるキャンバスのこと。
各種描画命令を使って RenderTarget の上に絵を描いていきます。
次のバリエーションがあります。

ID2D1RenderTarget
 → ID2D1BitmapRenderTarget
 → ID2D1HwndRenderTarget
 → ID2D1GdiInteropRenderTarget
 → ID2D1DCRenderTarget

それぞれ Bitmap (Texture) に描いたり、そのままウィンドウに描いたりできるということ。
ラインなどプリミティブを描画する命令もありますが、パス(Geometry) を使ったり
DirectWrite を併用してテキストも描くことができます。
描画時に SetTransform() で渡した 3x2 matrix が適用されるようです。
2D の回転、拡大縮小、平行移動などが可能。


● Geometry

Direct2D を特徴付ける存在といえるかもしれません。
ID2D1Geometry はパスや形状などジオメトリを格納します。
無理矢理例えれば Direct3D の VertexBuffer/IndexBuffer のような存在でしょうか。

パスを構成する要素、ラインやベジェ(2次、3次)、Arc を使用することができます。
Geometry の段階で Transform したり合成したり交差を考慮して演算したり、
といった操作が可能。結構自由度が高そうです。
ID2D1Geometry を継承しているインターフェースは次の通り。

ID2D1Geometry
 → ID2D1RectangleGeometry
 → ID2D1RoundedRectangleGeometry
 → ID2D1EllipseGeometry
 → ID2D1GeometryGroup
 → ID2D1TransformedGeometry
 → ID2D1PathGeometry


● Brush

ID2D1Brush は描画時に使用する状態を保持します。マテリアルやレンダーステートを
保持した StateBlock のようなイメージでしょうか。GDI にもあります。
下記の種類があります。

ID2D1Brush
 → ID2D1BitmapBrush
 → ID2D1SolidColorBrush
 → ID2D1LinearGradientBrush
 → ID2D1RadialGradientBrush


大きいのがこの 3つのグループです。
その関係は「 Geometry を Brush を使って RenderTarget に描画する 」ということ。


ID2D1RenderTarget, ID2D1Geometry, ID2D1Brush はどれも ID2D1Resource に
属しています。ID2D1Resource を継承しているインターフェースは他にもあります。

ID2D1Resource
 → ID2D1Bitmap
 → ID2D1GradientStopCollection
 → ID2D1Brush
 → ID2D1StrokeStyle
 → ID2D1Geometry
 → ID2D1Mesh
 → ID2D1Layer
 → ID2D1DrawingStateBlock
 → ID2D1RenderTarget

ID2D1Resource に属していないのは ~ Sink 系と ID2D1Factory のみ。

ID2D1SimplifiedGeometrySink
ID2D1GeometrySink
ID2D1TessellationSink

~Sink はまだよく理解していませんが、Geometry を操作する場合のイテレータの
ような存在ではないかと思われます。


関連エントリ
Direct2D と Direct3D10.1 の下位互換


● Direct2D とは

Direct3D に似ていますが別物です。
DirectDraw とも似ていますが役割が異なります。

現在 DirectX SDK ではなく Windows SDK for Windows 7 の方に含まれます。
下記のページより Windows7 SDK の beta 版を入手することが可能です。

Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: BETA

Direct3D11 と違い WindowsVista では動かないので、動作確認には Windows7 beta が
必要となります。ドキュメントはこちらから。

MSDN Direct2D

Direct2D は Direct3D よりも上位のレイヤに位置します。
レンダーバックエンドとして Direct3D を活用しつつ、ベクターや bitmap 等の 2D
レンダリングを可能とします。Direct3D や DirectDraw のようなハードウエア寄りの
プリミティブではなく、GDI のような高度な描画命令を多数有しています。
SVG/FXG/XAML のようなベクターグラフィックスに対応し、ポリゴンを表示するかわり
に美しい 2D をレンダリングすることに注力した API セットであるといえるでしょう。

現在の Direct2D が利用するのは Direct3D10.1 です。
10.1 といえば WARP によって高速なソフトウエアラスタライズも可能だし、リモート
デスクトップ等で利用可能なCommand Remoting だって使えます。

Direct2D の狙いはまさにここにあります。
ハードウエアアクセラレーション、ソフトウエアラスタライザ、そしてクライアント
サイドのレンダリングなど、2D の描画でもこれらの恩恵を受けられるようになります。


● Direct2D の謎

最初に疑問に思うのは Direct3D10.1 だと使えるハードがかなり限られてくるのでは
ないかということ。
従来 Direct3D10 は専用のビデオカードしか使えず D3D9 世代の GPU とは互換性が
ありませんでした。逆に Direct3D11 の方が下位互換性が強化されており、D3D9 世代の
GPU でも動作します。そのあたりをまとめたのは こちら

どうやら D3D10 でも下位互換がサポートされることになった模様です。
たとえば一番新しい Direct3D SDK November 2008 のヘッダファイル D3D10_1.h を
見ても、下記の通り FEATURE LEVEL には 10 世代の GPU しか記載されていません。

typedef 
enum D3D10_FEATURE_LEVEL1
    {	D3D10_FEATURE_LEVEL_10_0	= 0xa000,
	D3D10_FEATURE_LEVEL_10_1	= 0xa100
    } 	D3D10_FEATURE_LEVEL1;

ところが Windows7 SDK beta 付属の D3D10_1.h を見てみると・・

typedef 
enum D3D10_FEATURE_LEVEL1
    {	D3D10_FEATURE_LEVEL_10_0	= 0xa000,
	D3D10_FEATURE_LEVEL_10_1	= 0xa100,
	D3D10_FEATURE_LEVEL_9_1	= 0x9100,
	D3D10_FEATURE_LEVEL_9_2	= 0x9200,
	D3D10_FEATURE_LEVEL_9_3	= 0x9300
    } 	D3D10_FEATURE_LEVEL1;

いつの間にか下位の FEATURE LEVEL が追加されています。
我慢を強いられた Direct3D10/Vista 世代はいったい何だったのでしょうか。

つまり Direct2D は Direct3D9 世代の古い GPU でもハードウエアアクセラレーション
がかかります。(実際に試してみました→後述)


さらに Direct2D は対応ハードウエアがなくてもソフトウエアラスタライザによって
動作可能と書かれています。これが WARP を指しているのか、それとも Direct2D が
さらに独自でラスタライザを有しているのかわかりません。

ハードウエアアクセラレーションといいつつも、おそらくジオメトリ部分は CPU の
割合がそれなりに高いのではないかと思われます。アンチエリアス等のピクセル合成
部分において GPU が活用されているのではないでしょうか。
Direct3D11 世代になればジオメトリ処理でも GPU の割合が高くなるかもしれません。

以前アウトラインフォントの GPU 描画に取り組んだのはジオメトリも GPU で処理させる
ことが目的でした。


もう一つ疑問に思ったのは Direct2D のインターフェースが最初から D2D1 と "1" が
ついていること。DXGI1 など他の API と同調するためなのか、"2" の登場
(Direct3D11 版?) を想定しているのかわかりません。
DirectWrite には "1" がついていませんでした。


●下位互換性の確認

3台とも Windows7 beta

・DesktopPC RADEON HD4850 (Direct3D10.1 ShaderModel4.1) (Aero 有効)
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING

・EeePC901 945GE GMA950 (Direct3D9 ShaderModel2.0) (Aero 有効)
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING

・VAIO type P US15W GMA500 (Direct3D9 ShaderModel3.0) (Aero 無効)
  × D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING

Aero 無効かつ D3D10/11 全滅の GMA500 では HARDWARE_RENDERING だと
動きません。SOFTWARE_RENDERING では動作しました。
このことから HARDWARE/SOFTWARE のフラグ設定は機能しているものと思われます。
GMA950 の EeePC では両方動作しているので、ShaderModel2.0 でも Direct2D で
ハードウエアアクセラレーションが有効になっているのではないかと考えられます。
つまり Windows7 では Direct3D10.1 で ShaderModel2~3 が動いているということ。
直接 Direct3D10.1 を試した方が早かったかもしれません。


関連エントリ
Intel GMA500 のスペックについて考える。続き (2)
Windows7 リモートデスクトップと Direct3D
Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)
Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
Direct3D11 Technical Preview D3D11の互換性、WARP Driver