Is it possible to combine arbitrary SdfFileFormat and ArResolver plugins?

Background:

  1. We have a few ArResolver plugins that we use in-house to deal with cloud-based assets. These asset paths are invalid on the local machine.
  2. I recently compiled Adobe’s FileFormats plugins and discovered that they work by registering multiple SdfFileFormat plugins.

The Adobe’s plugins cannot recognize our in-house asset paths, and it is not a good idea to make them depend on our in-house packages. Based on my development experience, I decided to support them by using ArAssets, which has been fully tested for years since Ar2.0.

I modified the base class of SdfFileFormat to cache assets locally when matching a certain regular expression, and I have attached my patch to this thread. I’m pretty sure my patch works outside the USD modules since we are still using it. However, when the same patch is applied to USD’s or USD plugins’ source code, it will not take effect.

I’m writing to ask for help and to find out what the correct way to do this. I wonder if the patch fails due to the loading phase or some other complex dependency issues, but I can’t figure it out…

Ping @spiff, and hope you have a good day. :laughing:
SdfFileFormat.txt (6.7 KB)

I can’t really answer for the Adobe plugins specifically, but as for your primary question: yes you can definitely mix and match file format plugins and resolvers.

I unfortunately can’t share my code for our resolver that pulls data from a server and stores it in memory caches, but if memory serves

  1. The file format plugins first rely on you reporting back from _GetExtension in the resolver as they’re an extension based lookup.
  2. If you’re in memory, you also need a custom ArAsset to feed the bytes back to the file format plugins

Unless the Adobe plugins are working differently than the built in format plugins, that should be all you need.

Yep. The only thing I’d add over what @dhruvgovil said is that you might get better results when integrating into DCC’s if you support your assets via “URI resolvers” rather than a single, in-house, Primary resolver.

Hey, @dhruvgovil and @spiff ! I’ve researched the source code of the Omniverse and Adobe file format plugins. And here are my conclusions:

  1. Third-party file formats cannot use the ArAsset API to read or write assets. You can find more in details in the FBX plugin written by Adobe:
    https://github.com/adobe/USD-Fileformat-plugins/blob/main/fbx/src/fileFormat.cpp
    NVIDIA opened an issue related to Alembic as well, which has not yet been fully resolved.
    https://github.com/PixarAnimationStudios/OpenUSD/issues/2961

  2. NVIDIA’s solution is to develop a customized resolver that returns “omniabc” to Alembic files from sources other than local files. This forces the UsdAbcFileFormat plugin to download the file and then invoke the actual file format plugin to read it.
    https://github.com/NVIDIA-Omniverse/usd-resolver/blob/main/source/library/OmniUsdResolver_Ar2.cpp#L411

  3. The problem is, Adobe’s file format plugins cover seven different formats. For supporting them all with Omniverse, we will likely have to develop seven wrapper file format plugins to “hack” the reading/writing process. This task is too much, and it is not personally ideal. I was thinking of developing an abstract file format class for all Adobe’s plugins, but then USD asked me to register this abstract one. Because in the plugInfo.json, we have to specify the “bases” for all our plugins.

  4. I got lost in all of these. I cannot find any examples or templates for developing a USD plugin based on another USD plugin. I just can’t seem to make it work. I’m writing to ask for your help, and I’m not sure if I’m in the correct direction.

Thanks!
Calvin Gu

@roggiezhang @jomiller-nvidia For your visibility. It would be extremely helpful if your resolver were compatible with Adobe’s plugins and could handle all these file formats. We are trying to develop our solutions in case the Omniverse team cannot solve the problem. :smiling_face_with_tear:

Hi @CalvinGu. The omniabc wrapper was more of a temporary workaround then a pattern to follow while #2961 got resolved. Newer versions of OpenUSD shouldn’t require this workaround.

The fix for #2961 does still rely on the resolver being a “mirroring” resolver that keeps a local file path alive for the lifetime of the ArAsset. If I recall correctly, this was because the alembic library itself preferred files as input not because of any limitations on file format plugins themselves.

Other formats could adopt the pattern, requiring an ArAsset open and retrieving the file path from the opened asset, rather than assuming that the resolved path is a file path. They could take this further and support “non-mirroring” resolvers if the underlying libraries support efficient reading from non-files.

I think you’re raising a good point though-- the behavior of resolvers and formats can sometimes get entangled. It might be nice if there was a way for formats to advertise requiring or preferring mirroring so that resolvers can dispatch the behavior accordingly without resolvers having to have direct knowledge of the format implementation.

Thanks, @nvmkuruc !

I just had a long conversation with Joshua, and I got the solution from him. Below is part of our conversation:

Joshua

First, I really don’t have experience with the Adobe File Format plugins so I’m not sure how they deal with reading / writing assets. I’m assuming they do something similar to the Alembic File Format where they expect a file path to do normal file i/o (which does not work with cloud-based storage). If that’s the case, the wrapper file format within OmniUsdResolver can be used for multiple extensions / file formats it just needs to be tweaked a little bit. Specifically OmniUsdResolver::_GetExtension() just needs to match whatever extensions are associated with the Adobe File Format plugins, for example:

if (!isLocal(parsedUri)) {
    // you could also use a set with the different extensions that need to be downloaded
    if(extension == "abc" || extension == "adbe" || ... ) {
        return "omniabc";
    }
}

Note that “omniabc” is really just a placeholder. It can be anything as long as it matches what’s declared for extensions in plugInfo.json
That being said, there are problems with the wrapper file format and is more of a hack than anything else. Similar to what was mentioned by Matt so it’s best to not think of it as a pattern but workaround to the actual problem.

Calvin

Thanks, Joshua! That’s one of my assumptions. Thank you very much for confirming it.
You are correct, basically, all those seven file format plugins expect normal file i/o. In that thread, I was thinking of modifying all seven plugins with your “omniabc” functions. Then, they won’t depend on the Omni Resolver Plugin.
However, since you mentioned that “omniabc” is nothing but a placeholder, and could potentially cover all third-party formats. It would be much easier to allow “omniabc” plugin to invoke all of the others.
By the way, I don’t think the problem is caused by the limitations of normal file i/o. In fact, almost all the file formats, including FBX, allow streaming i/o and buffer i/o. They just cannot depend on ArAsset.
Which means that in the ArResolver, we can technically treat FBX/ABC as normal USD files and load them into ArAssets. The problem is that we cannot find an interface to interpret ArAsset into SdfLayer. The SdfFileFormat class only exposes the file path interfaces, and issue #2961 did not really fix it in the real sense of the word.

Joshua

oh yeah, I totally understand. #2961 by no means solved the problem but made it kind of work if the ArResolver impl downloads content to a local file that can be returned with ArAsset::GetFileUnsafe() .
I think there is a newer feature in C++ 23 that might help with streaming reads / writes (spanstream). Basically, you create the spanstream from the ArAsset then the file format works with the spanstream which is a part of the STL. But it requires C++ 23 support

Hopefully this “Ar3.0” can go live soon! :laughing: