4

I developed an ionic app for IOS using threeJS and PIXI. After choosing a language you can navigate between pages with 3D/2D scenes and play with them.

However, the more you enter in one of these pages, the more likely WKWebView gets reloaded (webViewWebContentProcessDidTerminate gets called). It depends on both the device you're using and the speed you're navigating the app.

After getting some data from CouchBase, my threeJS components load a glb file (2MB ~ 12MB) and, sometimes, a 360° background image (3MB ~ 5MB) added to the scene. In some scenes there's a WebGlTargetRenderer and in all of them, there's an instance of OrbitControls. I'm using a shared renderer avoiding to create too many WebGL contexts. Once the component gets destroyed I dispose everything I can.

/// HELPERS 
const disposeAll = (object: any) => {

  if (!object.isMesh) return;

  object.geometry.dispose();
  if (object.material.isMaterial) {
    cleanMaterial(object.material);
  } else {
    for (const material of object.material) cleanMaterial(material);
  }
}

const cleanMaterial = material => {
  material.dispose();

  if (material.map) {
    material.map.dispose();
    material.map = null;
  }

  material.needsUpdate = true;

   for (const key of Object.keys(material)) {
     const value = material[key]
     if(value?.dispose && typeof value.dispose === "function"){
       console.log("DISPOSING", value)
       value.dispose()
     }
   if (value && typeof value === 'object' && 'minFilter' in value) {
     value.dispose()
   }
  }
}

/////NGDESTROY
 ngOnDestroy() {
    clearInterval(this.interval);
    cancelAnimationFrame(this.requestId);
    this.resourceTracker.dispose();
    this.controls.dispose();
    this.scene.traverse(disposeAll);
    this.webGl.reset(); // RENDERER SERVICE
    this.scene = null;
  }


///// RENDERER SERVICE 
 reset() {
      this.renderer.dispose()
      this.renderer.renderLists.dispose()
      this.renderer.xr.dispose()
      this.renderer.state.reset()
      this.renderer.clear()
      this.renderer.properties.dispose()
}

