//==== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// // // Vertex/Pixel Shaders // //===========================================================================// #if defined( _WIN32 ) && !defined( _X360 ) #include #endif #include "vertexshaderdx11.h" #include "tier1/UtlSymbol.h" #include "tier1/UtlVector.h" #include "tier1/UtlDict.h" #include "tier1/utllinkedlist.h" #include "tier1/UtlBuffer.h" #include "tier1/UtlStringMap.h" #include "shaderapidx9/locald3dtypes.h" #include "shaderapidx11_global.h" #include "shaderapidx9/recording.h" #include "tier0/vprof.h" #include "materialsystem/imaterialsystem.h" #include "materialsystem/imaterialsystemhardwareconfig.h" #include "shaderapidx11.h" #include "materialsystem/ishader.h" #include "ishadersystem.h" #include "tier0/fasttimer.h" #include #include "filesystem.h" #include "convar.h" #include "materialsystem/shader_vcs_version.h" #include "tier1/lzmadecoder.h" #include "tier1/utlmap.h" #include "datacache/idatacache.h" #include "tier1/diff.h" #include "shaderdevicedx11.h" #include "filesystem/IQueuedLoader.h" #include "tier2/tier2.h" #include "shaderapi/ishaderutil.h" #include "tier0/icommandline.h" #include "Color.h" #include "tier0/dbg.h" // NOTE: This has to be the last file included! #include "tier0/memdbgon.h" #if 0 //def USE_ACTUAL_DX #include "../utils/bzip2/bzlib.h" #else int BZ2_bzBuffToBuffDecompress( char* dest, unsigned int* destLen, char* source, unsigned int sourceLen, int _small, int verbosity ) { return 0; } #endif // uncomment to get dynamic compilation for HLSL shaders // i don't think this works atm, but im probably using this wrong // X360 NOTE: By default, the system looks for a shared folder named "stdshaders" on the host machine and is completely compatible with -dvd. Ensure that the share is writable if you plan on generating UPDB's. #define DYNAMIC_SHADER_COMPILE // uncomment to get spew about what combos are being compiled. //#define DYNAMIC_SHADER_COMPILE_VERBOSE // uncomment and fill in with a path to use a specific set of shader source files. Meant for network use. // PC path format is of style "\\\\somemachine\\sourcetreeshare\\materialsystem\\stdshaders" // Xbox path format is of style "net:\\smb\\somemachine\\sourcetreeshare\\materialsystem\\stdshaders" // - Xbox dynamic compiles without a custom path default to look directly for "stdshaders" share on host pc //#define DYNAMIC_SHADER_COMPILE_CUSTOM_PATH "" // uncomment to get disassembled (asm) shader code in your game dir as *.asm //#define DYNAMIC_SHADER_COMPILE_WRITE_ASSEMBLY // uncomment to get disassembled (asm) shader code in your game dir as *.asm //#define WRITE_ASSEMBLY //#define PROFILE_SHADER_CREATE // debugging aid #define MAX_SHADER_HISTORY 16 #if !defined( _X360 ) #define SHADER_FNAME_EXTENSION ".vcs" #else #define SHADER_FNAME_EXTENSION ".360.vcs" #endif #ifdef DYNAMIC_SHADER_COMPILE volatile static char s_ShaderCompileString[]="dynamic_shader_compile_is_on"; #endif static const char *GetLightTypeName( VertexShaderLightTypes_t type ) { static const char *s_VertexShaderLightTypeNames[] = { "LIGHT_NONE", "LIGHT_SPOT", "LIGHT_POINT", "LIGHT_DIRECTIONAL", "LIGHT_STATIC", "LIGHT_AMBIENTCUBE", }; return s_VertexShaderLightTypeNames[type+1]; } #ifdef PROFILE_SHADER_CREATE static FILE *GetDebugFileHandle( void ) { static FILE *fp = NULL; if( !fp ) { fp = fopen( "shadercreate.txt", "w" ); Assert( fp ); } return fp; } #endif // PROFILE_SHADER_CREATE //----------------------------------------------------------------------------- // Explicit instantiation of shader buffer implementation //----------------------------------------------------------------------------- template class CShaderBuffer< ID3DBlob >; //----------------------------------------------------------------------------- // Used to find unique shaders //----------------------------------------------------------------------------- #ifdef MEASURE_DRIVER_ALLOCATIONS static CUtlMap< CRC32_t, int, int > s_UniqueVS( 0, 0, DefLessFunc( CRC32_t ) ); static CUtlMap< CRC32_t, int, int > s_UniquePS( 0, 0, DefLessFunc( CRC32_t ) ); static CUtlMap< IDirect3DVertexShader9*, CRC32_t, int > s_VSLookup( 0, 0, DefLessFunc( IDirect3DVertexShader9* ) ); static CUtlMap< IDirect3DPixelShader9*, CRC32_t, int > s_PSLookup( 0, 0, DefLessFunc( IDirect3DPixelShader9* ) ); #endif static void RegisterVS( const void* pShaderBits, int nShaderSize, ID3D11VertexShader* pShader ) { #ifdef MEASURE_DRIVER_ALLOCATIONS CRC32_t crc; CRC32_Init( &crc ); CRC32_ProcessBuffer( &crc, pShaderBits, nShaderSize ); CRC32_Final( &crc ); s_VSLookup.Insert( pShader, crc ); int nIndex = s_UniqueVS.Find( crc ); if ( nIndex != s_UniqueVS.InvalidIndex() ) { ++s_UniqueVS[nIndex]; } else { int nMemUsed = 23 * 1024; s_UniqueVS.Insert( crc, 1 ); VPROF_INCREMENT_GROUP_COUNTER( "unique vs count", COUNTER_GROUP_NO_RESET, 1 ); VPROF_INCREMENT_GROUP_COUNTER( "vs driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); } #endif } static void RegisterPS( const void* pShaderBits, int nShaderSize, ID3D11PixelShader* pShader ) { #ifdef MEASURE_DRIVER_ALLOCATIONS CRC32_t crc; CRC32_Init( &crc ); CRC32_ProcessBuffer( &crc, pShaderBits, nShaderSize ); CRC32_Final( &crc ); s_PSLookup.Insert( pShader, crc ); int nIndex = s_UniquePS.Find( crc ); if ( nIndex != s_UniquePS.InvalidIndex() ) { ++s_UniquePS[nIndex]; } else { int nMemUsed = 400; s_UniquePS.Insert( crc, 1 ); VPROF_INCREMENT_GROUP_COUNTER( "unique ps count", COUNTER_GROUP_NO_RESET, 1 ); VPROF_INCREMENT_GROUP_COUNTER( "ps driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed ); } #endif } static void UnregisterVS( ID3D11VertexShader* pShader ) { #ifdef MEASURE_DRIVER_ALLOCATIONS int nCRCIndex = s_VSLookup.Find( pShader ); if ( nCRCIndex == s_VSLookup.InvalidIndex() ) return; CRC32_t crc = s_VSLookup[nCRCIndex]; s_VSLookup.RemoveAt( nCRCIndex ); int nIndex = s_UniqueVS.Find( crc ); if ( nIndex != s_UniqueVS.InvalidIndex() ) { if ( --s_UniqueVS[nIndex] <= 0 ) { int nMemUsed = 23 * 1024; VPROF_INCREMENT_GROUP_COUNTER( "unique vs count", COUNTER_GROUP_NO_RESET, -1 ); VPROF_INCREMENT_GROUP_COUNTER( "vs driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); s_UniqueVS.Remove( nIndex ); } } #endif } static void UnregisterPS( ID3D11PixelShader* pShader ) { #ifdef MEASURE_DRIVER_ALLOCATIONS int nCRCIndex = s_PSLookup.Find( pShader ); if ( nCRCIndex == s_PSLookup.InvalidIndex() ) return; CRC32_t crc = s_PSLookup[nCRCIndex]; s_PSLookup.RemoveAt( nCRCIndex ); int nIndex = s_UniquePS.Find( crc ); if ( nIndex != s_UniquePS.InvalidIndex() ) { if ( --s_UniquePS[nIndex] <= 0 ) { int nMemUsed = 400; VPROF_INCREMENT_GROUP_COUNTER( "unique ps count", COUNTER_GROUP_NO_RESET, -1 ); VPROF_INCREMENT_GROUP_COUNTER( "ps driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed ); s_UniquePS.Remove( nIndex ); } } #endif } template int BinarySearchCombos( uint32 nStaticComboID, int nCombos, T const *pRecords ) { // Use binary search - data is sorted int nLowerIdx = 1; int nUpperIdx = nCombos; for (;;) { if ( nUpperIdx < nLowerIdx ) return -1; int nMiddleIndex = ( nLowerIdx + nUpperIdx ) / 2; uint32 nProbe = pRecords[nMiddleIndex-1].m_nStaticComboID; if ( nStaticComboID < nProbe ) { nUpperIdx = nMiddleIndex - 1; } else { if ( nStaticComboID > nProbe ) nLowerIdx = nMiddleIndex + 1; else return nMiddleIndex - 1; } } } inline int FindShaderStaticCombo( uint32 nStaticComboID, const ShaderHeader_t& header, StaticComboRecord_t *pRecords ) { if ( header.m_nVersion < 5 ) return -1; return BinarySearchCombos( nStaticComboID, header.m_nNumStaticCombos, pRecords ); } // cache redundant i/o fetched components of the vcs files struct ShaderFileCache_t { CUtlSymbol m_Name; CUtlSymbol m_Filename; ShaderHeader_t m_Header; bool m_bVertexShader; // valid for diff version only - contains the microcode used as the reference for diff algorithm CUtlBuffer m_ReferenceCombo; // valid for ver5 only - contains the directory CUtlVector< StaticComboRecord_t > m_StaticComboRecords; CUtlVector< StaticComboAliasRecord_t > m_StaticComboDupRecords; ShaderFileCache_t() { // invalid until version established m_Header.m_nVersion = 0; } bool IsValid() const { return m_Header.m_nVersion != 0; } bool IsOldVersion() const { return m_Header.m_nVersion < 5; } int IsVersion6() const { return ( m_Header.m_nVersion == 6 ); } int FindCombo( uint32 nStaticComboID ) { int nSearchAliases = BinarySearchCombos( nStaticComboID, m_StaticComboDupRecords.Count(), m_StaticComboDupRecords.Base() ); if ( nSearchAliases != -1 ) nStaticComboID = m_StaticComboDupRecords[nSearchAliases].m_nSourceStaticCombo; return FindShaderStaticCombo( nStaticComboID, m_Header, m_StaticComboRecords.Base() ); } bool operator==( const ShaderFileCache_t& a ) const { return m_Name == a.m_Name && m_bVertexShader == a.m_bVertexShader; } }; //----------------------------------------------------------------------------- // Vertex + pixel shader manager //----------------------------------------------------------------------------- class CShaderManager : public IShaderManager { public: CShaderManager(); virtual ~CShaderManager(); // Methods of IShaderManager virtual void Init(); virtual void Shutdown(); virtual IShaderBuffer *CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ); virtual VertexShaderHandle_t CreateVertexShader( IShaderBuffer* pShaderBuffer ); virtual void DestroyVertexShader( VertexShaderHandle_t hShader ); virtual PixelShaderHandle_t CreatePixelShader( IShaderBuffer* pShaderBuffer ); virtual void DestroyPixelShader( PixelShaderHandle_t hShader ); virtual VertexShader_t CreateVertexShader( const char *pVertexShaderFile, ShaderIndex_t nStaticVshIndex = 0 ); virtual PixelShader_t CreatePixelShader( const char *pPixelShaderFile, ShaderIndex_t nStaticPshIndex = 0 ); virtual void SetVertexShader( VertexShader_t shader ); virtual void SetPixelShader( PixelShader_t shader ); virtual void BindVertexShader( VertexShaderHandle_t shader ); virtual void BindPixelShader( PixelShaderHandle_t shader ); virtual void *GetCurrentVertexShader(); virtual void *GetCurrentPixelShader(); virtual void ResetShaderState(); void FlushShaders(); virtual void ClearVertexAndPixelShaderRefCounts(); virtual void PurgeUnusedVertexAndPixelShaders(); void SpewVertexAndPixelShaders(); const char *GetActiveVertexShaderName(); const char *GetActivePixelShaderName(); bool CreateDynamicCombos_Ver4( void *pContext, uint8 *pComboBuffer ); bool CreateDynamicCombos_Ver5( void *pContext, uint8 *pComboBuffer ); static void QueuedLoaderCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError ); private: struct ShaderStaticCombos_t { int m_nCount; // Can't use CUtlVector here since you CUtlLinkedList> doesn't work. HardwareShader_t *m_pHardwareShaders; struct ShaderCreationData_t { CUtlVector ByteCode; uint32 iCentroidMask; }; ShaderCreationData_t *m_pCreationData; }; struct ShaderLookupDx11_t { CUtlSymbol m_Name; ShaderIndex_t m_nStaticIndex; ShaderStaticCombos_t m_ShaderStaticCombos; DWORD m_Flags; int m_nRefCount; unsigned int m_hShaderFileCache; // for queued loading, bias an aligned optimal buffer forward to correct location int m_nDataOffset; // diff version, valid during load only ShaderDictionaryEntry_t *m_pComboDictionary; bool operator=( const ShaderLookupDx11_t &other ) { m_Name = other.m_Name; m_nStaticIndex = other.m_nStaticIndex; m_ShaderStaticCombos = other.m_ShaderStaticCombos; m_Flags = other.m_Flags; m_nRefCount = other.m_nRefCount; m_hShaderFileCache = other.m_hShaderFileCache; m_nDataOffset = other.m_nDataOffset; m_pComboDictionary = other.m_pComboDictionary; } ShaderLookupDx11_t() { m_Flags = 0; m_nRefCount = 0; m_ShaderStaticCombos.m_nCount = 0; m_ShaderStaticCombos.m_pHardwareShaders = 0; m_ShaderStaticCombos.m_pCreationData = 0; m_pComboDictionary = NULL; } void IncRefCount() { m_nRefCount++; } bool operator==( const ShaderLookupDx11_t& a ) const { return m_Name == a.m_Name && m_nStaticIndex == a.m_nStaticIndex; } }; #ifdef DYNAMIC_SHADER_COMPILE struct Combo_t { CUtlSymbol m_ComboName; int m_nMin; int m_nMax; }; struct ShaderCombos_t { CUtlVector m_StaticCombos; CUtlVector m_DynamicCombos; uint64 GetNumDynamicCombos( void ) const { uint64 combos = 1; int i; for( i = 0; i < m_DynamicCombos.Count(); i++ ) { combos *= ( m_DynamicCombos[i].m_nMax - m_DynamicCombos[i].m_nMin + 1 ); } return combos; } uint64 GetNumStaticCombos( void ) const { uint64 combos = 1; int i; for( i = 0; i < m_StaticCombos.Count(); i++ ) { combos *= ( m_StaticCombos[i].m_nMax - m_StaticCombos[i].m_nMin + 1 ); } return combos; } }; #endif private: void CreateStaticShaders(); void DestroyStaticShaders(); // The low-level dx call to set the vertex shader state void SetVertexShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader = DC_INVALID_HANDLE ); // The low-level dx call to set the pixel shader state void SetPixelShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader = DC_INVALID_HANDLE ); // Destroys all shaders void DestroyAllShaders(); // Destroy a particular vertex shader void DestroyVertexShader( VertexShader_t shader ); // Destroy a particular pixel shader void DestroyPixelShader( PixelShader_t shader ); bool LoadAndCreateShaders( ShaderLookupDx11_t &lookup, bool bVertexShader ); FileHandle_t OpenFileAndLoadHeader( const char *pFileName, ShaderHeader_t *pHeader ); #ifdef DYNAMIC_SHADER_COMPILE bool LoadAndCreateShaders_Dynamic( ShaderLookupDx11_t &lookup, bool bVertexShader ); const ShaderCombos_t *FindOrCreateShaderCombos( const char *pShaderName ); HardwareShader_t CompileShader( const char *pShaderName, ShaderIndex_t nStaticIndex, ShaderIndex_t nDynamicIndex, bool bVertexShader ); #endif void DisassembleShader( ShaderLookupDx11_t *pLookup, ShaderIndex_t dynamicCombo, uint8 *pByteCode ); CUtlFixedLinkedList< ShaderLookupDx11_t > m_VertexShaderDict; CUtlFixedLinkedList< ShaderLookupDx11_t > m_PixelShaderDict; CUtlSymbolTable m_ShaderSymbolTable; #ifdef DYNAMIC_SHADER_COMPILE //typedef HRESULT (__stdcall *ShaderCompileFromFileFunc_t)( LPCSTR pSrcFile, CONST D3DXMACRO* pDefines, // LPD3DXINCLUDE pInclude, LPCSTR pFunctionName, LPCSTR pProfile, DWORD Flags, // LPD3DXBUFFER* ppShader, LPD3DXBUFFER * ppErrorMsgs, LPD3DXCONSTANTTABLE * ppConstantTable ); CUtlStringMap m_ShaderNameToCombos; #endif // The current vertex and pixel shader HardwareShader_t m_HardwareVertexShader; HardwareShader_t m_HardwarePixelShader; CUtlFixedLinkedList< ShaderFileCache_t > m_ShaderFileCache; // false, creates during init. // true, creates on access, helps reduce d3d memory for tools, but causes i/o hitches. bool m_bCreateShadersOnDemand; #if defined( _DEBUG ) // for debugging (can't resolve UtlSym) // need some history because 360 d3d has rips related to sequencing char vshDebugName[MAX_SHADER_HISTORY][64]; int vshDebugIndex; char pshDebugName[MAX_SHADER_HISTORY][64]; int pshDebugIndex; #endif }; //----------------------------------------------------------------------------- // Singleton accessor //----------------------------------------------------------------------------- static CShaderManager s_ShaderManager; IShaderManager *g_pShaderManager = &s_ShaderManager; IShaderManager *g_pShaderManagerDx11 = g_pShaderManager; //----------------------------------------------------------------------------- // Constructor, destructor //----------------------------------------------------------------------------- CShaderManager::CShaderManager() : m_ShaderSymbolTable( 0, 32, true /* caseInsensitive */ ), m_VertexShaderDict( 32 ), m_PixelShaderDict( 32 ), m_ShaderFileCache( 32 ) { m_bCreateShadersOnDemand = false; #ifdef _DEBUG vshDebugIndex = 0; pshDebugIndex = 0; #endif } CShaderManager::~CShaderManager() { } //----------------------------------------------------------------------------- // Initialization, shutdown //----------------------------------------------------------------------------- void CShaderManager::Init() { // incomptaible on the 360, violates loading system // only used by pc to help tools reduce d3d footprint m_bCreateShadersOnDemand = IsPC() && ( ShaderUtil()->InEditorMode() || CommandLine()->CheckParm( "-shadersondemand" ) ); CreateStaticShaders(); } void CShaderManager::Shutdown() { DestroyAllShaders(); DestroyStaticShaders(); } //----------------------------------------------------------------------------- // Compiles shaders //----------------------------------------------------------------------------- IShaderBuffer *CShaderManager::CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ) { return g_pShaderDeviceDx11->CompileShader( pProgram, nBufLen, pShaderVersion ); } VertexShaderHandle_t CShaderManager::CreateVertexShader( IShaderBuffer* pShaderBuffer ) { return g_pShaderDeviceDx11->CreateVertexShader( pShaderBuffer ); } void CShaderManager::DestroyVertexShader( VertexShaderHandle_t hShader ) { g_pShaderDeviceDx11->DestroyVertexShader( hShader ); } PixelShaderHandle_t CShaderManager::CreatePixelShader( IShaderBuffer* pShaderBuffer ) { return g_pShaderDeviceDx11->CreatePixelShader( pShaderBuffer ); } void CShaderManager::DestroyPixelShader( PixelShaderHandle_t hShader ) { g_pShaderDeviceDx11->DestroyPixelShader( hShader ); } //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- HardwareShader_t s_pIllegalMaterialPS = INVALID_HARDWARE_SHADER; //----------------------------------------------------------------------------- // Static methods //----------------------------------------------------------------------------- void CShaderManager::CreateStaticShaders() { MEM_ALLOC_D3D_CREDIT(); if ( !HardwareConfig()->SupportsVertexAndPixelShaders() ) { return; } #if 0 if ( IsPC() ) { // GR - hack for illegal materials const DWORD psIllegalMaterial[] = { 0xffff0101, 0x00000051, 0xa00f0000, 0x00000000, 0x3f800000, 0x00000000, 0x3f800000, 0x00000001, 0x800f0000, 0xa0e40000, 0x0000ffff }; // create default shader Dx9Device()->CreatePixelShader( psIllegalMaterial, ( IDirect3DPixelShader9 ** )&s_pIllegalMaterialPS ); } #endif } void CShaderManager::DestroyStaticShaders() { #if 0 // GR - invalid material hack // destroy internal shader if ( s_pIllegalMaterialPS != INVALID_HARDWARE_SHADER ) { ( ( IDirect3DPixelShader9 * )s_pIllegalMaterialPS )->Release(); s_pIllegalMaterialPS = INVALID_HARDWARE_SHADER; } #endif } #ifdef DYNAMIC_SHADER_COMPILE static const char *GetShaderSourcePath( void ) { static char shaderDir[MAX_PATH]; // GR - just in case init this... static bool bHaveShaderDir = false; if( !bHaveShaderDir ) { bHaveShaderDir = true; # if ( defined( DYNAMIC_SHADER_COMPILE_CUSTOM_PATH ) ) { Q_strncpy( shaderDir, DYNAMIC_SHADER_COMPILE_CUSTOM_PATH, MAX_PATH ); } # else { # if ( defined( _X360 ) ) { char hostName[128] = ""; const char *pHostName = CommandLine()->ParmValue( "-host" ); if ( !pHostName ) { // the 360 machine name must be _360 DWORD length = sizeof( hostName ); DmGetXboxName( hostName, &length ); char *p = strstr( hostName, "_360" ); *p = '\0'; pHostName = hostName; } Q_snprintf( shaderDir, MAX_PATH, "net:\\smb\\%s\\stdshaders", pHostName ); } # else { Q_strncpy( shaderDir, __FILE__, MAX_PATH ); Q_StripFilename( shaderDir ); Q_StripLastDir( shaderDir, MAX_PATH ); Q_strncat( shaderDir, "stdshadersdx11", MAX_PATH, COPY_ALL_CHARACTERS ); } # endif } # endif } return shaderDir; } #endif #ifdef DYNAMIC_SHADER_COMPILE const CShaderManager::ShaderCombos_t *CShaderManager::FindOrCreateShaderCombos( const char *pShaderName ) { if( m_ShaderNameToCombos.Defined( pShaderName ) ) { return &m_ShaderNameToCombos[pShaderName]; } ShaderCombos_t &combos = m_ShaderNameToCombos[pShaderName]; char filename[MAX_PATH]; // try the vsh dir first. Q_strncpy( filename, GetShaderSourcePath(), MAX_PATH ); Q_strncat( filename, "\\", MAX_PATH, COPY_ALL_CHARACTERS ); Q_strncat( filename, pShaderName, MAX_PATH, COPY_ALL_CHARACTERS ); Q_strncat( filename, ".vsh", MAX_PATH, COPY_ALL_CHARACTERS ); CUtlInplaceBuffer bffr( 0, 0, CUtlInplaceBuffer::TEXT_BUFFER ); if ( bool bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ) ) { NULL; } else { // try the fxc dir. Q_strncpy( filename, GetShaderSourcePath(), MAX_PATH ); Q_strncat( filename, "\\", MAX_PATH, COPY_ALL_CHARACTERS ); Q_strncat( filename, pShaderName, MAX_PATH, COPY_ALL_CHARACTERS ); Q_strncat( filename, ".fxc", MAX_PATH, COPY_ALL_CHARACTERS ); bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); if ( !bOpenResult ) { // Maybe this is a specific version [20 & 20b] -> [2x] if ( Q_strlen( pShaderName ) >= 3 ) { char *pszEndFilename = filename + strlen( filename ); if ( !Q_stricmp( pszEndFilename - 6, "30.fxc" ) ) { // Total hack. Who knows what builds that 30 shader? strcpy( pszEndFilename - 6, "20b.fxc" ); bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); if ( !bOpenResult ) { strcpy( pszEndFilename - 6, "2x.fxc" ); bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); } if ( !bOpenResult ) { strcpy( pszEndFilename - 6, "20.fxc" ); bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); } } else { if ( !stricmp( pszEndFilename - 6, "20.fxc" ) ) { pszEndFilename[ -5 ] = 'x'; } else if ( !stricmp( pszEndFilename - 7, "20b.fxc" ) ) { strcpy( pszEndFilename - 7, "2x.fxc" ); --pszEndFilename; } else if ( !stricmp( pszEndFilename - 6, "11.fxc" ) ) { strcpy( pszEndFilename - 6, "xx.fxc" ); } bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); if ( !bOpenResult ) { if ( !stricmp( pszEndFilename - 6, "2x.fxc" ) ) { pszEndFilename[ -6 ] = 'x'; bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr ); } } } } } if ( !bOpenResult ) { Assert( 0 ); return NULL; } } while( char *line = bffr.InplaceGetLinePtr() ) { // dear god perl is better at this kind of shit! int begin = 0; int end = 0; // check if the line starts with '//' if( line[0] != '/' || line[1] != '/' ) { continue; } // Check if line intended for platform lines if( IsX360() ) { if ( Q_stristr( line, "[PC]" ) ) continue; } else { if ( Q_stristr( line, "[360]" ) || Q_stristr( line, "[XBOX]" ) ) continue; } // Skip any lines intended for other shader version if ( Q_stristr( pShaderName, "_ps20" ) && !Q_stristr( pShaderName, "_ps20b" ) && Q_stristr( line, "[ps" ) && !Q_stristr( line, "[ps20]" ) ) continue; if ( Q_stristr( pShaderName, "_ps20b" ) && Q_stristr( line, "[ps" ) && !Q_stristr( line, "[ps20b]" ) ) continue; if ( Q_stristr( pShaderName, "_ps30" ) && Q_stristr( line, "[ps" ) && !Q_stristr( line, "[ps30]" ) ) continue; if ( Q_stristr( pShaderName, "_ps40" ) && Q_stristr( line, "[ps" ) && !Q_stristr( line, "[ps40]" ) ) continue; if ( Q_stristr( pShaderName, "_vs20" ) && Q_stristr( line, "[vs" ) && !Q_stristr( line, "[vs20]" ) ) continue; if ( Q_stristr( pShaderName, "_vs30" ) && Q_stristr( line, "[vs" ) && !Q_stristr( line, "[vs30]" ) ) continue; if ( Q_stristr( pShaderName, "_vs40" ) && Q_stristr( line, "[vs" ) && !Q_stristr( line, "[vs40]" ) ) continue; char *pScan = &line[2]; while( *pScan == ' ' || *pScan == '\t' ) { pScan++; } bool bDynamic; if( Q_strncmp( pScan, "DYNAMIC", 7 ) == 0 ) { bDynamic = true; pScan += 7; } else if( Q_strncmp( pScan, "STATIC", 6 ) == 0 ) { bDynamic = false; pScan += 6; } else { continue; } // skip whitespace while( *pScan == ' ' || *pScan == '\t' ) { pScan++; } // check for colon if( *pScan != ':' ) { continue; } pScan++; // skip whitespace while( *pScan == ' ' || *pScan == '\t' ) { pScan++; } // check for quote if( *pScan != '\"' ) { continue; } pScan++; char *pBeginningOfName = pScan; while( 1 ) { if( *pScan == '\0' ) { break; } if( *pScan == '\"' ) { break; } pScan++; } if( *pScan == '\0' ) { continue; } // must have hit a quote. .done with string. // slam a NULL at the end quote of the string so that we have the string at pBeginningOfName. *pScan = '\0'; pScan++; // skip whitespace while( *pScan == ' ' || *pScan == '\t' ) { pScan++; } // check for quote if( *pScan != '\"' ) { continue; } pScan++; // make sure that we have a number after the quote. if( !isdigit( *pScan ) ) { continue; } while( isdigit( *pScan ) ) { begin = begin * 10 + ( *pScan - '0' ); pScan++; } if( pScan[0] != '.' || pScan[1] != '.' ) { continue; } pScan += 2; // make sure that we have a number if( !isdigit( *pScan ) ) { continue; } while( isdigit( *pScan ) ) { end = end * 10 + ( *pScan - '0' ); pScan++; } if( pScan[0] != '\"' ) { continue; } // sweet freaking jesus. .done parsing the line. // char buf[1024]; // sprintf( buf, "\"%s\" \"%s\" %d %d\n", bDynamic ? "DYNAMIC" : "STATIC", pBeginningOfName, begin, end ); // Plat_DebugString( buf ); Combo_t *pCombo = NULL; if( bDynamic ) { pCombo = &combos.m_DynamicCombos[combos.m_DynamicCombos.AddToTail()]; } else { pCombo = &combos.m_StaticCombos[combos.m_StaticCombos.AddToTail()]; } pCombo->m_ComboName = m_ShaderSymbolTable.AddString( pBeginningOfName ); pCombo->m_nMin = begin; pCombo->m_nMax = end; } return &combos; } #endif // DYNAMIC_SHADER_COMPILE #ifdef DYNAMIC_SHADER_COMPILE //----------------------------------------------------------------------------- // Used to deal with include files //----------------------------------------------------------------------------- class CDxInclude : public ID3DInclude { public: CDxInclude( const char *pMainFileName ); virtual HRESULT WINAPI Open( D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID * ppData, UINT * pBytes ); virtual HRESULT WINAPI Close( LPCVOID pData ); private: char m_pBasePath[MAX_PATH]; #if defined( _X360 ) char m_pFullPath[MAX_PATH]; #endif }; CDxInclude::CDxInclude( const char *pMainFileName ) { Q_ExtractFilePath( pMainFileName, m_pBasePath, sizeof(m_pBasePath) ); } HRESULT CDxInclude::Open( D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID * ppData, UINT * pBytes ) { char pTemp[MAX_PATH]; if ( !Q_IsAbsolutePath( pFileName ) && ( IncludeType == D3D_INCLUDE_LOCAL ) ) { Q_ComposeFileName( m_pBasePath, pFileName, pTemp, sizeof(pTemp) ); pFileName = pTemp; } CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); if ( !g_pFullFileSystem->ReadFile( pFileName, NULL, buf ) ) return E_FAIL; *pBytes = buf.TellMaxPut(); void *pMem = malloc( *pBytes ); memcpy( pMem, buf.Base(), *pBytes ); *ppData = pMem; # if ( defined( _X360 ) ) { Q_ComposeFileName( m_pBasePath, pFileName, m_pFullPath, sizeof(m_pFullPath) ); pFullPath = m_pFullPath; cbFullPath = MAX_PATH; } # endif return S_OK; } HRESULT CDxInclude::Close( LPCVOID pData ) { void *pMem = const_cast( pData ); free( pMem ); return S_OK; } static const char *FileNameToShaderModel( const char *pShaderName, bool bVertexShader ) { // Figure out the shader model const char *pShaderModel = NULL; if( bVertexShader ) { if ( Q_stristr( pShaderName, "vs40" ) ) { pShaderModel = "vs_4_0"; bVertexShader = true; } else if( Q_stristr( pShaderName, "vs20" ) ) { pShaderModel = "vs_2_0"; bVertexShader = true; } else if( Q_stristr( pShaderName, "vs11" ) ) { pShaderModel = "vs_1_1"; bVertexShader = true; } else if( Q_stristr( pShaderName, "vs14" ) ) { pShaderModel = "vs_1_1"; bVertexShader = true; } else if( Q_stristr( pShaderName, "vs30" ) ) { pShaderModel = "vs_3_0"; bVertexShader = true; } else { #ifdef _DEBUG Error( "Failed dynamic shader compiled\nBuild shaderapidx9.dll in debug to find problem\n" ); #else Assert( 0 ); #endif } } else { if ( Q_stristr( pShaderName, "ps40" ) ) { pShaderModel = "ps_4_0"; } else if( Q_stristr( pShaderName, "ps20b" ) ) { pShaderModel = "ps_2_b"; } else if( Q_stristr( pShaderName, "ps20" ) ) { pShaderModel = "ps_2_0"; } else if( Q_stristr( pShaderName, "ps11" ) ) { pShaderModel = "ps_1_1"; } else if( Q_stristr( pShaderName, "ps14" ) ) { pShaderModel = "ps_1_4"; } else if( Q_stristr( pShaderName, "ps30" ) ) { pShaderModel = "ps_3_0"; } else { #ifdef _DEBUG Error( "Failed dynamic shader compiled\nBuild shaderapidx9.dll in debug to find problem\n" ); #else Assert( 0 ); #endif } } return pShaderModel; } #endif #ifdef DYNAMIC_SHADER_COMPILE #if defined( _X360 ) static ConVar mat_flushshaders_generate_updbs( "mat_flushshaders_generate_updbs", "0", 0, "Generates UPDBs whenever you flush shaders." ); #endif HardwareShader_t CShaderManager::CompileShader( const char *pShaderName, ShaderIndex_t nStaticIndex, ShaderIndex_t nDynamicIndex, bool bVertexShader ) { VPROF_BUDGET( "CompileShader", "CompileShader" ); Assert( m_ShaderNameToCombos.Defined( pShaderName ) ); if( !m_ShaderNameToCombos.Defined( pShaderName ) ) { return INVALID_HARDWARE_SHADER; } const ShaderCombos_t &combos = m_ShaderNameToCombos[pShaderName]; #ifdef _DEBUG uint64 numStaticCombos = combos.GetNumStaticCombos(); uint64 numDynamicCombos = combos.GetNumDynamicCombos(); #endif Assert( nStaticIndex % numDynamicCombos == 0 ); Assert( ( nStaticIndex % numDynamicCombos ) >= 0 && ( nStaticIndex % numDynamicCombos ) < numStaticCombos ); Assert( nDynamicIndex >= 0 && nDynamicIndex < numDynamicCombos ); # ifdef DYNAMIC_SHADER_COMPILE_VERBOSE //Warning( "Compiling %s %s\n\tdynamic:", bVertexShader ? "vsh" : "psh", pShaderName ); Warning( "Compiling " ); if ( bVertexShader ) ConColorMsg( Color( 0, 255, 0, 255 ), "vsh - %s ", pShaderName ); else ConColorMsg( Color( 0, 255, 255, 255 ), "psh - %s ", pShaderName ); Warning( "\n\tdynamic:" ); # endif CUtlVector macros; // plus 1 for null termination, plus 1 for #define SHADER_MODEL_*, and plus 1 for #define _X360 on 360 macros.SetCount( combos.m_DynamicCombos.Count() + combos.m_StaticCombos.Count() + 2 + ( IsX360() ? 1 : 0 ) ); uint64 nCombo = nStaticIndex + nDynamicIndex; int macroIndex = 0; int i; for( i = 0; i < combos.m_DynamicCombos.Count(); i++ ) { int countForCombo = combos.m_DynamicCombos[i].m_nMax - combos.m_DynamicCombos[i].m_nMin + 1; int val = nCombo % countForCombo + combos.m_DynamicCombos[i].m_nMin; nCombo /= countForCombo; macros[macroIndex].Name = m_ShaderSymbolTable.String( combos.m_DynamicCombos[i].m_ComboName ); char buf[16]; sprintf( buf, "%d", val ); CUtlSymbol valSymbol( buf ); macros[macroIndex].Definition = valSymbol.String(); # ifdef DYNAMIC_SHADER_COMPILE_VERBOSE Warning( " %s=%s", macros[macroIndex].Name, macros[macroIndex].Definition ); # endif macroIndex++; } # ifdef DYNAMIC_SHADER_COMPILE_VERBOSE Warning( "\n\tstatic:" ); # endif for( i = 0; i < combos.m_StaticCombos.Count(); i++ ) { int countForCombo = combos.m_StaticCombos[i].m_nMax - combos.m_StaticCombos[i].m_nMin + 1; int val = nCombo % countForCombo + combos.m_StaticCombos[i].m_nMin; nCombo /= countForCombo; macros[macroIndex].Name = m_ShaderSymbolTable.String( combos.m_StaticCombos[i].m_ComboName ); char buf[16]; sprintf( buf, "%d", val ); CUtlSymbol valSymbol( buf ); macros[macroIndex].Definition = valSymbol.String(); # ifdef DYNAMIC_SHADER_COMPILE_VERBOSE Warning( " %s=%s", macros[macroIndex].Name, macros[macroIndex].Definition ); # endif macroIndex++; } # ifdef DYNAMIC_SHADER_COMPILE_VERBOSE Warning( "\n" ); # endif char filename[MAX_PATH]; Q_strncpy( filename, GetShaderSourcePath(), MAX_PATH ); Q_strncat( filename, "\\", MAX_PATH, COPY_ALL_CHARACTERS ); Q_strncat( filename, pShaderName, MAX_PATH, COPY_ALL_CHARACTERS ); Q_strncat( filename, ".fxc", MAX_PATH, COPY_ALL_CHARACTERS ); const char *pShaderModel = FileNameToShaderModel( pShaderName, bVertexShader ); // define the shader model char shaderModelDefineString[1024]; Q_snprintf( shaderModelDefineString, 1024, "SHADER_MODEL_%s", pShaderModel ); Q_strupr( shaderModelDefineString ); macros[macroIndex].Name = shaderModelDefineString; macros[macroIndex].Definition = "1"; macroIndex++; char x360DefineString[1024]; if( IsX360() ) { Q_snprintf( x360DefineString, 1024, "_X360", pShaderModel ); Q_strupr( x360DefineString ); macros[macroIndex].Name = x360DefineString; macros[macroIndex].Definition = "1"; macroIndex++; } // NULL terminate. macros[macroIndex].Name = NULL; macros[macroIndex].Definition = NULL; // Instead of erroring out, infinite-loop on shader compilation // (i.e. give developers a chance to fix the shader code w/out restarting the game) #ifdef _DEBUG int retriesLeft = 20; retry_compile: #endif // Try and open the file to see if it exists FileHandle_t fp = g_pFullFileSystem->Open( filename, "r" ); if ( fp == FILESYSTEM_INVALID_HANDLE ) { // Maybe this is a specific version [20 & 20b] -> [2x] if ( strlen( pShaderName ) >= 3 ) { char *pszEndFilename = filename + strlen( filename ); if ( !Q_stricmp( pszEndFilename - 6, "30.fxc" ) ) { strcpy( pszEndFilename - 6, "20b.fxc" ); fp = g_pFullFileSystem->Open( filename, "r" ); if ( fp == FILESYSTEM_INVALID_HANDLE ) { strcpy( pszEndFilename - 6, "2x.fxc" ); fp = g_pFullFileSystem->Open( filename, "r" ); } if ( fp == FILESYSTEM_INVALID_HANDLE ) { strcpy( pszEndFilename - 6, "20.fxc" ); fp = g_pFullFileSystem->Open( filename, "r" ); } } else { if ( !Q_stricmp( pszEndFilename - 6, "20.fxc" ) ) { pszEndFilename[ -5 ] = 'x'; fp = g_pFullFileSystem->Open( filename, "r" ); } else if ( !Q_stricmp( pszEndFilename - 7, "20b.fxc" ) ) { strcpy( pszEndFilename - 7, "2x.fxc" ); fp = g_pFullFileSystem->Open( filename, "r" ); } else if ( !stricmp( pszEndFilename - 6, "11.fxc" ) ) { strcpy( pszEndFilename - 6, "xx.fxc" ); fp = g_pFullFileSystem->Open( filename, "r" ); } if ( fp == FILESYSTEM_INVALID_HANDLE ) { if ( !stricmp( pszEndFilename - 6, "2x.fxc" ) ) { pszEndFilename[ -6 ] = 'x'; fp = g_pFullFileSystem->Open( filename, "r" ); } } } } } if ( fp != FILESYSTEM_INVALID_HANDLE ) { g_pFullFileSystem->Close( fp ); } wchar_t wfilename[MAX_PATH]; memset( wfilename, 0, MAX_PATH ); //mbtowc( wfilename, filename, strlen( filename ) ); int nCompileFlags = D3DCOMPILE_AVOID_FLOW_CONTROL; nCompileFlags |= D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY; #ifdef _DEBUG nCompileFlags |= D3DCOMPILE_DEBUG; #endif LPD3DBLOB pShader; LPD3DBLOB pErrorMessages; HRESULT hr; CDxInclude dxInclude( filename ); // Open the top-level file via our include interface LPCVOID lpcvData; UINT numBytes; hr = dxInclude.Open( (D3D_INCLUDE_TYPE)0, filename, NULL, &lpcvData, &numBytes ); LPCSTR pShaderData = (LPCSTR)lpcvData; MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, filename, -1, wfilename, MAX_PATH ); hr = D3DCompile( pShaderData, numBytes, filename, macros.Base(), &dxInclude, "main", pShaderModel, nCompileFlags, 0, &pShader, &pErrorMessages ); dxInclude.Close( lpcvData ); if ( hr != S_OK && pErrorMessages ) { Warning( "Failed to compile shader %s:\n", filename ); const char *pErrorMessageString = ( const char * )pErrorMessages->GetBufferPointer(); Warning( pErrorMessageString ); Warning( "\n" ); #if 0//#ifndef _DEBUG if ( retriesLeft-- > 0 ) { DevMsg( 0, "Failed dynamic shader compiled - fix the shader while the debugger is at the breakpoint, then continue\n" ); DebuggerBreakIfDebugging(); goto retry_compile; } if( !IsX360() ) //errors make the 360 puke and die. We have a better solution for this particular error Error( "Failed dynamic shader compile\nBuild shaderapidx9.dll in debug to find problem\n" ); #else Assert( 0 ); #endif return INVALID_HARDWARE_SHADER; } else { #ifdef DYNAMIC_SHADER_COMPILE_WRITE_ASSEMBLY // enable to dump the disassembly for shader validation char exampleCommandLine[2048]; Q_strncpy( exampleCommandLine, "// Run from stdshaders\n// ..\\..\\dx9sdk\\utilities\\fxc.exe ", sizeof( exampleCommandLine ) ); int i; for( i = 0; macros[i].Name; i++ ) { Q_strncat( exampleCommandLine, "/D", sizeof( exampleCommandLine ) ); Q_strncat( exampleCommandLine, macros[i].Name, sizeof( exampleCommandLine ) ); Q_strncat( exampleCommandLine, "=", sizeof( exampleCommandLine ) ); Q_strncat( exampleCommandLine, macros[i].Definition, sizeof( exampleCommandLine ) ); Q_strncat( exampleCommandLine, " ", sizeof( exampleCommandLine ) ); } Q_strncat( exampleCommandLine, "/T", sizeof( exampleCommandLine ) ); Q_strncat( exampleCommandLine, pShaderModel, sizeof( exampleCommandLine ) ); Q_strncat( exampleCommandLine, " ", sizeof( exampleCommandLine ) ); Q_strncat( exampleCommandLine, filename, sizeof( exampleCommandLine ) ); Q_strncat( exampleCommandLine, "\n", sizeof( exampleCommandLine ) ); ID3DBlob *pd3dxBuffer; HRESULT hr; hr = D3DDisassemble( pShader->GetBufferPointer(), pShader->GetBufferSize(), 0, NULL, &pd3dxBuffer ); Assert( hr == D3D_OK ); CUtlBuffer tempBuffer; tempBuffer.SetBufferType( true, false ); int exampleCommandLineLength = strlen( exampleCommandLine ); tempBuffer.EnsureCapacity( pd3dxBuffer->GetBufferSize() + exampleCommandLineLength ); memcpy( tempBuffer.Base(), exampleCommandLine, exampleCommandLineLength ); memcpy( ( char * )tempBuffer.Base() + exampleCommandLineLength, pd3dxBuffer->GetBufferPointer(), pd3dxBuffer->GetBufferSize() ); tempBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, pd3dxBuffer->GetBufferSize() + exampleCommandLineLength ); char filename[MAX_PATH]; sprintf( filename, "%s_%d_%d.asm", pShaderName, nStaticIndex, nDynamicIndex ); g_pFullFileSystem->WriteFile( filename, "DEFAULT_WRITE_PATH", tempBuffer ); #endif if ( bVertexShader ) { return g_pShaderDeviceDx11->CreateVertexShader( pShader->GetBufferPointer(), pShader->GetBufferSize() ); } else { return g_pShaderDeviceDx11->CreatePixelShader( pShader->GetBufferPointer(), pShader->GetBufferSize() ); // hack hack hack! need to get centroid info from the source } } if ( pShader ) { pShader->Release(); } if ( pErrorMessages ) { pErrorMessages->Release(); } } #endif #ifdef DYNAMIC_SHADER_COMPILE bool CShaderManager::LoadAndCreateShaders_Dynamic( ShaderLookupDx11_t &lookup, bool bVertexShader ) { const char *pName = m_ShaderSymbolTable.String( lookup.m_Name ); const ShaderCombos_t *pCombos = FindOrCreateShaderCombos( pName ); if ( !pCombos ) { return false; } int numDynamicCombos = pCombos->GetNumDynamicCombos(); lookup.m_ShaderStaticCombos.m_pHardwareShaders = new HardwareShader_t[numDynamicCombos]; lookup.m_ShaderStaticCombos.m_nCount = numDynamicCombos; lookup.m_ShaderStaticCombos.m_pCreationData = new ShaderStaticCombos_t::ShaderCreationData_t[numDynamicCombos]; int i; for( i = 0; i < numDynamicCombos; i++ ) { lookup.m_ShaderStaticCombos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER; } return true; } #endif //----------------------------------------------------------------------------- // Open the shader file, optionally gets the header //----------------------------------------------------------------------------- FileHandle_t CShaderManager::OpenFileAndLoadHeader( const char *pFileName, ShaderHeader_t *pHeader ) { //FileHandle_t fp = g_pFullFileSystem->Open( pFileName, "rb", "GAME" ); FileHandle_t fp = g_pFullFileSystem->Open( pFileName, "rb", "CORE" ); if ( fp == FILESYSTEM_INVALID_HANDLE ) { return FILESYSTEM_INVALID_HANDLE; } if ( pHeader ) { // read the header g_pFullFileSystem->Read( pHeader, sizeof( ShaderHeader_t ), fp ); switch ( pHeader->m_nVersion ) { case 4: // version with combos done as diffs vs a reference combo // vsh/psh or older fxc break; case 5: case 6: // version with optimal dictionary and compressed combo block break; default: Assert( 0 ); Warning( "Shader %s is the wrong version %d, expecting %d\n", pFileName, pHeader->m_nVersion, SHADER_VCS_VERSION_NUMBER ); g_pFullFileSystem->Close( fp ); return FILESYSTEM_INVALID_HANDLE; } } return fp; } //----------------------------------------------------------------------------- // Disassemble a shader for debugging. Writes .asm files. //----------------------------------------------------------------------------- void CShaderManager::DisassembleShader( ShaderLookupDx11_t *pLookup, ShaderIndex_t dynamicCombo, uint8 *pByteCode ) { #if defined( WRITE_ASSEMBLY ) const char *pName = m_ShaderSymbolTable.String( pLookup->m_Name ); ID3DXBuffer *pd3dxBuffer; HRESULT hr; hr = D3DXDisassembleShader( (DWORD*)pByteCode, false, NULL, &pd3dxBuffer ); Assert( hr == D3D_OK ); CUtlBuffer tempBuffer; tempBuffer.SetBufferType( true, false ); tempBuffer.EnsureCapacity( pd3dxBuffer->GetBufferSize() ); memcpy( ( char * )tempBuffer.Base(), pd3dxBuffer->GetBufferPointer(), pd3dxBuffer->GetBufferSize() ); tempBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, pd3dxBuffer->GetBufferSize() ); char filename[MAX_PATH]; sprintf( filename, "%s_%d_%d.asm", pName, pLookup->m_nStaticIndex, dynamicCombo ); g_pFullFileSystem->WriteFile( filename, "DEFAULT_WRITE_PATH", tempBuffer ); #endif } //----------------------------------------------------------------------------- // Create dynamic combos //----------------------------------------------------------------------------- bool CShaderManager::CreateDynamicCombos_Ver4( void *pContext, uint8 *pComboBuffer ) { ShaderLookupDx11_t* pLookup = (ShaderLookupDx11_t *)pContext; ShaderFileCache_t *pFileCache = &m_ShaderFileCache[pLookup->m_hShaderFileCache]; ShaderHeader_t *pHeader = &pFileCache->m_Header; int nReferenceComboSizeForDiffs = ((ShaderHeader_t_v4 *)pHeader)->m_nDiffReferenceSize; uint8 *pReferenceShader = NULL; uint8 *pDiffOutputBuffer = NULL; if ( nReferenceComboSizeForDiffs ) { // reference combo is *always* the largest combo, so safe worst case size for uncompression buffer pReferenceShader = (uint8 *)pFileCache->m_ReferenceCombo.Base(); pDiffOutputBuffer = (uint8 *)_alloca( nReferenceComboSizeForDiffs ); } // build this shader's dynamic combos bool bOK = true; int nStartingOffset = 0; for ( int i = 0; i < pHeader->m_nDynamicCombos; i++ ) { if ( pLookup->m_pComboDictionary[i].m_Offset == -1 ) { // skipped continue; } if ( !nStartingOffset ) { nStartingOffset = pLookup->m_pComboDictionary[i].m_Offset; } // offsets better be sequentially ascending Assert( nStartingOffset <= pLookup->m_pComboDictionary[i].m_Offset ); if ( pLookup->m_pComboDictionary[i].m_Size <= 0 ) { // skipped continue; } // get the right byte code from the monolithic buffer uint8 *pByteCode = (uint8 *)pComboBuffer + pLookup->m_nDataOffset + pLookup->m_pComboDictionary[i].m_Offset - nStartingOffset; int nByteCodeSize = pLookup->m_pComboDictionary[i].m_Size; if ( pReferenceShader ) { // reference combo better be the largest combo, otherwise memory corruption Assert( nReferenceComboSizeForDiffs >= nByteCodeSize ); // use the differencing algorithm to recover the full shader int nOriginalSize; ApplyDiffs( pReferenceShader, pByteCode, nReferenceComboSizeForDiffs, nByteCodeSize, nOriginalSize, pDiffOutputBuffer, nReferenceComboSizeForDiffs ); pByteCode = pDiffOutputBuffer; nByteCodeSize = nOriginalSize; } #if defined( WRITE_ASSEMBLY ) DisassembleShader( pLookup, i, pByteCode ); #endif HardwareShader_t hardwareShader = INVALID_HARDWARE_SHADER; if ( IsPC() && m_bCreateShadersOnDemand ) { // cache the code off for later pLookup->m_ShaderStaticCombos.m_pCreationData[i].ByteCode.SetSize( nByteCodeSize ); V_memcpy( pLookup->m_ShaderStaticCombos.m_pCreationData[i].ByteCode.Base(), pByteCode, nByteCodeSize ); pLookup->m_ShaderStaticCombos.m_pCreationData[i].iCentroidMask = pFileCache->m_bVertexShader ? 0 : pHeader->m_nCentroidMask; } else { if ( pFileCache->m_bVertexShader ) { hardwareShader = g_pShaderDeviceDx11->CreateVertexShader( reinterpret_cast< DWORD *>( pByteCode ), nByteCodeSize ); } else { hardwareShader = g_pShaderDeviceDx11->CreatePixelShader( reinterpret_cast< DWORD *>( pByteCode ), nByteCodeSize ); } if ( hardwareShader == INVALID_HARDWARE_SHADER ) { Assert( 0 ); bOK = false; break; } } pLookup->m_ShaderStaticCombos.m_pHardwareShaders[i] = hardwareShader; } delete [] pLookup->m_pComboDictionary; pLookup->m_pComboDictionary = NULL; return bOK; } //----------------------------------------------------------------------------- // Create dynamic combos //----------------------------------------------------------------------------- static uint32 NextULONG( uint8 * &pData ) { // handle unaligned read uint32 nRet; memcpy( &nRet, pData, sizeof( nRet ) ); pData += sizeof( nRet ); return nRet; } bool CShaderManager::CreateDynamicCombos_Ver5( void *pContext, uint8 *pComboBuffer ) { ShaderLookupDx11_t* pLookup = (ShaderLookupDx11_t *)pContext; ShaderFileCache_t *pFileCache = &m_ShaderFileCache[pLookup->m_hShaderFileCache]; uint8 *pCompressedShaders = pComboBuffer + pLookup->m_nDataOffset; uint8 *pUnpackBuffer = new uint8[MAX_SHADER_UNPACKED_BLOCK_SIZE]; // now, loop through all blocks bool bOK = true; while ( bOK ) { uint32 nBlockSize = NextULONG( pCompressedShaders ); if ( nBlockSize == 0xffffffff ) { // any more blocks? break; } switch( nBlockSize & 0xc0000000 ) { case 0: // bzip2 { // uncompress uint32 nOutsize = MAX_SHADER_UNPACKED_BLOCK_SIZE; int nRslt = BZ2_bzBuffToBuffDecompress( reinterpret_cast( pUnpackBuffer ), &nOutsize, reinterpret_cast( pCompressedShaders ), nBlockSize, 1, 0 ); if ( nRslt < 0 ) { // errors are negative for bzip Assert( 0 ); Warning( "BZIP Error (%d) decompressing shader", nRslt ); bOK = false; } pCompressedShaders += nBlockSize; nBlockSize = nOutsize; // how much data there is } break; case 0x80000000: // uncompressed { // not compressed, as is nBlockSize &= 0x3fffffff; memcpy( pUnpackBuffer, pCompressedShaders, nBlockSize ); pCompressedShaders += nBlockSize; } break; case 0x40000000: // lzma compressed { CLZMA lzDecoder; nBlockSize &= 0x3fffffff; size_t nOutsize = lzDecoder.Uncompress( reinterpret_cast( pCompressedShaders ), pUnpackBuffer ); pCompressedShaders += nBlockSize; nBlockSize = nOutsize; // how much data there is } break; default: { Assert( 0 ); Error(" unrecognized shader compression type = file corrupt?"); bOK = false; } } uint8 *pReadPtr = pUnpackBuffer; while ( pReadPtr < pUnpackBuffer+nBlockSize ) { uint32 nCombo_ID = NextULONG( pReadPtr ); uint32 nShaderSize = NextULONG( pReadPtr ); #if defined( WRITE_ASSEMBLY ) DisassembleShader( pLookup, nCombo_ID, pReadPtr ); #endif HardwareShader_t hardwareShader = INVALID_HARDWARE_SHADER; int iIndex = nCombo_ID; if ( iIndex >= pLookup->m_nStaticIndex ) iIndex -= pLookup->m_nStaticIndex; // ver5 stores combos as full combo, ver6 as dynamic combo # only if ( IsPC() && m_bCreateShadersOnDemand ) { // cache the code off for later pLookup->m_ShaderStaticCombos.m_pCreationData[iIndex].ByteCode.SetSize( nShaderSize ); V_memcpy( pLookup->m_ShaderStaticCombos.m_pCreationData[iIndex].ByteCode.Base(), pReadPtr, nShaderSize ); pLookup->m_ShaderStaticCombos.m_pCreationData[iIndex].iCentroidMask = pFileCache->m_bVertexShader ? 0 : pFileCache->m_Header.m_nCentroidMask; } else { if ( pFileCache->m_bVertexShader ) { hardwareShader = g_pShaderDeviceDx11->CreateVertexShader( reinterpret_cast< DWORD *>( pReadPtr ), nShaderSize ); } else { hardwareShader = g_pShaderDeviceDx11->CreatePixelShader( reinterpret_cast< DWORD *>( pReadPtr ), nShaderSize ); } if ( hardwareShader == INVALID_HARDWARE_SHADER ) { Warning( "failed to create shader\n" ); Assert( 0 ); bOK = false; break; } } pLookup->m_ShaderStaticCombos.m_pHardwareShaders[iIndex] = hardwareShader; pReadPtr += nShaderSize; } } delete[] pUnpackBuffer; return bOK; } //----------------------------------------------------------------------------- // Static method, called by thread, don't call anything non-threadsafe from handler!!! //----------------------------------------------------------------------------- void CShaderManager::QueuedLoaderCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError ) { ShaderLookupDx11_t* pLookup = (ShaderLookupDx11_t *)pContext; bool bOK = ( loaderError == LOADERERROR_NONE ); if ( bOK ) { if ( pContext2 ) { // presence denotes diff version bOK = s_ShaderManager.CreateDynamicCombos_Ver4( pContext, (uint8 *)pData ); } else { bOK = s_ShaderManager.CreateDynamicCombos_Ver5( pContext, (uint8 *)pData ); } } if ( !bOK ) { pLookup->m_Flags |= SHADER_FAILED_LOAD; } } //----------------------------------------------------------------------------- // Loads all shaders //----------------------------------------------------------------------------- bool CShaderManager::LoadAndCreateShaders( ShaderLookupDx11_t &lookup, bool bVertexShader ) { #ifdef DYNAMIC_SHADER_COMPILE lookup.m_Flags &= ~SHADER_DYNAMIC_COMPILE_IS_HLSL; #endif const char *pName = m_ShaderSymbolTable.String( lookup.m_Name ); // find it in the cache // a cache hit prevents costly i/o for static components, i.e. header, ref combo, etc. ShaderFileCache_t fileCacheLookup; fileCacheLookup.m_Name = lookup.m_Name; fileCacheLookup.m_bVertexShader = bVertexShader; int fileCacheIndex = m_ShaderFileCache.Find( fileCacheLookup ); if ( fileCacheIndex == m_ShaderFileCache.InvalidIndex() ) { // not found, create a new entry fileCacheIndex = m_ShaderFileCache.AddToTail(); } lookup.m_hShaderFileCache = fileCacheIndex; // fetch from cache ShaderFileCache_t *pFileCache = &m_ShaderFileCache[fileCacheIndex]; ShaderHeader_t *pHeader = &pFileCache->m_Header; FileHandle_t hFile = FILESYSTEM_INVALID_HANDLE; if ( pFileCache->IsValid() ) { // using cached header, just open file, no read of header needed hFile = OpenFileAndLoadHeader( m_ShaderSymbolTable.String( pFileCache->m_Filename ), NULL ); if ( hFile == FILESYSTEM_INVALID_HANDLE ) { // shouldn't happen Assert( 0 ); return false; } } else { V_memset( pHeader, 0, sizeof( ShaderHeader_t ) ); // try the vsh/psh dir first char filename[MAX_PATH]; Q_snprintf( filename, MAX_PATH, "shaders\\%s\\%s" SHADER_FNAME_EXTENSION, bVertexShader ? "vsh" : "psh", pName ); hFile = OpenFileAndLoadHeader( filename, pHeader ); if ( hFile == FILESYSTEM_INVALID_HANDLE ) { #ifdef DYNAMIC_SHADER_COMPILE // Dynamically compile if it's HLSL. if ( LoadAndCreateShaders_Dynamic( lookup, bVertexShader ) ) { lookup.m_Flags |= SHADER_DYNAMIC_COMPILE_IS_HLSL; return true; } else { return false; } #endif // next, try the fxc dir Q_snprintf( filename, MAX_PATH, "shaders\\fxc\\%s" SHADER_FNAME_EXTENSION, pName ); hFile = OpenFileAndLoadHeader( filename, pHeader ); if ( hFile == FILESYSTEM_INVALID_HANDLE ) { lookup.m_Flags |= SHADER_FAILED_LOAD; Warning( "Couldn't load %s shader %s\n", bVertexShader ? "vertex" : "pixel", pName ); return false; } } lookup.m_Flags = pHeader->m_nFlags; pFileCache->m_Name = lookup.m_Name; pFileCache->m_Filename = m_ShaderSymbolTable.AddString( filename ); pFileCache->m_bVertexShader = bVertexShader; if ( pFileCache->IsOldVersion() ) { int referenceComboSize = ((ShaderHeader_t_v4 *)pHeader)->m_nDiffReferenceSize; if ( referenceComboSize ) { // cache the reference combo pFileCache->m_ReferenceCombo.EnsureCapacity( referenceComboSize ); g_pFullFileSystem->Read( pFileCache->m_ReferenceCombo.Base(), referenceComboSize, hFile ); } } else { // cache the dictionary pFileCache->m_StaticComboRecords.EnsureCount( pHeader->m_nNumStaticCombos ); g_pFullFileSystem->Read( pFileCache->m_StaticComboRecords.Base(), pHeader->m_nNumStaticCombos * sizeof( StaticComboRecord_t ), hFile ); if ( pFileCache->IsVersion6() ) { // read static combo alias records int nNumDups; g_pFullFileSystem->Read( &nNumDups, sizeof( nNumDups ), hFile ); if ( nNumDups ) { pFileCache->m_StaticComboDupRecords.EnsureCount( nNumDups ); g_pFullFileSystem->Read( pFileCache->m_StaticComboDupRecords.Base(), nNumDups * sizeof( StaticComboAliasRecord_t ), hFile ); } } } } // FIXME: should make lookup and ShaderStaticCombos_t are pool allocated. int i; lookup.m_ShaderStaticCombos.m_nCount = pHeader->m_nDynamicCombos; lookup.m_ShaderStaticCombos.m_pHardwareShaders = new HardwareShader_t[pHeader->m_nDynamicCombos]; if ( IsPC() && m_bCreateShadersOnDemand ) { lookup.m_ShaderStaticCombos.m_pCreationData = new ShaderStaticCombos_t::ShaderCreationData_t[pHeader->m_nDynamicCombos]; } for ( i = 0; i < pHeader->m_nDynamicCombos; i++ ) { lookup.m_ShaderStaticCombos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER; } int nStartingOffset = 0; int nEndingOffset = 0; if ( pFileCache->IsOldVersion() ) { int nDictionaryOffset = sizeof( ShaderHeader_t ) + ((ShaderHeader_t_v4 *)pHeader)->m_nDiffReferenceSize; // read in shader's dynamic combos directory lookup.m_pComboDictionary = new ShaderDictionaryEntry_t[pHeader->m_nDynamicCombos]; g_pFullFileSystem->Seek( hFile, nDictionaryOffset + lookup.m_nStaticIndex * sizeof( ShaderDictionaryEntry_t ), FILESYSTEM_SEEK_HEAD ); g_pFullFileSystem->Read( lookup.m_pComboDictionary, pHeader->m_nDynamicCombos * sizeof( ShaderDictionaryEntry_t ), hFile ); // want single read of all this shader's dynamic combos into a target buffer // shaders are written sequentially, determine starting offset and length for ( i = 0; i < pHeader->m_nDynamicCombos; i++ ) { if ( lookup.m_pComboDictionary[i].m_Offset == -1 ) { // skipped continue; } // ensure offsets are in fact sequentially ascending Assert( lookup.m_pComboDictionary[i].m_Offset >= nStartingOffset && lookup.m_pComboDictionary[i].m_Size >= 0 ); if ( !nStartingOffset ) { nStartingOffset = lookup.m_pComboDictionary[i].m_Offset; } nEndingOffset = lookup.m_pComboDictionary[i].m_Offset + lookup.m_pComboDictionary[i].m_Size; } if ( !nStartingOffset ) { g_pFullFileSystem->Close( hFile ); Warning( "Shader '%s' - All dynamic combos skipped. This is bad!\n", m_ShaderSymbolTable.String( pFileCache->m_Filename ) ); return false; } } else { int nStaticComboIdx = pFileCache->FindCombo( lookup.m_nStaticIndex / pFileCache->m_Header.m_nDynamicCombos ); if ( nStaticComboIdx == -1 ) { g_pFullFileSystem->Close( hFile ); lookup.m_Flags |= SHADER_FAILED_LOAD; Warning( "Shader '%s' - Couldn't load combo %d of shader (dyn=%d)\n", m_ShaderSymbolTable.String( pFileCache->m_Filename ), lookup.m_nStaticIndex, pFileCache->m_Header.m_nDynamicCombos ); return false; } nStartingOffset = pFileCache->m_StaticComboRecords[nStaticComboIdx].m_nFileOffset; nEndingOffset = pFileCache->m_StaticComboRecords[nStaticComboIdx+1].m_nFileOffset; } // align offsets for unbuffered optimal i/o - fastest i/o possible unsigned nOffsetAlign, nSizeAlign, nBufferAlign; g_pFullFileSystem->GetOptimalIOConstraints( hFile, &nOffsetAlign, &nSizeAlign, &nBufferAlign ); unsigned int nAlignedOffset = AlignValue( ( nStartingOffset - nOffsetAlign ) + 1, nOffsetAlign ); unsigned int nAlignedBytesToRead = AlignValue( nEndingOffset - nAlignedOffset, nSizeAlign ); // used for adjusting provided buffer to actual data lookup.m_nDataOffset = nStartingOffset - nAlignedOffset; bool bOK = true; if ( IsX360() && g_pQueuedLoader->IsMapLoading() ) { LoaderJob_t loaderJob; loaderJob.m_pFilename = m_ShaderSymbolTable.String( pFileCache->m_Filename ); loaderJob.m_pPathID = "GAME"; loaderJob.m_pCallback = QueuedLoaderCallback; loaderJob.m_pContext = (void *)&lookup; loaderJob.m_pContext2 = (void *)pFileCache->IsOldVersion(); loaderJob.m_Priority = LOADERPRIORITY_DURINGPRELOAD; loaderJob.m_nBytesToRead = nAlignedBytesToRead; loaderJob.m_nStartOffset = nAlignedOffset; g_pQueuedLoader->AddJob( &loaderJob ); } else { // single optimal read of all dynamic combos into monolithic buffer uint8 *pOptimalBuffer = (uint8 *)g_pFullFileSystem->AllocOptimalReadBuffer( hFile, nAlignedBytesToRead, nAlignedOffset ); g_pFullFileSystem->Seek( hFile, nAlignedOffset, FILESYSTEM_SEEK_HEAD ); g_pFullFileSystem->Read( pOptimalBuffer, nAlignedBytesToRead, hFile ); if ( pFileCache->IsOldVersion() ) { bOK = CreateDynamicCombos_Ver4( &lookup, pOptimalBuffer ); } else { bOK = CreateDynamicCombos_Ver5( &lookup, pOptimalBuffer ); } g_pFullFileSystem->FreeOptimalReadBuffer( pOptimalBuffer ); } g_pFullFileSystem->Close( hFile ); if ( !bOK ) { lookup.m_Flags |= SHADER_FAILED_LOAD; } return bOK; } //----------------------------------------------------------------------------- // Creates and destroys vertex shaders //----------------------------------------------------------------------------- VertexShader_t CShaderManager::CreateVertexShader( const char *pFileName, ShaderIndex_t nStaticVshIndex ) { MEM_ALLOC_CREDIT(); if ( !pFileName ) { return INVALID_SHADER; } VertexShader_t shader; ShaderLookupDx11_t lookup; lookup.m_Name = m_ShaderSymbolTable.AddString( pFileName ); lookup.m_nStaticIndex = nStaticVshIndex; shader = m_VertexShaderDict.Find( lookup ); if ( shader == m_VertexShaderDict.InvalidIndex() ) { shader = m_VertexShaderDict.AddToTail( lookup ); if ( !LoadAndCreateShaders( m_VertexShaderDict[shader], true ) ) { return INVALID_SHADER; } } m_VertexShaderDict[shader].IncRefCount(); return shader; } //----------------------------------------------------------------------------- // Create pixel shader //----------------------------------------------------------------------------- PixelShader_t CShaderManager::CreatePixelShader( const char *pFileName, ShaderIndex_t nStaticPshIndex ) { MEM_ALLOC_CREDIT(); if ( !pFileName ) { return INVALID_SHADER; } PixelShader_t shader; ShaderLookupDx11_t lookup; lookup.m_Name = m_ShaderSymbolTable.AddString( pFileName ); lookup.m_nStaticIndex = nStaticPshIndex; shader = m_PixelShaderDict.Find( lookup ); if ( shader == m_PixelShaderDict.InvalidIndex() ) { shader = m_PixelShaderDict.AddToTail( lookup ); if ( !LoadAndCreateShaders( m_PixelShaderDict[shader], false ) ) { return INVALID_SHADER; } } m_PixelShaderDict[shader].IncRefCount(); return shader; } //----------------------------------------------------------------------------- // Clear the refCounts to zero //----------------------------------------------------------------------------- void CShaderManager::ClearVertexAndPixelShaderRefCounts() { for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); vshIndex != m_VertexShaderDict.InvalidIndex(); vshIndex = m_VertexShaderDict.Next( vshIndex ) ) { m_VertexShaderDict[vshIndex].m_nRefCount = 0; } for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); pshIndex != m_PixelShaderDict.InvalidIndex(); pshIndex = m_PixelShaderDict.Next( pshIndex ) ) { m_PixelShaderDict[pshIndex].m_nRefCount = 0; } } //----------------------------------------------------------------------------- // Destroy all shaders that have no reference //----------------------------------------------------------------------------- void CShaderManager::PurgeUnusedVertexAndPixelShaders() { return; // FIXME // iterate vertex shaders for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); vshIndex != m_VertexShaderDict.InvalidIndex(); ) { Assert( m_VertexShaderDict[vshIndex].m_nRefCount >= 0 ); // Get the next one before we potentially delete the current one. VertexShader_t next = m_VertexShaderDict.Next( vshIndex ); if ( m_VertexShaderDict[vshIndex].m_nRefCount <= 0 ) { DestroyVertexShader( vshIndex ); } vshIndex = next; } // iterate pixel shaders for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); pshIndex != m_PixelShaderDict.InvalidIndex(); ) { Assert( m_PixelShaderDict[pshIndex].m_nRefCount >= 0 ); // Get the next one before we potentially delete the current one. PixelShader_t next = m_PixelShaderDict.Next( pshIndex ); if ( m_PixelShaderDict[pshIndex].m_nRefCount <= 0 ) { DestroyPixelShader( pshIndex ); } pshIndex = next; } } void* CShaderManager::GetCurrentVertexShader() { return (void*)m_HardwareVertexShader; } void* CShaderManager::GetCurrentPixelShader() { return (void*)m_HardwarePixelShader; } //----------------------------------------------------------------------------- // The low-level dx call to set the vertex shader state //----------------------------------------------------------------------------- void CShaderManager::SetVertexShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader ) { } void CShaderManager::BindVertexShader( VertexShaderHandle_t hVertexShader ) { //if ( m_HardwareVertexShader != hVertexShader ) //{ g_pShaderAPIDx11->BindVertexShader( hVertexShader ); //m_HardwareVertexShader = hVertexShader; //} } //----------------------------------------------------------------------------- // Sets a particular vertex shader as the current shader //----------------------------------------------------------------------------- void CShaderManager::SetVertexShader( VertexShader_t shader ) { // Determine which vertex shader to use... if ( shader == INVALID_SHADER ) { BindVertexShader( 0 ); return; } int vshIndex = m_nVertexShaderIndex; Assert( vshIndex >= 0 ); if( vshIndex < 0 ) { vshIndex = 0; } //ShaderLookupDx11_t &tlookup = m_VertexShaderDict[shader]; //Warning( "vsh: %s static: %d dynamic: %d\n", m_ShaderSymbolTable.String( tlookup.m_Name ), // tlookup.m_nStaticIndex, m_nVertexShaderIndex ); #ifdef DYNAMIC_SHADER_COMPILE HardwareShader_t &dxshader = m_VertexShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[vshIndex]; if ( dxshader == INVALID_HARDWARE_SHADER ) { // compile it since we haven't already! ShaderLookupDx11_t &vshLookup = m_VertexShaderDict[shader]; dxshader = CompileShader( m_ShaderSymbolTable.String( vshLookup.m_Name ), vshLookup.m_nStaticIndex, vshIndex, true ); Assert( dxshader != INVALID_HARDWARE_SHADER ); } #else CShaderManager::ShaderLookupDx11_t *lookup = (ShaderLookupDx11_t *)&m_VertexShaderDict[shader]; if ( lookup->m_Flags & SHADER_FAILED_LOAD ) { Assert( 0 ); return; } #ifdef _DEBUG vshDebugIndex = (vshDebugIndex + 1) % MAX_SHADER_HISTORY; Q_strncpy( vshDebugName[vshDebugIndex], m_ShaderSymbolTable.String( lookup->m_Name ), sizeof( vshDebugName[0] ) ); #endif HardwareShader_t dxshader = lookup->m_ShaderStaticCombos.m_pHardwareShaders[vshIndex]; #endif if ( IsPC() && ( dxshader == INVALID_HARDWARE_SHADER ) && m_bCreateShadersOnDemand ) { #ifdef DYNAMIC_SHADER_COMPILE ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &m_VertexShaderDict[shader].m_ShaderStaticCombos.m_pCreationData[vshIndex]; #else ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &lookup->m_ShaderStaticCombos.m_pCreationData[vshIndex]; #endif dxshader = g_pShaderDeviceDx11->CreateVertexShader( ( DWORD * )pCreationData->ByteCode.Base(), pCreationData->ByteCode.Count() ); #ifdef DYNAMIC_SHADER_COMPILE // copy the compiled shader handle back to wherever it's supposed to be stored m_VertexShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[vshIndex] = dxshader; #else lookup->m_ShaderStaticCombos.m_pHardwareShaders[vshIndex] = dxshader; #endif } Assert( dxshader ); #ifndef DYNAMIC_SHADER_COMPILE if( !dxshader ) { Error( "!!!!!Using invalid shader combo!!!!! Consult a programmer and tell them to build debug materialsystem.dll and stdshader*.dll. Run with \"mat_bufferprimitives 0\" and look for CMaterial in the call stack and see what m_pDebugName is. You are likely using a shader combo that has been skipped.\n" ); } #endif BindVertexShader( (VertexShaderHandle_t)dxshader ); } //----------------------------------------------------------------------------- // The low-level dx call to set the pixel shader state //----------------------------------------------------------------------------- void CShaderManager::SetPixelShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader ) { } void CShaderManager::BindPixelShader( PixelShaderHandle_t hPixelShader ) { //if ( m_HardwarePixelShader != hPixelShader ) //{ g_pShaderAPIDx11->BindPixelShader( hPixelShader ); // m_HardwarePixelShader = hPixelShader; //} } //----------------------------------------------------------------------------- // Sets a particular pixel shader as the current shader //----------------------------------------------------------------------------- void CShaderManager::SetPixelShader( PixelShader_t shader ) { if ( shader == INVALID_SHADER ) { BindPixelShader( 0 ); return; } int pshIndex = m_nPixelShaderIndex; Assert( pshIndex >= 0 ); //ShaderLookupDx11_t &lookup = m_PixelShaderDict[shader]; //Warning( "psh: %s static: %d dynamic: %d\n", m_ShaderSymbolTable.String( lookup.m_Name ), // lookup.m_nStaticIndex, m_nPixelShaderIndex ); #ifdef DYNAMIC_SHADER_COMPILE HardwareShader_t &dxshader = m_PixelShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[pshIndex]; if ( dxshader == INVALID_HARDWARE_SHADER ) { // compile it since we haven't already! ShaderLookupDx11_t &vshLookup = m_PixelShaderDict[shader]; dxshader = CompileShader( m_ShaderSymbolTable.String( vshLookup.m_Name ), vshLookup.m_nStaticIndex, pshIndex, false ); // Assert( dxshader != INVALID_HARDWARE_SHADER ); if( IsX360() ) { //360 does not respond well at all to bad shaders or Error() calls. So we're staying here until we get something that compiles while( dxshader == INVALID_HARDWARE_SHADER ) { Warning( "A dynamically compiled pixel shader has failed to build. Pausing for 5 seconds and attempting rebuild.\n" ); Sleep( 5000 ); dxshader = CompileShader( m_ShaderSymbolTable.String( vshLookup.m_Name ), vshLookup.m_nStaticIndex, pshIndex, false ); } } } #else ShaderLookupDx11_t *lookup = (ShaderLookupDx11_t *)&m_PixelShaderDict[shader]; if ( lookup->m_Flags & SHADER_FAILED_LOAD ) { Assert( 0 ); return; } #ifdef _DEBUG pshDebugIndex = (pshDebugIndex + 1) % MAX_SHADER_HISTORY; Q_strncpy( pshDebugName[pshDebugIndex], m_ShaderSymbolTable.String( lookup->m_Name ), sizeof( pshDebugName[0] ) ); #endif HardwareShader_t dxshader = lookup->m_ShaderStaticCombos.m_pHardwareShaders[pshIndex]; #endif if ( IsPC() && ( dxshader == INVALID_HARDWARE_SHADER ) && m_bCreateShadersOnDemand ) { #ifdef DYNAMIC_SHADER_COMPILE ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &m_PixelShaderDict[shader].m_ShaderStaticCombos.m_pCreationData[pshIndex]; #else ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &lookup->m_ShaderStaticCombos.m_pCreationData[pshIndex]; #endif dxshader = g_pShaderDeviceDx11->CreatePixelShader( ( DWORD * )pCreationData->ByteCode.Base(), pCreationData->ByteCode.Count() ); #ifdef DYNAMIC_SHADER_COMPILE // copy the compiled shader handle back to wherever it's supposed to be stored m_PixelShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[pshIndex] = dxshader; #else lookup->m_ShaderStaticCombos.m_pHardwareShaders[pshIndex] = dxshader; #endif } AssertMsg( dxshader != INVALID_HARDWARE_SHADER, "Failed to set pixel shader." ); BindPixelShader( (PixelShaderHandle_t)dxshader ); } //----------------------------------------------------------------------------- // Resets the shader state //----------------------------------------------------------------------------- void CShaderManager::ResetShaderState() { // This will force the calls to SetVertexShader + SetPixelShader to actually set the state m_HardwareVertexShader = (HardwareShader_t)-1; m_HardwarePixelShader = (HardwareShader_t)-1; BindVertexShader( VERTEX_SHADER_HANDLE_INVALID ); BindPixelShader( PIXEL_SHADER_HANDLE_INVALID ); } //----------------------------------------------------------------------------- // Destroy a particular vertex shader //----------------------------------------------------------------------------- void CShaderManager::DestroyVertexShader( VertexShader_t shader ) { ShaderStaticCombos_t &combos = m_VertexShaderDict[shader].m_ShaderStaticCombos; int i; for ( i = 0; i < combos.m_nCount; i++ ) { if ( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER ) { ID3D11VertexShader* pShader = (ID3D11VertexShader* )combos.m_pHardwareShaders[i]; UnregisterVS( pShader ); #ifdef _DEBUG int nRetVal = #endif pShader->Release(); Assert( nRetVal == 0 ); } } delete [] combos.m_pHardwareShaders; combos.m_pHardwareShaders = NULL; if ( combos.m_pCreationData != NULL ) { delete [] combos.m_pCreationData; combos.m_pCreationData = NULL; } m_VertexShaderDict.Remove( shader ); } //----------------------------------------------------------------------------- // Destroy a particular pixel shader //----------------------------------------------------------------------------- void CShaderManager::DestroyPixelShader( PixelShader_t pixelShader ) { ShaderStaticCombos_t &combos = m_PixelShaderDict[pixelShader].m_ShaderStaticCombos; int i; for ( i = 0; i < combos.m_nCount; i++ ) { if ( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER ) { ID3D11PixelShader* pShader = ( ID3D11PixelShader * )combos.m_pHardwareShaders[i]; UnregisterPS( pShader ); #ifdef _DEBUG int nRetVal = #endif pShader->Release(); Assert( nRetVal == 0 ); } } delete [] combos.m_pHardwareShaders; combos.m_pHardwareShaders = NULL; if ( combos.m_pCreationData != NULL ) { delete [] combos.m_pCreationData; combos.m_pCreationData = NULL; } m_PixelShaderDict.Remove( pixelShader ); } //----------------------------------------------------------------------------- // Destroys all shaders //----------------------------------------------------------------------------- void CShaderManager::DestroyAllShaders( void ) { for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); vshIndex != m_VertexShaderDict.InvalidIndex(); ) { Assert( m_VertexShaderDict[vshIndex].m_nRefCount >= 0 ); VertexShader_t next = m_VertexShaderDict.Next( vshIndex ); DestroyVertexShader( vshIndex ); vshIndex = next; } for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); pshIndex != m_PixelShaderDict.InvalidIndex(); ) { Assert( m_PixelShaderDict[pshIndex].m_nRefCount >= 0 ); PixelShader_t next = m_PixelShaderDict.Next( pshIndex ); DestroyPixelShader( pshIndex ); pshIndex = next; } // invalidate the file cache m_ShaderFileCache.Purge(); } //----------------------------------------------------------------------------- // print all vertex and pixel shaders along with refcounts to the console //----------------------------------------------------------------------------- void CShaderManager::SpewVertexAndPixelShaders( void ) { // only spew a populated shader file cache Msg( "\nShader File Cache:\n" ); for ( int cacheIndex = m_ShaderFileCache.Head(); cacheIndex != m_ShaderFileCache.InvalidIndex(); cacheIndex = m_ShaderFileCache.Next( cacheIndex ) ) { ShaderFileCache_t *pCache = &m_ShaderFileCache[cacheIndex]; Msg( "Total Combos:%9d Static:%9d Dynamic:%7d SeekTable:%7d Ver:%d '%s'\n", pCache->m_Header.m_nTotalCombos, pCache->m_Header.m_nTotalCombos/pCache->m_Header.m_nDynamicCombos, pCache->m_Header.m_nDynamicCombos, pCache->IsOldVersion() ? 0 : pCache->m_Header.m_nNumStaticCombos, pCache->m_Header.m_nVersion, m_ShaderSymbolTable.String( pCache->m_Filename ) ); } Msg( "\n" ); // spew vertex shader dictionary int totalVertexShaders = 0; int totalVertexShaderSets = 0; for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); vshIndex != m_VertexShaderDict.InvalidIndex(); vshIndex = m_VertexShaderDict.Next( vshIndex ) ) { const auto &lookup = m_VertexShaderDict[vshIndex]; const char *pName = m_ShaderSymbolTable.String( lookup.m_Name ); Msg( "vsh 0x%8.8x: static combo:%9d dynamic combos:%6d refcount:%4d \"%s\"\n", vshIndex, ( int )lookup.m_nStaticIndex, ( int )lookup.m_ShaderStaticCombos.m_nCount, lookup.m_nRefCount, pName ); totalVertexShaders += lookup.m_ShaderStaticCombos.m_nCount; totalVertexShaderSets++; } // spew pixel shader dictionary int totalPixelShaders = 0; int totalPixelShaderSets = 0; for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); pshIndex != m_PixelShaderDict.InvalidIndex(); pshIndex = m_PixelShaderDict.Next( pshIndex ) ) { const auto &lookup = m_PixelShaderDict[pshIndex]; const char *pName = m_ShaderSymbolTable.String( lookup.m_Name ); Msg( "psh 0x%8.8x: static combo:%9d dynamic combos:%6d refcount:%4d \"%s\"\n", pshIndex, ( int )lookup.m_nStaticIndex, ( int )lookup.m_ShaderStaticCombos.m_nCount, lookup.m_nRefCount, pName ); totalPixelShaders += lookup.m_ShaderStaticCombos.m_nCount; totalPixelShaderSets++; } Msg( "Total unique vertex shaders: %d\n", totalVertexShaders ); Msg( "Total vertex shader sets: %d\n", totalVertexShaderSets ); Msg( "Total unique pixel shaders: %d\n", totalPixelShaders ); Msg( "Total pixel shader sets: %d\n", totalPixelShaderSets ); } CON_COMMAND( mat_spewvertexandpixelshaders, "Print all vertex and pixel shaders currently loaded to the console" ) { ( ( CShaderManager * )ShaderManager() )->SpewVertexAndPixelShaders(); } const char *CShaderManager::GetActiveVertexShaderName() { #if !defined( _DEBUG ) return ""; #else if ( !m_HardwareVertexShader ) { return "NULL"; } return vshDebugName[vshDebugIndex]; #endif } const char *CShaderManager::GetActivePixelShaderName() { #if !defined( _DEBUG ) return ""; #else if ( !m_HardwarePixelShader ) { return "NULL"; } return pshDebugName[pshDebugIndex]; #endif } #ifdef DYNAMIC_SHADER_COMPILE void CShaderManager::FlushShaders( void ) { for( VertexShader_t shader = m_VertexShaderDict.Head(); shader != m_VertexShaderDict.InvalidIndex(); shader = m_VertexShaderDict.Next( shader ) ) { int i; ShaderStaticCombos_t *combos = &m_VertexShaderDict[shader].m_ShaderStaticCombos; if( !( m_VertexShaderDict[shader].m_Flags & SHADER_DYNAMIC_COMPILE_IS_HLSL ) ) { // don't nuke non-HLSL shaders since we don't dynamically compile them. continue; } for( i = 0; i < combos->m_nCount; i++ ) { if( combos->m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER ) { #ifdef _DEBUG int nRetVal= #endif ( ( ID3D11VertexShader * )combos->m_pHardwareShaders[i] )->Release(); Assert( nRetVal == 0 ); } combos->m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER; } } for( PixelShader_t shader = m_PixelShaderDict.Head(); shader != m_PixelShaderDict.InvalidIndex(); shader = m_PixelShaderDict.Next( shader ) ) { int i; ShaderStaticCombos_t &combos = m_PixelShaderDict[shader].m_ShaderStaticCombos; if( !( m_PixelShaderDict[shader].m_Flags & SHADER_DYNAMIC_COMPILE_IS_HLSL ) ) { // don't nuke non-HLSL shaders since we don't dynamically compile them. continue; } for( i = 0; i < combos.m_nCount; i++ ) { if( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER ) { #ifdef _DEBUG int nRetVal = #endif ( ( ID3D11PixelShader * )combos.m_pHardwareShaders[i] )->Release(); Assert( nRetVal == 0 ); } combos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER; } } // invalidate the file cache m_ShaderFileCache.Purge(); } #endif #ifdef DYNAMIC_SHADER_COMPILE CON_COMMAND( mat_flushshaders, "flush all hardware shaders when using DYNAMIC_SHADER_COMPILE" ) { ( ( CShaderManager * )ShaderManager() )->FlushShaders(); } #endif