Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Super fast Javascript interaction on WebGL

Discussion in 'Web' started by tteneder, Jan 29, 2016.

  1. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    Hi folks,

    I figured something out worth sharing maybe:

    Source:
    https://github.com/atteneder/UnityJavascriptInteraction

    I'm on a project where we do heavy Javascript interaction with Unity and we're not happy with the SendMessage interface Unity provides.
    • It's slow (on my machine around 0.1 ms overhead per call, which is problematic to me).
    • It's limited to three parameter variants in JS: void, Number, String
    • You need extra glue code and calls to come up for the lack of return values.
    So I tried this:
    • Native C function that sets a callback to a static C# method. This is called on Awake somewhere.
    • Native C function that calls the callback.
    • Setting the emscriptenArgs to also export my caller C function.
    • Export, fire up a browser.
    • Call via emscripten's ccall.
    • Call the function with both methods 10000 times and measure the results.
    Here you go, in Chrome:
    Bildschirmfoto 2016-01-29 um 15.19.54.png

    Firefox is even faster:
    Bildschirmfoto 2016-01-29 um 15.54.14.png

    I attached the complete Unity project so you can fiddle with it.

    Notes:
    • I haven't covered passing parameters back and forth yet, which is not straight forward since you need some conversions / memory operations.
    • @Unity: It would be nice to have a (hidden) setting like "emscriptenArgs" that allows to give additional function names to emscripten's "-s EXPORTED_FUNCTIONS" parameter. I basically had to override it.
    Hope you find this helpful,
    atti

    EDIT:
    I'll update this post if I have new info to share:
    - Removed original source, added super-social GitHub link instead.
    - cwrap makes things even speedier! (thanks alexsuvorov)
     
    Last edited: Jan 29, 2016
    DMorock, Flavelius, kainoo and 4 others like this.
  2. alexsuvorov

    alexsuvorov

    Unity Technologies

    Joined:
    Nov 15, 2015
    Posts:
    327
    Hello atti.

    This is a very interesting observation. I have just tested your code and it does show significant overhead for SendMessage calls. Moreover, it appears that it is not the SendMessage itself that causes the overhead, but the accompanying Module.cwrap(), which creates a JavaScript function within each SendMessage call.

    Let's examine the original code:
    Code (JavaScript):
    1. function SendMessage(gameObject, func, param)
    2. {
    3.     if (param === undefined)
    4.         Module.cwrap('SendMessage', 'void', ['string', 'string'])(gameObject, func);
    5.     else if (typeof param === "string")
    6.         Module.cwrap('SendMessageString', 'void', ['string', 'string', 'string'])(gameObject, func, param);
    7.     else if (typeof param === "number")
    8.         Module.cwrap('SendMessageFloat', 'void', ['string', 'string', 'number'])(gameObject, func, param);
    9.     else
    10.         throw "" + param + " is does not have a type which is supported by SendMessage.";
    11. }
    which gives us
    Screen Shot 2016-01-29 at 21.43.03.png

    Now if we modify the code a bit so that the wrappers are generated only once, for example:
    Code (JavaScript):
    1. function SendMessage(gameObject, func, param)
    2. {
    3.     if (param === undefined) {
    4.       if (typeof this.SendMessage_vss != 'function')
    5.         this.SendMessage_vss = Module.cwrap('SendMessage', 'void', ['string', 'string']);
    6.       this.SendMessage_vss(gameObject, func);
    7.     } else if (typeof param === "string") {
    8.       if (typeof this.SendMessage_vsss != 'function')
    9.         this.SendMessage_vsss = Module.cwrap('SendMessageString', 'void', ['string', 'string', 'string']);
    10.       this.SendMessage_vsss(gameObject, func, param);
    11.     } else if (typeof param === "number") {
    12.       if (typeof this.SendMessage_vssn != 'function')
    13.         this.SendMessage_vssn = Module.cwrap('SendMessageFloat', 'void', ['string', 'string', 'number']);
    14.       this.SendMessage_vssn(gameObject, func, param);
    15.     } else
    16.         throw "" + param + " is does not have a type which is supported by SendMessage.";
    17. }
    then we will get absolutely different result:
    Screen Shot 2016-01-29 at 21.41.59.png

    I think this can be easily added to the release in the near future. Feel free to experiment with it, maybe you will be able to optimize the SendMessage even further. And thanks again for a great discovery!
     
  3. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    Hi alex,

    this looks really good! Your improvement kicks ass, because it's very beneficial for everyone using SendMessage without them having to change any code. Would be cool to have this in a next release.

    In the meantime I'll pursue the concept of calling custom C functions. I'll host the thing on GitHub very soon.
    I didn't even knew cwrap before, so I tried it on my callback as well via the "established" 10k call test and it got 4-5 times faster. We're talking about a 99% speed improvement now (instead of the ~96,6% I measured in my first post). That's beyond my exceptions, nice.

    edit:
    The difference gets even more noticeable if you disable exception handling (which we should anyways).
     
    Last edited: Jan 30, 2016
  4. alexsuvorov

    alexsuvorov

    Unity Technologies

    Joined:
    Nov 15, 2015
    Posts:
    327
    As you can see, with proper optimization SendMessage can be almost as efficient as the direct call. These two approaches are however different in nature. SendMessage can be used to call a method of a dynamically created object, referenced by its symbolic name. It therefore has to first resolve the symbolic name to perform the call. And with all the efficient caching involved here, it still has to check if the referenced object exist and is active. When dealing with game objects, this functionality can be very useful.

    On the other hand, if you are handling the call with c/c++, then all this object oriented overhead may become unnecessary for you, so it might be more efficient to just use the direct call. Note that you do not have to adjust the Emscripten command line to export a function. Instead you can just use EMSCRIPTEN_KEEPALIVE macro in the function declaration:
    Code (c):
    1. #include "emscripten.h"
    2. void EMSCRIPTEN_KEEPALIVE call_callback() {
    3.   ...
    4. }
    The function will then get exported automatically (https://kripken.github.io/emscripte...ence/emscripten.h.html#c.EMSCRIPTEN_KEEPALIVE).

    By the way, talking about the SendMessage optimization, you are able to perform this even in the current version of Unity. All you have to do is to override the existing SendMessage implementation with your own. You can do this directly from the index.html by adding, for example, a onRuntimeInitialized handler to the Module object:
    Code (html):
    1. ...
    2. <script src="Release/UnityLoader.js"></script>
    3. <script>
    4.   Module.onRuntimeInitialized = function() {
    5.     SendMessage = function(gameObject, func, param) {
    6.       // your implementation
    7.     }
    8.   }
    9. </script>
    10. ...
    Now you can just add this code to the index.html template (http://docs.unity3d.com/Manual/webgl-templates.html) so that it will be automatically added to the index.html each time you rebuild your project, for example:
    Code (html):
    1. <!doctype html>
    2. <html lang="en-us">
    3.   <head>
    4.     <meta charset="utf-8">
    5.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    6.     <title>Unity WebGL Player | %UNITY_WEB_NAME%</title>
    7.     <link rel="stylesheet" href="TemplateData/style.css">
    8.     <link rel="shortcut icon" href="TemplateData/favicon.ico" />
    9.     <script src="TemplateData/UnityProgress.js"></script>
    10.   </head>
    11.   <body class="template">
    12.     <p class="header"><span>Unity WebGL Player | </span>%UNITY_WEB_NAME%</p>
    13.     <div class="template-wrap clear">
    14.       <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" height="%UNITY_HEIGHT%px" width="%UNITY_WIDTH%px"></canvas>
    15.       <br>
    16.       <div class="logo"></div>
    17.       <div class="fullscreen"><img src="TemplateData/fullscreen.png" width="38" height="38" alt="Fullscreen" title="Fullscreen" onclick="SetFullscreen(1);" /></div>
    18.       <div class="title">%UNITY_WEB_NAME%</div>
    19.     </div>
    20.     <p class="footer">&laquo; created with <a href="http://unity3d.com/" title="Go to unity3d.com">Unity</a> &raquo;</p>
    21.     %UNITY_WEBGL_LOADER_GLUE%
    22.  
    23.     <script>
    24.       Module.onRuntimeInitialized = function() {
    25.         SendMessage = function(gameObject, func, param) {
    26.           if (param === undefined) {
    27.             if (typeof this.SendMessage_vss != 'function')
    28.               this.SendMessage_vss = Module.cwrap('SendMessage', 'void', ['string', 'string']);
    29.             this.SendMessage_vss(gameObject, func);
    30.           } else if (typeof param === "string") {
    31.             if (typeof this.SendMessage_vsss != 'function')
    32.               this.SendMessage_vsss = Module.cwrap('SendMessageString', 'void', ['string', 'string', 'string']);
    33.             this.SendMessage_vsss(gameObject, func, param);
    34.           } else if (typeof param === "number") {
    35.             if (typeof this.SendMessage_vssn != 'function')
    36.               this.SendMessage_vssn = Module.cwrap('SendMessageFloat', 'void', ['string', 'string', 'number']);
    37.             this.SendMessage_vssn(gameObject, func, param);
    38.           } else
    39.               throw "" + param + " is does not have a type which is supported by SendMessage.";
    40.         }
    41.       };
    42.     </script>
    43.  
    44.   </body>
    45. </html>
     
    prestonmatterport likes this.
  5. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    Hi alex,

    that's a lot of useful hints again! I'll give them a run later, thanks!

    You're right, comparing a cwrap calls of static methods with the SendMessage mechanism isn't fair/meaningful since SM is more than a simple call. I still did it because I don't need the SM overhead in my particular project, but still had/have to deal with the additional delay it created. So yeah, SendMessage is still useful for some cases. I may remove those comparing test or at least document that/why they're not fair.

    edit: did it!
     
    Last edited: Jan 30, 2016
  6. dansav

    dansav

    Joined:
    Sep 22, 2005
    Posts:
    510
    Is there any way to call my own unity functions directly from webGL javascript using cwrap or _(underscore) and some compiler directive like the keepalive directive mentioned above? Maybe some combination of the two techniques?

    I've tried some experiments using cwrap, and _(underscore) calling a function I create but they all fail with a message
    about not being able to find the function because it was probably removed.

    in unity c#
    int int_sqrt(int x){
    return(x*x);
    }
    in unityscript
    function int_sqrt(x:int){
    return x*x
    }

    If I try to use the Emscripten_KeepAlive directive you mentioned above. I get errors such as wrong preprocessor directive, unexpected int, Emscripten_KeepAlive cannot find directive.

    in unity c#
    #include emscripten.h
    int EMSCRIPTEN_KEEPALIVE int_sqrt(intx){
    return(x*x);
    }

     
    Last edited: Mar 26, 2016
  7. dansav

    dansav

    Joined:
    Sep 22, 2005
    Posts:
    510
    @atti
    Your results are amazing and exactly what I need, but I'm not sure I understand how to implement your discovery for my example. I've done mostly unityscript programming. My knowledge of c++ and c# extends only to modifying other people's plugins so I'm pretty confused conceptually about how it works.
     
  8. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    In the first post there's the github link to the demo project. have you tried running it?
    You then basically have to port the C# parts to UnityScript, if you insist, but I have no clew how this is done in detail since I don't use it at all.
     
  9. dansav

    dansav

    Joined:
    Sep 22, 2005
    Posts:
    510
    I tried the project. As I understand it, you basically communicate very quickly from javascript to the plugin because you kept_alive your c++ functions when they were compiled so they are visible by direct communication. You also implemented callbacks for several data types and it's very fast.

    I want to communicate directly from javascript to unity functions I've already created. For example, I can create a C# function that will return the velocity.x of a gameObject. My goal would be to call that function from javascript and get a return value back to javascript. If I call the plugin from javascript, would the plugin call the C# function and then return a value back to the plugin which would then return it to javascript?
     
  10. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    That's how I did it and how I know it should work. Calling C# directly is theoretically possible, but you would have to reverse engineer the C++ code generated by IL2CPP, which is probably hard, maybe not possible without Unity's help.
     
  11. alexsuvorov

    alexsuvorov

    Unity Technologies

    Joined:
    Nov 15, 2015
    Posts:
    327
    Hello dansav.

    atti is right. C# code is not compiled into JavaScript directly. First, il2cpp is used to generate C++ code for your scripts, and only then this generated C++ code is compiled into JavaScript. Even if you preserve your C# function from stripping and name mangling and manage to export it from the module somehow, you still should not rely on its intermediate il2cpp-generated C++ implementation. On the other hand, C plugin will be compiled into JavaScript directly, so you will have full control of the process this way (note that EMSCRIPTEN_KEEPALIVE macro only applies to C/C++ code).

    If I understand correctly, you are planning to perform a computation which requires interleaved C#/JavaScript code execution. Note that in many cases it is also possible to restructure the code in such a way that it will be not JavaScript code calling C# functions, but rather C# code calling JavaScript functions (which would be much easier to implement). Of course this does not apply to the cases where asynchronous JavaScript operations are involved.
     
  12. dansav

    dansav

    Joined:
    Sep 22, 2005
    Posts:
    510
    Thanks for the information. Is atti's solution only feasible for C# or would it also work in UnityScript?
     
  13. alexsuvorov

    alexsuvorov

    Unity Technologies

    Joined:
    Nov 15, 2015
    Posts:
    327
    Not completely sure at the moment if this solution applies to UnityScript, this should be double checked, but before you start using this scheme, you might want to consider possible alternatives.

    Note that in JavaScript you do not have the game object reference, but rather the game object symbolic name. This is exactly the reason why direct call to a static C function can be faster than SendMessage, as SendMessage has to first resolve the symbolic name and check if the found object is active. Therefore if you are planning to use symbolic object names in your JavaScript, you might not get much higher performance than usual SendMessage, unless you implement your own caching mechanism.

    Although SendMessage does not return the result of your computation back to JavaScript, you can still transfer this result to JavaScript explicitly using a helper JavaScript function. Consider the following example:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Runtime.InteropServices;
    4.  
    5. public class Cube : MonoBehaviour {
    6.    [DllImport("__Internal")]
    7.    private static extern void CubeComputation();
    8.    [DllImport("__Internal")]
    9.    private static extern void SetCubePosition (float x, float y, float z);
    10.  
    11.    void SendPositionToJavaScript() {
    12.      SetCubePosition (transform.position.x, transform.position.y, transform.position.z);
    13.    }
    14.  
    15.    void OnMouseDown() {
    16.      CubeComputation ();
    17.    }
    18.  
    19.    void Update() {
    20.      transform.position = new Vector3 (Mathf.Sin (Time.time), Mathf.Cos (Time.time), 0);
    21.    }
    22. }
    and the accompanying JavaScript plugin:
    Code (JavaScript):
    1. var CubePlugin = {
    2.   CubeComputation: function() {
    3.     SendMessage('Cube', 'SendPositionToJavaScript');
    4.     alert('Cube position is (' + _CubeComputation.x + ',' + _CubeComputation.y + ',' + _CubeComputation.z + ')');
    5.   },
    6.   SetCubePosition: function(x, y, z) {
    7.     _CubeComputation.x = x; _CubeComputation.y = y; _CubeComputation.z = z;
    8.   }
    9. };
    10. mergeInto(LibraryManager.library, CubePlugin);
    In this example CubeComputation JavaScript function is calling SendPositionToJavaScript C# method of the Cube game object, which is using helper SetCubePosition JavaScript function to store the result of the computation in the _CubeComputation JavaScript object, and this result (explicitly sent from C#) will be available to the CubeComputation function right after the SendMessage returns. This is of course not as compact as getting result from SendMessage directly, but it is functionally equivalent. Note that I have been using _CubeComputation JavaScript object to store the intermediate result, because this is what the CubeComputation plugin function will be compiled into, but you can also use any other JavaScript object to store the context of the computation. This solution is also not thread safe, but this should not be a problem at the moment, as JavaScript is currently single threaded.
     
    Last edited: Mar 29, 2016
  14. dansav

    dansav

    Joined:
    Sep 22, 2005
    Posts:
    510
    It looks like CubeComputation takes no variables, but then you are adding x,y,z objects to the function itself to store the output? and this output would be available on the webgl webpage javascript. Could you do the same thing with ExternalEval without the plugin. Application.ExternalEval(CubeComputation.x=x;CubeComputation.y=y;CubeComputation.z=z)?
     
  15. dansav

    dansav

    Joined:
    Sep 22, 2005
    Posts:
    510
    SendMessage says there is "no receiver" available when using a large string. As soon as I set the string size to a smaller string it amount it works again. I think in my experiment it was about above 200,000 characters when it started giving the "nor receiver" error. Is there a character limitation on SendMessage?
     
  16. alexsuvorov

    alexsuvorov

    Unity Technologies

    Joined:
    Nov 15, 2015
    Posts:
    327
    .
    It does not really matter which JavaScript object you are using to store the result. For example, you can store the result in window.CubeComputationX = x. Or you can create your own global JavaScript object like var MyResult = {} in advance and then use MyResult.x = x. It will work pretty much the same way. So I just used the _CubeComputation object as an already existing namespace, not to mess with global variables and not to create additional objects (note that in JavaScript functions are in fact objects and can have properties).

    I think Application.ExternalEval should work the same way, just you should use ExternalEval wisely, as it may imply security restriction in some cases (i.e. in Chrome packaged app).
     
    Last edited: Mar 30, 2016
  17. alexsuvorov

    alexsuvorov

    Unity Technologies

    Joined:
    Nov 15, 2015
    Posts:
    327
    I guess I am not aware of such limitation, except the module stack size limit (which is proportional to the total module memory size and should be around a few megabytes), but I can check on this.
    Normally, if you want to transfer large amount of data (i.e. a file) from JavaScript to WebGL application, you can either allocate the data in the global module memory using _malloc (this way you will not be limited by the module stack) and return the string address from the jslib plugin function. Or you can also create a blob in JavaScript, send it's url to WebGL application using SendMessage, and then download it using WWW request.
     
  18. dansav

    dansav

    Joined:
    Sep 22, 2005
    Posts:
    510
    Thanks for the easy to understand and comprehensive explanations about how things work.

    I've been trying to send an array of floats back and forth. I'm converting them to a string and back again. The conversions barely take any time at all, but sending it back and forth using SendMessage is really slow, as the string gets 10x larger it becomes 10 times slower to pass back and forth. atti's solution is about the same speed as your SendMessage speedup. Is there some shared memory that both the webgl webpage javascript and UnityScript could both access?
     
  19. alexsuvorov

    alexsuvorov

    Unity Technologies

    Joined:
    Nov 15, 2015
    Posts:
    327
    Yes, of course. You can interact with the WebGL application memory directly from JavaScript. Use the http://docs.unity3d.com/Manual/webgl-interactingwithbrowserscripting.html to check on the basics (especially PrintFloatArray function seems to be related to your use case). For example, you can allocate a float array in C#, send it's address to JavaScript (which is in fact just an offset on the module heap), and reuse this array in subsequent computations. Just make sure that C# array does not get garbage collected, and also when the array is destroyed, you should notify JavaScript that the heap offset is no longer valid. Or, to be completely safe, you can just reallocate a new array for each interaction with JavaScript, and destroy it after interaction is complete.
     
  20. alexsuvorov

    alexsuvorov

    Unity Technologies

    Joined:
    Nov 15, 2015
    Posts:
    327
    I guess this question is out of the scope of the current discussion, as this thread is mainly about non-standard ways of communication. I believe in your case the standard approach should be sufficient. Could you please create a new thread and move your last question there?
     
  21. intentionperception

    intentionperception

    Joined:
    Jun 13, 2016
    Posts:
    13
    This doc page suggests that a SendMessage call can be made at any time after index.html finishes interpreting JS (in a WebGL build):

    https://docs.unity3d.com/Manual/webgl-interactingwithbrowserscripting.html

    Yet, if I wait for a WebGL Release to finish building, copy all assets to an http server, load its index.html, and then open Chrome Dev Tools console and call SendMessage -- without calling it on a specific object -- then I get an error that it's undefined. There seems to be an unspoken requirement, perhaps related to the Module definition.
     
  22. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    You're right, that the runtime is not ready if you call SendMessage right away, but Emscripten gives you some options here:
    https://kripken.github.io/emscripte...ded-and-it-is-safe-to-call-compiled-functions

    Look at my example project:
    https://github.com/atteneder/UnityJavascriptInteraction

    right after the unity glue code (look at the template's index.html), I include test.js with basically this piece of code:
    Code (JavaScript):
    1. Module.onRuntimeInitialized = function() {
    2. console.log('Unity is ready now');
    3. }
    4.  
    hth,
    atti
     
  23. intentionperception

    intentionperception

    Joined:
    Jun 13, 2016
    Posts:
    13
    Your project solved my problem, thanks!

    Some tips for others who use it:
    1. Let's assume you want to use just one of atti's methods, say, the one for passing json as a string from the html container to the game object. You'd call "c_vxjson" from the html container (see test.js) and this will be received by TargetVxJson in the game object (see Receiver.cs)
    2. You can rename test.js (and update the filename in index.html) but inside of it do not remove any of the entries in Module.onRuntimeInitialized. I replaced everything else with a fn named "onGameReady" (containing the call to c_vxjson), and updated progress.js to call this fn instead of run_tests.
      1. Renaming "c_vv3json" also gave me a problem once, so I reverted that.
    3. Do not alter anything in Receiver.cs either, except the content of the fn you want to use. In the case of TargetVxJson, you may also want to rename the struct it uses for json deserialization (I made it a class with subclasses as well).
    4. You'll want to use Receiver.cs as a script for the game object you want to control from the html container. I haven't gotten this far yet but I expect it to work. I also don't know whether it's possible to dispatch commands to different game objects that each have their own script; I suspect Receiver.cs would need to be adapted to control other game objects even though it's attached to just one.
     
  24. Deleted User

    Deleted User

    Guest

    Hi, I tried your code hoping to solve javascript - webgl unity communication, but, after building from your repository code, I get this error when loading index.html:

    ReferenceError: Can't find variable: Module

    I'm on Unity 5.6 beta, and Safari on Mac (same result with Chrome, tough).

    Do you have an idea of what might cause the error?

    Thanks!
     
  25. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    Yes, when I tested 5.6 I noticed they renamed "Module". Can't remember exactly, but something like "GameInstance".
    Have a look into UnityLoader.js to see exactly or your browser's dev console.

    hth
     
  26. Deleted User

    Deleted User

    Guest

    Pardon my lack of knowledge ... what should I look for, exactly? What was "Module" before?
     
  27. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    I looked a test I did some weeks ago. Just add this at the top of tests.js:

    Code (JavaScript):
    1. if(!window.Module) {
    2.     window.Module = gameInstance.Module;
    3. }
    Module is now inside another object.
     
  28. Deleted User

    Deleted User

    Guest

    it now says: "ReferenceError: Can't find variable: gameInstance"

    This is the index.html generated by your project:

    Code (JavaScript):
    1. <!doctype html>
    2. <html lang="en-us">
    3.   <head>
    4.     <meta charset="utf-8">
    5.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    6.     <title>Unity WebGL Player | JavascriptInteraction</title>
    7.     <script src="progress.js"></script>
    8.     <style>
    9.     /* a style sheet needs to be present for cursor hiding and custom cursors to work. */
    10.     </style>
    11.   </head>
    12.   <body>
    13.     <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" height="200px" width="320px"></canvas>
    14.     %UNITY_WEBGL_LOADER_GLUE%
    15.     <script src="tests.js"></script>
    16.   </body>
    17. </html>
    Should't be somewhere a <script src="Build/UnityLoader.js"/> ?

    Edit: wait a minute, %UNITY_WEBGL_LOADER_GLUE% shouldn't be in index.html in the WebGL build .. so something went wrong when I built the WebGL

    p.s. I have 5.6 beta9
     
    Last edited by a moderator: Mar 2, 2017
  29. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    Last edited: Mar 10, 2017
    LCP likes this.
  30. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    After a quick look: I think not.
    My demo is fast because it's an alternative to SendMessage. The WebSockets plugin doesn't use SendMessage in first place.
    For example receiving (huge chunks of) data: Seems like they cache responses in WebSocket.jslib (socket.messages.push(array)) and poll them from C# via SocketRecv, which directly writes into the heap.
    Now you *could* theoretically invoke a native cwrapped C# function when a message is received (via socket.socket.onmessage), and pass on the content as parameter. But this is probably dangerous, since you cannot know in what state Unity is within its run-loop. So you'd have to synchronize things in C# again.

    My gut tells me it probably cannot get much faster, but feel free to prove me wrong.
     
    swdenis likes this.
  31. swdenis

    swdenis

    Joined:
    Aug 5, 2016
    Posts:
    1
    Thanks atti, I have a multiplayer webgl game and some users have experiencing a major delay with what they should be seeing, not sure where the problem is stemming from exactly.
     
  32. mehul-24

    mehul-24

    Joined:
    Jul 9, 2015
    Posts:
    2
    Does anyone know if there a unity version/patch which included the send message change? Or do i have to still add this to my template?


    Code (JavaScript):
    1. Module.onRuntimeInitialized = function() {
    2.         SendMessage = function(gameObject, func, param) {
    3.           if (param === undefined) {
    4.             if (typeof this.SendMessage_vss != 'function')
    5.               this.SendMessage_vss = Module.cwrap('SendMessage', 'void', ['string', 'string']);
    6.             this.SendMessage_vss(gameObject, func);
    7.           } else if (typeof param === "string") {
    8.             if (typeof this.SendMessage_vsss != 'function')
    9.               this.SendMessage_vsss = Module.cwrap('SendMessageString', 'void', ['string', 'string', 'string']);
    10.             this.SendMessage_vsss(gameObject, func, param);
    11.           } else if (typeof param === "number") {
    12.             if (typeof this.SendMessage_vssn != 'function')
    13.               this.SendMessage_vssn = Module.cwrap('SendMessageFloat', 'void', ['string', 'string', 'number']);
    14.             this.SendMessage_vssn(gameObject, func, param);
    15.           } else
    16.               throw "" + param + " is does not have a type which is supported by SendMessage.";
    17.         }
    18.       };
     
  33. MFKJ

    MFKJ

    Joined:
    May 13, 2015
    Posts:
    264
    Hi folks,

    I am searching a way to return value from sendMessage. Do anyone able to do this? cause it will allow me to work in asynchronous pattern otherwise i have to implement another function in order to get value.??
     
  34. GS_Unity3D

    GS_Unity3D

    Joined:
    Jul 2, 2020
    Posts:
    2
    Hi Folks,
    I am facing issue with UnityJavaScriptInteraction. Please help me out if you any idea.
    exception thrown: TypeError: Module.setValue is not a function
    got error from tests.js file. using unity 2019.4.1 and running @atti https://github.com/atteneder/UnityJavascriptInteraction/tree/unity56

    or is there any other way to call c# method from javascript file?
     
  35. lightmapper

    lightmapper

    Joined:
    Jan 14, 2015
    Posts:
    27
    I was looking at this too recently. I got this working by adding some additional emscrption arguments:

    https://emscripten.org/docs/getting...-typeerror-module-something-is-not-a-function
     
  36. DogNabi

    DogNabi

    Joined:
    Jan 17, 2014
    Posts:
    2
    In EditorTools.cs,
    Replace line.

    PlayerSettings.SetPropertyString("emscriptenArgs","--profiling -s ASSERTIONS=2"~~~

    To

    PlayerSettings.WebGL.emscriptenArgs = "--profiling -s ASSERTIONS=2 -s EXTRA_EXPORTED_RUNTIME_METHODS=['addRunDependency','removeRunDependency','ccall','cwrap','stackTrace','setValue','FS_createPath','FS_createDataFile']";
     
  37. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    Hi all,

    I finally invested the time to update this little demo to 2021.2.

    Observations after the update:
    • Original SendMessage is now actually faster than the wrapped, "optimized" one. Maybe a sign of Unity optimizing it.
    • JSON wrapped native callbacks are now actually slower than SendMessage versions. Maybe the JS runtime detects and optimizes the repeated variable init, or maybe SendMessage really is that good now.
    • The rest is still pretty darn fast
    hth
     
    DerrickBarra and KamilCSPS like this.
  38. Thaina

    Thaina

    Joined:
    Jul 13, 2012
    Posts:
    1,158
    All these process seem like it could be automated in compile time. I mean we could have some way we can add attribute to a static function that will mark that function for unity compiler to pick up and expose it in the unity instance

    In fact, given that `SendMessage` need to give the object name and make a lookup and also need to keep that object to exist in the scene just for listen to message. static function is more convenient and should be the default mode of js interop

    Code (CSharp):
    1. class MyClass
    2. {
    3.     [DllExportToJS("MyClass","MyAdd")] // objectName,functionName
    4.     static int MyAdd(int a,int b)
    5.     {
    6.         return a + b;
    7.     }
    8. }
    Code (JavaScript):
    1.  
    2. var result = unityInstance.Module.MyClass.MyAdd(1,2); // 3
    3.  
    Would you pleased to make these kind of functionality in to unity? Maybe make it be a package in the meantime and make it official integration of framework later
     
    Last edited: Feb 23, 2022
  39. poprev-3d

    poprev-3d

    Joined:
    Apr 12, 2019
    Posts:
    71
    @Thaina totally agree that would be extremely valuable and I think totally doable on the Unity side. Would be nice if some Typescript .d.ts description files were generated as well.
     
    Thaina likes this.
  40. CheitoVilla

    CheitoVilla

    Joined:
    Apr 18, 2018
    Posts:
    2

    Hi, i have an error and i dont know why.
    im using unity 2019.4.19 with your project, but when i make the build and put it on servez this is what it say:
    Code (CSharp):
    1. server started on ::8080 for path: /Users/*****/Downloads/Isobar/Exporter-web-unity/Exporter-web
    2. available on:
    3.    http://localhost:8080
    4.    http://127.*.*.1:8080
    5.    http://192.168.***.**:8080
    6. GET /
    7. GET /Build/%7B%7B%7B%20LOADER_FILENAME%20%7D%7D%7D
    8. GET /tests.js
    9. ERROR: GET /Build/%7B%7B%7B%20LOADER_FILENAME%20%7D%7D%7D [404: does not exist]
    how can i fix it?
     
  41. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    I've updated the project to Unity 2021.2 and it seems like the WebGL template is not backwards compatible.

    Either you use version 2021.2 or newer or you check out an older commit of the project (like this one)

    hth
     
  42. Thaina

    Thaina

    Joined:
    Jul 13, 2012
    Posts:
    1,158
    Are there any possibility to use externref in unity webgl?

    There are many situation we only need to pass javascript object into unity just to call another function in js. Since unity webgl is webassembly. It should already be possible to just use externref in some way didn't we?
     
  43. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    Never heard of that before (I'm not a Web Developer). Do you mind educating me on it?
    How does it relate to the topic of the thread or my demo?
     
  44. Thaina

    Thaina

    Joined:
    Jul 13, 2012
    Posts:
    1,158
    externref is the relatively new feature in webassembly. And just have recently movement for supported in newer version of LLVM and emscripten

    https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md
    https://chromestatus.com/feature/5166497248837632
    https://rustwasm.github.io/wasm-bindgen/reference/reference-types.html
    https://discourse.llvm.org/t/rfc-webassembly-reference-types-in-clang/66939
    https://github.com/emscripten-core/emscripten/pull/15913

    It was a type of wasm to support passing any reference type from javascript to let wasm hold onto it and pass it back to javascript. In my opinion it was similar to IntPtr in C# world that let C# interop with C or C++, but in this case it was for JS and wasm

    I think this thread are the most related topic about this feature. Supporting externref in unity would allow us to interact with javascript object directly and immediately without the need to serialize value. Many API in javascript also required object reference and we currently need to have so many boilerplate to interact with those api from C#
     
  45. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    174
    @Thaina Thanks, that sounds very interesting.

    What kind of Unity support are you looking for? I'd assume that using externref in your code should work out-of-the-box if the Emscripten version in use supports it (can be looked up here). I couldn't quickly figure out in which Emscripten version externref was added though.
     
  46. Thaina

    Thaina

    Joined:
    Jul 13, 2012
    Posts:
    1,158
    externref, if I understand correctly, not really usable in LLVM and emscripten of any current version of unity. It is in the recent version of LLVM and just newest version of emscripten

    I think it would be impossible for unity to use custom version of emscripten. However I think it could be possible for all recent version unity to have C# code directly interact with webassembly code. And maybe have some way to map externref to IntPtr (or System.Object (or JSObject which would be dynamic type))

    And that, I think it was lacking from unity. I don't know if it just not possible or it actually possible but there are nowhere mention it. If it's the latter case I would like to have some sample code or project. And this would unlock the potential of true direct JS/WASM interop from unity C# side

    Also there are already many webassembly library exist in the web. If there is some way we could interop with those library from C# unity itself it could be advantageous to not need to bridge the gap between wasm with js code

    Another approach might be if it possible to write or inject some wasm code into emscripten workflow directly, so we can use externref by bypassing the version of emscripten this way
     
    tteneder likes this.