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

举报
ShaderJoy 发表于 2021/12/30 01:19:22 2021/12/30
【摘要】 接这篇文章 OpenGL深入探索——像素缓冲区对象 (PBO)(附完整工程代码地址) 原理示意图如下: 关键代码如下: int main(int argc, char **argv){ initSharedMem(); // register exit callback atexit(exitC...


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


原理示意图如下:



关键代码如下:


  
  1. int main(int argc, char **argv)
  2. {
  3. initSharedMem();
  4. // register exit callback
  5. atexit(exitCB);
  6. // init GLUT and GL
  7. initGLUT(argc, argv);
  8. initGL();
  9. // get OpenGL info
  10. glInfo glInfo;
  11. glInfo.getInfo();
  12. glInfo.printSelf();
  13. #ifdef _WIN32
  14. // 检查视频显卡是否支持 PBO
  15. if (glInfo.isExtensionSupported("GL_ARB_pixel_buffer_object"))
  16. {
  17. // get pointers to GL functions
  18. glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffersARB");
  19. glBindBufferARB = (PFNGLBINDBUFFERARBPROC)wglGetProcAddress("glBindBufferARB");
  20. glBufferDataARB = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress("glBufferDataARB");
  21. glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)wglGetProcAddress("glBufferSubDataARB");
  22. glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)wglGetProcAddress("glDeleteBuffersARB");
  23. glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)wglGetProcAddress("glGetBufferParameterivARB");
  24. glMapBufferARB = (PFNGLMAPBUFFERARBPROC)wglGetProcAddress("glMapBufferARB");
  25. glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)wglGetProcAddress("glUnmapBufferARB");
  26. // check once again PBO extension
  27. if (glGenBuffersARB && glBindBufferARB && glBufferDataARB && glBufferSubDataARB &&
  28. glMapBufferARB && glUnmapBufferARB && glDeleteBuffersARB && glGetBufferParameterivARB)
  29. {
  30. pboSupported = pboUsed = true;
  31. std::cout << "Video card supports GL_ARB_pixel_buffer_object." << std::endl;
  32. }
  33. else
  34. {
  35. pboSupported = pboUsed = false;
  36. std::cout << "Video card does NOT support GL_ARB_pixel_buffer_object." << std::endl;
  37. }
  38. }
  39. // check EXT_swap_control is supported
  40. if (glInfo.isExtensionSupported("WGL_EXT_swap_control"))
  41. {
  42. // get pointers to WGL functions
  43. wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
  44. wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT");
  45. if (wglSwapIntervalEXT && wglGetSwapIntervalEXT)
  46. {
  47. // disable v-sync
  48. wglSwapIntervalEXT(0);
  49. std::cout << "Video card supports WGL_EXT_swap_control." << std::endl;
  50. }
  51. }
  52. #else // for linux, do not need to get function pointers, it is up-to-date
  53. if (glInfo.isExtensionSupported("GL_ARB_pixel_buffer_object"))
  54. {
  55. pboSupported = pboUsed = true;
  56. std::cout << "Video card supports GL_ARB_pixel_buffer_object." << std::endl;
  57. }
  58. else
  59. {
  60. pboSupported = pboUsed = false;
  61. std::cout << "Video card does NOT support GL_ARB_pixel_buffer_object." << std::endl;
  62. }
  63. #endif
  64. if (pboSupported)
  65. {
  66. // create 2 pixel buffer objects, you need to delete them when program exits.
  67. //(创建两个 PBO)
  68. // glBufferDataARB with NULL pointer reserves only memory space.
  69. glGenBuffersARB(PBO_COUNT, pboIds);
  70. glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[0]);
  71. glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_READ_ARB);
  72. glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[1]);
  73. glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_READ_ARB);
  74. glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
  75. }
  76. // start timer, the elapsed time will be used for updateVertices()
  77. timer.start();
  78. // the last GLUT call (LOOP)
  79. // window will be shown and display callback is triggered by events
  80. // NOTE: this call never return main().
  81. glutMainLoop(); /* Start GLUT event-processing loop */
  82. return 0;
  83. }


  
  1. void displayCB()
  2. {
  3. static int shift = 0;
  4. static int index = 0;
  5. int nextIndex = 0; // pbo index used for next frame
  6. // brightness shift amount
  7. shift = ++shift % 200;
  8. // increment current index first then get the next index(增加当前的index,再获得 nextIndex)
  9. // "index" is used to read pixels from a framebuffer to a PBO(从 FB 中读取像素到 index 指定的 PBO 中)
  10. // "nextIndex" is used to process pixels in the other PBO(nextIndex 指定 PBO 中待处理的像素[先前从 FB 中读取])
  11. index = (index + 1) % 2; // 两个 PBO 交替
  12. nextIndex = (index + 1) % 2;
  13. // set the framebuffer to read(设置当前读取的 FB)
  14. glReadBuffer(GL_FRONT);
  15. if (pboUsed) // with PBO
  16. {
  17. // read framebuffer ///
  18. t1.start(); // 启动计时器,计算读取 FB 到 PBO 的时间
  19. // copy pixels from framebuffer to PBO (从 FB 中拷贝像素到 index指定的 PBO 中)
  20. // Use offset instead of ponter.(注意glReadPixels最后一个参数不是指针,而是偏移量)
  21. // OpenGL should perform asynch DMA transfer, so glReadPixels() will return immediately.
  22. //(OpenGL 将执行一个异步的DMA[Direcct Memory Access],所以 glReadPixels方法会立刻返回,不会阻塞CPU时间)
  23. glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[index]);
  24. glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, PIXEL_FORMAT, GL_UNSIGNED_BYTE, 0);
  25. // measure the time reading framebuffer
  26. t1.stop();
  27. readTime = t1.getElapsedTimeInMilliSec();
  28. ///
  29. // process pixel data /
  30. t1.start(); // 启动计时器,计算处理 PBO中像素的时间
  31. // map the PBO that contain framebuffer pixels before processing it
  32. //(映射存储着先前 FB 像素的 PBO ,便于处理像素)
  33. glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[nextIndex]);
  34. // 后续的 add() 并没有改变 PBO 中的像素值,计算的结果保存在 colorBuffer 中,所以标记为只读
  35. GLubyte *src = (GLubyte *)glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
  36. if (src)
  37. {
  38. // change brightness
  39. add(src, SCREEN_WIDTH, SCREEN_HEIGHT, shift, colorBuffer);
  40. glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); // release pointer to the mapped buffer
  41. }
  42. // measure the time processing the pixels of PBO
  43. t1.stop();
  44. processTime = t1.getElapsedTimeInMilliSec();
  45. ///
  46. glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
  47. }
  48. else // without PBO
  49. {
  50. // read framebuffer ///
  51. t1.start();
  52. // 不使用 PBO 的情况下,最后一个参数就是保存读取数据的指针
  53. glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, PIXEL_FORMAT, GL_UNSIGNED_BYTE, colorBuffer);
  54. // measure the time reading framebuffer
  55. t1.stop();
  56. readTime = t1.getElapsedTimeInMilliSec();
  57. ///
  58. // covert to greyscale
  59. t1.start();
  60. // change brightness
  61. add(colorBuffer, SCREEN_WIDTH, SCREEN_HEIGHT, shift, colorBuffer);
  62. // measure the time reading framebuffer
  63. t1.stop();
  64. processTime = t1.getElapsedTimeInMilliSec();
  65. ///
  66. }
  67. // render to the framebuffer //
  68. glDrawBuffer(GL_BACK); // 设定绘制在后缓冲区
  69. toPerspective(); // set to perspective on the left side of the window(当前窗口左侧的投影矩阵)
  70. // clear buffer
  71. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  72. // tramsform camera
  73. glTranslatef(0, 0, -cameraDistance);
  74. glRotatef(cameraAngleX, 1, 0, 0); // pitch
  75. glRotatef(cameraAngleY, 0, 1, 0); // heading
  76. // draw a cube(在左侧绘制普通的立方体)
  77. glPushMatrix();
  78. draw();
  79. glPopMatrix();
  80. // draw the read color buffer to the right side of the window
  81. //(在右侧绘制之前处理的 colorBuffer)
  82. toOrtho(); // set to orthographic on the right side of the window(窗口右侧的正交矩阵[想象为把左侧的图处理一下直接贴上去])
  83. glRasterPos2i(0, 0); // 设置字体光栅的位置
  84. glDrawPixels(SCREEN_WIDTH, SCREEN_HEIGHT, PIXEL_FORMAT, GL_UNSIGNED_BYTE, colorBuffer);
  85. // draw info messages
  86. showInfo();
  87. printTransferRate();
  88. glutSwapBuffers();// 切换前后缓冲区
  89. }



程序的执行对比情况:




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



文章来源: panda1234lee.blog.csdn.net,作者:panda1234lee,版权归原作者所有,如需转载,请联系作者。

原文链接:panda1234lee.blog.csdn.net/article/details/53270443

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。