Skip to content

Commit

Permalink
Merge pull request #204 from Autodesk/implement_ufe_attribute_notify_dev
Browse files Browse the repository at this point in the history
Implement ufe attribute notify dev
  • Loading branch information
Krystian Ligenza authored Feb 3, 2020
2 parents a29704f + e4da124 commit 699a354
Show file tree
Hide file tree
Showing 7 changed files with 368 additions and 22 deletions.
80 changes: 76 additions & 4 deletions lib/ufe/StagesSubject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
#include "ProxyShapeHandler.h"
#include "private/InPathChange.h"

#include <ufe/path.h>
#ifdef UFE_V2_FEATURES_AVAILABLE
#include <ufe/attributes.h>
#endif
#include <ufe/hierarchy.h>
#include <ufe/path.h>
#include <ufe/scene.h>
#include <ufe/sceneNotification.h>
#include <ufe/transform3d.h>
Expand All @@ -31,6 +34,25 @@

#include <vector>

#ifdef UFE_V2_FEATURES_AVAILABLE
#include <unordered_map>

namespace {

// The attribute change notification guard is not meant to be nested, but
// use a counter nonetheless to provide consistent behavior in such cases.
int attributeChangedNotificationGuardCount = 0;

bool inAttributeChangedNotificationGuard()
{
return attributeChangedNotificationGuardCount > 0;
}

std::unordered_map<Ufe::Path, std::string> pendingAttributeChangedNotifications;

}
#endif

MAYAUSD_NS_DEF {
namespace ufe {

Expand Down Expand Up @@ -147,12 +169,12 @@ void StagesSubject::afterOpen()
me, &StagesSubject::stageChanged, stage);
}

// Set up our stage to AL_usdmaya_ProxyShape UFE path (and reverse)
// Set up our stage to proxy shape UFE path (and reverse)
// mapping. We do this with the following steps:
// - get all proxyShape nodes in the scene.
// - get their AL Python wrapper
// - get their Dag paths
// - get their Dag paths.
// - convert the Dag paths to UFE paths.
// - get their stage.
g_StageMap.clear();
auto proxyShapeNames = ProxyShapeHandler::getAllNames();
for (const auto& psn : proxyShapeNames)
Expand Down Expand Up @@ -207,6 +229,22 @@ void StagesSubject::stageChanged(UsdNotice::ObjectsChanged const& notice, UsdSta
{
auto usdPrimPathStr = changedPath.GetPrimPath().GetString();
auto ufePath = stagePath(sender) + Ufe::PathSegment(usdPrimPathStr, g_USDRtid, '/');

#ifdef UFE_V2_FEATURES_AVAILABLE
// isPrimPropertyPath() does not consider relational attributes
// isPropertyPath() does consider relational attributes
// isRelationalAttributePath() considers only relational attributes
if (changedPath.IsPrimPropertyPath()) {
if (inAttributeChangedNotificationGuard()) {
pendingAttributeChangedNotifications[ufePath] =
changedPath.GetName();
}
else {
Ufe::Attributes::notify(ufePath, changedPath.GetName());
}
}
#endif

// We need to determine if the change is a Transform3d change.
// We must at least pick up xformOp:translate, xformOp:rotateXYZ,
// and xformOp:scale.
Expand All @@ -223,5 +261,39 @@ void StagesSubject::onStageSet(const UsdMayaProxyStageSetNotice& notice)
afterOpen();
}

#ifdef UFE_V2_FEATURES_AVAILABLE
AttributeChangedNotificationGuard::AttributeChangedNotificationGuard()
{
if (inAttributeChangedNotificationGuard()) {
TF_CODING_ERROR("Attribute changed notification guard cannot be nested.");
}

if (attributeChangedNotificationGuardCount == 0 &&
!pendingAttributeChangedNotifications.empty()) {
TF_CODING_ERROR("Stale pending attribute changed notifications.");
}

++attributeChangedNotificationGuardCount;

}

AttributeChangedNotificationGuard::~AttributeChangedNotificationGuard()
{
if (--attributeChangedNotificationGuardCount < 0) {
TF_CODING_ERROR("Corrupt attribute changed notification guard.");
}

if (attributeChangedNotificationGuardCount > 0 ) {
return;
}

for (const auto& notificationInfo : pendingAttributeChangedNotifications) {
Ufe::Attributes::notify(notificationInfo.first, notificationInfo.second);
}

pendingAttributeChangedNotifications.clear();
}
#endif

} // namespace ufe
} // namespace MayaUsd
26 changes: 26 additions & 0 deletions lib/ufe/StagesSubject.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

