Archives

July 2007 の記事

WindowsVista SP1 で対応するらしい Direct3D 10.1 ですが、
今の 10.0 対応ビデオカードでそのまま 10.1 の新機能が
使えるとは限りません。(たぶん使えなさそう)
といっても 10.0 ハードに対応しないわけにもいかないでしょう。

D3D10CreateDevice1() で Device のインターフェースを作成するときに、
・D3D10_FEATURE_LEVEL_10_1
・D3D10_FEATURE_LEVEL_10_0
と2種類のパラメータ指定ができるようです。

ということは、10.1 がリリースされた場合は今後 SDK 上は 10.1 を
使っておき、初期化で 10.0 or 10.1 を切り替えることになるのでしょうか。

ShaderModel4.1 についてはまだ何もわかりません。
実行環境が無いとコンパイルもできないので、調べようにも手が出せない
状態です。


ちなみに DirectX SDK August2007 のバージョンは 9.20 1057 でした。

久しぶりに、以前調べた SDK 一覧に追加してみると
DirectX SDK April2007 とバージョン番号

9.19 / 1005.0000 Jun2007
9.19 / 1007.0000 Jun2007 07/10版
9.20 / 1057.0000 Aug2007 (10.1 TechPreview)

こうなります。Jun2007 も 2種類あって、今ダウンロードできるものは
7月版に入れ替わっています。


D3D10 で StreamOutput を使う場合、ID3D10GeometryShader の作成に
ID3D10Device::CreateGeometryShader() ではなく
ID3D10Device::CreateGeometryShaderWithStreamOutput() を使います。

Effect(fx) を使用している場合も、Effect(fx) ファイルの中で
 CompileShader() → ConstructGSWithSO()
の流れで、StreamOutput 対応 GeometryShader を作成します。
この辺はほとんどサンプルの記述どおりです。

出力フォーマットの記述など、扱いは Effect ファイルの方が楽なので、

・Effect(fx) ファイルに ConstructGSWithSO() で記述
・ID3D10Effect 作成
・GeometryShader のインターフェース取り出し

の手順で、WithStreamOutput の GeometryShader を簡単に取り出す
ことができます。

普段 GeometryShader を使わない場合は NULL 省略できますが、
StreamOutput では上記方法で GeometryShader を作らなければなりません。

少々不思議なのは D3D10 のサンプルでは、GeometryShader が不要な場合に
ConstructGSWithSO() にコンパイルした VertexShader を渡していること。

例えばこんな感じで使われており、実際にコンパイルしてみても
GeometryShader に VertexShader が入っていました。

VertexShader vs_transform= CompileShader( vs_4_0, VS_Transform() );
GeometryShader gs_transform= ConstructGSWithSO( vs_transform,
  "POSITION.xyzw;NORMAL.xyz;TEXCOORD.xy" );

StreamOutput 先のバッファを指定するには ID3D10Device::SOSetTargets()
を使います。また同時に現在 SO Stage が有効かどうかの切り替えもこの
SOSetTargets() で行われています。

D3D10_BIND_STREAM_OUTPUT で作った ID3D10Buffer を渡すか NULL を渡すか、
これだけです。

StreamOutput を使う場合は PixelShader を通らないので、PixelShader には
NULL を設定しなければなりません。
さらに Depth/Stencil Buffer が有効になっていると、Pixel Pipeline が
有効とみなされるようです。

StreamOutput 時は Depth/Stencil test を Disable しなければなりません。
この切り替えはもちろん ID3D10DepthStencilState です。

・Shaderやバッファ作成
 CreateGeometryShaderWithStreamOutput()
 CreateDepthStencilState()
 CreateBuffer( D3D10_BIND_STREAM_OUTPUT )

・描画時
 OMSetDepthStencilState( ZENABLE= D3DZB_FALSE , ZWRITEENABLE= FALSE )
 PSSetShader( NULL )
 SOSetTargets( iBuffer )
 Draw( ... )
 SOSetTargets( NULL )


バッファ内の StreamOutput 位置は SOSetTargets() の Offset で
指定することができます。

位置が重ならないようにリソースを作ったり Stream に入れたりしても
やはり入出力に同じ Buffer を与えることはできませんでした。

ちなみに GeometryShader で複数頂点を入力して Triangle 出力すると
Index が展開されるので、DrawIndexed() 用の mesh データでも
Draw() での描画になります。
Triangle の増減も可能なのだから仕方ないですが、頂点 Cache の効率を
考えると少々もったいないですね。

GeometryShader が不要で頂点単位の事前変換だけ行う場合は、
StreamOutput 時に POINTLIST を使います。
この場合 Triangle の増減はできないものの、出力頂点数は変化ないので
事前変換しつつ DrawIndexed() を使うことができます。


DirectX SDK August 2007 がもう公開されているとのことです。早い!
新 masafumi's Diary, 8月号
しかも Direct3D 10.1 Tech Preview が入ってるとのこと。
ざっと見てみました。

変更点など詳細はこちら
DirectX Software Development Kit

ShaderModel 4.1 の追加と、レンダリング関連の強化が行われるようです。
まだ詳細も不明で、使えるようになるのもまだまだ先でしょうが
これは楽しみです。以下ドキュメントに書いてなさそうな点です。

追加されたインターフェース名は最後に"1"がついています。

ID3D10BlendState1
ID3D10ShaderResourceView1
ID3D10Device1

10.1 のシンボル定数は D3D10_1_~ といった感じ。

作成は D3D10CreateDevice1() / D3D10CreateDeviceAndSwapChain1()


●BlendState1

10.0 では最大8枚の出力先に対して、
・BlendEnable
・RenderTargetWriteMask
を個別に指定することができました。Blend を使うかどうか、
書き込むチャンネルのみ独立して指定でき、合成方法は全プレーン共通です。

どうやら 10.1 では、Src/Desc 等のブレンドモードも RenderTarget
毎に別パラメータを設定できるようになりそうです。


●ShaderResourceView1

ShaderResourceView1 ではドキュメントにも書かれているように
TextureCubeArray が追加されています。
10.0 では Texture1D/2D/2DMS だけが Array 対象でした。
Cube の Array というと ShadowMap などに便利かもしれません。
Texture の Load コマンドのアドレッシングが 5次元になってしまうので
引数が追加されるのでしょうか。


インターフェースを ~1 に、
やヘッダ d3d10.h → d3d10_1.h の置き換え、
D3D10CreateDeviceAndSwapChain1() で D3D10_FEATURE_LEVEL_10_1 指定、
ライブラリ d3d10_1d.lib 追加等で一応 10.1 の Build はできます。
が、d3d10_1core.dll が無いので起動できませんでした。


Vertex として Stream で読み込んでいる Buffer を、
ShaderResourceView で同時にランダムアクセスすることができました。
これはいいね、使えそう。

可能
・Stream + ShaderResourceView

不可能
・StreamOutput + ShaderResourceView
・StreamOutput + StreamInput

これらのパラメータ組み合わせは D3D10_CREATE_DEVICE_DEBUG 時は
かなり細かくチェックしてくれて詳細な説明とともにエラーが出ます。

若干気をつけないといけないのはパラメータ設定の順番です。


IASetVertex BufA
SOSetTarget BufB
Draw

IASetVertex BufB
SOSetTarget BufC
Draw

SOSetTarget NULL


一見問題ないように見えますが、2つ目の IASetVertex BufB では、
BufB がまだ StreamOutput 対象だからだめだと怒られます。

先に SOSetTarget BufC を設定しなおして BufB を切り離すか、
間に SOSetTarget NULL が必要となります。


IASetVertex BufA
SOSetTarget BufB
Draw

SOSetTarget BufC
IASetVertex BufB
Draw

SOSetTarget NULL


同様のことは ShaderResource や StreamInput 側にも言えます。
特に Stream の 2~3 番目に設定された Buffer は NULL による解放を
を忘れがちなので、あとから StreamOutput の Target にしようとすると
エラーです。

ミスを無くすためには Set 系には毎回こまめに NULL を入れた方が
いいようです。


それにしても Direct3D10 + ShaderModel4.0 は、
こうしなければいけないっていう変な制限が少なく、
Buffer やら InstanceID とか組み合わせたり、
結構自由に考えたとおりに動いてくれるのが気持ちいいですね。
(速度や効率はおいといて)


d3d10.h (June2007) を見ると、
D3D10_16BIT_INDEX_STRIP_CUT_VALUE や
D3D10_32BIT_INDEX_STRIP_CUT_VALUE と一緒に、
D3D10_8BIT_INDEX_STRIP_CUT_VALUE なるシンボルが定義してあります。
これだけみるとなんだか 8bit IndexBuffer が使えそうです。

ID3D10Device::IASetIndexBuffer
マニュアルにはだめ (16bit or 32bit) と明記されてますが、さすが
Unified Shader だなと思って R8_UINT を試してみました。

結果はだめでディスプレイドライバがハングアップし、ドライバリセットが
かかります。
画面にプリミティブ形状は出るので、一応中で変換するのか
またはハード上バッファは読めるが API で禁止しているのかわかりません。
追求はしておりません。

Shader が自分で Buffer を読み進めれば似たようなことはできるのですが。

STRIP_CUT_VALUE は Strip 中にプリミティブの区切りを指定する特殊な
Index 値です。D3D9 までは頂点を同じ座標に何度も重ね書きすることで
Jump してましたが D3D10 では扱いが簡単になりました。


HLSL で記述した Effect(fx) ファイルには複数の technique を記述して
おくことができます。
またそれぞれの technique は複数の pass を含めることができるので、
1つの Effect ファイルにはたくさんのシェーダーが入っている可能性があります。

その記述方法としては、例えばこんな感じになります。

---------------------------------------------------------------------
technique10 Main
{
  pass P0
  {
    SetVertexShader( CompileShader( vs_4_0, VS_Main() ) );
    SetGeometryShader( CompileShader( gs_4_0, GS_Main() ) );
    SetPixelShader( CompileShader( ps_4_0, PS_Main() ) );
  }
}

technique10 Animation
{
  pass P0
  {
    SetVertexShader( CompileShader( vs_4_0, VS_Animation() ) );
    SetGeometryShader( CompileShader( gs_4_0, GS_Main() ) );
    SetPixelShader( CompileShader( ps_4_0, PS_Main() ) );
  }
}
---------------------------------------------------------------------


ここで technique Main と Animation の違いは VertexShader だけです。
Direct3D10 の fx_4_0 では、GeometryShader と PixelShader を
あらかじめコンパイルしておいて下記のように記述することができます。

---------------------------------------------------------------------
GeometryShader gs_main= CompileShader( gs_4_0, GS_Main() );
PixelShader ps_main= CompileShader( ps_4_0, PS_Main() );

technique10 Main
{
  pass P0
  {
    SetVertexShader( CompileShader( vs_4_0, VS_Main() ) );
    SetGeometryShader( gs_main );
    SetPixelShader( ps_main );
  }
}

technique10 Animation
{
  pass P0
  {
    SetVertexShader( CompileShader( vs_4_0, VS_Animation() ) );
    SetGeometryShader( gs_main );
    SetPixelShader( ps_main );
  }
}
---------------------------------------------------------------------

この両者やってることは変わらないのに、下の方がコンパイルした後のバイナリ
サイズでちょうど重複するシェーダーの分だけ小さくなります。
おそらくコンパイル時間も短縮されているのでしょう。
technique 間のシェーダー流用は多いし、コードもだんだん複雑に長くなって
きているので、この辺しっかり注意して書いた方がよさそうですね。


非常に情けない話です。
D3D10 DrawIndexedInstanced()が
で書いた DrawIndexedInstanced() のオフセットの問題は、やっぱりこちらの
プログラムが原因でした。
D3D10 Device の wrapper で StartIndexLocation を StartInstanceLocation と
間違って記述していました。

