2016-11-05 63 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