CUDA和OpenGL互操作的实现及分析
刘进锋.郭雷
(西北工业大学 自动化学院,陕西西安710129)

CUDAOpenGL概述

OpenGL是图形硬件的软件接口,它是在SGI等多家世界著名的计算机公司的倡导下,以SGI的GL三维图形库为基础制定的一个通用、共享的、开放式的、性能卓越的三维图形标准。OpenGL在医学成像、地理信息、石油勘探、气候模拟以及娱乐动画上有着广泛应用,它已经成为高性能图形和交互式视景处理的工业标准。

OpenGL不是一种编程语言,而是一种API(应用程序编程接口)。程序员可以使用某种编程语言(如C或C++)编写绘图软件,其中调用了一个或多个OpenGL库函数。作为一种API,OpenGL遵循C语言的调用约定。OpenGL开发资料可参考文献[1]和参考文献[2]

图形处理器(GPU)原本是处理计算机图形的专用设备,近十年来,由于高清晰度复杂图形实时处理的需求,GPU发展成为高并行度、多线程、多核的处理器。目前,主流GPU的运算能力已超过主流通用CPU,从发展趋势上来看将来差距会越拉越大。为了合理地利用GPU 资源,CUDA(统一计算设备架构)应运而生。CUDA是一种由NVIDIA推出的通用并行计算架构[3],该架构使GPU能够解决复杂的计算问题,并且由于CUDA编程语言基于标准的C语言,从而大大提高了可编程性。

CUDA和OpenGL互操作的基本方式是使用CUDA生成数据,然后使用OpenGL在屏幕上绘制出数据所表示的图形。两者的结合可以通过两种方式来实现:

(1)使用OpenGL的PBO(像素缓冲区对象)。在该方式下,CUDA直接生成像素数据,OpenGL显示这些像素;

(2)使用OpenGL的VBO(顶点缓冲区对象)。在该方式下,CUDA生成顶点网格数据,OpenGL可以根据需要绘制出平滑的表面图或线框图或一系列顶点。

这两种方式的核心都是利用cudaGLMapBufferObject函数将OpenGL的缓冲区映射到CUDA的内存空间上,这样,程序员就可以充分利用CUDA的优点写出性能高的程序在该内存空间上生成数据,这些数据不需要传送,OpenGL可以直接使用。如果不使用CUDA,这些数据需要由CPU来计算产生。一方面,CPU的计算速度通常比GPU慢;另一方面,这些数据需要传送到GPU上以供OpenGL显示使用。鉴于此,当数据量很大时,CUDA和OpenGL的混合使用效果明显。

2 CUDA和OpenGL互操作的过程[4]

CUDA和OpenGL互操作具体步骤如下:

(1)创建窗口及OpenGL运行环境。

(2)设置OpenGL视口和坐标系。要根据绘制的图形是2D还是3D等具体情况设置。(1)和(2)是所有OpenGL程序必需的,这里也没什么特殊之处,需要注意的是,后面的一些功能需要OpenGL 2.0及以上版本支持,所以在这里需要进行版本检查。

(3)创建CUDA环境。可以使用cuGLCtxCreate或cudaGLSetGLDevice来设置CUDA环境。该设置一定要放在其他CUDA的API调用之前。

(4)产生一个或多个OpenGL缓冲区用以和CUDA共享。使用PBO和使用VBO差不多,只是有些函数调用参数不同。以下是具体过程。

GLuint bufferID;
glGenBuffers(1,&bufferID);//产生一个buffer ID
glBindBuffer(parameter1,bufferID);
//将其设置为当前非压缩缓冲区,如果是PBO方式,parameter1设置为GL_PIXEL_UNPACK_BUFFER,如果
是VBO方式,parameter1设置为GL_ARRAY_BUFFER
glBufferData(parameter1,parameter2,NULL,GL_DYNAMIC _COPY);
//给该缓冲区分配数据,PBO方式下,parameter1设置为GL_PIXEL_UNPACK_BUFFER,parameter1设置为图像的长度*宽度*4。VBO方式下,parameter1设置为GL_ARRAY_BUFFER,parameter2设置为顶点数*16,因为每个顶点包含3个浮点坐标(x,y,z)和4个颜色字节(RGBA),这样一个顶点包含16 B

(5)用CUDA登记缓冲区。登记可以使用cuGLRegisterBufferObject或
cudaGLRegisterBufferObject,该命令告诉OpenGL和CUDA 驱动程序该缓冲区为二者共同使用。

(6)将OpenGL缓冲区映射到CUDA内存。可以使用cuGLMapBufferObject或cudaGLMapBufferObject,它实际是将CUDA内存的指针指向OpenGL的缓冲区,这样如果只有一个GPU,就不需要数据传递。当映射完成后,OpenGL不能再使用该缓冲区。

(7)使用CUDA往该映射的内存写图像数据。前面的准备工作在这里真正发挥作用了,此时可以调用CUDA的kernel,像使用全局内存一样使用映射了的缓冲区,向其中写数据。

