PBO(Pixel Buffer Object),将像素数据存储在显存中。
2、 高效并不在于传输更快,而在于与硬件相关的异步调用方式,调用之后CPU即返回执行其它操作(使用DMA方式的传输、由OpenGL直接控制)
3、在单个PBO情况下并不能得到很好的效果,毕竟传输过程仍然存在(但速度可能变快,比如显存内部的数据传输),但其异步性就提供了双PBO实现的可能性,用双PBO来进行加速
左图是传统的加载一个纹理的示意图,可以看到,首先将纹理图像读到内存,然后再从内存拷贝到纹理对象中,这两个过程均需CPU的参与;而右图采用了PBO加载纹理,首先将像素数据加载到PBO中,然后通过DMA方式传送到纹理对象,最后一步不需要CPU参与。
1. 用glGenBuffersARB()生成缓存对象;
2. 用glBindBufferARB()绑定缓存对象;
3. 用glBufferDataARB()将像素数据拷贝到缓存对象。
void* glMapBufferARB(GLenum target, GLenum access);
GLboolean glUnmapBufferARB(GLenum target);
GL_PIXEL_PACK_BUFFER_ARB 将像素数据传给PBO
GL_PIXEL_UNPACK_BUFFER_ARB 从PBO得到像素数据
比如说,glReadPixel就是从帧缓存中读取数据,写到PBO中,可理解为“pack”;glDrawPixel是从PBO中读取数据,写到到帧缓存,可理解为“unpack”;glGetTexImage是从纹理对象到PBO,可理解为“pack”;glTexImage2d从PBO写到纹理对象(texture object),可理解为“unpack”。
#include <iostream> #include <gl/glew.h> #include <gl/glut.h> using namespace std; GLuint pboIds[2]; GLuint textureId; // Storage For 6 face Textures //camera float cameraAngleX; float cameraAngleY; float cameraDistance = -5.0; //mouse bool mouseLeftDown; bool mouseRightDown; float LastXPos; float LastYPos; const int IMAGE_WIDTH = 1024; const int IMAGE_HEIGHT = 1024; const int CHANNEL_COUNT = 4; const int DATA_SIZE = IMAGE_WIDTH * IMAGE_HEIGHT * CHANNEL_COUNT; GLubyte* imageData = 0; void initGL() { glEnable(GL_TEXTURE_2D); // Enable Texture Mapping glShadeModel(GL_SMOOTH); // Enable Smooth Shading glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background glGenTextures(1, &textureId); glBindTexture(GL_TEXTURE_2D, textureId); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, IMAGE_WIDTH, IMAGE_HEIGHT, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)imageData); glBindTexture(GL_TEXTURE_2D, 0); glGenBuffers(2, pboIds); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[0]); glBufferData(GL_PIXEL_UNPACK_BUFFER, DATA_SIZE, 0, GL_STREAM_DRAW); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[1]); glBufferData(GL_PIXEL_UNPACK_BUFFER, DATA_SIZE, 0, GL_STREAM_DRAW); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } void updatePixels(GLubyte* dst, int size) { static int color = 0; if(!dst) return; int* ptr = (int*)dst; // copy 4 bytes at once for(int i = 0; i < IMAGE_HEIGHT; ++i) { for(int j = 0; j < IMAGE_WIDTH; ++j) { *ptr = color; ptr++; } color += 257; // add an arbitary number (no meaning) } ++color; // scroll down } void display() { static int index = 0; int nextIndex = 0; index = (index + 1) % 2; nextIndex = (index + 1) % 2; 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, IMAGE_WIDTH, IMAGE_HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, 0); // bind PBO to update pixel values glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[nextIndex]); glBufferData(GL_PIXEL_UNPACK_BUFFER, DATA_SIZE, 0, GL_STREAM_DRAW); 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 pointer to mapping buffer } glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glClear(GL_COLOR_BUFFER_BIT); glPushMatrix(); // tramsform camera glTranslatef(0, 0, cameraDistance); glRotatef(cameraAngleX, 1, 0, 0); // pitch glRotatef(cameraAngleY, 0, 1, 0); // heading glBindTexture(GL_TEXTURE_2D, textureId); glColor4f(1, 1, 1, 1); glBegin(GL_QUADS); glNormal3f(0, 0, 1); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f); glEnd(); glBindTexture(GL_TEXTURE_2D, 0); glPopMatrix(); glutSwapBuffers(); } void reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0f, (GLfloat)w/(GLfloat)h, 0.01f, 100.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void mouse(int button, int state, int x, int y) { if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { mouseLeftDown = true; LastXPos = x; LastYPos = y; } if (button == GLUT_RIGHT_BUTTON && state == GLUT_UP) { mouseRightDown = true; } } void mouseMotion(int x, int y) { if (mouseLeftDown) { cameraAngleX += GLfloat(y - LastYPos)/GLfloat(20.0); cameraAngleY += GLfloat(x - LastXPos)/GLfloat(20.0); LastYPos = y; LastXPos = x; } } int main(int argc, char **argv) { imageData = new GLubyte[DATA_SIZE]; memset(imageData, 0, DATA_SIZE); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); glutInitWindowSize(480, 640); glutInitWindowPosition(100, 100); glutCreateWindow("PBO"); glewInit(); if(!glewIsSupported("GL_VERSION_2_0")) { fprintf(stderr, "ERROR: Support for necessary OpengGL extensions missing."); } initGL(); glutDisplayFunc(display); glutIdleFunc(display); glutReshapeFunc(reshape); glutMouseFunc(mouse); glutMotionFunc(mouseMotion); glutMainLoop(); glDeleteBuffers(2, pboIds); glDeleteTextures(1, &textureId); delete []imageData; return 0; }
参考:http://www.songho.ca/opengl/gl_pbo.html