基于MFC的Direct3D程序设计
原文链接:MFC D3D Application: Direct3D Tutorial Part I
作者这个MFC程序中第一个有趣的地方是让用于Direct3D绘制的窗口类从CWnd类和CXD3D类继承下来:
//-----------------------------------------------------------------------------
// CXD3D class: the class a view class will derive from to provide a window
// handle to render into, and that will override the 3D scene rendering.
//-----------------------------------------------------------------------------
class CXD3D


{
protected:
// internal state variables
bool m_bActive; // toggled on Pause, can be queried upon
// initializing to issue a Create [false]
bool m_bStartFullscreen; // queried on ChooseInitialSettings [false]
bool m_bShowCursor; // in fullscreen mode [true]
bool m_bClipCursor; // in fullscreen mode [true]
bool m_bWindowed; // queried on BuildPresentParamsFromSettings
// [true]
bool m_bIgnoreSizeChange; // queried on HandlePossibleSizeChange [false]
bool m_bDeviceLost; // true when the device's Present fails
bool m_bDeviceObjectsInited; // true if InitDeviceObjects succeeds
bool m_bDeviceObjectsRestored; // true if RestoreDeviceObjects succeeds
// internal timing variables
FLOAT m_fTime; // absolute time handled by DXUtil_Timer
FLOAT m_fElapsedTime; // elapsed time handled by DXUtil_Timer
FLOAT m_fFPS; // the frame rate, or frames per second
// statistics
TCHAR m_strDeviceStats[256]; // device description
TCHAR m_strFrameStats[16]; // frame statistics
// main objects used for creating and rendering the 3D scene
HWND m_hWndRender; // device window
HWND m_hWndFocus; // focus window
LPDIRECT3D9 m_pd3d; // main D3D object
LPDIRECT3DDEVICE9 m_pd3dDevice; // D3D rendering device
D3DPRESENT_PARAMETERS m_d3dpp; // presentation parameters
DWORD m_dwCreateFlags; // sw/hw VP + pure device
DWORD m_dwWindowStyle; // saved for mode switches
RECT m_rcWindow; // window and client rects,
RECT m_rcClient; // saved for mode switches
// setup objects
CXD3DEnum Enumeration; // hierarchy of adapters, modes, devices, etc.
CXD3DSettings Settings; // current display settings
protected:
// internal error handling function
HRESULT DisplayErrorMsg(HRESULT hr, DWORD dwType);
// internal management functions
void BuildPresentParamsFromSettings();
bool FindBestWindowedMode(bool bHAL, bool bREF);
bool FindBestFullscreenMode(bool bHAL, bool bREF);
HRESULT ChooseInitialSettings();
HRESULT InitializeEnvironment();
HRESULT ResetEnvironment();
void CleanupEnvironment();
public:
HRESULT RenderEnvironment();
HRESULT HandlePossibleSizeChange();
protected:
void UpdateStats();
// Overridable functions for the 3D scene created by the app

virtual HRESULT OneTimeSceneInit()
{ return S_OK; }

virtual HRESULT InitDeviceObjects()
{ return S_OK; }

virtual HRESULT RestoreDeviceObjects()
{ return S_OK; }

virtual HRESULT FrameMove()
{ return S_OK; }


virtual HRESULT InvalidateDeviceObjects()
{ return S_OK; }

virtual HRESULT DeleteDeviceObjects()
{ return S_OK; }

virtual HRESULT FinalCleanup()
{ return S_OK; }
public:

virtual HRESULT Render()
{ return S_OK; }
// construct/destruct, create and pause/play
CXD3D();

virtual ~CXD3D()
{ }
virtual HRESULT CreateD3D();
virtual void Pause(bool bPause);
// active state wrapper

bool IsActive()
{ return m_bActive; };
// device and frame statistics wrappers

LPCTSTR GetDeviceStats()
{ return m_strDeviceStats; }

LPCTSTR GetFrameStats()
{ return m_strFrameStats; }
};

CXD3D实现文件

//-----------------------------------------------------------------------------
// XD3D.cpp: custom Direct3D (CXD3D) Implementation file
//-----------------------------------------------------------------------------

#include "stdafx.h"
#include "XD3D.h"

//-----------------------------------------------------------------------------
// CXD3D Constructor
//-----------------------------------------------------------------------------
CXD3D::CXD3D()


{
m_bActive = false;
m_bStartFullscreen = false;
m_bShowCursor = true;
m_bClipCursor = true;
m_bWindowed = true;
m_bIgnoreSizeChange = false;
m_bDeviceLost = false;
m_bDeviceObjectsInited = false;
m_bDeviceObjectsRestored = false;
m_fTime = 0.0f;
m_fElapsedTime = 0.0f;
m_fFPS = 0.0f;
m_strDeviceStats[0] = _T('\0');
m_strFrameStats[0] = _T('\0');

m_hWndRender = NULL;
m_hWndFocus = NULL;
m_pd3d = NULL;
m_pd3dDevice = NULL;
m_dwCreateFlags = 0L;
Pause(true); // until we're ready to render
}

