I was also struggling to disable double- or triple- buffering. Finally, I found a solution. (cf. Automatic buffer clear while using OpenGL on Android)
- Principally, such configuration is none of OpenGL (ES)'s business. It's EGL's.
- There is a configuration called
EGL_BUFFER_PRESERVED
which enables additive drawing.
- To use this configuration, you can choose to use
SurfaceView
directly other than GLSurfaceView
to fully control over EGL like @fadden suggested above. Or,
- 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...
}