トップページgamedev
240コメント91KB

PCで描画と内部処理の非同期処理ってどうやんの?

■ このスレッドは過去ログ倉庫に格納されています
0001(;´д⊂ヽ02/04/29 16:14ID:Gsu.P7Dk
PCならではの可変フレームレートで、かつ安定したキー入力とか演算の微分処理とか
実装したいんですが、具体的に、どういう風な構造にすればいいのか全然わかりません助けて

キー入力とかタイミングに厳しい処理を別スレッドで回すとして
描画担当スレッドはどーすればいいんでしょう?
(描画途中で別スレッドにより内部変数が書き換えられるケースが出てきますよね?)
0002名前は開発中のものです。02/04/29 16:19ID:???
みんな頑張ってるから
0003名前は開発中のものです。02/04/29 16:20ID:???
書き換えられたくないタイミングをマスクするしかおもいつかんのだが。
0004名前は開発中のものです。02/04/29 16:56ID:???
多重に持てば良いんじゃないの
0005名前は開発中のものです。02/04/29 16:57ID:???
単発スレ立てんなボケ。
0006名前は開発中のものです。02/04/29 16:59ID:???
ローカルルールをよく読めやGW厨

お約束
 3.簡単な質問やあいまいな(問題点の良く分からない)質問は汎用Q&Aスレッドへ
0007(;´д⊂ヽ 02/04/29 17:23ID:Gsu.P7Dk
すんません。単発つーか、結構深いテーマな気がしたんで
本でも扱ってないし

2Dゲーみたく、スプライトレジスタみたいのだとマスクとか多重化も
簡単なんだけど、3Dゲーでクラスだと色々面倒でねぇ

可変フレームレートつってもリフレッシュレートの関係で上限は120と
しても問題ないわけで、なんか上手く処理できんものんかと思ったわけです

ゲーム内ではよくある、1フレーム前の位置参照するような場合でも
前フレームが何ミリ秒前なのか可変だと困ると思うのです
仮に計算で逆算するにしても、ソウルキャリバーの剣の軌道なんかの
多段数の前フレーム参照は難しいかなと思うのです

そんな訳で僕の中では、上限120FPSで、処理落ちするときだけ描画を飛ばす
ような完全非同期ではない方法が正解だと思うのですが、ご意見聞きたいなぁと
0008名前は開発中のものです。02/04/29 17:26ID:???
>>7
それでいいんじゃない。
はい終了。
0009名前は開発中のものです。02/04/29 17:30ID:???
でも海外のFPSとかで、ロケット砲なんかのビルボードな煙軌道なんかは、そうでもない気がする
0010名前は開発中のものです。02/04/29 17:45ID:???
内部にカウンタ持ってるか、数フレーム前まで位置を保存しときゃ済む話だろ
0011名前は開発中のものです。02/04/29 17:58ID:???
ジサクジエン乙カレ。
>>1=3=7=9
0012名前は開発中のものです。02/04/29 17:59ID:???
3Dゲームに限らず、殆どのゲームは描画で多くの時間を費やしてる。
描画についていけないならデータ更新する、という>>7の方法でよい。
yaneSDKあたりにそれ関係の記事があったと思うので興味があればどうぞ。

それと、3Dゲームだと描画速度がFPS30位でも結構滑らかに見えてしまうので、
データ更新速度はFPS120ほど高くなくてもよいと思うよ。

質問内容がアレだったこともあるので、削除依頼は出しておいたほうが吉かと。
ログには残しておきたい内容だけれどねぇ。
0013名前は開発中のものです。02/04/29 18:08ID:???
>>7みたいに 1/120秒の内部ループはゲームによっては処理が重いかもしれん
だからといって1/60秒単位で変数を更新した場合、60FPSより早い描画だと、描画側が困っちゃうよね

ミサイルの先端はどんどん進むのに、後端(保存された数フレーム前情報)は1/60単位でしか更新されないと
煙の全長長くなっちゃうよね
(気付かないだろうけど、マシン毎になんか結果がことなるプログラムって気持ち悪い)

じゃぁミサイルの先端位置から後端の位置が計算できるか つーとそうでない場合もあるし
0014名前は開発中のものです。02/04/29 18:19ID:???
PCの場合スペック自慢つーかベンチ代りだったりするので
FPSが60頭打ちじゃ、手抜き!とか叩かれる。技術力もないみたいだし

キャラは同じ位置のままで再描画だけして、Over60FPS表示にしる!
どーせ人間の目では60fps以上区別つかないって言うしー
でも、中には区別できる人もいる罠
0015名前は開発中のものです。02/04/29 18:38ID:qMxpqTZM
Windowsアプリだと、描画はプライマリスレッド以外関与するなって鉄則があると思うんだけど
これはゲームにも当てはまるのか。
WindowのGUIをプライマリスレッドで管理し、
Direct3Dの描画をワーカースレッドで管理なんてOK?
軽く試してみたら、微妙に不具合が起きたのだけど。
0016名前は開発中のものです。02/04/29 18:49ID:???
>>14
実際に自分で簡単なサンプル作ってみればわかるが、
意外に誰でも区別できるぞ。
しかも、見るだけよりも操作するとはっきりわかる。
0017(;´д⊂ヽ02/04/29 18:58ID:Gsu.P7Dk
どうすればいいんでしょう
残影とか軌道が残るモノさえなければ、何にも考えなくていい気はするけど
0018名前は開発中のものです。02/04/29 19:13ID:???
残影とか軌道が残るモノをなくす。
0019名前は開発中のものです。02/04/29 19:15ID:???
残像オブジェクトに賞味期限もたせて、毎回チェック。
期限切れはあぼ〜ん。
0020名前は開発中のものです。02/04/29 19:22ID:???
可変フレームレートな環境(つまるところPC)で
内部計算と入力処理と描画処理をうまいこと回すっていうのは面白いネタだと
思うがなー。OS側の制約がうざいけど。

>>15
SetCooperativeLevel に DDSCL_MULTITHREADED というフラグがある
0021名前は開発中のものです。02/04/29 19:23ID:qMxpqTZM
つーか残像とか軌跡とかも1つのインスタンスとして保持すればいいんじゃないの?
せっかく描画とそれ以外に処理を分けているんだから、
描画の具合が内部処理に影響を与える仕様にはするべきではない。
内部処理から描画ルーチンへのデータの流れは一方通行にするべき。
00222102/04/29 19:26ID:qMxpqTZM
>>19そのまんまでした。
>>19に同意。
00232102/04/29 19:30ID:qMxpqTZM
>>20
ヘルプに書いてあった。

>この設計は、暗にマルチスレッド アプリケーションを意図している。
>特に、アプリケーションはウィンドウ メッセージ処理スレッドを
>Direct3D スレッドから完全に分離しなければならない。
>1 つのスレッドでモード変更を行いながら、
>別のスレッドで Direct3D の呼び出しを行うアプリケーションは、
>デッドロックの危険性がある。

>このような理由から、Direct3D では、Reset、CreateDevice、TestCoorperativeLevel、
>または IDirect3DDevice8 の最後の Release の各メソッドは、
>ウィンドウ メッセージを処理するスレッドと同じスレッドからのみ呼び出すことができるように設計されている。

つまりこれさえ気をつければ、Windowスレッドとの兼ね合いを気にする必要はない訳だね。
0024名前は開発中のものです。02/04/29 19:46ID:???
なるほど賞味期限にすればいいのかも
でも、描画と内部、別スレッドで回すっても、クラス多用だと変数の二重化が面倒だよなぁ
保存する変数がいろいろ散らばってるもんなぁ
行列だったり、オイラー、Quaternion、ブレンド係数だったり

その辺、どー書くのがスマート?
0025名前は開発中のものです。02/04/29 20:04ID:???
ローカルでしか使わないデータはローカルで管理。
みんなが使うデータは管理するクラスをひとつ作ってそいつが管理。
0026名前は開発中のものです。02/04/29 20:10ID:???
描画スレがデータに影響与えない設計にすべきだろ。
描画開始時に必要なデータをバッファにコピーしてきて、それから描画するとかな。
BeginCopyでデータをロックして読み出し開始、EndCopyでロック解除するみたいな。
(注:そんなAPIありません。自作でねw)
データが多いと大変だけど…設計次第。
0027名前は開発中のものです。02/04/29 21:29ID:kRVsyEhU
例えば、インターフェイスを使う場合
キャラclassには、標準として
Render系インターフェイスとFrame系インターフェイスと
別けて実装すればスッキリすると思う。
0028名前は開発中のものです。02/04/29 21:29ID:???
Quake系のソースとか読んだ人いないの?
どうやってるんだろ?
ちなみに俺は持ってるけど読んでません
0029名前は開発中のものです。02/04/29 21:51ID:???
どうしても別処理したいなら
描画は1フレームごとのタイマー割りこみで処理する。
内部処理側は1フレーム分の描画オブジェクトのセットが完成したら
渡す。

でも内部処理と描画を分けるのは賢くないので、waitvに描画処理を
埋め込む事を推奨する。
0030名前は開発中のものです。02/04/29 22:36ID:???
>>28
市販のゲームは、描画にかかった時間を計って、その分データを更新するって
方法が一般的じゃない?速いマシンではいくらでも速くなるように作らないとね。
0031名前は開発中のものです。02/04/30 03:27ID:???
まともにやろうと思えばWindowsシステムじゃせいぜい100〜200FPSで限界が来る。
まともじゃない方法ならそれ以上だせるが、そっちのほうが手抜きだな。
0032名前は開発中のものです。02/04/30 11:02ID:???
>>7
>単発つーか、結構深いテーマな気がしたんで
その結果がこれかよ…
0033名前は開発中のものです。02/04/30 14:35ID:???
スーファミ等のエミュレータが綺麗に処理していると思うんだけど
ソース公開しているのもあるよね。誰か調べたことのある人いる?
0034名前は開発中のものです。02/04/30 16:03ID:???
CAPCOMのエミュレーターはトリプルバッファとかでやってたな

リフレッシュ論争だけど、60FPSのゲームをリフレッシュレート85とかにしてれば
どうやってもガタツクよね?
0035名前は開発中のものです。02/04/30 16:14ID:a3cFuDTc
ゲームにおけるマルチスレッドの運用って感じで
このスレもまだまだ存在意義があると思うな。
んで質問。スレッド間通信で最もゲーム向きなもん(軽い)て何?
やっぱりメッセージかな?
0036名前は開発中のものです。02/04/30 16:53ID:???
>>32
質問スレって初歩的な事しかわかんないじゃん
玄人は読んでる人少ないし
0037名前は開発中のものです。02/05/01 00:46ID:T8nyusCw
一般的かどうかはともかく、俺の場合。

1)内部描画コマンドを定義する
2)ゲームのメインループとは別に描画スレッドを回す
3)メインループはタイマで毎秒60回ぶん回して、描画コマンドをキューに突っ込む
4)描画スレッドはキューに入っている描画コマンドを元に画面を作る

という感じ。
もちろん、描いてる途中で描画コマンドキューが書き換わったり、描画コマンドキュー
が構築されていないうちに描画されないようにするための工夫は必要。
(キューの多重化とか排他制御とか)
0038名前は開発中のものです。02/05/01 06:46ID:???
>>37
60回以上描き換えることがあっても、
内部処理は60回超えないってこと?
遅い場合は有利だけど、速い場合は利点はないってことでよいのかな?
00393702/05/02 01:16ID:nbxhpyDs
>>38
自分のやり方の場合、そうなります<60回/sec超えない

前フレームの処理にかかった時間を元に今フレームでの移動速度を補正
させつつ全力でぶん回す、というやり方もやったことはあるけど、個人的に
あまりエレガントだとは思えなかったので・・・。
0040名前は開発中のものです。02/05/02 02:06ID:???
あれ、>>37のやりかたで60回以上描きかえることがあるの?
描画コマンドが60回までしか発生しないから、描きかえも60回まででは?
0041名前は開発中のものです。02/05/02 02:19ID:???
ソースコードと実装のエレガントさをとるか、やってることのクールさをとるか。
00423702/05/02 02:33ID:ZkWoqS7A
>>40
あー、言葉足らずですみませんでした。
もちろん1フレーム描いたら、描画コマンドキューが更新(表裏切り替え)
されるまでは描画もしません(_o_)
0043名前は開発中のものです。02/05/02 02:48ID:???
やっぱ>>7にもどって 内部120回ループとかが行けてる?
0044名前は開発中のものです。02/05/02 03:26ID:iMh3R/Ps
>>43
120回なんか回したって意味ないんだから
60回上限にして、その分アイドルに回した方が
全然いいと思うがナ。
0045名前は開発中のものです。02/05/02 12:13ID:???
>>44
ウィンドウモードのことを考慮してるんじゃない?
フルスクリーン60Hz限定のゲームならいいんだけどね
更新60Hzでディスプレイ75Hzだと相当がたつく
0046名前は開発中のものです。02/05/03 00:35ID:???
>更新60Hzでディスプレイ75Hzだと相当がたつく

これって2Dモノはよくわかるけど、3Dだとわかる?
それ以前にWindows特有の処理落ちみたいのがあるし、FPSとか場面毎のポリゴン数によって
処理落ちしたりして、何が原因だかよくわからないんだよねー

ダブルバッファをスマートに書くのはテンプレート使えばいいんじゃない?
描画と完全に分けるならトリプルバッファが必要だと思うけど。
でも、そんな面倒な構造にする必要あるかね?
0047名前は開発中のものです。02/05/03 01:42ID:6JPEwFK2
>>44
PCでやる以上、それは割り切るしかないと思う。
タイマで計って廻した所で、完全にVSyncと同期させるのは無理だし〜
0048名前は開発中のものです。02/05/03 03:03ID:???
あのーここってリフレッシュレート論争と大差ないんですけど・・・・
0049名前は開発中のものです。02/05/03 03:17ID:???
>>46
60fps固定で処理してると、3Dでもガタつくよ。
モーションが飛ぶんじゃなくて、速度が不安定になるというか、ブレる感じ。

内部処理を固定するなら、描画処理は1フレーム分遅延させるのがベスト。
描画処理の方が速く回る場合は、補間させて滑らかな動き。
0050名前は開発中のものです。02/05/03 11:13ID:???
新リフレッシュレート論争スレの予感。

過去スレより
http://piza.2ch.net/tech/kako/972/972165437.html
>命題:「『1フレーム後に、変数v がa増加する』をどのように実装するか?」
>
>◆A宗:「v += a * dt」
>v-syncに同期。リフレッシュレートは任意。
>
>◆B宗1派:「v += a」
>タイマーに同期。一定間隔で値を更新する。
>
>◆B宗2派:「v += a」
>v-syncに同期。何らかの方法でリフレッシュレートを固定する。

私は一時A宗に傾きかけたが、結局B宗に戻りつつあるような気がする。
0051名前は開発中のものです。02/05/03 12:03ID:???
ゲームクリエイターズバイブルに描画と内部処理の分け方の話があったね。
0052名前は開発中のものです。02/05/03 13:17ID:???
>>50
そのスレ読んでないけど、内部処理と描画処理を分けるこのスレの趣旨とは
微妙に違うぞ。
このスレは内部処理はB宗で描画だけA宗にするって話。
それがいいか悪いかはともかく、どうやって実装するのかというスレ。
0053名前は開発中のものです。02/05/03 13:20ID:???
実のところ話していることは同じだという罠
0054名前は開発中のものです。02/05/03 18:25ID:???
内部処理では座標などの値を過去フレームと次回フレームの2つを持っておく。
描画スレッドでは描画開始時間と内部処理の過去フレームの時間から補完?

 float t = (render_time - old_time -) * (60 / 1000);
 D3DXVec3Lerp(&currnt[i], &prev[i], &next[i], t);

