2016-11-05 113 views
0

每帧更新VBO数据的最佳方法是什么?目前,我在每帧调用glBufferData之后使用glBufferSubData,并且我无法达到超过25fps而没有奇怪的口吃。Android OpenGL ES每帧更新VBO数据

我的主要目标是在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调用glBindBuffer,我还注意到了口吃。

我对OpenGL有点新鲜,所以我不清楚最佳的处理方式。据我所知,将数据从CPU传输到GPU可能存在瓶颈。有没有更有效的方法来解决这个问题?

+0

鉴于在没有任何数据传输的情况下使用静态VBO仍然会结结巴巴,为什么您认为这是您的问题? – solidpixel

+0

你是完全正确的。我查看了代码的其他部分,如果我删除了onDrawFrame中的Thread.sleep()调用,则该应用程序呈现时不会出现口吃(除非偶尔会出现滑落)。你有关于如何正确获得恒定帧速率的想法吗?如果Thread.sleep()在如此低的ms计数时不准确,那么还有什么其他方法?是否可以使用RENDERMODE_WHEN_DIRTY并在正确的时间设置另一个调用requestRender()的线程? – Karam

+0

添加并回答一些想法尝试。 – solidpixel

回答

0

值得注意的是,几乎所有的Android平台都使用vsync来锁定最终的framebuffer扫描显示控制器更新频率的一些倍数,以避免在扫描期间撕裂输出。

通常情况下,显示屏将是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)上发射帧。

+0

P.S.原则上你可以使用'eglSwapInterval()'将帧率降低到面板刷新率的可预测除数(例如,强制30 FPS),但是我在Android上碰运气了,所以我不确定我会依靠它。 – solidpixel

+0

感谢您的详细解答 - 认为是这样。我知道有一些自定义的内核可以关闭vsync。你认为用vsync关闭thread.sleep()调用会正常工作吗?或者它会变得猖獗并且变得紧张起来?我认为它不会起作用,但是如果有机会,我会尝试 – Karam

+0

原则上禁用vsync可以解决这个问题,但这不是应用程序正常控制的问题。 – solidpixel