I’ve been playing around with the UsdNamespaceEditor utility, and I would like to be able to test in a reliable manner whether one of its operation would result in any modification outside of the currently set edit target layer.
I know roughly when relocates can be used or not (reading the doc, writing various tests, and reading a few answers in this forum, especially Which layers will UsdNamespaceEditor change - #2 by spiff), but I’m not sure how best to check. Ideally, I’d like to have some sort of utility function that would check if a prim can be namespace edited in a non-destructive manner (e.g. can be edited via a relocate in the current edit target)
From that linked answer, it looks like I would need to check if the given prim only comes through composition arc(s), but I’m not sure how to test this reliably? My guess is inspecting the Pcp tree?
To add a bit more information: I’m trying to find a way to test a prim to see if it falls in what Spiff answered in the linked thread: relocates can’t apply to specs in the local layerStack
I don’t really know how to check if a spec is “local” or not, basically.
Edit: I have small helper function that seems to match the few test cases I have. I basically just iterate on the prim’s PcpNode stack, and I check if at least one has specs and a PcpArcTypeRoot arc type. In that case I assume the prim can’t be namespace edited in a non-destructive manner, otherwise it is (via relocates).
I’m not sure if this is enough though, of if other types of arc types can “prevent” the usage of a relocate?
In general, we cannot guarantee that any namespace edit is “completely non-destructive”. When we use that term in describing the effect of relocates, we’re just saying that a referenced asset will remain unchanged. But all opinions in the targetted layerStack that are stronger than E in LIVERPS will need to be moved/retargeted. So if a NamespaceEditor authors a relocates to the root layerStack move a prim defined inside a referenced environment, it will also destructively move any specs in the root layerStack that override the original/source location for the prim, onto the new destination path, and any relationships or connections targeting the prim in the local layerStack will also be reauthored.
This also has bearing to your question about whether we will only author opinions to the specified UsdEditTarget. The answer is no: For a UsdNamespaceEditor, the current EditTarget only identifies a layerStack, not a specific layer within that stack and we will update what needs to be updated in all layers of that stack - and ditto for any other “dependent” stages that you’ve attached to the NamespaceEditor using the AddDependentStage() method.
There’s no simple query currently to discover whether a relocate will be authored. But you can create two UsdNamespaceEditors - one whose options allow relocates and one whose options do not. If the allow one’s CanApply() returns true and the disallow one’s returns false, then you know you’ll get a relocate if you follow through. But as per 1, there may be one or (many) more edits in the authoring layerStack and layers of other dependent stages, as well.
Does that answer your questions? It would require a full, minute inspection of the stage to determine whether an edit would author only a relocate…
Thanks for the explanation. It does answer my question, but not in the way I was hoping
What I’m really after is to know for sure that a namespace edit will only modify the layer currently set as the edit target. So it looks like the safest option in my case is to stop trying to handle relocates (would be too complex and costly to make this robust if I understand correctly) and just inspect the composition of a prim, to determine if it’s fully contained in the current edit target’s layer.
I don’t think relocates really jack up the complexity of the question you are asking, and it’s not sufficient (in either case) to examine just the primStack of the prim you are moving, because other prims anywhere in the scene might have connections/relationship authored in other layers in the layerStack that target the prim you are moving. I don’t think that question is really answerable without doing more than half (ballpark) of the work that the NamespaceEditor will do. If you are operating in an environment with transactions or an undo system into which USD is integrated, I almost wonder if you’d want to go ahead and do the edit and then examine which layers were edited (you can listen for notices sent by the layers that changed), and then roll-back the edit if it was too messy?
Not very satisfying, I know… it’s a complicated problem space
Ah yes, the case of relationship. Damn, didn’t think about this one… in my tests I only had simple composition arcs.
We do have a full undo/redo system based on layer state delegates, so that might be an option indeed. I will need to experiment with this, thanks (I didn’t until now because I thought it might be a bit overkill, but if it’s the only reliable way, well I don’t have a choice :P)
Late-breaking good news! When I consulted with the experts, the belief is that it should not be too difficult to provide you the answer to your question (working out if it’s additionally returned by CanApply or a variant query or something separate) without loss of efficiency or code clarity.
Would you file a GitHub issue for this request, @dcourtois ?
(And just for flavor/context, at Pixar we rarely ever do namespace editing at the shot-level where we have deep layerStacks with restricted permissions for various users), which is why this problem is not one we considered up front).
For the time being, I went the route of using our custom undo system (which tracks everything via layer state delegates) to actually apply a “delete prim”, check which layers have been modified, and then revert all the changes.
It’s a bit overkill, but works well, except I have 1 small issue: let’s say I have some code like this:
UsdPrim prim = stage->GetPrimAtPath(...);
...
if (IsLocallyEditable(prim)) {
// Delete
} else {
prim.SetActive(false);
}
The call to prim.SetActive(false) will fail because the IsLocallyEditable function has deleted the prim! And even though the deletion has been properly undone, the original prim is no longer valid.
I could fix this contrived example by passing the prim by reference to the function, and in it getting the stage and path of the prim, to be able to re-assign it after the deletion has been undone. But it’s still super cumbersome as we might have copies of the prim in part of the code and calls to this function somewhere else, with no convenient way of passing the prim everywhere by reference.
Long story short: is there a way to test deletion of a prim without invalidating all copies of this prim? (my guess is no, but who knows :))
No, there isn’t. Independently of the enhancement you’ll be requesting to determine the layers affected by a proposed edit, we have another upcoming “follow-on to namespace editing” project that will be leveraged by OpenExec and could be leveraged by Undo systems that deals with persistent object identities (persistence through namespace editing operations). It will involve introducing a new kind (or sibling) of UsdObject that is effectively a “handle” to a UsdObject. Clients that care about resilience to namespace editing can request and hold onto one of these identities, and extract the UsdObject it identifies when they need it. An identity for a deleted object would be invalid while the object is deleted, but would become valid again if the deletion were undone.
Oooh, I can’t wait until this “prim handle” becomes available!!! That would simplify so much of our code!!
Thanks again for the help/answers, I really appreciate it! (And I’ll update this thread with the GitHub issue once it’s posted)