CUDA与OpenGL混合编程图形渲染

article/2025/6/23 17:21:21

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.hkcw.cn/article/mEoEMGFfJA.shtml

相关文章

俄乌第二轮谈判都谈了哪些内容 换俘与停火提议成焦点

6月2日,俄罗斯和乌克兰两国代表团在土耳其伊斯坦布尔就和平解决俄乌冲突举行第二轮直接谈判。会谈结束后,俄方代表团团长、俄总统助理梅金斯基表示,俄方对第二轮谈判的成果感到满意。梅金斯基透露,俄方向乌方提交了关于乌克兰问题的和平备忘录,其中包含实现真正停火的建议…

乒超联赛门票于6月3日开售 雄安新区首迎顶尖赛事

6月9日至11日,2025赛季中国乒乓球俱乐部超级联赛常规赛第一阶段比赛将在河北雄安新区雄安体育中心体育馆举行。赛事门票将于6月3日18:00在秀动票务平台开售,票价从288元至788元不等。2025赛季乒超联赛包括男子和女子团体赛,分为三个阶段的常规赛和总决赛,时间跨度从6月到12…

女子拖欠停车费近3000元被起诉:怎么证明是我的车在停放

多次把车停在路边收费停车位上却不交费,两年半拖欠了近3000元停车费,常德女子李倩(化名)被起诉到法院。对此李倩称,收费公司使用的感应、计费系统常出故障,无法证明收费时段是她的车在停放,请求法院驳回起诉。6月2日,记者从中国裁判文书网获悉,常德中院判决李倩须支付…

小伙被4根烧烤签扎进脖子 已脱离生命危险

6月2日凌晨2时许,有网友发帖称山西临汾一名小伙脖子上被扎了多根烧烤签。视频显示,小伙脖子上插着四根金属签子,签上还有烧烤肉串,急救人员小心翼翼地将他送往病床。据了解,事发凌晨零时前后,一家烧烤店内两名年轻人发生口角,一人扔出烧烤签时不慎将其扎进另一人的脖子。…

香港一银行遭劫匪抢走30余万港币 职员受伤已出院

6月2日,香港恒生银行沙田第一城分行发生一起持刀抢劫案。一名劫匪持刀威胁银行职员后劫走约30余万元港币及少量外币,随后逃离现场。案件中有一名职员颈部受轻伤,送往医院救治后已出院。当天17时许,警方接到报案称,一名男子独自进入银行,坐下约15分钟后突然拿出一把刀,威…

谁有望成为韩国新总统 李在明领跑选情

韩国第21届总统选举定于6月3日举行,主要候选人仍在抓紧最后机会展开竞选活动,争取更多选票。此次大选在前总统尹锡悦被弹劾之后举行,共有7名候选人登记参选,但其中两人已宣布退选,最终有5名候选人参加大选角逐。这5名候选人分别是共同民主党候选人李在明、国民力量党候选人…

连贾冰都瘦了!就等一个沈腾了 喜剧圈瘦身潮来袭

沙溢、贾玲、贾冰都瘦了,压力给到了沈腾。今天,话题“贾冰减肥成功瘦到脱相”登上微博热搜。起因是5月31日,演员贾冰的妻子发视频祝福大家端午节快乐,并配文“从此我家多了个瘦子”。从贾冰妻子发布的两人合影中可以看到,贾冰明显瘦了很多。评论区纷纷询问他怎么瘦了这么多…

韩大选正式投票 五候选人角逐总统

韩国第21届总统选举于当地时间3日6时在全国14295个投票站正式启动,投票截止时间为当晚8时。5月29日上午6时,韩国进行了“事前投票”,选民在首尔麻浦区等地的投票站等候投票。预计选举结果最早可于3日晚12时左右初现轮廓。新任总统将面临经济、政治等多重考验,并需缓解保守和…

欧洲最高的活火山喷发 数千米高羽流壮观景象

6月2日,意大利西西里岛的埃特纳火山发生大规模喷发,形成了数千米高的火山羽流。据社交媒体上的目击者称,远在50公里外的陶尔米纳和40公里外的卡塔尼亚都能听到爆炸声。西西里岛民防部门发布航空通知,要求所有航班避开该地区。埃特纳火山海拔超过3300米,有多个火山口,是欧…

大乐透开出10注一等奖!单注超800万 奖金高达814万