//-----------------------------------------------------------------------------
// CreateD3D(): provided m_hWnd has been initialised, it instantiates the d3d
// object, chooses initial d3d settings and initializes the d3d stuff.
//-----------------------------------------------------------------------------
HRESULT CXD3D::CreateD3D()


{
HRESULT hr;

// check for a window to render to
if (m_hWndRender == NULL)//待绘制的窗口为空
return DisplayErrorMsg(D3DAPPERR_NOWINDOW, MSGERR_CANNOTCONTINUE);
// instantiate a D3D Object
if ((m_pd3d = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)
return DisplayErrorMsg(D3DAPPERR_NODIRECT3D, MSGERR_CANNOTCONTINUE);

// build a list of D3D adapters, modes and devices
if (FAILED(hr = Enumeration.Enumerate(m_pd3d)))

{
SAFE_RELEASE(m_pd3d);
return DisplayErrorMsg(hr, MSGERR_CANNOTCONTINUE);
}

// use the device window as the focus window, unless otherwise specified
if (m_hWndFocus == NULL) m_hWndFocus = m_hWndRender;

// save some window properties into class members
m_dwWindowStyle = GetWindowLong(m_hWndRender, GWL_STYLE);
GetWindowRect(m_hWndRender, &m_rcWindow);
GetClientRect(m_hWndRender, &m_rcClient);

// choose the best settings to render
if (FAILED(hr = ChooseInitialSettings()))

{
SAFE_RELEASE(m_pd3d);
return DisplayErrorMsg(hr, MSGERR_CANNOTCONTINUE);
}

// initialize the timer
DXUtil_Timer(TIMER_START);

// initialize the app's custom (pre-device creation) stuff
if (FAILED(hr = OneTimeSceneInit()))

{
SAFE_RELEASE(m_pd3d);
return DisplayErrorMsg(hr, MSGERR_CANNOTCONTINUE);
}

// initialize the 3D environment, creating the device
if (FAILED(hr = InitializeEnvironment()))

{
SAFE_RELEASE(m_pd3d);
return DisplayErrorMsg(hr, MSGERR_CANNOTCONTINUE);
}

// D3D is ready to go so unpause it
Pause(false);

return S_OK;
}

//-----------------------------------------------------------------------------
// FindBestFullscreenMode(): Adjust settings with the best available fullscreen
// mode, subject to the HAL and REF constraints; returns false if no such mode
// can be found.
//-----------------------------------------------------------------------------
bool CXD3D::FindBestFullscreenMode(bool bHAL, bool bREF)


{
// for fullscreen, default to the first HAL device combo that supports the
// current desktop display mode, to any display mode if HAL is incompatible
// with the desktop mode, or to a non-HAL if no HAL is available
D3DDISPLAYMODE dmDesktop;
D3DDISPLAYMODE dmDesktopBest;
D3DDISPLAYMODE dmBest;
// fortunately for us, D3DFMT_UNKNOWN == 0
ZeroMemory(&dmDesktopBest, sizeof(D3DDISPLAYMODE));
ZeroMemory(&dmBest, sizeof(D3DDISPLAYMODE));
// 'best' storage
AdapterInfo* paiBest = NULL;
DeviceInfo* pdiBest = NULL;
DeviceCombo* pdcBest = NULL;

// iterators
AdapterInfo* pai;
DeviceInfo* pdi;
DeviceCombo* pdc;
// success flags
bool bBetter, bBest;

UINT i, j, k;

// traverse the adpater infos
for (i = 0; i < Enumeration.AdapterInfos.Length(); i++)

{
pai = &Enumeration.AdapterInfos[i];
// get the current display mode of each adapter
m_pd3d->GetAdapterDisplayMode(pai->AdapterOrdinal, &dmDesktop);
// traverse device infos on each adapter info
for (j = 0; j < pai->DeviceInfos.Length(); j++)

{
pdi = &pai->DeviceInfos[j];
// skip devices with other than the requested type
if (bHAL && pdi->DevType != D3DDEVTYPE_HAL)
continue;
if (bREF && pdi->DevType != D3DDEVTYPE_REF)
continue;
// traverse device combos for each device info
for (k = 0; k < pdi->DeviceCombos.Length(); k++)

{
pdc = &pdi->DeviceCombos[k];
// skip the windowed combos
if (pdc->Windowed)
continue;
// this device combo is 'better' than the current best if:
// (a) there's no best yet;
// (b) it's a HAL and the current best is not;
// (c) it's a HAL matching the desktop's format, while the
// current best does not;
// (d) it's a HAL and both the display and backbuffer formats
// match the desktop's, in which case it is also the best

bBetter = pdcBest == NULL ||
pdc->DevType == D3DDEVTYPE_HAL &&
pdcBest->DevType != D3DDEVTYPE_HAL ||
pdc->DevType == D3DDEVTYPE_HAL &&
pdc->DisplayFormat == dmDesktop.Format &&
pdcBest->DisplayFormat != dmDesktop.Format;
bBest = pdc->DevType == D3DDEVTYPE_HAL &&
pdc->DisplayFormat == dmDesktop.Format &&
pdc->BackBufferFormat == dmDesktop.Format;

bBetter |= bBest;

if (bBetter)

{
// make it the best so far
dmDesktopBest = dmDesktop;
paiBest = pai;
pdiBest = pdi;
pdcBest = pdc;
// this one looks great -- take it
if (bBest)
goto DoneSearchingFDC;
}
}
}
}

DoneSearchingFDC:

// no suitable dc found!
if (pdcBest == NULL)
return false;
// now we need to find a display mode on the best ai that uses the best
// dc's display format and is as close to the best desktop display mode
// as possible
D3DDISPLAYMODE dm;
for (i = 0; i < paiBest->DisplayModes.Length(); i++)

{
dm = paiBest->DisplayModes[i];
// formats must match
if (dm.Format != dmDesktopBest.Format)
continue;
// compare other properties
if (dm.Width == dmDesktopBest.Width &&
dm.Height == dmDesktopBest.Height &&
dm.RefreshRate == dmDesktopBest.RefreshRate)

{
// perfect match, break out
dmBest = dm;
break;
}
else if (dm.Width == dmDesktopBest.Width &&
dm.Height == dmDesktopBest.Height &&
dm.RefreshRate > dmBest.RefreshRate)

{
// faster
dmBest = dm;
}
else if (dm.Width == dmDesktopBest.Width)

{
// same width
dmBest = dm;
}
else if (dmBest.Width == 0)

{
// we don't have anything better yet
dmBest = dm;
}
}
// save these settings
Settings.Windowed = 0;
Settings.AdapterInfos[0] = paiBest;

// index to the best dm within the ai
Settings.ndm[0] = paiBest->DisplayModes.Find(dm);

// indices to the best di and dc
if (bBest)

{
Settings.ndi[0] = j;
Settings.ndc[0] = k;
}
else

{
// retract to the 'better' di and dc
Settings.ndi[0] = (UINT)paiBest->DeviceInfos.Find(*pdiBest);
Settings.ndc[0] = (UINT)pdiBest->DeviceCombos.Find(*pdcBest);
}
return true;
}

//-----------------------------------------------------------------------------
// FindBestWindowedMode(): adjusts settings with best available windowed mode,
// subject to the HAL and REF constraints; returns false if no such mode can be
// found.
//-----------------------------------------------------------------------------
bool CXD3D::FindBestWindowedMode(bool bHAL, bool bREF)


{
// get the display mode of the primary adapter, which is assumed to be
// where the window will appear
D3DDISPLAYMODE dm;
m_pd3d->GetAdapterDisplayMode(0, &dm);
// 'best' storage
AdapterInfo* paiBest = NULL;
DeviceInfo* pdiBest = NULL;
DeviceCombo* pdcBest = NULL;

// iterators
AdapterInfo* pai;
DeviceInfo* pdi;
DeviceCombo* pdc;

// success flags
bool bBetter, bBest;

UINT i, j, k;
// traverse the enumerated adapters information
for (i = 0; i < Enumeration.AdapterInfos.Length(); i++)

{
pai = &Enumeration.AdapterInfos[i];
// for each adapter, traverse the device infos
for (j = 0; j < pai->DeviceInfos.Length(); j++)

{
pdi = &pai->DeviceInfos[j];
// skip according to the requirements
if (bHAL && pdi->DevType != D3DDEVTYPE_HAL)
continue;
if (bREF && pdi->DevType != D3DDEVTYPE_REF)
continue;
// traverse device combos for this device
for (k = 0; k < pdi->DeviceCombos.Length(); k++)

{
pdc = &pdi->DeviceCombos[k];
// skip the non-windowed or distinct format combos
if (!pdc->Windowed)
continue;
if (pdc->DisplayFormat != dm.Format)
continue;
// this device combo is better than the current best if:
// (a) there's no best yet;
// (b) it's a HAL and the current best is not;
// (c) it's a HAL with matching backbuffer and display
// formats, in which case is also the best
bBetter = pdcBest == NULL ||

pdc->DevType == D3DDEVTYPE_HAL &&
pdcBest->DevType != D3DDEVTYPE_HAL;

bBest = pdc->DevType == D3DDEVTYPE_HAL &&
pdc->BackBufferFormat == pdc->DisplayFormat;

bBetter |= bBest;

if (bBetter)

{
// save it as the current best
paiBest = pai;
pdiBest = pdi;
pdcBest = pdc;
// this dc looks great -- take it
if (bBest)
goto DoneSearchingWDC;
}
}
}
}

DoneSearchingWDC:

// none found!!
if (pdcBest == NULL)
return false;

Settings.Windowed = 1;
Settings.AdapterInfos[1] = paiBest;

int l = paiBest->DisplayModes.Find(dm);

// for some bizarre multi-monitor setups in which the primary adapter's
// current dm is not available in a secondary one, there's nothing else
// we can do
if (i > 0 && l == -1)
return false;

// index to the best dm within the ai
Settings.ndm[1] = l;

// indices to the best di and dc
if (bBest)

{
Settings.ndi[1] = j;
Settings.ndc[1] = k;
}
else

{
// retract to the 'better' di and dc
Settings.ndi[1] = (UINT)paiBest->DeviceInfos.Find(*pdiBest);
Settings.ndc[1] = (UINT)pdiBest->DeviceCombos.Find(*pdcBest);
}

return true;
}

//-----------------------------------------------------------------------------
// ChooseInitialSettings(): according to the best mode founds and app settings
//-----------------------------------------------------------------------------
HRESULT CXD3D::ChooseInitialSettings()


{
bool bFoundFullscreen = FindBestFullscreenMode(false, false);
bool bFoundWindowed = FindBestWindowedMode(false, false);
if (m_bStartFullscreen && bFoundFullscreen)
Settings.Windowed = 0;
if (!bFoundWindowed && bFoundFullscreen)
Settings.Windowed = 0;
if (!bFoundFullscreen && !bFoundWindowed)
return D3DAPPERR_NOCOMPATIBLEDEVICES;

if (!m_bStartFullscreen && !bFoundWindowed)
return D3DAPPERR_NOCOMPATIBLEDEVICES;
return S_OK;
}

//-----------------------------------------------------------------------------
// HandlePossibleSizeChange(): reset the device if the client area size has
// changed; it will update the window properties, but it will not issue a reset
// unless old and new dimensions differ; a new window size will require a new
// backbuffer size, so the 3D environment must change accordingly.
//-----------------------------------------------------------------------------
HRESULT CXD3D::HandlePossibleSizeChange()


{
if (m_bIgnoreSizeChange)
return S_OK;

HRESULT hr = S_OK;
RECT rcOld = m_rcClient;

GetClientRect(m_hWndRender, &m_rcClient);

// check for client rect changes
if (rcOld.right - rcOld.left != m_rcClient.right - m_rcClient.left ||
rcOld.bottom - rcOld.top != m_rcClient.bottom - m_rcClient.top)

{
Pause(true);

// store the new dims
m_d3dpp.BackBufferWidth = m_rcClient.right - m_rcClient.left;
m_d3dpp.BackBufferHeight = m_rcClient.bottom - m_rcClient.top;
// reset
if (m_pd3dDevice != NULL)

{
if (FAILED(hr = ResetEnvironment()))

{
if (hr != D3DERR_OUTOFVIDEOMEMORY)
hr = D3DAPPERR_RESETFAILED;
DisplayErrorMsg(hr, MSGERR_CANNOTCONTINUE);
}
}
Pause(false);
}
return hr;
}

//-----------------------------------------------------------------------------
// BuildPresentParamsFromSettings(): 'builds' presentation parameters from
// the current settings
//-----------------------------------------------------------------------------
void CXD3D::BuildPresentParamsFromSettings()


{
m_d3dpp.Windowed = Settings.Windowed;
m_d3dpp.hDeviceWindow = m_hWndRender;
m_d3dpp.BackBufferCount = 1;
m_d3dpp.EnableAutoDepthStencil = Enumeration.AppUsesDepthBuffer;
m_d3dpp.MultiSampleType = Settings.GetMSType();
m_d3dpp.MultiSampleQuality = Settings.GetMSQuality();
m_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
m_d3dpp.Flags = 0;
if (Enumeration.AppUsesDepthBuffer)

{
m_d3dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
m_d3dpp.AutoDepthStencilFormat = Settings.GetDSFormat();
}
if (m_bWindowed)

{
m_d3dpp.BackBufferWidth = m_rcClient.right - m_rcClient.left;
m_d3dpp.BackBufferHeight = m_rcClient.bottom - m_rcClient.top;
m_d3dpp.FullScreen_RefreshRateInHz = 0;
}
else

{
m_d3dpp.BackBufferWidth = Settings.GetDisplayMode().Width;
m_d3dpp.BackBufferHeight = Settings.GetDisplayMode().Height;
m_d3dpp.FullScreen_RefreshRateInHz = Settings.GetDisplayMode().RefreshRate;
}

m_d3dpp.BackBufferFormat = Settings.GetBackBufferFormat();
m_d3dpp.PresentationInterval = Settings.GetPresentInterval();
}

//-----------------------------------------------------------------------------
// InitializeEnvironment(): checks for a null REF device, builds presentation
// parameters from settings, instances the d3d device, sets up device stats,
// saves the backbuffer description, sets up the fullscreen cursor and finally
// inits and restores device objects. If the last step fails, it retries, but
// in windowed mode (to display a warning) and the reference rasterizer.
//-----------------------------------------------------------------------------
HRESULT CXD3D::InitializeEnvironment()


{
HRESULT hr;
AdapterInfo* pai = Settings.GetAdapterInfo();
DeviceInfo* pdi = Settings.GetDeviceInfo();
// Warn user about a null REF device that cannot render anything
if (pdi->Caps.PrimitiveMiscCaps & D3DPMISCCAPS_NULLREFERENCE)
DisplayErrorMsg(D3DAPPERR_NULLREFDEVICE, 0);
// translate the VP type to a device creation behavior
switch (Settings.GetVPType())

{
case PURE_VP: m_dwCreateFlags = D3DCREATE_PUREDEVICE;
case HARD_VP: m_dwCreateFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; break;
case MIXD_VP: m_dwCreateFlags = D3DCREATE_MIXED_VERTEXPROCESSING; break;
case SOFT_VP: m_dwCreateFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING; break;
default: return E_FAIL;
}
// setup the creation presentation parameters
BuildPresentParamsFromSettings();
// create the device
hr = m_pd3d->CreateDevice(pdi->AdapterOrdinal,
pdi->DevType,
m_hWndRender,
m_dwCreateFlags,
&m_d3dpp,
&m_pd3dDevice);
if (SUCCEEDED(hr))

{
// store the device's description, beginning with type;
lstrcpy(m_strDeviceStats, DEVICETYPESTRING(pdi->DevType, false));
// then VP type, including non-HAL devices simulating hardware VP and
// the pure hardware VP variant
if ((m_dwCreateFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) == 0 &&
pdi->DevType != D3DDEVTYPE_HAL)
lstrcat(m_strDeviceStats, TEXT(" simulated"));

if (m_dwCreateFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING)

{
if (m_dwCreateFlags & D3DCREATE_PUREDEVICE)
lstrcat(m_strDeviceStats, TEXT(" pure"));
lstrcat(m_strDeviceStats, TEXT(" hardware"));
}
else if (m_dwCreateFlags & D3DCREATE_MIXED_VERTEXPROCESSING)
lstrcat(m_strDeviceStats, TEXT(" mixed"));
else
lstrcat(m_strDeviceStats, TEXT(" software"));
lstrcat(m_strDeviceStats, TEXT(" VP"));
//
and the adapter's description for HAL devices
if (pdi->DevType == D3DDEVTYPE_HAL)

{
lstrcat(m_strDeviceStats, TEXT(" on "));
// be sure not to overflow m_strDeviceStats when appending
const int nDescription = sizeof(pai->AdapterIdentifier.Description);
TCHAR szDescription[nDescription];
// DX Utils handle unicode somewhat gracefully
DXUtil_ConvertAnsiStringToGenericCch(szDescription,
pai->AdapterIdentifier.Description,
nDescription);
lstrcat(szDescription, TEXT(" @ "));
// append as many characters as space is left on the stats
_tcsncat(m_strDeviceStats,
szDescription,
UBOUND(m_strDeviceStats) - lstrlen(m_strDeviceStats) - 1);

TCHAR szDims[100];
TCHAR szFmt[100];
TCHAR szDepthFmt[100];
TCHAR* szMS;
_sntprintf(szDims, 100, TEXT("%dx%d, "),
m_d3dpp.BackBufferWidth,
m_d3dpp.BackBufferHeight);

szDims[99] = TEXT('\0');
// append as many characters as space is left on the stats
_tcsncat(m_strDeviceStats, szDims,
UBOUND(m_strDeviceStats) - lstrlen(m_strDeviceStats) - 1);

D3DFORMAT fmt = Settings.GetDisplayMode().Format;
// display format (including the back buffer format if they do not match)
if (fmt == m_d3dpp.BackBufferFormat)
lstrcpyn(szFmt, D3DUtil_D3DFormatToString(fmt, false), 100);
else
_sntprintf(szFmt, 100, TEXT("%s back, %s front"),
D3DUtil_D3DFormatToString(m_d3dpp.BackBufferFormat, false),
D3DUtil_D3DFormatToString(fmt, false));
szFmt[99] = TEXT('\0');
// append as many characters as space is left on the stats
_tcsncat(m_strDeviceStats,
szFmt,
UBOUND(m_strDeviceStats) - lstrlen(m_strDeviceStats) - 1);

// depth/stencil buffer format
if (Enumeration.AppUsesDepthBuffer)

{
_sntprintf(szDepthFmt, 100, TEXT(" (%s)"),
D3DUtil_D3DFormatToString(Settings.GetDSFormat(), false));
szDepthFmt[99] = TEXT('\0');
// append as many characters as space is left on the stats
_tcsncat(m_strDeviceStats,
szDepthFmt,
UBOUND(m_strDeviceStats) - lstrlen(m_strDeviceStats) - 1);
}
// multisampling type (no. of samples or nonmaskable)
szMS = MULTISAMPLESTRING(Settings.GetMSType(), false);

// append as many characters as space is left on the stats
_tcsncat(m_strDeviceStats,
szMS,
UBOUND(m_strDeviceStats) - lstrlen(m_strDeviceStats) - 1);
}
// setup the fullscreen cursor
if (m_bShowCursor && !m_bWindowed)

{
HCURSOR hCursor = (HCURSOR)GetClassLong(m_hWndRender, GCL_HCURSOR);
D3DUtil_SetDeviceCursor(m_pd3dDevice, hCursor, true);
m_pd3dDevice->ShowCursor(true);
}
// confine the cursor to the fullscreen window
if (m_bClipCursor)
ClipCursor(m_bWindowed ? NULL : &m_rcWindow);
// initialize the app's device-dependant objects
if (FAILED(hr = InitDeviceObjects()))
DeleteDeviceObjects();
else

{
m_bDeviceObjectsInited = true;
// restore the app's device-dependant objects
if (FAILED(hr = RestoreDeviceObjects()))
InvalidateDeviceObjects();
else

{
m_bDeviceObjectsRestored = true;
return S_OK;
}
}
// if any of that failed, cleanup before we try again
CleanupEnvironment();
}
// if failure comes strictly from IDirect3D9 we'll try falling back to
// the reference rasterizer; in other words, we'll ignore the error if
// it is a 'file not found' error, because it is not Direct3D's fault,
// it's the programmer's (or user's) fault!
if (hr != D3DAPPERR_MEDIANOTFOUND &&
hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) &&
pdi->DevType == D3DDEVTYPE_HAL)

{
// request a reference device
if (FindBestWindowedMode(false, true))

{
// main window must not be topmost to show an error message
SetWindowPos(m_hWndRender,
HWND_NOTOPMOST,
m_rcWindow.left,
m_rcWindow.top,
(m_rcWindow.right - m_rcWindow.left),
(m_rcWindow.bottom - m_rcWindow.top),
SWP_SHOWWINDOW);
//
in which we let the user know we are switching
DisplayErrorMsg(hr, MSGWARN_SWITCHEDTOREF);
// try again
hr = InitializeEnvironment();
}
}
return hr;
}

