Post 60。GLSL/SPIR-V を出荷するが、ドライバはそれを実際のマシンコード
に再度コンパイルする——レジスタ割り当て、スピル、スケジューリング
込みで。VK_KHR_pipeline_executable_properties はドライバの
コンパイラが実際に生成したものを公開する API:ステージ毎の統計
(VGPR、スピル、コードサイズ)と、RADV では完全な ISA
ディスアセンブリ。これが RenderDoc のパイプラインインスペクタが
内部で使っているもの。
example.sh
$ ./app
2 executables (compiled shader stages)
stage { Vertex }: 'vertex' — Vulkan Vertex Shader
stage { Fragment }: 'fragment' — Vulkan Fragment Shader
Driver pipeline hash 16879686041057200563
SGPRs 108
VGPRs 12
Spilled SGPRs 0
Code size 248
...
IR 'NIR Shader(s)' (The optimized NIR shader(s)): 4982 bytes
IR 'ACO IR' (ACO IR after optimizations): 5684 bytes
IR 'Assembly' (Final Assembly): 3642 bytes
---
BB0:
s_wqm_b64 exec, exec ; befe1d7e
lds_param_load v2, attr0.x ; ce0f0002
Example: VulkanExamples/p60_pipelexe
セットアップ
main.cpp (feature + pipeline flag)
vk::PhysicalDevicePipelineExecutablePropertiesFeaturesKHR f{};
f.setPipelineExecutableInfo(true);
device = phys.createDeviceUnique(
{.pNext = &featureChain, ...},
extensions = {VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME});
// 重要:このフラグがないとドライバは IR を保持しないかも——
// RADV では、これなしで作成されたパイプラインのデータを要求すると
// 内部表現クエリがクラッシュする(strlen(NULL))。
gi.setFlags(vk::PipelineCreateFlagBits::eCaptureInternalRepresentationsKHR);3つのクエリ
main.cpp
// 1) パイプラインがどんな実行可能物を生成したか?(ステージ毎に1つ)
auto execs = device->getPipelineExecutablePropertiesKHR(
{pipeline.get()}, dldi);
// 2) 実行可能物毎の統計
auto stats = device->getPipelineExecutableStatisticsKHR(
{pipeline.get(), execIndex}, dldi);
// RADV の報告:SGPRs, VGPRs, Spilled SGPRs/VGPRs, Code size,
// LDS size, Scratch size, instruction mix ...
// 3) 内部表現(ディスアセンブリ!)——手動2パス:
// Vulkan-Hpp の vector オーバーロードは pData を取得しない。
VkPipelineExecutableInfoKHR ei{VK_STRUCTURE_TYPE_...};
ei.pipeline = pipe; ei.executableIndex = i;
vkGetPipelineExecutableInternalRepresentationsKHR(dev,&ei,&n,nullptr);
std::vector<VkPipelineExecutableInternalRepresentationKHR> reps(n);
for (auto& r : reps) {
r.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INTERNAL_REPRESENTATION_KHR;
blobs[r_i].resize(8<<20); // 余裕を持ったサイズ
r.pData = blobs[r_i].data();
r.dataSize = blobs[r_i].size();
}
vkGetPipelineExecutableInternalRepresentationsKHR(dev,&ei,&n,reps.data());注意点
pDataバッファは事前割り当てが必要——ドライバはあなたが提供 したメモリに書き込み、(RADV では)dataSizeを実際のペイロードに 縮小しない。isTextIR にはstrnlen(pData, dataSize)を使う。- 統計はドライバ定義の名前——NVIDIA は命令数とレジスタ使用量を
報告し、RADV は ACO パイプライン全体(SGPR/VGPR/スピル+命令ミックス)
を報告する。インデックスをハードコードせず、
stat.nameで照合する。 - IR の利用可能性は様々:RADV は NIR → ACO IR → 最終アセンブリを 出す;NVIDIA は SASS 的な出力を出す;統計だけしか出さないドライバ もある。
- これはデバッグ/イントロスペクション API——リリースで無効の
ままにして全く問題ない;
pipelineExecutableInfoは診断ビルドでのみ 有効化。
なぜ気にするか
レジスタスピルとオキュパンシーは SPIR-V では見えない。スクラッチに
200バイトスピルするシェーダはソースから示唆されるより10倍遅い。
pipeline_executable は「理論上の」最適化がドライバのコンパイラを
実際に通過したか検証する方法——あるいはアンロールしたループが
レジスタ予算を吹き飛ばしたと知る方法。
この記事が役に立った場合は、TechOverflow での新しい記事の調査と公開を支援するため、「Buy Me a Coffee」でのご支援、または PayPal 経由でのご寄付をご検討ください