glClear()需要太长时间 - Android OpenGL ES 2

Ian*_*Ian 5 graphics android opengl-es timing

我正在使用OpenGL ES 2开发一个Android应用程序.我遇到的问题是该glClear()功能需要很长时间才能处理,因为帧延迟会导致游戏显得紧张.具有定时探针的程序运行的输出显示,虽然设置来自图集的所有顶点和图像仅需要不到1毫秒,但glClear()需要10到20毫秒.事实上,清算通常占总渲染时间的95%.我的代码基于常见教程,Render功能如下:

private void Render(float[] m, short[] indices) {
    Log.d("time", "--START RENDER--");

    // get handle to vertex shader's vPosition member
    int mPositionHandle = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "vPosition");

    // Enable generic vertex attribute array
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    // Prepare the triangle coordinate data
    GLES20.glVertexAttribPointer(mPositionHandle, 3,
    GLES20.GL_FLOAT, true,
    0, vertexBuffer);

    // Get handle to texture coordinates location
    int mTexCoordLoc = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "a_texCoord" );

    // Enable generic vertex attribute array
    GLES20.glEnableVertexAttribArray ( mTexCoordLoc );

    // Prepare the texturecoordinates
    GLES20.glVertexAttribPointer ( mTexCoordLoc, 2, GLES20.GL_FLOAT,
    false, 
    0, uvBuffer);

    // Get handle to shape's transformation matrix
    int mtrxhandle = GLES20.glGetUniformLocation(riGraphicTools.sp_Image, "uMVPMatrix");

    // Apply the projection and view transformation
    GLES20.glUniformMatrix4fv(mtrxhandle, 1, false, m, 0);

    // Get handle to textures locations
    int mSamplerLoc = GLES20.glGetUniformLocation (riGraphicTools.sp_Image, "s_texture" );

    // Set the sampler texture unit to 0, where we have saved the texture.
    GLES20.glUniform1i ( mSamplerLoc, 0);

    long clearTime = System.nanoTime();
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    Log.d("time", "Clear time is " + (System.nanoTime() - clearTime));

    // Draw the triangles
    GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length,
    GLES20.GL_UNSIGNED_SHORT, drawListBuffer);

    // Disable vertex array
    GLES20.glDisableVertexAttribArray(mPositionHandle);
    GLES20.glDisableVertexAttribArray(mTexCoordLoc);

    Log.d("time", "--END RENDER--");
}
Run Code Online (Sandbox Code Playgroud)

我试过移动png atlas/drawable-nodpi但它没有效果.

我也尝试过使用glFlush()glFinish()函数.有趣的是,如果我不打电话,glClear()那么必须自动调用它.这是因为总渲染时间仍然与调用时一样高,并且屏幕上没有前一帧的残余.只有第一次通话glClear()才是耗时的.如果再次调用,则后续调用仅为1或2毫秒.

我也尝试了不同的参数组合(例如GLES20.GL_DEPTH_BUFFER_BIT)和使用glClearColor().晴朗的时间仍然很高.

先感谢您.

Ret*_*adi 8

你没有衡量你的想法.测量OpenGL API调用的耗用时间大多没有意义.

异步

要理解的关键方面是OpenGL是一种将工作传递给GPU的API.最简单的心理模型(在很大程度上与现实相对应)是,当您进行OpenGL API调用时,您会将稍后提交给GPU的工作排队.例如,如果您glDraw*()拨打电话,请记录构建排队的工作项的呼叫,稍后将提交给GPU执行.

换句话说,API是高度异步的.通过API调用请求的工作在调用返回时尚未完成.在大多数情况下,它甚至没有提交给GPU执行.它只排队等候,稍后会提交,大部分时间都在您的控制之外.

这种一般方法的结果是,您进行glClear()呼叫测量的时间几乎与清除帧缓冲区所需的时间无关.

同步

现在我们已经确定了OpenGL API是如何异步的,下一个要理解的概念是需要一定程度的同步.

让我们看一下整体吞吐量受GPU限制的工作负载(通过GPU性能,或者因为帧速率受显示器刷新限制).如果我们保持整个系统完全异步,并且CPU可以比GPU处理它们更快地生成GPU命令,那么我们将排队逐渐增加的工作量.出于以下几个原因,这是不可取的:

  • 在极端情况下,排队的工作量将增长到无穷大,并且我们将从存储排队的GPU命令中耗尽内存.
  • 在需要响应用户输入的应用程序(如游戏)中,我们会在用户输入和呈现之间增加延迟.

为避免这种情况,驱动程序使用限制机制来防止CPU过远.如何处理这个问题的细节可能相当复杂.但作为一个简单的模型,它可能就像在GPU完成渲染之前超过1-2帧时阻塞CPU.理想情况下,您总是希望排队等待一些工作,以便GPU永远不会为图形有限的应用程序闲置,但您希望尽可能减少排队的工作量,以最大限度地减少内存使用和延迟.

你的测量的意义

通过解释所有这些背景信息,您的测量结果应该不那么令人惊讶.到目前为止,最可能的情况是您的glClear()呼叫会触发同步,您测量的时间是GPU充分赶上的时间,直到提交更多工作是有意义的.

请注意,这并不意味着所有先前提交的工作都需要完成.让我们看看一个有点假设的序列,但足够现实,以说明可能发生的事情:

  • 假设您进行glClear()构成渲染帧开始的调用n.
  • 此时,帧n - 3在显示器上,并且GPU正忙于处理帧的渲染命令n - 2.
  • 司机决定你真的不应该超过2帧.因此,它会阻止您的glClear()调用,直到GPU完成帧的渲染命令n - 2.
  • 它也可能决定它需要等到n - 2显示屏上显示帧,这意味着等待下一个光束同步.
  • 现在该帧n - 2在显示器上,之前包含帧的缓冲区n - 3不再使用.它现在可以用于帧n,这意味着现在可以提交glClear()帧命令n.

请注意,虽然您的glClear()调用在此场景中进行了各种等待,而您在API调用中花费的时间的一部分,但是没有一次用于实际清除帧的帧缓冲区.您可能只是坐在某种信号量(或类似的同步机制)上,等待GPU完成以前提交的工作.

结论

考虑到您的测量结果毕竟没有直接帮助,您可以从中学到什么?不幸的是不是很多.

如果您确实观察到您的帧速率不符合您的目标,例如因为您观察到口吃,或者甚至更好,因为您在一定时间段内测量帧速率,您唯一确定的是渲染速度太慢.进入性能分析的细节是一个对于这种格式来说太大的话题.只是为了简要介绍一下您可以采取的步骤:

  • 测量/分析您的CPU使用情况,以验证您是否真的受GPU限制.
  • 使用GPU供应商经常提供的GPU分析工具.
  • 简化渲染或跳过部分渲染,并查看性能如何变化.例如,如果简化几何体,它会变得更快吗?您可能受到顶点处理的限制.如果减少帧缓冲区大小,它会变快吗?或者,如果您简化片段着色器?您可能受到片段处理的限制.