使用Opengl PBO上传下载数据

来源:互联网 发布:ros系统 与 linux 编辑:程序博客网 时间:2024/06/06 03:52
PBO(pixel buffer object)是储存像素数据的缓冲区对象,主要有两个用途:
1. 上传一帧像素数据到PBO中,GL_UNPACK_ALIGNMENT

2. 从PBO中读取像素到cpu内存空间, GL_PIXEL_PACK_BUFFER


其实不使用PBO也可以上传下载数据,那么PBO好处何在?我们对比两种方式上传yuv420数据:

使用PBO的方法:

1. 创建用于上传像素数据的PBO buffer

glGenBuffers(1, pbo);glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo[i]);glBufferData(GL_PIXEL_UNPACK_BUFFER, data_size, NULL, GL_STREAM_DRAW);
2. 创建texture用于存储数据,只用GL_RED

glGenTextures(1, texture);glBindTexture(GL_TEXTURE_2D, texture[0]);glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, W, H, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

3. 对于一对PBO buffer和texture,首先pbo buffer绑定用于上传数据的GL_PIXEL_UNPACK_BUFFER

GLubyte* dst;glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo[0]);

4. 通过glMapBuffer拿到地址,并把需要上传的数据拷贝到其中

dst = (GLubyte*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);av_assert0(dst);memcpy(dst, data, W*H); // data is Y/U/V bufferglUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);

5. 绑定texture,通过调用glTexSubImage2D把pbo的数据拷贝到texture里

glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, texture[0]);glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // TODOglPixelStorei(GL_UNPACK_IMAGE_HEIGHT, H); // image height in client memoryglPixelStorei(GL_UNPACK_ROW_LENGTH, W); // pitch in client memoryglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, W, H, GL_RED, GL_UNSIGNED_BYTE, 0);

对比不使用PBO的方法,很简单,直接从memory地址读取:

glGenTextures(1, texture);glBindTexture(GL_TEXTURE_2D, texture[0]);// 创建textureglTexImage2D(GL_TEXTURE_2D, 0, GL_RED, W, H, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);// 直接从bufDataPtr地址读取内容到textureglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, W, H, GL_RED, GL_UNSIGNED_BYTE, bufDataPtr);



简单来说,对比不使用pbo的方法,glTexSubImage2D会造成cpu阻塞,而使用pbo的话glTexSubImage2D则会立刻返回。具体分析如下:

PBO的主要优点是可以通过DMA (Direct Memory Access) 快速地在显卡上传递像素数据,而不影响CPU的时钟周期(中断)。另一个优势是它还具备异步DMA传输。对比使用PBO前后的纹理传送方法。首先,资源被加载到系统内存中,然后使用glTexSubImage2D()函数从系统内存复制到OpenGL纹理对象中(Client->Server)。这两次数据传输(加载和复制)完全由CPU执行。


使用PBO的情况下,PBO是由OpenGL控制的。虽然CPU有参与加载纹理到PBO,但不涉及将像素数据从PBO传输到纹理对象的工作,而是由GPU(OpenGL驱动)来负责PBO到纹理对象的数据传输的,这也就意味着OpenGL执行DMA传输操作不会占用CPU的时钟周期。此外,OpenGL还可以安排稍后执行的异步DMA传输。所以glTexImage2D()或者glTexSubImage2D()可以立即返回,CPU也无需等待像素数据的传输了,可以继续其他工作。



下面是使用PBO的方式下载数据到cpu内存:

1. 创建PBO buffer

glGenBuffers(1, pbo_down);data_size = W * H;glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo_down[0]);glBufferData(GL_PIXEL_PACK_BUFFER, data_size, NULL, GL_STREAM_READ);

2. 绑定希望输出的FBO

GLubyte* src;glBindFramebuffer(GL_FRAMEBUFFER, fbo_render[0]); 

3. 准备读取GL_COLOR_ATTACHMENT0对应的texture内容

glReadBuffer(GL_COLOR_ATTACHMENT0);glPixelStorei(GL_PACK_ALIGNMENT, 4); // TODOglPixelStorei(GL_PACK_IMAGE_HEIGHT, H); // image height in client memoryglPixelStorei(GL_PACK_ROW_LENGTH, W);   // pitch in client memory

4. 绑定GL_PIXEL_PACK_BUFFER到刚创建的下载用pbo buffer

glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo_down[0]);


5. 通过glReadPixels从GL_COLOR_ATTACHMENT0读取纹理信息到绑定在GL_PIXEL_PACK_BUFFER上的pbo_down
glReadPixels(0, 0, W, H, GL_RED, GL_UNSIGNED_BYTE, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

6. 通过glMapBuffer拿到GL_PIXEL_PACK_BUFFER地址,通过memcpy拷贝出来到cpu内存

src = (GLubyte*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);av_assert0(src); // TODOav_assert0(out->data[0]);memcpy(out->data[0], src, W*H);glUnmapBuffer(GL_PIXEL_PACK_BUFFER);glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

相比之下,不使用pbo的简单方法,直接读取到cpu分配的内存downloadData里:

glReadPixels(0, 0, W, H, GL_RED, GL_UNSIGNED_BYTE, downloadData);







原创粉丝点击