DXライブラリでスレッドを使ったファイルロード

Cation

2012/1/29に公開されたDXライブラリ3.07で非同期読み込みが公式にサポートされたので、特殊用途以外では以下の記事の内容はあんまし意味ないです...
……また自力で実装した機能が1ヶ月以内に公式サポートだよどうなってんのorz

DXライブラリ環境でマルチスレッド使って、裏でファイルをロードする

というのが今回の記事のテーマ。
NowLoading中にアニメーションとかしたいですよね。
自分で実装しようとした時に上記の具体的なコード例があんまし見つからなかった上に、あってもC言語ベタベタだったり、betterCなC++だったりするしで結構アレだった。
そして取り敢えずロード失敗とかしないのが出来たっぽいので、記事にしてみた。

まず前提として、DXライブラリはマルチスレッドに対応していない。
スレッドを立ててLoadGraphやらLoadSoundMem、DrawGraphやらを実行すると、データぶっ壊れた状態でロードされたり、描画されなかったりで色々と爆発する。
一応公式でマルチスレッドで死に「にくい」状態にする関数は存在するが、まぁ結構な確率でロード失敗とか使えたもんじゃない。

DXライブラリの関数を並列化は無理...なので

逆に言えば、DXライブラリ固有の動作"のみ"が並列になっていなければよい。(…と思う)
実際に試してみて気づいたのが、どうやらファイルをメモリ上にロードする系の機能(FileRead_openやFileRead_readなど)はマルチスレッドで何も問題がない。
※ 間違ってたらとても恥ずかしいのでツッコミください
という事で、メモリ上に読み込みだけを上記関数で行い、そのメモリ上のファイルからハンドルを作成するだけ。
ハンドル作成に要する時間は僅かなので、60FPSを維持するのも簡単だろうと思う。

実際のコード

動くのはこちら(https://github.com/joy1192/joylib)のDXLibs以下。Boost必須。まぁC++使っててゲーム作っているならBoost使っていると思うので(ry
※ 上記の開発環境はVS2010+Boost1.47.0+DXライブラリ
何してるかを簡単に書くと、
スレッド上で

    // ファイルを格納するメモリ領域の確保
    // 明示的new/delete面倒なのでvector使っとく
    std::vector<char> buffer( FileSize, 0 );

    // 指定ファイルを開く
    int FileHandle = FileRead_open( "a.bmp" ) ;

    // ファイルを丸ごと確保済みメモリに読み込む
    FileRead_read( &buffer[0], buffer.size(), FileHandle ) ;

    // ファイルを閉じる
    FileRead_close( FileHandle ) ;

    // DXライブラリがマルチスレッドに対応してないので、クリティカルな部分を保護
    {
        // このスコープ内は排他制御するようにする
        boost::mutex::scoped_lock lock(guard);

        // メモリ上に存在するファイルを使ってハンドルを作成する関数
        // (画像の場合は)CreateGraphFromMemを使ってハンドルを作成
        int Handle = CreateGraphFromMem( &buffer[0], buffer.size() ) ;

        // これでハンドル作れたので、このint値を好みな方法でどっかに格納したり、返せばOK
    }
    // ハンドル作ったらbufferの中身は要らないので、関数の終わりでvectorのデストラクタによって破棄

みたいな感じのを実装する。そうすると、普通に裏でロードができている。
描画は試してないのでアレだけれども、まぁメインスレッドで描画してファイルのロードだけ後ろでやればスムーズに動くし良いんじゃないですかね!


これ変だろとかバグあんぞとか指摘してくれると非常に喜びます。

追記

vector面倒だからって使ってるけど、初期化動作考慮に入れるとパフォーマンス悪いんじゃない? との指摘を頂いたのでboost::chronoでナノ秒単位で実測してみたところ、vectormalloc(※1)でロード部分を書いた各プログラムの実行時間の差が
6MB.mp3 : 0.008秒
382kb.jpg:0.0004秒
程度になりました。
vectorの方が早い場合もあり、おそらく誤差範囲であろうという結論に達しました。
(i7機なのでスペックもっと落とすともしかして有意差が出るかも?)


※1:get_temporary_bufferだとmallocの方が早かった