mat: Store matc parameters in material packages (#9070)

This commit enhances the material compilation process by embedding the
`matc` command-line parameters directly into the compiled material file.
This feature is valuable for debugging, as it allows developers to
inspect the exact compilation settings used for a given material.

A key consideration is the potential for personally identifiable
information (PII) in the command-line arguments (e.g., file paths). To
address this, a `toPIISafeString` method has been implemented to filter
out PII-sensitive options before they are stored in the material.

With this change, the matc command below
    /path/to/matc -a opengl --api vulkan -p desktop -g -o /path/to/my.filamat /path/to/my.mat

is stored to the package as below. (veryfied by running `matinfo my.filamat`)
    Compilation Parameters:         -a opengl --api vulkan -p desktop -g
This commit is contained in:
Sungun Park
2025-08-17 12:25:37 -07:00
committed by GitHub
parent 39268a6ad0
commit 0ef7507464
8 changed files with 139 additions and 29 deletions

View File

@@ -58,6 +58,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
MaterialName = charTo64bitNum("MAT_NAME"),
MaterialVersion = charTo64bitNum("MAT_VERS"),
MaterialCompilationParameters = charTo64bitNum("MAT_CPRM"),
MaterialCacheId = charTo64bitNum("MAT_UUID"),
MaterialFeatureLevel = charTo64bitNum("MAT_FEAT"),
MaterialShading = charTo64bitNum("MAT_SHAD"),

View File

@@ -295,6 +295,9 @@ public:
//! Set the file name of this material file. Used in error reporting.
MaterialBuilder& fileName(const char* name) noexcept;
//! Set the commandline parameters of matc. Used for debugging purpose.
MaterialBuilder& compilationParameters(const char* params) noexcept;
//! Set the shading model.
MaterialBuilder& shading(Shading shading) noexcept;
@@ -876,6 +879,7 @@ private:
utils::CString mMaterialName;
utils::CString mFileName;
utils::CString mCompilationParameters;
class ShaderCode {
public:

View File

@@ -232,6 +232,11 @@ MaterialBuilder& MaterialBuilder::fileName(const char* name) noexcept {
return *this;
}
MaterialBuilder& MaterialBuilder::compilationParameters(const char* params) noexcept {
mCompilationParameters = CString(params);
return *this;
}
MaterialBuilder& MaterialBuilder::material(const char* code, size_t const line) noexcept {
mMaterialFragmentCode.setUnresolved(CString(code));
mMaterialFragmentCode.setLineOffset(line);
@@ -1594,6 +1599,8 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
container.emplace<uint32_t>(MaterialVersion, MATERIAL_VERSION);
container.emplace<uint8_t>(MaterialFeatureLevel, (uint8_t)info.featureLevel);
container.emplace<const char*>(MaterialName, mMaterialName.c_str_safe());
container.emplace<const char*>(MaterialCompilationParameters,
mCompilationParameters.c_str_safe());
container.emplace<uint32_t>(MaterialShaderModels, mShaderModels.getValue());
container.emplace<uint8_t>(ChunkType::MaterialDomain, static_cast<uint8_t>(mMaterialDomain));

View File

@@ -75,6 +75,8 @@ public:
virtual std::string toString() const noexcept = 0;
virtual std::string toPIISafeString() const noexcept = 0;
bool isDebug() const noexcept {
return mDebug;
}

View File

@@ -107,6 +107,12 @@ static bool printMaterial(ostream& text, const ChunkContainer& container) {
text << name.c_str() << endl;
}
CString compilationParameters;
if (read(container, MaterialCompilationParameters, &compilationParameters)) {
text << " " << setw(alignment) << left << "Compilation Parameters: ";
text << compilationParameters.c_str() << endl;
}
text << endl;
text << "Shading:" << endl;

View File

@@ -30,6 +30,53 @@ using namespace utils;
namespace matc {
static constexpr const char* OPTSTR = "hLxo:f:dm:a:l:p:D:T:P:OSEr:vV:gtwF1R";
static const struct option OPTIONS[] = {
{ "help", no_argument, nullptr, 'h' },
{ "license", no_argument, nullptr, 'L' },
{ "output", required_argument, nullptr, 'o' },
{ "output-format", required_argument, nullptr, 'f' },
{ "debug", no_argument, nullptr, 'd' },
{ "variant-filter", required_argument, nullptr, 'V' },
{ "platform", required_argument, nullptr, 'p' },
{ "optimize", no_argument, nullptr, 'x' }, // for backward compatibility
{ "optimize", no_argument, nullptr, 'O' }, // for backward compatibility
{ "optimize-size", no_argument, nullptr, 'S' },
{ "optimize-none", no_argument, nullptr, 'g' },
{ "preprocessor-only", no_argument, nullptr, 'E' },
{ "api", required_argument, nullptr, 'a' },
{ "feature-level", required_argument, nullptr, 'l' },
{ "no-essl1", no_argument, nullptr, '1' },
{ "define", required_argument, nullptr, 'D' },
{ "template", required_argument, nullptr, 'T' },
{ "material-parameter",required_argument, nullptr, 'P' },
{ "reflect", required_argument, nullptr, 'r' },
{ "print", no_argument, nullptr, 't' },
{ "version", no_argument, nullptr, 'v' },
{ "raw", no_argument, nullptr, 'w' },
{ "no-sampler-validation", no_argument, nullptr, 'F' },
{ "save-raw-variants", no_argument, nullptr, 'R' },
{ nullptr, 0, nullptr, 0 } // termination of the option list
};
// A list of options that may contain PII(Personally Identifiable Information) data.
// We ignore these options when we call the `toPIISafeString` method.
static const std::string_view PII_OPTIONS[] = {
"output",
};
static bool isPIIOption(const char* longOptionName) {
if (!longOptionName) {
return false;
}
for (auto option : PII_OPTIONS) {
if (option == longOptionName) {
return true;
}
}
return false;
}
static void usage(char* name) {
std::string exec_name(utils::Path(name).getName());
std::string usage(
@@ -181,35 +228,6 @@ static void parseDefine(std::string defineString, matp::Config::StringReplacemen
}
bool CommandlineConfig::parse() {
static constexpr const char* OPTSTR = "hLxo:f:dm:a:l:p:D:T:P:OSEr:vV:gtwF1R";
static const struct option OPTIONS[] = {
{ "help", no_argument, nullptr, 'h' },
{ "license", no_argument, nullptr, 'L' },
{ "output", required_argument, nullptr, 'o' },
{ "output-format", required_argument, nullptr, 'f' },
{ "debug", no_argument, nullptr, 'd' },
{ "variant-filter", required_argument, nullptr, 'V' },
{ "platform", required_argument, nullptr, 'p' },
{ "optimize", no_argument, nullptr, 'x' }, // for backward compatibility
{ "optimize", no_argument, nullptr, 'O' }, // for backward compatibility
{ "optimize-size", no_argument, nullptr, 'S' },
{ "optimize-none", no_argument, nullptr, 'g' },
{ "preprocessor-only", no_argument, nullptr, 'E' },
{ "api", required_argument, nullptr, 'a' },
{ "feature-level", required_argument, nullptr, 'l' },
{ "no-essl1", no_argument, nullptr, '1' },
{ "define", required_argument, nullptr, 'D' },
{ "template", required_argument, nullptr, 'T' },
{ "material-parameter",required_argument, nullptr, 'P' },
{ "reflect", required_argument, nullptr, 'r' },
{ "print", no_argument, nullptr, 't' },
{ "version", no_argument, nullptr, 'v' },
{ "raw", no_argument, nullptr, 'w' },
{ "no-sampler-validation", no_argument, nullptr, 'F' },
{ "save-raw-variants", no_argument, nullptr, 'R' },
{ nullptr, 0, nullptr, 0 } // termination of the option list
};
int opt;
int option_index = 0;
@@ -348,4 +366,72 @@ bool CommandlineConfig::parse() {
return true;
}
// This method concatenates all options and arguments, excluding the executable name, input file
// name, and any options found in PII_OPTIONS due to potential PII data.
std::string CommandlineConfig::toPIISafeString() const noexcept {
std::string result;
optind = 1; // Reset getopt's internal index before parsing
while (true) {
// getopt_long will only set `long_index` if a long option is parsed.
int long_index = -1;
int opt = getopt_long(mArgc, mArgv, OPTSTR, OPTIONS, &long_index);
if (opt == -1) {
break; // End of options
}
// Find the matched option.
const struct option* matched_option = nullptr;
if (long_index != -1) {
// A long option was parsed (e.g., --help)
matched_option = &OPTIONS[long_index];
} else if (opt > 0) {
// A short option was parsed (e.g., -h)
for (int i = 0; OPTIONS[i].name != nullptr; ++i) {
if (OPTIONS[i].val == opt) {
matched_option = &OPTIONS[i];
break;
}
}
}
if (!matched_option) {
std::cerr << "Failed to find the matched option: long_index=" << long_index
<< ", opt=" << opt << std::endl;
continue;
}
// Skip if it's a PII option.
if (isPIIOption(matched_option->name)) {
continue;
}
// Reconstruct the option.
if (long_index != -1) {
result += "--";
result += matched_option->name;
result += " ";
} else if (opt > 0) {
result += "-";
result += (char)matched_option->val;
result += " ";
}
// Add an argument if available.
if (optarg && matched_option && matched_option->has_arg != no_argument) {
result += optarg;
result += " ";
}
}
// We're ignoring the last (remaining) argument because it's user input with PII.
// Trim trailing space
if (!result.empty()) {
result.pop_back();
}
return result;
}
} // namespace matc

View File

@@ -122,6 +122,8 @@ public:
return parameters;
}
std::string toPIISafeString() const noexcept override;
private:
bool parse();

View File

@@ -76,6 +76,8 @@ bool MaterialCompiler::run(const matp::Config& config) {
return true;
}
builder.compilationParameters(config.toPIISafeString().c_str());
JobSystem js;
js.adopt();