diff options
Diffstat (limited to 'materialsystem/shaderapidx9/shaderdevicebase.cpp')
| -rw-r--r-- | materialsystem/shaderapidx9/shaderdevicebase.cpp | 1256 |
1 files changed, 1256 insertions, 0 deletions
diff --git a/materialsystem/shaderapidx9/shaderdevicebase.cpp b/materialsystem/shaderapidx9/shaderdevicebase.cpp new file mode 100644 index 0000000..aef5996 --- /dev/null +++ b/materialsystem/shaderapidx9/shaderdevicebase.cpp @@ -0,0 +1,1256 @@ +//========= 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 +} + + |