ynput / ayon-usd-resolver

Home of AYON USD resolver plugin
Apache License 2.0
6 stars 0 forks source link

[Feature Request] Define runtime remapping/pinning using resolver context #41

Open BigRoy opened 1 day ago

BigRoy commented 1 day ago

Description

Currently we support a "pinning file" so that, e.g. at rendertime, we can explicitly map certain entity URIs to a filepath without needing to query the AYON database at all. This file is currently generated for a USD file (on publish) and then the pinning file JSON is used to do remapping at that point in time.

Currently this also all done via environment variables, but it'd be hard to make that work with different resolver contexts in e.g. Houdini. We should be using the Resolver Contexts for these.

Rationale

We should be able to do this same thing, at runtime, through a Resolver Context. Even in such a way that an explicit path remapping can be added to e.g. replace some nested USD asset version with another one of our choice.

Proposed Solution

We should use the configuration path (or config strings) one can pass to the resolver to 'construct' the configuration the resolver would use (with only a fallback to enviroment variables if no explicit config is specified).

USD has its own API to pass these, for example

A path to a file containing configuration information for the asset resolver. The standard USD asset resolver and Houdini’s built-in asset resolver do not use this, but if you use a custom asset resolver, it may read this file to change its settings when loading layers for this stage. This parameter only affects the USD stage if this node has no input connected.

The contents of this file, whether it is used at all, and if it’s used, how it affects the behavior of the asset resolver, is completely up to asset resolver implementation.

For example, the configuration file might specify a version number, telling the resolver to prefer assets of that version when loading resources.

Husk Standalone e.g. also supports that using --resolver-context or --resolver-context-string

So we should support that USD feature.

Alternatives Considered

Keep using a single global cache - clearing it fully each time in a host integration like Houdini and with python force the path mapping to be applied. However, we'd then manually need to manage that all the time, using custom callbacks, even though USD API offers built-in functionality to configure resolver contexts - which are also nicely exposed in e.g. Houdini.

Additional Context

Currently we have a global cache lookup in the resolver, that includes the cached pinned paths globally - which conflicts with the remapping per resolver context. So I'd recommend restructuring like:

Click for Python Pseudocode that helped me mindmap these steps The resolver of course isn't Python - but this Python pseudocode helped me mindmap what the steps roughly would be. So adding it here as reference: ```python class Resolver: global_cache = {} # global cache shared between ALL resolvers in current session def __init__(self): # Local cache of this resolver in current resolver context self.local_cache = {} # The pinning/remaps defined by current resolver context self.pinning_cache = {} self.is_pinning = False def BindContext(self, context): # Set up the resolver settings based on the context self.local_cache.clear() config = read_config(context) self.pinning_cache = config["pins"] self.is_pinning = any(config["pins"]) def Resolve(self, path): # Each resolver has a local cache from source path to destination path if path in self.local_cache: return self.local_cache[path] # If not in the cache, confirm whether pinning is enabled and whether # the path is in the remap - if so, remap it. path = self.pinned_path(path) # At this point a filepath should be globally the same, regardless # of resolver context so we can lookup in the global cache if path in self.global_cache: return self.global_cache[path] # If AYON entity URI and AYON connection isn't force disabled # then resolve it if not DISABLE_AYONCPP_API and is_ayon_uri(path): return self.resolve_cached(path) # Otherwise just return the path unresolved return path def pinned_path(self, path): """Remap path using pinning file""" if not self.is_pinning: return path return self.pinning_cache.get(path, path) ``` Of course the input (non-remapped) path should be added to local cache and the mapped path (after `pinned_path()` call should be added to the global cache).

Impact

Mid

Assumed Complexity

Days

Impact Scale

no other tool

Other tools that get touched

No response

Is there an existing issue with this?

Are there any labels you wish to add?

BigRoy commented 9 hours ago

It may be good @Lypsolon to post on USD Alliance Forum and check whether this is the correct intended behavior of Resolver Context Asset Path because Maya USD seems to just always pass the loaded USD file and not some arbitrary filepath that is not the USD file itself. That's maybe also why Maya USD doesn't expose a custom 'resolver context asset path' attribute on a Maya USD Proxy Shape.

I think that's actually a flaw in Maya USD because the Houdini docs clearly describe the use case of using it to configure the resolver (and not necessarily being the USD file itself?) But if not - then we may need to store the metadata inside the root layer instead.

Seems like a solid question for USD Alliance forum.