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

EMSUSD-1975 - Optimize material libraries #4055

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 131 additions & 20 deletions lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -132,6 +136,7 @@ class LobePrunerImpl

std::unordered_map<PXR_NS::TfToken, NodeDefData, PXR_NS::TfToken::HashFunctor> _prunerData;
mx::DocumentPtr _library;
PXR_NS::TfToken::HashSet _optimizedNodeIds;
};

const std::string LobePrunerImpl::ND_PREFIX = "LPOPTIND_";
Expand Down Expand Up @@ -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<std::string> 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<float>();
} else {
const auto defInput = nd->getActiveInput(attrIt->first);
inputValue = defInput->getValue()->asA<float>();
}

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,
Expand All @@ -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)
frohnej-adsk marked this conversation as resolved.
Show resolved Hide resolved
{
const auto& nd = node.getNodeDef();
if (!nd) {
Expand Down Expand Up @@ -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);
frohnej-adsk marked this conversation as resolved.
Show resolved Hide resolved
return true;
}

Expand Down Expand Up @@ -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(
frohnej-adsk marked this conversation as resolved.
Show resolved Hide resolved
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);
Expand All @@ -414,6 +516,8 @@ void LobePrunerImpl::ensureLibraryHasOptimizedShader(
default: continue;
}
}

return optimizedNodeDef;
}

void LobePrunerImpl::optimizeZeroValue(
Expand Down Expand Up @@ -560,9 +664,16 @@ LobePruner::Ptr LobePruner::create() { return std::make_shared<LobePruner>(); }
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
Expand All @@ -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; }
Expand Down
20 changes: 10 additions & 10 deletions lib/mayaUsd/render/MaterialXGenOgsXml/LobePruner.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
frohnej-adsk marked this conversation as resolved.
Show resolved Hide resolved

/*! 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
Expand Down Expand Up @@ -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
Expand Down
12 changes: 7 additions & 5 deletions lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/mayaUsd/render/vp2RenderDelegate/material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions lib/mayaUsdAPI/render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions lib/mayaUsdAPI/render.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
12 changes: 6 additions & 6 deletions test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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);
Expand Down