桌面窗口管理器是什么呢?它是Vista中的一个新组件:Desktop Window Manager(DWM)。它建立在WPF核心图形层组件基础之上。还记得我在第一个Post中所提到的Composition引擎吗?DWM的桌面合成 就是建立在这个Composition引擎基础之上的新特征。它的出现几乎改变了Vista中应用程序的屏幕象素显示方式。
通过DWM的桌面合成,应用程序的显示不再是直接画到屏幕上,而是一个显示内存中的一个离屏Surface。然后由DWM将这些Surface合成显示到屏幕之上。
从用户体验的角度看,启用DWM后,提供的视觉效果有毛玻璃框架、3D窗口变换动画、窗口翻转和高分辨率支持。其中最明显的特征有:任务栏窗口的实时缩略图;Alt-Tab和Win-Tab组合键所看到的效果。
桌面合成(Composition)
在Vista之前,Windows要求应用程序画自己的可见区域,它们可以直接画在显卡的视频缓冲里面。而在Vista,系统要求应用程序把整个表面画到离屏Surface当中。然后由DWM控制所有的离屏表面,并把它们合成到一起显示到真正的屏幕上。
现在计算机的图形芯片飞速发展,具有了很强的处理能力,现在它们主要用于游戏等方面。DWM的 主要目标就是利用图形芯片的处理能力也给非游戏用户带来尽可能好的体验。因此DWM是基于DirectX,特别是Direct3D。更准备是说,DWM是 直接建立在一个称为Milcore的层次之上。Milcore又建立在DirectX之上。最终是用Direct3D纹理来表示窗口内容和窗口框架。 DWM/Milcore调用适当的Direct3D函数把所有的Direct3D纹理合成为最终的桌面。Vista桌面就可以理解为一个全屏幕的 Direct3D应用程序。
从窗口系统的显示角度上看,这可以给我们带来很大的方便,帮助实现更高质量的视觉效果,比如:
1、访问窗口:现在所有的窗口都显示到离屏表面,这些离屏表面就可以用于其他的地方了。例如前面提到的Alt-Tab组合键所看到的缩略图,用户还可以据此实现一些其他需求。
2、窗口操作中不再涉及到背景应用程序:在Vista之前,如果隐藏的窗口变得重新可见,这部 分区域只有在应用程序醒来后并响应了WM_PAINT才能被重画,这就引起了很多难看的效果。在Vista中,背景窗口不会收到WM_PAINT,也不会 被要求提交,因为他们的内容已经在离屏表面当中了,可以被直接拿来进行重新合成。
3、更眩的用户体验,这个不用多说了,大家可以有自己的体验感受。
4、高分辨率支持:大部分应用程序不知道它所运行的显示器的分辨率。随着高分辨率显示器的普及,这会使应用程序在物理空间看起来越来越小,效果很差。由于DWM访问了窗口的离屏表面,其特殊的位置可以调整应用程序的显示分辨率,使它在高分辨率显示器上有更好的效果。
在DWM中,我们的每个窗口都用一个Surface表示,都可以看到为是3D的网格。虽然每个窗口还是一个矩形,但它们都位于一个3D空间之中。窗口的操作,比如最大化,还原等等,都发生了变化,它们都是对网格进行3D变换实现的。与以往有了很大的区别。
在Windows Vista中窗口的毛玻璃效果非常绚丽。在窗口的边界,我们可以看到窗口下面的内容。这其中同时具有一个的透明和模糊效果。但是,在实现毛玻璃时,为了不 让下面的窗口内容过于清晰影响上面的窗口,DWM组还对下面的窗口实现了模糊效果。其中的实现要点有:
1、模糊下面的内容,这是由自定义的象素Shader实现,这个Shader是一个完全运行在GPU的小程序,它可以并行处理多个象素。
2、模糊只是针对窗口边界下的部分内容。这些内容需要从不同的缓冲提取出来。
3、摸索的方法类似于平均值处理:一个象素的值等于其邻居象素的平局值。
众所周知,Direct3D支持多个Surface,最后显示不同Surface时是通过Flip(翻转)实现的,DWM也是如此。这样实现的结果就是不会在出现以前的Tearing。使得桌面变得更平滑。
现在,我们的桌面虽然称得上是一个全屏幕的Direct3D应用程序。不管是老式只支持帖图加速的图形处理器还是新型的高速图形处理器,我们都需要操作图形处理器的存储系统。这就出现了两个重要的问题:
1、在窗口很多时,运行DWM需要的内存将是一个问题,它随着用户的窗口数增加而增加。
2、DWM会与其它的应用程序共享内存资源。比如DirectX应用程序、视频回放和WPF应用程序等等。
解决这些问题,微软提出了Windows显示驱动模型(Display Driver Model)。WDDM是Vista及以后操作系统的新的DirectX驱动模型。WDDM主要提供三项功能:1、虚拟化视频内存。2、允许与GPU的交 互。3、允许DirectX表面可以跨进程共享。
在WDDM中,显示内容是虚拟化的。这就表示显示内容与系统内存一样。我们知道,在系统内存中 如果内存分配完毕,此时还有新的分配要求,就会产生第二存储页面,然后由系统管理存储页面和主存储的页面算法和机制。现在,主存储是显示内存,而第二存储 页面是系统内存。在显示存储和系统内存都分配完后,将使用磁盘作为视频内存表面。当然,这种情况比较少见。但是这样的设计使得WDDM足够的健壮,应用程 序的可靠性也得到增强。在WDDM而言,它将实现非常关键的功能:执行内存的分配、实现分配内存和真正的显示内存的控制。WDDM本身也在不断的改进中。
既然WDDM已经实现了显示内存的虚拟化,那么这就意味着WDDM具有调整应用程序的GPU命 令优先级的功能。这种功能通常是由WDDM调度程序实现。因此WDDM必须能中断GPU的某些操作,并保存操作的上下文,以备在必要时恢复操作继续运行。 基于这项功能,WDDM提供了两种级别的调度支持:
1、基本调度。它是基于DirectX9的WDDM驱动和硬件所支持的调度粒度。也就是说单独的Primitive和Shader程序不能被中断,上下文交换必须在它们完成后进行。
2、高级调度。它是基于DirectX10的WDDM驱动和硬件所支持的调度粒度。这种调用支 持比Primitive和Shader更细粒度的中断。注意,虽然DirectX10支持高级调度,但是它并不是DirecX10所必须的。也就是说,只 有部分硬件支持高级调度。桌面窗口管理器使用DirectX9,因此它是支持基本调度。
前面曾经提到过,通过WDDM可以支持Surface的跨进程共享。共享DirectX表面对 于重定向DirectX应用程序非常重要。因为Vista必须要和以前的应用程序兼容,就必须支持以前用GDI、、DirectX编写的应用程序。WDM 必须把这些应用程序的窗口重定向到Surface,然后由WDM统一合成、最后显示一个单一的Surface。
需要注意的一点是:WDM只重定向Top-level的窗口。而对于MDI应用程序,它的所有Top-level窗口、子窗口会被合成为单独的一个Surface,然后交给DWM合成。根据以前的Windows图形系统。重定向主要分为以下三类:
1、只用GDI显示的窗口
毫无疑问,GDI应用程序在当今任是主流。WDM在重定向基于GDI的应用程序时,基本过程如下:
A、在系统内存中分配一个和窗口大小一样的内存块,然后和窗口关联。
B、显示内存中也分配一个同样大小、指定象素格式的显存块。
C、然后,当应用程序获得窗口的DC时,因为这个窗口还没有参与到WDM的合成,所以现在,它活动的DC不在是主视频内存中,而且前面在系统内存中分配的内存块。
D、经过GDI的操作后,在系统内存形成一个Surface。
E、系统会根据窗口的各种参数、适当的从系统内存复制显示内容到视频内存中。
F、这样,视频内存的数据就和窗口保持一致,剩下的工作就由WDM完成:合成各个Surface,形成最终的桌面。
另外还有两点需要特别说明:
(1)、双缓冲窗口。现在,双缓冲技术显示的窗口特别多,它能有效的避免闪烁。确实,在使用了 双缓冲的GDI窗口中,它本身就有两个内存块,一个在系统,一个在显卡。所以问题来了:为什么重定向时不直接使用双缓冲中的显示内存块呢?这样做主要有两 个问题:一是格式不相同,GDI显示的格式和DirectX的格式不相同。GDI不支持以DirectX格式进行提交。二是很多GDI操作(XOR、文 本、Alpha混合等等)是一种“读取-修改-写回”这样的三步操作。如果重定向时直接使用显存块,GDI显示的时候必须从显存读取原来的显示内容到 CPU(或者系统内存),然后再修改,最后写回到显存。这样的操作效率非常低。所以没有采用。
(2)、最小化窗口。通常,最小化后的窗口只显示在任务栏的一个很小范围内,我们需要画窗口的 大小估计也只有130X30。但是,在DWM中没有采用这种用法。因为,如果窗口最小化后我们把Surface的大小修改为130x30再按需显示,这样 会在Filp3D或者取缩略图时有问题,因此窗口已经被修改。所以,DWM在窗口最小化后保存了最后一次更新的Sureface内容。
2、只用DirectX显示的窗口
由于DirectX应用程序通常是通过调用Present()函数进行Surface的提交, 所以这类应用程序通常只需要一个窗口缓冲就可以实现重定向。这种DirectX窗口的重定向是由DirectX系统(实际上是DirectX 10)处理的,当DirectX10决定显示某个Surface时,它会调用DWM的接口,以此实现跨进程的共享。DirectX 10的“Furface共享”是一个非常独特的功能,其建立在WDDM之上。这也是运行DWM必须要WDDM的关键原因之一。
当Present()显示Surface时,DWM会得到通知,表示应该更新其原来的窗口Surface,更新后DWM再重新进行合成。注意WPF应用程序也是DirectX应用程序。它们也是采用这种重定向方式。
3、混合GDI和DirectX的窗口
现在,有很多的Top-level窗口采用了GDI+DirectX的方式进行内容显示。根据 混合的方式不同结果也不同。如果Top-Level窗口以及它的每个子窗口是用唯一的方式进行显示,比如主窗口只用DirectX,子窗口只用了 GDI,DWM对这样的窗口会形成自己的“合成”树。没有子窗口单独显示,然后整个Tree的Surface被合成为传递给桌面。这样的混合方式能很好的 实现。
对于单独一个 Top-level 窗口同时使用 GDI+DirectX 显示,比较麻烦。特别是 GDI+DirectX 对窗口的某个区域进行重复显示的情况。 DWM 通常不支持这样的窗口。