Halo 2 HD版: オリジナルXboxの限界に挑むリマスタープロジェクト
(icode4.coffee)- オリジナルXbox向け Halo 2 HDパッチ は、実行ファイルの改変、コンソールのハードウェア改造、ベンチマークツールの作成を組み合わせ、480pを超えて720pや1080pレンダリングを試みたプロジェクト
- 従来のHalo 2は480p表記とは異なり、内部的には 640×480のバックバッファ に描画した後、GPUが720×480へアップスケールしていたため、HD対応にはD3Dバッファとビデオモード処理の両方を変更する必要があった
- 720p動作は標準の64MB RAMでは不足し、128MB RAMアップグレード とカーネルのホットパッチが必要で、GPU向け連続物理メモリを上位64MBからも割り当てられるよう制限を回避した
- パフォーマンスは、トリプルバッファリング、texaccumレンダーターゲットのタイル化、GPU 233.33MHz→300MHzオーバークロックなどで改善され、Zanzibarベンチマークシーンは約19FPSから27〜28FPS程度まで向上した
- 追加RAMはテクスチャ・ジオメトリキャッシュ拡張やHDD転送速度改善にも活用され、最終パッチは720pを実用レベルにした一方、1080pは主にスクリーンショット向けのボーナスに近い
プロジェクト目標とハードウェア前提
- 目標は、オリジナルXbox版 Halo 2 にHD解像度対応を追加し、改造したコンソールハードウェアがどこまで耐えられるかを確認することだった
- 作業範囲には、ゲームパッチ、Xboxコンソールのハードウェア改造、性能ベンチマーク用のカスタムツール作成が含まれた
- プロジェクトのベースとなった改造Xboxは「god box」と呼ばれ、次の変更を含んでいた
- 標準の733MHz Pentium 3 CPUを、カスタムインターポーザ基板で1.4GHz Pentium 3派生CPUに交換
- CPUを約2GHzまでオーバークロック可能
- 追加RAMとSSDを使用
- ハードウェア改造を支えるカスタムカーネルまたはBIOSイメージを使用
- Halo 2の最大対応ビデオ解像度は480pで、720pと可能であれば1080i対応を追加することが目標だった
- 解像度を上げるとピクセルシェーダの計算量が増えてGPU負荷が高まるため、GPUオーバークロックが可能でなければ取り組む価値は低いと判断した
- その後GPUを約15%オーバークロック可能になり、「GENESIS-3」コンソールが開発用に用意された
Halo 2の480pと内部レンダリング構造
- Halo 2はパッケージ表記では480p対応だが、D3D present parameterに
D3DPRESENTFLAG_PROGRESSIVEが設定されておらず、画面サイズスケールも常に1.0fだった - 内部の
screen_boundsはビデオモードに関係なく 640×480 に設定されていた- オリジナルXboxでは480pは720×480として扱われる
- Halo 2は640×480バックバッファにレンダリングした後、GPUが720×480へアップスケールしてビデオエンコーダに送っていた
- ワイドスクリーンモードでは1.33:1のアナモフィックカメラを使い、同じ640×480サーフェスにより広い画面を圧縮してレンダリングしていた
- この方式は、TVのstretchモードで横方向の圧縮を相殺するための仕組みだった可能性がある
- パッチにはアナモフィックスケーリングを無効化するオプションも追加された
HDレンダリングのためのD3Dパッチ
- 解像度対応のため、3つの関数が主要な修正対象になった
_rasterizer_detect_video_mode: 720pでもprogressive scanが有効になるよう変更_rasterizer_init_screen_bounds: ビデオモードに応じて640×480、720×480、1280×720、1920×1080のサイズを設定rasterizer_device_initialize: D3Dバックバッファとpresent flagを設定
- 1080iモードでは画面幅が1920かを確認した上で
D3DPRESENTFLAG_PROGRESSIVEを外し、D3DPRESENTFLAG_INTERLACEDを設定した - 初期変更後、メインメニューで青いフィルタが消え、繰り返し縞模様や水面ジオメトリの切れが発生した
- 一部の問題は、バック/フロント/深度バッファに対する 640×480固定のビュー が原因だった
- 同じメモリを異なる幅で見るテクスチャ/サーフェスビューが生まれ、scan line配置がずれていた
D3Dメモリとレンダーターゲット再構成
- オリジナルXboxはCPUとGPUが同じRAMを使う 統合メモリアーキテクチャ を採用していた
- PCのようにVRAM上にD3D割り当てを作り、GPUが管理する構造ではない
- CPUがテクスチャ、レンダーターゲット、vertex bufferなどのメモリを作成し、GPUへ直接アドレスを渡せる
- Halo 2は約25個のレンダーターゲットを使うが、実際の固有バッファ割り当ては4〜5個しかなかった
- 複数のレンダーターゲットが同じメモリを別リソースビューとして共有し、メモリを節約していた
rasterizer_primary_targets_initializeは、D3Dが作成したback/front/depth bufferから追加レンダーターゲットとテクスチャビューを作っていたが、640×480サイズがハードコードされていた- パッチではこの関数をフックで包み、元の関数実行後にテクスチャ/サーフェスサイズを現在のバックバッファ解像度に合わせて修正した
- タイル化メモリのpitchは一般的な
width * bppと異なる場合があるため、D3D_CalcTilePitchで計算した - 誤ったpitchは特に1080iで縞模様の原因になり得る
- タイル化メモリのpitchは一般的な
texaccumレンダーターゲットのサイズ修正
- メインメニューでの水面ジオメトリ切れは、texaccum レンダーターゲットが640×480固定だったために起きていた
- XboxのDirectX実装では、ピクセルシェーダ1パスあたり4つのテクスチャサンプリングしか許されていなかった
- 4個を超える入力テクスチャが必要なオブジェクトは複数パスでレンダリングする必要がある
- texaccumレイヤーはディテールテクスチャを先に合成し、その後lightmapパスの入力として使われる
rasterizer_targets_initializeはtexaccumレンダーターゲットを640×480で割り当てていた_rasterizer_alloc_and_create_render_targetをフックで包み、target_index == 1のtexaccumターゲットの幅と高さを現在のバックバッファサイズへ変更した- この変更後、水面ジオメトリの切れは消え、マップ読み込み時の目立つレンダリング問題もなくなった
- 青いフィルタの問題は単純なサイズチェック更新で解決したが、詳細な手順は省略されている
720p動作を阻んだメモリ限界
- 720pに設定するとゲームは起動時にクラッシュし、原因は拡大したfront/back/depth bufferとrasterizer targetによるメモリ不足だった
- オリジナルXboxには、一般向けの64MB RAMモデルと、開発用の128MB RAM dev kit/debug consoleが存在した
- リテール基板にも追加RAMチップ用の実装スペース自体はあった
- RAMチップをはんだ付けし、改造カーネルを使えば追加64MBにアクセスできた
- 720p以上での動作には 128MB RAMアップグレード が必要だった
- 通常の480pですらRAMアップグレードなしで動かすには、ゲームのインメモリテクスチャキャッシュからメモリを奪う必要があり、その場合はテクスチャのpop-inが増えた
Halo 2メモリマネージャのパッチ
- Halo 2は起動時、利用可能な64MBのうち約 48.9MB を1つの大きなランタイムデータ領域として確保していた
- この領域は、レベルメタデータ、テクスチャ、ジオメトリ、アニメーション、サウンドキャッシュ、rasterizer target、ネットワーク/シミュレーション資源などに分割して使われる
- メモリ使用状況の可視化のために XboxImageGrabber を作成した
- ページテーブルエントリを走査し、RAM使用状態をビットマップとして可視化する
- ランタイムデータ領域は 0x80061000 というハードコードされたアドレスに割り当てられていた
- mapファイルのtag dataがこの基準アドレスで直列化されているため、このデータは常に同じアドレスに存在する必要がある
- それ以外のランタイムデータは移動可能だった
- 移動対象にした領域は、rasterizer target、texture cache、geometry cacheだった
- 特定の割り当て呼び出しをフックしてdebug memory regionへ移した
- 新しいレベル読み込みなどの解放タイミングには適切なfree呼び出しも追加した
- ランタイムデータ領域自体のサイズも縮小して無駄を減らした
physical_memory_mallocはコンパイラによってインライン展開されていたため、各call siteごとに個別パッチが必要だった
Xboxカーネルのホットパッチと上位64MB物理メモリ
- GPUに渡すメモリアドレスは物理アドレスでなければならず、そのメモリ範囲は連続している必要があった
- GPUにはページテーブルや仮想アドレス変換の概念がない
- 128MB RAMカーネルでも、既定では物理連続割り当ては最初の64MBでしかできず、仮想割り当てだけが128MB全体で可能だった
- 手動で上位64MBのページテーブルエントリを使い、GPUメモリのように利用するテストは成功した
- 上位64MBをGPU向け物理メモリとして使えない制約は、ハードウェアではなくカーネル側のソフトウェア制限だった
MmAllocateContiguousMemoryExの内部にはMAX_USABLE_PFNチェックがあった- 既存値は 0x83FE0000 で、64MB - 128KB に相当する
- 上位128KBはGPUスクラッチ領域64KBとCPUページテーブル64KBとして予約されていた
- パッチはゲーム起動時にコンソールへ128MB RAMがあるか確認した後、
MmAllocateContiguousMemoryEx内のmov edx, 0x3FDFを見つけて、128MB構成に合う新しい値へ書き換えた - その後
MmAllocateContiguousMemoryExとMmFreeContiguousMemoryを使い、128MB全域から連続物理メモリを割り当て・解放できるようになった - 副作用もあった
- ゲーム終了後にcold rebootせずdashboardへ戻る、あるいはDVD tray ejectなどのwarm rebootを行うと、次のアプリ/ゲームで深刻なグラフィックアーティファクトやクラッシュが発生し得た
- これを隠すため、ゲーム終了時にcold rebootを強制する追加パッチを入れた
720p/1080pレンダリング結果と性能ボトルネック
- 720pレンダリングは見た目こそ向上したが、重いシーンではFPSが10未満に落ちるほど性能は低かった
- 1080pはネイティブレンダリング自体は可能だが、Xboxコンソール出力は1080i信号しか扱えない
- D3Dバックバッファを直接ダンプすると、GPUがビデオエンコーダ向けhalf frameへ変換する前の1080pスクリーンショットを取得できた
- 性能測定では、標準Xbox、CPUのみオーバークロックしたgod box、CPU+GPUをオーバークロックしたgod boxの3構成を比較した
- Zanzibarの重い地点を「zanzibar benchmark scene」として使用した
- 初期測定では3構成のFPSはほぼ同じで、性能グラフから swap stall が原因と分かった
- Halo 2はvsync onとdouble bufferingを使用していた
- GPUがvblank待ちでswap chainを回転できず、stallする状況が発生していた
トリプルバッファリングとGPUボトルネックの確認
- 解決策としてback buffer countを2へ増やし、front buffer 1枚とback buffer 2枚、計3枚のバッファを使う トリプルバッファリング を適用した
D3DPRESENT_PARAMETERSでBackBufferCount = 2、D3DSWAPEFFECT_DISCARD、D3DPRESENT_INTERVAL_ONEを設定した- Halo 2のレンダリングエンジンはdouble buffering前提でback/front bufferポインタを毎フレーム交換していたため、swap hookとprimary target initialize hookも修正する必要があった
- 2つのprimary render surfaceとtexture viewが常に現在のback bufferを参照するよう変更した
- ゲームが内部的に2つのポインタをswapしても同じメモリを指すため、実質的にはno-opになる
- Zanzibarベンチマークシーンでは、標準GPUでもFPSは約22FPSになった
- 従来より約3FPS向上
- 30FPS上限基準で約10%増
- swap stallが消え、GPU使用率が最大になったことで、ボトルネックがGPUであると確認できた
GPUとRAMのオーバークロック
- texaccumレンダーターゲットをタイル化メモリに変えて、さらに1〜2FPSを得た
- Zanzibarベンチマークシーンは約19FPSから23〜24FPSまで改善した
- god boxのGPUオーバークロック時、Zanzibarベンチマークシーンは 27〜28FPS を記録した
- マップを移動している間は概ね30FPSを維持し、一部の重い領域でのみ低下した
- BIOS再フラッシュを避けるため、GPU clock generatorのmemory-mapped IO registerをゲーム起動時に直接調整した
- GPUクロック計算は
NVPLL_COEFFのM、N、P値と16.6667MHzのbase clockに基づいていた- 既定のN値28は233.33MHzのGPUクロックを作る
- N値を調整することで約8MHz刻みで設定可能にし、iniファイルで構成できるようにした
- 300MHz GPUオーバークロックは、標準GPU比でZanzibarベンチマークに約3FPSを上乗せした
- GPUごとのオーバークロック限界はチップによって異なった
- 1.0〜1.4 revisionコンソールのGPUは300MHz前半で限界が見えることが多かった
- 1.6 revisionコンソールのGPUは400MHz超でも安定動作した例があった
- RAMクロックもテストした
- Xboxメモリバスの理論最大帯域は6.4GB/s、実用値は約70%の4.5GB/sとして扱われた
- RAMは標準で200MHz近辺のため、約10MHz上げるだけでも不安定化し得る
- 約208MHz設定のテストでは0.7FPS向上が観測された
- 250MHz対応RAMチップも注文したが、記事執筆時点では取り付けと追加テストは行っていなかった
pop-in軽減とキャッシュ拡張
- Halo 2は元々テクスチャとジオメトリの pop-in 問題があり、2000年代初頭の機械式HDDを使うコンソールではより目立っていた
- 追加RAMを活用してtexture cacheとgeometry cacheを拡張した
- 標準geometry cacheはシングルプレイマップで6.5MB、マルチプレイマップで7MB
- texture cacheはmapサイズに応じて変動し、tag dataの後ろとlow detail texture cacheの前にある残り領域を使う
- キャッシュはLRU方式で動作する
- 30フレームごとに、直近30フレームで使われなかったデータを削除する
- キャッシュが満杯なら呼び出し側が強制evictionを指定するか、ロード要求が失敗して次フレームで再試行される
- テクスチャはlow/medium/high LODバッファを持てる
- high LODロードに失敗するとmediumまたはlow LODを試す
- 低いLODが先に表示され、その後高いLODへ切り替わることでpop-inが発生し得る
- マップには2×2から最大8×8サイズのemergency low detail texture cacheも含まれている
- 通常のテクスチャ読み込みに失敗してもモデルを一時的に画面へ描くために使われる
- Xbox Liveのフレンドメニューを開いて閉じると地形が極低解像度テクスチャで見える現象は、このキャッシュ使用に関連している
- Bungieのdebug buildにあるグラフ可視化機能を再構成し、キャッシュ使用量を直接見ながらサイズを調整した
- 最終設定ではgeometry cacheを20MB、texture cacheを固定30MBまで拡大した
- 両キャッシュとも標準設定のほぼ2倍規模になった
- Outskirts冒頭カットシーンではMaster Chiefが即座に高解像度テクスチャで表示され、余裕キャッシュも残った
- 残るpop-in軽減のため、HDD転送速度も引き上げた
- 標準のUDMA 2は約33.3MB/s
- UDMA 3は約44.4MB/s
- 80ピンIDEケーブルがあればUDMA 5で約100MB/sまで設定可能
- 標準IDEケーブルでは約10%、アップグレードIDEケーブルでは理論上最大300%の転送速度向上を提供する
- 最終的な720pメモリプロファイルでは、128MB RAMの75%以上を活用した
- 1080pモードではswap chainとrasterizer targetのメモリ使用量が大きすぎてキャッシュサイズを縮小する必要があり、実質的に128MBの大半を使っていた
結果物
- 全体として720pパッチはプレイ可能な水準まで改善され、1080p対応は主にスクリーンショット向けボーナスに近い
- なお性能とメモリ変更にはまだ改善の余地が残るが、Halo 2とXboxコンソールの限界をかなり押し広げた成果といえる
- Halo 2 HDパッチのダウンロードとソースコードは GitHub で公開されている
1件のコメント
Hacker News のコメント
記事末尾の動画リンク: https://www.youtube.com/watch?v=O_nk21389u8
動画には、オリジナルをアップスケールした480pと720pの横並び比較が含まれており、7分あたりから、720pを得つつ約30fpsのゲームプレイを維持するには何が必要かを説明している
記事も素晴らしいが、より高い解像度のために必要な変更点を要約する動画も良い
720×480が16:9解像度ではないとか「本物の480p」ではないという話は、実のところ1970年代のITU標準化方面に文句を言うべきものだ: https://tech.ebu.ch/docs/techreview/trev_304-rec601_wood.pdf
1980年2月のメモでは、欧州標準の有効走査線期間をすべて収めるには、有効ラインあたりのサンプル数が715.5より大きくなければならないとされ、その後 Rec. 601 と SMPTE 125 で使われた720サンプルが「機能する」最初の値として定着した
Rec. 601 は輝度チャンネルに有効ラインあたり720サンプル、色差信号にはそれぞれ360サンプルを提供し、HDTVを定義する際には、この既存TVシステムの水平解像度を2倍にして16:9のアスペクト比を適用した結果、1920サンプル/ラインと1080ラインへとつながった
1280×720のプログレッシブ走査システムも同じ720ピクセル系に属しており、ほとんどのデジタルTV・DVD・MPEGベースのシステムは、この4:2:2の基本標準形式から派生している
「ハッカー精神」そのものはまったく正当だが、コンソールにメモリを追加し、GPUをオーバークロックする手間をかけてまで、PC版ではなくHalo 2をこうしてプレイする理由が別にあるのか気になる
欠陥をさまざまなレベルで詳しく扱っている動画も多い
https://youtu.be/03K2Uz3s1hg?si=zaFO1XdzMcFvI1F6
十分に可能だ、あるいは場合によってはより優れているのだと証明する感覚がある
おそらく心理的な障害に近いものかもしれないが、概して軽くて楽しい部類で、人気デバイスの最新モデルを誰よりも早く買おうとする執着の反対側にあるもののように思う
どちらも一種の優越感を与えてくれる
今でも Halo 2 はオンラインロビーやキャンペーンでプレイでき、343のオリジナルグラフィックかリマスターHDグラフィックも選べる
本文で「タグデータシステムは可能な限り柔軟かつ高速に設計されており、その内部動作はエンジニアリング的に見事だ。Blamエンジンを最も柔軟なエンジンの一つにしていると思う理由だけでも記事を一本書けるが、この記事とは関係ない」とあったが、その記事が実際に書かれたらぜひ読みたい
初心者すぎてどう動いているのか追うのも難しかったし、あまりに動的で、そういう構造が可能であること自体を根本的に理解できなかった
プレイヤーの位置が設定を持つ武器と同じ文脈にあり、エフェクトまで同じリストの中にあるというのが、どうして可能なのか理解できなかった
今関心を持っている多くのことも、結局こうした概念の周辺をずっと巡り続けている
XboxとHalo 2モッディングの時代が、現代にもう一度戻ってきてほしい。
あの時期は進路選択に大きな影響を与えたし、今でもHalo 2は史上もっとも革新的なオンラインゲームだと信じている。
ゲームセーブを読み込む簡単なツールを買うだけで、数分でソフトモッド済みXboxを作れたが、今のコンソールはe-fuseを焼いてダウングレードを防ぎ、セキュリティ強化もはるかに多くなっている。
InsigniaがHalo 2対応を始めるプロジェクトとも重なって、クラシックなHalo 2には本当に良い時期だ。
今ソフトウェアエンジニアになっている理由の一つは、Webページがどう動くのかを簡単に覗けて、プログラムのメモリをいじり、ハードウェアを開けて内部を見られたからだ。
Halo PCの時代にレベルをモッディングしながら、ゲームの中に何が入っているのかを多く学んだし、「BSP」が何かを知っていることは役に立たない豆知識かもしれないが、もっと多くを理解できるという自信を与えてくれた。
今でも技術の世界に入り、学ぶことはできるが、本当の意味でのいじり回しを通じてそうなる道は見えにくい。
ソフトウェアはクラックやデバッグがはるかに難しくなり、不可能ではないにせよ参入障壁はずっと高くなった。
Webページも今なお覗くことはできるが、最近の多くのサイトは縮小・難読化されたJavaScriptの怪物を支えるためのdivの山になっており、ゲームはサーバーからコンテンツをストリーミングする依存が大きくなって、そもそも難しい場合も多い。
ハードウェアでも、機器を文鎮化しかねないセキュリティ手順、接着されたベゼルを開けるためのヒートガン、恒久的な損傷のリスクを受け入れなければならない。
こうした変化にはそれぞれ理由があったのだろうが、その過程で楽しさも大半が消え、今日のツールが技術的にはより優れていても、ゲームモッディングは昔とは違う。
今でもPCやSteam DeckのようなPCベースのコンソールなら十分にいじれるのに、わざわざロックされるよう設計された独自コンソールと戦う理由が何なのか分からない。
市場で安く買えるカスタムx86ハードウェアへのアクセス権を得ること以外に、何が得られるのかも曖昧だ。
強力なモッディングコミュニティを持つ対戦オンラインFPSは、PCではすでに10年以上前から盛んだった。
Project Cartographerのおかげでアクティブなコミュニティがある: https://halo2.online/home/
この人は、最近のBungieが自社IPに注いでいる以上にHalo 2へ献身的なようだ。
IPはMicrosoftが所有しており、開発は343 Industriesが担当している。
Bungieは今、Destiny/Destiny 2と、まもなく出るMarathonのエクストラクションシューターを抱えている。
あのゲームには本当に多くの時間を注いだし、自分のようにあの魔法に魅了された人がほかにもいてうれしい。
それでも、このレベルの献身は尊敬に値する。
過度に冷笑的に聞こえるかもしれないが、プロジェクト自体は本当に素晴らしい。
ただ、CPUを交換してオーバークロックし、RAMを増やし、ソリッドステートストレージを追加し、GPUまでオーバークロックした状態を、初代Xboxの限界まで押し上げていると言えるのかは疑問だ。
その時点では、実質的にXboxではないように思える。
Honda Civicにエンジンやサスペンションの改造を山ほど施して限界まで攻めると言ったとき、「それでも本当にCivicなのか?」と問うのは技術的には正しいかもしれないが、揚げ足取りのように聞こえることもある。
書き手には望むタイトルを付ける権利があり、頭の中で「OG Xboxを死ぬ寸前までモッディングする」くらいに読み替えればよい。
Halo 2は360ではより高い解像度で動かせるので、初代Xboxを対象にしたモッディングプロジェクトだと明確にするのは有用だ。
改造していないXboxを文字どおり指しているのではなく、モッディング対象が初代Xboxだったという意味だ。
標準IDEケーブルのコンソールでは転送速度が10%増え、アップグレードされたIDEケーブルを使うと理論上は最大300%まで増える可能性があるとも書かれている。
128MB RAMがあれば、追加RAMを活用して720pと1080iのビデオモードを有効にし、テクスチャとジオメトリのメモリキャッシュを増やしてポップインをほぼなくせる。
480pだけでよければ、純正コンソールでGPUをオーバークロックし、別のIDEケーブルを使えばよい。
SSDに関する表現は意図せず誤解を招いたようで、おそらく80ピンケーブルとSSDの組み合わせを指していたのかもしれない。
オーバークロックされたCPUは不要で、CPUがボトルネックでもなく、増設RAMは720p以上を望む場合にだけ必要だ。
このフィードバックは伝えたので、ブログ記事がこの部分をより明確に更新するかもしれない。
レトロコンピュータ界隈がそうだし、自動車のチューニングも似ている。
ある人は工場出荷状態の完璧なCorvetteを求め、別の人は50年分新しいエンジンを載せたModel Aを求める。
慣れ親しんだ既知のものを自分の意図どおりにひねるのは、格好いいことだ。
作業量がとてつもなく多い
友人の「doom」が自分の正体を明かしたがらなかった点も興味深い