【问题标题】:Fast Screen Flicker while NOT drawing on Android OpenGL不在 Android OpenGL 上绘图时快速屏幕闪烁
【发布时间】:2015-03-08 23:23:04
【问题描述】:

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

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

【问题讨论】:

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


    【解决方案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,
    ...
    

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

    【讨论】:

    • 感谢您的详细回答,我找到了解决问题的简单方法。我使用 RENDERMODE_WHEN_DIRTY 启动应用程序,并在 onTouch() 方法中将其更改为 RENDERMODE_CONTINUOUSLY。绘制完所有动画后,我将其改回脏动画并等到有人再次触摸屏幕。例如,有人触摸一个按钮,渲染模式会不断变化,直到绘制按钮触摸动画,然后再变回脏。这样我就不需要创建一个新线程,我的应用程序只在我需要时在 RENDERMODE_CONTINUOUSLY 中运行:)
    【解决方案2】:

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

    使用单独的线程来休眠渲染,直到状态变为脏状态,因此您只将实际渲染某些内容的帧发送到操作系统。这样一来,您不仅可以节省 GPU 负载,还可以阻止 CPU 线程在浪费电源的情况下旋转,而无所事事地轮询脏状态。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多