Direct3D 10 ss00 サンプルの解説 (1)

以前掲載した D3D10 のサンプルプログラム ss00 を少々解説します。
ss00.cpp 以外にライブラリやら DXUT も使わず、できるだけ
Direct3D 10 の API だけで済むようコードを減らしていったものです。
それでも初期化は思ったより長い関数になりました。
Direct3D 10 シェーダー4.0サンプルプログラム

あらためてゼロから書いてみると結構手間がかかりますね。
それでも DirectX3~6 あたりまでと比べると、シェーダーが必須になった
だけで、初期化自体は簡単になっているはずです。

ソースはこちらからどうぞ
HYPERでんち

動作には Vista + DirectX10 対応 GPU が必要です。

ss00.cpp

●初期化 InitDevice()

InitDevice() は初期化を行います。

Direct3D10 で最初に必要なインターフェースは次の2つです。

ID3D10Device
IDXGISwapChain

この2つは名称が異なるものの、D3D10CreateDeviceAndSwapChain()
関数一発で簡単に作ることができます。
D3D10CreateDeviceAndSwapChain()

DirectX9 以前のように、Device の前に IDirect3D を作る必要がなくなりました。
IDirect3D が行っていた Display 周りの選択や制御が DXGI に移行した形と
なっています。

D3DX10CreateDeviceAndSwapChain() 作成時に与えるパラメータは、
Direct3D9 の D3DPRESENT_PARAMETERS とほとんど同じです。
D3DPRESENT_PARAMETERS
ここは Direct3D9 のコードを基にしていても比較的容易に対応できる
部分でしょう。

フレームバッファのサイズ、リフレッシュレート、フレームバッファのフォーマット、
フロントバッファへ反映させるときのエフェクトやタイミング、フルスクリーン
かどうか、などを与えています。

DXGI_SWAP_CHAIN_DESC swapdesc= {
 {
  width, height, // フレームバッファサイズ
  { 60, 1, }, // リフレッシュレート
  DXGI_FORMAT_R8G8B8A8_UNORM, // フォーマット
  DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,
  DXGI_MODE_SCALING_UNSPECIFIED,
 },
 { 1, 0, }, // sample
 DXGI_USAGE_RENDER_TARGET_OUTPUT,
 1,
 hwnd, // Window ハンドル
 winmode, // WindowMode か FullScreen か
 DXGI_SWAP_EFFECT_DISCARD,
 0
};

D3D10CreateDeviceAndSwapChain(
 NULL,
 D3D10_DRIVER_TYPE_HARDWARE,
 NULL,
 D3D10_CREATE_DEVICE_DEBUG,
 D3D10_SDK_VERSION,
 &swapdesc,
 &g_iSwapChain, // IDXGISwapChain
 &g_iDevice // ID3D10Device
);

ID3D10Device と IDXGISwapChain ができたら View を作ります。
この View は Direct3D10 になって登場した新しい概念です。
メモリ上のテクスチャ空間に対して、実際にどのような手段で Shader が
アクセスするのか決定します。
リソースの自由度が上がった分、具体的にどのような使い方をするのか
ひと手間必要になったわけですね。

IDXGISwapChain から BackBuffer を取り出し、Direct3D からアクセスするための
ID3D10RenderTargetView を作成します。

ID3D10Texture2D* iBackBuffer= NULL;
g_iSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ),
  reinterpret_cast( &iBackBuffer ) );
g_iDevice->CreateRenderTargetView( iBackBuffer, NULL, &g_iRenderTargetView );
ZRELEASE( iBackBuffer );

次に Depth Buffer を作成しています。
ここでは何も考えずに Stencil のない D3D10 新フォーマットと思われる
D32_FLOAT を使っています。

ID3D10Texture2D* iTexture= NULL;
D3D10_TEXTURE2D_DESC tex2ddesc= {
 width, height, 1, 1, // width, height, mip, array
 DXGI_FORMAT_D32_FLOAT, // format
 { 1, 0, }, // sample
 D3D10_USAGE_DEFAULT,
 D3D10_BIND_DEPTH_STENCIL,
 0, // cpu flags
 0 // misc flags
};
g_iDevice->CreateTexture2D( &tex2ddesc, NULL, &iTexture );

D3D10_DEPTH_STENCIL_VIEW_DESC vdesc= {
 DXGI_FORMAT_D32_FLOAT,
 D3D10_DSV_DIMENSION_TEXTURE2D
};
g_iDevice->CreateDepthStencilView( iTexture, &vdesc, &g_iDepthStencilView );
ZRELEASE( iTexture );

初期化はこれでおしまいです。

ここからは実際にレンダリングに必要な設定を行います。
今回は非常に単純な描画なので、特に毎フレーム実行しなくても良い処理を
初期化の部分に書いてしまっています。
シェーダーの生成以外の部分、パラメータ設定関連は、必要であれば1フレーム
の描画中に何回か書き換えることになるでしょう。

// レンダーターゲットの設定
g_iDevice->OMSetRenderTargets( 1,
  &g_iRenderTargetView, g_iDepthStencilView );

// ビューポートの設定
D3D10_VIEWPORT viewport= {
 0, 0, width, height, 0.0f, 1.0f
};
g_iDevice->RSSetViewports( 1, &viewport );

これら関数はどちらも一度に複数個登録できます。今回は1つだけなので
定数 1 を最初に渡しています。

// shader の作成
ID3D10Blob* iblob= NULL;
if( FAILED( D3DX10CreateEffectFromFile(
  TEXT(“cube.fx”),
  NULL, NULL, “fx_4_0”, 0, 0, g_iDevice,
  NULL, NULL, &g_iEffect, &iblob, NULL ) ) ){

 OutputDebugStringA( reinterpret_cast(
   iblob->GetBufferPointer() ) ); // エラーが発生したら表示
 ZRELEASE( iblob );
}

ここでは fx ファイルから Shader を作成しています。
今回のサンプルでは ID3D10Effect をそのまま使っています。
HLSL のコンパイル時にはエラーが発生することがあるので、
コンパイラのエラーメッセージをそのまま表示出力しています。

最後に Effect の変数を初期化しています。

// projection の設定
D3DXMATRIX projection;
D3DXMatrixPerspectiveFovLH( &projection, 3.141592f/3.0f,
 WINDOW_SIZE_WIDTH/(float)WINDOW_SIZE_HEIGHT, 1.0f, 10000.0f );
g_iEffect->GetVariableByName( “Projection” )->AsMatrix()->SetMatrix( (float*)&projection );

次回は残りの処理と描画です。

Advanced W-ZERO3[es] のキーコード調査

ようやく Advanced W-ZERO3[es] WS011SH に触ることができました。
1~2時間ほどでしたけどキーコードの調査ができました。
協力してくれた Florian さんありがとうございました。

互換性のない問題の2キー

   es  ades
[,<] BC00 BC36
[.>] BE01 BE35

上記以外は互換性がありました。
ZERO3(WS003/004SH)や EM・ONE も es と同じです。
emobile EMONE のキーコード調査

ades の新規追加キー

[半全] F346

emobile EM・ONE の利用状況

Mugen Power 3400mAh の大容量バッテリーを使い出してから
ちょうど一ヶ月です。
EMOBILE EM・ONE スーパー大容量バッテリー
バッテリーの持ちの良さは満足です。
自分の利用頻度だとぎりぎり一週間持たない程度で、ほぼ6日目の通信中に
空っぽになります。

とはいえバッテリーの分だけ本体は確実に大きく重くなっているので、
  かばんに入れる→取り出す頻度が減る
点も、長寿命化に貢献している可能性が大です。

現在の主な用途は次の2つです

・通信用 HSDPA
  NetFront v3.3
  メールもこれ

・テキストエディタ
  PocketWZ 3.0 + WM5 patch + USB外付けキーボード
  会議の議事録、メモなどもこれ

PC との同期や開発用にデバッガを使う場合は Bluetooth を使っています。
USB はノートPC のモデムとして使うため、モデムモード固定にしています。
ただ最近は ZERO[es] や EM・ONE +キーボード済ませているので、
あまりノートを持ち歩かなくなりました。
(でもUSBキーボードは持ち歩いてます)

