OpenGL ES 3.0 帧缓冲区失效

很多朋友都听说过 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

(0)

相关推荐

发表回复

登录后才能评论