Simple-WebXR-Unity icon indicating copy to clipboard operation
Simple-WebXR-Unity copied to clipboard

Shared array breaks after performing memory intensive operations

Open katboi01 opened this issue 1 year ago • 3 comments

Hello, when trying to implement the library, I've come across an issue. My application relies heavily on downloading assets at runtime. I noticed, that when downloading and parsing files, the Enter VR button would sometimes stop working, or the VR view would freeze if a session was already running. By adding console.log() in various parts of the .jslib, I found that _dataArray would become unassigned/set to all zeroes after downloading a file. I think it may be related to garbage collection, or some other memory optimization. I tried using malloc in the .jslib script, and GCHandle in pinned mode in the .cs to ensure that memory does not get moved arround, however, I was unable to work around this issue. I tested the issue in Unity versions 2020.3.48, 2021.3.31 and 2022.3.37. I've come up with a minimal reproduction sample: Scene:

  • single camera with the Script.cs script attached.

Script.cs

using System.Collections.Generic;
using UnityEngine.Networking;
using Newtonsoft.Json.Linq;
using Rufus31415.WebXR;
using System.Collections;
using System.Linq;
using UnityEngine;

public class Script : MonoBehaviour
{
    public string Url = "https://www.tracenacademy.com/api/MobData";
    public List<string> Characters;

    private IEnumerator Start()
    {
        SimpleWebXR.EnsureInstance();
        SimpleWebXR.SessionStart.AddListener(EnableVRMode);

        using (UnityWebRequest www = UnityWebRequest.Get(Url))
        {
            yield return www.SendWebRequest();
            if (www.result != UnityWebRequest.Result.Success)
            {
                Debug.Log(www.error);
            }
            else
            {
                var str = www.downloadHandler.text;
                if (!string.IsNullOrEmpty(str))
                {
                    var array = JArray.Parse(str);
                    Characters = array.Select(line=>line.ToString()).ToList();
                    Debug.Log(Characters.Count);
                }
            }
        }

        Debug.Log("Ready");
    }

    private void EnableVRMode()
    {
        Debug.Log("VR Enabled");
    }
}

If SimpleWebXR.EnsureInstance(); is moved behind the using statement, the issue does not occur, however in my case, files can be downloaded at any time, which causes the shared array to desync.

Url comes from a public repository https://github.com/SimpleSandman/UmaMusumeAPI

There are no errors in the console or anything the like when the problem occurs. I'd be grateful if someone with more knowledge about the implementation could look into this.

katboi01 avatar Nov 16 '24 16:11 katboi01

Hi, thank you for your detailed report and for providing a reproduction sample. From your description, it seems the problem occurs due to shared memory (_dataArray in the .jslib) being modified or deallocated unexpectedly. The symptoms, such as VR session freezing or the Enter VR button becoming unresponsive, suggest a potential conflict between UnityWebRequest, the underlying WebGL memory model, and how SimpleWebXR manages its shared arrays.

Potential Causes

  1. Garbage Collection (GC): As you mentioned, the shared memory could be moved or collected by Unity's GC or the browser’s WebAssembly memory model. If _dataArray is not properly pinned or its lifecycle isn't tightly controlled, operations like downloading assets or parsing JSON may inadvertently trigger issues.

  2. Thread Blocking: While UnityWebRequest operates asynchronously, it might still interfere with the WebGL main thread in specific cases, especially if Unity or the browser tries to allocate/deallocate memory during runtime.

  3. Order of Initialization: Moving SimpleWebXR.EnsureInstance(); after the UnityWebRequest resolves the issue, which suggests that the timing of initialization and resource allocation is critical.

Suggestions and Workarounds

While we investigate further, here are some possible ways to mitigate the issue:

  1. Ensure Proper Memory Handling:

    • Ensure _dataArray in the .jslib is initialized and used consistently. If possible, manually pin it or ensure it's re-initialized before critical VR operations.
    • Avoid accessing or modifying _dataArray during file downloads unless necessary.
  2. Avoid Simultaneous Memory Operations:

    • Consider isolating the asset download process from VR session management to reduce the risk of interference. For example, you could queue asset downloads and pause VR session interactions until they are complete.
  3. Test on Different Browsers:

    • As WebGL behavior can vary between browsers, testing on Chrome, might help identify whether it’s a Unity or browser-specific issue.

Rufus31415 avatar Nov 21 '24 07:11 Rufus31415

Thank you for the reply. From suggested workarounds, I though 2. had a chance to work, but unfortunately, it does not. Ending the session does not clear the initialization flag, so when it is re-enabled, an attempt is made to use the old data array, which has since been re-allocated (I presume). Calling the initialization manually leads to a different error, which I'm still investigating. Overall, there are many static variables that make it difficult to try to re-initialize the VR service. I will update you if I find a different workaround, but it doesn't look promising.

katboi01 avatar Nov 23 '24 18:11 katboi01

I found that a similar problem was addressed in your other repository unity-webxr-export #65. I wasn't able to adapt that solution to this one though, as during the initialization process, many different variables are set in the array, therefore, it's not enough to just re-initialize the arrays when the values get reset. Of course I could just be doing something wrong. I tried:

  • changing all references of _dataArray, _byteArray and _handsArray to Module.*array*, checking the array length at the start of each function and re-initializing them as needed

  • same as above, but calling InitWebXR whenever one of the lengths is incorrect

This resulted in the game showing only white screen, I'm not sure what exactly happened, but it did not work as I'd expect. I also tried pinning the Unity arrays using GCHandle.Alloc();

katboi01 avatar Nov 24 '24 09:11 katboi01