Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve OSL generation of filename inputs #1547

212 changes: 127 additions & 85 deletions source/MaterialXGenOsl/OslShaderGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,25 @@ ShaderPtr OslShaderGenerator::generate(const string& name, ElementPtr element, G
emitLineBreak(stage);
}

// Inputs of type 'filename' has been generated into two shader inputs.
// So here we construct a single 'textureresource' from these inputs,
// to be used further downstream. See emitShaderInputs() for details.
VariableBlock& inputs = stage.getUniformBlock(OSL::UNIFORMS);
for (size_t i = 0; i < inputs.size(); ++i)
{
ShaderPort* input = inputs[i];
if (input->getType() == Type::FILENAME)
{
// Construct the textureresource variable.
const string newVariableName = input->getVariable() + "_";
const string& type = _syntax->getTypeName(input->getType());
emitLine(type + newVariableName + " = {" + input->getVariable() + ", " + input->getVariable() + "_colorspace}", stage);

// Update the variable name to be used downstream.
input->setVariable(newVariableName);
}
}

// Emit all texturing nodes. These are inputs to any
// closure/shader nodes and need to be emitted first.
emitFunctionCalls(graph, context, stage, ShaderNode::Classification::TEXTURE);
Expand Down Expand Up @@ -493,111 +512,75 @@ void OslShaderGenerator::emitLibraryIncludes(ShaderStage& stage, GenContext& con
emitLineBreak(stage);
}

namespace
{

std::unordered_map<string, string> GEOMPROP_DEFINITIONS =
{
{ "Pobject", "transform(\"object\", P)" },
{ "Pworld", "P" },
{ "Nobject", "transform(\"object\", N)" },
{ "Nworld", "N" },
{ "Tobject", "transform(\"object\", dPdu)" },
{ "Tworld", "dPdu" },
{ "Bobject", "transform(\"object\", dPdv)" },
{ "Bworld", "dPdv" },
{ "UV0", "{u,v}" },
{ "Vworld", "I" }
};

} // anonymous namespace

void OslShaderGenerator::emitShaderInputs(const VariableBlock& inputs, ShaderStage& stage) const
{
const std::unordered_map<const TypeDesc*, ShaderMetadata> UI_WIDGET_METADATA =
{
{ Type::FLOAT, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("number", Type::STRING->getName())) },
{ Type::INTEGER, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("number", Type::STRING->getName())) },
{ Type::FILENAME, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("filename", Type::STRING->getName())) },
{ Type::BOOLEAN, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("checkBox", Type::STRING->getName())) }
};

const std::set<const TypeDesc*> METADATA_TYPE_BLACKLIST =
static const std::unordered_map<string, string> GEOMPROP_DEFINITIONS =
{
Type::VECTOR2, // Custom struct types doesn't support metadata declarations.
Type::VECTOR4, //
Type::COLOR4, //
Type::FILENAME, //
Type::BSDF //
{ "Pobject", "transform(\"object\", P)" },
{ "Pworld", "P" },
{ "Nobject", "transform(\"object\", N)" },
{ "Nworld", "N" },
{ "Tobject", "transform(\"object\", dPdu)" },
{ "Tworld", "dPdu" },
{ "Bobject", "transform(\"object\", dPdv)" },
{ "Bworld", "dPdv" },
{ "UV0", "{u,v}" },
{ "Vworld", "I" }
};

