OpenGL与CUDA互操作
OpenGL与CUDA互操作
0. 基本概念:
VBO:顶点缓冲对象(Vertex Buffer Objects,VBO)
顶点缓冲对象VBO是在显卡存储空间中开辟出的一块内存缓存区,用于存储顶点的各类属性信息,如顶点坐标,顶点法向量,顶点颜色数据等。在渲染时,可以直接从VBO中取出顶点的各类属性数据,由于VBO在显存而不是在内存中,不需要从CPU传输数据,处理效率更高。
所以可以理解为VBO就是显存中的一个存储区域,可以保持大量的顶点属性信息。并且可以开辟很多个VBO,每个VBO在OpenGL中有它的唯一标识ID,这个ID对应着具体的VBO的显存地址,通过这个ID可以对特定的VBO内的数据进行存取操作。
VBO的创建以及配置
创建VBO的第一步需要开辟(声明/获得)显存空间并分配VBO的ID:
1 | //创建vertex buffer object对象 |
1.openGL的基本使用
1 | //被init()调用 |
1 | bool init() |
1 | void loop() |
1 | void runcuda() |
1 | void endrun() |
2. opengl和cuda互操作基本原理
CUDA和OpenGL互操作的过程
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差不多,只是有些函数调用参数不同。以下是具体过程。
(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. 详细文档
一、 缓冲区相关
1. glGenVertexArrays:
名称: glGenVertexArrays —生成顶点数组对象名称
1 | void glGenVertexArrays(GLsizei n, GLuint *arrays); |
n 指定要生成的顶点数组对象名称的数量。
arrays
指定一个数组,在其中存储生成的顶点数组对象名称。
2. glGenBuffers:
generate buffer object names 该函数用来生成缓冲区对象的名称,第一个参数是要生成的缓冲区对象的数量,第二个是要用来存储缓冲对象名称的数组
1 | GLuint vbo; |
3. glBindBuffer:
bind a named buffer object
第一个就是缓冲对象的类型,第二个参数就是要绑定的缓冲对象的名称,也就是我们在上一个函数里生成的名称,使用该函数将缓冲对象绑定到OpenGL上下文环境中以便使用。如果把target绑定到一个已经创建好的缓冲对象,那么这个缓冲对象将为当前target的激活对象;但是如果绑定的buffer值为0,那么OpenGL将不再对当前target使用任何缓存对象。
1 | void glBindBuffer(GLenum target,GLuint buffer);//函数原型 |
4. glBindVertexArray:
绑定一个顶点数组对象
1 | void glBindVertexArray( GLuint array);//原型,array 指定要绑定的顶点数组的名称 |
描述
glBindVertexArray将顶点数组对象与名称数组绑定。 array是先前从glGenVertexArrays调用返回的顶点数组对象的名称,或者为0以绑定默认的顶点数组对象绑定。如果不存在名称为array的顶点数组对象,则在第一次绑定array时创建一个对象。 如果绑定成功,则不会更改顶点数组对象的状态,并且任何先前的顶点数组对象绑定都会中断。
错误
如果array不为零或先前从调用glGenVertexArrays返回的顶点数组对象的名称,则生成GL_INVALID_OPERATION。
5. glBufferData:
1 | void glBufferData( GLenum target, |
target 指定target buffer object。必须是GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER常量。
size 指定buffer object的新data store的大小。
data 指定即将被拷贝进data store并初始化的数据的指针,或者null,如果没有data要拷贝。
usage 指定你希望data store使用的模式。必须是GL_STREAM_DRAW, GL_STATIC_DRAW, or GL_DYNAMIC_DRAW.
1.glBufferData为绑定在target上的buffer object currently创建了一个新的data store。任何之前存在的data store被删除。新创建的data store将被指定size和usage.如果data参数为NULL,data store将被这个pointer指向的data初始化。
2.usage参数提示了在GL实现中一个a buffer object’s data store将如何被访问。这使GL做出更明智的可能显著提升性能的选择。然而,如果没有,这将限制data store的准确的usage。usage可以被拆成两部分:一,被访问的频率(修改或使用),二,访问的性质。访问的频率是如下几个之一:
STREAM
该data store内容将被修改一次,且被使用的次数也很少;STATIC
该data store内容将被修改一次,但会被多次使用;DYNAMIC
该data store内容将被不断修改,且被多次使用;访问的性质如下:
DRAW
该data store内容将被应用程序修改,且被当做GL渲染和图像设置命令的源数据。
6. glEnableVertexAttribArray:
1 | void glEnableVertexAttribArray(GLuint index); |
glEnableVertexAttribArray enables the generic vertex attribute array specified by index. glDisableVertexAttribArray disables the generic vertex attribute array specified by index. 默认情况下,对所有客户端都是禁止状态,包括所有已生成的顶点属性数组。当使能时,如当调用 glDrawArrays or glDrawElements这些顶点数组命令时,这些顶点属性数组将被访问和用来渲染。
7. glVertexAttribPointer:
1 | void glVertexAttribPointer |
index
指定要修改的通用顶点属性的索引。
size
指定每个通用顶点属性的组件数。 必须为1,2,3或4.初始值为4。
type
指定数组中每个组件的数据类型。 接受符号常量GL_BYTE,GL_UNSIGNED_BYTE,GL_SHORT,GL_UNSIGNED_SHORT,GL_FIXED或GL_FLOAT。 初始值为GL_FLOAT。
normalized
指定在访问定点数据值时是应将其标准化(GL_TRUE)还是直接转换为定点值(GL_FALSE)。
stride
指定连续通用顶点属性之间的字节偏移量。 如果stride为0,则通用顶点属性被理解为紧密打包在数组中的。 初始值为0。
pointer
指定指向数组中第一个通用顶点属性的第一个组件的指针。 初始值为0。
缓冲区对象只是OpenGL众多对象中的一种,其实当我们使用其它对象时,都是类似的思路
1 | GLuint vbo; |
创建对象,绑定类型,设置数据。
二、
1. glutMainLoop
glutLeaveMainLoop()
2. glUseProgram
glUseProgram- 使用程序对象作为当前渲染状态的一部分
void glUseProgram(GLuint program);
参数
program 指定程序对象的句柄,该程序对象的可执行文件将用作当前渲染状态的一部分。
三、线程相关
GLFWthread glfwCreateThread( GLFWthreadfun fun, void *arg )
A thread can wait for another thread to die with the command glfwWaitThread:
int glfwWaitThread( GLFWthread ID, int waitmode )
终止一个线程
void glfwDestroyThread( GLFWthread ID )
产生互斥锁
GLFWmutex glfwCreateMutex( void )
终止
void glfwDestroyMutex( GLFWmutex mutex )
上锁
void glfwLockMutex( GLFWmutex mutex )
解锁
void glfwUnlockMutex( GLFWmutex mutex )
这个是等待状态变量
void glfwWaitCond( GLFWcond cond, GLFWmutex mutex, double timeout )
void glfwSignalCond( GLFWcond cond )
void glfwBroadcastCond( GLFWcond cond )