OpenGL与CUDA互操作方式总结

本文版权说明:

原文链接:https://www.cnblogs.com/csuftzzk/p/cuda_opengl_interoperability.html
作者:24K纯开源 Email: zhangzhongke007@163.com
出处:http://www.cnblogs.com/csuftzzk/
本文版权归24K纯开源和博客园共同拥有,欢迎转载,但未经作者同意必须保留此声明,且在文章明显位置给出原文链接,否则保留追究法律责任的权利。

一、介绍

CUDA是Nvidia推出的一个通用GPU计算平台,对于提升并行任务的效率非常有帮助。本人主管的项目中采用了OpenGL做图像渲染,但是在数据处理方面比较慢,导致帧率一直上不来。于是就尝试把计算工作分解成小的任务,使用核函数在CUDA中加速计算。对于CUDA和OpenGL如何交互以前从来没有接触过,这次在实施时趟了不少的坑。在这里记录下OpenGL与CUDA的互操作的两种方式。

二、基本操作流程

OpenGL与CUDA互操作可以分成两种,一种是OpenGL将Buffer对象注册到CUDA中去,供CUDA读写操作,然后再在OpenGL中使用。一般这种情况下注册的是VBO和PBO,VBO一般用于存储顶点坐标、索引等数据;PBO则一般用于存储图像数据,因此称作Pixel Buffer Object。另一种是OpenGL将Texture对象注册到CUDA中去,经CUDA处理后得到纹理内容,然后在OpenGL中渲染出来。不过不管是哪一种互操作类型,其操作流程是一致的:

在OpenGL里面初始化Buffer Object
在CUDA中注册OpenGL中的Buffer Object
CUDA锁定资源,获取操作资源的指针,在CUDA核函数中进行处理
CUDA释放资源,在OpenGL中使用Buffer Object
下面就以代码为例,讲讲两种方式的异同:

(1)OpenGL PBO/VBO在CUDA中的使用

// 初始化Buffer Object
//vertex array object
glGenVertexArrays(1, &this->VAO);
//Create vertex buffer object
glGenBuffers(2, this->VBO);
//Create Element Buffer Objects
glGenBuffers(1, &this->EBO);
 
//Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).
glBindVertexArray(this->VAO);
 
// 绑定VBO后即在CUDA中注册Buffer Object
glBindBuffer(GL_ARRAY_BUFFER, this->VBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(*this->malla)*this->numPoints, this->malla, GL_DYNAMIC_COPY);
cudaGraphicsGLRegisterBuffer(&this->cudaResourceBuf[0], this->VBO[0], cudaGraphicsRegisterFlagsNone);
 
