🌋
【Vulkan】GLSLレイトレーシングシェーダをランタイムコンパイルしてみる
glslangを使ってGLSLのレイトレーシングシェーダをSPIR-Vにランタイムコンパイルしてみます。
CMakeでSDKからリンクする
CMakeLists.txt
set(VULKAN_LIB $ENV{VULKAN_SDK}/Lib)
set(VULKAN_INCLUDE $ENV{VULKAN_SDK}/Include)
target_link_libraries(${PROJECT_NAME} PUBLIC
optimized ${VULKAN_LIB}/spirv-cross-core.lib
optimized ${VULKAN_LIB}/spirv-cross-cpp.lib
optimized ${VULKAN_LIB}/spirv-cross-glsl.lib
optimized ${VULKAN_LIB}/OGLCompiler.lib
optimized ${VULKAN_LIB}/OSDependent.lib
optimized ${VULKAN_LIB}/SPIRV-Tools.lib
optimized ${VULKAN_LIB}/SPIRV-Tools-opt.lib
optimized ${VULKAN_LIB}/glslang-default-resource-limits.lib
optimized ${VULKAN_LIB}/glslang.lib
optimized ${VULKAN_LIB}/GenericCodeGen.lib
optimized ${VULKAN_LIB}/MachineIndependent.lib
optimized ${VULKAN_LIB}/SPVRemapper.lib
optimized ${VULKAN_LIB}/SPIRV.lib
debug ${VULKAN_LIB}/spirv-cross-cored.lib
debug ${VULKAN_LIB}/spirv-cross-cppd.lib
debug ${VULKAN_LIB}/spirv-cross-glsld.lib
debug ${VULKAN_LIB}/OGLCompilerd.lib
debug ${VULKAN_LIB}/OSDependentd.lib
debug ${VULKAN_LIB}/SPIRV-Toolsd.lib
debug ${VULKAN_LIB}/SPIRV-Tools-optd.lib
debug ${VULKAN_LIB}/glslang-default-resource-limitsd.lib
debug ${VULKAN_LIB}/glslangd.lib
debug ${VULKAN_LIB}/GenericCodeGend.lib
debug ${VULKAN_LIB}/MachineIndependentd.lib
debug ${VULKAN_LIB}/SPVRemapperd.lib
debug ${VULKAN_LIB}/SPIRVd.lib
)
target_include_directories(${PROJECT_NAME} PUBLIC
${VULKAN_INCLUDE}/
${VULKAN_INCLUDE}/spirv_cross
${VULKAN_INCLUDE}/glslang/SPIRV
)
コンパイルする
コードはこんな感じです。
#include <fstream>
#include <iostream>
#include <SPIRV/GlslangToSpv.h>
#include <StandAlone/ResourceLimits.h>
namespace Compiler
{
std::string readFile(const std::string& path)
{
std::ifstream file{ path };
if (!file.is_open()) {
throw std::runtime_error("Failed to open file: " + path);
}
std::stringstream buffer;
buffer << file.rdbuf();
return buffer.str();
}
EShLanguage translateShaderStage(const std::string& filepath)
{
if (filepath.ends_with("vert")) return EShLangVertex;
else if (filepath.ends_with("frag")) return EShLangFragment;
else if (filepath.ends_with("comp")) return EShLangCompute;
else if (filepath.ends_with("rgen")) return EShLangRayGenNV;
else if (filepath.ends_with("rmiss")) return EShLangMissNV;
else if (filepath.ends_with("rchit")) return EShLangClosestHitNV;
else if (filepath.ends_with("rahit")) return EShLangAnyHitNV;
else assert(false && "Unknown shader stage");
}
std::vector<unsigned int> compileText(EShLanguage stage,
const std::string& glslShader)
{
glslang::InitializeProcess();
const char* shaderStrings[1];
shaderStrings[0] = glslShader.data();
glslang::TShader shader(stage);
// RayTracingを使えるようにバージョン設定
shader.setEnvTarget(glslang::EShTargetLanguage::EShTargetSpv,
glslang::EShTargetLanguageVersion::EShTargetSpv_1_5);
shader.setStrings(shaderStrings, 1);
EShMessages messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
if (!shader.parse(&glslang::DefaultTBuiltInResource, 100, false, messages)) {
throw std::runtime_error(glslShader + "\n" + shader.getInfoLog());
}
glslang::TProgram program;
program.addShader(&shader);
if (!program.link(messages)) {
throw std::runtime_error(glslShader + "\n" + shader.getInfoLog());
}
std::vector<unsigned int> spvShader;
glslang::GlslangToSpv(*program.getIntermediate(stage), spvShader);
glslang::FinalizeProcess();
return spvShader;
}
std::vector<unsigned int> compileFile(const std::string& path)
{
EShLanguage stage = translateShaderStage(path);
return compileText(stage, readFile(path));
}
} // namespace Compiler
注意点がひとつだけあり、レイトレーシング拡張 #extension GL_EXT_ray_tracing
を使用するには、EnvTarget を EShTargetSpv_1_4
以上にする必要があります。
ShaderModuleを作る
コンパイルしたらShaderModuleを作成できます。
std::vector spirvCode = Compiler::compileFile("shader/raytrace.rgen");
vk::ShaderModuleCreateInfo createInfo;
createInfo.setCode(spirvCode);
vk::ShaderModule shaderModule = device.createShaderModule(createInfo);
Discussion