Ankur Sakhuja
Ankur Sakhuja

Reputation: 86

Automatic buffer clear while using OpenGL on Android

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:-

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:)

Upvotes: 6

Views: 2589

Answers (4)

Thomas Perl
Thomas Perl

Reputation: 2348

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);
}

Upvotes: 1

holtaf
holtaf

Reputation: 787

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

Upvotes: 0

Jesse Hall
Jesse Hall

Reputation: 6807

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.

Upvotes: 1

torbjoernwh
torbjoernwh

Reputation: 445

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

Upvotes: 1

Related Questions