DrawInstanced() の StartInstanceLocation と同じようにしっかり意図通り動作
しております。申し訳ありません。


●VertexShader に対する入力データのまとめ

・VertexStream
  ・頂点単位入力
  ・インスタンス単位入力

・ShaderResourceView / tbuffer
  ・Random Access (ld,sampler)

・ConstantBuffer (cbuffer)
  ・Random Access (cレジスタ)


なぜか shader (4.0) の tbuffer がうまく動きません。
これはかなり はまり ました。
Texture など他に ShaderResource を使わない状態での
tbuffer または Buffer 宣言で、
最初のベクトルしか読み出せない症状が発生します。
例えば

tbuffer ColorData
{
 float4 color0;
 float4 color1;
 float4 color2;
};

と宣言し、
color0 = { 1, 0, 0, 1 } (赤)
color1 = { 1, 1, 0, 1 } (黄色)
color2 = { 0, 1, 0, 1 } (緑)
のようにパラメータを設定したとします。

VS または PS で color1 を読み出してもなぜか color0 の値「赤」が
表示されてしまいます。

これは ShaderResource にして
Buffer<float4>
のように宣言して Load を使っても同じでした。

使い方の問題だと思っておかしいおかしいと散々悩むこと丸一日。
ふと Reference Driver で試すとちゃんと黄色になるではありませんか。
もしやと思って、RADEON HD 2900XT で走らせるとこちらも「黄色」!!
これは参りました。

ちなみにそれまで使っていたのは
GeForce8800GTS + ForceWare 158.24
(2007/07/22 現在の公式最新版) です。

たまたま BETA 版ドライバが 2007/07/11 に出ていたので

BETA 版 ForceWare 163.11

に入れ替えたら、、きちんと黄色です!

以前 88 の BETA 版ドライバで懲りていたので、
入れ替えを控えていたのですよ。
Reference での実験も忘れないようにします。


Direct3D10 になって「caps がなくなった」のは大きな特徴のひとつです。
MS の DX10 ppt スライド等でも何度も出てきており、メリットとして強調
しています。

従来メーカー毎に、ビデオカードごとに、さらにはドライバごとに対応機能も
能力もばらばらなのが当たり前でした。

新しいビデオカードを手に入れたなら、まずは caps を調べます。
対応するサーフェースフォーマットを調べて、テクスチャサイズの制限を調べて、
使えるブレンドモードを調べて、、次に各種パフォーマンスと
やることがたくさんありました。

DirectX API のすべての機能が最初から使えるビデオカードなど無いので、
手に入れてから見たことがない機能が enable されていたりすると
妙に嬉しかったりするわけです。(←それは一部のマニアだけです)

nVIDIA と ATI の2強になってからは手間が大幅に減りましたが、このような
能力の差はかなりの開発の負担となります。結局どれでも動く当たり障りの
無い機能しか使えないし、動作確認もばかになりません。


Direct3D10 ではこの負担を減らすために、機能はどのビデオカードでも
同じように使えることが前提となりました。なので、caps が廃止された
ことの本当の意味は

 caps によって機能の違いを調べる必要が無い

ということです。


実際に RADEON HD2900XT を使ってみました。
caps が違ったり機能が違ったり、列挙の値が違ったりすることも全く無くて、
普段使ってる GeForce8800GTS と一見何も変わりません。今までのように API
を使う上での違いが全く見えません。ちょっとさびしいくらいです。

これは API から見えないだけで本当はハードの特性は異なっており、
機能ではなく速度面で調査する段階になったらかなりの個性が見えてくる
はずです。

だけどそのような違いも一見ドライバレベルで隠蔽されていて、おそらく下位
モデルで試しても全く同じなのでしょう。


プログラムの開発上は機能的な差がなくなりましたが、パフォーマンスを維持
するならある程度の機能的取捨選択のノウハウは必要になると考えられます。

一見違いが無くても、下位モデルでは一部機能がソフトウエア実装だったり
使えるリソースが大幅に減っていたりする可能性が十分考えられるからです。


これらの可能性があるとしても機能的な違いが開発側から見えないということは、
開発が楽になることの裏返しとして、
実はその選択をユーザー側に押し付けているだけなのかもしれません。

アプリケーション側での選択の余地が減った代わりに、ユーザーが自分で
パフォーマンスが足りなければ機能を落として、またはより良いハードを購入
して対処しなければならないからです。

作る側としてはそうならないよう、努力を忘れないよう、
肝に銘じておきます。


以前こちらで ConstantBuffer を書き換える方法を調べました。
D3D10 ConstantBuffer の更新方法
書き忘れがあったので補足します。
ID3D10Effect 自体は内部で (2) の方法、つまり

  ID3D10Device::UpdateSubresource()

を使っているようです。なぜかというと、ID3D10Effect で作成した Effect から
内部の ConstantBuffer を参照して DESC を見ると D3D10_USAGE_DEFAULT で
作られているからです。
なので、ConstantBuffer の書き換えは UpdateSubresource() が標準的な
使い方なのかもしれません。

だけど、ConstantBuffer に直接アクセスしている唯一のサンプル
HLSLWithoutFX10 では Map()/Unmap() を使っているんですよね。


長かったですが、ようやく方針も固まって、周辺ライブラリとかツールとか
できてきたので実際の描画実験に手を出せそうです。
リソース管理とかシェーダーの管理、パラメータのリンク方法、スクリプト、
データフォーマットなどかなり遠回りしました。非常に時間がかかってます。
DXUT は使わず、D3DX もテクスチャローダーだけ使っています。

fx は使ってますが、ID3DEffect から ID3D10Shader を取り出して、あとは
直接管理することにしました。この方法だと内部に無駄な ID3D10Buffer が
たくさん出来ているので、最終的には直接 ID3D~Shader を生成するつもりです。


「Direct3D10 June2007 SDK の非常に細かいこと」
ID3D10ShaderReflection から取れる D3D10_SHADER_DESC のうち
マニュアルに載ってる最後の 4つ
 UINT MovInstructionCount;
 UINT MovcInstructionCount;
 UINT ConversionInstructionCount;
 UINT BitwiseInstructionCount;
は実際には定義されていないです。

動作上全然問題ないので、これはたぶん必要ない、何の特にもならない情報だと
思います。(コンパイルされた命令の詳細とか普段使わないし)

ID3D10Effect から tbuffer を取るには ConstantBuffer 扱いでした。
GetConstantBufferBy~()->GetTextureBuffer()
でいけます。
リソースなので
GetVariableBy~()->AsResource()->GetResource()
かと思ったら違いました。
この手順を使うのは Buffer 宣言した場合です。
ID3D10ShaderReflection からアクセスする場合も TBUFFER は
ConstantBuffer ではなく Texture (ShaderResourceView)扱いです。


「Direct3D 10 ShaderReflection を安全に取得する方法」
以前こちらで ID3D10Effect から ID3D10ShaderReflection を取得する方法を
紹介しましたが、このままだと少々問題がありました。
D3D10/DX10 Effect Interface内情報
というのは、以前も書いたように Effect 内で NULL が渡されたシェーダーで
あっても D3D10Effect の API はほとんど有効なまま素通りしてしまうからです。

例えば

ID3D10Effect* iEffect; // Input

ID3D10EffectTechnique* iEffectTechnique= iEffect->GetTechniqueByIndex( 0 );
if( !iEffectTechnique || !iEffectTechnique->IsValid() ){
 return FALSE;
}
ID3D10EffectPass* iEffectPass= iEffectTechnique->GetPassByIndex( 0 );
if( !iEffectPass->IsValid() ){
 return FALSE;
}
D3D10_PASS_SHADER_DESC pass_shader_desc;
if( FAILED( ipass->GetGeometryShaderDesc( &pass_shader_desc ) ) ){
 return FALSE;
}
ID3D10EffectShaderVariable* iShaderVariable= pass_shader_desc.pShaderVariable;
if( !iShaderVariable->IsValid() ){
 return FALSE;
}
D3D10_EFFECT_SHADER_DESC effect_shader_desc;
if( FAILED( shader->GetShaderDesc( pass_shader_desc.ShaderIndex, &effect_shader_desc ) ){
 return FALSE;
}
ID3D10ShaderReflection* iReflection; // Output
if( FAILED( D3D10ReflectShader( esdesc.pBytecode, esdesc.BytecodeLength, &iReflection ) ){
}

と幾重にチェックを入れても引っかからず、最後の D3D10ReflectShader() で
E_INVALIDARG になります。一旦 iShaderVariable->GetGeometryShader() で実際の
シェーダーインターフェース
(ID3D10GeometryShader, ID3D10VertexShader, ID3D10PixelShader)
を取得して、設定されているシェーダーが NULL かどうか判定しても良いのですが、
この場合 Get で AddRef() されるため Release() が必要になってしまいます。

D3D10_EFFECT_SHADER_DESC effect_shader_desc が取れたところで、バイトコード
サイズによって

if( !effect_shader_desc.BytecodeLength ){
 return FALSE;
}

とはじいてあげるのが良さそうです。

またすっかり忘れてましたが、NULL になる可能性があるのは GeometryShader
だけではありませんでした。StreamOutput を使ったときは PixelShader も NULL
設定される可能性があります。


「DrawIndexedInstanced()がおかしい?」
Direct3D10 では ID3D10Device::Draw 系命令が5つあります。
Draw()/DrawIndexed() は従来の DrawPrimitive()/DrawIndexedPrimitive()
に非常に近くほとんどの描画はこの2つを使います。

Draw(
  UINT VertexCount, // 描画する頂点数
  UINT StartVertexLocation // 頂点の開始位置
 )

DrawIndexed(
  UINT IndexCount, // 描画するインデックス数
  UINT StartIndexLocation, // インデックスの開始位置
  INT BaseVertexLocation // インデックス指定する頂点の開始位置
 )

DrawInstanced()/DrawIndexedInstanced() は同じデータを指定回数分
繰り返し描画することができます。この Geometry Instancing については
以前こちらの方でも書きました。
D3D10 Geometry Instancing の問題と

DrawInstanced(
  UINT VertexCountPerInstance, // 描画する頂点数
  UINT InstanceCount, // 繰り返し描画回数
  UINT StartVertexLocation, // 頂点の開始位置
  UINT StartInstanceLocation, // インスタンスの開始位置
 )

DrawIndexedInstanced(
  UINT IndexCountPerInstance, // 描画するインデックス数
  UINT InstanceCount, // 繰り返し描画回数
  UINT StartIndexLocation, // インデックスの開始位置
  INT BaseVertexLocation, // インデックス指定する頂点の開始位置
  UINT StartInstanceLocation // インスタンスの開始位置
 )

これらの命令は Instance の描画回数を指定する引数が追加されています。
同時に Instance に対するオフセットも指定できるようになっています。

DrawIndexedInstanced() でこのオフセットである StartInstanceLocation を
使うとなぜかきちんと描画してくれません。
描画されなかったりポリゴンが飛んだり。

DrawInstanced() ではうまく行っており、こちらは意図通りに動作しています。
指定した値の分だけ Instance バッファをずらしてそこから描画してくれます。

実行は GeForce8800GTS ForceWare 158.24 ですが、Reference Driver でも
RADEON HD 2900XT でも全く同じ描画になるので、使い方を勘違いしている
だけかもしれません。もうしばらく検証してみます。


ちなみに Indexed 系の命令は BaseVertexLocation だけ INT 型で、負の値を
受け付けるようです。

考えられる用途としては、例えばもともと1つの巨大な頂点バッファを複数の
バッファに分割したとき、Index バッファの値を書き換えなくても描画可能な
ことでしょうか。

後ろにずらすだけなら IASetVertexBuffers() でもできますが、前方にずらす
ことは今まで確かにこれまでできなかったかもしれません。


DirectX10/Direct3D10 というと、やっぱり性能があがって機能的にすごい!
そんなイメージがあります。GPU メーカーや Microsoft が宣伝しているように
綺麗な見た目のインパクトや、今までと違う画期的なシェーダー、派手な
エフェクトなどのデモを想像するかもしれません。


だけどこの blog では、実際に Direct3D10 を使った描画エンジンを開発しながら
その過程で調べたことやわかったこと、考えたことなどを細々と記録しています。
見た目でわかりやすい部分にはあんまり触れてなくて、非常に地味かつ狭い分野
の話が多いです。

汎用的な描画やデータの流用を考えたエンジンの設計は非常に地味で地道で、
どのようにシェーダーやリソースを管理して、ツールとどう連携するかといった
部分に結構時間を取られます。
派手なのは地道に積み上げてきた各パーツが一気につながった最後の最後
だと思います。

また Direct3D10 の利点は単に「描画性能がすごい」だけでは決してなく、
設計上の自由度が上がって柔軟な設計を許容し、開発者の負担を減らす効果も
あります。

まだ効果よりは、自由度があがったゆえの設計の苦しみに悩んでいるのが実情
なのですが。


Direct3D10 の Constant Buffer は、レジスタではなくリソースとしての
メモリになりました。作成方法や更新方法など、扱いは VertexBuffer,
IndexBuffer, Texture など他のバッファとほぼ同じです。

ですがシェーダー内部からは従来どおりレジスタとして見えているようで、
何らかの高速にアクセスするための専用のメモリモードやキャッシュを
備えていると考えられます。


Constant Buffer の容量は最大 4096 vecotr で、他の用途に Bind された
バッファと違い上限があります。これもハードウエアデザイン上、キャッシュ
等の特殊な高速アクセスを実現するために必要な制限だったのかもしれません。

余談ですがなぜ 4096 が上限なのか理由を考えてみました。
 ・エンコードされた内部 OP コードで、アドレスフィールドが 12bit だから
 ・128byte (32bit×4) × 4096 = 64KByte だから
 ・Shader で一度にアクセス可能な ConstantBuffer 数は 16個。つまり
  Constant アクセスの命令フィールドは 16bit で、上位4bit がバッファ
  選択に使われている。

どれも想像で根拠はありません。そうえば DirectX3 の Direct3D では
作成可能な ExecuteBuffer のサイズが 64KByte まででした。
(ビデオカードやドライバによる変動はあるかもしれない)
ExecuteBuffer というのは今の PushBuffer のようなもので、当時は
これに直接値を書き込んでコマンドを組み立てていました。

また VertexBuffer ができて、ハードウエア Transform が可能になったあたり
でも 64KByte の壁があったように覚えています。(DirectX7 頃)
その後大きなバッファが作れるようになった DirectX8 でも 32bit IndexBuffer
しか対応していなければ 64K 頂点の上限がありました。
(どれも GPU と driver 依存の可能性があります)

また余談ですが DirectX9 の ShaderModel3.0 で、PixelShader の
Constant Register 数が 224 なのは 32個分他のレジスタにマッピングされて
いるからと考えられます。
i0~i15 と b0~b15 合わせて 32個か、または r0~r31 の分かもしれません。



冒頭で述べたように、Direct3D10 の ConstantBuffer は Shader からはレジスタ
として見えていても CPU からはあくまでメモリです。書き換えるためには他の
バッファと同じように次の2種類の方法を用いる必要があります。

ID3D10Buffer::Map()/Unmap()
ID3D10Device::UpdateSubresource()

Map()/Unmap() は D3D9 のリソースに対する Lock() と同じで、メモリ上の
イメージとして直接アクセスすることができます。

リソースのロックは GPU/CPU の同期を阻害してしまう可能性があります。
D3D10 では更新をスムーズに行うために、USAGE や GPU ACCESS Flag、
CPU ACCESS Flag 等で細かな動作指定が可能です。

ただし ConstantBuffer の場合は基本的にランダムにアクセスされるので、
ストリーム系のバッファと違って

 USAGE_DYNAMIC + CPU_ACCESS_WRITE + MAP_WRITE_NO_OVERWRITE

の組み合わせは使えません。USAGE_DYNAMIC + CPU_ACCESS_WRITE で Map() を
使う場合は必ず全領域書き換えの MAP_WRITE_DISCARD が必要となります。


UpdateSubresource() の方はメモリイメージの直接更新ではなく、PushBuffer
を使ったコマンドの発行に相当します。そのため従来のようなレジスタの更新
は、UpdateSubresource() を使ったほうが動作上近いかもしれません。

ConstantBuffer を書き換える手段をまとめると次の二通りです。

(1) Map()/Unmap() を使う方法
 ・D3D10_USAGE_DYNAMIC
 ・D3D10_CPU_ACCESS_WRITE
 ・D3D10_MAP_WRITE_DISCARD (全領域更新)
 ・ID3D10Buffer::Map()/Unmap()

(2) UpdateSubresource() を使う方法
 ・D3D10_USAGE_DEFAULT
 ・CPU Access Flag は 0
 ・ID3D10Device::UpdateSubresourece()


この辺の動作も、やっぱり ID3D10Effect を使ってシェーダーを使っている分には
全く意識する必要がありません。Constant Buffer の更新は ID3D10Effect 内部で
勝手にやってくれます。

そのせいか、Constant Buffer に絞ったリソースアクセスについては、あまり
ドキュメント化されていないようです。

もっともこの辺の処理が必要になるのは、ID3D10Effect を使わずに自前で
シェーダー用リソースの管理をする場合のみでしょう。
サンプルを流用したデモやシェーダーによる可能性の研究や実験なら使う必要は
無いと思います。


HLSL では matrix のフォーマットとして、宣言時に row_major と column_major
を選ぶことができます。例えば float4x4 (matrix) と float4 (vector) の乗算
ではどちらで宣言しても
 ・水平演算 dp4 命令を使うか
 ・並列に積和展開するか
の違いになります。命令ステップ数どちらもは 4 で動作上は影響が出ません。
以前こちらのメモに書いた 2種類のコード展開がちょうどこの
row_major と columns_major の違いに相当します。
並列積和か水平か
(row_major, columns_major の違いだけでなく乗算順の設計にも依存する)

なので、エンジン側の設計に合わせて row_major にするか columns_major に
するか選ぶことができます。混在も可能でその辺は HLSL コンパイラがうまい
具合に合わせてくれます。

ただし入力が float3 になると少々話が違ってきます。

例えば VertexShader の入力頂点 float3 を、LocalToWorld float4x4 に
乗算する場合を考えます。

  float4 wpos= mul( LocalToWorld, float4( Input.Position.xyz, 1 ) );
  Output.Pos= mul( WorldToProjection, wpos );

このとき入力頂点の w に 1を代入し、matrix の translation 成分が
そのまま加算されるようにします。

このコードは、積和に水平展開されるとちょうど最後の積和が
単純な加算に置き換わるだけなのでコードのステップ数に影響が出ません。

 vs_4_0
 dcl_input v0.xyz
 dcl_input v1.xyz
 dcl_input v2.xy
 dcl_output_siv o0.xyzw , position
 dcl_output o1.xyz
 dcl_output o2.xy
 dcl_constantbuffer cb0[21], immediateIndexed
 dcl_constantbuffer cb1[4], immediateIndexed
 dcl_temps 2
 mul r0.xyzw, v0.yyyy, cb1[1].xyzw
 mad r0.xyzw, cb1[0].xyzw, v0.xxxx, r0.xyzw
 mad r0.xyzw, cb1[2].xyzw, v0.zzzz, r0.xyzw
 add r0.xyzw, r0.xyzw, cb1[3].xyzw     // w=1 のときの加算
 mul r1.xyzw, r0.yyyy, cb0[5].xyzw
 mad r1.xyzw, cb0[4].xyzw, r0.xxxx, r1.xyzw
 mad r1.xyzw, cb0[6].xyzw, r0.zzzz, r1.xyzw
 mad o0.xyzw, cb0[7].xyzw, r0.wwww, r1.xyzw // ここは積和のまま
 mov o1.xyz, v1.xyzx
 mov o2.xy, v2.xyxx
 ret
 // Approximately 11 instruction slots used

ところが水平加算の dp4 に展開されると

 vs_4_0
 dcl_input v0.xyz
 dcl_input v1.xyz
 dcl_input v2.xy
 dcl_output_siv o0.xyzw , position
 dcl_output o1.xyz
 dcl_output o2.xy
 dcl_constantbuffer cb0[21], immediateIndexed
 dcl_constantbuffer cb1[4], immediateIndexed
 dcl_temps 2
 mov r0.xyz, v0.xyzx // w を書き換えるためにテンポラリにコピー
 mov r0.w, l(1.000000) // w=1 に代入
 dp4 r1.x, cb1[0].xyzw, r0.xyzw
 dp4 r1.y, cb1[1].xyzw, r0.xyzw
 dp4 r1.z, cb1[2].xyzw, r0.xyzw
 dp4 r1.w, cb1[3].xyzw, r0.xyzw
 dp4 o0.x, cb0[4].xyzw, r1.xyzw
 dp4 o0.y, cb0[5].xyzw, r1.xyzw
 dp4 o0.z, cb0[6].xyzw, r1.xyzw
 dp4 o0.w, cb0[7].xyzw, r1.xyzw
 mov o1.xyz, v1.xyzx
 mov o2.xy, v2.xyxx
 ret
 // Approximately 13 instruction slots used

w=1 の代入のために 2命令増えてしまいます。
消費するテンポラリ変数の個数は変わらないので、スレッド数の差は出ていない
と思われます。

ShaderModel1 の nVIDIA 拡張では、dp4 の時に vector 側の最後の成分を
強制的に 1 とみなす dph 命令がありました。もし dph 命令があったら
w=1 代入は不要なので、命令数に差は出ません。

命令の依存関係で見た場合は、mad よりも dp4 の方が直前の命令との相関性が
薄いので並列化では有利に見えます。もし依存関係によるパイプラインストール
が存在しているのなら dp4 の方が良いのかもしれません。
(とはいってもこの例ではバイパスが利くケースなのでストールは限られると
思います)
スレッドによる並列化で、そもそもパイプラインストールが存在しないなら
純粋に命令数が少ない mad の方がきっと速いのでしょう。

また実際はドライバによってさらに GPU 毎にネイティブな命令にコードに
置き換わっているので、このような単純な比較はできないのかもしれません。


2007/07/18
CEDEC 2007

今年はメルトダウンどころか鉄人も無いんですね。
CEDEC 2007
去年から参加の ATI もいないし、
DirectX 系のセッションが大幅に減った感じです。


パナソニックの公式ページに Vista へのアップグレード情報が載っていたので
アップグレードしてみました。2005年発売のレッツノート R4 です。評価リストでも
ぎりぎり一番下。だめだったら戻すつもり。
Windows Vista サポート評価情報(Windows XP からのアップグレード)

Let'snote LIGHT R4
 CF-R4HW
 PentiumM 753 (1.2GHz)
 915GMS Express
 RAM 1G

一旦 XP のリカバリかけてから行いました。
R4 は HDD 内のイメージから再インストールできます。
その後 Vista へのアップグレードです。
アップグレードだと DVD から起動する必要が無いので、適当な DVD ドライブ
を USB 接続して使いました。
手順は完全に評価情報ページにあるマニュアルどおりです。

結果

評価: 1.0 Windows エクスペリエンス インデックス
 プロセッサ: 2.8
 メモリ(RAM): 4.1
 グラフィックス: 1.9
 ゲーム用グラフィックス: 1.0
 プライマリハードディスク: 3.9

評価の数値にちょっと驚いたけど、内蔵グラフィック機能が古いので
こんなもんでしょうか。
915 でも一応 PixelShader2.0 は動くんですけど、、やっぱりだめか。
描画はもちろん Aero Glass は無理で、いかにもがんばって描いてます
といった感じのもたつきが見えます。
描画以外はそこそこ動いています。


ファームアップデートでワンセグ視聴が良い感じになったので軽く
使ってみました。


・Mugen Power 3400mAh バッテリー充電済み
・無線LAN OFF
・Bluetooth OFF
・HSDPA OFF
・画面の明るさ7段階中真ん中(設定→システム→バックライト→明るさ)
・音声出力 OFF
・ワンセグの電波状況は非常に良い


できる限り省電力に設定し、別の作業中だったので音声は OFF にしています。

この状態で TV 付けっぱなしにして 4時間10分放置。
その後バッテリー残量を見てみるとこの状態。



バッテリーが空になるまでまだまだ時間がかかりそうなのでここであきらめました。
これからは遠慮なく、ポータブル TV としても活用することにします。

blog内関連記事
emobile EM・ONE ファームアップとワンセグ
EM・ONE 新ファームウエア v1.01
emobile EM・ONE スーパー大容量バッテリ2
EMOBILE EM・ONE スーパー大容量バッテリー


あえて DirectX10 と書いてます。やはり始めるまでいろいろ障害があって、
今までよりも手を出しにくく敷居が高いように感じます。
その理由を考えてみました。

・WindowsVista が必要
・DirectX10 対応ビデオカードが必要

DirectX10 が動作する条件としてこの2つがあります。
そろえるだけでも結構な出費です。
PC まるごと置き換える予定がなければ、両方そろっての入れ替えは大事です。
必要なソフトの対応状況とか考えると、OS もビデオカードも入れ替えには
それぞれリスクがあります。


○今まではどうだったのか

古いバージョンの OS に対して DirectX のサポートが打ち切られることは
ありました。でも新しい DirectX の動作のために OS の入れ替えが必要になる
ことはまれでした。比較的早く動作制限が付いたのは NT4 くらいでしょうか。

OS と DirectX の更新は非同期で、それぞれお互いにリリースや対応状況に
影響しあうことがあまりなかったからです。
両方一度に入れ替えを必要とするのはたぶん今回が初めてでしょう。
それだけ Vista と DirectX は密接だということなのでしょう。


○GPU の場合

DirectX9 でも DirectX8 世代に登場した ShaderModel1.0 は使えますし、
DirectX8 でも DirectX7 世代の固定機能パイプラインは健在です。

新しい DirectX が登場しても、最新の目玉機能が使えないだけで
HAL 自体は1つ古い世代のビデオカードでも動きました。
また DirectX の機能自体も、すべてが必須の項目ではありません。

DirectX7 世代の機能しか持たないビデオカードが、DirectX8 対応と
書かれていることはよくあることです。実際 DirectX8 自体は動きます。
Shader は使えないけれど。

そのため先に DirectX SDK だけ入れ替えて、ライブラリなどを一通り
コンパイルが通るようにしておいて、あとからビデオカードを入手したら
新機能を試す、なんてこともできました。

ところが DirectX10 の場合、DirectX9 世代のビデオカードではそもそも
起動できず Reference Rasterizer しか使えません。
すべての機能への対応を要求するので、本当の DirectX10 GPU しか使えない
わけです。

直前に買ったばかりの DirectX9 ハイエンドカードが泣いています。
VRAM が 512MByte もあるのに Direct3D10 ではほとんど画面が止まったかと
思うようなソフトエミュレーションになってしまうのです。

これはこれで潔いし、そのうち時間が解決するだろうけれど、
古い環境からの置き換わりには少々時間がかかりそうな感じです。


「Direct3D10 の Geometry Instancing の問題と機能の解説」
Direct3D9 の ShaderModel3.0 から Geometry Instancing に対応し、
Direct3D10 では必須機能となりました。Geometry Instancing がなぜ重要
なのか、また D3D9 での機能については前回こちらで触れました。
Direct3D Geometry Instancing
今回は D3D10 での Geometry Instancing についてわかったことなどを
書いてみます。

D3D9 までの DrawPrimitive 命令は無くなり、D3D10 ではただの Draw 命令
となりました。これでいちいちプリミティブ数を計算して求める必要が無くなり
ました。頂点数やインデックス数だけで描画命令を発行できます。

●頂点指定の描画命令
 Draw()
 DrawInstanced()

●インデックス指定の描画命令
 DrawIndexed()
 DrawIndexedInstanced()

~Instanced() 命令は、それぞれの描画命令に繰り返し機能が付いたバージョンです。
繰り返す回数を指定することで、InputLayout の D3D10_INPUT_PER_VERTEX_DATA
指定した頂点バッファが、繰り返し数分だけ何度も読み込まれます。

例えば 24頂点の Cube の描画は

 iDevice->Draw( 24, 0 );

ですが、これを繰り返し回数 (InstanceCount) 8 回 で描画すると

 iDevice->DrawInstanced( 24, 8, 0, 0 );

となります。このとき同じ場所に同じモデルを 8回上書きするだけなので、
描画負荷が上がる以外に特に意味はありません。

この 8回の描画位置を何らかの方法で指定してあげる必要があります。
さまざまな方法が考えられますが、例えば Shader 内でも System Value
Semantic の SV_InstanceID を使って何個目の描画なのか取得することが
できます。Constant Buffer で Geometry を渡して Index に使えば
任意の位置に描画することが可能です。

Constant Buffer は容量的な上限があります。またアドレッシングしなくても
効率よくデータを読み込めるように、専用 Stream を使うことができます。
この Stream には区別のために D3D10_INPUT_PER_INSTANCE_DATA を指定します。

D3D10_INPUT_PER_INSTANCE_DATA 指定された Stream は、読み進めるための
ポインタを Instance 毎にしか更新しません。同一 Instance の頂点には
すべて同じ値が渡されるわけです。

これがまさに Direct3D9 の Geometry Instancing と同じもので、本来は
この使い方を想定して API の設計が行われているようです。
対して最初の Stream を使わない Direct3D10 独自の手法を
Stream Less Instancing と名付けておきます。

Per Instance Stream も単なる Buffer であって基本的には頂点と変わりません。
Draw() に DrawIndexed() があるように、Per Instance Stream でももっと
凝ったことをやろうとすると Index が欲しくなるでしょう。

Index とまでは行きませんが、D3D10 では通常の stream だけでも多少凝った
指定ができるようになっています。D3D10_INPUT_PER_INSTANCE_DATA にも同じ
ストリームを多重読み込みできるように、個別に読み込み回数を指定可能です。
ローカルなループを構成できるわけです。

このローカルな回数は InputLayout を作るときに設定します。
InstanceDataStepRate です。

この機能を使えば、繰り返し回数 (InstanceCount) = 8回のデータでも、
2個の Per Instance Data をそれぞれ 4回ずつ描画する
(InstanceDataStepRate = 4)、といった柔軟な設定が可能になります。

このとき D3D10_INPUT_PER_INSTANCE_DATA の Stream は 2個のジオメトリ
分の大きさですが、DrawInstanced()/DrawIndexedInstanced() には
InstanceCount = 8 を設定します。

ここで今の DirectX Runtime (June2007) には若干問題があって、
Debug Build だと Instance 用の VertexBuffer が描画回数に比べて
少なすぎるという WARNING が毎フレーム発生してしまいます。

[EXECUTION WARNING #356: DEVICE_DRAW_VERTEX_BUFFER_TOO_SMALL]

どうやら判定時に InstanceDataStepRate を見ていないようです。
当初自分のアプリケーションの問題かと思っていたのですが、
Sample の Instancing10 でも発生していました。

とりあえず、Direct3D10 の Geometry Instancing は、無理やり拡張したような
D3D9 と比べてかなりシェーダーで自由に扱うことができます。
情報が少ないのがネックですがかなり思い通りに使うことができるようです。

InstanceDataStepRate はいわゆる通常の Geometry Instancing に
さらに Stream Less Instancing を混在利用しているわけですし、
Texture/Buffer 読み込みを使えば Instance Data の Index 化もおそらく
可能でしょう。


先日のファームアップデートは安定度の向上が中心で、特にこれが変わった!
という目だったものも無いし問題も無いので、なかなか新しくなったという
実感がわきません。
EM・ONE 新ファームウエア v1.01
アップデータを起動したあとに「しまった」と思ったのは、
ついうっかり RealVGA(WVGA) 化したまま実行してしまったこと。
だけどアップデート作業も順調で、更新も何のトラブルもなく終わりました。
至って順調なので、ReavlVGA 化は気にしなくても良かったみたいです。

アップデート後、確かにワンセグの安定度が増したように感じます。


以前(v1.00時)はワンセグの電波状況も良くて放送が入るのに、
音声だけ時たま途切れがちになることがありました。
何らかの負荷が高くなって一瞬処理落ちしているような感じです。

同一の条件でも、携帯 V905SH の方ではスムーズで途切れることもなく
ワンセグの視聴ができました。


ファームウエアを v1.01a に更新した後は EM・ONE でも音声が安定して
途切れることがなく、大画面で放送を楽しむことができるようになりました。
EM・ONE は画面が大きく液晶も明るいので、机の上に置いても見やすいですね。

画質については、デジタル放送といいつつ結構機種によって違いがあるようです。

SHARP V905SH ワンセグ
 比較的シャープで、文字なども輪郭がくっきりしてきれいです。
 色は EM・ONE とくらべて浅目の落ち着いた感じです。

SHARP EM・ONE ワンセグ
 液晶のドット比率のせいもあるかもしれませんが、強いアンチエリアスが
 かかったような感じ。文字は V905SH に比べると細部がぼやけて見えます。
 色は強く、コントラストが高い濃い画面です。

ちなみに一番きれいなのは、外部アンテナを繋いで条件が良いときの
V905SH のアナログ放送です。ノイズは多少乗るけど滑らかです。
ただ外部アンテナが無く条件が悪いときはノイズだらけで全く見えません。

悪条件の下、単体でもきちんと絵が出て十分な視聴レベルであるワンセグは
やっぱりすごいのですね。


相変わらず Effect 管理と Reflection まわりを触っています。
ID3D10Effect から ID3D10ShaderReflection を取り出すには、
D3D10/DX10 Effect Interface内情報
に書いた手順の通りです。
取り出した Reflection を使って、描画のために Shader の Setup を
行うためには BindingDesc を調べる必要があります。


ID3D10ShaderReflection* iReflection; // input
D3D10_SHADER_DESC shdesc;
iReflection->GetDesc( &shdesc );
for( DWORD i= 0 ; i< shdesc.BoundResources ; i++ ){
 D3D10_SHADER_INPUT_BIND_DESC binddesc;
 iReflection->GetResourceBindingDesc( i, &binddesc );
  // :
}

これで binddesc.Name に対応する HLSL の変数名が、
bdesc.Type に Resource の種類が、
bdesc.BindPoint に Resource を割り付けるスロット番号が入ります。

bdesc.Type は D3D10_SHADER_INPUT_TYPE で定義されています。

--------------------------------------------------------
enum D3D10_SHADER_INPUT_TYPE
{
D3D10_SIT_CBUFFER,
D3D10_SIT_TBUFFER,
D3D10_SIT_TEXTURE,
D3D10_SIT_SAMPLER,
};
--------------------------------------------------------

上記の通り 4種類に分類されていることがわかります。

CBUFFER, SAMPLER は特に問題ありません。それぞれ
GSSetConstantBuffers(), GSSetSamplers() (VS~, PS~) 等の
API でシェーダーに割り当てるので、独自に BindPoint (Slot番号) が
割り当てられます。

TBUFFER と TEXTURE はどちらも Texture なので、シェーダーに
割り当てるための API は GSSetShaderResources() (VS~, PS~)
しかありません。
というのは、どちらも ID3D10ShaderResourceView で扱いは一緒なのです。
そのため Type は別でも BindPoint は両方合わせてカウントされます。

では TBUFFER と TEXTURE の違いは何かというと、
D3D10_SHADER_INPUT_BIND_DESC の ReturnType や Dimension が違います。
結局は Buffer と Texture の違いであって、Sampler を使えるかどうかの
違いといえるかもしれません。

なお ID3D10Effect 等は全部やってくれていますが、BindPoint は
GS, PS, VS それぞれ参照状況に応じて異なっている可能性があり、
さらに Effect 内に複数の Pass や Technique が存在する場合はその数だけ
区別しておく必要があります。
セットアップも必要に応じて GS, PS, VS 全部に対して実行しなければならず、
自前でやろうとすると思ったよりも手間がかかります。
やっぱり完全に ID3D10Effect 任せにした方が簡単ではあります。


フル充電したまま月曜から使ってまだ持っています。
使いっぷりによるとは思いますが、自分の普段の稼働率だと
平日で一週間いけそうです。
EMOBILE EM・ONE スーパー大容量バッテリー

EM・ONE はもともとかなり薄さにこだわって作られたそうです。
むちゃなお願い”から生まれた「EM・ONE」――その開発経緯とは?
標準バッテリーをはずしてみると、裏側のケース分の厚みを削ってあって
薄く仕上げるための苦労のあとが見えます。
スライド式のキーボードを持った2枚構造で薄く仕上げるには、
かなりの試行錯誤があったのでしょう。

でも普段もって歩く分には、意外にも「薄い」という感じはしません。
非スライド式の PDA ではもっと薄いものがいっぱいあるからかもしれません。

だけどスライドしてキーボードを出したときは別で、
インプットスタイルで触れるキーボード側の板は、本当に薄いなと感じます。

そんな EM・ONE に超大容量バッテリーをつけると、
一番触って薄いと実感できたキーボード面が2倍ほどの厚みになります。
キーを打っているときの安定感、重量感は別物。体感的には2倍以上です。

超大容量バッテリーのカバーは、底面を広く覆って安定します。
机においてもぐらつくこともありません。
ボタン類には干渉せず、電源スイッチもワンセグアンテナの引っ張り
出しも違和感なく行えます。
スタイラスの取り出しでは多少カバーの角が気になってしまうかも。

カバーに付いたスタンドは最初は硬くて引っ張り出せませんでした。
しばらく使ってると馴染んできます。安定性も申し分ないです。
立てたときは電源とUSBケーブルが干渉してしまうので、
単体で使うときに限られます。

実用時のバッテリー持続時間のテストはよほど暇なときで無いとむり
かもしれないです。過去のバッテリーテストこちら
emobile EM・ONE のバッテリーと操作
emobile EM・ONE 無線LANのバッテリー時間

バッテリカバーをはずすときは、三角マークのところを抑えて押し付ける
ようにしてスライドします。標準バッテリーのカバーと構造はいっしょです。


CG出版のインターフェースは組み込みよりの技術者向けの雑誌です。
「PHS通信モジュールW-SIMを利用したLinuxベース携帯電話の開発」など
WILLCOM 関連のネタも結構載っています。

今月号(2007年8月号)には W-SIM を直接 LAN に接続できる
つないでイーサの紹介や解説が載っていました。
「PHS経由でネットに接続できるEthernetアダプタのファームウェアをハック 前編」

この「つないでイーサ」は簡単にファームウエアを入れ替えることが可能で、
ユーザーが好きなプログラムを走らせることができるとのことです。
例として、サポートページで配布している SilentC が紹介されています。

プロンプトの無い行番号式スクリーンエディット画面には OK と表示されていて
run でプログラムの実行ができて
「? 計算式」だけで演算結果を表示しています。
C言語ながら、8bit パソコン時代の BASIC のような、対話式の良い使い勝手を
しっかり再現しているようです。

というのもそのはず、よく見るとこの記事を書いているのはあの Oyajin さんこと
中本伸一さん。OyajinAppointment など WindowsCE ではすっかりおなじみです。
そして Hu-BASIC の生みの親でもあるのですから。


新しいスリムになった PSP が発表されたようですね。
 ・薄くなって軽くなった新型PSPを秋に投入
 ・SCEA、スリムで軽い新型PSPを発表この秋、発売へ
よく見ると、スペックも若干更新されているようです。
Main Memory が 32MB から 64MB に増えています。

互換性を考えるとこのような仕様変更は考えにくいのですが、
UMD の読み込みスピードが改善されているらしい
ことなどから、UMD のキャッシュに使われるのかもしれません。
64MB というと Xbox1(VRAM兼) と同じですね。

TV出力も追加されているようです。
個人的には外部コントローラ用端子がほしい。


EM・ONE の新しいファームウエアが出ました。1.01a にアップデートできます。
同時に EM・ONE 用のバックアップツールも無償でダウンロードできるように
なりました。3DBox のゲームといい、なんだか気前が良いですね。

イー・モバイル、「EM・ONE」の新ファームウェア公開
EM・ONE ~本体アプリケーションバージョンアップについて~
EM・ONE ~バックアップツール「Sprite Backup(S01SH版*1)」を提供開始~

バックアップツール「Sprite Backup(S01SH版)」は、ファームウエアを更新
しなくても使えます。つまり v1.00 でも動作します。
というか、更新時のトラブルがあっても元に戻せるように用意されたツール
なので、むしろ更新前にバックアップをとっておく必要があります。

早速更新してみました。

手順どおりまずは Sprite Backup をダウンロードしてインストールして更新前の
バックアップを取ります。バックアップの前後に自動でリセットがかかります。

アップデータをダウンロードします。EM・ONE は本体のフラッシュメモリ容量も
多いので、ZERO3[es] の更新のときのように神経質にならずに済むのが
ありがたいです。

実行すると AC アダプタを繋ぐように言われます。
超大容量バッテリーを一度使い切ってしまいたかったけれど、仕方ないので繋ぎます。

アップデートのプログラムは1つですが、更新は前半と後半の2回行われます
前半に比べて後半はかなり時間がかかります。プログレスバーがとまったように
見えてもそのまま我慢です。

手順書どおりの操作で更新が完了しました。バージョン番号は

 v1.01a (バージョン1.00からアップデートされています。)

になりました。

  設定→システム→S01SH情報

確認手順も Windows フォルダの exe を探さなければいけない W-ZERO3[es] より
簡単になっています。

更新しても、Direct3DM で GoForce5500 のハードウエアアクセラレータは
使えるようになってませんでした。
そういえばパケットカウンタの前月、前々月がクリアされているのは更新のせい
でしょうか。

今回は安定度の向上が中心らしいので、もうしばらく使ってみます。


W-ZERO3[es] で Linux が動き出しました。以前こちら
W-ZERO3[es] Linux
W-ZERO3[es] Linux (2) mini SD
でも書きましたが、詳しくは
Linux on W-ZERO3
に書かれています。

まめに更新されていて、今では es でない W-ZERO3 (WS003SH) 等でも動作
するようになっています。

EM・ONE での動作にも期待がかかるのですが、、今のところはまだ動作対象外
のようです。EM・ONE はグラフィックアクセラレータ GoForce5500 が乗って
いるため、万が一動いても画面は見えないだろうしもしくは描画アクセスで
止まってしまう可能性があります。

無印対応以外にも、キーボードの刻印どおりに入力できるようになっていたり、
sd 周りを簡単に扱えるコマンドが入ったり、
開発者向けにクロス開発環境の作り方などがこちらで解説されています。
FrontPage - 開発room

実際にクロス環境の構築を試してみました。



(1) 仮想PC に Fedora7 を install

先日 PS3 に install したばかりなので、同じように Fedora 7 を使ってみます。

当初 VirtualPC2007 を使おうとしたのですが うまく動きませんでした。
今のカーネルと VirtualPC のエミュレーション対象との相性で問題があるようです。
(ubuntu 7.04 でもマウスが動きませんでした)

そこで、USB が使えたり、RemoteDesktop で仮想 PC へ接続が可能だという
VirtualBox を使ってみました。
WindowsVista に VirtualBox を install し、仮想マシンを作って
Fedora 7 の DVD image ( F-7-i386-DVD.iso ) から install します。
iso を直接マウントできるため物理的にメディアに焼きこむ必要はありません。

マウスのキャプチャ状態は 右の Ctrl キーで Host PC に戻ります。
(Virtual PC だと 右 Alt でした)

仮想マシン作成時に聞かれる設定はこんな感じです。

 ・OS の種類は Linux 2.6
 ・RAM は 512M (host PC はRAM 2G)

仮想 HDD のサイズは 20GB にしています。
Fedora 7 の Install オプションは下記の 3つ全部チェックしました。

  ・オフィスプロダクティビティ
  ・ソフトウエア開発
  ・webサーバー

使うインストーラの画面も手順も PS3 と全く一緒。
インストール後、ネットワークにつながることを確認したらおしまいです。
しばらくすると更新を催促されるのでそのまま更新しておきます。



(2) クロス開発環境の構築 buildroot

とりあえず W-ZERO3 用の開発環境を作ってみます。
開発メモ クロス開発環境の構築
を参考に進めました。


まず buildroot を入手します。下記から Download → Daily Snapshots
BUILDROOT

今回は buildroot-20070709 を使いました。
buildroot-20070709.tar.bz2 をダウンロードして PC に保存します。
共有フォルダを使って VirtualBox 上の Fedora7 にコピーします。

Fedora7 上の Firefox から直接ダウンロードしたほうが早かったかもしれません。

ターミナルを開いて、以後全部コマンドラインから作業します。
 アプリケーション→システムツール→端末

適当な作業フォルダを作って、先ほどダウンロードした
 buildroot-20070709.tar.bz2
を入れます。


 $ cd
 $ mkdir zero3
 $ mv Desktop/buildroot-20070709.tar.bz2 zero3

  (作業フォルダとして home に zero3 を作成し、Desktop にダウンロード
  した ~.bz2 を zero3 に移動する。)

tar を展開します。

 $ cd zero3
 $ bzcat buildroot-20070709.tar.bz2 | tar -xvf -

buildroot フォルダができるので、その中で config 設定します。

 $ cd buildroot
 $ make menuconfig

Menu 上で設定を行います。

 Target Architecture → arm に変更
 Target Architecture Variant → xscale に変更
 Target Options → cpio the root filesystem にチェック追加

[→] で <Exit> を選択して save 画面で <Yes>。

この後 make で build が始まります。
build 中必要に応じてパッケージを buildroot/dl フォルダにダウンロードして
くるようです。ダウンロードされたファイルは下記の通りです。
Wiki にあるように、可能なら先に任意のサイトからダウンロードして、
あらかじめ dl に置いておくと良いかもしれません。

  binutils-2.17.tar.bz2
  busybox-1.6.1.tar.bz2
  fakeroot_1.7.1.tar.gz
  gcc-4.2.0.tar.bz2
  genext2fs-1.4.tar.gz
  gmp-4.2.1.tar.bz2
  linux-2.6.21.5.tar.bz2
  mpfr-2.2.1.patch
  mpfr-2.2.1.tar.bz2
  uClibc-0.9.29.tar.bz2

make します。

 $ make

途中で config の追加設定を 2回きかれます。

 ◎Target Processor Type
   15. Intel Xscale (CONFIG_ARM_XSCALE)

  15 の XScale を選択

 ◎Use BX in function return (USE_BX)

  Wiki の解説どおり default の「Y」を選択


完了したら、arm 向けの各種コマンド類が下記の場所にできます。

 zero3/buildroot/build_arm/staging_dir/usr/bin

パスを通します。HOME に armenv というファイルを作って

 $ cd
 $ vi armenv

次の 1行を書き込んでおきます。

-----------------------------------------------------------------------
export PATH="$PATH:$HOME/zero3/buildroot/build_arm/staging_dir/usr/bin"
-----------------------------------------------------------------------

 $ . ./armenv

これで path が追加されます。



(3) 手抜きテスト

適当なプログラムを作ります。

 $ vi main.c

-----------------------------------------------------------------------
#include <stdio.h>
int main()
{
 printf( "HELLO, ZERO3\n" ); /* 適当 */
 return 0;
}
-----------------------------------------------------------------------

 $ arm-linux-gcc main.c
 $ file a.out

a.out: ELF 32-bit LSB executable, ARM, version 1, dynamically linked (uses shared libs), not stripped

なんとなくできたっぽいです。



(4) 実行テスト

(3) で作った a.out を miniSD に書き込んでおいてから、W-ZERO3[es] の
Linux を起動します。

最新版(20070707)では勝手に root で login するし、sd コマンドですぐに
miniSD をマウントできるしで、ものすごい楽です。

 # cd /mnt/sd1
 # ./a.out

HELLO, ZERO3

とりあえずうごいたっぽいです。
こんないい加減なものでは動作確認にならないかもしれないし、手順とか構築
とかもミスやおかしな点とかあるかもしれないです。
が、とりあえず今日はここまでにします。


ちなみに、本当は、PS3 上の Fedora7 で W-ZERO3 Linux 用の開発環境を構築
してみたかったのですが、やっぱり通りませんでした。
(PS3 Linux Fedora7 install はそれが目的だった)


すっかり Folding@Home にはまってしまい、PS3 はフル稼働状態でした。
Folding@home
Folding@home 熱いけど静かな戦い
その間に PS3 Linux も動きがあって、本体のファームウエア更新でインストール
手順が簡略化されたり、ADDON CD も 20070425 のあとにさらに 20070516 が
登場して Fedora7 対応となりました。更新のために PS3 Linux を再 install
してみます。
新ADDONCD 20070425

インストールにあたって下記のサイトを参考にさせていただきました。
FIXSTARS: PS3にFedora 7をインストールする
CellFanWiki


●Fedora7 の Install 手順のメモ

(0) 前準備

 ・PS3 のファームウエアは最新にしておく
 ・PS3 のネットワーク接続は 無線LAN ではなく有線LAN にしておく
    無線 LAN 設定は GAME-OS と Linux では別物であるため
 ・PS3 に繋げるための USB キーボードと USB マウスを用意しておく
 ・720p 以上の解像度の TV。(SD でも install できるが今回は説明しない)
 ・PS3 本体の HDD バックアップ用のメディア (必要に応じて)
 ・DVD-R を作れる PC

 などがインストール時に必要になることがあります。


(1) Fedora7 install 用 DVD の入手

 Fedora7 の PPC 版 DVD を入手して DVD-R に焼いておきます。iso イメージ
 として書き込みます。

 入手はこの辺
  ・Fedora Project : Torrents
  ・Fedora Project : Mirrors

 Mirror サイトから落とす場合は下記の場所です。
   releases/7/Fedora/ppc/iso/F-7-ppc-DVD.iso


(2) 20070516 版の otheros.bld のインストール

 二通り方法があるので都合の良い方法を用います。
 過去に一度 PS3 Linux を install したことがあっても、otheros.bld はまた
 新たに入れなおす必要があります。

 ◎ADDON CD を使う方法
   ADDON CD (PS3 Linux Distributor's Starter Kit) の
   「CELL-Linux-CL_20070516-ADDON.iso」をダウンロードします。
   iso イメージとして CD-R に焼いておきます。

   PS3 のドライブに入れて、PS3 のメニューから
     設定 → 本体設定 → 他のシステムのインストール
   を実行します。CD-ROM 内のファイルを認識したらインストールを行います。


 ◎他のメディアを使う方法
   CD-R を焼かずに直接 otheros.bld をインストールしても構いません。
   USBメモリや SDカード、iPod などのマスストレージ対応デバイスなど
   PS3 で認識できるならどんなメディアでも使えます。

   20070516版 ADDON CD の「\PS3\otheros」フォルダから、もしくは
   FIXSTARS のミラーサイトから otheros.bld をダウンロードします。
   CELL-Linux-CL_20070516-ADDON

   メディアの指定フォルダにコピーします。フォルダ構造は次のようになります。
    \PS3\otheros\otheros.bld
   
   PS3 上で
     設定 → 本体設定 → 他のシステムのインストール
   と選択して、メディアの中のファイルをインストールします。


(3) PS3 へのインストール場所の確保

 今回は PS3 本体の HDD にインストールしました。外部 HDD へインストール
 する場合はパーティション分割やフォーマットはおそらく不要でしょう。
 まだ実験していないので外部 HDD へのインストールは今回は説明しません。

 本体 HDD にインストールする場合、Linux 用のスペースを作成する必要が
 あります。すでにパーティションを分割してある場合はこの手順は不要です。

 方法は次の通りです。

  1. 本体HDD 内容のバックアップ
      設定 → 本体設定 → バックアップユーティリティ

  2. パーティション分割 (フォーマット)
     設定 → 本体設定 → フォーマットユーティリティ
     カスタム → パーティション選択

  3. バックアップ内容のリストア

 バックアップやリストア、パーティション分割などはすべて PS3 のメニュー
 から行うことができます。セーブデータなど重要な情報は、コピー可能なもの
 だけでも他のメディア等に複数保存しておくことをお勧めします。

  参考ページ
  ・PS3 の HDD バックアップ方法の実験
  ・CellFanWiki: HDDの分割はどれを選べばいい?


(4) インストーラの起動

 先に PS3 に USB キーボードと USB マウスを繋いでおいてください。

 (1) で作った DVD を PS3 のドライブに入れて、PS3 のメニューから

   設定 → 本体設定 → 優先起動システム

 を選んで「他のシステム」に切り替えます。すぐ起動するかどうか聞かれるので
 そのまま他のシステムを起動します。

 ブートローダーである「kboot」が起動します。
 プロンプト「kboot:」が表示されたらキーボードから次のように入力します。
 DVD を読み込んで Fedora 7 のインストーラが起動します。

   linux video=720p

 この手順は下記サイトをそのまま参考にしました。
 ・FIXSTARS: PS3にFedora 7をインストールする


(5) Fedora7 インストール

 最初にメディアのチェックをするかどうかきかれるので「skip」します。
 GUI のインストーラが起動したら以後の操作はマウスです。

 最初に Install 言語の選択画面になるので日本語を選びます。
 次から説明は日本語になります。

 パーティションは内蔵 HDD が選択されているので、そのままインストールを続け
 ます。パーティションの中身を消すかどうか聞かれたら「はい」。

 "メモリが足りないので直ちにパーティションを書き込んで swap を作成する~"
  という内容のダイアログが表示されるので「はい」

 ネットワークデバイスは自動で選ばれているので「DHCP の自動設定」そのまま
 にします。(設定変更が必要なら必要に応じて変更してください)

 タイムゾーンは「アジア/東京」

 rootパスワード入力には、自分で任意のパスワードを登録します。
 「確認」にも同じものを入れます。

 パッケージ選択画面になります。

   ・オフィスプロダクティビティ
   ・ソフトウエア開発
   ・webサーバー

 とりあえず 3つともチェックを入れました。HDD 容量と用途に応じて必要な
 ものを選択してください。

 このあとインストールが始まります。

  昔の ADDON CD を使ったテキストベースのインストールスクリプトと違い、
  暗転のまま止まることもなく、進行具合が一目でわかって安心です。
  特にはまりどころも無さそうです。

  このインストール作業は非常に時間がかかるのでここでひたすら待ちます。

  Windows の OS インストールに比べるとずっと時間がかかります。
  おそらく OS だけでなくアプリケーション等も全部含まれているからでしょう。
  Office や VisualStudio などもまとめて入れているようなものです。


(6) 初回起動設定

 延々待って、インストールが終了するとディスクがイジェクトされます。

 ここで再起動を促されて「再起動」ボタンを押します。しばらくして途中で画面が
 止まってしまいます。電源ボタンを押しっぱなしにして電源を切ってください。
 「ピッ」という音が2回聞こえるまで押しっぱなしです。
 少々間をおいて、また電源を入れ直します。

 電源を入れなおすと Fedora 7 が起動し初期化と残りの設定が始まります。

 ファイヤウォール画面になるので使用するサービスにチェックを入れておきます。
 今回は下記のものにチェックを入れておきました。

  SSH
  Samba
  Secure WWW
  WWW

 SELinux はとりあえずデフォルトのままにしておきます。

 日付と時刻もそのままです。

 Hardware Profile もデフォルト(Do not send)を選びました。

 ユーザーの作成画面でログイン用のユーザー名を入力し、パスワードも設定します。

 サウンド設定も、そのままできちんと音が出ているのでそのままです。

 これで「終了」です。


(7) ログイン画面と更新

 (6) で作成したユーザー名とパスワードでログインします。
 右上のアイコンが全部揃うまで待ちます。

 システム → 管理 → ネットワーク

  パスワードをきかれるので (5) で登録した rootパスワードを入れます。
  DNS タブの「ホスト名」で PS3 のマシン名を設定します。

 システム → シャットダウン
 で再起動します。

  ps3fb_~ と表示された後やはり止まるので、電源ボタン長押しで切ってから
  少々間をおいて、また電源を入れなおしてログインします。

 ログインしてしばらくすると更新を促されるので、更新しておきます。
 これも結構時間がかかります。終わったらまた再起動。


(8) PS3 独自のこと

 起動時の解像度の変更は /etc/yaboot.conf を書き換えます。
 ・FIXSTARS 解像度設定

 PS3 本来のゲーム画面に戻るには /sbin/boot-game-os コマンドを実行します。
 アプリケーション → システムツール → 端末
  $ su
  # /sbin/boot-game-os

 このコマンドは「優先起動システム」を PS3 に切り替えてから reboot します。
 やっぱり途中で止まるので電源を切って入れなおします。

 Linux に切り替えるにはやはり優先起動システムを他のシステムにします。


同一形状のモデルを同時に大量に描画する場合は、DirectX9 の
ShaderModel3.0 から導入された GeometryInstancing を使うことができます。
Geometry Instancing は同一の頂点バッファの内容を繰り返し描画できる
機能のことで、頂点ストリームの読み取り周期をずらしてスケジューリング
することで実現しています。

同じ形状のモデルを何個も描画する場合は、その数だけ毎回 Draw 呼び出し
を行うことになります。
WindowsPC 上の DirectX のボトルネックとして有名なのがこの Draw 呼び
出し回数の問題です。1フレームに呼び出す回数が増えるとそれだけで CPU
の負荷が高くなります。
CPU Emulation Driver (HEL) のように HAL を通さない場合や、Xbox の
ように API レイヤが薄い場合はここまで顕著な傾向は現れません。

このオーバーヘッドを軽減するアイデアとして登場したのが
Geometry Instancing です。複数のオブジェクトの描画を一度の Draw
呼び出しで済ませるための工夫といえるでしょう。

注意点は、Geometry Instancing によって恩恵を得られるのはあくまで
CPU 側だということです。必ず描画が速くなるわけではないし、pixel
負荷などで GPU がボトルネックの場合はほとんど速度が変わらない
可能性があります。

GPU の負担が軽くなる要素としては、パイプラインを止めないで済む
ということ。また Constant Register 等の更新が不要になる分効率良く
なるかもしれません。その代わり VertexShader で特殊なストリーム入力
のための負担が増えるのと、16本しかない入力パラメータを圧迫します。


まず描画回数を減らす方法として考えられるのは、必要なオブジェクト
を全部マージしてしまうことです。同じモデルであればマテリアルも共有
されるのでいっぺんに描画することができるでしょう。
Draw 回数のオーバーヘッドは激減しますが、各オブジェクトを個別に
動かしたり位置や向きを変更することができませんし、同一であるはずの
データを頂点バッファに展開するのでメモリ消費も多くなります。

Geometry Instancing ではこの考えをさらに進めて、描画すべき位置の
情報 (Geometry, Matrixなど) を頂点と同じようにストリームで渡す
ことにしました。
Geometry 情報をあらかじめ頂点バッファに書き込んでおきます。
これでマージされたオブジェクトながら、個々のオブジェクトを異なる
座標に配置できるようになります。

Geometry 情報を頂点単位に持たせるとメモリも馬鹿にならないし、
座標更新のために頂点数分書き換えなければなりません。
そのため次の2つのちょっとした追加機能が必要になりました。

・描画するモデル側は何度も繰り返し頂点バッファを読めるようにする

  24頂点の Cube を 10個描画するなら、24頂点の同じバッファを
  10回繰り返して読み込めるようにするわけです。
  Index Buffer を必要回数コピーするようなもの。
  いわゆる Maya とかの Instance。

・Geomtery 情報の頂点バッファは、1モデル単位で読み進める

  つまり 24頂点の Cube があれば、24頂点には同じ Geometry
  が渡されるようにします。

特殊な頂点バッファの使い方に見えますが、これが最小の追加で高い
効果を得られる方法だったのだと考えられます。


Geometry だけでなく、マテリアルカラーなども頂点ストリームで
入力できるので、個々に色や属性等も変更することが可能です。
これらが全部マージされたのと同じように一度の Draw 呼び出しで
描画できるわけです。


ShaderModel3.0 は ConstantRegister 容量の制約があるので、
異なるスキニングモーションをしているキャラクタを一度の Draw
で描画するのは少々難しいかと思います。

すべて同じポーズなら簡単です。例えば 30 frame 分のモーション
なら、各フレーム毎に描画をわけて、30回の Draw で済ませることは
できるかもしれません。
Geometry Instance 系の簡単なアニメーションデモなどはこの方法で
十分だと思います。

骨の数が少なければ、ストリームに Bone 用 Constant Register の
オフセットも入れておくことで、数個の異なるポーズのモーションを
一度に描画することはできるかもしれません。


Geometry Instance は ShaderModel3.0 から付いた機能ですが一部
例外があります。ATI(AMD) RADEON 9700~X800系の場合は、
ShaderModel2.0 であるにもかかわらずドライバのエミュレーション
で Geometry Instance を使うことができました。
あらかじめ Video Card のコントロールパネルで有効に設定して
おくことができます。
エミュレーションなのでパフォーマンス的には限界があるものの
Geometry Instance のコードがきちんと動作します。

このとき Direct3D9 の設定が Debug になっているとエラーチェックに
引っかかってしまいます。
「ShaderModel2.0 なのに Geometry Instance を使っている」
という旨のエラーが出て止まってしまうわけです。
エラーチェックの無い Retail モードだと大丈夫です。


Direct3D10 では DrawInstanced / DrawIndexedInstanced によって
最初から Geometry Instancing の描画がサポートされています。


3D Box のゲームコンテンツに、さらに 8本のソフトが追加されています。
「どこでも英会話」 2の1 ~ 2の8 です。



(前回 EM・ONE 3D Box のゲーム)
これで合計全部で18本。かなりボリュームがあります。
ダウンロードとインストールだけでも結構な量。

アイコンの数がすごいことになりました。



どこでも英会話の赤いアイコンだけで 16個もあります。


EMONE 用に大容量バッテリーを購入してみました。
Mugen Power 3400mAh バッテリ EM・ONE
容量は標準バッテリーの 2.8倍だそうです。
本体からはみ出す大きさで、バッテリーカバーごと交換します。
まずは気になる重さを調べてみました。すべて実測値です。


  ●3400mAh MP超大容量バッテリー
  ・81g MPバッテリー単体
  ・12g MP交換用バッテリーカバー
  ------------------
  合計 93g


  ●1200mAh 標準バッテリー(本体付属)
  ・29g 標準バッテリー
  ・5g  標準のバッテリーカバー
  ------------------
  合計 33g


よって、バッテリー交換で 60g 増える計算になります。
実測でもちょうど 60g の増加になりました。

  ●本体の重さ実測 (miniSDカード、標準スタイラス込み)
  ・242g 標準バッテリー時
  ・302g 3400mAh MP超大容量バッテリー取り付け時


次に取り付けによってどれくらい本体が大きくなるか
厚みを調べてみました。

  ●3400mAh MP超大容量バッテリーの厚み
  ・12mm 3400mAh MP超大容量バッテリー本体の厚み
  ・8mm 取り付け時の増加分の厚み


  ●1200mAh 標準バッテリーの厚み
  ・4mm 標準バッテリーの厚み


バッテリー単体では 3倍の厚さがあります。
ケースの増分は 8mm で、まとめると次のようになりました。

 19mm、242g 標準バッテリー
  ↓
 27mm、302g 3400mAh MP超大容量バッテリー


6G HDD 内蔵の Zaurus SL-C3200 の重さがカタログ値で

  SL-C3200 厚さ 25mm、298g

なので、これを超えています。
iPAQ h3630 + PCカードジャケットに比べたら小さいもんです。

  h3630 + PCカードジャケット 厚さ 3cm、327g


シェーダーはテクスチャを読み込むことができます。
1pixel の描画のために 5枚以上のテクスチャを読み込んだり、
16~64回もサンプリングしたり、といった使い方も珍しくなくなりました。
読み込むテクスチャの指定の仕方もシェーダーの進化とともに変化してきました。

Direct3D9 や D3D8 の ShaderModel1.0 は TextureStage が中心です。
これは DirectX6~7 から引き継いだ考え方で、各ステージに対して
Texture と Sampler (RenderState) を設定します。
そのため Texture Slot 番号、Sampler 番号、Texture Stage の各番号は
一致します。

同じテクスチャを何度も回読みたければ、複数の Stage にテクスチャを
設定する必要がありました。また使用できるステージ数も GeForce3/4 では
最大4ステージまででした。(RADEON 8500系は 6)

ShaderModel2.0 になると TextureStage が分離されます。
これによって演算のためにステージを消費し、読み込めるテクスチャ数が
減ることもなくなりました。
また同一テクスチャを何度も texld できるので、登録個数上限が 8個だと
してもテクスチャ読み込みの回数には上限がなくなりました。
(命令スロットの制限はあります)

Direct3D10 ではさらに分離が進んで、Texture と Sampler が完全に独立
しました。
Shader 内で任意の Sampler と ShaderResource を組み合わせることが
可能なのはもちろん、Sampler を使わずに ShaderResource から直接値を
読み込むことまでできます。

API 上一度に登録可能な Sampler の State 数も 16 slot、ShaderResource
(Texture や Buffer) も 128 slot と増加しています。

さらに Shader は統合されています。VertexShader、GeometryShader、
PixelShader どのシェーダーでも同じように Texture の読み込みが可能
なので、API もそれぞれ 3セットあります。
一度の Draw で設定可能な ShaderResource は 128×3 にもなります。

VSSetSamplers()
VSSetShaderReousrces()
GSSetSamplers()
GSSetShaderReousrces()
PSSetSamplers()
PSSetShaderReousrces()

これらの API は ID3D10Effect を使っている分には全く意識することがなく、
内部で面倒なこともやってくれるので非常に簡単です。

自前で Shader 管理を行うならば、同じ Sampler や ShaderResource で
あっても VS, GS, PS それぞれことなる slot で参照している可能性があるので
別々の設定を行わなければなりません。
D3D9 まででも Constant Register のマッピングは VS と PS で個別管理なので
一緒なのですが、、Effect が便利で頼りすぎていたのかもしれません。

といっても、今のところ PixleShader 以外では、Sampler を使ってイメージ
Texture を大量に読み込むような用途はそう頻繁には無いのかもしれません。


GPU も G9x (GeForce8xの次世代) では float64 にも対応するのでは
ないか、との話です。
GPUサーバーの時代を開くNVIDIAの「Tesla」
もともと ShaderModel1.0 の PixelShader は 9~12bit 整数演算で、
D3D10 の表記でいえば SNORM に相当します。固定少数値
-1.0~+1.0 にMapされていたわけです。
VertexShader は 32bit float でした。

ShaderModel2.0 になって ATI(AMD) RADEON は 9700 シリーズで 24bit float を
導入し、これが X800 系まで続きます。
ShaderModel2.0 で出遅れた nVIDIA の方は GeForceFX で 32bit float を
先行実現しましたが、実際は 16bit half float とのハイブリッドで、
half を積極的に使わないとパフォーマンスに影響が生じるものでした。

ShaderModel3.0 になると GeForce6800~ も RADEON X1800~ も、頂点
同様の 32bit 浮動少数精度を実現します。

ここまで来てようやく PixelShader の演算精度が VertexShader に肩を
並べることができたわけで、D3D10 ShaderModel4.0 世代のユニファイド
シェーダーへ向けて準備が整ったといえます。

ちなみに ATI(AMD) は X1800 より前にも、Xbox360 向け GPU にて
先行して 32bit 浮動少数精度の PixelShader、ユニファイドシェーダー、
ストリームアウトプットなどの機能を実現していました。

さて ShaderModel4.0 で IEEE754 単精度に対応したら次は倍精度 64bit
というわけです。この流れは実は DirectX SDK の D3D10 のマニュアルを
じっくり見ているとわかります。

なにせ HLSL のリファレンスを開くと、一番最初が変数型の説明で、
その最初のページに dobule 型がしっかりと記載されています。
はじめて ShaderModel4.0 のマニュアルを読み進めていたときに、
もう倍精度も対応しているのかと驚きました。
でも試したら使えませんでした。

ID3D10EffectType や ID3D10ShaderReflectionType 等が使っている
D3D10_SHADER_VARIABLE_TYPE を見ても、32bit INT と 32bit FLOAT
しかありません。本当に 64bit float に対応するなら D3D の core
を含めてまた大幅な更新が必要となりそうです。

ちなみに cbuffer 等で double 宣言を使ったどうなるか。
実はコンパイルはきちんと通ります。でも Reflection を取ってみると
ただの 32bit の float になっていました。
ShaderModel4.0 においては half も float 扱いとなるようです。


D3D9 までの TextureFilter は、MinFilter, MagFilter, MipFilter の
組み合わせでした。それぞれ設定可能な値は3タイプで次のようになります。
MIN : POINT, LINEAR, ANISOTROPIC
MAG : POINT, LINEAR, ANISOTROPIC
MIP : NONE, POINT, LINEAR
D3D10_FILTER では MIN, MAG, MIP の各組み合わせが個別のシンボル
(例の長いやつ)に定義されています。
だけどその定数値を良く見ると一定の法則がわかります。
D3D10_FILTER

bit0 = MIP LINEAR
bit2 = MAG LINEAR
bit4 = MIN LINEAR
bit6 = ANISOTROPIC (MIP,MAG,MIN全部 Linear のみ)
bit7 = COMPARISON
bit31 = TEXT 1BIT

実際 ANISOTROPIC 時に point sampling を組み合わせることはないので、
必要な組み合わせだけをシンボルに指定して定義したもののようです。
また MIP NONE がありません。
テクスチャが LOD を持っているかどうか、これだけで MIP の利用を決定しても
実用上特に不便しないし、実際 MIP が付いているのに切ることは稀だし、
必要なら Shader の Sample 時に Level を決め打ちできるし、
と State 側にはいらないのかもしれません。

今気が付いたけど SDK マニュアルの SampleLevel の説明が間違っているようです。
SampleLevel
中身が SampleCmpLevelZero のものになっていますね。
正しくは MipLevel の指定です。


D3D10_FILTER にはものすごく長いシンボルが定義されています。
例えばこんなの
  D3D10_FILTER_COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR
タイピングの困難さとかソースコードの折り返しやみやすさの問題とかは
おいといて、これらのシンボルを見ていると COMPARISON とつくものが
一通り増えていることがわかります。
msdn D3D10_FILTER

どうやら従来 nVIDIA の GPU にだけ搭載されていた Shadow Map 用の
ハードウエア機能が一般化されて D3D の標準機能に昇格したものと思われます。
nVIDIA Shadow Map は GeForce3 から搭載されたもので、depth 値を比較して
同時にフィルタリングをしてくれました。
いわゆる PCF の 4サンプル分までがバイリニアフィルタリングつきで
低コストで得られます。
PCF によるソフトシャドウは一般的なテクスチャフィルタは使えず
膨大なサンプリングが必要なので、ある程度ハードウエアで稼げるのはお得です。

この D3D10_FILTER の比較付きサンプリングを行うためには、
HLSL の専用命令 texture.SampleCmp() や texture.SampleCmpLevelZero()
を使う必要があります。サンプリングした結果を比較し、結果の bool 値が
フィルタリング対象となるわけです。

マニュアルをよく読むと使えるフォーマットに制限があるようです。
R32_FLOAT_X8X24_TYPELESS, R32_FLOAT, R24_UNORM_X8_TYPELESS, R16_UNORM
やっぱりまだこれらの命令は depth 値専用でしょうか。

まだ D3D10 上では試していないので、影描画に着手するあたりにはいろいろ
実験してみます。

以前 RADEON HD 2900XT でも Direct3D9 で作った nVIDIA Shadow Map の
影描画プログラムが、そのまま通ってしまうことをこちらで書きました。
D3D10/DX10 RADEON HD2900XT

RADEON X1900 世代ではバイリニア用サンプラを使った depth 値の同時
4 sample までがハードウエアサポートで、それ以降の比較やフィルタリング
はシェーダー実装する必要がありました。D3D10 の機能として採用された
おかげで nVIDIAと完全に同等の機能が使用可能になったのではないかと
考えられます。

ちなみに D3D10_FILTER の最後には D3D10_FILTER_TEXT_1BIT という特殊な
シンボルが定義されています。
フォントのための専用テクスチャフォーマット用らしいですね。
D3D10 からついた 1bit フォーマットのテクスチャで使うものと思われます。
デバッグ用フォントのフォーマットとして使えるならメモリ効率も良くて
お得ですね。


Effect Interface内情報の続きです。
ID3D10Effect は内部で必要な ConstantBuffer を生成しパラメータを保持
しています。cbuffer 宣言で明示的に ConstantBuffer を指定することが
できますが、実際に生成されているバッファは宣言した cbuffer の数よりも
1つ多いようです。
調べてみると '$Globals' という暗黙の CBuffer が作られており、おそらく
cbuffer 宣言無しに定義した global 変数が $Globals 扱いになるのでしょう。
ID3D10Effect の CBuffer は ID3D10EffectConstantBuffer 経由で取り出せます。

例えば作成済み D3D10Effect の ie1 から、cbuffer LMaterial の IBuffer
(ConstantBuffer) を参照するには次のようになります。

ID3D10Effect* ie1; // input
ID3D10Buffer* ib1= NULL; // output
ID3D10EffectConstantBuffer* cf1= ie1->GetConstantBufferByName( "LMaterial" );
cf1->GetConstantBuffer( &ib1 );

このとき ib1 は Get によって AddRef() されており参照カウンタ値は 2 です。
自前で用意した CBuffer を与えるとどうなるでしょうか。

ID3D10Effect のインスタンスを 2つ用意し、ie1 に ie2 の CBuffer を設定して
みました。

ID3D10Effect* ie1; // input
ID3D10Effect* ie2; // input
ID3D10EffectConstantBuffer* cf1= ie1->GetConstantBufferByName( "LMaterial" );
ID3D10EffectConstantBuffer* cf2= ie2->GetConstantBufferByName( "LMaterial" );
ID3D10Buffer* ib1= NULL;
ID3D10Buffer* ib2= NULL;
cf1->GetConstantBuffer( &ib1 );
cf2->GetConstantBuffer( &ib2 );
cf1->SetConstantBuffer( &1b2 ); // ie1 ← ie2

ここで ib1, ib2 それぞれの参照カウンタを見ると 1 と 3 になっています。
つまり ie1 側の CBuffer は事実上解放されており、ie2 側の CBuffer が
共有されていることが参照カウンタの増加から分かります。

ID3D10Shader を使わずとも ID3D10Effect だけでバッファの共有管理を自前で
行うことができそうです。


D3D10 では D3D9 と違い、Effect (fx) API は D3DX ではなく D3D10 に
含まれています。インターフェースは ID3D10Effect で、シェーダーの
コンパイルや fx に含まれる各種変数やステート情報へのアクセスなどが
できるようになっています。

従来どおり個別の VertexShader / GeometryShader / PixelShader も
扱うことができ、こちらは Shader インターフェースとしてまとめられています。
・ID3D10GeometryShader
・ID3D10PixelShader
・ID3D10VertexShader

D3D10Effect から各種シェーダーを直接取り出す手順はこんな感じになりました。

// vertex shader
D3D10Effect* ieffect; // input
D3D10VertexShader* ivsh= NULL; // output
D3D10EffectTechnique* itechnique= ieffect->GetTechniqueByName( "Main" );
D3D10EffectPass* ipass= itechnique->GetPassByIndex( 0 );
if( ipass->IsValid() ){
 D3D10_PASS_SHADER_DESC shdesc;
 ipass->GetVertexShaderDesc( &shdesc );
 ID3D10EffectShaderVariable* ishadervar= shdesc.pShaderVariable;
 if( ishadervar->IsValid() ){
  ishadervar->GetVertexShader( shdesc.ShaderIndex, &ivsh );
 }
}

これは technique Main の Pass0 の VertexShader を参照しています。
このとき、得られた D3D10VertexShader の参照カウンタが増加するので、
不要になったら ivsh には Release() が必要です。

それに対して D3D10EffectShaderVariable 等、Reflection 系のアクセス
インターフェースは COM ではないので参照カウンタがありません。
ID3D10ShaderReflectionVariable 等も全く同じです。
D3D10 では最初はこの辺の区別がつきづらく、慣れるまで少々分かりにくく
なりました。


ここでちょっとはまった注意点として GeometryShader があります。
というのは、Effect(fx) 内部で GeometryShader に NULL を設定していても
ipass->GetGeometryShaderDesc() が成功して D3D10EffectShaderVariable* を
参照できてしまうからです。IsValid() も通過します。
どうやら内部で static の NULL object を用意しているらしく、
GeometryShader に NULL を設定した場合はどの D3D10Effect でも全く同じ
D3D10EffectShaderVariable のアドレスが返ってきました。
D3D10EffectShaderVariable::GetGeometryShader() では NULL が返るので、
このことが分かっていれば全く問題はありません。


shader からさらに D3D10ShaderReflection を取得するには次のようになります。

ID3D10EffectShaderVariable* ishadervar; // input
ID3D10ShaderReflection* iref= NULL; // output
D3D10_EFFECT_SHADER_DESC efsdesc;
ishadervar->GetShaderDesc( &efsdesc );
D3D10ReflectionShader( efsdesc.pBytecode, efsdesc.BytecodeLength, &iref );


Reflection 等から変数の要素1つ1つ調べるには ~Variable から ~Type を
取得して調べていきます。
例えば Effect なら D3D10EffectType があり、Shader なら
D3D10ShaderReflectionType を使います。
Variable が構造体なら、さらに GetMemberTypeByIndex() 等で内部の
メンバー情報を参照していく必要があるようです。

もちろん変数名等を使って Effect が管理するバッファに値を格納するだけなら
Reflection とか考えずに
 GetVariableByName() → AsVector() → SetFloatVector()
とか使えば良いので簡単です。
だけど D3D9 と違って StateManager とか使わずに API だけでもきちんと
全情報にアクセスできるみたいなので、Effect 内の管理に頼らないで
パラメータ管理自前で乗っ取っても大丈夫そうです。
そういえば、変数名でなく取得したハンドルでのアクセス方法は
なくなっているみたいですね。


久しぶりに 3D Box を起動したら
「ゲーム」の引き出しにコンテンツがいっぱい入っていて驚きました。
・「右脳鍛錬ウノタン」判断力、集中力
・「どこでも英会話」1~8
合わせて10本あります。(2007/07/01現在)
EMOBILE コンテンツダウンロード

これらはダウンロード可能なファイルになっていてそれぞれ 2.5M~3MByte
ほどです。形式は見慣れた CAB ファイルで、一般のアプリケーション同様
インストールできます。
一度インストールすればスタートメニューのプログラムから実行できます。
やっぱり普通のソフトです。

数が多いけれど分割ダウンロードでは無いようです。
1つ1つ個別のソフトで、単体で起動できるしインストールするたびに
アイコンが増えていきます。

3D Box ではストリームのムービーだけでなく、このようなアプリケーション
の配信も行っているんですね。ちょっと得した気分です。


Readme-ja.txt を参考に miniSD を mount してみました。
内蔵キーボードで長いデバイス名を打つのは大変なので省略しています。

$ mknod /dev/sd b 254 1
$ mkdir /mnt/sd
$ mount /dev/sd /mnt/sd
$ df

Filesystem Size Used Available Use% Mounted on
/dev/sd 1.9G 256.0k 1.9G 0% /mnt/sd

SD にリダイレクトして書き込み → umount → PCで読み出し
等も普通にできました。

$ dmesg

» Read More



2007/07/01
W-ZERO3[es] Linux

W-ZERO3[es] (WS007SH) で動作する Linux が話題になっていたので
起動してみました。驚くほどあっさりと立ち上がりました。
Linux on W-ZERO3

まだカーネルが立ち上がるだけで、Linux が起動したからといって特に何か
できるわけではありません。ファイルシステムもなく、イメージに埋め込まれた
ミニルートで起動しているだけのようです。

ところがそれ故にインストールなど面倒な手順も一切なく、WindowsMobile
側のファイルシステムにも一切手を加えることなく Linux が立ち上がって
しまいます。このお手軽感はちょっとした驚きです。

2007/07/01 現在、起動までの手順は

 ・アーカイブをダウンロードし、カーネルイメージとローダーを \My Documents
  に入れる。
 ・ローダーを起動する。

これだけあっという間に Linux の login: プロンプトが現れます。
WindowsMobile 用のローダーがカーネルイメージを読み込んで、システムの
起動を行っているようです。(起動はあくまで自己責任で)
どのような仕組みで起動しているのかわかりませんが、WindowsMobile
(WindowsCE) 下でエミュレーションで動いているわけではないようです。

root で login すると組み込まれているコマンドの実行が可能です。

キー入力は内蔵のキーボードで行いますが、ASCII (英語) キーボードとして
認識しているようで、一部の記号がキーボード刻印と異なっています。
英語キーボードと日本語キーボードの配列の違いを考えて打てば記号入力も
できます。

例えばよく使うパイプ "|" は [\] の Shift なので、
[SHIFT] を押しながら [Fn]+[-=] (\) で打てます。

コロン ":" は英語キーボードだと [;] の Shift なので
[SHIFT] を押しながら [Fn]+[;,] です。

reboot すると ROM 側 (WindowsCE) が起動し元に戻ります。
時計がリセットされていますが PC とシンクロすれば再設定してくれます。

SDカードにはアクセスできるそうなので、フォーマットして
環境構築したりなども、もしかしたらできるのかもしれません。