Framebuffer FBO渲染到纹理很慢,在Android上使用OpenGL ES 2.0,为啥?
Posted
技术标签:
【中文标题】Framebuffer FBO渲染到纹理很慢,在Android上使用OpenGL ES 2.0,为啥?【英文标题】:Framebuffer FBO render to texture is very slow, using OpenGL ES 2.0 on Android, why?Framebuffer FBO渲染到纹理很慢,在Android上使用OpenGL ES 2.0,为什么? 【发布时间】:2012-05-23 23:38:53 【问题描述】:我正在使用 opengl es 2.0 编写一个 android 2d 游戏。在我将精灵绘制到后台缓冲区后,我将灯光绘制到 FBO 并尝试再次将其混合到后台缓冲区。 当我将 FBO 绘制到帧缓冲区时,即使是没有任何颜色的透明,三星 Galaxy w 上的帧速率也会从 60 下降到 30(它有一个 adreno 205 作为 gpu)。我到处搜索并尝试了所有方法,即使我在场景中绘制了一个精灵并将一个透明的 FBO 纹理混合到帧速率下降的屏幕上。我在该手机上尝试了其他带有灯光效果的游戏,它们运行良好,几乎所有游戏在该手机上都很好,我相信他们也使用了帧缓冲区。 在 Galaxy SII(mali 400 gpu)上运行良好,我对 opengl 很陌生,所以我相信我在某个地方犯了错误,我分享我的代码。
// Create a framebuffer and renderbuffer
GLES20.glGenFramebuffers(1, fb, offset);
GLES20.glGenRenderbuffers(1, depthRb, offset);
// Create a texture to hold the frame buffer
GLES20.glGenTextures(1, renderTex, offset);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTex[offset]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,
screenWidth, screenHeight, 0,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
null);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR);
//bind renderbuffer
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depthRb[offset]);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16,
screenWidth, screenHeight);
// bind the framebuffer
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fb[offset]);
// specify texture as color attachment
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, renderTex[offset], 0);
// specify depth_renderbufer as depth attachment
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT,
GLES20.GL_RENDERBUFFER, depthRb[0]);
// Check FBO status.
int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
if ( status == GLES20.GL_FRAMEBUFFER_COMPLETE )
Log.d("GLGame framebuffer creation", "Framebuffer complete");
// set default framebuffer
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
我在创建表面时会这样做一次。不确定是否正确。我保留纹理和帧缓冲区 ID,以便在需要时切换到它们。 我的绘图代码:
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
ShaderProgram program = glgame.getProgram();
//put vertices in the floatbuffer
mTriangleVertices.put(vertices, 0, len);
mTriangleVertices.flip();
GLES20.glVertexAttribPointer(program.POSITION_LOCATION, 2, GLES20.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
//preparing parameter for texture position
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
GLES20.glEnableVertexAttribArray(program.POSITION_LOCATION);
//preparing parameter for texture coords
GLES20.glVertexAttribPointer(program.TEXTURECOORD_LOCATION, 2, GLES20.GL_FLOAT,
false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES,
mTriangleVertices);
//set projection matrix
GLES20.glEnableVertexAttribArray(program.TEXTURECOORD_LOCATION);
GLES20.glUniformMatrix4fv(program.MATRIX_LOCATION, 1, false, matrix, 0);
//draw triangle with indices to form a rectangle
GLES20.glDrawElements(GLES20.GL_TRIANGLES, numSprites * 6, GLES20.GL_UNSIGNED_SHORT,
indicesbuf);
//clear buffers
mTriangleVertices.clear();
mVertexColors.clear();
一切都在屏幕上正确渲染,但是当我绘制 FBO 纹理时性能就被破坏了。 非常感谢您的帮助。我为此付出了很多努力,但没有找到解决方案。
【问题讨论】:
迟到的答案,但我在 ICS 下的 HD2 上做了类似的事情,而且几乎是一样的。我可以在 60fps 下毫无问题地渲染任何东西,但是当我使用 FBO 时,帧速率会下降到 45,如果我使用另一个 FBO,它会下降到 30。 嗯...我看到三星 Galaxy 上的一些游戏运行没有问题,它们似乎使用帧缓冲区来处理 2d 灯光,但我的代码运行缓慢。我想知道我是否犯了错误。 galaxy 使用 sgx 芯片组。就像我之前所说的,它与小型 gmem 和平铺架构有关,每次你更改帧缓冲区时,都需要将旧的帧缓冲区复制到普通内存,如果你在绑定后第一次调用时将其清除,则不需要从正常复制回来mem 到 gmem,这就是为什么帧缓冲区很慢的原因,尤其是在 adreno 200 上 :) 如果您有 adreno 设备,您可以检查应用程序是否使用带有 adreno 分析器的帧缓冲区,只需将其插入并下载当前帧,然后检查纹理中的 fb。如果你能找到一个你会确定的, 在 HD2 上很大程度上取决于您构建中使用的驱动程序,我有一个 2.3.3 构建,每个 fb 绑定都被识别为阻塞解析,而 ics 上的相同代码被识别为非阻塞解析并以大约 5-10 fps 的速度运行。我认为这可能与高通在 ics 推出后的一段时间内为开发者开放驱动程序有关。 【参考方案1】:根据 qualcomm 文档,您需要在每个 glbindframebuffer 之后进行 glclear,这是与平铺架构相关的问题,如果您正在切换帧缓冲区,则需要将数据从 fastmem 复制到普通内存以保存当前帧缓冲区并从 slowmem 复制到 fast mem 来获取新绑定的帧的内容,以防你在 glbind 之后清除没有数据从 slowmem 复制到 fastmem 并且你可以节省时间,但是你需要经常重新设计你的渲染管道,这样可以避免来回读取数据在慢速和快速内存之间,因此尝试在每次绑定后执行 glclear 应该会有所帮助,您还可以使用 adreno profiler 获取有关有问题的调用的更多信息,但我怀疑它对 adreno200 有帮助我正在尝试获取两个缓冲区进行模糊我以 10fps 结束,如果未清除 bindframebuffer 调用最多需要 20msec,如果它应该在 2ms 结束。
【讨论】:
我会在我能再次使用 adreno 205 时尝试这个。我找到了 qualcomm 文档,我会遵循它。非常感谢您的帮助! Here 是高通文档。 如果你想保留帧缓冲区的内容,只需执行glClear(0);
,这似乎也可以解决问题。
感谢您的回答。这个 qualcomm 文档是否可用?
嗯,它看起来确实如此:)。只是检查一下。而且它非常合乎逻辑,因为如果将数据复制到 fmem 后的第一步是擦除它,那么您不需要复制任何内容。最好的答案是对其进行基准测试,如果你愿意这样做,那么你会发现它会加快速度。以上是关于Framebuffer FBO渲染到纹理很慢,在Android上使用OpenGL ES 2.0,为啥?的主要内容,如果未能解决你的问题,请参考以下文章
C#开发的OpenRA的管理FBO(Framebuffer Object)