UE4-RTX4.23ブランチのMissShaderLightingは何をしているのか

NVIDIAがメンテナンスしているUE4のブランチの一つとして、RTXというBranchがあります。ここでは、RTX関連機能の追加や、プラットフォームに依存した積極的な最適化を適用したブランチを公開しています。その中で、RTX-4.23ブランチに追加されている機能として、Miss Shader Lightingという機能があります。今回はこの機能がどういうものなのかを見てみたいと思います。(2019/12/11現在)
https://github.com/NvPhysX/UnrealEngine/tree/RTX_dev

Miss Shader Lighting とは

 名前からも想像できる通り、RTシェーダーの中のMiss Shaderという処理ステージでライティング処理を行う機能です。通常のUE4のRTシェーダーは、直接光光源の評価は、Ray Generation Shaderで行いますが、これをMiss Shaderで行うように変更するものです。
 r.RayTracing.MissShaderLightingで機能の[On/Off]指定を行います。RTXブランチではデフォルトで有効になっているので特に変更は必要ありません。影響を受けるのは、Ray Tracing Reflection と Ray Tracing Translucency機能になります。他のRTシェーダーは、4.23現在では処理の変更はありません。実装は、FRayTracingReflectionsRGSと、FRayTracingTranslucencyRGSの、ShaderのPermutationDomainにFMissShaderLightingが追加されることで行われています。シェーダーコンパイル時にDIM_MISS_SHADER_LIGHTINGが定義され、コードの変更が適用される仕組みです。
 性能向上に幅がありますが、最低でも5~10%の高速化が得られ、極端なケースでは30%以上の性能向上が確認されているそうです。

計測

Miss Shader Lighting 無効時
Ray Tracing Reflections : 11.61ms

Miss Shader Lighting 有効時
Ray Tracing Reflections : 8.32ms

上記は、Seoul CityでRT Reflectionパスの描画時間をMiss Shader Lighting [On/Off]で計測しました。テスト結果としては28%の高速化となりました。

なぜ速くなるのか

 この最適化では、RTシェーダー全体が行わなければならないシェーディングの仕事量は、Miss Shader Lightingの[On/Off]で変化しません。にもかかわらず、30%近い処理時間の短縮が達成されています。Miss Shader Lightingの具体的な処理の変更は、直接光光源によるBRDF評価にあります。通常はRay Generation ShaderからShadow Rayを光源にキャストして、遮蔽情報を確認した後、直接光光源によるBRDF評価を行います。Miss Shader Lightingが有効な場合は、このShadow RayのMiss Shaderで、直接光光源によるBRDF評価を行い、Radianceの値をPayloadに載せてRay Generation Shaderに返します。結局のところ、同じ処理を行うのですが、Ray Generation Shaderからは、BRDF評価を行うためのシェーダーコードが無くなります。これによって、Ray Generation Shaderの使用レジスタ数の削減が達成されるので、GPUスレッドの並列実行性が向上するはずであるというのが仕組みになります。
 下記は、UE4のRT ReflectionのRay Generation Shaderの大まかな処理フローです。ループのネストの一番深いところにBRDF評価のコードがあるため、この処理を削減することによる、使用レジスタ削減効果は覿面です。ちなみに、実際のRT Reflectionシェーダーの処理フローは、Materialのソート処理が有効な場合は3パスに処理が分かれ、もっと複雑ですが、BRDF評価がネストの一番深いところにあるのは同様です。

NSight Graphicsを使って調べてみる

では、実際にGPUスレッドの並列実行性が向上しているかNSight Graphicsを使って確認してみたいと思います。

Miss Shader Lighting OFF

Miss Shader Lighting ON

上記のスクリーンショットでは、Miss Shader Lightingの[On/Off]それぞれで、RT Reflectionパスの処理をハイライトしています。Compute WarpのSM Occupancyは両者とも40%前半で、SM Warp Can’t Launchを見るとRegister Pressureによるものだと分かります。ただし、GPUスレッドの並列実行数を示す、Warp Occupancyは、両者に大きな違いが見られません。
 そのかわり、大きな違いとして挙げられるのは、VRAM ThoughputがMiss Shader LightingがOFFの時は41%あるのに対して、ONの時は17.9%と大きく低減しているのが分かります。ライティング処理を単純化した訳ではないので、アクセスしなくてはならないシェーダーリソースの総量に違いは無いはずですが、VRAMに対するアクティビティが大きく変化しています。


 上記は、Shared Memoryを4KB、128スレッドグループと設定した場合の、レジスタ使用数と同時実行されるWarpの数のグラフです。CUDA Occupancy Calculatorで自由に条件を変更して確認することができます。計測値ベースで、SM Occupancyが40%程度になるレジスタ使用数は、96~128辺りと思われます。おそらくですが、RT ReflectionのRay Generation Shaderは大変複雑な処理で、実際に必要なレジスタ数はもっと多いのではないかと思われます。そこで、シェーダーコンパイラが、SM Occpancyの極端な低下を避けるために、レジスタのスタック退避(Register Spilling)を行っているのではないかと思います。これが、Miss Shader Lightingによって、Ray Generation Shaderの複雑度が下がったため、Register Spillingが軽減されたのではないかと思われます。

まとめ

シェーディング結果の品質に一切の変更なく、30%近い高速化が得られるMiss Shader Lightingは非常に有用なものだと思います。実際のプロジェクトで、RT Reflectionの性能向上が必要な場合は一考の余地があるのではないかと思います。RTXブランチからの該当部分のマージは、それほど難しいものではないので、ご自分のエンジンコードへのマージをお勧めします。