//----------------------------------------------------------------------------- // File: D3DEnum.cpp // // Desc: Class enumerate through the DirectDraw drivers, Direct3D devices, // and the display modes available to each device. // // // Copyright (c) 1997-1998 Microsoft Corporation. All rights reserved //----------------------------------------------------------------------------- #include // HACK!!!!! #include #ifndef SM_CMONITORS #define SM_CMONITORS DECLARE_HANDLE(HMONITOR); #endif #include "D3DEnum.h" #include "D3DUtil.h" //----------------------------------------------------------------------------- // Constants and function prototypes for the user select driver dialog //----------------------------------------------------------------------------- DLGTEMPLATE* _BuildDriverSelectTemplate(); BOOL CALLBACK _DriverSelectProc( HWND, UINT, WPARAM, LPARAM ); //----------------------------------------------------------------------------- // Global data for the enumerator functions //----------------------------------------------------------------------------- static LPDIRECTDRAW4 g_pDD = NULL; // Used for callbacks static BOOL g_bRefRastEnumerated = FALSE; // For the reference rast static BOOL g_bDevicesEnumerated = FALSE; // Used during enumeration D3DEnum_DriverInfo* g_pFirstDriver = NULL; // List of DD drivers D3DEnum_DriverInfo* g_pDefaultDriver = NULL; // Default driver D3DEnum_DriverInfo* g_pCurrentDriver = NULL; // The selected DD driver static HRESULT (*g_fnAppConfirmFn)(DDCAPS*, D3DDEVICEDESC*) = NULL; //----------------------------------------------------------------------------- // Local callback functions used during enumeration //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Name: EnumDisplayModesCallback() // Desc: Callback function called for each display mode. Each available // display mode is added to a list for further choosing from the app. //----------------------------------------------------------------------------- static HRESULT WINAPI EnumDisplayModesCallback( DDSURFACEDESC2* pddsd, VOID* pvContext ) { // Check parameters if( NULL==pddsd || NULL==pvContext ) return DDENUMRET_CANCEL; D3DEnum_DeviceInfo* pDevice = (D3DEnum_DeviceInfo*)pvContext; D3DEnum_ModeInfo* pNewMode; DWORD dwBitDepth = pDevice->ddDesc.dwDeviceRenderBitDepth; DWORD dwModeDepth = pddsd->ddpfPixelFormat.dwRGBBitCount; // Check mode for compatability with device. Skip 8-bit modes. if( (32==dwModeDepth) && (0==(dwBitDepth&DDBD_32)) ) return DDENUMRET_OK; if( (24==dwModeDepth) && (0==(dwBitDepth&DDBD_24)) ) return DDENUMRET_OK; if( (16==dwModeDepth) && (0==(dwBitDepth&DDBD_16)) ) return DDENUMRET_OK; if( ( 8==dwModeDepth) ) return DDENUMRET_OK; // Create a new mode structure if( NULL == ( pNewMode = new D3DEnum_ModeInfo ) ) return DDENUMRET_CANCEL; // Initialize the new mode structure ZeroMemory( pNewMode, sizeof(D3DEnum_ModeInfo) ); memcpy( &pNewMode->ddsd, pddsd, sizeof(DDSURFACEDESC2) ); sprintf( pNewMode->strDesc, TEXT("%ld x %ld x %ld"), pddsd->dwWidth, pddsd->dwHeight, dwModeDepth ); // Link the new mode struct in the list of other display modes D3DEnum_ModeInfo** pMode = &pDevice->pFirstMode; while( *pMode ) pMode = &((*pMode)->pNext); (*pMode) = pNewMode; // If this is a 640x480x16 mode, save it as the default mode if( ( 640 == pddsd->dwWidth ) && ( 480 == pddsd->dwHeight ) && ( 16 == pddsd->ddpfPixelFormat.dwRGBBitCount ) ) pDevice->pCurrentMode = pNewMode; if( NULL == pDevice->pCurrentMode ) pDevice->pCurrentMode = pNewMode; return DDENUMRET_OK; } //----------------------------------------------------------------------------- // Name: Enum3DDevicesCallback() // Desc: Callback function called for each DirectX 3D device. The driver's // attributes are added to the list of available drivers. //----------------------------------------------------------------------------- static HRESULT WINAPI Enum3DDevicesCallback( GUID* pGUID, LPSTR strDesc, LPSTR strName, LPD3DDEVICEDESC pHALDesc, LPD3DDEVICEDESC pHELDesc, LPVOID pvContext ) { D3DEnum_DriverInfo* pDriver = (D3DEnum_DriverInfo*)pvContext; D3DEnum_DeviceInfo* pNewDevice; // Check params if( NULL==pGUID || NULL==pHALDesc || NULL==pHELDesc || NULL==pDriver ) return D3DENUMRET_CANCEL; // Handle specific device GUIDs. NullDevice renders nothing if( IsEqualGUID( *pGUID, IID_IDirect3DNullDevice ) ) return D3DENUMRET_OK; // Set a flag so we know enumeration is working. This is just a feature to // help return more informative return codes later on. g_bDevicesEnumerated = TRUE; // Get info about this device. BOOL bIsHardware = ( 0 != pHALDesc->dwFlags ); D3DDEVICEDESC* pDesc = bIsHardware ? pHALDesc : pHELDesc; // Only enumerate software rasterizers for the primary device (which has // a NULL GUID). This is to avoid duplicates if( pDriver->pGUID != NULL ) if( FALSE == bIsHardware ) return D3DENUMRET_OK; // Give the app a chance to accept or reject this device, based on // what feature set it supports if( g_fnAppConfirmFn ) if( FAILED( g_fnAppConfirmFn( &pDriver->ddDriverCaps, pDesc ) ) ) return D3DENUMRET_OK; // Create a new D3D Driver struct if( NULL == ( pNewDevice = new D3DEnum_DeviceInfo ) ) return D3DENUMRET_CANCEL; ZeroMemory( pNewDevice, sizeof(D3DEnum_DeviceInfo) ); // Copy remaining device attributes memcpy( &pNewDevice->guid, pGUID, sizeof(GUID) ); pNewDevice->pGUID = &pNewDevice->guid; strncpy( pNewDevice->strName, strName, 39 ); memcpy( &pNewDevice->ddDesc, pDesc, sizeof(D3DDEVICEDESC) ); pNewDevice->bIsHardware = bIsHardware; if( pNewDevice->bIsHardware ) pDriver->pCurrentDevice = pNewDevice; else { if( NULL == pDriver->pCurrentDevice ) if( D3DCOLOR_RGB & pHELDesc->dcmColorModel ) pDriver->pCurrentDevice = pNewDevice; } // Enumerate the display modes g_pDD->EnumDisplayModes( 0, NULL, pNewDevice, EnumDisplayModesCallback ); // Get the display mode's depth DDSURFACEDESC2 ddsd; ddsd.dwSize = sizeof(DDSURFACEDESC2); g_pDD->GetDisplayMode( &ddsd ); DWORD dwDisplayBPP = ddsd.ddpfPixelFormat.dwRGBBitCount; // Set the initial bWindowed flag if the device can render in a window and // supports the current display mode if( pDriver->ddDriverCaps.dwCaps2 & DDCAPS2_CANRENDERWINDOWED ) { for( D3DEnum_ModeInfo* pMode=pNewDevice->pFirstMode; pMode; pMode = pMode->pNext ) { if( pMode->ddsd.ddpfPixelFormat.dwRGBBitCount == dwDisplayBPP ) { pNewDevice->bCompatbileWithDesktop = TRUE; if( NULL == pDriver->pGUID ) pNewDevice->bWindowed = TRUE; } } } if( pNewDevice->pFirstMode ) { // Link it with the other D3D drivers in the DD Driver struct D3DEnum_DeviceInfo** pDevice = &pDriver->pFirstDevice; while( *pDevice ) pDevice = &((*pDevice)->pNext); (*pDevice) = pNewDevice; } else // Device has no modes, so delete it delete pNewDevice; if( IsEqualGUID( *pGUID, IID_IDirect3DRefDevice ) ) g_bRefRastEnumerated = TRUE; return D3DENUMRET_OK; } //----------------------------------------------------------------------------- // Name: DirectDrawEnumCallbackEx() // Desc: Callback function called for each DirectDraw driver. Unless we have // multimon or a type of card which uses a separate 2D card in // conjunction with the 3D card, this is only done once. //----------------------------------------------------------------------------- static BOOL WINAPI DirectDrawEnumCallbackEx( GUID FAR* pGUID, LPSTR strDesc, LPSTR strName, VOID*, HMONITOR hMonitor ) { // Use the GUID to create the DirectDraw object, so that information // can be extracted from it. LPDIRECTDRAW pDD; if( FAILED( DirectDrawCreate( pGUID, &pDD, 0L ) ) ) { DEBUG_MSG( TEXT("Can't create DDraw during enumeration!") ); return D3DENUMRET_OK; } // Query the DirectDraw driver for access to Direct3D. if( FAILED( pDD->QueryInterface( IID_IDirectDraw4, (VOID**)&g_pDD ) ) ) { DEBUG_MSG( TEXT("Can't query IDirectDraw4 during enumeration!") ); pDD->Release(); return D3DENUMRET_OK; } pDD->Release(); // Query the DirectDraw driver for access to Direct3D. LPDIRECT3D3 pD3D; if( FAILED( g_pDD->QueryInterface( IID_IDirect3D3, (VOID**)&pD3D ) ) ) { DEBUG_MSG( TEXT("Can't query IDirect3D3 during enumeration!") ); g_pDD->Release(); return D3DENUMRET_OK; } // Copy the DDDriver info into a new DriverInfo struct D3DEnum_DriverInfo* pNewDriver = new D3DEnum_DriverInfo; if( NULL == pNewDriver ) return D3DENUMRET_CANCEL; ZeroMemory( pNewDriver, sizeof(D3DEnum_DriverInfo) ); // Copy the GUID (if specified) and the driver name if( NULL != pGUID ) { memcpy( &pNewDriver->guid, pGUID, sizeof(GUID) ); pNewDriver->pGUID = &pNewDriver->guid; } strncpy( pNewDriver->strDesc, strDesc, 39 ); strncpy( pNewDriver->strName, strName, 39 ); pNewDriver->hMonitor = hMonitor; // Get the caps bits for the driver pNewDriver->ddDriverCaps.dwSize = sizeof(DDCAPS); pNewDriver->ddHELCaps.dwSize = sizeof(DDCAPS); g_pDD->GetCaps( &pNewDriver->ddDriverCaps, &pNewDriver->ddHELCaps ); // Now, enumerate all the 3D devices pD3D->EnumDevices( Enum3DDevicesCallback, pNewDriver ); if( pNewDriver->pFirstDevice ) { // Link the new DDDriver with the global list D3DEnum_DriverInfo** pDriver = &g_pFirstDriver; while( *pDriver ) pDriver = &((*pDriver)->pNext); (*pDriver) = pNewDriver; // Decide if this is a good default driver if( NULL == pGUID ) g_pCurrentDriver = pNewDriver; } else // Driver has no devices, so delete it delete pNewDriver; pD3D->Release(); g_pDD->Release(); return DDENUMRET_OK; } //----------------------------------------------------------------------------- // Name: DirectDrawEnumCallback() // Desc: Non-mulitmon version of the ddraw enumeration callback //----------------------------------------------------------------------------- static BOOL WINAPI DirectDrawEnumCallback( GUID FAR* pGUID, LPSTR strDesc, LPSTR strName, VOID* ) { return DirectDrawEnumCallbackEx( pGUID, strDesc, strName, NULL, NULL ); } //----------------------------------------------------------------------------- // Name: D3DEnum_FreeResources() // Desc: Frees all resources used for driver enumeration //----------------------------------------------------------------------------- VOID D3DEnum_FreeResources() { // Loop through each driver, and delete everything while( g_pFirstDriver ) { D3DEnum_DriverInfo* pDriverVictim = g_pFirstDriver; g_pFirstDriver = g_pFirstDriver->pNext; while( pDriverVictim->pFirstDevice ) { D3DEnum_DeviceInfo* pDeviceVictim = pDriverVictim->pFirstDevice; pDriverVictim->pFirstDevice = pDeviceVictim->pNext; while( pDeviceVictim->pFirstMode ) { D3DEnum_ModeInfo* pModeVictim = pDeviceVictim->pFirstMode; pDeviceVictim->pFirstMode = pModeVictim->pNext; delete pModeVictim; } delete pDeviceVictim; } delete pDriverVictim; } } //----------------------------------------------------------------------------- // Name: RefreshListForDesktopCompatibility() // Desc: Loops through list of devices, and marks a flag for whether the device // is compatible with the desktop bit depth. //----------------------------------------------------------------------------- static VOID RefreshListForDesktopCompatibility() { // Get the currect display mode description LPDIRECTDRAW pDD; DDSURFACEDESC ddsd; if( FAILED( DirectDrawCreate( NULL, &pDD, NULL ) ) ) return; ddsd.dwSize = sizeof(DDSURFACEDESC); pDD->GetDisplayMode( &ddsd ); pDD->Release(); // Get the display mode's depth DWORD dwDisplayBPP = ddsd.ddpfPixelFormat.dwRGBBitCount; // Loop through the devices, and check if any modes works with the current // display setting for( D3DEnum_DriverInfo* pDriver = g_pFirstDriver; pDriver; pDriver = pDriver->pNext ) { for( D3DEnum_DeviceInfo* pDevice=pDriver->pFirstDevice; pDevice; pDevice = pDevice->pNext ) { pDevice->bCompatbileWithDesktop = FALSE; for( D3DEnum_ModeInfo* pMode=pDevice->pFirstMode; pMode; pMode = pMode->pNext ) { if( pMode->ddsd.ddpfPixelFormat.dwRGBBitCount == dwDisplayBPP ) pDevice->bCompatbileWithDesktop = TRUE; } } } } //----------------------------------------------------------------------------- // Name: D3DEnum_EnumerateDevices() // Desc: Enumerates all drivers, devices, and modes. The optional app-supplied // callback is called for each enumerated device, to confirm that the // device supports the feature set required by the app. //----------------------------------------------------------------------------- HRESULT D3DEnum_EnumerateDevices( HRESULT (*AppConfirmFn)(DDCAPS*, D3DDEVICEDESC*) ) { g_fnAppConfirmFn = AppConfirmFn; g_bRefRastEnumerated = FALSE; // We need to manually get the procedure address for the DDrawEnumEx() // function. HMODULE hDDrawDLL = GetModuleHandle("DDRAW.DLL"); if( NULL == hDDrawDLL ) { DEBUG_MSG( TEXT("Can't load DDRAW.DLL!") ); return D3DENUMERR_NODIRECTDRAW; } // Find the DDraw enumeration function, and call it LPDIRECTDRAWENUMERATEEX pDDrawEnumFn = (LPDIRECTDRAWENUMERATEEX) GetProcAddress( hDDrawDLL, "DirectDrawEnumerateExA" ); if( pDDrawEnumFn ) pDDrawEnumFn( DirectDrawEnumCallbackEx, NULL, DDENUM_ATTACHEDSECONDARYDEVICES | DDENUM_DETACHEDSECONDARYDEVICES | DDENUM_NONDISPLAYDEVICES ); else DirectDrawEnumerate( DirectDrawEnumCallback, NULL ); // Select a driver. Ask for a hardware device that renders in a window return D3DEnum_SelectDefaultDriver( NULL ); } //----------------------------------------------------------------------------- // Name: D3DEnum_SelectDefaultDriver() // Desc: Picks a default driver according to the passed in flags. //----------------------------------------------------------------------------- HRESULT D3DEnum_SelectDefaultDriver( DWORD dwFlags ) { // Refresh the list of devices to mark which devices (if any) are // compatible with the current desktop (ie. can render in a window). RefreshListForDesktopCompatibility(); // If a specific driver was requested, perform that search here if( dwFlags & 0x0000003c ) { for( D3DEnum_DriverInfo* pDriver = g_pFirstDriver; pDriver; pDriver = pDriver->pNext ) { for( D3DEnum_DeviceInfo* pDevice = pDriver->pFirstDevice; pDevice; pDevice = pDevice->pNext ) { BOOL bFound = FALSE; if( IsEqualGUID( *pDevice->pGUID, IID_IDirect3DRGBDevice ) ) { if( dwFlags & D3DENUM_RGBEMULATION ) bFound = TRUE; } else if( IsEqualGUID( *pDevice->pGUID, IID_IDirect3DRefDevice ) ) { if( dwFlags & D3DENUM_REFERENCERAST ) bFound = TRUE; } else { if( dwFlags & D3DENUM_PRIMARYHAL ) if( pDriver == g_pFirstDriver ) bFound = TRUE; if( dwFlags & D3DENUM_SECONDARYHAL ) if( pDriver != g_pFirstDriver ) bFound = TRUE; } if( bFound ) { g_pCurrentDriver = pDriver; g_pCurrentDriver->pCurrentDevice = pDevice; return S_OK; } } } return D3DENUMERR_NOTFOUND; } // Do 4 passes, looping through drivers, devices and modes. The 1st pass // searches for hardware. The 2nd pass looks for software devices. The // final two passes allow fullscreen modes. for( WORD pass=0; pass<4; pass++ ) { BOOL bSeekHardware = ( pass & 0x1 ) ? FALSE : TRUE; BOOL bSeekWindowed = ( pass & 0x2 ) ? FALSE : TRUE; // Skip the passes we aren't allowing if( (TRUE==bSeekHardware) && (dwFlags&D3DENUM_SOFTWAREONLY) ) continue; if( (TRUE==bSeekWindowed) && (dwFlags&D3DENUM_FULLSCREENONLY) ) continue; for( D3DEnum_DriverInfo* pDriver = g_pFirstDriver; pDriver; pDriver = pDriver->pNext ) { DDCAPS* pCaps = &pDriver->ddDriverCaps; if( bSeekWindowed ) if( 0 == ( pCaps->dwCaps2 & DDCAPS2_CANRENDERWINDOWED ) ) continue; for( D3DEnum_DeviceInfo* pDevice = pDriver->pFirstDevice; pDevice; pDevice = pDevice->pNext ) { if( bSeekHardware != pDevice->bIsHardware ) continue; if( bSeekWindowed && FALSE == pDevice->bCompatbileWithDesktop ) continue; pDevice->bWindowed = bSeekWindowed; g_pCurrentDriver = pDriver; g_pCurrentDriver->pCurrentDevice = pDevice; return S_OK; } } } // No compatible devices were found. Return an error code if( FALSE == g_bDevicesEnumerated ) return D3DENUMERR_ENUMERATIONFAILED; // Enumeration really did fail if( FALSE == g_bRefRastEnumerated ) return D3DENUMERR_SUGGESTREFRAST; // Suggest enabling the RefRast return D3DENUMERR_NOCOMPATIBLEDEVICES; } //----------------------------------------------------------------------------- // Name: D3DEnum_UserDlgSelectDriver() // Desc: Displays a dialog box for the user to select a driver/device/mode. // The return values are akin to the Windows DialogBox() function. //----------------------------------------------------------------------------- INT D3DEnum_UserDlgSelectDriver( HWND hwndParent, BOOL bCurrentlyWindowed ) { INT nResult = -1; // Check in case drivers weren't properly enumerated beforehand. if( NULL == g_pCurrentDriver ) return -1; // Refresh the list of devices to mark which devices (if any) are // compatible with the current desktop (ie. can render in a window). RefreshListForDesktopCompatibility(); // Match the current windowed-vs-fullscreen state g_pCurrentDriver->pCurrentDevice->bWindowed = bCurrentlyWindowed; // Pop up a dialog box for the user's choice of driver/device/mode HINSTANCE hInstance = (HINSTANCE)GetWindowLong( hwndParent, GWL_HINSTANCE ); // Create dynamic dialog template DLGTEMPLATE* pDlgSelect = _BuildDriverSelectTemplate(); if( pDlgSelect ) { // Create dialog box from template nResult = DialogBoxIndirectParam( hInstance, pDlgSelect, hwndParent, (DLGPROC)_DriverSelectProc, 0L ); delete pDlgSelect; } return nResult; } //----------------------------------------------------------------------------- // Name: D3DEnum_GetSelectedDriver() // Desc: Returns the currently selected driver, device, and display mode //----------------------------------------------------------------------------- HRESULT D3DEnum_GetSelectedDriver( LPGUID* ppDriverGUID, LPGUID* ppDeviceGUID, LPDDSURFACEDESC2* ppddsd, BOOL* pbWindowed, BOOL* pbIsHardware ) { // Check parans if( (!ppDriverGUID) || (!ppDeviceGUID) ) return E_INVALIDARG; // Abort if things weren't setup correctly if( NULL == g_pCurrentDriver ) return D3DENUMERR_ENUMERATIONFAILED; // Copy the driver and device GUID ptrs (*ppDriverGUID) = g_pCurrentDriver->pGUID; (*ppDeviceGUID) = g_pCurrentDriver->pCurrentDevice->pGUID; if( ppddsd ) (*ppddsd) = &g_pCurrentDriver->pCurrentDevice->pCurrentMode->ddsd; if( pbWindowed ) (*pbWindowed) = g_pCurrentDriver->pCurrentDevice->bWindowed; if( pbIsHardware ) (*pbIsHardware) = g_pCurrentDriver->pCurrentDevice->bIsHardware; return S_OK; } //----------------------------------------------------------------------------- // Name: D3DEnum_GetSelectedDriver() // Desc: Returns the currently selected driver, device, and display mode //----------------------------------------------------------------------------- HRESULT D3DEnum_GetSelectedDriver( D3DEnum_DriverInfo** ppDriverInfo, D3DEnum_DeviceInfo** ppDeviceInfo ) { // Abort if things weren't setup correctly if( NULL == g_pCurrentDriver ) return D3DENUMERR_ENUMERATIONFAILED; // Copy the driver and device info ptrs if( ppDriverInfo ) *ppDriverInfo = g_pCurrentDriver; if( ppDeviceInfo ) *ppDeviceInfo = g_pCurrentDriver->pCurrentDevice; return S_OK; } //----------------------------------------------------------------------------- // Name: D3DEnum_GetFirstDriver() // Desc: Returns a ptr to the first DriverInfo structure in the list. //----------------------------------------------------------------------------- D3DEnum_DriverInfo* D3DEnum_GetFirstDriver() { return g_pFirstDriver; }