Imageクラスを再設計する

なんとかImageオブジェクトに対して自由に描画できないものだろうか。
と、ここんとこずーっと考えている。


たとえばImageオブジェクトを全てD3DUSAGE_RENDERTARGETとして、同時にD3DPOOL_DEFAULTとD3DPOOL_SYSTEMMEMを指定したテクスチャを2つ作る。
ビデオメモリとシステムメモリだ。
Image.draw系メソッドを追加し、これでDrawPrimitiveUPでテクスチャ間転送を行う。
従来のlineなどはシステムメモリ側をロックして書き込む。
いつロストするかわからないから、Image.draw系メソッドが成功したらすぐにGetRenderTargetDataしてシステムメモリに保存する。
しかしこれではものすごい遅そうだから、Image.draw系の処理は溜め込んでおいて、必要になったときに処理するようにする。
lineなどでシステムメモリを操作する場合は、最終的な描画か次のImage.draw系が実行されたときにUpdateTextureでコピーしてから操作するようにフラグを立てておく。
要はハードウェア描画が可能なD3DPOOL_MANAGEDの自力実装だ。
Image.draw系メソッドの呼び出し前にはTestCooperativeLevelでロスト状態をチェックする。ロストしていたらループして使えるようになるまで待つ。
復元処理はビデオメモリ側のテクスチャをすべて解放し、再作成してからシステムメモリのデータをコピーする。
すべて解放するためには、Ruby側が保持するポインタだけではダメだから、DXRubyでリスト構造を作って参照できるようにする。


だいたいこんな感じか。
Window.draw系メソッドは最終描画時のテクスチャが使用されるから、間にImage.draw系を挟んでも途中の絵は描画されない。
これはD3DXSpriteでもそのようになっていて、描画順ソートするためにはそういう仕様にしておかないといけない。
対策するためにはImageの更新と描画のタイミングでテクスチャをコピーしていけばいいが、そこまでしてどうにかしなければならないものではないと思う。
逆にImage.draw系メソッドはその時点のデータが使われることが保証される必要があるから、Window.draw系のようにソートしてはいけない。
で、このような仕組みを考えてみたわけだが、GetRenderTargetDataがどうしてもネックになると予想している。
転送速度が結構新しいPCを使っていても500MB/sec程度だったとどこかで見た覚えがあって、そうすると1フレーム10MB/secを切ってきて、640*480のサイズで1.2MB、画面サイズの画像を数回Image.draw→Image.lineするだけで1fps以下になりそうな遅さだ。
そういう使い方をするものではない、ということだろう。


もう一つ先を考えると、lineやbox程度ならD3DXLineや1ピクセルテクスチャの拡大でハードウェア描画をすることができる。
circleだけは無理だが、テクスチャを生成してロック、ソフトで描画→ハードウェア描画でどうにかなる。
GetRenderTargetDataするよりかは速いはずだ。
D3DXLineも結局テクスチャを生成しているのだと思うから、それ相応の遅さではあるだろう。
そっちも自前でやってもよい。
この手を使えば、1ピクセル単位の処理をするときにしか処理中のGetRenderTargetDataは発生しなくなる。
ハードウェア描画後のGetRenderTargetDataはどうしても必要だから、大きいImageオブジェクト複数への描画を毎フレーム行うのは速度的に無理があるが、そればっかりはしょうがない。
んで、それを実装すれば現在できないWindow.lineなどの処理もできるようになる。


まあ、lineやcircle程度をハードウェア描画させたほうがいいのかどうかは微妙なところだ。
GetRenderTargetDataよりもUpdateTextureのほうがずっと速いし、小さい図形ならソフトでやったほうが速くなる。
このへんの話はまた別ということで、基本的な方針は上で書いたようなやりかたをとりあえず試すことになるだろう。
かなり大きな修正になりそうだし、パフォーマンスや品質によりボツになる可能性もあるが、うまくいけば自由度と表現力の大幅な強化となるだろう。