从本篇教程开始,我们暂停coding,先来了解一下D3D11的pipeline,这些pipeline不涉及具体的硬件,而是着重于理解能够支持D3D11的pipeline实现。
参考资料:
http://fgiesen.wordpress.com/2011/07/01/a-trip-through-the-graphics-pipeline-2011-part-1/
通过前面的教程,我们知道,要用D3D11画一个三角形,我们需要做以下步骤:
这些步骤最终会在D3D11硬件上执行,从调用D3D11 API到最终在D3D11硬件上执行的大概数据流向图如下所示:
首先是我们的应用程序(APP), 它通过调用D3D11 API函数,实现创建资源(比如顶点缓冲),设置状态(比如深度模版状态),调用drawIndex函数等等。
D3D runtime会跟踪我们设置的状态,验证函数的参数是否正确,验证shader代码以及shader链接库等等,然后把这些调用的输出传送到driver中UMD,从某种程度上来说,我们可以把D3D runtime看成一个wrapper,它是应用程序和UMD之间的接口。
UMD(user mode driver)是指用户模式driver,它其实就是一些动态链接库(dll),完全运行在cpu端,它的主要功能包括:
1、 shader编译
D3D runtime把shader传输到UMD之前,会做一些验证工作,比如shader代码语法是否正确,设置的sampler数目是否超过最大限制等等。
在UMD中,shader代码最终会被编译成硬件相关的代码,传输到video memory中,shader参数会被传输到video memory中的const buffer中。
现在不论AMD还是NV,都是一套驱动支持不同代的显卡,所以shader代码通常先被编译成IR(中间代码),然后再编译成具体硬件相关的shader汇编语言。
2、UMD能实现一定的内存管理功能。
比如创建纹理,为纹理分配空间等等(UMD管理的内存主要是GPU能够访问的system memory以及CPU能够直接访问的video memory,这些内存都称作remote access memory)。
3、调度system memory和mapped video memory之间的传输。
4、转化应用程序所有的状态以及drawing操作成硬件理解的方式
一般是通过UMD中硬件抽象层转化为硬件相关的packet,packet类型主要包括硬件寄存器设置packet,drawcall packet等等,然后把packet写入command buffer(或称作DMA buffer),这些buffer先被拷贝到remote buffer,最终会被传送到gpu端的indirect ring buffer。
5、 根据顶点缓冲和索引缓冲创建时候的格式,顶点缓冲和索引缓冲会被UMD直接放入video memory(设置格式为直接创建在video memory中,类似opengl中的vbo)或者延迟载入(创建在system memory中),在gpu实际读取时候,才传入video memory。
我们知道,纹理图像通常是线性格式,而在gpu中我们通常使用z tile格式,在UMD中我们并不会进行纹理的地址转化,而是根据传入的参数把特定tile mode传入gpu,gpu会根据tile mode进行纹理的swizzle操作。z order 相关知识可以看wiki页http://en.wikipedia.org/wiki/Z-order_curve
纹理swizzle的知识请参考:http://www.cnblogs.com/mikewolf2002/archive/2012/03/11/2390097.html
GPU厂商都愿意把更多的功能写入UMD,因为其仅是一个dll,容易调试、可以实现多线程操作(比如一个线程编译shader,一个线程处理纹理),即使UMD崩溃了,也不会引起系统蓝屏,因为它和我们普通的应用程序没有本质区别。
最后再来了解一个context switch的概念:我们的GPU不是都运行在console上,只对应一个app,可能多个app通过时间片轮换来访问GPU, 这时就涉及到context switch,context switch的时候,我们要保存当前app设置的gpu状态以及执行的gpu数据等,以便os再切换到这个app时候,恢复该app的运行。
KMD( kernel mode driver),是指Kernel模式driver,KMD负责直接和硬件打交道,可能在系统中有多个UMD实例,但KMD只能有一个。一旦KMD崩溃,操作系统可能会出现蓝屏错误,KMD主要功能包括:
1、在多个应用程序使用GPU的情况下,KMD通过slice time分时操作来管理应用程序。
比如在一个时间片内一个app进行物理内存操作,另一个时间片内另一个app初始化GPU,设置显示模式等,在不同app间切换时,就需要context switch,响应中断等。
2、对版权保护的视频内容,KMD会在player和GPU之间建立特殊的path,防止用户模式的代码dump视频内容。
3、现在KMD都是thin的,它最重要的功能是管理硬件使用的command buffer,分配内存
UMD使用的remote buffer就是有KMD负责分配的。Indirect ring buffer也是KMD分配的,它位于video memory中,最终会被传输到GPU的command processor单元。
其中ring buffer是一个环形队列,它的简单实现可以参考:http://fgiesen.wordpress.com/2010/12/14/ring-buffers-and-queues/
下面是我按自己的理解画的一个driver简单示意图,不一定正确,但应该有那么一点意思:
[更正一下,迈克老狼 2012-11-24:上面的图我是根据opengl的driver图画的,对D3D11 还有一个DXGI层,它负责和KMD交互。应用程序把函数传给D3D11 runtime, 它和UMD交互,UMD产生gpu执行的命令,通常是以packet的方式体现。DXGI通常用来处理底层硬件接口,比如D3D11中用的adpater,交换链等,并对它们进行包装,以便应用程序使用,另外我们看到,应用程序也可以直接访问DXGI,比如一些非D3D的程序,通过GDI来画图,就是直接把命令传给DXGI。windows7的driver是Windows display driver model (WDDM),它可以虚拟gpu资源,对应用程序透明]