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を使ってね☆
まあ、調べてみてどうにかできそうだったら直しておこう。
それとは別に、DirectXとRuby1.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でそのあたりを実験するかな、と。どうせ根本的なところを色々と書き直す予定だし。