【问题标题】:Android OpenGL ES Updating VBO data every frameAndroid OpenGL ES 每帧更新 VBO 数据
【发布时间】:2016-11-08 04:26:52
【问题描述】:

每帧更新 VBO 数据的最佳方法是什么?目前我正在使用 glBufferSubData 在每帧调用 glBufferData 后使用 null ,并且我不能达到超过 25fps 而不会出现奇怪的口吃。

我的主要目标是以 50fps 的速度在两张图像之间翻转。

这是一些代码:

public void onDrawFrame(GL10 glUnused) {
    endTime = System.currentTimeMillis();
    dt = endTime - startTime;
    if (dt < 20)
        try {
            Thread.sleep(20 - dt);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    startTime = System.currentTimeMillis();

    ...some matrix operations

    render();
}

void render() {
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo[0]);
    GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, floatbuffer.capacity() * BYTES_PER_FLOAT,
                    null, GLES20.GL_STREAM_DRAW);

    ...a bunch of if statements

   if(side==1) {
       GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, floatbuffer.capacity() * BYTES_PER_FLOAT,floatbuffer);
       side = 0;
    } else {
       GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, floatbuffer2.capacity() * BYTES_PER_FLOAT, floatbuffer2);
       side = 1;
    }

    GLES20.glVertexAttribPointer(positionAttribute, POSITION_DATA_SIZE_IN_ELEMENTS, GLES20.GL_FLOAT, false, STRIDE, 0);
    GLES20.glEnableVertexAttribArray(positionAttribute);

    GLES20.glVertexAttribPointer(colorAttribute, COLOR_DATA_SIZE_IN_ELEMENTS, GLES20.GL_FLOAT, false, STRIDE, (POSITION_DATA_SIZE_IN_ELEMENTS) * BYTES_PER_FLOAT);
    GLES20.glEnableVertexAttribArray(colorAttribute);

    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, ibo[0]);
    GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_SHORT, 0);

    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
}

我还尝试设置多个 vbo,每个 vbo 都有自己不变的数据,并在我要显示的 vbo 上调用 glBindBuffer,但我仍然注意到卡顿。

我对 OpenGL 有点陌生,所以我不清楚最好的方法。我了解将数据从 CPU 传输到 GPU 可能存在瓶颈。有没有更有效的方法来解决这个问题?

【问题讨论】:

  • 鉴于使用没有任何数据传输的静态 VBO 仍然卡顿,您为什么认为这是您的问题?
  • 你完全正确。我查看了代码的其他部分,如果我删除了 onDrawFrame 中的 Thread.sleep() 调用,则应用程序呈现时不会卡顿(除了偶尔的失误)。您对如何正确获得恒定帧速率有任何想法吗?如果 Thread.sleep() 在如此低的毫秒数下不准确,还有什么其他方法?是否可以使用 RENDERMODE_WHEN_DIRTY 并设置另一个在正确的时间调用 requestRender() 的线程?
  • 添加并回答了一些尝试的想法。

标签: android opengl-es


【解决方案1】:

值得注意的是,几乎所有 Android 平台都使用 vsync 将最终帧缓冲区扫描锁定到显示控制器更新频率的某个倍数,以避免在扫描输出期间撕裂输出。

通常显示为 60FPS 面板,因此您需要每 16.6 毫秒从应用程序中获取新的更新。如果您错过了 vsync 截止日期(保证您会在 20 ms 睡眠中完成),那么您至少会遇到一些轻微的卡顿,其中一帧必须显示超过一帧以弥合传入帧序列中的间隙。应用程序对复制哪个帧的控制为零,因此您无法真正提前纠正它。

例如,如果您的应用程序是 45FPS,那么您最终会在显示控制器上得到一个最终输出帧序列,看起来像 1、1、2、3、3、4、5、5、6 等。 (例如,显示器必须每 1/60 秒扫描一帧,并且只会运行最新的帧)。显示时间与应用程序正在使用的第二步的精确 1/45 不匹配,因此这会在视觉上使输出动画抖动。

唯一真正的解决方法是在面板扫描时间的倍数上发出帧(例如 60FPS = 16.6ms、30FPS = 33.3ms、20FPS = 50ms)。

【讨论】:

  • P.S.原则上您可以使用eglSwapInterval() 将帧速率降低到面板刷新率的可预测除数(例如强制 30 FPS),但我在 Android 上运气不好,所以我不确定我会依赖它。
  • 感谢您的详细回答 - 认为是这种情况。我知道有一些自定义内核可以在其中关闭 vsync。你认为关闭 vsync 后 thread.sleep() 调用会正常运行吗?或者它会变得猖獗并且同样紧张不安?我不认为它会起作用,但如果有机会我会尝试它
  • 原则上禁用vsync可以解决这个问题,但这不是应用程序正常控制的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多