プログラミング
FPGA上でVoodooグラフィックスと1990年代後半のゲーミングPCを再現する
Recreating Voodoo Graphics and a Late-1990s Gaming PC on an FPGA (nand2mario.github.io)
要約
筆者はFPGA上で3dfx Voodooグラフィックスチップ(SST-1)をSystemVerilogで実装し、z486 CPUと組み合わせることで、1990年代後半のDOSゲーミングPC環境「z486 XL」を構築しました。これにより、FPGA上で「Tomb Raider」などのVoodoo対応ゲームをオリジナルのレンダラーで動作させることが可能になりました。このプロジェクトは、当時のPCゲーミング体験を現代のハードウェアで再現し、その技術的側面を探求するものです。
全文翻訳
私はこの1ヶ月、z486_MiSTerの機能追加とパフォーマンス向上に費やし、主に1990年代前半のゲームを扱ってきました。数年先を見据えると、もう一つ探求したい変化がありました。それは3Dグラフィックスカードの登場です。私に強い印象を残した最初のカードはVoodooでした。ゲームはNeed for Speed II SEでした。滑らかなテクスチャ、フォグ、そして全体のスピード感が、PCゲーミングの新世代を感じさせました。z486 CPUが存在する今、FPGA上でそれを再現できるでしょうか?この寄り道の成果がzSSTです。これは3dfx Voodoo Graphics、すなわちSST-1のSystemVerilog実装です。私のz486 CPUと周辺のPCハードウェアと組み合わせることで、z486 XLを形成します。これは、Xilinx KV260ボードのプログラマブルロジック上で動作するVoodooグラフィックス搭載のDOS PCです。「Tomb Raider」は、オリジナルの3dfxレンダラーで動作するようになりました。
シミュレーションでUtah teapotをレンダリングするzSST。
FPGA PC全体での「Tomb Raider」。
zSSTは、準備された三角形、テクスチャフィルタリングとミップマッピング、深度とアルファテスト、フォグ、ブレンディング、ディザリング、フレームバッファアクセス、バッファスワップといった、Voodooの主要機能のほとんどを実装しています。固定小数点と浮動小数点両方のセットアップインターフェースをサポートしています。ハードウェアでのゲームテストは、まだ「Tomb Raider」に集中しています。より広範な互換性や後のVoodoo世代は、また別の機会の課題です。CPUとレンダラーはKV260上で100 MHzで動作します。そのボードは、統合されたデザインのために十分なロジック、DSPブロック、オンチップメモリ、およびDDR帯域幅を備えています。DE10-Nanoには、このグラフィックス追加のための余裕はありません。KV260はオンボードDDRを使用しており、追加の外付けSDRAMモジュールはありません。
プログラミングモデルからの出発
幸いなことに、参考になる資料は豊富にあります。3dfxは1999年にGlideソースを公開しました。NVIDIAが2000年12月にコアグラフィックス資産を買収する前のことです。現存するGlideソースとSST-1仕様は、ソフトウェアがどのように三角形を準備し、ピクセルパイプラインを設定し、テクスチャとフレームバッファを管理するかを説明しています。この仕様は回路図ではなく、振る舞いのターゲットです。ソフトウェアがレジスタに書き込んだときに何が起こるかを伝えますが、実装の選択肢の多くは開かれています。86Boxは、複雑なレンダリング動作に関する有用な参照を提供します。以前のMAME Voodooの作業も、この保存の歴史の一部です。SpinalVoodooは、テストのために特に有用なGlideトレースと参照スクリーンショットを提供しました。
三角形から3Dへ、クロックあたり1ピクセル
Voodoo Graphicsは三角形をピクセルに変換し、多くの3D処理をホストCPUに任せます。そのコマンドインターフェースは驚くほどコンパクトです。5つの主要なコマンドレジスタがアクセラレータを駆動します。
コマンド
アクション
triangleCMD
準備された三角形のレンダリングを開始します。
ftriangleCMD
浮動小数点セットアップインターフェースを介して三角形を開始します。
nopCMD
パイプラインをフラッシュし、オプションで統計カウンタをリセットします。
fastfillCMD
色データおよび/または深度データをクリップされた長方形でクリアします。
swapbufferCMD
表示バッファを切り替えます。即時または垂直リトレースに同期させます。
どちらの三角形コマンドも同じレンダリングパイプラインを起動します。他のレジスタには座標、勾配、レンダリング状態が格納され、メモリマップド領域はテクスチャアップロードと直接フレームバッファアクセスを提供します。主な描画プリミティブは単なる準備された三角形です。ゲーム開発者にとって、Glideはより親しみやすいインターフェースを提供します。
void grDrawTriangle(const GrVertex *a, const GrVertex *b, const GrVertex *c);
この呼び出しの前に、ホストCPUは3Dジオメトリを変換し、頂点ライティングを計算し、クリッピングし、画面に投影します。次にGlideは、画面空間の三角形とそのパラメータ勾配(サーフェス全体で値を補間するために使用される増分)を準備し、レンダリングを開始するために三角形コマンドを書き込みます。GeForce 256のような後続のGPUとは異なり、SST-1にはハードウェア変換・ライティングエンジンがありません。それでも、アクセラレータには多くの作業が残されています。ラスタライザーは、三角形の内側にあるピクセル中心を見つけ、その色、深度、テクスチャ座標を補間します。テクスチャユニットはテクセルを取得してフィルタリングし、フレームバッファユニットは色を結合し、可視性テストとフォグを適用し、既存の画像とブレンドし、結果を書き込みます。1つのピクセルが完了するにはいくつかのステージが必要ですが、新しいピクセルは継続的に入力できます。オリジナルのカードは、この作業を2つのASIC、FBI(Frame Buffer Interface)とTREX(テクスチャマッピングユニット、通常TMUと呼ばれる)に分割しています。50 MHzのグラフィックスクロックで、公称ピーク性能は1クロックあたり1つのテクスチャ処理済み、深度テスト済み出力ピクセル、つまり毎秒5000万ピクセルです。1クロックあたり1ピクセルというのは、1ピクセルが1クロックで完了することを意味するわけではありません。異なるステージが異なるピクセルを同時に処理できるということです。あるピクセルがテクスチャ処理されている間に、前のピクセルがブレンディングされ、別のピクセルが出力される可能性があります。パイプラインが満杯になると、メモリが追いつく限り、理想的にはクロックあたり1ピクセルを受け入れて完了できます。これが固定機能パイプラインの魅力です。ソフトウェアレンダラーはピクセルごとに多くの命令を実行しますが、専用ハードウェアはピクセルの安定したストリーム全体でその作業をオーバーラップさせます。Voodooは、流れるような30 FPS以上でリッチにテクスチャリングされた3Dゲームに命を吹き込みました。これがその人気の大きな理由です。
ピクセルパイプラインの構築
x86 CPUと比較すると、算術パスは心地よく規則的です。ピクセルが補間されたパラメータからテクスチャリングと色操作を経てフレームバッファに至るまでの経路を、数値の表現方法から見ていきましょう。
浮動小数点インターフェースの背後にある固定小数点
浮動小数点演算は、現代のGPUプログラミングの中心です。SST-1は興味深い移行期に位置しています。ソフトウェアは浮動小数点値を送信できますが、レンダリング機構の大部分は固定小数点、つまり暗黙のスケールファクターを持つ整数で動作します。
セットアップ値
固定小数点レジスタ形式
スクリーンXおよびY
12.4
赤、緑、青、アルファ
12.12
深度Z
20.12
テクスチャS/WおよびT/W
14.18
逆数W
2.30
ここで、12.4はバイナリポイントの前に12ビット(符号を含む)、小数点以下4ビットを意味します。したがって、10.5のスクリーン座標は整数168として格納されます。16を掛けてエンコードし、16で割って値を復元します。これらの小数ビットにより、ラスタライザーはピクセル中心間の頂点を処理できます。fvertex、fstart、および浮動小数点勾配レジスタは、IEEE単精度値を格納します。SST-1はそれらを内部の固定小数点表現に変換し、zSSTはその契約に従います。三角形が準備されると、スキャンラインに沿って進むことは、主に各補間パラメータに事前に計算された増分を加えることを意味します。ピクセルごとの作業の多くは、単純な整数加算になります。
1ピクセルあたり4テクセル
パースペクティブ補正されたテクスチャリングのために、TMUはS/W、T/W、1/Wを補間し、次に最初の2つを3番目の値で割ってテクスチャ座標を復元します。これにより、サーフェスが後退するにつれて、床や壁のテクスチャがパースペクティブを保ちます。TMUはまた、ミップレベルを選択します。これは、サーフェスの広い領域をカバーするピクセルに対して、テクスチャの縮小版です。これにより、遠くでのエイリアシングやちらつきが軽減されます。次に、バイリニアフィルタリングは、サンプリング位置の周りの4つの隣接するテクセル(テクスチャのピクセル)を結合します。まず、上のペアを水平に、次に下のペアをフィルタリングし、最後にそれらの2つの結果の間で垂直にフィルタリングします。小数位置が重みを決定し、テクセル色間の突然のジャンプではなく、テクセル色間の滑らかな遷移を生み出します。4つのテクスチャ読み込みで1つのフィルタリングされたサンプルが生成されます。zSSTでは、4ステージのフロントエンドがパースペクティブ計算と詳細レベル(LOD)計算をパイプライン化します。アドレス生成とキャッシュルックアップがテクセルを供給し、2つのレジスタ化されたデコードステージがそれらの格納されたフォーマットをフィルタリングおよびテクスチャ結合用の色に変換します。パレットベースのテクスチャやNCCエンコードされたテクスチャは異なるデコードルールを必要としますが、最終的には同じピクセルストリームに供給されます。
色、テスト、フォグ、ブレンディング
テクスチャとフレームバッファデータが利用可能になると、zSSTのFBIピクセルパスは6つのレジスタ化されたステージを使用します。
ステージ
主な作業
F0
ソースを選択し、クロマキーをチェックし、Z/W深度値を準備します。
F1
色を適用します。