2019年8月19日 星期一

建置 glslang 在 Windows

建置 glslang 在 Windows

前言

  最近研究 Vulkan 時發現 API 本身不提供 Shader 編譯器,官方的建議是利用官方提供的 glslangValidator ,這個程式可以在 Vulkan SDK 裡找到,利用 Console 命令編譯 Shader後輸出Spir-V程式碼。但如果需要在 Runtime 時編譯 Shader 的話就需要自己編譯一個編譯器,幸運的是官方有提供編譯器,但不幸的是官方不會 Build 好在 Vulkan SDK ,只能自己 Build,由於 Build 的過程有些繁雜,在此將過程做個紀錄。

內容

  Vulkan 官方提供兩個編譯器,glslang 與 shaderc,這兩個編譯器的專案都在 Vulkan SDK 裡面,但為什麼官方需要兩個編譯器呢?在官方的範例 (Vulkan SDK)\Samples\API-Samples\utils\util.cpp 裡有一段程式碼如下
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *pshader, std::vector<unsigned int> &spirv) {
#ifndef __ANDROID__
    EShLanguage stage = FindLanguage(shader_type);
    glslang::TShader shader(stage);
    glslang::TProgram program;
    const char *shaderStrings[1];
    TBuiltInResource Resources = {};
    init_resources(Resources);

    // Enable SPIR-V and Vulkan rules when parsing GLSL
    EShMessages messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);

    shaderStrings[0] = pshader;
    shader.setStrings(shaderStrings, 1);

    if (!shader.parse(&Resources, 100, false, messages)) {
        puts(shader.getInfoLog());
        puts(shader.getInfoDebugLog());
        return false;  // something didn't work
    }

    program.addShader(&shader);

    //
    // Program-level processing...
    //

    if (!program.link(messages)) {
        puts(shader.getInfoLog());
        puts(shader.getInfoDebugLog());
        fflush(stdout);
        return false;
    }

    glslang::GlslangToSpv(*program.getIntermediate(stage), spirv);
#else
    // On Android, use shaderc instead.
    shaderc::Compiler compiler;
    shaderc::SpvCompilationResult module =
        compiler.CompileGlslToSpv(pshader, strlen(pshader), MapShadercType(shader_type), "shader");
    if (module.GetCompilationStatus() != shaderc_compilation_status_success) {
        LOGE("Error: Id=%d, Msg=%s", module.GetCompilationStatus(), module.GetErrorMessage().c_str());
        return false;
    }
    spirv.assign(module.cbegin(), module.cend());
#endif
    return true;
}

 GLSLtoSPV 是把 GLSL 編成 Spir-V 碼, Function 在開始就使用 Preprocessor 隔開,一邊是"非Android",另一邊是"Android"。在"非Android"時會使用 glslang ,另一邊則是 shaderc, glslang 完全沒出現在 (Vulkan SDK)\include 裡,所以要自己 Build ,這沒什麼問題,但 shaderc 的部分遽然有出現在 (Vulkan SDK)\include !可惜我實際 include 後會有錯誤,所以還是要建置  glslang 。

  glslang 所使用的建置是 CMake ,設定的方式可以參考下圖
使用CMake 建置 glslang
在"1"的位置設定 glslang 的專案位置,"2"的部分是建置的目標位置,這裡是直接在 glslang 的資料夾下產生 ProjWindows ,並把目標為位置設定在那,接著按下"3",就可以取得組態,組態取完後按下"4"就可以產生 Visual studio 方案。開啟方案後請建置"SPIRV"專案,如下圖
建置"SPIRV"專案

接著手動建置 Release 組態的"spirv-remap"專案,如下圖
手動建置 Release 組態的"spirv-remap"專案

這個步驟的主要目的產生 Release 的 SPVRemapper.lib,建置 Release 的"SPIRV"專案並不會產生,所以請手動建置。SPVRemapper.lib雖不需要用在編譯,但如果需要建置 Vulkan SDK 裡的其它專案時會用到,請不要省略此步驟。不幸的是建置完後並不會將所有相關的 lib 都集中起來,所以要自己 Copy ,這裡整理了需要 Copy 的檔案,清單如下
(CMake build target)\glslang\Debug\glslangd.lib
(CMake build target)\glslang\Release\glslang.lib
(CMake build target)\hlsl\Debug\HLSLd.lib
(CMake build target)\hlsl\Release\HLSL.lib
(CMake build target)\OGLCompilersDLL\Debug\OGLCompilerd.lib
(CMake build target)\OGLCompilersDLL\Release\OGLCompiler.lib
(CMake build target)\glslang\OSDependent\Windows\Debug\OSDependentd.lib
(CMake build target)\glslang\OSDependent\Windows\Release\OSDependent.lib
(CMake build target)\SPIRV\Debug\SPIRVd.lib
(CMake build target)\SPIRV\Release\SPIRV.lib
(CMake build target)\External\spirv-tools\source\Debug\SPIRV-Toolsd.lib
(CMake build target)\External\spirv-tools\source\Release\SPIRV-Tools.lib
(CMake build target)\External\spirv-tools\source\opt\Debug\SPIRV-Tools-optd.lib
(CMake build target)\External\spirv-tools\source\opt\Release\SPIRV-Tools-opt.lib

  最後還有需要 include 的檔案,這個比較簡單, (Vulkan SDK)\glslang\glslang 與  (Vulkan SDK)\glslang\SPIRV 兩個資料夾 的內容就是所有需要 include 的檔案,請示需要 Copy。

參考資料

Vulkan Tutorial

沒有留言:

張貼留言