拙訳
グラフィクスのプログラミングモデルは複雑である(Programming Models for Graphics are Complex)
今日のトークでは、リアルタイムグラフィクスで用いるプログラミングモデルと、これらを改善できそうな方法に触れたい。会場の皆が容易に同意するであろう通りに、グラフィクスエンジニアリングのプログラミングモデルとして我々が現時点で持っているものはかなり複雑な猛獣である。我々はその領域で数々の特徴を持つ。
問題空間(Problem space)
プラットフォーム及びAPIの多様性(Platform and API divergence)
近年のグラフィクスプログラミングはとても断片化した複雑なプラットフォームやAPIのエコシステムの上に生きている。
リソース管理は必須である(Resource Management is Required)
グラフィクスに関連することを行うには何事にも、リソース(テクスチャ、シェーダ、メッシュ、アニメーション、など)をどうにかして得なければならない。これはしばしば大量のボイラープレートを意味し得る。
ヘテロジニアスな計算モデル(Heterogenous Computational Models)
その上、ヘテロジニアスなマシンへのプログラミングについて考える必要がある --- GPUとCPUは大幅に異なるプログラミングモデルを持ち、ゲーム及びグラフィクスエンジンのプログラミングはすべての構成要素を提供する必要がある。グラフィクスアルゴリズム研究のアプリケーションも同様。
高級なアルゴリズム空間は深遠な構成可能性を必要とする(Rich Algorithm Space Needs Deep Configurability)
リアルタイムグラフィクスで用いる高レベルパイプラインアルゴリズムは急増している --- フォワードレンダリング、Forward+、ディファードレンダリング、Clusteredアルゴリズム、Z-Binning、GPUオクルージョン管理、例えばfine-prune tiled lightingのような様々なTiledアプローチ、などなど。これらすべては特定のパイプライン目標に対する有用性を持つ。例えば、VRはシングルパスのフォワードレンダラから利益を得るが、多くのコンソールゲームはディファードレンダリングを採用している。これらすべてのアルゴリズムは、それをサポートする予定である場合、基礎にあるエンジンの深い構成可能性を必要とする。
コンテンツクリエイターとエンジニアリングの橋渡し(Bridge Content Creators and Engineering)
もちろん、ゲームエンジンやグラフィクスアプリケーションはしばしばコンテンツとエンジニアリングの橋渡しとして役立つ。
グラフィクスプログラマーの務めはコンテンツ(アート)とプラットフォーム(ハードウェア)の間のインターフェイスを定義、熟考することである(The task of a graphics programmer is to define and mediate the interface between content (art) and platform (HW))
[Foley 2016Foley, T. 2016. A Modern Language for Real-Time Graphics: What is Needed?. Open Problems in Real-Time Rendering course. ACM SIGGRAPH. https://openproblems.realtimerendering.com/s2016/index.html.]
コンテンツとプラットフォーム(Content and Platform)
このスライドの上部分には コンテンツ がある。これはアーティストが理解して作成しようとする概念のすべてである。我々には形状、動作、外観、照明なようなものがある。
このスライドの下部分には、我々が対象とする ハードウェアプラットフォーム がある。これは、スレッドやSIMDのサポートを持つCPUコアや伝統的なラスタライゼーションパイプラインとある種の汎用用途の計算の両方をサポートするGPUハードウェアがあり、近年似たようなものが見られる。
アーティストが生成するものを得るために、我々はコンテンツをハードウェアプラットフォームにマップする方法に対する計画を持つ必要がある。
アセット毎、プラットフォーム毎マッピングを特定する?(Specify mapping per-asset, per-platform?)
これを行い得る(悪い)方法のひとつはアセット毎、プラットフォーム毎の基準でコードを書くことによるものである。これは明らかにスケールしない。つまりは、我々が実際に行っていることではない。そして、それが我々をその定義の核心へと導く…
アプリケーション固有のインターフェイスを用いてマップする(Map using application-specified interface)
実践で実際に行うと主張することは以下のことである。
グラフィクスプログラマーは インターフェイス --- 特定のエンジンやプロダクションに特有な概念を定義する。
これ はアニメーションリグを表現するであろう方法である。
これ は反射関数を表現するであろう方法である。
このインターフェイスでの表現のいくつかは(例えば、メッシュでは)純粋なデータであるかもしれないし、その他は(例えば、アーティストが”noodle graph”のようなパターン生成を記述する場合に)コードを含むかもしれない。
いずれにしても、その務めは以下である。
- 選ばれたインターフェイスにおける概念に従うように、アーティストによって生成されるアセットを対応させる。そして、
- ひとつ以上の対象のプラットフォームにこれらの概念を効率的に対応させる。
ビジュアルフィードバックのために必要とされる頻繁なイテレーション(Frequent Iteration Needed for Visual Feedback)
そしてもちろん、テクニックに対してイテレーションの回数が増やせれば、一般には、それはより良くなる。故に、開発者が最短距離で成功 するために必要不可欠なものはこれら自身のイテレーションループである。
グラフィクスコミュニティとしての我々の目標(Our goal as graphics community)
究極的にグラフィクスプログラマーとしての我々の目標は何か。極簡単に言えば、グラフィクスプログラマーとしての我々の目標はグラフィクス技術を刷新することである。
グラフィクスを刷新するときに設計すること(When innovating graphics, we design:)
これを行うとき、我々は以下に焦点を当てる。
- 新しいハードウェア能力の設計(CPU/GPU/など)
- 新しいグラフィクス低レベルAPIの設計(例: DirectX 12、GCN、など)
- ヘテロジニアスな計算モデルの設計
- 新しいドメイン言語の設計(HLSL、Cg、など)
- 新しいアルゴリズムの設計(フォワード対ディファード、FPTL、Clustered、など)
- 新しいワークフローの設計(コンテンツ作成(プロシージャル植生配置、LODシステム、など)や開発ワークフロー(実際のアルゴリズムの開発))
- 新しいパフォーマンス技術の設計
本日は触れないトピック(Not covering these topics today:)
本日はグラフィクス/ゲームエンジンにおける開発のプログラミングモデルに焦点を当てているので、以下の進化や変化には触れないだろう。
- 新しいハードウェア能力
- 新しいグラフィクス低レベルAPI
- 新しい計算モデル
- 新しいドメイン言語
- おすすめの読み物:
- [Foley 2016Foley, T. 2016. A Modern Language for Real-Time Graphics: What is Needed?. Open Problems in Real-Time Rendering course. ACM SIGGRAPH. https://openproblems.realtimerendering.com/s2016/index.html.]
- [Ante 2017Ante, J. 2017. C# job system & compiler. Unite Europe. https://www.youtube.com/watch?v=AXUvnk7Jws4.]
- [Lauritzen 2017Lauritzen, A. 2017. Future Directions for Compute-for-Graphics. Open Problems in Real-Time Rendering course. ACM SIGGRAPH. https://openproblems.realtimerendering.com/s2017/index.html.]
Unityでは実際に、C#のジョブ化APIをサポートするためのC#からILへのコンパイラを開発したり、とプログラミングモデルの改善を活発に調査している。
本日の中心(Today’s Focus)
- 新しいアルゴリズム
- 新しいワークフロー
- 新しいパフォーマンス技術
本日は、グラフィクスエンジンにおいて新しい開発ワークフローを用いることによって新しいアルゴリズムのより良い開発を可能にするために我々ができることを述べたいと思う。言い換えれば、新しいアルゴリズムとレンダリングパイプラインによるグラフィクスゲームエンジンのプログラミングモデルの進化である。
目標: グラフィクス技術の容易な革新(Goals: Easy Innovation for Graphics Techniques)
言い換えれば、他の変数に制約を課しつつ、グラフィクスの 技術、可用性、パフォーマンス を刷新する方法である。
我々の制約とは?(What are our constraints?)
前に進むために、すべてがその制約の選択を定義して理解する必要がある。
なぜ進化には制約が重要なのか?(プログラマブルモデルか否か)(Why do constraints matter for evolution? (Programmable models or otherwise))
なぜプログラマブルモデルの進化には制約が重要であるのか?
制約の利点(Constraints Benefits)
- 軸を固定して、スタックの残りの進化に焦点を当てる
- 例: HLSL、コンピュート(ドメイン固有言語)
制約は軸を固定して、スタックの残りの進化に焦点を当てることができるようになる。低レベルAPIはいくつかの側面を制約し、その他のアクセシビリティに焦点を当てる。例: スケジューリング管理の利便性ではなく、Wavefrontスケジューリングのアクセシビリティに焦点を当てる計算モデル --- 制約はGPUがスケジューリングの厳密な詳細を管理することであり、GPUで実行しているカーネルを記述することに焦点を当てる。
ドメイン固有言語は制約から大幅に利益を得る --- 例: 一般的なC++プログラミング機能を欠くHLSL(ハードウェアを直接対象とすることでシェーダ開発において大きな革新を可能にする)。
- ソフトウェアアーキテクチャはしばしば同じ原則を用いる
- いくつかの側面に制約を課すことはその領域において高速なイノベーションを可能にする
RenderMonkeyは、頂点シェーダとピクセルシェーダを作成すること以外のプログラムについてのすべてのことに制約を課し、これらのプログラムを実行するのに必要なスキャフォールディング要素のすべてで動作するためのワークフローを大幅に単純化する、素晴らしい例である。
ShaderToy
(ピクセルシェーダのみで記述する)
[Tatarchuk 2015Tatarchuk, N. 2015. Destiny's Multithreaded Rendering Architecture. Game Developer Conference. https://gdcvault.com/play/1021926/Destiny-s-Multithreaded-Rendering.]では、保証されたスレッドセーフ単位を記述するために制約を課したAPIを提供する。
これはマルチスレッドなグラフィクス機能を記述するための高速なイテレーションを可能にした。
[O'Donnell 2017O'Donnell, Y. 2017. FrameGraph: Extensible Rendering Architecture in Frostbite. Game Developers Conference. https://www.gdcvault.com/play/1024612/FrameGraph-Extensible-Rendering-Architecture-in.]は、レンダパスを特定するための制約を課したAPIを提供して、リソース管理の可視化の簡単なイテレーションを可能にした。
このアーキテクチャはリソースエイリアシングによりメモリ節約を生み出し、非同期コンピュート実行の設計の助けになり、レンダリングパイプライン構成を単純化した。注意点として、これは依然としてC++でプログラムされた。
革新にはどんな原則が役立つか?(What principles help innovation?)
より良いイノベーションをサポートするフレームワークの属性とは?
- 再現性のある実行の挙動の容易な共有
再現性のある挙動の容易な共有はコミュニティ全体を通した学習を加速させる。もちろん、完全なコンテンツでエンドツーエンド実行できる実行ファイルが最善である。
- 広範囲にサポートするインフラストラクチャ/サービス
イノベーションに役立つフレームワークはユーザーがボイラープレートコードを回避できるソリッドなインフラストラクチャを提供すべき。グラフィクスでは、これはアセットやオブジェクトの管理の提供、スレッディング、低レベルプラットフォームのサポート、などを意味する。
- ゲームエンジンがそれを容易に提供する
- 完全なコンテンツを持つUnity個人版プロジェクトを共有する
- フレームワークは柔軟性を持つべき
- 重いインフラストラクチャレイヤは革新を妨げるべきではない
制約を枷とするべからず(Mustn’t Let Constraints Become Shackles)
- 何から何まで扱うことができないときは主に制約を大切にする
- 能力が成長するのに伴い、トレンドは常に一般化に向かう
- 開発しやすいため
制約それ自身に関して、問題が対処するには大きすぎるときにすべての制約は良いことに注意すべきである。システムの能力が成長するたびに、システムはこの量の柔軟性をサポートするので、より柔軟なモデルを作成するために常に一般化することにも注意する。
改善するのに日和見性の一般化を用いる --- パフォーマンスや能力が成熟するたび(Use Opportunistic Generalization to Improve - As performance or capabilities mature)
こんにちのリアルタイムグラフィクスエンジンプログラミング(Real-Time Graphics Engine Programming Today)
出発点として、こんにちのリアルタイムグラフィクスエンジンプログラミングでの最新技術について話す時間を取りたいと思う。
これは、アプリケーションコードを書き進めるとき、私の経験に大いに基づいている。
グラフィクスエンジンパイプライン(Graphics Engine Pipeline)
低レベルAPIレイヤー(Low-level API layers)
我々は一般に低レベルAPI抽象化を書くところから始める。これは、デバイス抽象化、プラットフォーム固有リソース抽象化、レンダサーフェス、レンダステート管理、シェーダ管理などが行われる低レベルレンダリングレイヤーである。この時点で、我々は頻繁にゲームタイプ、ビジュアルスタイル、アルゴリズム独立であるレベルで作業する。もちろん、特定の抽象化を実装する必要がないならば(例えば、非同期コンピュートを使う計画がなかったならば)、アプリケーション開発者はその場合にこの抽象化の実装をスキップしてもよい。
エンジンレイヤー抽象化(Engine-layer Abstractions)
次に、我々は一般にエンジンレベル抽象化のレイヤーで作業する。
その上により高いレベルのコードを書く。
- 個別のレンダラ
- スキン、静的、インスタンス、など
- カリング(可視性、ライトリスト管理、など)
- レンダリングジョブのジョブ化、および、ドローコールとGPUコマンドバッファ生成のスレッディング
- これはビューの処理を扱う所でもある。
- フェーズ管理(CPU/GPU同期、または、非同期コンピュートスケジューリング管理)
この時点で、我々は依然としてかなり深い所にいる。フレームレンダリングに関するどの固有のアルゴリズムも導入しなかったし、ひどくゲーム固有のものもない(最適化の理由のため2Dか3Dのどちらで行うかのパスの分岐は別として)。このレイヤーを正確に建築するならば、シングルフレームかステレオフレームのどちらをサブミットするかを考えることすら必要ない。
レンダパス抽象化(Render Passes Abstractions)
次に、より高レベルなレンダループ制御フローコードを書く。これはレンダパス、ポストプロセッシング、ライティングやシャドウアプリケーションのようなフルスクリーンパス、シャドウ生成テクニックを書く所であり、さまざまなビューをセットアップし、カメラ管理を行う所である。
しかし、これには調査の必要性が生じる --- この段階の複雑さはどんな例がある?レンダリングパイプラインパスを書くことの意味することとは?
手短な振り返り(A Quick Retrospective)
ソフトウェアラスタライザー(Software Rasterizers)
もちろん、グラフィクスはしばらく前にソフトウェアラスタライザーをを書くところから始まった。この時点では、低パフォーマンスだが完全な柔軟性を持っていた。すべてはCPUで実行し、表現したいことの完全な制御権を持った。これは単に遅かった。
固定機能T&L(Fixed-function T&L)
次に、我々はテクスチャリングやライティングを行う固定機能ハードウェアを得る所の並列化のあるレベルを導入した。いくつかの制御を得た(非常に固有のレンダリングメソッド)が、いくつかのパフォーマンスを取り戻した。しかし、その時ではゲームは同じように見え、リアルタイム領域での時間フレームの間にマテリアルモデルやライティング技術でのイノベーションはほとんどなかった。
プログラマブルシェーダ(Programmable Shaders)
その後、段々と制御を取り戻し始めた。まずはプログラマブルシェーダによって --- そして、出来上がるレンダリングのビジュアル実現可能性が進化し始めた。
ハードウェアでのベクタ化されたユニファイドシェーダパイプライン
大規模並列GPU(Massively parallel GPU)
そしてもちろん、スカラ実行とかなり良好なパフォーマンスを持つコンピュートベースの大規模並列GPUの昨今の進化では、マテリアルモデル、シャドウやライティング技術、画像クオリティ、その他多くのリアルタイムレンダリングの側面での大量のイノベーションを確認している。ビジュアル的に、今ではゲームやリアルタイムアプリケーションでの様々な見た目のスタイルがある。
しかし、これはレンダパスにどれだけ関係している?
グラフィクス機能の進化の振り返り(Graphics Feature Evolution Retrospective)
- 初期のゲーム: 固定機能の個別機能
- 利点
- 実装するのが素早い(== 安価)
- 高速
- 分析や評価がしやすい
- 欠点
- 制限された機能性とアート制御性
- 表現豊かではない
レンダリングパイプラインの複雑さは数桁増加した(The complexity of rendering pipelines has risen several orders of magnitude)
グラフィクスエンジンパイプライン(Graphics Engine Pipeline)
- ゲームロジックベースのレンダリング制御
- レンダパス抽象化
- エンジンレイヤー抽象化
- 低レベルAPIレイヤー
グラフィクスエンジンアーキテクチャの様々な部分のbig pictureを持つ今、特定のグラフィクスアルゴリズムを開発する間にこれらの上で反復することは何を意味するか?例えば、マテリアルシェーダやGバッファレイアウトとライティングにいくつかの変更が必要な新しい物理ベースマテリアルモデル表現を探したいとしたら?
グラフィクス開発のイテレーション(Graphics Development Iteration)
- エンジンレイヤー抽象化
- 低レベルAPIレイヤー ↑ ボイラーコード! ここはいじりたくない!
新しいGバッファレイアウトで反復しているときにやりたくないことはプラットフォームレイヤーやリソースローディングなどをいじらなければならないことである。そして、それはゲーム/グラフィクスエンジンフレームワークの外のほとんどのエンジニアがやらなければならないことである。しばしば、それはコピペであるが、依然として退屈なボイラープレートコードのレイヤーである。御免被る。
- ゲームロジックベースのレンダリング制御
- レンダパス抽象化 ↑ イテレーションは全部ここで! シェーダ、フルスクリーンパス、レンダパス、レンダバッファ、…
我々が本当に集中したいことはシェーダパスやレンダテクスチャレイアウトなどで反復することである。アルゴリズムの核心 --- すなわち、時間をかけたい所である。
では、ゲームグラフィクスエンジンでこれらの個別のレイヤーをプログラムすることが意味することを見ていこう。
現在のグラフィクスエンジン開発(Current Graphics Engines Development)
ほぼほぼ、我々がゲームエンジンレイヤーを書くときは、C++で作業する(PS3やSPUアセンブリについては忘れたフリをしましょう)。
シェーダDSLを使っても作業する。一般に、このレイヤーは主にこのスタックの高次の要素に触れる。
かなり珍しいことだが、シェーダ実装の依存関係がスタックの低レベル部分に浸透することがある。一般に、これはシェーダ機能性がハードウェア固有の機能に紐付いているときに起こる。この場合、エンジンの低レベル部分はより深い変更を必要とする。とはいえ、これらはグラフィクスアルゴリズム開発ではより突飛な方である。
C++で作業すると…(Working in C++ land…)
C++のイテレーションがどうなるかというと、我々は十分承知しているが、C++のビルド+リンクしてからである。エンジンが複雑さを増加させるにつれて、これはイテレーション時間を遅くすることを意味する。インクリメンタルビルドをもってしても、我々は依然として遅いリンク時間にとらわれている。
- 素早く頻繁なイテレーションには辛い
- より良くできるか?
発想の転換が必要(Need a Philosophy Shift)
グラフィクスエンジンプログラミングでのパラダイムシフト(Paradigm Shift for Graphics Engine Programming)
- 機能は小さなC++コードを持つ
- APIを公開する
- 固有部分をC#で実装する
これはエンジンの再コンパイルををする必要なしにすべてを修正、デバッグ、改善が可能なスクリプトによる記述の柔軟性をもたらす。
レンダリングフレームワークをどうしたいか?(What Do We Want Our Rendering Framework to Be?)
- Lean
- 最小の表面積
- テストしやすい
- 疎結合
- ユーザー中心
- ユーザープロジェクト空間で生きる
- デバッグしやすい
- ともに働くのがすばやく、やりやすい
- 拡張および修正しやすい
- 最適
- 非常に 高速に処理する
- 以下に最適
- 特定のプラットフォーム
- アプリケーションタイプ
- ユーザーが望む目標を達成するために必要な操作のみを行えるようにする
- 2Dパイプラインでするなら、それだけすればよく、3DやVR用に設計されたコードレイヤーをスキップすらしなくてよい
- 明示的
- それ以上でもそれ以下でもなく、伝えたことを確実に行う
- マジックなし
- クリーンAPI
なぜそれはUnityに重要か?(Why is that important to Unity?)
我々のコミュニティは多種多様のビジュアルスタイルやゲームタイプを作る
モダンエンジンアーキテクチャ(Modern engine architecture)
モダンなゲームエンジンは大きく構成可能であり、変化するスループットに動的に反応し、プラットフォーム制約についての賢明な選択をする必要がある。
新しいグラフィクステクニックを開発しやすく(Ease of new graphics technique development)
スクリプタブルレンダリングパイプライン(Scriptable render pipelines)
新しいグラフィクスプログラマブルモデル: SRP(New Graphics Programmable Model: SRP)
低レベルエンジンは未だに以前と同様にC++でやっている。
しかし、レンダパス制御のための機能性のいくつかとゲームベースのレンダリングをC#に移行した。
もちろん、シェーダプログラミングは未だに存在するが、シェーダは主にC#レイヤーと一致する必要がある。
SRPの高レベルコンセプト(SRP High-Level Concept)
- スクリプトから フィルタされた ドローコールを呼び出す
- シェーダは特定のレンダリングパイプラインの設計できる
- 可視オブジェクト/ライトなどのリストと組み合わせる
- レンダリングパイプラインの高レベルコードを大幅に単純化する
Footnotes
-
訳注:都合により省略 ↩