唉。死不悔改,而且一错再错。
首先,问题是错的。我在另一个帖子里
Windows 10 给 Linux 子系统写显卡驱动的是一个人吗,是谁呢?提到的是Direct3D9/11的驱动,是给Drawbridge写的,而不是Linux子系统。他们在发展上有个先后的关系,但不是一个东西。题主对此完全一知半解的情况下,不仔细看就断章取义,实在太胡搅蛮缠。
其次,不撸兔子的说法,基本每个点都是错误的。
“parrallels的虚拟显卡最多只是把OpenGL调用转发到宿主机上。“
不,那么做行不通。(甚至parallels都拼错了)
”Linux子系统和Windows子系统在内核看来是同等地位的。应该不需要搞这套东西才对。”
不,Linux子系统在Picoprocess里。Linux子系统和Windows本身是guest和host的关系。
“不提供CUDA真正的原因肯定不是不想做。而是已经做好了”
不,根本没写。而且如果你懂CUDA的话,就知道转API容易,难点是显存访问和管理。
“只是把Library OS里面的调用转发到外面。“
不,那么做行不通。
好了,现在我就来说说为什么直接把API调用转到外面行不通。会有很多细节,像题主和不撸兔子这样没做过的人根本不会了解。
1. 流量。
API调用看似简单,只要把所有的API都转出去,就行了。原先远程桌面就是这么做的。但缺点是,API调用所要消耗的流量远大于在驱动层搞。举个例子,把同一个设置状态调用两次,你就得在API层转两次。最多就是cache一下,把变化的传了。而在D3D的体系里,有runtime做状态管理,只会把真正修改了有起作用的状态往下传给驱动,并会做严格的状态验证。所以在驱动层,你所需要转的数据远少于API层。
2. 出错控制。
如果运行过程出了严重问题,你需要的是不让host crash,最多让guest crash。如果从API层走,那么这件事情无法避免。如果guest里有多了个验证,就会把错误留在子系统,而不会传出来。
3. bug-to-bug兼容
如果只是API层,那么要求guest和host的runtime完全一致,才可以保证100%兼容。而从驱动走,runtime是在guest,那么只要都符合WDDM,就没问题。guest和host的runtime可以解耦。
4. 什么是驱动。
驱动不在于你怎么实现,你是直接去真实硬件,还去了虚拟硬件。只要是按照驱动的规范实现、编译、部署,系统看到的是个设备,那就是驱动。我给Drawbridge写的第一版,是可以在普通的Windows上装的,你能在设备管理器里看到多了一块显卡,程序可以在上面建立设备,进行渲染。(由于没有实现电源管理的部分,在休眠后会睡死)
这里放一张WDDM的架构图。驱动需要实现的是user-mode display driver(UMD)和Display miniport driver(KMD)。OpenGL installable client driver属于可选。
从这张图也可以看到,你如果从API层面转(也就是D3D runtime和OpenGL runtime),那么系统并不会认为你是个设备,也没法按照系统的规范去使用你这个设备。那就不是个驱动,也没法实现完整的功能。
另一个严重问题是,GDI的部分你怎么办。GDI是直接调用win32k,win32k又会通过dxgkrnl去调用KMD。难道想连GDI都一起转出来?
到了Android上,情况会更遭。Android的驱动接口和OpenGLES很像,所以从表面上看,从驱动层和从API层区别不大。但遭就遭在不同的那一点点。首先是系统有一些私有接口,只有surface flinger等android系统内会调用,而host不会有对应的。这得在驱动里解决。第二,android上的显存管理是由驱动做的,不但host没有对应,其他设备也会直接调用显存管理做一些事情,比如说摄像头。这都不可能通过API层面实现。必须提供一个驱动让系统使用。
总的来说,比起API层,驱动层至少要有状态管理和command list,以及可能要有显存管理。这些,我写的驱动里都有。
5. 如何传输数据
不要以为host和guest都在一台电脑,传输数据就很容易。图形的东西数据量之大,API调用次数之多,都是远远超过其他方面的API的。还要考虑异构(CPU和GPU)上的延迟,传输数据这方面根本不trival。
前面提到的command list,就是一部分必做的事情。如果来一个API调用,就穿一次,即便memcpy也受不了。更好地实现是放到一个列表,只有在某些需要同步的调用下才把整个command list发送出去。我的实验是,用和没用,性能差6倍。另一方面是对于大块的数据,纹理、vb,host和guest之间如何直接映射,减少拷贝而又不影响安全性,这都是得考虑的事情。
以上这几点不光对D3D有效,对OpenGL/OpenGLES也有效。换句话说,不管什么系统,都应该在驱动层,而不是API层做这件事情。否则死路一条。
而在CUDA方面,更难处理。因为不像D3D/OGL,texture和buffer这些都是用封装过后的形态表示(D3D用的对象,OGL用的id),只要建立一个host和guest的映射,之后在上面的操作就能连上。CUDA里面你可以申请一块显存,接着就用指针到处指了。所以需要仔细做全显存所有权分析和数据映射。这些问题别说解决,连发现都不容易。
少年,你们仍然对力量一无所知。