diff --git a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp index fd0c08e43..959de54ce 100644 --- a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp @@ -101,7 +101,25 @@ namespace { using namespace Containers::Literals; -Containers::StringView formatForExtension(const char* prefix, const Containers::StringView filename) { +Containers::StringView stringForFormat(const Format format) { + switch(format) { + #define _c(format) case Format::format: return #format ## _s; + _c(Glsl) + _c(Spirv) + _c(SpirvAssembly) + _c(Hlsl) + _c(Msl) + _c(Wgsl) + _c(Dxil) + #undef _c + + case Format::Unspecified: return {}; + } + + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +Format formatForExtension(const char* prefix, const Containers::StringView filename) { /** @todo lowercase only the extension, once Directory::split() is done */ const std::string normalized = Utility::String::lowercase(filename); @@ -126,7 +144,7 @@ Containers::StringView formatForExtension(const char* prefix, const Containers:: Utility::String::endsWith(normalized, ".asm.rcall") || Utility::String::endsWith(normalized, ".asm.mesh") || Utility::String::endsWith(normalized, ".asm.task")) - return "SpirvAssembly"_s; + return Format::SpirvAssembly; /* https://github.com/KhronosGroup/glslang/blob/3ce148638bdc3807316e358dee4a5c9583189ae7/StandAlone/StandAlone.cpp#L260-L274 */ else if(Utility::String::endsWith(normalized, ".glsl") || Utility::String::endsWith(normalized, ".vert") || @@ -143,9 +161,9 @@ Containers::StringView formatForExtension(const char* prefix, const Containers:: Utility::String::endsWith(normalized, ".rcall") || Utility::String::endsWith(normalized, ".mesh") || Utility::String::endsWith(normalized, ".task")) - return "Glsl"_s; + return Format::Glsl; else if(Utility::String::endsWith(normalized, ".spv")) - return "Spirv"_s; + return Format::Spirv; Error{} << prefix << "cannot determine the format of" << filename; return {}; @@ -156,9 +174,15 @@ Containers::StringView formatForExtension(const char* prefix, const Containers:: std::pair AnyConverter::doValidateFile(const Stage stage, const Containers::StringView filename) { CORRADE_INTERNAL_ASSERT(manager()); - /* Decide on a plugin name based on the extension */ - const Containers::StringView format = formatForExtension("ShaderTools::AnyConverter::validateFile():", filename); + /* Prefer the explicitly set input format. If not set, fall back to + detecting based on extension. */ + const Containers::StringView format = stringForFormat( + _state->inputFormat != Format::Unspecified ? _state->inputFormat : + formatForExtension("ShaderTools::AnyConverter::validateFile():", filename) + ); if(format.isEmpty()) return {}; + + /* Decide on a plugin name based on the format */ const std::string plugin = Utility::formatString("{}ShaderConverter", format); /* Try to load the plugin */ @@ -207,12 +231,19 @@ std::pair AnyConverter::doValidateFile(const Stage sta bool AnyConverter::doConvertFileToFile(const Stage stage, const Containers::StringView from, const Containers::StringView to) { CORRADE_INTERNAL_ASSERT(manager()); - /* Decide on a plugin name based on the input and output extension. This - might result in invalid combinations such as SpirvToGlslShaderConverter - which can't be really handled yet but I think that's okay for now */ - const Containers::StringView formatFrom = formatForExtension("ShaderTools::AnyConverter::convertFileToFile():", from); - const Containers::StringView formatTo = formatForExtension("ShaderTools::AnyConverter::convertFileToFile():", to); + /* Prefer the explicitly set input format. If not set, fall back to + detecting based on input and output extension. */ + const Containers::StringView formatFrom = stringForFormat( + _state->inputFormat != Format::Unspecified ? _state->inputFormat : formatForExtension("ShaderTools::AnyConverter::convertFileToFile():", from) + ); + const Containers::StringView formatTo = stringForFormat( + _state->outputFormat != Format::Unspecified ? _state->outputFormat : formatForExtension("ShaderTools::AnyConverter::convertFileToFile():", to) + ); if(formatFrom.isEmpty() || formatTo.isEmpty()) return {}; + + /* Decide on a plugin name based on the format. This might result in + invalid combinations such as SpirvToGlslShaderConverter which can't be + really handled yet but I think that's okay for now. */ const std::string plugin = Utility::formatString( formatFrom == formatTo ? "{}ShaderConverter" : "{}To{}ShaderConverter", formatFrom, formatTo); diff --git a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h index 46521b32f..950e54b92 100644 --- a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h +++ b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h @@ -57,8 +57,10 @@ namespace Magnum { namespace ShaderTools { @m_keywords{AnyShaderConverter} -Detects file type based on file extension, loads corresponding plugin and then -tries to either validate or convert the file with it. Detected file formats: +Loads a plugin corresponding to a format either explicitly set using +@ref setInputFormat() / @ref setOutputFormat() or detected based on input / +output file extension plugin and then tries to either validate or convert the +file with it. These formats are detected based on extension: - GLSL (`*.glsl`, `*.vert`, `*.frag`, `*.geom`, `*.comp`, `*.tesc`, `*.tese`, `*.rgen`, `*.rint`, `*.rahit`, `*.rchit`, `*.rmiss`, `*.rcall`, `*.mesh`, diff --git a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp index a0cc2faf2..3bea57e53 100644 --- a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp @@ -64,7 +64,9 @@ struct AnyConverterTest: TestSuite::Tester { void convertPropagateOptimization(); void detectValidate(); + void detectValidateExplicitFormat(); void detectConvert(); + void detectConvertExplicitFormat(); void unknown(); @@ -120,9 +122,13 @@ AnyConverterTest::AnyConverterTest() { addInstancedTests({&AnyConverterTest::detectValidate}, Containers::arraySize(DetectValidateData)); + addTests({&AnyConverterTest::detectValidateExplicitFormat}); + addInstancedTests({&AnyConverterTest::detectConvert}, Containers::arraySize(DetectConvertData)); + addTests({&AnyConverterTest::detectConvertExplicitFormat}); + addTests({&AnyConverterTest::unknown}); /* Load the plugin directly from the build tree. Otherwise it's static and @@ -567,6 +573,27 @@ void AnyConverterTest::detectValidate() { #endif } +void AnyConverterTest::detectValidateExplicitFormat() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + /* It should pick up this format and not bother with the extension */ + converter->setInputFormat(Format::Hlsl); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_COMPARE(converter->validateFile({}, "file.spv"), + std::make_pair(false, "")); + #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin HlslShaderConverter is not static and was not found in nonexistent\n" + "ShaderTools::AnyConverter::validateFile(): cannot load the HlslShaderConverter plugin\n"); + #else + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin HlslShaderConverter was not found\n" + "ShaderTools::AnyConverter::validateFile(): cannot load the HlslShaderConverter plugin\n"); + #endif +} + void AnyConverterTest::detectConvert() { auto&& data = DetectConvertData[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -587,6 +614,27 @@ void AnyConverterTest::detectConvert() { #endif } +void AnyConverterTest::detectConvertExplicitFormat() { + Containers::Pointer converter = _manager.instantiate("AnyShaderConverter"); + + /* It should pick up this format and not bother with the extension */ + converter->setInputFormat(Format::Hlsl); + converter->setOutputFormat(Format::Wgsl); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToFile({}, "file.spv", Utility::Directory::join(ANYSHADERCONVERTER_TEST_OUTPUT_DIR, "file.glsl"))); + #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin HlslToWgslShaderConverter is not static and was not found in nonexistent\n" + "ShaderTools::AnyConverter::convertFileToFile(): cannot load the HlslToWgslShaderConverter plugin\n"); + #else + CORRADE_COMPARE(out.str(), + "PluginManager::Manager::load(): plugin HlslToWgslShaderConverter was not found\n" + "ShaderTools::AnyConverter::convertFileToFile(): cannot load the HlslToWgslShaderConverter plugin\n"); + #endif +} + void AnyConverterTest::unknown() { std::ostringstream output; Error redirectError{&output};