DirectX根组件包含辅助显示计算的数学结构 另外两个组件相应地包含了Direct3D和D3DX的功能 添加这些引用后 只需查看清单1中添加的using语句 以确保名称空间被正确引用 这一步确保您不需要完全限定类型 例如 如果不添加using语句 那么声音
墨初 知识笔记 77阅读
后台缓冲区是要显示的实际数据(象素)在发送到显卡进行处理并输出到屏幕之前存储的地方。背景缓冲区的格式决定了可以显示多少种颜色。大多数格式都遵循特定的命名约定——每个字符后跟一个数字,例如A8R8G8B8。由字符指定的分量与它后面的数字具有相同的位数。在A8R8G8B8中,这种格式可以包含32位颜色。
彩信息,alpha、red、green和blue各用8位。最常见的构成是:A Alpha
R Red
G Green
B Blue
X Unused
你可以查看DirectX SDK文档得到更多关于格式的信息。由于我们还需要知道该设备时候可以显示在窗体中,所以这个方法还有一个参数(最后一个)。尽管大多数游戏都运行在全屏模式下,但是编写和调试在全屏模式下运行的游戏却很困难。在调试过程中,这个应用程序在窗体模式而不是全屏模式下显示。
请注意 窗体模式是我们运行的大多数应用程序的显示方式。其中大多数应用程序带有边框和控制菜单,右上角带有最小化、最大化和关闭按钮。在全屏模式下,应用程序覆盖了整个屏幕,并且在大多数情况下没有边框。如果全屏模式使用了另外的屏幕大小(你当前使用的桌面),你可以改变桌面的分辨率。 |
你可能注意到了默认行为是接受该设备,但是在接受之前进行了两项检查。第一项检查确保了传递进来的设备可以进行alpha混合(游戏的用户界面需要这个),如果它不能够实现,就返回false表明这个设备不能被接受。接着,它检查是否支持活动光源(active light)的能力。没有光源的屏幕看起来是平面的、是假的,因此一般至少需要一个光源。
还有一些代码在设备建立之前的确修改了该设备,你可能想知道这段代码的作用。能够执行处理过程的设备需要用多种方法来显示顶点,要么在硬件中计算或软件中计算,或者两者都使用。如果处理过程完全在硬件中进行,这就是另外一种模式,就叫做"纯硬件设备",它潜在地提供了更高的性能。这段代码检查你当前是否要建立一个硬件处理设备,如果你正准备这样做,并且该纯设备是可以使用的,那么它就切换到这种模式中。你不能建立纯设备(如果该设备是可用的)的唯一情形是你计划调用该设备上的某个get方法或属性。由于在这个例子中你不需要这样操作,所以你可以自由地使用能力更加强大的设备。
示例框架中有一些不安全(unsafe)的代码,因此你需要更新自己的项目并处理这些问题。见图2。
图2:允许不安全的代码 |
列举所有设备选项
现在你可以让框架组件开始列举系统中的设备了。首先,为游戏引擎类声明一个构造函数,并把main中建立的示例框架实例作为参数传递进去。如列表3所示。
列表3:添加构造函数
private Framework sampleFramework = null; // 示例的框架组件 /// 建立该类的一个新的实例 public GameEngine(Framework f) { // 存储框架组件 sampleFramework = f; } |
该构造函数除了存储示例框架实例之外没有做其它的任何操作,这是因为这个实例是游戏中其它的一切东西几乎都需要使用的。在你调用示例框架之后,它所做的第一件事情是试图列举系统中的所有设备。在你的项目文件中,你在Framework文件夹中可以看到dxmutenum.cs文件。这个文件包含了列举系统中所有设备所需要的全部代码。由于理解如何列举和为什么列举设备是非常重要的,所以请你打开这个文件。
你首先应该注意到Enumeration类自身是不能被创建的,并且每个可用的成员和方法都是用static(静态的)关键字声明的。由于在正常情况下(最少是现在)应用程序在运行的时候,你的图形硬件是不会改变的,因此这些列举代码只需要在应用程序开头运行一次。
列举工作是从Enumerate方法开始的,该方法在设备建立之前被示例框架调用。请注意,这个方法的唯一参数是你自己在游戏引擎类中所实现的接口。这个接口被保存下来,因为随后,随着设备组合的列举,会调用IsDeviceAcceptable方法来决定某个设备是否应该添加到有效设备列表中。
那么设备到底是怎样列举出来的呢?这些功能都位于受控DirectX的Manager类中。如果你非常熟悉非受控的DirectX应用程序编程接口(API),那么我告诉你,这个类映射了IDirect3D9组件对象模型(COM)接口。请留意列表4中的Enumerate方法中的第一个循环。
列表4:列举设备
// 查找系统中的每个适配器 for each(AdapterInformation ai in Manager.Adapters) { EnumAdapterInformation adapterInfo = new EnumAdapterInformation(); // 存储一些信息 adapterInfo.AdapterOrdinal = (uint)ai.Adapter; // 序号 adapterInfo.AdapterInformation = ai.Information; // 信息 // 获取这个适配器上的所有显示模式 // 建立一个所有显示适配器格式的临时列表 adapterFormatList.Clear(); // 现在检测支持哪种格式 for(int i = 0; i < allowedFormats.Length; i++) { // 检查这种格式的每一种可支持的显示模式 for each(DisplayMode dm in ai.SupportedDisplayModes[allowedFormats[i]]) { if ( (dm.Width < minimumWidth) || (dm.Height < minimumHeight) || (dm.Width > maximumWidth) || (dm.Height > maximumHeight) || (dm.RefreshRate < minimumRefresh) || (dm.RefreshRate > maximumRefresh) ) { continue; // 这种格式是无效的 } // 添加到列表中 adapterInfo.displayModeList.Add(dm); // 如果先前并不存在就把它添加到格式列表中 if (!adapterFormatList.Contains(dm.Format)) { adapterFormatList.Add(dm.Format); } } } // 获取适配器显示模式 DisplayMode currentAdapterMode = ai.CurrentDisplayMode; // 检查这种格式是否在列表中 if (!adapterFormatList.Contains(currentAdapterMode.Format)) { adapterFormatList.Add(currentAdapterMode.Format); } // 对显示模式列表进行排序 adapterInfo.displayModeList.Sort(sorter); // 获取这个适配器上每个设备的信息 EnumerateDevices(adapterInfo, adapterFormatList); // 如果适配器上至少有一个设备,并且它是兼容的,就把它添加到列表中 if (adapterInfo.deviceInfoList.Count > 0) { adapterInformationList.Add(adapterInfo); } } |
Manager类的Adapters(适配器)属性是一个包含了系统中所有"适配器"信息的集合。"适配器"这个术语可能有点不恰当,但是它的基本定义是指任何监视器可以连接到的东西。例如,假设你有一块ATI Radeon 9800 XT显卡。虽然只有一块显卡,但是可能把两个不同的监视器连接到它上面(通过视频图形适配器[VGA]端口和后面的数字视觉接口[DVI]端口)。当用这两种监视器的时候,这块显卡就有两个适配器,因此是两种设备。
请注意 这是一种通过把设备创建为适配器组的方式在"不同的"设备之间共享资源的方法。这种方法受到了少许限制。你可以查阅DirectX文档了解更多的信息。 |
这个循环至少会迭代一次,这依赖于你的系统。在把当前活动的适配器的基本信息存储起来以后,代码必须找到在全屏模式下这个适配器可以支持的所有显示模式。你可能发现了受到支持的模式都可以直接从当前正在列举的适配器信息中直接列举出来,代码也是这样做的。
列举某个适配器模式的时候,第一步是检查最小和最大的范围集合。大多数设备支持很多模式,但是其中很多我们现在不会使用了。很多年前,你可能见过在320x200全屏窗口中运行游戏,但是现在不会发生这种情况(除非你正好在玩手持式游戏,例如Gameboy Advance)。示例框架选择的最小的大小为640x480窗体,没有设置最大的尺寸。
请注意 示例框架选择的最小尺寸为640x480并不意味着在全屏模式下它就会选择最小的尺寸。在全屏模式下,示例框架选择最好的可用尺寸,它一般是当前桌面的大小(通常不是640x480的)。 |
在符合框架组件需求的受到支持的模式被添加到列表中后,当前的显示模式就会被添加进来,因为这个模式肯定受到支持。最后,通过实现IComparer接口,这些模式会被排序。见列表5。
列表5:对显示模式进行排序
public class DisplayModeSorter : IComparer { /// 比较两种显示模式 public int Compare(object x, object y) { DisplayMode d1 = (DisplayMode)x; DisplayMode d2 = (DisplayMode)y; if (d1.Width > d2.Width) return +1; if (d1.Width < d2.Width) return -1; if (d1.Height > d2.Height) return +1; if (d1.Height < d2.Height) return -1; if (d1.Format > d2.Format) return +1; if (d1.Format < d2.Format) return -1; if (d1.RefreshRate > d2.RefreshRate) return +1; if (d1.RefreshRate < d2.RefreshRate) return -1; // 它们一定相同,所以返回0 return 0; } } |
IComparer接口允许我们在数组或集合上执行简单的、快速排序算法。这个接口提供的唯一的方法是Compare,它必须返回整型值--也就是如果左边的数据项大于右边的就返回+1,如果左边的数据项小于右边的就返回-1,如果相等就返回0。你可以看到,在上面的实现中,显示模式的宽度有最高的优先级,接着是高度、格式和刷新率。这个次序规定了在比较两种模式(例如1280x1024和1280x768)的时候正确的操作方法。
这些模式被排序之后,就调用EnumerateDevices方法。列表6显示了这个方法。
列表6:列举设备类型
private static void EnumerateDevices(EnumAdapterInformation adapterInfo, ArrayList adapterFormatList) { // 在查找设备类型的时候忽略任何异常 DirectXException.IgnoreExceptions(); // 列举每个Direct3D设备类型 for(uint i = 0; i < deviceTypeArray.Length; i++) { // 建立一个新设备信息对象 EnumDeviceInformation deviceInfo = new EnumDeviceInformation(); // 存储该类型 deviceInfo.DeviceType = deviceTypeArray[i]; // 试图获取其性能 deviceInfo.Caps = Manager.GetDeviceCaps((int)adapterInfo.AdapterOrdinal, deviceInfo.DeviceType); // 获取该设备上每个设备组合的信息 EnumerateDeviceCombos( adapterInfo, deviceInfo, adapterFormatList); // 我们有设备组合吗? if (deviceInfo.deviceSettingsList.Count > 0) { // 有,把它添加到列表中 adapterInfo.deviceInfoList.Add(deviceInfo); } } // 打开异常处理开关 DirectXException.EnableExceptions(); } |
查看这段代码的时候,你必须注意两个非常重要的信息。你能猜到是哪两个吗?如果你猜的是对DirectXException类的调用那就对了。第一个调用关闭了受控DirectX部件内部任何异常的产生。你可能会怀疑这样做的优点,实际上这样做是出于性能的考虑。捕捉和抛出异常是很昂贵的操作,而这段代码可能产生大量的异常。你可能希望尽快地执行列举过程,因此过程中产生的任何异常都被简单地忽略了,在这个 函数执行完之后,就恢复正常的异常处理过程。这段代码看起来相当简洁,你可能会问"这段代码为什么倾向于产生异常呢"?
我有一个很好的答案:大多数情形是某种设备不支持DirectX 9。也许你没有升级显卡驱动程序或当前的显卡驱动程序所需要的必要代码路径不正确;也可能是由于显卡本身太老了,没有能力支持DirectX 9;有时候一些人通过包含不支持DirectX 9的PCI显卡激活了系统中的多监视器模式。
这个方法中的代码试图得到这个适配器的性能信息并列举出不同的组合方式,并且它试图获取每个可用的设备的这些信息。可能的设备类型包括:
· 硬件(Hardware)--建立的最常见的设备类型。呈现过程由硬件(显卡)来完成。
· 引用(Reference)--这种设备不管硬件是否能够执行处理过程,可以呈现Direct3D运行时支持的任何设置。所有的处理过程在软件中进行,这意味着在游戏中这种设备类型很慢。
· 软件(Software)--除非你编写了光栅化程序(rasterizer),否则永远不会使用这个选项。
假设在列举过程中找到了某些设备组合,就把它存储到列表中。列举类存储了少量的列表,示例框架在以后可以使用它们。列表7是EnumerateDeviceCombos方法。
列表7:列举设备组合
private static void EnumerateDeviceCombos(EnumAdapterInformation adapterInfo, EnumDeviceInformation deviceInfo, ArrayList adapterFormatList) { // 查找这种设备支持哪种适配器格式 for each(Format adapterFormat in adapterFormatList) { for(int i = 0; i < backbufferFormatsArray.Length; i++) { bool windowed = false; do { if ((!windowed) && (adapterInfo.displayModeList.Count == 0)) continue; if (!Manager.CheckDeviceType((int)adapterInfo.AdapterOrdinal, deviceInfo.DeviceType, adapterFormat, backbufferFormatsArray[i], windowed)) continue; // 不支持的 // 我们需要加速象素阴影混合吗? if (isPostPixelShaderBlendingRequired) { if (!Manager.CheckDeviceFormat( (int)adapterInfo.AdapterOrdinal, deviceInfo.DeviceType, adapterFormat, Usage.QueryPostPixelShaderBlending, ResourceType.Textures, backbufferFormatsArray[i])) continue; // 不支持的 } // 如果提供了某个应用程序回调函数,就要确保这个设备受到该应用程序的支持 if (deviceCreationInterface != null) { if (!deviceCreationInterface.IsDeviceAcceptable(deviceInfo.Caps, adapterFormat, backbufferFormatsArray[i],windowed)) continue; // 应用程序不喜欢这个设备 } EnumDeviceSettingsCombo deviceCombo = new EnumDeviceSettingsCombo(); // 存储信息 deviceCombo.AdapterOrdinal = adapterInfo.AdapterOrdinal; deviceCombo.DeviceType = deviceInfo.DeviceType; deviceCombo.AdapterFormat = adapterFormat; deviceCombo.BackBufferFormat = backbufferFormatsArray[i]; deviceCombo.IsWindowed = windowed; BuildDepthStencilFormatList(deviceCombo); BuildMultiSampleTypeList(deviceCombo); if (deviceCombo.multiSampleTypeList.Count == 0) { continue; } BuildConflictList(deviceCombo); BuildPresentIntervalList(deviceInfo, deviceCombo); deviceCombo.adapterInformation = adapterInfo; deviceCombo.deviceInformation = deviceInfo; // 把组合添加到设备列表中 deviceInfo.deviceSettingsList.Add(deviceCombo); windowed = !windowed; } while (windowed); } } } |
总结
在本文中,你开始建立了第一个游戏项目,并且看到了示例框架的一些内容。你看到了大量的列举系统中可能支持的设备组合的代码。这个示例框架是你在未来编写游戏的一个重要的出发点。
共2页。

标签: