最近在学习OpenGL,过程中需要使用CUDA进行并行计算。因此,需要解决OpenGL与CUDA的交互问题。学习记录如下:
- Step1. 共享数据区
想到交互,不难想到通信,数据共享等词语。这里使用的是共享数据的方式来完成OpenGL与CUDA的交互。而OpenGL与CUDA都有着自己独特的类型定义。因此,对于共享的数据区,我们需要给它起两个不同的名字,分别为OpenGL以及CUDA服务
OpenGL:
- GLuint bufferObj;
- GLuint bufferObj;
CUDA:
- cudaGraphicsResource *resource;
- cudaGraphicsResource *resource;
- Step2.将显卡设备与OpenGL关联(已废除)
注:在CUDA5.0版本以及以后的版本,不需要此操作。参考NVIDIA官方文档如下:
Deprecated
This function is deprecated as of CUDA 5.0.
This function is deprecated and should no longer be used. It is no longer necessary to associate a CUDA device with an OpenGL context in order to achieve maximum interoperability performance.
具体的设置代码为:
- cudaDeviceProp prop;
- int dev;
- memset(&prop, 0, sizeof(cudaDeviceProp));
- prop.major = 1;
- prop.minor = 0;
- cudaChooseDevice(&dev, &prop);
- cudaGLSetGLDevice(dev);
- cudaDeviceProp prop;
- int dev;
- memset(&prop, 0, sizeof(cudaDeviceProp));
- prop.major = 1;
- prop.minor = 0;
- cudaChooseDevice(&dev, &prop);
- cudaGLSetGLDevice(dev);
- Step3. 初始化OpenGL
- #define DIM 512
- glutInit(argc, argv);
- glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
- glutInitWindowSize(DIM, DIM);
- glutCreateWindow("bitmap");
- glewInit();
- #define DIM 512
- glutInit(argc, argv);
- glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
- glutInitWindowSize(DIM, DIM);
- glutCreateWindow("bitmap");
- glewInit();
这里需要注意的是:
到此为止,基本的准备工作就完成了。下面开始实际的工作。
共享数据缓冲区是在CUDA C核函数 和 OpenGL渲染操作之间 实现互操作的关键部分。为了实现两者之间的数据传递,我们首先需要创建一个缓冲区。
- Step4. 使用OpenGL API创建数据缓冲区
- const GLubyte* a;
- a = glGetString(GL_EXTENSIONS);
- glGenBuffers(1, &bufferObj);//生成一个缓冲区句柄
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, bufferObj);//将句柄绑定到像素缓冲区(即缓冲区存放的数据类型为:PBO)
- glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, DIM*DIM*4, NULL, GL_DYNAMIC_DRAW_ARB);//申请内存空间并设置相关属性以及初始值
- const GLubyte* a;
- a = glGetString(GL_EXTENSIONS);
- glGenBuffers(1, &bufferObj);//生成一个缓冲区句柄
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, bufferObj);//将句柄绑定到像素缓冲区(即缓冲区存放的数据类型为:PBO)
- glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, DIM*DIM*4, NULL, GL_DYNAMIC_DRAW_ARB);//申请内存空间并设置相关属性以及初始值
- GL_PIXEL_UNPACK_BUFFER_ARB
- GL_PIXEL_UNPACK_BUFFER_ARB
- GL_DYNAMIC_DRAW_ARB
- GL_DYNAMIC_DRAW_ARB
这时,可能你会疑问,前两句代码是干嘛的?
因为,GL_PIXEL_UNPACK_BUFFER_ARB对glew扩展库的版本有要求,所以最好检查一下当前环境是否支持GL_PIXEL_UNPACK_BUFFER_ARB枚举值。
- Step5. 把缓冲区分享给CUDA
由于我们的目的是要使用CUDA的并行计算能力,所以CUDA必须要有权利访问共享数据缓冲区。
要实现该操作,需要将缓冲区句柄注册为一个图形资源,即Graphics Resource;然后“分享给CUDA”
- cudaGraphicsGLRegisterBuffer(&resource, bufferObj, cudaGraphicsMapFlagsNone)
- cudaGraphicsGLRegisterBuffer(&resource, bufferObj, cudaGraphicsMapFlagsNone)
代码中的resource即之前定义的:
- cudaGraphicsResource *resource;
- cudaGraphicsResource *resource;
方法cudaGraphicsGLRegisterBuffer的参数3表示缓冲区属性,它有以下三种可选值:
1.
cudaGraphicsRegisterFlagsNone
: Specifies no hints about how this resource will be used. It is therefore assumed that this resource will be read from and written to by CUDA. This is the default value.
2.
cudaGraphicsRegisterFlagsReadOnly
: Specifies that CUDA will not write to this resource.(只读)
3.
cudaGraphicsRegisterFlagsWriteDiscard
: Specifies that CUDA will not read from this resource and will write over the entire contents of the resource, so none of the data previously stored in the resource will be preserved.(只写)
- Step6. 让CUDA映射共享资源,并获取相对于显卡而言的设备指针
- uchar4* devPtr;
- size_t size;
- cudaGraphicsMapResources(1, &resource, NULL);
- cudaGraphicsResourceGetMappedPointer((void**)&devPtr, &size, resource);
- uchar4* devPtr;
- size_t size;
- cudaGraphicsMapResources(1, &resource, NULL);
- cudaGraphicsResourceGetMappedPointer((void**)&devPtr, &size, resource);
CUDA官方文档中这样描述:CUDA在访问图形接口(比如openGL)的共享资源之前,需要首先对其进行映射(map),然后才可以访问共享数据区,CUDA对资源的访问过程中,OpenGL不能对该数据区其进行任何操作,直到CUDA对数据区解除映射(unmap)为止。
Nvidia的原文描述如下:
Map graphics resources for access by CUDA. Maps the count graphics resources in resources for access by CUDA.
The resources in resources may be accessed by CUDA until they are unmapped. The graphics API from whichresources were registered should not access any resources while they are mapped by CUDA. If an application does so, the results are undefined.
映射完成后,我们需要获得缓冲区对于显卡(设备)而言的指针,即代码中的 devPtr。没有设备指针,我们怎么进行并行计算呢。
- Step7. 执行CUDA核函数
- dim3 grids(DIM/16, DIM/16);
- dim3 threads(16, 16);
- kernel_opengl<<<grids, threads>>>(devPtr);
- dim3 grids(DIM/16, DIM/16);
- dim3 threads(16, 16);
- kernel_opengl<<<grids, threads>>>(devPtr);
一个简单的核函数kernel_opengl的定义如下:
- __global__ void kernel_opengl(uchar4* ptr){
- int x = threadIdx.x + blockIdx.x * blockDim.x;
- int y = threadIdx.y + blockIdx.y * blockDim.y;
- int offset = x + y * blockDim.x * gridDim.x;
- float fx = x/(float)DIM - 0.5f;
- float fy = y/(float)DIM - 0.5f;
- unsigned char green = 128 + 127 * sin(abs(fx*100) - abs(fy*100));
- ptr[offset].x = 0;
- ptr[offset].y = green;
- ptr[offset].z = 0;
- ptr[offset].w = 255;
- }
- __global__ void kernel_opengl(uchar4* ptr){
- int x = threadIdx.x + blockIdx.x * blockDim.x;
- int y = threadIdx.y + blockIdx.y * blockDim.y;
- int offset = x + y * blockDim.x * gridDim.x;
- float fx = x/(float)DIM - 0.5f;
- float fy = y/(float)DIM - 0.5f;
- unsigned char green = 128 + 127 * sin(abs(fx*100) - abs(fy*100));
- ptr[offset].x = 0;
- ptr[offset].y = green;
- ptr[offset].z = 0;
- ptr[offset].w = 255;
- }
- Step8. 解除CUDA对共享缓冲区的映射
- cudaGraphicsUnmapResources(1, &resource, NULL)
- cudaGraphicsUnmapResources(1, &resource, NULL)
- Step9. 调用OpenGL API显示
- glutKeyboardFunc(key_func);
- glutDisplayFunc(draw_func);
- glutMainLoop();
- glutKeyboardFunc(key_func);
- glutDisplayFunc(draw_func);
- glutMainLoop();
其中,显示回调函数为:
- static void draw_func(void){
- glDrawPixels(DIM, DIM, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- glutSwapBuffers();
- }
- static void draw_func(void){
- glDrawPixels(DIM, DIM, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- glutSwapBuffers();
- }
因为,之前的代码:
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, bufferObj);
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, bufferObj);
通过查看glDrawPixels的文档:
- void glDrawPixels(
- GLsizei width,
- GLsizei height,
- GLenum format,
- GLenum type,
- const GLvoid *pixels
- );
- void glDrawPixels(
- GLsizei width,
- GLsizei height,
- GLenum format,
- GLenum type,
- const GLvoid *pixels
- );
—————————————————————— 华丽的分割线 ————————————————
最终,运行程序,得到指定的结果。
转自:http://blog.csdn.net/lingling_y/article/details/8915163