//-----------------------------------------------------------------------------
// ResetEnvironment(): signals the app it must invalidate all video memory
// objects and render the current frame if paused, then resets the device. The
// backbuffer description is saved.
//-----------------------------------------------------------------------------
HRESULT CXD3D::ResetEnvironment()


{
HRESULT hr;
// invalidate any resources thatt cannot survive the reset
if (m_bDeviceObjectsRestored)

{
m_bDeviceObjectsRestored = false;
InvalidateDeviceObjects();
}
if (FAILED(hr = m_pd3dDevice->Reset(&m_d3dpp)))
return hr;

// restore (re-create) resources
if (FAILED(hr = RestoreDeviceObjects()))

{
InvalidateDeviceObjects();
return hr;
}
m_bDeviceObjectsRestored = true;
return S_OK;
}

//-----------------------------------------------------------------------------
// RenderEnvironment(): tests for coop level, possibly resets the environment
// and issues the app defined FrameMove and Render that will actually draw the
// scene.
//-----------------------------------------------------------------------------
HRESULT CXD3D::RenderEnvironment()


{
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 truly lost, (i.e., a fullscreen device just
// lost focus), wait until we get it back
if (hr == D3DERR_DEVICELOST)
return S_OK;
// eventually, we will get this return value, indicating
// that we can reset the device
if (hr == D3DERR_DEVICENOTRESET)

{
// if we are windowed, read the desktop mode and use the same
// format for the back buffer UNTESTED WITH THE NEW SETTINGS CLASS!!!
if (m_bWindowed)

{
m_pd3d->GetAdapterDisplayMode(Settings.GetAdapterInfo()->AdapterOrdinal,
&Settings.GetDisplayMode());
m_d3dpp.BackBufferFormat = Settings.GetDisplayMode().Format;
}
//
before resetting the device
if (FAILED(hr = ResetEnvironment()))
return hr;
}
return hr;
}
// we have a device
m_bDeviceLost = false;
}
// setup the app's timers
FLOAT fTime = DXUtil_Timer(TIMER_GETAPPTIME);
FLOAT fElapsedTime = DXUtil_Timer(TIMER_GETELAPSEDTIME);
// skip rendering if no time elapsed (the application is paused)
if (fElapsedTime == 0.0f)
return S_OK;
// store the time
m_fTime = fTime;
m_fElapsedTime = fElapsedTime;
// move the scene
if (FAILED(hr = FrameMove()))
return hr;
// render the scene as normal
if( FAILED(hr = Render()))
return hr;
// update the FPS
UpdateStats();
// present the next buffer in the swap chain
if(m_pd3dDevice->Present(NULL, NULL, NULL, NULL) == D3DERR_DEVICELOST)
m_bDeviceLost = true;
return S_OK;
}

