用Quadro卡带Geforce卡实现非SLI多显卡多显示器协同绘制

news/2024/10/29 7:23:43/

核弹厂为了卖显卡真是无所不用其极。当然人家号称Geforce就是用来玩游戏的,Quadro才适合更高端的工作。可是如果我只是用来做三维场景绘制,你Quadro比起Geforce也没见得有很大优势,一块K4000顶得上三块GTX760的价格,直接吓得不省人事。幸好CAD&CG实验室有钱,让我借得一块K4000玩玩。

之前就说过插了4块GTX却只有一块在跑的凄惨经历。多番打听之后基本确凿了一件事情:单纯GTX是别想了。那么咱咬咬牙,用Quadro总是可以的。但是,如果没钱,只买得起一块Quadro,还有希望吗?又请出Google,寻遍中外论坛,未见有如我之奇葩要求者。算了,求人不如求己,自己试试吧。

要实现多显卡协同工作,首先需要有一块Quadro显卡,什么型号无所谓。然后,其他显卡,则Quadro、Telsa、Geforce都可以,多少张也都无所谓。

由于个人编程能力有限,窗口编写方面就借用大名鼎鼎的NeHe教程的框架,也就是用Win32 Api编写的窗口框架。当然,还得对框架进行一些修改。为什么不用freeglut之类的库?因为freeglut不让我碰它建好的窗口的RC,而它的代码我不会改……(计算机系大神们改起来是分分钟的事情)

然后,要用到nVidia家的WGL_NV_gpu_affinity特性。要使用这个功能,除了glew之外,还需要wglew:

#include	"GL/glew.h"
#include	"GL/wglew.h"

Windows创建一个窗口的顺序是建立一个窗口DC(device context),再根据这个窗口的属性建立对应的用于OpenGL绘制的RC(render context)。要实现多显卡协同工作,就必须针对每一块显卡单独建立RC,也就意味着, 要针对每块显卡单独建立窗口DC,然后绑定对应RC。


首先是注册窗口类。NeHe教程里注册窗口类和创建窗口是写在一起的。但是我们要创建多个同一个类的窗口,却不能把同样的类注册多次,所以先把RegisterClass的部分单独抽出来,只注册一次。

然后就是CreateGLWindow函数。在这里,我加了一个传入参数:RCNum,用于标识窗口绑定到哪块显卡上。在wglMakeCurrent(hDC, hRC)一句出现后,初始化GL参数的InitGL()出现前,加入:

	glewInit(); //glew初始化必须放在NV枚举之前/创建一个Affinity RC并替代原有的RC///HGPUNV hGPU = NULL;wglEnumGpusNV(RCnum, &hGPU);	//枚举第RCnum号GPUHGPUNV GpuMask[2] = { hGPU, NULL };	//创建GPUMaskHDC hDCNV = NULL;hDCNV = wglCreateAffinityDCNV(GpuMask);	//得到Affinity DCint pf = ChoosePixelFormat(hDCNV, &pfd);	//根据Affinity DC读取像素格式SetPixelFormat(hDCNV, pf, &pfd);DescribePixelFormat(hDCNV, pf, sizeof(PIXELFORMATDESCRIPTOR),&pfd);hRC = wglCreateContext(hDCNV);	//根据Affinity DC创建Affinity RC,并用它代替掉原来的RCprintf("hGPU: 0x%p hDCNV: 0x%p\n", hGPU, hDCNV);	//显示一下地址信息。不为null就算是成功了。wglMakeCurrent(hDC,hRC);	//记得重新绑定一下窗口DC

GpuMask的作用就是把需要工作的显卡标识出来,则之后的Affinity DC和Affinity RC都会跟GpuMask指定的显卡绑定。还有一点就是glewInit()函数必须在操作多显卡的代码之前,否则会出错。具体的工作原理倒是没搞清楚。


然后就可以用CreateGLWindow函数来创建一个用于OpenGL绘制的窗口了。此处有一点也是很重要的,就是创建出来的窗口的位置,必须跟当前窗口所在的显示器对应的显卡一致。也就是说,假如RCNum参数填了2,就应该把窗口创建在2号显卡(从0开始算)所连接的显示器里,并且窗口不要越界。假如RCNum传了2,而把窗口开到了1号显卡连接的显示器上,则绘制时工作的是2号显卡,但是绘制完成的内容则要通过内存转到1号显卡里给显示器显示,造成显卡之间的数据交换,极其影响帧数。


