Skip to content

Instantly share code, notes, and snippets.

@meshula
Last active June 11, 2023 16:58
Show Gist options
  • Save meshula/f192d3797567c53e55af65646b5c327b to your computer and use it in GitHub Desktop.
Save meshula/f192d3797567c53e55af65646b5c327b to your computer and use it in GitHub Desktop.
usd static linking thoughts

ARCH_CONSTRUCTOR is used by pxr/base/arch/initConfig.cpp:ARCH_CONSTRUCTOR(Arch_InitConfig, 2, void) pxr/base/tf/initConfig.cpp:ARCH_CONSTRUCTOR(Tf_InitConfig, 2, void) pxr/base/tf/initConfig.cpp:ARCH_CONSTRUCTOR(Tf_InitConfigPost, 202, void) pxr/base/plug/initConfig.cpp:ARCH_CONSTRUCTOR(Plug_InitConfig, 2, void) pxr/base/vt/value.cpp:ARCH_CONSTRUCTOR(Vt_CastRegistryInit, 255)

and by the macros TF_REGISTRY_DEFINE TF_REGISTRY_DEFINE_WITH_TYPE

TF_REGISTRY_DEFINE_WITH_TYPE is only used by TF_INSTANTIATE_TYPE which is not used by anything

TF_REGISTRY_DEFINE is only used by TF_REGISTRY_FUNCTION and TF_REGISTRY_FUNCTION_WITH_TAG

these are used by every type in nearly every library

so the question is, can all of those explicit constructor initializations be called by an initUsd.cpp, and can the types be loaded not at dload time through static initialization, but can TF_REGISTRY_FUNCTION* be coerced to be run at runtime, after the dloads are finished, and before Tf_InitConfigPost is called, from initUsd.cpp?

Can some mechanism be leveraged to discover all the type registrations, for that purpose? Regex the code and do it manually? Make initUsd.cpp just a big "this is for game programmers!" file maintained manually that we can warn mainstream usd devs off of?

Can we make a special "game static linking mode" that uses a set of modified macros so that the system can be built as is, BUT ALSO compiled in the new, "no static initialization mode"?

To some degree the "game mode" would bypass the plugin loading mechanism, so the pluginfo's would have to be maybe built into the library as interned strings.

The end result would be a simple-to-link variant of USD for engines. Not for tools or DCCs but specifically for embedding in places where dynamic loading doesn't make sense, and where user configuration, such as loose plugins are undesirable because you want to avoid hacking soft-points.

@Lujker
Copy link

Lujker commented Jun 7, 2023

Hi, did you manage to connect USD as a static library to the Visual Studio project? I had problems with the fact that if I connect USD as a dll to my project, in which I use a custom allocator, when allocating memory for some objects in the USD code, allocation occurs through a standard allocator, and it is deleted through mine as a result, I catch the program crash. In theory, my error should disappear if I connect USD as a static library, but there are problems with this. Have you tried connecting USD as a static library? It seems here you are writing about a similar problem to mine.
At this stage, I catch an error when trying to interact with the USD code from my code (in particular, when creating a new UsdStage), and registration when connecting the dll, judging by the results, differs from opening the project in static linking

@meshula
Copy link
Author

meshula commented Jun 7, 2023

Hi, yes, I statically link USD in Visual Studio, and xcode, in several of my projects. I might be able to help you. What error do you get when you are trying to create a new stage?

@Lujker
Copy link

Lujker commented Jun 8, 2023

@meshula At this stage, I found an error when searching for a plugin when executing code in a file pxr\usd\ar\resolver.cpp on the line
const PlugPluginPtr plugin = _GetPluginForType(resolverType) I get an invalid object. But I'll start from the beginning, maybe the thing is that I'm not building static libraries correctly or connecting them to the project incorrectly. At the moment I am building a build through a script build_usd.py having previously indicated that I need a non-monolithic and non-shared assembly by specifying this in the script. Also, additionally, I added add_definitions("-DPXR_STATIC") to the CMakefile. After that, I get the build folder in which I have an assembly with several .dll from boost and .lib library files. I copy this folder to my workspace and add all library files to my project in the standard way and add a define to the PXR_STATIC project. After that, I copy the build\lib\usd folder to the folder with my application executable file without changing anything. After that, I correctly assemble my code, which works if I use dynamic linking with USD and everything runs correctly. But after trying to create a UsdStage, I get an error while executing the code in the file pxr\usd\ar\resolver.cpp on the line TF_VERIFY(availablePrimaryResolvers.back().type == defaultResolverType); because availablePrimaryResolvers is empty at this stage. If you can tell me how to properly collect and add USD to the project in the form of a static library, I would be very grateful. I could not find such detailed instructions on the Internet. Also, I assume that my mistake may be due to the fact that I am not correctly linking libraries with the project, perhaps there are any global variables or defines that need to be defined? Thank you in advance for your help!

@meshula
Copy link
Author

meshula commented Jun 8, 2023

You are very close. Your next step should be to resolve the empty resolvers problem. Your build and linkage are actually working properly already.

This error you encountered happens when USD cannot find the pluginfos! So all you need to do now, is indicate where the pluginfos are to be found.

Here's a hint, that should help you get to the next stage. I don't like to play with paths or environment variables, so I override the search paths myself in my main. It's important to add this code exactly at the start of main, before any other code can run.

My builds are named according to the date, and I put it in a define to make it easy to change. You can see my build is in c:/dev/usdXXXX.

A real app would be much smarter about pointing to its own data of course, but this is my quick hack I use in little test programs.

#define INSTALL_LOCN "usd0528"

int main(int argc, char *argv[])
{
    //-------------------------------------------------------------------------
    // USD set up
    //-------------------------------------------------------------------------
    std::vector<std::string> test;
    std::vector<std::string> paths;
    paths.emplace_back("c:/dev/" INSTALL_LOCN "/lib/usd");
    paths.emplace_back("c:/dev/" INSTALL_LOCN "/plugin/usd");
    std::vector<std::string> msgs;
    msgs.emplace_back("looking for plugs here: C:/dev/" INSTALL_LOCN "/lib/usd and /plugin/usd");
    Plug_SetPaths(paths, msgs, true);

@Lujker
Copy link

Lujker commented Jun 11, 2023

@meshula Thank you very much, without your advice I would have spent a lot more time and effort on fixing connection errors. In addition to what you wrote, I came across the fact that I need to set the /WHOLEARCHIVE:"*.lib" flag in the linker settings for each lib file that I add to the project, otherwise I would also get errors and incorrect library operation at startup. I hope the points described above will help other people in connecting the library. And thank you again for your help!

@meshula
Copy link
Author

meshula commented Jun 11, 2023

I'm glad everything worked out! Good note on /wholearchive!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment