我正在使用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--");
}
我尝试了moving the png atlas to /drawable-nodpi
,但没有效果。
我也尝试过使用glFlush()
和glFinish()
功能。
有趣的是,如果我不调用glClear()
,则必须自动调用它。这是因为总渲染时间仍然与调用时一样高,并且屏幕上没有前一帧的残余。只有第一次调用glClear()
非常耗时。如果再次调用它,后续调用只有1或2毫秒。
我还尝试了不同的参数组合(例如GLES20.GL_DEPTH_BUFFER_BIT
)和glClearColor()
。晴朗的时间仍然很高。
提前谢谢。
答案 0 :(得分:8)
你没有衡量自己的想法。测量OpenGL API调用的耗用时间大多没有意义。
要理解的关键方面是OpenGL是一种将工作传递给GPU的API。最简单的心理模型(在很大程度上与现实相对应)是,当您进行OpenGL API调用时,您会将稍后提交给GPU的工作排队。例如,如果您进行glDraw*()
调用,请记录构建排队的工作项的调用,稍后将提交给GPU执行。
换句话说,API是高度异步的。通过API调用请求的工作在调用返回时尚未完成。在大多数情况下,它甚至都没有提交给GPU执行。它只排队等候,稍后会提交,大部分时间都在您的控制之外。
这种一般方法的结果是,您进行glClear()
调用的时间与清除帧缓冲所需的时间几乎无关。
现在我们已经确定了OpenGL API是如何异步的,下一个要理解的概念是需要一定程度的同步。
让我们看一下整体吞吐量受GPU限制的工作负载(通过GPU性能,或者因为帧速率受显示器刷新限制)。如果我们保持整个系统完全异步,并且CPU可以比GPU处理它们更快地生成GPU命令,那么我们将排队逐渐增加的工作量。出于以下几个原因,这是不可取的:
为避免这种情况,驱动程序使用限制机制来防止CPU过远。如何处理这个问题的细节可能相当复杂。但作为一个简单的模型,它可能就像在GPU完成渲染之前超过1-2帧时阻塞CPU。理想情况下,您总是希望排队一些工作,以便GPU永远不会为图形有限的应用程序闲置,但您希望尽可能减少排队的工作量,以最大限度地减少内存使用和延迟。
通过解释所有这些背景信息,您的测量结果应该不那么令人惊讶。到目前为止,最有可能的情况是您的glClear()
调用会触发同步,您测量的时间是GPU充分赶上的时间,直到提交更多工作是有意义的。
请注意,这并不意味着所有以前提交的作品需要完成。让我们看一下有点假设的序列,但要足够现实,以说明会发生什么:
glClear()
调用,形成渲染帧n
的开头。n - 3
帧,GPU正忙于处理第n - 2
帧的渲染命令。glClear()
调用,直到GPU完成帧n - 2
的渲染命令。n - 2
帧,这意味着等待下一次光束同步。n - 2
,之前包含框架n - 3
的缓冲区不再使用。现在可以将其用于框架n
,这意味着现在可以提交框架glClear()
的{{1}}命令。请注意,虽然您的n
调用在此方案中进行了各种等待,而您在API调用所花费的时间中进行了测量,但这一次都没有用于实际清除帧缓冲帧。您可能只是坐在某种信号量(或类似的同步机制)上,等待GPU完成以前提交的工作。
考虑到您的测量结果并非直接有用,您可以从中学到什么?不幸的是,不是很多。
如果您确实发现您的帧速率不符合您的目标,例如因为你观察到口吃,甚至更好,因为你在一段时间内测量帧率,你唯一知道的是你的渲染速度太慢。进入性能分析的细节是一个对于这种格式来说太大的话题。只是为了简要介绍一下您可以采取的步骤: