OpenGL PBO学习

OpenGL Pixel Buffer Object (PBO)

Overview

OpenGL pixel_buffer_object 扩展非常接近 vertex_buffer_object。它只是扩展出 vertex_buffer_object扩展,以便不仅将顶点数据并且将像素数据也存储到缓冲区对象中。这种存储像素数据的缓冲对象称为像素缓冲对象(PBO)。 pixel_buffer_object扩展借用了所有 VBO 框架和 API,此外还添加了 2 个额外的“target”令牌。这些令牌协助 PBO 内存管理器(OpenGL 驱动程序)确定缓冲对象的最佳位置;系统内存、共享内存或显存。此外,target令牌定义了绑定的 PBO 将用于 2 个不同的操作:GL_PIXEL_PACK_BUFFER 将像素数据传输到 PBO,或 GL_PIXEL_UNPACK_BUFFER将像素数据从 PBO 传输。
在这里插入图片描述
例如,glReadPixels()glGetTexImage() 是“pack”像素操作,而 glDrawPixels()glTexImage2D()glTexSubImage2D() 是“unpack”操作。当 PBO 与 GL_PIXEL_PACK_BUFFER 令牌绑定时,glReadPixels()从 OpenGL 帧缓冲区读取像素数据并将数据写入(打包)到 PBO。当 PBO 与 GL_PIXEL_UNPACK_BUFFER 令牌绑定时,glDrawPixels()从 PBO 读取(解包)像素数据并将它们复制到 OpenGL 帧缓冲区。

PBO 的主要优点是通过 DMA(直接内存访问)与图形卡进行快速像素数据传输,而无需占用 CPU 周期。而且,PBO 的另一个优点是异步 DMA 传输。

让我们将传统的纹理传输方法与使用像素缓冲区对象进行比较。下图左侧是从图像源(图像文件或视频流)加载纹理数据的常规方式。材质源首先加载到系统内存中,然后使用 glTexImage2D() 从系统内存复制到 OpenGL 纹理对象。这 2 个传输过程(加载和复制)都由 CPU 执行。

相反,在右侧图中,图像源可以直接加载到 PBO 中,该 PBO 由 OpenGL 控制。 CPU 仍然涉及将源加载到 PBO,但不会将像素数据从 PBO 传输到纹理对象。相反,GPU(OpenGL 驱动程序)管理将数据从 PBO 复制到纹理对象。这意味着 OpenGL 在不浪费 CPU 周期的情况下执行 DMA 传输操作。此外,OpenGL 可以安排异步 DMA 传输以供以后执行。因此,`glTexImage2D()` 立即返回,CPU 可以执行其他操作而无需等待像素传输完成。

有两种主要的 PBO 方法可以提高像素数据传输的性能:流式纹理更新(streaming texture update)和从帧缓冲区异步回读(asynchronous read_back from the framebuffer)。

Creating PBO

如前所述,Pixel Buffer Object 借用了Vertex Buffer Object 的所有 API。唯一的区别是 PBO 有 2 个额外的令牌:GL_PIXEL_PACK_BUFFERGL_PIXEL_UNPACK_BUFFER

GL_PIXEL_PACK_BUFFER 用于将像素数据从 OpenGL 传输到您的应用程序,而 GL_PIXEL_UNPACK_BUFFER表示将像素数据从应用程序传输到 OpenGL。OpenGL 参考这些标记来确定 PBO 的最佳内存空间,例如,用于上传(unpacking)纹理的显存,或用于读取(pack)帧缓冲区的系统内存。但是,这些目标标记只是举例。 具体实现时OpenGL 驱动程序为您决定合适的位置。

Creating a PBO requires 3 steps:

  1. 使用glGenBuffers()生成一个新的缓冲区对象。
  2. 使用 glBindBuffer()绑定缓冲区对象。
  3. 使用 glBufferData()将像素数据复制到缓冲区对象。

如果在glBufferData() 中指定指向源数组的为 NULL 指针,则 PBO 仅分配具有给定数据大小的内存空间。 glBufferData() 的最后一个参数是 PBO 的另一个性能提示,用于提供如何使用缓冲区对象。

GL_STREAM_DRAW用于流式纹理上传,GL_STREAM_READ 用于异步帧缓冲区回读。

请查看 VBO文档 以获取更多详细信息。

Mapping PBO

PBO 提供了一种内存映射机制,将OpenGL 控制的缓冲区对象映射到客户端的内存地址空间。因此,客户端可以使用glMapBuffer()glUnmapBuffer()修改缓冲区对象的一部分或整个缓冲区。

void* glMapBuffer(GLenum target, GLenum access)

GLboolean glUnmapBuffer(GLenum target)

  • 1
  • 2
  • 3

如果映射成功,glMapBuffer()将返回指向缓冲区对象的指针。否则返回NULL。target参数是 GL_PIXEL_PACK_BUFFERGL_PIXEL_UNPACK_BUFFER。第二个参数,access 指定如何处理映射的缓冲区;从 PBO 读取数据 (GL_READ_ONLY),将数据写入 PBO (GL_WRITE_ONLY),或两者 (GL_READ_WRITE)。

如果 GPU 仍在使用缓冲区对象,则 glMapBuffer() 将不会返回,直到 GPU 使用相应的缓冲区对象完成其工作。为了避免这种停顿(等待),请在 glMapBuffer() 之前使用 NULL 指针调用 glBufferData()。然后,OpenGL 将丢弃旧缓冲区,并为缓冲区对象分配新的内存空间。

使用 PBO 后,缓冲区对象必须使用 glUnmapBuffer() 取消映射。如果成功,glUnmapBuffer()返回 GL_TRUE。否则,它返回 GL_FALSE

Others

为了最大化流传输性能,您可以使用多个像素缓冲区对象。该图显示同时使用 2 个 PBO; glTexSubImage2D()从 PBO 复制像素数据,同时将纹理源写入另一个 PBO。对于第 n 帧,PBO 1 用于 glTexSubImage2D(),PBO 2 用于获取新的纹理源。对于第 n+1 帧,2 个像素缓冲区正在切换角色并继续更新纹理。由于异步 DMA 传输,更新和复制过程可以同时执行。 CPU 将纹理源更新到 PBO,而 GPU 从另一个 PBO 复制纹理。
在这里插入图片描述
传统的 glReadPixels() 会阻塞管道并等待所有像素数据传输完毕。然后,它将控制权返回给应用程序。相反,带有 PBO 的 glReadPixels() 可以调度异步 DMA 传输并立即返回而不会停顿。因此,应用程序 (CPU) 可以立即执行其他进程,同时通过 OpenGL (GPU) 使用 DMA 传输数据。此演示使用 2 个像素缓冲区。在第 n 帧,应用程序使用 glReadPixels() 将像素数据从 OpenGL 帧缓冲区读取到 PBO 1,并在 PBO 2 中处理像素数据。这些读取和处理可以同时执行,因为 glReadPixels() 到 PBO 1 立即返回,CPU 立即开始处理 PBO 2 中的数据。并且,我们在每一帧上在 PBO 1 和 PBO 2 之间交替。
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值