Change Fusion integration to socket communication

I have a suggestion on how to make the Fusion integration better.
Currently we launch a whole new instance of OpenPype from Fusion to communicate with.

As it’s possible to communicate and control any Fusion instance through scripting I’m proposing to change the Fusion integration to this kind of communication.

This means that the current Fusion UI menu would be replaced with a simple drop-down in the Fusion menu
2023-07-31_13-55-11
2023-07-31_13-55-58

When the user presses any button in the menu it will send a request to OpenPype to run a function. The request also includes the current Fusion instances UUID and what comp is being open. That way the wanted request will be executed in the correct window and comp each time.

If we use Sockets to communicate between Fusion and OP we get the added benefit not needing python installed on our systems as we’re gonna use OPs python environment! We also lose the need to install PySide6 just for the Fusion integration.

Does this sound like something I should make a PR for or does anyone else have any thoughts on this?

2 Likes

I don’t know much about the technical details, however I believe it will be a better experience !

artists will deal with one program “fusion” and its addon “OP” that works just as any other addon.
and if they have multiple fusion sessions they won’t have to worry about which OP instance they should interact with.


I think it fits Ideas category more.

In short, yes - if it doesn’t complicate the code too much for developers working on OpenPype publish plug-ins etc. for Fusion.

As long as we maintain support for having multiple fusion instances running, fine with me. We should be able to run the relevant OP background process from Fusion and connect to it using the unique UUID for that Fusion instance, etc.

For completeness sake, you don’t need PySide6 - you need any compatible Qt package with qtpy.
You’d then still instead need to be able to import the Fusion libraries into the standalone Python (and ensure whatever DLL that requires actually loads correctly for any interpreter version that OpenPype is at - also it might introduce other subtle differences in that OpenPype isn’t the full python interpreter as far as I know but only the subset of it that is required for the “frozen” state for an openpype build, but I might be mistaken.

I’ve always felt that doing sockets right can be tricky.

In this case, since Fusion itself already runs separate from the actual script processes we run I suppose it’s actually doing that but obscured to the user. If we can make it so that we don’t need to change any plug-ins and code logic for that but just how it runs I think this would be great. If it influences a lot of how a Fusion developer needs to write code for OpenPype x Fusion I’m a bit worried it might overcomplicate things for many developers to start touching it.

It has definitely come up before to avoid the floating menu and exposing it through menu entries (like in other hosts) by triggering socket connections or alike. I even recall there being a dedicated issue for that too but I can’t find it anywhere - so maybe it only came up on discord in the past.

You might take a look at Adobe integrations (PS, AE), where something similar is used. But it would be tricky if there could be multiple Fusions running at same time, imho.

To make proper separation of UIs and host integration we would have to remake all tools to be capable of doing that.

After scimming though OPs Fusion integration I thin we only need to modify the get_fusion_module() and get_bmd_library() functions in the fusion lib.py (and of course modify the Fusion meny)!
Each time a call from the Fusion Menu is being made we need to import the fusionscript.dll (also called the bmd_library) to OP’s python instance if we haven’t done that already on Fusion launch and then set the UUID as a variable.

get_fusion_module() would then fetch the Fusion module using bmd.scriptapp("Fusion", "localhost", 0, fusion_uuid) and the get_bmd_library() would simply get the already fetched bmd library, or import it if it isn’t already imported.

Here is a test python script I whippted up if you wish to try:

# Load the BMD Resolve Studio Python scripting bindings
import sys
import os
import imp


def FuScriptLib():
    try:
        import fusionscript as script_module

        return script_module
    except ImportError as e:
        lib_path = ""
        if sys.platform.startswith("darwin"):
            lib_path = "/Applications/Blackmagic Fusion 18/Fusion.app/Contents/MacOS/fusionscript.so"
        elif sys.platform.startswith("win"):
            lib_path = "C:\\Program Files\\Blackmagic Design\\DaVinci Resolve\\fusionscript.dll"
        elif sys.platform.startswith("linux"):
            lib_path = "/opt/BlackmagicDesign/Fusion18/fusionscript.so"

        if not os.path.isfile(lib_path):
            print("[Fusion] [Library Does Not Exist on Disk]", lib_path)

        bmd = imp.load_dynamic("fusionscript", lib_path)

        if not bmd:
            raise ImportError("[BMD] Could not locate module dependencies") from e

        return bmd


# Get the Resolve and Fusion objects
fusion_uuid = "aff33fbf-54c4-4d62-ac4b-87a5372b96be" # got using print(bmd.getappuuid())
bmd = FuScriptLib()
fusion = bmd.scriptapp("Fusion", "localhost", 0, fusion_uuid)
if not fusion:
    raise ImportError("[Fusion] Could not connect to your Fusion instance")

Here I have hardcoded my fusion uuid just for my tests. If you want to try it out you can find your uuid using print(bmd.getappuuid()) in Fusions console.

This also works for Resolve as long as you point to the fusionscript.dll inside the Resolve folder instead of the Fusion folder!

The only problem is that the library imp is deprecated since python version 3.4 and will be removed in version 3.12. They suggest moving over to importlib but it doesn’t seem to work with .dll
It should be possible to use the library ctypes.CDLL but then you would need to call the scriptapp function by its scrambled name.

BlackmagicDesign themself is still using imp so I guess when it’s time to start moving to python 3.12 they will figure out a new way to access the dll in a good way and we could update there after.

P.S. I had some trouble running the code first, it just stopped at the imp.load_dynamic. The reson was I didn’t have the environment PYTHONPATH set pointing to my python installation. After setting it everything worked perfect :slight_smile:

I made a new PR with this implementation: Fusion - Revamp OpenPype menu by EmberLightVFX · Pull Request #5416 · ynput/OpenPype · GitHub

Everything should work as expected but I haven’t currently bin able to open any UI yet. When I do I get the error openpype.pipeline.anatomy.ProjectNotSet: Implementation bug: Project name is not set. Anatomy requires to load data for specific project.
I’m sure I’m executing the command to open any window the wrong way. I have tried to look how other addons does it but I never found a solution. Would love some help with this part!

1 Like

That sounds like install_host hasn’t triggered yet and thus OP hasn’t initialized as needed?

Ad the imp.load_dynamic. Could you try this

import sys
from importlib.machinery import ExtensionFileLoader, ModuleSpec


def load_dynamic(name, path):
    if name in sys.modules:
        return sys.modules[name]
    loader = ExtensionFileLoader(name, path)

    spec = ModuleSpec(name=name, loader=loader, origin=path)
    return loader.create_module(spec)