📄 d3dapplication.cpp
字号:
// --------------------------------------------------------------------------
// Dingus project - a collection of subsystems for game/graphics applications
// --------------------------------------------------------------------------
#include "stdafx.h"
#include <windowsx.h>
#include "D3DApplication.h"
#include "DXUtil.h"
#include "D3DUtil.h"
#include "resource.h"
#include <regstr.h>
using namespace dingus;
//---------------------------------------------------------------------------
// global access to the app (needed for the global gWndProc)
static CD3DApplication* gD3DApp = NULL;
//---------------------------------------------------------------------------
// Static msg handler which passes messages to the application class.
LRESULT CALLBACK gWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
return gD3DApp->msgProc( hWnd, uMsg, wParam, lParam );
}
//---------------------------------------------------------------------------
// CD3DApplication
//---------------------------------------------------------------------------
CD3DApplication::CD3DApplication()
: mD3D(NULL), mD3DDevice(NULL),
mHWnd(NULL), mHWndFocus(NULL), mHMenu(NULL),
mWindowed(true), mActive(false), mDeviceLost(false),
mMinimized(false), mMaximized(false), mIgnoreSizeChange(false),
mDeviceObjectsInited(false), mDeviceObjectsRestored(false),
mCreateFlags(0),
mFrameMoving(true), mSingleStep(false),
mTime(0.0), mElapsedTime(0.0), mFPS(0.0f),
mWindowTitle( _T("D3D9 Application")),
mCreationWidth(400),
mCreationHeight(300),
mShowCursorWhenFullscreen(false), mStartFullscreen(false), mVSyncFullscreen(true),
mSelectDeviceAtStartup(false),
mDebugTimer(false),
mSSInStartingPause(false),
mSSCheckingPassword(false),
mSSIsWin9x(false),
mSSMouseMoveCount(0),
mSSHwndParent(0), mSSPasswordDLL(0), mSSVerifyPasswordProc(0)
{
gD3DApp = this;
mDeviceStats[0] = _T('\0');
mFrameStats[0] = _T('\0');
mSSRegistryPath[0] = _T('\0');
pause( true ); // pause until we're ready to render
// When mClipCursorWhenFullscreen is true, the cursor is limited to
// the device window when the app goes fullscreen. This prevents users
// from accidentally clicking outside the app window on a multimon system.
// This flag is turned off by default for debug builds, since it makes
// multimon debugging difficult.
#if defined(_DEBUG) || defined(DEBUG)
mClipCursorWhenFullscreen = false;
#else
mClipCursorWhenFullscreen = true;
#endif
}
/**
* Static function used by D3DEnumeration
*/
bool CD3DApplication::checkDeviceHelper( const D3DCAPS9& caps, eVertexProcessing vertexProc, D3DFORMAT backBufferFormat )
{
DWORD behavior;
if( vertexProc == SOFTWARE_VP )
behavior = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
else if( vertexProc == MIXED_VP )
behavior = D3DCREATE_MIXED_VERTEXPROCESSING;
else if( vertexProc == HARDWARE_VP )
behavior = D3DCREATE_HARDWARE_VERTEXPROCESSING;
else if( vertexProc == PURE_HARDWARE_VP )
behavior = D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE;
else
behavior = 0; // TODO: throw exception
return SUCCEEDED( gD3DApp->checkDevice( caps, behavior, backBufferFormat ) );
}
HRESULT CD3DApplication::create( HINSTANCE hInstance, bool screenSaverMode )
{
HRESULT hr;
// if we're screensaver - set low priority and parse cmd line
if( screenSaverMode ) {
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_IDLE );
mSSMode = ssParseCmdLine( GetCommandLine() );
// force windowed in preview mode
if( mSSMode == SM_PREVIEW )
mStartFullscreen = false;
} else {
mSSMode = SM_NONE;
mSSHwndParent = NULL;
}
mHInstance = hInstance;
//
// setup d3d object, enumerate possible options, etc.
// create the Direct3D object
mD3D = Direct3DCreate9( D3D_SDK_VERSION );
if( mD3D == NULL )
return displayErrorMsg( (HRESULT)NODIRECT3D, APPMUSTEXIT );
// Build a list of Direct3D adapters, modes and devices. The
// checkDevice() callback is used to confirm that only devices that
// meet the app's requirements are considered.
mEnumeration.setDirect3D( *mD3D );
mEnumeration.mConfirmDeviceCallback = checkDeviceHelper;
if( FAILED( hr = mEnumeration.enumerate() ) ) {
safeRelease( mD3D );
return displayErrorMsg( hr, APPMUSTEXIT );
}
if( FAILED( hr = chooseInitialD3DSettings() ) ) {
safeRelease( mD3D );
return displayErrorMsg( hr, APPMUSTEXIT );
}
//
// manage screensaver running modes
switch( mSSMode ) {
case SM_CONFIG:
ssDoConfig();
safeRelease( mD3D );
return S_OK;
break;
case SM_PASSCHANGE:
ssChangePassword();
safeRelease( mD3D );
return S_OK;
break;
}
// force no dialog at startup if we're screensaver
if( mSSMode != SM_NONE )
mSelectDeviceAtStartup = false;
if( mSelectDeviceAtStartup ) {
bool ok = justShowSettingsDialog();
if( !ok ) {
safeRelease( mD3D );
if( mHWnd )
SendMessage( mHWnd, WM_CLOSE, 0, 0 );
return E_FAIL;
}
}
//
// create main window
// Unless a substitute hWnd has been specified, create a window to
// render into, OR
// do the needed thing if we're screensaver
if( mSSMode==SM_NONE && mHWnd == NULL ||
(mSSMode==SM_PREVIEW || mSSMode==SM_TEST || mSSMode==SM_FULL) )
{
// Register the windows class
WNDCLASS wndClass = { 0, gWndProc, 0, 0, hInstance,
LoadIcon( hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON) ),
LoadCursor( NULL, IDC_ARROW ),
(HBRUSH)GetStockObject(WHITE_BRUSH),
NULL, _T("D3D Window")
};
// TBD - need this?
//if( screenSaverMode )
// wndClass.style = CS_VREDRAW | CS_HREDRAW;
RegisterClass( &wndClass );
// Set the window's initial style and size
RECT rc;
if( mSSMode==SM_PREVIEW ) {
GetClientRect( mSSHwndParent, &rc );
mWindowStyle = WS_VISIBLE | WS_CHILD;
AdjustWindowRect( &rc, mWindowStyle, false );
// create the render window
mHWnd = CreateWindow( _T("D3D Window"), mWindowTitle, mWindowStyle,
CW_USEDEFAULT, CW_USEDEFAULT,
(rc.right-rc.left), (rc.bottom-rc.top), mSSHwndParent,
NULL, hInstance, 0 );
} else {
mWindowStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME |
WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE;
SetRect( &rc, 0, 0, mCreationWidth, mCreationHeight );
AdjustWindowRect( &rc, mWindowStyle, true );
// create the render window
mHWnd = CreateWindow( _T("D3D Window"), mWindowTitle, mWindowStyle,
CW_USEDEFAULT, CW_USEDEFAULT,
(rc.right-rc.left), (rc.bottom-rc.top), NULL,
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( mHWndFocus == NULL )
mHWndFocus = mHWnd;
// Save window properties
mWindowStyle = GetWindowLong( mHWnd, GWL_STYLE );
GetWindowRect( mHWnd, &mWindowBounds );
GetClientRect( mHWnd, &mWindowClient );
// In screensaver preview mode, "pause" (enter a limited message loop)
// briefly before proceeding, so the display control panel knows to
// update itself.
if( mSSMode == SM_PREVIEW ) {
mSSInStartingPause = true;
// Post a message to mark the end of the initial group of window messages
PostMessage( mHWnd, WM_USER, 0, 0 );
MSG msg;
while( mSSInStartingPause ) {
if( !GetMessage( &msg, mHWnd, 0, 0 ) ) {
PostQuitMessage(0);
break;
}
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
// read SS settings
if( mSSMode != SM_NONE )
ssReadSettings();
// Initialize the application timer
dingus::timer( TIMER_START );
// Initialize the app
if( FAILED( hr = initialize() ) ) {
safeRelease( mD3D );
return displayErrorMsg( hr, APPMUSTEXIT );
}
// Initialize the 3D environment for the app
if( FAILED( hr = initialize3DEnvironment() ) ) {
safeRelease( mD3D );
return displayErrorMsg( hr, APPMUSTEXIT );
}
// The app is ready to go
pause( false );
return S_OK;
}
/**
* Sets up mSettings with best available windowed mode, subject to the
* requireHAL and requireREF constraints. Returns false if no such mode
* can be found.
*/
bool CD3DApplication::findBestWindowedMode( bool requireHAL, bool requireREF )
{
// Get display mode of primary adapter (which is assumed to be where the
// window will appear)
D3DDISPLAYMODE primaryDesktopDM;
mD3D->GetAdapterDisplayMode( 0, &primaryDesktopDM );
const SD3DAdapterInfo* bestAdapterInfo = NULL;
const SD3DDeviceInfo* bestDeviceInfo = NULL;
const SD3DDeviceCombo* bestDeviceCombo = NULL;
for( int iai = 0; iai < mEnumeration.mAdapterInfos.size(); ++iai ) {
const SD3DAdapterInfo* adInfo = mEnumeration.mAdapterInfos[iai];
for( int idi = 0; idi < adInfo->deviceInfos.size(); ++idi ) {
const SD3DDeviceInfo* devInfo = adInfo->deviceInfos[idi];
if( requireHAL && devInfo->deviceType != D3DDEVTYPE_HAL )
continue;
if( requireREF && devInfo->deviceType != D3DDEVTYPE_REF )
continue;
for( int idc = 0; idc < devInfo->deviceCombos.size(); ++idc ) {
const SD3DDeviceCombo* devCombo = devInfo->deviceCombos[idc];
bool matchesBB = (devCombo->backBufferFormat == devCombo->adapterFormat);
if( !devCombo->isWindowed )
continue;
if( devCombo->adapterFormat != primaryDesktopDM.Format )
continue;
// If we haven't found a compatible DeviceCombo yet, or if
// this set is better (because it's a HAL, and/or because
// formats match better), save it
if( !bestDeviceCombo ||
bestDeviceCombo->deviceType != D3DDEVTYPE_HAL && devCombo->deviceType == D3DDEVTYPE_HAL ||
devCombo->deviceType == D3DDEVTYPE_HAL && matchesBB )
{
bestAdapterInfo = adInfo;
bestDeviceInfo = devInfo;
bestDeviceCombo = devCombo;
if( devCombo->deviceType == D3DDEVTYPE_HAL && matchesBB ) {
// This windowed device combo looks great -- take it
goto _endComboSearch;
}
// Otherwise keep looking for better combo
}
}
}
}
_endComboSearch:
CD3DSettings::SSettings& settings = mSettings.mSettings[CD3DSettings::WINDOWED];
if( bestDeviceCombo == NULL ) {
settings.adapterInfo = NULL;
settings.deviceInfo = NULL;
settings.deviceCombo = NULL;
return false;
}
mSettings.mMode = CD3DSettings::WINDOWED;
settings.adapterInfo = bestAdapterInfo;
settings.deviceInfo = bestDeviceInfo;
settings.deviceCombo = bestDeviceCombo;
settings.displayMode = primaryDesktopDM;
mSettings.mWindowedWidth = mWindowClient.right - mWindowClient.left;
mSettings.mWindowedHeight = mWindowClient.bottom - mWindowClient.top;
if( mEnumeration.mUsesDepthBuffer )
settings.depthStencilFormat = (D3DFORMAT)bestDeviceCombo->depthStencilFormats[0];
settings.multisampleType = (D3DMULTISAMPLE_TYPE)bestDeviceCombo->multiSampleTypes[0];
settings.multisampleQuality = 0;
settings.vertexProcessing = (eVertexProcessing)bestDeviceCombo->vertexProcessings[0];
settings.presentInterval = bestDeviceCombo->presentIntervals[0];
return true;
}
/**
* Sets up mSettings with best available fullscreen mode, subject to the
* requireHAL and requireREF constraints. Returns false if no such
* mode can be found.
*/
bool CD3DApplication::findBestFullscreenMode( bool requireHAL, bool requireREF )
{
// For fullscreen, default to first HAL DeviceCombo that supports the
// current desktop display mode, or any display mode if HAL is not
// compatible with the desktop mode, or non-HAL if no HAL is available
D3DDISPLAYMODE adapterDesktopDM;
D3DDISPLAYMODE bestAdapterDesktopDM;
D3DDISPLAYMODE bestDM;
bestAdapterDesktopDM.Width = 0;
bestAdapterDesktopDM.Height = 0;
bestAdapterDesktopDM.Format = D3DFMT_UNKNOWN;
bestAdapterDesktopDM.RefreshRate = 0;
const SD3DAdapterInfo* bestAdapterInfo = NULL;
const SD3DDeviceInfo* bestDeviceInfo = NULL;
const SD3DDeviceCombo* bestDeviceCombo = NULL;
for( int iai = 0; iai < mEnumeration.mAdapterInfos.size(); ++iai ) {
const SD3DAdapterInfo* adInfo = mEnumeration.mAdapterInfos[iai];
mD3D->GetAdapterDisplayMode( adInfo->adapterOrdinal, &adapterDesktopDM );
for( int idi = 0; idi < adInfo->deviceInfos.size(); ++idi ) {
const SD3DDeviceInfo* devInfo = adInfo->deviceInfos[idi];
if( requireHAL && devInfo->deviceType != D3DDEVTYPE_HAL )
continue;
if( requireREF && devInfo->deviceType != D3DDEVTYPE_REF )
continue;
for( int idc = 0; idc < devInfo->deviceCombos.size(); ++idc ) {
const SD3DDeviceCombo* devCombo = devInfo->deviceCombos[idc];
bool matchesBB = (devCombo->backBufferFormat == devCombo->adapterFormat);
bool matchesDesktop = (devCombo->adapterFormat == adapterDesktopDM.Format);
if( devCombo->isWindowed )
continue;
// If we haven't found a compatible set yet, or if this set
// is better (because it's a HAL, and/or because formats match
// better), save it
if( !bestDeviceCombo ||
bestDeviceCombo->deviceType != D3DDEVTYPE_HAL && devInfo->deviceType == D3DDEVTYPE_HAL ||
devCombo->deviceType == D3DDEVTYPE_HAL && bestDeviceCombo->adapterFormat != adapterDesktopDM.Format && matchesDesktop ||
devCombo->deviceType == D3DDEVTYPE_HAL && matchesDesktop && matchesBB )
{
bestAdapterDesktopDM = adapterDesktopDM;
bestAdapterInfo = adInfo;
bestDeviceInfo = devInfo;
bestDeviceCombo = devCombo;
if( devInfo->deviceType == D3DDEVTYPE_HAL && matchesDesktop && matchesBB )
{
// This fullscreen device combo looks great -- take it
goto _endComboSearch;
}
// Otherwise keep looking for a better combo
}
}
}
}
_endComboSearch:
CD3DSettings::SSettings& settings = mSettings.mSettings[CD3DSettings::FULLSCREEN];
if( bestDeviceCombo == NULL ) {
settings.adapterInfo = NULL;
settings.deviceInfo = NULL;
settings.deviceCombo = NULL;
return false;
}
// Need to find a display mode on the best adapter that uses
// bestDeviceCombo->adapterFormat and is as close to bestAdapterDesktopDM's
// res as possible
bestDM.Width = 0;
bestDM.Height = 0;
bestDM.Format = D3DFMT_UNKNOWN;
bestDM.RefreshRate = 0;
for( int idm = 0; idm < bestAdapterInfo->displayModes.size(); ++idm ) {
const D3DDISPLAYMODE& pdm = bestAdapterInfo->displayModes[idm];
if( pdm.Format != bestDeviceCombo->adapterFormat )
continue;
if( pdm.Width == bestAdapterDesktopDM.Width &&
pdm.Height == bestAdapterDesktopDM.Height &&
pdm.RefreshRate == bestAdapterDesktopDM.RefreshRate )
{
// found a perfect match, so stop
bestDM = pdm;
break;
} else if( pdm.Width == bestAdapterDesktopDM.Width &&
pdm.Height == bestAdapterDesktopDM.Height &&
pdm.RefreshRate > bestDM.RefreshRate )
{
// refresh rate doesn't match, but width/height match, so keep this
// and keep looking
bestDM = pdm;
} else if( pdm.Width == bestAdapterDesktopDM.Width ) {
// width matches, so keep this and keep looking
bestDM = pdm;
} else if( bestDM.Width == 0 ) {
// we don't have anything better yet, so keep this and keep looking
bestDM = pdm;
}
}
mSettings.mMode = CD3DSettings::FULLSCREEN;
settings.adapterInfo = bestAdapterInfo;
settings.deviceInfo = bestDeviceInfo;
settings.deviceCombo = bestDeviceCombo;
settings.displayMode = bestDM;
if( mEnumeration.mUsesDepthBuffer )
settings.depthStencilFormat = (D3DFORMAT)bestDeviceCombo->depthStencilFormats[0];
settings.multisampleType = (D3DMULTISAMPLE_TYPE)bestDeviceCombo->multiSampleTypes[0];
settings.multisampleQuality = 0;
settings.vertexProcessing = (eVertexProcessing)bestDeviceCombo->vertexProcessings[0];
settings.presentInterval = mVSyncFullscreen ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;
return true;
}
HRESULT CD3DApplication::chooseInitialD3DSettings()
{
bool foundFullscreen = findBestFullscreenMode( false, false );
bool foundWindowed = findBestWindowedMode( false, false );
if( (mStartFullscreen || !foundWindowed || mSSMode==SM_FULL) && foundFullscreen )
mSettings.mMode = CD3DSettings::FULLSCREEN;
if( !foundFullscreen && (!foundWindowed || mSSMode==SM_FULL) )
return (HRESULT)NOCOMPATIBLEDEVICES;
return S_OK;
}
void CD3DApplication::ssReadSettings()
{
// TBD
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -