简介
虽然微软下一代的DirectX技术要到微软的Longhorn操作系统发布之时才会正式与大家见面,但微软DirectX技术开发小组已经在多次的技术会议上公布了他们下一代DirectX技术的一些相关内容。而在著名的MSDN(Microsoft Developer Network)上也公布了关于下一代DirectX技术的一些总体性的内容,这些相关的内容让我们从一个较为整体的角度去了解微软下一代DirectX技术的最新特性,并从中解读微软下一代DirectX技术将创造出的3D效能。
设计目标 | |
#$[*106889.jpg*#a*#0*#0*#center*]$# | 设计目标
|
点击看清晰大图 |
DirectX的发展要到了DirectX 7才开始真正引起人们的注意,和前几代产品相比,DirectX 7除了增强了原有版本的性能之外,还增加了对特定图形特效的支持,比如说对环境凹凸贴图特效的支持便是一例。而DirectX 8则是一款具有革命性意义的产品,它从根本上改变了前几代DirectX的架构,并引导了新的发展趋势,在DirectX 8中,微软引入了许多新的并具有通用性的子系统。其中,最为引人瞩目的就是可编程的管线特性,这项技术让开发人员不再只是通过几个开关参数和设定值来调整Vertex Shader(顶点着色器)以及Pixel Shader(象素着色器)特效,取而代之的是,在DirectX 8当中,只要给定一组输入的值以及一组输出的值,我们就可以实现各种不同的特效,想要什么就有什么,只要我们所要的特效能够在硬件所实现的范围之内——在DirectX 8当中,Vertex Shader特效是这样实现的,但Pixel Shader方面则稍微麻烦一点,Pixel Shader所对应的硬件资源相当有限,而且并非完全是可编程的。在处理向量方面,DirectX 8提供给我们十分方便的手段,我们输入一组向量值但只需要确定一个输出值(存放在帧缓存当中的数据——也就是输出值为一帧图象或者数帧图象),然而DirectX 8在这个方面做得还不够完善,一切到了DirectX 9的时候才渐渐成熟起来。
DirectX发展趋势 | |
#$[*106893.jpg*#a*#0*#0*#center*]$# | DirectX图形技术发展趋势
|
点击看清晰大图 |
一体化着色器(Shader)模型
DirectX 9当中引入了版本为2.0/2.X/3.0的Vertex Shader以及Pixel Shader可编程模型。而所有支持DirectX 9的图形处理器都必须至少支持2.0版的Shader模型,2.X版本的Shader模型将在更高级的图形处理器当中获得硬件级的支持,而3.0版的Shader模型则需要全新一代的图形处理器方可实现全面的硬件级支持(有消息指在2004年就可以推出这样的产品)。Pixel Shader 2.0所需要的通用寄存器数量是DirectX 8中所需寄存器数量的6倍,而指令集数量也大大增加,为原指令集数量的平方数,如此多的改进和增强特性使得Pixel Shader的特效实现能力和Vertex Shader持平,在程序上不再受到任何的限制。Pixel Shader的改进使得在Vertex Shader当中查找纹理的操作增多,特别是在应用程序当中,当我们需要对任意一块贴图进行更换的话,这种操作的次数更加是明显增多不少。此外,在3.0版Shader模型当中,同样有这样的问题存在,因此可以说,硬件上将Vertex Shader以及Pixel Shader分开做两个独立的单元是一种相当大的资源冗余。因此,在一代的4.0版Shader模型当中,全部Shader模型都将采用同样的构造,全套的技术特性都是完全一致的,这样在硬件上,设计者就无需为不同的Shader设计不同的执行单元,只要按照所对应的接口以及操作方式全部融为一体,仅设置一种独立的Shader执行单元,这样Shader执行单元的效能提升就可以带来Vertex Shader以及Pixel Shader双方面的效能提升。
统一Shader模型是一项重要而有意义的工作,比如说,当我们需要处理Vertex特效的时候,但这种特效又需要应用Pixel Shader方面的指令,或者是我们需要处理Pixel上的特效,但却需要Vertex Shader方面的支持,于是常常就出现捉襟见肘的情形,要用什么但是那样东西却不能够立刻拿到手边,于是,将两种Shader模型统一到一起成为一个单独的Shader模型就成为一件非常有意义的工作,这样在创造各种3D特效的时候,我们就可以随意使用Shader制造各种特效而不必受到拘束,这是一个非常好的想法,但随即我们就知道这样的做法目前来看是很不实际的,有一个相当关键的因素我们还没有考虑到,目前图形处理器(GPU)自身的显存控制系统问题……
虚拟显存技术
目前消费类图形处理芯片内存子系统是以图形计算为核心内容进行设计的,在显存当中存放着纹理、顶点、三角形以及着色器所需要处理的数据。假如你要用某种纹理贴图渲染一个立体几何框架,而相应的纹理贴图数据尚未加载到显存当中,那么GPU芯片基本上就是停工的,GPU只有等到显存里面已经有纹理贴图的数据方可进行渲染操作。虽然这种设计应用广泛,但毫无疑问,这样的设计方案本身还是有很多问题的。首先,这是一种相当浪费资源的做法,对任意一帧来说,我们在很多情况下未必会使用整个纹理对象,比如说mip-maps纹理贴图,而事实上,我们需要最少资源消耗的mip-maps纹理贴图往往却消耗了我们最多的带宽。在一些空旷的游戏场景当中更是如此,远处的景物体积较小我们仅需要非常低分辨率的mip-maps就可以完成贴图工作,用大量的带宽去传接小量的数据,巨大的浪费就产生了。回到原来所讨论显存系统设计方案当中来,GPU要到加载完所有的数据才开始进行渲染工作,当我们仅需要传递非常少的纹理贴图数据的时候,问题还不会显得十分突出,但当我们不断的去传送这些纹理贴图的时候,AGP和系统主内存就开始变得紧张起来。目前,显卡本地显存所提供的带宽是AGP总线带宽的10倍。如果我们需要处理的纹理贴图,着色器等方面所需要的数据超过了显卡上的显存容量,也就是说,GPU芯片要开始等待这些东西加载完了才能够开始新的操作,那么问题就显得很突出了,我们的3D应用程序的帧速就开始变得十分不稳定,并出现大幅度的下降。
其中一个解决的办法是在分配给AGP控制器所用的内存上传输所有的纹理贴图,这样就可以完全解决显存速度与AGP总线速度不匹配的问题,但问题是并不是全部的帧都要经过相对缓慢的AGP总线,如果我们采取通过AGP控制器传输纹理贴图的办法来解决问题的话,那么结果就只有一个:一切都跟最慢的同步起来了,于是一切都变慢了,这是不可行的方案,因此我们需要一种新的办法来解决这个问题。
现在图形处理器逐步开始演变成为一种通用型处理单元,而不再是一些专用的处理单元,因此我们可以参考CPU内存系统管理的方式来解决这个问题,而事实上,在通用型处理单元的发展过程中,类似的问题是不可避免的,比如说,我们不可能把所有的程序都放在处理器本身的缓存当中,因为我们不知道程序本身的大小,但直接从外部存储器中执行程序又会变得很慢,而经典的解决办法就是抛开物理内存的限制而使用虚拟内存管理模式。通过虚拟内存管理模式,程序员不再需要知道处理器内部的缓存和外部存储器的大小,取而代之的办法是将所有存储设备都映射成一个整体的虚拟内存空间,虚拟内存被划分为一个个较小的内存页面,而每个内存页面的经典大小值为4K。管理这些页面的策略就很重要了,急需处理的页面就要放置到处理器内部的缓存当中(Cache),次要一些的就暂时放在系统内存当中,而当内存容量不足的时候,我们就需要将这些页面放到硬盘里面。
将虚拟内存管理的方法推演到图形处理单元上,于是就有虚拟显存技术了,虽说虚拟显存在存储器管理的策略上是承袭原来的那一套,但我们仍需要关心虚拟显存技术采用之后图形处理系统的表现是否依旧流畅。假设,我们按照经典方法将纹理贴图、着色器等需要处理的数据划分为4KB大小(按照虚拟内存页面经典值设置),一个4KB大小的页面文件等效于32×32×32bit大小的一个子纹理贴图块,我们认为这样一个大小的纹理贴图块是一个比较大的取值,我们不需要传输很多“页”就可以再传输一个大块的纹理贴图了,传输太快就会引起一些抖动的情况出现,前面传完了,后面还没跟上,为了保障速度上的平衡匹配,我们可以将这个页面的值适当调整到一个更小的值上,这样我们就能够以极小的性能损失实现更高效的显存管理。
整数指令集
对程序员来说,编程的时候不必受到处理器指令集的限制将大大提升写程序的效率,但对于图形处理器来说,要实现这个目标还有很长的路要走。其中,一个最为急待改进的方面就是处理整数运算方面的指令集。目前,在着色器当中处理的大部分指令都是对应处理浮点数运算而设置的,的确,对于图象方面的处理而言,采用浮点运算指令集对于程序开发而言将带来许多便利,但当我们进行动态分支预测以及只读内存查找的时候,比如说定位一个顶点缓存值的地址的时候,用浮点运算指令就会带来相当多的麻烦。
在目前的图形处理器上,我们唯一需要进行内存寻址的操作就是查找相应的纹理数据,而这些纹理数据均为浮点类型数值。如果一个内存地址值并不能够唯一对应于一个纹理数据的取值,那么通常就会得到两种结果,我们用一个最为接近的纹理数据取值代替我们所需要精确查找的纹理数据取值,或者是把几个附近的纹理数据取值做一次平均,把结果作为我们需要的纹理数据取值。对于图象的纹理效果而言,这样的做法完全没有问题,但毫无疑问,这样是无法实现通用化内存寻址的,因为用不是很精确的浮点数是无法将连续的内存块进行精确的标识。微软在他们的4.0版一体化Shader模型中加入了相应的指令集,完全解决了上述的这些问题,使得通用化内存寻址成为可能。
不受限制的资源
虚拟显存技术的引入同时也为资源受限的问题提供相当不错的解决方案。首先,通过引入虚拟显存管理策略,以AGP总线为接口的系统内存能够以更高的效率向图形子系统传输数据;其次,由于仅采用统一的逻辑地址空间对虚拟显存进行编址,因此程序员可以在一个相对不受限制的空间里面进行自由操作,只要不超出整个逻辑地址空间就可以了。从中,大家也可以看到如何将所有物理存储设备映射成一个统一的虚拟存储空间的策略是相当重要的。当然了,通过增加虚拟显存管理的策略并不能够达到当前最优的性能(最优的性能只有在对硬件进行完全的优化的时候才能够得到,通过通用化的管理方法是无法得到最优性能的),但却可以大大增加程序开发应用的效率并达到一个可接受的效能程度,这才是我们所希望看到的结果。虚拟显存地址空间的分配也无需过于拘束,比如说采用了虚拟显存管理技术的3DLabs Wildcat VP图形处理芯片拥有16GB的虚拟地址寻址空间,这是一个相当可观的内存空间,对于程序员来说,他们的开发就不会因此而受到过多的限制。
不受限制的资源 | |
#$[*106924.jpg*#a*#0*#0*#center*]$# | 不受限制的资源
|
点击看清晰大图 |
虚拟显存技术和Shader模型结合起来的使用效能是一个相当有趣的话题。其中一个值得探究的问题就是内存中的Shader指令处理问题,对于传统的内存系统而言,Shader的指令长度并没有受到限制,而在内存当中,Shader的指令是被当作一个抽象的数据块进行处理的,那么这个数据块的大小是否能够很好的放到图形处理单元的执行管道当中呢?这一点我们是不知道的。只要Shader指令被加载到图形处理单元的时候,在这条指令推出执行单元之前全部的顶点和象素都会被处理一次,因此我们可以得出这样的一个结论:如果Shader指令较长的时候,那么在图形处理单元当中就必须开辟更多的空间以供应指令执行管道之所需,或者将整个Shader指令划分为几个较小的部分然后多次运行。由于在图形处理单元当中,为了能够提高效能,指令执行管道的大小一般都是固定的,因此它并不会因为指令过长而动态增加执行管道的大小,因此当我们处理较长Shader指令的时候,我们采用将指令划分为几个较小的部分来管理,而回想起先前我们所说的虚拟内存管理策略的时候,我们也提到过页面管理的方法,这与较长Shader指令处理方法可以说是不谋而合,也就是说我们只要设定一个限制值,将较小Shader指令划分为几个大小相同的部分然后放到虚拟内存当中,虚拟内存向图形处理单元传输的数据块大小是固定的,这样图形处理单元当中的指令执行管道就可以很好的进行相应的处理了。
因此对于Shader模型而言,虚拟内存并非作为一个单独的实体对应存在,为了适应Shader指令的执行应用,虚拟内存被划分为许多相对独立的页面。假定图形处理单元当中设置的指令执行管道可以执行一整个页面当中所包含的指令,那么我们的Shader操作就可以建立起一套流水线式的运作机制,加载一个页面,然后运行,停止下来之后再加载一个新的页面,然后运行,如此反覆指导全部包含指令的页面被加载到处理器当中,此间,执行管道的作用便相当于通用处理器当中的L1 Cache,而整套运作的机制和通用处理器所使用的机制是完全一致的。
通过虚拟显存技术,Shader指令的长度可以不受限制,存储系统可以存放的图象纹理可以更多,寻址的方式变得轻松简单,于是我们的下一代DirectX技术就实现了可用资源“不受限制”的强大功能。当然了,资源的“不受限制”还是受到硬件实际可用资源的限制,指令过长无法直接放置到图形处理器的执行管道里面,那么就需要多次加载指令页面,一来一回性能必然受到影响,而当我们所需要处理的纹理数据太多,有相当一部分实际上是放在了系统内存甚至是硬盘空间当中的话,从系统内存、硬盘空间到显卡显存之间的过渡也会让整个图形子系统的性能受到影响。为了解决这个问题,微软定出了两个门限值,超过第一个门限值的时候,系统的实时处理能力开始严重下降(但仍旧能够工作,3D设计场合并不需要太高的实时性能),超过第二个门限值的时候,系统就不能够正常工作(在640×480分辨率下,fps值不足10)。
通用输入输出模型
一体化Shader模型的设计导致许多有趣的结果出现,其中有不少还是不那么明显就能够看出来的。比较明显的就是在顶点着色器当中进行纹理贴图的操作,无疑这对于普遍的贴图位移映射操作是相当重要的因素,但我们不能够就局限于此。另外一个相对不那么明显的结果就是我们可以从顶点着色器(Shader)当中直接对顶点缓存进行写数据的操作,这项操作使得我们可以暂时缓存起一些在稍后时间才应用的计算结果。而这项操作对于绘画高阶曲面以及进行贴图位移映射是相当重要的,比如说我们可以先计算出应当镶嵌或者是移开的纹理贴图,然后将计算的结果首先缓存在显存当中的顶点缓存当中,当我们需要调用的时候我们只需要进行相应的查找即可。
通用输入输出模型 | |
#$[*106935.jpg*#a*#0*#0*#center*]$# | 通用输入输出模型
|
点击看清晰大图 |
将着色器(Shader)的相关操作和虚拟显存技术结合起来才是最重要的一项影响因素,通过虚拟显存技术,读写纹理数据和普通读写内存块数据的操作是完全一致的。当这两项技术结合起来之后,我们就相当于在下一代DirectX技术当中开启了一个通用的输入输出模型——我们可以向“内存”进行任意数据的读写,写入的任意数据都可以被任意一级的管线读取出来或者是等待以后使用。我们不需要考虑这些数据的类型到底是什么,无论它们是和顶点相关的,还是和象素或者是图形的结构相关的数据,我们都不需要加以理会,只要给出一些缓存数据的索引值以及一些联通性所需的数据,我们就可以根据我们的意愿画出所需要的图形图象。事实上,我们可以生成任意的光影效果,所有的光影特效都可以在GPU的一个单一的周期当中计算完毕,计算完之后我们将计算的结果缓存到虚拟显存当中,然后按照步骤读取数据渲染场景就可以了。
增强的图象镶嵌处理
微软的DirectX 8首次引入高阶曲面效果,而起初nVIDIA和ATi的硬件芯片都支持这项技术,nVIDIA方面称之为“RT-Patch”技术,而ATi方面则称之为“N-Patch”技术,但两者对高阶曲面的支持程度都相当局限,应用起来颇为不便,因此到后来不少开发商都放弃了在3D应用程序当中使用这种技术,后来,nVIDIA和ATi都干脆放弃对这项技术的支持,到了DirectX 9,微软旧事重提,在DX9中,微软提供了自适应图形镶嵌技术以及替换贴图的支持,虽然高阶曲面依旧难以应用并且所获得的支持度也相当有限,但DX9所提供的替换贴图技术却引起了不少开发商的兴趣,但不幸的是,硬件芯片并未提供对高阶曲面效果的支持,虽然程序开发商对于位移贴图技术相当感兴趣但苦于没有硬件支持也只好放弃采用这项技术。Matrox是替换贴图技术的最初发起者,因此在最开始的时候并没有太多图形硬件芯片厂商跟进这项技术,后来连Matrox自己也放弃了对这项技术的支持。目前,支持最新Pixel Shader 3.0以及Vertex Shader 3.0的硬件开发已经提上日程多时,我们希望在新一代的硬件中重新看到对高阶曲面镶嵌以及对替换贴图两项技术的支持,但无疑,目前的DirectX技术当中所有支持高阶曲面的运算都受到相当多的限制。
增强的图象镶嵌处理 | |
#$[*106941.jpg*#a*#0*#0*#center*]$# | 高阶基元计算方程
|
点击看清晰大图 |
如果在硬件芯片中可以直接支持各种高阶曲面计算的方程,比如说是Catmull-Rom曲面、贝赛尔曲线、B木条曲线等,这将是一个伟大的创举。如果能够在当中加入自适应镶嵌贴图的支持,那么就更加好了。如果在DirectX当中能够实现对以上所述的各种高阶曲面的支持,那么我们更加应该毫不犹豫的去应用这些效果,我们可以在我们设计的场景当中引入这些效果而不再受到任何的限制。而微软将在他们的下一代DirectX技术当中提供这些所有的一切。整合了对替换贴图技术的支持以及全新的拓扑结构处理单元,开发商找不到拒绝采用新效果的理由,除非硬件芯片还没能够提供足够的支持。
改进的通用应用程序接口(API)
并非所有的改进都会以新功能新特性的方式体现出来,用于DirectX的接口上的改进也不少,特别是对应于状态改变的情况。就目前而言,如果我们需要渲染一个场景,所有的一切都必须分割成一批批的几何结构,每一个批次的几何结构将使用同样的纹理贴图,同样的顶点缓存数据块,完全一致的着色器和转换矩阵。基本上,如果某个物体上存在着一块不同的几何结构体,那么它就需要被拿出来单独进行渲染,而这种操作会通过DirectX接口函数调用实现,或者通过显卡的驱动程序调用甚至是处理器内部的指令状态切换等来实现,无疑这样的操作将导致相当多的开销出现。其中,有部分的开销可以通过微软的操作系统与DirectX之间的接口配合当中予以减少,而这部分计划将于微软的Longhorn操作系统中实现。
另外一种减少这种转换开销的办法就是在图形芯片当中加入“栅格列举”技术。栅格列举技术首先获取一个单一的栅格,然后因应不同的转换算法、不同的纹理贴图以及不同的替换贴图列举出几个不同的实例。
#$[*106946.jpg*#栅格列举技术*#0*#0*#center*]$#
实际上,给定一个通用的输入输出模型,我们可以将所有可见的纹理以及变换矩阵放到一个数组当中,所有的着色器都可以访问这个数组,并将几何结构作为一个整批的结构提交给所有的着色器共享,然后就让着色器来确定对应于哪一个几何结构应当采用什么样的纹理以及转换矩阵。本来是由CPU来确定到底程序进行什么状态的计算,通过上述方法,状态转换的工作就交给了GPU完成,这样就大大减轻了CPU自身的负担。
Pixel Shader中的帧缓存操作
我们时常会遇到一些对图象进行附加处理的工作,比如说数字图象分层、色彩校正以及对某些部位增加色调。然而,在DirectX 9中,由于大部分情况处于实时渲染模式,我们无法读取正在处理的某一帧图象中的某一个象素值,同样我们是否写入了某个值也是不被保证的。事实上,绝大部分的图形处理芯片(GPU)都能够让我们读出当前渲染的某一帧图象中的纹理数据,但这并非是一个合法的行为,因为在任意时刻这样的操作都可能被中断,而开发人员通常都不鼓励使用这项功能。解决的办法就是建立两个独立的纹理,并从中选一进行附加操作,但这样就要消耗两倍的存储空间。到了下一代DirectX技术,开发者终于如愿获得他们最希望得到的功能——直接在Pixel Shader中对帧缓存进行操作的功能(目前仅限于Pixel),但拥有支持下一代DirectX技术的显卡并不意味着实时渲染模式就没有问题,或者是在新一代显卡中甚至根本就放弃了对这种技术(直接的帧缓存操作)的支持。这项技术很可能只是作为一种可选的功能出现,如此一来,程序员在开发程序的时候就可能会忽略这项技术的存在或者采用旧的办法来解决问题。
帧缓存操作 | |
#$[*106951.jpg*#a*#0*#0*#center*]$# | 帧缓存操作
|
点击看清晰大图 |
如果这项技术被要求为强制性的标准,那么我们就必须在Pixel Shader当中实现目前已有的固定混合函数的功能,但我们可以省去附加对象定位的开销。而实际上,不少硬件厂商都可能选择放弃在硬件中实现固定混合函数,取而代之的做法就是在Pixel Shader中模拟混合函数的功能,ATi已经在他们的GPU芯片中加入了类似的做法,他们通过Vertex Shader来模拟原有的固定Vertex处理函数。这样就意味着图形处理器设计厂商需要在Shader处理器上增加额外的晶体管以更加高的效能实现以上的功能。我们也期待硬件厂商能够实现这方面的功能。
结论
上面我们详细的介绍了微软的下一代DirectX技术将出现的一些新的功能特性,其中最为引人瞩目的就是引入了两大技术,分别是一体化Shader模型以及虚拟显存技术。一体化Shader模型的引入目的只有一个,就是为了方便开发者不再受到原来的Vertex Shader以及Pixel Shader的限制而从容的进行创作各种3D特效,而虚拟显存技术则为整个图形子系统提供了强有力的后援机制,包括方便的内存管理方法、实现不受限制的资源调用。而通用输入输出模型的引入为开发者提供了一个相当方便的调用接口,为便利的开发提供了有力的机制支持。总的说来,微软的下一代DirectX技术将是一场3D API方面的革命,这场革命并非是指它将提供更好更炫目更真实的3D效果(虽然这也是它的目的之一),而是新的技术使得DirectX成为附加在图形子系统之上的一层操作标准,通过接口的简化以及管理的通用化,这种操作的标准将渐渐演化为一种操作系统形式的标准,而图形硬件也提升到系统级的高度,通用性将成为硬件设计厂商考虑得更多的因素。
当然了,以上的技术特性并非是一个硬性的实现目标,到了我们用上新的DirectX技术的时候,一切的谜团或者才能够解开,我们相信伴随着硬件技术的发展以及软件技术的不断革新,我们将能够不断的享受到最新最好的3D效果。