(8)取消OpenGL缓冲区映射。要等前面CUDA的活动完成以后,使用cuGLUnmapBufferObject或cudaGLUnmapBufferObject函数取消映射。

(9)前面的步骤完成以后就可以真正开始绘图了, OpenGL的PBO和VBO的绘图方式不同,分别为以下两个过程。

①如果只是绘制平面图形,需要使用OpenGL的PBO及纹理。

glEnable(GL_TEXTURE_2D); //使纹理可用
glGenTextures(1,&textureID); //生成一个textureID
glBindTexture(GL_TEXTURE_2D,textureID);
//使该纹理成为当前可用纹理
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,Width, Height,0,GL_BGRA,GL_UNSIGNED_BYTE,NULL);
//分配纹理内存。最后的参数设置数据来源,这里设置为NULL,表示数据来自PBO,不是来自主机内存
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN _FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_ FILTER,GL_LINEAR);//必须设置滤波模式,GL_LINEAR允许图形伸缩时线性差值。如果不需要线性差值,可以用GL_TEXTURE_RECTANGLE_ARB代替GL_TEXTURE_2D以提高性能,同时在glTexParameteri()调用里使用GL_NEAREST替换GL_LINEAR
然后就可以指定4个角的纹理坐标,绘制长方形了。

②绘制3D场景,需要使用VBO。
glEnableClientState(GL_VERTEX_ARRAY);
//使顶点和颜色数组可用
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3,GL_FLOAT,16,0);
//设置顶点和颜色指针
glColorPointer(4,GL_UNSIGNED_BYTE,16,12);
glDrawArrays(GL_POINTS,0,numVerticies);
//根据顶点数据绘图,参数可以使用GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES,GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS,GL_QUAD_STRIP,GL_POLYGON
(10)前后缓存区来回切换,实现动画显示效果。调用SwapBuffers(),缓冲区切换通常会在垂直刷新间隙来处理,因此,可以在控制面板上关掉垂直同步,使得缓冲区切换立刻进行。

3 CUDA和OpenGL互操作性能实例分析

3.1 测试实例

 这是一个相对简单的实例,其主要功能是不断地动态改变一个纹理图案中每个像素的颜色并显示。该实例使用了OpenGL的PBO并利用了OpenGL与CUDA互操作方式,纹理图案数据的生成主要由CUDA的kernel函数完成,完整程序及CUDA的kernel函数请参看参考文献[5]

如果不使用CUDA,整个程序结构变化不大,主要差别是生成该纹理图案的函数在CPU上运行,因而该函数及其调用方式要重写,具体函数如下:
void kernel(uchar4*pos,unsigned int width,unsigned int height,float time)
{ unsigned int index,x,y;
for(x=0;x<width;x++)
for(y=0;y<height;y++)
{ unsigned char r=(x+(int)time)&0xff;
unsigned char g=(y+(int)time)&0xff;
unsigned char b=((x+y)+(int)time)&0xff;
index=x*width+y;
pos[index].w=0;
pos[index].x=r;
pos[index].y=g;
pos[index].z=b;
}
}

其中,参数pos表示像素数组,width为图像宽度,height为图像高度,time是每次调用该函数时固定递增的一个值。

3.2 测试结果

上述实例在两种环境中做了实验,CUDA版本都是3.2。测试环境1的主要配置如下:CPU为Intel Core i3-M380,主频为2.53 GHz,GPU为 NVIDIA NVS 3100M,内存为2 GB。测试环境2的主要配置如下:CPU是Intel Core2 duo E7400,主频为2.8 GHz,GPU使用GeForce 9800 GTX+,内存为2 GB。测试时,显示设置的垂直同步要关闭。

测试时设置纹理图像的长和宽都是512,CUDA的线程块为1 024,每个线程块内的线程数为256,在OpenGL的显示回调函数里统计f/s(刷新率),结果如表1所示。

 从实验结果可以看出,CUDA与OpenGL结合的方式效果显著,显示速度比不使用CUDA提高了7~8倍。
CUDA是一种较新的方便使用GPU进行通用计算的架构,OpenGL是图形处理的工业标准。两者的互操作充分利用了GPU的特点,因而显得非常自然和合理,实验验证了两者配合使用的效果。该方式为高性能图形图像显示及科学计算可视化提供了良好的模式架构。

参考文献:

