6

I'm writing a certain OpenGL application where I'd specifically like to draw frames incrementally. For this, I'd like to disable automatic clearing of buffer which I understand, is default behavior of GLSurfaceView.Renderer#onDrawFrame(). Can someone please help me on how to do this? I need to write the application in Java, not using native SDK.

I understand that I could possibly do this by :-

  1. setting EGL_SWAP_BEHAVIOR_PRESERVED_BIT bit for EGL_SURFACE_TYPE attribute while doing eglChooseConfig, and
  2. setting EGL_SWAP_BEHAVIOR attribute to EGL_BUFFER_PRESERVED by calling eglSurfaceAttrib on the EGLSurface object

However, I gather from the Khronos doc that:-

  • EGL_SWAP_BEHAVIOR_PRESERVED_BIT are supported only if the EGL version is 1.4 or greater.
  • EGL_SWAP_BEHAVIOR is supported only if the EGL version is 1.2 or greater.

Now, I understand that I could access EGL in two ways in my Android application:-

  1. use the Khronos API class EGL10 in javax.microedition.khronos.egl package (EGL11 doesn't seem to be implemented yet)
  2. use the Android API class EGL14 in android.opengl package (similar to using class android.opengl.GLES20)

The problem with (1) is that the version is < 1.4 so it doesn't support the functionality I need. The problem with (2) is that my application simply crashes the moment I call any method in EGL14, and I'm not sure how I'm supposed to use it (I could not find a single example program / tutorial on how EGL14 is supposed to be used in an app). In particular, I'd like to learn how to get a valid GL context from EGL14: in the case of EGL10, I could do that by calling javax.microedition.khronos.egl.EGLContext.getGL(). However, I see no equivalent method in class android.opengl.EGLContext. In fact, all EGL-related classes in android.opengl except EGL14 seem to be mostly empty.

My best bet was to follow the same line of reasoning as in case of GLES20: to call the methods only inside the GLSurfaceView.Renderer methods: onDrawFrame(), onSurfaceCreated(), onSurfaceChanged(), because these supply valid GL (GL10) and EGL (EGLConfig) contexts as arguments. So I put following code inside onDrawFrame():-

public void onDrawFrame(GL10 gl) {
    ...
    android.opengl.EGLDisplay d = null;
    if ( (d = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)) == EGL14.EGL_NO_DISPLAY) {
        Log.i("Triangle", "EGL14.eglGetDisplay() failed!");
    } else {
        Log.i("Triangle", "EGL14.eglGetDisplay() succeeded!");
    }
    ...
}

I believe I won't have to instantiate EGL14 before calling the above since all methods are static. However, the call to EGL14.eglGetDisplay() crashes the app.

Any help would be much appreciated, thanks:)

hata
  • 11,633
  • 6
  • 46
  • 69
  • Hello, I was facing the same problem, and I've just found out that my issue was a call to "glColorMask", after removing my call to this method everything got solved. Probably your issue's cause is different, but just in case it helps your I let you the hint. – PerracoLabs Aug 04 '13 at 12:52

4 Answers4

1

Instead of using EGL directly, you could extend GLSurfaceView and call setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); on init.

This setting prevents the GLSurfaceView frame from being redrawn until you call requestRender(), which is more efficient for this sample app.

See the android docs 1 on how to setup GLES with Java.

Building an OpenGL ES Environment

torbjoernwh
  • 445
  • 3
  • 9
  • 1
    Thanks for the reply, however I think you misunderstood my question. I don't wish to control when the new frame is drawn (continuously or on demand). What I want is, when the new frame does get drawn, it should not automatically clear the contents of the previous frame (as is the default behavior of onDrawFrame()). In other words, the contents already drawn in one frame should be retained, and the subsequent frame should be drawn on top of the previous frame. – Ankur Sakhuja Apr 29 '13 at 05:23
  • Could you just override onDrawFrame to do nothing? If its default behaviour is to call glClear. – torbjoernwh Apr 29 '13 at 16:03
1

The EGL version implemented may be higher than the interface you're using. The actual version is returned by EGL10.eglInitialize(). If it's [1,4] or higher, you can pass [EGL10.EGL_SURFACE_TYPE, EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT] when you call EGL10.eglChooseConfig(). It's okay to use the EGL14 definition of EGL_SWAP_BEHAVIOR_PRESERVED_BIT here -- it's just an int defined by the EGL spec.

How does EGL14.eglGetDisplay() crash the app. Is it throwing an exception that isn't getting caught? Might be worth filing a bug (with as much detail about what you're doing, and on what device) at https://code.google.com/p/android/issues/list.

Jesse Hall
  • 6,441
  • 23
  • 29
1

To build upon Jesse Hall's solution:

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import android.opengl.EGL14;

You can have an inner class in your GLSurfaceView subclass that implements an EGLConfigChooser:

private static class MyEGLConfigChooser implements GLSurfaceView.EGLConfigChooser {
    public EGLConfig chooseConfig (EGL10 egl, EGLDisplay display) {
        EGLConfig [] configs = new EGLConfig[1];
        int [] num_config = new int[1];
        int [] attrib_list  = new int[] {
            EGL10.EGL_RED_SIZE, 8,
            EGL10.EGL_GREEN_SIZE, 8,
            // ... + anything else you want ...
            EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT | EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT,
            EGL10.EGL_NONE,
        };

        if (egl.eglChooseConfig(display, attrib_list, configs, configs.length, num_config) && num_config[0] > 0) {
            // We just pick the first here, but you could interrogate all
            return configs[0];
        }

        return null;
    }
}

In your constructor of the GLSurfaceView subclass, add:

setEGLConfigChooser(new MyEGLConfigChooser());

Then, in the implementation of your GLSurfaceView.Renderer, whenever the surface changes, you can set the attributes on it:

public void onSurfaceChanged(GL10 gl, int width, int height) {
    EGL14.eglSurfaceAttrib(EGL14.eglGetCurrentDisplay(),
                           EGL14.getCurrentSurface(EGL14.EGL_DRAW),
                           EGL14.EGL_SWAP_BEHAVIOR,
                           EGL14.EGL_BUFFER_PRESERVED);
}
Thomas Perl
  • 2,178
  • 23
  • 20
  • This solution worked for me, thank you! And I would suggest that you don't have to define `MyEGLConfigChooser` as an inner class of GLSurfaceView subclass. You can define it as an anonymous class or use lambda implementing `GLSurfaceView.EGLConfigChooser`. Also you can invoke `setEGLConfigChooser` in the MainActivity right before `GLSurfaceView#setRenderer`. – hata Feb 15 '22 at 17:02
0

You simply can render to texture and then draw that texture to screen.

holtaf
  • 787
  • 6
  • 19