🌋

【Vulkan】GLSLレイトレーシングシェーダをランタイムコンパイルしてみる

2022/04/30に公開

glslangを使ってGLSLのレイトレーシングシェーダをSPIR-Vにランタイムコンパイルしてみます。
https://github.com/KhronosGroup/glslang

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