Team Fortress 2 Source Code as on 22/4/2020
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.

2699 lines
74 KiB

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