ゲームループとopenGLの間のマルチスレッドを利用する


10

openGLレンダラーに基づくゲームのコンテキストで話す:

2つのスレッドがあるとしましょう:

  1. ゲーム内オブジェクトのゲームロジックや物理などを更新します

  2. ゲームオブジェクトのデータ(スレッド1が更新し続ける)に基づいて、各ゲームオブジェクトのopenGL描画呼び出しを行います。

ゲームの現在の状態で各ゲームオブジェクトの2つのコピーがない限り、スレッド2が描画呼び出しを行っている間、スレッド1を一時停止する必要があります。そうでない場合、ゲームオブジェクトはそのオブジェクトの描画呼び出しの途中で更新されます。望ましくない!

ただし、スレッド1を停止してスレッド2から安全に描画呼び出しを行うと、マルチスレッド/同時実行の目的全体が失われます。

マルチコアアーキテクチャを活用してパフォーマンスを向上させるために、数百または数千または同期オブジェクト/フェンスを使用する以外に、これに対するより良いアプローチはありますか?

マルチスレッドを使用して、現在のゲームの状態にまだ含まれていないオブジェクトのテクスチャをロードしてシェーダーをコンパイルできることはわかっていますが、アクティブ/可視オブジェクトに対して、描画と更新の競合を引き起こさずにそれを行うにはどうすればよいですか?

ゲームオブジェクトごとに個別の同期ロックを使用するとどうなりますか?このように、すべてのスレッドは、更新/描画サイクル全体ではなく、1つのオブジェクト(理想的なケース)でのみブロックします!しかし、各オブジェクトのロックを取得するのにどのくらいのコストがかかりますか(ゲームには1000のオブジェクトがある場合があります)?


1.スレッドが2つしかないと仮定すると、適切にスケーリングされません。4コアは非常に一般的で、増加するだけです。2.ベストプラクティス戦略の回答:コマンドクエリの責任分離とアクターモデル/コンポーネントエンティティシステムを適用します。3.現実的な答え:ロックなどで従来のC ++の方法をハックするだけです。
Den

1つの共通データストア、1つのロック。
ジャスティン

@justinちょうど私が同期ロックで言ったように、マルチスレッドの唯一の利点は昼間に残酷に殺害されます、更新スレッドは描画スレッドがすべてのオブジェクトを呼び出すまで待機する必要があり、その後描画スレッドは更新ループがものの更新を完了するまで待機します!シングルスレッドアプローチよりも悪い
Allahjane

1
非同期グラフィックAPI(openGLなど)を使用するゲームでのマルチスレッドアプローチはまだアクティブなトピックであり、標準またはほぼ完璧なソリューションはありません
Allahjane

1
エンジンとレンダラーに共通のデータストアは、ゲームオブジェクトであってはなりません。それは、レンダラーが可能な限り迅速に処理できるといういくつかの表現である必要があります。
ジャスティン

回答:


13

これまでに説明したロックを使用したアプローチは、非常に非効率的であり、単一のスレッドを使用する場合よりも遅くなる可能性があります。各スレッドでデータのコピーを保持する他のアプローチは、おそらく「速度の点で」うまく機能しますが、コピーを同期させるには、法外なメモリコストとコードの複雑さがあります。

これにはいくつかの代替アプローチがあります。マルチスレッドレンダリングの一般的な解決策の1つは、コマンドのダブルバッファを使用することです。これは、レンダラーバックエンドを別のスレッドで実行することで構成され、すべての描画呼び出しとレンダリングAPIとの通信が実行されます。ゲームロジックを実行するフロントエンドスレッドは、コマンドバッファー(ダブルバッファー)を介してバックエンドレンダラーと通信します。。この設定では、フレームの完了時に同期点が1つだけあります。フロントエンドがレンダーコマンドで1つのバッファを埋めている間、バックエンドはもう1つを消費しています。両方のスレッドのバランスが取れていると、枯渇することはありません。このアプローチは最適ではありませんが、レンダリングされたフレームにレイテンシが導入されるため、OpenGLドライバーは独自のプロセスで既にこれを行っている可能性が高いため、パフォーマンスの向上を慎重に測定する必要があります。また、せいぜい2つのコアしか使用しません。このアプローチは、Doom 3Quake 3などの成功したゲームでも使用されています。

マルチコアCPUをより有効に活用する、よりスケーラブルなアプローチは、独立したタスクに基づくもので、セカンダリスレッドで処理される非同期リクエストを起動し、リクエストを起動したスレッドは他の作業を続行します。ロックを回避するために、タスクは他のスレッドとの依存関係がないことが理想的です(ペストのような共有/グローバルデータも避けてください!)。タスクベースのアーキテクチャは、アニメーションの計算、AIパスファインディング、手続き型の生成、シーンプロップの動的ロードなど、ゲームのローカライズされた部分でより使いやすくなっています。ゲームは自然にイベントフルであり、ほとんどの種類のイベントは非同期であるため、別のスレッドで実行するのは簡単です。

最後に、私は読むことをお勧めします:


ちょっと興味があるんだけど!Doom 3がこのアプローチを使用したことをどのようにして知っていますか?開発者はテクニックを決して出さないと思いました!
Allahjane

4
@ Allahjane、Doom 3はオープンソースです。私が提供したリンクには、ゲームの全体的なアーキテクチャのレビューがあります。間違いです。そうです、完全にオープンソースのゲームを見つけることはまれですが、開発者は通常、ブログや論文、GDCなどのイベントでそのテクニックやトリックを公開しています。
2015年

1
うーん。これはすごい読み物でした!これを知ってくれてありがとう!ダニを取得します:)
Allahjane
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.