Skip to content

Commit

Permalink
Merge pull request #2410 from Autodesk/t_bailp/MAYA-123361/unshared-s…
Browse files Browse the repository at this point in the history
…tage-session

MAYA-123361 fix unshared stage toggling and loading
  • Loading branch information
seando-adsk authored Jun 17, 2022
2 parents b1bd44f + 4a38000 commit 4ab425b
Show file tree
Hide file tree
Showing 11 changed files with 262 additions and 55 deletions.
2 changes: 1 addition & 1 deletion lib/mayaUsd/fileio/jobs/readJob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ bool UsdMaya_ReadJob::Read(std::vector<MDagPath>* addedDagPaths)
mImportData.stageInitialLoadSet());
} else {
UsdStageCacheContext stageCacheContext(UsdMayaStageCache::Get(
mImportData.stageInitialLoadSet() == UsdStage::InitialLoadSet::LoadAll));
mImportData.stageInitialLoadSet(), UsdMayaStageCache::ShareMode::Shared));
if (mArgs.pullImportStage)
stage = mArgs.pullImportStage;
else
Expand Down
148 changes: 125 additions & 23 deletions lib/mayaUsd/nodes/proxyShapeBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,9 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock)
}

// Normal context computation
UsdStageRefPtr usdStage;
UsdStageRefPtr sharedUsdStage;
UsdStageRefPtr unsharedUsdStage;
UsdStageRefPtr finalUsdStage;
SdfPath primPath;

MDataHandle inDataHandle = dataBlock.inputValue(inStageDataAttr, &retValue);
Expand Down Expand Up @@ -711,7 +713,7 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock)
if (!inDataHandle.data().isNull()) {
MayaUsdStageData* inStageData
= dynamic_cast<MayaUsdStageData*>(inDataHandle.asPluginData());
usdStage = inStageData->stage;
sharedUsdStage = inStageData->stage;
primPath = inStageData->primPath;
isIncomingStage = true;
} else {
Expand All @@ -721,7 +723,7 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock)
const auto cacheId = UsdStageCache::Id::FromLongInt(cacheIdNum);
const auto stageCached = cacheId.IsValid() && UsdUtilsStageCache::Get().Contains(cacheId);
if (stageCached) {
usdStage = UsdUtilsStageCache::Get().Find(cacheId);
sharedUsdStage = UsdUtilsStageCache::Get().Find(cacheId);
isIncomingStage = true;
} else {
//
Expand Down Expand Up @@ -778,14 +780,19 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock)
// UsdStage. See https://github.com/Autodesk/maya-usd/issues/528 for
// more information.
UsdStageCacheContext ctx(
UsdMayaStageCache::Get(loadSet == UsdStage::InitialLoadSet::LoadAll));
UsdMayaStageCache::Get(loadSet, UsdMayaStageCache::ShareMode::Shared));

SdfLayerRefPtr rootLayer
= sharableStage ? computeRootLayer(dataBlock, fileString) : nullptr;
if (nullptr == rootLayer)
rootLayer = SdfLayer::FindOrOpen(fileString);