实际上如果只是这么建几个窗口的话,最后会发现显卡的负荷是间歇性的。原因是单线程的程序只能等待一块显卡SwapBuffer结束之后,才能进行下一块显卡的Swapbuffer,导致实际上同时还是只有一块显卡工作。因此程序还需要应用多线程技术,创建对应数量的线程,在每个线程内部创建窗口。线程之间互相不受影响,也不需要调用wglMakeCurrent(hDC, hRC)来切换窗口。如果需要窗口之间的同步,还需要使用Event来控制线程之间的进度(WGL_NV_gpu_affinity的说明里还提到了叫SwapGroup的特性,可以针对这种情况自动管理同步,但是我一直没有成功,还请指教)。这就涉及多线程的管理,不在本文细说。

如此就可以实现多显卡协同工作。如果几个窗口需要绘制同样的场景,记得每个窗口都要把场景所需要的顶点、纹理等传入GPU的数据初始化一遍。还是那句话,避免显卡之间的数据交换


参考:

https://www.opengl.org/registry/specs/NV/gpu_affinity.txt

NeHe的OpenGL教程



http://www.ppmy.cn/news/629253.html

相关文章

SLI导致双显卡被TensorFlow同时占用问题(Windows下)

最近学习TensorFlow,被一些不是bug的问题折腾的头晕脑胀,借此写一下解决方法。本人是在win10下使用TensorFlow的,所以ubuntu下的绕行吧,不会出现这些问题。(此文有些地方我重新整理了一遍,放在了相约机器人…

说说nVidia Geforce和AMD HD显卡的多显卡非SLI/CrossFire绘制逻辑

实验室的系统需要绘制3D模型后用多块显卡来进行尽可能多的窗口输出。市面上有以Matrox为代表的多屏显卡,此类显卡输出功能极其强大,电视墙什么的毫无压力,但是——绘制能力捉急,不指望用它进行模型绘制渲染。绘制渲染方面还是普通…

vue表格实现一个简单的合并单元格功能

用的是vue2ant-design-vue 但是vue3或者element-ui也是同理 先上效果 需要后端的数据将相同id的放在一起 否则也会有问题 例如: this.list [{id: 1,name: 舟山接收站,...}{id: 2,name: 舟山接收站碳中和LNG,...},{id: 2,name: 舟山接收站碳中和LNG,...} ]// th…

【Python爬虫与数据分析】面向对象设计

目录 一、类的构建与继承 二、多继承 三、钻石继承 四、多态 五、鸭子类型 六、类的组合 七、类的私有属性和方法 八、魔法方法 九、单例模式 一、类的构建与继承 类的属性包含成员变量和成员函数(方法) 成员函数又分为静态方法、魔法方法、普…

【安装VBox】

安装VBox 在VBox里加载Centos8.4的镜像 新增虚拟机设置 (参考https://www.cnblogs.com/sword88/p/12059711.html) 创建Red Linux 64bit 虚拟机8G内存磁盘大小64G的虚拟机网络设置桥接网卡直接用root用户登录再设置ssh登录(或者用用户登录…

python 网络编程——urllib,urllib2简单运用

知识点一:字典 字典呢就是一种数据结构,我们也可以将它称作映射,可以储存任意类型对象。它是由一个键/值对组成,举个通俗一点的例子,以我们的电话簿为例,值电话,键姓名。 需要注意的是在字典中…

box-shadow

box-shadow前言 box-shadow是css3的一个新属性,用来实现阴影效果,阴影分为内阴影和外阴影两个效果,可以通过逗号添加多个阴影效果。 box-shadow阴影用法 box-shadow:inset offset-x offset-y blur-radius spread-radius color,....; box-sh…

《安卓》一个按钮实现全选和全不选

目的描述: 在一个activity上显示三个不同名字的按钮,并设置一个名字为全选的按钮,当对它点击可以选择其余所有按钮,再次点击则取消所选的按钮。 截图展示: 单个选择 点击全选后全部选择 思路整理记录: 1.在…