Add --no-link option

Adds the --no-link option which outputs the compiled shader binaries
without linking them. This is a first step towards allowing users to
create SPIR-v binary, non-executable libraries.

When using the --no-link option, all functions are decorated with the
Export linkage attribute.
This commit is contained in:
Nathaniel Cesario 2023-08-17 13:49:18 -06:00 committed by arcady-lunarg
parent a4aceb57de
commit 4c57db1595
24 changed files with 1671 additions and 1454 deletions

View file

@ -73,40 +73,41 @@ extern "C" {
}
// 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),
EOptionHlslOffsets = (1 << 23),
EOptionHlslIoMapping = (1 << 24),
EOptionAutoMapLocations = (1 << 25),
EOptionDebug = (1 << 26),
EOptionStdin = (1 << 27),
EOptionOptimizeDisable = (1 << 28),
EOptionOptimizeSize = (1 << 29),
EOptionInvertY = (1 << 30),
EOptionDumpBareVersion = (1 << 31),
enum TOptions : uint64_t {
EOptionNone = 0,
EOptionIntermediate = (1ull << 0),
EOptionSuppressInfolog = (1ull << 1),
EOptionMemoryLeakMode = (1ull << 2),
EOptionRelaxedErrors = (1ull << 3),
EOptionGiveWarnings = (1ull << 4),
EOptionLinkProgram = (1ull << 5),
EOptionMultiThreaded = (1ull << 6),
EOptionDumpConfig = (1ull << 7),
EOptionDumpReflection = (1ull << 8),
EOptionSuppressWarnings = (1ull << 9),
EOptionDumpVersions = (1ull << 10),
EOptionSpv = (1ull << 11),
EOptionHumanReadableSpv = (1ull << 12),
EOptionVulkanRules = (1ull << 13),
EOptionDefaultDesktop = (1ull << 14),
EOptionOutputPreprocessed = (1ull << 15),
EOptionOutputHexadecimal = (1ull << 16),
EOptionReadHlsl = (1ull << 17),
EOptionCascadingErrors = (1ull << 18),
EOptionAutoMapBindings = (1ull << 19),
EOptionFlattenUniformArrays = (1ull << 20),
EOptionNoStorageFormat = (1ull << 21),
EOptionKeepUncalled = (1ull << 22),
EOptionHlslOffsets = (1ull << 23),
EOptionHlslIoMapping = (1ull << 24),
EOptionAutoMapLocations = (1ull << 25),
EOptionDebug = (1ull << 26),
EOptionStdin = (1ull << 27),
EOptionOptimizeDisable = (1ull << 28),
EOptionOptimizeSize = (1ull << 29),
EOptionInvertY = (1ull << 30),
EOptionDumpBareVersion = (1ull << 31),
EOptionCompileOnly = (1ull << 32),
};
bool targetHlslFunctionality1 = false;
bool SpvToolsDisassembler = false;
@ -166,7 +167,7 @@ void ProcessConfigFile()
}
int ReflectOptions = EShReflectionDefault;
int Options = 0;
std::underlying_type_t<TOptions> Options = EOptionNone;
const char* ExecutableName = nullptr;
const char* binaryFileName = nullptr;
const char* depencyFileName = nullptr;
@ -889,6 +890,8 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
bumpArg();
} else if (lowerword == "version") {
Options |= EOptionDumpVersions;
} else if (lowerword == "no-link") {
Options |= EOptionCompileOnly;
} else if (lowerword == "help") {
usage();
break;
@ -1310,6 +1313,7 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
//
glslang::TProgram& program = *new glslang::TProgram;
const bool compileOnly = (Options & EOptionCompileOnly) != 0;
for (auto it = compUnits.cbegin(); it != compUnits.cend(); ++it) {
const auto &compUnit = *it;
for (int i = 0; i < compUnit.count; i++) {
@ -1326,6 +1330,9 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
shader->setSourceEntryPoint(sourceEntryPointName);
}
if (compileOnly)
shader->setCompileOnly();
shader->setOverrideVersion(GlslVersion);
std::string intrinsicString = getIntrinsic(compUnit.text, compUnit.count);
@ -1445,7 +1452,8 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
if (! shader->parse(GetResources(), defaultVersion, false, messages, includer))
CompileFailed = true;
program.addShader(shader);
if (!compileOnly)
program.addShader(shader);
if (! (Options & EOptionSuppressInfolog) &&
! (Options & EOptionMemoryLeakMode)) {
@ -1460,27 +1468,28 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
// Program-level processing...
//
// Link
if (! (Options & EOptionOutputPreprocessed) && ! program.link(messages))
LinkFailed = true;
// Map IO
if (Options & EOptionSpv) {
if (!program.mapIO())
if (!compileOnly) {
// Link
if (!(Options & EOptionOutputPreprocessed) && !program.link(messages))
LinkFailed = true;
}
// Report
if (! (Options & EOptionSuppressInfolog) &&
! (Options & EOptionMemoryLeakMode)) {
PutsIfNonEmpty(program.getInfoLog());
PutsIfNonEmpty(program.getInfoDebugLog());
}
// Map IO
if (Options & EOptionSpv) {
if (!program.mapIO())
LinkFailed = true;
}
// Reflect
if (Options & EOptionDumpReflection) {
program.buildReflection(ReflectOptions);
program.dumpReflection();
// Report
if (!(Options & EOptionSuppressInfolog) && !(Options & EOptionMemoryLeakMode)) {
PutsIfNonEmpty(program.getInfoLog());
PutsIfNonEmpty(program.getInfoDebugLog());
}
// Reflect
if (Options & EOptionDumpReflection) {
program.buildReflection(ReflectOptions);
program.dumpReflection();
}
}
std::vector<std::string> outputFiles;
@ -1490,44 +1499,58 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
if (CompileFailed || LinkFailed)
printf("SPIR-V is not generated for failed compile or link\n");
else {
for (int stage = 0; stage < EShLangCount; ++stage) {
if (program.getIntermediate((EShLanguage)stage)) {
std::vector<unsigned int> spirv;
spv::SpvBuildLogger logger;
glslang::SpvOptions spvOptions;
if (Options & EOptionDebug) {
spvOptions.generateDebugInfo = true;
if (emitNonSemanticShaderDebugInfo) {
spvOptions.emitNonSemanticShaderDebugInfo = true;
if (emitNonSemanticShaderDebugSource) {
spvOptions.emitNonSemanticShaderDebugSource = true;
}
}
} else if (stripDebugInfo)
spvOptions.stripDebugInfo = true;
spvOptions.disableOptimizer = (Options & EOptionOptimizeDisable) != 0;
spvOptions.optimizeSize = (Options & EOptionOptimizeSize) != 0;
spvOptions.disassemble = SpvToolsDisassembler;
spvOptions.validate = SpvToolsValidate;
glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv, &logger, &spvOptions);
// Dump the spv to a file or stdout, etc., but only if not doing
// memory/perf testing, as it's not internal to programmatic use.
if (! (Options & EOptionMemoryLeakMode)) {
printf("%s", logger.getAllMessages().c_str());
if (Options & EOptionOutputHexadecimal) {
if (!glslang::OutputSpvHex(spirv, GetBinaryName((EShLanguage)stage), variableName))
exit(EFailUsage);
} else {
if (!glslang::OutputSpvBin(spirv, GetBinaryName((EShLanguage)stage)))
exit(EFailUsage);
}
outputFiles.push_back(GetBinaryName((EShLanguage)stage));
if (!SpvToolsDisassembler && (Options & EOptionHumanReadableSpv))
spv::Disassemble(std::cout, spirv);
std::vector<glslang::TIntermediate*> intermediates;
if (!compileOnly) {
for (int stage = 0; stage < EShLangCount; ++stage) {
if (auto* i = program.getIntermediate((EShLanguage)stage)) {
intermediates.emplace_back(i);
}
}
} else {
for (const auto* shader : shaders) {
if (auto* i = shader->getIntermediate()) {
intermediates.emplace_back(i);
}
}
}
for (auto* intermediate : intermediates) {
std::vector<unsigned int> spirv;
spv::SpvBuildLogger logger;
glslang::SpvOptions spvOptions;
if (Options & EOptionDebug) {
spvOptions.generateDebugInfo = true;
if (emitNonSemanticShaderDebugInfo) {
spvOptions.emitNonSemanticShaderDebugInfo = true;
if (emitNonSemanticShaderDebugSource) {
spvOptions.emitNonSemanticShaderDebugSource = true;
}
}
} else if (stripDebugInfo)
spvOptions.stripDebugInfo = true;
spvOptions.disableOptimizer = (Options & EOptionOptimizeDisable) != 0;
spvOptions.optimizeSize = (Options & EOptionOptimizeSize) != 0;
spvOptions.disassemble = SpvToolsDisassembler;
spvOptions.validate = SpvToolsValidate;
spvOptions.compileOnly = compileOnly;
glslang::GlslangToSpv(*intermediate, spirv, &logger, &spvOptions);
// Dump the spv to a file or stdout, etc., but only if not doing
// memory/perf testing, as it's not internal to programmatic use.
if (!(Options & EOptionMemoryLeakMode)) {
printf("%s", logger.getAllMessages().c_str());
const auto filename = GetBinaryName(intermediate->getStage());
if (Options & EOptionOutputHexadecimal) {
if (!glslang::OutputSpvHex(spirv, filename, variableName))
exit(EFailUsage);
} else {
if (!glslang::OutputSpvBin(spirv, filename))
exit(EFailUsage);
}
outputFiles.push_back(filename);
if (!SpvToolsDisassembler && (Options & EOptionHumanReadableSpv))
spv::Disassemble(std::cout, spirv);
}
}
}
}
@ -2075,7 +2098,10 @@ void usage()
" --variable-name <name>\n"
" --vn <name> creates a C header file that contains a\n"
" uint32_t array named <name>\n"
" initialized with the shader binary code\n");
" initialized with the shader binary code\n"
" --no-link Only compile shader; do not link (GLSL-only)\n"
" NOTE: this option will set the export linkage\n"
" attribute on all functions\n");
exit(EFailUsage);
}