diff --git a/lib/mayaUsd/nodes/proxyShapeBase.cpp b/lib/mayaUsd/nodes/proxyShapeBase.cpp index 2fd6caa1d9..b6ed22c2b6 100644 --- a/lib/mayaUsd/nodes/proxyShapeBase.cpp +++ b/lib/mayaUsd/nodes/proxyShapeBase.cpp @@ -159,6 +159,11 @@ MObject MayaUsdProxyShapeBase::drawGuidePurposeAttr; MObject MayaUsdProxyShapeBase::sessionLayerNameAttr; MObject MayaUsdProxyShapeBase::rootLayerNameAttr; MObject MayaUsdProxyShapeBase::mutedLayersAttr; +#if MAYA_API_VERSION >= 20240000 && MAYA_API_VERSION <= 20249999 +// Change counter attributes +MObject MayaUsdProxyShapeBase::updateCounterAttr; +MObject MayaUsdProxyShapeBase::resyncCounterAttr; +#endif // Output attributes MObject MayaUsdProxyShapeBase::outTimeAttr; MObject MayaUsdProxyShapeBase::outStageDataAttr; @@ -402,6 +407,28 @@ MStatus MayaUsdProxyShapeBase::initialize() retValue = addAttribute(outStageDataAttr); CHECK_MSTATUS_AND_RETURN_IT(retValue); +#if MAYA_API_VERSION >= 20240000 && MAYA_API_VERSION <= 20249999 + updateCounterAttr + = numericAttrFn.create("updateId", "upid", MFnNumericData::kInt64, -1, &retValue); + CHECK_MSTATUS_AND_RETURN_IT(retValue); + numericAttrFn.setStorable(false); + numericAttrFn.setWritable(false); + numericAttrFn.setHidden(true); + numericAttrFn.setInternal(true); + retValue = addAttribute(updateCounterAttr); + CHECK_MSTATUS_AND_RETURN_IT(retValue); + + resyncCounterAttr + = numericAttrFn.create("resyncId", "rsid", MFnNumericData::kInt64, -1, &retValue); + CHECK_MSTATUS_AND_RETURN_IT(retValue); + numericAttrFn.setStorable(false); + numericAttrFn.setWritable(false); + numericAttrFn.setHidden(true); + numericAttrFn.setInternal(true); + retValue = addAttribute(resyncCounterAttr); + CHECK_MSTATUS_AND_RETURN_IT(retValue); +#endif + outStageCacheIdAttr = numericAttrFn.create("outStageCacheId", "ostcid", MFnNumericData::kInt, -1, &retValue); CHECK_MSTATUS_AND_RETURN_IT(retValue); @@ -568,6 +595,24 @@ void MayaUsdProxyShapeBase::postConstructor() } } +#if MAYA_API_VERSION >= 20240000 && MAYA_API_VERSION <= 20249999 +/* virtual */ +bool MayaUsdProxyShapeBase::getInternalValue(const MPlug& plug, MDataHandle& handle) +{ + bool retVal = true; + + if (plug == updateCounterAttr) { + handle.set(_UsdStageUpdateCounter); + } else if (plug == resyncCounterAttr) { + handle.set(_UsdStageResyncCounter); + } else { + retVal = MPxSurfaceShape::getInternalValue(plug, handle); + } + + return retVal; +} +#endif + /* virtual */ MStatus MayaUsdProxyShapeBase::compute(const MPlug& plug, MDataBlock& dataBlock) { @@ -1998,10 +2043,19 @@ void MayaUsdProxyShapeBase::_OnStageObjectsChanged(const UsdNotice::ObjectsChang MProfilingScope profilingScope( _shapeBaseProfilerCategory, MProfiler::kColorB_L1, "Process USD objects changed"); +#if MAYA_API_VERSION >= 20240000 && MAYA_API_VERSION <= 20249999 + switch (UsdMayaStageNoticeListener::ClassifyObjectsChanged(notice)) { + case UsdMayaStageNoticeListener::ChangeType::kIgnored: return; + case UsdMayaStageNoticeListener::ChangeType::kResync: ++_UsdStageResyncCounter; + // [[fallthrough]]; // We want that fallthrough to have the update always triggered. + case UsdMayaStageNoticeListener::ChangeType::kUpdate: ++_UsdStageUpdateCounter; break; + } +#else if (UsdMayaStageNoticeListener::ClassifyObjectsChanged(notice) == UsdMayaStageNoticeListener::ChangeType::kIgnored) { return; } +#endif // This will definitely force a BBox recomputation on "Frame All" or when framing a selected // stage. Computing bounds in USD is expensive, so if it pops up in other frequently used diff --git a/lib/mayaUsd/nodes/proxyShapeBase.h b/lib/mayaUsd/nodes/proxyShapeBase.h index a65460c480..ed7b377195 100644 --- a/lib/mayaUsd/nodes/proxyShapeBase.h +++ b/lib/mayaUsd/nodes/proxyShapeBase.h @@ -126,6 +126,14 @@ class MayaUsdProxyShapeBase MAYAUSD_CORE_PUBLIC static MObject mutedLayersAttr; +#if MAYA_API_VERSION >= 20240000 && MAYA_API_VERSION <= 20249999 + // Change counter attributes + MAYAUSD_CORE_PUBLIC + static MObject updateCounterAttr; + MAYAUSD_CORE_PUBLIC + static MObject resyncCounterAttr; +#endif + // Output attributes MAYAUSD_CORE_PUBLIC static MObject outTimeAttr; @@ -171,6 +179,10 @@ class MayaUsdProxyShapeBase MAYAUSD_CORE_PUBLIC void postConstructor() override; +#if MAYA_API_VERSION >= 20240000 && MAYA_API_VERSION <= 20249999 + MAYAUSD_CORE_PUBLIC + bool getInternalValue(const MPlug&, MDataHandle&) override; +#endif MAYAUSD_CORE_PUBLIC MStatus compute(const MPlug& plug, MDataBlock& dataBlock) override; MAYAUSD_CORE_PUBLIC @@ -406,6 +418,12 @@ class MayaUsdProxyShapeBase size_t _excludePrimPathsVersion { 1 }; size_t _UsdStageVersion { 1 }; +#if MAYA_API_VERSION >= 20240000 && MAYA_API_VERSION <= 20249999 + // Notification counters: + MInt64 _UsdStageUpdateCounter { 1 }; + MInt64 _UsdStageResyncCounter { 1 }; +#endif + MayaUsd::ProxyAccessor::Owner _usdAccessor; static ClosestPointDelegate _sharedClosestPointDelegate; diff --git a/test/lib/ufe/testContextOps.py b/test/lib/ufe/testContextOps.py index e90b2cb268..d5e9e7eac5 100644 --- a/test/lib/ufe/testContextOps.py +++ b/test/lib/ufe/testContextOps.py @@ -1492,6 +1492,27 @@ def testGeomCoponentAssignment(self): self.assertEqual(topSubset.GetFamilyNameAttr().Get(), "componentTag") self.assertFalse(topSubset.GetPrim().HasAPI(UsdShade.MaterialBindingAPI)) + if mayaUtils.mayaMajorVersion() == 2024: + # We also can check the old sync counters: + counters= { "resync": cmds.getAttr(psPathStr + '.resyncId'), + "update" : cmds.getAttr(psPathStr + '.upid')} + + def assertIsOnlyUpdate(self, counters, shapePathStr): + resyncCounter = cmds.getAttr(shapePathStr + '.resyncId') + updateCounter = cmds.getAttr(shapePathStr + '.updateId') + self.assertEqual(resyncCounter, counters["resync"]) + self.assertGreater(updateCounter, counters["update"]) + counters["resync"] = resyncCounter + counters["update"] = updateCounter + + def assertIsResync(self, counters, shapePathStr): + resyncCounter = cmds.getAttr(shapePathStr + '.resyncId') + updateCounter = cmds.getAttr(shapePathStr + '.updateId') + self.assertGreater(resyncCounter, counters["resync"]) + self.assertGreater(updateCounter, counters["update"]) + counters["resync"] = resyncCounter + counters["update"] = updateCounter + messageHandler = mayaUtils.TestProxyShapeUpdateHandler(psPathStr) messageHandler.snapshot() @@ -1508,40 +1529,58 @@ def testGeomCoponentAssignment(self): # We expect a resync after this assignment: self.assertTrue(messageHandler.isResync()) + if mayaUtils.mayaMajorVersion() == 2024: + assertIsResync(self, counters, psPathStr) # setting a value the first time is a resync due to the creation of the attribute: attrs = ufe.Attributes.attributes(shaderItem) metallicAttr = attrs.attribute("inputs:metallic") ufeCmd.execute(metallicAttr.setCmd(0.5)) self.assertTrue(messageHandler.isResync()) + if mayaUtils.mayaMajorVersion() == 2024: + assertIsResync(self, counters, psPathStr) # Subsequent changes are updates: ufeCmd.execute(metallicAttr.setCmd(0.7)) self.assertTrue(messageHandler.isUpdate()) + if mayaUtils.mayaMajorVersion() == 2024: + assertIsOnlyUpdate(self, counters, psPathStr) # First undo is an update: cmds.undo() self.assertTrue(messageHandler.isUpdate()) + if mayaUtils.mayaMajorVersion() == 2024: + assertIsOnlyUpdate(self, counters, psPathStr) # Second undo is a resync: cmds.undo() self.assertTrue(messageHandler.isResync()) + if mayaUtils.mayaMajorVersion() == 2024: + assertIsResync(self, counters, psPathStr) # Third undo is also resync: cmds.undo() self.assertTrue(messageHandler.isResync()) + if mayaUtils.mayaMajorVersion() == 2024: + assertIsResync(self, counters, psPathStr) # First redo is resync: cmds.redo() self.assertTrue(messageHandler.isResync()) + if mayaUtils.mayaMajorVersion() == 2024: + assertIsResync(self, counters, psPathStr) # Second redo is resync: cmds.redo() self.assertTrue(messageHandler.isResync()) + if mayaUtils.mayaMajorVersion() == 2024: + assertIsResync(self, counters, psPathStr) # Third redo is update: cmds.redo() self.assertTrue(messageHandler.isUpdate()) + if mayaUtils.mayaMajorVersion() == 2024: + assertIsOnlyUpdate(self, counters, psPathStr) currentCacheId = messageHandler.getStageCacheId() # Changing the whole stage is a resync: @@ -1549,6 +1588,7 @@ def testGeomCoponentAssignment(self): cmds.setAttr('{}.filePath'.format(psPathStr), testFile, type='string') self.assertTrue(messageHandler.isResync()) + # The old smart signaling for Maya 2024 will not catch that. # But that will be the last resync: testFile = testUtils.getTestScene("MaterialX", "sin_compound.usda") @@ -1564,6 +1604,7 @@ def testGeomCoponentAssignment(self): cmds.setAttr('{}.filePath'.format(psPathStr), testFile, type='string') self.assertTrue(messageHandler.isResync()) + # The old smart signaling for Maya 2024 will not catch that. messageHandler.terminate()