最近のグラフィックボードは、高性能なモデルになるほど購入費用が大きくなる。
最新のグラボが欲しくても、簡単には買い替えられない。
そこで、ふと考えた。
「古いグラボを2枚組み合わせて、1枚の高性能グラボのように使うことはできないのか?」
例えば、手元に余っているグラボや中古で安く購入したグラボを2枚用意する。
それらをソフトウェアで制御して、ゲームの描画処理を分担させる。
ゲーム自体がマルチGPUに対応していなくても、外部ソフトウェアが自動的に処理を振り分けてくれれば、古いグラボでも性能を引き出せるのではないだろうか。
今回は、この構想について、既存技術と実現可能性を調べながら考えてみた。
以前は、複数のグラボを組み合わせてゲームの描画性能を高める技術が存在した。
代表的なのが、NVIDIAのSLIとAMDのCrossFireだ。
SLI(NVIDIA)
対応するNVIDIA製グラボを複数接続し、ゲームの描画処理を分担する仕組み。
CrossFire(AMD)
対応するAMD製グラボを組み合わせて、ゲームの描画処理を分散させる仕組み。
ただし、これらの技術には問題があった。
複数のグラボを搭載しても、すべてのゲームで性能が向上するわけではない。
ゲームやドライバーの対応状況によっては、2枚搭載しても性能がほとんど変わらないことがある。
さらに、フレームの表示間隔が不均一になったり、GPU間の同期が必要になったりする問題もある。
現在のPCゲームでは、従来型のSLIやCrossFireを利用して性能を向上させる構成は一般的ではなくなっている。
では、ゲーム側が対応していなくても、外部ソフトウェアで複数のグラボを制御できないだろうか?
今回の構想を考えるきっかけになったのが、Steamで販売されている「Lossless Scaling」だ。
Steamで販売されている、ゲームのアップスケーリングやフレーム生成に対応したソフトウェア。
ゲーム本体に専用のフレーム生成機能が搭載されていなくても利用できる。
Steam公式ページ
対応環境や最新の機能・価格はこちらで確認できる。
Lossless Scaling をSteamで見る
通常、ゲームでフレーム生成を利用するには、ゲーム側がDLSSやFSRなどの機能に対応している必要がある場合が多い。
しかし、Lossless Scalingはゲームの描画結果を外部から取得し、フレームを補間することで表示上のFPSを増やせる。
例えば、ゲーム本体が60FPSで動作している場合、フレーム生成によって120FPS相当の映像を表示することができる。
さらに、Lossless Scalingには、2枚目のGPUを利用してフレーム生成を行う機能もある。
つまり、ゲーム本体の描画をメインGPUに担当させ、サブGPUにフレーム生成を任せることができる。
GPU 1
ゲーム本体を描画
60FPSでゲーム映像を生成
GPU 2
フレーム生成を担当
GPU 1が生成した映像を受け取り、中間フレームを生成
モニターに120FPS相当で表示
ただし、これはゲーム本体の描画処理を2枚のGPUで分担しているわけではない。
あくまでゲームが出力した映像を補間する仕組みだ。
表示FPSは増えても、ゲーム内の処理速度や操作に対する応答性が同じ割合で向上するわけではない。
今回実現したいのは、フレーム生成をさらに発展させ、ゲーム本体の描画処理そのものを複数のGPUで分担する仕組みだ。
考えているのは、次のような仕組みだ。
ゲーム本体
マルチGPU非対応の一般的なPCゲーム
独自マルチGPU制御ソフト
ゲームの描画命令を取得し、GPUの処理能力に応じて描画処理を分配する。
GPU 1
古いグラボA
描画処理の一部を担当
GPU 2
古いグラボB
残りの描画処理を担当
描画結果を合成して表示
例えば、単体では30FPS程度しか出せない古いグラボを2枚使い、60FPSに近い性能を目指す。
もちろん、実際には2枚の性能を単純に合計できるわけではない。
それでも、ゲーム側の対応に依存せずに複数GPUを利用できれば、中古グラボを再利用する新しい選択肢になるかもしれない。
考えられる方式は、大きく2つある。
GPU 1
フレーム1
フレーム3
フレーム5
フレーム7
GPU 2
フレーム2
フレーム4
フレーム6
フレーム8
1 → 2 → 3 → 4 → 5 → 6 → 7 → 8
完成したフレームを順番に表示
AFRは、GPUごとに異なるフレームを担当する方法だ。
例えば、GPU 1が奇数フレーム、GPU 2が偶数フレームを描画する。
各GPUが独立して描画できれば、単体GPUより多くのフレームを生成できる可能性がある。
しかし、最近のゲームでは、前のフレームの描画結果を次のフレームでも利用することがある。
そのため、GPU間でデータを共有したり、描画完了を待ったりする処理が必要になる。
描画が完了するタイミングによっては、フレームの表示間隔が不均一になり、FPSの数値ほど滑らかに感じられないこともある。
1枚のフレームを分割し、2枚のGPUで同時に描画するイメージ。
SFRは、1つのフレームを複数の領域に分割して描画する方法だ。
GPU 1が画面の左半分を描画し、GPU 2が右半分を描画する。
最後に2枚のGPUの描画結果を合成し、1枚の映像として表示する。
ただし、画面を半分に分割しても、GPUの処理負荷が均等に半分になるとは限らない。
また、影や反射、画面全体に適用するエフェクトなどは、単純に画面を分割するだけでは正しく処理できないことがある。
こうした問題を解決するには、ゲームの描画内容を理解して処理を振り分ける仕組みが必要になる。
実は、DirectX 12には「Explicit Multiadapter」という複数GPUを利用するための仕組みがある。
これを利用すれば、アプリケーション側から複数のGPUを認識し、それぞれに処理を割り当てることができる。
異なるメーカーのGPUを組み合わせる構成も、APIの仕組みとしては想定されている。
例えば、NVIDIA製のグラボとAMD製のグラボ、あるいはIntelのCPU内蔵GPUを組み合わせるといった構成だ。
しかし、ここには大きな問題がある。
DirectX 12が複数GPUに対応していても、ゲーム自体が自動的に複数GPUで高速化されるわけではない。
複数GPUを利用するには、描画命令やGPU間のデータ転送、同期などをアプリケーション側で適切に制御する必要がある。
今回考えているソフトウェアは、マルチGPU非対応ゲームに対して、その制御を外部から実現することを目指すものだ。
参考:Microsoft Learn:Direct3D 12のマルチエンジン
例えば、VRAMが8GBのグラボを2枚用意したとする。
単純に考えれば、合計16GBのVRAMが利用できそうに思える。
しかし、実際にはそう簡単ではない。
GPU 1
GPU 2
8GB + 8GB = 自由に使える16GB、ではない。
両GPUが同じテクスチャやモデルを必要とする場合、同じデータをそれぞれのVRAMに保持する必要がある。
さらに、GPU間で描画結果を転送するときには、PCIeの転送速度も重要になる。
例えば、1920×1080、32bitカラーの映像は、非圧縮で1フレーム約8.3MBになる。
これを1秒間に120フレーム転送すると、映像データだけでも約1GB/秒の転送量になる。
これは最終的な映像データのみの計算であり、実際のマルチGPU描画では、そのほかにもテクスチャや描画用のデータ、同期処理などが必要になる場合がある。
古いグラボ2枚を組み合わせても、GPU間のデータ転送に時間がかかりすぎれば、単体GPUより性能が低下する可能性もある。
現在のPC構成は以下のとおり。
| パーツ | 構成 |
|---|---|
| CPU | Intel Core i9-12900K |
| マザーボード | ASUS ROG STRIX Z690-F GAMING WIFI |
| CPU内蔵GPU | Intel UHD Graphics 770 |
| OS | Windows |
| メインGPU | 使用中のグラボ |
| サブGPU | 余っているグラボを利用予定 |
i9-12900KにはIntel UHD Graphics 770が搭載されているため、現在のグラボとCPU内蔵GPUを併用する実験も考えられる。
ただし、内蔵GPUは処理性能が限られるため、ゲーム本体の高負荷な描画を分担する用途では、かえって処理速度が低下する可能性がある。
また、マザーボードの2枚目のGPU用スロットは、PCIe 3.0 x16形状・最大x4動作となっている。
複数GPU間で頻繁にデータを転送する場合、この帯域が制約になる可能性も考慮する必要がある。
さらに、現在は10GbEの増設LANカードも使用しているため、実際にサブGPUを搭載する場合は、PCIeスロットの空き状況や電源容量も確認しなければならない。
今回の構想を実現するために、次のような段階的な開発を考えている。
STEP 1
まずはLossless Scalingのように、メインGPUでゲームを描画し、サブGPUでフレーム生成を行う構成を試す。
GPU間の映像転送や同期処理にどの程度の負荷がかかるのかを確認する。
STEP 2
自作の簡単な描画プログラムを用意し、2枚のGPUに描画処理を分担させる。
AFRやSFRを実装して、単体GPUとの処理速度の違いを確認する。
STEP 3
特定のオフラインゲームを対象に、描画命令を取得して別GPUに振り分ける仕組みを研究する。
ゲームの描画処理には多数の依存関係があるため、汎用的な自動分散が実現できるかは、この段階で検証する必要がある。
STEP 4
複数GPUで描画できる仕組みを実装できた場合、GPUの性能や描画時間を測定し、それぞれの負荷を調整する。
性能の異なるグラボでも、処理能力に応じて分担できる仕組みを目指す。
なお、既存ゲームの描画命令に介入する仕組みは、アンチチート機能と競合する可能性がある。
そのため、初期の開発・検証は、自作プログラムやオフライン環境で行うことを想定している。
今回の検討で分かったのは、複数GPUを使う技術自体は存在するものの、マルチGPU非対応のゲームを外部ソフトウェアだけで高速化することは、簡単ではないということだ。
Lossless Scalingのように、ゲームの描画とフレーム生成を分担する仕組みであれば、現在でも実現できる。
しかし、ゲーム本体の描画を2枚のGPUで分担するには、ゲームが利用する描画APIやGPU間のデータ共有、フレームの同期など、さまざまな問題を解決する必要がある。
特に、どんなゲームでも簡単に2枚のGPUで高速化できる汎用ソフトウェアを開発するとなると、難易度は非常に高い。
それでも、特定のゲームや描画方式に限定すれば、技術的に検証できる余地はある。
「高価な最新グラボを1枚買うのではなく、安い中古グラボを2枚組み合わせて、ソフトウェアの力で性能を引き出す」
そんな仕組みが実現できれば、使われなくなった古いグラボにも、新しい活用方法が生まれるかもしれない。
まずは手元の余っているグラボを使い、フレーム生成の分担から実験してみたい。
今後、実際にマルチGPUの描画制御ソフトウェアを開発することがあれば、その過程も記事にしていこうと思う。