(regarding resource tracker: https://threejsfundamentals.org/threejs/lessons/threejs-cleanup.html)

I don't get any useful information profiling my app. The memory timeline reaches about 1MB when navigating but then it goes down again. Just "ArrayBuffer" in Javascript allocations increases in size. For sure, it allocates more memory compared to when none of threeJs components gets created.

I'll list my attempts to understand what's going on and some resources I found related to this issue.

According to https://discourse.threejs.org/t/three-js-application-snaps-or-reloads-on-ios-devices/21833 and https://github.com/ionic-team/cordova-plugin-ionic-webview/issues/337, the issue might be related to the overload of the main-thread or a memory leak. Thus, I checked Memory Warnings in Xcode. Sometimes the warning gets logged and the wkwebview reloads, sometimes it doesn't. Sometimes the warning doesn't show up but wkwebview gets reloaded anyway. So, even if ArrayBuffer keeps increasing, I thought It could be a thread-related issue.

What I've already implemented:

• Calling this.zone.ronOutsideAngular( ( ) => this.animate( ) )

• Loading every glb files and backgrounds one after another using a manager await new GLTFLoader(manager(this.loadTransitionPolarizedModel.bind(this))).loadAsync(this.modelUrl); (probably not very useful)

• As I said, using one renderer and cleaning up ThreeJs scenes:

Clean up Threejs WebGl contexts

https://discourse.threejs.org/t/when-to-dispose-how-to-completely-clean-up-a-three-js-scene/1549

https://threejs.org/docs/index.html#manual/en/introduction/How-to-dispose-of-objects

What I tried and then removed because nothing changed:

• Using one shared canvas

• Worried about the increase of "ArrayBuffer", I read this https://github.com/mrdoob/three.js/issues/17089 and I tried to add a local version of threeJS based on the PR of the issue. Then I tried: https://stackoverflow.com/a/61269038/15623292

• Using a shared Scene

• Writing a Cordova plugin which removes the cache and releases memory

What I tested:

• Removing the Glb model, it doesn't crash

• Loading the Glb model but not adding it to the scene. It worked for a while then it crashed again. Then I loaded a much smaller file still without adding it to the scene and it worked (I spent 10 minutes going back and forth between pages)

• Removing renderer.setPixelRatio and renderer.setSize, nothing

If this is a memory-related issue since it gets worse the more I open threeJS or PIXI components, I honestly don't know what else to do. If this is because of the overload of the main thread, I thought of using a Web Worker to split up the computation. Unfortunately, I can't put all scenes in a Web Worker since the OffScreenCanvas API is not supported in safari. Therefore I wouldn't know what to calculate in the worker. I thought I could load the glb file there and then return it to the main thread but I haven't tested it yet. If that is related to IOS and webkit, well, I don't have much to do. I'm really running out of ideas.

I'm not asking you to solve it, but do you guys have any ideas of what it could be done or what I could try? Or, do you have any advice to check what is going on, maybe writing some native code?

Do you know if I can write some Cordova plugin to handle threejs scenes as if it were a js webworker somehow?

Regarding what Xcode (doesn't) say: iOS iPhone 11 flashing black and white while browsler is constantly reloading: RBS Background assertion ConnectionTerminationWatchdog (I get the same error)

Other:

https://bugs.webkit.org/show_bug.cgi?id=194268

https://github.com/mapbox/mapbox-gl-js/issues/4695

https://developer.apple.com/forums/thread/662251

Here's my dependencies & plugins

"dependencies": {
    "@angular/animations": "~11.2.0",
    "@angular/common": "~11.2.0",
    "@angular/core": "~11.2.0",
    "@angular/forms": "~11.2.0",
    "@angular/platform-browser": "~11.2.0",
    "@angular/platform-browser-dynamic": "~11.2.0",
    "@angular/router": "~11.2.0",
    "@capacitor/core": "3.0.1",
    "@ionic-native/core": "^5.32.0",
    "@ionic-native/file": "^5.34.0",
    "@ionic-native/file-transfer": "^5.34.0",
    "@ionic-native/network": "^5.32.1",
    "@ionic-native/safari-view-controller": "^5.34.0",
    "@ionic-native/screen-orientation": "^5.32.0",
    "@ionic-native/splash-screen": "^5.32.0",
    "@ionic-native/status-bar": "^5.32.0",
    "@ionic-native/tealium": "^5.33.1",
    "@ionic/angular": "^5.5.2",
    "@ionic/storage": "^3.0.4",
    "@ionic/storage-angular": "^3.0.6",
    "@ngx-translate/core": "^13.0.0",
    "@ngx-translate/http-loader": "^6.0.0",
    "@types/three": "^0.128.0",
    "cordova-browser": "6.0.0",
    "cordova-plugin-add-swift-support": "2.0.2",
    "es6-promise-polyfill": "^1.2.0",
    "flag-icon-css": "^3.5.0",
    "gsap": "^3.6.1",
    "lodash": "^4.17.21",
    "ng-data-picker": "^0.1.5",
    "ng-packagr": "^11.0.0",
    "pixi-filters": "^3.2.2",
    "pixi.js": "5.3.9",
    "rollbar": "^2.24.0",
    "rxjs": "~6.6.0",
    "three": "^0.128.0",
    "tslib": "^2.0.0",
    "uuid": "^3.4.0",
    "zone.js": "~0.10.2"
}


...
    "cordova-azure-notification-hubs": "^1.0.6",
    "cordova-ios": "^6.2.0",
    "cordova-plugin-couchbase-lite-2.0": "1.0.1",
    "cordova-plugin-device": "2.0.2",
    "cordova-plugin-file": "^6.0.2",
    "cordova-plugin-file-hash": "^0.4.1",
    "cordova-plugin-file-md5": "^0.3.3",
    "cordova-plugin-file-transfer": "^1.7.1",
    "cordova-plugin-ionic-keyboard": "^2.0.5",
    "cordova-plugin-ionic-webview": "^5.0.0",
    "cordova-plugin-network-information": "^2.0.2",
    "cordova-plugin-safariviewcontroller": "^2.0.0",
    "cordova-plugin-screen-orientation": "^3.0.2",
    "cordova-plugin-splashscreen": "^5.0.2",
    "cordova-plugin-statusbar": "^2.4.2",
    "cordova-plugin-whitelist": "1.3.3",
    "cordova-sqlite-storage": "^6.0.0",
....

Paolo
  • 43
  • 4

0 Answers0