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.
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:
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:
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.
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";
}
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)();
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).
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).