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-101 Override primWriter #3189

Merged
merged 12 commits into from
Jul 28, 2023
Merged
19 changes: 19 additions & 0 deletions lib/mayaUsd/fileio/primWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,25 @@ class UsdMayaPrimWriter
const SdfPath& usdPath,
UsdMayaWriteJobContext& jobCtx);

/// The level of support a writer can offer for a given context
///
/// A basic writer that gives correct results across most contexts should
/// report `Fallback`, while a specialized writer that really shines in a
/// given context should report `Supported` when the context is right and
/// `Unsupported` if the context is not as expected.
enum class ContextSupport
{
Supported,
Fallback,
Unsupported
};

/// A static function is expected for all writers and allows
/// declaring how well this class can support the current context.
MAYAUSD_CORE_PUBLIC
static ContextSupport
CanExport(const UsdMayaJobExportArgs& exportArgs, const MFnDependencyNode& exportNode);

MAYAUSD_CORE_PUBLIC
virtual ~UsdMayaPrimWriter();

Expand Down
158 changes: 133 additions & 25 deletions lib/mayaUsd/fileio/primWriterRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,75 @@ TF_DEFINE_PRIVATE_TOKENS(
);
// clang-format on

typedef std::map<std::string, UsdMayaPrimWriterRegistry::WriterFactoryFn> _Registry;
static _Registry _reg;
static std::set<std::string> _mayaTypesThatDoNotCreatePrims;
namespace {
struct _RegistryEntry
{
UsdMayaPrimWriterRegistry::ContextPredicateFn _pred;
UsdMayaPrimWriterRegistry::WriterFactoryFn _writer;
int _index;
};

// typedef std::map<std::string, UsdMayaPrimWriterRegistry::WriterFactoryFn> _Registry;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this typedef in comment was left-over and probably should be removed.

typedef std::unordered_multimap<std::string, _RegistryEntry> _Registry;
static _Registry _reg;
static std::set<std::string> _mayaTypesThatDoNotCreatePrims;
static int _indexCounter = 0;

_Registry::const_iterator _Find(
const std::string& mayaTypeName,
const UsdMayaJobExportArgs& exportArgs,
const MFnDependencyNode& exportNode)
{
using ContextSupport = UsdMayaPrimWriter::ContextSupport;

_Registry::const_iterator ret = _reg.cend();
_Registry::const_iterator first, last;
std::tie(first, last) = _reg.equal_range(mayaTypeName);
while (first != last) {
ContextSupport support = first->second._pred(exportArgs, exportNode);
// Look for a "Supported" reader. If no "Supported" reader is found, use a "Fallback" reader
if (support == ContextSupport::Supported) {
ret = first;
break;
} else if (support == ContextSupport::Fallback && ret == _reg.end()) {
ret = first;
}
++first;
}

return ret;
}

} // namespace

/* static */
void UsdMayaPrimWriterRegistry::Register(
const std::string& mayaTypeName,
UsdMayaPrimWriterRegistry::ContextPredicateFn pred,
UsdMayaPrimWriterRegistry::WriterFactoryFn fn,
bool fromPython)
{
TF_DEBUG(PXRUSDMAYA_REGISTRY)
.Msg("Registering UsdMayaPrimWriter for maya type %s.\n", mayaTypeName.c_str());

int index = _indexCounter++;
_reg.insert(std::make_pair(mayaTypeName, _RegistryEntry { pred, fn, index }));

// The unloader uses the index to know which entry to erase when there are
// more than one for the same mayaTypeName.
UsdMaya_RegistryHelper::AddUnloader(
[mayaTypeName, index]() {
_Registry::const_iterator it, itEnd;
std::tie(it, itEnd) = _reg.equal_range(mayaTypeName);
for (; it != itEnd; ++it) {
if (it->second._index == index) {
_reg.erase(it);
break;
}
}
},
fromPython);
}

/* static */
void UsdMayaPrimWriterRegistry::Register(
Expand All @@ -55,14 +121,31 @@ void UsdMayaPrimWriterRegistry::Register(
TF_DEBUG(PXRUSDMAYA_REGISTRY)
.Msg("Registering UsdMayaPrimWriter for maya type %s.\n", mayaTypeName.c_str());

std::pair<_Registry::iterator, bool> insertStatus
= _reg.insert(std::make_pair(mayaTypeName, fn));
if (insertStatus.second) {
UsdMaya_RegistryHelper::AddUnloader(
[mayaTypeName]() { _reg.erase(mayaTypeName); }, fromPython);
} else {
TF_CODING_ERROR("Multiple writers for type %s", mayaTypeName.c_str());
}
int index = _indexCounter++;

// Use default ContextSupport if not specified
_reg.insert(std::make_pair(
mayaTypeName,
_RegistryEntry { [](const UsdMayaJobExportArgs&, const MFnDependencyNode&) {
return UsdMayaPrimWriter::ContextSupport::Fallback;
},
fn,
index }));

// The unloader uses the index to know which entry to erase when there are
// more than one for the same mayaTypeName.
UsdMaya_RegistryHelper::AddUnloader(
[mayaTypeName, index]() {
_Registry::const_iterator it, itEnd;
std::tie(it, itEnd) = _reg.equal_range(mayaTypeName);
for (; it != itEnd; ++it) {
if (it->second._index == index) {
_reg.erase(it);
break;
}
}
},
fromPython);
}

/* static */
Expand All @@ -74,32 +157,57 @@ void UsdMayaPrimWriterRegistry::RegisterRaw(
}

/* static */
UsdMayaPrimWriterRegistry::WriterFactoryFn
UsdMayaPrimWriterRegistry::Find(const std::string& mayaTypeName)
UsdMayaPrimWriterRegistry::WriterFactoryFn UsdMayaPrimWriterRegistry::Find(
const std::string& mayaTypeName,
const UsdMayaJobExportArgs& exportArgs,
const MFnDependencyNode& exportNode)
{
TfRegistryManager::GetInstance().SubscribeTo<UsdMayaPrimWriterRegistry>();

// unfortunately, usdTypeName is diff from the tfTypeName which we use to
// register. do the conversion here.
WriterFactoryFn ret = nullptr;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDK what that erased comment about usdTypeName vs tfTypeName was about... I guess it is now irrelevant?

if (TfMapLookup(_reg, mayaTypeName, &ret)) {
return ret;
_Registry::const_iterator it = _Find(mayaTypeName, exportArgs, exportNode);

if (it != _reg.end()) {
return it->second._writer;
}

static const TfTokenVector SCOPE = { _tokens->UsdMaya, _tokens->PrimWriter };
UsdMaya_RegistryHelper::FindAndLoadMayaPlug(SCOPE, mayaTypeName);

it = _Find(mayaTypeName, exportArgs, exportNode);

if (it != _reg.end()) {
return it->second._writer;
}

// ideally something just registered itself. if not, we at least put it in
// the registry in case we encounter it again.
pierrebai-adsk marked this conversation as resolved.
Show resolved Hide resolved
if (!TfMapLookup(_reg, mayaTypeName, &ret)) {
TF_DEBUG(PXRUSDMAYA_REGISTRY)
.Msg(
"No usdMaya writer plugin for maya type %s. No maya plugin found.\n",
mayaTypeName.c_str());
_reg[mayaTypeName] = nullptr;
if (_reg.count(mayaTypeName) == 0) {
// Nothing registered at all, remember that:
_reg.insert(std::make_pair(
mayaTypeName,
_RegistryEntry { [](const UsdMayaJobExportArgs&, const MFnDependencyNode&) {
return UsdMayaPrimWriter::ContextSupport::Fallback;
},
nullptr,
-1 }));
}

return ret;
return nullptr;
}

/* static */
void UsdMayaPrimWriterRegistry::Poke(const std::string& mayaTypeName)
{
TfRegistryManager::GetInstance().SubscribeTo<UsdMayaPrimWriterRegistry>();

_Registry::const_iterator first, last;
std::tie(first, last) = _reg.equal_range(mayaTypeName);

if (first == last) {
// If the type name is not currently in our registry, check for plugin registry
static const TfTokenVector SCOPE = { _tokens->UsdMaya, _tokens->PrimWriter };
UsdMaya_RegistryHelper::FindAndLoadMayaPlug(SCOPE, mayaTypeName);
}
}

/* static */
Expand Down
42 changes: 41 additions & 1 deletion lib/mayaUsd/fileio/primWriterRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,39 @@ struct UsdMayaPrimWriterRegistry
/// macro.
typedef std::function<bool(const UsdMayaPrimWriterArgs&, UsdMayaPrimWriterContext*)> WriterFn;

/// Predicate function, i.e. a function that can tell the level of support
/// the writer function will provide for a given set of export options.
using ContextPredicateFn = std::function<
UsdMayaPrimWriter::ContextSupport(const UsdMayaJobExportArgs&, const MFnDependencyNode&)>;

/// \brief Register \p fn as a factory function providing a
/// UsdMayaPrimWriter subclass that can be used to write \p mayaType.
/// Provide a supportability of the primWriter. Use "supported" to
/// override the default primWriter
///
/// If you can't provide a valid UsdMayaPrimWriter for the given arguments,
/// return a null pointer from the factory function \p fn.
///
/// Example for registering a writer factory in your custom plugin:
/// \code{.cpp}
/// class MyWriter : public UsdMayaPrimWriter {
/// static UsdMayaPrimWriterSharedPtr Create(
/// const MFnDependencyNode& depNodeFn,
/// const SdfPath& usdPath,
/// UsdMayaWriteJobContext& jobCtx);
/// };
/// TF_REGISTRY_FUNCTION_WITH_TAG(UsdMayaPrimWriterRegistry, MyWriter) {
/// UsdMayaPrimWriterRegistry::Register("myCustomMayaNode",
/// MyWriter::Create);
/// }
/// \endcode
MAYAUSD_CORE_PUBLIC
static void Register(
const std::string& mayaType,
ContextPredicateFn pred,
WriterFactoryFn fn,
bool fromPython = false);

/// \brief Register \p fn as a factory function providing a
/// UsdMayaPrimWriter subclass that can be used to write \p mayaType.
/// If you can't provide a valid UsdMayaPrimWriter for the given arguments,
Expand Down Expand Up @@ -109,7 +142,14 @@ struct UsdMayaPrimWriterRegistry
///
/// If there is no writer plugin for \p mayaTypeName, returns nullptr.
MAYAUSD_CORE_PUBLIC
static WriterFactoryFn Find(const std::string& mayaTypeName);
static WriterFactoryFn Find(
const std::string& mayaTypeName,
const UsdMayaJobExportArgs& exportArgs,
const MFnDependencyNode& exportNode);

/// \brief Check for external primWriter for \p mayaTypeName.
MAYAUSD_CORE_PUBLIC
static void Poke(const std::string& mayaTypeName);

pierrebai-adsk marked this conversation as resolved.
Show resolved Hide resolved
/// \brief Registers a maya node type to *not* create a new prim.
///
Expand Down
13 changes: 0 additions & 13 deletions lib/mayaUsd/fileio/shaderWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,6 @@ class UsdMayaShaderWriter : public UsdMayaPrimWriter
const SdfPath& usdPath,
UsdMayaWriteJobContext& jobCtx);

/// The level of support a writer can offer for a given context
///
/// A basic writer that gives correct results across most contexts should
/// report `Fallback`, while a specialized writer that really shines in a
/// given context should report `Supported` when the context is right and
/// `Unsupported` if the context is not as expected.
enum class ContextSupport
{
Supported,
Fallback,
Unsupported
};

/// A static function is expected for all shader writers and allows
/// declaring how well this class can support the current context.
///
Expand Down
2 changes: 1 addition & 1 deletion lib/mayaUsd/fileio/utils/adaptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ TfType UsdMayaAdaptor::GetUsdType() const
// The adaptor type mapping might be registered externally in a prim writer
// plugin. This simply pokes the prim writer registry to load the prim
// writer plugin in order to pull in the adaptor mapping.
UsdMayaPrimWriterRegistry::Find(depNode.typeName().asChar());
UsdMayaPrimWriterRegistry::Poke(depNode.typeName().asChar());
TfRegistryManager::GetInstance().SubscribeTo<UsdMayaAdaptor>();

const auto iter = _schemaLookup.find(depNode.typeName().asChar());
Expand Down
9 changes: 5 additions & 4 deletions lib/mayaUsd/fileio/writeJobContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,8 +537,7 @@ UsdMayaPrimWriterSharedPtr UsdMayaWriteJobContext::CreatePrimWriter(
// This is either a DG node or a non-instanced DAG node, so try to look up
// a writer plugin. We search through the node's type ancestors, working
// backwards until we find a prim writer plugin.
const std::string mayaTypeName(depNodeFn.typeName().asChar());
if (UsdMayaPrimWriterRegistry::WriterFactoryFn primWriterFactory = _FindWriter(mayaTypeName)) {
if (UsdMayaPrimWriterRegistry::WriterFactoryFn primWriterFactory = _FindWriter(depNodeFn)) {
if (UsdMayaPrimWriterSharedPtr primPtr = primWriterFactory(depNodeFn, writePath, *this)) {
// We found a registered user prim writer that handles this node
// type, so return now.
Expand All @@ -551,8 +550,10 @@ UsdMayaPrimWriterSharedPtr UsdMayaWriteJobContext::CreatePrimWriter(
}

UsdMayaPrimWriterRegistry::WriterFactoryFn
UsdMayaWriteJobContext::_FindWriter(const std::string& mayaNodeType)
UsdMayaWriteJobContext::_FindWriter(const MFnDependencyNode& mayaNode)
{
const std::string mayaNodeType(mayaNode.typeName().asChar());

// Check if type is already cached locally.
auto iter = mWriterFactoryCache.find(mayaNodeType);
if (iter != mWriterFactoryCache.end()) {
Expand All @@ -564,7 +565,7 @@ UsdMayaWriteJobContext::_FindWriter(const std::string& mayaNodeType)
= UsdMayaUtil::GetAllAncestorMayaNodeTypes(mayaNodeType);
for (auto i = ancestorTypes.rbegin(); i != ancestorTypes.rend(); ++i) {
if (UsdMayaPrimWriterRegistry::WriterFactoryFn primWriterFactory
= UsdMayaPrimWriterRegistry::Find(*i)) {
= UsdMayaPrimWriterRegistry::Find(*i, mArgs, mayaNode)) {
mWriterFactoryCache[mayaNodeType] = primWriterFactory;
return primWriterFactory;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/mayaUsd/fileio/writeJobContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ class UsdMayaWriteJobContext
std::vector<UsdMayaPrimWriterSharedPtr>::const_iterator* end) const;

/// Prim writer search with ancestor type resolution behavior.
UsdMayaPrimWriterRegistry::WriterFactoryFn _FindWriter(const std::string& mayaNodeType);
UsdMayaPrimWriterRegistry::WriterFactoryFn _FindWriter(const MFnDependencyNode& mayaNode);

struct MObjectHandleComp
{
Expand Down
Loading