ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Macへのゲーム展開 Part 3:Metalでのレンダリング
ゲームをMacで移植するための3部シリーズの締めくくりとして、レンダリングコードでMetalをサポートする方法をご紹介します。Game Porting ToolKit で既存のWindowsバイナリを評価し、HLSLシェーダをMetalに移行したら、ハイエンドの最新ゲームが必要とする機能を最適に実装する方法を学びましょう。GPUリソースバインディング、レジデンシー、同期の管理方法を紹介します。GPUコマンドを最適化し、MetalFX Upscalingを使用してリッチなビジュアルをレンダリングする方法などをご紹介します。 このセッションを最大限に活用するには、まず WWDC23の"Bring your game to Mac, Part 1: Make a game plan"や"Bring your game to Mac, Part 2: Compile your shaders"をご覧ください。
関連する章
- 0:00 - Intro
- 1:58 - Manage GPU resources
- 9:08 - Optimize rendering commands
- 18:00 - Handle indirect rendering
- 22:41 - Upscale with MetalFX
- 25:31 - Wrap-Up
リソース
- Applying temporal antialiasing and upscaling using MetalFX
- Metal
- MetalFX
- Modern Rendering with Metal
関連ビデオ
Tech Talks
WWDC23
WWDC22
WWDC21
WWDC20
-
ダウンロード
♪ ♪
Georgi: こんにちは ようこそ! 私Georgi Rakidovは GPU Graphics Display Softwareの ソフトウェアエンジニアです 本セッションはゲームを Macに導入する際に役立つ 3部構成シリーズの第3部です 最初のセッションでは 新しいGame Porting Toolkitを使用して 修正されていないWindowsゲームを Mac上で実行し グラフィックスやオーディオ ディスプレイ 機能を評価する方法について説明しました 第2セッションでは新しい Metal Shader Converterツールで 既存のHLSLシェーダーを Metalにコンパイルすることにより 開発時間をどれだけ 短縮できるか紹介しました このセッションでは レンダラーをMetalに移植し Apple Siliconから優れたパフォーマンスを 引き出す方法について詳しく説明することで ゲームをMacに導入する プロセスを完成させます レンダラーをMetalに移植する際 エンジンがほかのプラットフォームの グラフィックスAPIの概念をMetalに マッピングする必要があると気づくでしょう 本セッションではApple GPUの強力な アーキテクチャを活用できるように Metalのベストプラクティスとともに 4つのトピックを取り上げます 各ゲームはテクスチャや データバッファを含むGPUリソースを GPUで利用可能にし シェーダーがそれらにアクセスする方法を 設定する責任を負います あなたのゲームはGPUへの コマンド送信方法を最適化することで Appleプロセッサの強力なグラフィックス アーキテクチャを活用できます ゲームでは通常 間接レンダリングを使って 最新のレンダリング技術を実装します MetalFXは低解像度に レンダリングしてから MetalFXで最終解像度に アップスケーリングすることで 各フレームのレンダリング時間を短縮します リソースの管理に関してはGPUが 各テクスチャやデータバッファなどに アクセスする方法を各エンジンが 決定しなければなりません Metalではシェーダーが リソースにアクセスできるように バインディングを提供し リソースをGPUが アクセス可能なメモリに常駐させ それらへのアクセスを同期させることを 考えることが重要です リソースのバインディングとシェーダーは 密接に関連しています 今年導入した新ツール Metal Shader Converterを使用して既存のシェーダーを 変換することから始めましょう これによりMetalへのシェーダーの 移植時間を大幅に節約することができます 詳しくはこのシリーズPart2の 「Compile your Shaders」を ご覧ください Metal Shader Converterでは2つの バインディングモデルから選択できます 「Automaticレイアウト」ではコンバータが バインディング情報を自動的に生成します または「Explicitレイアウト」で バインディング情報を Metal Shader Converterに渡せます Explicitレイアウトは非常に柔軟で ほかのプラットフォームの バインディングモデルを 実装する必要がある場合に役立ちます 例えば API 設計の中にはシェーダ ルートシグネチャを使用するものがあり 以下は4 つのエントリを持つ 典型的なものです 一連のテクスチャを指すdescriptorテーブル バッファルートパラメータ 32 ビット定数 および一連のサンプラを指す 別の記述子テーブルです 各 descriptorテーブルは すべてのテクスチャや すべてのサンプラ またはすべてのバッファなど 同じタイプの要素を含むリソース配列です Metalの引数バッファはエレメントが複数の タイプであることができる点でより柔軟です しかし エンジンが均質な 配列を期待するのであれば 引数バッファで簡単にエンコードできます この例では テクスチャ descriptorテーブルに 相当するものをエンコードしています これは各テクスチャの MetalリソースIDの格納により テクスチャdescriptorテーブルとして機能する Metalバッファを確保することで始まります 各テクスチャを作成すると コードはそのリソースIDを テーブルに直接格納します 素晴らしい点は このようなコードを レンダリングループの外側で 前もって実行できることです サンプラーdescriptorテーブルをエンコードする プロセスもほとんど同じです テクスチャと同じように コードはまず サンプラーdescriptorテーブルとなる Metalバッファを作成します コードが各サンプラーの descriptorを設定するときに supportArgumentBuffersプロパティを yesに設定します コードがdescriptorを使用して サンプラーを作成したあと サンプラーのリソースIDを テーブルに保存します 引数バッファを使用して トップレベルのルートシグネチャ自体を 表すこともできます この例では ルート署名の構造体を定義し そのインスタンスを 1つ格納できるMetalバッファを作成します このコードではテクスチャテーブルと サンプラーテーブルの GPUアドレスを含め 構造体の各フィールドに 適切な値を割り当てています ルートシグネチャの変換に 必要なのはこれだけです 引数バッファはMetal 3では 非常に効率的です トップレベルの引数バッファを シェーダにバインドするだけです この部分はレンダリングループ内で 行われますが descriptorテーブルと ルート構造はレンダリングループ外で 事前に作成できます Metal 3引数バッファは ルートシグネチャや 記述子テーブルなど ほかのバインディングモデルを 翻訳するための柔軟で 性能の高い方法を提供します リソースは シェーダがアクセスするために 指定されたパスまたは レンダリングステージの実行中に 常駐する必要があります また リソースがパス間で共有される場合は それらのパスの実行順序を 同期させる必要があります Metalの引数バッファで バインドレスリソースを使用するには すべてのGPUアーキテクチャで 明示的なレジデンシー管理が必要であり Metalはレジデンシーを制御する 効率的な方法を提供します すべての読み取り専用リソースを大きな ヒープにグループ化することを推奨します そうすれば useHeapを エンコーダごとに1回呼び出すだけで すべての読み取り専用リソースが そのパスまたは レンダリングステージの間に常駐し シェーダがアクセスできるようになります これがその方法です すべての読み取り専用リソースを 割り当てるために必要なサイズの ヒープを作成し このヒープから 各リソースを割り当てます そしてレンダリング時にuseHeapを呼び出し すべてのリソースを常駐させます 書き込み可能なリソースの場合は 少し異なります 書き込み可能なリソースを個別に割り当て 適切な使用フラグを指定して useResourceを呼び出すことを 検討してください この場合 Metalが同期を処理してくれて パフォーマンスを最適化します これによりMetalエンコーダー間で リソースを手動で同期させる 負担を避けることができます 以前と同様に リソースの割り当てから始めます 今回は ヒープにバックアップされていないものです 次に これらのリソースにアクセスする エンコーダに対してのみ 適切な使用フラグを指定して useResource を呼び出します この例では エンコーダはテクスチャに書き込み バッファから読み込んでいます この推奨事項を表にまとめました 読み取り専用と書き込み可能な リソースの両方が トップレベルの引数バッファから アクセスされます 理想的なケースでは エンコーダごとに1回だけ設定します 読み取り専用リソースは ヒープにグループ化され ハザード追跡モードは Untrackedに設定されます ヒープ内のすべての リソースを常駐させるには エンコーダーごとに1回 useHeapを呼び出します 書き込み可能なリソースは 個別に割り当てられ ハザード追跡と同期は Metalに任されます そして 各リソースの呼び出しに対して エンコーダーごとに useResourceを1回呼び出します これは効率的なアプローチです CPUオーバーヘッドが少ない バインドレスモデルを実装しており アプリケーションはハザードトラッキングや 同期といった多大な労力を必要とする 複雑なタスクや開発時間を 気にする必要がありません バインドレスやレジデンシーと 同期の詳細については セッション 「Go bindless with Metal 3」 を参照してください リソースバインディングとレジデンシーと 同期をコードに実装したら 画面上に何かをレンダリングするには エンジンがレンダラーに コマンドを送信する必要があります Apple Processorにはコマンド実行を 最適化するための多くの機能があります GPUはTile-Based Deferred Renderer またはTBDRで CPUとGPUが システムメモリを共有する ユニファイドメモリアーキテクチャを 採用しています また GPUにはタイルメモリと呼ばれる 高速なオンチップメモリがあります このアーキテクチャを活用するため Metalにはパスの概念があり レンダリングコマンドをパスにグループ化し それらのパスを適切に 設定することが目標となります TBDRアーキテクチャの詳細については 関連プレゼンテーションの 「Bring your Metal app to Apple Silicon Macs」および 「Harness AppleGPUs with Metal」を 参照してください ほかのAPIは様々なタイプのGPU コマンドを混合する連続的なストリームを 持つことができ あなたのエンジンは これを想定しているかもしれません コマンドをMetalに変換するには まずコマンドバッファを作成します 次にグラフィックスやコンピュートや ブリットといったコマンドの種類に応じて それらをパスにグループ化します 各パスのコマンドを コマンドエンコーダを使って コマンドバッファに書き込みます 最後に すべてのコマンドがエンコードされたら GPUによる実行のために コマンドバッファを コマンドキューに提出します エンジンはレンダリングコマンドを 効率的にMetalに変換するために 4つのベストプラクティスを 考慮することができます まず レンダリング開始前に コピーをバッチ処理すること 同じタイプのコマンドをグループ化すること レンダーターゲットをクリアするために空の エンコーダーを持たないようにすることです そして最後に MetalのLoadアクションと Storeアクションを最適化して メモリ帯域幅を最小限に抑えます これらのベストプラクティスは 例を使って簡単に説明できます 次のシーケンスを考えてみましょう レンダーターゲットのクリア 描画やコピー ディスパッチそして別の描画についてです 特に このシーケンスで 発生するシステムメモリと タイルメモリ間のすべての メモリトラフィックを見てください これは理想的ではありません ストリームの途中のコピーは 後続の描画 この場合は描画1のために 均一なデータをコピーします 可能であれば レンダリングパスの中断を避けるために レンダリング前にこれらのコピーを移動して バッチ化することをお勧めします 変更後はコピーが最初になり 次にクリア ドロー0 ディスパッチ ドロー1となります 2つの描画コールとディスパッチの間に 依存関係がない場合は 描画とディスパッチを一緒に バッチ処理できるように それらの順序を変更する必要があります この例では 描画とディスパッチの 呼び出しの順番を入れ替えると 連続して2つのレンダーパスが得られます このシナリオは同じ レンダーターゲットを共有する場合 それらを1つのレンダーパスに マージするのに最適で メモリ帯域幅を大幅に節約できます そうすればデータがタイルメモリから システムメモリに移動し 2つの描画の間を 往復する必要がなくなるため 不要なメモリトラフィックがなくなります すでに改善していますが さらに最適化できるでしょう clearは空のエンコーダーで 次の描画で使用されるレンダーターゲットを クリアするという1つの目的しかありません Metalでは これを行う 非常に効率的な方法があります レンダーターゲットを使用する 最初のレンダーパスに LoadActionClearを使用するだけです これははるかに良い方法ですが もう1つ推奨事項があります ロードアクションと ストアアクションも最適化できます 次のパスで使用する レンダーターゲットの内容を システムメモリに保存するだけでよいのです この例では 描画1の後 最初のレンダーターゲットだけが 使用されると仮定します ほかのすべてのレンダーターゲットは 中間的なものであり コンテンツを保存する必要はありません Metalでは各レンダーターゲットの ストアアクションを制御できます この場合 最初のレンダーターゲットには StoreActionStoreを使用し 他のレンダーターゲットには StoreActionDontCareを使用できます これで完了です! これが最初のコマンドシーケンスです タイルメモリと システムメモリの間を5往復します そしてこれがいくつかの簡単な最適化後の コマンドシーケンスです タイルメモリからシステムメモリへの 最後のフラッシュは1回だけです メモリ帯域幅は大幅に削減されています これはレンダリング前に コピーを移動すること 同じタイプのコマンドをグループ化すること 空のエンコーダーでレンダーターゲットを クリアするのを避けること ロードとストアの動作を 最適化することで達成されています GPUツールはこれらの問題を 特定する際に役立ちます XcodeのMetal Debuggerは 自動的に最適化の機会を見つけるため ゲームで最高のパフォーマンスを 得ることができます Metalパスの依存関係を検査し 理解することもできます また デバッグとプロファイリングのための フル機能のツールが付属しています Metalデバッガーを使って 言及された問題を特定するのは簡単です Metalワークロードをキャプチャすると Metal Debuggerは Summaryビューアを表示します 下部のインサイトセクションには 4つのカテゴリーに分類された― 最適化の機会が表示されます メモリや帯域幅とパフォーマンス そしてAPI使用状況です このワークロードでは2つの 帯域幅に関する洞察を強調していきます 1つ目は未使用リソースです インサイトを選択すると 右側のパネルに概要と それに対処するための アドバイスが表示されます GBufferパスが必要以上に アタッチメントを保存しています この場合 GBufferパスは アルベド/アルファテクスチャを ロードして保存します しかし アルベドテクスチャは本フレームの 後半では使用されないため保存は冗長です したがって保存アクションを DontCareに設定することで修正できます 次のInsightを確認しましょう レンダーパスを組み合わせることで 帯域幅を削減することができます ここではGBufferパスとForwardパスを 1つのパスに結合できることが 示唆されています 右側のReveal in Dependencies ボタンをクリックして Dependenciesビューアで このレンダーパスを見つけることで これらのパスが何を読み書きしているか さらに詳しく知ることもできます Dependencies viewerはパス間の 依存関係を検査するのに最適なツールです ここではレンダーアタッチメントの 上下に表示されている― ロードアクションとストアアクションを 一目で見ることができます このパスのすべてのアタッチメントには ストアアクションがストアされていますが カラー0と深度アタッチメントだけが 今後のパスで使用されます 前回の洞察で このことが明らかになりました 少しズームアウトすると GBufferパスからForwardパスへ流れる データエッジが表示されます インサイトが示したように GBufferパスとForwardパスは 同じアタッチメントから 保存と読み込みを行っているため 帯域幅を節約するためにマージできます この2つのパスをマージすることで帯域幅が 節約され パフォーマンスが向上します 以上Metal Debuggerを使用して ゲーム内で 最適化の機会を見つける方法の 一例を紹介しました Metal Debuggerの詳細については 関連セッションの 「Gain insights into your Metal app with Xcode 12 」と 「Discover Metal debugging, profiling, and asset creation tools」をご覧ください 間接レンダリングはハイエンドゲームが 高度なレンダリング技術を 実装するために使用する重要な機能です このトピックでは ExecuteIndirectの仕組みと この特定のコマンドを Metalに変換する方法を確認します 間接レンダリングでは 複数の描画コマンドを エンコードする代わりに それらの引数はメモリ上の 通常のバッファに格納され バッファを参照しながら一つの ExecuteIndirectコマンドを エンコードし GPUが バッファから各描画コールの 引数を取得して実行する 描画コールの数を指定します このアプローチの主なアイデアは ExecuteIndirectコマンドの 前に実行するようスケジュールされた コンピュートシェーダによって 間接バッファのコンテンツを 投入できるようにすることです この方法では GPUは自ら作業を準備し 何を描画するかを決定します 間接的な引数を持つコマンドの実行は GPU駆動レンダリングループなどの 高度な技法を実装するための重要な機能です このコマンドをMetalに変換するには Draw Indirectと Metal Indirect Command Buffers またはICBを使う2つの方法があります Metalでは レンダラーは 各ExecuteIndirectを DrawIndirectへの一連のAPI 呼び出しに変換する必要があります それぞれがバッファを参照し 描画引数のオフセットを提供します これがそのコードです このExecuteIndirect が持つ可能性のある 描画呼び出しの最大数を調べてください それぞれについて間接引数のバッファと そのバッファ内のオフセットを指定して 個別の描画をエンコードします 反復の最後に オフセットを次の間接引数の セットを指すように移動します このアプローチは実装が非常に簡単で ほとんどすべての状況で機能します しかし 何千もの 描画呼び出しがあるシーンがあり ゲームのパフォーマンスがCPU エンコード時間によって制限される場合は MetalのIndirect Command Buffersを 検討する必要があります ICBは間接的な描画引数を持つ バッファのスーパーセットです 描画引数に加え バッファバインディングを設定し GPUからPipeline State Objectsを レンダリングすることもできます ICBからのコマンドをGPU上で 実行するようにスケジュールするには executeCommandsInBufferコマンドを エンコードする必要があります 通常 ExecuteIndirectでは すべての描画コールが同じ Pipeline State Objectを共有します そしてPSOが変わるたびに 新しいExecuteIndirectコマンドを エンコードしなければなりません ICBを使用している場合は 間接実行コマンドをステート変更によって それほど頻繁に分割する必要はありません すべてのPSOとバッファバインディングは ICBから設定できるので エンコードする必要はありません シーンの構造によってはエンコード時間を 大幅に短縮できるかもしれません ICBを活用するために 間接引数を入力する― 既存のシェーダーを 変更する必要はありません ほかのプラットフォームと 同じシェーダーを共有して Metal Shader Converterで コンパイルし 間接引数生成後 間接レンダリングパスの前に 小さな計算カーネルを追加することで 描画引数をICBに変換できます ICBを計算カーネルにエンコードするには Metal Shading Languageで記述します シェーダへの入力として 変換したい間接引数へのポインタがあります 次に 引数が有効かどうかをチェックし そのとき初めてコマンドをエンコードします encodeCommand 関数で レンダーパイプラインの状態や バッファバインディング 描画呼び出しを設定します これにより描画引数が 間接コマンドバッファ内の レンダーコマンドに変換されます これが間接レンダリングを Metalに変換する方法です 一連の描画間接コマンドまたは Metal間接コマンドバッファを使用できます 間接レンダリングを使って 高度なレンダリング技術を 実装する方法を学びたい場合は 「Modern Rendering with Metal」の サンプルコードをご覧ください リソースをパイプラインにバインドし コマンドをコマンドバッファに適切に エンコードすることで ゲームが 正しい画像を生成できるようになったら アップスケーリングを活用して プレイヤーのデバイスから より多くのパフォーマンスが引き出せます MetalFXによるアップスケーリングは GPUの作業量を減らすことで 各フレームの処理時間を短縮します MetalFXはアップスケーリング パイプライン実装にすぐに使える方法です 低解像度の画像を目標の出力解像度まで スケーリングすることで 出力解像度で 直接レンダリングするよりも 短時間で動作します MetalFXは昨年Mac用に導入され 高性能なアップスケーリングを提供します MetalFXは2つのアップスケーリング アルゴリズムをサポートしています 最高のパフォーマンスを実現する 「Spatial」と 出力解像度のネイティブレンダリングに 近い品質を実現する「Temporal」です MetalFXをエンジンに統合することで より優れたパフォーマンスで より高い解像度でレンダリング可能となり プレーヤーの体験が向上します 今年の新機能にはiOSのサポートや 最大3倍のアップスケーリング Metal-cppでのサポートなどがあります
エンジンがすでにほかのプラットフォームで 既存のアップスケーリングソリューションを サポートしている場合 MetalFXの統合に エンジン側のコーディングや 修正はあまり必要ありません MetalFXのサポートにはエンジンでアップ スケーリングをサポートする必要があります もう1つの要件はマテリアルシェーダーの テクスチャサンプリングの詳細レベルを 手動で制御するレンダラーです テンポラルアップスケーリングにはジッター シーケンスとモーションベクトルが必要です 使用エンジンが テンポラルアンチエイリアシングを サポートしている場合はおそらく すでにそれらを持っているでしょう MetalFXのテンポラルアップ スケーリングはレンダリングの露出を 考慮することができ 2つ選択肢があります レンダラーが1×1の露出テクスチャを サポートしている場合はそれを使用します そうでない場合は自動露出機能を有効にして 品質が向上するかどうかを確認できます カメラカットや極端なカメラの動きの履歴を リセットすることを忘れないでください アプリケーションにMetalFXを 統合する方法の詳細については ドキュメントおよび昨年の 「Boost performance with MetalFX upscaling」を参照してください Metalにはアプリのレンダリング時間を 最大限に活用するための 強力なオプションがあります リソースを管理し 可能な限り 効率的にバインドすることができます シェーダーがリソースに アクセスする方法に基づいて リソースを共有するパスが 正しい順序で実行されるようにし リソースをGPUに常駐させて 利用可能にします XcodeのMetal Debuggerで 最適化の機会を見つけて適用し コマンド投入を最適化することで アプリケーションはAppleの 強力なグラフィックスアーキテクチャの 可能性を最大限に活用できます 多くの最新レンダリング技術の鍵となる 間接レンダリングを実装することで GPUが自ら作業を決定できるようにします MetalFXを使用して レンダリングをアップスケールすることで レンダリングループにおける アプリの貴重な時間を節約できます レンダリングのヒントや ガイドラインについては 「Optimize Metal Performance for Apple silicon Macs」をご覧ください ご視聴ありがとうございました ♪ ♪
-
-
3:55 - Encode the texture tables.
// Encode the texture tables outside of the rendering loop. id<MTLBuffer> textureTable = [device newBufferWithLength:sizeof(MTLResourceID) * texturesCount options:MTLResourceStorageModeShared]; MTLResourceID* textureTableCPUPtr = (MTLResourceID*)textureTable.contents; for (uint32_t i = 0; i < texturesCount; ++i) { // create the textures. id<MTLTexture> texture = [device newTextureWithDescriptor:textureDesc[i]]; // encode texture in argument buffer textureTableCPUPtr[i] = texture.gpuResourceID; }
-
4:33 - Encode the sampler tables.
// Encode the sampler tables outside of the rendering loop. id<MTLBuffer> samplerTable = [device newBufferWithLength:sizeof(MTLResourceID) * samplersCount options:MTLResourceStorageModeShared]; MTLResourceID* samplerTableCPUPtr = (MTLResourceID*)samplerTable.contents; for (uint32_t i = 0; i < samplersCount; ++i) { // create sampler descriptor MTLSamplerDescriptor* desc = [MTLSamplerDescriptor new]; desc.supportArgumentBuffers = YES; . . . // create a sampler id<MTLSamplerState> sampler = [device newSamplerStateWithDescriptor:desc]; // encode the sampler in argument buffer samplerTableCPUPtr[i] = sampler.gpuResourceID; }
-
5:05 - Encode the top level argument buffer.
// Encode the top level argument buffer. struct TopLevelAB { MTLResourceID* textureTable; float* myBuffer; uint32_t myConstant; MTLResourceID* samplerTable; }; id<MTLBuffer> topAB = [device newBufferWithLength:sizeof(TopLevelAB) options:MTLResourceStorageModeShared]; TopLevelAB* topABCPUPtr = (TopLevelAB*)topAB.contents; topABCPUPtr->textureTable = (MTLResourceID*)textureTable.gpuAddress; topABCPUPtr->myBuffer = (float*)myBuffer.gpuAddress; topABCPUPtr->myConstant = 128; topABCPUPtr->samplerTable = (MTLResourceID*)samplerTable.gpuAddress;
-
6:49 - Allocate the read-only resources.
// Allocate the read-only resources from a heap. MTLHeapDescriptor* heapDesc = [MTLHeapDescriptor new]; heapDesc.size = requiredSize; heapDesc.type = MTLHeapTypeAutomatic; id<MTLHeap> heap = [device newHeapWithDescriptor:heapDesc]; // Allocate the textures and the buffers from the heap. id<MTLTexture> texture = [heap newTextureWithDescriptor:desc]; id<MTLBuffer> buffer = [heap newBufferWithLength:length options:options]; . . . // Make the heap resident once for each encoder that uses it. [encoder useHeap:heap];
-
7:34 - Allocate the writable resources.
// Allocate the writable resources individually. id<MTLTexture> textureRW = [device newTextureWithDescriptor:desc]; id<MTLBuffer> bufferRW = [device newBufferWithLength:length options:options]; // Mark these resources resident when they're needed in the current encoder. // Specify the resource usage in the encoder using MTLResourceUsage. [encoder useResource:textureRW usage:MTLResourceUsageWrite stages:stage]; [encoder useResource:bufferRW usage:MTLResourceUsageRead stages:stage];
-
19:31 - Encode the execute indirect
// Encode the execute indirect command as a series of indirect draw calls. for (uint32_t i = 0; i < maxDrawCount; ++i) { // Encode the current indirect draw call. [renderEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexType:MTLIndexTypeUInt16 indexBuffer:indexBuffer indexBufferOffset:indexBufferOffset indirectBuffer:drawArgumentsBuffer indirectBufferOffset:drawArgumentsBufferOffset]; // Advance the draw arguments buffer offset to the next indirect arguments. drawArgumentsBufferOffset += sizeof(MTLDrawIndexedPrimitivesIndirectArguments); }
-
21:48 - Translate the indirect draw arguments to ICB.
// Kernel written in Metal Shading Language to translate the indirect draw arguments to an ICB. kernel void translateToICB(device const Command* indirectCommands [[ buffer(0) ]], device const ICBContainerAB* icb [[ buffer(1) ]], . . .) { . . . device const Command* indirectCommand = &indirectCommands[commandIndex]; device const MTLDrawIndexedPrimitivesIndirectArguments* args = &command->mdiBuffer[mdiIndex]; render_command drawCall(icb->buffer, indirectCommand->mdiCmdStart + mdiIndex); if(args->indexCount > 0 && args->instanceCount > 0) { encodeCommand(indirectCommand, args, drawCall); } else { cmd.reset(); } } // Encode a render command on the GPU. void encodeCommand(device const Command* indirectCommand, device const MTLDrawIndexedPrimitivesIndirectArguments* args, thread render_command& drawCall) { drawCall.set_render_pipeline_state(indirectCommand->pso); for(ushort i = 0; i < indirectCommand->vertexBuffersCount; ++i) { drawCall.set_vertex_buffer(indirectCommand->vertexBuffer[i].buffer, indirectCommand->vertexBuffer[i].slot); } for(ushort i = 0; i < indirectCommand->fragmentBuffersCount; ++i) { drawCall.set_fragment_buffer(indirectCommand->fragmentBuffer[i].buffer, indirectCommand->fragmentBuffer[i].slot); } drawCall.draw_indexed_primitives(primitive_type::triangle, args->indexCount, indirectCommand->indexBuffer + args->indexStart, args->instanceCount, args->baseVertex, args->baseInstance); }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。