#include <maya/MCallbackIdArray.h>

#include <ufe/ufe.h> // For UFE_V2_FEATURES_AVAILABLE

PXR_NAMESPACE_USING_DIRECTIVE

MAYAUSD_NS_DEF {
Expand Down Expand Up @@ -86,5 +88,29 @@ class MAYAUSD_CORE_PUBLIC StagesSubject : public TfWeakBase

}; // StagesSubject

#ifdef UFE_V2_FEATURES_AVAILABLE
//! \brief Guard to delay attribute changed notifications.
/*!
Instantiating an object of this class allows the attribute changed
notifications to be delayed until the guard expires.
The guard collapses down notifications for a given UFE path, which is
desirable to avoid duplicate notifications. However, it is an error to
have notifications for more than one attribute within a single guard.
*/
class MAYAUSD_CORE_PUBLIC AttributeChangedNotificationGuard {
public:

AttributeChangedNotificationGuard();
~AttributeChangedNotificationGuard();

//@{
//! Cannot be copied or assigned.
AttributeChangedNotificationGuard(const AttributeChangedNotificationGuard&) = delete;
const AttributeChangedNotificationGuard& operator&(const AttributeChangedNotificationGuard&) = delete;
//@}
};
#endif

} // namespace ufe
} // namespace MayaUsd
45 changes: 38 additions & 7 deletions lib/ufe/UsdAttribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//

#include "UsdAttribute.h"
#include "StagesSubject.h"

#include <pxr/base/tf/token.h>
#include <pxr/base/vt/value.h>
Expand Down Expand Up @@ -47,6 +48,36 @@ static constexpr char kErrorMsgEnumNoValue[] = "Enum string attribute has no val
namespace
{

template<typename T>
bool setUsdAttr(const PXR_NS::UsdAttribute& attr, const T& value)
{
// As of 24-Nov-2019, calling Set() on a UsdAttribute causes two "info only"
// change notifications to be sent (see StagesSubject::stageChanged). With
// the current USD implementation (USD 19.11), UsdAttribute::Set() ends up
// in UsdStage::_SetValueImpl(). This function calls in sequence:
// - UsdStage::_CreateAttributeSpecForEditing(), which has an SdfChangeBlock
// whose expiry causes a notification to be sent.
// - SdfLayer::SetField(), which also has an SdfChangeBlock whose
// expiry causes a notification to be sent.
// These two calls appear to be made on all calls to UsdAttribute::Set(),
// not just on the first call.
//
// Trying to wrap the call to UsdAttribute::Set() inside an additional
// SdfChangeBlock fails: no notifications are sent at all. This is most
// likely because of the warning given in the SdfChangeBlock documentation:
//
// https://graphics.pixar.com/usd/docs/api/class_sdf_change_block.html
//
// which stages that "it is not safe to use [...] [a] downstream API [such
// as Usd] while a changeblock is open [...]".
//
// Therefore, we have implemented an attribute change block notification of
// our own in the StagesSubject, which we invoke here, so that only a
// single UFE attribute changed notification is generated.
MayaUsd::ufe::AttributeChangedNotificationGuard guard;
return attr.Set<T>(value);
}

std::string getUsdAttributeValueAsString(const PXR_NS::UsdAttribute& attr)
{
if (!attr.HasValue()) return std::string();
Expand Down Expand Up @@ -92,7 +123,7 @@ void setUsdAttributeVectorFromUfe(PXR_NS::UsdAttribute& attr, const U& value)
T vec;
UFE_ASSERT_MSG(attr.Get<T>(&vec), kErrorMsgInvalidType);
vec.Set(value.x(), value.y(), value.z());
bool b = attr.Set<T>(vec);
bool b = setUsdAttr<T>(attr, vec);
UFE_ASSERT_MSG(b, kErrorMsgFailedSet);
}

Expand Down Expand Up @@ -202,7 +233,7 @@ void UsdAttributeEnumString::set(const std::string& value)
PXR_NS::TfToken dummy;
UFE_ASSERT_MSG(fUsdAttr.Get<PXR_NS::TfToken>(&dummy), kErrorMsgInvalidType);
PXR_NS::TfToken tok(value);
bool b = fUsdAttr.Set<PXR_NS::TfToken>(tok);
bool b = setUsdAttr<PXR_NS::TfToken>(fUsdAttr, tok);
UFE_ASSERT_MSG(b, kErrorMsgFailedSet);
}