[转]CUDA和OpenGL互操作的实现及分析的更多相关文章

  1. CUDA与OpenGL互操作

    当处理较大数据量的时候,往往会用GPU进行运算,比如OpenGL或者CUDA.在实际的操作中,往往CUDA实现并行计算会比OpenGL更加方便,而OpenGL在进行后期渲染更具有优势.由于CUDA中的 ...

  2. CUDA和OpenGL互操作经典博文赏析和学习

    1.使用cuda+opengl图形互操作性实现MPR.原学位论文学习:实时交互的医学图像可视化.在该论文的第5.1.1节. 2.cuda与opengl互操作之PBO 3.cuda与opengl互操作之 ...

  3. CUDA与OpenGL互操作实例

    本文要解决的问题是如何实现CUDA和OpenGL的互操作,使得GPU能够将通用计算的运算结果交给OpenGL进行绘制. 本文的应用程序主要包括两个方面: 1.      使用CUDA核函数生成图像数据 ...

  4. CUDA 与 OpenGL 的互操作

    CUDA 与 OpenGL 的互操作一般是使用CUDA生成数据,然后在OpenGL中渲染数据对应的图形.这两者的结合有两种方式: 1.使用OpenGL中的PBO(像素缓冲区对象).CUDA生成像素数据 ...

  5. cuda+ffmpeg+opengl解码rtsp h264码流多路

    Cuda 解码 全尺寸 解码 .全尺寸窗口绘制测试( 分别 测试 视频 文件和 IP 相机 实时视频 ) 1080 p 视屏 文件 全尺寸 解码 全尺寸 显示 72 0p IP 相机 全尺寸 解码 全 ...

  6. Android 显示系统:OpenGL简介和Gralloc代码分析

    一.OpenGL ES与EGL Android的GUI系统是基于OpenGL/EGL来实现的. 由于OpenGL是通用函数库,在不同平台系统上需要被“本土化”——把它与具体平台的窗口系统建立起关联,F ...

  7. CUDA编程

    目录: 1.什么是CUDA 2.为什么要用到CUDA 3.CUDA环境搭建 4.第一个CUDA程序 5. CUDA编程 5.1. 基本概念 5.2. 线程层次结构 5.3. 存储器层次结构 5.4. ...

  8. OpenGL与CUDA互操作方式总结

    一.介绍 CUDA是Nvidia推出的一个通用GPU计算平台,对于提升并行任务的效率非常有帮助.本人主管的项目中采用了OpenGL做图像渲染,但是在数据处理方面比较慢,导致帧率一直上不来.于是就尝试把 ...

  9. [转]OpenGL与CUDA互操作方式总结

    一.介绍 CUDA是Nvidia推出的一个通用GPU计算平台,对于提升并行任务的效率非常有帮助.本人主管的项目中采用了OpenGL做图像渲染,但是在数据处理方面比较慢,导致帧率一直上不来.于是就尝试把 ...

随机推荐

  1. Spark 1.6.2 + Beam 2.0.0读取Mongodb数据进行相应逻辑处理

    不多说,直接上干货! http://blog.csdn.net/jianglushou9763/article/details/73332805 如果需要 APACHE BEAM2.0.0版本如何支持 ...

  2. 精选9个值得学习的 HTML5 效果

    此文转自:http://www.cnblogs.com/lhb25/p/9-html5-effects.html,仅供本人学习参考,版权归原作者所有! 精选9个值得学习的 HTML5 效果[附源码] ...

  3. jdk7.NIO.2学习笔记之目录文件及权限

    package com.zl.jdk7; import java.io.File; import java.io.IOException; import java.nio.file.Path; imp ...

  4. 【LDAP】ldap目录服务的命名模型

    ldap的命名模型 命名模型规定了在目录中如何组织和表示条目 1.   目录信息树(DIT) 目录信息树有点类似于DNS的结构.每一个条目都有自己的父条目(因为主条目的父条目是top,所以这句话是成立 ...

  5. 系统更新后vs2012无法打开方案资源管理器

    系统更新后vs2012无法打开方案资源管理器 vs调试报错: 未找到与约束 ContractName Microsoft.VisualStudio.Language.Intellisense.IGly ...

  6. 《本博客将搬至CSDN》 博客主QQ 654436731 有关于本博客任何文章的问题欢迎打扰

    地址 http://blog.csdn.net/sajiazaici

  7. Google Kickstart在线测试规则以及注意事项

    谷歌招聘在如火如荼的进行中,进谷歌都需要经过谷歌kickstart在线测试,然后过了之后还有五轮的面试- -.好吧毕竟你待遇高,你强你有理.. 下面介绍一下进谷歌的第一关google kickstar ...

  8. kafka基本机制

    Kafka目前主要作为一个分布式的发布订阅式的消息系统使用,下面简单介绍一下kafka的基本机制 1.3.1 消息传输流程 Producer即生产者,向Kafka集群发送消息,在发送消息之前,会对消息 ...

  9. python数据类型(数字\字符串\列表)

    一.基本数据类型——数字 1.布尔型 bool型只有两个值:True和False 之所以将bool值归类为数字,是因为我们也习惯用1表示True,0表示False. (1)布尔值是False的各种情况 ...

  10. .net 写魔兽登录

    代码如下: 登录页面: public partial class FrmLogin : Form { public FrmLogin() { InitializeComponent(); } priv ...