//-----------------------------------------------------------------------------
// CleanupEnvironment(): cleanup device objects
//-----------------------------------------------------------------------------
void CXD3D::CleanupEnvironment()


{
if (m_pd3dDevice != NULL)

{
if (m_bDeviceObjectsRestored)

{
m_bDeviceObjectsRestored = false;
InvalidateDeviceObjects();
}
if (m_bDeviceObjectsInited)

{
m_bDeviceObjectsInited = false;
DeleteDeviceObjects();
}
if (m_pd3dDevice->Release() > 0)
DisplayErrorMsg(D3DAPPERR_NONZEROREFCOUNT, MSGERR_CANNOTCONTINUE);
m_pd3dDevice = NULL;
}
}

//-----------------------------------------------------------------------------
// UpdateStats()
//-----------------------------------------------------------------------------
void CXD3D::UpdateStats()


{
// keep track of the frame count
static float fLastTime = 0.0f;
static DWORD dwFrames = 0;
float fTime = DXUtil_Timer(TIMER_GETABSOLUTETIME);
++dwFrames;
// let a full second elapse before updating the scene stats
if (fTime - fLastTime <= 1.0f)
return;
m_fFPS = dwFrames / (fTime - fLastTime);
fLastTime = fTime;
dwFrames = 0;
const int nMax = UBOUND(m_strFrameStats);
_sntprintf(m_strFrameStats, nMax, _T("%.02f fps"), m_fFPS);
m_strFrameStats[nMax - 1] = TEXT('\0');
}

//-----------------------------------------------------------------------------
// Pause(): toggles the active state of the app and resets the timer
//-----------------------------------------------------------------------------
void CXD3D::Pause(bool bPause)


{
static DWORD dwAppPausedCount = 0;
dwAppPausedCount += BSCALE(bPause);
m_bActive = dwAppPausedCount == 0;
// handle the first pause request (of many, nestable pause requests) and
// stop the scene from animating
if (bPause && dwAppPausedCount == 1)
DXUtil_Timer(TIMER_STOP);
// restart the timer
if (dwAppPausedCount == 0)
DXUtil_Timer(TIMER_START);
}

//-----------------------------------------------------------------------------
// Name: DisplayErrorMsg()
//-----------------------------------------------------------------------------
HRESULT CXD3D::DisplayErrorMsg(HRESULT hr, DWORD dwType)


{
static bool s_bFatalErrorReported = false;
TCHAR strMsg[512];
// If a fatal error message has already been reported, the app
// is already shutting down, so don't show more error messages.
if (s_bFatalErrorReported)
return hr;
switch(hr)

{
case D3DAPPERR_NODIRECT3D:
_tcscpy(strMsg,
_T("Could not initialize Direct3D; check that the\n")
_T("latest version of DirectX is correctly installed\n")
_T("on your system."));
break;
case D3DAPPERR_NOCOMPATIBLEDEVICES:
_tcscpy(strMsg,
_T("Could not find any compatible Direct3D devices."));
break;
case D3DAPPERR_NOWINDOWABLEDEVICES:
_tcscpy(strMsg,
_T("Cannot run in a desktop window with the current\n")
_T("display settings; change your desktop settings\n")
_T("to a 16- or 32-bit display mode and retry."));
break;
case D3DAPPERR_NOHARDWAREDEVICE:
_tcscpy(strMsg,
_T("No hardware-accelerated Direct3D devices found."));
break;
case D3DAPPERR_HALNOTCOMPATIBLE:
_tcscpy(strMsg,
_T("This application requires functionality not\n")
_T("available on your Direct3D hardware accelerator."));
break;
case D3DAPPERR_NOWINDOW:
_tcscpy(strMsg,
_T("No window to render to was supplied.\n"));
break;
case D3DAPPERR_NOWINDOWEDHAL:
_tcscpy(strMsg,
_T("Your Direct3D hardware accelerator cannot render\n")
_T("into a window.\n"));
break;
case D3DAPPERR_NODESKTOPHAL:
_tcscpy(strMsg,
_T("Your Direct3D hardware accelerator cannot render\n")
_T("into a window with the current desktop display\n")
_T("settings."));
break;
case D3DAPPERR_NOHALTHISMODE:
_tcscpy(strMsg,
_T("This application requires functionality that is\n")
_T("not available on your Direct3D hardware accelerator\n")
_T("with the current desktop display settings.\n"));
break;
case D3DAPPERR_MEDIANOTFOUND:
case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
_tcscpy(strMsg, _T("Could not load required media."));
break;
case D3DAPPERR_RESETFAILED:
_tcscpy(strMsg, _T("Could not reset the Direct3D device."));
break;
case D3DAPPERR_INITFAILED:
_tcscpy(strMsg, _T("Could not initialize the Direct3D device."));
break;
case D3DAPPERR_NONZEROREFCOUNT:
_tcscpy(strMsg,
_T("A D3D object has a non-zero reference count\n")
_T("(meaning things were not properly cleaned up)."));
break;
case D3DAPPERR_NULLREFDEVICE:
_tcscpy(strMsg,
_T("Warning: Nothing will be rendered.\n")
_T("The reference rendering device was selected, but\n")
_T("your computer only has a reduced-functionality\n")
_T("reference device installed. Install the DirectX\n")
_T("SDK to get the full reference device.\n"));
break;
case E_OUTOFMEMORY:
_tcscpy(strMsg, _T("Not enough memory."));
break;
case D3DERR_OUTOFVIDEOMEMORY:
_tcscpy(strMsg, _T("Not enough video memory."));
break;
default:
_tcscpy(strMsg, _T("Unknown Direct3D error."));
}
if (dwType == MSGWARN_SWITCHEDTOREF)

{
_tcscat(strMsg,
_T("\n\nSwitching to the reference rasterizer, a\n")
_T("software device that implements the entire\n")
_T("Direct3D feature set, but runs very slowly."));
}
if (dwType == MSGERR_CANNOTCONTINUE)

{
s_bFatalErrorReported = true;
_tcscat(strMsg, _T("\n\nDirect3D cannot continue."));
}
MessageBox(NULL, strMsg, "Direct3D", MB_ICONWARNING | MB_OK);
// close the window
if (s_bFatalErrorReported && m_hWndRender) SendMessage(m_hWndRender, WM_CLOSE, 0, 0);
return hr;
}
Enumeration
|
+-- AdapterInfos[0]
| |
| +-- DisplayModes[0]
| +-- DisplayModes[1]
| 
| |
| +-- DeviceInfos[0]
| | |
| | +-- DeviceCombos[0]
| | | |
| | | +-- VPTypes
| | | +-- DSFormats
| | | +-- MSTypes
| | | +-- MSQualityLevels
| | | +-- DSMSConflicts
| | | +-- PresentIntervals
| | +-- DeviceCombos[1]
| | 
| +-- DeviceInfos[1]
| 
+-- AdapterInfos[1]
作者这个MFC程序中第一个有趣的地方是让用于Direct3D绘制的窗口类从CWnd类和CXD3D类继承下来:
class CD3DWnd : public CXD3D, public CWnd
CD3DWnd类是用于窗口中控件(比如一个PictureBox)的基类,这个控件将提供普通Cwnd类的功能,但同时也拥有CXD3D的3D绘制的能力。
先来看CXD3D类:





































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































