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. 动作方法中 参数,Json

    一.方法中可以出现的参数类 1.HttpServletRequest 2.HttpServletResponse 3.HttpSession 4.Model 二.返回接收json数据 1. 接收,返回 ...

  2. Java中使用MongoUtils对mongodb数据库进行增、删、查、改

    本文主要介绍在java应用中如何使用MongoUtils工具类对 mongodb进行增.删.查.改操作. 一.配置 1.将 common.jar库引入到项目环境中: (源代码:https://gite ...

  3. Spring定时器——时间设置规则

    平时经常有一些写Spring定时器的需求,但是每次要设置定时器的时间的时候,老是既不住,到处找别人的博客又各种不爽,所以就自己记录一份吧,顺便整理一下. org.springframework.sch ...

  4. 2018-12-20 第二章Java 预习作业

    一.什么是变量?如何使用变量? 答:变量是在程序运行中其值可以改变的量,它是Java程序的一个基本存储单元. 变量的语法格式如下: [访问修饰符]变量类型 变量名[=初始值]: 二.java中基本数据 ...

  5. scss-嵌套规则

    在编写css代码的时候,可能由于嵌套的原因,需要多次重复书写选择器. 代码如下: #content article h1 { color: #333 } #content article p { ma ...

  6. 10th week task -2 Object 的起源

    Objective-C与C++.Java等面向对象语言类似,不过很多方面有所差别.若是用过另一种面向对象语言,那么就能理解Objective-C所用的许多范式与模版了.然而语法上也许会显得陌生,因为该 ...

  7. 【代码笔记】Java——远程监控、反射、代理、内省机制

    远程控制的基本原理 远程控制(RemoteControl)拥有控制端和被控端双方. 控制方通过请求,取得对远端的操作,实现远端的事件回放功能,同时,应该看得到远端桌面的画面.而被控方必须在建立Serv ...

  8. textarea高度随着内容的多少而变化,高度可以删减

    问题:可以多行输入,并且输入框的高度随着内容的多少而变化,输入框的高度不能只增不减 由于 input 只能单行输入 textarea可以多行输入,并且高度可以随着内容的增加而增加,但是当内容删减的时候 ...

  9. Android 多个activity之间的共享数据

    最近打算做一个时间助手,一个service监听课表信息.课表信息可以通过另外的activity来设置,所以这里就涉及到了数据的同步问题.我设置后的信息必须同步到监听的信息去. 在java中我们用全局变 ...

  10. Div+Css布局教程(-)CSS必备知识

    目录: 1.Div+Css布局教程(-)CSS必备知识 注:本教程要求对html和css有基础了解. 一.CSS布局属性 Width:设置对象的宽度(width:45px). Height:设置对象的 ...