自作のレンダリングシステムにリソースをキャッシュする方法


10

バックグラウンド:

私は、C ++とOpenGLを使用して、エンティティコンポーネントシステムタイプアーキテクチャ用のシンプルな3Dレンダリングシステムを設計しています。システムは、レンダラーとシーングラフで構成されています。レンダラーの最初の反復を終えたら、シーングラフをECSアーキテクチャーに配布します。今のところ、それは何らかの方法でプレースホルダーです。可能であれば、レンダラーの目標は次のとおりです。

  1. シンプルさ。これは研究プロジェクト用であり、システムを簡単に変更および拡張できるようにしたい(したがって、ECSアプローチ)。
  2. パフォーマンス。私のシーンには、多くの小さなモデルと、多くのジオメトリを持つ大きなボリュームがあるかもしれません。OGLコンテキストからオブジェクトを取得し、レンダリングフレームごとにジオメトリをバッファリングすることはできません。キャッシュミスを回避するために、データの局所性を目指しています。
  3. 柔軟性。スプライト、モデル、ボリューム(ボクセル)をレンダリングできる必要があります。
  4. 分離。レンダラーを作成した後、シーングラフがコアECSアーキテクチャにリファクタリングされる場合があります。
  5. モジュラー。シーングラフを変更せずに、さまざまなレンダラーを入れ替えることができると便利です。
  6. 参照透明度、つまり、いつでも有効なシーンを指定できるため、そのシーンでは常に同じ画像がレンダリングされます。特にこの目標は必ずしも必要ではありません。シーンのシリアル化を簡略化し(シーンの保存と読み込みができるようにする必要があります)、テスト/実験の目的で実行時に異なるシーンを入れ替える柔軟性が得られると思いました。

問題とアイデア:

私はいくつかの異なるアプローチを考え出しましたが、各レンダリングノードのOGLリソース(VAO、VBO、シェーダーなど)をキャッシュする方法に苦労しています。以下は、これまでに考えてきたさまざまなキャッシングの概念です。

  1. 一元化されたキャッシュ。各シーンノードにはIDがあり、レンダラーにはIDをレンダリングノードにマップするキャッシュがあります。各レンダーノードには、ジオメトリに関連付けられたVAOとVBOが含まれています。キャッシュミスはリソースを取得し、ジオメトリをキャッシュ内のレンダーノードにマップします。ジオメトリが変更されると、ダーティフラグが設定されます。レンダラーがシーンノードを反復処理しているときにダーティジオメトリフラグを確認すると、レンダラーを使用してデータを再バッファーします。シーンノードが削除されると、イベントがブロードキャストされ、レンダラーは関連するレンダーノードをキャッシュから削除し、リソースを解放します。または、ノードに削除のマークが付けられており、レンダラーがノードを削除する責任があります。このアプローチは、4と5も考慮しながら、最も厳密に目標6を達成すると思います。2は、配列アクセスの代わりにマップルックアップを使用すると、複雑さが増し、データの局所性が失われます。
  2. 分散キャッシュ。上記と同様ですが、各シーンノードにはレンダーノードがあります。これにより、マップルックアップがバイパスされます。データの局所性に対処するために、レンダーノードをレンダラーに保存できます。その場合、シーンノードは代わりにレンダリングノードへのポインターを持つことができ、レンダラーはポインターをキャッシュミスに設定します。この種のエンティティコンポーネントアプローチを模倣しているので、アーキテクチャの他の部分と一貫性があると思います。ここでの問題は、シーンノードがレンダラー実装固有のデータを保持することです。レンダラでのレンダリング方法を変更する場合(スプライトとボリュームのレンダリングなど)、レンダーノードを変更するか、シーンノードに「コンポーネント」を追加する必要があります(つまり、シーングラフも変更します)。プラス面では、これは、最初の反復レンダラーを起動して実行する最も簡単な方法のようです。
  3. 分散メタデータ。レンダラーキャッシュメタデータコンポーネントは、各シーンノードに格納されます。このデータは実装固有ではなく、ID、タイプ、およびキャッシュに必要なその他の関連データを保持しています。次に、IDを使用して配列内で直接キャッシュルックアップを実行できます。タイプは、使用するレンダリングアプローチのタイプ(スプライトとボリュームなど)を示します。
  4. 訪問者+分散マッピング。レンダラーはビジターであり、シーンノードはビジターパターンの要素です。各シーンノードは、レンダラーのみが操作するキャッシュキー(メタデータのようにIDのみ)を保持します。IDは、一般化されたマップ検索の代わりに配列に使用できます。レンダラーを使用すると、シーンノードのタイプに基づいてシーンノードが異なるレンダリング関数をディスパッチでき、IDは任意のキャッシュで使用できます。デフォルトまたは範囲外のIDは、キャッシュミスを示します。

この問題をどのように解決しますか?それとも何か提案はありますか?私のテキストの壁を読んでくれてありがとう!


1
何か進歩はありましたか?
Andreas

これは非常に複雑な質問であり、おそらくいくつかの個別の質問に分割する必要があります。これは基本的に「エンジンをどのように設計すればよいですか?」私のアドバイスは、最初により単純なコンポーネントをサポートするものを設計し、その後、機能を追加してリファクタリングすることです。また、探している多くの情報を網羅している3Dゲームエンジンのデザインブックを参照してください。
Ian Young

回答:


2

質問をもう一度読んだ後、問題が複雑すぎていると強く感じます。理由は次のとおりです。

  1. レンダリングシステムには、実際には2つのタイプしかありません。フォワードとディファードで、シーングラフに依存するものはありません。

  2. レンダリングシステムで実際に発生する唯一のパフォーマンスの問題は、ポリゴン数が多く、シェーダーとクライアントコードが非効率であることによるものです。

  3. キャッシュミスは確かにパフォーマンスを低下させますが、それはあなたが思っているかもしれないモンスターではありません。パフォーマンス向上の80%は、より効率的なアルゴリズムによるものです。コードの事前最適化を間違えないでください。

それは言った:

自作のシーングラフを使用している場合は、シーングラフコードのレンダリング部分を設計するために、すでに「レンダラー」インターフェース(または基本クラス)を使用しているはずです。ダブルディスパッチを使用するビジターパターンは、色、テクスチャ、メッシュ、変換など、多くのタイプのグラフノードを使用している可能性があるため、これに対する優れたアプローチです。シーンツリー構造を深度優先でウォークし、自身を引数として渡します。このように、レンダラーは基本的にはシェーダーのコレクションであり、フレームバッファーの1つまたは2つです。この結果、レンダリングシステムでは検索/削除コードは不要になり、シーングラフ自体が必要になります。

あなたが直面している問題に取り組む他の方法は確かにありますが、私はあまりにも長い間、答えを出したくありません。ですから、私の最善のアドバイスは、まず単純な作業を行い、次にそれを拡張して弱点を見つけ、次に他のアプローチを試し、それらの長所/短所が実際にどこにあるかを確認することです。

その後、十分な情報に基づいて決定を下すことができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.