Recursion error on clicking when there are two meshes directly under pointer

I have tried desperately to reproduce the exact series of events that lead to this outside our application, but I have been unable to. However, i can tell you what is happening from a code standpoint.

We are using a number of meshes with PointerDragBehaviors on them. If both meshes are at the same location, the following occurs.

  1. a PointerInfo gets generated
  2. This calls .pickInfo, and since there is no _pickInfo yet, that calls _generatePickInfo
  3. this line gets called: this._pickInfo = this._inputManager._pickMove(this.event);
  4. The pickMove callback ends up triggering more behaviors which ends up calling .pickInfo AGAIN
  5. Since we’re still in the callstack of this._inputManager._pickMove(this.event), this._pickInfo has not been assigned yet, so it drops into _generatePickInfo AGAIN (effectively recursion)
  6. This completes… which then sets _inputManager to null.
  7. The recursion is done, and so the first call of this._inputManager._pickMove completes and assigns to this._pickInfo.
  8. It then attempts to call this._inputManager._setRayOnPointerInfo(this._pickInfo, this.event);… but inputManager has been set to null and everything breaks.

We have tried and tried to figure out how we are causing this “double calling” of .pickInfo, and it only seems to happen when two items are directly on top of each other.

  1. Does anyone have any tips on how to find the culprit based on your knowledge of how babylonjs creates these PointerInfo and drag behaviors?
  2. We patched locally by doing the following, is that a viable solution for us to put into babylonjs to safeguard against these situations?:
_generatePickInfo() {
        if (this._inputManager) {
            this._pickInfo = this._inputManager._pickMove(this.event);
            if(!this._inputManager) {
                return;
            }
            this._inputManager._setRayOnPointerInfo(this._pickInfo, this.event);
            this._inputManager = null;
        }
    }

Stack trace below

pointerEvents.js:124
 Uncaught TypeError: Cannot read properties of null (reading '_setRayOnPointerInfo')
    at PointerInfo._generatePickInfo (pointerEvents.js:124:32)
    at get meshUnderPointer (scene.inputManager.js:107:35)
    at get meshUnderPointer (scene.js:1495:35)
    at ActionEvent.CreateNew (actionEvent.js:47:78)
    at InputManager._processPointerUp (scene.inputManager.js:393:114)
    at eval (scene.inputManager.js:805:22)
    at InputManager._initClickEvent (scene.inputManager.js:537:29)
    at InputManager._onPointerUp (scene.inputManager.js:743:18)
    at Observer.eval [as callback] (scene.inputManager.js:861:34)
    at Observable.notifyObservers (observable.js:294:49)

cc @amoebachant

Thanks for reaching out, I’m looking into this!

Hi @Jon_Jekeli , I tried building a repro but haven’t been able to get into this state where the PointerInfo doesn’t have _pickInfo yet and the _pickMove() ends up triggering another call to _generatePickInfo(). I do agree that if there is a reentrancy case here, then _inputManager could indeed get nulled out and the call to _setRayOnPointerInfo() would fail.

Your proposed check seems like a reasonable safeguard against that case. I’d like to ensure there are no other consequences to such reentrancy though, so if you could provide a repro playground I’d appreciate it.

Thanks!

We tried for a couple days to create a repro playground, and unfortunately we werent able to and had to move on to other tasks. I can try and spend some more time on it; my best guess is that its because we have a few instances where we are calling startDrag manually? I tried various playgrounds where we called start drag from within other drag events and that all seemed fine.

1 Like