SDL-users メーリングリストで話題になった 「SDL sound under Mac OS X and Linux」threadをまとめてみました。
とあるエミュレータをMacOS Xに移植してるんだけど...
ダイジェスト風に。脚色してたらごめんなさい
※1 オフスクリーンバッファに描画して、変更部分を記憶しておいて、 フレーム毎にその部分をハードウエアサーフェスに転送している、 ような感じみたいです。
※2 エミュレータの画面出力をソフトウエアサーフェス内で実現し、 それを実際にスクリーンに出す、という処理をしたいらしいです。
試してみたら10%遅くなった、という話から続くようです。
だったらなんでハードウエアサーフェスの方が... に対する回答例。 全文訳を試みます。
> > > メモリ内のサーフェスとビデオメモリのサーフェスの比較が元の話でしょ。 > > で、ビデオメモリの方がメインメモリより遅いのは自明じゃん > > > > -->Neil > > > だったらなんでハードウエアサーフェスの方がソフトウエアサーフェスよりも > 速いのか説明してみろよ。ほれ! やれやれ。とっても単純なことだよ: ビデオメモリへの書込みはシステムメモリの書込みよりも遅くて ビデオメモリ内でのコピーはビデオメモリへの書込みよりも ずっと速いってことでしょ。 このとき、4つの選択肢がある: 1. システムメモリでシーンを組んでから、出来上がったイメージをビデオメモリに 転送する 2. ビデオメモリにシーンを直接構成する 3. イメージをビデオメモリに転送して、それからその画像を操作するための (ビデオ内の)ハードウエアを使ってシーンを構成する 4. 以上のやりかたの混合 SDLではどのやりかたもできるけど、ではどのやりかたが一番遅いと思う? そう、2番だよね。バスを越えて1つ1つ点を打ってゆくんだから。じゃあ ほとんどの人が性能をあげるために試すのはどれだと思う? そう、 ビデオメモリはシステムメモリよりも速いんだからやっぱり2番だ。本当に? 何も書いたり読んだりする必要がない場合に限り正しいけど :) じゃあなんで1番が2番より速くなると思う? それは1つ1つの点にアクセスする よりも、まとまった領域をビデオメモリに転送する方がずっと速いからだ。 バスの性能を最大限に使い切ることができるだけじゃなくて、ほとんどの ハードウエアではCPUとは独立に、並列にDMA転送を行うことができるんだし。 3番目のやつが一番速いから、SDL APIの次のバージョンではこのやりかたの ために最適化される予定だ。なんでこれが一番速いんだろう? それは君が 必要としているデータがすべてビデオメモリ上にあるからだ。そして ビデオカードにちょっとのコマンドを送ってシーンを構成して、 ハードウエアの処理が終わるのを待って(待たないこともあるけど)、 できあがったシーンを表示するコマンドを送ればおしまいだ。 3Dハードウエアが強烈なエフェクトなんかをこなしているのは、 そういったコマンドを豊富に備えていて、またそれらが効率よく 動作するようにデザインされているから。 だから、君が自分のやろうとしていることを正確に分かっていなかったり、 対象としているプラットフォームやドライバが把握できていないのだったら、 どんなdepthが最適であろうとそれを使い、またハードウエアサーフェスを 使わないことがSDLを使う上で一番効果的な方法になる。 こうすると、SDL_UpdateRects()が呼ばれたときに、システムメモリから シングルバックバッファに転送するときに一切の変換処理をしなくなるからだ。 つまり君は8bpp、15bpp、16bpp、32bppすべてに対応させる必要があるわけだ。 一番簡単な方法は、君の絵をSDL_DisplayFormat()に通して 現在のディスプレイフォーマットに合わせておいて、転送のときに 変換処理が必要ないようにすることだ。 8bppのときは、自分でディザのルーチンを組んでおく必要があるかもしれない。 SDLの色変換はスピード優先だから一切のディザリングを行ってないからね。 この方法の問題は、一つはいつそのシーンが表示されるのかについて 何の操作もしていないという点がある。 ここで僕はちょっと汚い秘密を教えよう。 フルスクリーンモードでなければ君にできることは何もないんだ。 でも、もし君がこれからすることが分かってて、フルスクリーンモードで 動くことが分かっているんだったら、ビデオモードを設定するときに (SDL_FULLSCREEN|SDL_DOUBLEBUF) をflagsに指定することで、ビデオメモリの フリップができるようになるんだ。 これらのフラグが渡されると、君は2つのビデオメモリバッファを 手に入れる。それらはSDL_Flip()を呼ぶことで交互に表示されるよ。 フリップのタイミングは垂直同期にあわせることで、ちらつきを 抑えることができる。そのためには、君がビデオメモリの世界にいて、また できるだけ多くのサーフェスをビデオメモリ内に作っておく 必要があるわけだけど、便利なことに、SDL_DisplayFormat() は、 もしディスプレイサーフェスがビデオメモリ内にあるときは、 君のサーフェスをビデオメモリに転送してくれるんだ。 *ただし* アルファブレンディングつきの2Dのハードウエア転送はサポートされて いない。ということは、アルファブレンディングはとても遅くなるということだ。 つまり、srcのサーフェスからピクセルを読んで、 dstのサーフェスからピクセルを読んで、システムメモリ内でブレンドを 行って、それをビデオメモリに戻すという処理が必要になる。 ビデオメモリから読む処理は書く処理よりもずっと遅いから、実際にやって みればきっとものすごい遅いことが分かると思うよ。 さて話をまとめてみよう: 君が本当に何をしようとしているのか分かっていない なら、ソフトウエアサーフェスを使おう。全てのプラットフォームでサポート されているし、概ね高速と言えるからね。 君が本当に何をしようとしているのか理解しているなら、いくつかの ハードウエア/ドライバの組み合わせによっては、page-flippedの ビデオメモリを確保することができるけど、ビデオメモリへの直接アクセスに 失敗したときには、ソフトウエアバックバッファにフォールバックできるように しておく必要がある。もし君がソフトウエアディスプレイサーフェスを使っていて、 毎フレームで画面全体を更新しないなら、変更した場所だけSDL_UpdateRects()で 更新するようにしてみよう。 使えるときはいつでもSDL_DisplayFormat()やSDL_DisplayFormatAlpha()を 使おう。 もしアルファブレンディングをするなら、かならず3D APIか、 2Dならソフトウエアメモリを使うようにしよう。 もし君がソフトウエアメモリを使っていて、アルファチャンネルや カラーキーを持っているなら、SDL_RLEACCELを使おう。 エンコーディングを行うことで、 転送時の時間のかかるピクセルごとのチェックを大幅にスピードアップする ことができるからね。 最後に、もし君が3Dハードウエアの上でのみ動かそうと考えていて、 ものすごいエフェクトとかを使ってみたいなら、2Dの転送じゃなくて OpenGLを使うことを検討してみよう。 SDLにはOpenGLコンテキストをセットアップし、 ビデオバッファのスワップを行うためのAPIを提供ている。他の入力の 制御とかはまったく変更しなくていいんだ。 SDLのサーフェスをテクスチャに変換してOpenGLのコマンドで 表示させることもできる。SDLソースの中にあるtestgl.cというのが そのサンプルになってるから見てみてね。 やれやれ、これをちゃんと書きあげてWebにのっけとくべきだな。 まさにFAQだ。 質問は歓迎するよ。来週には答えられると思うから。 -Sam Lantinga, Software Engineer, Blizzard Entertainment
SDL_SWSURFACE、SDL_HWSURFACE、それからディスプレイといったあたり、 メインメモリとビデオメモリ、といったあたりの状況を きちんと区別できていないといけないことを痛感しました。 Fredrickさん、Patrickさんそれぞれが微妙に 誤解しているようですね。Neilさんの最後の言い分もちょっと変だと 思います。「Someone suggested moving his software surface to the hardware surface」なんてことを言った人は誰もいないし。
もっとも私も訳し始めたときはだいぶ誤解していたようです (なのでつまらんflameだと思ってた)。だから、Samさんのこの文章を呼び出した 功績は大きいんじゃないかなーと思っています。
名前の問題もあると思うんですが。SDL_DisplayFormatが変換を 行う関数だとはちょっと想像するのが難しいし。 SDL_ConvertSurfaceも似たような誤解がありますよね。
さて、訳に対する質問はzinnia@risky-safety.orgまでどうぞ。 原文に対する質問はSDL users MLか Samさんに(英語で)直接していただいた方が話が早いと思います。
戻る