大皖新闻讯 6月2日,中国体彩网更新了彩票开奖公告。其中,超级大乐透开出10注一等奖,单注奖金8139831元。具体如下:编辑 汪艳责任编辑:zx0176

Docker 在电商场景中的应用:从 Java 应用到数据库的容器化改造(以 MySQL/Redis 为例演示容器化部署)

在电商领域,面对瞬息万变的市场需求、海量的并发流量以及复杂多样的业务逻辑,系统的稳定性、可伸缩性和快速迭代能力显得尤为重要。传统的应用部署方式往往伴随着环境不一致、依赖管理复杂、资源隔离性差等痛点。此时,Docker 容器化技术 应运而生,为电商平台的构建和运维带…

学习BI---基本操作---数据集操作

什么是数据集&#xff0c; 数据集&#xff08;Dataset&#xff09;​​ 是指从原始数据源&#xff08;如数据库、Excel、API等&#xff09;提取并经过标准化处理后的数据集合&#xff0c;通常以二维表形式存储&#xff0c;用于支撑报表、仪表盘等可视化分析。 数据集在QuickB…

【入门】【练9.3】 加四密码

| 时间限制&#xff1a;C/C 1000MS&#xff0c;其他语言 2000MS 内存限制&#xff1a;C/C 64MB&#xff0c;其他语言 128MB 难度&#xff1a;中等 分数&#xff1a;100 OI排行榜得分&#xff1a;12(0.1*分数2*难度) 出题人&#xff1a;root | 描述 要将 China…

马思纯聚餐结束后在停车场等男友 甜蜜等待羡煞旁人

马思纯聚餐结束后在停车场等男友 甜蜜等待羡煞旁人!嘿,宝子们!这娱乐圈的瓜田里又长出一颗超甜的瓜!马思纯和张哲轩的感情生活最近又让大家狠狠吃了一把糖。你有没有想过,为什么他们的感情能一直这么稳定又甜蜜呢?马思纯和朋友们聚餐结束后,在停车场耐心等男友张哲轩。她…

“六年来首次”,印媒:莫迪可能缺席

据《今日印度》2日报道,知情人士透露,由于新德里与渥太华关系冷淡,印度总理莫迪或将缺席6月中旬在加拿大举行的七国集团(G7)峰会。报道称,这可能将是莫迪六年来首次缺席该峰会。印度总理莫迪资料图图源:印媒报道称,消息人士表示,对于加拿大将于6月15日至17日主办的G7峰…

吴京回顾赛车危险经历 曾遇翻车事故 挑战极限完赛环塔

吴京回顾赛车危险经历 曾遇翻车事故 挑战极限完赛环塔!6月1日,爱跑2025中国环塔国际拉力赛圆满结束。赛车陆续返回喀什赛区麦盖提N39赛段终点,车手和领航员们激动地拥抱庆祝他们在沙漠赛段中的坚持与不放弃。影视演员吴京首次参加环塔拉力赛便成功完赛,并在SS9赛段获得T4组…

【js逆向_MD5加密】某继续教育网络学习平台 密码加密

网址&#xff1a;aHR0cHM6Ly96amp4ankudHp2dGMuZWR1LmNuL2xvZ2luLmh0bWw Password加密&#xff1a; 断点调试 控制台输出&#xff1a;

《QDebug 2025年5月》

一、Qt Widgets 问题交流 1.开启PassThrough缩放后&#xff0c;QLabel在非百分百DPI缩放时显示图像模糊 // QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); // 没影响 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // Qt5 QGuiApplication::s…

【Redis技术进阶之路】「系统架构系列中篇」高可用之Master-Slave主从架构的复制问题(分析旧版点制功能)

分析旧版点制功能 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 Redis复制机制&#xff08;replicate&#xff09;案例介绍数据库状态一致 旧版复制功能的实现同步操作​​命令传播案例说明…

【黑马程序员uniapp】项目配置、请求函数封装

黑马程序员前端项目uniapp小兔鲜儿微信小程序项目视频教程&#xff0c;基于Vue3TsPiniauni-app的最新组合技术栈开发的电商业务全流程_哔哩哔哩_bilibili 参考 有代码&#xff0c;还有app、h5页面、小程序的演示 小兔鲜儿-vue3ts-uniapp-一套代码多端部署: 小兔鲜儿-vue3ts-un…