for (size_t i = 0; i < inputs.size(); ++i)
{
const ShaderPort* input = inputs[i];

const string& type = _syntax->getTypeName(input->getType());
string value = _syntax->getValue(input, true);

emitLineBegin(stage);
emitString(type + " " + input->getVariable(), stage);

const string& geomprop = input->getGeomProp();
if (!geomprop.empty())
if (input->getType() == Type::FILENAME)
{
auto it = GEOMPROP_DEFINITIONS.find(geomprop);
if (it != GEOMPROP_DEFINITIONS.end())
{
value = it->second;
}
// Shader inputs of type 'filename' (textures) need special handling.
// In OSL codegen a 'filename' is translated to the custom type 'textureresource',
// which is a struct containing a file string and a colorspace string.
// For the published shader interface we here split this into two separate inputs,
// which gives a nicer shader interface with widget metadata on each input.

ValuePtr value = input->getValue();
const string valueStr = value ? value->getValueString() : EMPTY_STRING;

// Add the file string input
emitLineBegin(stage);
emitString("string " + input->getVariable() + " = \"" + valueStr + "\"", stage);
emitMetadata(input, stage);
emitString(",", stage);
emitLineEnd(stage, false);

// Add the colorspace string input
emitLineBegin(stage);
emitString("string " + input->getVariable() + "_colorspace = \"" + input->getColorSpace() + "\"", stage);
jstone-lucasfilm marked this conversation as resolved.
Show resolved Hide resolved
emitLineEnd(stage, false);
emitScopeBegin(stage, Syntax::DOUBLE_SQUARE_BRACKETS);
emitLine("string widget = \"colorspace\"", stage, false);
emitScopeEnd(stage, false, false);
}

if (value.empty())
else
{
value = _syntax->getDefaultValue(input->getType());
}
emitString(" = " + value, stage);

//
// Add shader input metadata.
//

auto widgetMetadataIt = UI_WIDGET_METADATA.find(input->getType());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metadata generation was moved out to a separate method, see below.

const ShaderMetadata* widgetMetadata = widgetMetadataIt != UI_WIDGET_METADATA.end() ? &widgetMetadataIt->second : nullptr;
const ShaderMetadataVecPtr& metadata = input->getMetadata();
emitLineBegin(stage);
emitString(type + " " + input->getVariable(), stage);

if (widgetMetadata || (metadata && metadata->size()))
{
StringVec metadataLines;
if (metadata)
string value = _syntax->getValue(input, true);
const string& geomprop = input->getGeomProp();
if (!geomprop.empty())
{
for (size_t j = 0; j < metadata->size(); ++j)
auto it = GEOMPROP_DEFINITIONS.find(geomprop);
if (it != GEOMPROP_DEFINITIONS.end())
{
const ShaderMetadata& data = metadata->at(j);
if (METADATA_TYPE_BLACKLIST.count(data.type) == 0)
{
const string& delim = (widgetMetadata || j < metadata->size() - 1) ? Syntax::COMMA : EMPTY_STRING;
const string& dataType = _syntax->getTypeName(data.type);
const string dataValue = _syntax->getValue(data.type, *data.value, true);
metadataLines.push_back(dataType + " " + data.name + " = " + dataValue + delim);
}
value = it->second;
}
}
if (widgetMetadata)
if (value.empty())
{
const string& dataType = _syntax->getTypeName(widgetMetadata->type);
const string dataValue = _syntax->getValue(widgetMetadata->type, *widgetMetadata->value, true);
metadataLines.push_back(dataType + " " + widgetMetadata->name + " = " + dataValue);
}
if (metadataLines.size())
{
emitLineEnd(stage, false);
emitScopeBegin(stage, Syntax::DOUBLE_SQUARE_BRACKETS);
for (auto line : metadataLines)
{
emitLine(line, stage, false);
}
emitScopeEnd(stage, false, false);
value = _syntax->getDefaultValue(input->getType());
}

emitString(" = " + value, stage);
emitMetadata(input, stage);
}

if (i < inputs.size())
Expand All @@ -622,6 +605,65 @@ void OslShaderGenerator::emitShaderOutputs(const VariableBlock& outputs, ShaderS
}
}

void OslShaderGenerator::emitMetadata(const ShaderPort* port, ShaderStage& stage) const
{
static const std::unordered_map<const TypeDesc*, ShaderMetadata> UI_WIDGET_METADATA =
{
{ Type::FLOAT, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("number", Type::STRING->getName())) },
{ Type::INTEGER, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("number", Type::STRING->getName())) },
{ Type::FILENAME, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("filename", Type::STRING->getName())) },
{ Type::BOOLEAN, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("checkBox", Type::STRING->getName())) }
};

static const std::set<const TypeDesc*> METADATA_TYPE_BLACKLIST =
{
Type::VECTOR2, // Custom struct types doesn't support metadata declarations.
Type::VECTOR4, //
Type::COLOR4, //
Type::FILENAME, //
Type::BSDF //
};

auto widgetMetadataIt = UI_WIDGET_METADATA.find(port->getType());
const ShaderMetadata* widgetMetadata = widgetMetadataIt != UI_WIDGET_METADATA.end() ? &widgetMetadataIt->second : nullptr;
const ShaderMetadataVecPtr& metadata = port->getMetadata();

if (widgetMetadata || (metadata && metadata->size()))
{
StringVec metadataLines;
if (metadata)
{
for (size_t j = 0; j < metadata->size(); ++j)
{
const ShaderMetadata& data = metadata->at(j);
if (METADATA_TYPE_BLACKLIST.count(data.type) == 0)
{
const string& delim = (widgetMetadata || j < metadata->size() - 1) ? Syntax::COMMA : EMPTY_STRING;
const string& dataType = _syntax->getTypeName(data.type);
const string dataValue = _syntax->getValue(data.type, *data.value, true);
metadataLines.push_back(dataType + " " + data.name + " = " + dataValue + delim);
}
}
}
if (widgetMetadata)
{
const string& dataType = _syntax->getTypeName(widgetMetadata->type);
const string dataValue = _syntax->getValue(widgetMetadata->type, *widgetMetadata->value, true);
metadataLines.push_back(dataType + " " + widgetMetadata->name + " = " + dataValue);
}
if (metadataLines.size())
{
emitLineEnd(stage, false);
emitScopeBegin(stage, Syntax::DOUBLE_SQUARE_BRACKETS);
for (auto line : metadataLines)
{
emitLine(line, stage, false);
}
emitScopeEnd(stage, false, false);
}
}
}

namespace OSL
{

Expand Down
3 changes: 3 additions & 0 deletions source/MaterialXGenOsl/OslShaderGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ class MX_GENOSL_API OslShaderGenerator : public ShaderGenerator

/// Emit a block of shader outputs.
virtual void emitShaderOutputs(const VariableBlock& inputs, ShaderStage& stage) const;

/// Emit metadata for a shader parameter.
virtual void emitMetadata(const ShaderPort* port, ShaderStage& stage) const;
};

namespace OSL
Expand Down