Published

11 October 2013

Tags

Contents

为了让图形课的作业可以用 OpenCL 做实时绘制,于是乎开始折腾如何让 OpenCL 中的图片可以高效的渲染到屏幕上,饶了一大圈学了不少 OpenGL 的用法,最后最好的解决方案也只能是共享texture/image,然后在 OpenGL中在绘制一遍。


PBO

首先是尝试使用 PBO(Pixel Buffer Object) 来和OpenCL交互,使用起来非常方便,和之前文章提到的 VBO 用法是一样的。

创建了 PBO 以后,直接用以下代码就可以绘制到

//绑定pbo
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[index]);
//绘制,因为我们已经 bind pbo 了,所以 data 参数设置为 0, opengl就会从pbo中读数据
void glDrawPixels(width, height, format, type, 0);

看 OpenGL 的介绍:“glDrawPixels — write a block of pixels to the frame buffer”,应该很快吧,都直接写到帧缓存里了。

但是实际结果是比较慢的,800*600 需要花将近4ms,fps300多,1920*1080就要接近10ms,自己直接把数据从 OpenCL 拷到 CPU 再拷到 OpenGL,发现速度是一样的。

事后认真看了下 PBO 的介绍,发现 PBO 分两种:

  • GL_PIXEL_UNPACK_BUFFER_ARB 用于从CPU传送数据给GPU
  • GL_PIXEL_PACK_BUFFER_ARB 用于从GPU传送数据给CPU

我一开始就理解错了,这玩意本来就只是用来和 CPU 传送数据的,于是这个方法失败


FBO

于是乎开始折腾 FBO, 网上找到这么个 PPT 讲的比较清楚,这个 PPT 中实现的内容大概是用 OpenGL 渲染出一个图片以后,传给 OpenCL 做一次高斯模糊再显示出来,用法如下:

// 创建并绑定FBO
glGenFramebuffersEXT(1, &fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
// 创建一个RenderBuffer
glGenRenderbuffersEXT(1, &rb_color);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rb_color);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, width, height);
// 吧rbo挂载到FBO的颜色缓冲区
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, rb_color);

// 从gl创建cl buffer
cl_mem cl_scene; 
cl_scene = clCreateFromGLRenderbuffer(cxGPUContext, CL_MEM_READ_ONLY, rb_color, 0);

用法也比较简单,在 OpenCL 可以直接使用 image2d_t 对象接触,但是问题又来了,bind frame buffer以后屏幕就什么也显示不出来了,从文章中仔细阅读FBO以后大概理解了FBO的工作原理。

FBO(FrameBufferObject),被用来让用户创建FrameBuffer,一开始我们的图像系统为我们已经创建好了一个FrameBuffer,被称之为Default Framebuffer,它由若干个ColorBuffer,一个DepthBuffer,一个StencilBuffer构成,而 FBO 允许用户自己通过创建 RenderBufferTexture 来挂载管理这些不同的 buffer,他的用途多半用在渲染到 texture。

问题就出在,如果你bind了自己创建的frameBuffer,那么 OpenGL 就会吧图像渲染到你的 frameBuffer 里去,而 Default FrameBuffer 里还是空的,于是屏幕就黑了。

由于对 OpenGL 了解不深刻,也不清楚怎么直接管理 Default FrameBuffer ,尝试了下直接 bind renderbuffer 到 Default FrameBuffer 上去返回无效操作,于是乎这个方法也作废了…

不过总的来说 FBO 还是很有用的。

update

OpenGPU 论坛上提问以后得到了大神的回复,使用 glBlitFramebuffer,这个函数是 OpenGL3.0的标准。

尝试了一番以后发现速度和第三个方法差不多。


Texture/image

最后的方法还是回归到直接让 OpenGL 和 OpenCL 共享 Texture,然后用 OpenGL 绘制。

绘制的话直接用了以下代码:

glEnable(GL_TEXTURE_RECTANGLE);

bind();
glBegin ( GL_QUADS );
    glTexCoord2f (     0,      0 ); glVertex2f ( -1.0F, -1.0F );
    glTexCoord2f (     0, height ); glVertex2f ( -1.0F,  1.0F );
    glTexCoord2f ( width, height ); glVertex2f (  1.0F,  1.0F );
    glTexCoord2f ( width,      0 ); glVertex2f (  1.0F, -1.0F );
glEnd ( );
unBind();

glDisable(GL_TEXTURE_RECTANGLE);

似乎有更快的方法,使用 glDrawPixels,但是上面这方法绘制1080P每一帧也只用了 0.1ms ,于是乎懒得再继续折腾了

另外有个蛋疼的事情是, OpenCL 中的 float3 实际上就是 float4,如果你用一个 float3 指针指向一个地址修改,他会把最后第四位赋值为 0,这点挺坑爹的。


参考文章

  1. FBO,PBO,VBO都介绍的超详细而且带demo
  2. OpenCL,OpenGL,GLSL实现的光线跟踪
  3. [1]的翻译
  4. OpenCL操作FBO


blog comments powered by Disqus