#include "hdPrman/matfiltResolveVstructs.h"
#include "hdPrman/debugCodes.h"
#include <memory>
#include <mutex>
#include <set>
#include <unordered_map>
#include "pxr/base/tf/staticTokens.h"
#include "pxr/usd/sdr/registry.h"
#include "pxr/usd/sdr/shaderNode.h"
#include "pxr/usd/sdr/shaderProperty.h"
PXR_NAMESPACE_OPEN_SCOPE
TF_DEFINE_PRIVATE_TOKENS(
_tokens,
(vstructmemberaliases) \
(enableVstructConditions)
);
namespace {
struct _VstructInfoEntry {
typedef std::shared_ptr<_VstructInfoEntry> Ptr;
typedef std::unordered_map<TfToken, std::vector<TfToken>,
TfToken::HashFunctor> MemberMap;
typedef std::unordered_map<TfToken, TfToken,
TfToken::HashFunctor> ReverseMemberMap;
typedef std::unordered_map<TfToken, MatfiltVstructConditionalEvaluator::Ptr,
TfToken::HashFunctor> ConditionalMap;
MemberMap members;
ReverseMemberMap reverseMembers;
ConditionalMap conditionals;
};
struct _ShaderInfoEntry {
typedef std::shared_ptr<_ShaderInfoEntry> Ptr;
typedef std::map<TfToken, _VstructInfoEntry::Ptr> VstructInfoMap;
VstructInfoMap vstructs;
static Ptr
Build(const TfToken & nodeTypeId,
const NdrTokenVec & shaderTypePriority)
{
auto result = Ptr(new _ShaderInfoEntry);
if (auto sdrShader =
SdrRegistry::GetInstance().GetShaderNodeByIdentifier(
nodeTypeId, shaderTypePriority))
{
for (const auto & inputName : sdrShader->GetInputNames()) {
auto sdrInput = sdrShader->GetShaderInput(inputName);
if (!sdrInput) {
continue;
}
_ProcessProperty(result, sdrInput);
}
for (const auto & outputName : sdrShader->GetOutputNames()) {
auto sdrOutput = sdrShader->GetShaderOutput(outputName);
if (!sdrOutput) {
continue;
}
_ProcessProperty(result, sdrOutput);
}
}
return result;
}
static Ptr
Get(const TfToken & nodeTypeId,
const NdrTokenVec & shaderTypePriority)
{
std::lock_guard<std::mutex> lock(_cachedEntryMutex);
auto I = _cachedEntries.find(nodeTypeId);
if (I == _cachedEntries.end()) {
auto result = Build(nodeTypeId, shaderTypePriority);
_cachedEntries[nodeTypeId] = result;
return result;
}
return (*I).second;
}
private:
static std::unordered_map<TfToken, Ptr, TfToken::HashFunctor>
_cachedEntries;
static std::mutex _cachedEntryMutex;
static void
_ProcessProperty(Ptr result, SdrShaderPropertyConstPtr prop)
{
if (!prop->IsVStructMember()) {
return;
}
const TfToken & vsName = prop->GetVStructMemberOf();
const TfToken & vsMemberName = prop->GetVStructMemberName();
if (vsName.IsEmpty() || vsMemberName.IsEmpty()) {
return;
}
_VstructInfoEntry::Ptr entry;
auto I = result->vstructs.find(vsName);
if (I != result->vstructs.end()) {
entry = (*I).second;
} else {
entry.reset(new _VstructInfoEntry);
result->vstructs[vsName] = entry;
}
TfToken vsMemberAlias;
const auto & metadata = prop->GetMetadata();
{
auto I = metadata.find(_tokens->vstructmemberaliases);
if (I != metadata.end()) {
vsMemberAlias = TfToken((*I).second);
}
}
auto & memberNames = entry->members[prop->GetName()];
memberNames.push_back(vsMemberName);
entry->reverseMembers[vsMemberName] = prop->GetName();
if (!vsMemberAlias.IsEmpty()) {
memberNames.push_back(vsMemberAlias);
entry->reverseMembers[vsMemberAlias] = prop->GetName();
}
const TfToken & condExpr = prop->GetVStructConditionalExpr();
if (!condExpr.IsEmpty()) {
entry->conditionals[prop->GetName()] =
MatfiltVstructConditionalEvaluator::Parse(condExpr.data());
}
}
};
std::unordered_map<TfToken, _ShaderInfoEntry::Ptr, TfToken::HashFunctor>
_ShaderInfoEntry::_cachedEntries;
std::mutex _ShaderInfoEntry::_cachedEntryMutex;
}
static void
_ResolveVstructsForNode(
MatfiltNetwork & network,
const SdfPath & nodeId,
MatfiltNode & node,
std::set<SdfPath> resolvedNodeNames,
const NdrTokenVec & shaderTypePriority,
bool enableConditions)
{
if (resolvedNodeNames.find(nodeId) != resolvedNodeNames.end()) {
TF_DEBUG(HDPRMAN_VSTRUCTS)
.Msg("No resovled node name for %s\n", nodeId.GetText());
return;
}
resolvedNodeNames.insert(nodeId);
auto shaderInfo =
_ShaderInfoEntry::Get(node.nodeTypeId, shaderTypePriority);
if (shaderInfo->vstructs.empty()) {
TF_DEBUG(HDPRMAN_VSTRUCTS)
.Msg("Node %s has no vstructs\n", node.nodeTypeId.GetText());
return;
}
auto inputConnectionsCopy = node.inputConnections;
for (auto & connectionI : inputConnectionsCopy) {
const auto & inputName = connectionI.first;
auto I = shaderInfo->vstructs.find(inputName);
if (I == shaderInfo->vstructs.end()) {
continue;
}
TF_DEBUG(HDPRMAN_VSTRUCTS)
.Msg("Found input %s with a vstruct\n", inputName.GetText());
const auto & vstructInfo = (*I).second;
const auto & upstreamConnections = connectionI.second;
if (upstreamConnections.empty()) {
TF_DEBUG(HDPRMAN_VSTRUCTS)
.Msg("Ignoring since no connection\n");
continue;
}
TF_DEBUG(HDPRMAN_VSTRUCTS)
.Msg("Found upstream vstruct connection\n");
const auto & upstreamConnection = upstreamConnections.front();
auto upstreamNodeI =
network.nodes.find(upstreamConnection.upstreamNode);
if (upstreamNodeI == network.nodes.end()) {
continue;
}
auto & upstreamNode = (*upstreamNodeI).second;
auto upstreamShaderInfo = _ShaderInfoEntry::Get(
upstreamNode.nodeTypeId, shaderTypePriority);
auto upstreamVstructI = upstreamShaderInfo->vstructs.find(
upstreamConnection.upstreamOutputName);
if (upstreamVstructI == upstreamShaderInfo->vstructs.end()) {
continue;
}
auto upstreamVstruct = (*upstreamVstructI).second;
_ResolveVstructsForNode(network, upstreamConnection.upstreamNode,
upstreamNode, resolvedNodeNames,
shaderTypePriority, enableConditions);
node.inputConnections.erase(inputName);
for (auto & memberI : vstructInfo->members) {
const TfToken & memberInputName = memberI.first;
if (node.inputConnections.find(memberInputName)
!= node.inputConnections.end()) {
continue;
}
for (const TfToken & memberName : memberI.second) {
auto revMemberI =
upstreamVstruct->reverseMembers.find(memberName);
if (revMemberI == upstreamVstruct->reverseMembers.end()) {
continue;
}
const TfToken & upstreamMemberOutputName = (*revMemberI).second;
auto condI = upstreamVstruct->conditionals.find(
upstreamMemberOutputName);
if (enableConditions &&
condI != upstreamVstruct->conditionals.end())
{
auto & evaluator = (*condI).second;
evaluator->Evaluate(
nodeId,
memberInputName,
upstreamConnection.upstreamNode,
upstreamMemberOutputName,
shaderTypePriority,
network);
} else {
node.inputConnections[memberInputName] = {{
upstreamConnection.upstreamNode,
upstreamMemberOutputName}};
TF_DEBUG(HDPRMAN_VSTRUCTS)
.Msg("Connected condition-less %s.%s to %s.%s\n",
nodeId.GetText(),
memberInputName.GetText(),
upstreamConnection.upstreamNode.GetText(),
upstreamMemberOutputName.GetText());
}
break;
}
}
}
}
void
MatfiltResolveVstructs(
const SdfPath & networkId,
MatfiltNetwork & network,
const std::map<TfToken, VtValue> & contextValues,
const NdrTokenVec & shaderTypePriority,
std::vector<std::string> * outputErrorMessages)
{
std::set<SdfPath> resolvedNodeNames;
bool enableConditions = true;
auto I = contextValues.find(_tokens->enableVstructConditions);
if (I != contextValues.end()) {
const VtValue & value = (*I).second;
if (value.IsHolding<bool>()) {
enableConditions = value.UncheckedGet<bool>();
}
}
for (auto & I : network.nodes) {
const SdfPath & nodeId = I.first;
MatfiltNode & node = I.second;
_ResolveVstructsForNode(
network,
nodeId,
node,
resolvedNodeNames,
shaderTypePriority,
enableConditions);
}
}
PXR_NAMESPACE_CLOSE_SCOPE