I’ve been experimenting with custom ArResolverContext and populating it through CreateDefaultContextForAsset. This works excellently for any code that opens a usd stage directly from the path.
I noticed in usdview it appeared the top level file reference was not using the resolver context but everything underneath it was. Distilling the code in usdview, I found
This looks incorrect to me. It also explains why the top level layer does not get the context. Based on what I’ve been learning, I would expect the code to require this structure.
Would the way usdview is currently loading the scene expected to be valid? Would either of these approaches be considered more valid than the other? I’m starting to worry we may be limited with what we can do with CreateDefaultContextForAsset since it seems to get used so inconsistently across existing tools and applications.
I think you’re right that the order should be flipped in usdview so you are able to create the context first.
To your point, yes, a bunch of tools may not create the context when you want them to. You can really only hope they do already, and submit issues to the ones who don’t. Of course, that’s difficult for closed source apps.
sonofagun. That’s a regression introduced in 2017(!) when we wanted to validate that the fileformat of the referred path was legal prior to opening the stage. Only the UsdStage::Open() overloads that take a filepath (rather than a layer) will use a provided Context to resolve the layer’s path. We should fix that.
I will note, though, that both usdedit and usdcat are following the same/similar pattern, i.e. the layer is resolved without benefit of a DefaultContextForAsset. All three tools (at least) should probably be aligned in their behavior.
Spiff you mentioned that usdedit also has the issue, I assume this is due to how it makes use of usdcat under the hood? I tested both usdcat and usdedit with my usdcat fix from the PR and both tools appear to be correctly respecting the context now. Let me know if I missed anything.
From what I can tell, opening a stage doesn’t call CreateDefaultContextForAsset for the root layer, only for the other layers. So I’m not sure this should have been considered a regression. According to the UsdStage::Open documentation:
If pathResolverContext is provided it will be bound when whenever asset path resolution is done for this stage, regardless of what other context may be bound at that time. Otherwise Usd will create a context for all future asset path resolution for the stage by calling ArResolver::CreateDefaultContextForAsset with the layer’s repository path if the layer has one, otherwise its resolved path.
By changing usdview/usdcat/etc.. to explicitly create a default context from the layer path, we’ve now introduced a different behavior than simply opening the stage directly.
The recent change to usdview was actually fixing a regression, as the current behavior is the one it was designed with. We should document this, probably in our Toolset documentation.
The way UsdStage::Open() handles resolver contexts could have gone a number of ways - we’ve heard reasonable requests that if no context is specified, we should instead fetch and retain the currently bound context… and we could have used the DefaultContextForAsset to resolve the root layer. If we had used the DefaultContextForAsset internally for the root layer, there would have been no surprises in the usdview code. But I think we reasoned: you can either pass an already-loaded SdfLayer, or a path to Open(); in the former case, it’s likely you bound a context before FindOrOpen()'ing that layer, and that bound-context is what Open() would use in locating the root layer when specified by path.
So basically we’re saying it’s the application’s responsibility, one way or another, to provide context for opening the root layer, but that when no context is explicitly provided, we guarantee that for a given resolved root layer path, all the references in the stage will resolve consistently no matter the calling site of Open(). And now I think that’s why we didn’t want to “capture” the bound context for a stage’s internal use, as it could lead to the same root layer being opened on two different stages and getting very different results, with no obvious, local-to-calling-site differences.