//----------------------------------------------------------------------------- // File: D3DApp.cpp // // Desc: Application class for the Direct3D samples framework library. //----------------------------------------------------------------------------- #define STRICT #include #include #include #include #include #include #include "DXUtil.h" #include "D3DUtil.h" #include "D3DEnumeration.h" #include "D3DSettings.h" #include "D3DApp.h" #include "resource.h" //----------------------------------------------------------------------------- // Global access to the app (needed for the global WndProc()) //----------------------------------------------------------------------------- static CD3DApplication* g_pD3DApp = NULL; //----------------------------------------------------------------------------- // Name: CD3DApplication() // Desc: Constructor //----------------------------------------------------------------------------- CD3DApplication::CD3DApplication() { g_pD3DApp = this; m_pD3D = NULL; m_pd3dDevice = NULL; m_hWnd = NULL; m_hWndFocus = NULL; m_hMenu = NULL; m_bWindowed = true; m_bActive = false; m_bDeviceLost = false; m_bMinimized = false; m_bMaximized = false; m_bIgnoreSizeChange = false; m_bDeviceObjectsInited = false; m_bDeviceObjectsRestored = false; m_dwCreateFlags = 0; m_bFrameMoving = true; m_bSingleStep = false; m_fTime = 0.0f; m_fElapsedTime = 0.0f; m_fFPS = 0.0f; m_strDeviceStats[0] = _T('\0'); m_strFrameStats[0] = _T('\0'); m_strWindowTitle = _T("D3D9 Application"); m_dwCreationWidth = 400; m_dwCreationHeight = 300; m_bShowCursorWhenFullscreen = false; m_bStartFullscreen = false; Pause( true ); // Pause until we're ready to render // When m_bClipCursorWhenFullscreen 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) m_bClipCursorWhenFullscreen = false; #else m_bClipCursorWhenFullscreen = true; #endif } //----------------------------------------------------------------------------- // Name: WndProc() // Desc: Static msg handler which passes messages to the application class. //----------------------------------------------------------------------------- LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { return g_pD3DApp->MsgProc( hWnd, uMsg, wParam, lParam ); } //----------------------------------------------------------------------------- // Name: ConfirmDeviceHelper() // Desc: Static function used by D3DEnumeration //----------------------------------------------------------------------------- bool CD3DApplication::ConfirmDeviceHelper( D3DCAPS9* pCaps, VertexProcessingType vertexProcessingType, D3DFORMAT backBufferFormat ) { DWORD dwBehavior; if (vertexProcessingType == SOFTWARE_VP) dwBehavior = D3DCREATE_SOFTWARE_VERTEXPROCESSING; else if (vertexProcessingType == MIXED_VP) dwBehavior = D3DCREATE_MIXED_VERTEXPROCESSING; else if (vertexProcessingType == HARDWARE_VP) dwBehavior = D3DCREATE_HARDWARE_VERTEXPROCESSING; else if (vertexProcessingType == PURE_HARDWARE_VP) dwBehavior = D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE; else dwBehavior = 0; // TODO: throw exception return SUCCEEDED( g_pD3DApp->ConfirmDevice( pCaps, dwBehavior, backBufferFormat ) ); } //----------------------------------------------------------------------------- // Name: Create() // Desc: //----------------------------------------------------------------------------- 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; } //----------------------------------------------------------------------------- // Name: FindBestWindowedMode() // Desc: Sets up m_d3dSettings with best available windowed mode, subject to // the bRequireHAL and bRequireREF constraints. Returns false if no such // mode can be found. //----------------------------------------------------------------------------- bool CD3DApplication::FindBestWindowedMode( bool bRequireHAL, bool bRequireREF ) { // Get display mode of primary adapter (which is assumed to be where the window // will appear) D3DDISPLAYMODE primaryDesktopDisplayMode; m_pD3D->GetAdapterDisplayMode(0, &primaryDesktopDisplayMode); D3DAdapterInfo* pBestAdapterInfo = NULL; D3DDeviceInfo* pBestDeviceInfo = NULL; D3DDeviceCombo* pBestDeviceCombo = NULL; for( UINT iai = 0; iai < m_d3dEnumeration.m_pAdapterInfoList->Count(); iai++ ) { D3DAdapterInfo* pAdapterInfo = (D3DAdapterInfo*)m_d3dEnumeration.m_pAdapterInfoList->GetPtr(iai); for( UINT idi = 0; idi < pAdapterInfo->pDeviceInfoList->Count(); idi++ ) { D3DDeviceInfo* pDeviceInfo = (D3DDeviceInfo*)pAdapterInfo->pDeviceInfoList->GetPtr(idi); if (bRequireHAL && pDeviceInfo->DevType != D3DDEVTYPE_HAL) continue; if (bRequireREF && pDeviceInfo->DevType != D3DDEVTYPE_REF) continue; for( UINT idc = 0; idc < pDeviceInfo->pDeviceComboList->Count(); idc++ ) { D3DDeviceCombo* pDeviceCombo = (D3DDeviceCombo*)pDeviceInfo->pDeviceComboList->GetPtr(idc); bool bAdapterMatchesBB = (pDeviceCombo->BackBufferFormat == pDeviceCombo->AdapterFormat); if (!pDeviceCombo->IsWindowed) continue; if (pDeviceCombo->AdapterFormat != primaryDesktopDisplayMode.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( pBestDeviceCombo == NULL || pBestDeviceCombo->DevType != D3DDEVTYPE_HAL && pDeviceCombo->DevType == D3DDEVTYPE_HAL || pDeviceCombo->DevType == D3DDEVTYPE_HAL && bAdapterMatchesBB ) { pBestAdapterInfo = pAdapterInfo; pBestDeviceInfo = pDeviceInfo; pBestDeviceCombo = pDeviceCombo; if( pDeviceCombo->DevType == D3DDEVTYPE_HAL && bAdapterMatchesBB ) { // This windowed device combo looks great -- take it goto EndWindowedDeviceComboSearch; } // Otherwise keep looking for a better windowed device combo } } } } EndWindowedDeviceComboSearch: if (pBestDeviceCombo == NULL ) return false; m_d3dSettings.pWindowed_AdapterInfo = pBestAdapterInfo; m_d3dSettings.pWindowed_DeviceInfo = pBestDeviceInfo; m_d3dSettings.pWindowed_DeviceCombo = pBestDeviceCombo; m_d3dSettings.IsWindowed = true; m_d3dSettings.Windowed_DisplayMode = primaryDesktopDisplayMode; m_d3dSettings.Windowed_Width = m_rcWindowClient.right - m_rcWindowClient.left; m_d3dSettings.Windowed_Height = m_rcWindowClient.bottom - m_rcWindowClient.top; if (m_d3dEnumeration.AppUsesDepthBuffer) m_d3dSettings.Windowed_DepthStencilBufferFormat = *(D3DFORMAT*)pBestDeviceCombo->pDepthStencilFormatList->GetPtr(0); m_d3dSettings.Windowed_MultisampleType = *(D3DMULTISAMPLE_TYPE*)pBestDeviceCombo->pMultiSampleTypeList->GetPtr(0); m_d3dSettings.Windowed_MultisampleQuality = 0; m_d3dSettings.Windowed_VertexProcessingType = *(VertexProcessingType*)pBestDeviceCombo->pVertexProcessingTypeList->GetPtr(0); m_d3dSettings.Windowed_PresentInterval = *(UINT*)pBestDeviceCombo->pPresentIntervalList->GetPtr(0); return true; } //----------------------------------------------------------------------------- // Name: FindBestFullscreenMode() // Desc: Sets up m_d3dSettings with best available fullscreen mode, subject to // the bRequireHAL and bRequireREF constraints. Returns false if no such // mode can be found. //----------------------------------------------------------------------------- bool CD3DApplication::FindBestFullscreenMode( bool bRequireHAL, bool bRequireREF ) { // 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 adapterDesktopDisplayMode; D3DDISPLAYMODE bestAdapterDesktopDisplayMode; D3DDISPLAYMODE bestDisplayMode; bestAdapterDesktopDisplayMode.Width = 0; bestAdapterDesktopDisplayMode.Height = 0; bestAdapterDesktopDisplayMode.Format = D3DFMT_UNKNOWN; bestAdapterDesktopDisplayMode.RefreshRate = 0; D3DAdapterInfo* pBestAdapterInfo = NULL; D3DDeviceInfo* pBestDeviceInfo = NULL; D3DDeviceCombo* pBestDeviceCombo = NULL; for( UINT iai = 0; iai < m_d3dEnumeration.m_pAdapterInfoList->Count(); iai++ ) { D3DAdapterInfo* pAdapterInfo = (D3DAdapterInfo*)m_d3dEnumeration.m_pAdapterInfoList->GetPtr(iai); m_pD3D->GetAdapterDisplayMode( pAdapterInfo->AdapterOrdinal, &adapterDesktopDisplayMode ); for( UINT idi = 0; idi < pAdapterInfo->pDeviceInfoList->Count(); idi++ ) { D3DDeviceInfo* pDeviceInfo = (D3DDeviceInfo*)pAdapterInfo->pDeviceInfoList->GetPtr(idi); if (bRequireHAL && pDeviceInfo->DevType != D3DDEVTYPE_HAL) continue; if (bRequireREF && pDeviceInfo->DevType != D3DDEVTYPE_REF) continue; for( UINT idc = 0; idc < pDeviceInfo->pDeviceComboList->Count(); idc++ ) { D3DDeviceCombo* pDeviceCombo = (D3DDeviceCombo*)pDeviceInfo->pDeviceComboList->GetPtr(idc); bool bAdapterMatchesBB = (pDeviceCombo->BackBufferFormat == pDeviceCombo->AdapterFormat); bool bAdapterMatchesDesktop = (pDeviceCombo->AdapterFormat == adapterDesktopDisplayMode.Format); if (pDeviceCombo->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 (pBestDeviceCombo == NULL || pBestDeviceCombo->DevType != D3DDEVTYPE_HAL && pDeviceInfo->DevType == D3DDEVTYPE_HAL || pDeviceCombo->DevType == D3DDEVTYPE_HAL && pBestDeviceCombo->AdapterFormat != adapterDesktopDisplayMode.Format && bAdapterMatchesDesktop || pDeviceCombo->DevType == D3DDEVTYPE_HAL && bAdapterMatchesDesktop && bAdapterMatchesBB ) { bestAdapterDesktopDisplayMode = adapterDesktopDisplayMode; pBestAdapterInfo = pAdapterInfo; pBestDeviceInfo = pDeviceInfo; pBestDeviceCombo = pDeviceCombo; if (pDeviceInfo->DevType == D3DDEVTYPE_HAL && bAdapterMatchesDesktop && bAdapterMatchesBB) { // This fullscreen device combo looks great -- take it goto EndFullscreenDeviceComboSearch; } // Otherwise keep looking for a better fullscreen device combo } } } } EndFullscreenDeviceComboSearch: if (pBestDeviceCombo == NULL) return false; // Need to find a display mode on the best adapter that uses pBestDeviceCombo->AdapterFormat // and is as close to bestAdapterDesktopDisplayMode's res as possible bestDisplayMode.Width = 0; bestDisplayMode.Height = 0; bestDisplayMode.Format = D3DFMT_UNKNOWN; bestDisplayMode.RefreshRate = 0; for( UINT idm = 0; idm < pBestAdapterInfo->pDisplayModeList->Count(); idm++ ) { D3DDISPLAYMODE* pdm = (D3DDISPLAYMODE*)pBestAdapterInfo->pDisplayModeList->GetPtr(idm); if( pdm->Format != pBestDeviceCombo->AdapterFormat ) continue; if( pdm->Width == bestAdapterDesktopDisplayMode.Width && pdm->Height == bestAdapterDesktopDisplayMode.Height && pdm->RefreshRate == bestAdapterDesktopDisplayMode.RefreshRate ) { // found a perfect match, so stop bestDisplayMode = *pdm; break; } else if( pdm->Width == bestAdapterDesktopDisplayMode.Width && pdm->Height == bestAdapterDesktopDisplayMode.Height && pdm->RefreshRate > bestDisplayMode.RefreshRate ) { // refresh rate doesn't match, but width/height match, so keep this // and keep looking bestDisplayMode = *pdm; } else if( pdm->Width == bestAdapterDesktopDisplayMode.Width ) { // width matches, so keep this and keep looking bestDisplayMode = *pdm; } else if( bestDisplayMode.Width == 0 ) { // we don't have anything better yet, so keep this and keep looking bestDisplayMode = *pdm; } } m_d3dSettings.pFullscreen_AdapterInfo = pBestAdapterInfo; m_d3dSettings.pFullscreen_DeviceInfo = pBestDeviceInfo; m_d3dSettings.pFullscreen_DeviceCombo = pBestDeviceCombo; m_d3dSettings.IsWindowed = false; m_d3dSettings.Fullscreen_DisplayMode = bestDisplayMode; if (m_d3dEnumeration.AppUsesDepthBuffer) m_d3dSettings.Fullscreen_DepthStencilBufferFormat = *(D3DFORMAT*)pBestDeviceCombo->pDepthStencilFormatList->GetPtr(0); m_d3dSettings.Fullscreen_MultisampleType = *(D3DMULTISAMPLE_TYPE*)pBestDeviceCombo->pMultiSampleTypeList->GetPtr(0); m_d3dSettings.Fullscreen_MultisampleQuality = 0; m_d3dSettings.Fullscreen_VertexProcessingType = *(VertexProcessingType*)pBestDeviceCombo->pVertexProcessingTypeList->GetPtr(0); m_d3dSettings.Fullscreen_PresentInterval = D3DPRESENT_INTERVAL_DEFAULT; return true; } //----------------------------------------------------------------------------- // Name: ChooseInitialD3DSettings() // Desc: //----------------------------------------------------------------------------- HRESULT CD3DApplication::ChooseInitialD3DSettings() { bool bFoundFullscreen = FindBestFullscreenMode( false, false ); bool bFoundWindowed = FindBestWindowedMode( false, false ); if( m_bStartFullscreen && bFoundFullscreen ) m_d3dSettings.IsWindowed = false; if( !bFoundWindowed && bFoundFullscreen ) m_d3dSettings.IsWindowed = false; if( !bFoundFullscreen && !bFoundWindowed ) return D3DAPPERR_NOCOMPATIBLEDEVICES; return S_OK; } //----------------------------------------------------------------------------- // Name: MsgProc() // Desc: Message handling function. //----------------------------------------------------------------------------- LRESULT CD3DApplication::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch( uMsg ) { case WM_PAINT: // Handle paint messages when the app is paused if( m_pd3dDevice && !m_bActive && m_bWindowed && m_bDeviceObjectsInited && m_bDeviceObjectsRestored ) { Render(); m_pd3dDevice->Present( NULL, NULL, NULL, NULL ); } break; case WM_GETMINMAXINFO: ((MINMAXINFO*)lParam)->ptMinTrackSize.x = 100; ((MINMAXINFO*)lParam)->ptMinTrackSize.y = 100; break; case WM_ENTERSIZEMOVE: // Halt frame movement while the app is sizing or moving Pause( true ); break; case WM_SIZE: // Pick up possible changes to window style due to maximize, etc. if( m_bWindowed && m_hWnd != NULL ) m_dwWindowStyle = GetWindowLong( m_hWnd, GWL_STYLE ); if( SIZE_MINIMIZED == wParam ) { if( m_bClipCursorWhenFullscreen && !m_bWindowed ) ClipCursor( NULL ); Pause( true ); // Pause while we're minimized m_bMinimized = true; m_bMaximized = false; } else if( SIZE_MAXIMIZED == wParam ) { if( m_bMinimized ) Pause( false ); // Unpause since we're no longer minimized m_bMinimized = false; m_bMaximized = true; HandlePossibleSizeChange(); } else if( SIZE_RESTORED == wParam ) { if( m_bMaximized ) { m_bMaximized = false; HandlePossibleSizeChange(); } else if( m_bMinimized) { Pause( false ); // Unpause since we're no longer minimized m_bMinimized = false; HandlePossibleSizeChange(); } else { // If we're neither maximized nor minimized, the window size // is changing by the user dragging the window edges. In this // case, we don't reset the device yet -- we wait until the // user stops dragging, and a WM_EXITSIZEMOVE message comes. } } break; case WM_EXITSIZEMOVE: Pause( false ); HandlePossibleSizeChange(); break; case WM_SETCURSOR: // Turn off Windows cursor in fullscreen mode if( m_bActive && !m_bWindowed ) { SetCursor( NULL ); if( m_bShowCursorWhenFullscreen ) m_pd3dDevice->ShowCursor( true ); return true; // prevent Windows from setting cursor to window class cursor } break; case WM_MOUSEMOVE: if( m_bActive && m_pd3dDevice != NULL ) { POINT ptCursor; GetCursorPos( &ptCursor ); if( !m_bWindowed ) ScreenToClient( m_hWnd, &ptCursor ); m_pd3dDevice->SetCursorPosition( ptCursor.x, ptCursor.y, 0 ); } break; case WM_ENTERMENULOOP: // Pause the app when menus are displayed Pause(true); break; case WM_EXITMENULOOP: Pause(false); break; case WM_NCHITTEST: // Prevent the user from selecting the menu in fullscreen mode if( !m_bWindowed ) return HTCLIENT; break; case WM_POWERBROADCAST: switch( wParam ) { #ifndef PBT_APMQUERYSUSPEND #define PBT_APMQUERYSUSPEND 0x0000 #endif case PBT_APMQUERYSUSPEND: // At this point, the app should save any data for open // network connections, files, etc., and prepare to go into // a suspended mode. return true; #ifndef PBT_APMRESUMESUSPEND #define PBT_APMRESUMESUSPEND 0x0007 #endif case PBT_APMRESUMESUSPEND: // At this point, the app should recover any data, network // connections, files, etc., and resume running from when // the app was suspended. return true; } break; case WM_SYSCOMMAND: // Prevent moving/sizing and power loss in fullscreen mode switch( wParam ) { case SC_MOVE: case SC_SIZE: case SC_MAXIMIZE: case SC_KEYMENU: case SC_MONITORPOWER: if( false == m_bWindowed ) return 1; break; } break; case WM_COMMAND: switch( LOWORD(wParam) ) { case IDM_CHANGEDEVICE: // Prompt the user to select a new device or mode Pause(true); UserSelectNewDevice(); Pause(false); return 0; case IDM_TOGGLEFULLSCREEN: // Toggle the fullscreen/window mode Pause( true ); if( FAILED( ToggleFullscreen() ) ) DisplayErrorMsg( D3DAPPERR_RESETFAILED, MSGERR_APPMUSTEXIT ); Pause( false ); return 0; case IDM_EXIT: // Recieved key/menu command to exit app SendMessage( hWnd, WM_CLOSE, 0, 0 ); return 0; } break; case WM_CLOSE: Cleanup3DEnvironment(); SAFE_RELEASE( m_pD3D ); FinalCleanup(); HMENU hMenu; hMenu = GetMenu(hWnd); if( hMenu != NULL ) DestroyMenu( hMenu ); DestroyWindow( hWnd ); PostQuitMessage(0); m_hWnd = NULL; return 0; } return DefWindowProc( hWnd, uMsg, wParam, lParam ); } //----------------------------------------------------------------------------- // Name: HandlePossibleSizeChange() // Desc: Reset the device if the client area size has changed. //----------------------------------------------------------------------------- HRESULT CD3DApplication::HandlePossibleSizeChange() { HRESULT hr = S_OK; RECT rcClientOld; rcClientOld = m_rcWindowClient; if( m_bIgnoreSizeChange ) return S_OK; // Update window properties GetWindowRect( m_hWnd, &m_rcWindowBounds ); GetClientRect( m_hWnd, &m_rcWindowClient ); if( rcClientOld.right - rcClientOld.left != m_rcWindowClient.right - m_rcWindowClient.left || rcClientOld.bottom - rcClientOld.top != m_rcWindowClient.bottom - m_rcWindowClient.top) { // A new window size will require a new backbuffer // size, so the 3D structures must be changed accordingly. Pause( true ); m_d3dpp.BackBufferWidth = m_rcWindowClient.right - m_rcWindowClient.left; m_d3dpp.BackBufferHeight = m_rcWindowClient.bottom - m_rcWindowClient.top; if( m_pd3dDevice != NULL ) { // Reset the 3D environment if( FAILED( hr = Reset3DEnvironment() ) ) { if( hr != D3DERR_OUTOFVIDEOMEMORY ) hr = D3DAPPERR_RESETFAILED; DisplayErrorMsg( hr, MSGERR_APPMUSTEXIT ); } } Pause( false ); } return hr; } //----------------------------------------------------------------------------- // Name: Initialize3DEnvironment() // Desc: //----------------------------------------------------------------------------- HRESULT CD3DApplication::Initialize3DEnvironment() { HRESULT hr; D3DAdapterInfo* pAdapterInfo = m_d3dSettings.PAdapterInfo(); D3DDeviceInfo* pDeviceInfo = m_d3dSettings.PDeviceInfo(); m_bWindowed = m_d3dSettings.IsWindowed; // Prepare window for possible windowed/fullscreen change AdjustWindowForChange(); // Set up the presentation parameters BuildPresentParamsFromSettings(); if( pDeviceInfo->Caps.PrimitiveMiscCaps & D3DPMISCCAPS_NULLREFERENCE ) { // Warn user about null ref device that can't render anything DisplayErrorMsg( D3DAPPERR_NULLREFDEVICE, 0 ); } DWORD behaviorFlags; if (m_d3dSettings.GetVertexProcessingType() == SOFTWARE_VP) behaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING; else if (m_d3dSettings.GetVertexProcessingType() == MIXED_VP) behaviorFlags = D3DCREATE_MIXED_VERTEXPROCESSING; else if (m_d3dSettings.GetVertexProcessingType() == HARDWARE_VP) behaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING; else if (m_d3dSettings.GetVertexProcessingType() == PURE_HARDWARE_VP) behaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE; else behaviorFlags = 0; // TODO: throw exception // Create the device hr = m_pD3D->CreateDevice( m_d3dSettings.AdapterOrdinal(), pDeviceInfo->DevType, m_hWndFocus, behaviorFlags, &m_d3dpp, &m_pd3dDevice ); if( SUCCEEDED(hr) ) { // When moving from fullscreen to windowed mode, it is important to // adjust the window size after recreating the device rather than // beforehand to ensure that you get the window size you want. For // example, when switching from 640x480 fullscreen to windowed with // a 1000x600 window on a 1024x768 desktop, it is impossible to set // the window size to 1000x600 until after the display mode has // changed to 1024x768, because windows cannot be larger than the // desktop. if( m_bWindowed ) { SetWindowPos( m_hWnd, HWND_NOTOPMOST, m_rcWindowBounds.left, m_rcWindowBounds.top, ( m_rcWindowBounds.right - m_rcWindowBounds.left ), ( m_rcWindowBounds.bottom - m_rcWindowBounds.top ), SWP_SHOWWINDOW ); } // Store device Caps m_pd3dDevice->GetDeviceCaps( &m_d3dCaps ); m_dwCreateFlags = behaviorFlags; // Store device description if( pDeviceInfo->DevType == D3DDEVTYPE_REF ) lstrcpy( m_strDeviceStats, TEXT("REF") ); else if( pDeviceInfo->DevType == D3DDEVTYPE_HAL ) lstrcpy( m_strDeviceStats, TEXT("HAL") ); else if( pDeviceInfo->DevType == D3DDEVTYPE_SW ) lstrcpy( m_strDeviceStats, TEXT("SW") ); if( behaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING && behaviorFlags & D3DCREATE_PUREDEVICE ) { if( pDeviceInfo->DevType == D3DDEVTYPE_HAL ) lstrcat( m_strDeviceStats, TEXT(" (pure hw vp)") ); else lstrcat( m_strDeviceStats, TEXT(" (simulated pure hw vp)") ); } else if( behaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING ) { if( pDeviceInfo->DevType == D3DDEVTYPE_HAL ) lstrcat( m_strDeviceStats, TEXT(" (hw vp)") ); else lstrcat( m_strDeviceStats, TEXT(" (simulated hw vp)") ); } else if( behaviorFlags & D3DCREATE_MIXED_VERTEXPROCESSING ) { if( pDeviceInfo->DevType == D3DDEVTYPE_HAL ) lstrcat( m_strDeviceStats, TEXT(" (mixed vp)") ); else lstrcat( m_strDeviceStats, TEXT(" (simulated mixed vp)") ); } else if( behaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING ) { lstrcat( m_strDeviceStats, TEXT(" (sw vp)") ); } if( pDeviceInfo->DevType == D3DDEVTYPE_HAL ) { // Be sure not to overflow m_strDeviceStats when appending the adapter // description, since it can be long. Note that the adapter description // is initially CHAR and must be converted to TCHAR. lstrcat( m_strDeviceStats, TEXT(": ") ); const int cchDesc = sizeof(pAdapterInfo->AdapterIdentifier.Description); TCHAR szDescription[cchDesc]; DXUtil_ConvertAnsiStringToGenericCch( szDescription, pAdapterInfo->AdapterIdentifier.Description, cchDesc ); int maxAppend = sizeof(m_strDeviceStats) / sizeof(TCHAR) - lstrlen( m_strDeviceStats ) - 1; _tcsncat( m_strDeviceStats, szDescription, maxAppend ); } // Store render target surface desc LPDIRECT3DSURFACE9 pBackBuffer = NULL; 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 = InitDeviceObjects(); if( FAILED(hr) ) { DeleteDeviceObjects(); } else { m_bDeviceObjectsInited = true; hr = RestoreDeviceObjects(); if( FAILED(hr) ) { InvalidateDeviceObjects(); } else { m_bDeviceObjectsRestored = true; return S_OK; } } // Cleanup before we try again Cleanup3DEnvironment(); } // If that failed, fall back to the reference rasterizer if( hr != D3DAPPERR_MEDIANOTFOUND && hr != HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) && pDeviceInfo->DevType == D3DDEVTYPE_HAL ) { if (FindBestWindowedMode(false, true)) { m_bWindowed = true; AdjustWindowForChange(); // Make sure main window isn't topmost, so error message is visible SetWindowPos( m_hWnd, HWND_NOTOPMOST, m_rcWindowBounds.left, m_rcWindowBounds.top, ( m_rcWindowBounds.right - m_rcWindowBounds.left ), ( m_rcWindowBounds.bottom - m_rcWindowBounds.top ), SWP_SHOWWINDOW ); // Let the user know we are switching from HAL to the reference rasterizer DisplayErrorMsg( hr, MSGWARN_SWITCHEDTOREF ); hr = Initialize3DEnvironment(); } } return hr; } //----------------------------------------------------------------------------- // Name: BuildPresentParamsFromSettings() // Desc: //----------------------------------------------------------------------------- void CD3DApplication::BuildPresentParamsFromSettings() { m_d3dpp.Windowed = m_d3dSettings.IsWindowed; m_d3dpp.BackBufferCount = 1; m_d3dpp.MultiSampleType = m_d3dSettings.MultisampleType(); m_d3dpp.MultiSampleQuality = m_d3dSettings.MultisampleQuality(); m_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; m_d3dpp.EnableAutoDepthStencil = m_d3dEnumeration.AppUsesDepthBuffer; m_d3dpp.hDeviceWindow = m_hWnd; if( m_d3dEnumeration.AppUsesDepthBuffer ) { m_d3dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL; m_d3dpp.AutoDepthStencilFormat = m_d3dSettings.DepthStencilBufferFormat(); } else { m_d3dpp.Flags = 0; } if( m_bWindowed ) { m_d3dpp.BackBufferWidth = m_rcWindowClient.right - m_rcWindowClient.left; m_d3dpp.BackBufferHeight = m_rcWindowClient.bottom - m_rcWindowClient.top; m_d3dpp.BackBufferFormat = m_d3dSettings.PDeviceCombo()->BackBufferFormat; m_d3dpp.FullScreen_RefreshRateInHz = 0; m_d3dpp.PresentationInterval = m_d3dSettings.PresentInterval(); } else { m_d3dpp.BackBufferWidth = m_d3dSettings.DisplayMode().Width; m_d3dpp.BackBufferHeight = m_d3dSettings.DisplayMode().Height; m_d3dpp.BackBufferFormat = m_d3dSettings.PDeviceCombo()->BackBufferFormat; m_d3dpp.FullScreen_RefreshRateInHz = m_d3dSettings.Fullscreen_DisplayMode.RefreshRate; m_d3dpp.PresentationInterval = m_d3dSettings.PresentInterval(); } } //----------------------------------------------------------------------------- // Name: Reset3DEnvironment() // Desc: //----------------------------------------------------------------------------- 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; } //----------------------------------------------------------------------------- // Name: ToggleFullScreen() // Desc: Called when user toggles between fullscreen mode and windowed mode //----------------------------------------------------------------------------- HRESULT CD3DApplication::ToggleFullscreen() { HRESULT hr; int AdapterOrdinalOld = m_d3dSettings.AdapterOrdinal(); D3DDEVTYPE DevTypeOld = m_d3dSettings.DevType(); Pause( true ); m_bIgnoreSizeChange = true; // Toggle the windowed state m_bWindowed = !m_bWindowed; m_d3dSettings.IsWindowed = m_bWindowed; // Prepare window for windowed/fullscreen change AdjustWindowForChange(); // If AdapterOrdinal and DevType are the same, we can just do a Reset(). // If they've changed, we need to do a complete device teardown/rebuild. if (m_d3dSettings.AdapterOrdinal() == AdapterOrdinalOld && m_d3dSettings.DevType() == DevTypeOld) { // Reset the 3D device BuildPresentParamsFromSettings(); hr = Reset3DEnvironment(); } else { Cleanup3DEnvironment(); hr = Initialize3DEnvironment(); } if( FAILED( hr ) ) { if( hr != D3DERR_OUTOFVIDEOMEMORY ) hr = D3DAPPERR_RESETFAILED; m_bIgnoreSizeChange = false; if( !m_bWindowed ) { // Restore window type to windowed mode m_bWindowed = !m_bWindowed; m_d3dSettings.IsWindowed = m_bWindowed; AdjustWindowForChange(); SetWindowPos( m_hWnd, HWND_NOTOPMOST, m_rcWindowBounds.left, m_rcWindowBounds.top, ( m_rcWindowBounds.right - m_rcWindowBounds.left ), ( m_rcWindowBounds.bottom - m_rcWindowBounds.top ), SWP_SHOWWINDOW ); } return DisplayErrorMsg( hr, MSGERR_APPMUSTEXIT ); } m_bIgnoreSizeChange = false; // When moving from fullscreen to windowed mode, it is important to // adjust the window size after resetting the device rather than // beforehand to ensure that you get the window size you want. For // example, when switching from 640x480 fullscreen to windowed with // a 1000x600 window on a 1024x768 desktop, it is impossible to set // the window size to 1000x600 until after the display mode has // changed to 1024x768, because windows cannot be larger than the // desktop. if( m_bWindowed ) { SetWindowPos( m_hWnd, HWND_NOTOPMOST, m_rcWindowBounds.left, m_rcWindowBounds.top, ( m_rcWindowBounds.right - m_rcWindowBounds.left ), ( m_rcWindowBounds.bottom - m_rcWindowBounds.top ), SWP_SHOWWINDOW ); } Pause( false ); return S_OK; } //----------------------------------------------------------------------------- // Name: ForceWindowed() // Desc: Switch to a windowed mode, even if that means picking a new device // and/or adapter //----------------------------------------------------------------------------- HRESULT CD3DApplication::ForceWindowed() { HRESULT hr; if( m_bWindowed ) return S_OK; if( !FindBestWindowedMode(false, false) ) { return E_FAIL; } m_bWindowed = true; // Now destroy the current 3D device objects, then reinitialize Pause( true ); // Release all scene objects that will be re-created for the new device Cleanup3DEnvironment(); // Create the new device if( FAILED(hr = Initialize3DEnvironment() ) ) return DisplayErrorMsg( hr, MSGERR_APPMUSTEXIT ); Pause( false ); return S_OK; } //----------------------------------------------------------------------------- // Name: AdjustWindowForChange() // Desc: Prepare the window for a possible change between windowed mode and // fullscreen mode. This function is virtual and thus can be overridden // to provide different behavior, such as switching to an entirely // different window for fullscreen mode (as in the MFC sample apps). //----------------------------------------------------------------------------- HRESULT CD3DApplication::AdjustWindowForChange() { if( m_bWindowed ) { // Set windowed-mode style SetWindowLong( m_hWnd, GWL_STYLE, m_dwWindowStyle ); if( m_hMenu != NULL ) { SetMenu( m_hWnd, m_hMenu ); m_hMenu = NULL; } } else { // Set fullscreen-mode style SetWindowLong( m_hWnd, GWL_STYLE, WS_POPUP|WS_SYSMENU|WS_VISIBLE ); if( m_hMenu == NULL ) { m_hMenu = GetMenu( m_hWnd ); SetMenu( m_hWnd, NULL ); } } return S_OK; } //----------------------------------------------------------------------------- // Name: UserSelectNewDevice() // Desc: Displays a dialog so the user can select a new adapter, device, or // display mode, and then recreates the 3D environment if needed //----------------------------------------------------------------------------- HRESULT CD3DApplication::UserSelectNewDevice() { HRESULT hr; // Can't display dialogs in fullscreen mode if( m_bWindowed == false ) { if( FAILED( ToggleFullscreen() ) ) { DisplayErrorMsg( D3DAPPERR_RESETFAILED, MSGERR_APPMUSTEXIT ); return E_FAIL; } } CD3DSettingsDialog settingsDialog( &m_d3dEnumeration, &m_d3dSettings); if( settingsDialog.ShowDialog( m_hWnd ) != IDOK ) return S_OK; settingsDialog.GetFinalSettings( &m_d3dSettings ); m_bWindowed = m_d3dSettings.IsWindowed; // Release all scene objects that will be re-created for the new device Cleanup3DEnvironment(); // Inform the display class of the change. It will internally // re-create valid surfaces, a d3ddevice, etc. if( FAILED( hr = Initialize3DEnvironment() ) ) { if( hr != D3DERR_OUTOFVIDEOMEMORY ) hr = D3DAPPERR_RESETFAILED; if( !m_bWindowed ) { // Restore window type to windowed mode m_bWindowed = !m_bWindowed; m_d3dSettings.IsWindowed = m_bWindowed; AdjustWindowForChange(); SetWindowPos( m_hWnd, HWND_NOTOPMOST, m_rcWindowBounds.left, m_rcWindowBounds.top, ( m_rcWindowBounds.right - m_rcWindowBounds.left ), ( m_rcWindowBounds.bottom - m_rcWindowBounds.top ), SWP_SHOWWINDOW ); } return DisplayErrorMsg( hr, MSGERR_APPMUSTEXIT ); } // 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; } //----------------------------------------------------------------------------- // Name: Run() // Desc: //----------------------------------------------------------------------------- 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; } //----------------------------------------------------------------------------- // Name: Render3DEnvironment() // Desc: Draws the scene. //----------------------------------------------------------------------------- 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; } //----------------------------------------------------------------------------- // Name: UpdateStats() // Desc: //----------------------------------------------------------------------------- void CD3DApplication::UpdateStats() { // Keep track of the frame count static FLOAT fLastTime = 0.0f; static DWORD dwFrames = 0; FLOAT fTime = DXUtil_Timer( TIMER_GETABSOLUTETIME ); ++dwFrames; // Update the scene stats once per second if( fTime - fLastTime > 1.0f ) { m_fFPS = dwFrames / (fTime - fLastTime); fLastTime = fTime; dwFrames = 0; TCHAR strFmt[100]; D3DFORMAT fmtAdapter = m_d3dSettings.DisplayMode().Format; if( fmtAdapter == m_d3dsdBackBuffer.Format ) { lstrcpyn( strFmt, D3DUtil_D3DFormatToString( fmtAdapter, false ), 100 ); } else { _sntprintf( strFmt, 100, TEXT("backbuf %s, adapter %s"), D3DUtil_D3DFormatToString( m_d3dsdBackBuffer.Format, false ), D3DUtil_D3DFormatToString( fmtAdapter, false ) ); } strFmt[99] = TEXT('\0'); TCHAR strDepthFmt[100]; if( m_d3dEnumeration.AppUsesDepthBuffer ) { _sntprintf( strDepthFmt, 100, TEXT(" (%s)"), D3DUtil_D3DFormatToString( m_d3dSettings.DepthStencilBufferFormat(), false ) ); strDepthFmt[99] = TEXT('\0'); } else { // No depth buffer strDepthFmt[0] = TEXT('\0'); } TCHAR* pstrMultiSample; switch( m_d3dSettings.MultisampleType() ) { case D3DMULTISAMPLE_NONMASKABLE: pstrMultiSample = TEXT(" (Nonmaskable Multisample)"); break; case D3DMULTISAMPLE_2_SAMPLES: pstrMultiSample = TEXT(" (2x Multisample)"); break; case D3DMULTISAMPLE_3_SAMPLES: pstrMultiSample = TEXT(" (3x Multisample)"); break; case D3DMULTISAMPLE_4_SAMPLES: pstrMultiSample = TEXT(" (4x Multisample)"); break; case D3DMULTISAMPLE_5_SAMPLES: pstrMultiSample = TEXT(" (5x Multisample)"); break; case D3DMULTISAMPLE_6_SAMPLES: pstrMultiSample = TEXT(" (6x Multisample)"); break; case D3DMULTISAMPLE_7_SAMPLES: pstrMultiSample = TEXT(" (7x Multisample)"); break; case D3DMULTISAMPLE_8_SAMPLES: pstrMultiSample = TEXT(" (8x Multisample)"); break; case D3DMULTISAMPLE_9_SAMPLES: pstrMultiSample = TEXT(" (9x Multisample)"); break; case D3DMULTISAMPLE_10_SAMPLES: pstrMultiSample = TEXT(" (10x Multisample)"); break; case D3DMULTISAMPLE_11_SAMPLES: pstrMultiSample = TEXT(" (11x Multisample)"); break; case D3DMULTISAMPLE_12_SAMPLES: pstrMultiSample = TEXT(" (12x Multisample)"); break; case D3DMULTISAMPLE_13_SAMPLES: pstrMultiSample = TEXT(" (13x Multisample)"); break; case D3DMULTISAMPLE_14_SAMPLES: pstrMultiSample = TEXT(" (14x Multisample)"); break; case D3DMULTISAMPLE_15_SAMPLES: pstrMultiSample = TEXT(" (15x Multisample)"); break; case D3DMULTISAMPLE_16_SAMPLES: pstrMultiSample = TEXT(" (16x Multisample)"); break; default: pstrMultiSample = TEXT(""); break; } const int cchMaxFrameStats = sizeof(m_strFrameStats) / sizeof(TCHAR); _sntprintf( m_strFrameStats, cchMaxFrameStats, _T("%.02f fps (%dx%d), %s%s%s"), m_fFPS, m_d3dsdBackBuffer.Width, m_d3dsdBackBuffer.Height, strFmt, strDepthFmt, pstrMultiSample ); m_strFrameStats[cchMaxFrameStats - 1] = TEXT('\0'); } } //----------------------------------------------------------------------------- // Name: Pause() // Desc: Called in to toggle the pause state of the app. //----------------------------------------------------------------------------- void CD3DApplication::Pause( bool bPause ) { static DWORD dwAppPausedCount = 0; dwAppPausedCount += ( bPause ? +1 : -1 ); m_bActive = ( dwAppPausedCount ? false : true ); // Handle the first pause request (of many, nestable pause requests) if( bPause && ( 1 == dwAppPausedCount ) ) { // Stop the scene from animating if( m_bFrameMoving ) DXUtil_Timer( TIMER_STOP ); } if( 0 == dwAppPausedCount ) { // Restart the timers if( m_bFrameMoving ) DXUtil_Timer( TIMER_START ); } } //----------------------------------------------------------------------------- // Name: Cleanup3DEnvironment() // Desc: Cleanup scene objects //----------------------------------------------------------------------------- void CD3DApplication::Cleanup3DEnvironment() { 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_APPMUSTEXIT ); m_pd3dDevice = NULL; } } //----------------------------------------------------------------------------- // Name: DisplayErrorMsg() // Desc: Displays error messages in a message box //----------------------------------------------------------------------------- HRESULT CD3DApplication::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. You may\n") _T("want to check that the latest version of\n") _T("DirectX is correctly installed on your\n") _T("system. Also make sure that this program\n") _T("was compiled with header files that match\n") _T("the installed DirectX DLLs.") ); break; case D3DAPPERR_NOCOMPATIBLEDEVICES: _tcscpy( strMsg, _T("Could not find any compatible Direct3D\n") _T("devices.") ); break; case D3DAPPERR_NOWINDOWABLEDEVICES: _tcscpy( strMsg, _T("This sample cannot run in a desktop\n") _T("window with the current display settings.\n") _T("Please change your desktop settings to a\n") _T("16- or 32-bit display mode and re-run this\n") _T("sample.") ); break; case D3DAPPERR_NOHARDWAREDEVICE: _tcscpy( strMsg, _T("No hardware-accelerated Direct3D devices\n") _T("were found.") ); break; case D3DAPPERR_HALNOTCOMPATIBLE: _tcscpy( strMsg, _T("This sample requires functionality that is\n") _T("not available on your Direct3D hardware\n") _T("accelerator.") ); break; case D3DAPPERR_NOWINDOWEDHAL: _tcscpy( strMsg, _T("Your Direct3D hardware accelerator cannot\n") _T("render into a window.\n") _T("Press F2 while the app is running to see a\n") _T("list of available devices and modes.") ); break; case D3DAPPERR_NODESKTOPHAL: _tcscpy( strMsg, _T("Your Direct3D hardware accelerator cannot\n") _T("render into a window with the current\n") _T("desktop display settings.\n") _T("Press F2 while the app is running to see a\n") _T("list of available devices and modes.") ); break; case D3DAPPERR_NOHALTHISMODE: _tcscpy( strMsg, _T("This sample requires functionality that is\n") _T("not available on your Direct3D hardware\n") _T("accelerator with the current desktop display\n") _T("settings.\n") _T("Press F2 while the app is running to see a\n") _T("list of available devices and modes.") ); 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_NONZEROREFCOUNT: _tcscpy( strMsg, _T("A D3D object has a non-zero reference\n") _T("count (meaning things were not properly\n") _T("cleaned up).") ); break; case D3DAPPERR_NULLREFDEVICE: _tcscpy( strMsg, _T("Warning: Nothing will be rendered.\n") _T("The reference rendering device was selected, but your\n") _T("computer only has a reduced-functionality reference device\n") _T("installed. Install the DirectX SDK to get the full\n") _T("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("Generic application error. Enable\n") _T("debug output for detailed information.") ); } if( MSGERR_APPMUSTEXIT == dwType ) { s_bFatalErrorReported = true; _tcscat( strMsg, _T("\n\nThis sample will now exit.") ); MessageBox( NULL, strMsg, m_strWindowTitle, MB_ICONERROR|MB_OK ); // Close the window, which shuts down the app if( m_hWnd ) SendMessage( m_hWnd, WM_CLOSE, 0, 0 ); } else { if( MSGWARN_SWITCHEDTOREF == dwType ) _tcscat( strMsg, _T("\n\nSwitching to the reference rasterizer,\n") _T("a software device that implements the entire\n") _T("Direct3D feature set, but runs very slowly.") ); MessageBox( NULL, strMsg, m_strWindowTitle, MB_ICONWARNING|MB_OK ); } return hr; }