CPU 負荷が低い 新しい 3D API

昨年の AMD Mantle を皮切りに DirectX 12 が発表され、
つい先日 Apple から Metal も登場しました。
DirectX 11 以降停滞かつ安定していた状態から一変、
新しい GPU 向け API への流れが加速しつつあります。
どれも DirectX11, OpenGL とは互換性がない全く新しい API セットです。

これまでと趣が大きく異なっているのは CPU のための刷新だということ。
新しい描画機能への対応はなく GPU へのハードウエア追加も特に求められていません。
目的は CPU 負荷の軽減です。

API           Platform   Beta SDK   GPUs
-------------------------------------------------------------
Mantle        Windows    2014/5     RADEON GCN
Direct3D 12   Windows    ?          GCN,Fermi,Kepler,Maxwell
Metal         iOS 8      2014/6     PowerVR G6430

それだけ CPU の負荷の高さが問題になっていたことになります。

これまでは共通 API の互換性の代償として厚いドライバのレイヤーが存在し、
さらに性能向上により CPU が転送するコマンドの規模も大きくなりました。
Multi Core CPU が当たり前となっているにもかかわらず、
旧態依然とした OpenGL はスレッドによる最適化を阻んでいます。

ドライバのオーバーヘッドは、ゲーム専用機 (Game Console) と汎用機
PC/Smartphone の大きな違いの一つとなっていました。

●シンプルに

固定機能は段階的に減っていき、3D 描画に必要なアルゴリズムのほとんどが
アプリケーション側のソフトウエアで実装されるようになりました。
汎用化が進んで GPU の用途が広がると、既存の高度な機能やドライバの手厚い保護が
かえってプログラミングの自由を妨げることがあります。

GPU はもともと進化が早く、機能も性能も使われ方も短期間で変化してきました。
Desktop から Mobile に移っても同様です。
高度なレベルで統合された API は大きな変化についていくのが困難になります。
何でもやってくれる命令は便利ですが、ある程度使われ方が決まっていないと
仕様を決められないからです。
変更が頻繁に行われるほど設計はよりシンプルになり、依存を減らす方向に進みます。

Direct3D 10 では Shader の統合やリソースの Buffer 化といった改革が行われました。
実際に使ってみるとリソース管理は思ったより簡単ではない事に気が付きます。
Resource View には細かなフラグ設定が必要で、
慣れるまでは組み合わせの制限に悩まされました。
本当に自由に感じたのは Direct3D 11 の ComputeShader からです。
用途もデータの仕様もすべてプログラマが決められます。

Texture Atlas は複数のテクスチャを巨大なテクスチャに統合します。
中の配置をプログラマが自分で管理しなければなりませんが、
その代わりシェーダーは uv だけで好きなテクスチャを読み出すことが可能です。
もし仮に GPU のメモリ全部を巨大な 1 枚のテクスチャとみなすことができたら
uv はポインタとほとんど同じものになるでしょう。
現状リソース管理はプログラマに開放されていませんが、
Texture Atlas は制限を超えるための手法の一つとみなすことが出来ます。

OpenGL の Shader 命令は DirectX よりもかなり後にデザインされたものなので、
Uniform の配置やシェーダー同士のバインドも自動化されています。
OpenGL 3.x 以降はメモリ配置をプログラマが決められるようになり、
4.x 以降はシンボルのバインドも単純な番号指定に置き換わりつつあります。
Direct3D の低レベル命令に近づいています。

GPU は汎用性を増していますが、互換性を維持した積み重ねは
必要以上に複雑になってしまうことがあります。
新しい API では、CPU 負荷の低減と同時に
よりシンプルで使いやすい API への回帰も期待できます。

● Command Buffer

描画時に CPU が行っているのは Command Buffer の構築です。
いわば GPU (Command Processor) が実行するプログラムそのもので、
アプリケーションは毎フレーム動的にプログラムを生成していることになります。

ドライバはできるだけ GPU の性能を引き出すように作られているので、
無駄な Command を省くなどの最適化が行われます。
ハードウエア毎に構造が異なるので、GPU Native な形式への変換も必要になります。
GPU Command の生成と発行はそれなりにコストがかかります。

ステートの値が必要になるのは Draw のタイミングなので、
Command の生成は描画命令まで遅延します。
Draw 命令に負荷が集中して見えるのはそのためです。

各種 Buffer 化は Command 負荷軽減手段の一つとなります。
パイプラインステートだと描画のたびに Command Buffer に書き込まれますが、
Buffer なら予め転送しておいたリソースをアサインするだけで済むからです。

●ゲーム専用機 (Console)

ゲーム専用機では PC と事情が大きく異なります。

 ・互換性の枷がない
  ・ハードウエア互換性が不要 (ハードは単一)
  ・ソフトウエア互換性を持たない (SDK の上位互換性を持たない)
 ・必要に応じてより低レベルな最適化手段が用意されている
 ・ハード内部の情報がある程度公開されている

ゲーム専用機は数年サイクルでリフレッシュされ、互換性の確保には
専用ハードウエアまたはソフトウエアエミュレーションが用いられます。
そのためソフトウエア (SDK) 互換性があまり重要ではありません。

最初から GPU Native な形式を使用できることもあり、
CPU のオーバーヘッドも PC と比べるとかなり低くなっています。

必要に応じてより低レベルな最適化が可能なことも専用機の特徴です。

最近は少ないと思いますが、例えば GPU Command を直接操作できるなら
事前にモデルデータを GPU Native な Command 形式に変換しておくことが出来ます。
メモリに Buffer Data と Command Buffer をロードするだけで描画が可能となります。
動的な Command 生成と比べて CPU 負荷はほとんど生じません。
(ただしいくつかのトレードオフが発生するので必ずしも最善とはいえない)

ハードの内部構造がある程度公開されている点もプログラマの負担を減らしてくれます。
描画アルゴリズムの設計時に内部の仕組みがわかっていれば、
どの方法が効率が良いのかある程度判断できるからです。
あまり迷わなく済みます。

●互換性とこれから

・ゲーム専用機との差が小さくなる
・API の分裂

新しい API は今の CPU/GPU 性能と使われ方に合わせた再設計が行われます。
全体的な動作効率があがり、CPU オーバーヘッドの低減などパフォーマンス特性は
よりゲーム専用機に近づいていくものと考えられます。
その反面、現状ではプラットフォームごとに仕様が分断されており、
互換性においては新たな課題が残ります。

OpenGL ES 2.0 はモバイルからブラウザまでプラットフォームの枠を超えて
用いられており、統一された API として大きな意味を持っていました。
今後同じように OpenGL ES 3.0/3.1 が広く用いられるようになるのかといえば
必ずしもそうではないようです。
特に iOS の場合は Metal 対応デバイスと一致しているため、
性能や機能のために OpenGL ES 3.0 を選ぶメリットが無くなりました。

                                      ES2.0  ES3.0  Metal
----------------------------------------------------------
Apple A5/A6    PowerVR SGX543/554       Y      -      -
Apple A7       PowreVR G6430            Y      Y      Y

Android の ES 3.0 や Desktop の OpenGL 4.x と互換性を保つためには必要ですが、
性能や使いやすさを優先するなら Metal が選ばれる可能性が高まります。
用途に応じた使い分けが行われるでしょう。

とはいえ各種プラットフォームへの個別対応は大変です。
OpenGL なら Low Overhead Profile や Multi thread Extension のような、
プラットフォームを超えた新しい仕様が登場することを期待します。

関連ページ
3D Low overhead API