Imageとテクスチャ

最近のGPUはビデオメモリが数GBとかになっていて、普通にテクスチャを置く程度ならいくらでも置けるようになってきている。とはいえ安いものでは少ないのもあるし、APUとかチップセット内蔵とかになるとメインメモリ共有になってたりもするし、ビデオメモリのアーキテクチャとその管理というのは昔からずっとややこしいままだ。
DirectX9では画像のデータを置く場所や管理方法を指定することができて、通常はDirectXに管理させるようにする。基本的にはビデオメモリとメインメモリの両方にデータを置いて、描画時にはビデオメモリのデータを使い、CPUでの編集時はメインメモリを編集→ビデオメモリに転送という感じで動作する。ビデオメモリが足りなくなったら自動的にビデオメモリのデータを消して、必要になったらメインメモリからまた自動的に転送する。このタイプのデータはデバイスロストが発生しても自動的に復帰する。
DXRubyのImageオブジェクトの画像はDirectXに管理してもらっている。昔は色々検討したり実験したりしていたのだが、結局労力&パフォーマンス的にそこに落ち着くことになった。ただ、RenderTargetの画像だけはGPUで描き込むテクスチャだからビデオメモリ上にのみ存在するタイプになっていて、メインメモリ上には存在しない。ImageとRenderTargetが別クラスになっているのはこの制約のせいなのだな。

今回はImageオブジェクトと内部テクスチャデータについて。


描画系を見る前に、描画データをどのように持つかというのを知っておく必要がある。ということで、Imageオブジェクトとテクスチャデータをどのように保持しているのかの話。
DXRubyはもともと同じテクスチャを参照する複数のImageオブジェクトを作ることができた。いまでもImage.load_tilesとかのメソッドで配列化すると1つのテクスチャを参照する複数のImageオブジェクトができる。オプションで別のテクスチャにすることもできるようになっている。
このへんには色々と経緯があって。描画時にテクスチャを切り替えるとオーバーヘッドがでかくて、できれば全部の画像を1つのテクスチャにまとめて、部分参照したImageオブジェクトを作るのが望ましい、みたいな感じだったのだ。ただまあ、そこまで限界を狙って実行速度を追求するユーザーもおらんし、とか言ってるうちにPCやRubyも速くなって、必要ないかなーと言う感じになってきて、そういう仕様を消す方向で修正してきたけども、それでもマップ描画だけは厳しかったから、そこだけ残っているわけだ。
根本的なコードにはまだその仕様はばっちり残っていて、そのへんを理解しないとたぶん無駄にややこしい仕掛けに見える。

DXRubyのソースのimage.hにこのような宣言がある。

/* Imageオブジェクトの中身 */
struct DXRubyImage {
    struct DXRubyTexture *texture;
    float x;     /* x始点位置      */
    float y;     /* y始点位置      */
    float width; /* イメージの幅   */
    float height;/* イメージの高さ */
};

/* テクスチャデータ */
struct DXRubyTexture {
    LPDIRECT3DTEXTURE9 pD3DTexture;     /* ピクチャに使うテクスチャ   */
    int width;
    int height;
    int refcount;
};

ここでいうDXRubyImageというのがImageオブジェクトが持つ構造体で、見てわかるようにx/y/width/heightは部分参照用にテクスチャデータと別の値が持てるようになっている。当初はこの値がRuby側から設定可能だった。いまは不可能。
DXRubyTextureのほうはImageが参照するテクスチャデータで、DirectXのDirect3DTexture9オブジェクトを持つ。refcountという変数があって、Imageから参照されると増える。ImageがGCに回収されたときに減って、0になるとfreeされるという参照カウント処理を自前で実装してある。
このあたりの処理はマップ描画がどうにか速くなってくれればバッサリと削除できるわけだが、DircetX9に何かを期待するわけにもいかないからずっとこのままだろう。

テクスチャを扱う部分はD3DXを使っていて、これがmingwgccにヘッダが無い。うまいこと細工すれば使えるという話もあるのだが、なんかいろいろ面倒なので現状コンパイラはVC2008を使っている。VC2008でmswin32とリンクできる拡張ライブラリを作るにはVC6.0でコンパイルされたRubyが必要で、1.9.2p136までは提供してくれているのだけども。
http://www.garbagecollect.jp/ruby/mswin32/ja/download/release.html
1.9.3も無い状態なので2.0は望めそうにない。DXRubyのRuby2.0対応はいまのところ計画できない、と言うことになる。