summaryrefslogtreecommitdiff
path: root/materialsystem/shaderapidx9/shaderdevicebase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'materialsystem/shaderapidx9/shaderdevicebase.cpp')
-rw-r--r--materialsystem/shaderapidx9/shaderdevicebase.cpp1256
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)&copyData);
+ }
+ 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
+}
+
+