64bit 開発設定のメモ

Windows で x64 用の build 環境を作る時のメモです。

●VisualStudio 2005 IDE

MSDN 方法 : Visual C++ プロジェクトを 64 ビット プラットフォーム用に設定する

構成マネージャで新しい Platform を追加するだけでいいようです。
WindowsCE の CPU 追加と同じような感じ。
その後 Options の Projects and Solutions → VC++ Directories でも
Platform を選択してフォルダなどの確認や追加をします。
32bit 版 Vista で作業しているので、クロス開発用バイナリ x86_amd64 が
選択されていました。
DirectX SDK 用に下記の設定を追加します。必ず一番上にしておきます。

Executable files:
 $(DXSDK_DIR)Utilities\Bin\x64

Inclue files:
 $(DXSDK_DIR)Include

Library files:
 $(DXSDK_DIR)Lib\x64

●コマンドライン用

MSDN 方法 : 64 ビットの Visual C++ ツールセットをコマンド ラインから有効にする

vcvarsall.bat の中を見ると各環境用の個別のファイルを呼び出して
いるだけでした。それぞれ専用の環境変数設定が用意されています。

x86        32bit 用
amd64      64bit 用
x86_amd64  32bit 上で 64bit バイナリの作成 (クロスコンパイル)

環境設定用 bat ファイル

x86       VC\bin\vcvars32.bat
adm64      VC\bin\amd64\vcvarsamd64.bat
x86_adm64  VC\bin\x86_amd64\vcvarsx86_amd64.bat

ひとつの Makefile で各環境用のバイナリを作るなら、これらの設定も
Makefile に記述することになるでしょう。

x64 の場合 lib のそれぞれのパスに lib\amd64 を追加するだけで、
または DirectX SDK のように x86 を x64 に置き換えます。
binary path の場合は上記のようにクロスコンパイルを含めた 3 種類の
構成があります。x86_amd64 では x86 用 bin も必要なので、その前に
x86_amd64 用パスを追加します。

x86
  C:\Program Files\Microsoft Visual Studio 8\VC\bin

amd64
  C:\Program Files\Microsoft Visual Studio 8\VC\bin\amd64

x86_amd64
  C:\Program Files\Microsoft Visual Studio 8\VC\bin\x86_amd64
  C:\Program Files\Microsoft Visual Studio 8\VC\bin

あとはコンパイルオプションの変更など。
ほぼ同じ指定が可能で -arch:SSE2 は不要。

__asm ははじかれたけど、intrin.h は大丈夫そうです。

_T() と TEXT() の違いやソースの文字コード

非常にいまさらな感じで、とっくに常識かもしれませんが、文字列
リテラルに用いる _T() と TEXT() はどこが違うのか気になったので
調べてみました。

Windows で文字や文字列を扱う場合、TCHAR (_TCHAR) 型を良く用います。
これはコンパイル時のオプションによって char と wchar_t に定義内容
が変わります。
TCHAR 型を使用しておくと同一ソースコードでも比較的容易に文字列の
扱いを切り替えることができます。

特に NT 系の API では、文字列として両方受け付けるよう関数が2セット
用意されていて、混在することが可能です。
9x 系の時代は MBCS による表現が用いられていたので、Local かつ
当時から引き継いでいるプログラムなどはそのまま char 型として扱う
ことができます。

逆に WindowsCE は wchar_t しか受け付けないので、CE とのコード共有を
考えると TCHAR 型への対応は重要でした。
ゲーム機でも Dreamcast では WindowsCE を選択できたので、
WindowsCE を使う場合は WCHAR 等の wide 型が用いられました。

TCHAR と同じように文字列定数や文字定数も切り替える必要があります。
その場合に用いられるのが _T() や TEXT() マクロです。この両者は同じ
意味だといわれています。

例えば _T(“initdata.dds”) とか TEXT(“maze.fx”) など、どちらで
記述しても特に差はありません。

_T() の実際の宣言は VC/include の tchar.h にありました。
定義部分だけ抽出すると下記のようになります。

