Fix crash in Shadergen for constant nodes with zero inputs#2746
Conversation
| size_t numEdits = 0; | ||
| for (ShaderNode* node : getNodes()) | ||
| { | ||
| if (node->numInputs() == 0) { |
There was a problem hiding this comment.
If we stick with this approach I think it makes sense to atleast move this inside the check for CONSTANT on line 982 - so we we don't accidentally skip other optimizations that may be added later.
But I wonder if the better thing to do here is to look at why you're custom node is being identified as a CONSTANT node. That classification is specifically to capture the standard library constant node that has the expected value input. From what I can tell of the classification code in ShaderNode.cpp - it's just matching the node string from the node definition. And in your example they appear different, so I'm a little confused as to why your custom node gets classified this way.
Another option might be to improve the CONSTANT classification logic in ShaderNode.cpp to check for the existence of the expected value input on the node definition, as that is really the assumption being made by the optimization.
There was a problem hiding this comment.
Some thoughts if it helps:
I think all constant nodes assume they follow by the existing low level <constant> definition. Thus there must be an input.
The definition provided has a constant node inside but does not expose an input so you could rationalize it is not a constant node. i.e. there is no such thing as a constant node which has no inputs.
Also there is no way to "bypass" this node since there is no way to tell what it evaluates. You could set the <output> to have a pass-through value but that is not the same meaning as a constant bypass. It means to disable the node.
There was a problem hiding this comment.
Some tracing info:
- The constant color node in the nodegraph implementation is classified as CONSTNT so triggers a bypass. This is set when reading and build the shader graph.
- Then the
embbed_constant_instancehowever is also tagged as CONSTANT but there is no explicit code to do so perhaps so it's copied over ? After flattening this node should no longer be even considered but it is.
So it appears the bypass occurs and this check makes sense as a firewall check (see comments later on in the code which say a uniform input is required to be on the nodedef).
There was a problem hiding this comment.
If we stick with this approach I think it makes sense to at least move this inside the check for CONSTANT on line 982 - so we we don't accidentally skip other optimizations that may be added later.
Good point. We could go with the check inside each if-cases dependent on classification. As DOT also expects an input (and I think the same issue can occur for custom nodes congaing a dot-node)
I'm leaning to remove the check completely and exactly match the classification against the CONSTANT/DOT number. As suggested here
|
Did some more digging and is the cause of the situation:
void ShaderGraph::finalize(GenContext& context)
{
// Allow node implementations to update the classification
// on its node instances
for (ShaderNode* node : getNodes())
{
node->getImplementation().addClassification(*node);
}which basically promotes all child node classifications to the compound parent which is created for I thus think the constant folding code is too "lax" in checking for constants since the CONSTANT classification bit is set but it could very well not be a constant since many classification bits could be promoted in the above code. The CONSTANT bits == 64 + original bit of 1 gives you 65 for I think if you check explicit for only the constant classification then you can still check if there are inputs but this is a better check. Same for DOT. i.e. the logic is not does it have a classification of CONSTANT or DOT, but that it only has a classification of CONSTANT or DOT. BTW, I'm not sure why classifications are promoted upwards. Guess it makes sense for some cases like PBR classifications or texture classifications but make constant tagging iffy. |
|
Nice find @kwokcb
I think it makes sense to remove my node->hasClassification(ShaderNode::Classification::CONSTANT)to: node->getClassification() == ShaderNode::Classification::CONSTANTor add a helper function to the node class: like
I didn't expect this behavior. I didn't expect our custom node to keep the constant classification. |
|
It would be nice for code clarity and maintenance to have a new matching method. Any future nodes added to the elide logic could use this as well. Also, would it be too much trouble to also make Thanks. |
3111f25 to
121d2d6
Compare
|
Thank you for the feedback. I have updated the PR with all suggestions. I also updated the comment description for the |
|
Got a failing test, it crashed caused by not bypassing the "seperate alpha" dot node in the gltf_pbr colortexture node, line 427-L429. Its not considered for bypassing as it does not explicitly matching the DOT classification. That node is classified as both "DOT" and "TEXTURE", The failing test is I'm not sure that we can explicit match, as the classification bits are describing more then category. I'm now leaning towards if its correct that some custom nodes (such as the one in the issue) should be classified as "CONSTANT". Not sure how to proceed. If reverting to only throwing in the bypass function, the graph editor will not crash, but other applications with custom nodes may. Maybe we should add a new type of classification corresponding to category, or not loft the "CONSTANT" classification if there is constant nodes in the implementation. |
|
I believe the issue you are seeing is because a // Second, check for specific nodes types
else if (nodeDef.getNodeString() == CONSTANT)
{
newNode->_classification = Classification::TEXTURE | Classification::CONSTANT;
}
else if (nodeDef.getNodeString() == DOT)
{
newNode->_classification = Classification::TEXTURE | Classification::DOT;
}Thus
void CompoundNode::addClassification(ShaderNode& node) const
{
// Add classification from the graph implementation.
uint32_t rootClassification = _rootGraph->getClassification();
// If constant or dot do not promote if there is an existing classication
bool elide = _rootGraph->hasClassification(ShaderNode::Classification::CONSTANT) ||
_rootGraph->hasClassification(ShaderNode::Classification::DOT);
uint32_t existingClassification = node.getClassification();
uint32_t new_classification = existingClassification | rootClassification;
if (elide)
{
// Test if only TEXTURE and constant or dot are being added
uint32_t test_classification = new_classification & ~(ShaderNode::Classification::TEXTURE);
if (test_classification == ShaderNode::Classification::CONSTANT ||
test_classification == ShaderNode::Classification::DOT)
{
std::cout << "Disallow promoting constant and dot if there is already an existing classification !!!" << existingClassification << " Node: " << node.getName() << " COmpound:" << getName() << std::endl;
return;
}
}
node.addClassification(rootClassification);As maybe this is getting too complicated for a crash fix, just adding in the (inputs == 0) check like you had before is good enough. Then leave any curretnt findings as issue discussion points. Sorry for wasting any time here. The classification / const logic is more convoluted than I thought. |
Thanks for the feedback, no worries, I'm glad you helot out figuring out what the issue was :) I reverted the explicit match to the I kept the code throwing errors in the bypass-function, as they may be relevant in the future. If those checks is deemed unnecessary, I can remove them from this PR. |
jstone-lucasfilm
left a comment
There was a problem hiding this comment.
Thanks for working through these different ideas, @bowald, and I like this latest, simplified proposal for the fix.
464a221
into
AcademySoftwareFoundation:main
This is a follow up to the PR #2746. Me and @rasmustilljander continued to look into issues related to upgrading our MaterialX 1.38 materials to MaterialX 1.39. The PR #2746 solved an issue for custom nodes getting classified as CONSTANTS but without inputs. However, we discover some more appearances having shaders generated with syntax errors not fixed by last PR. This PR tightens the check to make sure only constant/dot nodes are elided.
Closes #2745
Please read Issue #2745 for more background details.
This PR skips optimization "bypassing" values from the input of a node, if the node don't contain any inputs.
This fixes a crash for documents where a custom node was defined which was classified as constant but had zero inputs.