弾幕計算の高速化
Re:弾幕計算の高速化
三角関数をテーブル化してみてはどうでしょうか。
予め、cosやsinの計算結果を配列などに代入しておき、
後は計算をせずに、直接 計算した後の値を引っ張り出すという方式です。
予め、cosやsinの計算結果を配列などに代入しておき、
後は計算をせずに、直接 計算した後の値を引っ張り出すという方式です。
Re:弾幕計算の高速化
1.処理速度の評価のために処理時間を実測しましょう。
2.実測した時間を元にボトルネックを見つけましょう。
弾幕の計算がボトルネックだと思い込んでいるようですが、
これが正しいかもしれませんし間違っているかもしれません。
時間を計るまでは、これは単なる思い込みです。
もしもまだならば、必ず計ってください。
処理時間はマシンスペック等と単純な相関があるとは限らないので、
ターゲットとなる環境で測定するのが望ましいです。
環境が変わるとボトルネックの場所が変わる可能性があります。
弾幕の計算がボトルネックだとすると、弾幕の計算を省略した場合、
劇的に高速化されるはずです。弾幕を移動させないとか、全弾幕を
一律で上方向に等速で移動させるとか、計算の負荷がかからないと
思われる方法で試してみてください。
もしも期待した効果が得られないならば、
弾幕の計算を効率化させることは無駄ということです。
2.実測した時間を元にボトルネックを見つけましょう。
弾幕の計算がボトルネックだと思い込んでいるようですが、
これが正しいかもしれませんし間違っているかもしれません。
時間を計るまでは、これは単なる思い込みです。
もしもまだならば、必ず計ってください。
処理時間はマシンスペック等と単純な相関があるとは限らないので、
ターゲットとなる環境で測定するのが望ましいです。
環境が変わるとボトルネックの場所が変わる可能性があります。
弾幕の計算がボトルネックだとすると、弾幕の計算を省略した場合、
劇的に高速化されるはずです。弾幕を移動させないとか、全弾幕を
一律で上方向に等速で移動させるとか、計算の負荷がかからないと
思われる方法で試してみてください。
もしも期待した効果が得られないならば、
弾幕の計算を効率化させることは無駄ということです。
Re:弾幕計算の高速化
まずは時間を測定してみましょう。
方法としては
①処理の最初と終わりの時間の差を出す
②今までの平均をdAveTimeとし回数をCntとし今回の計測結果をTimeとすると
dAveTime += (Time-dAveTime)/(++Cnt);
として平均を更新し、表示する
方法としては
①処理の最初と終わりの時間の差を出す
②今までの平均をdAveTimeとし回数をCntとし今回の計測結果をTimeとすると
dAveTime += (Time-dAveTime)/(++Cnt);
として平均を更新し、表示する

Re:弾幕計算の高速化
>>たいちう
計測方法がわからないので、FPSで計算してみました。プログラムは龍神とほとんど一緒です。
1、out_main(あたり判定)なしの場合 62FPS
2、out_main(あたり判定)ありの場合 60FPS
3、shot_main(弾幕メイン)なしの場合 62FPS
4、defineのSHOT_BULLET_MAXを400まで減らす 62~33FPS
5、shot_main(弾幕メイン)ありの場合 62~33FPS
* 4,5ではミシャクジさまのところで33FPSぐらいになります。
SHOT_BULLET_MAXは、弾幕計算のループ回数なので、減らすことにより速くなりました。
しかし、これから多くの弾を出す場合はきつくなってしまいます。7次元様は「気ロちゃん風雨に負けず」でも
60FPS出してましたし・・・何とかならないか考えてます。
>>lbfuvab
そうやって計算するのですか。ありがとうございます。
さっそくやってみます。
>>conio
配列に入れるとは
float sin[180] = {0 , 0.0175 , 0.0349 ,,,}
float cos[180] = {1 , 0.9998 , 0,9994 ,,,}
のようなことでしょうか?
計測方法がわからないので、FPSで計算してみました。プログラムは龍神とほとんど一緒です。
1、out_main(あたり判定)なしの場合 62FPS
2、out_main(あたり判定)ありの場合 60FPS
3、shot_main(弾幕メイン)なしの場合 62FPS
4、defineのSHOT_BULLET_MAXを400まで減らす 62~33FPS
5、shot_main(弾幕メイン)ありの場合 62~33FPS
* 4,5ではミシャクジさまのところで33FPSぐらいになります。
SHOT_BULLET_MAXは、弾幕計算のループ回数なので、減らすことにより速くなりました。
しかし、これから多くの弾を出す場合はきつくなってしまいます。7次元様は「気ロちゃん風雨に負けず」でも
60FPS出してましたし・・・何とかならないか考えてます。
>>lbfuvab
そうやって計算するのですか。ありがとうございます。
さっそくやってみます。
>>conio
配列に入れるとは
float sin[180] = {0 , 0.0175 , 0.0349 ,,,}
float cos[180] = {1 , 0.9998 , 0,9994 ,,,}
のようなことでしょうか?
Re:弾幕計算の高速化
PSPのアーキテクチャを知らないのですが、浮動小数点を計算することがボトルネックなら全てintで計算したらどうですか?
640x480ピクセルを64000x48000ピクセルとして計算し、表示するときに100で割れば高速化出来そうな気がします。
サインコサインテーブルもintにし、計算過程に浮動小数計算が含まれないようにしてはどうでしょう
640x480ピクセルを64000x48000ピクセルとして計算し、表示するときに100で割れば高速化出来そうな気がします。
サインコサインテーブルもintにし、計算過程に浮動小数計算が含まれないようにしてはどうでしょう
Re:弾幕計算の高速化
sin,cosの値を配列にするですが、
float sin[181],cos[181]; //0°も含めるため
for(i=0 ; i<=181 ; i++){
sin = sin(i);
cos = cos(i);
}
でもできますか?
float sin[181],cos[181]; //0°も含めるため
for(i=0 ; i<=181 ; i++){
sin = sin(i);
cos = cos(i);
}
でもできますか?
Re:弾幕計算の高速化
まず、孤度法と度数法ってご存知ですか?
180°は円周率で表せるというやつです。
もし解らなければググってみれば出てきます。
180°はπです。ですから龍神録でも
#define PI 3.141592654f
for(i=0;i<180;i++)
.x = sin(PI/180*i);
みたいに使っていると思います。
180°がPIなのですから、1°はPI/180ですよね。それを基準に計算してみてください。
後、1°ずつのテーブルでは誤差が大きすぎると思います。せめてその10倍は欲しいところです。
また、お書きになっているプログラムは181になっていますが、
これではオーバーフローしてしまいます。180でいいと思います。
ところで、int型で計算するのは無しになったのですか?
※
皆さん仰る通り、実装する前にまずボトルネックを探す方がいいと思います。
180°は円周率で表せるというやつです。
もし解らなければググってみれば出てきます。
180°はπです。ですから龍神録でも
#define PI 3.141592654f
for(i=0;i<180;i++)
.x = sin(PI/180*i);
みたいに使っていると思います。
180°がPIなのですから、1°はPI/180ですよね。それを基準に計算してみてください。
後、1°ずつのテーブルでは誤差が大きすぎると思います。せめてその10倍は欲しいところです。
また、お書きになっているプログラムは181になっていますが、
これではオーバーフローしてしまいます。180でいいと思います。
ところで、int型で計算するのは無しになったのですか?
※
皆さん仰る通り、実装する前にまずボトルネックを探す方がいいと思います。
Re:弾幕計算の高速化
すみません。ボ~っとしてたせいですっかり孤度法と度数法を忘れていました。
やはり、プログラムの高速化のためにはsin,cos,atan2の計算を少なくさせるのがいいですよね・・・
他には何が必要か考えてみます。
>>int型で計算するのは無しになったのですか?
部分的にint型に直してみようと思います。ですが、どれほど速くなるかわかりませんが・・・
>>皆さん仰る通り、実装する前にまずボトルネックを探す方がいいと思います
その通りですね。やってみることにします。
スレと違う質問なのですが、処理時間の計測は
①処理の最初と終わりの時間の差を出す
②今までの平均をdAveTimeとし回数をCntとし今回の計測結果をTimeとすると
dAveTime += (Time-dAveTime)/(++Cnt);
とありますが、具体的には
int Time[2],time;
int dAveTime;
int Color = GetColor( 255 , 255 , 255 ) ;
void main(){
Time[0] = GetNowCount;
本体
Time[1] = GetNowCount;
time = Time[1]-Time[0];
dAveTime += (time-dAveTime)/(++Cnt);
DrawFormatString(x,y,Color,"dAveTime%d",dAveTime);
DrawFormatString(x,y,Color," time %d" , time)
}
ということでしょうか?
プログラムで、どうやって計測させるかよくわかりません。
やはり、プログラムの高速化のためにはsin,cos,atan2の計算を少なくさせるのがいいですよね・・・
他には何が必要か考えてみます。
>>int型で計算するのは無しになったのですか?
部分的にint型に直してみようと思います。ですが、どれほど速くなるかわかりませんが・・・
>>皆さん仰る通り、実装する前にまずボトルネックを探す方がいいと思います
その通りですね。やってみることにします。
スレと違う質問なのですが、処理時間の計測は
①処理の最初と終わりの時間の差を出す
②今までの平均をdAveTimeとし回数をCntとし今回の計測結果をTimeとすると
dAveTime += (Time-dAveTime)/(++Cnt);
とありますが、具体的には
int Time[2],time;
int dAveTime;
int Color = GetColor( 255 , 255 , 255 ) ;
void main(){
Time[0] = GetNowCount;
本体
Time[1] = GetNowCount;
time = Time[1]-Time[0];
dAveTime += (time-dAveTime)/(++Cnt);
DrawFormatString(x,y,Color,"dAveTime%d",dAveTime);
DrawFormatString(x,y,Color," time %d" , time)
}
ということでしょうか?
プログラムで、どうやって計測させるかよくわかりません。
Re:弾幕計算の高速化
こんばんは。私もDXPを使ってPSP上による動作を目指している者です。
PSPにおける再現にあたっていくつか述べられそうな箇所があるので綴りたいと思います。
ちなみに現在は最新版でなくDXP ver0.5.5を使用しています。
1.三角関数をテーブル参照にする
これをしないで何が高速化か!ってくらい速くなりました。
といっても弾道計算と弾幕処理(boss_shotHとか)以外もテーブル参照させると何故か遅くなるため、その他はfastmathの関数を使っています。
2.除算,剰余計算を取り払う
シフトやビット演算による計算ができる箇所は変えました。
3.良く使う関数はinline修飾
ただし、よく使う関数以外も修飾すると遅くなったりするのでシラミ潰しに調べた方がいいです。
4.double→float
5.画像をVRAMに置く + 画像サイズを小さくして画像最適化サイトでサイズ削減 + 画像を16ビット画面モードも16ビットに
PSPのVRAMには2MByteまでしか置けませんが、メインメモリと比べて画像転送速度が速いという点と、
転送が直ぐ終わるように画像サイズをギリギリまで小さくするという事で、
PSPのメモリアクセスが遅いという欠点を補います。
画像の最適化は次のサイトを使っています→ttp://www.gracepointafterfive.com/punypng/
透過が重要でないPNG画像は思い切ってパレット画像にしてしまうというのも手です。
画面モードと画像を16ビットにすることで軽く出来ます。
これはSetDisplayFormatやConvertGraphFormat関数で指定出来ます。
またテクスチャの選択処理を減らすために512*512ピクセル以内にできるだけ纏めて、テクスチャ数を減らした方がいいです。
6.垂直同期を待たないようにする
変わります。またそちらで確認されているか分かりませんが、バグでもある画面のチラつきがなくなりました。
7.333MHzにクロックアップ
変わります。
主に以上の事をしただけですが、浮動小数のままでもケロちゃん弾幕や反魂蝶八分咲が60fps安定しました。
PSPにおける再現にあたっていくつか述べられそうな箇所があるので綴りたいと思います。
ちなみに現在は最新版でなくDXP ver0.5.5を使用しています。
1.三角関数をテーブル参照にする
これをしないで何が高速化か!ってくらい速くなりました。
といっても弾道計算と弾幕処理(boss_shotHとか)以外もテーブル参照させると何故か遅くなるため、その他はfastmathの関数を使っています。
2.除算,剰余計算を取り払う
シフトやビット演算による計算ができる箇所は変えました。
3.良く使う関数はinline修飾
ただし、よく使う関数以外も修飾すると遅くなったりするのでシラミ潰しに調べた方がいいです。
4.double→float
5.画像をVRAMに置く + 画像サイズを小さくして画像最適化サイトでサイズ削減 + 画像を16ビット画面モードも16ビットに
PSPのVRAMには2MByteまでしか置けませんが、メインメモリと比べて画像転送速度が速いという点と、
転送が直ぐ終わるように画像サイズをギリギリまで小さくするという事で、
PSPのメモリアクセスが遅いという欠点を補います。
画像の最適化は次のサイトを使っています→ttp://www.gracepointafterfive.com/punypng/
透過が重要でないPNG画像は思い切ってパレット画像にしてしまうというのも手です。
画面モードと画像を16ビットにすることで軽く出来ます。
これはSetDisplayFormatやConvertGraphFormat関数で指定出来ます。
またテクスチャの選択処理を減らすために512*512ピクセル以内にできるだけ纏めて、テクスチャ数を減らした方がいいです。
6.垂直同期を待たないようにする
変わります。またそちらで確認されているか分かりませんが、バグでもある画面のチラつきがなくなりました。
7.333MHzにクロックアップ
変わります。
主に以上の事をしただけですが、浮動小数のままでもケロちゃん弾幕や反魂蝶八分咲が60fps安定しました。
Re:弾幕計算の高速化
時間計測するのは計算したい処理の前後で時間はかって引けばいいだけではないでしょうか?
fprintf出力するなり、printfDxで結果を確認するなりすればいいと思います。
fprintf出力するなり、printfDxで結果を確認するなりすればいいと思います。
Re:弾幕計算の高速化
>>ft
いろいろ参考になりました。ありがとうございます。
僕と同じようにDXPでSTGをやる人がいて、なんか嬉しいです。
C言語は基本的なものを習得して本格的なプログラミングは初めてで、まだ一カ月です・・・
長い目を持って頑張ってみます。
いろいろ参考になりました。ありがとうございます。
僕と同じようにDXPでSTGをやる人がいて、なんか嬉しいです。
C言語は基本的なものを習得して本格的なプログラミングは初めてで、まだ一カ月です・・・
長い目を持って頑張ってみます。
Re:弾幕計算の高速化
連続で書き込みまくりですみません。
こちらでもPSPの画面でちらつきがあり、理由がわかりませんでした。
ft様によれば垂直同期によるものですが、解決策を教えていただけないでしょうか・・・
*僕の力では調べてもわからなかったので(涙)
こちらでもPSPの画面でちらつきがあり、理由がわかりませんでした。
ft様によれば垂直同期によるものですが、解決策を教えていただけないでしょうか・・・
*僕の力では調べてもわからなかったので(涙)
Re:弾幕計算の高速化
すみません、訂正します。垂直同期を待たなくてもチラついていたのを思い出しました。
ループで必ず行う3大処理ProcessLoop() 中の
if(ClearDrawScreen()!=0)return -1;//画面クリア処理がエラーなら-1を返す
をコメントアウトすることでチラつきが無くなりました。
ただ、これはエラーが無いという勝手に立てた前提のもとですので、動作にどんな支障が出るのか分かりません。
本題の垂直同期を待たないようにする方法ですが、2つあります。
①int SetWaitVSyncFlag(int flag)関数で垂直同期を待つかどうかフラグで処理。
②DXP Ver0.5.5のソースファイルを開いて、その中の
sceDisplayWaitVblankStart();
という垂直同期を待つ関数を全て消してビルドしたものを使う(最新版はソース公開していません)
です。後者は確実に垂直同期を待つことが出来なくなります。
ちなみにSetWaitVSyncFlagを呼ぶ必要が無いという理由で、私は②をしていますが、
①と②の速度の比較はしたことないです。
ループで必ず行う3大処理ProcessLoop() 中の
if(ClearDrawScreen()!=0)return -1;//画面クリア処理がエラーなら-1を返す
をコメントアウトすることでチラつきが無くなりました。
ただ、これはエラーが無いという勝手に立てた前提のもとですので、動作にどんな支障が出るのか分かりません。
本題の垂直同期を待たないようにする方法ですが、2つあります。
①int SetWaitVSyncFlag(int flag)関数で垂直同期を待つかどうかフラグで処理。
②DXP Ver0.5.5のソースファイルを開いて、その中の
sceDisplayWaitVblankStart();
という垂直同期を待つ関数を全て消してビルドしたものを使う(最新版はソース公開していません)
です。後者は確実に垂直同期を待つことが出来なくなります。
ちなみにSetWaitVSyncFlagを呼ぶ必要が無いという理由で、私は②をしていますが、
①と②の速度の比較はしたことないです。