トップページ > 過去ログ > 記事閲覧
MV1DrawFrameの処理時間について
名前:バトーキン 日時: 2010/06/22 06:15

お世話になります。 また、動画を作ってテストプログラムを公開しました。 www.nicovideo.jp/watch/sm11122007 このプログラム中、3Dマップの描画処理に、想像以上の時間がかかってしまい、 この描画時間を、なんとか減らしたいと困っております;; お知恵を貸してください。 モデル中では、木や草などの半透明要素を、 Zソートする単位でフレーム分けしており、 これを for ループで、MV1DrawFrame() を使用して描画しています。 for(int i=0; i<フレーム数; i++){   MV1DrawFrame(model,i); } 時間がかかっているのは、このforループの部分で、 私の環境 (GF6600) では、500〜600ループほどで 16ミリ秒 を超えてしまいます。 60fps にこだわりはないので、30fps 化は考えているのですが、 それでも、今の感じでは、ちょっとフレーム数が増えると、 あっという間に、33ミリ秒を超えてしまいそうで、他の処理に使える時間が残りません。 描画部分のみ再現した、検証プログラム+ソースを用意しました。 www1.axfc.net/uploader/Sc/so/126305.zip&key=dxlib ・Zソートはしていません。 ・描画フレームは1600ほど。 私の環境では、描画時間は 33ミリ秒ほどでした。 これでも、モデル自体は、かなり簡素に作っているつもりなんですが。 なんとか、この処理時間を減らす方法はないものでしょうか? Zソートの結果を、早く描画できる方法なら、何でもかまいません。 例えば、 全く同じ内容でも、MV1DrawModel() で 1回 の描画なら、 2〜3ミリ秒の処理時間で済んでいるので、 メモリ上のモデルデータにアクセスして、 フレームの並び順を操作した後、MV1DrawModel()で、1回で描画する、 といった方法などは、難しいでしょうか?

Page: 1 |

Re: MV1DrawFrameの処理時間について ( No.1 )
名前:管理人 日時:2010/06/24 01:57

