月別アーカイブ: 2008年11月

Direct3D11/DirectX11 (16) GPU を使ったアウトラインフォントの描画の(4)

結局 GeometryShader だけで描画できました。
これだと D3D11 でもハードウエアアクセラレートかかるので
回転アニメーションとかも余裕。

tess font gs

入力頂点は、最後の1つを複製して 6頂点単位に変更。
それぞれが float2 × 4 なのは変わらず。

・エッジ x4
・中心座標
・ダミー

描画 TOPOLOGY は D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ に変更。
これを triangleadj で受け取れば 6 頂点入力ができます。

HS/DS を NULL にして代わりに GS を設定するだけ。

iContext->RSSetState( iRS_WIREFRAME );
UINT	stride= sizeof(float)*2*4;
UINT	offset= 0;
iContext->IASetVertexBuffers( 0, 1, &iVBuffer, &stride, &offset );
iContext->IASetInputLayout( iLayout );
iContext->VSSetShader( iVS, NULL, 0 );
iContext->GSSetConstantBuffers( 0, 1, &iBufferV );
iContext->GSSetShader( iGS, NULL, 0 );
iContext->PSSetShader( iPS, NULL, 0 );
iContext->OMSetRenderTargets( 1, &iRenderTargetView, iDepthStencilView );
iContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ );
iContext->Draw( vdata0_size*sizeof(float)/(sizeof(float)*2*4), 0 );

各エッジを最大 8 分割と仮定すると最大
8 triangle × 3 × 4edge = 96 頂点出力される可能性があります。

各シェーダーを vs_4_0 gs_4_0 ps_4_0 向けにコンパイルすると
RADEON HD4850 で HARDWARE 動作しました。API は D3D11 のまま。

// gs_4_0
struct VS_OUTPUT {
	float2	vPos0	:	VSPOS0;
	float2	vC0	:	VSPOS1;
	float2	vC1	:	VSPOS2;
	float2	vPos1	:	VSPOS3;
};

cbuffer pf {
	float4x4	WVP;
};

struct DS_OUTPUT {
	float4	vPos	: SV_Position;
};

void addv( float2 pos, inout TriangleStream stream )
{
	DS_OUTPUT	dout;
	dout.vPos= mul( float4( pos.xy, 0, 1 ), WVP );
	stream.Append( dout );
}

float2 UVtoPositionB( const VS_OUTPUT p, float t )
{
	float	t2= 1.0-t;
	float4	u= float4( t2*t2*t2, t2*t2* t*3, t2* t* t*3, t* t* t );
	return	 u.x*p.vPos0.xy +u.y*p.vC0.xy +u.z*p.vC1.xy +u.w*p.vPos1.xy;
}

void Curve4( float2 center, const VS_OUTPUT v,
			inout TriangleStream stream )
{
	const int	step= 8; // = TessFactor
	float	t= 0;
	float	tstep= 1.0/step;
	float2	prevpos= UVtoPositionB( v, 0 );
	for( int i= 0 ; i< step ; i++ ){
		t+= tstep;
		float2	pos= UVtoPositionB( v, t );
		addv( prevpos, stream );
		addv( pos, stream );
		addv( center, stream );
		stream.RestartStrip();
		prevpos= pos;
	}
}

void Edge( float2 center, const VS_OUTPUT v,
			inout TriangleStream stream )
{
	if( v.vC0.x > -1e4 ){
		Curve4( center, v, stream );
	}else{
		// 直線の場合
		addv( v.vPos0, stream );
		addv( v.vPos1, stream );
		addv( center, stream );
		stream.RestartStrip();
	}
}

// step(8) x 3 x 4 = 96
[maxvertexcount(96)]
void main(
	triangleadj VS_OUTPUT input[6],
	inout TriangleStream stream )
{
	Edge( input[4].vPos0, input[0], stream );
	Edge( input[4].vPos0, input[1], stream );
	Edge( input[4].vPos0, input[2], stream );
	Edge( input[4].vPos0, input[3], stream );
}

