雨だと思ったら雪降った。想像以上にたくさん降ってたくさん積もった。 ひきこもってるので特に困らなかったが、度々屋根から雪が雪崩れておそろしい音をたてるのがびびる。
LOOX UにUbuntuでも入れてみるかと試した。最初はUbuntuの12.10を入れたがあまりの遅さに諦めた。 Unityが遅そうなのでXubuntuにしたら常識的な速度で動いてくれたのでこいつをインストールしよう。
GMA500だけど問題なく動いたのでOpenGLの動作を確認してみる。glmarkというのを見つけたので入れて実行してみると、まあまあな速度で動いてるなぁ……と思ったらllvmpipeだと?!ソフトウェアレンダリングじゃないですかー。 調べててみるとgma500_gfxというGMA500ドライバは2Dしか対応してないとのこと。3Dのアクセラレーションは無理だそうな。なるほどUnityが遅いわけだ。 しかし一見そこそこな速度でglmarkが動くってのはすごいな。1コア2スレのAtom 2GHzであれだけの描画をできるもんなんだな。
HWアクセラレーションをするにはIntelのEMGDドライバ1を入れればいいのかと思ったが、Ubuntuに今のEMGDを入れるのは自前でなんとかしないと無理っぽい。公式にはFedoraかMeeGoの特定バージョン用しかないので自前でってのは俺にはかなり難しそうだ。Ubuntuのforum見るとEMGDのことは忘れろ的なことが書いてあった。むむむ……。
LinuxでもOpenGLのHWアクセラレーションができないとなると特にXubuntu使ってる意味はないのでWindowsに戻ってきた。インストール先がUSBメモリということもあっていろいろ遅かったしな。Windows用のEMGDを自前で作って入れればいいんだけれど、設定をどうすればいいのかわからなくて作れてないんだよなぁ。
Embedded Media and Graphics Driverの略でEMGDなのでEMGDドライバと言うとドライバが二重になってしまうんだが、みんなこう言うので…… ↩
LOOX Uが落ちる問題は背景の描画を一部消したら落ちなくなったのでだましだまし使うことにした。
行列演算のクソ重かった原因がわかった。
行列のかけ算しかしてないと思ったところはカメラのViewとかProjection行列を取得してて、こいつらの計算が重かった。 といっても1フレームに一回計算すればよいだけなんでちょっとくらい遅くても問題ないはず。 で、確かに計算結果のキャッシュはしてたんだけど、行列は計算済みですよと設定するのを忘れてて取得のたびに計算してた。おい!直したらフレームレートが倍になった。
これでだいぶ速くはなったけど、まだ描画のうち50%以上は行列演算だなぁ。つか行列のかけ算だなぁ。 なんとかちょっと変更して速くできないか考えたけど無理っぽい。4x4行列のかけ算にはスカラー値を64回かけ算と48回足し算する必要があるんだっけ? それぞれでFloatオブジェクトを生成しちゃってるからそりゃ遅いよね。Flonumがあればもうちょいましになるかもしれないけど、今使ってるのIronRubyだしなぁ。
とにかくオブジェクトを生成せずにベクトルを扱える方法でできれば一括で計算できたりすると嬉しいと思ったんだが、NArrayてのがあったな。調べてみると良さそうなんだけど、IronRubyで使えるNArrayは無いようだ。知ってた。JRubyで使えるNArrayも無いようだ。誰か移植してそうだと思ったんだけど……。
IronRuby用のNArray移植くらいなら作ってもよいかと思ったんだけどJRuby用にも作らないといけないとなるとしんどいな。諦めてOpenTKのMatrix4を使って作るのが早いか。
Matrixの内部構造はただの配列だったところをOpenTKのMatrix4を内部に持つようにしてみた。ラップしてある分オーバーヘッドあるかなーと思ったがめちゃくちゃ速くなった。オーバーヘッドなんて大したことなかったようだ。やったー。
調子に乗ってVectorも内部的にOpenTKのVector4を使うようにしてみたがなんか動作がおかしい。double精度だったのがfloat精度になったからかとも思ったけどそれにしちゃちょっと動きがおかしすぎる。よくよく調べたらIronRubyの最適化バグだなこりゃ……。
3D描画はそれなりの時間で済むようになった感じだけど、今度は2D描画が重い。2Dつっても文字だけなんだけど。フォントはテクスチャじゃなくてベクター(?)で保持してるので描くには一文字ずつglDrawElementsしてる。どうもこれがとても重いようだ。そりゃそうだ。
一文字ずつ描画コールしないなら、各文字の頂点に描画位置を足したものを頂点バッファに入れていって一回で全部描画するのがよさそう。でも作るのがめんどいのとRubyでそれやると逆に重くなりそう。
小手先の対応をしようとすると、今は描画位置と描画色をuniform変数に入れてるけどこれを一文字描画毎に設定してるのが結構時間かかってるので、attributeにして疑似インスタンス化するのが考えられる。
疑似インスタンス化ってのはuniform変数のかわりにattributeを使ってglVertexAttrib4fとかで設定して1glDrawElementsする奴。描画セットアップ→glVertexAttrib→glDrawElements→glVertexAttrib→glDrawElements→…、というようにglVertexAttribとglDrawElementsを繰り返すことで一部のパラメータをちょっと変えつつ同じ物を沢山描画するので疑似インスタンス化になる。結局glDrawElementsを沢山呼んでるじゃねーか!なんだけど、OpenGLのglDrawElementsはオーバーヘッドが少ないからこの方法でもけっこう速いらしい。
問題はANGLE使ってるので結局中身がD3D9だということだ。疑似インスタンス化意味ねーじゃん!と思ったけど簡単なので試してみたら思ったより速くなった。ANGLEやるじゃん。べつに疑似インスタンス化じゃなくて真のインスタンス化すりゃいいんだけど、LOOX UでOpenGLが動かなくてANGLE使ってる関係上GLES2.0レベルしか使えないのでした。というか一文字毎に描画する物かわってるからインスタンス化でやろうとするとすげー大変だわ……。これ疑似インスタンス化にもなってなくね?
文字描画はまだ不満はあるものの、調べてるとなぜかパフォーマンスカウンタの取得が遅いんじゃないかという状況が見えてくるがそんな重そうなところもなく原因はわからず。そもそも重くて困ってるわけでもないのでこの辺で打ち切ってゲーム作ろう。
uniform変数のかわりなのでglVertexAttribPointerでなく、glVertexAttribで1つだけ設定して全頂点で同じ値を使う。 ↩
IronRuby 1.1.3でのはなし。
OpenTKのVector4を使ってたんですよ。これは値型(C#で言うところのstruct)でx・y・z・wの4つのメンバ変数がpublicアクセスできるようになってる。まあ何もおかしいところはないわけです。
これをIronRubyで使うとどうなるか。
require 'OpenTK.dll'
v = OpenTK::Vector4.new(1, 2, 3, 4)
p v #=> (1, 2, 3, 4)
v.z = 5
p v #=> (1, 2, 5, 4)
とかなるわけですね。全く問題ない。と思いきや
require 'OpenTK.dll'
v = OpenTK::Vector4.new(1, 2, 3, 4)
p v #=> (1, 2, 3, 4)
1000.times do |i|
v.z = i
end
p v #=> (1, 2, 344, 4)
v.z = 5
p v #=> (1, 2, 344, 4)
おや、344とかどこから出てきたんですかね。 しかもそのあとzに5を代入してるはずなのに反映されてない。どういうことだ……という問題。
原因なんだけど、まずRuby側から値型のメンバ変数にアクセスすると、Ruby側から見えるアクセッサが定義されるみたい1。
require 'OpenTK.dll'
v = OpenTK::Vector4.new(1, 2, 3, 4)
v.z = 5
p v.methods #=> [:z=, 'add', 'sub', ...,
'x', 'x=', 'y', 'y=',
'z', 'z=', 'w', 'w=',
...]
いろいろ省略してるけど、:z=
と'z='
の二つがあるのがわかる。前者があとから増えた奴っぽい。:x=
とかはアクセスしてないので出来てない。
これが出来た時点では特に問題ないんだけど、何度もアクセスしてると急に設定できなくなってしまうようだ。
何度も使ってると問題がでるということで、たぶん沢山使ってると最適化をかけようとするのではないだろうか。だがVector4が値型なので最適化されたアクセッサ内でコピーが作られて、コピーに対して値を設定しちゃってるんじゃないかと思う。IronRubyかDLRかのバグだなぁ。 ソースを見て予想が当ってるか、回避策がないか確認したいところだが、最適化だとすると調べるの大変そう。一旦諦めるか。
ちなみにメンバ変数の読み出しは問題ない。アクセッサの中でコピーされたところで変わらないもんね。 あとプロパティのアクセスは全く問題ないので、メンバ変数に直接設定するの以外は大丈夫。
回避策としてはそんな値型使わないくらいしかないかも。OpenTKのVector以外では大した問題ないし。
これが値型に限るかどうかは確認はしてない。あんまりpublicなメンバ変数とかないしね。でも思い当たる原因からすれば値型に限られそう。 ↩