Custom ArResolver contexts with Usd.Stage.Open()

Hi, I’m developing a ArResolver subclass that also implements contexts. Part of our implementation handles “querystring” like arguments:

scheme:/asset+foo=bar

The arguments are a cue to our asset system on how to resolve “asset”. Things work well if you explicitly bind an ArResolverContext up front:

from pxr import Ar, Usd
uri = "scheme:/asset+foo=bar"
ctx = Ar.CreateDefaultContextForAsset(uri)
with Ar.ResolverContextBinder(ctx):
  Usd.Stage.Open(uri)

In this mode, our custom ArResolverContext gets created and knows about the “foo=bar” setting. The ArResolver can reference it and works great.

However, things work differently when I don’t bind the resolver context:

Usd.Stage.Open(uri)

If I trace through the calls, what I see is:

_CreateIdentifier('scheme:/asset+foo=bar', '')
_Resolve('scheme:/asset+foo=bar', '')
_IsContextDependentPath('scheme:/asset+foo=bar')
_GetAssetInfo('scheme:/asset+foo=bar', '<resolved path here>')
_IsContextDependentPath('scheme:/asset+foo=bar')
_GetModificationTimestamp('scheme:/asset+foo=bar', '<resolved path here>')
_CreateDefaultContextForAsset('<resolved path here>')

Where I think things go wrong are that final _CreateDefaultContextForAsset(). At this point I’ve lost the “foo=bar” setting unless I store it globally in one of the prior steps.

I can see this above behavior in usdcat for example, but not usdview. DCC plugins like Maya and Katana seem to behave like they are calling Usd.Stage.Open() too, with no context bound.

I traced that _CreateDefaultContextForAsset() call to the _CreatePathResolverContext() function.

Cheekily, I modified my asset resolver to set the repoPath in _GetAssetInfo() to the assetPath I’m given there. This works more like I expected it to, but I realize I shouldn’t be doing that since it’s deprecated. :wink:

Is it generally expected studios should always be binding a resolver context if they are writing a custom resolver? Is there a way I should be implementing my resolver or initializing my DCC runtime to handle this better?

I’m curious how practically that works in apps like Maya and Katana. In Houdini, I know there are special fields for initializing a resolver context.

2 Likes

Hi @pkilgo!

I don’t know enough to answer your question about how this works in Maya and Katana – perhaps someone else here can chime in.

But re: your resolver implementation, one possibility would be to have your resolver return the URI itself as the resolved path. Your resolver’s implementation of _Resolve would take the URI and check if it points to something valid, and if so just return the URI itself.

So instead of:
Resolve("scheme:/asset+foo=bar") => "/path/on/disk/to/asset/foo/bar"
you’d have:
Resolve("scheme:/asset+foo=bar") => "scheme:/asset+foo=bar"

And then your resolver’s implementation of _OpenAsset (and other functions) would need to be able to handle that URI (e.g., by translating it to a filesystem path and then forwarding that along)

This sort of issue has come up a few times and this is typically the first approach we suggest. I’d be interested to hear whether something like this would work for you or if it’d be infeasible with how your assets are set up.

Internally, we’ve talked about modifying APIs like _CreateDefaultContextForAsset to take both the original and resolved path as parameters to provide more information to resolver implementations, but we’ve been hesitant to change the API for a variety of reasons. Your feedback would be a great data point to have in that discussion!

  • Sunya

Thanks for the reply Sunya. I played around with _OpenAsset() and it does seem like that might be an option. One thing that may complicate it is we actually want foo=bar to inform how ALL the assets are resolved, not just the one URI.

i.e. say I open usdview like:

usdview scheme:/shot+foo=bar

and somewhere in my scene structure, there is a reference like:

scheme:/asset/file.usd

we would still want the “foo=bar” setting to be active while resolving “asset.” It seems like contexts are better suited to this, but maybe you could implement _CreateIdentifier() to add “foo=bar” when you go to resolve “asset”. But… seems like you need a context bound to do that?

Interesting that you all have considered the API change to _CreateDefaultContextForAsset(), since we were thinking how nice it would be if we had the original and resolved paths. It might not be the total solve but it would help out many things.