if (rootLayer) {
// Note: computeSessionLayer will find a session layer *only* if the
// Maya scene had been saved and thus serialized the session
// layer. Otherwise it returns null which will mean to use
// whatever session layer happens to be associated with the
// stage we potentially find in the stage cache.
SdfLayerRefPtr sessionLayer = computeSessionLayer(dataBlock);

MProfilingScope profilingScope(
Expand All @@ -798,34 +805,47 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock)
= MGlobal::optionVarIntValue(kSessionLayerOptionVarName) == 1;
targetSession = targetSession || !rootLayer->PermissionToEdit();

if (sessionLayer || targetSession) {
if (!sessionLayer)
sessionLayer = SdfLayer::CreateAnonymous();
usdStage = UsdStage::Open(
// Note: UsdStage::Open has the peculiar design that it will return
// any previously open stage that happen to match its arguments,
// all its arguments, but only those arguments.
//
// So *not* passing in a session layer will find any stage that
// has the given root layer. That is why it is important *not* to
// pass the session layer if the session layer is null. Otherwise
// the cache would try find a stage *without* a session layer.
//
// So, not passing the (null) session layer is how a newly-created
// shared stage with the same root layer will find the correct stage
// with the existing session layer.
//
// If the stage is not in the cache and no session layer is passed
// then UsdStage::Open will create the in-memory session layer for us,
// just as we want.
if (sessionLayer) {
sharedUsdStage = UsdStage::Open(
rootLayer,
sessionLayer,
ArGetResolver().CreateDefaultContextForAsset(fileString),
loadSet);
} else {
usdStage = UsdStage::Open(
sharedUsdStage = UsdStage::Open(
rootLayer,
ArGetResolver().CreateDefaultContextForAsset(fileString),
loadSet);
}
if (sessionLayer && targetSession) {
usdStage->SetEditTarget(sessionLayer);
} else {
usdStage->SetEditTarget(usdStage->GetRootLayer());
}

sharedUsdStage->SetEditTarget(
targetSession ? sharedUsdStage->GetSessionLayer()
: sharedUsdStage->GetRootLayer());
} else {
// Create a new stage in memory with an anonymous root layer.
usdStage = UsdStage::CreateInMemory(kAnonymousLayerName, loadSet);
sharedUsdStage = UsdStage::CreateInMemory(kAnonymousLayerName, loadSet);
}
}
}
}

if (!usdStage) {
if (!sharedUsdStage) {
return MS::kFailure;
}

Expand All @@ -838,7 +858,7 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock)
CHECK_MSTATUS_AND_RETURN_IT(retValue);

if (isIncomingStage) {
std::vector<std::string> incomingLayers { usdStage->GetRootLayer()->GetIdentifier() };
std::vector<std::string> incomingLayers { sharedUsdStage->GetRootLayer()->GetIdentifier() };
_incomingLayers = UsdMayaUtil::getAllSublayers(incomingLayers, true);
} else {
_incomingLayers.clear();
Expand All @@ -859,10 +879,11 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock)
}
}
}
finalUsdStage = sharedUsdStage;
}
// Own the stage
else {
SdfLayerRefPtr inRootLayer = usdStage->GetRootLayer();
SdfLayerRefPtr inRootLayer = sharedUsdStage->GetRootLayer();

if (!_unsharedStageRootLayer) {
_unsharedStageRootLayer = SdfLayer::CreateAnonymous(kUnsharedStageLayerName);
Expand Down Expand Up @@ -903,16 +924,18 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock)
newReferencedLayers, _unsharedStageRootLayer, MayaUsdMetadata->ReferencedLayers);
}

usdStage = UsdStage::UsdStage::Open(_unsharedStageRootLayer, loadSet);
unsharedUsdStage = getUnsharedStage(loadSet);
finalUsdStage = unsharedUsdStage;
}

if (usdStage) {
primPath = usdStage->GetPseudoRoot().GetPath();
copyLoadRulesFromAttribute(thisMObject(), *usdStage);
if (finalUsdStage) {
primPath = finalUsdStage->GetPseudoRoot().GetPath();
copyLoadRulesFromAttribute(thisMObject(), *finalUsdStage);
updateShareMode(sharedUsdStage, unsharedUsdStage, loadSet);
}

// Set the outUsdStageData
stageData->stage = usdStage;
stageData->stage = finalUsdStage;
stageData->primPath = primPath;

// Set the data on the output plug
Expand All @@ -925,6 +948,83 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock)
return MS::kSuccess;
}

UsdStageRefPtr MayaUsdProxyShapeBase::getUnsharedStage(UsdStage::InitialLoadSet loadSet)
{
// The unshared stages are *also* kept in a stage cache so that we can find them
// again when proxy shape attribute change. For example, if the 'loadPayloads'
// attribute change, we want to find the same unshared stage, we don't want to lose
// edits, in particular in its session layer.
//
// We also need to be able to find them when switching a stage between non-shared
// and shared, so that we can transfer the content of the session layer.
//
// Fortunately, the USD stage cache matches stages using *all* arguments provided.
// So if the unshared session layer is unique to this proxy shape, there is no
// chance of finding it by accident from another proxy shape.
UsdStageCacheContext ctx(
UsdMayaStageCache::Get(loadSet, UsdMayaStageCache::ShareMode::Unshared));

if (!_unsharedStageSessionLayer)
_unsharedStageSessionLayer = SdfLayer::CreateAnonymous();

return UsdStage::UsdStage::Open(_unsharedStageRootLayer, _unsharedStageSessionLayer, loadSet);
}

