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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 像素缓冲区对象(PBO)的异步Read-Back 源码解析

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


原理示意图如下:

image.png

关键代码如下:

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();// 切换前后缓冲区
}

程序的执行对比情况:


image.png

可见通过 PBO的异步read-back技术,FPS 还是有明显提高的(我的显卡是 GeForce GTX 970,CPU 是 i7-6700 HQ,主频为2.6GHz)。


目录
相关文章
|
2天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
2天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
2天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
21天前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
3天前
|
安全 搜索推荐 数据挖掘
陪玩系统源码开发流程解析,成品陪玩系统源码的优点
我们自主开发的多客陪玩系统源码,整合了市面上主流陪玩APP功能,支持二次开发。该系统适用于线上游戏陪玩、语音视频聊天、心理咨询等场景,提供用户注册管理、陪玩者资料库、预约匹配、实时通讯、支付结算、安全隐私保护、客户服务及数据分析等功能,打造综合性社交平台。随着互联网技术发展,陪玩系统正成为游戏爱好者的新宠,改变游戏体验并带来新的商业模式。
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
76 2
|
2月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
79 0
|
2月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
65 0
|
2月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
69 0
|
2月前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
91 0

推荐镜像

更多