androidkotlinopengl-esglsurfaceview

How to implement GLSurfaceView.EGLContextFactory in Android application to create an OpenGL ES context so that we can check the OpenGL ES version?


My question is How could I print the maximum OpenGL ES version supported by a physical device by implementing GLSurfaceView.EGLContextFactory ?

I was following a tutorial here on how to setup the environment in an Android application using OpenGL ES.

The way I structure my code is exactly like the tutorial tells me.

  1. My Main Activity is setup like this class MainActivity: Activity() whereby Activity is from android.app.Activity package.

  2. Inside MainActivity there is an instance of GLSurfaceView from android.opengl.GLSurfaceView package.

  3. A class named MyGLSurfaceView is created to implement or inherits from GLSurfaceView. Inside it, it has an instance of GLSurfaceView.Renderer.

  4. A class named MyGLRenderer is created to implement or inherits from GLSurfaceView.Renderer.

  5. A class named Model is created in the same package with the required OpenGL ES class to create the environment.

so, Model class needs to access Android Context from android.content.Context package to do some work, like to access a file.

In here, it tells me to write:

private const val EGL_CONTEXT_CLIENT_VERSION = 0x3098
private const val glVersion = 3.0
private class ContextFactory : GLSurfaceView.EGLContextFactory {

override fun createContext(egl: EGL10, display: EGLDisplay, eglConfig: EGLConfig): 
EGLContext {

    Log.w(TAG, "creating OpenGL ES $glVersion context")
    return egl.eglCreateContext(
            display,
            eglConfig,
            EGL10.EGL_NO_CONTEXT,
            intArrayOf(EGL_CONTEXT_CLIENT_VERSION, glVersion.toInt(), EGL10.EGL_NONE)
    ) // returns null if 3.0 is not supported
    }
}

But, I still do not understand how to implement this code. Because, I already created OpenGL ES context inside the constructor of MyGLSurfaceView like so:

init {

    // Create an OpenGL ES 2.0 context
    setEGLContextClientVersion(2)

    renderer = MyGLRenderer(context)
    
    // Set the Renderer for drawing on the GLSurfaceView
    setRenderer(renderer)
    }

It says there, in the documentation that I must create the OpenGL ES context first before reading the version that the device can support, like the maximum version it can run.

The problem is GLSurfaceView.EGLContextFactory is a static interface which means it is a global or shared object and need to be implemented first, i.e inherits from that class and overriding the createContext() method and destroyContext().

How should I implement GLSurfaceView.EGLContextFactory? should I declare it as nested class of MyGLSurfaceView.

I have tried like this by creating a nested class called ContextFactory which implements the interface GLSurfaceView.EGLContextFactory:

private const val EGL_CONTEXT_CLIENT_VERSION=0x3098
private const val glVersion=3.0
const val TAG="ContextFactory"
class MyGLSurfaceView(context: Context):GLSurfaceView(context) {
private val renderer: MyGLRenderer

init {

    // Create an OpenGL ES 2.0 context
    setEGLContextClientVersion(2)

    renderer = MyGLRenderer(context)
    //renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
    // Set the Renderer for drawing on the GLSurfaceView
    setRenderer(renderer)
    glGetString(glVersion.toInt())
    //I am assuming `ContextFactory` has already been instantiated since it implements
    //a `static interface` so, I do not need to create another instance of it here in 
    // the constructor. So, I just directly trying to read `maximum OpenGL ES version` 
    //supported in my device.
    }

but in the logcat inside warn category it says:

Rcall to OpenGL ES API with no current context (logged once per thread) Reading a NULL string not supported here.

MyGLSurfaceView class looks like this:

 import android.content.Context
 import android.opengl.GLES20.glGetString
 import android.opengl.GLSurfaceView
 import android.util.Log
 import javax.microedition.khronos.egl.EGL10
 import javax.microedition.khronos.egl.EGLConfig
 import javax.microedition.khronos.egl.EGLContext
 import javax.microedition.khronos.egl.EGLDisplay

 private const val EGL_CONTEXT_CLIENT_VERSION=0x3098
 private const val glVersion=3.0
 const val TAG="ContextFactory"
 class MyGLSurfaceView(context: Context):GLSurfaceView(context) {
     private val renderer: MyGLRenderer

    init {
        setEGLContextClientVersion(2)
        renderer = MyGLRenderer(context)
        setRenderer(renderer)
        glGetString(glVersion.toInt())
    }

    private class ContextFactory: GLSurfaceView.EGLContextFactory{
        override fun createContext(egl: EGL10?, display: EGLDisplay?, eglConfig: 
        EGLConfig?): EGLContext? {
            Log.w(TAG, "creating OpenGL ES $glVersion context")
            if (egl != null) {
                return egl.eglCreateContext(
                    display,
                    eglConfig,
                    EGL10.EGL_NO_CONTEXT,
                    intArrayOf(EGL_CONTEXT_CLIENT_VERSION, glVersion.toInt(), 
                    EGL10.EGL_NONE)
                )
            }
            return null
        }

        override fun destroyContext(p0: EGL10?, p1: EGLDisplay?, p2: EGLContext?) {
            TODO("Not yet implemented")
        }
    }
}

Solution

  • First create a class that inherits from GLSurfaceView.EGLContextFactory interface like so:

    import android.opengl.GLSurfaceView
    import android.util.Log
    import javax.microedition.khronos.egl.EGL10 //make sure to import from 
    //javax not from `android.opengles.*` package
    import javax.microedition.khronos.egl.EGLConfig
    import javax.microedition.khronos.egl.EGLContext
    import javax.microedition.khronos.egl.EGLDisplay
    
    
    private const val EGL_CONTEXT_CLIENT_VERSION = 0x3098
    private const val glVersion = 3.3
    
    class ContextFactory : GLSurfaceView.EGLContextFactory {
        override fun createContext(egl: EGL10, display: EGLDisplay, eglConfig: 
            EGLConfig): EGLContext {
             // returns null if 3.0 is not supported
            Log.w("OpenGl version", "creating OpenGL ES $glVersion context")
            return egl.eglCreateContext(
                display,
                eglConfig,
                EGL10.EGL_NO_CONTEXT,
                intArrayOf(EGL_CONTEXT_CLIENT_VERSION, glVersion.toInt(), 
                EGL10.EGL_NONE)
            ) //
        }
    
        override fun destroyContext(egl: EGL10, display: EGLDisplay, context: 
            EGLContext) {
            egl.eglDestroyContext(display, context)
        }
    }
    

    in MyGLSurfaceView class:

    class MyGLSurfaceView(context: Context) : GLSurfaceView(context) {
         private val renderer: MyGLRenderer
         private var previousX: Float = 0f
         private var previousY: Float = 0f
    
    init {
        //setEGLContextClientVersion(2)
        setEGLContextFactory(ContextFactory()) //need to use this function 
        //instead of the above one
        renderer = MyGLRenderer(context)
        setRenderer(renderer)
    }
    

    if the program crashes, it means, it doesn't support the current version that was specified in the ContextFactory class. If it can run smoothly, it means it supports the currently specified version.

    One more thing GLSurfaceView.EGLContextFactory is actually a normal interface, not a static or global interface when I thought previously.