🌋

【Vulkan】シェーダリフレクションで手動バインディングから解放されたい

2022/04/30に公開

シェーダとC++間のバインディングを手作業で一致させるのは大変つらい気持ちになります。

こんな感じ。

sample.rgen
#version 460
#extension GL_EXT_ray_tracing : enable
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
layout(binding = 1, set = 0, rgba8) uniform image2D inputImage;

void main()
{
}
sample.cpp
std::vector<vk::DescriptorSetLayoutBinding> bindings{
  { 0, vk::DescriptorType::eAccelerationStructureKHR, 
    1, vk::ShaderStageFlagBits::eRaygenKHR},
  { 1, vk::DescriptorType::eStorageImage, 
    1, vk::ShaderStageFlagBits::eRaygenKHR},
};

なのでSPIRV-Crossを使って、シェーダからバインディング情報を取得しましょう。
https://github.com/KhronosGroup/SPIRV-Cross

こんな感じ。

#include <spirv_glsl.hpp>

// spirvコードを読み込む
std::vector<unsigned int> code = readFile("...");

// 今回は数字ではなく変数名で対応をとれるようにしてみます
std::unordered_map<std::string, vk::DescriptorSetLayoutBinding> bindingMap;

// 情報を取得する
spirv_cross::CompilerGLSL glsl{ spvShader };
spirv_cross::ShaderResources resources = glsl.get_shader_resources();

for (auto&& resource : resources.acceleration_structures) {
    vk::DescriptorSetLayoutBinding binding;
    binding.binding = glsl.get_decoration(resource.id, spv::DecorationBinding);
    binding.descriptorType = vk::DescriptorType::eAccelerationStructureKHR;
    map[resource.name] = binding;
}
for (auto&& resource : resources.storage_images) {
    vk::DescriptorSetLayoutBinding binding;
    binding.binding = glsl.get_decoration(resource.id, spv::DecorationBinding);
    binding.descriptorType = vk::DescriptorType::eStorageImage;
    map[resource.name] = binding;
}
// ...他のリソースも同様

こうすれば、バインディングの数値から解放され、以下のような使い方が可能になります。番号だけでなくディスクリプタタイプなども取れるので、いろいろ手間が省けます。

// 疑似コード
Accel accel;
Image image;
DescriptorSet descSet;
descSet.update(bindingMap["topLevelAS"], accel);
descSet.update(bindingMap["inputImage"], image);

嬉しい!

おわり。

Discussion