Expand Down Expand Up @@ -269,7 +300,7 @@ void TypedUsdAttribute<std::string>::set(const std::string& value)
{
std::string dummy;
UFE_ASSERT_MSG(fUsdAttr.Get<std::string>(&dummy), kErrorMsgInvalidType);
bool b = fUsdAttr.Set<std::string>(value);
bool b = setUsdAttr<std::string>(fUsdAttr, value);
UFE_ASSERT_MSG(b, kErrorMsgFailedSet);
return;
}
Expand All @@ -278,7 +309,7 @@ void TypedUsdAttribute<std::string>::set(const std::string& value)
PXR_NS::TfToken dummy;
UFE_ASSERT_MSG(fUsdAttr.Get<PXR_NS::TfToken>(&dummy), kErrorMsgInvalidType);
PXR_NS::TfToken tok(value);
bool b = fUsdAttr.Set<PXR_NS::TfToken>(tok);
bool b = setUsdAttr<PXR_NS::TfToken>(fUsdAttr, tok);
UFE_ASSERT_MSG(b, kErrorMsgFailedSet);
return;
}
Expand All @@ -300,7 +331,7 @@ void TypedUsdAttribute<Ufe::Color3f>::set(const Ufe::Color3f& value)
GfVec3f vec;
UFE_ASSERT_MSG(fUsdAttr.Get<GfVec3f>(&vec), kErrorMsgInvalidType);
vec.Set(value.r(), value.g(), value.b());
bool b = fUsdAttr.Set<GfVec3f>(vec);
bool b = setUsdAttr<GfVec3f>(fUsdAttr, vec);
UFE_ASSERT_MSG(b, kErrorMsgFailedSet);
}

Expand Down Expand Up @@ -360,7 +391,7 @@ void TypedUsdAttribute<T>::set(const T& value)
{
T dummy;
UFE_ASSERT_MSG(fUsdAttr.Get<T>(&dummy), kErrorMsgInvalidType);
bool b = fUsdAttr.Set<T>(value);
bool b = setUsdAttr<T>(fUsdAttr, value);
UFE_ASSERT_MSG(b, kErrorMsgFailedSet);
}

Expand Down Expand Up @@ -481,7 +512,7 @@ bool UsdAttribute::setValue(const std::string& value)
if (!cast.IsEmpty())
cast.Swap(val);

