From cd47c0c78d62d71380aecac48ec8288922f296dc Mon Sep 17 00:00:00 2001 From: Jerry Gamache Date: Thu, 19 Dec 2024 16:12:26 -0500 Subject: [PATCH 1/3] EMSUSD-1975 - Optimize material libraries Allow deep library optimization ff a user introduces a material library that has nodes implemented via NodeGraph that embed optimizable nodes. Also fixed issues encountered while dealing with MaterialX namespaces. --- .../render/MaterialXGenOgsXml/LobePruner.cpp | 151 +++++++++++++++--- .../render/MaterialXGenOgsXml/LobePruner.h | 20 +-- .../MaterialXGenOgsXml/ShaderGenUtil.cpp | 12 +- .../render/vp2RenderDelegate/material.cpp | 4 +- lib/mayaUsdAPI/render.cpp | 7 + lib/mayaUsdAPI/render.h | 6 + .../lib/mayaUsd/utils/test_ShaderGenUtils.cpp | 12 +- 7 files changed, 169 insertions(+), 43 deletions(-) diff --git a/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp b/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp index 9d92935b0..dc1e5aef7 100644 --- a/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp +++ b/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp @@ -85,10 +85,14 @@ class LobePrunerImpl LobePrunerImpl(LobePrunerImpl&&) = delete; LobePrunerImpl& operator=(LobePrunerImpl&&) = delete; - bool getOptimizedNodeCategory(const mx::Node& node, std::string& nodeCategory); + bool getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& nodeDef); + mx::StringVec getOptimizedAttributeNames(const mx::NodeDefPtr& nodeDef) const; PXR_NS::TfToken getOptimizedNodeId(const PXR_NS::HdMaterialNode2& node); + bool isOptimizedNodeId(const PXR_NS::TfToken& nodeId); + + void optimizeLibrary(const MaterialX::DocumentPtr& library); static const std::string ND_PREFIX; static const std::string DARK_BASE; @@ -105,7 +109,7 @@ class LobePrunerImpl const mx::InputPtr& input, const mx::NodeGraphPtr& ng, const mx::NodeDefPtr& nd); - void + mx::NodeDefPtr ensureLibraryHasOptimizedShader(const PXR_NS::TfToken& nodeDefName, const std::string& flags); void optimizeZeroValue( mx::NodeGraphPtr& optimizedNodeGraph, @@ -132,6 +136,7 @@ class LobePrunerImpl std::unordered_map _prunerData; mx::DocumentPtr _library; + PXR_NS::TfToken::HashSet _optimizedNodeIds; }; const std::string LobePrunerImpl::ND_PREFIX = "LPOPTIND_"; @@ -216,6 +221,90 @@ bool LobePrunerImpl::isLobeInput(const mx::InputPtr& input, const mx::NodeDefPtr return true; } +void LobePrunerImpl::optimizeLibrary(const MaterialX::DocumentPtr& library) +{ + if (!_library || _prunerData.empty()) { + return; + } + + std::set allDefinedNodeGraphs; + // Go thru all NodeGraphs found in the library that have an associated NodeDef: + for (const auto& ng : library->getNodeGraphs()) { + if (ng->hasNodeDefString()) { + allDefinedNodeGraphs.insert(ng->getName()); + } + } + for (const auto& impl : library->getImplementations()) { + if (impl->hasNodeGraph()) { + allDefinedNodeGraphs.insert(impl->getNodeGraph()); + } + } + + for (const auto& ngName : allDefinedNodeGraphs) { + const auto ng = library->getNodeGraph(ngName); + // Go thru all the nodes of that NodeGraph + for (const auto& node : ng->getNodes()) { + // Can this node be optimized? + const auto& nd = node->getNodeDef(); + if (!nd) { + continue; + } + + const auto ndName = PXR_NS::TfToken(nd->getName()); + const auto ndIt = _prunerData.find(ndName); + if (ndIt == _prunerData.end()) { + continue; + } + + // This NodeGraph contains an optimizable embedded surface shader node. + std::string flags(ndIt->second._attributeData.size(), 'x'); + + bool canOptimize = false; + + auto attrIt = ndIt->second._attributeData.cbegin(); + for (size_t i = 0; attrIt != ndIt->second._attributeData.cend(); ++attrIt, ++i) { + const auto nodeinput = node->getActiveInput(attrIt->first); + float inputValue = 0.5F; + if (nodeinput) { + // Can not optimize if connected in any way. + if (nodeinput->hasNodeName() || nodeinput->hasOutputString() + || nodeinput->hasInterfaceName()) { + continue; + } + inputValue = nodeinput->getValue()->asA(); + } else { + const auto defInput = nd->getActiveInput(attrIt->first); + inputValue = defInput->getValue()->asA(); + } + + for (const auto& optimizableValue : attrIt->second) { + if (optimizableValue.first == inputValue) { + if (inputValue == 0.0F) { + flags[i] = '0'; + } else { + flags[i] = '1'; + } + canOptimize = true; + } + } + } + + if (canOptimize) { + const auto optimizedNodeDef = ensureLibraryHasOptimizedShader(ndName, flags); + // Replace the node with an optimized one: + const auto nsPrefix = optimizedNodeDef->hasNamespace() + ? optimizedNodeDef->getNamespace() + ":" + : std::string {}; + + node->setCategory(nsPrefix + optimizedNodeDef->getNodeString()); + if (node->hasNodeDefString()) { + node->setNodeDefString(optimizedNodeDef->getName()); + } + } + } + } +} + void LobePrunerImpl::addOptimizableValue( float value, const mx::InputPtr& input, @@ -242,7 +331,7 @@ void LobePrunerImpl::addOptimizableValue( valueMap.find(value)->second.push_back(PXR_NS::TfToken(input->getParent()->getName())); } -bool LobePrunerImpl::getOptimizedNodeCategory(const mx::Node& node, std::string& nodeCategory) +bool LobePrunerImpl::getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& nodeDef) { const auto& nd = node.getNodeDef(); if (!nd) { @@ -288,8 +377,7 @@ bool LobePrunerImpl::getOptimizedNodeCategory(const mx::Node& node, std::string& } if (canOptimize) { - ensureLibraryHasOptimizedShader(ndName, flags); - nodeCategory = node.getCategory() + "_" + flags; + nodeDef = ensureLibraryHasOptimizedShader(ndName, flags); return true; } @@ -356,39 +444,53 @@ PXR_NS::TfToken LobePrunerImpl::getOptimizedNodeId(const PXR_NS::HdMaterialNode2 } if (canOptimize) { - ensureLibraryHasOptimizedShader(node.nodeTypeId, flags); - return PXR_NS::TfToken( - ND_PREFIX + nodeDef->GetFamily().GetString() + "_" + flags + "_surfaceshader"); + return PXR_NS::TfToken(ensureLibraryHasOptimizedShader(node.nodeTypeId, flags)->getName()); } return retVal; } -void LobePrunerImpl::ensureLibraryHasOptimizedShader( +bool LobePrunerImpl::isOptimizedNodeId(const PXR_NS::TfToken& nodeId) +{ + return _optimizedNodeIds.count(nodeId) != 0; +} + +mx::NodeDefPtr LobePrunerImpl::ensureLibraryHasOptimizedShader( const PXR_NS::TfToken& nodeDefName, const std::string& flags) { const auto ndIt = _prunerData.find(nodeDefName); if (ndIt == _prunerData.end()) { - return; + return {}; } - const auto originalNodeDef = _library->getNodeDef(nodeDefName.GetString()); - const auto originalNodeGraph = _library->getNodeGraph(ndIt->second._nodeGraphName); - const std::string optimizedNodeName = originalNodeDef->getNodeString() + "_" + flags; - const std::string optimizedNodeDefName = ND_PREFIX + optimizedNodeName + "_surfaceshader"; - if (_library->getNodeDef(optimizedNodeDefName)) { + const auto originalNodeDef = _library->getNodeDef(nodeDefName.GetString()); + const auto originalNodeGraph = _library->getNodeGraph(ndIt->second._nodeGraphName); + const auto nsPrefix = originalNodeDef->hasNamespace() + ? originalNodeDef->getNamespace() + mx::NAME_PREFIX_SEPARATOR + : std::string {}; + auto optimizedNodeName = originalNodeDef->getNodeString() + "_" + flags; + if (!nsPrefix.empty() && optimizedNodeName.rfind(nsPrefix, 0) == 0) { + optimizedNodeName = optimizedNodeName.substr(nsPrefix.size()); + } + const auto optimizedNodeNameWithNS = nsPrefix + optimizedNodeName; + const std::string optimizedNodeDefName + = nsPrefix + ND_PREFIX + optimizedNodeName + "_surfaceshader"; + if (const auto existingNd = _library->getNodeDef(optimizedNodeDefName)) { // Already there - return; + return existingNd; } + _optimizedNodeIds.insert(PXR_NS::TfToken(optimizedNodeDefName)); + auto optimizedNodeDef = _library->addNodeDef(optimizedNodeDefName, "surfaceshader", optimizedNodeName); optimizedNodeDef->copyContentFrom(originalNodeDef); optimizedNodeDef->setSourceUri(""); optimizedNodeDef->setNodeString(optimizedNodeName); - auto optimizedNodeGraph = _library->addNodeGraph("NG_" + optimizedNodeName + "_surfaceshader"); + auto optimizedNodeGraph + = _library->addNodeGraph(nsPrefix + "LPOPTING_" + optimizedNodeName + "_surfaceshader"); optimizedNodeGraph->copyContentFrom(originalNodeGraph); optimizedNodeGraph->setSourceUri(""); optimizedNodeGraph->setNodeDefString(optimizedNodeDefName); @@ -414,6 +516,8 @@ void LobePrunerImpl::ensureLibraryHasOptimizedShader( default: continue; } } + + return optimizedNodeDef; } void LobePrunerImpl::optimizeZeroValue( @@ -560,9 +664,16 @@ LobePruner::Ptr LobePruner::create() { return std::make_shared(); } LobePruner::~LobePruner() = default; LobePruner::LobePruner() = default; -bool LobePruner::getOptimizedNodeCategory(const mx::Node& node, std::string& nodeCategory) +void LobePruner::optimizeLibrary(const MaterialX::DocumentPtr& library) +{ + if (_impl) { + _impl->optimizeLibrary(library); + } +} + +bool LobePruner::getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& nodeDef) { - return _impl ? _impl->getOptimizedNodeCategory(node, nodeCategory) : false; + return _impl ? _impl->getOptimizedNodeDef(node, nodeDef) : false; } mx::StringVec LobePruner::getOptimizedAttributeNames(const mx::NodeDefPtr& nodeDef) const @@ -582,7 +693,7 @@ void LobePruner::setLibrary(const mx::DocumentPtr& library) bool LobePruner::isOptimizedNodeId(const PXR_NS::TfToken& nodeId) { - return nodeId.GetString().rfind(LobePrunerImpl::ND_PREFIX, 0) == 0; + return _impl ? _impl->isOptimizedNodeId(nodeId) : false; } const std::string& LobePruner::getOptimizedNodeDefPrefix() { return LobePrunerImpl::ND_PREFIX; } diff --git a/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.h b/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.h index 8c514ce24..777777855 100644 --- a/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.h +++ b/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.h @@ -54,19 +54,19 @@ class MAYAUSD_CORE_PUBLIC LobePruner */ void setLibrary(const mx::DocumentPtr& library); + /*! Traverses and optimizes in place all NodeGraphs found in the library. Useful if a library + * contains NodeGraphs that embed optimizable nodes. + * @param[in] library is the library used to generate shaders. + */ + void optimizeLibrary(const MaterialX::DocumentPtr& library); + /*! Checks if a node is optimizable and if this is the case, create the optimized NodeDef and - * NodeGraph in the library and return the optimized node category. An optimized node category - * will consist of the name of the original category followed by a series of characters - * describing which attibutes were optimized: - * - 'x' that attribute was not optimized (intermediate value or connected) - * - '0' a zero value was optimized - * - '1' a one value was optimized - * The ordered list of attributes names can be found by calling getOptimizedAttributeNames(). + * NodeGraph in the library and return the optimized NodeDef. * @param[in] node is a node we want to optimize. All nodes are welcome. - * @param[out] nodeCategory is the node category of the optimized node. + * @param[out] nodeDef is the NodeDef of the optimized node. * \return true if an optimization was found */ - bool getOptimizedNodeCategory(const mx::Node& node, std::string& nodeCategory); + bool getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& nodeDef); /*! Get the list of attribute names that are optimization targets for a specific NodeDef. * @param[in] nodeDef is preferably node definition that has previously been optimized, but all @@ -95,7 +95,7 @@ class MAYAUSD_CORE_PUBLIC LobePruner * @param[in] nodeId is a node:id we want to check. * \return true if that node was generated by a LobePruner. */ - static bool isOptimizedNodeId(const PXR_NS::TfToken& nodeId); + bool isOptimizedNodeId(const PXR_NS::TfToken& nodeId); /*! Returns the NodeDef prefix common to all LobePruner optimized definitions. * \return the LobePruner NodeDef prefix diff --git a/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp b/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp index 5c8f476eb..2a8ff3880 100644 --- a/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp +++ b/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp @@ -233,11 +233,13 @@ mx::NodePtr TopoNeutralGraph::cloneNode(const mx::Node& node, mx::GraphElement& if (!nodeDef) { throw mx::Exception("Ambiguous node is not fully resolvable"); } - std::string optimizedNodeCategory; - if (_lobePruner && _lobePruner->getOptimizedNodeCategory(node, optimizedNodeCategory)) { - destNode->setCategory(optimizedNodeCategory); - destNode->setNodeDefString( - LobePruner::getOptimizedNodeDefPrefix() + optimizedNodeCategory + "_surfaceshader"); + mx::NodeDefPtr optimizedNodeDef; + if (_lobePruner && _lobePruner->getOptimizedNodeDef(node, optimizedNodeDef)) { + const auto nsPrefix = optimizedNodeDef->hasNamespace() + ? optimizedNodeDef->getNamespace() + ":" + : std::string {}; + destNode->setCategory(nsPrefix + optimizedNodeDef->getNodeString()); + destNode->setNodeDefString(optimizedNodeDef->getName()); for (const auto& attrName : _lobePruner->getOptimizedAttributeNames(nodeDef)) { _optimizedAttributes.push_back(node.getNamePath() + "." + attrName); } diff --git a/lib/mayaUsd/render/vp2RenderDelegate/material.cpp b/lib/mayaUsd/render/vp2RenderDelegate/material.cpp index 7d24d3b74..90c0aa5bd 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/material.cpp +++ b/lib/mayaUsd/render/vp2RenderDelegate/material.cpp @@ -359,6 +359,7 @@ struct _MaterialXData #if MX_COMBINED_VERSION >= 13808 _lobePruner = MaterialXMaya::ShaderGenUtil::LobePruner::create(); _lobePruner->setLibrary(_mtlxLibrary); + _lobePruner->optimizeLibrary(_mtlxLibrary); // TODO: Optimize published shaders. // SCENARIO: User publishes a shader with a NodeGraph implementation that encapsulates a @@ -3038,8 +3039,7 @@ MHWRender::MShaderInstance* HdVP2Material::CompiledNetwork::_CreateMaterialXShad const mx::FileSearchPath& crLibrarySearchPath(_GetMaterialXData()._mtlxSearchPath); #if MX_COMBINED_VERSION >= 13808 if (mtlxSdrNode - || MaterialXMaya::ShaderGenUtil::LobePruner::isOptimizedNodeId( - surfTerminal->nodeTypeId)) { + || _GetMaterialXData()._lobePruner->isOptimizedNodeId(surfTerminal->nodeTypeId)) { #else if (mtlxSdrNode) { #endif diff --git a/lib/mayaUsdAPI/render.cpp b/lib/mayaUsdAPI/render.cpp index 8022c28a7..f58b6b5db 100644 --- a/lib/mayaUsdAPI/render.cpp +++ b/lib/mayaUsdAPI/render.cpp @@ -157,6 +157,13 @@ void LobePruner::setLibrary(const MaterialX::DocumentPtr& library) } } +void LobePruner::optimizeLibrary(const MaterialX::DocumentPtr& library) +{ + if (_imp && _imp->_lobePruner) { + _imp->_lobePruner->optimizeLibrary(library); + } +} + struct TopoNeutralGraphImpl { TopoNeutralGraphImpl(const MaterialX::ElementPtr& material) diff --git a/lib/mayaUsdAPI/render.h b/lib/mayaUsdAPI/render.h index b3f71699d..ed4593f7c 100644 --- a/lib/mayaUsdAPI/render.h +++ b/lib/mayaUsdAPI/render.h @@ -135,6 +135,12 @@ class MAYAUSD_API_PUBLIC LobePruner */ void setLibrary(const MaterialX::DocumentPtr& library); + /*! Traverses and optimizes in place all NodeGraphs found in the library. Useful if a library + * contains NodeGraphs that embed optimizable nodes. + * @param[in] library is the library used to generate shaders. + */ + void optimizeLibrary(const MaterialX::DocumentPtr& library); + private: LobePruner(); diff --git a/test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp b/test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp index 6083f92ad..c60799cd0 100644 --- a/test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp +++ b/test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp @@ -179,17 +179,17 @@ TEST(ShaderGenUtils, lobePruner) const auto node = doc->addNode("standard_surface", "bob", "surfaceshader"); - std::string optimizedCategory; - ASSERT_TRUE(lobePruner->getOptimizedNodeCategory(*node, optimizedCategory)); + mx::NodeDefPtr optimizedNodeDef; + ASSERT_TRUE(lobePruner->getOptimizedNodeDef(*node, optimizedNodeDef)); // An x means can not optimize on that attribute // A 0 means we optimized due to this value being zero - ASSERT_EQ(optimizedCategory, "standard_surface_x0000x00x000"); + ASSERT_EQ(optimizedNodeDef->getNodeString(), "standard_surface_x0000x00x000"); auto input = node->addInputFromNodeDef("subsurface"); input->setValueString("1.0"); - ASSERT_TRUE(lobePruner->getOptimizedNodeCategory(*node, optimizedCategory)); + ASSERT_TRUE(lobePruner->getOptimizedNodeDef(*node, optimizedNodeDef)); // Now have a 1 for subsurface since we can also optimize the 1 value for mix nodes. - ASSERT_EQ(optimizedCategory, "standard_surface_x0000x00x010"); + ASSERT_EQ(optimizedNodeDef->getNodeString(), "standard_surface_x0000x00x010"); PXR_NS::HdMaterialNode2 usdNode; usdNode.nodeTypeId = PXR_NS::TfToken("ND_standard_surface_surfaceshader"); @@ -198,7 +198,7 @@ TEST(ShaderGenUtils, lobePruner) optimizedNodeId.GetString(), sgu::LobePruner::getOptimizedNodeDefPrefix() + "standard_surface_x0000x00x000_surfaceshader"); - ASSERT_TRUE(sgu::LobePruner::isOptimizedNodeId(optimizedNodeId)); + ASSERT_TRUE(lobePruner->isOptimizedNodeId(optimizedNodeId)); usdNode.nodeTypeId = PXR_NS::TfToken("ND_mix_surfaceshader"); optimizedNodeId = lobePruner->getOptimizedNodeId(usdNode); From ccf4260876747285fea183aba2c06bcb9652b3a7 Mon Sep 17 00:00:00 2001 From: Jerry Gamache Date: Fri, 20 Dec 2024 10:51:25 -0500 Subject: [PATCH 2/3] Simplified and self documenting as requested. --- .../render/MaterialXGenOgsXml/LobePruner.cpp | 25 +++++++++---------- .../render/MaterialXGenOgsXml/LobePruner.h | 5 ++-- .../MaterialXGenOgsXml/ShaderGenUtil.cpp | 5 ++-- .../lib/mayaUsd/utils/test_ShaderGenUtils.cpp | 7 +++--- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp b/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp index dc1e5aef7..927d704a7 100644 --- a/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp +++ b/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp @@ -85,7 +85,7 @@ class LobePrunerImpl LobePrunerImpl(LobePrunerImpl&&) = delete; LobePrunerImpl& operator=(LobePrunerImpl&&) = delete; - bool getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& nodeDef); + mx::NodeDefPtr getOptimizedNodeDef(const mx::Node& node); mx::StringVec getOptimizedAttributeNames(const mx::NodeDefPtr& nodeDef) const; @@ -110,7 +110,7 @@ class LobePrunerImpl const mx::NodeGraphPtr& ng, const mx::NodeDefPtr& nd); mx::NodeDefPtr - ensureLibraryHasOptimizedShader(const PXR_NS::TfToken& nodeDefName, const std::string& flags); + getOrAddOptimizedNodeDef(const PXR_NS::TfToken& nodeDefName, const std::string& flags); void optimizeZeroValue( mx::NodeGraphPtr& optimizedNodeGraph, const OptimizableValueMap& optimizationMap, @@ -290,7 +290,7 @@ void LobePrunerImpl::optimizeLibrary(const MaterialX::DocumentPtr& library) } if (canOptimize) { - const auto optimizedNodeDef = ensureLibraryHasOptimizedShader(ndName, flags); + const auto optimizedNodeDef = getOrAddOptimizedNodeDef(ndName, flags); // Replace the node with an optimized one: const auto nsPrefix = optimizedNodeDef->hasNamespace() ? optimizedNodeDef->getNamespace() + ":" @@ -331,17 +331,17 @@ void LobePrunerImpl::addOptimizableValue( valueMap.find(value)->second.push_back(PXR_NS::TfToken(input->getParent()->getName())); } -bool LobePrunerImpl::getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& nodeDef) +mx::NodeDefPtr LobePrunerImpl::getOptimizedNodeDef(const mx::Node& node) { const auto& nd = node.getNodeDef(); if (!nd) { - return false; + return {}; } const auto ndName = PXR_NS::TfToken(nd->getName()); const auto ndIt = _prunerData.find(ndName); if (ndIt == _prunerData.end()) { - return false; + return {}; } std::string flags(ndIt->second._attributeData.size(), 'x'); @@ -377,11 +377,10 @@ bool LobePrunerImpl::getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& n } if (canOptimize) { - nodeDef = ensureLibraryHasOptimizedShader(ndName, flags); - return true; + return getOrAddOptimizedNodeDef(ndName, flags); } - return false; + return {}; } mx::StringVec LobePrunerImpl::getOptimizedAttributeNames(const mx::NodeDefPtr& nodeDef) const @@ -444,7 +443,7 @@ PXR_NS::TfToken LobePrunerImpl::getOptimizedNodeId(const PXR_NS::HdMaterialNode2 } if (canOptimize) { - return PXR_NS::TfToken(ensureLibraryHasOptimizedShader(node.nodeTypeId, flags)->getName()); + return PXR_NS::TfToken(getOrAddOptimizedNodeDef(node.nodeTypeId, flags)->getName()); } return retVal; @@ -455,7 +454,7 @@ bool LobePrunerImpl::isOptimizedNodeId(const PXR_NS::TfToken& nodeId) return _optimizedNodeIds.count(nodeId) != 0; } -mx::NodeDefPtr LobePrunerImpl::ensureLibraryHasOptimizedShader( +mx::NodeDefPtr LobePrunerImpl::getOrAddOptimizedNodeDef( const PXR_NS::TfToken& nodeDefName, const std::string& flags) { @@ -671,9 +670,9 @@ void LobePruner::optimizeLibrary(const MaterialX::DocumentPtr& library) } } -bool LobePruner::getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& nodeDef) +mx::NodeDefPtr LobePruner::getOptimizedNodeDef(const mx::Node& node) { - return _impl ? _impl->getOptimizedNodeDef(node, nodeDef) : false; + return _impl ? _impl->getOptimizedNodeDef(node) : mx::NodeDefPtr {}; } mx::StringVec LobePruner::getOptimizedAttributeNames(const mx::NodeDefPtr& nodeDef) const diff --git a/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.h b/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.h index 777777855..418fbd33c 100644 --- a/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.h +++ b/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.h @@ -63,10 +63,9 @@ class MAYAUSD_CORE_PUBLIC LobePruner /*! Checks if a node is optimizable and if this is the case, create the optimized NodeDef and * NodeGraph in the library and return the optimized NodeDef. * @param[in] node is a node we want to optimize. All nodes are welcome. - * @param[out] nodeDef is the NodeDef of the optimized node. - * \return true if an optimization was found + * \return the optimized NodeDef if one was found, nullptr otherwise */ - bool getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& nodeDef); + mx::NodeDefPtr getOptimizedNodeDef(const mx::Node& node); /*! Get the list of attribute names that are optimization targets for a specific NodeDef. * @param[in] nodeDef is preferably node definition that has previously been optimized, but all diff --git a/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp b/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp index 2a8ff3880..a01141321 100644 --- a/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp +++ b/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp @@ -233,8 +233,9 @@ mx::NodePtr TopoNeutralGraph::cloneNode(const mx::Node& node, mx::GraphElement& if (!nodeDef) { throw mx::Exception("Ambiguous node is not fully resolvable"); } - mx::NodeDefPtr optimizedNodeDef; - if (_lobePruner && _lobePruner->getOptimizedNodeDef(node, optimizedNodeDef)) { + auto optimizedNodeDef + = _lobePruner ? _lobePruner->getOptimizedNodeDef(node) : mx::NodeDefPtr {}; + if (optimizedNodeDef) { const auto nsPrefix = optimizedNodeDef->hasNamespace() ? optimizedNodeDef->getNamespace() + ":" : std::string {}; diff --git a/test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp b/test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp index c60799cd0..d6929363b 100644 --- a/test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp +++ b/test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp @@ -179,15 +179,16 @@ TEST(ShaderGenUtils, lobePruner) const auto node = doc->addNode("standard_surface", "bob", "surfaceshader"); - mx::NodeDefPtr optimizedNodeDef; - ASSERT_TRUE(lobePruner->getOptimizedNodeDef(*node, optimizedNodeDef)); + auto optimizedNodeDef = lobePruner->getOptimizedNodeDef(*node); + ASSERT_TRUE(optimizedNodeDef); // An x means can not optimize on that attribute // A 0 means we optimized due to this value being zero ASSERT_EQ(optimizedNodeDef->getNodeString(), "standard_surface_x0000x00x000"); auto input = node->addInputFromNodeDef("subsurface"); input->setValueString("1.0"); - ASSERT_TRUE(lobePruner->getOptimizedNodeDef(*node, optimizedNodeDef)); + optimizedNodeDef = lobePruner->getOptimizedNodeDef(*node); + ASSERT_TRUE(optimizedNodeDef); // Now have a 1 for subsurface since we can also optimize the 1 value for mix nodes. ASSERT_EQ(optimizedNodeDef->getNodeString(), "standard_surface_x0000x00x010"); From 82a113b0898da1f20fee82a67c691b9eaa9621e7 Mon Sep 17 00:00:00 2001 From: Jerry Gamache Date: Fri, 20 Dec 2024 12:42:45 -0500 Subject: [PATCH 3/3] Fix crash when used with a real world shader library --- .../render/MaterialXGenOgsXml/LobePruner.cpp | 105 +++++++++++------- 1 file changed, 63 insertions(+), 42 deletions(-) diff --git a/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp b/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp index 927d704a7..98984bdb5 100644 --- a/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp +++ b/lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp @@ -57,13 +57,13 @@ class LobePrunerImpl // nodeGraphName, // map< attributeName, // AttributeMap // map< attributeValue, // OptimizableValueMap - // NodeVector + // NodeSet // > // > // } // > - using NodeVector = std::vector; - using OptimizableValueMap = std::map; + using NodeSet = PXR_NS::TfToken::HashSet; + using OptimizableValueMap = std::map; // We want attributes alphabetically sorted: using AttributeMap = std::map; struct NodeDefData @@ -73,7 +73,7 @@ class LobePrunerImpl }; // Also helps if we have a reverse connection map from source node to dest node: - using Destinations = std::vector; + using Destinations = std::set; using ReverseCnxMap = std::map; public: @@ -133,6 +133,7 @@ class LobePrunerImpl mx::NodePtr& node, const std::string& darkNodeName, const std::string& darkNodeDefName) const; + void updateReverseMap(ReverseCnxMap& reverseMap, const std::string& nameToRemove) const; std::unordered_map _prunerData; mx::DocumentPtr _library; @@ -325,10 +326,10 @@ void LobePrunerImpl::addOptimizableValue( auto& valueMap = attrMap._attributeData.find(interfaceName)->second; if (!valueMap.count(value)) { - valueMap.emplace(value, NodeVector {}); + valueMap.emplace(value, NodeSet {}); } - valueMap.find(value)->second.push_back(PXR_NS::TfToken(input->getParent()->getName())); + valueMap.find(value)->second.insert(PXR_NS::TfToken(input->getParent()->getName())); } mx::NodeDefPtr LobePrunerImpl::getOptimizedNodeDef(const mx::Node& node) @@ -502,7 +503,7 @@ mx::NodeDefPtr LobePrunerImpl::getOrAddOptimizedNodeDef( if (!reverseMap.count(sourceNodeName)) { reverseMap.emplace(sourceNodeName, Destinations {}); } - reverseMap.find(sourceNodeName)->second.push_back(node->getName()); + reverseMap.find(sourceNodeName)->second.insert(node->getName()); } } } @@ -587,38 +588,38 @@ void LobePrunerImpl::optimizeMixNode( if (!bgInput) { return; } - for (const auto& destNodeName : reverseMap.find(mixNode->getName())->second) { - auto destNode = optimizedNodeGraph->getNode(destNodeName); - if (!destNode) { - return; - } - for (auto input : destNode->getInputs()) { - if (input->getNodeName() == mixNode->getName()) { - input->removeAttribute(mx::PortElement::NODE_NAME_ATTRIBUTE); - if (bgInput->hasNodeName()) { - input->setNodeName(bgInput->getNodeName()); - auto& nodeVector = reverseMap.find(bgInput->getNodeName())->second; - nodeVector.push_back(destNodeName); - nodeVector.erase( - std::remove_if( - nodeVector.begin(), - nodeVector.end(), - [mixNode](const std::string& s) { return s == mixNode->getName(); }), - nodeVector.end()); - } - if (bgInput->hasInterfaceName()) { - input->setInterfaceName(bgInput->getInterfaceName()); - } - if (bgInput->hasOutputString()) { - input->setOutputString(bgInput->getOutputString()); - } - if (bgInput->hasValueString()) { - input->setValueString(bgInput->getValueString()); + const auto nodesToUpdateIt = reverseMap.find(mixNode->getName()); + if (nodesToUpdateIt != reverseMap.end()) { + for (const auto& destNodeName : nodesToUpdateIt->second) { + auto destNode = optimizedNodeGraph->getNode(destNodeName); + if (!destNode) { + return; + } + for (auto input : destNode->getInputs()) { + if (input->getNodeName() == mixNode->getName()) { + input->removeAttribute(mx::PortElement::NODE_NAME_ATTRIBUTE); + if (bgInput->hasNodeName()) { + input->setNodeName(bgInput->getNodeName()); + const auto bgInputsToUpdateIt = reverseMap.find(bgInput->getNodeName()); + if (bgInputsToUpdateIt != reverseMap.end()) { + bgInputsToUpdateIt->second.insert(destNodeName); + } + } + if (bgInput->hasInterfaceName()) { + input->setInterfaceName(bgInput->getInterfaceName()); + } + if (bgInput->hasOutputString()) { + input->setOutputString(bgInput->getOutputString()); + } + if (bgInput->hasValueString()) { + input->setValueString(bgInput->getValueString()); + } } } } } optimizedNodeGraph->removeNode(mixNode->getName()); + updateReverseMap(reverseMap, mixNode->getName()); } void LobePrunerImpl::optimizeMultiplyNode( @@ -627,19 +628,23 @@ void LobePrunerImpl::optimizeMultiplyNode( ReverseCnxMap& reverseMap) const { // Result will be a zero value of the type it requests: - for (const auto& destNodeName : reverseMap.find(node->getName())->second) { - auto destNode = optimizedNodeGraph->getNode(destNodeName); - for (auto input : destNode->getInputs()) { - if (input->getNodeName() == node->getName()) { - input->removeAttribute(mx::PortElement::NODE_NAME_ATTRIBUTE); - const auto defaultValueIt = kZeroMultiplyValueMap.find(input->getType()); - if (defaultValueIt != kZeroMultiplyValueMap.end()) { - input->setValueString(defaultValueIt->second); + const auto nodesToUpdateIt = reverseMap.find(node->getName()); + if (nodesToUpdateIt != reverseMap.end()) { + for (const auto& destNodeName : nodesToUpdateIt->second) { + auto destNode = optimizedNodeGraph->getNode(destNodeName); + for (auto input : destNode->getInputs()) { + if (input->getNodeName() == node->getName()) { + input->removeAttribute(mx::PortElement::NODE_NAME_ATTRIBUTE); + const auto defaultValueIt = kZeroMultiplyValueMap.find(input->getType()); + if (defaultValueIt != kZeroMultiplyValueMap.end()) { + input->setValueString(defaultValueIt->second); + } } } } } optimizedNodeGraph->removeNode(node->getName()); + updateReverseMap(reverseMap, node->getName()); } void LobePrunerImpl::optimizePbrNode( @@ -658,6 +663,22 @@ void LobePrunerImpl::optimizePbrNode( } } +void LobePrunerImpl::updateReverseMap(ReverseCnxMap& reverseMap, const std::string& nameToRemove) + const +{ + // Need to remove anything leading to that deleted node: + std::vector emptyEntries; + for (auto& mapEntry : reverseMap) { + mapEntry.second.erase(nameToRemove); + if (mapEntry.second.empty()) { + emptyEntries.push_back(mapEntry.first); + } + } + for (const auto& emptyEntry : emptyEntries) { + reverseMap.erase(emptyEntry); + } +} + LobePruner::Ptr LobePruner::create() { return std::make_shared(); } LobePruner::~LobePruner() = default;