// tchar.h
#ifdef  _UNICODE
#define __T(x)      L ## x
#else
#define __T(x)      x
#endif

#define _T(x)       __T(x)
#define _TEXT(x)    __T(x)

シンボル _UNICODE の定義によって __T() を切り替え、それを _T() と
_TEXT() に定義しなおしています。2段階定義になっているのは、おそらく
マクロ展開のためにプリプロセッサのパスを最低2回通すためでしょう。

 例えば #define DDSFILE “initdata.dds” と定義した場合
 _T(DDSFILE) だと L”initdata.dds” になりますが
 __T(DDSFILE) の場合は LDDSFILE となります。

TEXT() の定義は VC/PlatformSDK/include の winnt.h の中にありました。

// winnt.h
ifdef UNICODE
#define __TEXT(quote) L##quote      // r_winnt
#else
#define __TEXT(quote) quote         // r_winnt
#endif

#define TEXT(quote) __TEXT(quote)   // r_winnt

定義されている意味は同じです。こちらはシンボル UNICODE の定義に
よって __TEXT() の内容が変わり、それをさらに TEXT() に定義
しなおしています。

_TEXT() は tchar.h だけど TEXT() と __TEXT() は winnt.h など、
双方シンボルが衝突しないように微妙に使い分けられているようです。

_T() と TEXT() はそれぞれ定義している場所が違うので、include して
いるヘッダによっては必ずしも両方使えるとは限らないようです。
また UNICODE、_UNICODE の両方を必ず定義するか、または定義しない
ようにしないと定義内容が異なってしまいます。

ヘッダを調べると、一部のヘッダ (OleDlg.h, MSAcm.h, pdh.h) では
UNICODE と _UNICODE の同期を取るコードが含まれていました。

OleDlg.h
// syncronize UNICODE options
#if defined(_UNICODE) && !defined(UNICODE)
        #define UNICODE
#endif
#if defined(UNICODE) && !defined(_UNICODE)
        #define _UNICODE
#endif

ただしこれらのヘッダは必ず include されるとは限らないので、
結局は VisualStudio が挿入するコマンド行シンボル
/D “UNICODE” /D “_UNICODE” に依存しているようです。

ちなみに Windows の wide character は UNICODE の 16bit エンコー
ディングで、MBCS では ShiftJIS が使われていました。
UTF-8 など、UNICODE でも MBCS の可能性があります。
文字まわりはいつも苦労するところですが、個人的にはプログラム中
でも UTF-8 を使うことが結構あります。

プログラム中で扱う文字コードの他に、ソースコードの文字をどうしようか、
といった悩みもあります。以前は OS の言語によってソースを ShiftJIS
とみなしてしまう仕様があって、0x5c(\) の処理でよく問題が
出ていました。

マニュアルを見ると VisualStudio2005 では、BOM さえあれば UTF-8
でも大丈夫そうです。
MSDN コンパイラおよびリンカでの Unicode のサポート

試してみました。テキストエディタでソースを ShiftJIS で開いて

// 表
 Error

とか書いて保存します。UTF-8 で開くと

// [95]\
 Error

となります。文字としては不正ですがとりあえずやってみます。
bom つきで保存するとコンパイルエラーにはならず、行末の \ が
有効となっていることがわかります。(vim で :se bomb )
bom 無しで保存すると ShiftJIS とみなされ、行末の \ が無視
されてコンパイルエラーになります。(vim で :se nobomb )
きちんと UTF-8 を認識しています。

さらに、UTF-8 かつ BOM 付きで実際にコンパイルされた文字コードを
見てみます。

wchar_t* wstr= L"あい";
char*    str= "あい";

ソースコードは 0xe3 0x81 0x82 0xe3 0x81 0x84 で UTF-8 です。

wstr の表示出力は 0x3042 0x3044 0x0000
str の表示出力は 0x82 0xa0 0x82 0xa2 0x00

