DirectXとRuby1.9の相性

ずいぶん前になるが、DXRubyでThreadを使うと動かないという話をどっかで見た。それも複数。
んで、手元でこんなコードを書くと、

require 'dxruby'

image = Image.new(100,100,C_WHITE)

rot = 0
th = Thread.new do
  loop do
    sleep 0.01
    rot += 1
  end
end

font = Font.new(32)
Window.loop do
  Window.draw_rot(0,0,image, rot)
  Window.draw_font(0,200,rot.to_s, font)
  if Input.key_push?(K_ESCAPE)
    break
  end
end

うーん、動くは動くけどなんか変だ。rotの値が1フレームに1しか増えていないように見える。
Ruby1.9ではGVLがあるからその絡みかな、と思ってFPS制御用のSleep中にGVLを解除するように直しておいた。たぶんスレッド周りの動作はマシになったと思う。1.8はよくわからんので放置。みんなRuby1.9を使ってね☆
まあ、調べてみてどうにかできそうだったら直しておこう。

それとは別に、DirectXRuby1.9の相性という問題がある。これはけっこう致命的な話なのでマニュアルに明記しておく必要がありそうだ。
DirectXは初期化したスレッドじゃないと描画を発行できないという制限があるのだ。Ruby1.8の時は単一スレッドで動作していたから影響なかったが、Ruby1.9はThreadオブジェクトがネイティブスレッドになっている。DirectXの初期化はRubyそのもののスレッドでやってるからつまりメインスレッドで、これらの結果としてDXRubyはRuby1.9ではメインスレッドからじゃないと描画(Window.loopのループともいう)を実行できない。
例えばこんなコードだ。

require 'dxruby'

image = Image.new(100,100,C_WHITE)

Thread.new do
  Window.loop do
    Window.draw(0,0,image)
    break if Input.key_push?(K_ESCAPE)
  end
end.join

これは1.8では動くが1.9では動かない。
この問題をどうにかしようと思うと、DXRuby内部をマルチスレッド化してDirectXを扱う専用のスレッドを用意しないといけないのではないかと考えている。根本的な仕掛けが変わることになるのですぐにはできない。
DXRuby1.4をリリースしたあと、1.5devでそのあたりを実験するかな、と。どうせ根本的なところを色々と書き直す予定だし。