Skip to content

Commit 6d0e937

Browse files
Fix node key collisions in ShaderGraph (#2768)
This changelist fixes a name collision error in the ShaderGraph node map. Previously, nodes were keyed by their base name (i.e. getName), which is only unique within the scope of the parent graph. In complex documents where multiple nodes shared the same base name, these map keys would collide, leading to shader generation errors and crashes. In order to address this, the map key now uses the full name path of the node (i.e. getNamePath), which is guaranteed to be unique across the entire document. One specific issue addressed by this change is the Graph Editor crash reported in #1932.
1 parent aefa3f5 commit 6d0e937

File tree

5 files changed

+42
-30
lines changed

5 files changed

+42
-30
lines changed

source/MaterialXGenShader/ShaderGraph.cpp

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ void ShaderGraph::createConnectedNodes(const ElementPtr& downstreamElement,
8383
throw ExceptionShaderGenError("Upstream element to connect is not a node '" +
8484
upstreamElement->getName() + "'");
8585
}
86-
const string& newNodeName = upstreamNode->getName();
87-
ShaderNode* newNode = getNode(newNodeName);
86+
ShaderNode* newNode = getNode(upstreamNode->getNamePath());
8887
if (!newNode)
8988
{
9089
newNode = createNode(upstreamNode, context);
@@ -101,7 +100,7 @@ void ShaderGraph::createConnectedNodes(const ElementPtr& downstreamElement,
101100
InputPtr graphInput = activeInput->getInterfaceInput();
102101
if (graphInput && graphInput->hasDefaultGeomPropString())
103102
{
104-
ShaderInput* shaderInput = getNode(upstreamNode->getName())->getInput(activeInput->getName());
103+
ShaderInput* shaderInput = newNode->getInput(activeInput->getName());
105104
addDefaultGeomNode(shaderInput, *graphInput->getDefaultGeomProp(), context);
106105
}
107106
}
@@ -127,12 +126,11 @@ void ShaderGraph::createConnectedNodes(const ElementPtr& downstreamElement,
127126
"' on upstream node '" + upstreamNode->getName() + "'");
128127
}
129128

130-
// Check if it was a node downstream
129+
// Connect to downstream node or graph output socket.
131130
NodePtr downstreamNode = downstreamElement->asA<Node>();
132131
if (downstreamNode)
133132
{
134-
// We have a node downstream
135-
ShaderNode* downstream = getNode(downstreamNode->getName());
133+
ShaderNode* downstream = getNode(downstreamNode->getNamePath());
136134
if (downstream)
137135
{
138136
if (downstream == newNode)
@@ -152,7 +150,7 @@ void ShaderGraph::createConnectedNodes(const ElementPtr& downstreamElement,
152150
}
153151
else
154152
{
155-
throw ExceptionShaderGenError("Could not find downstream node ' " + downstreamNode->getName() + "'");
153+
throw ExceptionShaderGenError("No connecting element for downstream node '" + downstreamNode->getName() + "'");
156154
}
157155
}
158156
}
@@ -542,8 +540,7 @@ ShaderGraphPtr ShaderGraph::create(const ShaderGraph* parent, const string& name
542540
graph->addOutputSockets(*nodeDef, context);
543541

544542
// Create this shader node in the graph.
545-
ShaderNodePtr newNode = ShaderNode::create(graph.get(), node->getName(), *nodeDef, context);
546-
graph->addNode(newNode);
543+
ShaderNode* newNode = graph->createNode(node->getName(), node->getNamePath(), nodeDef, context);
547544

548545
// Share metadata.
549546
graph->setMetadata(newNode->getMetadata());
@@ -624,7 +621,7 @@ ShaderGraphPtr ShaderGraph::create(const ShaderGraph* parent, const string& name
624621
}
625622

626623
// Apply color and unit transforms to each input.
627-
graph->applyInputTransforms(node, newNode.get(), context);
624+
graph->applyInputTransforms(node, newNode, context);
628625

629626
// Set root for upstream dependency traversal
630627
root = node;
@@ -696,7 +693,7 @@ void ShaderGraph::applyInputTransforms(ConstNodePtr node, ShaderNode* shaderNode
696693
}
697694
}
698695

699-
ShaderNode* ShaderGraph::createNode(const string& name, ConstNodeDefPtr nodeDef, GenContext& context)
696+
ShaderNode* ShaderGraph::createNode(const string& name, const string& uniqueId, ConstNodeDefPtr nodeDef, GenContext& context)
700697
{
701698
if (!nodeDef)
702699
{
@@ -705,7 +702,8 @@ ShaderNode* ShaderGraph::createNode(const string& name, ConstNodeDefPtr nodeDef,
705702

706703
// Create this node in the graph.
707704
ShaderNodePtr newNode = ShaderNode::create(this, name, *nodeDef, context);
708-
_nodeMap[name] = newNode;
705+
newNode->_uniqueId = uniqueId;
706+
_nodeMap[uniqueId] = newNode;
709707
_nodeOrder.push_back(newNode.get());
710708

711709
return newNode.get();
@@ -717,7 +715,7 @@ ShaderNode* ShaderGraph::createNode(ConstNodePtr node, GenContext& context)
717715

718716
// Create this node in the graph.
719717
context.pushParentNode(node);
720-
ShaderNode* newNode = createNode(node->getName(), nodeDef, context);
718+
ShaderNode* newNode = createNode(node->getName(), node->getNamePath(), nodeDef, context);
721719
newNode->initialize(*node, *nodeDef, context);
722720
context.popParentNode();
723721

@@ -807,7 +805,7 @@ ShaderNode* ShaderGraph::inlineNodeBeforeOutput(ShaderGraphOutputSocket* output,
807805
}
808806

809807
// create the new node, and connect its output to the provided graph output
810-
auto newNode = createNode(newNodeName, nodeDef, context);
808+
auto newNode = createNode(newNodeName, newNodeName, nodeDef, context);
811809
if (!newNode)
812810
{
813811
throw ExceptionShaderGenError("Error while creating node '"+newNodeName+"' of type '"+nodeDefName+"'");
@@ -852,19 +850,19 @@ ShaderGraphEdgeIterator ShaderGraph::traverseUpstream(ShaderOutput* output)
852850

853851
void ShaderGraph::addNode(ShaderNodePtr node)
854852
{
855-
_nodeMap[node->getName()] = node;
853+
_nodeMap[node->getUniqueId()] = node;
856854
_nodeOrder.push_back(node.get());
857855
}
858856

859-
ShaderNode* ShaderGraph::getNode(const string& name)
857+
ShaderNode* ShaderGraph::getNode(const string& uniqueId)
860858
{
861-
auto it = _nodeMap.find(name);
859+
auto it = _nodeMap.find(uniqueId);
862860
return it != _nodeMap.end() ? it->second.get() : nullptr;
863861
}
864862

865-
const ShaderNode* ShaderGraph::getNode(const string& name) const
863+
const ShaderNode* ShaderGraph::getNode(const string& uniqueId) const
866864
{
867-
return const_cast<ShaderGraph*>(this)->getNode(name);
865+
return const_cast<ShaderGraph*>(this)->getNode(uniqueId);
868866
}
869867

870868
void ShaderGraph::finalize(GenContext& context)
@@ -1045,15 +1043,19 @@ void ShaderGraph::optimize(GenContext& context)
10451043
}
10461044

10471045
// Remove any unused nodes
1048-
for (ShaderNode* node : _nodeOrder)
1046+
for (auto it = _nodeMap.begin(); it != _nodeMap.end();)
10491047
{
1050-
if (usedNodesSet.count(node) == 0)
1048+
if (usedNodesSet.count(it->second.get()) == 0)
10511049
{
10521050
// Break all connections
1053-
disconnect(node);
1051+
disconnect(it->second.get());
10541052

10551053
// Erase from storage
1056-
_nodeMap.erase(node->getName());
1054+
it = _nodeMap.erase(it);
1055+
}
1056+
else
1057+
{
1058+
++it;
10571059
}
10581060
}
10591061

source/MaterialXGenShader/ShaderGraph.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@ class MX_GENSHADER_API ShaderGraph : public ShaderNode
6161
/// Return true if this node is a graph.
6262
bool isAGraph() const override { return true; }
6363

64-
/// Get an internal node by name
65-
ShaderNode* getNode(const string& name);
64+
/// Get an internal node by its unique identifier.
65+
ShaderNode* getNode(const string& uniqueId);
6666

67-
/// Get an internal node by name
68-
const ShaderNode* getNode(const string& name) const;
67+
/// Get an internal node by its unique identifier.
68+
const ShaderNode* getNode(const string& uniqueId) const;
6969

7070
/// Get a vector of all nodes in order
7171
const vector<ShaderNode*>& getNodes() const { return _nodeOrder; }
@@ -137,11 +137,12 @@ class MX_GENSHADER_API ShaderGraph : public ShaderNode
137137
GenContext& context);
138138

139139
/// Create a new node in a graph from a node definition.
140+
/// The uniqueId argument is used as the node's key in the graph's node map.
140141
/// Note - this does not initialize the node instance with any concrete values, but
141142
/// instead creates an empty instance of the provided node definition
142-
ShaderNode* createNode(const string& name, ConstNodeDefPtr nodeDef, GenContext& context);
143+
ShaderNode* createNode(const string& name, const string& uniqueId, ConstNodeDefPtr nodeDef, GenContext& context);
143144

144-
/// Add a node to the graph
145+
/// Add a node to the graph, keyed by the node's unique identifier.
145146
void addNode(ShaderNodePtr node);
146147

147148
/// Add input sockets from an interface element (nodedef, nodegraph or node)

source/MaterialXGenShader/ShaderNode.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ const string ShaderNode::GEOMETRIC_GROUPNAME = "geometric";
164164
ShaderNode::ShaderNode(const ShaderGraph* parent, const string& name) :
165165
_parent(parent),
166166
_name(name),
167+
_uniqueId(name),
167168
_classification(0),
168169
_impl(nullptr)
169170
{

source/MaterialXGenShader/ShaderNode.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,13 @@ class MX_GENSHADER_API ShaderNode
430430
return _name;
431431
}
432432

433+
/// Return the unique identifier for this node, used as its key
434+
/// in the parent graph's node map.
435+
const string& getUniqueId() const
436+
{
437+
return _uniqueId;
438+
}
439+
433440
/// Return the implementation used for this node.
434441
const ShaderNodeImpl& getImplementation() const
435442
{
@@ -495,6 +502,7 @@ class MX_GENSHADER_API ShaderNode
495502

496503
const ShaderGraph* _parent;
497504
string _name;
505+
string _uniqueId;
498506
uint32_t _classification;
499507

500508
std::unordered_map<string, ShaderInputPtr> _inputMap;

source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ void testUniqueNames(mx::GenContext& context, const std::string& stage)
313313
// Make sure the output and internal node output has their variable names set
314314
const mx::ShaderGraphOutputSocket* sgOutputSocket = shader->getGraph().getOutputSocket();
315315
REQUIRE(sgOutputSocket->getVariable() != outputQualifier);
316-
const mx::ShaderNode* sgNode1 = shader->getGraph().getNode(node1->getName());
316+
const mx::ShaderNode* sgNode1 = shader->getGraph().getNode(node1->getNamePath());
317317
REQUIRE(sgNode1->getOutput()->getVariable() == "unique_names_out");
318318
}
319319

0 commit comments

Comments
 (0)