結局 UTF-16(UCS-2) と ShiftJIS に変換されていました。
UTF-8 そのままは通らないようです。

Direct3D 10 DXGI_FORMAT の機能対応一覧

DXGI_FORMAT_R11G11B10_FLOAT は RenderTarget として使えましたが、
DXGI_FORMAT_R9G9B9E5_SHAREDEXP はレンダリングできませんでした。
Direct3D 10 DXGI_FORMAT_R11G11B10_FLOAT の実験(2)
Direct3D 10 DXGI_FORMAT_R11G11B10_FLOAT の実験

また Direct3D10/DirectX10 では Pixel 形式も頂点形式もインデックスも
統合されています。BC1 (DXT1) は画像圧縮用フォーマットなので、
これは頂点形式に使えるのでしょうか。

フォーマット Type 毎に対応している機能や、どの用途に使えるのか
ID3D10Device::CheckFormatSupport() で調べることができます。

各フォーマットの機能一覧を作成してみました。

Direct3D 10.0 DXGI_FORMAT 機能対応一覧

表の BF は CreateBuffer() で作成できるかどうか、
1D ~ 3D はそれぞれ CreateTexture1D() ~ 3D() で作成できるかどうか、
Texture として使えるかどうかを意味しています。

BF = ID3D10Buffer
1D = ID3DTexture1D
2D = ID3DTexture2D
3D = ID3DTexture3D

VB, IB, SO は、VertexBuffer, IndexBuffer, StreamOutput や
InputLayout の定義に使えるかどうかです。これを見ると、やはり
BC1~BC5 (DXT) や、R8G8_B8G8/G8R8_G8B8 (YUV) などの圧縮形式は
画像以外に使えないことがわかります。

R9G9B9E5_SHAREDEXP はだめですが、R11G11B10_FLOAT は頂点でも
使用できるようです。

IndexBuffer に使えるのは
R32_UINT (32bit), R16_UINT (16bit) しかありませんでした。

VB = InputLayout (VertexBuffer)
IB = IndexBuffer
SO = StreamOutput

SL, SS, SC は、シェーダー中のリソースアクセス手段を示しています。
SL は Sampler を使わないで直接 Load() 命令が使えるかどうか、
SS は Sampler 経由の Sample() 命令が使えるかどうかです。

同じように SC は SampleCmp() 系命令に対応しています。わかりやすく
言えば、SC が付いたフォーマットはハードウエアシャドウマップ
(NVIDIA ShadowMap) に使えるということです。

SL = Load()
SS = Sample() / SampleGrad() / SampleLevel()
SC = SampleCmp() / SampleCmpLevelZero()

MI は MipMap です。ほとんどすべてのフォーマットが使用可能に
なっています。MA は Mipmap の自動生成対応かどうか。

MI = MipMap
MA = MipMap 自動生成

RT が付いたものは RenderTarget に使えます。整数形式を含めて
レンダリング可能な形式は結構多いようです。ただし Blend 可能な
なのは BL が付いたものだけです。Blend 対応フォーマットはそれほど
多くありませんが、32bit FLOAT もしっかり blend 対応になっている
など D3D9 世代と比べたらかなりの贅沢さです。

DS は DepthStencil 用フォーマットです。DepthStencil はまだまだ
特殊なフォーマットであることがわかります。

MT が付いたものはさらに MultiSample 対応になります。
DI (Display) は単なる RenderTarget ではなく、FrontBuffer として
使用可能なフォーマットを意味しているようです。

RT = RenderTarget
BL = Blend 対応
DS = DepthStencil
MT = MultiSample RenderTarget
DI = Display

その他の機能シンボルの意味は下記のとおりです。

CL = CPU で Lock (Map)できる
CW = 他の format に cast できる
MR = MultiSample Resolution 対応
ML = Multisample テクスチャを Load() 可能

表を見ていると 1bit 形式の DXGI_FORMAT_R1_UNORM はかなり特殊な
フォーマットであることがわかります。2D Texture にしか使えず
Mipmap もありません。RenderTarget もだめ。

またこのフォーマット機能表を確認しておくと、Direct3D 10.1 の
機能拡張の意味が見えてきます。
まず block-compressed (BC?) texture へのレンダリングができる
ようになるとのこと。

さらに Blend 対応の拡張があります。10.0 だと下記のものだけですが、
これが DirectX 10.1 ではすべての UNORM、SNORM で使えるように
なるそうです。

R32G32B32A32_FLOAT
R32G32_FLOAT
R32_FLOAT
R16G16B16A16_FLOAT
R16G16_FLOAT
R16_FLOAT
R11G11B10_FLOAT
R10G10B10A2_UNORM
R8G8B8A8_UNORM
R8G8B8A8_UNORM_SRGB
R8G8_UNORM
R8_UNORM
A8_UNORM

Direct3D 10 DXGI_FORMAT_R11G11B10_FLOAT の実験(2)

D3D10/DX10 の新しいテクスチャフォーマットの続きです。
Direct3D 10 DXGI_FORMAT_R11G11B10_FLOAT の実験

DXGI_FORMAT_R11G11B10_FLOAT の 11bit / 10bit FLOAT は、
指数部が 16F と同じだったので 16F との相互変換は比較的容易です。
符号を落として下位bit を切り詰めるだけで十分かもしれません。
実際に相互変換してみました。

まず DXGI_FORMAT_R11G11B10_FLOAT への変換です。16F に変換した後
下位 bit の切り落としを行っています。符号が無いので入力が負数の
場合は 0 にクランプしています。

DWORD F32toF11( const float* f32 )
{
    WORD  _f16[3];
    D3DXFloat32To16Array(
            reinterpret_cast( _f16 ), f32, 3 );

    for( int i= 0 ; i< 3 ; i++ ){
        if( _f16[i] & 0x8000 ){
            _f16[i]= 0;
        }
    }

    return  (((DWORD)_f16[0] >>  4) & 0x000007ff)
           |(((DWORD)_f16[1] <<  7) & 0x003ff800)
           |(((DWORD)_f16[2] << 17) & 0xffc00000);
}

次に DXGI_FORMAT_R11G11B10_FLOAT から 32F に変換してみます。
同じように一旦 16F を経由しています。

void F11toF32( float* f32, DWORD f11 )
{
    WORD   _f16[3];
    _f16[0]= (WORD)((f11 << 4) & 0x7ff0);
    _f16[1]= (WORD)((f11 >> 7) & 0x7ff0);
    _f16[2]= (WORD)((f11 >>17) & 0x7fe0);

    D3DXFloat16To32Array( f32,
            reinterpret_cast( _f16 ), 3 );
}

D3DXFLOAT16 のメンバはマニュアルでは WORD Value となってますが、
実際には小文字の value でした。しかも protected だったので
上の例では cast でごまかしています。

DXGI_FORMAT_R11G11B10_FLOAT と同様、Direct3D10/DirectX10 でもう
1つ追加された新フォーマットがあります。

DXGI_FORMAT_R9G9B9E5_SHAREDEXP

こちらも調べてみました。5bit の exponent を持つことから、
R11G11B10_FLOAT 同様により広い範囲をカバーできます。ただし共有
されているため極端にスケールの異なる component を持つことは
できないでしょう。

FLOAT のグループではないことと、フォーマット名に E の成分も記載
されていることから、exponent の演算はシェーダーを併用するのでは
ないかと予想していました。

ところが実際に試すと E5 の成分がきちんと反映された値が返ってきます。
またシェーダーでは E5 の値を直接読み取ることができず、w は存在
しませんでした。(w を読み取ろうとすると 1.0 固定となる)
また RenderTarget にはできません。

R9G9B9E5_SHAREDEXP の各 bit の意味は下記の通りです。

E5    B9        G9        R9
01111 111111111 111111111 111111111

指数部は 5bit なので、16F や R11G11B10_FLOAT と同じように 15
が 0 の offset 15 と仮定します。このとき R9G9B9 を 0 にすると
真っ黒になりました。