最適化していないので、四隅と中心の頂点の計算が重複したままです。

関連エントリ
Direct3D11/DirectX11 (15) GPU を使ったアウトラインフォントの描画の(3)
Direct3D11/DirectX11 (14) GPU を使ったアウトラインフォントの描画の(2)
Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など
Direct3D11/DirectX11 (12) テセレータのレンダーステート他
Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
Direct3D11/DirectX11 (10) テセレータの補間
Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
Direct3D11/DirectX11 (8) テセレータの動作
Direct3D11/DirectX11 (7) テセレータの流れの基本部分

Direct3D11/DirectX11 (15) GPU を使ったアウトラインフォントの描画の(3)

↓手書きのフォントもベクタ表示
tess font 40

続きです。
前回表示したアウトラインフォント のシェーダー側の動作となります。

入力データは quad のエッジ情報です。通常は VertexShader に頂点情報を渡しますが、
接続する 2頂点+2コントロールポイント座標を一組としてシェーダーに渡します。

1つの quad を描画するためには 4辺分+中心座標が必要なので、これが 5組あります。
よって描画データは下記の通り。

// 1 quad 分
v0 c0 c1 v1
v0 c0 c1 v1
v0 c0 c1 v1
v0 c0 c1 v1
g0 0  0  0

 v0= x,y 頂点
 c0= x,y コントロールポイント
 g0= x,y 中心座標
 0= 0,0 未使用

ラインの場合コントロールポイントが不要なので c0, c1 は使いません。
中心座標は x,y のみなので、残りは全部 0 です。

カーブの場合: v0 c0 c1 v1
ラインの場合: v0 -1e5 0 v1

例
249.593597f,395.421600f,  302.652008f,346.582397f,  341.562408f,290.375214f,  366.312805f,226.812805f,
366.312805f,226.812805f,  -1e5f,0,  0,0,  324.124786f,219.500000f,
324.124786f,219.500000f,  298.250397f,282.316803f,  263.750397f,333.031189f,  220.624802f,371.656006f,
220.624802f,371.656006f,  -1e5f,0,  0,0,  249.593597f,395.421600f,
293.008575f,305.777374f,  0,0, 0,0, 0,0,

IA に渡す頂点の定義は次の通り。float2 × 4

D3D11_INPUT_ELEMENT_DESC	ldesc[]= {
{  "VPOS", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0,
				D3D11_INPUT_PER_VERTEX_DATA, 0	},
{  "VPOS", 1, DXGI_FORMAT_R32G32_FLOAT, 0, sizeof(float)*2,
				D3D11_INPUT_PER_VERTEX_DATA, 0	},
{  "VPOS", 2, DXGI_FORMAT_R32G32_FLOAT, 0, sizeof(float)*4,
				D3D11_INPUT_PER_VERTEX_DATA, 0	},
{  "VPOS", 3, DXGI_FORMAT_R32G32_FLOAT, 0, sizeof(float)*6,
				D3D11_INPUT_PER_VERTEX_DATA, 0	},
};

これが 5 個なので D3D11_PRIMITIVE_TOPOLOGY_5_CONTROL_POINT_PATCHLIST
で描画します。

Vertex Shader は素通り

struct VS_INPUT {
	float2	vPos0	: VPOS0;
	float2	vC0	: VPOS1;
	float2	vC1	: VPOS2;
	float2	vPos1	: VPOS3;
};
struct VS_OUTPUT {
	float2	vPos0	: VSPOS0;
	float2	vC0	: VSPOS1;
	float2	vC1	: VSPOS2;
	float2	vPos1	: VSPOS3;
};

VS_OUTPUT main( VS_INPUT idata )
{
	VS_OUTPUT	odata;
	odata.vPos0= idata.vPos0;
	odata.vPos1= idata.vPos1;
	odata.vC0= idata.vC0;
	odata.vC1= idata.vC1;
	return	odata;
}

Hull Shader はカーブかラインか区別して異なる TessFactor を設定します。

