makeDynCall replacing dynCall_ in Unity 6?

In the docs, it is mentioned that “The dynCall function is deprecated. If you have any code that uses dynCall , replace it with makeDynCall”.

But when I build for web, the only reference for makeDynCall I see in the generated framework.js file is:

error.message = 'Detected use of deprecated "Module.dynCall_*" API. Use "makeDynCall" API instead. Refer to https://docs.unity3d.com/6000.0/Documentation/Manual/web-interacting-browser-deprecated.html#dyncall for more information.\n' + error.message

And I see that Unity does generate the dynCall_* functions, and internal Unity functions use them as well.
Also in the doc it mentioned dynCall and not dynCall_* which are different type of functions.

I’m confused.

How should we handle this supposed change?

Thanks

makeDynCall, dynCall, etc. are for advanced use cases, they are Emscripten functions, not Unity functions, and hence I believe we don’t have much information about them in our docs. Where in your Unity Web build you can find these functions? I did a build of somewhat empty project and I cannot see anything.

I use dynCall_ in my plugins, this is why I want to know what to expect following the doc I linked.

And I see that Unity use dynCall_ for things like JS_Accelerometer_callback, JS_LinearAccelerationSensor_callback, JS_GravitySensor_callback, JS_Gyroscope_callback, JS_OrientationSensor_callback, webSocketState and others.

But I don’t see declaration of makeDynCall in the generated code. So I guess the doc is wrong?

Oh, now I know what’s going on. In order to not to use the deprecated dynCall API, one must enable WebAssembly Table, which is not enabled by default, but it (and WebAssembly 2023 in general) is recommended going forward:

1 Like

I see. Thanks.
How would you recommend for a plugin to support both options?
What would be the correct way to check what method to use?

Hi!

The new sytnax {{{ makeDynCall('vii', 'callback') }}}(1, 2) will always work no matter if the option “Use WebAssembly.Table” is enabled or not. So it is best to just use that syntax. It is basically a macro that will be replaced by Emscripten when the JavaScript code is combined. If “Use WebAssembly.Table” is disabled it will just be replaced with the old Module.dynCall_vii(callback, 1, 2) syntax, otherwise it will use the new WebAssembly table feature to make the dynamic call(which is a bit faster).
What versions of Unity do you need to support with your plugin? {{{ makeDynCall('vii', 'callback') }}}(1, 2) works since Unity 2022.2.

Unfortunately, I found no documentation for that feature in the Emscripten docs. But this PR shows how it works:

If you also need to support older versions there is also this legacy version of the macro:

{{{ makeDynCall('sig') }}} (ptr, arg1, arg2);

This should work for all Unity versions.

4 Likes

Thanks!
Does those macros work on ‘jspre’ files as well, or only ‘jslib’?

Unfortunatly, this macro will only work in .jslib files. .jspre files will be inserted verbatim into the framework.
I will have to do a bit of research to see if there is a solution of jspre files.

2 Likes

This should work for .jspre files:

Module["TestCallbackFunc"] = function (cb, arg1) {
  // {{{ makeDynCall('vi', 'cb') }}}(arg1);  
  if (typeof getWasmTableEntry !== "undefined")
    getWasmTableEntry(cb)(arg1);  
  else if (typeof Module.dynCall_vi !== "undefined")
    Module.dynCall_vi(cb, arg1);
  else
    throw "Could not make dynCall because neither getWasmTableEntry nor Module.dynCall_* is available";
}
2 Likes

Thanks.
As I support older versions as well, knowing it’s not supported on jspre, where most of the dynCalls I’m doing are, is good enough.

1 Like

This feature needs better docs.

For those who got here and wonder how to call a function with no arguments:

var somePtr = ... // pointer/callback to a C/C# call
{{{ makeDynCall('v', 'somePtr') }}}(); // 'somePtr' - the name of the pointer/callback to the C/C# call should be in quotes when using the macro
// or
getWasmTableEntry(somePtr)();
1 Like

Hey, so I’m trying to get this working with Unity 6 as well with my .jslib file but I’m getting an error saying:

An error occurred running the Unity content on this page. See your browser JavaScript console for more info. The error was:
ReferenceError: makeDynCall is not defined
at window.sendToGameClient (http://localhost:3000/build/build/Build/build.framework.js:1690:18)

This is my JSLib:

mergeInto(LibraryManager.library, {
   Init: function (fnPtr) {
      console.log("Init v1.0.2");

      window.sendToGameClient = function (uInt8Array) {
         // Allocate memory in WebAssembly and copy the Uint8Array into it
         const length = uInt8Array.length;
         const buffer = _malloc(length);
         HEAPU8.set(uInt8Array, buffer);

         { { { makeDynCall('vii', 'fnPtr') } } } (buffer, length);

         _free(buffer);
      };
   },

   Send: function (bytePtr, length) {
      const copiedArray = new Uint8Array(length);
      copiedArray.set(HEAPU8.subarray(bytePtr, bytePtr + length));

      try {
         if (window.receiveBytes) {
            window.receiveBytes(copiedArray);
         }
      } catch (e) {
         console.error("Error calling receiveBytes", e);
      }
   }
});

Not sure what’s going wrong, it doesnt work in Preview 6 or 6000.1.0a2.

I have Use WebAssembly.Table checked (but it doesn’t work either way).

Any clues as to what might be going on?

Try with no space between each { and }.

{{{ makeDynCall('vii', 'fnPtr') }}}(buffer, length);

It’s a macro string that the emscripten looks for and replace on build time.

2 Likes

Yep that solved it! Thanks!

1 Like

I’ve got different versions of Unity my plugins support. Does this work at least back to 2021?

When supporting 2021 and alter versions with the same code I would suggest to use this version of the macro:

The main difference is that the pointer to the function is passed at runtime and not to the macro itself.

Refer to this PR in emscripten for more information on this:

2 Likes

Is there a way to conditionally compile things in .jspre/.jslib like in c#?

Or do we need to do something similar to your post above when determining if the table exists?

Yes it is possible to use this construct in .jslib files:

#if VARIABLE
   var foo = 0.0;
   javaScriptCode();
   {{{ statement }}} // <-  statement will be evaluated by the JS preprocessor and inserted as text
#endif

This feature is supplied by Emscripten. But it will not work for .jspre files.
Unfortunatly the only preprocessor variable to check the unity version in JavaScript is UNITY_VERSION which returns the version as a string(same as Application.unityVersion).

1 Like

What I ended up doing was

In the jslib I check if specific dyncall_ exist, and if not I’m using the new method.
Then I don’t need to update the jspre.

I don’t recommend it for every case. But in my case it was a nicer solution IMO.
It can break if the new method will change.

1 Like

yeah I was also considering some wrapper like this as well.

I feel like a shim is needed in the editor/emscripten side of things to just make the transition smoother under the hood.