OpenGL FBO ,PBO ,VBO

本文深入解析OpenGL Pixel Buffer Object (PBO)的概念与使用方法,强调其通过DMA进行快速像素数据传输的优势,尤其在纹理更新与帧缓冲读回操作中表现突出。文章详细介绍了PBO的创建、映射机制及两种主要应用:流式纹理更新和异步帧缓冲读回,通过实例代码展示如何利用PBO提升性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://www.songho.ca/opengl/gl_pbo.html

 

Overview

OpenGL Pixel Buffer Object (PBO) 
OpenGL PBO

OpenGL ARB_pixel_buffer_object extension is very close to ARB_vertex_buffer_object. It simply expands ARB_vertex_buffer_object extension in order to store not only vertex data but also pixel data into the buffer objects. This buffer object storing pixel data is called Pixel Buffer Object (PBO). ARB_pixel_buffer_object extension borrows all VBO framework and APIs, plus, adds 2 additional "target" tokens. These tokens assist the PBO memory manger (OpenGL driver) to determine the best location of the buffer object; system memory, shared memory or video memory. Also, the target tokens clearly specify that the bound PBO will be used in one of 2 different operations; GL_PIXEL_PACK_BUFFER to transfer pixel data to a PBO, or GL_PIXEL_UNPACK_BUFFER to transfer pixel data from PBO.

For example, glReadPixels() and glGetTexImage() are "pack" pixel operations, and glDrawPixels(), glTexImage2D() and glTexSubImage2D() are "unpack" operations. When a PBO is bound with GL_PIXEL_PACK_BUFFER token, glReadPixels() reads pixel data from a OpenGL framebuffer and write (pack) the data into the PBO. When a PBO is bound with GL_PIXEL_UNPACK_BUFFER token, glDrawPixels() reads (unpack) pixel data from the PBO and copy them to OpenGL framebuffer.

The main advantage of PBO is fast pixel data transfer to and from a graphics card through DMA (Direct Memory Access) without involing CPU cycles. And, the other advantage of PBO is asynchronous DMA transfer. Let's compare a conventional texture transfer method with using a Pixel Buffer Object. The left side of the following diagram is a conventional way to load texture data from an image source (image file or video stream). The source is first loaded into the system memory, and then, copied from the system memory to an OpenGL texture object with glTexImage2D(). These 2 transfer processes (load and copy) are all performed by CPU.

Conventional texture loading 
Texture loading without PBO

Texture loading with PBO 
Texture loading with PBO

On the contrary in the right side diagram, the image source can be directly loaded into a PBO, which is controlled by OpenGL. CPU still involves to load the source to the PBO, but, not for transferring the pixel data from a PBO to a texture object. Instead, GPU (OpenGL driver) manages copying data from a PBO to a texture object. This means OpenGL performs a DMA transfer operation without wasting CPU cycles. Further, OpenGL can schedule an asynchronous DMA transfer for later execution. Therefore, glTexImage2D() returns immediately, and CPU can perform something else without waiting the pixel transfer is done.

There are 2 major PBO approaches to improve the performance of the pixel data transfer: streaming texture update and asynchronous read-back from the framebuffer.

Creating PBO

As mentioned earlier, Pixel Buffer Object borrows all APIs from Vertex Buffer Object. The only difference is there are 2 additional tokens for PBOs: GL_PIXEL_PACK_BUFFER and GL_PIXEL_UNPACK_BUFFER. GL_PIXEL_PACK_BUFFER is for transferring pixel data from OpenGL to your application, and GL_PIXEL_UNPACK_BUFFER means transferring pixel data from an application to OpenGL. OpenGL refers to these tokens to determine the best memory space of a PBO, for example, a video memory for uploading (unpacking) textures, or system memory for reading (packing) the framebuffer. However, these target tokens are solely hint. OpenGL driver decides the appropriate location for you.

Creating a PBO requires 3 steps;

  1. Generate a new buffer object with glGenBuffers().
  2. Bind the buffer object with glBindBuffer().
  3. Copy pixel data to the buffer object with glBufferData().

If you specify a NULL pointer to the source array in glBufferData(), then PBO allocates only a memory space with the given data size. The last parameter of glBufferData() is another performance hint for PBO to provide how the buffer object will be used. GL_STREAM_DRAW is for streaming texture upload and GL_STREAM_READ is for asynchronous framebuffer read-back.

Please check VBO for more details.

Mapping PBO

