Skip to content

Instantly share code, notes, and snippets.

@nidefawl
Last active March 3, 2017 09:10
Show Gist options
  • Save nidefawl/586958f469ea04c6762277d67a56b11d to your computer and use it in GitHub Desktop.
Save nidefawl/586958f469ea04c6762277d67a56b11d to your computer and use it in GitHub Desktop.
#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;
}
}
#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