return fUsdAttr.Set(val);
return setUsdAttr<PXR_NS::VtValue>(fUsdAttr, val);
}
#endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ createNode script -n "uiConfigurationScriptNode";
+ " -width 1\n -height 1\n -sceneRenderFilter 0\n $editorName;\n modelEditor -e -viewSelected 0 $editorName;\n\t\tif (!$useSceneConfig) {\n\t\t\tpanel -e -l $label $panelName;\n\t\t}\n\t}\n\n\n\t$panelName = `sceneUIReplacement -getNextPanel \"modelPanel\" (localizedPanelLabel(\"Persp View\")) `;\n\tif (\"\" != $panelName) {\n\t\t$label = `panel -q -label $panelName`;\n\t\tmodelPanel -edit -l (localizedPanelLabel(\"Persp View\")) -mbv $menusOkayInPanels $panelName;\n\t\t$editorName = $panelName;\n modelEditor -e \n -camera \"persp\" \n -useInteractiveMode 0\n -displayLights \"default\" \n -displayAppearance \"smoothShaded\" \n -activeOnly 0\n -ignorePanZoom 0\n -wireframeOnShaded 0\n -headsUpDisplay 1\n -holdOuts 1\n -selectionHiliteDisplay 1\n -useDefaultMaterial 0\n -bufferMode \"double\" \n -twoSidedLighting 0\n -backfaceCulling 0\n -xray 0\n -jointXray 0\n"
+ " -activeComponentsXray 0\n -displayTextures 0\n -smoothWireframe 0\n -lineWidth 1\n -textureAnisotropic 0\n -textureHilight 1\n -textureSampling 2\n -textureDisplay \"modulate\" \n -textureMaxSize 16384\n -fogging 0\n -fogSource \"fragment\" \n -fogMode \"linear\" \n -fogStart 0\n -fogEnd 100\n -fogDensity 0.1\n -fogColor 0.5 0.5 0.5 1 \n -depthOfFieldPreview 1\n -maxConstantTransparency 1\n -rendererName \"vp2Renderer\" \n -objectFilterShowInHUD 1\n -isFiltered 0\n -colorResolution 256 256 \n -bumpResolution 512 512 \n -textureCompression 0\n -transparencyAlgorithm \"frontAndBackCull\" \n -transpInShadows 0\n -cullingOverride \"none\" \n -lowQualityLighting 0\n -maximumNumHardwareLights 1\n -occlusionCulling 0\n -shadingModel 0\n"
+ " -useBaseRenderer 0\n -useReducedRenderer 0\n -smallObjectCulling 0\n -smallObjectThreshold -1 \n -interactiveDisableShadows 0\n -interactiveBackFaceCull 0\n -sortTransparent 1\n -controllers 1\n -nurbsCurves 1\n -nurbsSurfaces 1\n -polymeshes 1\n -subdivSurfaces 1\n -planes 1\n -lights 1\n -cameras 1\n -controlVertices 1\n -hulls 1\n -grid 1\n -imagePlane 1\n -joints 1\n -ikHandles 1\n -deformers 1\n -dynamics 1\n -particleInstancers 1\n -fluids 1\n -hairSystems 1\n -follicles 1\n -nCloths 1\n -nParticles 1\n -nRigids 1\n -dynamicConstraints 1\n -locators 1\n -manipulators 1\n -pluginShapes 1\n -dimensions 1\n -handles 1\n -pivots 1\n -textures 1\n"
+ " -strokes 1\n -motionTrails 1\n -clipGhosts 1\n -greasePencils 1\n -shadows 0\n -captureSequenceNumber -1\n -width 1125\n -height 825\n -sceneRenderFilter 0\n $editorName;\n modelEditor -e -viewSelected 0 $editorName;\n\t\tif (!$useSceneConfig) {\n\t\t\tpanel -e -l $label $panelName;\n\t\t}\n\t}\n\n\n\t$panelName = `sceneUIReplacement -getNextPanel \"outlinerPanel\" (localizedPanelLabel(\"ToggledOutliner\")) `;\n\tif (\"\" != $panelName) {\n\t\t$label = `panel -q -label $panelName`;\n\t\toutlinerPanel -edit -l (localizedPanelLabel(\"ToggledOutliner\")) -mbv $menusOkayInPanels $panelName;\n\t\t$editorName = $panelName;\n outlinerEditor -e \n -docTag \"isolOutln_fromSeln\" \n -showShapes 0\n -showAssignedMaterials 0\n -showTimeEditor 1\n -showReferenceNodes 0\n -showReferenceMembers 0\n -showAttributes 0\n -showConnected 0\n -showAnimCurvesOnly 0\n -showMuteInfo 0\n"
+ " -strokes 1\n -motionTrails 1\n -clipGhosts 1\n -greasePencils 1\n -shadows 0\n -captureSequenceNumber -1\n -width 1125\n -height 825\n -sceneRenderFilter 0\n $editorName;\n modelEditor -e -viewSelected 0 $editorName;\n\t\tif (!$useSceneConfig) {\n\t\t\tpanel -e -l $label $panelName;\n\t\t}\n\t}\n\n\n\t$panelName = `sceneUIReplacement -getNextPanel \"outlinerPanel\" (localizedPanelLabel(\"ToggledOutliner\")) `;\n\tif (\"\" != $panelName) {\n\t\t$label = `panel -q -label $panelName`;\n\t\toutlinerPanel -edit -l (localizedPanelLabel(\"ToggledOutliner\")) -mbv $menusOkayInPanels $panelName;\n\t\t$editorName = $panelName;\n outlinerEditor -e \n -docTag \"isolOutln_fromSeln\" \n -showShapes 1\n -showAssignedMaterials 0\n -showTimeEditor 1\n -showReferenceNodes 0\n -showReferenceMembers 0\n -showAttributes 0\n -showConnected 0\n -showAnimCurvesOnly 0\n -showMuteInfo 0\n"
+ " -organizeByLayer 1\n -organizeByClip 1\n -showAnimLayerWeight 1\n -autoExpandLayers 1\n -autoExpand 0\n -showDagOnly 1\n -showAssets 1\n -showContainedOnly 1\n -showPublishedAsConnected 0\n -showParentContainers 0\n -showContainerContents 1\n -ignoreDagHierarchy 0\n -expandConnections 0\n -showUpstreamCurves 1\n -showUnitlessCurves 1\n -showCompounds 1\n -showLeafs 1\n -showNumericAttrsOnly 0\n -highlightActive 1\n -autoSelectNewObjects 0\n -doNotSelectNewObjects 0\n -dropIsParent 1\n -transmitFilters 0\n -setFilter \"defaultSetFilter\" \n -showSetMembers 1\n -allowMultiSelection 1\n -alwaysToggleSelect 0\n -directSelect 0\n -isSet 0\n -isSetMember 0\n -displayMode \"DAG\" \n -expandObjects 0\n -setsIgnoreFilters 1\n"
+ " -containersIgnoreFilters 0\n -editAttrName 0\n -showAttrValues 0\n -highlightSecondary 0\n -showUVAttrsOnly 0\n -showTextureNodesOnly 0\n -attrAlphaOrder \"default\" \n -animLayerFilterOptions \"allAffecting\" \n -sortOrder \"none\" \n -longNames 0\n -niceNames 1\n -showNamespace 1\n -showPinIcons 0\n -mapMotionTrails 0\n -ignoreHiddenAttribute 0\n -ignoreOutlinerColor 0\n -renderFilterVisible 0\n -renderFilterIndex 0\n -selectionOrder \"chronological\" \n -expandAttribute 0\n $editorName;\n\t\tif (!$useSceneConfig) {\n\t\t\tpanel -e -l $label $panelName;\n\t\t}\n\t}\n\n\n\t$panelName = `sceneUIReplacement -getNextPanel \"outlinerPanel\" (localizedPanelLabel(\"Outliner\")) `;\n\tif (\"\" != $panelName) {\n\t\t$label = `panel -q -label $panelName`;\n\t\toutlinerPanel -edit -l (localizedPanelLabel(\"Outliner\")) -mbv $menusOkayInPanels $panelName;\n"
+ "\t\t$editorName = $panelName;\n outlinerEditor -e \n -showShapes 0\n -showAssignedMaterials 0\n -showTimeEditor 1\n -showReferenceNodes 0\n -showReferenceMembers 0\n -showAttributes 0\n -showConnected 0\n -showAnimCurvesOnly 0\n -showMuteInfo 0\n -organizeByLayer 1\n -organizeByClip 1\n -showAnimLayerWeight 1\n -autoExpandLayers 1\n -autoExpand 0\n -showDagOnly 1\n -showAssets 1\n -showContainedOnly 1\n -showPublishedAsConnected 0\n -showParentContainers 0\n -showContainerContents 1\n -ignoreDagHierarchy 0\n -expandConnections 0\n -showUpstreamCurves 1\n -showUnitlessCurves 1\n -showCompounds 1\n -showLeafs 1\n -showNumericAttrsOnly 0\n -highlightActive 1\n -autoSelectNewObjects 0\n -doNotSelectNewObjects 0\n -dropIsParent 1\n"
Expand Down
Loading

0 comments on commit 699a354

Please sign in to comment.