Browse Source

shaderconverter: add a very basic --info option.

pull/495/head
Vladimír Vondruš 5 years ago
parent
commit
c879f91f5a
  1. 133
      src/Magnum/ShaderTools/shaderconverter.cpp

133
src/Magnum/ShaderTools/shaderconverter.cpp

@ -34,6 +34,7 @@
#include "Magnum/Math/Functions.h" #include "Magnum/Math/Functions.h"
#include "Magnum/ShaderTools/AbstractConverter.h" #include "Magnum/ShaderTools/AbstractConverter.h"
#include "Magnum/ShaderTools/Stage.h" #include "Magnum/ShaderTools/Stage.h"
#include "Magnum/ShaderTools/Implementation/spirv.h"
namespace Magnum { namespace Magnum {
@ -63,10 +64,10 @@ information.
@code{.sh} @code{.sh}
magnum-shaderconverter [-h|--help] [--validate] [--link] magnum-shaderconverter [-h|--help] [--validate] [--link]
[-C|--converter NAME]... [--plugin-dir DIR] [-C|--converter NAME]... [--plugin-dir DIR]
[-c|--converter-options key=val,key2=val2,]... [-q|--quiet] [-v|--verbose] [-c|--converter-options key=val,key2=val2,]... [--info] [-q|--quiet]
[--warning-as-error] [-E|--preprocess-only] [-D|--define name=value]... [-v|--verbose] [--warning-as-error] [-E|--preprocess-only]
[-U|--undefine name]... [-O|--optimize LEVEL] [-g|--debug-info LEVEL] [-D|--define name=value]... [-U|--undefine name]... [-O|--optimize LEVEL]
[--input-format glsl|spv|spvasm|hlsl|metal]... [-g|--debug-info LEVEL] [--input-format glsl|spv|spvasm|hlsl|metal]...
[--output-format glsl|spv|spvasm|hlsl|metal]... [--output-format glsl|spv|spvasm|hlsl|metal]...
[--input-version VERSION]... [--output-version VERSION]... [--input-version VERSION]... [--output-version VERSION]...
[--] input... output [--] input... output
@ -75,9 +76,10 @@ magnum-shaderconverter [-h|--help] [--validate] [--link]
Arguments: Arguments:
- `input` --- input file(s) - `input` --- input file(s)
- `output` --- output file, disallowed for `--validate`. If neither - `output` --- output file; ignored if `--info` is present, disallowed for
`--validate` nor `--link` is present, corresponds to the `--validate`. If neither `--info`, `--validate` nor `--link` is present,
@ref ShaderTools::AbstractConverter::convertFileToFile() function. corresponds to the @ref ShaderTools::AbstractConverter::convertFileToFile()
function.
- `-h`, `--help` --- display this help message and exit - `-h`, `--help` --- display this help message and exit
- `--validate` --- validate input. Corresponds to the - `--validate` --- validate input. Corresponds to the
@ref ShaderTools::AbstractConverter::validateFile() function. @ref ShaderTools::AbstractConverter::validateFile() function.
@ -87,6 +89,7 @@ Arguments:
- `--plugin-dir DIR` --- override base plugin dir - `--plugin-dir DIR` --- override base plugin dir
- `-c`, `--converter-options key=val,key2=val2,` --- configuration options - `-c`, `--converter-options key=val,key2=val2,` --- configuration options
to pass to the converter(s) to pass to the converter(s)
- `--info` --- print SPIR-V module info and exit
- `-q`, `--quiet` --- quiet output from converter plugin(s). Corresponds to - `-q`, `--quiet` --- quiet output from converter plugin(s). Corresponds to
the @ref ShaderTools::ConverterFlag::Quiet flag. the @ref ShaderTools::ConverterFlag::Quiet flag.
- `-v`, `--verbose` --- verbose output from converter plugin(s). Corresponds - `-v`, `--verbose` --- verbose output from converter plugin(s). Corresponds
@ -170,15 +173,71 @@ magnum-shaderconverter phong.frag -DDIFFUSE_TEXTURE -DNORMAL_TEXTURE --input-ver
using namespace Corrade::Containers::Literals; using namespace Corrade::Containers::Literals;
using namespace Magnum; using namespace Magnum;
namespace {
ShaderTools::Stage spvExecutionModelToStage(const SpvExecutionModel model) {
switch(model) {
#define _c(model, stage) case SpvExecutionModel ## model: return ShaderTools::Stage::stage;
_c(Vertex, Vertex)
_c(Fragment, Fragment)
_c(Geometry, Geometry)
_c(TessellationControl, TessellationControl)
_c(TessellationEvaluation, TessellationEvaluation)
_c(GLCompute, Compute)
_c(RayGenerationKHR, RayGeneration)
_c(AnyHitKHR, RayAnyHit)
_c(ClosestHitKHR, RayClosestHit)
_c(MissKHR, RayMiss)
_c(IntersectionKHR, RayIntersection)
_c(CallableKHR, RayCallable)
_c(TaskNV, MeshTask)
_c(MeshNV, Mesh)
_c(Kernel, Kernel)
#undef _c
case SpvExecutionModelMax: break;
}
/* Encode unknown stages with the highest bit set. SpvExecutionModelMax is
0x7fffffff, so this shouldn't lead to any data loss. */
return ShaderTools::Stage((1u << 31)|model);
}
void printSpirvInfo(Containers::ArrayView<const UnsignedInt> data) {
while(Containers::Optional<ShaderTools::Implementation::SpirvEntrypoint> entrypoint = ShaderTools::Implementation::spirvNextEntrypoint(data)) {
Debug d;
d << "Entrypoint" << entrypoint->name << "(" << Debug::nospace << spvExecutionModelToStage(entrypoint->executionModel) << Debug::nospace << ")" << Debug::newline;
Containers::Array<ShaderTools::Implementation::SpirvEntrypointInterface> interface{Containers::ValueInit, entrypoint->interfaces.size()};
ShaderTools::Implementation::spirvEntrypointInterface(data, *entrypoint, interface);
for(const ShaderTools::Implementation::SpirvEntrypointInterface& i: interface) {
d << " ";
if(!i.storageClass) d << "(unknown)";
else if(*i.storageClass == SpvStorageClassInput)
d << "in";
else if(*i.storageClass == SpvStorageClassOutput)
d << "out";
else d << "SpvStorageClass(" << Debug::nospace << *i.storageClass << Debug::nospace << ")";
if(i.location) d << "(location=" << Debug::nospace << *i.location << Debug::nospace << ")";
d << Debug::newline;
}
}
}
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
Utility::Arguments args; Utility::Arguments args;
args.addArrayArgument("input").setHelp("input", "input file(s)") args.addArrayArgument("input").setHelp("input", "input file(s)")
.addArgument("output").setHelp("output", "output file, disallowed for --validate") .addArgument("output").setHelp("output", "output file; ignored if --info is present, disallowed for --validate")
.addBooleanOption("validate").setHelp("validate", "validate input") .addBooleanOption("validate").setHelp("validate", "validate input")
.addBooleanOption("link").setHelp("link", "link multiple input files together") .addBooleanOption("link").setHelp("link", "link multiple input files together")
.addArrayOption('C', "converter").setHelp("converter", "shader converter plugin(s)") .addArrayOption('C', "converter").setHelp("converter", "shader converter plugin(s)")
.addOption("plugin-dir").setHelp("plugin-dir", "override base plugin dir", "DIR") .addOption("plugin-dir").setHelp("plugin-dir", "override base plugin dir", "DIR")
.addArrayOption('c', "converter-options").setHelp("converter-options", "configuration options to pass to the converter(s)", "key=val,key2=val2,…") .addArrayOption('c', "converter-options").setHelp("converter-options", "configuration options to pass to the converter(s)", "key=val,key2=val2,…")
.addBooleanOption("info").setHelp("info", "print SPIR-V module info and exit")
.addBooleanOption('q', "quiet").setHelp("quiet", "quiet output from converter plugin(s)") .addBooleanOption('q', "quiet").setHelp("quiet", "quiet output from converter plugin(s)")
.addBooleanOption('v', "verbose").setHelp("verbose", "verbose output from converter plugin(s)") .addBooleanOption('v', "verbose").setHelp("verbose", "verbose output from converter plugin(s)")
.addBooleanOption("warning-as-error").setHelp("warning-as-error", "treat warnings as errors") .addBooleanOption("warning-as-error").setHelp("warning-as-error", "treat warnings as errors")
@ -193,15 +252,21 @@ int main(int argc, char** argv) {
.addArrayOption("input-version").setHelp("input-version", "input format version for each converter", "VERSION") .addArrayOption("input-version").setHelp("input-version", "input format version for each converter", "VERSION")
.addArrayOption("output-version").setHelp("output-version", "output format version for each converter", "VERSION") .addArrayOption("output-version").setHelp("output-version", "output format version for each converter", "VERSION")
.setParseErrorCallback([](const Utility::Arguments& args, Utility::Arguments::ParseError error, const std::string& key) { .setParseErrorCallback([](const Utility::Arguments& args, Utility::Arguments::ParseError error, const std::string& key) {
/* If --validate is passed, we don't need the output argument */ /* If --info / --validate is passed, we don't need the output
argument */
if(error == Utility::Arguments::ParseError::MissingArgument && if(error == Utility::Arguments::ParseError::MissingArgument &&
key == "output" && args.isSet("validate")) return true; key == "output" && (args.isSet("info") || args.isSet("validate")))
return true;
/* Handle all other errors as usual */ /* Handle all other errors as usual */
return false; return false;
}) })
.setGlobalHelp(R"(Converts, compiles, optimizes and links shaders of different formats. .setGlobalHelp(R"(Converts, compiles, optimizes and links shaders of different formats.
If --info is given and the input looks like a SPIR-V binary, the utility prints
information about its entrypoints using builtin SPIR-V reflection capabilities.
If it's not a SPIR-V binary, it's converted to it first.
If --validate is given, the utility will validate the input file using passed If --validate is given, the utility will validate the input file using passed
--converter (or AnyShaderConverter if none is specified), print the validation --converter (or AnyShaderConverter if none is specified), print the validation
log on output and exit with a non-zero code if the validation fails. If --link log on output and exit with a non-zero code if the validation fails. If --link
@ -237,6 +302,12 @@ see documentation of a particular converter for more information.)")
Error{} << "Output file shouldn't be set for --validate:" << args.value<Containers::StringView>("output"); Error{} << "Output file shouldn't be set for --validate:" << args.value<Containers::StringView>("output");
return 1; return 1;
} }
/* Not an error in this case, it should be possible to just append
--info to existing command line without having to remove anything.
But print a warning at least, it could also be a mistyped option. */
if(args.isSet("info"))
Warning{} << "Ignoring output file for --info:" << args.value<Containers::StringView>("output");
} }
if(!args.isSet("link")) { if(!args.isSet("link")) {
if(args.arrayValueCount("input") != 1) { if(args.arrayValueCount("input") != 1) {
@ -264,6 +335,17 @@ see documentation of a particular converter for more information.)")
return 6; return 6;
} }
/* If we want just SPIR-V info and the input looks like a SPIR-V binary,
do that right away without going through any plugin. If it doesn't, we
try again after using a converter.s */
if(args.isSet("info")) {
const Containers::Array<char> data = Utility::Directory::read(args.arrayValue("input", 0));
if(Containers::ArrayView<const UnsignedInt> spirv = ShaderTools::Implementation::spirvData(data, data.size())) {
printSpirvInfo(spirv);
return 0;
}
}
/* Set up a converter manager */ /* Set up a converter manager */
PluginManager::Manager<ShaderTools::AbstractConverter> converterManager{ PluginManager::Manager<ShaderTools::AbstractConverter> converterManager{
args.value("plugin-dir").empty() ? std::string{} : args.value("plugin-dir").empty() ? std::string{} :
@ -286,8 +368,11 @@ see documentation of a particular converter for more information.)")
if(i < args.arrayValueCount("converter-options")) if(i < args.arrayValueCount("converter-options"))
Implementation::setOptions(*converter, args.arrayValue("converter-options", i)); Implementation::setOptions(*converter, args.arrayValue("converter-options", i));
/* Parse format, if passed */ /* Parse format, if passed. If --info is desired, implicitly set the
output format to SPIR-V */
ShaderTools::Format inputFormat{}, outputFormat{}; ShaderTools::Format inputFormat{}, outputFormat{};
if(args.isSet("info"))
outputFormat = ShaderTools::Format::Spirv;
auto parseFormat = [](Containers::StringView format) -> Containers::Optional<ShaderTools::Format> { auto parseFormat = [](Containers::StringView format) -> Containers::Optional<ShaderTools::Format> {
if(format == ""_s) return ShaderTools::Format::Unspecified; if(format == ""_s) return ShaderTools::Format::Unspecified;
if(format == "glsl"_s) return ShaderTools::Format::Glsl; if(format == "glsl"_s) return ShaderTools::Format::Glsl;
@ -387,6 +472,32 @@ see documentation of a particular converter for more information.)")
converter->setFlags(flags); converter->setFlags(flags);
/* If we want just SPIR-V info, convert to a SPIR-V and exit */
if(args.isSet("info")) {
/* The info exits right after, so this branch shouldn't get
re-entered again */
CORRADE_INTERNAL_ASSERT(i == 0);
if(!(converter->features() >= ShaderTools::ConverterFeature::ConvertData)) {
Error{} << converterName << "doesn't support data conversion";
return 18; /* same code as the same message below */
}
if(!(data = converter->convertFileToData(ShaderTools::Stage::Unspecified, args.arrayValue<Containers::StringView>("input", 0)))) {
Error{} << "Cannot convert" << args.arrayValue<Containers::StringView>("input", 0);
return 20; /* same code as the same message below */
}
Containers::ArrayView<const UnsignedInt> spirv = ShaderTools::Implementation::spirvData(data, data.size());
if(!spirv) {
Error{} << "The output is not a SPIR-V binary, can't print info";
return 23;
}
printSpirvInfo(spirv);
return 0;
}
/* If validating, do it just with the first passed converter and then /* If validating, do it just with the first passed converter and then
exit */ exit */
if(args.isSet("validate")) { if(args.isSet("validate")) {

Loading…
Cancel
Save