#define	MAX_POINTS	5

struct VS_OUTPUT {
	float2	vPos0	: VSPOS0;
	float2	vC0	: VSPOS1;
	float2	vC1	: VSPOS2;
	float2	vPos1	: VSPOS3;
};

struct HS_OUTPUT {
	float2	vPos0	: HSPOS0;
	float2	vC0	: HSPOS1;
	float2	vC1	: HSPOS2;
	float2	vPos1	: HSPOS3;
};


struct HS_DATA {
	float	Edge[4]		: SV_TessFactor;
	float	Inside[2]	: SV_InsideTessFactor;
};

HS_DATA func( InputPatch ip )
{
	HS_DATA	pdata;
	const float	tf= 8.0;
	const float	lineFlag= -1e4;
	// ラインを区別する
	pdata.Edge[3]= ip[0].vC0.x > lineFlag ? tf : 1.0f;
	pdata.Edge[2]= ip[1].vC0.x > lineFlag ? tf : 1.0f;
	pdata.Edge[1]= ip[2].vC0.x > lineFlag ? tf : 1.0f;
	pdata.Edge[0]= ip[3].vC0.x > lineFlag ? tf : 1.0f;
	// 内部は 2固定
	pdata.Inside[0]= 2.0;
	pdata.Inside[1]= 2.0;
	return	pdata;
}

[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(5)]
[patchconstantfunc("func")]
HS_OUTPUT main(
	InputPatch ip,
	uint id : SV_OutputControlPointID
	)
{
	HS_OUTPUT	odata;
	odata.vPos0= ip[id].vPos0;
	odata.vPos1= ip[id].vPos1;
	odata.vC0= ip[id].vC0;
	odata.vC1= ip[id].vC1;
	return	odata;
}

Doman Shader は生成された頂点がどのエッジか区別して、それぞれ異なるパラメータ
補間を適用します。
InsideTessFactor が 2.0 なので、Tessellator で分割されるのはエッジだけです。
上下左右のエッジは

・u が 0.0
・u が 1.0
・v が 0.0
・v が 1.0

で区別することができます。上のどれでもない場合は中心座標です。

例として 1辺だけ TessFactor[0] = 4 の場合を考えます。
このとき生成される頂点の uv は下記の 8点となります。

四隅
(0.0, 0.0)
(1.0, 0.0)
(0.0, 1.0)
(1.0, 1.0)

中心
(0.5, 0.5)

分割で挿入された点
(0.0, 0.25)
(0.0, 0.50)
(0.0, 0.75)

よって u が 0.0 の場合を区別すれば必要なエッジだけ取り出せることがわかります。

#define	CONTROL_POINT	5

struct HS_DATA {
	float	Edge[4]		: SV_TessFactor;
	float	Inside[2]	: SV_InsideTessFactor;
};

struct HS_OUTPUT {
	float2	vPos0	: HSPOS0;
	float2	vC0	: HSPOS1;
	float2	vC1	: HSPOS2;
	float2	vPos1	: HSPOS3;
};

struct DS_OUTPUT {
	float4	vPos	: SV_Position;
};

cbuffer pf {
	float4x4	WVP;
};

// カーブの場合
float2 UVtoPositionB( const HS_OUTPUT p, float t )
{
	float	t2= 1.0-t;
	float4	u= float4( t2*t2*t2, t2*t2* t*3, t2* t* t*3, t* t* t );
	return	 u.x*p.vPos0.xy +u.y*p.vC0.xy +u.z*p.vC1.xy +u.w*p.vPos1.xy;
}

// 直線の場合
float2 UVtoPositionL( const HS_OUTPUT p, float ux )
{
	return	p.vPos0 * (1-ux) + p.vPos1 * ux; // 実際は不要
}

float2 UVtoPosition( const HS_OUTPUT p, float ux )
{
	if( p.vC0.x < -1e4 ){
		return	UVtoPositionL( p, ux );
	}
	return	UVtoPositionB( p, ux );
}

[domain("quad")]
DS_OUTPUT main(
		HS_DATA ip,
		float2 uv : SV_DomainLocation,
		const OutputPatch bpatch
	)
{
	DS_OUTPUT	dout;
	const float	EPC= 0.0f; // 誤差が問題になるなら増やす
	float2	pos= float2( 0, 0 );
	// 各エッジを切り分ける
	if( uv.y >= 1.0-EPC ){
		pos= UVtoPosition( bpatch[0], uv.x );
	}else if( uv.y <= 0.0+EPC ){
		pos= UVtoPosition( bpatch[2], 1-uv.x );
	}else if( uv.x >= 1.0-EPC ){
		pos= UVtoPosition( bpatch[1], 1-uv.y );
	}else if( uv.x <= 0.0+EPC ){
		pos= UVtoPosition( bpatch[3],   uv.y );
	}else{
		pos= bpatch[4].vPos0.xy; // 中心
	}
	dout.vPos= mul( float4( pos.xy, 0, 1 ), WVP );
	return	dout;
}

エッジの切り分けは、四隅の頂点が共有されている点に注意です。
どの uv 値をどのエッジに割り当てるのかは任意で構いませんが、四隅が共有されている
関係上、エッジの並びが連続していなければカーブが不連続になります。

またエッジ単位に Tess Factor のスケールを入れれば、長い辺など必要な場所のみ
より細かく分割するなどの応用ができるでしょう。

前回のアルゴリズムはまだ完全に形状を分割できるとは限らず、いくつか問題が生じる
ケースがあります。

(1) 分割時に対応する頂点が見つけられない場合がある
(2) アウトラインからはみ出さない位置に中心頂点を配置できない場合がある

このような状態に陥った場合、対象のプリミティブのエッジを分割して
頂点(アンカー)を追加することで対処可能です。

シェーダーを書き出していて気がつきましたが、これ GeometryShader だけでも実現
できそうですね。GeometryShader には 5 頂点入力がないので、頂点数さえ合わせれば
ハードウエアで実行できそうです。

関連エントリ
Direct3D11/DirectX11 (14) GPU を使ったアウトラインフォントの描画の(2)
Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など
Direct3D11/DirectX11 (12) テセレータのレンダーステート他
Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
Direct3D11/DirectX11 (10) テセレータの補間
Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
Direct3D11/DirectX11 (8) テセレータの動作
Direct3D11/DirectX11 (7) テセレータの流れの基本部分
Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
Direct3D11 Technical Preview D3D11の互換性、WARP Driver

Direct3D11/DirectX11 (14) GPU を使ったアウトラインフォントの描画の(2)

Direct3D 11 の Tessellator を使って、GPU だけでアウトラインフォントの描画を
行ってみました。
↓左からそれぞれ TessFactor (分割数) = 1, 2, 12

tess font 20

改良しました。

(1) 事前の変換処理を自動化した。
(2) isoline ではなく quad プリミティブを使ったテセレートを行っており、
  必要に応じて任意のエッジをテセレートできる。

まだ (1) がやっとなのであまり最適化していません。

前回
Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など

tess font 21

↑左が前回のもの。2点 X-Y を挿入し、赤 (B-C-X-Y) と 緑 (D-E-X-Y) の 2つの
プリミティブに分解。

右が今回のもの。B-C-D-E で 1つの quad で表現され、必要なエッジのみ分割。
中央の X の位置は事前に求めておく

塗りつぶしたフォントの描画のために、事前にプリミティブに分割します。
思ったよりも難しい処理で、特に細長い三角形ができてしまうとアウトラインから
はみ出します。

tess font 22

↑では波線が本来のカーブです。A の上のエッジが沈み込んで対角線に重なっています。
カーブが極端になると対角線エッジの直線部分が表に出てしまいます。
(前回はこれをうまく解決できなかったので手作業だった)

いろいろと試行錯誤した結果がこれ↓です。

tess font 24
↑ましになったもの。

元↓はこちら。