PBO provides a memory mapping mechanism to map the OpenGL controlled buffer object to the client's memory address space. So, the client can modify a portion of the buffer object or the entire buffer by using glMapBuffer()and glUnmapBuffer().


void* glMapBuffer(GLenum target, GLenum access)

GLboolean glUnmapBuffer(GLenum target)

glMapBuffer() returns the pointer to the buffer object if success. Otherwise it returns NULL. The target parameter is either GL_PIXEL_PACK_BUFFER or GL_PIXEL_UNPACK_BUFFER. The second parameter, accessspecifies what to do with the mapped buffer; read data from the PBO (GL_READ_ONLY), write data to the PBO (GL_WRITE_ONLY), or both (GL_READ_WRITE).

Note that if GPU is still working with the buffer object, glMapBuffer() will not return until GPU finishes its job with the corresponding buffer object. To avoid this stall(wait), call glBufferData() with NULL pointer right before glMapBuffer(). Then, OpenGL will discard the old buffer, and allocate new memory space for the buffer object.

The buffer object must be unmapped with glUnmapBuffer() after use of the PBO. glUnmapBuffer() returns GL_TRUE if success. Otherwise, it returns GL_FALSE.

Example: Streaming Texture Uploads

Streaming Texture with PBO

Download the source and binary: pboUnpack.zip (Updated: 2018-09-05).

This demo application uploads (unpack) streaming textures to an OpenGL texture object using PBO. You can switch to the different transfer modes (single PBO, double PBOs and without PBO) by pressing the space key, and compare the performance differences.

The texture sources are written directly on the mapped pixel buffer every frame in the PBO modes. Then, these data are transferred from the PBO to a texture object using glTexSubImage2D(). By using PBO, OpenGL can perform asynchronous DMA transfer between a PBO and a texture object. It significantly increases the texture upload performance. If asynchronous DMA transfer is supported, glTexSubImage2D() should return immediately, and CPU can process other jobs without waiting the actual texture copy.

Streaming texture uploads with 2 PBOs 
Streaming texture uploads with 2 PBOs

To maximize the streaming transfer performance, you may use multiple pixel buffer objects. The diagram shows that 2 PBOs are used simultaneously; glTexSubImage2D() copies the pixel data from a PBO while the texture source is being written to the other PBO.

For nth frame, PBO 1 is used for glTexSubImage2D() and PBO 2 is used to get new texture source. For n+1th frame, 2 pixel buffers are switching the roles and continue to update the texture. Because of asynchronous DMA transfer, the update and copy processes can be performed simultaneously. CPU updates the texture source to a PBO while GPU copies texture from the other PBO.


// "index" is used to copy pixels from a PBO to a texture object
// "nextIndex" is used to update pixels in the other PBO
index = (index + 1) % 2;
nextIndex = (index + 1) % 2;

// bind the texture and PBO
glBindTexture(GL_TEXTURE_2D, textureId);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[index]);

// copy pixels from PBO to texture object
// Use offset instead of ponter.
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, 0);


// bind PBO to update texture source
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[nextIndex]);

// Note that glMapBuffer() causes sync issue.
// If GPU is working with this buffer, glMapBuffer() will wait(stall)
// until GPU to finish its job. To avoid waiting (idle), you can call
// first glBufferData() with NULL pointer before glMapBuffer().
// If you do that, the previous data in PBO will be discarded and
// glMapBuffer() returns a new allocated pointer immediately
// even if GPU is still working with the previous data.
glBufferData(GL_PIXEL_UNPACK_BUFFER, DATA_SIZE, 0, GL_STREAM_DRAW);

