不在 Android OpenGL 上绘图时屏幕快速闪烁

Fast Screen Flicker while NOT drawing on Android OpenGL

我想节省电池使用时间。我的应用程序有时只需要绘制。 所以我在 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 不会改变并且除了第一帧之外始终为 false。

编辑:

我将代码更改为:

int dirty = 0;

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

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

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

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

使用一个单独的线程来休眠渲染,直到状态变为脏,所以您只将实际渲染过的帧发送到 OS。这样,您不仅可以节省 GPU 负载,还可以停止 CPU 线程在无所事事地轮询脏状态时浪费功率。

onDrawFrame() 被调用时,已经来不及决定不想画框了。无论你用那种方法画什么,都会被呈现出来。如果你不绘制任何东西,渲染目标中发生的任何事情都会被呈现。无法保证那是什么。它可能是旧框架,或随机垃圾。

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

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

另一种选择是您致电:

eglSurfaceAttrib(display, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);

同时设置 context/surface。这要求在 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,
...

但同样,这在所有设备上都不支持。因此,只有当您针对特定设备时,它才是真正的选择,或者如果它不受支持,则有功能回退。