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

Runtime token support #1157

Merged
merged 15 commits into from
Apr 7, 2021
6 changes: 6 additions & 0 deletions source/MaterialXRuntime/Private/PvtPort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ PvtInput::PvtInput(const RtIdentifier& name, const RtIdentifier& type, uint32_t

bool PvtInput::isConnectable(const PvtOutput* output) const
{
// TODO : Tokens are not connectable for now.
if (isToken() || output->isToken())
Copy link
Author

Choose a reason for hiding this comment

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

Disallow connections.

{
return false;
}

// We cannot connect to ourselves.
if (_parent == output->_parent)
{
Expand Down
36 changes: 36 additions & 0 deletions source/MaterialXRuntime/Private/PvtPort.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <MaterialXRuntime/RtValue.h>
#include <MaterialXRuntime/RtTypeDef.h>
#include <MaterialXRuntime/RtTraversal.h>
#include <MaterialXRuntime/Identifiers.h>

/// @file
/// TODO: Docs
Expand Down Expand Up @@ -106,6 +107,23 @@ class PvtPort : public PvtObject
attr->asIdentifier() = unit;
}

bool isToken() const
{
return (_flags & RtPortFlag::TOKEN) != 0;
}

void setIsToken(bool val)
{
if (val)
{
_flags |= RtPortFlag::TOKEN;
}
else
{
_flags &= ~RtPortFlag::TOKEN;
}
}

static const RtIdentifier DEFAULT_OUTPUT_NAME;
static const RtIdentifier COLOR_SPACE;
static const RtIdentifier UNIT;
Expand Down Expand Up @@ -148,6 +166,24 @@ class PvtInput : public PvtPort
}
}

bool isUIVisible() const
Copy link
Author

Choose a reason for hiding this comment

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

Wrote some wrappers for reuse.

{
// For now since connections require ports to be created and hence visible
// always assume a connection means visible.
if (isConnected())
{
return true;
}
const RtTypedValue* attr = getAttribute(Identifiers::UIVISIBLE, RtType::BOOLEAN);
return attr ? attr->asBool() : true;
}

void setIsUIVisible(bool val)
{
RtTypedValue* attr = createAttribute(Identifiers::UIVISIBLE, RtType::BOOLEAN);
attr->asBool() = val;
}

