Eric Chou
Eric Chou

Reputation: 11

How to set GLSurfaceView not clean things I've draw before?

I was trying to use opengl es 2.0 in android. I use GLSurfaceView as my main view. Here is the question. I want to draw several points in a row. That is when I click on the view, I draw one point and call requestRender. But I dont want to store all the point I triggered because there will be plenty of them. So my onDrawFrame() only have one sentence, maybe like this:

GLES20.glDrawArrays(GLES20.GL_POINTS, 8, 1);

The question is, this logic works well on my android virtual machine, but all the things I've draw just disappear when I draw a new point.

Is there a way to retain all the points I've draw on GLSurfaceView and then keep on drawing without saving all the points?

Upvotes: 1

Views: 655

Answers (3)

hata
hata

Reputation: 12478

I was also struggling to disable double- or triple- buffering. Finally, I found a solution. (cf. Automatic buffer clear while using OpenGL on Android)

  1. Principally, such configuration is none of OpenGL (ES)'s business. It's EGL's.
  2. There is a configuration called EGL_BUFFER_PRESERVED which enables additive drawing.
  3. To use this configuration, you can choose to use SurfaceView directly other than GLSurfaceView to fully control over EGL like @fadden suggested above. Or,
  4. You can still use GLSurfaceView and setEGLConfigChooser a custom EGLConfigChooser object to it, then invoke EGL14.eglSurfaceAttrib at an appropriate timing.

Custom EGLConfigChooser Class:

Enabling EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT.

/**
 * This class will choose a RGB_888 surface with or without a depth buffer.
 * (Choosing a RGB_888 with a depth buffer is GLSurfaceView's default behavior.)
 *
 * In addition to the default behavior, it will enable EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT
 * of EGL10.EGL_SURFACE_TYPE.
 *
 * cf. https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglChooseConfig.xhtml
 */
class SimpleConfigChooser(
    private val eglContextClientVersion: Int, withDepthBuffer: Boolean = true,
) : GLSurfaceView.EGLConfigChooser {

    private val value = IntArray(1)

    private val redSize = 8
    private val greenSize = 8
    private val blueSize = 8
    private val alphaSize = 0
    private val depthSize = if (withDepthBuffer) 16 else 0
    private val stencilSize = 0

    private val configSpec = filterConfigSpec(intArrayOf(
        EGL10.EGL_RED_SIZE, redSize,
        EGL10.EGL_GREEN_SIZE, greenSize,
        EGL10.EGL_BLUE_SIZE, blueSize,
        EGL10.EGL_ALPHA_SIZE, alphaSize,
        EGL10.EGL_DEPTH_SIZE, depthSize,
        EGL10.EGL_STENCIL_SIZE, stencilSize,
        EGL10.EGL_SURFACE_TYPE, (EGL10.EGL_WINDOW_BIT or EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT),
        EGL10.EGL_NONE
    ))

    private fun filterConfigSpec(configSpec: IntArray): IntArray {
        if (eglContextClientVersion != 2 && eglContextClientVersion != 3) {
            return configSpec
        }
        /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
         * And we know the configSpec is well formed.
         */
        val len = configSpec.size
        val newConfigSpec = IntArray(len + 2)
        System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1)
        newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE
        if (eglContextClientVersion == 2) {
            newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT /* EGL_OPENGL_ES2_BIT */
        } else {
            newConfigSpec[len] = EGLExt.EGL_OPENGL_ES3_BIT_KHR /* EGL_OPENGL_ES3_BIT_KHR */
        }
        newConfigSpec[len + 1] = EGL10.EGL_NONE
        return newConfigSpec
    }

    override fun chooseConfig(egl: EGL10, display: EGLDisplay): EGLConfig {
        val numConfig = IntArray(1)
        require(egl.eglChooseConfig(
            display, configSpec, null, 0, numConfig
        )) { "eglChooseConfig#1/2 failed" }

        val numConfigs = numConfig[0]
        require(numConfigs > 0) { "No configs match configSpec" }

        val configs = arrayOfNulls<EGLConfig>(numConfigs)
        require(egl.eglChooseConfig(
            display, configSpec, configs, numConfigs, numConfig
        )) { "eglChooseConfig#2/2 failed" }

        return chooseConfig(egl, display, configs)
            ?: throw IllegalArgumentException("No config chosen")
    }

    private fun chooseConfig(
        egl: EGL10, display: EGLDisplay, configs: Array<EGLConfig?>,
    ): EGLConfig? {
        for (config in configs) {
            if (config == null) {
                continue
            }
            val d: Int = findConfigAttrib(egl, display, config,
                EGL10.EGL_DEPTH_SIZE, 0)
            val s: Int = findConfigAttrib(egl, display, config,
                EGL10.EGL_STENCIL_SIZE, 0)
            if (d >= depthSize && s >= stencilSize) {
                val r: Int = findConfigAttrib(egl, display, config,
                    EGL10.EGL_RED_SIZE, 0)
                val g: Int = findConfigAttrib(egl, display, config,
                    EGL10.EGL_GREEN_SIZE, 0)
                val b: Int = findConfigAttrib(egl, display, config,
                    EGL10.EGL_BLUE_SIZE, 0)
                val a: Int = findConfigAttrib(egl, display, config,
                    EGL10.EGL_ALPHA_SIZE, 0)
                if (r == redSize && g == greenSize
                    && b == blueSize && a == alphaSize
                ) {
                    return config
                }
            }
        }
        return null
    }

    private fun findConfigAttrib(
        egl: EGL10, display: EGLDisplay, config: EGLConfig, attribute: Int, defaultValue: Int,
    ): Int {
        return if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
            value[0]
        } else defaultValue
    }
}

In your GLSurfaceView.Renderer subclass:

Invoke EGL14.eglSurfaceAttrib.

override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
    EGL14.eglSurfaceAttrib(
        EGL14.eglGetCurrentDisplay(),
        EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW),
        EGL14.EGL_SWAP_BEHAVIOR, EGL14.EGL_BUFFER_PRESERVED
    )

    // some other work...
}

MainActivity:

Set the custom EGLConfigChooser to GLSurfaceView.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val binding = MainActivityBinding.inflate(layoutInflater)

    val EGL_CONTEXT_CLIENT_VERSION = 2

    binding.glSurfaceView.setEGLContextClientVersion(
        EGL_CONTEXT_CLIENT_VERSION
    )
    binding.glSurfaceView.setEGLConfigChooser(
        SimpleConfigChooser(EGL_CONTEXT_CLIENT_VERSION)
    )
    binding.glSurfaceView.setRenderer(Renderer()) // this should be the last

    setContentView(binding.root)

    // some other work...
}

Upvotes: 0

fadden
fadden

Reputation: 52313

You need to redraw the entire screen every frame. The output is usually double- or triple-buffered, so you can't rely on the availability of the previous contents. Always start with a glClear() call.

One way to do what you want is to render onto an FBO, then blit the FBO to the screen. You can find an example of this in Grafika's "record GL app" activity, which is actually using it so it can render each frame twice (once to the screen, once to the video encoder), and in various examples on the web. The basic idea is that you're rendering onto a GLES texture, rather than directly into the Surface, so there's no double-buffering to contend with.

Upvotes: 1

Ramachandran S
Ramachandran S

Reputation: 1

If you have used GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT) in onFrameRedraw() method, remove it and instead, use it in onSurfaceCreated() method of the Renderer.

Upvotes: 0

Related Questions