分类: C/C++
2008-02-20 13:03:28
作者:clayman
转载请注明作者,商业使用请联系我^_^
理解框架
首先创建工程,添加对DirectX程序集的引用。接下来,把sample framework添加到工程中。我们把这些文件放到一个单独的文件夹中,在解决方案管理器中点击右键---添加---新建文件夹,并把它命名为framework。右键点击新创建的文件夹,选择添加现有项,导航到SDK的\Samples\Managed\Common目录下,把每一个文件添加到项目中。
好了,现在回到我们刚才创建的Form1.cs文件中来,可以看到大部分自动生成的代码都是用来创建Windows Form应用程序的。因此删除所有代码,并添加如下代码:
using System;
using System.Configuration;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.Samples.DirectX.UtilityToolkit;
public class GameEngine : IDeviceCreation
{
static int
{
using(Framework sampleFramework = new Framework())
{
return sampleFramework.ExitCode;
}
}
}
这里有三个需要注意的地方 。首先,我们只留下了一个修改过的main方法。由于其他代码都是窗体设计器为Windows Form程序生成的,所以完全可以删除它们。其次,代码现在还不能通过编译,应为GameEngine类还有两个接口没有实现。第三,这段代码实际上什么也没做。
首先我们来实现IDeviceCreation接口,你将通过他来控制枚举和创建device。在这里,枚举的含义包括检查目标机器上有几块显卡、几个显示器、可以支持多少种显示模式、控制刷新率等等。
public bool IsDeviceAcceptable(Caps caps, Format adapterFormat,Format backBufferFormat, bool windowed)
{
if (!Manager.CheckDeviceFormat(caps.AdapterOrdinal, caps.DeviceType,adapterFormat, Usage.QueryPostPixelShaderBlending,
ResourceType.Textures, backBufferFormat))
return false;
if (caps.MaxActiveLights == 0)
return false;
return true;
}
public void ModifyDeviceSettings(DeviceSettings settings, Caps caps)
{
if ( (caps.DeviceCaps.SupportsPureDevice) && ((settings.BehaviorFlags & CreateFlags.HardwareVertexProcessing) != 0 ) )
settings.BehaviorFlags |= CreateFlags.PureDevice;
}
第一个方法将在device初始化时调用,用来检查device最低能支持什么特性(capability)。当sample framework在系统上枚举设备时,他会对所找到的每一种可能组合调用这个方法。注意看这个方法是如何决定返回值的。他的第一个参数包含了大量关于制定设备的信息,可以帮你决定它是否是你希望创建的device类型。接下来的参数一个是关于后备缓冲的,另一个则是关于设备格式。最后一个参数则是检查是否支持窗口模式。虽然大多数游戏都是运行在全屏模式,但在窗口模式之下调试程序会方便一些。这个方法默认情况下将返回true,但他还做了两个特别的检查。首先,检查它是否支持alpha混合(创建游戏的用户界面将用到他)。其次,检查是否支持动态灯光----没有灯光物体看起来会很单调而且不真实,所以至少使用一个灯光。
再来看第二个方法:在创建device之前调用它,来修改创建设备的参数。Setting参数包含了框架为device所制定的设置,你可以自由的修改这些设置。需要特别注意的是sample framework不会验证这些设置的有效性,因此,你需要自己来验证。
在继续之前,还有一件事情要做。由于sample framework包含一些unsafe的代码块,因此,必须允许工程中包含不安全代码:
现在,可以使用框架来枚举设备了。首先,为GameEngine类添加一个构造函数,把从main方法中创建的sample framework实例作为参数:
private Framework sampleFramework = null;
public GameEngine(Framework f)
{
// Store framework
sampleFramework = f;
}
在调用了sample framework之后,他所做的第一件事就是枚举系统设备。在工程中,打开之前添加的dxmutenum.cs文件。这个文件包含了枚举设备的所有代码。由于知道如何以及为什么枚举设备是很重要的,我们来仔细看一下这些代码。
首先注意到Enumeration是不能被实例化的,塔顶每一个方法和成员也都是静态的。因为就目前来说,你的硬件图形设备在运行时不太可以改变,所以枚举的过程在程序一开始运行一次就可以了。
大多数的枚举过程都是在创建device之前,通过sample framework调用Enumerate方法开始的。这个方法所接受的唯一参数,就是我们至今为止在GameEngine类中实现的接口。在枚举设备状态组合的时候,需要调用IsDeviceAcceptable方法来判断这个设备状态是否应该添加到正确的设备列表中。那么到底是如何来枚举设备的呢?实际上大多数功能都是通过Manager类来完成的。如果你熟悉普通的DirectX API,那么这个类实际上就是COM接口Idirect3D9的映射。(注:枚举设备的过程这里不再讲解,请看我以前翻译过的文章)
把所有符合框架要求的显示模式都保存到一个列表中,最后,通过实现Icomparer接口对他们进行排序。
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;
return 0;
}
}
这里的算法很简单,大家自己看吧。保存了可用的显示模式之后,调用EnumerateDevices方法。
private static void EnumerateDevices(EnumAdapterInformation adapterInfo,
ArrayList adapterFormatList)
{
// Ignore any exceptions while looking for these device types
DirectXException.IgnoreExceptions();
// Enumerate each Direct3D device type
for(uint i = 0; i < deviceTypeArray.Length; i++)
{
// Create a new device information object
EnumDeviceInformation deviceInfo = new EnumDeviceInformation();
// Store the type
deviceInfo.DeviceType = deviceTypeArray[i];
// Try to get the capabilities
deviceInfo.Caps = Manager.GetDeviceCaps((int)adapterInfo.AdapterOrdinal, deviceInfo.DeviceType);
// Get information about each device combination on this device
EnumerateDeviceCombos( adapterInfo, deviceInfo, adapterFormatList);
// Do we have any device combinations?
if (deviceInfo.deviceSettingsList.Count > 0)
{
// Yes, add it
adapterInfo.deviceInfoList.Add(deviceInfo);
}
}
// Turn exception handling back on
DirectXException.EnableExceptions();
}
观察一下这段代码,你应该注意到并且记住两件事。猜猜是什么?如果答案是DirectXException类方法调用,那么恭喜,答对了。DirectXException.IgnoreExceptions();方法关闭了Managed DirectX程序集中所有异常抛出。你可能会问这样做有什么好处,答案是性能的提升。捕捉和抛出异常是系统花费很大的操作,而这段代码有可能导致多个异常的抛出。你只希望快速的完成枚举,因此,暂时忽略所有异常,在这段代码结束的时候,再打开异常机制。虽然这里的代码很简单,但你可能会问问什么会导致异常。
很高兴你问到了这个问题,那么我们就来仔细讨论一下吧。最常见的问题就是你的设备不支持DirectX 9。有可能你没有更新驱动程序,或者当前的驱动程序没有正确安装。也有可能是你的显卡太老了无法使用DirectX 9。通常PCI接口的显卡都不能很好的支持DirectX 9了。
这段代码尝试获得关于显卡能力的信息,并为适配器枚举合适的组合。可用的设备类型有以下几种:Hardware, Reference, software。
假设枚举时找到了合适的设备设置组合,就把他保存到一个列表中。Enumeration类为之后创建device保存了一些列表,观察EnumerateDeviceCombos方法。注意,如果IsDeviceAcceptable方法返回false,那么就忽略这种设备组合类型。