Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2839 lines
79 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // vmpi_bareshell.cpp : Defines the entry point for the console application.
  9. //
  10. #include <windows.h>
  11. #include <conio.h>
  12. #include <process.h>
  13. #include "vmpi.h"
  14. #include "filesystem.h"
  15. #include "vmpi_filesystem.h"
  16. #include "vmpi_distribute_work.h"
  17. #include "vmpi_tools_shared.h"
  18. #include "cmdlib.h"
  19. #include "UtlVector.h"
  20. #include "Utlhash.h"
  21. #include "UtlBuffer.h"
  22. #include "utlstring.h"
  23. #include "tier2/utlstreambuffer.h"
  24. #include "UtlLinkedList.h"
  25. #include "UtlStringMap.h"
  26. #include "tier0/icommandline.h"
  27. #include "tier1/strtools.h"
  28. #include "vstdlib/jobthread.h"
  29. #include "threads.h"
  30. #include "tier0/dbg.h"
  31. #include "tier1/smartptr.h"
  32. #include "interface.h"
  33. #include "ishadercompiledll.h"
  34. #include <direct.h>
  35. #include "io.h"
  36. #include <sys/types.h>
  37. #include <sys/stat.h>
  38. #include "materialsystem/shader_vcs_version.h"
  39. #include "ilaunchabledll.h"
  40. #include <tier1/diff.h>
  41. #include "utlnodehash.h"
  42. #include "lzma/lzma.h"
  43. #include "mathlib/mathlib.h"
  44. #include "tier1/checksum_crc.h"
  45. #include "tier0/tslist.h"
  46. #include "tools_minidump.h"
  47. #include "shadercompile_ps3_helpers.h"
  48. #include "cmdsink.h"
  49. #include "d3dxfxc.h"
  50. #include "subprocess.h"
  51. #include "cfgprocessor.h"
  52. // Set this to one when working on shaders to get immediate errors rather than waiting for the end of the compile.
  53. #define IMMEDIATEERRORS 0
  54. // Type conversions should be controlled by programmer explicitly - shadercompile makes use of 64-bit integer arithmetics
  55. #pragma warning( error : 4244 )
  56. static inline uint32 uint64_as_uint32( uint64 x )
  57. {
  58. Assert( x < uint64( uint32( ~0 ) ) );
  59. return uint32( x );
  60. }
  61. static inline UtlSymId_t int_as_symid( int x )
  62. {
  63. Assert( ( sizeof( UtlSymId_t ) >= sizeof( int ) ) || ( x >= 0 && x < ( int )( unsigned int )( UtlSymId_t(~0) ) ) );
  64. return UtlSymId_t( x );
  65. }
  66. static bool isspace_force_valid_characters( char c )
  67. {
  68. return !!V_isspace( ( unsigned char )c );
  69. }
  70. static bool isalpha_force_valid_characters( char c )
  71. {
  72. return !!V_isalpha( ( unsigned char )c );
  73. }
  74. // VMPI packets
  75. #define STARTWORK_PACKETID 5
  76. #define ERRMSG_PACKETID 7
  77. #define SHADERHADERROR_PACKETID 8
  78. #define MACHINE_NAME 9
  79. #ifdef _DEBUG
  80. //#define DEBUGFP
  81. #endif
  82. // Dealing with job list
  83. namespace
  84. {
  85. CArrayAutoPtr< CfgProcessor::CfgEntryInfo > g_arrCompileEntries;
  86. uint64 g_numShaders = 0, g_numCompileCommands = 0, g_numStaticCombos = 0;
  87. uint64 g_nStaticCombosPerWorkUnit = 0, g_numCompletedStaticCombos = 0, g_numCommandsCompleted = 0;
  88. uint64 g_numSkippedStaticCombos = 0;
  89. CfgProcessor::CfgEntryInfo const * GetEntryByStaticComboNum( uint64 nStaticCombo, uint64 *pnStaticCombo )
  90. {
  91. CfgProcessor::CfgEntryInfo const *pInfo;
  92. uint64 nRemainStaticCombos = nStaticCombo;
  93. for ( pInfo = g_arrCompileEntries.Get(); pInfo && pInfo->m_szName; ++ pInfo )
  94. {
  95. if ( nRemainStaticCombos >= pInfo->m_numStaticCombos )
  96. nRemainStaticCombos -= pInfo->m_numStaticCombos;
  97. else
  98. break;
  99. }
  100. if ( pnStaticCombo )
  101. *pnStaticCombo = nRemainStaticCombos;
  102. return pInfo;
  103. }
  104. }; // `anonymous` namespace
  105. char * PrettyPrintNumber( uint64 k )
  106. {
  107. static char chCompileString[50] = {0};
  108. char *pchPrint = chCompileString + sizeof( chCompileString ) - 3;
  109. for ( uint64 j = 0; k > 0; k /= 10, ++ j )
  110. {
  111. ( j && !( j % 3 ) ) ? ( * pchPrint -- = ',' ) : 0;
  112. * pchPrint -- = '0' + char( k % 10 );
  113. }
  114. ( * ++ pchPrint ) ? 0 : ( * pchPrint = 0 );
  115. return pchPrint;
  116. }
  117. const char *g_pShaderPath = NULL;
  118. char g_WorkerTempPath[MAX_PATH];
  119. char g_ExeDir[MAX_PATH];
  120. #ifdef DEBUGFP
  121. FILE *g_WorkerDebugFp = NULL;
  122. #endif
  123. bool g_bGotStartWorkPacket = false;
  124. double g_flStartTime;
  125. bool g_bVerbose = false;
  126. bool g_bIsX360 = false;
  127. bool g_bIsPS3 = false;
  128. bool g_bGeneratePS3DebugInfo = false;
  129. bool g_bOptimizePS3ShaderScheduling = false;
  130. bool g_bSuppressWarnings = false;
  131. FORCEINLINE long AsTargetLong( long x ) { return ( ( g_bIsX360 || g_bIsPS3 ) ? ( BigLong( x ) ) : ( x ) ); }
  132. struct ShaderInfo_t
  133. {
  134. ShaderInfo_t() { memset( this, 0, sizeof( *this ) ); }
  135. uint64 m_nShaderCombo;
  136. uint64 m_nTotalShaderCombos;
  137. const char *m_pShaderName;
  138. const char *m_pShaderSrc;
  139. unsigned m_CentroidMask;
  140. uint64 m_nDynamicCombos;
  141. uint64 m_nStaticCombo;
  142. unsigned m_Flags; // from IShader.h
  143. char m_szShaderModel[ 12 ];
  144. };
  145. void Shader_ParseShaderInfoFromCompileCommands( CfgProcessor::CfgEntryInfo const *pEntry, ShaderInfo_t &shaderInfo );
  146. struct CByteCodeBlock
  147. {
  148. CByteCodeBlock *m_pNext, *m_pPrev;
  149. int m_nCRC32;
  150. uint64 m_nComboID;
  151. size_t m_nCodeSize;
  152. uint8 *m_ByteCode;
  153. CByteCodeBlock( void )
  154. {
  155. m_ByteCode = NULL;
  156. }
  157. CByteCodeBlock( void const *pByteCode, size_t nCodeSize, uint64 nComboID )
  158. {
  159. m_ByteCode = new uint8[nCodeSize];
  160. m_nComboID = nComboID;
  161. m_nCodeSize = nCodeSize;
  162. memcpy( m_ByteCode, pByteCode, nCodeSize );
  163. m_nCRC32 = CRC32_ProcessSingleBuffer( pByteCode, nCodeSize );
  164. }
  165. ~CByteCodeBlock( void )
  166. {
  167. if ( m_ByteCode )
  168. delete[] m_ByteCode;
  169. }
  170. };
  171. static int __cdecl CompareDynamicComboIDs( CByteCodeBlock * const *pA, CByteCodeBlock * const *pB )
  172. {
  173. if ( (*pA)->m_nComboID < (*pB)->m_nComboID )
  174. return -1;
  175. if ( (*pA)->m_nComboID > (*pB)->m_nComboID )
  176. return 1;
  177. return 0;
  178. }
  179. struct CStaticCombo // all the data for one static combo
  180. {
  181. CStaticCombo *m_pNext, *m_pPrev;
  182. uint64 m_nStaticComboID;
  183. CUtlVector< CByteCodeBlock* > m_DynamicCombos;
  184. struct PackedCode : protected CArrayAutoPtr<uint8> {
  185. size_t GetLength() const { if( uint8 *pb = Get() ) return *reinterpret_cast<size_t *>( pb ); else return 0; }
  186. uint8 *GetData() const { if( uint8 *pb = Get() ) return pb + sizeof( size_t ); else return NULL; }
  187. uint8 *AllocData( size_t len ) { Delete(); if ( len ) { Attach( new uint8[ len + sizeof( size_t ) ] ); *reinterpret_cast<size_t *>( Get() ) = len; } return GetData(); }
  188. } m_abPackedCode; // Packed code for entire static combo
  189. uint64 Key( void ) const
  190. {
  191. return m_nStaticComboID;
  192. }
  193. CStaticCombo( uint64 nComboID )
  194. {
  195. m_nStaticComboID = nComboID;
  196. }
  197. ~CStaticCombo( void )
  198. {
  199. m_DynamicCombos.PurgeAndDeleteElements();
  200. }
  201. void AddDynamicCombo( uint64 nComboID, void const *pComboData, size_t nCodeSize )
  202. {
  203. CByteCodeBlock *pNewBlock = new CByteCodeBlock( pComboData, nCodeSize, nComboID );
  204. m_DynamicCombos.AddToTail( pNewBlock );
  205. }
  206. void SortDynamicCombos( void )
  207. {
  208. m_DynamicCombos.Sort( CompareDynamicComboIDs );
  209. }
  210. uint8 *AllocPackedCodeBlock( size_t nPackedCodeSize )
  211. {
  212. return m_abPackedCode.AllocData( nPackedCodeSize );
  213. }
  214. };
  215. typedef CUtlNodeHash<CStaticCombo, 7097, uint64> StaticComboNodeHash_t;
  216. template <>
  217. inline StaticComboNodeHash_t ** Construct( StaticComboNodeHash_t ** pMemory )
  218. {
  219. return ::new( pMemory ) StaticComboNodeHash_t *( NULL ); // Explicitly new with NULL
  220. }
  221. struct CShaderMap : public CUtlStringMap<StaticComboNodeHash_t *> {
  222. ;
  223. } g_ShaderByteCode;
  224. CStaticCombo * StaticComboFromDictAdd( char const *pszShaderName, uint64 nStaticComboId )
  225. {
  226. StaticComboNodeHash_t *& rpNodeHash = g_ShaderByteCode[ pszShaderName ];
  227. if ( !rpNodeHash )
  228. {
  229. rpNodeHash = new StaticComboNodeHash_t;
  230. }
  231. // search for this static combo. make it if not found
  232. CStaticCombo *pStaticCombo = rpNodeHash->FindByKey( nStaticComboId );
  233. if ( !pStaticCombo )
  234. {
  235. pStaticCombo = new CStaticCombo( nStaticComboId );
  236. rpNodeHash->Add( pStaticCombo );
  237. }
  238. return pStaticCombo;
  239. }
  240. CStaticCombo * StaticComboFromDict( char const *pszShaderName, uint64 nStaticComboId )
  241. {
  242. if ( StaticComboNodeHash_t *pNodeHash = g_ShaderByteCode[ pszShaderName ] )
  243. return pNodeHash->FindByKey( nStaticComboId );
  244. else
  245. return NULL;
  246. }
  247. CUtlStringMap<ShaderInfo_t> g_ShaderToShaderInfo;
  248. class CompilerMsgInfo
  249. {
  250. public:
  251. CompilerMsgInfo() : m_numTimesReported( 0 ) {}
  252. public:
  253. void SetMsgReportedCommand( char const *szCommand, int numTimesReported = 1, const char *szMachineName = "" ) { if ( !m_numTimesReported ) { m_sFirstCommand = szCommand; if ( szMachineName ) m_sFirstMachineName = szMachineName; } m_numTimesReported += numTimesReported; }
  254. public:
  255. char const * GetFirstCommand() const { return m_sFirstCommand.String(); }
  256. char const * GetFirstMachineName() const { return m_sFirstMachineName.String(); }
  257. int GetNumTimesReported() const { return m_numTimesReported; }
  258. protected:
  259. CUtlString m_sFirstCommand;
  260. CUtlString m_sFirstMachineName;
  261. int m_numTimesReported;
  262. };
  263. CUtlStringMap<bool> g_Master_ShaderHadError;
  264. CUtlStringMap<bool> g_Master_ShaderWrittenToDisk;
  265. CUtlStringMap<CompilerMsgInfo> g_Master_CompilerMsgInfo;
  266. namespace Threading
  267. {
  268. enum Mode { eSingleThreaded = 0, eMultiThreaded = 1 };
  269. // A special object that makes single-threaded code incur no penalties
  270. // and multithreaded code to be synchronized properly.
  271. template < class MT_MUTEX_TYPE = CThreadFastMutex >
  272. class CSwitchableMutex
  273. {
  274. public:
  275. public:
  276. FORCEINLINE explicit CSwitchableMutex( Mode eMode, MT_MUTEX_TYPE *pMtMutex = NULL ) : m_pMtx( pMtMutex ), m_pUseMtx( eMode ? pMtMutex : NULL ) {}
  277. public:
  278. FORCEINLINE void SetMtMutex( MT_MUTEX_TYPE *pMtMutex ) { m_pMtx = pMtMutex; m_pUseMtx = ( m_pUseMtx ? pMtMutex : NULL ); }
  279. FORCEINLINE void SetThreadedMode( Mode eMode ) { m_pUseMtx = ( eMode ? m_pMtx : NULL ); }
  280. public:
  281. FORCEINLINE void Lock() { if ( MT_MUTEX_TYPE *pUseMtx = m_pUseMtx ) pUseMtx->Lock(); }
  282. FORCEINLINE void Unlock() { if ( MT_MUTEX_TYPE *pUseMtx = m_pUseMtx ) pUseMtx->Unlock(); }
  283. FORCEINLINE bool TryLock() { if ( MT_MUTEX_TYPE *pUseMtx = m_pUseMtx ) return pUseMtx->TryLock(); else return true; }
  284. FORCEINLINE bool AssertOwnedByCurrentThread() { if ( MT_MUTEX_TYPE *pUseMtx = m_pUseMtx ) return pUseMtx->AssertOwnedByCurrentThread(); else return true; }
  285. FORCEINLINE void SetTrace( bool b ) { if ( MT_MUTEX_TYPE *pUseMtx = m_pUseMtx ) pUseMtx->SetTrace( b ); }
  286. FORCEINLINE uint32 GetOwnerId() { if ( MT_MUTEX_TYPE *pUseMtx = m_pUseMtx ) return pUseMtx->GetOwnerId(); else return 0; }
  287. FORCEINLINE int GetDepth() { if ( MT_MUTEX_TYPE *pUseMtx = m_pUseMtx ) return pUseMtx->GetDepth(); else return 0; }
  288. private:
  289. MT_MUTEX_TYPE *m_pMtx;
  290. CInterlockedPtr< MT_MUTEX_TYPE > m_pUseMtx;
  291. };
  292. namespace Private
  293. {
  294. typedef CThreadMutex MtMutexType_t;
  295. MtMutexType_t g_mtxSyncObjMT;
  296. }; // namespace Private
  297. CSwitchableMutex< Private::MtMutexType_t > g_mtxGlobal( eSingleThreaded, &Private::g_mtxSyncObjMT );
  298. class CGlobalMutexAutoLock
  299. {
  300. public:
  301. CGlobalMutexAutoLock() { g_mtxGlobal.Lock(); }
  302. ~CGlobalMutexAutoLock() { g_mtxGlobal.Unlock(); }
  303. };
  304. }; // namespace Threading
  305. // Access to global data should be synchronized by these global locks
  306. #define GLOBAL_DATA_MTX_LOCK() Threading::g_mtxGlobal.Lock()
  307. #define GLOBAL_DATA_MTX_UNLOCK() Threading::g_mtxGlobal.Unlock()
  308. #define GLOBAL_DATA_MTX_LOCK_AUTO Threading::CGlobalMutexAutoLock UNIQUE_ID;
  309. unsigned long VMPI_Stats_GetJobWorkerID( void )
  310. {
  311. return 0;
  312. }
  313. bool StartWorkDispatch( MessageBuffer *pBuf, int iSource, int iPacketID )
  314. {
  315. g_bGotStartWorkPacket = true;
  316. return true;
  317. }
  318. CDispatchReg g_StartWorkReg( STARTWORK_PACKETID, StartWorkDispatch );
  319. CDispatchReg g_PS3ShaderDebugInfoReg( PS3_SHADER_DEBUG_INFO_PACKETID, PS3ShaderDebugInfoDispatch );
  320. CDispatchReg g_PS3ShaderCompileLogReg( PS3_SHADER_COMPILE_LOG_PACKETID, PS3ShaderCompileLogDispatch );
  321. // Consume all characters for which (isspace) is true
  322. template < typename T >
  323. char * ConsumeCharacters( char *szString, T pred )
  324. {
  325. if ( szString )
  326. {
  327. while ( *szString && pred( *szString ) )
  328. {
  329. ++ szString;
  330. }
  331. }
  332. return szString;
  333. }
  334. char * FindNext( char *szString, char *szSearchSet )
  335. {
  336. bool bFound = (szString == NULL);
  337. char *szNext = NULL;
  338. if ( szString && szSearchSet )
  339. {
  340. for ( ; *szSearchSet; ++ szSearchSet )
  341. {
  342. if ( char *szTmp = strchr( szString, *szSearchSet ) )
  343. {
  344. szNext = bFound ? ( min( szNext, szTmp ) ) : szTmp;
  345. bFound = true;
  346. }
  347. }
  348. }
  349. return bFound ? szNext : ( szString + strlen( szString ) );
  350. }
  351. char * FindLast( char *szString, char *szSearchSet )
  352. {
  353. bool bFound = (szString != NULL);
  354. char *szNext = NULL;
  355. if ( szString && szSearchSet )
  356. {
  357. for ( ; *szSearchSet; ++ szSearchSet )
  358. {
  359. if ( char *szTmp = strrchr( szString, *szSearchSet ) )
  360. {
  361. szNext = bFound ? ( max( szNext, szTmp ) ) : szTmp;
  362. bFound = true;
  363. }
  364. }
  365. }
  366. return bFound ? szNext : ( szString + strlen( szString ) );
  367. }
  368. void ErrMsgDispatchMsgLine( char const *szCommand, char *szMsgLine, char const *szShaderName = NULL )
  369. {
  370. // When the filename is specified in front of the message, make sure it is truncated to the bare name only
  371. if ( isalpha_force_valid_characters( *szMsgLine ) && szMsgLine[1] == ':' )
  372. {
  373. // Preceded by drive letter
  374. szMsgLine += 2;
  375. }
  376. // Trim the path from the msg
  377. // e.g. make string
  378. // c:\temp\shadercompiletemp\1234\myfile.fxc(435): warning X3083: Truncating ...
  379. // look like
  380. // myfile.fxc(435): warning X3083: Truncating ...
  381. // which will be both readable and same coming from different worker machines
  382. char *szEndFileLinePlant = FindNext( szMsgLine, ":" );
  383. if ( ':' == *szEndFileLinePlant )
  384. {
  385. *szEndFileLinePlant = 0;
  386. if ( char *szLastSlash = FindLast( szMsgLine, "\\/" ) )
  387. {
  388. if ( *szLastSlash )
  389. {
  390. *szLastSlash = 0;
  391. szMsgLine = szLastSlash + 1;
  392. }
  393. }
  394. *szEndFileLinePlant = ':';
  395. }
  396. // If the shader file name is not given in the message add it
  397. if ( szShaderName )
  398. {
  399. static char chFitLongMsgLine[4096];
  400. if ( *szMsgLine == '(' )
  401. {
  402. sprintf( chFitLongMsgLine, "%s%s", szShaderName, szMsgLine );
  403. szMsgLine = chFitLongMsgLine;
  404. }
  405. else if ( !strncmp( szMsgLine, "memory(", 7 ) )
  406. {
  407. sprintf( chFitLongMsgLine, "%s%s", szShaderName, szMsgLine+6 );
  408. szMsgLine = chFitLongMsgLine;
  409. }
  410. }
  411. // Now store the message with the command it was generated from
  412. g_Master_CompilerMsgInfo[ szMsgLine ].SetMsgReportedCommand( szCommand, 1, VMPI_GetLocalMachineName() );
  413. }
  414. void ErrMsgDispatchInt( char *szMessage, char const *szShaderName = NULL )
  415. {
  416. // First line is the command number "szCommand"
  417. char *szCommand = ConsumeCharacters( szMessage, V_isspace );
  418. char *szMessageListing = FindNext(szCommand, "\r\n");
  419. char chTerminator = *szMessageListing;
  420. *( szMessageListing ++ ) = 0;
  421. // Now come the command lines actually
  422. while ( chTerminator )
  423. {
  424. char *szMsgText = ConsumeCharacters( szMessageListing, isspace_force_valid_characters );
  425. szMessageListing = FindNext( szMsgText, "\r\n" );
  426. chTerminator = *szMessageListing;
  427. *( szMessageListing ++ ) = 0;
  428. if( *szMsgText )
  429. {
  430. // Trim command at redirection character if present
  431. * FindNext( szCommand, ">" ) = 0;
  432. ErrMsgDispatchMsgLine( szCommand, szMsgText, szShaderName );
  433. }
  434. }
  435. }
  436. //
  437. // BUFFER:
  438. // 1 byte = * = buffer type
  439. //
  440. // string = message
  441. // 1 byte = \n = newline delimiting the message
  442. //
  443. // string = command that first encountered the message
  444. // 1 byte = \n = newline delimiting the command
  445. //
  446. // string = printed number of times the message was encountered
  447. // 1 byte = \n = newline delimiting the number
  448. //
  449. // 1 byte = 0 = null-terminator for the buffer
  450. //
  451. bool ErrMsgDispatch( MessageBuffer *pBuf, int iSource, int iPacketID )
  452. {
  453. GLOBAL_DATA_MTX_LOCK_AUTO;
  454. bool bInvalidPkgRetCode = true;
  455. // Parse the err msg packet
  456. char *szMsgLine = pBuf->data + 1;
  457. char *szCommand = FindNext( szMsgLine, "\n" );
  458. if ( !*szCommand )
  459. return bInvalidPkgRetCode;
  460. *( szCommand ++ ) = 0;
  461. char *szNumTimesReported = FindNext( szCommand, "\n" );
  462. if ( !*szNumTimesReported )
  463. return bInvalidPkgRetCode;
  464. *( szNumTimesReported ++ ) = 0;
  465. char *szTerminator = FindNext( szNumTimesReported, "\n" );
  466. if ( !*szTerminator )
  467. return bInvalidPkgRetCode;
  468. *( szTerminator ++ ) = 0;
  469. #if IMMEDIATEERRORS
  470. char str[ 4096 ];
  471. uint64 iFirstCommand = _strtoui64( szCommand, NULL, 10 );
  472. CfgProcessor::ComboHandle hCombo = NULL;
  473. CfgProcessor::CfgEntryInfo const *pComboEntryInfo = NULL;
  474. if ( CfgProcessor::Combo_GetNext( iFirstCommand, hCombo, g_numCompileCommands ) )
  475. {
  476. Combo_FormatCommand( hCombo, str );
  477. pComboEntryInfo = Combo_GetEntryInfo( hCombo );
  478. Combo_Free( hCombo );
  479. }
  480. else
  481. {
  482. sprintf( str, "cmd # %s", szCommand );
  483. }
  484. fprintf( stderr, "%s\n%s\nMachine: %s\n", szMsgLine, str, VMPI_GetMachineName( iSource ) );
  485. #endif
  486. // Set the msg info
  487. g_Master_CompilerMsgInfo[ szMsgLine ].SetMsgReportedCommand( szCommand, atoi( szNumTimesReported ), VMPI_GetMachineName( iSource ) );
  488. return true;
  489. }
  490. CDispatchReg g_ErrMsgReg( ERRMSG_PACKETID, ErrMsgDispatch );
  491. void ShaderHadErrorDispatchInt( char const *szShader )
  492. {
  493. g_Master_ShaderHadError[ szShader ] = true;
  494. }
  495. //
  496. // BUFFER:
  497. // 1 byte = * = buffer type
  498. //
  499. // string = shader name
  500. // 1 byte = 0 = null-terminator for the name
  501. //
  502. bool ShaderHadErrorDispatch( MessageBuffer *pBuf, int iSource, int iPacketID )
  503. {
  504. GLOBAL_DATA_MTX_LOCK_AUTO;
  505. ShaderHadErrorDispatchInt( pBuf->data + 1 );
  506. return true;
  507. }
  508. CDispatchReg g_ShaderHadErrorReg( SHADERHADERROR_PACKETID, ShaderHadErrorDispatch );
  509. void DebugOut( const char *pMsg, ... )
  510. {
  511. if (g_bVerbose)
  512. {
  513. char msg[2048];
  514. va_list marker;
  515. va_start( marker, pMsg );
  516. _vsnprintf( msg, sizeof( msg ), pMsg, marker );
  517. va_end( marker );
  518. Msg( "%s", msg );
  519. #ifdef DEBUGFP
  520. fprintf( g_WorkerDebugFp, "%s", msg );
  521. fflush( g_WorkerDebugFp );
  522. #endif
  523. }
  524. }
  525. void Vmpi_Worker_DefaultDisconnectHandler( int procID, const char *pReason )
  526. {
  527. Msg( "Master disconnected.\n ");
  528. DebugOut( "Master disconnected.\n" );
  529. TerminateProcess( GetCurrentProcess(), 1 );
  530. }
  531. typedef void ( * DisconnectHandlerFn_t )( int procID, const char *pReason );
  532. DisconnectHandlerFn_t g_fnDisconnectHandler = Vmpi_Worker_DefaultDisconnectHandler;
  533. // Worker should implement this so it will quit nicely when the master disconnects.
  534. void MyDisconnectHandler( int procID, const char *pReason )
  535. {
  536. // If we're a worker, then it's a fatal error if we lose the connection to the master.
  537. if ( !g_bMPIMaster && g_fnDisconnectHandler )
  538. {
  539. (* g_fnDisconnectHandler)( procID, pReason );
  540. }
  541. }
  542. // new format:
  543. // ver#
  544. // total shader combos
  545. // total dynamic combos
  546. // flags
  547. // centroid mask
  548. // total non-skipped static combos
  549. // [ (sorted by static combo id)
  550. // static combo id
  551. // file offset of packed dynamic combo
  552. // ]
  553. // 0xffffffff (sentinel key)
  554. // end of file offset (so can tell compressed size of last combo)
  555. //
  556. // # of duplicate static combos (if version >= 6 )
  557. // [ (sorted by static combo id)
  558. // static combo id
  559. // id of static bombo which is identical
  560. // ]
  561. //
  562. // each packed dynamic combo for a given static combo is stored as a series of compressed blocks.
  563. // block 1:
  564. // ulong blocksize (high bit set means uncompressed)
  565. // block data
  566. // block2..
  567. // 0xffffffff indicates no more blocks for this combo
  568. //
  569. // each block, when uncompressed, holds one or more dynamic combos:
  570. // dynamic combo id (full id if v<6, dynamic combo id only id >=6)
  571. // size of shader
  572. // ..
  573. // there is no terminator - the size of the uncompressed shader tells you when to stop
  574. // this record is then bzip2'd.
  575. // qsort driver function
  576. // returns negative number if idA is less than idB, positive when idA is greater than idB
  577. // and zero if the ids are equal
  578. static int __cdecl CompareDupComboIndices( const StaticComboAliasRecord_t *pA, const StaticComboAliasRecord_t *pB )
  579. {
  580. if ( pA->m_nStaticComboID < pB->m_nStaticComboID )
  581. return -1;
  582. if ( pA->m_nStaticComboID > pB->m_nStaticComboID )
  583. return 1;
  584. return 0;
  585. }
  586. static void FlushCombos( size_t *pnTotalFlushedSize, CUtlBuffer *pDynamicComboBuffer, MessageBuffer *pBuf )
  587. {
  588. if ( !pDynamicComboBuffer->TellPut() )
  589. // Nothing to do here
  590. return;
  591. size_t nCompressedSize;
  592. uint8 *pCompressedShader = LZMA_Compress( reinterpret_cast<uint8 *> ( pDynamicComboBuffer->Base() ),
  593. pDynamicComboBuffer->TellPut(),
  594. &nCompressedSize );
  595. // high 2 bits of length =
  596. // 00 = bzip2 compressed
  597. // 10 = uncompressed
  598. // 01 = lzma compressed
  599. // 11 = unused
  600. if ( ! pCompressedShader )
  601. {
  602. // it grew
  603. long lFlagSize = AsTargetLong( 0x80000000 | pDynamicComboBuffer->TellPut() );
  604. pBuf->write( &lFlagSize, sizeof( lFlagSize ) );
  605. pBuf->write( pDynamicComboBuffer->Base(), pDynamicComboBuffer->TellPut() );
  606. *pnTotalFlushedSize += sizeof( lFlagSize ) + pDynamicComboBuffer->TellPut();
  607. }
  608. else
  609. {
  610. long lFlagSize = AsTargetLong( 0x40000000 | nCompressedSize );
  611. pBuf->write( &lFlagSize, sizeof( lFlagSize ) );
  612. pBuf->write( pCompressedShader, nCompressedSize );
  613. delete[] pCompressedShader;
  614. *pnTotalFlushedSize += sizeof( lFlagSize ) + nCompressedSize;
  615. }
  616. pDynamicComboBuffer->Clear(); // start over
  617. }
  618. static void OutputDynamicCombo( size_t *pnTotalFlushedSize, CUtlBuffer *pDynamicComboBuffer,
  619. MessageBuffer *pBuf, uint64 nComboID, int nComboSize,
  620. uint8 *pComboCode )
  621. {
  622. if ( pDynamicComboBuffer->TellPut() + nComboSize+16 >= MAX_SHADER_UNPACKED_BLOCK_SIZE )
  623. {
  624. FlushCombos( pnTotalFlushedSize, pDynamicComboBuffer, pBuf );
  625. }
  626. pDynamicComboBuffer->PutInt( uint64_as_uint32( nComboID ) );
  627. pDynamicComboBuffer->PutInt( nComboSize );
  628. // pDynamicComboBuffer->PutInt( CRC32_ProcessSingleBuffer( pComboCode, nComboSize ) );
  629. pDynamicComboBuffer->Put( pComboCode, nComboSize );
  630. }
  631. static void OutputDynamicComboDup( size_t *pnTotalFlushedSize, CUtlBuffer *pDynamicComboBuffer,
  632. MessageBuffer *pBuf, uint64 nComboID, uint64 nBaseCombo )
  633. {
  634. if ( pDynamicComboBuffer->TellPut() + 8 >= MAX_SHADER_UNPACKED_BLOCK_SIZE )
  635. {
  636. FlushCombos( pnTotalFlushedSize, pDynamicComboBuffer, pBuf );
  637. }
  638. pDynamicComboBuffer->PutInt( uint64_as_uint32( nComboID ) | 0x80000000 );
  639. pDynamicComboBuffer->PutInt( uint64_as_uint32( nBaseCombo ) );
  640. }
  641. void GetVCSFilenames( char *pszMainOutFileName, ShaderInfo_t const &si )
  642. {
  643. sprintf( pszMainOutFileName, "%s\\shaders\\fxc", g_pShaderPath );
  644. struct _stat buf;
  645. if( _stat( pszMainOutFileName, &buf ) == -1 )
  646. {
  647. printf( "mkdir %s\n", pszMainOutFileName );
  648. // doh. . need to make the directory that the vcs file is going to go into.
  649. _mkdir( pszMainOutFileName );
  650. }
  651. strcat( pszMainOutFileName, "\\" );
  652. strcat( pszMainOutFileName, si.m_pShaderName );
  653. if ( g_bIsX360 )
  654. {
  655. strcat( pszMainOutFileName, ".360" );
  656. }
  657. else if ( g_bIsPS3 )
  658. {
  659. strcat( pszMainOutFileName, ".ps3" );
  660. }
  661. strcat( pszMainOutFileName, ".vcs" ); // Different extensions for main output file
  662. // Check status of vcs file...
  663. if( _stat( pszMainOutFileName, &buf ) != -1 )
  664. {
  665. // The file exists, let's see if it's writable.
  666. if( !( buf.st_mode & _S_IWRITE ) )
  667. {
  668. // It isn't writable. . we'd better change its permissions (or check it out possibly)
  669. printf( "Warning: making %s writable!\n", pszMainOutFileName );
  670. _chmod( pszMainOutFileName, _S_IREAD | _S_IWRITE );
  671. }
  672. }
  673. }
  674. // WriteShaderFiles
  675. //
  676. // should be called either on the main thread or
  677. // on the async writing thread.
  678. //
  679. // So the function WriteShaderFiles should not be reentrant, however the
  680. // data that it uses might be updated by the main thread when built pieces
  681. // are received from the workers.
  682. //
  683. #define STATIC_COMBO_HASH_SIZE 73
  684. struct StaticComboAuxInfo_t : StaticComboRecord_t
  685. {
  686. uint32 m_nCRC32; // CRC32 of packed data
  687. struct CStaticCombo *m_pByteCode;
  688. };
  689. static int __cdecl CompareComboIds( const StaticComboAuxInfo_t *pA, const StaticComboAuxInfo_t *pB )
  690. {
  691. if ( pA->m_nStaticComboID < pB->m_nStaticComboID )
  692. return -1;
  693. if ( pA->m_nStaticComboID > pB->m_nStaticComboID )
  694. return 1;
  695. return 0;
  696. }
  697. static void WriteShaderFiles( const char *pShaderName )
  698. {
  699. if ( !g_Master_ShaderWrittenToDisk.Defined( pShaderName ) )
  700. g_Master_ShaderWrittenToDisk[ pShaderName ] = true;
  701. else
  702. return;
  703. bool bShaderFailed = g_Master_ShaderHadError.Defined( pShaderName );
  704. char const *szShaderFileOperation = bShaderFailed ? "Removing failed" : "Writing";
  705. //
  706. // Progress indication
  707. //
  708. if ( g_numCommandsCompleted < g_numCompileCommands )
  709. {
  710. static char chProgress[] = { '/', '-', '\\', '|' };
  711. static int iProgressSymbol = 0;
  712. Msg( "\b%c", chProgress[ ( ++ iProgressSymbol ) % 4 ] );
  713. }
  714. else
  715. {
  716. char chShaderName[33];
  717. Q_snprintf( chShaderName, 29, "%s...", pShaderName );
  718. sprintf( chShaderName + sizeof( chShaderName ) - 5, "..." );
  719. Msg( "\r%s %s \r", szShaderFileOperation, chShaderName );
  720. }
  721. //
  722. // Retrieve the data we are going to operate on
  723. // from global variables under lock.
  724. //
  725. GLOBAL_DATA_MTX_LOCK();
  726. StaticComboNodeHash_t *pByteCodeArray;
  727. {
  728. StaticComboNodeHash_t *&rp = g_ShaderByteCode[pShaderName]; // Get a static combo pointer, reset it as well
  729. pByteCodeArray = rp;
  730. rp = NULL;
  731. /*
  732. Assert( pByteCodeArray );
  733. if ( !pByteCodeArray )
  734. ShaderHadErrorDispatchInt( pShaderName );
  735. */
  736. }
  737. ShaderInfo_t shaderInfo = g_ShaderToShaderInfo[pShaderName];
  738. if ( !shaderInfo.m_pShaderName )
  739. {
  740. for ( CfgProcessor::CfgEntryInfo const *pAnalyze = g_arrCompileEntries.Get() ;
  741. pAnalyze->m_szName ;
  742. ++ pAnalyze )
  743. {
  744. if ( !strcmp( pAnalyze->m_szName, pShaderName ) )
  745. {
  746. Shader_ParseShaderInfoFromCompileCommands( pAnalyze, shaderInfo );
  747. g_ShaderToShaderInfo[ pShaderName ] = shaderInfo;
  748. break;
  749. }
  750. }
  751. }
  752. GLOBAL_DATA_MTX_UNLOCK();
  753. if ( !shaderInfo.m_pShaderName )
  754. return;
  755. //
  756. // Shader vcs file name
  757. //
  758. char szVCSfilename[MAX_PATH];
  759. GetVCSFilenames( szVCSfilename, shaderInfo );
  760. if ( bShaderFailed )
  761. {
  762. DebugOut( "Removing failed shader file \"%s\".\n", szVCSfilename );
  763. unlink( szVCSfilename );
  764. return;
  765. }
  766. if ( !pByteCodeArray )
  767. return;
  768. DebugOut( "%s : %I64u combos centroid mask: 0x%x numDynamicCombos: %I64u flags: 0x%x\n",
  769. pShaderName, shaderInfo.m_nTotalShaderCombos,
  770. shaderInfo.m_CentroidMask, shaderInfo.m_nDynamicCombos, shaderInfo.m_Flags );
  771. //
  772. // Static combo headers
  773. //
  774. CUtlVector< StaticComboAuxInfo_t > StaticComboHeaders;
  775. StaticComboHeaders.EnsureCapacity( 1 + pByteCodeArray->Count() ); // we know how much ram we need
  776. CUtlVector< int > comboIndicesHashedByCRC32[STATIC_COMBO_HASH_SIZE];
  777. CUtlVector< StaticComboAliasRecord_t > duplicateCombos;
  778. // now, lets fill in our combo headers, sort, and write
  779. for( int nChain = 0 ; nChain < NELEMS( pByteCodeArray->m_HashChains) ; nChain++ )
  780. {
  781. for( CStaticCombo *pStatic = pByteCodeArray->m_HashChains[ nChain ].m_pHead;
  782. pStatic;
  783. pStatic = pStatic->m_pNext )
  784. {
  785. if ( pStatic->m_abPackedCode.GetLength() )
  786. {
  787. StaticComboAuxInfo_t Hdr;
  788. Hdr.m_nStaticComboID = uint64_as_uint32( pStatic->m_nStaticComboID );
  789. Hdr.m_nFileOffset = 0; // fill in later
  790. Hdr.m_nCRC32 = CRC32_ProcessSingleBuffer( pStatic->m_abPackedCode.GetData(), pStatic->m_abPackedCode.GetLength() );
  791. int nHashIdx = Hdr.m_nCRC32 % STATIC_COMBO_HASH_SIZE;
  792. Hdr.m_pByteCode = pStatic;
  793. // now, see if we have an identical static combo
  794. bool bIsDuplicate = false;
  795. for( int i = 0; i < comboIndicesHashedByCRC32[nHashIdx].Count() ; i++ )
  796. {
  797. StaticComboAuxInfo_t const &check = StaticComboHeaders[comboIndicesHashedByCRC32[nHashIdx][i]];
  798. if (
  799. ( check.m_nCRC32 == Hdr.m_nCRC32 ) &&
  800. ( check.m_pByteCode->m_abPackedCode.GetLength() == pStatic->m_abPackedCode.GetLength() ) &&
  801. ( memcmp( check.m_pByteCode->m_abPackedCode.GetData(), pStatic->m_abPackedCode.GetData(), check.m_pByteCode->m_abPackedCode.GetLength() ) == 0 )
  802. )
  803. {
  804. // this static combo is the same as another one!!
  805. StaticComboAliasRecord_t aliasHdr;
  806. aliasHdr.m_nStaticComboID = Hdr.m_nStaticComboID;
  807. aliasHdr.m_nSourceStaticCombo = check.m_nStaticComboID;
  808. duplicateCombos.AddToTail( aliasHdr );
  809. bIsDuplicate = true;
  810. break;
  811. }
  812. }
  813. if ( ! bIsDuplicate )
  814. {
  815. StaticComboHeaders.AddToTail( Hdr );
  816. comboIndicesHashedByCRC32[nHashIdx].AddToTail( StaticComboHeaders.Count() - 1 );
  817. }
  818. }
  819. }
  820. }
  821. // add sentinel key
  822. StaticComboAuxInfo_t Hdr;
  823. Hdr.m_nStaticComboID = 0xffffffff;
  824. Hdr.m_nFileOffset = 0;
  825. StaticComboHeaders.AddToTail( Hdr );
  826. // now, sort. sentinel key will end up at end
  827. StaticComboHeaders.Sort( CompareComboIds );
  828. // Set the CRC to zero for now. . will patch in copyshaders.pl with the correct CRC.
  829. unsigned int crc32 = 0;
  830. //
  831. // Shader file stream buffer
  832. //
  833. CUtlStreamBuffer ShaderFile( szVCSfilename, NULL ); // Streaming buffer for vcs file (since this can blow memory)
  834. ShaderFile.SetBigEndian( g_bIsX360 || g_bIsPS3 ); // Swap the header bytes to X360 format
  835. // ------ Header --------------
  836. ShaderFile.PutInt( SHADER_VCS_VERSION_NUMBER ); // Version
  837. ShaderFile.PutInt( uint64_as_uint32( shaderInfo.m_nTotalShaderCombos ) );
  838. ShaderFile.PutInt( uint64_as_uint32( shaderInfo.m_nDynamicCombos ) );
  839. ShaderFile.PutUnsignedInt( shaderInfo.m_Flags );
  840. ShaderFile.PutUnsignedInt( shaderInfo.m_CentroidMask );
  841. ShaderFile.PutUnsignedInt( StaticComboHeaders.Count() );
  842. ShaderFile.PutUnsignedInt( crc32 );
  843. // static combo dictionary
  844. int nDictionaryOffset= ShaderFile.TellPut();
  845. // we will re write this one we know the offsets
  846. ShaderFile.Put( StaticComboHeaders.Base(), sizeof( StaticComboRecord_t ) * StaticComboHeaders.Count() ); // dummy write, 8 bytes per static combo
  847. ShaderFile.PutUnsignedInt( duplicateCombos.Count() );
  848. // now, write out all duplicate header records
  849. // sort duplicate combo records for binary search
  850. duplicateCombos.Sort( CompareDupComboIndices );
  851. for( int i = 0; i < duplicateCombos.Count(); i++ )
  852. {
  853. ShaderFile.PutUnsignedInt( duplicateCombos[i].m_nStaticComboID );
  854. ShaderFile.PutUnsignedInt( duplicateCombos[i].m_nSourceStaticCombo );
  855. }
  856. // now, write out all static combos
  857. for( int i=0 ; i<StaticComboHeaders.Count(); i++ )
  858. {
  859. StaticComboRecord_t &SRec = StaticComboHeaders[i];
  860. SRec.m_nFileOffset = ShaderFile.TellPut();
  861. if ( SRec.m_nStaticComboID != 0xffffffff ) // sentinel key?
  862. {
  863. CStaticCombo *pStatic=pByteCodeArray->FindByKey( SRec.m_nStaticComboID );
  864. Assert( pStatic );
  865. // Put the packed chunk of code for this static combo
  866. if ( size_t nPackedLen = pStatic->m_abPackedCode.GetLength() )
  867. ShaderFile.Put( pStatic->m_abPackedCode.GetData(), nPackedLen );
  868. ShaderFile.PutInt( 0xffffffff ); // end of dynamic combos
  869. }
  870. if ( g_bIsX360 || g_bIsPS3 )
  871. {
  872. SRec.m_nFileOffset = BigLong( SRec.m_nFileOffset );
  873. SRec.m_nStaticComboID = BigLong( SRec.m_nStaticComboID );
  874. }
  875. }
  876. ShaderFile.Close();
  877. //
  878. // Re-writing the combo header
  879. //
  880. {
  881. FILE *Handle=fopen( szVCSfilename, "rb+" );
  882. if (! Handle )
  883. printf(" failed to re-open %s\n",szVCSfilename );
  884. fseek( Handle, nDictionaryOffset, SEEK_SET );
  885. // now, rewrite header. data is already byte-swapped appropriately
  886. for( int i = 0; i < StaticComboHeaders.Count(); i++ )
  887. {
  888. fwrite( &( StaticComboHeaders[i].m_nStaticComboID ), 4, 1, Handle );
  889. fwrite( &( StaticComboHeaders[i].m_nFileOffset ), 4, 1, Handle );
  890. }
  891. fclose( Handle );
  892. }
  893. // Finalize, free memory
  894. delete pByteCodeArray;
  895. if ( g_numCommandsCompleted >= g_numCompileCommands )
  896. {
  897. Msg( "\r \r" );
  898. }
  899. }
  900. // pBuf is ready to read the results written to the buffer in ProcessWorkUnitFn.
  901. // work is done. .master gets it back this way.
  902. // compiled code in pBuf
  903. void Master_ReceiveWorkUnitFn( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker )
  904. {
  905. GLOBAL_DATA_MTX_LOCK_AUTO;
  906. uint64 comboStart = iWorkUnit * g_nStaticCombosPerWorkUnit;
  907. uint64 comboEnd = comboStart + g_nStaticCombosPerWorkUnit;
  908. comboEnd = min( g_numStaticCombos, comboEnd );
  909. char const *chLastShaderName = "";
  910. ShaderInfo_t siLastShaderInfo;
  911. memset( &siLastShaderInfo, 0, sizeof( siLastShaderInfo ) );
  912. siLastShaderInfo.m_pShaderName = chLastShaderName;
  913. uint64 nComboOfTheEntry = 0;
  914. CfgProcessor::CfgEntryInfo const *pEntry = GetEntryByStaticComboNum( comboStart, &nComboOfTheEntry );
  915. nComboOfTheEntry = pEntry->m_numStaticCombos - 1 - nComboOfTheEntry;
  916. for( uint64 iCombo = comboStart; iCombo ++ < comboEnd;
  917. ( ( ! nComboOfTheEntry -- ) ? ( ++ pEntry, nComboOfTheEntry = pEntry->m_numStaticCombos - 1 ) : 0 ) )
  918. {
  919. Assert( nComboOfTheEntry < pEntry->m_numStaticCombos );
  920. // Read length
  921. int len;
  922. pBuf->read( &len, sizeof( len ) );
  923. // Length can indicate the number of skips to make
  924. if ( len <= 0 )
  925. {
  926. // remember how many static combos get skipped
  927. g_numSkippedStaticCombos += -len;
  928. // then we skip as instructed
  929. for ( int64 numSkips = - len - 1;
  930. numSkips > 0; )
  931. {
  932. if ( numSkips <= nComboOfTheEntry )
  933. {
  934. nComboOfTheEntry -= numSkips;
  935. iCombo += numSkips;
  936. numSkips = 0;
  937. }
  938. else
  939. {
  940. numSkips -= nComboOfTheEntry + 1;
  941. iCombo += nComboOfTheEntry + 1;
  942. ++ pEntry;
  943. nComboOfTheEntry = pEntry->m_numStaticCombos - 1;
  944. }
  945. }
  946. if ( iCombo < comboEnd )
  947. continue;
  948. else
  949. break;
  950. }
  951. // Shader code arrived
  952. char const *chShaderName = pEntry->m_szName;
  953. // If starting new shader remember shader info
  954. if ( chLastShaderName != chShaderName )
  955. {
  956. Shader_ParseShaderInfoFromCompileCommands( pEntry, siLastShaderInfo );
  957. chLastShaderName = chShaderName;
  958. g_ShaderToShaderInfo[ chLastShaderName ] = siLastShaderInfo;
  959. }
  960. // Read buffer
  961. uint8 *pCodeBuffer = StaticComboFromDictAdd( chShaderName, nComboOfTheEntry )->AllocPackedCodeBlock( len );
  962. if ( pCodeBuffer )
  963. pBuf->read( pCodeBuffer, len );
  964. }
  965. }
  966. //
  967. // A function that will wait for right Ctrl+Alt+Shift to be held down simultaneously.
  968. // This is useful for debugging short-lived processes and gives time for debugger to
  969. // get attached.
  970. //
  971. void DebugSafeWaitPoint( bool bForceWait = false )
  972. {
  973. static bool s_bDebuggerAttached = ( CommandLine()->FindParm( "-debugwait" ) == 0 );
  974. if ( bForceWait )
  975. {
  976. s_bDebuggerAttached = false;
  977. }
  978. if ( !s_bDebuggerAttached )
  979. {
  980. Msg( "Waiting for right Ctrl+Alt+Shift to continue..." );
  981. while ( !s_bDebuggerAttached )
  982. {
  983. Msg( "." );
  984. Sleep(1000);
  985. if ( short( GetAsyncKeyState( VK_RCONTROL ) ) < 0 &&
  986. short( GetAsyncKeyState( VK_RSHIFT ) ) < 0 &&
  987. short( GetAsyncKeyState( VK_RMENU ) ) < 0 )
  988. {
  989. s_bDebuggerAttached = true;
  990. }
  991. }
  992. Msg( " ok.\n" );
  993. }
  994. }
  995. // same as "system", but doesn't pop up a window
  996. void MySystem( char const * const pCommand, CmdSink::IResponse **ppResponse )
  997. {
  998. // Trap the command in InterceptFxc
  999. if ( InterceptFxc::TryExecuteCommand( pCommand, ppResponse ) )
  1000. {
  1001. Sleep( 0 );
  1002. return;
  1003. }
  1004. unlink( "shader.o" );
  1005. char szTempFileName[100];
  1006. sprintf( szTempFileName, "sc%d_%d.bat", GetCurrentProcessId(), GetCurrentThreadId() );
  1007. FILE *batFp = fopen( szTempFileName, "w" );
  1008. fprintf( batFp, "%s\n", pCommand );
  1009. fclose( batFp );
  1010. STARTUPINFO si;
  1011. PROCESS_INFORMATION pi;
  1012. ZeroMemory( &si, sizeof(si) );
  1013. si.cb = sizeof(si);
  1014. ZeroMemory( &pi, sizeof(pi) );
  1015. // Start the child process.
  1016. if( !CreateProcess( NULL, // No module name (use command line).
  1017. szTempFileName, // Command line.
  1018. NULL, // Process handle not inheritable.
  1019. NULL, // Thread handle not inheritable.
  1020. FALSE, // Set handle inheritance to FALSE.
  1021. IDLE_PRIORITY_CLASS | CREATE_NO_WINDOW, // No creation flags.
  1022. NULL, // Use parent's environment block.
  1023. g_WorkerTempPath, // Use parent's starting directory.
  1024. &si, // Pointer to STARTUPINFO structure.
  1025. &pi ) // Pointer to PROCESS_INFORMATION structure.
  1026. )
  1027. {
  1028. Error( "CreateProcess failed." );
  1029. Assert( 0 );
  1030. }
  1031. // Wait until child process exits.
  1032. WaitForSingleObject( pi.hProcess, INFINITE );
  1033. // Close process and thread handles.
  1034. CloseHandle( pi.hProcess );
  1035. CloseHandle( pi.hThread );
  1036. unlink( szTempFileName );
  1037. }
  1038. // Assemble a reply package to the master from the compiled bytecode
  1039. // return the length of the package.
  1040. size_t AssembleWorkerReplyPackage( CfgProcessor::CfgEntryInfo const *pEntry, uint64 nComboOfEntry,
  1041. MessageBuffer *pBuf )
  1042. {
  1043. GLOBAL_DATA_MTX_LOCK();
  1044. CStaticCombo *pStComboRec = StaticComboFromDict( pEntry->m_szName, nComboOfEntry );
  1045. StaticComboNodeHash_t *pByteCodeArray = g_ShaderByteCode[ pEntry->m_szName ];
  1046. GLOBAL_DATA_MTX_UNLOCK();
  1047. size_t nBytesWritten = 0;
  1048. if ( pStComboRec && pStComboRec->m_DynamicCombos.Count() )
  1049. {
  1050. CUtlBuffer ubDynamicComboBuffer;
  1051. ubDynamicComboBuffer.SetBigEndian( g_bIsX360 || g_bIsPS3 );
  1052. pStComboRec->SortDynamicCombos();
  1053. // iterate over all dynamic combos.
  1054. for(int i = 0 ; i < pStComboRec->m_DynamicCombos.Count(); i++ )
  1055. {
  1056. CByteCodeBlock *pCode = pStComboRec->m_DynamicCombos[i];
  1057. // check if we have already output an identical combo
  1058. bool bDup = false;
  1059. #if 0
  1060. // check for duplicate bytecode. actually doesn't save much because bzip does a good
  1061. // job compressing dupes.
  1062. for( int j = 0; j < i; j++ )
  1063. {
  1064. if (
  1065. ( pCode->m_nCRC32 == pStComboRec->m_DynamicCombos[j]->m_nCRC32 ) &&
  1066. ( pCode->m_nCodeSize == pStComboRec->m_DynamicCombos[j]->m_nCodeSize ) &&
  1067. ( memcmp( pCode->m_ByteCode, pStComboRec->m_DynamicCombos[i]->m_ByteCode, pCode->m_nCodeSize ) == 0 )
  1068. ) // identical bytecode?
  1069. {
  1070. bDup = true;
  1071. OutputDynamicComboDup( &nBytesWritten, &ubDynamicComboBuffer,
  1072. pBuf, pCode->m_nComboID,
  1073. pStComboRec->m_DynamicCombos[j]->m_nComboID );
  1074. }
  1075. }
  1076. #endif
  1077. if ( ! bDup )
  1078. OutputDynamicCombo( &nBytesWritten, &ubDynamicComboBuffer,
  1079. pBuf, pCode->m_nComboID,
  1080. pCode->m_nCodeSize, pCode->m_ByteCode );
  1081. }
  1082. FlushCombos( &nBytesWritten, &ubDynamicComboBuffer, pBuf );
  1083. }
  1084. // Time to limit amount of prints
  1085. static float s_fLastInfoTime = 0;
  1086. float fCurTime = ( float ) Plat_FloatTime();
  1087. GLOBAL_DATA_MTX_LOCK();
  1088. if ( pStComboRec )
  1089. {
  1090. CStaticCombo *pCombo = pByteCodeArray->FindByKey( nComboOfEntry );
  1091. pByteCodeArray->DeleteByKey( nComboOfEntry );
  1092. delete pCombo;
  1093. }
  1094. if( fabs( fCurTime - s_fLastInfoTime ) > 1.f )
  1095. {
  1096. Msg( "\rCompiling %s [ %2d remaining ] ... \r",
  1097. pEntry->m_szName, nComboOfEntry );
  1098. s_fLastInfoTime = fCurTime;
  1099. }
  1100. GLOBAL_DATA_MTX_UNLOCK();
  1101. return nBytesWritten;
  1102. }
  1103. // Copy a reply package to the master from the compiled bytecode
  1104. // return the length of the data copied.
  1105. size_t CopyWorkerReplyPackage( CfgProcessor::CfgEntryInfo const *pEntry, uint64 nComboOfEntry,
  1106. MessageBuffer *pBuf, int nSkipsSoFar )
  1107. {
  1108. GLOBAL_DATA_MTX_LOCK();
  1109. CStaticCombo *pStComboRec = StaticComboFromDict( pEntry->m_szName, nComboOfEntry );
  1110. StaticComboNodeHash_t *pByteCodeArray = g_ShaderByteCode[ pEntry->m_szName ]; // Get a static combo pointer
  1111. GLOBAL_DATA_MTX_UNLOCK();
  1112. int len = pStComboRec ? pStComboRec->m_abPackedCode.GetLength() : NULL;
  1113. if ( len )
  1114. {
  1115. if ( nSkipsSoFar )
  1116. {
  1117. pBuf->write( &nSkipsSoFar, sizeof( nSkipsSoFar ) );
  1118. }
  1119. pBuf->write( &len, sizeof( len ) );
  1120. if ( len )
  1121. pBuf->write( pStComboRec->m_abPackedCode.GetData(), len );
  1122. }
  1123. if ( pStComboRec )
  1124. {
  1125. GLOBAL_DATA_MTX_LOCK();
  1126. CStaticCombo *pCombo = pByteCodeArray->FindByKey( nComboOfEntry );
  1127. pByteCodeArray->DeleteByKey( nComboOfEntry );
  1128. delete pCombo;
  1129. GLOBAL_DATA_MTX_UNLOCK();
  1130. }
  1131. return size_t( len );
  1132. }
  1133. template < typename TMutexType >
  1134. class CWorkerAccumState : public CParallelProcessorBase < CWorkerAccumState < TMutexType > >
  1135. {
  1136. friend ThisParallelProcessorBase_t;
  1137. private:
  1138. static bool & DisconnectState() { static bool sb = false; return sb; }
  1139. static void Special_DisconnectHandler( int procID, const char *pReason ) { DisconnectState() = true; }
  1140. public:
  1141. explicit CWorkerAccumState( TMutexType *pMutex ) :
  1142. m_pMutex( pMutex ), m_iFirstCommand( 0 ), m_iNextCommand( 0 ),
  1143. m_iEndCommand( 0 ), m_iLastFinished( 0 ),
  1144. m_hCombo( NULL ),
  1145. m_fnOldDisconnectHandler( g_fnDisconnectHandler ),
  1146. m_autoRestoreDisconnectHandler( g_fnDisconnectHandler )
  1147. {
  1148. DisconnectState() = false;
  1149. }
  1150. ~CWorkerAccumState() { QuitSubs(); }
  1151. void RangeBegin( uint64 iFirstCommand, uint64 iEndCommand );
  1152. void RangeFinished( void );
  1153. void ExecuteCompileCommand( CfgProcessor::ComboHandle hCombo );
  1154. void ExecuteCompileCommandThreaded( CfgProcessor::ComboHandle hCombo );
  1155. void HandleCommandResponse( CfgProcessor::ComboHandle hCombo, CmdSink::IResponse *pResponse );
  1156. public:
  1157. using ThisParallelProcessorBase_t::Run;
  1158. public:
  1159. bool OnProcess();
  1160. bool OnProcessST();
  1161. protected:
  1162. TMutexType *m_pMutex;
  1163. protected:
  1164. struct SubProcess
  1165. {
  1166. DWORD dwIndex;
  1167. DWORD dwSvcThreadId;
  1168. uint64 iRunningCommand;
  1169. PROCESS_INFORMATION pi;
  1170. SubProcessKernelObjects *pCommObjs;
  1171. };
  1172. CTHREADLOCAL( SubProcess * ) m_lpSubProcessInfo;
  1173. CUtlVector < SubProcess * > m_arrSubProcessInfos;
  1174. uint64 m_iFirstCommand;
  1175. uint64 m_iNextCommand;
  1176. uint64 m_iEndCommand;
  1177. uint64 m_iLastFinished;
  1178. CfgProcessor::ComboHandle m_hCombo;
  1179. DisconnectHandlerFn_t m_fnOldDisconnectHandler;
  1180. CAutoPushPop< DisconnectHandlerFn_t > m_autoRestoreDisconnectHandler;
  1181. void QuitSubs( void );
  1182. void TryToPackageData( uint64 iCommandNumber );
  1183. void PrepareSubProcess( SubProcess **ppSp, SubProcessKernelObjects **ppCommObjs );
  1184. };
  1185. template < typename TMutexType >
  1186. void CWorkerAccumState < TMutexType > ::RangeBegin( uint64 iFirstCommand, uint64 iEndCommand )
  1187. {
  1188. m_iFirstCommand = iFirstCommand;
  1189. m_iNextCommand = iFirstCommand;
  1190. m_iEndCommand = iEndCommand;
  1191. m_iLastFinished = iFirstCommand;
  1192. m_hCombo = NULL;
  1193. CfgProcessor::Combo_GetNext( m_iNextCommand, m_hCombo, m_iEndCommand );
  1194. g_fnDisconnectHandler = Special_DisconnectHandler;
  1195. // Notify all connected sub-processes that the master is still alive
  1196. for ( int k = 0; k < m_arrSubProcessInfos.Count(); ++ k )
  1197. {
  1198. if ( SubProcess *pSp = m_arrSubProcessInfos[ k ] )
  1199. {
  1200. SubProcessKernelObjects_Memory shrmem( pSp->pCommObjs );
  1201. if ( void *pvMemory = shrmem.Lock() )
  1202. {
  1203. strcpy( ( char * ) pvMemory, "keepalive" );
  1204. shrmem.Unlock();
  1205. }
  1206. }
  1207. }
  1208. }
  1209. template < typename TMutexType >
  1210. void CWorkerAccumState < TMutexType > ::RangeFinished( void )
  1211. {
  1212. if( !DisconnectState() )
  1213. {
  1214. // Finish packaging data
  1215. TryToPackageData( m_iEndCommand - 1 );
  1216. }
  1217. else
  1218. {
  1219. // Master disconnected
  1220. QuitSubs();
  1221. }
  1222. g_fnDisconnectHandler = m_fnOldDisconnectHandler;
  1223. }
  1224. template < typename TMutexType >
  1225. void CWorkerAccumState < TMutexType > ::QuitSubs( void )
  1226. {
  1227. CUtlVector < HANDLE > m_arrWait;
  1228. m_arrWait.EnsureCapacity( m_arrSubProcessInfos.Count() );
  1229. for ( int k = 0; k < m_arrSubProcessInfos.Count(); ++ k )
  1230. {
  1231. if ( SubProcess *pSp = m_arrSubProcessInfos[ k ] )
  1232. {
  1233. SubProcessKernelObjects_Memory shrmem( pSp->pCommObjs );
  1234. if ( void *pvMemory = shrmem.Lock() )
  1235. {
  1236. strcpy( ( char * ) pvMemory, "quit" );
  1237. shrmem.Unlock();
  1238. }
  1239. m_arrWait.AddToTail( pSp->pi.hProcess );
  1240. }
  1241. }
  1242. if ( m_arrWait.Count() )
  1243. {
  1244. DWORD dwWait = WaitForMultipleObjects( m_arrWait.Count(), m_arrWait.Base(), TRUE, 2 * 1000 );
  1245. if ( WAIT_TIMEOUT == dwWait )
  1246. {
  1247. Warning( "Timed out while waiting for sub-processes to shut down!\n" );
  1248. }
  1249. }
  1250. for ( int k = 0; k < m_arrSubProcessInfos.Count(); ++ k )
  1251. {
  1252. if ( SubProcess *pSp = m_arrSubProcessInfos[ k ] )
  1253. {
  1254. CloseHandle( pSp->pi.hThread );
  1255. CloseHandle( pSp->pi.hProcess );
  1256. delete pSp->pCommObjs;
  1257. delete pSp;
  1258. }
  1259. }
  1260. if ( DisconnectState() )
  1261. Vmpi_Worker_DefaultDisconnectHandler( 0, "Master disconnected during compilation." );
  1262. }
  1263. template < typename TMutexType >
  1264. void CWorkerAccumState < TMutexType > ::PrepareSubProcess( SubProcess **ppSp, SubProcessKernelObjects **ppCommObjs )
  1265. {
  1266. SubProcess *pSp = m_lpSubProcessInfo.Get();
  1267. SubProcessKernelObjects *pCommObjs = NULL;
  1268. if ( pSp )
  1269. {
  1270. pCommObjs = pSp->pCommObjs;
  1271. }
  1272. else
  1273. {
  1274. pSp = new SubProcess;
  1275. m_lpSubProcessInfo.Set( pSp );
  1276. pSp->dwSvcThreadId = ThreadGetCurrentId();
  1277. char chBaseNameBuffer[0x30];
  1278. sprintf( chBaseNameBuffer, "SHCMPL_SUB_%08X_%I64X_%08X", pSp->dwSvcThreadId, time( NULL ), GetCurrentProcessId() );
  1279. pCommObjs = pSp->pCommObjs = new SubProcessKernelObjects_Create( chBaseNameBuffer );
  1280. ZeroMemory( &pSp->pi, sizeof( pSp->pi ) );
  1281. STARTUPINFO si;
  1282. ZeroMemory( &si, sizeof( si ) );
  1283. si.cb = sizeof( si );
  1284. char chCommandLine[0x100];
  1285. sprintf( chCommandLine, "\"%s\\shadercompile.exe\" -subprocess %s", g_WorkerTempPath, chBaseNameBuffer );
  1286. #ifdef _DEBUG
  1287. V_strncat( chCommandLine, " -allowdebug", sizeof( chCommandLine ) );
  1288. #endif
  1289. BOOL bCreateResult = CreateProcess( NULL, chCommandLine, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, g_WorkerTempPath, &si, &pSp->pi );
  1290. ( void ) bCreateResult;
  1291. Assert( bCreateResult && "CreateProcess failed?" );
  1292. m_pMutex->Lock();
  1293. pSp->dwIndex = m_arrSubProcessInfos.AddToTail( pSp );
  1294. m_pMutex->Unlock();
  1295. }
  1296. if ( ppSp ) *ppSp = pSp;
  1297. if ( ppCommObjs ) *ppCommObjs = pCommObjs;
  1298. }
  1299. template < typename TMutexType >
  1300. void CWorkerAccumState < TMutexType > ::ExecuteCompileCommandThreaded( CfgProcessor::ComboHandle hCombo )
  1301. {
  1302. // DebugOut( "threaded: running: \"%s\"\n", szCommand );
  1303. SubProcessKernelObjects *pCommObjs = NULL;
  1304. PrepareSubProcess( NULL, &pCommObjs );
  1305. // Execute the command
  1306. SubProcessKernelObjects_Memory shrmem( pCommObjs );
  1307. {
  1308. void *pvMemory = shrmem.Lock();
  1309. Assert( pvMemory );
  1310. Combo_FormatCommand( hCombo, ( char * ) pvMemory );
  1311. shrmem.Unlock();
  1312. }
  1313. // Obtain the command response
  1314. {
  1315. void const *pvMemory = shrmem.Lock();
  1316. Assert( pvMemory );
  1317. // TODO: Vitaliy :: TEMP fix:
  1318. // Usually what happens if we fail to lock here is
  1319. // when our subprocess dies and to recover we will
  1320. // attempt to restart on another worker.
  1321. if ( !pvMemory )
  1322. // ::RaiseException( GetLastError(), EXCEPTION_NONCONTINUABLE, 0, NULL );
  1323. TerminateProcess( GetCurrentProcess(), 1 );
  1324. CmdSink::IResponse *pResponse;
  1325. if ( pvMemory )
  1326. pResponse = new CSubProcessResponse( pvMemory );
  1327. else
  1328. pResponse = new CmdSink::CResponseError;
  1329. HandleCommandResponse( hCombo, pResponse );
  1330. delete pResponse;
  1331. shrmem.Unlock();
  1332. }
  1333. }
  1334. template < typename TMutexType >
  1335. void CWorkerAccumState < TMutexType > ::ExecuteCompileCommand( CfgProcessor::ComboHandle hCombo )
  1336. {
  1337. CmdSink::IResponse *pResponse = NULL;
  1338. {
  1339. char chBuffer[ 4096 ];
  1340. Combo_FormatCommand( hCombo, chBuffer );
  1341. DebugOut( "running: \"%s\"\n", chBuffer );
  1342. MySystem( chBuffer, &pResponse );
  1343. }
  1344. HandleCommandResponse( hCombo, pResponse );
  1345. }
  1346. template < typename TMutexType >
  1347. void CWorkerAccumState < TMutexType > ::HandleCommandResponse( CfgProcessor::ComboHandle hCombo, CmdSink::IResponse *pResponse )
  1348. {
  1349. VMPI_HandleSocketErrors();
  1350. if ( !pResponse )
  1351. pResponse = new CmdSink::CResponseFiles( "shader.o", "output.txt" );
  1352. // Command info
  1353. CfgProcessor::CfgEntryInfo const *pEntryInfo = Combo_GetEntryInfo( hCombo );
  1354. uint64 iComboIndex = Combo_GetComboNum( hCombo );
  1355. uint64 iCommandNumber = Combo_GetCommandNum( hCombo );
  1356. if ( pResponse->Succeeded() )
  1357. {
  1358. GLOBAL_DATA_MTX_LOCK();
  1359. uint64 nStComboIdx = iComboIndex / pEntryInfo->m_numDynamicCombos;
  1360. uint64 nDyComboIdx = iComboIndex - ( nStComboIdx * pEntryInfo->m_numDynamicCombos );
  1361. StaticComboFromDictAdd( pEntryInfo->m_szName, nStComboIdx )->AddDynamicCombo( nDyComboIdx , pResponse->GetResultBuffer(), pResponse->GetResultBufferLen() );
  1362. GLOBAL_DATA_MTX_UNLOCK();
  1363. }
  1364. // Tell the master that this shader failed
  1365. if ( !pResponse->Succeeded() )
  1366. {
  1367. GLOBAL_DATA_MTX_LOCK();
  1368. ShaderHadErrorDispatchInt( pEntryInfo->m_szName );
  1369. GLOBAL_DATA_MTX_UNLOCK();
  1370. }
  1371. // Process listing even if the shader succeeds for warnings
  1372. char const *szListing = pResponse->GetListing();
  1373. if ( ( !g_bSuppressWarnings && szListing ) || !pResponse->Succeeded() )
  1374. {
  1375. char chCommandNumber[50];
  1376. sprintf( chCommandNumber, "%I64u", iCommandNumber );
  1377. char chUnreportedListing[0xFF];
  1378. if ( !szListing )
  1379. {
  1380. sprintf( chUnreportedListing, "(0): error %s: shadercompile.cpp: Compiler failed without error description - possible DX_PROXY DLL, D3DX DLL, D3DCOMPILER DLL, or other .DLL dependency problem?", chCommandNumber );
  1381. szListing = chUnreportedListing;
  1382. }
  1383. // Send the listing for dispatch
  1384. CUtlBinaryBlock errMsg;
  1385. errMsg.SetLength(
  1386. strlen( chCommandNumber ) + 1 + // command + newline
  1387. strlen( szListing ) + 1 + // listing + newline
  1388. 1 // null-terminator
  1389. );
  1390. sprintf( ( char * ) errMsg.Get(), "%s\n%s\n", chCommandNumber, szListing );
  1391. GLOBAL_DATA_MTX_LOCK();
  1392. ErrMsgDispatchInt( ( char * ) errMsg.Get(), pEntryInfo->m_szShaderFileName );
  1393. GLOBAL_DATA_MTX_UNLOCK();
  1394. }
  1395. // Maybe zip things up
  1396. TryToPackageData( iCommandNumber );
  1397. }
  1398. template < typename TMutexType >
  1399. void CWorkerAccumState < TMutexType > ::TryToPackageData( uint64 iCommandNumber )
  1400. {
  1401. m_pMutex->Lock();
  1402. uint64 iFinishedByNow = iCommandNumber + 1;
  1403. // Check if somebody is running an earlier command
  1404. for ( int k = 0; k < m_arrSubProcessInfos.Count(); ++ k )
  1405. {
  1406. if ( SubProcess *pSp = m_arrSubProcessInfos[ k ] )
  1407. {
  1408. if ( pSp->iRunningCommand < iCommandNumber )
  1409. {
  1410. iFinishedByNow = 0;
  1411. break;
  1412. }
  1413. }
  1414. }
  1415. uint64 iLastFinished = m_iLastFinished;
  1416. if ( iFinishedByNow > m_iLastFinished )
  1417. {
  1418. m_iLastFinished = iFinishedByNow;
  1419. m_pMutex->Unlock();
  1420. }
  1421. else
  1422. {
  1423. m_pMutex->Unlock();
  1424. return;
  1425. }
  1426. CfgProcessor::ComboHandle hChBegin = CfgProcessor::Combo_GetCombo( iLastFinished );
  1427. CfgProcessor::ComboHandle hChEnd = CfgProcessor::Combo_GetCombo( iFinishedByNow );
  1428. Assert( hChBegin && hChEnd );
  1429. CfgProcessor::CfgEntryInfo const *pInfoBegin = Combo_GetEntryInfo( hChBegin );
  1430. CfgProcessor::CfgEntryInfo const *pInfoEnd = Combo_GetEntryInfo( hChEnd );
  1431. uint64 nComboBegin = Combo_GetComboNum( hChBegin ) / pInfoBegin->m_numDynamicCombos;
  1432. uint64 nComboEnd = Combo_GetComboNum( hChEnd ) / pInfoEnd->m_numDynamicCombos;
  1433. for ( ; pInfoBegin && (
  1434. ( pInfoBegin->m_iCommandStart < pInfoEnd->m_iCommandStart ) ||
  1435. ( nComboBegin > nComboEnd ) ); )
  1436. {
  1437. // Zip this combo
  1438. MessageBuffer mbPacked;
  1439. size_t nPackedLength = AssembleWorkerReplyPackage( pInfoBegin, nComboBegin, &mbPacked );
  1440. if ( nPackedLength )
  1441. {
  1442. // Packed buffer
  1443. GLOBAL_DATA_MTX_LOCK();
  1444. uint8 *pCodeBuffer = StaticComboFromDictAdd( pInfoBegin->m_szName,
  1445. nComboBegin )->AllocPackedCodeBlock( nPackedLength );
  1446. GLOBAL_DATA_MTX_UNLOCK();
  1447. if ( pCodeBuffer )
  1448. mbPacked.read( pCodeBuffer, nPackedLength );
  1449. }
  1450. // Next iteration
  1451. if ( ! nComboBegin -- )
  1452. {
  1453. Combo_Free( hChBegin );
  1454. if ( ( hChBegin = CfgProcessor::Combo_GetCombo( pInfoBegin->m_iCommandEnd ) ) != NULL )
  1455. {
  1456. pInfoBegin = Combo_GetEntryInfo( hChBegin );
  1457. nComboBegin = pInfoBegin->m_numStaticCombos - 1;
  1458. }
  1459. }
  1460. }
  1461. Combo_Free( hChBegin );
  1462. Combo_Free( hChEnd );
  1463. }
  1464. template < typename TMutexType >
  1465. bool CWorkerAccumState < TMutexType > ::OnProcess()
  1466. {
  1467. m_pMutex->Lock();
  1468. CfgProcessor::ComboHandle hThreadCombo = m_hCombo ? Combo_Alloc( m_hCombo ) : NULL;
  1469. m_pMutex->Unlock();
  1470. uint64 iThreadCommand = ~uint64(0);
  1471. SubProcess *pSp = NULL;
  1472. PrepareSubProcess( &pSp, NULL );
  1473. for ( ; ; )
  1474. {
  1475. m_pMutex->Lock();
  1476. if ( DisconnectState() )
  1477. Combo_Free( m_hCombo );
  1478. if ( m_hCombo )
  1479. {
  1480. Combo_Assign( hThreadCombo, m_hCombo );
  1481. pSp->iRunningCommand = Combo_GetCommandNum( hThreadCombo );
  1482. Combo_GetNext( iThreadCommand, m_hCombo, m_iEndCommand );
  1483. }
  1484. else
  1485. {
  1486. Combo_Free( hThreadCombo );
  1487. iThreadCommand = ~uint64(0);
  1488. pSp->iRunningCommand = ~uint64(0);
  1489. }
  1490. m_pMutex->Unlock();
  1491. if ( hThreadCombo )
  1492. {
  1493. ExecuteCompileCommandThreaded( hThreadCombo );
  1494. }
  1495. else
  1496. break;
  1497. }
  1498. Combo_Free( hThreadCombo );
  1499. return false;
  1500. }
  1501. template < typename TMutexType >
  1502. bool CWorkerAccumState < TMutexType > ::OnProcessST()
  1503. {
  1504. while ( m_hCombo )
  1505. {
  1506. ExecuteCompileCommand( m_hCombo );
  1507. Combo_GetNext( m_iNextCommand, m_hCombo, m_iEndCommand );
  1508. }
  1509. return false;
  1510. }
  1511. //
  1512. // Worker_ProcessCommandRange_Singleton
  1513. //
  1514. class Worker_ProcessCommandRange_Singleton
  1515. {
  1516. public:
  1517. static Worker_ProcessCommandRange_Singleton *& Instance() { static Worker_ProcessCommandRange_Singleton *s_ptr = NULL; return s_ptr; }
  1518. static Worker_ProcessCommandRange_Singleton * GetInstance() { Worker_ProcessCommandRange_Singleton *p = Instance(); Assert( p ); return p; }
  1519. public:
  1520. Worker_ProcessCommandRange_Singleton() { Assert( !Instance() ); Instance() = this; Startup(); }
  1521. ~Worker_ProcessCommandRange_Singleton() { Assert( Instance() == this ); Instance() = NULL; Shutdown(); }
  1522. public:
  1523. void ProcessCommandRange( uint64 shaderStart, uint64 shaderEnd );
  1524. protected:
  1525. void Startup( void );
  1526. void Shutdown( void );
  1527. //
  1528. // Multi-threaded section
  1529. protected:
  1530. struct MT {
  1531. MT() : pWorkerObj( NULL ), pThreadPool( NULL ) {}
  1532. typedef CThreadFastMutex MultiThreadMutex_t;
  1533. MultiThreadMutex_t mtx;
  1534. typedef CWorkerAccumState < MultiThreadMutex_t > WorkerClass_t;
  1535. WorkerClass_t *pWorkerObj;
  1536. IThreadPool *pThreadPool;
  1537. ThreadPoolStartParams_t tpsp;
  1538. } m_MT;
  1539. //
  1540. // Single-threaded section
  1541. protected:
  1542. struct ST {
  1543. ST() : pWorkerObj( NULL ) {}
  1544. typedef CThreadNullMutex MultiThreadMutex_t;
  1545. MultiThreadMutex_t mtx;
  1546. typedef CWorkerAccumState < MultiThreadMutex_t > WorkerClass_t;
  1547. WorkerClass_t *pWorkerObj;
  1548. } m_ST;
  1549. };
  1550. void Worker_ProcessCommandRange_Singleton::Startup( void )
  1551. {
  1552. bool bInitializedThreadPool = false;
  1553. CPUInformation const &cpu = GetCPUInformation();
  1554. if ( cpu.m_nLogicalProcessors > 1 )
  1555. {
  1556. // Attempt to initialize thread pool
  1557. m_MT.pThreadPool = CommandLine()->FindParm("-singlethreaded") ? NULL : g_pThreadPool;
  1558. if ( m_MT.pThreadPool )
  1559. {
  1560. m_MT.tpsp.bIOThreads = false;
  1561. m_MT.tpsp.nThreads = cpu.m_nLogicalProcessors - 1;
  1562. if ( m_MT.pThreadPool->Start( m_MT.tpsp ) )
  1563. {
  1564. if ( m_MT.pThreadPool->NumThreads() >= 1 )
  1565. {
  1566. // Make sure that our mutex is in multi-threaded mode
  1567. Threading::g_mtxGlobal.SetThreadedMode( Threading::eMultiThreaded );
  1568. m_MT.pWorkerObj = new MT::WorkerClass_t( &m_MT.mtx );
  1569. bInitializedThreadPool = true;
  1570. }
  1571. else
  1572. {
  1573. m_MT.pThreadPool->Stop();
  1574. }
  1575. }
  1576. if ( !bInitializedThreadPool )
  1577. m_MT.pThreadPool = NULL;
  1578. }
  1579. }
  1580. // Otherwise initialize single-threaded mode
  1581. if ( !bInitializedThreadPool )
  1582. {
  1583. m_ST.pWorkerObj = new ST::WorkerClass_t( &m_ST.mtx );
  1584. }
  1585. }
  1586. void Worker_ProcessCommandRange_Singleton::Shutdown( void )
  1587. {
  1588. if ( m_MT.pThreadPool )
  1589. {
  1590. if( m_MT.pWorkerObj )
  1591. delete m_MT.pWorkerObj;
  1592. m_MT.pThreadPool->Stop();
  1593. m_MT.pThreadPool = NULL;
  1594. }
  1595. else
  1596. {
  1597. if ( m_ST.pWorkerObj )
  1598. delete m_ST.pWorkerObj;
  1599. }
  1600. }
  1601. void Worker_ProcessCommandRange_Singleton::ProcessCommandRange( uint64 shaderStart, uint64 shaderEnd )
  1602. {
  1603. if ( m_MT.pThreadPool )
  1604. {
  1605. MT::WorkerClass_t *pWorkerObj = m_MT.pWorkerObj;
  1606. pWorkerObj->RangeBegin( shaderStart, shaderEnd );
  1607. pWorkerObj->Run();
  1608. pWorkerObj->RangeFinished();
  1609. }
  1610. else
  1611. {
  1612. ST::WorkerClass_t *pWorkerObj = m_ST.pWorkerObj;
  1613. pWorkerObj->RangeBegin( shaderStart, shaderEnd );
  1614. pWorkerObj->OnProcessST();
  1615. pWorkerObj->RangeFinished();
  1616. }
  1617. }
  1618. // You must process the work unit range.
  1619. void Worker_ProcessCommandRange( uint64 shaderStart, uint64 shaderEnd )
  1620. {
  1621. Worker_ProcessCommandRange_Singleton::GetInstance()->ProcessCommandRange( shaderStart, shaderEnd );
  1622. }
  1623. // You must append data to pBuf with the work unit results.
  1624. void Worker_ProcessWorkUnitFn( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf )
  1625. {
  1626. uint64 comboStart = iWorkUnit * g_nStaticCombosPerWorkUnit;
  1627. uint64 comboEnd = comboStart + g_nStaticCombosPerWorkUnit;
  1628. comboEnd = min( g_numStaticCombos, comboEnd );
  1629. // Determine the commands required to be executed:
  1630. uint64 nComboOfTheEntry = 0;
  1631. CfgProcessor::CfgEntryInfo const *pEntry = NULL;
  1632. pEntry = GetEntryByStaticComboNum( comboEnd, &nComboOfTheEntry );
  1633. uint64 commandEnd = pEntry->m_iCommandStart + nComboOfTheEntry * pEntry->m_numDynamicCombos;
  1634. Assert( commandEnd <= g_numCompileCommands );
  1635. pEntry = GetEntryByStaticComboNum( comboStart, &nComboOfTheEntry );
  1636. uint64 commandStart = pEntry->m_iCommandStart + nComboOfTheEntry * pEntry->m_numDynamicCombos;
  1637. // Compile all the shader combos
  1638. Worker_ProcessCommandRange( commandStart, commandEnd );
  1639. nComboOfTheEntry = pEntry->m_numStaticCombos - 1 - nComboOfTheEntry;
  1640. // Copy off the reply packages
  1641. int nSkipsSoFar = 0;
  1642. for ( uint64 kCombo = comboStart; kCombo < comboEnd; ++ kCombo )
  1643. {
  1644. size_t nCpBytes = CopyWorkerReplyPackage( pEntry, nComboOfTheEntry, pBuf, nSkipsSoFar );
  1645. if ( nCpBytes )
  1646. nSkipsSoFar = 0;
  1647. else
  1648. -- nSkipsSoFar;
  1649. if ( nComboOfTheEntry == 0 )
  1650. {
  1651. ++pEntry;
  1652. nComboOfTheEntry = pEntry->m_numStaticCombos;
  1653. }
  1654. nComboOfTheEntry--;
  1655. }
  1656. if ( nSkipsSoFar )
  1657. {
  1658. pBuf->write( &nSkipsSoFar, sizeof( nSkipsSoFar ) );
  1659. }
  1660. // Copy off SCE-CGC compiler generated metadata, used for shader debugging
  1661. if ( g_bIsPS3 )
  1662. {
  1663. PS3SendShaderCompileLogContentsToMaster();
  1664. if ( g_bGeneratePS3DebugInfo )
  1665. {
  1666. SendSubDirectoryToMaster( "cgc-capture" );
  1667. }
  1668. }
  1669. //////////////////////////////////////////////////////////////////////////
  1670. //
  1671. // Now deliver all our accumulated spew to the master
  1672. //
  1673. //////////////////////////////////////////////////////////////////////////
  1674. // Failed shaders
  1675. for ( int k = 0, kEnd = g_Master_ShaderHadError.GetNumStrings(); k < kEnd; ++ k )
  1676. {
  1677. char const *szShaderName = g_Master_ShaderHadError.String( k );
  1678. if ( !g_Master_ShaderHadError[ int_as_symid( k ) ] )
  1679. continue;
  1680. int const len = strlen( szShaderName );
  1681. CUtlBinaryBlock bb;
  1682. bb.SetLength( 1 + len + 1 );
  1683. sprintf( ( char * ) bb.Get(), "%c%s", SHADERHADERROR_PACKETID, szShaderName );
  1684. VMPI_SendData( bb.Get(), bb.Length(), VMPI_MASTER_ID );
  1685. VMPI_HandleSocketErrors();
  1686. }
  1687. // Compiler spew
  1688. for ( int k = 0, kEnd = g_Master_CompilerMsgInfo.GetNumStrings(); k < kEnd; ++ k )
  1689. {
  1690. char const * const szMsg = g_Master_CompilerMsgInfo.String( k );
  1691. CompilerMsgInfo const &cmi = g_Master_CompilerMsgInfo[ int_as_symid( k ) ];
  1692. char const * const szFirstCmd = cmi.GetFirstCommand();
  1693. int const numReported = cmi.GetNumTimesReported();
  1694. char chNumReported[0x40];
  1695. sprintf( chNumReported, "%d", numReported );
  1696. CUtlBinaryBlock bb;
  1697. bb.SetLength( 1 + strlen(szMsg) + 1 + strlen( szFirstCmd ) + 1 + strlen( chNumReported ) + 1 + 1 );
  1698. sprintf( ( char * ) bb.Get(), "%c%s\n%s\n%s\n", ERRMSG_PACKETID, szMsg, szFirstCmd, chNumReported );
  1699. VMPI_SendData( bb.Get(), bb.Length(), VMPI_MASTER_ID );
  1700. VMPI_HandleSocketErrors();
  1701. }
  1702. // Clean all reported msgs
  1703. g_Master_CompilerMsgInfo.Purge();
  1704. }
  1705. void Shader_ParseShaderInfoFromCompileCommands( CfgProcessor::CfgEntryInfo const *pEntry, ShaderInfo_t &shaderInfo )
  1706. {
  1707. if ( CfgProcessor::ComboHandle hCombo = CfgProcessor::Combo_GetCombo( pEntry->m_iCommandStart ) )
  1708. {
  1709. char cmd[ 4096 ];
  1710. Combo_FormatCommand( hCombo, cmd );
  1711. {
  1712. memset( &shaderInfo, 0, sizeof( ShaderInfo_t ) );
  1713. const char *pCentroidMask;
  1714. const char *pFlags;
  1715. const char *pShaderModel;
  1716. if ( g_bIsPS3 )
  1717. {
  1718. pCentroidMask = strstr( cmd, "-DCENTROIDMASK=" );
  1719. pFlags = strstr( cmd, "-DFLAGS=0x" );
  1720. pShaderModel = strstr( cmd, "-DSHADER_MODEL_" );
  1721. }
  1722. else
  1723. {
  1724. pCentroidMask = strstr( cmd, "/DCENTROIDMASK=" );
  1725. pFlags = strstr( cmd, "/DFLAGS=0x" );
  1726. pShaderModel = strstr( cmd, "/DSHADER_MODEL_" );
  1727. }
  1728. if( !pCentroidMask || !pFlags || !pShaderModel )
  1729. {
  1730. Assert( !"!pCentroidMask || !pFlags || !pShaderModel" );
  1731. return;
  1732. }
  1733. // Don't need to adjust the string for PS3 because it's the same length
  1734. sscanf( pCentroidMask + strlen( "/DCENTROIDMASK=" ), "%u", &shaderInfo.m_CentroidMask );
  1735. sscanf( pFlags + strlen( "/DFLAGS=0x" ), "%x", &shaderInfo.m_Flags );
  1736. // Copy shader model
  1737. pShaderModel += strlen( "-DSHADER_MODEL_" );
  1738. for ( char *pszSm = shaderInfo.m_szShaderModel, * const pszEnd = pszSm + sizeof( shaderInfo.m_szShaderModel ) - 1;
  1739. pszSm < pszEnd ; ++ pszSm )
  1740. {
  1741. char &rchLastChar = (*pszSm = *pShaderModel ++);
  1742. if ( !rchLastChar ||
  1743. V_isspace( rchLastChar ) ||
  1744. '=' == rchLastChar )
  1745. {
  1746. rchLastChar = 0;
  1747. break;
  1748. }
  1749. }
  1750. shaderInfo.m_nShaderCombo = 0;
  1751. shaderInfo.m_nTotalShaderCombos = pEntry->m_numCombos;
  1752. shaderInfo.m_nDynamicCombos = pEntry->m_numDynamicCombos;
  1753. shaderInfo.m_nStaticCombo = 0;
  1754. shaderInfo.m_pShaderName = pEntry->m_szName;
  1755. shaderInfo.m_pShaderSrc = pEntry->m_szShaderFileName;
  1756. }
  1757. Combo_Free( hCombo );
  1758. }
  1759. }
  1760. void Worker_GetLocalCopyOfShaders( void )
  1761. {
  1762. // Create virtual files for all of the stuff that we need to compile the shader
  1763. // make sure and prefix the file name so that it doesn't find it locally.
  1764. char filename[1024];
  1765. sprintf( filename, "%s\\uniquefilestocopy.txt", g_pShaderPath );
  1766. CUtlInplaceBuffer bffr( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1767. if( !g_pFileSystem->ReadFile( filename, NULL, bffr ) )
  1768. {
  1769. fprintf( stderr, "Can't open uniquefilestocopy.txt!\n" );
  1770. exit( -1 );
  1771. }
  1772. while( char *pszLineToCopy = bffr.InplaceGetLinePtr() )
  1773. {
  1774. sprintf( filename, "%s\\%s", g_pShaderPath, pszLineToCopy );
  1775. if ( g_bVerbose )
  1776. printf( "getting local copy of shader: \"%s\" (\"%s\")\n", pszLineToCopy, filename );
  1777. CUtlBuffer fileBuf;
  1778. if ( !g_pFileSystem->ReadFile( filename, NULL, fileBuf ) )
  1779. {
  1780. Warning( "Can't find \"%s\"\n", filename );
  1781. continue;
  1782. }
  1783. // Grab just the filename.
  1784. char justFilename[MAX_PATH];
  1785. char *pLastSlash = max( strrchr( pszLineToCopy, '/' ), strrchr( pszLineToCopy, '\\' ) );
  1786. if ( pLastSlash )
  1787. Q_strncpy( justFilename, pLastSlash + 1, sizeof( justFilename ) );
  1788. else
  1789. Q_strncpy( justFilename, pszLineToCopy, sizeof( justFilename ) );
  1790. sprintf( filename, "%s%s", g_WorkerTempPath, justFilename );
  1791. if ( g_bVerbose )
  1792. printf( "creating \"%s\"\n", filename );
  1793. FILE *fp3 = fopen( filename, "wb" );
  1794. if ( !fp3 )
  1795. {
  1796. Error( "Can't open '%s' for writing.", pszLineToCopy );
  1797. continue;
  1798. }
  1799. fwrite( fileBuf.Base(), 1, fileBuf.GetBytesRemaining(), fp3 );
  1800. fclose( fp3 );
  1801. // SUPER EVIL, but if we don't do this, Windows will randomly nuke files of ours
  1802. // while we're running since they're in the temp path.
  1803. static CUtlVector< FILE * > s_arrHackedFiles;
  1804. static struct X_s_arrHackedFiles { ~X_s_arrHackedFiles() {
  1805. for ( int k = 0; k < s_arrHackedFiles.Count(); ++ k )
  1806. fclose( s_arrHackedFiles[k] );
  1807. } } s_autoCloseHackedFiles;
  1808. /* THIS IS THE EVIL LINE ----> */ FILE *fHack = fopen( filename, "r" );
  1809. s_arrHackedFiles.AddToTail( fHack );
  1810. // -- END of EVIL
  1811. }
  1812. }
  1813. void Worker_GetLocalCopyOfBinary( const char *pFilename )
  1814. {
  1815. CUtlBuffer fileBuf;
  1816. char tmpFilename[MAX_PATH];
  1817. sprintf( tmpFilename, "%s\\%s", g_ExeDir, pFilename );
  1818. if ( g_bVerbose )
  1819. printf( "trying to open: %s\n", tmpFilename );
  1820. FILE *fp = fopen( tmpFilename, "rb" );
  1821. if( !fp )
  1822. {
  1823. Assert( 0 );
  1824. fprintf( stderr, "Can't open %s!\n", pFilename );
  1825. exit( -1 );
  1826. }
  1827. fseek( fp, 0, SEEK_END );
  1828. int fileLen = ftell( fp );
  1829. fseek( fp, 0, SEEK_SET );
  1830. fileBuf.EnsureCapacity( fileLen );
  1831. int nBytesRead = fread( fileBuf.Base(), 1, fileLen, fp );
  1832. fclose( fp );
  1833. fileBuf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  1834. char newFilename[MAX_PATH];
  1835. sprintf( newFilename, "%s%s", g_WorkerTempPath, pFilename );
  1836. FILE *fp2 = fopen( newFilename, "wb" );
  1837. if( !fp2 )
  1838. {
  1839. Assert( 0 );
  1840. fprintf( stderr, "Can't open %s!\n", newFilename );
  1841. exit( -1 );
  1842. }
  1843. fwrite( fileBuf.Base(), 1, fileLen, fp2 );
  1844. fclose( fp2 );
  1845. // SUPER EVIL, but if we don't do this, Windows will randomly nuke files of ours
  1846. // while we're running since they're in the temp path.
  1847. fopen( newFilename, "r" );
  1848. }
  1849. void Worker_GetLocalCopyOfBinaries( void )
  1850. {
  1851. Worker_GetLocalCopyOfBinary( "mysql_wrapper.dll" ); // This is necessary so VMPI doesn't run in SDK mode.
  1852. Worker_GetLocalCopyOfBinary( "vstdlib.dll" );
  1853. Worker_GetLocalCopyOfBinary( "tier0.dll" );
  1854. }
  1855. void Shared_ParseListOfCompileCommands( void )
  1856. {
  1857. double tt_start = Plat_FloatTime();
  1858. char fileListFileName[1024] = {0};
  1859. sprintf( fileListFileName, "%s\\filelist.txt", g_pShaderPath );
  1860. CUtlInplaceBuffer bffr( 0, 0, CUtlInplaceBuffer::TEXT_BUFFER );
  1861. if( !g_pFileSystem->ReadFile( fileListFileName, NULL, bffr) )
  1862. {
  1863. DebugOut( "Can't open %s!\n", fileListFileName );
  1864. fprintf( stderr, "Can't open %s!\n", fileListFileName );
  1865. exit( -1 );
  1866. }
  1867. CfgProcessor::ReadConfiguration( &bffr );
  1868. CfgProcessor::DescribeConfiguration( g_arrCompileEntries );
  1869. for ( CfgProcessor::CfgEntryInfo const *pInfo = g_arrCompileEntries.Get();
  1870. pInfo && pInfo->m_szName; ++ pInfo )
  1871. {
  1872. ++ g_numShaders;
  1873. g_numStaticCombos += pInfo->m_numStaticCombos;
  1874. g_numCompileCommands = pInfo->m_iCommandEnd;
  1875. }
  1876. double tt_end = Plat_FloatTime();
  1877. Msg( "\rCompiling %s commands. \r", PrettyPrintNumber( g_numCompileCommands ), (tt_end - tt_start) );
  1878. }
  1879. void SetupExeDir( int argc, char **argv )
  1880. {
  1881. strcpy( g_ExeDir, argv[0] );
  1882. Q_StripFilename( g_ExeDir );
  1883. if ( g_ExeDir[0] == 0 )
  1884. {
  1885. Q_strncpy( g_ExeDir, ".\\", sizeof( g_ExeDir ) );
  1886. }
  1887. Q_FixSlashes( g_ExeDir );
  1888. }
  1889. void SetupPaths( int argc, char **argv )
  1890. {
  1891. GetTempPath( sizeof( g_WorkerTempPath ), g_WorkerTempPath );
  1892. strcat( g_WorkerTempPath, "shadercompiletemp\\" );
  1893. char tmp[MAX_PATH];
  1894. sprintf( tmp, "rd /s /q \"%s\"", g_WorkerTempPath );
  1895. system( tmp );
  1896. _mkdir( g_WorkerTempPath );
  1897. // printf( "g_WorkerTempPath: \"%s\"\n", g_WorkerTempPath );
  1898. CommandLine()->CreateCmdLine( argc, argv );
  1899. g_pShaderPath = CommandLine()->ParmValue( "-shaderpath", "" );
  1900. g_bVerbose = CommandLine()->FindParm("-verbose") != 0;
  1901. }
  1902. void SetupDebugFile( void )
  1903. {
  1904. #ifdef DEBUGFP
  1905. const char *pComputerName = getenv( "COMPUTERNAME" );
  1906. char filename[MAX_PATH];
  1907. sprintf( filename, "\\\\fileserver\\user\\gary\\debug\\%s.txt", pComputerName );
  1908. g_WorkerDebugFp = fopen( filename, "w" );
  1909. Assert( g_WorkerDebugFp );
  1910. DebugOut( "opened debug file\n" );
  1911. #endif
  1912. }
  1913. void CompileShaders_NoVMPI()
  1914. {
  1915. Worker_ProcessCommandRange_Singleton pcr;
  1916. //
  1917. // We will iterate on the cfg entries and process them
  1918. //
  1919. for ( CfgProcessor::CfgEntryInfo const *pEntry = g_arrCompileEntries.Get();
  1920. pEntry && pEntry->m_szName; ++ pEntry )
  1921. {
  1922. //
  1923. // Stick the shader info
  1924. //
  1925. ShaderInfo_t siLastShaderInfo;
  1926. memset( &siLastShaderInfo, 0, sizeof( siLastShaderInfo ) );
  1927. Shader_ParseShaderInfoFromCompileCommands( pEntry, siLastShaderInfo );
  1928. g_ShaderToShaderInfo[ pEntry->m_szName ] = siLastShaderInfo;
  1929. //
  1930. // Compile stuff
  1931. //
  1932. Worker_ProcessCommandRange( pEntry->m_iCommandStart, pEntry->m_iCommandEnd );
  1933. //
  1934. // Now when the whole shader is finished we can write it
  1935. //
  1936. char const *szShaderToWrite = pEntry->m_szName;
  1937. g_numCommandsCompleted = g_numCompileCommands;
  1938. WriteShaderFiles( szShaderToWrite );
  1939. g_numCommandsCompleted = pEntry->m_iCommandEnd;
  1940. }
  1941. Msg( "\r \r" );
  1942. }
  1943. class CDistributeShaderCompileMaster : public IWorkUnitDistributorCallbacks
  1944. {
  1945. public:
  1946. CDistributeShaderCompileMaster( void );
  1947. ~CDistributeShaderCompileMaster( void );
  1948. public:
  1949. virtual void OnWorkUnitsCompleted( uint64 numWorkUnits );
  1950. private:
  1951. void ThreadProc( void );
  1952. friend DWORD WINAPI CDistributeShaderCompileMaster::ThreadProcAdapter( LPVOID pvArg );
  1953. static DWORD WINAPI ThreadProcAdapter( LPVOID pvArg ) { reinterpret_cast< CDistributeShaderCompileMaster * >( pvArg )->ThreadProc(); return 0; }
  1954. private:
  1955. HANDLE m_hThread;
  1956. HANDLE m_hEvent;
  1957. CThreadFastMutex m_mtx;
  1958. BOOL m_bRunning;
  1959. private:
  1960. CfgProcessor::CfgEntryInfo const *m_pAnalyzeShaders;
  1961. CUtlVector< char const * > m_arrShaderNamesToWrite;
  1962. };
  1963. CDistributeShaderCompileMaster::CDistributeShaderCompileMaster( void ) :
  1964. m_hThread( NULL ),
  1965. m_hEvent( NULL ),
  1966. m_bRunning( TRUE )
  1967. {
  1968. m_hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  1969. m_hThread = CreateThread( NULL, 0, ThreadProcAdapter, reinterpret_cast< LPVOID >(this), 0, NULL );
  1970. m_pAnalyzeShaders = g_arrCompileEntries.Get();
  1971. }
  1972. CDistributeShaderCompileMaster::~CDistributeShaderCompileMaster( void )
  1973. {
  1974. m_bRunning = FALSE;
  1975. SetEvent( m_hEvent );
  1976. WaitForSingleObject( m_hThread, INFINITE );
  1977. CloseHandle( m_hThread );
  1978. CloseHandle( m_hEvent );
  1979. }
  1980. void CDistributeShaderCompileMaster::OnWorkUnitsCompleted( uint64 numWorkUnits )
  1981. {
  1982. // Make sure that our mutex is in multi-threaded mode
  1983. Threading::g_mtxGlobal.SetThreadedMode( Threading::eMultiThreaded );
  1984. // Figure out how many commands have completed based on work units
  1985. g_numCompletedStaticCombos = numWorkUnits * g_nStaticCombosPerWorkUnit;
  1986. uint64 numStaticCombosOfTheEntry = 0;
  1987. CfgProcessor::CfgEntryInfo const *pEntry = GetEntryByStaticComboNum( g_numCompletedStaticCombos, &numStaticCombosOfTheEntry );
  1988. g_numCommandsCompleted = pEntry->m_iCommandStart + numStaticCombosOfTheEntry * pEntry->m_numDynamicCombos;
  1989. // Iterate over the shaders yet to be written and see if we can queue them
  1990. for ( ; m_pAnalyzeShaders->m_szName &&
  1991. m_pAnalyzeShaders->m_iCommandEnd <= g_numCommandsCompleted;
  1992. ++ m_pAnalyzeShaders
  1993. )
  1994. {
  1995. m_mtx.Lock();
  1996. m_arrShaderNamesToWrite.AddToTail( m_pAnalyzeShaders->m_szName );
  1997. SetEvent( m_hEvent );
  1998. m_mtx.Unlock();
  1999. }
  2000. }
  2001. void CDistributeShaderCompileMaster::ThreadProc( void )
  2002. {
  2003. for ( ; m_bRunning; )
  2004. {
  2005. WaitForSingleObject( m_hEvent, INFINITE );
  2006. // Do a pump of shaders to write
  2007. for ( int numShadersWritten = 0; /* forever */ ; ++ numShadersWritten )
  2008. {
  2009. m_mtx.Lock();
  2010. char const * szShaderToWrite = NULL;
  2011. if ( m_arrShaderNamesToWrite.Count() > numShadersWritten )
  2012. szShaderToWrite = m_arrShaderNamesToWrite[ numShadersWritten ];
  2013. else
  2014. m_arrShaderNamesToWrite.RemoveAll();
  2015. m_mtx.Unlock();
  2016. if ( !szShaderToWrite )
  2017. break;
  2018. // We have the shader to write asynchronously
  2019. WriteShaderFiles( szShaderToWrite );
  2020. }
  2021. }
  2022. }
  2023. int ShaderCompile_Main( int argc, char* argv[] )
  2024. {
  2025. InstallSpewFunction();
  2026. g_bSuppressPrintfOutput = false;
  2027. g_flStartTime = Plat_FloatTime();
  2028. SetupDebugFile();
  2029. numthreads = 1; // managed specifically in Worker_ProcessCommandRange_Singleton::Startup
  2030. /*
  2031. Special section of code implementing "-subprocess" flag
  2032. */
  2033. if ( int iSubprocess = CommandLine()->FindParm( "-subprocess" ) )
  2034. {
  2035. char const *szSubProcessData = CommandLine()->GetParm( 1 + iSubprocess );
  2036. return ShaderCompile_Subprocess_Main( szSubProcessData );
  2037. }
  2038. // This needs to get called before VMPI is setup because in SDK mode, VMPI will change the args around.
  2039. SetupExeDir( argc, argv );
  2040. g_bIsX360 = CommandLine()->FindParm( "-x360" ) != 0;
  2041. g_bIsPS3 = CommandLine()->FindParm( "-ps3" ) != 0;
  2042. g_bGeneratePS3DebugInfo = CommandLine()->FindParm( "-ps3debug" ) != 0;
  2043. g_bOptimizePS3ShaderScheduling = CommandLine()->FindParm( "-ps3optimizeschedules" ) != 0;
  2044. // g_bSuppressWarnings = g_bIsX360;
  2045. bool bShouldUseVMPI = ( CommandLine()->FindParm( "-nompi" ) == 0 );
  2046. if ( bShouldUseVMPI )
  2047. {
  2048. // Master, start accepting connections.
  2049. // Worker, make a connection.
  2050. DebugOut( "Before VMPI_Init\n" );
  2051. g_bSuppressPrintfOutput = true;
  2052. VMPIRunMode mode = VMPI_RUN_NETWORKED;
  2053. if ( !VMPI_Init( argc, argv, "dependency_info_shadercompile.txt", MyDisconnectHandler, mode ) )
  2054. {
  2055. g_bSuppressPrintfOutput = false;
  2056. DebugOut( "MPI_Init failed.\n" );
  2057. Error( "MPI_Init failed." );
  2058. }
  2059. extern void VMPI_SetWorkUnitsPartitionSize( int numWusToDeal );
  2060. VMPI_SetWorkUnitsPartitionSize( 32 );
  2061. }
  2062. SetupPaths( argc, argv );
  2063. g_bSuppressPrintfOutput = false;
  2064. DebugOut( "After VMPI_Init\n" );
  2065. // Setting up the minidump handlers
  2066. if ( bShouldUseVMPI && !g_bMPIMaster )
  2067. SetupToolsMinidumpHandler( VMPI_ExceptionFilter );
  2068. else
  2069. SetupDefaultToolsMinidumpHandler();
  2070. if ( CommandLine()->FindParm( "-game" ) == 0 )
  2071. {
  2072. // Used with filesystem_stdio.dll
  2073. FileSystem_Init( NULL, 0, FS_INIT_COMPATIBILITY_MODE );
  2074. }
  2075. else
  2076. {
  2077. // SDK uses this since it only has filesystem_steam.dll.
  2078. FileSystem_Init( NULL, 0, FS_INIT_FULL );
  2079. }
  2080. DebugOut( "After VMPI_FileSystem_Init\n" );
  2081. Shared_ParseListOfCompileCommands();
  2082. DebugOut( "After Shared_ParseListOfCompileCommands\n" );
  2083. if ( bShouldUseVMPI )
  2084. {
  2085. // Partition combos
  2086. g_nStaticCombosPerWorkUnit = 0;
  2087. if ( g_numStaticCombos )
  2088. {
  2089. if ( g_numStaticCombos <= 1024 )
  2090. g_nStaticCombosPerWorkUnit = 1;
  2091. else if ( g_numStaticCombos > 1024 * 10 )
  2092. g_nStaticCombosPerWorkUnit = 10;
  2093. else
  2094. g_nStaticCombosPerWorkUnit = g_numStaticCombos / 1024;
  2095. }
  2096. uint64 nWorkUnits;
  2097. if( g_nStaticCombosPerWorkUnit == 0 )
  2098. {
  2099. nWorkUnits = 1;
  2100. g_nStaticCombosPerWorkUnit = g_numStaticCombos;
  2101. }
  2102. else
  2103. {
  2104. nWorkUnits = g_numStaticCombos / g_nStaticCombosPerWorkUnit + 1;
  2105. }
  2106. DebugOut( "Before conditional\n" );
  2107. if ( g_bMPIMaster )
  2108. {
  2109. if ( g_bIsPS3 && g_bGeneratePS3DebugInfo )
  2110. {
  2111. // Prepare the files on the master which we will use to store the large amount of
  2112. // debug metadata generated by the Sony Cg compiler on each worker machine.
  2113. InitializePS3ShaderDebugPackFiles();
  2114. }
  2115. // Send all of the workers the complete list of work to do.
  2116. DebugOut( "Before STARTWORK_PACKETID\n" );
  2117. char packetID = STARTWORK_PACKETID;
  2118. VMPI_SendData( &packetID, sizeof( packetID ), VMPI_PERSISTENT );
  2119. // Compile master distribution tracker
  2120. CDistributeShaderCompileMaster dscm;
  2121. g_pDistributeWorkCallbacks = &dscm;
  2122. {
  2123. char chCommands[50], chStaticCombos[50], chNumWorkUnits[50];
  2124. sprintf( chCommands, "%s", PrettyPrintNumber( g_numCompileCommands ) );
  2125. sprintf( chStaticCombos, "%s", PrettyPrintNumber( g_numStaticCombos ) );
  2126. sprintf( chNumWorkUnits, "%s", PrettyPrintNumber( nWorkUnits ) );
  2127. Msg( "\rCompiling %s commands in %s work units.\n", chCommands, chNumWorkUnits );
  2128. }
  2129. // nWorkUnits is how many work units. . .1000 is good.
  2130. // The work unit number impies which combo to do.
  2131. DebugOut( "Before DistributeWork\n" );
  2132. DistributeWork( nWorkUnits, NULL, Master_ReceiveWorkUnitFn );
  2133. g_pDistributeWorkCallbacks = NULL;
  2134. }
  2135. else
  2136. {
  2137. // wait until we get a packet from the master to start doing stuff.
  2138. MessageBuffer buf;
  2139. DebugOut( "Before VMPI_DispatchUntil\n" );
  2140. while ( !g_bGotStartWorkPacket )
  2141. {
  2142. VMPI_DispatchNextMessage();
  2143. }
  2144. DebugOut( "after VMPI_DispatchUntil\n" );
  2145. DebugOut( "Before Worker_GetLocalCopyOfShaders\n" );
  2146. Worker_GetLocalCopyOfShaders();
  2147. DebugOut( "Before Worker_GetLocalCopyOfBinaries\n" );
  2148. Worker_GetLocalCopyOfBinaries();
  2149. DebugOut( "Before _chdir\n" );
  2150. _chdir( g_WorkerTempPath );
  2151. if ( g_bIsPS3 )
  2152. {
  2153. char szLogFilename[MAX_PATH];
  2154. if ( GetEnvironmentVariableA( "PS3COMPILELOG", szLogFilename, sizeof( szLogFilename ) ) == 0 )
  2155. {
  2156. uint nUniqueIndex = ( (DWORD)GetCurrentProcessId() ^ (DWORD)GetCurrentThreadId() ) + (DWORD)GetTickCount() + (DWORD)&nWorkUnits;
  2157. sprintf_s( szLogFilename, sizeof( szLogFilename ), "%s__ps3compilelog%08X__.tmp", g_WorkerTempPath, nUniqueIndex );
  2158. _unlink( szLogFilename );
  2159. SetEnvironmentVariableA( "PS3COMPILELOG", szLogFilename );
  2160. }
  2161. SetEnvironmentVariableA( "PS3FINDOPTIMALSCHEDULES", g_bOptimizePS3ShaderScheduling ? "1" : "0" );
  2162. SetEnvironmentVariableA( "PS3OPTIMALSCHEDULESFILE", g_bOptimizePS3ShaderScheduling ? "" : "ps3optimalschedules.bin" );
  2163. if ( g_bGeneratePS3DebugInfo )
  2164. {
  2165. // Required by the Sony Cg compiler to emit debug metadata. Files are emitted on worker machine then copied back to master.
  2166. SetEnvironmentVariable( "SCECGC_CAPTUREDIR", g_WorkerTempPath );
  2167. }
  2168. }
  2169. // nWorkUnits is how many work units. . .1000 is good.
  2170. // The work unit number impies which combo to do.
  2171. DebugOut( "Before DistributeWork\n" );
  2172. // Allows calling into ProcessCommandRange inside the worker function
  2173. {
  2174. Worker_ProcessCommandRange_Singleton pcr;
  2175. DistributeWork( nWorkUnits, Worker_ProcessWorkUnitFn, NULL );
  2176. }
  2177. }
  2178. g_bSuppressPrintfOutput = true;
  2179. g_bSuppressPrintfOutput = false;
  2180. }
  2181. else // no VMPI
  2182. {
  2183. Worker_GetLocalCopyOfShaders();
  2184. Worker_GetLocalCopyOfBinaries();
  2185. _chdir( g_WorkerTempPath );
  2186. {
  2187. char chCommands[50], chStaticCombos[50];
  2188. sprintf( chCommands, "%s", PrettyPrintNumber( g_numCompileCommands ) );
  2189. sprintf( chStaticCombos, "%s", PrettyPrintNumber( g_numStaticCombos ) );
  2190. Msg( "\rCompiling %s commands in %s static combos.\n", chCommands, chStaticCombos );
  2191. }
  2192. CompileShaders_NoVMPI();
  2193. }
  2194. Msg( "\r \r" );
  2195. if ( g_bMPIMaster || !bShouldUseVMPI )
  2196. {
  2197. char str[ 4096 ];
  2198. // Write everything that succeeded
  2199. int nStrings = g_ShaderByteCode.GetNumStrings();
  2200. for( int i = 0; i < nStrings; i++ )
  2201. {
  2202. WriteShaderFiles( g_ShaderByteCode.String(i) );
  2203. }
  2204. // Write all the errors
  2205. //////////////////////////////////////////////////////////////////////////
  2206. //
  2207. // Now deliver all our accumulated spew to the output
  2208. //
  2209. //////////////////////////////////////////////////////////////////////////
  2210. bool bValveVerboseComboErrors = ( getenv( "VALVE_VERBOSE_COMBO_ERRORS" ) &&
  2211. atoi( getenv( "VALVE_VERBOSE_COMBO_ERRORS" ) ) ) ? true : false;
  2212. // Compiler spew
  2213. for ( int k = 0, kEnd = g_Master_CompilerMsgInfo.GetNumStrings(); k < kEnd; ++ k )
  2214. {
  2215. char const * const szMsg = g_Master_CompilerMsgInfo.String( k );
  2216. CompilerMsgInfo const &cmi = g_Master_CompilerMsgInfo[ int_as_symid( k ) ];
  2217. char const * const szFirstCmd = cmi.GetFirstCommand();
  2218. char const * const szFirstMachineName = cmi.GetFirstMachineName();
  2219. int const numReported = cmi.GetNumTimesReported();
  2220. uint64 iFirstCommand = _strtoui64( szFirstCmd, NULL, 10 );
  2221. CfgProcessor::ComboHandle hCombo = NULL;
  2222. CfgProcessor::CfgEntryInfo const *pComboEntryInfo = NULL;
  2223. if ( CfgProcessor::Combo_GetNext( iFirstCommand, hCombo, g_numCompileCommands ) )
  2224. {
  2225. Combo_FormatCommand( hCombo, str );
  2226. pComboEntryInfo = Combo_GetEntryInfo( hCombo );
  2227. Combo_Free( hCombo );
  2228. }
  2229. else
  2230. {
  2231. sprintf( str, "cmd # %s", szFirstCmd );
  2232. }
  2233. Msg( "\n%s\n", szMsg );
  2234. Msg( " Reported %d time(s), first machine \"%s\", example command:\n", numReported, szFirstMachineName );
  2235. if ( bValveVerboseComboErrors )
  2236. {
  2237. Msg( " Verbose Description:\n" );
  2238. if ( pComboEntryInfo )
  2239. {
  2240. Msg( " Src File: %s\n", pComboEntryInfo->m_szShaderFileName );
  2241. Msg( " Tgt File: %s\n", pComboEntryInfo->m_szName );
  2242. }
  2243. // Between /DSHADERCOMBO= and /Dmain
  2244. char const *pBegin;
  2245. char const *pEnd;
  2246. if ( g_bIsPS3 )
  2247. {
  2248. pBegin = strstr( str, "-DSHADERCOMBO=" );
  2249. pEnd = strstr( str, "-Dmain" );
  2250. }
  2251. else
  2252. {
  2253. pBegin = strstr( str, "/DSHADERCOMBO=" );
  2254. pEnd = strstr( str, "/Dmain" );
  2255. }
  2256. if ( pBegin )
  2257. {
  2258. // Don't need to adjust the string for PS3 because it's the same length
  2259. pBegin += strlen( "/DSHADERCOMBO=" ) ;
  2260. char const *pSpace = strchr( pBegin, ' ' );
  2261. if ( pSpace )
  2262. Msg( " Combo # : %.*s\n", ( pSpace - pBegin ), pBegin );
  2263. }
  2264. if ( !pEnd )
  2265. pEnd = str + strlen( str );
  2266. while ( pBegin && *pBegin && !V_isspace( *pBegin ) )
  2267. ++ pBegin;
  2268. while ( pBegin && *pBegin && V_isspace( *pBegin ) )
  2269. ++ pBegin;
  2270. // Now parse all combo defines in [pBegin, pEnd]
  2271. while ( pBegin && *pBegin && ( pBegin < pEnd ) )
  2272. {
  2273. char const *pDefine;
  2274. if ( g_bIsPS3 )
  2275. {
  2276. pDefine = strstr( pBegin, "-D" );
  2277. }
  2278. else
  2279. {
  2280. pDefine = strstr( pBegin, "/D" );
  2281. }
  2282. if ( !pDefine || pDefine >= pEnd )
  2283. break;
  2284. char const *pEqSign = strchr( pDefine, '=' );
  2285. if ( !pEqSign || pEqSign >= pEnd )
  2286. break;
  2287. char const *pSpace = strchr( pEqSign, ' ' );
  2288. if ( !pSpace || pSpace >= pEnd )
  2289. pSpace = pEnd;
  2290. pBegin = pSpace;
  2291. Msg( " %.*s %.*s\n",
  2292. ( pSpace - pEqSign - 1 ), pEqSign + 1,
  2293. ( pEqSign - pDefine - 2 ), pDefine + 2 );
  2294. }
  2295. }
  2296. Msg( " %s\n", str );
  2297. }
  2298. // Failed shaders summary
  2299. for ( int k = 0, kEnd = g_Master_ShaderHadError.GetNumStrings(); k < kEnd; ++ k )
  2300. {
  2301. char const *szShaderName = g_Master_ShaderHadError.String( k );
  2302. if ( !g_Master_ShaderHadError[ int_as_symid( k ) ] )
  2303. continue;
  2304. Msg( "FAILED: %s\n", szShaderName );
  2305. }
  2306. //
  2307. // End
  2308. //
  2309. double end = Plat_FloatTime();
  2310. GetHourMinuteSecondsString( (int)( end - g_flStartTime ), str, sizeof( str ) );
  2311. DebugOut( "%s elapsed\n", str );
  2312. DebugOut( "Precise timing = %.5f\n", ( end - g_flStartTime ) );
  2313. if ( bShouldUseVMPI )
  2314. {
  2315. VMPI_FileSystem_Term();
  2316. DebugOut( "Before VMPI_Finalize\n" );
  2317. VMPI_Finalize();
  2318. }
  2319. if ( g_bIsPS3 && g_bGeneratePS3DebugInfo )
  2320. {
  2321. // On the master, expand the giant TOC/Pack files we've built into many tiny files needed by the shader debug process.
  2322. ExpandPS3DebugInfo();
  2323. }
  2324. }
  2325. return g_Master_ShaderHadError.GetNumStrings();
  2326. }
  2327. class CShaderCompileDLL : public IShaderCompileDLL
  2328. {
  2329. int main( int argc, char **argv );
  2330. };
  2331. int CShaderCompileDLL::main( int argc, char **argv )
  2332. {
  2333. return ShaderCompile_Main( argc, argv );
  2334. }
  2335. EXPOSE_SINGLE_INTERFACE( CShaderCompileDLL, IShaderCompileDLL, SHADER_COMPILE_INTERFACE_VERSION );
  2336. class CLaunchableDLL : public ILaunchableDLL
  2337. {
  2338. int main( int argc, char **argv )
  2339. {
  2340. return ShaderCompile_Main( argc, argv );
  2341. }
  2342. };
  2343. EXPOSE_SINGLE_INTERFACE( CLaunchableDLL, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION );