OpenGL ES 2.0:经常使用bindbuffer降低性能

时间:2017-07-14 06:31:28

标签: android performance opengl-es-2.0 frame-rate

我的opengl 2.0游戏中存在性能问题。帧率很好,直到我在游戏中做出改变。它的某种爆发游戏有64种形状(砖块)。我现在想要的是当球击中砖时它没有被立即移除 - 它改变状态,包括改变纹理或更正确 - 地图集的uv-coord。我有一个textureatlas,我所做的只是为循环中的每个纹理调用GLES20.bindBuffer(),而不是调用外部的循环。之前我对所有形状都有相同的uv-coord,但现在我根据砖的状态改变它,这就是为什么我需要在循环中使用绑定

 private void drawShape() {


    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboDataListLevelSprites.get(iName).getBuff_id_vertices());
    GLES20.glEnableVertexAttribArray(mPositionHandle);
    GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, 0);


    //used this snipped before when using the same image (uv-coords) for all bricks
    /*GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboDataListLevelSprites.get(iName).getBuff_id_uvs());
    GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
    GLES20.glVertexAttribPointer(mTextureCoordinateHandle, 2, GLES20.GL_FLOAT, false, 0, 0);
    */

    for (Iterator<BrickProperties> it = arrayListBricks.iterator(); it.hasNext(); ) {



        BrickProperties bp = it.next();
        //now bindbuffer inside loop just too switch uv-coords of the atlas when its time to use another image 
        int buffIndexVal = bp.get_status_diff();
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, BrickProperties.get_buff_id_uvs()[buffIndexVal]);
        GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
        GLES20.glVertexAttribPointer(mTextureCoordinateHandle, 2, GLES20.GL_FLOAT, false, 0, 0);


        Matrix.setIdentityM(mModelMatrix, 0);
        Matrix.translateM(mModelMatrix, 0, bp.getTranslateData()[0], bp.getTranslateData()[1], bp.getTranslateData()[2]);

        if (bp.get_status() == 0) {
            it.remove();
        }

        render();
    }
} 


private void render() {

    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
    GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0);
    Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
}

我理解性能下降的原因是所有对GPU的bindbuffer调用,但我怎么可能解决这个问题呢?

2 个答案:

答案 0 :(得分:1)

为每个对象绑定一个缓冲区是一回事,但是还有另外一件事就是使用2个缓冲区来绘制单个对象。您还可以在render方法的开头进行冗余调用以取消绑定缓冲区,只需将其删除即可。

在大多数情况下(可能是所有情况),您需要交错的顶点数据以提高性能。所以使用

{
   position,
   textureCoordinates
}

在一个缓冲区中。

我在你的情况下看到你有两个相同物体的状态,第二个状态会改变顶点坐标而不是位置坐标。如果缓冲区相对较大(我假设不是),则在两者之间共享位置数据可能是有意义的。无论如何,我建议您使用缓冲结构作为

{
   position,
   textureCoordinates,
   secondaryTextureCoordinates
}

然后使用单独的缓冲区或甚至将辅助纹理坐标放到同一缓冲区的另一部分。

所以如果顶点缓冲区相对较小,那么我建议你使用&#34; atlas&#34;程序。对于你的情况,这意味着创建两倍大小的缓冲区并放置所有坐标(位置重复)并放置这个顶点数据,以便有一个接一个的部分。

我假设您可以轻松地为当前绘图执行此操作,并有效地将绑定缓冲区的数量减少到每个绘制调用0(您只需要在某些初始化时绑定它)。现在,您将拥有第二部分,您将为每个绘制元素设置属性指针,以便您可以控制使用哪个纹理坐标。这将再次呈现冗余呼叫,在您的情况下可以避免:

由于数据结构在缓冲区中是一致的,因此没有理由将指针设置为一次。只需在绑定缓冲区时将它们设置为开头一次,然后使用绘制调用中的偏移量来控制实际使用缓冲区的哪个部分GLES20.glDrawArrays(GLES20.GL_TRIANGLES, offset, 6)

如果操作正确,您的绘制方法应如下所示:

for (Iterator<BrickProperties> it = arrayListBricks.iterator(); it.hasNext(); ) {
    BrickProperties bp = it.next();

    Matrix.setIdentityM(mModelMatrix, 0);
    Matrix.translateM(mModelMatrix, 0, bp.getTranslateData()[0], bp.getTranslateData()[1], bp.getTranslateData()[2]);

    if (bp.get_status() == 0) {
        it.remove();
    }

    Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
    GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0);
    Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, bp.vertexOffset, 6);
}

这将删除所有绑定并仅保留矩阵运算和绘制调用。如果管道中有其他图形,则需要在循环之前进行缓冲区绑定,否则可以将其作为初始化的一部分。在这两种情况下,呼叫都应该大大减少。

要在此添加注释,通常的做法是使用另一个跟踪openGL状态的对象以避免冗余调用。您不是要调用GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferID..,而是调用类似contextObject.bindVertexBuffer(bufferID)的内容来检查bufferID是否与之前的调用相同。如果是,则不会进行实际绑定。如果您创建并使用此类系统,那么在调用缓冲区绑定和其余对象设置的位置上几乎没有区别,因为冗余调用将不起作用。仍然只有这个程序不会使你的情况最佳,所以你仍然需要两者。

答案 1 :(得分:1)

您可以绑定缓冲区并同时使用相同的UV-s绘制所有对象。而不是遍历每个砖块,迭代使用相同UV-s的所有对象。

另外,尝试对对象进行批处理,以便一次性绘制它们。使用索引缓冲区对象可能会有所帮助。