上のように R G B すべての bit を 1 にするとほぼ (1.0, 1.0, 1.0)
相当になっています。ただ厳密に一致せず、それぞれわずかに 1.0
より小さな値になります。

この結果からわかるのは、指数部は 16F や R11G11B10_FLOAT ほぼ同じ
内容で合っているのではないかということ。
また R9G9B9 は IEEE754 等の浮動少数の仮数に近いものではなく、
9bit の値がそのまま格納されていることです。511 が厳密に 1.0 に
ならないため、R8G8B8_UNORM のような UNORM 変換 1/511 倍ではなく
1/512 倍している可能性があります。

次に 0xc0040201 を入れてみたところ完全に 1.0 に一致しました。

// 0xc0040201
E5    B9        G9        R9
11000 000000001 000000001 000000001

これは上記のように各 component が 1 で指数は 24 になっています。
offset は 15 なので 24-15 = 9、つまり 1 * (2^9) = 512 相当に
なります。簡単に書けば 1<<9 です。 同様に 0x84020100 でも (1.0, 1.0, 1.0) になります。こちらは
最上位に合わせており次のようになります。

// 0x84020100
E5    B9        G9        R9
10000 100000000 100000000 100000000

16-15 = 1、256 * (2^1) = 512 です。
よって offset 15 の exponent とみなすよりも、offset 16 で正規化
し、最上位 bit を隠していない浮動少数とみなした方がしっくりくる
かもしれません。

16F も R11G11B10_FLOAT も R9G9B9E5_SHAREDEXP も指数部は全く同じ
5bit となっていました。それぞれ exponent の機能はほぼ同一なので、
この仕様はもしかしたらハードウエア的な都合で決められたものなの
かもしれません。

ZERO3[es] に 小型 Bluetooth アダプタ PTM-UBT3S をつないでみた

EM・ONE を WindowsMobile6 アップグレードのため発送しました。
EMOBILE EM・ONE WindowsMobile6 アップグレード申し込み
2007/10/05 の受付開始の朝にすぐ申し込んで、用紙が届いたのが
2007/10/10。2007/10/11 発送でいつ届くか楽しみです。

しばらく EM・ONE が使えなくなったので、久しぶりに W-ZERO3[es] (WS007SH)
を使っています。久しぶりに使うと ZERO3[es] は妙に使いやすく感じます。
カーソルキーが押しやすいのと、長いこと使って慣れていたせい
かもしれません。

最近は Bluetooth キーボード を持ち歩いているので Bluetooth 接続だけは
何とか実現したいところ。

試しに、以前購入した PTM-UBT3S をつないでみました。

変換コネクタが巨大に見えてしまいます。

PTM-UBT3S

↑PTM-UBT3S 本体の角は引っかかりそうだったので やすりで削っています。

つないだところ。

PTM-UBT3SとZERO3[es]

Bluetooth の設定機能を呼び出すために、こちらの星羽さんの
WakeBT Ver1.01 を使わせていただきました。
[自作] Bluetooth機能を呼び起こす WakeBT Ver1.01
インストールして実行するだけなのでとても簡単でした。便利です。

あとは EM・ONE と同じ手順で、設定画面からペアリングすることが可能です。
とりあえず確認したのは下記の 2つです。

・PC との ActiveSync 接続
・Bluetooth キーボード RBK-2000BT2 の接続

便利になりました。
音声系のデバイスは持っていないので、ヘッドセットとかは試してないです。

ちょっと気になったのは、このアダプタをつないでいると ZERO3 が
オートパワーオフしなくなること。ここだけ注意です。
どこかに設定があるのかもしれません。待ち受けだといいのかも。

関連エントリ
小型 Bluetooth アダプタ Princeton PTM-UBT3S
Bluetooth キーボード Rboard for Keitai RBK-2000BT2
em1key Bluetooth keyboard RBK-2000BTII の設定 その3