在CXD3D::CreateD3D函数中,首先初始化了一个D3D对象,然后构建了一个列表,这个列表包含了机器上的所有显卡,显卡模式和设备。我们需要知道机器上有多少显卡(一般只有一个),而且每个显卡也可以支持多个设备。对于每个设备会有一种支持的格式,设置和能力,这对于应用程序来说不一定是合适的,因此我们需要一个列表来跟踪这些信息,以便挑选出合适的。
再来看枚举类CXD3DEnum,它用来为应用程序中使用的分辨率,颜色,A通道,显示格式,后备缓冲格式,深度/模板缓冲格式,多重采样类型,提交显示时间间隔等参数建立约束。
文中枚举类的代码很多,但我们只需要记住一点就可以了,要使Direct3D建立起来,我们首先得不断地枚举,枚举,再枚举,不断检查它的各种属性,各种能力是否满足,基本上检查的顺序可以用下图来表示:



























作者:洞庭散人
出处:http://phinecos.cnblogs.com/
本博客遵从Creative Commons Attribution 3.0 License,若用于非商业目的,您可以自由转载,但请保留原作者信息和文章链接URL。
分类:
C/C++/VC++
posted on 2008-07-08 21:47 Phinecos(洞庭散人) 阅读(3655) 评论(4) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述