void MayaUsdProxyShapeBase::updateShareMode(
const UsdStageRefPtr& sharedUsdStage,
const UsdStageRefPtr& unsharedUsdStage,
UsdStage::InitialLoadSet loadSet)
{
// Based on the previous shared mode and current shared mode of the stage,
// transfer the content of the session layer from one to the other as needed.
const auto shareMode = isShareableStage() ? ShareMode::Shared : ShareMode::Unshared;
if (shareMode == _previousShareMode)
return;

// Only transfer the session content if the previous mode was known
// or if the current mode is unshared, as the session layer content
// is put in the shared stage when loaded from disk in a Maya scene.
//
// IOW:
// Shared -> Unshared : copy
// Unshared -> Shared : copy
// Unknown -> Unshared : copy
//
// Shared -> Shared : content already in place, pruned above
// Unshared -> Unshared : content already in place, pruned above
// Unknown -> Shared : content already in place
//
// X -> Unknown : impossible since the new mode is always known
if (_previousShareMode != ShareMode::Unknown || shareMode == ShareMode::Unshared)
transferSessionLayer(shareMode, sharedUsdStage, unsharedUsdStage, loadSet);

_previousShareMode = shareMode;
}

void MayaUsdProxyShapeBase::transferSessionLayer(
ShareMode currentMode,
const UsdStageRefPtr& sharedUsdStage,
const UsdStageRefPtr& unsharedUsdStage,
UsdStage::InitialLoadSet loadSet)
{
// When flipping to shared from unshared, the unshared set was not loaded.
// Load it now to be able to transfer the session layer content.
UsdStageRefPtr validUnsharedUsdStage
= unsharedUsdStage ? unsharedUsdStage : getUnsharedStage(loadSet);

SdfLayerHandle sharedSession = sharedUsdStage->GetSessionLayer();
SdfLayerHandle unsharedSession = validUnsharedUsdStage->GetSessionLayer();

if (!sharedSession || !unsharedSession)
return;

if (currentMode == ShareMode::Shared) {
sharedSession->TransferContent(unsharedSession);
} else {
unsharedSession->TransferContent(sharedSession);
}
}

