简体   繁体   中英

Android NDK OpenGL glDeleteTextures causes error: call to OpenGL ES API with no current context

I am working on an android application which renders video from an external camera. The video frames are rendered to the screen through opengl in the NDK layer as we do the communication with the camera in ndk and it is faster. I did not write the code as it came from a 3rd party (the developer of the camera) and they have implemented the method surfaceDestroyed in MyGLSurfaceView as follows:

   @Override
public void surfaceDestroyed(SurfaceHolder holder) {
      Log.d(TAG, "Inside GL surfaceDestroyed" );
       // TODO Auto-generated method stub
      myRenderer.surfaceDestroyed(); //call this to clean up the renderer.  
      super.surfaceDestroyed(holder);
}

Where myRenderer is

 myRenderer = new MyGLRenderer(mContext);

Now in in the renderer the following code was written:

public class MyGLRenderer implements Renderer {
    public void surfaceDestroyed()
    {
        Log.d(TAG, "Inside surfaceDestroyed" );
        /* Note: As per doc, GLSurfaceView kills the renderer and deletes any textures associated with it.
         * However, it does not clean up textures created in NDK. So, we need to do this explicitly.
         */
        mGLAdapter.destroyGlTexturesJni(myRenderer);
    }

Notice the comment that textures created in NDK won't get cleaned up. Is this true? The reason I ask is that I get the following error from this call:

01-19 12:01:19.715: E/libEGL(27208): call to OpenGL ES API with no current context (logged once per thread)

I have put some debugging statements to figure out exactly where in the NDK code the error gets thrown and have found it to be in the destructor for some class called ImageRender (which calls Clean), here is the line that triggers it:

void ImageRender::Clean()
{   
// destruct texture
if (m_unTexture != -1)
    glDeleteTextures( 1,&m_unTexture );
     ....

It looks like they set the m_unTexture (this is the gl context right?) here:

    int ImageRender::InitTexture( const int nWidth, const int nHeight )
    {
// Enable texture 2D first.
glEnable( GL_TEXTURE_2D );
// create texture
glGenTextures( 1,&m_unTexture );
// bind texture to target.
glBindTexture( GL_TEXTURE_2D,m_unTexture );
    ...

and throught the code if they check glGetError and if they don't get GL_NO_ERROR they set it to it to -1 so they know it's invalid.

Ok so my question is do I really need to cleanup the textures created in NDK, and if so, how can I without the error that I have seen. I tried calling using:

    //from MyGLSurfaceView
    @Override
public void surfaceDestroyed(SurfaceHolder holder) {
    Log.d(TAG, "Inside GL surfaceDestroyed" );
            // TODO Auto-generated method stub

      queueEvent(new Runnable() {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            myRenderer.surfaceDestroyed(); //call this to clean up the renderer.    
        }
       });
    super.surfaceDestroyed(holder);
}

to ensure the renderer is called on the opengl thread, but that had the same error.

Any suggestions? By the way the code seems to work fine if I simply don't call this method, which I had done for quite awhile but I was having issues where when I started an activity that used opengl frame it would be initialized with the last frame from a previous opengl activity. It seemed that putting this call back in fixed the problem but only some of the time.

You can not call myRenderer.surfaceDestroyed on UI thread, because it has no OpenGL context. You must use queueEvent, to run surfaceDestroyed on OpenGL thread. The problem with what you have implemented with Runnable is following - queueEvent is non-blocking. So most likely super.surfaceDestroyed(holder) gets called before myRenderer.surfaceDestroyed() and GL context is destroyed before any of your native code is being invoked.

Not sure if this will work, but try blocking UI thread after queueEvent call and wait for Runnable to finish it's job. Something like this:

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    Log.d(TAG, "Inside GL surfaceDestroyed" );

    queueEvent(new Runnable() {
        @Override
        public void run() {
            myRenderer.surfaceDestroyed();
            synchronized (myRenderer) { 
                myRenderer.notify(); 
            }
        }
    });

    synchronized (myRenderer)
    {
        try { myRenderer.wait(100); }
        catch (InterruptedException e) { }
    }

    super.surfaceDestroyed(holder);
}

This will block UI thread for max 100 milliseconds allowing your native code to clean up context. Increase 100 to more if needed.

In worst case - just don't free GL resources yourself. Let the GL context free them for you.

To answer your other question: m_unTexture is the GL texture name (not the GL context). The -1 value appears to just be a default initialization value for the texture name that will not get touched if glGenTextures() fails to allocate a new name for the texture.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM