|
#include "spircompiler.h" |
|
|
|
#include "message.h" |
|
#include "../SPIRV/GlslangToSpv.h" |
|
#include "ResourceLimits.h" |
|
#ifdef SPIRV_OPT_APPLY |
|
#include "opt/build_module.h" |
|
#include "opt/ir_loader.h" |
|
#include "opt/pass_manager.h" |
|
#include "opt/passes.h" |
|
#endif |
|
#ifdef SPIRV_OPT_APPLY |
|
using namespace spvtools; |
|
#endif |
|
|
|
// |
|
// Translate the meaningful subset of command-line options to parser-behavior options. |
|
// |
|
void SetMessageOptions(int Options, EShMessages& messages) |
|
{ |
|
if (Options & EOptionRelaxedErrors) |
|
messages = (EShMessages)(messages | EShMsgRelaxedErrors); |
|
if (Options & EOptionIntermediate) |
|
messages = (EShMessages)(messages | EShMsgAST); |
|
if (Options & EOptionSuppressWarnings) |
|
messages = (EShMessages)(messages | EShMsgSuppressWarnings); |
|
if (Options & EOptionSpv) |
|
messages = (EShMessages)(messages | EShMsgSpvRules); |
|
if (Options & EOptionVulkanRules) |
|
messages = (EShMessages)(messages | EShMsgVulkanRules); |
|
if (Options & EOptionOutputPreprocessed) |
|
messages = (EShMessages)(messages | EShMsgOnlyPreprocessor); |
|
if (Options & EOptionReadHlsl) |
|
messages = (EShMessages)(messages | EShMsgReadHlsl); |
|
if (Options & EOptionCascadingErrors) |
|
messages = (EShMessages)(messages | EShMsgCascadingErrors); |
|
if (Options & EOptionKeepUncalled) |
|
messages = (EShMessages)(messages | EShMsgKeepUncalled); |
|
} |
|
|
|
|
|
void InfoLogMsg(const char* msg, const char* name, const int num) |
|
{ |
|
if (num >= 0) |
|
printf("#### %s %s %d INFO LOG ####\n", msg, name, num); |
|
else |
|
printf("#### %s %s INFO LOG ####\n", msg, name); |
|
} |
|
|
|
// Simple bundling of what makes a compilation unit for ease in passing around, |
|
// and separation of handling file IO versus API (programmatic) compilation. |
|
struct ShaderCompUnit { |
|
ShaderCompUnit(EShLanguage _stage, const char** _fileNames, const char** _texts) : stage(_stage), fileNames(_fileNames), texts(_texts){} |
|
EShLanguage stage; |
|
const char** fileNames; |
|
const char** texts; |
|
}; |
|
extern "C" { |
|
void initProcess() { |
|
glslang::InitializeProcess(); |
|
} |
|
int compileGLSL(const char *szShaderCode, int shaderType, int Options, struct spv_compile_output* spvOutput) { |
|
int status = 0; |
|
EShLanguage lang = EShLangVertex; |
|
std::string logStr; |
|
std::string logErrStr; |
|
logStr+="Making shader "+std::to_string(shaderType)+"\n"; |
|
logStr+=std::string("Options ")+std::to_string(Options)+"\n"; |
|
switch (shaderType) { |
|
case VK_SHADER_STAGE_VERTEX_BIT: |
|
lang = EShLangVertex; |
|
break; |
|
case VK_SHADER_STAGE_FRAGMENT_BIT: |
|
lang = EShLangFragment; |
|
break; |
|
case VK_SHADER_STAGE_COMPUTE_BIT: |
|
lang = EShLangCompute; |
|
break; |
|
case VK_SHADER_STAGE_GEOMETRY_BIT: |
|
lang = EShLangGeometry; |
|
break; |
|
case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: |
|
lang = EShLangTessControl; |
|
break; |
|
case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: |
|
lang = EShLangTessEvaluation; |
|
break; |
|
default: |
|
return 1; |
|
} |
|
|
|
const char* entryPointName = nullptr; |
|
const char* sourceEntryPointName = nullptr; |
|
|
|
TBuiltInResource Resources = glslang::DefaultTBuiltInResource; |
|
logStr += "DefaultBuiltIn: "+glslang::GetDefaultTBuiltInResourceString(); |
|
|
|
std::array<unsigned int, EShLangCount> baseSamplerBinding; |
|
std::array<unsigned int, EShLangCount> baseTextureBinding; |
|
std::array<unsigned int, EShLangCount> baseImageBinding; |
|
std::array<unsigned int, EShLangCount> baseUboBinding; |
|
baseSamplerBinding.fill(0); |
|
baseTextureBinding.fill(0); |
|
baseImageBinding.fill(0); |
|
baseUboBinding.fill(0); |
|
|
|
const char* shaderFileName = "shader"; |
|
|
|
|
|
std::vector<ShaderCompUnit> compUnits; |
|
compUnits.push_back(ShaderCompUnit(lang, &shaderFileName, &szShaderCode)); |
|
// keep track of what to free |
|
std::list<glslang::TShader*> shaders; |
|
|
|
EShMessages messages = EShMsgDefault; |
|
SetMessageOptions(Options, messages); |
|
|
|
// |
|
// Per-shader processing... |
|
// |
|
|
|
glslang::TProgram& program = *new glslang::TProgram; |
|
for (auto it = compUnits.cbegin(); it != compUnits.cend(); ++it) { |
|
const auto &compUnit = *it; |
|
glslang::TShader* shader = new glslang::TShader(compUnit.stage); |
|
shader->setStringsWithLengthsAndNames(compUnit.texts, NULL, compUnit.fileNames, 1); |
|
if (entryPointName) // HLSL todo: this needs to be tracked per compUnits |
|
shader->setEntryPoint(entryPointName); |
|
if (sourceEntryPointName) |
|
shader->setSourceEntryPoint(sourceEntryPointName); |
|
|
|
shader->setShiftSamplerBinding(baseSamplerBinding[compUnit.stage]); |
|
shader->setShiftTextureBinding(baseTextureBinding[compUnit.stage]); |
|
shader->setShiftImageBinding(baseImageBinding[compUnit.stage]); |
|
shader->setShiftUboBinding(baseUboBinding[compUnit.stage]); |
|
shader->setFlattenUniformArrays((Options & EOptionFlattenUniformArrays) != 0); |
|
shader->setNoStorageFormat((Options & EOptionNoStorageFormat) != 0); |
|
|
|
if (Options & EOptionAutoMapBindings) |
|
shader->setAutoMapBindings(true); |
|
|
|
shaders.push_back(shader); |
|
|
|
const int defaultVersion = Options & EOptionDefaultDesktop ? 110 : 100; |
|
|
|
/* if (Options & EOptionOutputPreprocessed) { |
|
std::string str; |
|
glslang::TShader::ForbidIncluder includer; |
|
if (shader->preprocess(&Resources, defaultVersion, ENoProfile, false, false, |
|
messages, &str, includer)) { |
|
PutsIfNonEmpty(str.c_str()); |
|
} |
|
else { |
|
CompileFailed = true; |
|
} |
|
StderrIfNonEmpty(shader->getInfoLog()); |
|
StderrIfNonEmpty(shader->getInfoDebugLog()); |
|
continue; |
|
}*/ |
|
if (!shader->parse(&Resources, defaultVersion, false, messages)) { |
|
status = 2; |
|
} |
|
|
|
program.addShader(shader); |
|
|
|
if (!(Options & EOptionSuppressInfolog)) { |
|
logStr += shader->getInfoLog(); |
|
logStr += "\n"; |
|
logStr += shader->getInfoDebugLog(); |
|
logStr += "\n"; |
|
} |
|
} |
|
|
|
// |
|
// Program-level processing... |
|
// |
|
|
|
// Link |
|
if (!status&&!program.link(messages)) { |
|
status = 3; |
|
} |
|
|
|
// Map IO |
|
if (!status&&Options & EOptionSpv) { |
|
if (!program.mapIO()) |
|
status = 4; |
|
} |
|
|
|
// Report |
|
if (!(Options & EOptionSuppressInfolog)) { |
|
logStr += program.getInfoLog(); |
|
logStr += "\n"; |
|
logStr += program.getInfoDebugLog(); |
|
logStr += "\n"; |
|
} |
|
|
|
// Reflect |
|
if (Options & EOptionDumpReflection) { |
|
program.buildReflection(); |
|
program.dumpReflection(); |
|
} |
|
|
|
#ifdef SPIRV_OPT_APPLY |
|
|
|
opt::PassManager pass_manager; |
|
pass_manager.SetMessageConsumer( |
|
[&spvOutput](spv_message_level_t level, const char* source, |
|
const spv_position_t& position, const char* message) { |
|
logStr += StringifyMessage(level, source, position, message); |
|
logStr += "\n"; |
|
}); |
|
pass_manager.AddPass<opt::StripDebugInfoPass>(); |
|
pass_manager.AddPass<opt::EliminateDeadConstantPass>(); |
|
pass_manager.AddPass<opt::FoldSpecConstantOpAndCompositePass>(); |
|
pass_manager.AddPass<opt::UnifyConstantPass>(); |
|
//pass_manager.AddPass<opt::EliminateDeadCodePass>(); |
|
// Dump SPIR-V |
|
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_1; |
|
spv_context context = spvContextCreate(target_env); |
|
#endif |
|
if (Options & EOptionSpv && !status) { |
|
|
|
for (int stage = 0; stage < EShLangCount; ++stage) { |
|
if (program.getIntermediate((EShLanguage)stage)) { |
|
std::vector<unsigned int> spirv; |
|
spv::SpvBuildLogger logger; |
|
glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv, &logger); |
|
unsigned int len = (unsigned int)(spirv.size() * 4); |
|
const char* data = (const char*)&spirv[0]; |
|
std::vector<uint32_t> target; |
|
int aLen = len; |
|
#ifdef SPIRV_OPT_APPLY |
|
// do validation |
|
spv_diagnostic diagnostic = nullptr; |
|
spv_const_binary_t binary = { spirv.data(), spirv.size() }; |
|
spv_result_t error = spvValidate(context, &binary, &diagnostic); |
|
if (error) { |
|
spvDiagnosticPrint(diagnostic); |
|
status = 6; |
|
} |
|
else { |
|
std::unique_ptr<ir::Module> module = BuildModule( |
|
target_env, pass_manager.consumer(), spirv.data(), spirv.size()); |
|
pass_manager.Run(module.get()); |
|
|
|
module->ToBinary(&target, /* skip_nop = */ true); |
|
|
|
len = (unsigned int)(target.size() * 4); |
|
data = (const char*)&target[0]; |
|
} |
|
|
|
spvDiagnosticDestroy(diagnostic); |
|
#endif |
|
spvOutput->dataSize = len; |
|
spvOutput->data = (char*) malloc(len); |
|
memcpy(spvOutput->data, data, len); |
|
logStr += logger.getAllMessages(); |
|
logStr += "\n"; |
|
logStr += "INPUT TEXT LEN "+std::to_string(strlen(compUnits[0].texts[0])); |
|
logStr += "\n"; |
|
logStr += "Output data size: "+std::to_string(aLen); |
|
logStr += "\n"; |
|
spvOutput->stage = stage; |
|
break; |
|
} |
|
} |
|
} |
|
const char* szLogStr = logStr.c_str(); |
|
size_t logStrLen = strlen(szLogStr)+1; |
|
spvOutput->log = (char*) calloc(logStrLen, 1); |
|
memcpy(spvOutput->log, szLogStr, logStrLen); |
|
// Free everything up, program has to go before the shaders |
|
// because it might have merged stuff from the shaders, and |
|
// the stuff from the shaders has to have its destructors called |
|
// before the pools holding the memory in the shaders is freed. |
|
delete &program; |
|
while (shaders.size() > 0) { |
|
delete shaders.back(); |
|
shaders.pop_back(); |
|
} |
|
#ifdef SPIRV_OPT_APPLY |
|
spvContextDestroy(context); |
|
#endif |
|
return status; |
|
} |
|
|
|
} |