Possible memory leak in rhino3dm.wasm for JS library

Hello,

We are using rhino3dm to compress and decompress meshes. We ran into an issue the other day where the app crashed with no error message. When using the debugger we could see this error message: Uncaught RuntimeError: memory access out of bounds

The decompress method has never produced an issue like this. We have also used compress before but then there was only one mesh with the same rhino3dm instance. In this situation we compressed 3 meshes with the same instance and on the fourth the error occured.

Our code then looked like this:

// This is any since the library has incorrect type declarations
  private rhinoInstance: any

  private async getRhinoInstance() {
    if (this.rhinoInstance) return this.rhinoInstance

    this.rhinoInstance = await rhino3dm()

    return this.rhinoInstance
  }

  /**
   * Converts a three mesh into a base64 string using draco compression
   */
  async compressThreeMesh(threeMesh: THREE.Mesh): Promise<string> {
    const rhino = await this.getRhinoInstance()

    const bufferGeometry = threeMesh.geometry.clone().rotateX((3 * Math.PI) / 2)
    bufferGeometry.computeVertexNormals()
    let rhinoMesh = rhino.Mesh.createFromThreejsJSON({ data: bufferGeometry })

    const jsonMesh = rhinoMesh.encode()
    rhinoMesh = rhino.CommonObject.decode(jsonMesh)

    const compressed = rhino.DracoCompression.compress(rhinoMesh).toBase64String()

    return compressed
  }

When researching this we found a similar issue with a user of three JS: Unsafe to reuse a Decoder instance · Issue #656 · google/draco · GitHub

We then tested this by creating a new rhino instance for each call:

// This is any since the library has incorrect type declarations
  private rhinoInstance: any

  private async getRhinoInstance() {
    // New instance for every call
    return await rhino3dm()
  }

  /**
   * Converts a three mesh into a base64 string using draco compression
   */
  async compressThreeMesh(threeMesh: THREE.Mesh): Promise<string> {
    const rhino = await this.getRhinoInstance()

    const bufferGeometry = threeMesh.geometry.clone().rotateX((3 * Math.PI) / 2)
    bufferGeometry.computeVertexNormals()
    let rhinoMesh = rhino.Mesh.createFromThreejsJSON({ data: bufferGeometry })

    const jsonMesh = rhinoMesh.encode()
    rhinoMesh = rhino.CommonObject.decode(jsonMesh)

    const compressed = rhino.DracoCompression.compress(rhinoMesh).toBase64String()

    return compressed
  }

With this change we don’t run into this issue anymore.

Now the questions is: What is the best practices for this? Is this solution feasible? Could there be a memory leak in the rhino3dm.wasm binary?

2 Likes

@fraguada Do you know what might cause this?

Best,
Erik