ページ 11

自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 17:24
by 珈琲
先日からやっと3Dフライトシューティングのゲームが形になってきました。
UIを作れるぐらい根幹システムが出来上がり、半分終わったような気持ちで居たのですが、

いざ、敵機を50機ほど出してみたらFPSが50を切ってしまいました!
100機ほど出してみたら37FPSほどです。
勿論、更新処理をしているオブジェクトは敵機の数+背景オブジェクトぐらいしかありません。

実際に敵機を50機も100機も出す予定はないのですが、機銃やミサイル、それに追従する煙などの3Dオブジェクトの数を考えたら
これぐらいのオブジェクト数でFPSが落ちてしまっていては型なしです。


そこで、ゲーム処理の軽量化考えているのですが3Dゲームの作成は初めてなのでイマイチどこらへんが重いかわかりません。
ご指摘お願いします。

1フレーム中に回す処理一覧です。(コードそのままですが

コード:

//登録初期化

	//描画リストをリセット
	CDrawList::GetInstance()->Reset();

	//ロックオンターゲットリストをリセット
	CTargetObjectList::GetInstance()->Reset();

	//一過性オブジェクトリストの要素の寿命をチェック
	CTransientObjectList::GetInstance()->LifeCheck();

//登録

	//キャラクターが各種リストにEntry
	for(size_t i=0;i<CharacterControlList.size();i++){
		CharacterControlList[i]->Entry();               //←ここをsize=50ぐらいにするとFPSが50ほどに下がってしまう
	}

	//一過性オブジェクトリストの要素が各種リストにEntry
	CTransientObjectList::GetInstance()->Entry();

	//衝突判定通知
	CCollisionDetectionList::GetInstance()->Run();

	//3DUIのZバッファ登録
	CDrawUI::GetInstance()->Entry3D();

//更新

	//キャラクターのアップデート
	for(size_t i=0;i<CharacterControlList.size();i++){
		CharacterControlList[i]->Update();               //←ここをsize=50ぐらいにするとFPSが50ほどに下がってしまう
	}

	//一過性オブジェクトのアップデート
	CTransientObjectList::GetInstance()->Update();

	//ステージのアップデート
	FieldStage->Update();

	//カメラのアップデート
	CCamera::GetInstance()->Update();

//描画

	//描画
	CDrawList::GetInstance()->Draw();

	CDrawUI::GetInstance()->Draw2D();
ミサイルや機銃が削除されるたびにvectorのリストに登録から削除すると、データをまるごと移動すると聞いたので処理重そうだなと・・・
だったら1フレームに1度リセットして、必要なフレームで毎回登録しようと考えたのですが、裏目に出たのでしょうか・・・(´・ω・`)

ちなみに一過性オブジェクトリストという名のリストは、ゲームループ中で生成され削除されるオブジェクトです。
なので、ゲーム開始時にインスタンス化された敵機による負荷とかかわっていません。

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 17:27
by 珈琲
また、あまり意味が無いかもしれませんが、どこが重いのかというのを調べるために敵機50の状態で以下のテストをしました。(初期状態 FPS:37)
・Zソートをすべてスキップする → そもそも透明オブジェクトがなかったためあまりFPSが変わらない
・衝突判定(ソースコード23行目)をスキップ → 本命、FPSが52~54ほどに上昇やっぱり重い
・Zバッファによる描画(47行目)をスキップ → すごい軽い、FPSがほぼ60だけど、テスト自体に意味がない
・視野距離を1/10に → あまり変わらなかった
・視野角を半分に → あまりかわらない
・解像度を横に2倍に → 変わらない。
 また、敵機が画面外に出た瞬間にFPSが43ぐらいに上がった → 描画並みにその他の処理が重いということ?
・キャラクター(機体)がロックオンターゲットリストに登録するのをスキップ → FPS 43、衝突判定の通知と差異があるのが変
・キャラクター(機体)が描画リストに登録するのをスキップ →変わらない。描画処理はやっぱり重くなさそう

予想:判定していないのにもかかわらず下から2行目のテストの結果から10フレーム分謎の処理が・・・?

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 17:36
by ISLe
プログラムを変えずに敵機のモデルを簡易なものにしたらどうなりますか?

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 18:18
by 珈琲
頂点数4からなるメッシュ貼ってないオブジェクトに置き換えてみましたがFPSはほぼ変わらずです

1フレームに数回するvectorのerase()と、1フレームにオブジェクトの数だけpushbackするのとではどちらのほうが軽いんでしょうか

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 20:07
by softya(ソフト屋)
珈琲 さんが書きました:頂点数4からなるメッシュ貼ってないオブジェクトに置き換えてみましたがFPSはほぼ変わらずです
モデルが関係ないとすると、
珈琲 さんが書きました:1フレームに数回するvectorのerase()と、1フレームにオブジェクトの数だけpushbackするのとではどちらのほうが軽いんでしょうか
それも関係ないと思います。あっちこっちに時間計測を入れて、時間をロスしている部分を探す他はないかなって気がしますね。

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 20:48
by h2so5
ところで、コンパイラの最適化を有効にした場合にFPSはどうなりますか?
(それとも、最適化した状態での結果でしょうか)

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 21:03
by 珈琲
Releaseモードで、デバッグしないで実行を選択してみました。


すごい、軽いです、
FPS60キープです。

一体なぜ・・・?

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 21:07
by softya(ソフト屋)
デバッグビルド版はデバッグの機能のために色々重い処理を行なっています。
そのため、処理落ちが発生しやすいです。特にSTLが重いと聞いたことがあります。

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 21:21
by 珈琲
敵機を500機ほどにしてみました。
それでもFPS58を下回りません、最適化ってすごい

あ、それとちょっと脱線しますが
ミサイルの煙についてなんですが、ビルボードで描画をするのですが
想定している敵機数が23、それぞれが放つミサイルが大体30機ほどなのですが、これに煙などのエフェクトを加えると、
計算すると描画オブジェクト数が40000ぐらいになるのですが、この数で実際にゲームを作っている人はいるのでしょうか?
今試したらFPSが30ぐらいになったのですが・・・

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 21:22
by 珈琲
softya(ソフト屋) さんが書きました:デバッグビルド版はデバッグの機能のために色々重い処理を行なっています。
そのため、処理落ちが発生しやすいです。特にSTLが重いと聞いたことがあります。
なるほど・・・

みなさんありがとうございました!

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 21:37
by softya(ソフト屋)
そもそも、エースコンバットなどはモデルも距離で切り替えるなど描画で負荷を減らす工夫をしているはずです。
なので、ミサイルの煙も近くと遠くではぜんぜん違う処理をしているはずです。

あと最大オブジェクト数の計算が怪しい気が。そんなに飛び交ったら画面見づらくありません?

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 21:47
by 珈琲
画像

大体こんな感じで、ほとんどが煙なんですが、
Zソートの関係で3Dモデルと同じインターフェイスクラスを継承しないと行けないので・・・ビルボードの画像なのに結構情報量が多いんです(`;ω;´)

Releaseモードで撮影したのですがこの時点で30000~40000ほどあるんですが、左上のFPSが30ぐらいで、やっぱり重いです
Dxlib::DrawBillboard3D使ってるのですが、大量描画には向かないんですかね?

理想は
画像
こんなんなんですが・・・

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 21:48
by 珈琲
あれ?今スクリーンショットみて気づいたけどZソートされてない?

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 21:55
by softya(ソフト屋)
単なる描画の遅延なのかちゃんと計測して下さいね。
描画の遅延と処理の遅延では、処理落ちする部分が変わってきます。
描画の遅延の場合はScreenFlip()が遅延しますので、処理遅延とは計測方法を変えないといけません。

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 22:20
by ym114
DirectXの話になるのですが,8000枚のビルボードを描画するためにdraw()を8000回呼ぶよりも,ビルボード8000枚分の頂点バッファを書き換えてdraw()を1回呼んでまとめて描画する方が速かったりします.(DXライブラリの内部ではどのように描画しているのでしょう?)

処理の遅延は描画の遅延と比べて微々たるものなので,軽量化するためにまずは出来る限り描画しないようにするといいと思います.

Re: 自作ゲームで処理の重い場所

Posted: 2013年7月31日(水) 23:10
by 珈琲
>描画の遅延の場合はScreenFlip()が遅延しますので、処理遅延とは計測方法を変えないといけません。
あ、じゃあScreenFlip()だけ囲んで計測すればいいのですね
ちょっとやってみます。

>処理の遅延は描画の遅延と比べて微々たるものなので,軽量化するためにまずは出来る限り描画しないようにするといいと思います.
なるほど、8000回頂点バッファを書き換えて~という話は面白いですね
速度がなぜ違うのが想像付きませんけど・・・

描画自体の数を少なくするというと、円形の煙から棒形に変えて、長さを稼ぐとかでしょうか

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 00:06
by ISLe
珈琲 さんが書きました:なるほど、8000回頂点バッファを書き換えて~という話は面白いですね
速度がなぜ違うのが想像付きませんけど・・・
ビデオメモリにアクセスする速度がシステムメモリにアクセスする速度よりも遥かに遅いという単純な理由です。
特に、ビデオメモリにアクセスするための切り替え作業に時間がかかります。

頂点バッファをまとめる以外に、テクスチャをまとめてできるだけ切り替えないのも効果的ですが、DXライブラリは使い勝手優先でそういう最適化ができるようにはデザインされていません。

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 11:29
by 珈琲
描画速度を表示してみたのですが、ちょっと値が変です。

コード:

	clock_t start,end;
	start = clock();

	if(ScreenFlip()!=0)return -1;

	end = clock();
	CDebugFunction::GetInstance()->SetDrawTime( (double)(end-start)/CLOCKS_PER_SEC );

コード:

CDebugFunction::GetInstance()->DrawDrawTime(){
	DrawFormatString(40,40,GetColor(50,255,50),"描画速度:%2f",DrawTime);
}
ゲーム開始時は
0.012を表示するのですが
敵機がミサイルを打ち始めて(処理がかかると)
0.000000と
0.001000の2つを(ほぼ)交互に表示します。


参考
http://www.mm2d.net/c/c-02.shtml

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 11:44
by softya(ソフト屋)
たしか、それ以上の精度では測れなかったのでないでしょうか。
あと、0.001程度なら描画が処理落ちの原因でないと思われますが、どこかにFPS制御を挟んでいるのなら一旦コメントして計測してみて下さい。
きっちりしすぎているので、何らかの別の制御が働いているように見えます。

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 12:02
by 珈琲
FPS制御は私はコードを作ってなくて、恐らくDxlibがやってくれてるものかと思います。

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 12:06
by softya(ソフト屋)
それにしては妙に安定してる数値ですね。もっと変動するはずなんですが。
FPSを表示してますが、その仕組みのなかで制御していないですよね?

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 12:38
by 珈琲
FPSの計測はほとんど
http://marupeke296.com/DXCLSSmp_FPSCounter.html
のコピーです

制御はしていないと思います。

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 12:42
by softya(ソフト屋)
たしかに無いですね。ところでtimeGetTime();で計測しても時間は同じになるか計測してみてもらえませんか?

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 13:10
by 珈琲

コード:

	DWORD s = timeGetTime();

	if(ScreenFlip()!=0)return -1;

	DWORD e = timeGetTime();

	CDebugFunction::GetInstance()->SetDrawTime( (double)e-s);

コード:

void CDebugFunction::DrawDrawTime(){
	
	DrawFormatString(40,40,GetColor(50,255,50),"描画速度:%f",DrawTime/1000);
}
結果は殆ど同じでした。描画オブジェクト数が多くなると描画速度が早くなっているというのはなんともよくわからない

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 14:00
by softya(ソフト屋)
ここでScreenFlip()の機能を説明しておくと裏画面と表画面を切り替える訳ですが、GPUがCPUと独立・並行に行う裏画面への描画終了を待って垂直同期の切り替えタイミングに合わせて切り替えます。つまり、60hzのモニターなら描画が終わっていれば16.6・・・ms周期で切り替えタイミングが来ます。
ScreenFlip()の入口と出口で計測するのは、この描画待ちと切り替え待ち時間を合わせて計測することになります。

今回の結果から推測できるのは、描画が遅れていず、CPUの処理周期が垂直同期の切り替えタイミングに近い一定であれば、今回の計測結果のようになると言うことです。
この理屈は理解出来ますか? これを理解出来ないとテストが困難なので分からない所を聞いて下さい。

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 14:20
by 珈琲
えーと、表示された時間が0に近ければ近いほど、待機時間が短いということなので、「その他全般の処理」が遅れている、というのがわかる ということでしょうか?

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 14:25
by softya(ソフト屋)
なにか誤解がありそうなので、もう一度書くとFPSが遅いのも問題ですが、16.6・・・の倍数の処理時間でCPUがなぜか一定していると言う状況が私には不思議ですって事です。
あとDXLIBの描画命令と実際のGPU描画が別のタイミング(別のフレーム)に行われているということを理解して下さいね。

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 14:49
by 珈琲
>16.6・・・の倍数の処理時間でCPUがなぜか一定している
処理時間が16.6を超えてしまった場合その分だけ描画が遅れるという事ですよね?
つまり、
珈琲 さんが書きました: ゲーム開始時は
0.012を表示するのですが
敵機がミサイルを打ち始めて(処理がかかると)
0.000000と
0.001000の2つを(ほぼ)交互に表示します。
FPSが規定の60を下回った場合、描画待機時間は0.000000というのは別に不自然でないのでは?


>あとDXLIBの描画命令と実際のGPU描画が別のタイミング(別のフレーム)に行われているということを理解して下さいね。
ダブルバッファリングとかの話でしょうか?
GPUをあまり意識したことなかったのでDXLIBとGPUがどのようなやり取りをしているのか知識がないです。

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 14:56
by softya(ソフト屋)
16.6・・・の周期で切り替わるのを待つって書きましたよね。
なので普通は最初のような待機が発生するんです。
うーん。図で書かないとわかんないかな。自分で書いてみてくださいね。
絶対値として、16.6・・・の倍数時にしか切り替わりません。つまり、ScreenFilp()を抜けて来ません。

Re: 自作ゲームで処理の重い場所

Posted: 2013年8月01日(木) 15:15
by 珈琲
あ、多分理解できました。

1フレームの処理内容に増減があるのに1/60秒できっかり仕事を終わらせるCPUなんてあるわけないですよね
処理時間が超えたなら超えたで,次の描画機会である1/60秒後まで待機するという話ですね