下面就是入口点,这一次我们将从入口点开始分析一下D3DApp框架的运行过程。
//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: Entry point to the program. Initializes everything, and goes into a
// message-processing loop. Idle time is used to render the scene.
//-----------------------------------------------------------------------------
INT WINAPI WinMain (HINSTANCE hInst, HINSTANCE, LPSTR, INT)
{
cMyHost d3dApp;
srand(timeGetTime());
// InitCommonControls();
if (FAILED (d3dApp.Create (hInst)))
return 0;
INT result= d3dApp.Run();
cCodeTimer::RootTimer.outputAllTimers(0xffffffff);
return result;
}
第一句就是新建了一个cMyHost对象d3dApp。而cMyHost的构造函数是空的,主要是在父类中做参数的初始化。
那么下一句就是d3dApp.Create(hInst)了。
HRESULT CD3DApplication::Create( HINSTANCE hInstance )
{
HRESULT hr;
// Create the Direct3D object
m_pD3D = Direct3DCreate9( D3D_SDK_VERSION );
if( m_pD3D == NULL )
return DisplayErrorMsg( D3DAPPERR_NODIRECT3D, MSGERR_APPMUSTEXIT );
// Build a list of Direct3D adapters, modes and devices. The
// ConfirmDevice() callback is used to confirm that only devices that
// meet the app's requirements are considered.
m_d3dEnumeration.SetD3D( m_pD3D );
m_d3dEnumeration.ConfirmDeviceCallback = ConfirmDeviceHelper;
if( FAILED( hr = m_d3dEnumeration.Enumerate() ) )
{
SAFE_RELEASE( m_pD3D );
return DisplayErrorMsg( hr, MSGERR_APPMUSTEXIT );
}
// Unless a substitute hWnd has been specified, create a window to
// render into
if( m_hWnd == NULL)
{
// Register the windows class
WNDCLASS wndClass = { 0, WndProc, 0, 0, hInstance,
LoadIcon( hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON) ),
LoadCursor( NULL, IDC_ARROW ),
(HBRUSH)GetStockObject(WHITE_BRUSH),
NULL, _T("D3D Window") };
RegisterClass( &wndClass );
// Set the window's initial style
m_dwWindowStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME |
WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE;
// Set the window's initial width
RECT rc;
SetRect( &rc, 0, 0, m_dwCreationWidth, m_dwCreationHeight );
AdjustWindowRect( &rc, m_dwWindowStyle, true );
// Create the render window
m_hWnd = CreateWindow( _T("D3D Window"), m_strWindowTitle, m_dwWindowStyle,
CW_USEDEFAULT, CW_USEDEFAULT,
(rc.right-rc.left), (rc.bottom-rc.top), 0,
LoadMenu( hInstance, MAKEINTRESOURCE(IDR_MENU) ),
hInstance, 0 );
}
// The focus window can be a specified to be a different window than the
// device window. If not, use the device window as the focus window.
if( m_hWndFocus == NULL )
m_hWndFocus = m_hWnd;
// Save window properties
m_dwWindowStyle = GetWindowLong( m_hWnd, GWL_STYLE );
GetWindowRect( m_hWnd, &m_rcWindowBounds );
GetClientRect( m_hWnd, &m_rcWindowClient );
if( FAILED( hr = ChooseInitialD3DSettings() ) )
{
SAFE_RELEASE( m_pD3D );
return DisplayErrorMsg( hr, MSGERR_APPMUSTEXIT );
}
// Initialize the application timer
DXUtil_Timer( TIMER_START );
// Initialize the app's custom scene stuff
if( FAILED( hr = OneTimeSceneInit() ) )
{
SAFE_RELEASE( m_pD3D );
return DisplayErrorMsg( hr, MSGERR_APPMUSTEXIT );
}
// Initialize the 3D environment for the app
if( FAILED( hr = Initialize3DEnvironment() ) )
{
SAFE_RELEASE( m_pD3D );
return DisplayErrorMsg( hr, MSGERR_APPMUSTEXIT );
}
// The app is ready to go
Pause( false );
return S_OK;
}
代码的注释非常详细,不过DX9初始化感觉有点麻烦。
1.ChooseInitialD3DSettings函数主要完成的功能是设置全屏和窗口模式。
2.DXUtil_Timer用来设置时间,这个函数可以用来完成有关时间的操作。
3.在D3DApp框架中,OneTimeSceneInit函数基本没用,需要子类重写来完成所需要的初始化。这一篇主要还是以D3DApp为主,因此就不看cGameHost的实现了。
4.Initialize3DEnvironment,初始化3D环境,完成的就是创建设备这类DX9初始化必需的步骤。
再次回到入口点函数,下一句就是d3dApp.Run()。这一次我们依然只看D3DApp中完成的事。
INT CD3DApplication::Run()
{
// Load keyboard accelerators
HACCEL hAccel = LoadAccelerators( NULL, MAKEINTRESOURCE(IDR_MAIN_ACCEL) );
// Now we're ready to recieve and process Windows messages.
bool bGotMsg;
MSG msg;
msg.message = WM_NULL;
PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );
while( WM_QUIT != msg.message )
{
// Use PeekMessage() if the app is active, so we can use idle time to
// render the scene. Else, use GetMessage() to avoid eating CPU time.
if( m_bActive )
bGotMsg = ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) != 0 );
else
bGotMsg = ( GetMessage( &msg, NULL, 0U, 0U ) != 0 );
if( bGotMsg )
{
// Translate and dispatch the message
if( hAccel == NULL || m_hWnd == NULL ||
0 == TranslateAccelerator( m_hWnd, hAccel, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
else
{
if( m_bDeviceLost )
{
// Yield some CPU time to other processes
Sleep( 100 ); // 100 milliseconds
}
// Render a frame during idle time (no messages are waiting)
if( m_bActive )
{
if( FAILED( Render3DEnvironment() ) )
SendMessage( m_hWnd, WM_CLOSE, 0, 0 );
}
}
}
if( hAccel != NULL )
DestroyAcceleratorTable( hAccel );
return (INT)msg.wParam;
}
这就是消息处理了,直到程序退出。消息处理其中一个重要地方在于渲染。
HRESULT CD3DApplication::Render3DEnvironment()
{
HRESULT hr;
if( m_bDeviceLost )
{
// Test the cooperative level to see if it's okay to render
if( FAILED( hr = m_pd3dDevice->TestCooperativeLevel() ) )
{
// If the device was lost, do not render until we get it back
if( D3DERR_DEVICELOST == hr )
return S_OK;
// Check if the device needs to be reset.
if( D3DERR_DEVICENOTRESET == hr )
{
// If we are windowed, read the desktop mode and use the same format for
// the back buffer
if( m_bWindowed )
{
D3DAdapterInfo* pAdapterInfo = m_d3dSettings.PAdapterInfo();
m_pD3D->GetAdapterDisplayMode( pAdapterInfo->AdapterOrdinal, &m_d3dSettings.Windowed_DisplayMode );
m_d3dpp.BackBufferFormat = m_d3dSettings.Windowed_DisplayMode.Format;
}
if( FAILED( hr = Reset3DEnvironment() ) )
return hr;
}
return hr;
}
m_bDeviceLost = false;
}
// Get the app's time, in seconds. Skip rendering if no time elapsed
FLOAT fAppTime = DXUtil_Timer( TIMER_GETAPPTIME );
FLOAT fElapsedAppTime = DXUtil_Timer( TIMER_GETELAPSEDTIME );
if( ( 0.0f == fElapsedAppTime ) && m_bFrameMoving )
return S_OK;
// FrameMove (animate) the scene
if( m_bFrameMoving || m_bSingleStep )
{
// Store the time for the app
m_fTime = fAppTime;
m_fElapsedTime = fElapsedAppTime;
// Frame move the scene
if( FAILED( hr = FrameMove() ) )
return hr;
m_bSingleStep = false;
}
// Render the scene as normal
if( FAILED( hr = Render() ) )
return hr;
UpdateStats();
// Show the frame on the primary surface.
hr = m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
if( D3DERR_DEVICELOST == hr )
m_bDeviceLost = true;
return S_OK;
}
第一部分是处理设备丢失的情况,老实说我到底也没弄清楚设备丢失是怎么个情况,貌似切换屏幕会导致设备丢失。不过丢失了就要重新设置有关窗口的设置。调用了下面的重置函数。
HRESULT CD3DApplication::Reset3DEnvironment()
{
HRESULT hr;
// Release all vidmem objects
if( m_bDeviceObjectsRestored )
{
m_bDeviceObjectsRestored = false;
InvalidateDeviceObjects();
}
// Reset the device
if( FAILED( hr = m_pd3dDevice->Reset( &m_d3dpp ) ) )
return hr;
// Store render target surface desc
LPDIRECT3DSURFACE9 pBackBuffer;
m_pd3dDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer );
pBackBuffer->GetDesc( &m_d3dsdBackBuffer );
pBackBuffer->Release();
// Set up the fullscreen cursor
if( m_bShowCursorWhenFullscreen && !m_bWindowed )
{
HCURSOR hCursor;
#ifdef _WIN64
hCursor = (HCURSOR)GetClassLongPtr( m_hWnd, GCLP_HCURSOR );
#else
hCursor = (HCURSOR)ULongToHandle( GetClassLong( m_hWnd, GCL_HCURSOR ) );
#endif
D3DUtil_SetDeviceCursor( m_pd3dDevice, hCursor, true );
m_pd3dDevice->ShowCursor( true );
}
// Confine cursor to fullscreen window
if( m_bClipCursorWhenFullscreen )
{
if (!m_bWindowed )
{
RECT rcWindow;
GetWindowRect( m_hWnd, &rcWindow );
ClipCursor( &rcWindow );
}
else
{
ClipCursor( NULL );
}
}
// Initialize the app's device-dependent objects
hr = RestoreDeviceObjects();
if( FAILED(hr) )
{
InvalidateDeviceObjects();
return hr;
}
m_bDeviceObjectsRestored = true;
// If the app is paused, trigger the rendering of the current frame
if( false == m_bFrameMoving )
{
m_bSingleStep = true;
DXUtil_Timer( TIMER_START );
DXUtil_Timer( TIMER_STOP );
}
return S_OK;
}
这个函数就没有什么好说的了,里面调用的好几个函数都需要子类重写,但基本上功能就是这样了。
Render3DEnvironment剩下的部分就是子类主要工作了,重写渲染场景和更新数据的函数。
那么D3DApp框架的阅读就到此结束。老实说,我觉得D3DApp的条理性比DXUT的要好,为什么要换成以重写回调函数来完成功能的DXUT呢,实在不懂。下一篇,cGameHost类的阅读。