プログラム全般」カテゴリーアーカイブ

_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 そのままは通らないようです。

static タスクシステム

ほとんどのゲーム開発では、一般的にタスクシステムと呼ばれる
独自のフレームワークを使用しています。
あまりドキュメント化されたり表に出ることが無いものの、
プロジェクトを通じてプログラマ間に秘伝的に伝播していくので
実に多くのバリエーションがあります。

プロジェクトの数だけ存在するといっても良いかもしれません。
基本構造と目的はほぼ似通っており、セガタスクと呼ばれる
こともあります。

OS のタスク同様、処理ごとに CPU 時間を細かく分割していく
仕組みです。ゲーム開発で特徴的なのは、すべての処理を
フレーム単位で管理することです。
1フレームの時間は約 16.7~33.3msec と決まっているので、
この中でそのフレームに必要なタスクを、全部一巡しなければ
なりません。

タスクシステムの利点、またはその利用目的は、大きく分けて
2種類あると考えています

 ・タスク数の動的な増減への対処
 ・モジュール分割や作業分担といった開発時の利便性のため

ゲーム中必要な処理は能動的に変化します。例えばオブジェクト
数の変化 – 追加や削除など、動的な処理単位の変化を効率よく
管理することが求められます。

オブジェクトだけでなく、ゲームやシステム全体の動作自身も
タスク化することができます。各種モジュールやサービスなども、
ゲーム進行やモードの遷移にあわせて組み換えが可能となる
わけです。

オブジェクトのような細かい単位と、ゲームシーン等の大きな
処理単位では、規模も目的も異なります。

これらの処理単位を同じ枠組みを使って同一に扱うこともあれば、
グループ毎に別の管理機構を用いることもあります。親子関係、
階層構造を持たせて凝った構造にしているものもあります。
この辺の考え方や設計はそのプロジェクトの方針に依存します。

ゲームの開発規模も、今ではプログラマで10数人規模とかなり
大きくなっています。オブジェクトの増減といったプログラム
処理上の都合だけでなく、分業化におけるフレームワーク
としても非常に重要な役割を持っています。
ある意味人間も管理するわけです。

各タスクは独立しているため作業分担が比較的容易で、システム
がきちんとしていれば、お互いの干渉衝突を最小限にしつつ
開発を進められることになります。
(でも終盤はそうも言っていられません)

上で述べたように、1フレームの処理を切り分けながら処理を
組み立てていきます。各タスクは1フレーム内で順番依存を
持っており、実行順をある程度明確にしなければなりません。

さまざまな手法がありますが、例えば各タスクに割り当てた
プライオリティ値を元に、実行順番を決めるものもあります。

システム系モジュールや作業分担時のタスクは、動的な追加
削除はあっても、動的にプライオリティが変わることが
ほとんどありません。

そういう enum や define 値で順番を決められたタスクも、
動的なリストで追加時にソートされ、リストをたどって仮想関数
呼び出しが行われています。
これを static にできないか考えてみました。
(ちなみに筆者は script ベースのまた違う手法を使っています。)

いわゆる TypeList です。
今回はソートのために Binary Tree を作ってみました。

各 Task は任意の priority 値と呼び出し口 Func() だけ定義
すればよく、Func() が static なら基底クラスも不要です。
各 task は別ソースだとして

class DrawBegin_Task {
public:
    enum {
        priority= 3000,
    };
    static void Func()
    {
    }
};

class DrawEnd_Task {
public:
    enum {
        priority= 5000,
    };
    static void Func()
    {
    }
};

class Input_Task {
public:
    enum {
        priority= 1000,
    };
    static void Func()
    {
        //..
    }
};

こんな形で呼び出します。

template
struct task {
    enum {
        value= A::priority,
    };
    static void	Exec()
    {
        A::Func();
    }
};

typedef	TS::Array<
            task,
            task,
            task
        > TaskList;

void MainLoop
{
    TS::ExecTask::result>::Exec();
}

実際の実行順は TaskList 上の順番とは異なり、priority 番号
の小さい順に並び替えられます。

そのため一度 TaskList に登録してしまえば、各処理ごとに
priority を自由にいじって処理の挿入先を変更することができます。
これはコンパイル時に決定します。
画面をクリアするだけとか Flip するだけの単純な task は
inline 展開が可能です。(__forceinline が必要でした)

とはいえ、もともと 1フレームに数回程度、非常に実行頻度の低い
部分なので static 化したとしてもパフォーマンス的には全く
変わらないでしょう。

動的な変更を行うオブジェクト管理は、これらの下に設けることに
なります。

上の例では static な関数しか呼べませんが、各タスクの
インスタンスも管理したいならこんな感じになるでしょうか。
それぞれ _TaskBase を継承しておきます。
ついでに priority も外部からも指定できるようにしておきます。

class _TaskBase {
};

static _TaskBase*  _InstanceTable[ TS::Length::value ];

template
struct itask {
    enum {
        value= pri,
    };
    static void	Exec();
};

template
RegisterInstance( A* _instance )
{
    _InstanceTable[TS::Index >::value]= _instance;
}

template
void  itask::Exec()
{
    _TaskBase* _inst= _InstanceTable[TS::Index >::value];
    reinterpret_cast(_inst)->Func();
}

class Camera_Task : public _TaskBase {
public:
    enum {
        priority= 2000,
    };
    void Func()
    {
        //..
    }
};

使い方は TaskList に登録して、RegisterInstance() を
呼び出すだけです。

typedef	TS::Array<
            itask,
            itask,
            itask,
            itask
        >    TaskList;

void MainLoop
{
    RegisterInstance( new Camera_Task );

    TS::ExecTask::result>::Exec();
}

あとは MainLoop から priority 順に呼び出してくれます。
この場合も仮想関数は不要で、呼び出しは static の例と同じように
静的に行われます。
またインスタンスを持たないものは RegisterInstance() する必要が
無く、Func() も static のままで構いません。
(ここは本当は何らかのエラー判定が欲しい部分です。)

一応実際に試したのでソースをのせてみます。
(確認はVisualStudio2005のみ)

StaticTaskSystem.h

wheelhandle_ss01t.zip 実際に使用した例のアーカイブ

本当はもっといろいろ改良が必要だと思います。

コンパイル速度の実験

最近 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 も変えないで半分くらいの時間で済むのだったら・・大変なことです。

vi の話

まったく 3D とは関係ないし、とても個人的なことで恐縮ですが
たまには開発環境に関するメモも記しておきたいと思います。

普段開発等で使ってるテキストエディタは vi です。
当初 vi を選んだ理由は単純に

 当時使っていた非力なミニコンでは、一番軽いエディタが vi だったため

です。(フルスペックの Emacs は非常に重くて、使っていると他の人に迷惑が
かかったらしい)

他にも端末の都合 (Ctrl+n 等の、Ctrl を併用したキー操作がリピート
できないキャラクタ端末だった) もありますが、
vi の選択は比較的消極的な理由でした。(emacs は憧れでした)

そんなわけで vi を使い始め、慣れてからは結局長いこと使い続けています。
日本語で文章を書いたりマニュアル作成もずっと vi です。

vi だけでなく emacs 系の操作もそれなりに使ってます。shell の行編集は
emacs mode ですし、そして何よりこの blog の初エントリが emacs 風の
キー操作に設定することですから。

WZ Mobile で Emacs もどき

WindowsMobile 等、vi が動かない処理系などでは結構 emacs 系のキーアサイン
で使っています。(PocketWZ3.0 も)

その非常に古くからある古典的なエディタの vi なのですが、ここ最近、
むしろ若い人の方が好んで vi (vim) を使っていて驚くことがあります。

確かに vim がものすごい勢いでパワーアップしており、その変貌ぶりには
目を見張るものがあります。

昔 vi を使っていた経験のある人に向けた、オリジナルを再現するためだけの
単なる vi クローンでは無いわけです。
新しい機能を次から次へと取り込んで、新機能や使いやすさだけでも使う価値
は十分。

昔から知っているオリジナルのユーザーにも、そして便利な機能を求める新しい
ユーザーにも、どちらにも魅力的なエディタとなっているのでしょう。

vi クローン系エディタはこれまでいくつも作られてきましたが、まず最初の
難関がオリジナルの再現度でした。ちょっとしたコマンドの組み合わせによっ
て複雑な動作が可能な vi は、些細な機能の違いも大きな差に感じることが
あります。

最初比較的使ったクローン系は stevie でした。
再現度も割り切りがあって画面更新のアルゴリズムはあまり優秀じゃない
ものの、軽量なので何度も移植したり手を加えたりと、
長い間お世話になりました。

画面分割機能を持った vi として xvi もありました。
これも簡易日本語化して移植したことがありますが、それ以外に極端に目立った
特長は無く、このときは結局カスタムされた stevie を使い続けていました。

elvis は emacs 系のノウハウが活かされており完成度は高かったのですが、
最初 vi らしさの1つである “行折り返し” が無く、なかなか常用に踏み切れ
ませんでした。これも移植したことがあります。

満を持して登場した vim にはやっぱり決定打となるべき魅力がありました。
再現度も高く ex モードまであって、オリジナルの vi にも無かった
Multi Level Undo ができること。visual select ができたり、プリプロセッサ
#if~ の対応も matcing fence できたり、新機能だけでも非常に便利な
エディタになっていました。

主に使い始めたのは vim3.0 からです。これもやはり日本語化移植を
行いました。(後にこれらのパッチを土田さんがまとめてくださいました)

ちなみに当時の vim は 8bit 透過ではなくて、内部コード 7bit で設計
されていました。最上位 8bit 目が visual mode の選択フラグとなっており、
反転表示するための描画情報だったのです。

そのため、8bit 透過を売りにする他のエディタに比べると、visual mode 用
データを別メモリに展開したりなどなど日本語化は結構手間がかかりました・・。

現在の vim の最新版は
http://www.vim.org/
こちらからダウンロードすることができます。
当たり前のように多国語対応なので、そのままで日本語が通ります。
便利です。

さまざまなツールやマクロ、syntax ファイルも公開されています。
例えば Shader fx (HLSL) 用の syntax を install すれば、fx の予約語など
きれいに色分け表示されます。
専用の syntax でなくても、とりあえず .fx を cpp の設定に追加するだけでも
割ときれいにいきます。

普段 fx や doxygen 用の設定を入れていますので、機会があればこの辺りの
設定などもご紹介します。