てのを全部に対してやるだけだったらそれなりにエレガントかも。
0055名前は開発中のものです。02/05/03 19:15ID:???
>>54
それだと、キーフレームアニメーションなんかやると
アニメーションの終端を突破したりしないか?
IF文で終端で止めにすればいいという問題でもない
(アニメーションAが終わったらアニメーションBにスイッチする場合とか)
0056名前は開発中のものです。02/05/04 12:02ID:???
>>55
たいした問題じゃない。
0057名前は開発中のものです。02/05/04 16:15ID:???
>>54はスレッドを分けなくてもできるね。
排他制御しないぶん楽。
0058名前は開発中のものです。02/05/04 19:49ID:???
毎ループ数値積分してるやつはどうなるの?
0059名前は開発中のものです。02/05/04 20:29ID:DoQwTAVw
内部処理回数が決まってないと リプレイの実装が面倒でないか?
車ゲーのリプレイは適当でいいけど

まぁ、最低60回は回すように組んでおくとして、数値微分なんかの
凾狽ェ小さすぎて問題になることなんてあるだろうか?
0060名前は開発中のものです。02/05/04 20:53ID:???
ない。
0061名前は開発中のものです。02/05/04 23:03ID:???
丸め誤差にさえ気をつけていれば大丈夫
0062名前は開発中のものです。02/05/05 00:10ID:PKpkIeKE
FPS制御用にワーカースレッドを使ったクラスを作ったが
普通の関数バージョンに比べて遅くなった。
つーかフレームのコールにPostMessage使ってるし当然か。
なんだか設計思想が間違ってたようで激しく鬱。
0063名前は開発中のものです。02/05/05 00:42ID:???
>>62
PostMessageはまずいでしょ…ガタガタになっちゃう。
ま、誰しも一度は通る失敗だけどね。
0064名前は開発中のものです。02/05/05 01:07ID:PKpkIeKE
>>63
でもさ、それ以外思い浮かばんのよね。
それ以外だと、メッセージループに
if( pFPS30->BeCalled() ){
RenderMain();
}
を埋め込む以外なくなる。
0065名前は開発中のものです。02/05/05 02:14ID:QlZCUdc2
グローバル変数だ!
0066名前は開発中のものです。02/05/05 02:29ID:???
volatile bool g_BeCalled より
PostMessage(arg->hWnd, WM_PAINT, 0, 0)を
選ぶ62たんはWindowsプログラマの鏡!
00676202/05/05 03:42ID:yqpWzWOE
俺のプログラムだと、
FPS用のスレッドでパフォーマンスタイマーの
ビジーループをぐるぐる回して処理している為
たとえ内部処理、描画処理を1FPSに設定しても
CPU占有率が100%になってしまう。
何かスマートな実装方法は無いかえ?
0068名前は開発中のものです。02/05/05 03:59ID:???
HALT
0069名前は開発中のものです。02/05/05 04:06ID:yqpWzWOE
>>68
Sleep系とパフォーマンスタイマーの併用ってこと?
パフォーマンスタイマーから大まかなSleep時間を
求めて眠らすとか?
それとも精度には不安があるがSleep系のみで管理とか?
ビジーループを使わず、タイマーを使う良い方法が
あったら教えて欲しい。
0070名前は開発中のものです。02/05/05 04:29ID:XpophaTo
精度に不安って他にプログラム走らせてない場合も不安定なんだろうか。
00716902/05/05 05:40ID:CUy74S1o
>パフォーマンスタイマーから大まかなSleep時間を
>求めて眠らすとか?
MsgWaitForMultipleObjectsを使ってこの案を実践したら
60FPSでCPU使用率100%だったのが0%まで落とす事に成功しました。
なんだか上手くいき過ぎて怖い。^^
でも試して上手くいった時は快感!だったりする。

>>70
60FPSの場合、精度は16msec程度必要。
まあSleep系は悪くても誤差10msecらしいから
一応大丈夫といえばそうらしい。
0072名前は開発中のものです。02/05/05 10:34ID:???
>>70
Windowsで、他にプログラムを走らせない状況を作るのは不可能。
■ このスレッドは過去ログ倉庫に格納されています