// map the buffer object into client's memory
GLubyte* ptr = (GLubyte*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
if(ptr)
{
    // update data directly on the mapped buffer
    updatePixels(ptr, DATA_SIZE);
    glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release the mapped buffer
}

// it is good idea to release PBOs with ID 0 after use.
// Once bound with 0, all pixel operations are back to normal ways.
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

Example: Asynchronous Read-back

Asynchronous readback with PBO

Download the source and binary: pboPack.zip (Updated: 2018-09-05).

This demo application reads (pack) the pixel data from the framebuffer (left-side) to a PBO, then, draws it back to the right side of the window after modifying the brightness of the image. You can toggle PBO on/off by pressing the space key, and measure the performance of glReadPixels().

Conventional glReadPixels() blocks the pipeline and waits until all pixel data are transferred. Then, it returns control to the application. On the contrary, glReadPixels() with PBO can schedule asynchronous DMA transfer and returns immediately without stall. Therefore, the application (CPU) can execute other process right away, while transferring data with DMA by OpenGL (GPU).

Asynchronous readback with 2 PBOs 
Asynchronous glReadPixels() with 2 PBOs

This demo uses 2 pixel buffers. At frame n, the application reads the pixel data from OpenGL framebuffer to PBO 1using glReadPixels(), and processes the pixel data in PBO 2. These read and process can be performed simultaneously, because glReadPixels() to PBO 1 returns immediately and CPU starts to process data in PBO 2 without delay. And, we alternate between PBO 1 and PBO 2 on every frame.


// "index" is used to read pixels from framebuffer to a PBO
// "nextIndex" is used to update pixels in the other PBO
index = (index + 1) % 2;
nextIndex = (index + 1) % 2;

// set the target framebuffer to read
glReadBuffer(GL_FRONT);

// read pixels from framebuffer to PBO
// glReadPixels() should return immediately.
glBindBuffer(GL_PIXEL_PACK_BUFFER, pboIds[index]);
glReadPixels(0, 0, WIDTH, HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, 0);

// map the PBO to process its data by CPU
glBindBuffer(GL_PIXEL_PACK_BUFFER, pboIds[nextIndex]);
GLubyte* ptr = (GLubyte*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
if(ptr)
{
    processPixels(ptr, ...);
    glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
}

// back to conventional pixel operation
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
### OpenGLFBOPBO的概念及用法 #### 一、FBO(Frame Buffer Object) FBO是一种可以替代默认帧缓冲区的对象,允许开发者自定义渲染目标。通过将纹理或其他存储对象附加到FBO的不同附件点上,可以在GPU内存中实现离屏渲染。 - **创建与绑定** 创建FBO需要调用`glGenFramebuffers()`函数生成一个或多个FBO名称,并使用`glBindFramebuffer()`将其绑定到当前上下文中[^4]。 ```cpp GLuint fbo; glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); ``` - **附着纹理** 可以通过`glFramebufferTexture2D()`将2D纹理图像关联到FBO上。此操作会指定哪个纹理作为颜色、深度或模板缓冲区的一部分[^4]。 ```cpp glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, textureId, 0); ``` - **验证完整性** 在完成所有必要的配置之后,应该检查FBO的状态是否有效。这可以通过调用`glCheckFramebufferStatus()`来完成[^4]。 ```cpp GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if(status != GL_FRAMEBUFFER_COMPLETE){ // Handle error } ``` #### 二、PBO(Pixel Buffer Object) PBO主要用于加速像素数据传输过程中的性能瓶颈问题。它提供了两种模式:一种是从CPU发送数据至GPU(写入),另一种是从GPU获取数据回到CPU(读取)。这两种功能分别对应于`GL_PIXEL_UNPACK_BUFFER`和`GL_PIXEL_PACK_BUFFER`这两个枚举值[^2]。 - **分配空间** 类似于VBO的操作方式,先生成再绑定最后分配所需大小的空间给这个新建立起来的PBO实例[^2]。 ```cpp GLuint pbo; glGenBuffers(1,&pbo); glBindBuffer(GL_PIXEL_UNPACK_BUFFER,pbo); glBufferData(GL_PIXEL_UNPACK_BUFFER,sizeOfData,GL_STREAM_DRAW); ``` - **上传/下载数据** 当设置好相应的参数后就可以利用标准API接口像平常一样处理这些资源了只是此时它们已经被映射到了特定类型的PBO当中去了而已[^3]。 对于上传情况来说就是简单的调用了下面这样的命令序列即可完成整个流程: ```cpp glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboId); glTexImage2D(...); // or glTexSubImage2D(...) ``` 对于下载而言则稍微复杂一点因为还需要额外考虑同步机制等问题不过基本原理还是相同的即先把目标区域的数据拷贝出来然后再交给应用程序进一步解析或者保存下来等等用途上去做后续工作罢了[^2]。 --- ### 区别总结表 | 特性 | FBO | PBO | |--------------|-----------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| | 主要作用 | 提供了一个灵活的方式来管理渲染输出的目标 | 加快像素级数据传送的速度 | | 关键字 | `GL_FRAMEBUFFER`, `GL_RENDERBUFFER`, etc | `GL_PIXEL_UNPACK_BUFFER`, `GL_PIXEL_PACK_BUFFER` | | 数据流向 | 支持多种类型的数据连接,比如颜色缓存、深度测试结果等 | 单向流动——要么从应用层传递到显卡内部去绘制图形;要么反过来由后者返回前者以便查询状态变化之类的场景下使用 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值