Hey people, I’ve been trying to figure out how I send a byte array from my unity webgl build to my own js code in the browser.
So far I haven’t been able to successfully iterate over my byte array, I’ve tried every HEAP and maybe 20 variations of the float example in the unity manual/api for webgl browser communication and I’ve also tried passing it as a char array but the results are senseless either way.
Could someone here enlighten me on how I’m supposed to get my byte array out of the heap after passing it to the code in my jslib?
Thank you very much for finding a mistake in our docs example, it will be corrected soon.
The correct code should read: console.log(HEAPF32[(array>>2)+i]);
In case of a byte array you can do basically the same, just you don’t need to shift the address, as the size of a HEAP8 or HEAPU8 element is 1 byte, i.e. console.log(HEAPU8[array+i]);
The complete code example would be:
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
public class NewBehaviourScript : MonoBehaviour {
[DllImport("__Internal")]
private static extern void PrintByteArray(byte[] array, int size);
void Start () {
byte[] myArray = {1, 2, 3};
PrintByteArray(myArray, myArray.Length);
}
}
And the plugin:
mergeInto(LibraryManager.library, {
PrintByteArray: function(array, size) {
for(var i = 0; i < size; i++)
console.log(HEAPU8[array + i]);
},
});
Yes the issue in the docs confused me because I found threads where people reported it “working” so we thought we were confusing how >> 2 would work et.c.
SendMessage = function (gameObject, func, param) {
if (param === undefined)
Module.ccall("SendMessage", null, ["string", "string"], [gameObject, func]);
else if (typeof param === "string")
Module.ccall("SendMessageString", null, ["string", "string", "string"], [gameObject, func, param]);
else if (typeof param === "number")
Module.ccall("SendMessageFloat", null, ["string", "string", "number"], [gameObject, func, param]);
else
throw "" + param + " is does not have a type which is supported by SendMessage.";
}
Then SendMessage should provide quite decent performance (assuming that you have to interact with game objects referenced by symbolic name).
In case of static functions it would be of course more efficient to use the mentioned delegate-based interaction, described by atti in the same thread.
Also you might consider other possibilities when dealing with large chunks of data, i.e. allocate a block of shared memory only once and then work with it both from c# and JavaScript (related discussion can be found here http://forum.unity3d.com/threads/trying-to-share-float-array-between-c-and-webgl-browser-javascript.394861/, just note that such implementation would be version specific, but still can be considered in cases when performance is critical)
The problem is with high volume traffic we preferrably don’t want to send strings, and it’s also very small and optimized payload per message which we want to only pass as many bytes as we need where number would I guess be 4 bytes per “float” so it has to be 4 8 12 16 in size instead of 3 or 5 bytes?
Any idea how we modify this example to a byte array? I see “string” and “number”, also javascript really wants uint8 as a “type” as oppose to byte like in C# et.c.
We’re setting up a very basic pipeline for byte compressed data between unitys webgl and js, no need to reference GO’s preferrably just a static method call with payload through parameter~
The basic idea is that you can only send Int32 (Uint32), Float32 and double from JavaScript to asm.js as function parameters. Means, when you want to send a string, you first allocate some block of memory for it on the heap, then convert the string to utf8 and write the data into the allocated block, then send the address of this block (which is in fact just an offset on the heap buffer) to an asm.js function as a parameter. This is exactly what the cwrap function (used in your example for string handling) is doing.
The same for arrays, you only send the offset of the array on the heap to the asm.js function, and offset is just a number, whether it represents a char* or a float*. The main questions therefore are:
Is the transferred array data already present on the heap at the time of the call? In which case it would make sense to send it’s original address and not create additional copy. Arrays can be allocated directly on the module heap, in which case they can be accessed directly both from asm.js and JavaScript. On the contrary, native JavaScript objects (including arrays, except the module heap itself) can only be accessed from JavaScript, but not from asm.js. So, if your array is a native JavaScript array, you will have to make a copy of it on the heap before each asm.js call, which is not very efficient.
If you do have to maintain a native JavaScript array and create a copy of it on the heap before each call, you should carefully consider the copy lifetime. You can allocate it either in the ‘dynamic’ heap memory with Module._malloc (where it can stay for a while), or on the heap ‘stack’ with Runtime.stackAlloc (which is faster, but the array should then be cleaned up after the call).
So, this very much depends on the specific use case.
If you don’t want to deal with heap allocations, C plugins and delegates, consider the reverse call. Specifically, instead of transferring JavaScript array to C#, you can make a call from C# to JavaScript function with a byte[ ] parameter, which the JavaScript function can then fill in. The result of such operation will be the same, while it does not involve any low-level code: