我相信你的猜测是完全正确的。 OpenGL 的常用方式是,每次要求您重绘时,您都需要绘制一个完整的帧,包括初始清除。如果你不这样做,行为通常是不确定的。在您的情况下,看起来确实使用了三重缓冲,并且您的绘图分布在 3 个单独的表面上。
此模型不适用于增量绘图,因为绘制全帧非常昂贵。您可以考虑几个选项。
优化您的绘图
这不是直接的解决方案,但总是值得考虑的事情。如果您能找到一种方法来提高渲染效率,则可能不需要增量渲染。你没有显示你的渲染代码,所以你可能只是有太多的点来获得一个好的帧率。
但无论如何,请确保您有效地使用 OpenGL。例如,将您的积分存储在 VBO 中,并仅更新更改为 glBufferSubData() 的部分。
绘制到 FBO,然后 blit
这是最通用和实用的解决方案。不是直接绘制到主帧缓冲区,而是使用帧缓冲区对象 (FBO) 渲染到纹理。您在此 FBO 上完成所有绘图,并在需要重绘时将其复制到主帧缓冲区。
为了从 FBO 复制到主帧缓冲区,您将需要 ES 2.0 中的一对简单的顶点/片段着色器。在 ES 3.0 及更高版本中,您可以使用glBlitFramebuffer()。
优点:
- 适用于任何设备,仅使用标准 ES 2.0 功能。
- 易于实施。
缺点:
单缓冲
EGL 是在 Android 中将 OpenGL 连接到窗口系统的底层 API,它确实具有创建单个缓冲表面的属性。虽然很少建议使用单缓冲渲染,但您的用例是少数仍然可以考虑的用例之一。
虽然 API 定义存在,但 documentation 将支持指定为可选:
客户端 API 可能无法遵守请求的呈现缓冲区。要确定上下文渲染到的实际缓冲区,请调用 eglQueryContext。
我自己从未尝试过这个,所以我不知道支持有多广泛,或者它是否在 Android 上受支持。如果您想尝试一下,以下概述了如何实现它:
如果您从 GLSurfaceView 派生用于 OpenGL 渲染,则需要提供自己的 EGLWindowSurfaceFactory,如下所示:
class SingleBufferFactory implements GLSurfaceView.EGLWindowSurfaceFactory {
public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
EGLConfig config, Object nativeWindow) {
int[] attribs = {EGL10.EGL_RENDER_BUFFER, EGL10.EGL_SINGLE_BUFFER,
EGL10.EGL_NONE};
return egl.eglCreateWindowSurface(display, config, nativeWindow, attribs);
}
public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) {
egl.eglDestroySurface(display, surface);
}
}
然后在您的GLSurfaceView 子类构造函数中,在调用setRenderer() 之前:
setEGLWindowSurfaceFactory(new SingleBufferFactory());
优点:
缺点:
- 部分或所有设备可能不支持。
- 单缓冲渲染可能效率低下。
使用 EGL_BUFFER_PRESERVE
EGL API 允许您指定请求缓冲区内容保留在eglSwapBuffers() 上的表面属性。不过,这在EGL10 界面中不可用。您必须使用EGL14 接口,该接口至少需要 API 级别 17。
要设置它,请使用:
EGL14.eglSurfaceAttrib(EGL14.eglGetCurrentDisplay(), EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW),
EGL14.EGL_SWAP_BEHAVIOR, EGL14.EGL_BUFFER_PRESERVED);
您应该能够将它放在您的GLSurfaceView.Renderer 实现的onSurfaceCreated() 方法中。
这在某些设备上受支持,但在其他设备上不支持。您可以通过查询配置的EGL_SURFACE_TYPE 属性来查询它是否支持,并根据EGL_SWAP_BEHAVIOR_PRESERVED_BIT 位检查它。或者您可以将这部分作为您的配置选择。
优点:
- 可以直接绘制到主帧缓冲区,无需副本。
- 仍然可以使用双/三缓冲渲染。
缺点:
结论
我可能会检查特定设备上的EGL_BUFFER_PRESERVE 支持,如果支持就使用它。否则,请使用 FBO 和 blit 方法。