もう1つ個人的に非常に重要なアプリとして TOMBO があります。
昔から PDA で書き込んできたメモやデータの閲覧に使っており、
15年以上前の SHARP の電子手帳で、ぽちぽちと打ち込んだデータまで
しっかり手元に残ってます。

PA-8500~ 電子手帳 「メモ機能」
 ↓
PI-3000~ ザウルス 「レポート&自由帳」
 ↓
MI-110M~ ザウルス 「レポート&自由帳」
 ↓
iPAQ h3630 PocketPC 「TOMBO」
 ~ ZERO3 ~ EM・ONE

こんな感じで上位機種へのデータ転送&乗換えを繰り返してきました。
PocketPC に乗り換えるときに、レポート&自由帳のコンバート先として
TOMBO がぴったりでした。蓄積したデータを保持するために今では
なくてはならないものとなっています。
このソフトには本当にお世話になってます。

Direct3D 10 ClearState

Direct3D10 では、SetShaderResources や SetConstantBuffers や
OMSetRenderTargets や SOSetTargets や IASetVertexBuffers など、
レンダリングに必要なリソース登録 API のほとんどが
複数設定可能になっています。
一度に登録可能なスロット数も 16だったり 128だったり、
非常に多くなりました。

Set してあるインターフェースは Release() して解放されず、
Read 用に Set してあるリソースは Write 用に Set することができません。
使い終わったら、または別の用途で設定する場合にそれぞれ
NULL を設定して登録を解除します。

API も Slot も増えたので、シーン終了時など一度に初期化するのは
結構大変になったなと思ってました。
ところがこれ、ID3D10Device::ClearState() だけでいけるようです。
こんな便利な関数があったとは。
マニュアルはきちんと読んでおいた方がいいですね。

コンパイル速度の実験

最近 RADEON HD2900XT での動作確認もぼちぼち始めました。
この RADEON 用に用意したマシンは決して最新スペックのものではないです。
Pentium4 の 3.2GHz と、1年半~2年くらい前の標準的な構成で、
電源だけ取り替えて RAM を 2G に増設したものです。
だけど妙にコンパイルが速く感じるので不思議に思ってました。

普段使っている PC は 2年前の Pentium4 3.4GHz なので、
気のせいかなと思いつつ計測してみました。

実験は VisualStudio 2005 Professional のコンパイラで、
ソースファイルはヘッダ込みで 280本ほど。
Release と Debug の両方を同時に生成しているため、1つのソースを
2回コンパイルしていることになります。
結果は下記のとおり。

・RADEON用、Pentium4 3.2GHz RAM 2GB WindowsVista
  約5分 (セキュリティソフトなし)

・main PC、Pentium4 3.4GHz RAM 2GB WindowsVista
  約9分 (セキュリティソフト ON)

ほんとに速かった。
試しに main PC 側も常駐のセキュリティを切ってみると

・main PC、Pentium4 3.4GHz RAM 2GB WindowsVista
  約5分 (セキュリティソフト OFF)

速い! コンソールを流れるメッセージの速度が明らかに違います。

使っているのは avast! で、変更したのは「オンアクセス保護」の停止です。
他のセキュリティ系ソフトで試してないのでわかりませんが、予想以上に
影響がありますね。

他の PC でも試してみました。

・Core2Duo E6600 2.4GHz RAM 2GB WindowsVista
  avast ON : 約8分
  avast OFF : 約3分30秒

こちらは 2倍以上も違う結果になりました。
ON だとほとんど CPU の違いが吸収されてしまっています。
増えた分の大半は HDD とか I/O 関連が占めているのかもしれません。

これらは作業中の ついで の計測で、他のソフトも起動したままの状態で、
あまり厳密なチェックではない点ご了承ください。

でもこんなに差があるなら、開発中は LAN ケーブル抜いて、
セキュリティレベル下げてのコンパイルもありですね。

DirectX9 の時は、シェーダーの全コンパイルを行うと 2時間近くも時間が
かかってました。ファイル数が 1万を優に超えてたためです。
もしこれが、PC も変えないで半分くらいの時間で済むのだったら・・大変なことです。