パート1:はじめにパート2:マルチスレッドパート3:レンダリング(注ごと-翻訳中)
パート4:Doomクラシック-統合(注ごと-翻訳中)
Doom IIIのエンジンは、ほとんどのPCがシングルプロセッサであった2000年から2004年の間に書かれました。 idTech4エンジンアーキテクチャはSMPサポートを念頭に置いて設計されましたが、最終的にマルチスレッドサポートを行うことになりました(
John Carmackのインタビューを参照 )。
それ以来、多くの変更が行われました。Microsoftの「
マルチコアシステム用プログラミング 」という優れた記事があります。
長年にわたって、プロセッサのパフォーマンスは着実に向上してきており、ゲームや他のプログラムは、労力を必要とせずにこの電力の増加から恩恵を受けています。
ルールが変更されました。 シングルコアプロセッサのパフォーマンスは現在、非常にゆっくりと成長しています。 ただし、パソコンとコンソールの計算能力は成長し続けています。 唯一の違いは、マルチコアプロセッサの存在により、基本的にこのような増加が得られることです。
プロセッサのパワーの増加は以前と同様に印象的ですが、開発者はこのパワーの可能性を完全に引き出すためにマルチスレッドコードを記述する必要があります。
ターゲットプラットフォームとDoom III BFGはマルチコアです。
- Xbox 360には、キセノントライコアプロセッサが1つ搭載されています。 プラットフォームの同時マルチスレッド化は6つの論理コアです。
- PS3には、PowerPCプロセッサと8つの相乗的コア(SPE)に基づくメインユニット(PPE)があります。
- 多くの場合、PCにはクアッドコアプロセッサが搭載されています。 ハイパースレッディングにより、このプラットフォームは8つの論理コアを取得します。
その結果、idTech4はマルチスレッドのサポートだけでなく、idTech5のJob Processing Systemでも強化され、マルチコアシステムのサポートが追加されました。
注:少し前まで、Xbox OneとPS4の仕様が発表されました。どちらも8つのコアを備えています。 ゲーム開発者がマルチスレッドプログラミングに長けているもう1つの理由。
Doom 3 BFGスレッドモデル

PCでは、ゲームは3つのスレッドで実行されます。
- バックエンドインターフェイスレンダリングストリーム(GPUコマンドの送信)
- ゲームロジックとフロントエンドインターフェイスレンダリングのフロー
- 高周波ジョイスティックからのデータ入力ストリーム(250Hz)
さらに、idTech4はさらに2つのワークフローを作成します。 これらは、3つのメインストリームのいずれかを支援するために必要です。 それらは可能な限りスケジューラーによって管理されます。
主なアイデア
Id Softwareは、2009年のマルチコアプログラミングソリューションを
Beyond Programming Shadersのプレゼンテーションで発表しました。 ここでの2つの主なアイデア:
- 異なるスレッドで処理するための個別のタスク処理(「ワーカー」による「ジョブ」)
- 同期をオペレーティングシステムに委任しないでください:アトミック操作のために自分で同期を行います
システムコンポーネント
システムは3つのコンポーネントで構成されています。
タスクはまさにあなたが期待するものです:
struct job_t { void (* function )(void *);
注:コードのコメントによると、「タスクは、切り替えコストを上回るために少なくとも
1000サイクル続く必要があります。 一方、複数のプロセス間で良好な負荷バランスを維持するために、タスクは
数100,000サイクルを超えてはなりません。
ハンドラーは、シグナルを待機している間、非アクティブのままになるスレッドです。 アクティブ化されると、タスクを見つけようとします。 ハンドラーは、アトミック操作を使用して同期を回避し、一般リストからジョブを取得しようとします。
同期は、シグナル、ミューテックス、アトミック操作の3つのプリミティブを介して実行されます。 後者は、エンジンがCPUのフォーカスを維持できるようにするため、推奨されます。 それらの実装
については、このページの下部で詳しく説明
します 。
建築

サブシステムの頭脳はParalleleJobManagerです。 彼は、スレッドハンドラーの生成と、タスクを格納するキューの作成を担当しています。
そして、同期をバイパスする最初のアイデア:ジョブリストをいくつかのセクションに分割し、各セクションに1つのスレッドのみがアクセスするため、同期は不要です。 エンジンでは、このようなキューはidParallelJobListと呼ばれます。
Doom III BFGには3つのセクションしかありません。
- フロントエンドAをレンダリング
- バックエンドAをレンダリングする
- 公益事業
PCでは、起動時に2つのワークフローが作成されますが、おそらくXBox360とPS3でさらに多くのワークフローが作成されます。
2009年のプレゼンテーションによると、idTech5にはさらにセクションが追加されました。
- 欠陥検出
- アニメーション処理
- 障害物回避
- テクスチャ処理
- 粒子透過処理
- 組織シミュレーション
- 水面シミュレーション
- 詳細なモデル生成
注:プレゼンテーションでは、1フレーム遅延の概念についても言及していますが、コードのこの部分はDoom III BFGには適用されません。
タスク配布

実行中のハンドラーは常にジョブを待っています。 このプロセスでは、ミューテックスやモニターを使用する必要はありません。アトミックインクリメントは重複することなくタスクを分散します。
使用する
タスクは1つのスレッドのみがアクセスできるセクションに分割されるため、同期は必要ありません。 ただし、システムハンドラにタスクを提供することは、ミューテックスを意味します。
方法

- タスクの追加 :同期の必要なし、タスクはキューに追加されます
- ディスパッチ :ミューテックス同期。各ハンドラーは、ローカルのジョブリストから一般的なジョブリストを補充します。
- 同期信号(OS委任) :
ハンドラーの実行方法

ハンドラーは無限ループで実行されます。 各反復で、リングバッファがチェックされ、タスクが見つかった場合、ローカルスタックへの参照によってコピーされます。
ローカルスタック:スレッドスタックは、メカニズムが停止しないようにJobListsアドレスを格納するために使用されます。 スレッドがJobListを「ブロック」できない場合、RUN_STALLEDモードになります。 この停止は、ローカルJobListから一般リストにスタックを移動することによりキャンセルできます。
興味深いのは、すべてが相互メカニズムなしで行われることです:アトミック操作のみです。
無限ループ int idJobThread::Run() { threadJobListState_t threadJobListState[MAX_JOBLISTS]; while ( !IsTerminating() ) { int currentJobList = 0;
就職 int idParallelJobList::RunJobs( unsigned int threadNum, threadJobListState_t & state, bool singleJob ) {
IDソフトウェア同期ツール
Id Softwareは、3種類の同期メカニズムを使用します。1.モニター(idSysSignal):信号は、フローを停止するために使用されます。 ハンドラーは、ジョブが欠落している場合、idSysSignal.Wait()を使用してオペレーティングシステムスケジューラーから自身を削除します。
2.ミューテックス(idSysMutex):3.アトミック操作(idSysInterlockedInteger):