不使用Android OpenGL时快速屏幕闪烁

时间:2015-03-08 09:41:40

标签: android opengl-es screen opengl-es-2.0 flicker

我想节省电池续航时间。我的应用程序有时只需绘制。 所以我在onDraw方法中将此代码添加到我的渲染器中:

boolean dirty = true;

public void onDrawFrame(GL10 arg0) {
    if (!dirty) return;
    dirty = false;

    ..... draw images ....
}

所以我的应用程序只在我想要的时候被绘制。但是,如果我不在每一帧上绘制我的应用程序,它会非常快速地闪烁。看起来每2帧左右就会绘制一次,而在所有其他帧中,只会画出黑屏。

我知道我可以将渲染模式设置为RENDERMODE_WHEN_DIRTY。但我不想创建另一个线程来检查它是否脏。

我的问题是为什么会闪烁?在我进行检查之前,我不会调用任何方法或GLES20调用:if (!dirty) return;我确信boolean dirty没有变化,除了第一帧之外总是假的。

修改

我将代码更改为:

int dirty = 0;

public void onDrawFrame(GL10 arg0) {
    if (dirty > 1) return;
    dirty++;

    ..... draw images ....
}

这可以阻止闪烁!看起来你必须至少画2次,所以你没有这个奇怪的屏幕闪烁。无论如何,我现在将尝试使用更干净的方式并创建一个调用requestRender()的线程,当我想绘制一些东西并将我的渲染模式设置为RENDERMODE_WHEN_DIRTY

2 个答案:

答案 0 :(得分:1)

我猜想窗口系统仍会假设你已经更新了帧,并将缓冲区放到了屏幕上 - 也就是说它怎么知道你没有画出任何东西?屏幕上最终会出现在内存缓冲区中的任何内容(通常是N-2帧之前的帧)。

使用单独的线程来休眠渲染,直到状态变为脏,因此您只发送实际向操作系统渲染内容的帧。通过这种方式,您不仅可以节省GPU负载,还可以阻止CPU线程在浪费功率时无所事事,无需轮询脏状态。

答案 1 :(得分:1)

调用onDrawFrame()时,判断您不想绘制框架已经太晚了。无论你在该方法中绘制什么,都将呈现。如果你没有绘制任何东西,那么渲染目标中的任何内容都将被呈现。对于那是什么并不能保证。它可能是较旧的框架,也可能是随机垃圾。

您发现渲染帧至少两次的解决方法可能适用于您当前的系统,但我不会指望它在任何地方工作。它还会浪费电池电量。当您跳过实际渲染时,仍然会显示帧。

最好的解决方案是使用RENDERMODE_WHEN_DIRTY,并在实际需要时触发更新。我不确定为什么你需要一个额外的线程。在程序逻辑中的某个时刻,您将更改需要重绘的数据/状态。发生这种情况时,您可以直接调用requestRender(),而不是设置必须由另一个线程检查的标志。

另一种选择是你打电话:

eglSurfaceAttrib(display, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
设置上下文/表面时

。这要求在eglSwapBuffers()之后保留缓冲区内容。但它有一个很大的警告:所有设备都不支持此功能。您可以测试它是否支持:

EGLint surfType = 0;
eglGetConfigAttrib(display, config, EGL_SURFACE_TYPE, &surfType);
if (surfType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) {
    // supported!
}

您也可以在选择配置时请求此功能。作为传递给eglChooseConfig()的属性的一部分,请添加:

...
EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT,
...

但同样,在所有设备上不支持。因此,如果您针对特定设备进行定位,那么它只是一个选项,如果不支持则具有功能性后备功能。