很多朋友都听说过 glInvalidateFramebuffer(帧缓冲区失效)这个 API ,很多读者私信问过很多次:帧缓冲区失效应该怎么使用?在什么条件下使用?有什么好处?
函数原型:
void glInvalidateFramebuffer(GLenum target,
GLsizei numAttachments,
const GLenum *attachments);
glInvalidateFramebuffer 接口是 OpenGL ES 3.0 引入的,提供了一个通知驱动程序不再需要帧缓冲区内容的机制。
target 必须是 GL_READ_FRAMEBUFFER、GL_DRAW_FRAMEBUFFER 或 GL_FRAMEBUFFER。标记 GL_FRAMEBUFFER 被视为 GL_DRAW_FRAMEBUFFER。Attachments 包含要失效的 numAttachments 列表。如果指定的附件在绑定帧缓冲区中不存在,则会被忽略。
如果绑定了帧缓冲区对象,则附件可能包含 GL_COLOR_ATTACHMENTi、GL_DEPTH_ATTACHMENT、GL_STENCIL_ATTACHMENT 或 GL_DEPTH_STENCIL_ATTACHMENT。如果framebuffer对象不完整,glInvalidateFramebuffer可能会被忽略。
使用方式,如让当前的帧缓冲区对象的第一个颜色缓冲区失效,实现如下:
//Java
int attachments[] = {GLES30.GL_COLOR_ATTACHMENT0};
GLES30.glInvalidateFramebuffer(GLES30.GL_FRAMEBUFFER, 1, attachments, 0);
//C/C++
GLenum attachments1[] = {GL_COLOR_ATTACHMENT0};
glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments1);
帧缓冲区失效机制有什么好处?
帧缓冲区失效机制使得驱动程序可以采取多种优化步骤:
(1)跳过在块状渲染(TBR)架构中为了进一步渲染到顿缓冲区而做的不必要的图块内容恢复;
(2)跳过多 GPU 系统中 GPU之间不必要的数据复制;
(3)跳过某些实现中为了改进性能而对特定缓存的刷新。这种功能对于许多应用程序中实现峰值性能很重要,特别是那些执行大量屏幕外渲染的应用。
有了帧缓冲区失效机制,GPU 就可以删除不再需要的顿缓冲区内容,以减少每个帧保留的内容数量。
此外,如果图块数据不再有效,GPU 还可以消除从芯片内建存储器到系统内存不必要的数据传输,因为 GPU 和系统内存之间内存带宽需求明显降低,所以电力消耗随之下降,性能则得到改善。
以上是《OpenGL ES 编程指南》里面摘抄的解释,估计是翻译的问题,看起来让人云里雾里的。帧缓冲区失效机制实际上是一种更加细化的优化方式,主要为了降低功耗,在一定程度上可以优化性能。
帧缓冲区失效机制应该在什么情况下使用?怎么使用?
一般是多次使用帧缓冲区的场景,比如多重采样反锯齿、多重目标渲染和 glBlitFramebuffer(位块传送)。
帧缓冲区失效机制在多重采样反锯齿中使用的例子:
//上面完成了渲染到多重采样缓冲区 mMSAAFramebuffer
//接下来进行位块传送将多重采样缓冲区内容“拷贝”到普通的帧缓冲区 mFramebuffer
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER, mMSAAFramebuffer);
//这个时候已经完成了多重采样,不再需要 mMSAAFramebuffer 绑定的深度缓冲区了,可以将其内容设置为无效
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, (GLenum[]){GL_DEPTH_ATTACHMENT});
//glBlitFramebuffer(位块传送)
glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
//glBlitFramebuffer(位块传送)后,不再需要 mMSAAFramebuffer 的颜色缓冲区了,可以将其内容设置为无效
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, (GLenum[]){GL_COLOR_ATTACHMENT0});
有这个例子它的使用场景就比较好理解了,上述多重采样场景结束之后就用不到多重采样缓冲区 mMSAAFramebuffer 绑定的深度缓冲区了,可以将其内容设置为无效。
glBlitFramebuffer(位块传送)之后,也不再需要 mMSAAFramebuffer 的颜色缓冲区了,可以将其内容设置为无效。
假如帧缓冲区失效机制用错了或者用的时机不对,有什么后果?
答案是用错了也没啥问题,驱动会判定你的操作无效。不信,我们下面再整个例子看看,还是使用 glBlitFramebuffer 。
void BlitFrameBufferExample::Draw(int screenW, int screenH)
{
// 离屏渲染
glViewport(0, 0, m_RenderImage.width, m_RenderImage.height);
glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);
glUseProgram(m_FboProgramObj);
glBindVertexArray(m_VaoIds[1]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
glUniform1i(m_FboSamplerLoc, 0);
GO_CHECK_GL_ERROR();
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
GO_CHECK_GL_ERROR();
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 上屏
glViewport(0, 0, screenW, screenH);
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_FboId);
glReadBuffer(GL_COLOR_ATTACHMENT0);
//报错1280,因为把本次渲染的目标缓冲区设置成无效了,那还渲染个淡淡
GLenum attachments1[] = {GL_COLOR_ATTACHMENT0};
glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments1);
//无报错,0
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, attachments1);
glBlitFramebuffer(0, 0, m_RenderImage.width, m_RenderImage.height,
0, screenH, screenW, 0,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
//报错1280,同样是把本次渲染的目标缓冲区设置成无效了,那还渲染个淡淡,淡淡 glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments1);
//无报错,0,这里已经完成上屏渲染,不再需要 m_FboId 的颜色缓冲区了
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, attachments1);
}
以上得出结论,在当前渲染中不需要更新的帧缓冲区,可以使用 glInvalidateFramebuffer 设置无效。
源码链接:
https://github.com/githubhaohao/NDK_OpenGLES_3_0
参考:
《OpenGL ES 编程指南》
https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glInvalidateFramebuffer.xhtml
进技术交流群,扫码添加我的微信:Byte-Flow
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/40015.html