動画を拝見いたしました、設定された目標に驚くと同時に、描画速度に関する悔しさも伝わってきました 最初に謝らせてください 異常なCPU負荷はDXライブラリが原因です 申し訳ありません・・・ > 時間がかかっているのは、このforループの部分で、 > 私の環境 (GF6600) では、500〜600ループほどで 16ミリ秒 を超えてしまいます。 > 全く同じ内容でも、MV1DrawModel() で 1回 の描画なら、 > 2〜3ミリ秒の処理時間で済んでいるので、 こちらのご発言を拝見したときにDXライブラリに何かあると確信しました orz 検証プログラムを実行させていただき、私の環境 Core 2 Quad + Geforce 8800GT でも 15msec を超えるのを確認致しました 長文が始まる前にとりあえず異常な処理負荷の増大を修正したバージョンをアップしましたので、 宜しければお試しください m(_ _;m http://homepage2.nifty.com/natupaji/DxLib/DxLibVCTest.exe // VisualC++ 用 http://homepage2.nifty.com/natupaji/DxLib/DxLibBCCTest.exe // BorlandC++ 用 (中身を既存のライブラリのファイルに上書きして、BCCをお使いの 場合は『再構築』、VCをお使いの場合は『リビルド』をして下さい) // ビューアー http://homepage2.nifty.com/natupaji/DxLib/DxLibModelViewerTest.zip 負荷増大の原因は稚拙なもので、シェイプメッシュ機能( MMDの表情など頂点自体が変形する機能 )の更新処理にありました MV1DrawModel, MV1DrawFrame, MV1DrawMesh の何れかの描画関数が呼ばれた際に 実際の描画を行う前にシェイプのブレンド率などの変更があった場合はシェイプメッシュの更新処理を 行うのですが、この「シェイプの状態に変更があったかどうか」の確認処理が処理時間増大の原因でした アップしていただいたモデルでは当然シェイプ機能は使用されていないので、この「更新確認処理」に 時間が掛かると言うのはおかしな話ですが、シェイプ機能を使用しているかどうかの判断を「フレーム単位」で 行っていて、且つモデル全体に対するシェイプメッシュ更新処理の関数しか用意していなかったので、 結果として1回の描画関数( MV1DrawModel or MV1DrawFrame or MV1DrawMesh )につき、モデルの全フレームに対して 「シェイプメッシュが含まれていて、且つ状態が変更されたかどうか」の確認処理が行われるように なってしまっていました MV1DrawModel 一回でモデル全体を描画する場合はこの確認作業は1回しか行われないのでそこまでの 負荷増大には繋がりませんが、MV1DrawFrame でフレーム単位で描画した場合は各フレームの描画前に この確認作業が行われるのでモデル全体で 1600フレームある状態で1600フレーム全てを MV1DrawFrame で 描画すると1600フレームに対するシェイプ更新確認作業が1600回行われることになり、これが負荷が異常に 増大する原因となっていました これを回避するのは簡単で、まず「モデルにシェイプが含まれていない場合は更新確認作業を行わない」 という至極当然の分岐を一つ挟むだけです 普通はもう少し踏み込んで「モデル全体で更新されたシェイプが一つも無い場合は更新確認作業を行わない」 まで実装します 今までは上記のどちらも実装していませんでした 本当に申し訳ありません・・・ 今回の修正では上記二つの処理を実装しました この処理を実装した上でも確かに MV1DrawModel 一回の描画でモデル全体を描画したほうがCPUの負荷も 多少軽くなりますが、実際は本来頭を悩ませるべき対象である「描画自体の負荷」の方が関数呼び出しの 回数増加による負荷上昇を大きく上回りますので、今回の実装には含めませんでした ところで、fpsについてですが、今回の問題を解消してもパワーの無いノートパソコンやネットブックでは 現在の island_test.mqo でも60fpsギリギリです、 更に、もし利用者の方がマップデータを自由に作成でき、 且つオンラインソフトということで3Dモデルが何体同時に画面上に表示されるか分からないという仕様ですと 如何なる環境でも60fpsを保つというのはほぼ不可能になると思います なので、最初から 30fps、ではなく可変fps( 毎フレーム、前のフレームの間に経過した時間分だけ処理を進める方式 )に しておいた方が良いのではないかと思うのですが、如何でしょうか?
Re: MV1DrawFrameの処理時間について ( No.2 )
名前:バトーキン 日時:2010/06/24 10:01

いつもいつも、素早い対応ありがとうございます。 最新版を試させていただきました。 劇的にサクサクと動くようになりました! ありがとうございます! 動画を載せる前に、報告にきていれば良かったのですが、 対策案と検証プログラムを用意してから…、とモタモタしていたら、 このタイミングになってしまいました。すいません; コンテンツとして面白くなるように、動画では何かと大げさに表現することもありますが、 どうか気にしないでくださいませ; ライブラリの力に頼りっきりのプログラムなので、色々とありがたい限りです。 >描画負荷の原因 なるほど、やっぱりCPU起因だったんですね。 コメントで色々と情報を寄せてもらったんですが、 グラボの性能と結果が、比例していないような気がしていたところでした。 大げさな仕様追加などがなくても治る場所で、良かったです。 対応については、これで充分です。ありがとうございました。 >可変fps 30fps化、というのは、お手本にしている MHFというゲームが 30fps らしいのと、 後々は、他ユーザとの同期などの処理も入ってくるので、 処理時間はたっぷり目に確保、という程度の理由で考えていたのですが、 可変fps……ですか。 どういった実装になるのでしょう。 サンプルの「弾当てゲーム」には、(経過時間 / 待機時間)で、 例えば、1フレームの処理に、32〜33ミリ秒を超えていたら、 次の1フレーム間には、2フレーム分の処理をする、というような処理が入っていたと思います。 あれとは、また違う感じなのでしょうか?
Re: MV1DrawFrameの処理時間について ( No.3 )
名前:管理人 日時:2010/06/26 23:09

無事軽くなったようで何よりです > コンテンツとして面白くなるように、動画では何かと大げさに表現することもありますが、 > どうか気にしないでくださいませ; 本来必要の無い苦労をお掛けしてしまったことは事実なので 申し訳ない気持ちに変わりはありませんが、 そう言っていただけると救われます m(_ _;m > 可変fps MHFは触ったことが無いのでわかりませんが、「弾当てゲーム」の「描画部分だけ可変レート」を 発展させて「ソフトの進行処理」も可変にしたものです キャラクターの行動やアニメーションの再生時間進行を全て「経過時間」を考慮するようにすると、 どんなに fps が下がってもソフト内の進行速度は下がることがなくなります 可変fpsのサンプルを作ってみました、サンプルプログラム実行用フォルダの中にある DxChara.x があれば実行できます、解説はプログラムの後で・・・ #include "DxLib.h" #define SPEED (800.0f) // モデルの移動スピード #define SPACE (250.0f) // 1体辺りの描画間隔 // 描画モデル数の初期値 int DrawModelNum = 1 ; // WinMain int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { float FrameTime ; LONGLONG PrevTime ; int ModelHandle ; int i, j ; float HalfSize ; float DrawX ; int AnimAttachIndex ; float AnimPlayTime ; // ウインドウモード ChangeWindowMode( TRUE ) ; // VSYNC待ちをしない SetWaitVSyncFlag( FALSE ) ; // DXライブラリ初期化処理 if( DxLib_Init() == -1 ) return -1; // エラーが起きたら直ちに終了 // モデルの読み込み ModelHandle = MV1LoadModel( "DxChara.x" ) ; // カメラのセット SetCameraPositionAndTarget_UpVecY( VGet( 0.0f, 2769.0f, -2600.0f ), VGet( 0.0f, 170.0f, -380.0f ) ) ; // アニメーションのアタッチ AnimAttachIndex = MV1AttachAnim( ModelHandle, 0 ) ; // アニメーションの再生時間をリセット AnimPlayTime = 0.0f ; // 最初のフレームの経過時間は60分の1秒 FrameTime = 1.0f / 60.0f ; // 最初の時間を取得しておく PrevTime = GetNowHiPerformanceCount() ; // 描画位置を初期化 DrawX = 0.0f ; // メインループ while( ProcessMessage() == 0 ) { ClearDrawScreen() ; // 上下キーで描画モデルの数を増減 switch( GetInputChar( TRUE ) ) { case CTRL_CODE_DOWN : if( DrawModelNum > 1 ) DrawModelNum -- ; break ; case CTRL_CODE_UP : DrawModelNum ++ ; break ; } // 左右キーで描画位置を移動 if( CheckHitKey( KEY_INPUT_LEFT ) ) { DrawX -= SPEED * FrameTime ; } if( CheckHitKey( KEY_INPUT_RIGHT ) ) { DrawX += SPEED * FrameTime ; } // アニメーション処理 AnimPlayTime += 6000.0f * FrameTime ; if( AnimPlayTime >= MV1GetAttachAnimTotalTime( ModelHandle, AnimAttachIndex ) ) { AnimPlayTime -= MV1GetAttachAnimTotalTime( ModelHandle, AnimAttachIndex ) ; } MV1SetAttachAnimTime( ModelHandle, AnimAttachIndex, AnimPlayTime ) ; // モデルの描画 HalfSize = ( DrawModelNum - 1 ) * SPACE / 2.0f ; for( i = 0 ; i < DrawModelNum ; i ++ ) { for( j = 0 ; j < DrawModelNum ; j ++ ) { MV1SetPosition( ModelHandle, VGet( -HalfSize + j * SPACE + DrawX, 0.0f, -HalfSize + i * SPACE ) ) ; MV1DrawModel( ModelHandle ) ; } } // 情報描画 DrawFormatString( 0, 0, GetColor( 255,255,255 ), "FrameTime:%.4f秒", FrameTime ) ; DrawFormatString( 0, 16, GetColor( 255,255,255 ), "FPS:%.3f", 1.0f / FrameTime ) ; DrawFormatString( 0, 32, GetColor( 255,255,255 ), "DrawNum:%d", DrawModelNum * DrawModelNum ) ; DrawFormatString( 0, 48, GetColor( 255,255,255 ), "DrawX:%.1f", DrawX ) ; DrawString( 0, 480 - 16, "操作 キーボード上下:描画モデル増減 キーボード左右キー:描画位置移動", GetColor( 255,255,255 ) ) ; // 裏画面の内容を表画面に反映 ScreenFlip() ; // このフレームの経過時間を算出 FrameTime = ( float )( GetNowHiPerformanceCount() - PrevTime ) / 1000000.0f ; // 今の時間を保存 PrevTime = GetNowHiPerformanceCount() ; } // DXライブラリ使用の終了処理 DxLib_End() ; // ソフトの終了 return 0 ; } 実行すると DxChara.x が歩くモーションが流れている状態で1体だけ描画されています 画面左上には1フレームに経過した時間とそこから導き出されるFPSと描画されている モデルの数とモデルの描画X座標が表示されています キーボードの上キーを押すと描画されるモデルの数がガンガン増えていきます 増えると共にfpsは下がりますが、モデルの歩くモーションの流れる早さは変わりません キーボードの左右キーを押すとモデルの描画位置が左右に移動しますが、この移動の速度も fpsに関わらず一定です なぜそうなっているかと言いますと、「モーションの再生時間をどれだけ進めるか」や 「どれだけ描画位置を変化させるか」の計算に1フレームの経過時間(変数 FrameTime )を 使用しているからです、この方式を使うと理論上はfpsがどんなに下がってもソフトの進行速度は 下がらなくなります マシンに掛かる負荷やPCの性能によってfpsが変動し続けるのでオンラインシステムを構築する場合に 経過フレーム数を基準とした同期処理はできなくなりますが、この方式だと30fps固定にするより更に 実行できる環境が増えると思います アクション要素があったりすると高性能PCを持っている方が有利になってしまいますが・・・
Re: MV1DrawFrameの処理時間について ( No.4 )
名前:バトーキン 日時:2010/06/27 12:57

おお、サンプルプログラムまで。 丁寧にありがとうございます。 なるほど、 実行してみると、よく意味が分かりました。 処理に使う数値の方を変化させるんですね。こういう手法は思いつきませんでした。 習作ですし、新しい手法はどんどん試してみたいと思います。 ちょうど、負荷をかけまくってみるプログラムを作っていたので、 それに組み込んでみます。 勉強になります。 ありがとうございます。

Page: 1 |