mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
752 lines
35 KiB
752 lines
35 KiB
/* |
|
This file is part of Magnum. |
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
|
2020, 2021 Vladimír Vondruš <mosra@centrum.cz> |
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a |
|
copy of this software and associated documentation files (the "Software"), |
|
to deal in the Software without restriction, including without limitation |
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
|
and/or sell copies of the Software, and to permit persons to whom the |
|
Software is furnished to do so, subject to the following conditions: |
|
|
|
The above copyright notice and this permission notice shall be included |
|
in all copies or substantial portions of the Software. |
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
DEALINGS IN THE SOFTWARE. |
|
*/ |
|
|
|
#include "AbstractConverter.h" |
|
|
|
#include <Corrade/Containers/Array.h> |
|
#include <Corrade/Containers/EnumSet.hpp> |
|
#include <Corrade/Containers/Optional.h> |
|
#include <Corrade/Containers/String.h> |
|
#include <Corrade/Containers/StringStl.h> |
|
#include <Corrade/Utility/DebugStl.h> |
|
#include <Corrade/Utility/Directory.h> |
|
|
|
#include "Magnum/FileCallback.h" |
|
|
|
#ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT |
|
#include "Magnum/ShaderTools/configure.h" |
|
#endif |
|
|
|
namespace Magnum { namespace ShaderTools { |
|
|
|
std::string AbstractConverter::pluginInterface() { |
|
return |
|
/* [interface] */ |
|
"cz.mosra.magnum.ShaderTools.AbstractConverter/0.1" |
|
/* [interface] */ |
|
; |
|
} |
|
|
|
#ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT |
|
std::vector<std::string> AbstractConverter::pluginSearchPaths() { |
|
return PluginManager::implicitPluginSearchPaths( |
|
#ifndef MAGNUM_BUILD_STATIC |
|
Utility::Directory::libraryLocation(&pluginInterface), |
|
#else |
|
{}, |
|
#endif |
|
#ifdef CORRADE_IS_DEBUG_BUILD |
|
MAGNUM_PLUGINS_SHADERCONVERTER_DEBUG_DIR, |
|
#else |
|
MAGNUM_PLUGINS_SHADERCONVERTER_DIR, |
|
#endif |
|
#ifdef CORRADE_IS_DEBUG_BUILD |
|
"magnum-d/" |
|
#else |
|
"magnum/" |
|
#endif |
|
"shaderconverters"); |
|
} |
|
#endif |
|
|
|
AbstractConverter::AbstractConverter() = default; |
|
|
|
AbstractConverter::AbstractConverter(PluginManager::Manager<AbstractConverter>& manager): PluginManager::AbstractManagingPlugin<AbstractConverter>{manager} {} |
|
|
|
AbstractConverter::AbstractConverter(PluginManager::AbstractManager& manager, const std::string& plugin): PluginManager::AbstractManagingPlugin<AbstractConverter>{manager, plugin} {} |
|
|
|
ConverterFeatures AbstractConverter::features() const { |
|
const ConverterFeatures features = doFeatures(); |
|
CORRADE_ASSERT(features & ~(ConverterFeature::InputFileCallback|ConverterFeature::Preprocess|ConverterFeature::Optimize|ConverterFeature::DebugInfo), |
|
"ShaderTools::AbstractConverter::features(): implementation reported no features", {}); |
|
return features; |
|
} |
|
|
|
void AbstractConverter::setFlags(const ConverterFlags flags) { |
|
CORRADE_ASSERT(!(flags >= (ConverterFlag::Quiet|ConverterFlag::Verbose)), |
|
"ShaderTools::AbstractConverter::setFlags(): can't have both Quiet and Verbose set", ); |
|
CORRADE_ASSERT((features() & ConverterFeature::Preprocess) || !(flags & ConverterFlag::PreprocessOnly), |
|
"ShaderTools::AbstractConverter::setFlags(): PreprocessOnly not supported by the implementation", ); |
|
_flags = flags; |
|
doSetFlags(flags); |
|
} |
|
|
|
void AbstractConverter::doSetFlags(ConverterFlags) {} |
|
|
|
void AbstractConverter::addFlags(ConverterFlags flags) { |
|
setFlags(_flags|flags); |
|
} |
|
|
|
void AbstractConverter::clearFlags(ConverterFlags flags) { |
|
setFlags(_flags & ~flags); |
|
} |
|
|
|
void AbstractConverter::setInputFileCallback(Containers::Optional<Containers::ArrayView<const char>>(*callback)(const std::string&, InputFileCallbackPolicy, void*), void* const userData) { |
|
/* Clearing the *File bits as those are present in *Data as well and thus |
|
this would pass even if only file conversion/validation is supported, |
|
which is wrong */ |
|
CORRADE_ASSERT(features() & (ConverterFeature::InputFileCallback|ConverterFeature::ValidateData|ConverterFeature::ConvertData|ConverterFeature::LinkData) & ~(ConverterFeature::ValidateFile|ConverterFeature::ConvertFile|ConverterFeature::LinkFile), |
|
"ShaderTools::AbstractConverter::setInputFileCallback(): converter supports neither loading from data nor via callbacks, callbacks can't be used", ); |
|
|
|
_inputFileCallback = callback; |
|
_inputFileCallbackUserData = userData; |
|
doSetInputFileCallback(callback, userData); |
|
} |
|
|
|
void AbstractConverter::doSetInputFileCallback(Containers::Optional<Containers::ArrayView<const char>>(*)(const std::string&, InputFileCallbackPolicy, void*), void*) {} |
|
|
|
void AbstractConverter::setInputFormat(const Format format, const Containers::StringView version) { |
|
return doSetInputFormat(format, version); |
|
} |
|
|
|
void AbstractConverter::setInputFormat(const Format format) { |
|
return setInputFormat(format, {}); |
|
} |
|
|
|
void AbstractConverter::setOutputFormat(const Format format, const Containers::StringView version) { |
|
return doSetOutputFormat(format, version); |
|
} |
|
|
|
void AbstractConverter::setOutputFormat(const Format format) { |
|
return setOutputFormat(format, {}); |
|
} |
|
|
|
void AbstractConverter::setDefinitions(const Containers::ArrayView<const std::pair<Containers::StringView, Containers::StringView>> definitions) { |
|
CORRADE_ASSERT(features() & ConverterFeature::Preprocess, |
|
"ShaderTools::AbstractConverter::setDefinitions(): feature not supported", ); |
|
doSetDefinitions(definitions); |
|
} |
|
|
|
void AbstractConverter::setDefinitions(std::initializer_list<std::pair<Containers::StringView, Containers::StringView>> definitions) { |
|
return setDefinitions(Containers::arrayView(definitions)); |
|
} |
|
|
|
void AbstractConverter::doSetDefinitions(Containers::ArrayView<const std::pair<Containers::StringView, Containers::StringView>>) { |
|
CORRADE_ASSERT_UNREACHABLE("ShaderTools::AbstractConverter::setDefinitions(): feature advertised but not implemented", ); |
|
} |
|
|
|
void AbstractConverter::setOptimizationLevel(const Containers::StringView level) { |
|
CORRADE_ASSERT(features() & ConverterFeature::Optimize, |
|
"ShaderTools::AbstractConverter::setOptimizationLevel(): feature not supported", ); |
|
doSetOptimizationLevel(level); |
|
} |
|
|
|
void AbstractConverter::doSetOptimizationLevel(Containers::StringView) { |
|
CORRADE_ASSERT_UNREACHABLE("ShaderTools::AbstractConverter::setOptimizationLevel(): feature advertised but not implemented", ); |
|
} |
|
|
|
void AbstractConverter::setDebugInfoLevel(const Containers::StringView level) { |
|
CORRADE_ASSERT(features() & ConverterFeature::DebugInfo, |
|
"ShaderTools::AbstractConverter::setDebugInfoLevel(): feature not supported", ); |
|
doSetDebugInfoLevel(level); |
|
} |
|
|
|
void AbstractConverter::doSetDebugInfoLevel(Containers::StringView) { |
|
CORRADE_ASSERT_UNREACHABLE("ShaderTools::AbstractConverter::setDebugInfoLevel(): feature advertised but not implemented", ); |
|
} |
|
|
|
std::pair<bool, Containers::String> AbstractConverter::validateData(const Stage stage, const Containers::ArrayView<const void> data) { |
|
CORRADE_ASSERT(features() & ConverterFeature::ValidateData, |
|
"ShaderTools::AbstractConverter::validateData(): feature not supported", {}); |
|
CORRADE_ASSERT(!(_flags & ConverterFlag::PreprocessOnly), |
|
"ShaderTools::AbstractConverter::validateData(): PreprocessOnly is not allowed in combination with validation", {}); |
|
|
|
/* Cast to a non-void type for more convenience */ |
|
std::pair<bool, Containers::String> out = doValidateData(stage, Containers::arrayCast<const char>(data)); |
|
CORRADE_ASSERT(out.second.isSmall() || !out.second.deleter(), |
|
"ShaderTools::AbstractConverter::validateData(): implementation is not allowed to use a custom String deleter", {}); |
|
return out; |
|
} |
|
|
|
std::pair<bool, Containers::String> AbstractConverter::doValidateData(Stage, Containers::ArrayView<const char>) { |
|
CORRADE_ASSERT_UNREACHABLE("ShaderTools::AbstractConverter::validateData(): feature advertised but not implemented", {}); |
|
} |
|
|
|
std::pair<bool, Containers::String> AbstractConverter::validateFile(const Stage stage, const Containers::StringView filename) { |
|
CORRADE_ASSERT(features() & (ConverterFeature::ValidateFile|ConverterFeature::ValidateData), |
|
"ShaderTools::AbstractConverter::validateFile(): feature not supported", {}); |
|
CORRADE_ASSERT(!(_flags & ConverterFlag::PreprocessOnly), |
|
"ShaderTools::AbstractConverter::validateFile(): PreprocessOnly is not allowed in combination with validation", {}); |
|
|
|
std::pair<bool, Containers::String> out; |
|
|
|
/* If input file callbacks are not set or the converter supports handling |
|
them directly, call into the implementation */ |
|
if(!_inputFileCallback || (doFeatures() & ConverterFeature::InputFileCallback)) { |
|
out = doValidateFile(stage, filename); |
|
|
|
/* Otherwise, if validating data is supported, use the callback and pass |
|
the data through to validateData(). Mark the file as ready to be closed |
|
once validating is finished. */ |
|
} else if(doFeatures() & ConverterFeature::ValidateData) { |
|
/* This needs to be duplicated here and in the doValidateFile() |
|
implementation in order to support both following cases: |
|
- plugins that don't support InputFileCallback but have their own |
|
doValidateFile() implementation (callback needs to be used here, |
|
because the base doValidateFile() implementation might never get |
|
called) |
|
- plugins that support InputFileCallback but want to delegate the |
|
actual file loading to the default implementation (callback used |
|
in the base doValidateFile() implementation, because this branch |
|
is never taken in that case) */ |
|
const Containers::Optional<Containers::ArrayView<const char>> data = _inputFileCallback(filename, InputFileCallbackPolicy::LoadTemporary, _inputFileCallbackUserData); |
|
if(!data) { |
|
Error{} << "ShaderTools::AbstractConverter::validateFile(): cannot open file" << filename; |
|
return {}; |
|
} |
|
out = doValidateData(stage, *data); |
|
_inputFileCallback(filename, InputFileCallbackPolicy::Close, _inputFileCallbackUserData); |
|
|
|
/* Shouldn't get here, the assert is fired already in setFileCallback() */ |
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ |
|
|
|
CORRADE_ASSERT(out.second.isSmall() || !out.second.deleter(), |
|
"ShaderTools::AbstractConverter::validateFile(): implementation is not allowed to use a custom String deleter", {}); |
|
return out; |
|
} |
|
|
|
std::pair<bool, Containers::String> AbstractConverter::doValidateFile(const Stage stage, const Containers::StringView filename) { |
|
CORRADE_ASSERT(features() >= ConverterFeature::ValidateData, "ShaderTools::AbstractConverter::validateFile(): feature advertised but not implemented", {}); |
|
|
|
/* If callbacks are set, use them. This is the same implementation as in |
|
validateFile(), see the comment there for details. */ |
|
if(_inputFileCallback) { |
|
const Containers::Optional<Containers::ArrayView<const char>> data = _inputFileCallback(filename, InputFileCallbackPolicy::LoadTemporary, _inputFileCallbackUserData); |
|
if(!data) { |
|
Error{} << "ShaderTools::AbstractConverter::validateFile(): cannot open file" << filename; |
|
return {}; |
|
} |
|
std::pair<bool, Containers::String> out = doValidateData(stage, *data); |
|
_inputFileCallback(filename, InputFileCallbackPolicy::Close, _inputFileCallbackUserData); |
|
return out; |
|
|
|
/* Otherwise open the file directly */ |
|
} else { |
|
if(!Utility::Directory::exists(filename)) { |
|
Error() << "ShaderTools::AbstractConverter::validateFile(): cannot open file" << filename; |
|
return {}; |
|
} |
|
|
|
return doValidateData(stage, Utility::Directory::read(filename)); |
|
} |
|
} |
|
|
|
Containers::Array<char> AbstractConverter::convertDataToData(const Stage stage, const Containers::ArrayView<const void> data) { |
|
CORRADE_ASSERT(features() >= ConverterFeature::ConvertData, |
|
"ShaderTools::AbstractConverter::convertDataToData(): feature not supported", {}); |
|
|
|
/* Cast to a non-void type for more convenience */ |
|
Containers::Array<char> out = doConvertDataToData(stage, Containers::arrayCast<const char>(data)); |
|
CORRADE_ASSERT(!out.deleter(), |
|
"ShaderTools::AbstractConverter::convertDataToData(): implementation is not allowed to use a custom Array deleter", {}); |
|
return out; |
|
} |
|
|
|
Containers::Array<char> AbstractConverter::doConvertDataToData(Stage, Containers::ArrayView<const char>) { |
|
CORRADE_ASSERT_UNREACHABLE("ShaderTools::AbstractConverter::convertDataToData(): feature advertised but not implemented", {}); |
|
} |
|
|
|
bool AbstractConverter::convertDataToFile(const Stage stage, const Containers::ArrayView<const void> data, const Containers::StringView to) { |
|
CORRADE_ASSERT(features() >= ConverterFeature::ConvertData, |
|
"ShaderTools::AbstractConverter::convertDataToFile(): feature not supported", {}); |
|
|
|
/** @todo this needs expansion once output callbacks are supported as well */ |
|
|
|
/* Cast to a non-void type for more convenience */ |
|
Containers::Array<char> out = doConvertDataToData(stage, Containers::arrayCast<const char>(data)); |
|
if(!out) return false; |
|
|
|
if(!Utility::Directory::write(to, out)) { |
|
Error{} << "ShaderTools::AbstractConverter::convertDataToFile(): cannot write to file" << to; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
Containers::Array<char> AbstractConverter::convertDataToDataUsingInputFileCallbacks(const char* const prefix, const Stage stage, const Containers::StringView from) { |
|
const Containers::Optional<Containers::ArrayView<const char>> data = _inputFileCallback(from, InputFileCallbackPolicy::LoadTemporary, _inputFileCallbackUserData); |
|
if(!data) { |
|
Error{} << prefix << "cannot open file" << from; |
|
return {}; |
|
} |
|
Containers::Array<char> out = doConvertDataToData(stage, *data); |
|
_inputFileCallback(from, InputFileCallbackPolicy::Close, _inputFileCallbackUserData); |
|
return out; |
|
} |
|
|
|
bool AbstractConverter::convertFileToFile(const Stage stage, const Containers::StringView from, const Containers::StringView to) { |
|
CORRADE_ASSERT(features() & (ConverterFeature::ConvertFile|ConverterFeature::ConvertData), |
|
"ShaderTools::AbstractConverter::convertFileToFile(): feature not supported", {}); |
|
|
|
/** @todo this needs expansion once output callbacks are supported as well */ |
|
|
|
/* If input file callbacks are not set or the converter supports handling |
|
them directly, call into the implementation */ |
|
if(!_inputFileCallback || (doFeatures() & ConverterFeature::InputFileCallback)) { |
|
return doConvertFileToFile(stage, from, to); |
|
|
|
/* Otherwise, if converting data is supported, use the callback and pass |
|
the data through to convertDataToData(). Mark the file as ready to be |
|
closed once conversion is finished. */ |
|
} else if(doFeatures() & ConverterFeature::ConvertData) { |
|
/* This needs to be duplicated here and in the doConvertFileToFile() |
|
implementation in order to support both following cases: |
|
- plugins that don't support InputFileCallback but have their own |
|
doConvertFileToFile() implementation (callback needs to be used |
|
here, because the base doConvertFileToFile() implementation might |
|
never get called) |
|
- plugins that support InputFileCallback but want to delegate the |
|
actual file loading to the default implementation (callback used |
|
in the base doConvertFileToFile() implementation, because this |
|
branch is never taken in that case) */ |
|
Containers::Array<char> out = convertDataToDataUsingInputFileCallbacks("ShaderTools::AbstractConverter::convertFileToFile():", stage, from); |
|
if(!out) return false; |
|
|
|
if(!Utility::Directory::write(to, out)) { |
|
Error{} << "ShaderTools::AbstractConverter::convertFileToFile(): cannot write to file" << to; |
|
return false; |
|
} |
|
|
|
return true; |
|
|
|
/* Shouldn't get here, the assert is fired already in setFileCallback() */ |
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ |
|
} |
|
|
|
bool AbstractConverter::doConvertFileToFile(const Stage stage, const Containers::StringView from, const Containers::StringView to) { |
|
CORRADE_ASSERT(features() >= ConverterFeature::ConvertData, "ShaderTools::AbstractConverter::convertFileToFile(): feature advertised but not implemented", {}); |
|
|
|
/** @todo this needs expansion once output callbacks are supported as well */ |
|
Containers::Array<char> out; |
|
|
|
/* If callbacks are set, use them. This is the same implementation as in |
|
convertFileToFile(), see the comment there for details. */ |
|
if(_inputFileCallback) { |
|
out = convertDataToDataUsingInputFileCallbacks("ShaderTools::AbstractConverter::convertFileToFile():", stage, from); |
|
|
|
/* Otherwise open the file directly */ |
|
} else { |
|
if(!Utility::Directory::exists(from)) { |
|
Error() << "ShaderTools::AbstractConverter::convertFileToFile(): cannot open file" << from; |
|
return {}; |
|
} |
|
|
|
out = doConvertDataToData(stage, Utility::Directory::read(from)); |
|
} |
|
|
|
if(!out) return false; |
|
|
|
if(!Utility::Directory::write(to, out)) { |
|
Error{} << "ShaderTools::AbstractConverter::convertFileToFile(): cannot write to file" << to; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
Containers::Array<char> AbstractConverter::convertFileToData(const Stage stage, const Containers::StringView from) { |
|
CORRADE_ASSERT(features() >= ConverterFeature::ConvertData, |
|
"ShaderTools::AbstractConverter::convertFileToData(): feature not supported", {}); |
|
|
|
Containers::Array<char> out; |
|
|
|
/* If input file callbacks are not set or the converter supports handling |
|
them directly, call into the implementation */ |
|
if(!_inputFileCallback || (doFeatures() & ConverterFeature::InputFileCallback)) { |
|
out = doConvertFileToData(stage, from); |
|
|
|
/* Otherwise use the callback and pass the data through to |
|
convertDataToData(). Mark the file as ready to be closed once conversion |
|
is finished. */ |
|
} else { |
|
/* This needs to be duplicated here and in the doConvertFileToData() |
|
implementation in order to support both following cases: |
|
- plugins that don't support InputFileCallback but have their own |
|
doConvertFileToData() implementation (callback needs to be used |
|
here, because the base doConvertFileToData() implementation might |
|
never get called) |
|
- plugins that support InputFileCallback but want to delegate the |
|
actual file loading to the default implementation (callback used |
|
in the base doConvertFileToData() implementation, because this |
|
branch is never taken in that case) */ |
|
out = convertDataToDataUsingInputFileCallbacks("ShaderTools::AbstractConverter::convertFileToData():", stage, from); |
|
} |
|
|
|
CORRADE_ASSERT(!out.deleter(), |
|
"ShaderTools::AbstractConverter::convertFileToData(): implementation is not allowed to use a custom Array deleter", {}); |
|
return out; |
|
} |
|
|
|
Containers::Array<char> AbstractConverter::doConvertFileToData(const Stage stage, const Containers::StringView from) { |
|
/* If callbacks are set, use them. This is the same implementation as in |
|
convertFileToFile(), see the comment there for details. */ |
|
if(_inputFileCallback) { |
|
return convertDataToDataUsingInputFileCallbacks("ShaderTools::AbstractConverter::convertFileToData():", stage, from); |
|
|
|
/* Otherwise open the file directly */ |
|
} else { |
|
if(!Utility::Directory::exists(from)) { |
|
Error() << "ShaderTools::AbstractConverter::convertFileToData(): cannot open file" << from; |
|
return {}; |
|
} |
|
|
|
return doConvertDataToData(stage, Utility::Directory::read(from)); |
|
} |
|
} |
|
|
|
Containers::Array<char> AbstractConverter::linkDataToData(const Containers::ArrayView<const std::pair<Stage, Containers::ArrayView<const void>>> data) { |
|
CORRADE_ASSERT(features() >= ConverterFeature::LinkData, |
|
"ShaderTools::AbstractConverter::linkDataToData(): feature not supported", {}); |
|
CORRADE_ASSERT(!(_flags & ConverterFlag::PreprocessOnly), |
|
"ShaderTools::AbstractConverter::linkDataToData(): PreprocessOnly is not allowed in combination with linking", {}); |
|
CORRADE_ASSERT(!data.empty(), |
|
"ShaderTools::AbstractConverter::linkDataToData(): no data passed", {}); |
|
|
|
/* Cast to a non-void type for more convenience */ |
|
Containers::Array<char> out = doLinkDataToData(Containers::arrayCast<const std::pair<Stage, Containers::ArrayView<const char>>>(data)); |
|
CORRADE_ASSERT(!out.deleter(), |
|
"ShaderTools::AbstractConverter::linkDataToData(): implementation is not allowed to use a custom Array deleter", {}); |
|
return out; |
|
} |
|
|
|
Containers::Array<char> AbstractConverter::linkDataToData(const std::initializer_list<std::pair<Stage, Containers::ArrayView<const void>>> data) { |
|
return linkDataToData(Containers::arrayView(data)); |
|
} |
|
|
|
Containers::Array<char> AbstractConverter::doLinkDataToData(Containers::ArrayView<const std::pair<Stage, Containers::ArrayView<const char>>>) { |
|
CORRADE_ASSERT_UNREACHABLE("ShaderTools::AbstractConverter::linkDataToData(): feature advertised but not implemented", {}); |
|
} |
|
|
|
bool AbstractConverter::linkDataToFile(const Containers::ArrayView<const std::pair<Stage, Containers::ArrayView<const void>>> data, const Containers::StringView to) { |
|
CORRADE_ASSERT(features() >= ConverterFeature::LinkData, |
|
"ShaderTools::AbstractConverter::linkDataToFile(): feature not supported", {}); |
|
CORRADE_ASSERT(!(_flags & ConverterFlag::PreprocessOnly), |
|
"ShaderTools::AbstractConverter::linkDataToFile(): PreprocessOnly is not allowed in combination with linking", {}); |
|
CORRADE_ASSERT(!data.empty(), |
|
"ShaderTools::AbstractConverter::linkDataToFile(): no data passed", {}); |
|
|
|
/** @todo this needs expansion once output callbacks are supported as well */ |
|
|
|
/* Cast to a non-void type for more convenience */ |
|
Containers::Array<char> out = doLinkDataToData(Containers::arrayCast<const std::pair<Stage, Containers::ArrayView<const char>>>(data)); |
|
if(!out) return false; |
|
|
|
if(!Utility::Directory::write(to, out)) { |
|
Error{} << "ShaderTools::AbstractConverter::linkDataToFile(): cannot write to file" << to; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool AbstractConverter::linkDataToFile(const std::initializer_list<std::pair<Stage, Containers::ArrayView<const void>>> data, const Containers::StringView to) { |
|
return linkDataToFile(Containers::arrayView(data), to); |
|
} |
|
|
|
Containers::Array<char> AbstractConverter::linkDataToDataUsingInputFileCallbacks(const char* const prefix, const Containers::ArrayView<const std::pair<Stage, Containers::StringView>> from) { |
|
Containers::Array<std::pair<Stage, Containers::ArrayView<const char>>> data{Containers::NoInit, from.size()}; |
|
|
|
/* First load all files. Remember how many of these succeeded so we can |
|
close them again after */ |
|
std::size_t i; |
|
for(i = 0; i != from.size(); ++i) { |
|
const Containers::Optional<Containers::ArrayView<const char>> contents = _inputFileCallback(from[i].second, InputFileCallbackPolicy::LoadTemporary, _inputFileCallbackUserData); |
|
if(!contents) break; |
|
|
|
data[i].first = from[i].first; |
|
data[i].second = *contents; |
|
} |
|
|
|
/* If all input files loaded successfully, process */ |
|
Containers::Array<char> out; |
|
if(i == from.size()) out = doLinkDataToData(data); |
|
|
|
/* Close again all input files that loaded successfully */ |
|
for(std::size_t ii = 0; ii != i; ++ii) |
|
_inputFileCallback(from[ii].second, InputFileCallbackPolicy::Close, _inputFileCallbackUserData); |
|
|
|
/* Now that we have cleaned up correctly, it's time print the error message |
|
if something didn't go well. IN this case doLinkDataToData() was not |
|
called at all. */ |
|
if(i != from.size()) { |
|
Error{} << prefix << "cannot open file" << from[i].second; |
|
return {}; |
|
} |
|
|
|
/* Return the data. This could have failed too, but the error message got |
|
printed by the implementation already */ |
|
return out; |
|
} |
|
|
|
bool AbstractConverter::linkFilesToFile(const Containers::ArrayView<const std::pair<Stage, Containers::StringView>> from, const Containers::StringView to) { |
|
CORRADE_ASSERT(features() & (ConverterFeature::LinkFile|ConverterFeature::LinkData), |
|
"ShaderTools::AbstractConverter::linkFilesToFile(): feature not supported", {}); |
|
CORRADE_ASSERT(!(_flags & ConverterFlag::PreprocessOnly), |
|
"ShaderTools::AbstractConverter::linkFilesToFile(): PreprocessOnly is not allowed in combination with linking", {}); |
|
CORRADE_ASSERT(!from.empty(), |
|
"ShaderTools::AbstractConverter::linkFilesToFile(): no files passed", {}); |
|
|
|
/** @todo this needs expansion once output callbacks are supported as well */ |
|
|
|
/* If input file callbacks are not set or the converter supports handling |
|
them directly, call into the implementation */ |
|
if(!_inputFileCallback || (doFeatures() & ConverterFeature::InputFileCallback)) { |
|
return doLinkFilesToFile(from, to); |
|
|
|
/* Otherwise, if converting data is supported, use the callback and pass |
|
the data through to convertDataToData(). Mark the file as ready to be |
|
closed once conversion is finished. */ |
|
} else if(doFeatures() & ConverterFeature::LinkData) { |
|
/* This needs to be duplicated here and in the doLinkFilesToFile() |
|
implementation in order to support both following cases: |
|
- plugins that don't support InputFileCallback but have their own |
|
doLinkFilesToFile() implementation (callback needs to be used |
|
here, because the base doLinkFilesToFile() implementation might |
|
never get called) |
|
- plugins that support InputFileCallback but want to delegate the |
|
actual file loading to the default implementation (callback used |
|
in the base doLinkFilesToFile() implementation, because this |
|
branch is never taken in that case) */ |
|
Containers::Array<char> out = linkDataToDataUsingInputFileCallbacks("ShaderTools::AbstractConverter::linkFilesToFile():", from); |
|
if(!out) return false; |
|
|
|
if(!Utility::Directory::write(to, out)) { |
|
Error{} << "ShaderTools::AbstractConverter::linkFilesToFile(): cannot write to file" << to; |
|
return false; |
|
} |
|
|
|
return true; |
|
|
|
/* Shouldn't get here, the assert is fired already in setFileCallback() */ |
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ |
|
} |
|
|
|
bool AbstractConverter::linkFilesToFile(const std::initializer_list<std::pair<Stage, Containers::StringView>> from, const Containers::StringView to) { |
|
return linkFilesToFile(Containers::arrayView(from), to); |
|
} |
|
|
|
bool AbstractConverter::doLinkFilesToFile(const Containers::ArrayView<const std::pair<Stage, Containers::StringView>> from, const Containers::StringView to) { |
|
CORRADE_ASSERT(features() >= ConverterFeature::LinkData, "ShaderTools::AbstractConverter::linkFilesToFile(): feature advertised but not implemented", {}); |
|
|
|
/** @todo this needs expansion once output callbacks are supported as well */ |
|
Containers::Array<char> out; |
|
|
|
/* If callbacks are set, use them. This is the same implementation as in |
|
convertFileToFile(), see the comment there for details. */ |
|
if(_inputFileCallback) { |
|
out = linkDataToDataUsingInputFileCallbacks("ShaderTools::AbstractConverter::linkFilesToFile():", from); |
|
|
|
/* Otherwise open the files directly */ |
|
} else { |
|
Containers::Array<Containers::Array<char>> fileData{from.size()}; |
|
for(std::size_t i = 0; i != from.size(); ++i) { |
|
if(!Utility::Directory::exists(from[i].second)) { |
|
Error() << "ShaderTools::AbstractConverter::linkFilesToFile(): cannot open file" << from[i].second; |
|
return {}; |
|
} |
|
|
|
fileData[i] = Utility::Directory::read(from[i].second); |
|
} |
|
|
|
/** @todo merge the allocations once we have an ArrayTuple */ |
|
Containers::Array<std::pair<Stage, Containers::ArrayView<const char>>> data{Containers::NoInit, from.size()}; |
|
for(std::size_t i = 0; i != from.size(); ++i) { |
|
data[i].first = from[i].first; |
|
data[i].second = fileData[i]; |
|
} |
|
|
|
out = doLinkDataToData(data); |
|
} |
|
|
|
if(!out) return false; |
|
|
|
if(!Utility::Directory::write(to, out)) { |
|
Error{} << "ShaderTools::AbstractConverter::linkFilesToFile(): cannot write to file" << to; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
Containers::Array<char> AbstractConverter::linkFilesToData(const Containers::ArrayView<const std::pair<Stage, Containers::StringView>> from) { |
|
CORRADE_ASSERT(features() >= ConverterFeature::LinkData, |
|
"ShaderTools::AbstractConverter::linkFilesToData(): feature not supported", {}); |
|
CORRADE_ASSERT(!(_flags & ConverterFlag::PreprocessOnly), |
|
"ShaderTools::AbstractConverter::linkFilesToData(): PreprocessOnly is not allowed in combination with linking", {}); |
|
CORRADE_ASSERT(!from.empty(), |
|
"ShaderTools::AbstractConverter::linkFilesToData(): no files passed", {}); |
|
|
|
Containers::Array<char> out; |
|
|
|
/* If input file callbacks are not set or the converter supports handling |
|
them directly, call into the implementation */ |
|
if(!_inputFileCallback || (doFeatures() & ConverterFeature::InputFileCallback)) { |
|
out = doLinkFilesToData(from); |
|
|
|
/* Otherwise use the callback and pass the data through to |
|
convertDataToData(). Mark the file as ready to be closed once conversion |
|
is finished. */ |
|
} else { |
|
/* This needs to be duplicated here and in the doConvertFileToData() |
|
implementation in order to support both following cases: |
|
- plugins that don't support InputFileCallback but have their own |
|
doLinkFilesToData() implementation (callback needs to be used |
|
here, because the base doLinkFilesToData() implementation might |
|
never get called) |
|
- plugins that support InputFileCallback but want to delegate the |
|
actual file loading to the default implementation (callback used |
|
in the base doLinkFilesToData() implementation, because this |
|
branch is never taken in that case) */ |
|
out = linkDataToDataUsingInputFileCallbacks("ShaderTools::AbstractConverter::linkFilesToData():", from); |
|
} |
|
|
|
CORRADE_ASSERT(!out.deleter(), |
|
"ShaderTools::AbstractConverter::linkFilesToData(): implementation is not allowed to use a custom Array deleter", {}); |
|
return out; |
|
} |
|
|
|
Containers::Array<char> AbstractConverter::linkFilesToData(const std::initializer_list<std::pair<Stage, Containers::StringView>> from) { |
|
return linkFilesToData(Containers::arrayView(from)); |
|
} |
|
|
|
Containers::Array<char> AbstractConverter::doLinkFilesToData(const Containers::ArrayView<const std::pair<Stage, Containers::StringView>> from) { |
|
/* If callbacks are set, use them. This is the same implementation as in |
|
linkFilesToFile(), see the comment there for details. */ |
|
if(_inputFileCallback) { |
|
return linkDataToDataUsingInputFileCallbacks("ShaderTools::AbstractConverter::linkFilesToData():", from); |
|
|
|
/* Otherwise open the files directly */ |
|
} else { |
|
Containers::Array<Containers::Array<char>> fileData{from.size()}; |
|
for(std::size_t i = 0; i != from.size(); ++i) { |
|
if(!Utility::Directory::exists(from[i].second)) { |
|
Error() << "ShaderTools::AbstractConverter::linkFilesToData(): cannot open file" << from[i].second; |
|
return {}; |
|
} |
|
|
|
fileData[i] = Utility::Directory::read(from[i].second); |
|
} |
|
|
|
/** @todo merge the allocations once we have an ArrayTuple */ |
|
Containers::Array<std::pair<Stage, Containers::ArrayView<const char>>> data{Containers::NoInit, from.size()}; |
|
for(std::size_t i = 0; i != from.size(); ++i) { |
|
data[i].first = from[i].first; |
|
data[i].second = fileData[i]; |
|
} |
|
|
|
return doLinkDataToData(data); |
|
} |
|
} |
|
|
|
Debug& operator<<(Debug& debug, const ConverterFeature value) { |
|
debug << "ShaderTools::ConverterFeature" << Debug::nospace; |
|
|
|
switch(value) { |
|
/* LCOV_EXCL_START */ |
|
#define _c(v) case ConverterFeature::v: return debug << "::" #v; |
|
_c(ValidateData) |
|
_c(ValidateFile) |
|
_c(ConvertData) |
|
_c(ConvertFile) |
|
_c(LinkData) |
|
_c(LinkFile) |
|
_c(InputFileCallback) |
|
_c(Preprocess) |
|
_c(Optimize) |
|
_c(DebugInfo) |
|
#undef _c |
|
/* LCOV_EXCL_STOP */ |
|
} |
|
|
|
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedByte(value)) << Debug::nospace << ")"; |
|
} |
|
|
|
Debug& operator<<(Debug& debug, const ConverterFeatures value) { |
|
return Containers::enumSetDebugOutput(debug, value, "ShaderTools::ConverterFeatures{}", { |
|
ConverterFeature::ValidateData, |
|
/* Implied by ValidateData, has to be after */ |
|
ConverterFeature::ValidateFile, |
|
ConverterFeature::ConvertData, |
|
/* Implied by ConvertData, has to be after */ |
|
ConverterFeature::ConvertFile, |
|
ConverterFeature::LinkData, |
|
/* Implied by LinkData, has to be after */ |
|
ConverterFeature::LinkFile, |
|
ConverterFeature::InputFileCallback, |
|
ConverterFeature::Preprocess, |
|
ConverterFeature::Optimize, |
|
ConverterFeature::DebugInfo |
|
}); |
|
} |
|
|
|
Debug& operator<<(Debug& debug, const ConverterFlag value) { |
|
debug << "ShaderTools::ConverterFlag" << Debug::nospace; |
|
|
|
switch(value) { |
|
/* LCOV_EXCL_START */ |
|
#define _c(v) case ConverterFlag::v: return debug << "::" #v; |
|
_c(Quiet) |
|
_c(Verbose) |
|
_c(WarningAsError) |
|
_c(PreprocessOnly) |
|
#undef _c |
|
/* LCOV_EXCL_STOP */ |
|
} |
|
|
|
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedByte(value)) << Debug::nospace << ")"; |
|
} |
|
|
|
Debug& operator<<(Debug& debug, const ConverterFlags value) { |
|
return Containers::enumSetDebugOutput(debug, value, "ShaderTools::ConverterFlags{}", { |
|
ConverterFlag::Quiet, |
|
ConverterFlag::Verbose, |
|
ConverterFlag::WarningAsError, |
|
ConverterFlag::PreprocessOnly |
|
}); |
|
} |
|
|
|
Debug& operator<<(Debug& debug, const Format value) { |
|
debug << "ShaderTools::Format" << Debug::nospace; |
|
|
|
switch(value) { |
|
/* LCOV_EXCL_START */ |
|
#define _c(v) case Format::v: return debug << "::" #v; |
|
_c(Unspecified) |
|
_c(Glsl) |
|
_c(Spirv) |
|
_c(SpirvAssembly) |
|
_c(Hlsl) |
|
_c(Msl) |
|
_c(Wgsl) |
|
_c(Dxil) |
|
#undef _c |
|
/* LCOV_EXCL_STOP */ |
|
} |
|
|
|
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedByte(value)) << Debug::nospace << ")"; |
|
} |
|
|
|
}}
|
|
|