Arrays shared between C# and jslib in WebGL - buffer undefined in 2023.3.0a6

An issue encountered going from 2022->2023 and the solution that worked for me.

Without doing this, the browser would crash due to buffer being undefined.

In 2022 LTS and earlier when sharing a float[ ] between C# and jslib, we’d have a line in a jslib like this:

.. = new Float32Array(buffer, byteOffset, length);

Finally did get it working again by changing it like this (…tested in Windows 11 Chrome, Edge, Firefox):

new Float32Array(myUnityInstanceReference.asm.memory.buffer, byteOffset, length);

I’m guessing it’s not broken due to something specific in my project because the code was working for ages prior to 2023.x. Happy to be proven wrong, but not interested in testing further because it’s working now, enough time wasted already trying things until success with the above - but if anyone else runs into this, try same approach and good luck :slight_smile:

So it turns out the above workaround only works in Shorter Build Time builds (I also tested Disk Size, Runtime Speed and Runtime Speed with LTO). When I first posted, that was the kind I was testing with… lucky break I guess (sort of)!

I’ve created a repro and submitted a bug report. IN-55971

Here’s what the repro shows in Javascript console in 2022 LTS/earlier (Chrome, Windows 11):
9369710--1310429--Result_in_2022.png

And in 2023, the crash that happens instead:
9369710--1310432--Result_in_2023.png

The repro consists of the 3D URP template, plus a script and jslib, the above images, a text file with some info and a scene which has the following Example.cs on a GameObject:

using UnityEngine;
using System.Runtime.InteropServices;
public class Example : MonoBehaviour
{
  [DllImport("__Internal")]
  public static extern void JS_Example(float[] array, int length);

  float[] data = new float[] { 1, 2, 3 };
  void Start()
  {
#if UNITY_EDITOR || !UNITY_WEBGL
    Debug.Log("This example must be built and run in a web browser.");
#else
    Debug.Log("Values before jslib function: " + data[0].ToString() + ", " + data[1].ToString() + ", " + data[2].ToString());
    JS_Example(data, 3);
    Debug.Log("Values after jslib function: " + data[0].ToString() + ", " + data[1].ToString() + ", " + data[2].ToString());
#endif
  }
}

The Example.jslib:

mergeInto(LibraryManager.library, {

  JS_Example: function(byteOffset, length) {

    // in 2023.3.0a6, crashes due to buffer being undefined:
  
    var test = new Float32Array(buffer, byteOffset, length);

    test[0] = 3;
    test[1] = 2;
    test[2] = 1;

  }

});

@jukka_j @unityruba

Edit: Also in 2023.3.0a7

Updated by email: “Your bug report ReferenceError is thrown when sharing an array between C# and JavaScript in WebGL has been confirmed and transferred to the appropriate internal development team at Unity.”

Thanks - hopefully it’s easy to fix! :slight_smile:

UUM-53149

This breakage was caused by this PR: Remove unneeded `buffer` global by sbc100 · Pull Request #18454 · emscripten-core/emscripten · GitHub, where Google developers of Emscripten removed the ‘buffer’ variable as unnecessary.

Instead of referencing

new Float32Array(myUnityInstanceReference.asm.memory.buffer, byteOffset, length);

try referencing

new Float32Array(wasmMemory.buffer, byteOffset, length);

or

new Float32Array(HEAP8.buffer, byteOffset, length);

We are hesitant to fix this regression, because it would be akin to forking Emscripten for Unity’s use (which we have tried not to do). Would the above fix work?

1 Like

Thanks for looking into this, I’ll test your recommendations today and post here again by this evening at the latest.

Confirmed in 2023.3.0a11:

WORKS: wasmMemory.buffer, Shorter Build Time
WORKS: wasmMemory.buffer, Runtime Speed
WORKS: wasmMemory.buffer, Runtime Speed with LTO
WORKS: wasmMemory.buffer, Disk Size
WORKS: wasmMemory.buffer, Disk Size with LTO

WORKS: HEAP8.buffer, Shorter Build Time
WORKS: HEAP8.buffer, Runtime Speed
WORKS: HEAP8.buffer, Runtime Speed with LTO
WORKS: HEAP8.buffer, Disk Size
WORKS: HEAP8.buffer, Disk Size with LTO

Lastly, tested quickly if using “buffer” by itself again still crashed the page (to confirm my jslib changes weren’t ignored due to caching somehow throughout the above process), which it did.

So… looks like 100% good to go as-is, issue can be closed, I’m changing this to resolved and will post a link to this thread where this technique was mentioned and I learned it from, so anyone else following that will be aware of the change and hopefully won’t report it again when they upgrade to 2023 - thanks again! :slight_smile:

Thanks for this bug report man!..Now I’m just wondering, is wasmMemory.buffer and HEAP8.buffer both functionality equivalent to the old buffer ? And if they are, more importantly, do they also work on older Unity versions? I guess it’s just easier to replace buffer with wasmMemory.buffer or HEAP8.buffer if they work on all Unity versions.

@jukka_j do these methods work on Unity 2019 and higher, and are they equivalent to just using buffer on Unity < 2023?

1 Like

Also, @jukka_j based on the link below, I see there are 8 kinds of “views” into memory including HEAP8 - out of curiosity when I did the above tests I also tested a couple of different build types using HEAP32 and those worked as well. I’m just going to use wasmMemory personally, but for some additional clarity, is there any situation where we’d want to use HEAP8 instead, or where we’d want to use another of the types? I currently assume .buffer of wasmMemory and .buffer of all 8 types are effectively the same, but my confidence in that assumption isn’t 100%
https://emscripten.org/docs/api_reference/preamble.js.html#type-accessors-for-the-memory-model

1 Like

“HEAP8.buffer” will work back to the dawn of time of Emscripten. “wasmMemory.buffer” might not work with older Emscripten/Unity versions, like 2018/2019 or so, but will work in all Unity versions starting from circa 2020.x forwards. (I don’t exactly recall what was the version that wasmMemory was introduced)

All of these “xxx.buffer” are exact aliases to each other, and all reference the same original global buffer variable. So whether you use HEAP8.buffer or HEAP32.buffer does not matter.

(however naturally using the appropriate views is important, so HEAP8 when you have a signed 8-bit access, and HEAPU32 when you have an unsigned 32-bit access, etc)

3 Likes

@jukka_j @adamgolden It seems that Module._malloc is also now undefined. What’s the alternative? This is a huge deal malloc doesn’t work anymore T_T.

EDIT

@jukka_j Maybe it’s a bug and I need to report it? Or is it a part of the Emscripten update?

I have several WebGL game builds that use _malloc in jslibs (without the Module. before it). My latest are using 2023.3.0a16, but I haven’t experienced any issues with previous versions either.

1 Like

Try with Unity 2023.2.0f1. It doesn’t work. Or maybe it’s the Module. that doesn’t work anymore and only _malloc without Module. works. I have to try it as well.

EDIT

So…the issue was the preceding Module. On Unity 2023 and later, just _malloc can be used. Thanks @adamgolden .

Also worth noting… if you have the web assembly table enabled

all the regular dyncalls will stop working with an error saying they are undefined…this means that you can’t mix webgl plugins using the old dyncall system with the new system.

Which is very bad and makes the new system basically useless, because I can’t make a plugin that will be incompatible with every other webgl plugin on the store.

1 Like