Last active
March 3, 2017 09:10
-
-
Save nidefawl/586958f469ea04c6762277d67a56b11d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef SPIR_COMPILER_H_T__ | |
#define SPIR_COMPILER_H_T__ | |
#ifndef VK_SHADER_STAGE_VERTEX_BIT | |
#define VK_SHADER_STAGE_VERTEX_BIT 0x01 | |
#endif | |
#ifndef VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | |
#define VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT 0x02 | |
#endif | |
#ifndef VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT | |
#define VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT 0x04 | |
#endif | |
#ifndef VK_SHADER_STAGE_GEOMETRY_BIT | |
#define VK_SHADER_STAGE_GEOMETRY_BIT 0x08 | |
#endif | |
#ifndef VK_SHADER_STAGE_FRAGMENT_BIT | |
#define VK_SHADER_STAGE_FRAGMENT_BIT 0x10 | |
#endif | |
#ifndef VK_SHADER_STAGE_COMPUTE_BIT | |
#define VK_SHADER_STAGE_COMPUTE_BIT 0x20 | |
#endif | |
// Command-line options | |
enum TOptions { | |
EOptionNone = 0, | |
EOptionIntermediate = (1 << 0), | |
EOptionSuppressInfolog = (1 << 1), | |
EOptionMemoryLeakMode = (1 << 2), | |
EOptionRelaxedErrors = (1 << 3), | |
EOptionGiveWarnings = (1 << 4), | |
EOptionLinkProgram = (1 << 5), | |
EOptionMultiThreaded = (1 << 6), | |
EOptionDumpConfig = (1 << 7), | |
EOptionDumpReflection = (1 << 8), | |
EOptionSuppressWarnings = (1 << 9), | |
EOptionDumpVersions = (1 << 10), | |
EOptionSpv = (1 << 11), | |
EOptionHumanReadableSpv = (1 << 12), | |
EOptionVulkanRules = (1 << 13), | |
EOptionDefaultDesktop = (1 << 14), | |
EOptionOutputPreprocessed = (1 << 15), | |
EOptionOutputHexadecimal = (1 << 16), | |
EOptionReadHlsl = (1 << 17), | |
EOptionCascadingErrors = (1 << 18), | |
EOptionAutoMapBindings = (1 << 19), | |
EOptionFlattenUniformArrays = (1 << 20), | |
EOptionNoStorageFormat = (1 << 21), | |
EOptionKeepUncalled = (1 << 22), | |
}; | |
// | |
// Return codes from main/exit(). | |
// | |
enum TFailCode { | |
ESuccess = 0, | |
EFailUsage, | |
EFailCompile, | |
EFailLink, | |
EFailCompilerCreate, | |
EFailThreadCreate, | |
EFailLinkerCreate | |
}; | |
struct spv_compile_output { | |
int stage; | |
char* log; | |
unsigned long dataSize; | |
char* data; | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment