//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #define DISABLE_PROTECTED_THINGS #include "togl/rendermechanism.h" #include "shaderdevicebase.h" #include "tier1/KeyValues.h" #include "tier1/convar.h" #include "tier1/utlbuffer.h" #include "tier0/icommandline.h" #include "tier2/tier2.h" #include "filesystem.h" #include "datacache/idatacache.h" #include "shaderapi/ishaderutil.h" #include "shaderapibase.h" #include "shaderapi/ishadershadow.h" #include "shaderapi_global.h" #include "winutils.h" #ifdef _X360 #include "xbox/xbox_win32stubs.h" #endif //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- IShaderUtil* g_pShaderUtil; // The main shader utility interface CShaderDeviceBase *g_pShaderDevice; CShaderDeviceMgrBase *g_pShaderDeviceMgr; CShaderAPIBase *g_pShaderAPI; IShaderShadow *g_pShaderShadow; bool g_bUseShaderMutex = false; // Shader mutex globals bool g_bShaderAccessDisallowed; CShaderMutex g_ShaderMutex; //----------------------------------------------------------------------------- // FIXME: Hack related to setting command-line values for convars. Remove!!! //----------------------------------------------------------------------------- class CShaderAPIConVarAccessor : public IConCommandBaseAccessor { public: virtual bool RegisterConCommandBase( ConCommandBase *pCommand ) { // Link to engine's list instead g_pCVar->RegisterConCommand( pCommand ); char const *pValue = g_pCVar->GetCommandLineValue( pCommand->GetName() ); if( pValue && !pCommand->IsCommand() ) { ( ( ConVar * )pCommand )->SetValue( pValue ); } return true; } }; static void InitShaderAPICVars( ) { static CShaderAPIConVarAccessor g_ConVarAccessor; if ( g_pCVar ) { ConVar_Register( FCVAR_MATERIAL_SYSTEM_THREAD, &g_ConVarAccessor ); } } //----------------------------------------------------------------------------- // Read dx support levels //----------------------------------------------------------------------------- #if defined( DX_TO_GL_ABSTRACTION ) #if defined( OSX ) // OSX #define SUPPORT_CFG_FILE "dxsupport_mac.cfg" // TODO: make this different for Mac? #define SUPPORT_CFG_OVERRIDE_FILE "dxsupport_override.cfg" #else // Linux/Win GL #define SUPPORT_CFG_FILE "dxsupport_linux.cfg" // TODO: make this different for Linux? #define SUPPORT_CFG_OVERRIDE_FILE "dxsupport_override.cfg" #endif #else // D3D #define SUPPORT_CFG_FILE "dxsupport.cfg" #define SUPPORT_CFG_OVERRIDE_FILE "dxsupport_override.cfg" #endif //----------------------------------------------------------------------------- // constructor, destructor //----------------------------------------------------------------------------- CShaderDeviceMgrBase::CShaderDeviceMgrBase() { m_pDXSupport = NULL; } CShaderDeviceMgrBase::~CShaderDeviceMgrBase() { } //----------------------------------------------------------------------------- // Factory used to get at internal interfaces (used by shaderapi + shader dlls) //----------------------------------------------------------------------------- static CreateInterfaceFn s_TempFactory; void *ShaderDeviceFactory( const char *pName, int *pReturnCode ) { if (pReturnCode) { *pReturnCode = IFACE_OK; } void *pInterface = s_TempFactory( pName, pReturnCode ); if ( pInterface ) return pInterface; pInterface = Sys_GetFactoryThis()( pName, pReturnCode ); if ( pInterface ) return pInterface; if ( pReturnCode ) { *pReturnCode = IFACE_FAILED; } return NULL; } //----------------------------------------------------------------------------- // Connect, disconnect //----------------------------------------------------------------------------- bool CShaderDeviceMgrBase::Connect( CreateInterfaceFn factory ) { LOCK_SHADERAPI(); Assert( !g_pShaderDeviceMgr ); s_TempFactory = factory; // Connection/convar registration CreateInterfaceFn actualFactory = ShaderDeviceFactory; ConnectTier1Libraries( &actualFactory, 1 ); InitShaderAPICVars(); ConnectTier2Libraries( &actualFactory, 1 ); g_pShaderUtil = (IShaderUtil*)ShaderDeviceFactory( SHADER_UTIL_INTERFACE_VERSION, NULL ); g_pShaderDeviceMgr = this; s_TempFactory = NULL; if ( !g_pShaderUtil || !g_pFullFileSystem || !g_pShaderDeviceMgr ) { Warning( "ShaderAPIDx10 was unable to access the required interfaces!\n" ); return false; } // NOTE! : Overbright is 1.0 so that Hammer will work properly with the white bumped and unbumped lightmaps. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f ); return true; } void CShaderDeviceMgrBase::Disconnect() { LOCK_SHADERAPI(); g_pShaderDeviceMgr = NULL; g_pShaderUtil = NULL; DisconnectTier2Libraries(); ConVar_Unregister(); DisconnectTier1Libraries(); if ( m_pDXSupport ) { m_pDXSupport->deleteThis(); m_pDXSupport = NULL; } } //----------------------------------------------------------------------------- // Query interface //----------------------------------------------------------------------------- void *CShaderDeviceMgrBase::QueryInterface( const char *pInterfaceName ) { if ( !Q_stricmp( pInterfaceName, SHADER_DEVICE_MGR_INTERFACE_VERSION ) ) return ( IShaderDeviceMgr* )this; if ( !Q_stricmp( pInterfaceName, MATERIALSYSTEM_HARDWARECONFIG_INTERFACE_VERSION ) ) return ( IMaterialSystemHardwareConfig* )g_pHardwareConfig; return NULL; } //----------------------------------------------------------------------------- // Returns the hardware caps for a particular adapter //----------------------------------------------------------------------------- const HardwareCaps_t& CShaderDeviceMgrBase::GetHardwareCaps( int nAdapter ) const { Assert( ( nAdapter >= 0 ) && ( nAdapter < GetAdapterCount() ) ); return m_Adapters[nAdapter].m_ActualCaps; } //----------------------------------------------------------------------------- // Utility methods for reading config scripts //----------------------------------------------------------------------------- static inline int ReadHexValue( KeyValues *pVal, const char *pName ) { const char *pString = pVal->GetString( pName, NULL ); if (!pString) { return -1; } char *pTemp; int nVal = strtol( pString, &pTemp, 16 ); return (pTemp != pString) ? nVal : -1; } static bool ReadBool( KeyValues *pGroup, const char *pKeyName, bool bDefault ) { int nVal = pGroup->GetInt( pKeyName, -1 ); if ( nVal != -1 ) { // Warning( "\t%s = %s\n", pKeyName, (nVal != false) ? "true" : "false" ); return (nVal != false); } return bDefault; } static void ReadInt( KeyValues *pGroup, const char *pKeyName, int nInvalidValue, int *pResult ) { int nVal = pGroup->GetInt( pKeyName, nInvalidValue ); if ( nVal != nInvalidValue ) { *pResult = nVal; // Warning( "\t%s = %d\n", pKeyName, *pResult ); } } //----------------------------------------------------------------------------- // Utility method to copy over a keyvalue //----------------------------------------------------------------------------- static void AddKey( KeyValues *pDest, KeyValues *pSrc ) { // Note this will replace already-existing values switch( pSrc->GetDataType() ) { case KeyValues::TYPE_NONE: break; case KeyValues::TYPE_STRING: pDest->SetString( pSrc->GetName(), pSrc->GetString() ); break; case KeyValues::TYPE_INT: pDest->SetInt( pSrc->GetName(), pSrc->GetInt() ); break; case KeyValues::TYPE_FLOAT: pDest->SetFloat( pSrc->GetName(), pSrc->GetFloat() ); break; case KeyValues::TYPE_PTR: pDest->SetPtr( pSrc->GetName(), pSrc->GetPtr() ); break; case KeyValues::TYPE_WSTRING: pDest->SetWString( pSrc->GetName(), pSrc->GetWString() ); break; case KeyValues::TYPE_COLOR: pDest->SetColor( pSrc->GetName(), pSrc->GetColor() ); break; default: Assert( 0 ); break; } } //----------------------------------------------------------------------------- // Finds if we have a dxlevel-specific config in the support keyvalues //----------------------------------------------------------------------------- KeyValues *CShaderDeviceMgrBase::FindDXLevelSpecificConfig( KeyValues *pKeyValues, int nDxLevel ) { KeyValues *pGroup = pKeyValues->GetFirstSubKey(); for( pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) { int nFoundDxLevel = pGroup->GetInt( "name", 0 ); if( nFoundDxLevel == nDxLevel ) return pGroup; } return NULL; } //----------------------------------------------------------------------------- // Finds if we have a dxlevel and vendor-specific config in the support keyvalues //----------------------------------------------------------------------------- KeyValues *CShaderDeviceMgrBase::FindDXLevelAndVendorSpecificConfig( KeyValues *pKeyValues, int nDxLevel, int nVendorID ) { if ( IsX360() ) { // 360 unique dxlevel implies hw config, vendor variance not applicable return NULL; } KeyValues *pGroup = pKeyValues->GetFirstSubKey(); for( pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) { int nFoundDxLevel = pGroup->GetInt( "name", 0 ); int nFoundVendorID = ReadHexValue( pGroup, "VendorID" ); if( nFoundDxLevel == nDxLevel && nFoundVendorID == nVendorID ) return pGroup; } return NULL; } //----------------------------------------------------------------------------- // Finds if we have a vendor-specific config in the support keyvalues //----------------------------------------------------------------------------- KeyValues *CShaderDeviceMgrBase::FindCPUSpecificConfig( KeyValues *pKeyValues, int nCPUMhz, bool bAMD ) { if ( IsX360() ) { // 360 unique dxlevel implies hw config, cpu variance not applicable return NULL; } for( KeyValues *pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) { const char *pName = pGroup->GetString( "name", NULL ); if ( !pName ) continue; if ( ( bAMD && Q_stristr( pName, "AMD" ) ) || ( !bAMD && Q_stristr( pName, "Intel" ) ) ) { int nMinMegahertz = pGroup->GetInt( "min megahertz", -1 ); int nMaxMegahertz = pGroup->GetInt( "max megahertz", -1 ); if( nMinMegahertz == -1 || nMaxMegahertz == -1 ) continue; if( nMinMegahertz <= nCPUMhz && nCPUMhz < nMaxMegahertz ) return pGroup; } } return NULL; } //----------------------------------------------------------------------------- // Finds if we have a vendor-specific config in the support keyvalues //----------------------------------------------------------------------------- KeyValues *CShaderDeviceMgrBase::FindCardSpecificConfig( KeyValues *pKeyValues, int nVendorId, int nDeviceId ) { if ( IsX360() ) { // 360 unique dxlevel implies hw config, vendor variance not applicable return NULL; } KeyValues *pGroup = pKeyValues->GetFirstSubKey(); for( pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) { int nFoundVendorId = ReadHexValue( pGroup, "VendorID" ); int nFoundDeviceIdMin = ReadHexValue( pGroup, "MinDeviceID" ); int nFoundDeviceIdMax = ReadHexValue( pGroup, "MaxDeviceID" ); if ( nFoundVendorId == nVendorId && nDeviceId >= nFoundDeviceIdMin && nDeviceId <= nFoundDeviceIdMax ) return pGroup; } return NULL; } //----------------------------------------------------------------------------- // Finds if we have a vendor-specific config in the support keyvalues //----------------------------------------------------------------------------- KeyValues *CShaderDeviceMgrBase::FindMemorySpecificConfig( KeyValues *pKeyValues, int nSystemRamMB ) { if ( IsX360() ) { // 360 unique dxlevel implies hw config, memory variance not applicable return NULL; } for( KeyValues *pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) { // Used to help us debug this code // const char *pDebugName = pGroup->GetString( "name", "blah" ); int nMinMB = pGroup->GetInt( "min megabytes", -1 ); int nMaxMB = pGroup->GetInt( "max megabytes", -1 ); if ( nMinMB == -1 || nMaxMB == -1 ) continue; if ( nMinMB <= nSystemRamMB && nSystemRamMB < nMaxMB ) return pGroup; } return NULL; } //----------------------------------------------------------------------------- // Finds if we have a texture mem size specific config //----------------------------------------------------------------------------- KeyValues *CShaderDeviceMgrBase::FindVidMemSpecificConfig( KeyValues *pKeyValues, int nVideoRamMB ) { if ( IsX360() ) { // 360 unique dxlevel implies hw config, vidmem variance not applicable return NULL; } for( KeyValues *pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) { int nMinMB = pGroup->GetInt( "min megatexels", -1 ); int nMaxMB = pGroup->GetInt( "max megatexels", -1 ); if ( nMinMB == -1 || nMaxMB == -1 ) continue; if ( nMinMB <= nVideoRamMB && nVideoRamMB < nMaxMB ) return pGroup; } return NULL; } //----------------------------------------------------------------------------- // Methods related to reading DX support levels given particular devices //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Reads in the dxsupport.cfg keyvalues //----------------------------------------------------------------------------- static void OverrideValues_R( KeyValues *pDest, KeyValues *pSrc ) { // Any same-named values get overridden in pDest. for ( KeyValues *pSrcValue=pSrc->GetFirstValue(); pSrcValue; pSrcValue=pSrcValue->GetNextValue() ) { // Shouldn't be a container for more keys. Assert( pSrcValue->GetDataType() != KeyValues::TYPE_NONE ); AddKey( pDest, pSrcValue ); } // Recurse. for ( KeyValues *pSrcDir=pSrc->GetFirstTrueSubKey(); pSrcDir; pSrcDir=pSrcDir->GetNextTrueSubKey() ) { Assert( pSrcDir->GetDataType() == KeyValues::TYPE_NONE ); KeyValues *pDestDir = pDest->FindKey( pSrcDir->GetName() ); if ( pDestDir && pDestDir->GetDataType() == KeyValues::TYPE_NONE ) { OverrideValues_R( pDestDir, pSrcDir ); } } } static KeyValues * FindMatchingGroup( KeyValues *pSrc, KeyValues *pMatch ) { KeyValues *pMatchSubKey = pMatch->FindKey( "name" ); bool bHasSubKey = ( pMatchSubKey && ( pMatchSubKey->GetDataType() != KeyValues::TYPE_NONE ) ); const char *name = bHasSubKey ? pMatchSubKey->GetString() : NULL; int nMatchVendorID = ReadHexValue( pMatch, "VendorID" ); int nMatchMinDeviceID = ReadHexValue( pMatch, "MinDeviceID" ); int nMatchMaxDeviceID = ReadHexValue( pMatch, "MaxDeviceID" ); KeyValues *pSrcGroup = NULL; for ( pSrcGroup = pSrc->GetFirstTrueSubKey(); pSrcGroup; pSrcGroup = pSrcGroup->GetNextTrueSubKey() ) { if ( name ) { KeyValues *pSrcGroupName = pSrcGroup->FindKey( "name" ); Assert( pSrcGroupName ); Assert( pSrcGroupName->GetDataType() != KeyValues::TYPE_NONE ); if ( Q_stricmp( pSrcGroupName->GetString(), name ) ) continue; } if ( nMatchVendorID >= 0 ) { int nVendorID = ReadHexValue( pSrcGroup, "VendorID" ); if ( nMatchVendorID != nVendorID ) continue; } if ( nMatchMinDeviceID >= 0 && nMatchMaxDeviceID >= 0 ) { int nMinDeviceID = ReadHexValue( pSrcGroup, "MinDeviceID" ); int nMaxDeviceID = ReadHexValue( pSrcGroup, "MaxDeviceID" ); if ( nMinDeviceID < 0 || nMaxDeviceID < 0 ) continue; if ( nMatchMinDeviceID > nMinDeviceID || nMatchMaxDeviceID < nMaxDeviceID ) continue; } return pSrcGroup; } return NULL; } static void OverrideKeyValues( KeyValues *pDst, KeyValues *pSrc ) { KeyValues *pSrcGroup = NULL; for ( pSrcGroup = pSrc->GetFirstTrueSubKey(); pSrcGroup; pSrcGroup = pSrcGroup->GetNextTrueSubKey() ) { // Match each group in pSrc to one in pDst containing the same "name" value: KeyValues * pDstGroup = FindMatchingGroup( pDst, pSrcGroup ); //Assert( pDstGroup ); if ( pDstGroup ) { OverrideValues_R( pDstGroup, pSrcGroup ); } } // if( CommandLine()->FindParm( "-debugdxsupport" ) ) // { // CUtlBuffer tmpBuf; // pDst->RecursiveSaveToFile( tmpBuf, 0 ); // g_pFullFileSystem->WriteFile( "gary.txt", NULL, tmpBuf ); // } } KeyValues *CShaderDeviceMgrBase::ReadDXSupportKeyValues() { if ( CommandLine()->CheckParm( "-ignoredxsupportcfg" ) ) return NULL; if ( m_pDXSupport ) return m_pDXSupport; KeyValues *pCfg = new KeyValues( "dxsupport" ); const char *pPathID = "EXECUTABLE_PATH"; if ( IsX360() && g_pFullFileSystem->GetDVDMode() == DVDMODE_STRICT ) { // 360 dvd optimzation, expect it inside the platform zip pPathID = "PLATFORM"; } // First try to read a game-specific config, if it exists if ( !pCfg->LoadFromFile( g_pFullFileSystem, SUPPORT_CFG_FILE, pPathID ) ) { pCfg->deleteThis(); return NULL; } char pTempPath[1024]; if ( g_pFullFileSystem->GetSearchPath( "GAME", false, pTempPath, sizeof(pTempPath) ) > 1 ) { // Is there a mod-specific override file? KeyValues *pOverride = new KeyValues( "dxsupport_override" ); if ( pOverride->LoadFromFile( g_pFullFileSystem, SUPPORT_CFG_OVERRIDE_FILE, "GAME" ) ) { OverrideKeyValues( pCfg, pOverride ); } pOverride->deleteThis(); } m_pDXSupport = pCfg; return pCfg; } //----------------------------------------------------------------------------- // Returns the max dx support level achievable with this board //----------------------------------------------------------------------------- void CShaderDeviceMgrBase::ReadDXSupportLevels( HardwareCaps_t &caps ) { // See if the file tells us otherwise KeyValues *pCfg = ReadDXSupportKeyValues(); if ( !pCfg ) return; KeyValues *pDeviceKeyValues = FindCardSpecificConfig( pCfg, caps.m_VendorID, caps.m_DeviceID ); if ( pDeviceKeyValues ) { // First, set the max dx level int nMaxDXSupportLevel = 0; ReadInt( pDeviceKeyValues, "MaxDXLevel", 0, &nMaxDXSupportLevel ); if ( nMaxDXSupportLevel != 0 ) { caps.m_nMaxDXSupportLevel = nMaxDXSupportLevel; } // Next, set the preferred dx level int nDXSupportLevel = 0; ReadInt( pDeviceKeyValues, "DXLevel", 0, &nDXSupportLevel ); if ( nDXSupportLevel != 0 ) { caps.m_nDXSupportLevel = nDXSupportLevel; // Don't slam up the dxlevel level to 92 on DX10 cards in OpenGL Linux/Win mode (otherwise Intel will get dxlevel 92 when we want 90) if ( !( IsOpenGL() && ( IsLinux() || IsWindows() ) ) ) { if ( caps.m_bDX10Card ) { caps.m_nDXSupportLevel = 92; } } } else { caps.m_nDXSupportLevel = caps.m_nMaxDXSupportLevel; } } } //----------------------------------------------------------------------------- // Loads the hardware caps, for cases in which the D3D caps lie or where we need to augment the caps //----------------------------------------------------------------------------- void CShaderDeviceMgrBase::LoadHardwareCaps( KeyValues *pGroup, HardwareCaps_t &caps ) { if( !pGroup ) return; // don't just blanket kill clip planes on POSIX, only shoot them down if we're running ARB, or asked for nouserclipplanes. //FIXME need to take into account the caps bit that GLM can now provide, so NV can use normal clipping and ATI can fall back to fastclip. if ( CommandLine()->FindParm("-arbmode") || CommandLine()->CheckParm( "-nouserclip" ) ) { caps.m_UseFastClipping = true; } else { caps.m_UseFastClipping = ReadBool( pGroup, "NoUserClipPlanes", caps.m_UseFastClipping ); } caps.m_bNeedsATICentroidHack = ReadBool( pGroup, "CentroidHack", caps.m_bNeedsATICentroidHack ); caps.m_bDisableShaderOptimizations = ReadBool( pGroup, "DisableShaderOptimizations", caps.m_bDisableShaderOptimizations ); } //----------------------------------------------------------------------------- // Reads in the hardware caps from the dxsupport.cfg file //----------------------------------------------------------------------------- void CShaderDeviceMgrBase::ReadHardwareCaps( HardwareCaps_t &caps, int nDxLevel ) { KeyValues *pCfg = ReadDXSupportKeyValues(); if ( !pCfg ) return; // Next, read the hardware caps for that dx support level. KeyValues *pDxLevelKeyValues = FindDXLevelSpecificConfig( pCfg, nDxLevel ); // Look for a vendor specific line for a given dxlevel. KeyValues *pDXLevelAndVendorKeyValue = FindDXLevelAndVendorSpecificConfig( pCfg, nDxLevel, caps.m_VendorID ); // Finally, override the hardware caps based on the specific card KeyValues *pCardKeyValues = FindCardSpecificConfig( pCfg, caps.m_VendorID, caps.m_DeviceID ); // Apply if( pCardKeyValues && ReadHexValue( pCardKeyValues, "MinDeviceID" ) == 0 && ReadHexValue( pCardKeyValues, "MaxDeviceID" ) == 0xffff ) { // The card specific case is a catch all for device ids, so run it before running the dxlevel and card specific stuff. LoadHardwareCaps( pDxLevelKeyValues, caps ); LoadHardwareCaps( pCardKeyValues, caps ); LoadHardwareCaps( pDXLevelAndVendorKeyValue, caps ); } else { // The card specific case is a small range of cards, so run it last to override all other configs. LoadHardwareCaps( pDxLevelKeyValues, caps ); // don't run this one since we have a specific config for this card. // LoadHardwareCaps( pDXLevelAndVendorKeyValue, caps ); LoadHardwareCaps( pCardKeyValues, caps ); } } //----------------------------------------------------------------------------- // Reads in ConVars + config variables //----------------------------------------------------------------------------- void CShaderDeviceMgrBase::LoadConfig( KeyValues *pKeyValues, KeyValues *pConfiguration ) { if( !pKeyValues ) return; if( CommandLine()->FindParm( "-debugdxsupport" ) ) { CUtlBuffer tmpBuf; pKeyValues->RecursiveSaveToFile( tmpBuf, 0 ); Warning( "%s\n", ( const char * )tmpBuf.Base() ); } for( KeyValues *pGroup = pKeyValues->GetFirstSubKey(); pGroup; pGroup = pGroup->GetNextKey() ) { AddKey( pConfiguration, pGroup ); } } //----------------------------------------------------------------------------- // Computes amount of ram //----------------------------------------------------------------------------- static unsigned long GetRam() { MEMORYSTATUS stat; GlobalMemoryStatus( &stat ); char buf[256]; V_snprintf( buf, sizeof( buf ), "GlobalMemoryStatus: %llu\n", (uint64)(stat.dwTotalPhys) ); Plat_DebugString( buf ); return (stat.dwTotalPhys / (1024 * 1024)); } //----------------------------------------------------------------------------- // Gets the recommended configuration associated with a particular dx level //----------------------------------------------------------------------------- bool CShaderDeviceMgrBase::GetRecommendedConfigurationInfo( int nAdapter, int nDXLevel, int nVendorID, int nDeviceID, KeyValues *pConfiguration ) { LOCK_SHADERAPI(); const HardwareCaps_t& caps = GetHardwareCaps( nAdapter ); if ( nDXLevel == 0 ) { nDXLevel = caps.m_nDXSupportLevel; } nDXLevel = GetClosestActualDXLevel( nDXLevel ); if ( nDXLevel > caps.m_nMaxDXSupportLevel ) return false; KeyValues *pCfg = ReadDXSupportKeyValues(); if ( !pCfg ) return true; // Look for a dxlevel specific line KeyValues *pDxLevelKeyValues = FindDXLevelSpecificConfig( pCfg, nDXLevel ); // Look for a vendor specific line for a given dxlevel. KeyValues *pDXLevelAndVendorKeyValue = FindDXLevelAndVendorSpecificConfig( pCfg, nDXLevel, nVendorID ); // Next, override with device-specific overrides KeyValues *pCardKeyValues = FindCardSpecificConfig( pCfg, nVendorID, nDeviceID ); // Apply if ( pCardKeyValues && ReadHexValue( pCardKeyValues, "MinDeviceID" ) == 0 && ReadHexValue( pCardKeyValues, "MaxDeviceID" ) == 0xffff ) { // The card specific case is a catch all for device ids, so run it before running the dxlevel and card specific stuff. LoadConfig( pDxLevelKeyValues, pConfiguration ); LoadConfig( pCardKeyValues, pConfiguration ); LoadConfig( pDXLevelAndVendorKeyValue, pConfiguration ); } else { // The card specific case is a small range of cards, so run it last to override all other configs. LoadConfig( pDxLevelKeyValues, pConfiguration ); // don't run this one since we have a specific config for this card. // LoadConfig( pDXLevelAndVendorKeyValue, pConfiguration ); LoadConfig( pCardKeyValues, pConfiguration ); } // Next, override with cpu-speed based overrides const CPUInformation& pi = *GetCPUInformation(); int nCPUSpeedMhz = (int)(pi.m_Speed / 1000000.0f); bool bAMD = Q_stristr( pi.m_szProcessorID, "amd" ) != NULL; char buf[256]; V_snprintf( buf, sizeof( buf ), "CShaderDeviceMgrBase::GetRecommendedConfigurationInfo: CPU speed: %d MHz, Processor: %s\n", nCPUSpeedMhz, pi.m_szProcessorID ); Plat_DebugString( buf ); KeyValues *pCPUKeyValues = FindCPUSpecificConfig( pCfg, nCPUSpeedMhz, bAMD ); LoadConfig( pCPUKeyValues, pConfiguration ); // override with system memory-size based overrides int nSystemMB = GetRam(); DevMsg( "%d MB of system RAM\n", nSystemMB ); KeyValues *pMemoryKeyValues = FindMemorySpecificConfig( pCfg, nSystemMB ); LoadConfig( pMemoryKeyValues, pConfiguration ); // override with texture memory-size based overrides int nTextureMemorySize = GetVidMemBytes( nAdapter ); int vidMemMB = nTextureMemorySize / ( 1024 * 1024 ); KeyValues *pVidMemKeyValues = FindVidMemSpecificConfig( pCfg, vidMemMB ); if ( pVidMemKeyValues && nTextureMemorySize > 0 ) { if ( CommandLine()->FindParm( "-debugdxsupport" ) ) { CUtlBuffer tmpBuf; pVidMemKeyValues->RecursiveSaveToFile( tmpBuf, 0 ); Warning( "pVidMemKeyValues\n%s\n", ( const char * )tmpBuf.Base() ); } KeyValues *pMatPicmipKeyValue = pVidMemKeyValues->FindKey( "ConVar.mat_picmip", false ); // FIXME: Man, is this brutal. If it wasn't 1 day till orange box ship, I'd do something in dxsupport maybe if ( pMatPicmipKeyValue && ( ( nDXLevel == caps.m_nMaxDXSupportLevel ) || ( vidMemMB < 100 ) ) ) { KeyValues *pConfigMatPicMip = pConfiguration->FindKey( "ConVar.mat_picmip", false ); int newPicMip = pMatPicmipKeyValue->GetInt(); int oldPicMip = pConfigMatPicMip ? pConfigMatPicMip->GetInt() : 0; pConfiguration->SetInt( "ConVar.mat_picmip", max( newPicMip, oldPicMip ) ); } } // Hack to slam the mat_dxlevel ConVar to match the requested dxlevel pConfiguration->SetInt( "ConVar.mat_dxlevel", nDXLevel ); if ( CommandLine()->FindParm( "-debugdxsupport" ) ) { CUtlBuffer tmpBuf; pConfiguration->RecursiveSaveToFile( tmpBuf, 0 ); Warning( "final config:\n%s\n", ( const char * )tmpBuf.Base() ); } return true; } //----------------------------------------------------------------------------- // Gets recommended congifuration for a particular adapter at a particular dx level //----------------------------------------------------------------------------- bool CShaderDeviceMgrBase::GetRecommendedConfigurationInfo( int nAdapter, int nDXLevel, KeyValues *pCongifuration ) { Assert( nAdapter >= 0 && nAdapter <= GetAdapterCount() ); MaterialAdapterInfo_t info; GetAdapterInfo( nAdapter, info ); return GetRecommendedConfigurationInfo( nAdapter, nDXLevel, info.m_VendorID, info.m_DeviceID, pCongifuration ); } //----------------------------------------------------------------------------- // Returns only valid dx levels //----------------------------------------------------------------------------- int CShaderDeviceMgrBase::GetClosestActualDXLevel( int nDxLevel ) const { if ( nDxLevel < ABSOLUTE_MINIMUM_DXLEVEL ) return ABSOLUTE_MINIMUM_DXLEVEL; if ( nDxLevel == 80 ) return 80; if ( nDxLevel <= 89 ) return 81; if ( IsOpenGL() ) { return ( nDxLevel <= 90 ) ? 90 : 92; } if ( nDxLevel <= 94 ) return 90; if ( IsX360() && nDxLevel <= 98 ) return 98; if ( nDxLevel <= 99 ) return 95; return 100; } //----------------------------------------------------------------------------- // Mode change callback //----------------------------------------------------------------------------- void CShaderDeviceMgrBase::AddModeChangeCallback( ShaderModeChangeCallbackFunc_t func ) { LOCK_SHADERAPI(); Assert( func && m_ModeChangeCallbacks.Find( func ) < 0 ); m_ModeChangeCallbacks.AddToTail( func ); } void CShaderDeviceMgrBase::RemoveModeChangeCallback( ShaderModeChangeCallbackFunc_t func ) { LOCK_SHADERAPI(); m_ModeChangeCallbacks.FindAndRemove( func ); } void CShaderDeviceMgrBase::InvokeModeChangeCallbacks() { int nCount = m_ModeChangeCallbacks.Count(); for ( int i = 0; i < nCount; ++i ) { m_ModeChangeCallbacks[i](); } } //----------------------------------------------------------------------------- // Factory to return from SetMode //----------------------------------------------------------------------------- void* CShaderDeviceMgrBase::ShaderInterfaceFactory( const char *pInterfaceName, int *pReturnCode ) { if ( pReturnCode ) { *pReturnCode = IFACE_OK; } if ( !Q_stricmp( pInterfaceName, SHADER_DEVICE_INTERFACE_VERSION ) ) return static_cast< IShaderDevice* >( g_pShaderDevice ); if ( !Q_stricmp( pInterfaceName, SHADERAPI_INTERFACE_VERSION ) ) return static_cast< IShaderAPI* >( g_pShaderAPI ); if ( !Q_stricmp( pInterfaceName, SHADERSHADOW_INTERFACE_VERSION ) ) return static_cast< IShaderShadow* >( g_pShaderShadow ); if ( pReturnCode ) { *pReturnCode = IFACE_FAILED; } return NULL; } //----------------------------------------------------------------------------- // // The Base implementation of the shader device // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // constructor, destructor //----------------------------------------------------------------------------- CShaderDeviceBase::CShaderDeviceBase() { m_bInitialized = false; m_nAdapter = -1; m_hWnd = NULL; m_hWndCookie = NULL; m_dwThreadId = ThreadGetCurrentId(); } CShaderDeviceBase::~CShaderDeviceBase() { } void CShaderDeviceBase::SetCurrentThreadAsOwner() { m_dwThreadId = ThreadGetCurrentId(); } void CShaderDeviceBase::RemoveThreadOwner() { m_dwThreadId = 0xFFFFFFFF; } bool CShaderDeviceBase::ThreadOwnsDevice() { if ( ThreadGetCurrentId() == m_dwThreadId ) return true; return false; } // Methods of IShaderDevice ImageFormat CShaderDeviceBase::GetBackBufferFormat() const { return IMAGE_FORMAT_UNKNOWN; } int CShaderDeviceBase::StencilBufferBits() const { return 0; } bool CShaderDeviceBase::IsAAEnabled() const { return false; } //----------------------------------------------------------------------------- // Methods for interprocess communication to release resources //----------------------------------------------------------------------------- #define MATERIAL_SYSTEM_WINDOW_ID 0xFEEDDEAD #ifdef USE_ACTUAL_DX static VD3DHWND GetTopmostParentWindow( VD3DHWND hWnd ) { // Find the parent window... VD3DHWND hParent = GetParent( hWnd ); while ( hParent ) { hWnd = hParent; hParent = GetParent( hWnd ); } return hWnd; } static BOOL CALLBACK EnumChildWindowsProc( VD3DHWND hWnd, LPARAM lParam ) { int windowId = GetWindowLongPtr( hWnd, GWLP_USERDATA ); if (windowId == MATERIAL_SYSTEM_WINDOW_ID) { COPYDATASTRUCT copyData; copyData.dwData = lParam; copyData.cbData = 0; copyData.lpData = 0; SendMessage(hWnd, WM_COPYDATA, 0, (LPARAM)©Data); } return TRUE; } static BOOL CALLBACK EnumWindowsProc( VD3DHWND hWnd, LPARAM lParam ) { EnumChildWindows( hWnd, EnumChildWindowsProc, lParam ); return TRUE; } static BOOL CALLBACK EnumWindowsProcNotThis( VD3DHWND hWnd, LPARAM lParam ) { if ( g_pShaderDevice && ( GetTopmostParentWindow( (VD3DHWND)g_pShaderDevice->GetIPCHWnd() ) == hWnd ) ) return TRUE; EnumChildWindows( hWnd, EnumChildWindowsProc, lParam ); return TRUE; } #endif //----------------------------------------------------------------------------- // Adds a hook to let us know when other instances are setting the mode //----------------------------------------------------------------------------- #ifdef STRICT #define WINDOW_PROC WNDPROC #else #define WINDOW_PROC FARPROC #endif #ifdef USE_ACTUAL_DX static LRESULT CALLBACK ShaderDX8WndProc(VD3DHWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { #if !defined( _X360 ) // FIXME: Should these IPC messages tell when an app has focus or not? // If so, we'd want to totally disable the shader api layer when an app // doesn't have focus. // Look for the special IPC message that tells us we're trying to set // the mode.... switch(msg) { case WM_COPYDATA: { if ( !g_pShaderDevice ) break; COPYDATASTRUCT* pData = (COPYDATASTRUCT*)lParam; // that number is our magic cookie number if ( pData->dwData == CShaderDeviceBase::RELEASE_MESSAGE ) { g_pShaderDevice->OtherAppInitializing(true); } else if ( pData->dwData == CShaderDeviceBase::REACQUIRE_MESSAGE ) { g_pShaderDevice->OtherAppInitializing(false); } else if ( pData->dwData == CShaderDeviceBase::EVICT_MESSAGE ) { g_pShaderDevice->EvictManagedResourcesInternal( ); } } break; } return DefWindowProc( hWnd, msg, wParam, lParam ); #endif } #endif //----------------------------------------------------------------------------- // Install, remove ability to talk to other shaderapi apps //----------------------------------------------------------------------------- void CShaderDeviceBase::InstallWindowHook( void* hWnd ) { Assert( m_hWndCookie == NULL ); #ifdef USE_ACTUAL_DX #if !defined( _X360 ) VD3DHWND hParent = GetTopmostParentWindow( (VD3DHWND)hWnd ); // Attach a child window to the parent; we're gonna store special info there // We can't use the USERDATA, cause other apps may want to use this. HINSTANCE hInst = (HINSTANCE)GetWindowLongPtr( hParent, GWLP_HINSTANCE ); WNDCLASS wc; memset( &wc, 0, sizeof( wc ) ); wc.style = CS_NOCLOSE | CS_PARENTDC; wc.lpfnWndProc = ShaderDX8WndProc; wc.hInstance = hInst; wc.lpszClassName = "shaderdx8"; // In case an old one is sitting around still... UnregisterClass( "shaderdx8", hInst ); RegisterClass( &wc ); // Create the window m_hWndCookie = CreateWindow( "shaderdx8", "shaderdx8", WS_CHILD, 0, 0, 0, 0, hParent, NULL, hInst, NULL ); // Marks it as a material system window SetWindowLongPtr( (VD3DHWND)m_hWndCookie, GWLP_USERDATA, MATERIAL_SYSTEM_WINDOW_ID ); #endif #endif } void CShaderDeviceBase::RemoveWindowHook( void* hWnd ) { #ifdef USE_ACTUAL_DX #if !defined( _X360 ) if ( m_hWndCookie ) { DestroyWindow( (VD3DHWND)m_hWndCookie ); m_hWndCookie = 0; } VD3DHWND hParent = GetTopmostParentWindow( (VD3DHWND)hWnd ); HINSTANCE hInst = (HINSTANCE)GetWindowLongPtr( hParent, GWLP_HINSTANCE ); UnregisterClass( "shaderdx8", hInst ); #endif #endif } //----------------------------------------------------------------------------- // Sends a message to other shaderapi applications //----------------------------------------------------------------------------- void CShaderDeviceBase::SendIPCMessage( IPCMessage_t msg ) { #ifdef USE_ACTUAL_DX #if !defined( _X360 ) // Gotta send this to all windows, since we don't know which ones // are material system apps... if ( msg != EVICT_MESSAGE ) { EnumWindows( EnumWindowsProc, (DWORD)msg ); } else { EnumWindows( EnumWindowsProcNotThis, (DWORD)msg ); } #endif #endif } //----------------------------------------------------------------------------- // Find view //----------------------------------------------------------------------------- int CShaderDeviceBase::FindView( void* hWnd ) const { /* FIXME: Is this necessary? // Look for the view in the list of views for (int i = m_Views.Count(); --i >= 0; ) { if (m_Views[i].m_HWnd == (VD3DHWND)hwnd) return i; } */ return -1; } //----------------------------------------------------------------------------- // Creates a child window //----------------------------------------------------------------------------- bool CShaderDeviceBase::AddView( void* hWnd ) { LOCK_SHADERAPI(); /* // If we haven't created a device yet if (!Dx9Device()) return false; // Make sure no duplicate hwnds... if (FindView(hwnd) >= 0) return false; // In this case, we need to create the device; this is our // default swap chain. This here says we're gonna use a part of the // existing buffer and just grab that. int view = m_Views.AddToTail(); m_Views[view].m_HWnd = (VD3DHWND)hwnd; // memcpy( &m_Views[view].m_PresentParamters, m_PresentParameters, sizeof(m_PresentParamters) ); HRESULT hr; hr = Dx9Device()->CreateAdditionalSwapChain( &m_PresentParameters, &m_Views[view].m_pSwapChain ); return !FAILED(hr); */ return true; } void CShaderDeviceBase::RemoveView( void* hWnd ) { LOCK_SHADERAPI(); /* // Look for the view in the list of views int i = FindView(hwnd); if (i >= 0) { // FIXME m_Views[i].m_pSwapChain->Release(); m_Views.FastRemove(i); } */ } //----------------------------------------------------------------------------- // Activates a child window //----------------------------------------------------------------------------- void CShaderDeviceBase::SetView( void* hWnd ) { LOCK_SHADERAPI(); ShaderViewport_t viewport; g_pShaderAPI->GetViewports( &viewport, 1 ); // Get the window (*not* client) rect of the view window m_ViewHWnd = (VD3DHWND)hWnd; GetWindowSize( m_nWindowWidth, m_nWindowHeight ); // Reset the viewport (takes into account the view rect) // Don't need to set the viewport if it's not ready g_pShaderAPI->SetViewports( 1, &viewport ); } //----------------------------------------------------------------------------- // Gets the window size //----------------------------------------------------------------------------- void CShaderDeviceBase::GetWindowSize( int& nWidth, int& nHeight ) const { #if defined( USE_SDL ) // this matches up to what the threaded material system does g_pShaderAPI->GetBackBufferDimensions( nWidth, nHeight ); #else // If the window was minimized last time swap buffers happened, or if it's iconic now, // return 0 size #ifdef _WIN32 if ( !m_bIsMinimized && !IsIconic( ( HWND )m_hWnd ) ) #else if ( !m_bIsMinimized && !IsIconic( (VD3DHWND)m_hWnd ) ) #endif { // NOTE: Use the 'current view' (which may be the same as the main window) RECT rect; #ifdef _WIN32 GetClientRect( ( HWND )m_ViewHWnd, &rect ); #else toglGetClientRect( (VD3DHWND)m_ViewHWnd, &rect ); #endif nWidth = rect.right - rect.left; nHeight = rect.bottom - rect.top; } else { nWidth = nHeight = 0; } #endif }