像素缓冲区对象(PBO)的异步Read-Back 源码解析

来源:互联网 发布:淘宝网如何注销账户 编辑:程序博客网 时间:2024/05/01 00:38


接这篇文章 OpenGL深入探索——像素缓冲区对象 (PBO)(附完整工程代码地址)


原理示意图如下:



关键代码如下:

int main(int argc, char **argv){    initSharedMem();    // register exit callback    atexit(exitCB);    // init GLUT and GL    initGLUT(argc, argv);    initGL();    // get OpenGL info    glInfo glInfo;    glInfo.getInfo();    glInfo.printSelf();#ifdef _WIN32    // 检查视频显卡是否支持 PBO    if (glInfo.isExtensionSupported("GL_ARB_pixel_buffer_object"))    {        // get pointers to GL functions        glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffersARB");        glBindBufferARB = (PFNGLBINDBUFFERARBPROC)wglGetProcAddress("glBindBufferARB");        glBufferDataARB = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress("glBufferDataARB");        glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)wglGetProcAddress("glBufferSubDataARB");        glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)wglGetProcAddress("glDeleteBuffersARB");        glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)wglGetProcAddress("glGetBufferParameterivARB");        glMapBufferARB = (PFNGLMAPBUFFERARBPROC)wglGetProcAddress("glMapBufferARB");        glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)wglGetProcAddress("glUnmapBufferARB");        // check once again PBO extension        if (glGenBuffersARB && glBindBufferARB && glBufferDataARB && glBufferSubDataARB &&                glMapBufferARB && glUnmapBufferARB && glDeleteBuffersARB && glGetBufferParameterivARB)        {            pboSupported = pboUsed = true;            std::cout << "Video card supports GL_ARB_pixel_buffer_object." << std::endl;        }        else        {            pboSupported = pboUsed = false;            std::cout << "Video card does NOT support GL_ARB_pixel_buffer_object." << std::endl;        }    }    // check EXT_swap_control is supported    if (glInfo.isExtensionSupported("WGL_EXT_swap_control"))    {        // get pointers to WGL functions        wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");        wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT");        if (wglSwapIntervalEXT && wglGetSwapIntervalEXT)        {            // disable v-sync            wglSwapIntervalEXT(0);            std::cout << "Video card supports WGL_EXT_swap_control." << std::endl;        }    }#else // for linux, do not need to get function pointers, it is up-to-date    if (glInfo.isExtensionSupported("GL_ARB_pixel_buffer_object"))    {        pboSupported = pboUsed = true;        std::cout << "Video card supports GL_ARB_pixel_buffer_object." << std::endl;    }    else    {        pboSupported = pboUsed = false;        std::cout << "Video card does NOT support GL_ARB_pixel_buffer_object." << std::endl;    }#endif    if (pboSupported)    {        // create 2 pixel buffer objects, you need to delete them when program exits.//(创建两个 PBO)        // glBufferDataARB with NULL pointer reserves only memory space.        glGenBuffersARB(PBO_COUNT, pboIds);        glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[0]);        glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_READ_ARB);        glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[1]);        glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_READ_ARB);        glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);    }    // start timer, the elapsed time will be used for updateVertices()    timer.start();    // the last GLUT call (LOOP)    // window will be shown and display callback is triggered by events    // NOTE: this call never return main().    glutMainLoop(); /* Start GLUT event-processing loop */    return 0;}

省略掉中间其他一些无关代码,剩下关键方法:

void displayCB(){    static int shift = 0;    static int index = 0;    int nextIndex = 0;                  // pbo index used for next frame    // brightness shift amount    shift = ++shift % 200;    // increment current index first then get the next index(增加当前的index,再获得 nextIndex)    // "index" is used to read pixels from a framebuffer to a PBO(从 FB 中读取像素到 index 指定的 PBO 中)    // "nextIndex" is used to process pixels in the other PBO(nextIndex 指定 PBO 中待处理的像素[先前从 FB 中读取])    index = (index + 1) % 2;// 两个 PBO 交替    nextIndex = (index + 1) % 2;    // set the framebuffer to read(设置当前读取的 FB)    glReadBuffer(GL_FRONT);    if (pboUsed) // with PBO    {        // read framebuffer ///////////////////////////////        t1.start();  // 启动计时器,计算读取 FB 到 PBO 的时间        // copy pixels from framebuffer to PBO (从 FB 中拷贝像素到 index指定的 PBO 中)        // Use offset instead of ponter.(注意glReadPixels最后一个参数不是指针,而是偏移量)        // OpenGL should perform asynch DMA transfer, so glReadPixels() will return immediately.//(OpenGL 将执行一个异步的DMA[Direcct Memory Access],所以 glReadPixels方法会立刻返回,不会阻塞CPU时间)        glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[index]);        glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, PIXEL_FORMAT, GL_UNSIGNED_BYTE, 0);        // measure the time reading framebuffer        t1.stop();        readTime = t1.getElapsedTimeInMilliSec();        ///////////////////////////////////////////////////        // process pixel data /////////////////////////////        t1.start(); // 启动计时器,计算处理 PBO中像素的时间        // map the PBO that contain framebuffer pixels before processing it//(映射存储着先前 FB 像素的 PBO ,便于处理像素)        glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[nextIndex]);// 后续的 add() 并没有改变 PBO 中的像素值,计算的结果保存在 colorBuffer 中,所以标记为只读        GLubyte *src = (GLubyte *)glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);        if (src)        {            // change brightness            add(src, SCREEN_WIDTH, SCREEN_HEIGHT, shift, colorBuffer);            glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);     // release pointer to the mapped buffer        }        // measure the time processing the pixels of PBO        t1.stop();        processTime = t1.getElapsedTimeInMilliSec();        ///////////////////////////////////////////////////        glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);    }    else        // without PBO    {        // read framebuffer ///////////////////////////////        t1.start();// 不使用 PBO 的情况下,最后一个参数就是保存读取数据的指针        glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, PIXEL_FORMAT, GL_UNSIGNED_BYTE, colorBuffer);        // measure the time reading framebuffer        t1.stop();        readTime = t1.getElapsedTimeInMilliSec();        ///////////////////////////////////////////////////        // covert to greyscale ////////////////////////////        t1.start();        // change brightness        add(colorBuffer, SCREEN_WIDTH, SCREEN_HEIGHT, shift, colorBuffer);        // measure the time reading framebuffer        t1.stop();        processTime = t1.getElapsedTimeInMilliSec();        ///////////////////////////////////////////////////    }    // render to the framebuffer //////////////////////////    glDrawBuffer(GL_BACK);// 设定绘制在后缓冲区    toPerspective(); // set to perspective on the left side of the window(当前窗口左侧的投影矩阵)    // clear buffer    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);    // tramsform camera    glTranslatef(0, 0, -cameraDistance);    glRotatef(cameraAngleX, 1, 0, 0);   // pitch    glRotatef(cameraAngleY, 0, 1, 0);   // heading    // draw a cube(在左侧绘制普通的立方体)    glPushMatrix();    draw();    glPopMatrix();    // draw the read color buffer to the right side of the window//(在右侧绘制之前处理的 colorBuffer)    toOrtho();      // set to orthographic on the right side of the window(窗口右侧的正交矩阵[想象为把左侧的图处理一下直接贴上去])    glRasterPos2i(0, 0);// 设置字体光栅的位置    glDrawPixels(SCREEN_WIDTH, SCREEN_HEIGHT, PIXEL_FORMAT, GL_UNSIGNED_BYTE, colorBuffer);    // draw info messages    showInfo();    printTransferRate();    glutSwapBuffers();// 切换前后缓冲区}



程序的执行对比情况:




 可见通过 PBO的异步read-back技术,FPS 还是略有提高的。



0 0
原创粉丝点击