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);
GLubyte* dst;
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo[0]);
dst = (GLubyte*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
av_assert0(dst);
memcpy(dst, data, W*H); // data is Y/U/V buffer
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // TODO
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, H); // image height in client memory
glPixelStorei(GL_UNPACK_ROW_LENGTH, W); // pitch in client memory
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, W, H, GL_RED, GL_UNSIGNED_BYTE, 0);
glGenTextures(1, texture);
glBindTexture(GL_TEXTURE_2D, texture[0]);
// 创建texture
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);
// 直接从bufDataPtr地址读取内容到texture
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, W, H, GL_RED, GL_UNSIGNED_BYTE, bufDataPtr);
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);
GLubyte* src;
glBindFramebuffer(GL_FRAMEBUFFER, fbo_render[0]);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glPixelStorei(GL_PACK_ALIGNMENT, 4); // TODO
glPixelStorei(GL_PACK_IMAGE_HEIGHT, H); // image height in client memory
glPixelStorei(GL_PACK_ROW_LENGTH, W); // pitch in client memory
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo_down[0]);
glReadPixels(0, 0, W, H, GL_RED, GL_UNSIGNED_BYTE, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
src = (GLubyte*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
av_assert0(src); // TODO
av_assert0(out->data[0]);
memcpy(out->data[0], src, W*H);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glReadPixels(0, 0, W, H, GL_RED, GL_UNSIGNED_BYTE, downloadData);