Skip to content

Commit cd47c0c

Browse files
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.
1 parent ecabe06 commit cd47c0c

File tree

7 files changed

+169
-43
lines changed

7 files changed

+169
-43
lines changed

lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp

+131-20
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,14 @@ class LobePrunerImpl
8585
LobePrunerImpl(LobePrunerImpl&&) = delete;
8686
LobePrunerImpl& operator=(LobePrunerImpl&&) = delete;
8787

88-
bool getOptimizedNodeCategory(const mx::Node& node, std::string& nodeCategory);
88+
bool getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& nodeDef);
89+
8990
mx::StringVec getOptimizedAttributeNames(const mx::NodeDefPtr& nodeDef) const;
9091

9192
PXR_NS::TfToken getOptimizedNodeId(const PXR_NS::HdMaterialNode2& node);
93+
bool isOptimizedNodeId(const PXR_NS::TfToken& nodeId);
94+
95+
void optimizeLibrary(const MaterialX::DocumentPtr& library);
9296

9397
static const std::string ND_PREFIX;
9498
static const std::string DARK_BASE;
@@ -105,7 +109,7 @@ class LobePrunerImpl
105109
const mx::InputPtr& input,
106110
const mx::NodeGraphPtr& ng,
107111
const mx::NodeDefPtr& nd);
108-
void
112+
mx::NodeDefPtr
109113
ensureLibraryHasOptimizedShader(const PXR_NS::TfToken& nodeDefName, const std::string& flags);
110114
void optimizeZeroValue(
111115
mx::NodeGraphPtr& optimizedNodeGraph,
@@ -132,6 +136,7 @@ class LobePrunerImpl
132136

133137
std::unordered_map<PXR_NS::TfToken, NodeDefData, PXR_NS::TfToken::HashFunctor> _prunerData;
134138
mx::DocumentPtr _library;
139+
PXR_NS::TfToken::HashSet _optimizedNodeIds;
135140
};
136141

137142
const std::string LobePrunerImpl::ND_PREFIX = "LPOPTIND_";
@@ -216,6 +221,90 @@ bool LobePrunerImpl::isLobeInput(const mx::InputPtr& input, const mx::NodeDefPtr
216221
return true;
217222
}
218223

224+
void LobePrunerImpl::optimizeLibrary(const MaterialX::DocumentPtr& library)
225+
{
226+
if (!_library || _prunerData.empty()) {
227+
return;
228+
}
229+
230+
std::set<std::string> allDefinedNodeGraphs;
231+
// Go thru all NodeGraphs found in the library that have an associated NodeDef:
232+
for (const auto& ng : library->getNodeGraphs()) {
233+
if (ng->hasNodeDefString()) {
234+
allDefinedNodeGraphs.insert(ng->getName());
235+
}
236+
}
237+
for (const auto& impl : library->getImplementations()) {
238+
if (impl->hasNodeGraph()) {
239+
allDefinedNodeGraphs.insert(impl->getNodeGraph());
240+
}
241+
}
242+
243+
for (const auto& ngName : allDefinedNodeGraphs) {
244+
const auto ng = library->getNodeGraph(ngName);
245+
// Go thru all the nodes of that NodeGraph
246+
for (const auto& node : ng->getNodes()) {
247+
// Can this node be optimized?
248+
const auto& nd = node->getNodeDef();
249+
if (!nd) {
250+
continue;
251+
}
252+
253+
const auto ndName = PXR_NS::TfToken(nd->getName());
254+
const auto ndIt = _prunerData.find(ndName);
255+
if (ndIt == _prunerData.end()) {
256+
continue;
257+
}
258+
259+
// This NodeGraph contains an optimizable embedded surface shader node.
260+
std::string flags(ndIt->second._attributeData.size(), 'x');
261+
262+
bool canOptimize = false;
263+
264+
auto attrIt = ndIt->second._attributeData.cbegin();
265+
for (size_t i = 0; attrIt != ndIt->second._attributeData.cend(); ++attrIt, ++i) {
266+
const auto nodeinput = node->getActiveInput(attrIt->first);
267+
float inputValue = 0.5F;
268+
if (nodeinput) {
269+
// Can not optimize if connected in any way.
270+
if (nodeinput->hasNodeName() || nodeinput->hasOutputString()
271+
|| nodeinput->hasInterfaceName()) {
272+
continue;
273+
}
274+
inputValue = nodeinput->getValue()->asA<float>();
275+
} else {
276+
const auto defInput = nd->getActiveInput(attrIt->first);
277+
inputValue = defInput->getValue()->asA<float>();
278+
}
279+
280+
for (const auto& optimizableValue : attrIt->second) {
281+
if (optimizableValue.first == inputValue) {
282+
if (inputValue == 0.0F) {
283+
flags[i] = '0';
284+
} else {
285+
flags[i] = '1';
286+
}
287+
canOptimize = true;
288+
}
289+
}
290+
}
291+
292+
if (canOptimize) {
293+
const auto optimizedNodeDef = ensureLibraryHasOptimizedShader(ndName, flags);
294+
// Replace the node with an optimized one:
295+
const auto nsPrefix = optimizedNodeDef->hasNamespace()
296+
? optimizedNodeDef->getNamespace() + ":"
297+
: std::string {};
298+
299+
node->setCategory(nsPrefix + optimizedNodeDef->getNodeString());
300+
if (node->hasNodeDefString()) {
301+
node->setNodeDefString(optimizedNodeDef->getName());
302+
}
303+
}
304+
}
305+
}
306+
}
307+
219308
void LobePrunerImpl::addOptimizableValue(
220309
float value,
221310
const mx::InputPtr& input,
@@ -242,7 +331,7 @@ void LobePrunerImpl::addOptimizableValue(
242331
valueMap.find(value)->second.push_back(PXR_NS::TfToken(input->getParent()->getName()));
243332
}
244333

245-
bool LobePrunerImpl::getOptimizedNodeCategory(const mx::Node& node, std::string& nodeCategory)
334+
bool LobePrunerImpl::getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& nodeDef)
246335
{
247336
const auto& nd = node.getNodeDef();
248337
if (!nd) {
@@ -288,8 +377,7 @@ bool LobePrunerImpl::getOptimizedNodeCategory(const mx::Node& node, std::string&
288377
}
289378

290379
if (canOptimize) {
291-
ensureLibraryHasOptimizedShader(ndName, flags);
292-
nodeCategory = node.getCategory() + "_" + flags;
380+
nodeDef = ensureLibraryHasOptimizedShader(ndName, flags);
293381
return true;
294382
}
295383

@@ -356,39 +444,53 @@ PXR_NS::TfToken LobePrunerImpl::getOptimizedNodeId(const PXR_NS::HdMaterialNode2
356444
}
357445

358446
if (canOptimize) {
359-
ensureLibraryHasOptimizedShader(node.nodeTypeId, flags);
360-
return PXR_NS::TfToken(
361-
ND_PREFIX + nodeDef->GetFamily().GetString() + "_" + flags + "_surfaceshader");
447+
return PXR_NS::TfToken(ensureLibraryHasOptimizedShader(node.nodeTypeId, flags)->getName());
362448
}
363449

364450
return retVal;
365451
}
366452

367-
void LobePrunerImpl::ensureLibraryHasOptimizedShader(
453+
bool LobePrunerImpl::isOptimizedNodeId(const PXR_NS::TfToken& nodeId)
454+
{
455+
return _optimizedNodeIds.count(nodeId) != 0;
456+
}
457+
458+
mx::NodeDefPtr LobePrunerImpl::ensureLibraryHasOptimizedShader(
368459
const PXR_NS::TfToken& nodeDefName,
369460
const std::string& flags)
370461
{
371462
const auto ndIt = _prunerData.find(nodeDefName);
372463
if (ndIt == _prunerData.end()) {
373-
return;
464+
return {};
374465
}
375466

376-
const auto originalNodeDef = _library->getNodeDef(nodeDefName.GetString());
377-
const auto originalNodeGraph = _library->getNodeGraph(ndIt->second._nodeGraphName);
378-
const std::string optimizedNodeName = originalNodeDef->getNodeString() + "_" + flags;
379-
const std::string optimizedNodeDefName = ND_PREFIX + optimizedNodeName + "_surfaceshader";
380-
if (_library->getNodeDef(optimizedNodeDefName)) {
467+
const auto originalNodeDef = _library->getNodeDef(nodeDefName.GetString());
468+
const auto originalNodeGraph = _library->getNodeGraph(ndIt->second._nodeGraphName);
469+
const auto nsPrefix = originalNodeDef->hasNamespace()
470+
? originalNodeDef->getNamespace() + mx::NAME_PREFIX_SEPARATOR
471+
: std::string {};
472+
auto optimizedNodeName = originalNodeDef->getNodeString() + "_" + flags;
473+
if (!nsPrefix.empty() && optimizedNodeName.rfind(nsPrefix, 0) == 0) {
474+
optimizedNodeName = optimizedNodeName.substr(nsPrefix.size());
475+
}
476+
const auto optimizedNodeNameWithNS = nsPrefix + optimizedNodeName;
477+
const std::string optimizedNodeDefName
478+
= nsPrefix + ND_PREFIX + optimizedNodeName + "_surfaceshader";
479+
if (const auto existingNd = _library->getNodeDef(optimizedNodeDefName)) {
381480
// Already there
382-
return;
481+
return existingNd;
383482
}
384483

484+
_optimizedNodeIds.insert(PXR_NS::TfToken(optimizedNodeDefName));
485+
385486
auto optimizedNodeDef
386487
= _library->addNodeDef(optimizedNodeDefName, "surfaceshader", optimizedNodeName);
387488
optimizedNodeDef->copyContentFrom(originalNodeDef);
388489
optimizedNodeDef->setSourceUri("");
389490
optimizedNodeDef->setNodeString(optimizedNodeName);
390491

391-
auto optimizedNodeGraph = _library->addNodeGraph("NG_" + optimizedNodeName + "_surfaceshader");
492+
auto optimizedNodeGraph
493+
= _library->addNodeGraph(nsPrefix + "LPOPTING_" + optimizedNodeName + "_surfaceshader");
392494
optimizedNodeGraph->copyContentFrom(originalNodeGraph);
393495
optimizedNodeGraph->setSourceUri("");
394496
optimizedNodeGraph->setNodeDefString(optimizedNodeDefName);
@@ -414,6 +516,8 @@ void LobePrunerImpl::ensureLibraryHasOptimizedShader(
414516
default: continue;
415517
}
416518
}
519+
520+
return optimizedNodeDef;
417521
}
418522

419523
void LobePrunerImpl::optimizeZeroValue(
@@ -560,9 +664,16 @@ LobePruner::Ptr LobePruner::create() { return std::make_shared<LobePruner>(); }
560664
LobePruner::~LobePruner() = default;
561665
LobePruner::LobePruner() = default;
562666

563-
bool LobePruner::getOptimizedNodeCategory(const mx::Node& node, std::string& nodeCategory)
667+
void LobePruner::optimizeLibrary(const MaterialX::DocumentPtr& library)
668+
{
669+
if (_impl) {
670+
_impl->optimizeLibrary(library);
671+
}
672+
}
673+
674+
bool LobePruner::getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& nodeDef)
564675
{
565-
return _impl ? _impl->getOptimizedNodeCategory(node, nodeCategory) : false;
676+
return _impl ? _impl->getOptimizedNodeDef(node, nodeDef) : false;
566677
}
567678

568679
mx::StringVec LobePruner::getOptimizedAttributeNames(const mx::NodeDefPtr& nodeDef) const
@@ -582,7 +693,7 @@ void LobePruner::setLibrary(const mx::DocumentPtr& library)
582693

583694
bool LobePruner::isOptimizedNodeId(const PXR_NS::TfToken& nodeId)
584695
{
585-
return nodeId.GetString().rfind(LobePrunerImpl::ND_PREFIX, 0) == 0;
696+
return _impl ? _impl->isOptimizedNodeId(nodeId) : false;
586697
}
587698

588699
const std::string& LobePruner::getOptimizedNodeDefPrefix() { return LobePrunerImpl::ND_PREFIX; }

lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.h

+10-10
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,19 @@ class MAYAUSD_CORE_PUBLIC LobePruner
5454
*/
5555
void setLibrary(const mx::DocumentPtr& library);
5656

57+
/*! Traverses and optimizes in place all NodeGraphs found in the library. Useful if a library
58+
* contains NodeGraphs that embed optimizable nodes.
59+
* @param[in] library is the library used to generate shaders.
60+
*/
61+
void optimizeLibrary(const MaterialX::DocumentPtr& library);
62+
5763
/*! Checks if a node is optimizable and if this is the case, create the optimized NodeDef and
58-
* NodeGraph in the library and return the optimized node category. An optimized node category
59-
* will consist of the name of the original category followed by a series of characters
60-
* describing which attibutes were optimized:
61-
* - 'x' that attribute was not optimized (intermediate value or connected)
62-
* - '0' a zero value was optimized
63-
* - '1' a one value was optimized
64-
* The ordered list of attributes names can be found by calling getOptimizedAttributeNames().
64+
* NodeGraph in the library and return the optimized NodeDef.
6565
* @param[in] node is a node we want to optimize. All nodes are welcome.
66-
* @param[out] nodeCategory is the node category of the optimized node.
66+
* @param[out] nodeDef is the NodeDef of the optimized node.
6767
* \return true if an optimization was found
6868
*/
69-
bool getOptimizedNodeCategory(const mx::Node& node, std::string& nodeCategory);
69+
bool getOptimizedNodeDef(const mx::Node& node, mx::NodeDefPtr& nodeDef);
7070

7171
/*! Get the list of attribute names that are optimization targets for a specific NodeDef.
7272
* @param[in] nodeDef is preferably node definition that has previously been optimized, but all
@@ -95,7 +95,7 @@ class MAYAUSD_CORE_PUBLIC LobePruner
9595
* @param[in] nodeId is a node:id we want to check.
9696
* \return true if that node was generated by a LobePruner.
9797
*/
98-
static bool isOptimizedNodeId(const PXR_NS::TfToken& nodeId);
98+
bool isOptimizedNodeId(const PXR_NS::TfToken& nodeId);
9999

100100
/*! Returns the NodeDef prefix common to all LobePruner optimized definitions.
101101
* \return the LobePruner NodeDef prefix

lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp

+7-5
Original file line numberDiff line numberDiff line change
@@ -233,11 +233,13 @@ mx::NodePtr TopoNeutralGraph::cloneNode(const mx::Node& node, mx::GraphElement&
233233
if (!nodeDef) {
234234
throw mx::Exception("Ambiguous node is not fully resolvable");
235235
}
236-
std::string optimizedNodeCategory;
237-
if (_lobePruner && _lobePruner->getOptimizedNodeCategory(node, optimizedNodeCategory)) {
238-
destNode->setCategory(optimizedNodeCategory);
239-
destNode->setNodeDefString(
240-
LobePruner::getOptimizedNodeDefPrefix() + optimizedNodeCategory + "_surfaceshader");
236+
mx::NodeDefPtr optimizedNodeDef;
237+
if (_lobePruner && _lobePruner->getOptimizedNodeDef(node, optimizedNodeDef)) {
238+
const auto nsPrefix = optimizedNodeDef->hasNamespace()
239+
? optimizedNodeDef->getNamespace() + ":"
240+
: std::string {};
241+
destNode->setCategory(nsPrefix + optimizedNodeDef->getNodeString());
242+
destNode->setNodeDefString(optimizedNodeDef->getName());
241243
for (const auto& attrName : _lobePruner->getOptimizedAttributeNames(nodeDef)) {
242244
_optimizedAttributes.push_back(node.getNamePath() + "." + attrName);
243245
}

lib/mayaUsd/render/vp2RenderDelegate/material.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ struct _MaterialXData
359359
#if MX_COMBINED_VERSION >= 13808
360360
_lobePruner = MaterialXMaya::ShaderGenUtil::LobePruner::create();
361361
_lobePruner->setLibrary(_mtlxLibrary);
362+
_lobePruner->optimizeLibrary(_mtlxLibrary);
362363

363364
// TODO: Optimize published shaders.
364365
// SCENARIO: User publishes a shader with a NodeGraph implementation that encapsulates a
@@ -3038,8 +3039,7 @@ MHWRender::MShaderInstance* HdVP2Material::CompiledNetwork::_CreateMaterialXShad
30383039
const mx::FileSearchPath& crLibrarySearchPath(_GetMaterialXData()._mtlxSearchPath);
30393040
#if MX_COMBINED_VERSION >= 13808
30403041
if (mtlxSdrNode
3041-
|| MaterialXMaya::ShaderGenUtil::LobePruner::isOptimizedNodeId(
3042-
surfTerminal->nodeTypeId)) {
3042+
|| _GetMaterialXData()._lobePruner->isOptimizedNodeId(surfTerminal->nodeTypeId)) {
30433043
#else
30443044
if (mtlxSdrNode) {
30453045
#endif

lib/mayaUsdAPI/render.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,13 @@ void LobePruner::setLibrary(const MaterialX::DocumentPtr& library)
157157
}
158158
}
159159

160+
void LobePruner::optimizeLibrary(const MaterialX::DocumentPtr& library)
161+
{
162+
if (_imp && _imp->_lobePruner) {
163+
_imp->_lobePruner->optimizeLibrary(library);
164+
}
165+
}
166+
160167
struct TopoNeutralGraphImpl
161168
{
162169
TopoNeutralGraphImpl(const MaterialX::ElementPtr& material)

lib/mayaUsdAPI/render.h

+6
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ class MAYAUSD_API_PUBLIC LobePruner
135135
*/
136136
void setLibrary(const MaterialX::DocumentPtr& library);
137137

138+
/*! Traverses and optimizes in place all NodeGraphs found in the library. Useful if a library
139+
* contains NodeGraphs that embed optimizable nodes.
140+
* @param[in] library is the library used to generate shaders.
141+
*/
142+
void optimizeLibrary(const MaterialX::DocumentPtr& library);
143+
138144
private:
139145
LobePruner();
140146

test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp

+6-6
Original file line numberDiff line numberDiff line change
@@ -179,17 +179,17 @@ TEST(ShaderGenUtils, lobePruner)
179179

180180
const auto node = doc->addNode("standard_surface", "bob", "surfaceshader");
181181

182-
std::string optimizedCategory;
183-
ASSERT_TRUE(lobePruner->getOptimizedNodeCategory(*node, optimizedCategory));
182+
mx::NodeDefPtr optimizedNodeDef;
183+
ASSERT_TRUE(lobePruner->getOptimizedNodeDef(*node, optimizedNodeDef));
184184
// An x means can not optimize on that attribute
185185
// A 0 means we optimized due to this value being zero
186-
ASSERT_EQ(optimizedCategory, "standard_surface_x0000x00x000");
186+
ASSERT_EQ(optimizedNodeDef->getNodeString(), "standard_surface_x0000x00x000");
187187

188188
auto input = node->addInputFromNodeDef("subsurface");
189189
input->setValueString("1.0");
190-
ASSERT_TRUE(lobePruner->getOptimizedNodeCategory(*node, optimizedCategory));
190+
ASSERT_TRUE(lobePruner->getOptimizedNodeDef(*node, optimizedNodeDef));
191191
// Now have a 1 for subsurface since we can also optimize the 1 value for mix nodes.
192-
ASSERT_EQ(optimizedCategory, "standard_surface_x0000x00x010");
192+
ASSERT_EQ(optimizedNodeDef->getNodeString(), "standard_surface_x0000x00x010");
193193

194194
PXR_NS::HdMaterialNode2 usdNode;
195195
usdNode.nodeTypeId = PXR_NS::TfToken("ND_standard_surface_surfaceshader");
@@ -198,7 +198,7 @@ TEST(ShaderGenUtils, lobePruner)
198198
optimizedNodeId.GetString(),
199199
sgu::LobePruner::getOptimizedNodeDefPrefix()
200200
+ "standard_surface_x0000x00x000_surfaceshader");
201-
ASSERT_TRUE(sgu::LobePruner::isOptimizedNodeId(optimizedNodeId));
201+
ASSERT_TRUE(lobePruner->isOptimizedNodeId(optimizedNodeId));
202202

203203
usdNode.nodeTypeId = PXR_NS::TfToken("ND_mix_surfaceshader");
204204
optimizedNodeId = lobePruner->getOptimizedNodeId(usdNode);

0 commit comments

Comments
 (0)