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

Implement ufe attribute notify dev #204

Merged
merged 8 commits into from
Feb 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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