Shadertoyという面白そうなサイトがあったので、とりあえず適当な時計を投稿してみました。
https://www.shadertoy.com/view/4dfSzn (Google Chrome 30、Firefox 28.0で動作確認しました)
しかし、表示は単純なのにとても重いです。
ガイドラインでは「常に30fps以上になるものを目指そう!」と書いてあるのに対し、4fps程度しか出ません。
でも、自分のPCが糞スペであることはわかっているし、
もっと重くて閲覧すると「ディスプレイドライバが応答を停止しましたが、復帰しました」と出るような作品もあったので、
気にせず投稿しちゃいました。
しかし、これだけの表示で無駄にfpsが低いのはやはり気になります。
コードを見ると、大量の条件分岐があります。条件分岐を含むが重いのはよく知られていることです。
でも、もしかしたらブラウザ上で実行しているから重いのかもしれません。
とりあえずそのままDXライブラリのシェーダーに移植し、実行してみましょう。
・・・
結果:15fps程度
やはりブラウザ上よりはfpsが上がりましたが、それでも低いです。
単純に画像を作成する処理なら何でもシェーダーに投げれば速い、というわけではなく、シェーダーにも向き不向きがあるのでしょう。
というわけで、文字の描画はDXライブラリの標準Draw系関数で行い、背景の描画のみをシェーダーに任せてみました。
すると・・・
結果:700~900fps程度
かなり速くなりました。
しかし、本当はシェーダーの処理自体が遅く、シェーダーを全く使わなければ更に速いかもしれません。
というわけで試してみました。文字も背景もDraw系関数で行い、シェーダーは使用しません。
・・・
結果:65~75fps程度
全てシェーダーで描画する場合ほどではないですが、遅くなってしまいました。
やはり描画したいグラフィックによっては、シェーダーを利用する方が速いようです。
実行速度まとめ
[table=border:1px solid #ddd;border-collapse:collapse;][tr=]
[td=border:1px solid #ddd;padding: 0.5em;]実行方法[/td]
[td=border:1px solid #ddd;padding: 0.5em;]fps[/td]
[td=border:1px solid #ddd;padding: 0.5em;]処理時間/描画処理[/td]
[td=border:1px solid #ddd;padding: 0.5em;]処理時間/フレーム[/td]
[/tr][tr=]
[td=border:1px solid #ddd;padding: 0.5em;]Shadertoy[/td]
[td=border:1px solid #ddd;padding: 0.5em;]3.9~4.2程度[/td]
[td=border:1px solid #ddd;padding: 0.5em;]-[/td]
[td=border:1px solid #ddd;padding: 0.5em;]-[/td]
[/tr][tr=]
[td=border:1px solid #ddd;padding: 0.5em;]DXライブラリ・シェーダー[/td]
[td=border:1px solid #ddd;padding: 0.5em;]15程度[/td]
[td=border:1px solid #ddd;padding: 0.5em;]55~70us程度[/td]
[td=border:1px solid #ddd;padding: 0.5em;]65000us程度[/td]
[/tr][tr=]
[td=border:1px solid #ddd;padding: 0.5em;]DXライブラリ・Draw+シェーダー[/td]
[td=border:1px solid #ddd;padding: 0.5em;]700~900程度[/td]
[td=border:1px solid #ddd;padding: 0.5em;]80~100us程度[/td]
[td=border:1px solid #ddd;padding: 0.5em;]1000~1200us程度[/td]
[/tr][tr=]
[td=border:1px solid #ddd;padding: 0.5em;]DXライブラリ・Draw[/td]
[td=border:1px solid #ddd;padding: 0.5em;]65~75程度[/td]
[td=border:1px solid #ddd;padding: 0.5em;]12000~14000us程度[/td]
[td=border:1px solid #ddd;padding: 0.5em;]13000~15000us程度[/td]
[/tr][/table]
・環境
Windows Vista Home Premium SP2 32ビット
Intel(R) Core(TM)2Duo T8100 @2.10GHz 2.10GHz
RAM 4.00GB
・各パラメータの解説
実行方法:そのまま
fps:1秒あたりのフレーム数。
ベンチマーク結果の表示、メッセージ処理、ClearDrawScreenの時間も含まれるため、
厳密には単純に「処理時間/フレーム」の逆数というわけではない。
処理時間/描画処理:描画関数の呼び出しにかかった時間。ClearDrawScreenの後からScreenFlipの前までの時間。
処理時間/フレーム:1フレームの処理にかかった時間。ClearDrawScreenの後からScreenFlipの後までの時間。
「処理時間/描画処理」にScreenFlipにかかった時間を加えたものと言える。
この実験結果から、この環境では、
単純な矩形などの範囲を1色で塗りつぶすならDraw系の関数を、
グラデーションをかけるなど各ピクセルに対して処理を行いたいならシェーダーを利用した方が速い、
というようなことが言えそうです。
しかし、シェーダー、Draw系の関数ともに自分はあまり詳しくないと思うので、
よりよい描画方法があるかもしれません。
何かよりよいDXライブラリでの処理方法があれば、コメントで教えていただけるとありがたいです。
また、この実験およびシェーダーのみで描画するコードに無駄に重い処理を追加してみる実験によって、
DXライブラリのDrawPolygon2DToShader関数によってシェーダーに描画指示を出しても、
描画が終了するまでその場で待つわけではなく、その後の処理と並列で描画処理が行われ、
ScreenFlip関数を呼んだ時に同期されるように思われる、ということが分かりました。
(シェーダーのみで描画するコードに無駄に重い処理を追加したとき、
処理時間/描画処理が30000us程度になっても、処理時間/フレームは65000us程度のまま変わらなかった)
さらに、シェーダーのみで描画し、60fpsを下回っている状態でも、
このプログラムのCPU使用率はほぼ0%であることがわかりました。
この性質をうまく応用すれば、従来のOpenMPなどによる並列演算に加え、
シェーダーでも演算を行うことで、全体の演算速度を少しだけ上げることができそうな気がします。
しかし、シェーダーで数値計算を行わせるための入出力手法が確立していない上、
現時点での自分の実験結果では誤差も大きいので、
計算の途中経過をシェーダーに与え、計算速度への影響を少なくしながらビジュアライズさせる、
という用途の方が実用的かもしれません。
最後に、今回使用したDXライブラリを用いた時計のバイナリとソースコードを添付しました。
それぞれ
というコマンドラインで、描画サイズの変更が可能です。
シェーダーのみで描画するバージョンは実行時のCPU使用率がほぼ0%なので、
外部ソフトで最前面に表示すれば、デスクトップアクセサリとしても実用的かもしれません。
適材適所
適材適所
- 添付ファイル
-
[拡張子 zip は無効化されているため、表示できません]
RE: 適材適所
CUDAとかのGPUを使った並列コンピューティングとは違うのでしょうか。みけCAT さんが書きました:この性質をうまく応用すれば、従来のOpenMPなどによる並列演算に加え、
シェーダーでも演算を行うことで、全体の演算速度を少しだけ上げることができそうな気がします。
しかし、シェーダーで数値計算を行わせるための入出力手法が確立していない上、
現時点での自分の実験結果では誤差も大きいので、
計算の途中経過をシェーダーに与え、計算速度への影響を少なくしながらビジュアライズさせる、
という用途の方が実用的かもしれません。
Re: 適材適所
私の環境ではブラウザ上で約30FPSです。
DXライブラリのDraw系関数も内部でシェーダを使っていると思います。Direct3D9xで用意された標準のシェーダか、或いはDXライブラリが用意したものかはともかくですが。
文字の描画はDXライブラリの標準Draw系関数で行い、背景の描画のみをシェーダーに任せた場合に速いのは、文字列の描画を行っていないからだと思います。DXライブラリは文字列のテクスチャをキャッシュで保持してそのテクスチャを描画するだけなので、文字色が不変で再描画の必要がなければ高速に描画できます。結果も合成するわけですから、分岐もありません。シェーダーのみでの描画より高速なのは当然だと思います。シェーダーでもキャッシュなど最適化すれば速くなります。実装が悪いです。分岐するぐらいなら背景を作ってその上にキャッシュした文字を上書きする方がいいです。
シェーダーが遅い以上に、シェーダーの利用回数が多いのです。
シェーダー内での条件分岐が遅いのはその通りで、分岐を消すだけで60FPSになります。動的分岐の遅さは、静的分岐にしてしまう(シェーダを複数用意)などの手段で高速化が可能です。まぁバージョン&環境依存のトリッキーな方法も無くはないですが。
シェーダーを利用した数値演算はすでに広まっている技術です。DXライブラリが醜悪なDDM時代の遺物であるDirect3D9xを使っているので、出来ないのかもしれませんが。コンピュートシェーダと呼ばれる、描画以外の目的に使われるシェーダ・パスが多くのグラフィックスパイプラインに用意されています。本気でシェーダを学ぶのなら、DXライブラリを使うのはやめたほうがよいのではないかと思います。
DXライブラリのDraw系関数も内部でシェーダを使っていると思います。Direct3D9xで用意された標準のシェーダか、或いはDXライブラリが用意したものかはともかくですが。
文字の描画はDXライブラリの標準Draw系関数で行い、背景の描画のみをシェーダーに任せた場合に速いのは、文字列の描画を行っていないからだと思います。DXライブラリは文字列のテクスチャをキャッシュで保持してそのテクスチャを描画するだけなので、文字色が不変で再描画の必要がなければ高速に描画できます。結果も合成するわけですから、分岐もありません。シェーダーのみでの描画より高速なのは当然だと思います。シェーダーでもキャッシュなど最適化すれば速くなります。実装が悪いです。分岐するぐらいなら背景を作ってその上にキャッシュした文字を上書きする方がいいです。
シェーダーが遅い以上に、シェーダーの利用回数が多いのです。
シェーダー内での条件分岐が遅いのはその通りで、分岐を消すだけで60FPSになります。動的分岐の遅さは、静的分岐にしてしまう(シェーダを複数用意)などの手段で高速化が可能です。まぁバージョン&環境依存のトリッキーな方法も無くはないですが。
シェーダーを利用した数値演算はすでに広まっている技術です。DXライブラリが醜悪なDDM時代の遺物であるDirect3D9xを使っているので、出来ないのかもしれませんが。コンピュートシェーダと呼ばれる、描画以外の目的に使われるシェーダ・パスが多くのグラフィックスパイプラインに用意されています。本気でシェーダを学ぶのなら、DXライブラリを使うのはやめたほうがよいのではないかと思います。