google / filament

Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WebGL2
https://google.github.io/filament/
Apache License 2.0
17.8k stars 1.89k forks source link

Can't release unused graphics memory on Android device #3184

Closed memostark closed 4 years ago

memostark commented 4 years ago

I'm using Sceneform 1.16 which leverages Filament 1.7.0 My main activity is a simple list, when I click an item, it opens a new activity with a fragment with AR and loads some GLB models when certain image is detected (some models are up to 15mb in size) however after closing the activity the graphics memory still there. This quickly becomes a problem after I open a couple of items because the app crashes, I assume because of the memory.

I expected the memory to be automatically freed when the activity is closed. I tried to do that manually using Sceneform's SceneView.destroyAllResources(), digging through the code I found out it calls Filament RenderableManager's destroy method for each Renderable Instance, however when I use this method the app crashes.

Fatal Exception: java.lang.IllegalStateException: Object couldn't be destoyed (double destroy()?)
       at com.google.android.filament.Engine.assertDestroy(Engine.java:663)
       at com.google.android.filament.Engine.destroyIndirectLight(Engine.java:532)
       at com.google.ar.sceneform.rendering.FilamentEngineWrapper.destroyIndirectLight(FilamentEngineWrapper.java:136)
       at com.google.ar.sceneform.rendering.Renderer.setLightProbe(Renderer.java:354)
       at com.google.ar.sceneform.Scene.setLightProbe(Scene.java:196)
       at com.google.ar.sceneform.Scene.setLightEstimate(Scene.java:486)
       at com.google.ar.sceneform.ArSceneView.updateNormalLightEstimate(ArSceneView.java:695)
       at com.google.ar.sceneform.ArSceneView.updateLightEstimate(ArSceneView.java:502)
       at com.google.ar.sceneform.ArSceneView.onBeginFrame(ArSceneView.java:473)
       at com.google.ar.sceneform.SceneView.doFrameNoRepost(SceneView.java:333)
       at com.google.ar.sceneform.SceneView.doFrame(SceneView.java:317)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:997)
       at android.view.Choreographer.doCallbacks(Choreographer.java:797)
       at android.view.Choreographer.doFrame(Choreographer.java:728)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:984)
       at android.os.Handler.handleCallback(Handler.java:883)
       at android.os.Handler.dispatchMessage(Handler.java:100)
       at android.os.Looper.loop(Looper.java:237)
       at android.app.ActivityThread.main(ActivityThread.java:8019)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)

I tried using this method both before and after destroying the activity and I'm getting the same problem, though sometimes the "Object couldn't be destoyed (double destroy()?)" error was caused at com.google.android.filament.Engine.destroyTexture(Engine.java:581)

You can see the graphics memory takes the most with 1.2 GB, afterwards the app crashes with no stacktrace. Captura de pantalla (4)

I also tried updating filament to 1.8.1 but still have the same problems

Sorry if this is not the place to post this, I'm not sure if this is a bug or I'm not using Sceneform and Filament properly.

romainguy commented 4 years ago

This seems like a bug in Sceneform. If I recall correctly you are supposed to call SceneView.destroy() from your Activity's onDestroy() callback. destroyAllResources() is a static that you should probably only use when your application terminates.

memostark commented 4 years ago

I forgot to mention that my activity has an ArFragment which calls SceneView.destroy() whenever is destroyed, just for trying I called that method from my Activity's onDestroy() but the memory is still not released.

I also tried using FilamentAsset.releaseSourceData() for each RenderableInstance but still no luck.

memostark commented 4 years ago

As Romainguy said, it was a Scenform 1.16 bug. For anyone that has the same problem, I found a workaround:

Go to Sceneform's RenderableInstance and make Filament's AssetLoader a member variable

public class RenderableInstance {

AssetLoader loader;

void createFilamentAssetModelInstance() {
  ....
  loader = new AssetLoader( ....
}

}

Create this new method and use the loader to destroy the assets

public void destroyAsset(){
    if(filamentAsset != null) {
      loader.destroyAsset(filamentAsset);
      filamentAsset = null;
    }
  }

Now you can call this method whenever you need to release the resources, in my case I call it from a custom AnchorNode class that has a node with a RenderableInstance