tess font 23
↑だめな分割 (はみ出ているのはバグ)。

(白線=カーブ、青線=直線、ピンクの点=中心の頂点、水色の点=コントロールポイント)

(1) 角度が一番開いた頂点を求める。
(2) (1) の頂点から直線を引ける他の頂点を探す。
  (エッジと交差しない、かつ隣接でない、かつ文字の外側ではない)
(3) (2) の条件を満たす頂点の中で一番近い頂点と結ぶ。エッジを挿入して領域を分ける
(4) 4 頂点以下の領域ができたら取り除く。

すべての領域が取り除かれるまで再帰的に繰り返します。
比較的細長い領域への分割を避けてぶつ切りになります。

内外判定のために方向をそろえる必要があるため挿入したエッジは両面分必要です。

循環している可能性があるので、(3) で単一のエッジを挿入しても 2つの領域が
できるとは限りません。

(4) の場合くりぬいた領域が裏返しで繋がっている可能性があるため、4頂点以下の
パスのループでも閉じているかどうか判定が必要です。(対角線の可視など)
上の「だめな分割」の図は失敗しています。

領域毎に、アウトラインをじゃましない位置に中心座標を配置します。(上の図のピンクの点)
ただしアンカーが少なくカーブが極端な場合、最適な中心座標は求まらない可能性が
あります。まだ完全には解決していません。

tess font 25
↑ TessFactor = 8 のワイヤー表示です。

●データの準備

xml で対応ツールが比較的多いことと参照しやすいため、SVG / FXG フォーマットを
使用しています。
Scalable Vector Graphics (SVG) 1.1 Specification

含まれるカーブの種類は複数あり、直線部分も Line で出力されるので、変換時に
情報を保存しておきます。
現在エッジ情報は 座標ペア × 4 (Cubic Bezier時) で、Arc は未対応です。

v0.xy, c0.xy, c1.xy, v1.xy

Line 時は c0.xy, c1.xy は未使用。c0.x に範囲外の値を入れて、シェーダー内で
未使用(直線)かどうか判断できるようにしています。

(1) カーブの種類をを考慮してパスデータを 4 パラメータに変換
(2) エッジの方向をノーマライズする

(2) は内外判定のため。進行方向 v0 → v1 に向かって右側が必ず文字の外側など、
どちらかに統一されていれば OK。

次回に続きます。

関連エントリ
Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など
Direct3D11/DirectX11 (12) テセレータのレンダーステート他
Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
Direct3D11/DirectX11 (10) テセレータの補間
Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
Direct3D11/DirectX11 (8) テセレータの動作
Direct3D11/DirectX11 (7) テセレータの流れの基本部分
Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
Direct3D11 Technical Preview D3D11の互換性、WARP Driver

Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など

●TessFactor と方向の対応

テセレータによる分割数は Hull Shader で指定します。

float	Edge[4]  : SV_TessFactor;
float	Index[2] : SV_InsideTessFactor;

quad の場合上記 6パラメータで指定します。このとき各 TessFactor とテセレータ
出力の uv 値との対応は下記の通り。

                    u          v
SV_TessFactor[0]    0.0        0~1.0
SV_TessFactor[1]    0~1.0     0.0
SV_TessFactor[2]    1.0        0~1.0
SV_TessFactor[3]    0~1.0     1.0

例えば u方向を右向き → 、v方向を上向き ↑ として 2D でレンダリングすると
TessFactor[0][1][2][3] は 左、下、右、上 の順番となります。

 (0,1)  [3]   (1,1)
  ┌─────┐
  │     │
  │     │
 [0]│     │[2]
  │     │
  │     │
  └─────┘
 (0,0)  [1]   (1,0)

InsideTessFactor は下記の通り。

SV_InsideTessFactor[0] uの分割 (u=0~1.0)
SV_InsideTessFactor[1] vの分割 (v=0~1.0)

● Dynamic Shader Linkage の補足

Dynamic Shader Linkage には 2つの大きな変更があります。

(1) シェーダー内の命令の分岐先を外部から切換えられるようになった
(2) HLSL の記述で java 風の class 定義ができるようになった

表面的には (2) の方が大きいように見えますが、本来の目的は動的シェーダーリンク
という名前の通り (1) の方です。
むしろ (1) を実現するために、さまざまな候補の中から (2) の実装方法を選択した
だけだといえるかもしれません。
(詳しくはこちら Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage)

シェーダーによって、ハードウエアのレンダリング動作を自分で記述することが
できるようになりました。
自由度が高まる反面、動作の種類毎に個別の異なるシェーダープログラムを用意
しなければならないという問題も生じました。

例えばライティングアルゴリズムによって数種類。基本的なものだけでも

・環境光源
・スカイライト
・平行光源
・点光源
・スポットライト

等複数存在します。
複数光源を照射する場合、オブジェクトの配置位置によって光源の組み合わせが変わる
かもしれません。例えば屋外だと平行光源が必要ですが屋内だと必要なかったりします。

・屋外用光源シェーダー
 太陽平行光源+環境スカイライト+エフェクト点光源

・屋内用光源シェーダー
 環境光源+点光源×1~4

など。

マテリアルによってはテクスチャをレイヤーで複数重ねたり、それぞれのブレンド式
を変えるかもしれません。
ノーマルマップやレリーフマップを持つ場合、付随して頂点に必要な情報も増えます。

他にも各レイヤー毎に独立した Texcoord (uv) を持たせるのか、テクスチャによって
は共有するのか、パラメータで生成するのかなど。

ゲームで実際に使うことを考えると、法線マップを圧縮したり、アルファチャンネルの
用途をマテリアルによって使い分けたり、場面によって精度を落としたり復号したり、
半透明やテクスチャアルファによる抜きの扱いでシェーダーを分けたり。
影のレンダリングのありなしや、ライトスペースの求め方の違い、フィルタリングの
種類による変更など。

VertexShader でもアニメーションによって、頂点ウエイトの個数、ビルボード化、
ジオメトリインスタンシングへの対応など種類が必要です。

これらをすべて自由に組み合わせられるような汎用的な描画の仕組みを作ると
シェーダーの数は爆発的に増えます。

もちろんシェーダープログラムも外部の情報へアクセスはできますし、分岐やループ
命令もありました。しかしながらシェーダーの組み合わせをによるバリエーション数を
減らすために用意された機能ではなかったことが問題でした。

マテリアル(描画命令)単位で大きな処理の切換えをしたいだけなのに、コード内で
分岐を行うのは少々大げさでです。
最適化の妨げになるし条件判断コードの実行によるパフォーマンスへの影響もあった
ので、どちらを取るか選択を迫られます。
(シェーダーを切換えるための負荷が減るメリットはあります)

Direct3D 11 (Shader Model 5.0) の Dynamic Shader Linkage の導入は
これらの問題を解決するために設けられたものです。
シェーダーのバリエーションを減らして管理しやすくすることが目的で、プログラム内
の命令ジャンプの飛び先を外部から切換えることが可能になりました。

分岐切り替えの粒度も不必要に細かくなく適切で、レンダリング命令単位(マテリアル単位)
で決められるし、その条件判断もシェーダーの外部で一度だけやればいいわけです。

期待が大きい反面、気になる点もあります。

・動的リンクで切換え可能なコードは予め 1つのシェーダーにコンパイルされている必要がある
・最適化への影響

オブジェクトのインスタンスも単なる Buffer へのマッピングに過ぎず、すべて静的に
決まってなければなりません。
非常に巨大な単一のシェーダープログラムができあがる可能性があります。

分岐先の決定は描画時まで遅延されるため、その分 HLSL コンパイラでできる最適化は
減少します。間接ジャンプはインライン化を阻害しますし、結局サブルーチン分の
消費レジスタも予めアロケートしておかなければならないのではないか、との疑問も
生じます。

シェーダーの単なる分岐と本当に違うのか、効果があるのかどうか、
動的リンクによるパフォーマンスの影響はどの程度なのか、
実ハードが出てからあらためて検証する必要がありそうです。

ちなみに D3D11 付属の HLSL コンパイラでは、class 定義だけなら ShaderModel 4.0
以前をターゲットにしても使えます。あくまで表記上の違いだけなので、できることは
今までと変わりません。
動的な飛び先の切り替え (interface 経由の呼び出し) は ShaderModel 5.0 以降のみ
となります。

関連エントリ
Direct3D11/DirectX11 (12) テセレータのレンダーステート他
Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
Direct3D11/DirectX11 (10) テセレータの補間
Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
Direct3D11/DirectX11 (8) テセレータの動作
Direct3D11/DirectX11 (7) テセレータの流れの基本部分
Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
Direct3D11 Technical Preview D3D11の互換性、WARP Driver

Direct3D11/DirectX11 (12) テセレータのレンダーステート他

●レンダーステート

テセレータにはレンダーステートがありません。
ID3D11DeviceContext::TSSet~ 等の命令は一切無し。
その代りになるのが Hull Shader のたくさんある attribute です。

[domain("tri")]
[partitioning("fractional_even")]
[outputtopology("triangle_ccw")]
[outputcontrolpoints(4)]
[patchconstantfunc("func")]
float4 main(
	InputPatch ip,
	uint id : SV_OutputControlPointID
	) : TSPOS
{
   ...
}

HullShader の attribute は単なるヒントではなく、Tessellator の動作そのものを
決定しています。
テセレータの動作を変えるにはシェーダーまるごと置き換える必要があり、レンダー
ステートとシェーダーが一体化しているともいえます。

今までのシェーダーからは少々特殊ですが、管理するオブジェクトが減るので扱いは
楽です。他のシェーダーでも、シェーダーとペアで使うことがわかりきったステートなら
シェーダーに含めてしまって構わないのかもしれません。

●Effect

ShaderModel 5.0 にはまだ Effect ( .fx, fx_5_0 ) が無いようです。
現在のベータ SDK では、core API を使って各シェーダーを個別に生成したり管理する
必要があります。
Effect (ID3D10Effect) を core API 化してしまった Direct3D10 と比べると
何となくトーンダウンしているようにも見えます。

これが方針転換なのか、単に未完成で beta に含まれていないだけなのかまだわかりません。
シェーダー数が増えて大変そうだし後者のような気もします。
だけど Shader Compiler の分離もあるので、D3D10 の反省を踏まえての仕様変更の可能性もあります。
ツールやゲームエンジン等の設計にも関わってくるので、今後どうなるのか気になるところです。

●Tessellator と Triangle patch

Tessellator が扱える patch のタイプには isoline tri quad の 3種類あります。
(Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など)

quad / isoline はこれまでなんどか取り上げたとおり、テセレータは
float2 の DomainLocation (uv) を出力します。
tri の場合は 3次元の uvw を返します。

この u v w はそれぞれ、Triangle の 3頂点に対する割合(距離)を示して
います。(barycentric coordinates)

float3 v1, v2, v3;
pos= v1*uvw.x + v2*uvw.y + v3*uvw.z;

↓左から TessFactor = 222-2、333-3、444-4

tess tri

中 (InsdeTessFactor) を 2に固定して外側だけ割った場合。
↓左から TessFactor = 111-2、888-2、181-2

tess tri

右端(181-2)は、1 つのエッジだけ 8分割したものです。
以前アウトラインフォントの描画では quad あたり 1辺の分割に限定して isoline
を使いました。事前処理でこのようなプリミティブにきちんと分解できれば、
一度に複数のエッジを分割対象とすることも可能です。
まだまだ効率化はできるでしょう。

関連エントリ
Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
Direct3D11/DirectX11 (10) テセレータの補間
Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
Direct3D11/DirectX11 (8) テセレータの動作
Direct3D11/DirectX11 (7) テセレータの流れの基本部分
Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
Direct3D11 Technical Preview D3D11の互換性、WARP Driver