MStatus MayaUsdProxyShapeBase::computeOutStageData(MDataBlock& dataBlock)
{
MProfilingScope computeOutStageDatacomputeOutStageData(
Expand Down Expand Up @@ -1618,6 +1718,8 @@ MayaUsdProxyShapeBase::MayaUsdProxyShapeBase(
const bool useLoadRulesHandling)
: MPxSurfaceShape()
, _isUfeSelectionEnabled(enableUfeSelection)
, _previousShareMode(ShareMode::Unknown)
, _unsharedStageSessionLayer(nullptr)
, _unsharedStageRootLayer(nullptr)
, _unsharedStageRootSublayers()
, _incomingLayers()
Expand Down
27 changes: 27 additions & 0 deletions lib/mayaUsd/nodes/proxyShapeBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,15 @@ class MayaUsdProxyShapeBase
void copyInternalData(MPxNode* srcNode) override;

private:
// The possible the shared mode of the stage.
// The 'Unknown' mode is when the proxy shape is created and has not yet been computed.
enum class ShareMode
{
Unknown,
Shared,
Unshared
};

MayaUsdProxyShapeBase(const MayaUsdProxyShapeBase&);
MayaUsdProxyShapeBase& operator=(const MayaUsdProxyShapeBase&);

Expand All @@ -347,6 +356,19 @@ class MayaUsdProxyShapeBase
MStatus computeOutStageData(MDataBlock& dataBlock);
MStatus computeOutStageCacheId(MDataBlock& dataBlock);

void updateShareMode(
const UsdStageRefPtr& sharedUsdStage,
const UsdStageRefPtr& unsharedUsdStage,
UsdStage::InitialLoadSet loadSet);

void transferSessionLayer(
ShareMode currentMode,
const UsdStageRefPtr& sharedUsdStage,
const UsdStageRefPtr& unsharedUsdStage,
UsdStage::InitialLoadSet loadSet);

UsdStageRefPtr getUnsharedStage(UsdStage::InitialLoadSet loadSet);

SdfPathVector _GetExcludePrimPaths(MDataBlock dataBlock) const;
int _GetComplexity(MDataBlock dataBlock) const;
UsdTimeCode _GetTime(MDataBlock dataBlock) const;
Expand All @@ -373,7 +395,12 @@ class MayaUsdProxyShapeBase
// Whether or not the proxy shape has enabled UFE/subpath selection
const bool _isUfeSelectionEnabled;

// Track the shared mode of the stage as seen in the last compute.
// Starts off as Unknown when the proxy shape is first created.
ShareMode _previousShareMode { ShareMode::Unknown };

// For unshared composition
SdfLayerRefPtr _unsharedStageSessionLayer;
SdfLayerRefPtr _unsharedStageRootLayer;

// We need to keep track of unshared sublayers (otherwise they get removed)
Expand Down
21 changes: 13 additions & 8 deletions lib/mayaUsd/nodes/stageNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,24 @@ MStatus UsdMayaStageNode::compute(const MPlug& plug, MDataBlock& dataBlock)
if (SdfLayerRefPtr rootLayer = SdfLayer::FindOrOpen(usdFile)) {
static const MString kSessionLayerOptionVarName(
MayaUsdOptionVars->ProxyTargetsSessionLayerOnOpen.GetText());
const bool loadAll = true;
UsdStageCacheContext ctx(UsdMayaStageCache::Get(loadAll));
UsdStageCacheContext ctx(UsdMayaStageCache::Get(
UsdStage::InitialLoadSet::LoadAll, UsdMayaStageCache::ShareMode::Shared));

// Note: UsdStage::Open uses all its argument to match an existing stage
// in the cache, including the sesssion layer if passed explicitly.
// So do *not* pass a newly-created session layer in as would create
// another stage instead of re-using the existing one.
//
// If the stage is not in the cache and no session layer is passed
// then UsdStage::Open will create the in-memory session layer for us.
usdStage = UsdStage::Open(rootLayer, ArGetResolver().GetCurrentContext());

bool targetSession = MGlobal::optionVarIntValue(kSessionLayerOptionVarName) == 1;
targetSession = targetSession || !rootLayer->PermissionToEdit();

if (targetSession) {
SdfLayerRefPtr sessionLayer = SdfLayer::CreateAnonymous();
usdStage
= UsdStage::Open(rootLayer, sessionLayer, ArGetResolver().GetCurrentContext());
SdfLayerRefPtr sessionLayer = usdStage->GetSessionLayer();
if (sessionLayer && targetSession) {
usdStage->SetEditTarget(sessionLayer);
} else {
usdStage = UsdStage::Open(rootLayer, ArGetResolver().GetCurrentContext());
usdStage->SetEditTarget(usdStage->GetRootLayer());
}
}
Expand Down
15 changes: 13 additions & 2 deletions lib/mayaUsd/python/wrapStageCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <pxr/base/tf/pyResultConversions.h>
#include <pxr/pxr.h>

#include <boost/python.hpp>
#include <boost/python/args.hpp>
#include <boost/python/class.hpp>
#include <boost/python/def.hpp>
Expand All @@ -28,14 +29,24 @@ using namespace boost;

PXR_NAMESPACE_USING_DIRECTIVE

namespace {
UsdStageCache& _UsdMayaStageCacheGet(bool loadAll, bool shared)
{
return UsdMayaStageCache::Get(
loadAll ? UsdStage::InitialLoadSet::LoadAll : UsdStage::InitialLoadSet::LoadNone,
shared ? UsdMayaStageCache::ShareMode::Shared : UsdMayaStageCache::ShareMode::Unshared);
}

} // namespace

void wrapStageCache()
{
class_<UsdMayaStageCache>("StageCache")

.def(
"Get",
&UsdMayaStageCache::Get,
args("loadAll"),
_UsdMayaStageCacheGet,
python::args("loadAll", "shared"),
return_value_policy<reference_existing_object>())
.staticmethod("Get")
.def("Clear", &UsdMayaStageCache::Clear)
Expand Down
Loading

0 comments on commit 4ab425b

Please sign in to comment.