sceneform-android icon indicating copy to clipboard operation
sceneform-android copied to clipboard

MissingGlContextException when opening another screen from AR

Open jakub-cosmose opened this issue 2 years ago • 9 comments

I have a Flutter app the uses ArCore with com.gorisse.thomas.sceneform:sceneform:1.20.4 (I checked also 1.20.5). When I open the AR view, open another screen from it and go back, the app crashes with:

E/native  ( 1062): ARCoreError: third_party/arcore/ar/core/session.cc:1740
E/native  ( 1062):  at third_party/arcore/ar/core/session.cc:1740 [type.googleapis.com/util.ErrorSpacePayload='ArStatusErrorSpace::AR_ERROR_MISSING_GL_CONTEXT']
E/native  ( 1062): === Source Location Trace: ===
E/native  ( 1062): third_party/arcore/ar/core/session.cc:1740
E/native  ( 1062): 
E/native  ( 1062): ################### Undecorated Trace End  #################
E/native  ( 1062): 
D/AndroidRuntime( 1062): Shutting down VM
E/AndroidRuntime( 1062): FATAL EXCEPTION: main
E/AndroidRuntime( 1062): Process: co.cosmose.dealhunter, PID: 1062
E/AndroidRuntime( 1062): com.google.ar.core.exceptions.MissingGlContextException
E/AndroidRuntime( 1062): 	at java.lang.reflect.Constructor.newInstance0(Native Method)
E/AndroidRuntime( 1062): 	at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
E/AndroidRuntime( 1062): 	at com.google.ar.core.Session.throwExceptionFromArStatus(Session.java:16)
E/AndroidRuntime( 1062): 	at com.google.ar.core.Session.nativeUpdate(Native Method)
E/AndroidRuntime( 1062): 	at com.google.ar.core.Session.update(Session.java:2)
E/AndroidRuntime( 1062): 	at com.google.ar.sceneform.ArSceneView.onBeginFrame(ArSceneView.java:463)
E/AndroidRuntime( 1062): 	at com.google.ar.sceneform.SceneView.doFrameNoRepost(SceneView.java:466)
E/AndroidRuntime( 1062): 	at com.google.ar.sceneform.SceneView.doFrame(SceneView.java:450)
E/AndroidRuntime( 1062): 	at com.google.ar.sceneform.ArSceneView.doFrame(ArSceneView.java:552)
E/AndroidRuntime( 1062): 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1042)
E/AndroidRuntime( 1062): 	at android.view.Choreographer.doCallbacks(Choreographer.java:839)
E/AndroidRuntime( 1062): 	at android.view.Choreographer.doFrame(Choreographer.java:771)
E/AndroidRuntime( 1062): 	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1030)
E/AndroidRuntime( 1062): 	at android.os.Handler.handleCallback(Handler.java:873)
E/AndroidRuntime( 1062): 	at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 1062): 	at android.os.Looper.loop(Looper.java:201)
E/AndroidRuntime( 1062): 	at android.app.ActivityThread.main(ActivityThread.java:6864)
E/AndroidRuntime( 1062): 	at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 1062): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
E/AndroidRuntime( 1062): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)

The crash occurs only when the screen has the CardFormField widget from flutter_stripe, which uses ReactNative underneath.

jakub-cosmose avatar Mar 07 '22 11:03 jakub-cosmose

We are also having same kind of MissingGlContextException in a similar case.

We are showing the AR Scene view in one scene and when user go back (finishing the activity) and again coming back to the same activity, this error is appearing. I tried to clear the session and all, but doesn't fixes the crash.

monsterbrain avatar Mar 08 '22 11:03 monsterbrain

@monsterbrain do you have a native Android app or a Flutter app? Can you provide a simple app to reproduce the problem? I my case the crash occurs only in some very specific setup (Flutter app and opening a screen from a Flutter Stripe library that uses a particular widget type), but if there's an easier way to reproduce that, that may give some hints about the problem reason. I also tried doing some clearing, even closing the AR screen completely and opening it again from scratch, but nothing worked.

jakub-cosmose avatar Mar 08 '22 11:03 jakub-cosmose

@jakub-cosmose We have a Native Android app with AR scenes in multiple activities that get switched ..

monsterbrain avatar Mar 08 '22 17:03 monsterbrain

After a long night of debugging my colleagues created a work-around for the problem in our app. The reason of the problem is that there's the assumption in Sceneform that nothing will touch the singleton EGL context. Unfortunately, some code used by the Stripe library cleared the singleton. The work-around is to save the EGL context every time the activity is paused and restore it every time it's resumed. Saving can be done by storing the value of EGL14.eglGetCurrentContext() and then setting the singleton context to EGL14.EGL_NO_CONTEXT using EGL14.eglMakeCurrent. Restoring can be done with the EGL14.eglMakeCurrent call. There's also one other problem with Sceneform: the method ArSceneView.destroy() doesn't call renderer?.dispose(), so it has to be called manually every time the ArSceneView.destroy() is called.

jakub-cosmose avatar Mar 09 '22 07:03 jakub-cosmose

Well done!!! Could you create a PR with it?

ThomasGorisse avatar Mar 09 '22 07:03 ThomasGorisse

I'm not sure if my code will work in all cases, so I'd need to make much more testing before making a PR. I can't promise that I'll be able to do it any time soon but I'll try. My application has a single screen with AR, so I just save the EGL context using static variables. It might or might not work for more complex apps. Below I provide the whole code that I used for my work-around:

When ArSceneView is created or resumed:

    private fun restoreEglContext() {
        if (Looper.getMainLooper().thread != Thread.currentThread()) {
            throw IllegalStateException("restoreEglContext called from non-UI thread")
        }
        debugLog("Restoring EGL context")
        if (savedContext != null && savedContext != EGL14.EGL_NO_CONTEXT) {
            if (!EGL14.eglMakeCurrent(savedDisplay, savedDrawSurface, savedReadSurface, savedContext)) {
                debugLog("Failed to restore")
            }
        } else {
            debugLog("Nothing to restore")
        }
    }

When ArSceneView is paused:

    private fun saveEglContext() {
        if (Looper.getMainLooper().thread != Thread.currentThread()) {
            throw IllegalStateException("saveEglContext called from non-UI thread")
        }
        debugLog("Saving EGL context")
        val currentContext = EGL14.eglGetCurrentContext()
        if (currentContext == null || currentContext == EGL14.EGL_NO_CONTEXT) {
            debugLog("Nothing to save")
        } else {
            savedContext = currentContext
            savedDisplay = EGL14.eglGetCurrentDisplay()
            savedDrawSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW)
            savedReadSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_READ)
            EGL14.eglMakeCurrent(
                savedDisplay,
                EGL14.EGL_NO_SURFACE,
                EGL14.EGL_NO_SURFACE,
                EGL14.EGL_NO_CONTEXT
            )
        }
    }

When ArSceneView is destoyed:

arSceneView?.renderer?.dispose()

Static variables to store the context:

    companion object {
        private var savedContext: EGLContext? = null
        private var savedDisplay: EGLDisplay? = null
        private var savedReadSurface: EGLSurface? = null
        private var savedDrawSurface: EGLSurface? = null
    }

EDIT: I actually don't know if the approach in my work-around is something that should be used in the library. Probably the EGL14.eglMakeCurrent should be called internally for every frame.

jakub-cosmose avatar Mar 09 '22 11:03 jakub-cosmose

Thanks! Quick question: why are you using sceneform-android instead of sceneview-android if you are in kotlin?

ThomasGorisse avatar Mar 09 '22 12:03 ThomasGorisse

why are you using sceneform-android instead of sceneview-android if you are in kotlin?

I don't use it directly, but through the library https://pub.dev/packages/arcore_flutter_plugin It originally uses the abandoned Sceneform. I've created a local fork and migrating it to sceneform-android required far less changes than to sceneview-android. You can see my not fully successful attempt for the migration here: https://github.com/giandifra/arcore_flutter_plugin/issues/166

jakub-cosmose avatar Mar 09 '22 12:03 jakub-cosmose

I'm not sure if my code will work in all cases, so I'd need to make much more testing before making a PR. I can't promise that I'll be able to do it any time soon but I'll try. My application has a single screen with AR, so I just save the EGL context using static variables. It might or might not work for more complex apps. Below I provide the whole code that I used for my work-around:

When ArSceneView is created or resumed:

    private fun restoreEglContext() {
        if (Looper.getMainLooper().thread != Thread.currentThread()) {
            throw IllegalStateException("restoreEglContext called from non-UI thread")
        }
        debugLog("Restoring EGL context")
        if (savedContext != null && savedContext != EGL14.EGL_NO_CONTEXT) {
            if (!EGL14.eglMakeCurrent(savedDisplay, savedDrawSurface, savedReadSurface, savedContext)) {
                debugLog("Failed to restore")
            }
        } else {
            debugLog("Nothing to restore")
        }
    }

When ArSceneView is paused:

    private fun saveEglContext() {
        if (Looper.getMainLooper().thread != Thread.currentThread()) {
            throw IllegalStateException("saveEglContext called from non-UI thread")
        }
        debugLog("Saving EGL context")
        val currentContext = EGL14.eglGetCurrentContext()
        if (currentContext == null || currentContext == EGL14.EGL_NO_CONTEXT) {
            debugLog("Nothing to save")
        } else {
            savedContext = currentContext
            savedDisplay = EGL14.eglGetCurrentDisplay()
            savedDrawSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW)
            savedReadSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_READ)
            EGL14.eglMakeCurrent(
                savedDisplay,
                EGL14.EGL_NO_SURFACE,
                EGL14.EGL_NO_SURFACE,
                EGL14.EGL_NO_CONTEXT
            )
        }
    }

When ArSceneView is destoyed:

arSceneView?.renderer?.dispose()

Static variables to store the context:

    companion object {
        private var savedContext: EGLContext? = null
        private var savedDisplay: EGLDisplay? = null
        private var savedReadSurface: EGLSurface? = null
        private var savedDrawSurface: EGLSurface? = null
    }

EDIT: I actually don't know if the approach in my work-around is something that should be used in the library. Probably the EGL14.eglMakeCurrent should be called internally for every frame.

I added this code in my sceneView(not arSceneView) , but I am not getting the glcontext. I am getting EGL14.eglGetCurrentContext() as EGL14.EGL_NO_CONTEXT

Is there any limitations for getting context on sceneView?

Arun1947 avatar Jun 08 '22 07:06 Arun1947

I'm not sure if my code will work in all cases, so I'd need to make much more testing before making a PR. I can't promise that I'll be able to do it any time soon but I'll try. My application has a single screen with AR, so I just save the EGL context using static variables. It might or might not work for more complex apps. Below I provide the whole code that I used for my work-around: When ArSceneView is created or resumed:

    private fun restoreEglContext() {
        if (Looper.getMainLooper().thread != Thread.currentThread()) {
            throw IllegalStateException("restoreEglContext called from non-UI thread")
        }
        debugLog("Restoring EGL context")
        if (savedContext != null && savedContext != EGL14.EGL_NO_CONTEXT) {
            if (!EGL14.eglMakeCurrent(savedDisplay, savedDrawSurface, savedReadSurface, savedContext)) {
                debugLog("Failed to restore")
            }
        } else {
            debugLog("Nothing to restore")
        }
    }

When ArSceneView is paused:

    private fun saveEglContext() {
        if (Looper.getMainLooper().thread != Thread.currentThread()) {
            throw IllegalStateException("saveEglContext called from non-UI thread")
        }
        debugLog("Saving EGL context")
        val currentContext = EGL14.eglGetCurrentContext()
        if (currentContext == null || currentContext == EGL14.EGL_NO_CONTEXT) {
            debugLog("Nothing to save")
        } else {
            savedContext = currentContext
            savedDisplay = EGL14.eglGetCurrentDisplay()
            savedDrawSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW)
            savedReadSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_READ)
            EGL14.eglMakeCurrent(
                savedDisplay,
                EGL14.EGL_NO_SURFACE,
                EGL14.EGL_NO_SURFACE,
                EGL14.EGL_NO_CONTEXT
            )
        }
    }

When ArSceneView is destoyed:

arSceneView?.renderer?.dispose()

Static variables to store the context:

    companion object {
        private var savedContext: EGLContext? = null
        private var savedDisplay: EGLDisplay? = null
        private var savedReadSurface: EGLSurface? = null
        private var savedDrawSurface: EGLSurface? = null
    }

EDIT: I actually don't know if the approach in my work-around is something that should be used in the library. Probably the EGL14.eglMakeCurrent should be called internally for every frame.

I added this code in my sceneView(not arSceneView) , but I am not getting the glcontext. I am getting EGL14.eglGetCurrentContext() as EGL14.EGL_NO_CONTEXT

Is there any limitations for getting context on sceneView?

Thanks for sharing this. It fixed the issue.

ali-waris avatar Dec 16 '22 06:12 ali-waris

Adding on to this issue,

In our case, in activity A, there was a camera view and Sceneview (not AR view). And when opening the ARSceneview activity, this crash is occurring.

It seems to have something to do with the Camera causing the GIContextException.

When we removed the Camera view (or change it to compatibility mode) the crash was gone.

Hope it helps someone.

monsterbrain avatar Dec 28 '22 04:12 monsterbrain

This workaround helped prevent the crash from happening but it breaks the EGL state as there's only a black screen if it does restore.

emitchel avatar Aug 15 '23 16:08 emitchel

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. Thank you for your contributions.

github-actions[bot] avatar Nov 14 '23 05:11 github-actions[bot]

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.

github-actions[bot] avatar Nov 23 '23 05:11 github-actions[bot]