当前位置: 首页 > backend >正文

CUDA与OpenGL混合编程图形渲染

CUDA与OpenGL混合编程图形渲染

CUDA与OpenGL混合编程图形渲染

  • CUDA与OpenGL混合编程图形渲染
  • 前言
    • 一、 通过OpenGL映射GPU内存
    • 二、CUDA使用的两种OpenGL内存对象
      • 1、像素缓冲对象(PBO):OpenGL中用于存储像素的一段内存。2D图像是由多个像素和颜色点组成的。CUDA程序映射PBO,并逐个像素生成或修改图像,然后利用OpenGL进行显示。
      • 2、 向量缓冲对象(VBO):OpenGL中用于存储3D向量的一段内存。CUDA程序映射VBO,生成或修改3D位置信息,之后OpenGL将这些网格渲染成彩色表面、3D线框图像或3D点集。
    • 三、CUDA与OpenGL互操作的关键cudaGraphicsResourceGetMappedPointer函数,用于获取映射后的设备指针
    • 四、 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及纹理
        • ② 绘制3D场景,需要使用VBO
      • 10、前后缓存区来回切换,实现动画显示效果。调用SwapBuffers(),缓冲区切换通常会在垂直刷新间隙来处理,因此,可以在控制面板上关掉垂直同步,使得缓冲区切换立刻进行。
  • 总结


前言

一、 通过OpenGL映射GPU内存

  1. 从CUDA编程者角度看,OpenGL在GPU上创建并管理的通过缓冲的内存区域称为缓冲对象。
  2. CUDA Kernel将一段缓冲映射如CUDA内存空间,可实现CUDA和OpenGL的互操作。当释放该缓冲或解除映射时,其控制权重回OpenGL。因为不需要进行内存拷贝,所以映射是一种处理速度很快的低开销操作,实现OpenGL和CUDA之间的高速互操作能力。
  3. 与OpenGL进行互操作,需要在所有其他Runtime调用之前用cudaGLSetGLDevice()函数来指定CUDA设备。注意,cudaSetDevice()和cudaGLSetGLDevice()是互相排斥的。一旦对某个资源对CUDA进行了注册,便可以根据需要通过cudaGraphicsMapResources()和sourceSetMapFlags()方法可用于设备提示标识(例如,只读,只写等),以便CUDA驱动程序进行优化资源管理。

二、CUDA使用的两种OpenGL内存对象

1、像素缓冲对象(PBO):OpenGL中用于存储像素的一段内存。2D图像是由多个像素和颜色点组成的。CUDA程序映射PBO,并逐个像素生成或修改图像,然后利用OpenGL进行显示。

2、 向量缓冲对象(VBO):OpenGL中用于存储3D向量的一段内存。CUDA程序映射VBO,生成或修改3D位置信息,之后OpenGL将这些网格渲染成彩色表面、3D线框图像或3D点集。

glBindBuffer()

glBufferData()/glBufferSubData()/glGetBufferSubData()

glMapBuffer()/glUnmapBuffer()

三、CUDA与OpenGL互操作的关键cudaGraphicsResourceGetMappedPointer函数,用于获取映射后的设备指针

典型调用流程‌

  1. 注册OpenGL缓冲对象:cudaGraphicsGLRegisterBuffer(&resource, bufferObj, flags)
  2. 映射资源:cudaGraphicsMapResources(1, &resource, 0)
  3. 获取设备指针:cudaGraphicsResourceGetMappedPointer((void**)&devPtr, &size, resource)
  4. 核函数处理:kernel<<<…>>>(devPtr, …)(直接操作显存数据)
  5. 解除映射:cudaGraphicsUnmapResources(1, &resource, 0)

四、 CUDA和OpenGL互操作的混合编程步骤

1、 创建窗口及OpenGL运行环境

glutInit(argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(window_width, window_height);
glutCreateWindow("Cuda GL Interop (VBO)");
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutMotionFunc(motion);
glutTimerFunc(REFRESH_DELAY, timerEvent, 0);

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

 // 版本检查 if (!isGLVersionSupported(2, 0)){fprintf(stderr, "ERROR: Support for necessary OpenGL extensions missing.");fflush(stderr);return false;}// default initializationglClearColor(0.0, 0.0, 0.0, 1.0);glDisable(GL_DEPTH_TEST);// viewport   设置视口glViewport(0, 0, window_width, window_height);// projection 坐标系glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(60.0, (GLfloat)window_width / (GLfloat)window_height, 0.1, 10.0);

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

 // 3. 创建CUDA环境 cudaGLSetGLDevice(gpuGetMaxGflopsDeviceId());

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

 void createVBO(GLuint* vbo, struct cudaGraphicsResource** vbo_res,unsigned int vbo_res_flags)
{assert(vbo);// create buffer object//产生一个buffer IDglGenBuffers(1, vbo);//将其设置为当前非压缩缓冲区,// 如果是PBO方式,parameter1设置为GL_PIXEL_UNPACK_BUFFER,// 如果是VBO方式,parameter1设置为GL_ARRAY_BUFFERglBindBuffer(GL_ARRAY_BUFFER, *vbo);// initialize buffer objectunsigned int size = mesh_width * mesh_height * 4 * sizeof(float);//给该缓冲区分配数据,// PBO方式下,parameter1设置为GL_PIXEL_UNPACK_BUFFER,parameter2设置为图像的长度*宽度*4。// VBO方式下,parameter1设置为GL_ARRAY_BUFFER,parameter2设置为顶点数*16,因为每个顶点包含3个浮点坐标(x,y,z)和4个颜色字节(RGBA),这样一个顶点包含16BglBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER, 0);// register this buffer object with CUDAcheckCudaErrors(cudaGraphicsGLRegisterBuffer(vbo_res, *vbo, vbo_res_flags));SDK_CHECK_ERROR_GL();
}

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

 // register this buffer object with CUDAcheckCudaErrors(cudaGraphicsGLRegisterBuffer(vbo_res, *vbo, vbo_res_flags));

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


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

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

void runCuda(struct cudaGraphicsResource** vbo_resource)
{// map OpenGL buffer object for writing from CUDAfloat4* dptr;checkCudaErrors(cudaGraphicsMapResources(1, vbo_resource, 0));size_t num_bytes;// 是CUDA与OpenGL互操作的关键函数,用于获取映射后的设备指针// 该函数仅适用于通过cudaGraphicsGLRegisterBuffer注册的缓冲对象(如PBO/VBO),// 纹理对象需使用cudaGraphicsSubResourceGetMappedArraycheckCudaErrors(cudaGraphicsResourceGetMappedPointer((void**)&dptr, &num_bytes,*vbo_resource));//printf("CUDA mapped VBO: May access %ld bytes\n", num_bytes);// execute the kernel//    dim3 block(8, 8, 1);//    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);//    kernel<<< grid, block>>>(dptr, mesh_width, mesh_height, g_fAnim);launch_kernel(dptr, mesh_width, mesh_height, g_fAnim);// unmap buffer objectcheckCudaErrors(cudaGraphicsUnmapResources(1, vbo_resource, 0));
}

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

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

//使纹理可用
glEnable(GL_TEXTURE_2D)//生成一个textureID
glGenTextures(1&textureID)//使该纹理成为当前可用纹理
glBindTexture(GL_TEXTURE_2D,textureID)//分配纹理内存。最后的参数设置数据来源,这里设置为NULL,表示数据来自PBO,不是来自主机内存
glTexImage2D(GL_TEXTURE_2D0GL_RGBA8,Width, Height,0GL_BGRAGL_UNSIGNED_BYTENULL)//必须设置滤波模式,GL_LINEAR允许图形伸缩时线性差值。如果不需要线性差值,可以用GL_TEXTURE_RECTANGLE_ARB代替GL_TEXTURE_2D以提高性能,同时在glTexParameteri()调用里使用GL_NEAREST替换GL_LINEAR 然后就可以指定4个角的纹理坐标,绘制长方形了
glTexParameteri(GL_TEXTURE_2DGL_TEXTURE_MIN_FILTERGL_LINEAR)glTexParameteri(GL_TEXTURE_2DGL_TEXTURE_MAG_FILTERGL_LINEAR)
② 绘制3D场景,需要使用VBO
//使顶点和颜色数组可用
glEnableClientState(GL_VERTEX_ARRAY)//设置顶点和颜色指针
glEnableClientState(GL_COLOR_ARRAY)glVertexPointer(3GL_FLOAT160)//根据顶点数据绘图,参数可以使用GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES,GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS,GL_QUAD_STRIP,GL_POLYGONglColorPointer(4GL_UNSIGNED_BYTE1612)glDrawArrays(GL_POINTS0,numVerticies)

10、前后缓存区来回切换,实现动画显示效果。调用SwapBuffers(),缓冲区切换通常会在垂直刷新间隙来处理,因此,可以在控制面板上关掉垂直同步,使得缓冲区切换立刻进行。

效果图
在这里插入图片描述

总结

CUDA项目源码地址:https://github.com/chensongpoixs/ccuda_sample/tree/master/02_cuda_texture

http://www.xdnf.cn/news/10641.html

相关文章:

  • 【Python 算法零基础 4.排序 ⑦ 桶排序】
  • 【算法训练营Day05】哈希表part1
  • 《软件项目管理》第二章(项目准备与启动)期末周复习总结笔记
  • uniapp中view标签使用范围
  • Kotlin 中companion object {} 什么时候触发
  • C语言:内存函数
  • 前端面试高频问题通关指南--通用性问题
  • Python----目标检测(使用YOLO 模型进行线程安全推理和流媒体源)
  • SQL: 窗口滑动(Sliding Window)
  • 数学建模期末速成 最短路径
  • 23. Merge k Sorted Lists
  • Alist Win 基本用法
  • JavaSE知识总结(集合篇) ~个人笔记以及不断思考~持续更新
  • Python中使用pandas
  • C++ list代码练习、set基础概念、set对象创建、set大小操作
  • SQL 窗口函数深度解析:ROW_NUMBER 实战指南
  • volatile,synchronized,原子操作实现原理,缓存一致性协议
  • LabVIEW准分子激光器智能控制系统
  • 35.x64汇编写法(二)
  • Elasticsearch 读写流程深度解析
  • JAVA中的注解和泛型
  • 用 Whisper 打破沉默:AI 语音技术如何重塑无障碍沟通方式?
  • Mybatis框架各配置文件主要内容详解(二)
  • 神经网络与深度学习(第二章)
  • 数字化转型全场景安全解析:从产品到管理的防线构建与实施要点
  • 由浅入深一文详解同余原理
  • 【Android】MT6835 + MT6631 WiFi进入Meta模式出现WiFi_HQA_OpenAdapter failed
  • Higress项目解析(二):Proxy-Wasm Go SDK
  • 车载诊断架构 --- DTC消抖参数(Trip Counter DTCConfirmLimit )
  • 12.1 GUI 事件处理