bool isConnected() const
{
return _connection != nullptr;
Expand Down
6 changes: 6 additions & 0 deletions source/MaterialXRuntime/RtConnectableApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ namespace

bool RtConnectableApi::acceptConnection(const RtInput& input, const RtOutput& output) const
{
// TODO: Tokens are not currently considered to be connectable
if (input.isToken())
{
return false;
}

// Default implementation checks if the types are matching.
//
// TODO: Check if the data types are compatible/castable
Expand Down
54 changes: 46 additions & 8 deletions source/MaterialXRuntime/RtFileIo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ namespace
const uint32_t flags = elem->asA<Input>()->getIsUniform() ? RtPortFlag::UNIFORM : 0;
port = schema.createInput(portName, portType, flags);
}
else if (elem->isA<Token>())
Copy link
Author

Choose a reason for hiding this comment

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

File read.

{
const uint32_t flags = RtPortFlag::UNIFORM | RtPortFlag::TOKEN;
port = schema.createInput(portName, portType, flags);
}

if (port)
{
Expand Down Expand Up @@ -487,7 +492,17 @@ namespace
if (!socket)
{
const RtIdentifier inputType(elem->getType());
RtInput input = nodegraph.createInput(socketName, inputType);

Copy link

Choose a reason for hiding this comment

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

Just a suggestion, but you could convert this into one or two lines if you wanted to:

const uint32_t flags = elem->isA() ? RtPortFlag::UNIFORM | RtPortFlag::TOKEN : 0;
RtInput input = nodegraph.createInput(socketName, inputType, flags);

or even:
RtInput input = nodegraph.createInput(socketName, inputType, elem->isA() ? RtPortFlag::UNIFORM | RtPortFlag::TOKEN : 0);

Copy link
Author

Choose a reason for hiding this comment

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

I can do the first. Bit too hard to read the second :).

RtInput input = RtInput();
if (elem->isA<Token>())
{
const uint32_t flags = RtPortFlag::UNIFORM | RtPortFlag::TOKEN;
input = nodegraph.createInput(socketName, inputType, flags);
}
else
{
input = nodegraph.createInput(socketName, inputType);
}
socket = nodegraph.getInputSocket(input.getName());

// Set the input value
Expand Down Expand Up @@ -901,7 +916,16 @@ namespace
for (PvtObject* obj : src->getInputs())
{
const PvtInput* input = obj->asA<PvtInput>();
ValueElementPtr destPort = destNodeDef->addInput(input->getName().str(), input->getType().str());
ValueElementPtr destPort = nullptr;
if (input->isToken())
Copy link
Author

Choose a reason for hiding this comment

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

File save.

{
destPort = destNodeDef->addToken(input->getName().str());
destPort->setType(input->getType().str());
}
else
{
destPort = destNodeDef->addInput(input->getName().str(), input->getType().str());
}
if (input->isUniform())
{
destPort->setIsUniform(true);
Expand Down Expand Up @@ -941,10 +965,8 @@ namespace
RtInput input = node.getInput(i);
if (input)
{
const RtTypedValue* uiVisible1 = input.getAttribute(Identifiers::UIVISIBLE, RtType::BOOLEAN);
const RtTypedValue* uiVisible2 = nodedefInput.getAttribute(Identifiers::UIVISIBLE, RtType::BOOLEAN);
const bool uiHidden1 = uiVisible1 && !uiVisible1->asBool();
const bool uiHidden2 = uiVisible2 && !uiVisible2->asBool();
const bool uiHidden1 = input.isUIVisible();
const bool uiHidden2 = nodedefInput.isUIVisible();
const bool writeUiVisibleData = uiHidden1 != uiHidden2;

// Write input if it's connected or different from default value.
Expand All @@ -955,7 +977,15 @@ namespace
ValueElementPtr valueElem;
if (input.isUniform())
{
valueElem = destNode->addInput(input.getName().str(), input.getType().str());
if (input.isToken())
{
valueElem = destNode->addToken(input.getName().str());
valueElem->setType(input.getType().str());
}
else
{
valueElem = destNode->addInput(input.getName().str(), input.getType().str());
}
valueElem->setIsUniform(true);
if (input.isConnected())
{
Expand Down Expand Up @@ -1049,7 +1079,15 @@ namespace
ValueElementPtr v = nullptr;
if (nodegraphInput.isUniform())
{
v = destNodeGraph->addInput(nodegraphInput.getName().str(), nodegraphInput.getType().str());
if (nodegraphInput.isToken())
{
v = destNodeGraph->addToken(nodegraphInput.getName().str());
Copy link

Choose a reason for hiding this comment

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

Question out of curiousity. If addInput takes a type as a second parameter, shouldn't addToken as well so you don't need two lines to accomplish what could be done on one line?

v->setType(nodegraphInput.getType().str());
}
else
{
v = destNodeGraph->addInput(nodegraphInput.getName().str(), nodegraphInput.getType().str());
}
v->setIsUniform(true);
}
else
Expand Down
20 changes: 20 additions & 0 deletions source/MaterialXRuntime/RtPort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ void RtPort::setUnitType(const RtIdentifier& unit)
return hnd()->asA<PvtPort>()->setUnitType(unit);
}

bool RtPort::isToken() const
{
return hnd()->asA<PvtPort>()->isToken();
}

void RtPort::setIsToken(bool uniform)
{
hnd()->asA<PvtPort>()->setIsToken(uniform);
}


RT_DEFINE_RUNTIME_OBJECT(RtInput, RtObjType::INPUT, "RtInput")

Expand All @@ -96,6 +106,16 @@ void RtInput::setUniform(bool uniform)
hnd()->asA<PvtInput>()->setUniform(uniform);
}

bool RtInput::isUIVisible() const
{
return hnd()->asA<PvtInput>()->isUIVisible();
}

void RtInput::setIsUIVisible(bool val)
{
hnd()->asA<PvtInput>()->setIsUIVisible(val);
}

bool RtInput::isConnected() const
{
return hnd()->asA<PvtInput>()->isConnected();
Expand Down
15 changes: 15 additions & 0 deletions source/MaterialXRuntime/RtPort.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class RtPortFlag

/// Port is a nodegraph internal socket.
static const uint32_t SOCKET = 0x00000002;

/// Port holds a token
static const uint32_t TOKEN = 0x00000004;
};

/// @class RtPort
Expand Down Expand Up @@ -80,6 +83,12 @@ class RtPort : public RtObject

/// Set the unit type for this port.
void setUnitType(const RtIdentifier& unit);

/// Return true if this input is a token.
bool isToken() const;

/// Sets the input to be a token or not a token
void setIsToken(bool val);
};


Expand All @@ -103,6 +112,12 @@ class RtInput : public RtPort
/// Sets the input to be a uniform or not a uniform
void setUniform(bool uniform);

/// Return true if this input should be visible in the UI.
bool isUIVisible() const;

/// Sets the input's visibilit in the UI.
void setIsUIVisible(bool val);

/// Return true if this input is connected.
bool isConnected() const;

Expand Down
11 changes: 8 additions & 3 deletions source/MaterialXRuntime/RtStage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,17 @@ RtPrim RtStage::createNodeDef(RtPrim nodegraphPrim,
for (RtInput input : nodegraph.getInputs())
{
RtInput nodedefInput = nodedef.createInput(input.getName(), input.getType());
nodedefInput.setUniform(input.isUniform());
PvtObject *src = PvtObject::cast(input);
for (const RtIdentifier& name : src->getAttributeNames())
{
const RtTypedValue* srcAttr = src->getAttribute(name);
RtTypedValue* destAttr = nodedefInput.createAttribute(name, srcAttr->getType());
RtValue::copy(srcAttr->getType(), srcAttr->getValue(), destAttr->getValue());
}
nodedefInput.setIsToken(input.isToken());
RtValue::copy(input.getType(), input.getValue(), nodedefInput.getValue());
}

// TODO : Add support for tokens

// Add an output per nodegraph output
for (RtOutput output : nodegraph.getOutputs())
{
Expand Down
21 changes: 19 additions & 2 deletions source/MaterialXTest/MaterialXRuntime/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ namespace
}
};

// Commonly used tokens.
// Commonly used identifiers.
const mx::RtIdentifier X("x");
const mx::RtIdentifier Y("y");
const mx::RtIdentifier Z("z");
Expand All @@ -95,6 +95,7 @@ namespace
const mx::RtIdentifier IN3("in3");
const mx::RtIdentifier OUT("out");
const mx::RtIdentifier IN("in");
const mx::RtIdentifier TOKEN1("token1");
const mx::RtIdentifier REFLECTIVITY("reflectivity");
const mx::RtIdentifier SURFACESHADER("surfaceshader");
const mx::RtIdentifier UIFOLDER("uifolder");
Expand Down Expand Up @@ -156,7 +157,7 @@ TEST_CASE("Runtime: Material Element Upgrade", "[runtime]")
}
}

TEST_CASE("Runtime: Token", "[runtime]")
TEST_CASE("Runtime: Identifiers", "[runtime]")
{
mx::RtIdentifier tok1("hej");
mx::RtIdentifier tok2("hey");
Expand Down Expand Up @@ -1299,20 +1300,36 @@ TEST_CASE("Runtime: FileIo NodeGraph", "[runtime]")
// Create a nodegraph.
mx::RtNodeGraph graph = stage->createPrim(mx::RtNodeGraph::typeName());
graph.createInput(IN, mx::RtType::FLOAT);
graph.createInput(TOKEN1, mx::RtType::FLOAT, mx::RtPortFlag::UNIFORM | mx::RtPortFlag::TOKEN);
Copy link
Author

Choose a reason for hiding this comment

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

Basic test for create and connectivity.

graph.createOutput(OUT, mx::RtType::FLOAT);
mx::RtInput graphIn = graph.getInput(IN);
REQUIRE(graphIn.isUIVisible());
graphIn.setIsUIVisible(false);
REQUIRE(!graphIn.isUIVisible());
mx::RtInput graphToken = graph.getInput(TOKEN1);
REQUIRE(graphToken.isUniform());
mx::RtOutput graphOut = graph.getOutput(OUT);
mx::RtOutput graphInSocket = graph.getInputSocket(IN);
mx::RtOutput graphTokenSocket = graph.getInputSocket(TOKEN1);
mx::RtInput graphOutSocket = graph.getOutputSocket(OUT);
REQUIRE(graphIn);
REQUIRE(graphToken);
REQUIRE(graphOut);
REQUIRE(graphInSocket);
REQUIRE(graphTokenSocket);
REQUIRE(graphOutSocket);

// Add nodes to the graph.
const mx::RtIdentifier ADD_FLOAT_NODEDEF("ND_add_float");
mx::RtNode add1 = stage->createPrim(graph.getPath(), NONAME, ADD_FLOAT_NODEDEF);
mx::RtNode add2 = stage->createPrim(graph.getPath(), NONAME, ADD_FLOAT_NODEDEF);
try {
graphTokenSocket.connect(add1.getInput(IN1));
}
catch (mx::Exception&)
{
}
REQUIRE(!graphTokenSocket.isConnected());
graphInSocket.connect(add1.getInput(IN1));
add1.getOutput(OUT).connect(add2.getInput(IN1));
add2.getOutput(OUT).connect(graphOutSocket);
Expand Down