glBindBuffer(GL_ARRAY_BUFFER, this->VBO[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(*this->malla)*this->numPoints, this->malla, GL_DYNAMIC_COPY);
cudaGraphicsGLRegisterBuffer(&this->cudaResourceBuf[1], this->VBO[1], cudaGraphicsRegisterFlagsNone);
 
// 在CUDA中映射资源,锁定资源
cudaGraphicsMapResources(1, &this->cudaResourceBuf[0], 0);
cudaGraphicsMapResources(1, &this->cudaResourceBuf[1], 0);
 
point *devicePoints1;
point *devicePoints2;
size_t size = sizeof(*this->malla)*this->numPoints;
// 获取操作资源的指针,以便在CUDA核函数中使用
cudaGraphicsResourceGetMappedPointer((void **)&devicePoints1, &size, this->cudaResourceBuf[0]);
cudaGraphicsResourceGetMappedPointer((void **)&devicePoints2, &size, this->cudaResourceBuf[1]);
// execute kernel
dim3 dimGrid(20, 20, 1);
dim3 dimBlock(this->X/dimGrid.x, this->Y/dimGrid.y, 1);
modifyVertices<<<dimGrid, dimBlock>>>(devicePoints1, devicePoints2,this->X, this->Y);
modifyVertices<<<dimGrid, dimBlock>>>(devicePoints2, devicePoints1,this->X, this->Y);
 
// 处理完了即可解除资源锁定,OpenGL可以开始利用处理结果了。
// 注意在CUDA处理过程中,OpenGL如果访问这些锁定的资源会出错。
cudaGraphicsUnmapResources(1, &this->cudaResourceBuf[0], 0);
cudaGraphicsUnmapResources(1, &this->cudaResourceBuf[1], 0);

值得注意的是,由于这里绑定的是VBO,属于Buffer对象,因此调用的CUDA API是这两个:

cudaGraphicsGLRegisterBuffer();
cudaGraphicsResourceGetMappedPointer();

(2)OpenGL Texture在CUDA中的使用

// 初始化两个Texture并绑定
cudaGraphicsResource_t cudaResources[2];
GLuint textureID[2];
glEnable(GL_TEXTURE_2D);
glGenTextures(2, textureID);
glBindTexture(GL_TEXTURE_2D, textureID[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1000, 1000, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
 
glBindTexture(GL_TEXTURE_2D, textureID[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1000, 1000, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
// 在CUDA中注册这两个Texture
cudaError_t err = cudaGraphicsGLRegisterImage(&cudaResources[0], textureID[0], GL_TEXTURE_2D, cudaGraphicsRegisterFlagsWriteDiscard);
if (err != cudaSuccess)
{
    std::cout << "cudaGraphicsGLRegisterImage: " << err << "Line: " << __LINE__;
    return -1;
}
err = cudaGraphicsGLRegisterImage(&cudaResources[1], textureID[1], GL_TEXTURE_2D, cudaGraphicsRegisterFlagsWriteDiscard);
if (err != cudaSuccess)
{
    std::cout << "cudaGraphicsGLRegisterImage: " << err << "Line: " << __LINE__;
    return -1;
}
// 在CUDA中锁定资源,获得操作Texture的指针,这里是CudaArray*类型
cudaError_t err = cudaGraphicsMapResources(2, cudaResource, 0);
err = cudaGraphicsSubResourceGetMappedArray(&this->cuArrayL, cudaResource[0], 0, 0);
err = cudaGraphicsSubResourceGetMappedArray(&this->cuArrayR, cudaResource[1], 0, 0);
 
// 数据拷贝至CudaArray。这里因为得到的是CudaArray,处理时不方便操作,于是先在设备内存中
// 分配缓冲区处理,处理完后再把结果存到CudaArray中,仅仅是GPU内存中的操作。
cudaMemcpyToArray(cuArrayL, 0, 0, pHostDataL, imgWidth*imgHeight * sizeof(uchar4), cudaMemcpyDeviceToDevice);
cudaMemcpyToArray(cuArrayR, 0, 0, pHostDataR, imgWidth*imgHeight * sizeof(uchar4), cudaMemcpyDeviceToDevice);
// 处理完后即解除资源锁定,OpenGL可以利用得到的Texture对象进行纹理贴图操作了。
cudaGraphicsUnmapResources(1, &cudaResource[0], 0);
cudaGraphicsUnmapResources(1, &cudaResource[1], 0);
  注意这里因为使用的是Texture对象,因此使用了不同的API:
cudaGraphicsGLRegisterImage();
cudaGraphicsSubResourceGetMappedArray();

VBO/PBO是属于OpenGL Buffer对象,而OpenGL Texture则是另一种对象。因此,两种类型的处理需要区别对待。在这个地方耽搁了很久,就是因为没有看文档说明。下面一段话正是对这种情况的说明:

From the CUDA Reference Guide entry for cudaGraphicsResourceGetMappedPointer():

If resource is not a buffer then it cannot be accessed via a pointer and cudaErrorUnknown is returned.

From the CUDA Reference Guide entry for cudaGraphicsSubResourceGetMappedArray():

If resource is not a texture then it cannot be accessed via an array and cudaErrorUnknown is returned.

In other words, use GetMappedPointer for mapped buffer objects. Use GetMappedArray for mapped texture objects.

三、参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值