DXUT编译指南(转)
DXUT设计指南
DXUT是一个建立在Direct3D API之上的,被大部分Direct3D指南和例子所使用的层。 它的目标是创建Direct3D例子、原型、工具,更容易的建立坚固、专业的游戏。
See also: DXUT参考.
|
DXUT概观
本主题提供对DXUT概观的高级介绍。
概观
DXUT框架的设计是为了帮助开发者在创建窗口、设备,处理窗口消息和设备事件时,更有效率(消耗较少的时间)。
这是使用该框架的应用程序的主函数:
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, INT ) { // 设置回调函数,这些函数允许DXUT通知应用程序更换设备,用户输入和窗口消息。 // 回调函数是可选的,因此你要做的仅是设置你感兴趣的事件的回调函数。 DXUTSetCallbackDeviceCreated( OnCreateDevice ); DXUTSetCallbackDeviceReset( OnResetDevice ); DXUTSetCallbackDeviceLost( OnLostDevice ); DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
DXUTSetCallbackFrameRender( OnFrameRender ); DXUTSetCallbackFrameMove( OnFrameMove );
// 初始化DXUT并创建想要的Win32窗口和应用程序的Direct3D设备。调用这些 // 可选函数中的每一个,此外它们允许你设置几个选项来控制框架的行为。 DXUTInit( TRUE, TRUE, TRUE ); DXUTCreateWindow( L"BasicHLSL" ); DXUTCreateDevice( D3DADAPTER_DEFAULT, TRUE, 640, 480 );
// 通过DXUT来处理消息循环并分派渲染调用。当在空闲时间和处理窗口消息的 // 时间间隔时,框架将调用OnFrameMove和OnFrameRender回调函数。 DXUTMainLoop(); return DXUTGetExitCode(); } |
在例子代码中,框架做了大部分的工作。它创建窗口、设备,处理主消息循环,当应用程序事件触发时,提供相应的回调函数,例如:在设备重置或渲染每帧时。DXUT框架是组件化的,应用程序可以使用框架的全部功能或部分功能。
这个设计指南的其他部分详细的包括了这些步骤,并着重于应用程序可选择的控制或可替代的步骤。
更详细的语法和函数的使用,回调函数,结构,列举和常量等信息可以在DXUT参考中找到。
特点
为了帮助你创建一个应用程序,框架提供下列服务:
- 简单的窗口和设备的创建。
- 设备事件 (created, reset, lost, destroyed) 和窗口事件 (messages, keyboard, mouse)的通知。
- 窗口模式与全屏模式之间的转换, 硬件设备和软件设备之间的转换。
- 精确的计时器。
- 命令行支持和自动化测试。
- 使用对话框或API的设备选择。
- 一套纹理化的GUI控件,包括一个可使用输入法编辑器的编辑框。
- 各种扩展类,例如简单的摄像机类型。
局限性
为了便于使用,框架只支持绑定一个设备的窗口。需要同时使用多个设备,或显示多个Direct3D窗口的高级程序,是本框架不支持的,大部分类型的应用程序将能够使用本框架来实现。
启动一个新工程
最简单的方法,启动一个使用新的Visual Studio .NET中的DXUT开发的项目:
- 启动例子浏览器(SampleBrowser.exe),在下面位置找到DirectX SDK:(SDK root)\Samples\SampleBrowser\
- 在例子浏览器里,选择一个已有的Direct3D 例子项目,将从这开始。
- 点击 “安装项目”("Install Project")链接,然后拷贝Visual Studio .NET项目文件到一个新的本地目录。
- 你可以给项目重命名,这时浏览器将根据提供的新工程名,去更改相应的文件和源代码。
DirectX April 2005 SDK中DXUT的改进
基于用户的反溃, DXUT框架在DirectX April 2005 SDK中进行了改进更新。下面是主要的差异和改进的列表。
- Callback functions now pass a void* pUserContext from the DXUTSetCallback* function to the callback. This allows the callback functions to receive context from the application such as a class pointer.
- The framework's GUI is now separate and optional from the core framework. The creation and interfacing of CDXUTDialogResourceManager is now the responsibility of the application if it wishes to use the framework's GUI.
- The framework now allows applications to reject device changes via the LPDXUTCALLBACKMODIFYDEVICESETTINGS function which returns a bool. Returning false from this callback will notify the framework to keep using the current device instead of changing to the new device. For example, by default on a multiple monitor configuration dragging the window between monitors will cause the framework to change devices. However if the application can not use the other device, it must be able to reject this change and continue to use the current device. This callback can now be set separate of the CreateDevice function by using DXUTSetCallbackDeviceChanging.
- Passing 0 as the width and height to the DXUTCreateDevice function now creates a backbuffer of the same size as the client window.
- The DXUTGetExitCode function now returns 11 if the last device was a D3DDEVTYPE_REF device type.
自DirectX Summer 2003 SDK以来DXUT的变化
The new framework is significantly redesigned from DXUT that shipped in the DirectX Summer 2003 SDK Update. The following is a list of major differences and improvements.
- Flat API design. The new framework is a set of global functions that more clearly define how the application interacts with the framework.
- Clearer naming. The device, frame, and message events have been renamed to be more easily understood.
- Modular. An application can use only parts of the framework if desired. An application can use the framework to create its own window or its own device, or it can use its own main loop.
- Better device selection. The application can fully customize the presentation parameters or behavior flags before creating the device. The new framework also has a more advanced algorithm for finding valid device settings.
- Toggling between hal and reference devices (see Device Types). Built-in toggling support is available, and toggling can be bound to a keystroke for simplified debugging.
- Better error handling. Error handling and macros can be used by the application to display error message boxes immediately if an API is called incorrectly.
- Better multiple-monitor support. If this feature is enabled, the new framework will automatically change devices if the window is moved to a different monitor.
- GUI controls. A set of textured GUI controls features an IME-enabled edit box for use in samples, prototypes, and professional games.
- No dialog dependency onGDI. Unlike the previous framework, the new framework does not require anyGDI dialog resources in the application.
- Late binding with DirectX. Late binding verifies that the required DirectX version is installed on the system.
- Unicode support. The new framework supports Unicode. Windows NT-based operating systems natively support Unicode, which means that ANSI calls need to be converted to Unicode. With this and better international support, Unicode is the better choice because it will result in better performance on these operating systems. For Windows 98 and Windows Millennium Edition (Windows Me) systems, consider using the Microsoft Layer for Unicode (MSLU).
- Documentation. The framework library functions in the Dxut.h header are documented; see DXUT Reference.
初始化DXUT
使用DXUT的第一步是初始化。可以简单的用DXUTInit 函数:
HRESULT DXUTInit ( BOOL bParseCommandLine = TRUE, BOOL bHandleDefaultHotkeys = TRUE, BOOL bShowMsgBoxOnError = TRUE ); |
你将在应用程序的 WinMain 函数开始后不远的地方调用DXUTInit。如果DXUTInit没有在应用程序中调用,它将使用默认的参数在框架中自动调用。
如果第一个参数bParseCommandLine为TRUE,框架将对命令行参数做出响应。例如,运行BasicHLSL Sample执行下列命令行参数:
BasicHLSL.exe -windowed -width:600 -height:600 |
DXUT框架将尝试执行这些窗口设定,对于命令行支持参数的全部列表,请查看DXUTInit函数部分的帮助。
第二个参数bHandleDefaultHotkeys通知DXUT框架对一些预先执行的击键做出响应,例如:ALT+ENTER。再说一次,全部的列表请查看DXUTInit函数部分的帮助。如果这个参数为FALSE,程序将不做响应。
最后一个参数bShowMsgBoxOnError,当框架发现一个错误时,显示一个消息框。设置它为FALSE则运行自动测试或完全由用户经验控制的专业程序。
使用基于DXUT的程序窗口
你的程序可以只使用一个新的DXUT函数,来处理大部分窗口管理的任务。
创建窗口
创建一个Direct3D应用程序窗口包括下列步骤:
- 定义一个响应窗口的消息。
- 创建。
- 使用。
- 使用。
这些步骤如果没做正确将会引起bugs。虽然它对于一个Direct3D程序员来说,这可能很没劲,但却是每个程序所必需的。DXUT框架使用DXUTCreateWindow函数来简单化这个过程:
HRESULT DXUTCreateWindow( const WCHAR *strWindowTitle = L"Direct3D Window", HINSTANCE hInstance = NULL, HICON hIcon = NULL, HMENU hMenu = NULL, INT x = CW_USEDEFAULT, INT y = CW_USEDEFAULT ); |
所有参数都是可选的:
- strWindowTitle是窗口标题,也显示在任务栏,它表示工程名称。
- hInstance是应用程序实例的句柄。大部分程序的默认值为NULL。
- hIcon是应用程序的图标。如果为NULL,则应用程序的可执行文件里包含的第一个图标将被使用,所以NULL值也能很好的工作。
- hMenu 处理菜单,如果想的话可以设置它,大部分游戏无论无何不能使用标准菜单,但作为替代,可以用自己定制的游戏类界面来创建它们。
- 最后2个参数描述窗口位置。如果应用程序运行在全屏模式,则忽视这2个参数。
最简单的调用方法,程序可以像下面这样调用DXUTCreateWindow:
DXUTCreateWindow( L"My New Game" ); |
只用一个参数来调用这个函数,strWindowTitle将促使DXUT框架去创建窗口,并自动处理重要的窗口消息。如果需要重新取得窗口的句柄,可以调用DXUTGetHWND。
如果想让程序响应窗口消息,可以用DXUTSetCallbackMsgProc 去设置回调函数:
void DXUTSetCallbackMsgProc( LPDXUTCALLBACKMSGPROC pCallbackMsgProc, void* pUserContext = NULL ); |
PCallbackMsgProc参数是一个LPDXUTCALLBACKMSGPROC类型的回调函数,下面是定义:
LRESULT CALLBACK FUNCTION MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL* pbNoFurtherProcessing, void* pUserContext ) { return 0; } |
在这个回调函数里,程序不需要去响应任何消息,因为所有重要的消息将被DXUT框架处理。为了阻止DXUT框架处理消息,应用程序可以设置*pbNoFurtherProcessing为TRUE。(查看LPDXUTCALLBACKMSGPROC),然而,当使用这个设置时要小心,因为它可能会阻止DXUT框架的正确行为。
使用自己的窗口
如果你想让程序去创建自己的窗口,并且和框架一起使用,而不是让DXUT处理窗口的操作,那么你可以使用DXUTSetWindow函数:
HRESULT DXUTSetWindow( HWND hWndFocus, HWND hWndDeviceFullScreen, HWND hWndDeviceWindowed, BOOL bHandleMessages = TRUE ); |
这个函数带有3个窗口句柄,它们是完全相同的,除非程序在窗口模式和全屏模式中使用不同的窗口。当应用程序因按ALT+TAB、鼠标点击、或其他用户输入,而转到后台时,将提供焦点窗口的句柄通知给Direct3D。应用程序应该经常传递焦点窗口的句柄给DXUTSetWindow函数,不管它创建多少个Direct3D设备。
除了用一个窗口初始化框架之外,应用程序需要用窗口消息通知框架,窗口接收有序的消息给正确的框架行为处理。如果框架创建了窗口,窗口消息是自动处理的。否则,应用程序可以使用DXUTStaticWndProc函数,从内部的窗口WindowProc回调函数传递窗口消息给框架,下面是定义:
LRESULT CALLBACK DXUTStaticWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); |
如果你不想让应用程序使用DXUTStaticWndProc,你可以交替复制在函数中的功能,尽管它是不被推荐的。
使用DXUT设备
使用最新版的DXUT创建DirectX设备。它可以代替你用应用程序直接创建设备,而框架的其他功能仍然可用。
创建设备
下面是用标准的Direct3D方法创建设备,CreateDevice:
HRESULT CreateDevice( UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DDevice9 **ppReturnedDeviceInterface );
|
这个方法需要正确的adapter(适配器),设备类型(hal or reference),窗口句柄,行为标记(软件/硬件顶点处理和其他设备标记),和一个表达参数(一个D3DPRESENT_PARAMETERS结构的参数)。此外,D3DPRESENT_PARAMETERS结构有众多成员,它们是:特定的后备缓存区设置,采样器设备,swap效果,窗口模式,深度模版缓存区设置,刷新速度等等。
给所有这些参数选择有效的设定就是个挑战,那么DXUT框架的DXUTCreateDevice函数简化了这一过程:
HRESULT DXUTCreateDevice( UINT AdapterOrdinal = D3DADAPTER_DEFAULT, BOOL bWindowed = TRUE, INT nSuggestedWidth = 640, INT nSuggestedHeight = 480, LPDXUTCALLBACKISDEVICEACCEPTABLE pCallbackIsDeviceAcceptable = NULL, LPDXUTCALLBACKMODIFYDEVICESETTINGS pCallbackModifyDeviceSettings = NULL ); |
大多数基本用法是很简单的,使用默认的参数调用函数:
DXUTCreateDevice(); |
对于这个简单的调用,框架创建设备的默认设定,它可以工作在大部分情况下。下面是设备创建的默认设定:
Direct3D创建标记 |
描述 |
DXUTCreateDevice的默认值 |
AdapterFormat parameter of CheckDeviceFormat |
适配器表面格式 |
Desktop display mode, or D3DFMT_X8R8G8B8 if the desktop display mode is less than 32 bits. |
Adapter parameter of IDirect3D9::CreateDevice |
显示适配器的序号 |
|
D3DPRESENT_PARAMETERS. BackBufferCount |
后备缓冲区数量 |
2, indicating triple buffering. |
D3DPRESENT_PARAMETERS. BackBufferFormat |
缓冲区格式 |
Desktop display mode, or D3DFMT_X8R8G8B8 if the desktop display mode is less than 32 bits. |
D3DPRESENT_PARAMETERS. AutoDepthStencilFormat |
设备将创建表面的深度缓冲区格式 |
D3DFMT_D16 if the backbuffer format is 16 bits or less, or D3DFMT_D32 otherwise. |
The DeviceType parameter of IDirect3D9::CreateDevice |
设备的列举类型 |
D3DDEVTYPE_HAL if available, otherwise D3DDEVTYPE_REF or failure code if neither is available. |
D3DPRESENT_PARAMETERS. MultiSampleQuality |
品质级别 |
MultiSampleQuality = 0, indicating multisampling is disabled. |
D3DPRESENT_PARAMETERS. Flags |
Presentation parameters flags. |
|
D3DPRESENT_PARAMETERS. PresentationInterval |
Presentation interval. |
D3DPRESENT_INTERVAL_IMMEDIATE for windowed mode, or D3DPRESENT_INTERVAL_DEFAULT for full-screen mode. |
D3DPRESENT_PARAMETERS. FullScreen_RefreshRateInHz |
显示器的刷新速度 |
0, indicating windowed mode. |
D3DPRESENT_PARAMETERS. BackBufferWidth and .BackBufferHeight |
显示模式 |
640 x 480 pixels for windowed mode, or the desktop resolution for full-screen mode. |
D3DPRESENT_PARAMETERS. AutoDepthStencilFormat |
设备将创建表面的模版缓冲区格式 |
D3DFMT_D16 if the backbuffer format is 16 bits or less, or D3DFMT_D32 otherwise. |
D3DPRESENT_PARAMETERS. SwapEffect |
交换效果. |
D3DSWAPEFFECT_DISCARD |
BehaviorFlags parameter of IDirect3D9::CreateDevice |
顶点处理标记 |
D3DCREATE_HARDWARE_VERTEXPROCESSING if supported, otherwise D3DCREATE_SOFTWARE_VERTEXPROCESSING. |
D3DPRESENT_PARAMETERS. Windowed |
窗口或全屏模式 |
true, indicating windowed mode. |
hFocusWindow parameter of CreateDevice |
处理创建的窗口 (查看使用基于DXUT的程序窗口). |
hWndFocus parameter of DXUTSetWindow |
D3DPRESENT_PARAMETERS. hDeviceWindow |
设备窗口的处理 |
hWndDeviceFullScreen or hWndDeviceWindowed parameters of DXUTSetWindow |
D3DPRESENT_PARAMETERS. EnableAutoDepthStencil |
深度/模版缓冲区的创建标记 |
true. |
比使用一个设备创建很多默认设定更好的方法是,应用程序能够通过传递给CreateDevice函数的参数来使设备应用更多的控制。例如,你可能通过SuggestedWidth和nSuggestedHeight参数改变窗口的尺寸:
DXUTCreateDevice( D3DADAPTER_DEFAULT, false, 1024, 768, NULL, NULL, NULL ); |
为了取得更多的控制,应用程序可以使用2个可选的回调函数: LPDXUTCALLBACKISDEVICEACCEPTABLE和LPDXUTCALLBACKMODIFYDEVICESETTINGS.
选择最佳设备设定
你可以在程序里使用IsDeviceAcceptable回调函数来帮助框架选择佳的设备设定,请看下列代码:
bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext ) { // TODO: 可接受的设定返回true,否则返回false return true; } |
这个回调函数的原型是LPDXUTCALLBACKISDEVICEACCEPTABLE。框架为下列5个设定中每个唯一有效的组合,一次性的调用这个函数:
D3DDEVTYPE DeviceType; UINT AdapterOrdinal; D3DFORMAT AdapterFormat; D3DFORMAT BackBufferFormat; bool Windowed; |
注意:适配器序号和设备类型不直接传递给回调函数,而是分别的传递,D3DCAPS9结构中的设备类型(DeviceType)和设备序号(AdapterOrdinal)成员。
在这个回调函数里,应用程序可以拒绝它不支持的或接受任何组合。举个例子,程序可以使用下列代码拒绝16-bit格式的后备缓冲区,并且让所有设备不支持像素着色器ps_2_0以下的版本:
bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, bool bWindowed ) { if( pCaps->PixelShaderVersion < D3DPS_VERSION(2,0) ) return false; if( BackBufferFormat == D3DFMT_X1R5G5B5 || BackBufferFormat == D3DFMT_R5G6B5 ) return false; return true; } |
回调函数调用设定中每个唯一的组合以后,框架队列保存可接受的组合并选择他们中最好的之一。排名较高的组合包括下面这些:
- D3DDEVTYPE_HAL, 为了取得硬件的加速度。
- 如果程序在全屏模式下显示,框架默认的格式是与桌面适配器匹配的,所以从窗口向全屏的模式转换是很快的。如果桌面显示模式小于32bits时会触发异常,在这种情况下框架默认D3DFMT_X8R8G8B8格式。
- 后备缓冲区格式,它与适配器格式相匹配。
对于这些选择的设定中最高级别的组合,行为标记和表达参数在创建设备时仍然是必需的,对于这些设定,Direct3D使用上表所示的默认值。
修改可用的设备设定
应用程序可以修改框架的可选项,使用第2个回调函数:ModifyDeviceSettings,类似下面这样:
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps ) { // TODO: Include device creation requirements here. // 创建设备返回true,保持当前设备返回false return true; } |
这个回调函数的方法原型是:LPDXUTCALLBACKMODIFYDEVICESETTINGS。DXUTDeviceSettings结构在框架中是这样定义的:
struct DXUTDeviceSettings { UINT AdapterOrdinal; D3DDEVTYPE DeviceType; D3DFORMAT AdapterFormat; DWORD BehaviorFlags; D3DPRESENT_PARAMETERS pp; }; |
这个结构包括了创建设备所需要的所有东西(除了窗口句柄),假定早就创建了窗口句柄。框架用有效的值填充这个结构,并允许程序通过ModifyDeviceSettings回调函数去更改设备(创建选择的设备)。
在这个回调函数中程序可以更改行为标记,像DXUTDeviceSettings结构中的表达参数一样好用。如果应用程序在回调函数里没有更改任何东西,设备将创建成功。然而,任何设备设定的更改都需要应用程序对设备的支持,否则设备创建可能会失败。
例如,如果应用程序需要一个D3DFMT_D24S8格式的深度模版,将检查设备是否支持它,使用下面的代码:
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps ) { IDirect3D9* pD3D = DXUTGetD3DObject(); if( SUCCEEDED( pD3D->CheckDeviceFormat( pDeviceSettings->AdapterOrdinal, pDeviceSettings->DeviceType, pDeviceSettings->AdapterFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24S8 ) ) ) { if( SUCCEEDED( pD3D->CheckDepthStencilMatch( pDeviceSettings->AdapterOrdinal, pDeviceSettings->DeviceType, pDeviceSettings->AdapterFormat, pDeviceSettings->pp.BackBufferFormat, D3DFMT_D24S8 ) ) ) { pDeviceSettings->pp.AutoDepthStencilFormat = D3DFMT_D24S8; } }
return true; } |
作为替代, 回调函数也可以使用框架的CD3DEnumeration 对象去检查是否支持D3DFMT_D24S8格式:
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps ) { CD3DEnumeration *pEnum = DXUTGetEnumeration(); CD3DEnumDeviceSettingsCombo *pCombo;
pCombo = pEnum->GetDeviceSettingsCombo( pDeviceSettings );
if( pCombo->depthStencilFormatList.Contains( D3DFMT_D24S8 ) ) pDeviceSettings->pp.AutoDepthStencilFormat = D3DFMT_D24S8;
return true; } |
应用程序更改设备设定后,框架用新的设定创建设备。
这个回调函数返回bool值。如果应用程序返回TRUE,框架将创建标准设备。如果返回FALSE,如果有设备存在的话,框架不更改设备,保持当前的设备。这样就允许应用程序拒绝框架的请求(更改应用程序所不支持的设备)。例如,在一个多显示器的结构里,默认在显示器间拖曳窗口将导致框架更改设备,然而如果应用程序不能使用另一个设备,它必须能够拒绝更改,并继续使用当前的设备。
软件顶点处理
如果我在硬件上创建一个Direct3D设备,它支持硬件像素处理,但不支持硬件顶点处理,那么你需要设置行为标记。在IsDeviceAcceptable回调函数里,为了确保正确的回调给软件顶点处理,当IsDeviceAcceptable回调函数里不能拒绝基于顶点着色器版本的设备时,你要采取防备措施;并确保行为标记在ModifyDeviceSettings回调函数里被正确的调用。这里有一个例子:
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps ) { // 如果设备不支持硬件的T&L 或硬件的顶点着色器版本1.1,那么转换到SWVP(软件顶点处理). if( (pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0 || pCaps->VertexShaderVersion < D3DVS_VERSION(1,1) ) { pDeviceSettings->BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING; } else // 支持 { pDeviceSettings->BehaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING; }
return true; } |
使用你自己的设备
你可以不必依赖框架创建Direct3D设备,替代的方法是,用程序自己创建设备,并且传递它给框架使用。类似的方法是应用程序可以覆盖框架的window creation设定。用所有想要的设定简单的创建一个设备,然后调用DXUTSetDevice函数使框架能够在设备上进行渲染。
注意 如果应用程序创建了独立于框架的设备,在主循环执行完毕之后清除资源时,程序必须释放设备接口。
使用DXUT主循环
在窗口和设备创建后,程序需要使用主循环(调用一个渲染循环或消息循环)来响应窗口消息。更新并渲染场景,处理设备事件。程序可以自己实现主循环,也使用DXUT实现。回调函数的注册允许DXUT去处理设备、框架和事件消息。
进入主消息循环
要使用框架的主循环,简单的调用DXUTMainLoop,唯一的参数设置为NULL。
尽管用框架去处理消息循环相当容易,对于一些高级程序,定制主循环是更好的设计。使用DXUTMainLoop函数定制的主循环是可能的,但需要在程序中写更多的代码,下面是例子代码:
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, INT ) { DXUTSetCallbackDeviceCreated( OnCreateDevice ); DXUTSetCallbackDeviceReset( OnResetDevice ); DXUTSetCallbackDeviceLost( OnLostDevice ); DXUTSetCallbackDeviceDestroyed( OnDestroyDevice ); DXUTSetCallbackFrameRender( OnFrameRender ); DXUTSetCallbackFrameMove( OnFrameMove );
DXUTInit( TRUE, TRUE, TRUE ); DXUTCreateWindow( L"BasicHLSL" ); DXUTCreateDevice( D3DADAPTER_DEFAULT, TRUE, 640, 480 );
// ------------------------------------------------ // 定制主循环 HWND hWnd = DXUTGetHWND(); BOOL bGotMsg; MSG msg; msg.message = WM_NULL; PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );
while( WM_QUIT != msg.message ) { // 使用PeekMessage(),因为我们使用空闲时间渲染场景 bGotMsg = ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) != 0 );
if( bGotMsg ) { // 转换并分派消息 if( 0 == TranslateAccelerator( hWnd, NULL, &msg ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } else { // 在空闲时间渲染一帧 (没有消息则等待ing) DXUTRender3DEnvironment(); } } // 定制主循环结束 // ------------------------------------------------ return DXUTGetExitCode(); } |
在这个例子代码中,程序调用DXUTRender3DEnvironment函数来进行每帧的更新、渲染场景和处理设备事件。在程序中完全的复制这个函数是可行的,但我们不推荐这样做。
处理事件
该框架使用回调函数机制来实现程序对事件的响应。应用程序只需要注册或设置一个框架的函数指针,当事件触发时框架将调用这个函数。框架不需要注册所有回调函数,因此应用程序仅释放因需要而注册的回调函数就行了。在DirectX April 2005 SDK中,回调函数从void* pUserContext类型的函数中传递一个void* pUserContext来完成回调。这样允许回调函数接收应用程序的上下文,例如类的指针。在框架中触发的3种类型的事件:
设备事件
当应用程序在渲染Direct3D设备时,设备的丢弃(lost)是有可能的。导致这样事情的发生有很多原因,比如当用户按ALT+TAB键离开全屏程序时,当用户按CTRL+ALT+DEL,或者当用户运行其他全屏的3D应用程序时。当调用特定的函数时,例如Present,这时就会返回一个D3DERR_DEVICELOST值,这时Direct3D API会通知应用程序。
当设备丢弃时,释放所有Direct3D对象是应用程序的责任,但当设备丢弃时就不可挽回了,例如在D3DPOOL_DEFAULT类型的内存中创建的对象。如果对象没有被释放,那么它从丢弃的状态返回时,设备将不能重置(reset)。
当设备丢弃时应用程序必须等待。当设备返回时,程序必须调用Reset,并重新创建所有设备对象,重置设备将无法找回丢弃的设备对象。
使用DXUT框架,在程序中使用回调函数去处理不同的事件是很简单的:设备更改,创建,重置,丢弃或释放。当设备丢弃时框架将会记录下来,当它从丢弃状态返回时框架将全部重置设备。框架在适当的时候使用程序的回调函数去释放和重建设备对象。下列是所有需要应用程序实现的回调函数:图表略。
回调注册函数 |
应用程序回调函数 |
框架调用的时间 |
在Direct3D设备创建以后调用。它给了程序一个阻止设备更改的机会。 |
||
在Direct3D设备创建后被调用,通常发生在应用程序初始化和设备重建期间。 |
||
在Direct3D设备重置以后调用,通常发生在设备丢弃之后。 |
||
在Direct3D 设备进入丢弃状态和设备重置期间。 |
||
在Direct3D 毁坏后调用,通常发生应用程序终止或设备重建时。 |
当在窗口与全屏模式之间切换设备时, 它通常会执行设备重置(reset),但有时它必须重建Direct3D 。
这些回调注册函数是可选的。然而,如果程序释放了没有注册设备,并且设备使用:DXUTSetCallbackDeviceDestroyed和DXUTSetCallbackDeviceCreated创建了的回调函数,那么框架不能够重建设备,因为它将没有办法通知应用程序,设备被释放或重建了。在这种情况下,更改设备或在hal和reference devices之间转换将无法工作。
同样,如果程序没有注册丢弃的设备并且设备调用:DXUTSetCallbackDeviceLost和DXUTSetCallbackDeviceReset重置回调函数。那么当设备丢弃和重置时,框架将没有办法通知程序。在这种情况下,如果所有的设备对象不在D3DPOOL_MANAGED类型的内存里,重置设备将导致Direct3D失败。
同样注意,如果应用程序使用DXUTCreateDevice函数,它不需要调用DXUTSetCallbackDeviceChanging,因为框架将会记得LPDXUTCALLBACKMODIFYDEVICESETTINGS函数。
框架事件
DXUT框架同样提供框架事件,它在渲染过程期间的每帧中调用。应用程序将注册并实现下列的回调函数:
应用程序回调函数 |
回调注册函数 |
框架调用的时间 |
场景渲染 |
每帧开始的时候调用一次 |
这个回调函数是程序更新场景最好的地方, 但它不包括实际的渲染调用, 它在框架的渲染回调函数 (LPDXUTCALLBACKFRAMERENDER) 里。 |
||
LPDXUTCALLBACKFRAMERENDER |
如果窗口需要重画时,在每帧结束的时候调用。 |
将在这个回调函数里执行所有场景的渲染调用。 这个回调函数返回后,框架将调用Present 来显示在交换链中的下个缓冲区的内容。 |
消息事件
框架通过回调函数和相应的注册函数传递窗口消息,键盘事件和鼠标事件。编写程序以提供响应事件。
应用程序回调函数 |
回调注册函数 |
描述 |
从DXUT消息队列中处理窗口消息。 |
||
从DXUT消息队列中处理键盘消息。 |
||
从DXUT消息队列中处理鼠标消息。 |
处理DXUT的错误
本主题包括被推荐的过程,该过程处理从DXUT程序中返回的错误。
错误处理效率
Direct3D API的设计使处理失败更容易。即使大部分基本的Direct3D API函数返回HRESULTs值,API被设计成只有少数的方法和函数返回设备错误,如:D3DERR_DEVICELOST或D3DERR_DRIVERINTERNALERROR。然而,一个典型的Direct3D应用程序将使用许多不同的API方法和函数,当传递了错误的参数会返回D3DERR_INVALIDCALL。
当一个Direct3D应用程序开发时,应该检测所有的API调用是否失败,如果失败发生那么函数将是无法预期的,程序应该设计成立即通知开发者或记录错误。这样当API函数调用出错时,开发者可以很快捕捉到出错位置。然而对于一个稳定的程序,大多数Direct3D API函数能够正确的调用并安全的忽略错误代码,在一些关键API函数中返回错误并处理异常,例如:Present 或 TestCooperativeLevel。
因为只有少数代码需要进行错误处理,因此在处理大部分重要的Direct3D函数时,你能够改善执行速度并使程序代码更有预见性、更强壮。在其他少数的API函数中必须对失败进行正确的处理,例如:Present,它是在框架内部处理的。
错误消息
框架中的错误处理,在Direct3D API的设计中是如何匹配错误处理的(Error handling in the framework matches how error handling in the Direct3D API was designed.)。对于严重的错误,例如:缺少媒体资源,程序会通知用户并终止。对于在每一帧中调用的大多数API函数,错误处理仅仅是在debug编译版本中给开发者显示一个错误对话框。对于更详细的builds,这些错误是被忽视的。框架用在Dxstdafx.h中定义的几个宏来完成:
#if defined(DEBUG) | defined(_DEBUG) #define V(x) { hr = x; if( FAILED(hr) ) { DXUTTrace( __FILE__, (DWORD)__LINE__, hr, L#x, TRUE ); } } #define V_RETURN(x) { hr = x; if( FAILED(hr) ) { return DXUTTrace( __FILE__, (DWORD)__LINE__, hr, L#x, TRUE ); } } #else #define V(x) { hr = x; { #define V_RETURN(x) { hr = x; if( FAILED(hr) ) { return hr; } } #endif |
一个典型的程序可以像下面这样使用V()宏:
V( g_pEffect->BeginPass( iPass ) ); V( g_pEffect->CommitChanges() ); V( g_pMesh->DrawSubset( 0 ) ); |
每个调用都使用V()宏,release编译版本将忽视API返回的代码。对于debug编译版本,宏将显示一个错误消息,类似下面这样:
图 1. V()宏返回错误消息给debug编译版本
编译器的debug输出窗口将显示类似下面这样的消息框:
|
c:\basichlsl\basichlsl.cpp(242): D3DXCreateFont( pd3dDevice, 15, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial", NULL ) hr=D3DERR_INVALIDCALL (0x8876086c) |
当使用Visual Studio .NET时,只要在debug输出窗口上简单的双击这一行,就会跳过发生错误的那行代码。
应用程序可以像下面这样使用V_RETURN()宏:
V_RETURN( D3DXCreateEffectFromFile( pd3dDevice, str, NULL, NULL, dwShaderFlags, NULL, &g_pEffect, NULL ) ); |
这个宏将在debug编译版本中显示一个类似的错误消息,但对于release编译版本将同样返回任何失败的HRESULT类型给调用函数。这些错误消息的显示,能够避开一个对DXUTInit的调用或下面的命令行参数:
-noerrormsgboxes
要了解更多宏的用法,请查看 Tutorials and Samples部分。
DXUT高级设备选择
本主题包括控制使用DXUT创建Direct3D设备的高级细节。
- 选择最佳的设备
- 控制设备选择对话框
选择最佳设备(Best Available Device)
DXUT使用高度灵活的方法从列表中选择最佳的设备,这个设备列表和队列系统可以独立的使用,在每帧的空闲期间(rest of the framework)调用DXUTFindValidDeviceSettings函数:
HRESULT DXUTFindValidDeviceSettings( DXUTDeviceSettings* pOut, DXUTDeviceSettings* pIn, DXUTMatchOptions* pMatchOptions ) |
PIn参数是一个已有的DXUTDeviceSettings结构的指针。PMatchOptions参数描述保存的是哪个输入设备的设定,哪个最接近的有效设定将被匹配,当选择当前最佳的设备时哪个设定将被忽视。
举个例子,假如调用函数想有一个D3DFMT_A2B10G10R10格式的hal设备的后备缓冲区。如果系统中的hal设备不支持这个后备缓冲区格式,但reference设备已经安装,函数在reference设备和更改为与reference设备兼容的后备缓冲格式中做出选择。匹配的选择在DXUT_MATCH_TYPE列举中,让调用者控制如何选择这些模式。
每个匹配的选项(查看DXUTMatchOptions) 必须是下列类型之一:
DXUT_MATCH_TYPE 值 |
描述 |
DXUTMT_IGNORE_INPUT |
为默认的设备设定使用最接近有效值的值。 |
DXUTMT_PRESERVE_INPUT |
使用没改变的输入参数,没有找到有效的设备是可能的。 |
DXUTMT_CLOSEST_TO_INPUT |
为输入使用最接近有效值的值。 |
如果pMatchOptions为NULL,那么所有匹配选项是DXUTMT_IGNORE_INPUT。如果没能找到有效的设备设定,DXUTFindValidDeviceSettings返回一个失败的代码;否则,函数返回成功的代码,有效的设备设定被写到pOut参数里。
控制设备选择对话框
你可能想完全控制DXUT设备选择对话框中列出的,选项列表中选择的项目。例如,可能是通知设备选择对话框,程序需要一个模版缓冲。
在框架的设备选择对话框中控制选项的列表,可以使用LPDXUTCALLBACKISDEVICEACCEPTABLE回调函数。将允许你去控制设备设定的哪个组合(adapter ordinal, device type, adapter format, back buffer format, and windowed)。在设备选择对话框中控制其他设定,你可以使用CD3Denumeration类的成员函数,它定义在DXUTenum.cpp文件中,公布在下面的头文件中:
(SDK root)\Samples\C++\Common\DXUTenum.h
举个例子,在对话框中控制深度缓冲区格式,你可以使用下列代码:
CGrowableArray<D3DFORMAT>* pDSList; pDSList = DXUTGetEnumeration()->GetPossibleDepthStencilFormatList();
nIndex = pDSList->IndexOf( D3DFMT_D16 ); if( nIndex >= 0 ) pDSList->Remove( nIndex );
nIndex = pDSList->IndexOf( D3DFMT_D24X8 ); if( nIndex >= 0 ) pDSList->Remove( nIndex );
nIndex = pDSList->IndexOf( D3DFMT_D24S8 ); if( nIndex >= 0 ) pDSList->Remove( nIndex ); |
调用CD3Denumeratio类的函数,像这个例子代码,必须在设备创建以前使用。
DXUT高级功能
下面可选择的函数允许你更改CXUT的行为、取得内部变量。
Function |
Description |
Window Management |
|
取得应用程序实例的句柄。 |
|
取得当前设备窗口的句柄。 |
|
取得焦点窗口的句柄。 |
|
取得全屏模式设备窗口的句柄。 |
|
取得窗口模式设备窗口的句柄。. |
|
取得应用程序设备窗口的矩形客户区。 |
|
取得应用程序窗口标题的指针。 |
|
应用程序是否在窗口模式。 |
|
Device Management |
|
在全屏模式下为鼠标的用法设置可选项。 |
|
在多显示器下设置框架函数的行为选项。 |
|
应用程序的窗口和全屏模式间的转换。 |
|
应用程序在 hal 和 reference devices间的转换。 |
|
DXUT Framework Management |
|
丢失所有先前的框架状态,重置框架状态(初始化默认状态)。 |
|
引发程序终止和框架的清除。 |
|
取得框架出口代码。 |
|
Direct3D Variable Retrieval |
|
取得 IDirect3D9对象的指针。 |
|
取得IDirect3DDevice9接口指针,描述当前设备。 |
|
取得DXUTDeviceSettings结构用来创建当前设备。 |
|
取得当前设备的表示参数。 |
|
取得D3DSURFACE_DESC 接口的指针,描述当前适配器的后备缓冲区。 |
|
取得当前设备能力的D3DCAPS9 指针。 |
|
Statistics |
|
取得当前每秒的帧数。 |
|
取得一个字符串指针,包含当前每秒帧数,resolution,后备缓冲区格式,深度/模版缓冲区格式。 |
|
取得一个字符串指针,包含当前设备类型,顶点处理行为,设备名称。 |
|
Time |
|
取得当前时间(秒) |
|
取得上一帧到现在所消耗的帧数。 |
|
使用或禁用一个帧与帧间的常量时间。 |
|
Timer |
|
初始化一个新的时间器。 |
|
卸载一个已有的时间器。 |
|
Pause |
|
设置帧间时间并且/或者渲染过程的暂停状态。 |
|
在当前的设备上是否渲染已经暂停。 |
|
在当前的设备上是否时间已经暂停。 |
|
User Input |
|
当函数被调用时,指定的一个键盘键是否处在弹起或按下状态。 |
|
当函数被调用时,指定的一个鼠标键是否处在弹起或按下状态。 |
DXUT参考
这节包含了在DXUT的dxut.h头文件中提供的基本的参考信息。DXUT创建Direct3D例子、原型、工具,并更容易的建立坚固、专业的游戏。它简化了窗口和Direct3D API的使用。
参考内容分为下列部分:
See Also
函数(functions)
DXUT提供下列函数给应用程序使用:
- DXUTCreateDevice
- DXUTCreateDeviceFromSettings
- DXUTCreateWindow
- DXUTFindValidDeviceSettings
- DXUTGetBackBufferSurfaceDesc
- DXUTGetD3DDevice
- DXUTGetD3DObject
- DXUTGetDeviceCaps
- DXUTGetDeviceSettings
- DXUTGetDeviceStats
- DXUTGetElapsedTime
- DXUTGetExitCode
- DXUTGetFPS
- DXUTGetFrameStats
- DXUTGetHINSTANCE
- DXUTGetHWND
- DXUTGetHWNDDeviceFullScreen
- DXUTGetHWNDDeviceWindowed
- DXUTGetHWNDFocus
- DXUTGetPresentParameters
- DXUTGetShowMsgBoxOnError
- DXUTGetTime
- DXUTGetWindowClientRect
- DXUTGetWindowTitle
- DXUTInit
- DXUTIsKeyDown
- DXUTIsMouseButtonDown
- DXUTIsRenderingPaused
- DXUTIsTimePaused
- DXUTIsWindowed
- DXUTKillTimer
- DXUTMainLoop
- DXUTPause
- DXUTRender3DEnvironment
- DXUTResetFrameworkState
- DXUTSetCallbackDeviceChanging
- DXUTSetCallbackDeviceCreated
- DXUTSetCallbackDeviceDestroyed
- DXUTSetCallbackDeviceLost
- DXUTSetCallbackDeviceReset
- DXUTSetCallbackFrameMove
- DXUTSetCallbackFrameRender
- DXUTSetCallbackKeyboard
- DXUTSetCallbackMouse
- DXUTSetCallbackMsgProc
- DXUTSetConstantFrameTime
- DXUTSetCursorSettings
- DXUTSetDevice
- DXUTSetMultimonSettings
- DXUTSetTimer
- DXUTSetWindow
- DXUTShutdown
- DXUTStaticWndProc
- DXUTToggleFullscreen
- DXUTToggleREF
回调函数(structures)
DXUT 提供下列回调函数,它们可以在应用程序中实现:
- LPDXUTCALLBACKDEVICECREATED
- LPDXUTCALLBACKDEVICEDESTROYED
- LPDXUTCALLBACKDEVICELOST
- LPDXUTCALLBACKDEVICERESET
- LPDXUTCALLBACKFRAMEMOVE
- LPDXUTCALLBACKFRAMERENDER
- LPDXUTCALLBACKISDEVICEACCEPTABLE
- LPDXUTCALLBACKKEYBOARD
- LPDXUTCALLBACKMODIFYDEVICESETTINGS
- LPDXUTCALLBACKMOUSE
- LPDXUTCALLBACKMSGPROC
- LPDXUTCALLBACKTIMER
结构(structures)
DXUT提供下列结构:
列举(Enumerations)
DXUT提供下列列举类型:
常量(Constants)
DXUT 提供下列常量: