新機能追加の件

このあいだ、会社の帰りにふと思いついたので実装してみた。

RenderTarget#regenerate_proc=

現状ではRenderTargetはデバイスロスト問題を回避するために毎フレーム強制的にクリアしている。この制限があると、RenderTargetで画面を構成しようとした場合に描画回数が増えてしまい、フレームごとに同じ画像を描画しているだけだったとしてもパフォーマンスが悪くなる。WSなんかの遅さの原因は主にこれだと思うし、全画面に対するエフェクトもモノによっては作るのに苦労することになる。
RenderTarget#to_imageで保存してキャッシュすることもできるが、この処理は大変遅いので画像が次のフレームで変わるかどうかわからない場合や、頻繁に変わる場合には使いにくい。
こういったパフォーマンス的な問題点に対処するために、RenderTargetをクリアしないオプションを用意した。
RenderTarget#regenerate_proc=にProcオブジェクトを渡すことで、RenderTargetはクリアされなくなる。渡したProcはデバイスロストが発生してRenderTargetがクリアされてしまった場合に呼ばれるので、ここでRenderTargetの画像を再生するようなコードを書いておく。
DXRubyではデバイスロストで消えるのはRenderTargetのみで、Imageの画像は消えない。

実例

require 'dxruby'

rt = RenderTarget.new(640,480)
#rt.bgcolor=C_RED
#rt.regenerate_proc = ->{rt.clear}

x = 0
Window.loop do
  rt.draw_box_fill(x, 0, x+100, 100, C_WHITE)
  Window.draw(0, 0, rt)
  x += 1
  Window.test_device_lost if Input.key_push?(K_SPACE)
end

このコードでは白い四角が右に移動していくわけだが、

コメント行を有効にすると白い四角が移動せずに右に拡大していくことになる。RenderTargetがクリアされないので前の状態が残っているということだ。この時点で背景が赤にならないのは、クリアされないのでbgcolorが反映されないからである。

また、デバイスロストを発生させるのは面倒なので、簡単にテストできるようにWindow.test_device_lostを用意した。これを呼ぶと存在するすべてのRenderTargetオブジェクトが強制的に黒で塗りつぶされ、regenerate_procが設定されていた場合は呼ばれる。
結果として、スペースキーを押すとクリアされるので大きくなっていた四角は初期サイズに戻り、RenderTarget#clear(新メソッド)により背景が赤になって、途中から再び大きくなっていくような動作をする。

って書いてから思ったのだが、これは動作の解説なだけで、実際どういう場合に有効に使えるのかの説明には全くなっていない。

用途

基本的にはパフォーマンスの改善ということではあるのだが、どういったパターンで改善されるのかは使い方による。
また、短時間のエフェクトで追記描画をするという使い方はアリで、デバイスロストが発生したとしてもエフェクトがすぐに終わる場合は、デバイスロストから復帰するよりもエフェクトが先に終わってしまったりもするだろうし、見えてたとしても短い間だけ画像が崩れるような感じで済むのでたいした問題は無いかもしれない。場合によっては追記描画をするほうがコードが簡単になる例もありそうだ。
いずれにせよ、結構マニアックな機能ということになりそうなので、面白い使い方があれば教えてもらえると嬉しいし、コードを提供してもらえたらサンプルに入れちゃってもいいかもしれない。