インフィニットループ 技術ブログ

2023年11月27日 (月)

著者 : y-murakami

【D3D12】DirectX12でUnityのSRP的なものを作成した

 宮城県仙台市の3Dゲームエンジニア、にしきんです。

 これまでもっぱらUnityを扱っていたのですが、高度な描画回りのトピックにとりかかるには手元で低レベル描画API環境を実用的なレベルでくみ上げておかないと、やはり厳ついなと感じました。そこで、2か月ほどC++とD3D12を勉強しながら個人的に抽象パイプラインを組んでみました。ようやく落ち着いたので、こしらえたものをご紹介します。

SRPっぽいものは便利

 SRPは抽象的な描画パイプライン構築のためのAPIですが、今世代の描画APIにとって抽象描画パイプライン構築は自然なアイディアだと思っています。

 直接D3D12やVulkan等をいい感じにまとめて記述しても良いのですが、今世代の描画APIはハードウェアとの対応が近い関係で、皆大事なコンセプトについてはどれも大体同じです。自分たちがGPUに求める共通コントロールをあらかじめ見つけておいて、クライアント側に提供しておけば、各々で自由度をもちつつ簡潔に価値を求められるはずです。

Unity(2023)のSRPでは何が足りない?

 UnityのSRPには大胆な抽象化が与えられており、現代のローレベルAPIとは差があります。世代的に異なるDX11やOpenGL等もサポートに含んでいることや、CommandBufferが用意された歴史的なタイミングも関係しているかもしれません。

 SRPのような大きな抽象化のもとにザックリと描画に指示をすることは、プロトタイピング的に何かを表現したい場合はよいかもしれませんが、現実的なシナリオに挑もうとするとコントロールが足りないかなと感じる方もいらっしゃるでしょう。

青い箱には解説が書いてありましたが、掲載に伴い除いています ちなみにまだ草案で実際に試してないため、このまま動くかは不明

 一例として、上記のようなフレームがオーバーラップする時間軸でハードウェアキューを操作しようとすると、SRPでは難しいはずです。(Unityでもコンピュートキューは一応コントロール可能です。ただし、スワップチェイン周りのコントロールは不可能です。また、コピーキューはブラックボックスですが、存在はします。)

 Unityではいくつかの点、メッシュ描画回りやリソースのバッファリング、バリアやGPUへのアップロードやハードウェアキューのコントロールなど不明瞭であるため、幅広いGPU要素についてパフォーマンス上の特性と付き合いつつコントロールすることが難しいと思います。Unreal Engine等他エンジンと異なりソースが公開されていないため、雨乞いをしてただ祈っているような構図が生まれやすいかもしれません。

作成したAPIの一部紹介

 どのような形で利用できるのか、実際のコードをお見せしながらご紹介します。

 抽象パイプラインはRender関数を実装し、上のコードのようにコマンドを組み立てます。
これはレンダーターゲットを適当な色で染めつつ、垂直同期を待たずにゲームロジックに戻れるようなパイプラインのコードです。

 ぱっと見でSRPと異なるのは、リソースバリアを明記する必要がある点でしょうか。(この部分はD3D12とVulkanでコントロールに差があり、今後変えるかもしれないです。一応D3D12にもエンハンスドバリアーというものが追加されているようです。)

 また、各リソースのバッファリングの有無についても、作成時に明示的な指示をしており、上記の部分では分かりづらいのですが、取得時にバッファリングの存在がある程度明らかとなるようなつくりになっております。上記のパイプラインでは利用されているカラーバッファがバッファリングされているものです。

 尚、行末のワーカースレッドの中では、スワップチェインのバッファに中身を移すために概ね以下のような処理が行われています。

 見た通りですがカラーバッファをコピーして、Presentして(実際に完了するまで戻らない関数)、それが終わったらCPUスレッドの終了を通知しております。Present待機が別スレッドで行われることによって、垂直同期の待機中に別のロジックが走ることが出来るようになる訳ですね。

 お見せしたとおり、SRPに比べ多くのコンセプトが明示されているのが特徴ですが、今のところルートシグネチャ、ディスクリプタ周りは厳しい制約を設けて隠ぺいしております。(上記のコードでは利用される場面がないが。)

 内部的にディスクリプタヒープの管理はかなりうまく対応したつもりなのでお話したいのですが、マイナーが過ぎるので一生しません。ただ、この辺のノリはGPU描画発行やバインドレスをやっていく中で後々変えるところがあると思いますので、もしかすると抽象パイプラインとしてコントロールする対象に昇格する可能性はあります。

 さて、大体何がやりたいのかは伝わりましたか?

リソースのアロケーション部分などもお見せしたいのですが、長くなりそうなのでやめておきます。

おわりに

 これからシーン描画のための仕組みを整えますが、その後はGPU描画発行関連トピックと、最終的にレイトレーシングをやっていきます。楽しみです。

 D3D12をやってみると、実用に耐える水準を目指すとコードの記述量が水面下ではものすごくなってしまったり、またコンセプトの設計は慎重さが必要だったりと中々面白いです。グラフィクスの低レベルAPIは、ただ単に動くだけでは全く価値がないわけですが、これはおもしろさの一つかもしれませんね。アーキテクトとして、ドメインの知識とOOPの知識をつぎ込めるよい題材だと感じます。

 そういえば、もしかすると貼ったコードだけでも分かるのかもしれないのですが、同時に始めたC++には個人的にまだあまり興味がなく、冒涜的な使い方をしている自覚があるので、落ち着いたらもう少し調べてみようかなと思ってます。
 OOPとしてはC#と同等な表現が出来る範囲が分かればよく、メモリやスレッドのようなハードウェアのコントロールのための記述くらいしか調べてないんですよね。

★インフィニットループでは3Dゲーム開発にも取り組んでおります。興味がある方は是非採用情報をご確認ください。★

ブログ記事検索

このブログについて

このブログは、札幌市・仙台市の「株式会社インフィニットループ」が運営する技術ブログです。 お仕事で使えるITネタを社員たちが発信します!