//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// // vmpi_bareshell.cpp : Defines the entry point for the console application. // #include #include #include #include "vmpi.h" #include "filesystem.h" #include "vmpi_filesystem.h" #include "vmpi_distribute_work.h" #include "vmpi_tools_shared.h" #include "cmdlib.h" #include "tier1/utlvector.h" #include "tier1/utlbuffer.h" #include "tier1/utllinkedlist.h" #include "tier1/utlstringmap.h" #include "tier0/icommandline.h" #include "tier1/strtools.h" #include "threads.h" #include "tier0/dbg.h" #include "interface.h" #include "ilaunchabledll.h" #include #include "io.h" #include #include #define STARTWORK_PACKETID 5 #define WORKUNIT_PACKETID 6 #define ERRMSG_PACKETID 7 #define TEXTUREHADERROR_PACKETID 8 #define MACHINE_NAME 9 #define TEXTURES_PER_WORKUNIT 1 #ifdef _DEBUG //#define DEBUGFP #endif const char *g_pGameDir = NULL; const char *g_pTextureOutputDir = NULL; char g_WorkerTempPath[MAX_PATH]; char g_ExeDir[MAX_PATH]; CUtlVector g_CompileCommands; int g_nTexturesPerWorkUnit = 0; #ifdef DEBUGFP FILE *g_WorkerDebugFp = NULL; #endif bool g_bGotStartWorkPacket = false; double g_flStartTime; bool g_bVerbose = false; struct TextureInfo_t { }; void Texture_ParseTextureInfoFromCompileCommands( int nCompileCommandID, TextureInfo_t &shaderInfo ); void Worker_GetSourceFiles( int iWorkUnit ); void Worker_GetLocalCopyOfTextureSource( const char *pVtfName ); // g_ByteCode["shadername"][shadercombo][bytecodeoffset] //typedef CUtlVector VectorOfBuffers_t; //CUtlStringMap g_TextureToTextureInfo; typedef CUtlVector CharVector_t; struct SourceTargetPair_t { char *pSrcName; char *pTargetName; }; CUtlVector g_SourceTargetPairs; CUtlStringMap g_Master_TextureHadError; CDispatchReg g_DistributeWorkReg( WORKUNIT_PACKETID, DistributeWorkDispatch ); unsigned long VMPI_Stats_GetJobWorkerID( void ) { return 0; } bool StartWorkDispatch( MessageBuffer *pBuf, int iSource, int iPacketID ) { g_bGotStartWorkPacket = true; return true; } CDispatchReg g_StartWorkReg( STARTWORK_PACKETID, StartWorkDispatch ); bool ErrMsgDispatch( MessageBuffer *pBuf, int iSource, int iPacketID ) { static CUtlStringMap errorMessages; if( !errorMessages.Defined( pBuf->data + 1 ) ) { errorMessages[pBuf->data + 1] = true; fprintf( stderr, "ERROR: %s\n", pBuf->data + 1 ); } return true; } CDispatchReg g_ErrMsgReg( ERRMSG_PACKETID, ErrMsgDispatch ); bool TextureHadErrorDispatch( MessageBuffer *pBuf, int iSource, int iPacketID ) { g_Master_TextureHadError[pBuf->data+1] = true; return true; } CDispatchReg g_TextureHadErrorReg( TEXTUREHADERROR_PACKETID, TextureHadErrorDispatch ); void DebugOut( const char *pMsg, ... ) { char msg[2048]; va_list marker; va_start( marker, pMsg ); _vsnprintf( msg, sizeof( msg ), pMsg, marker ); va_end( marker ); if (g_bVerbose) { Msg( "%s", msg ); } #ifdef DEBUGFP fprintf( g_WorkerDebugFp, "%s", msg ); fflush( g_WorkerDebugFp ); #endif } // Worker should implement this so it will quit nicely when the master disconnects. void MyDisconnectHandler( int procID, const char *pReason ) { // If we're a worker, then it's a fatal error if we lose the connection to the master. if ( !g_bMPIMaster ) { Msg( "Master disconnected.\n "); DebugOut( "Master disconnected.\n" ); TerminateProcess( GetCurrentProcess(), 1 ); } } // pBuf is ready to read the results written to the buffer in ProcessWorkUnitFn. // work is done. .master gets it back this way. // compiled code in pBuf void Master_ReceiveWorkUnitFn( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker ) { DebugOut( "Master_ReceiveWorkUnitFn\n" ); int textureStart = iWorkUnit * g_nTexturesPerWorkUnit; int textureEnd = textureStart + g_nTexturesPerWorkUnit; textureEnd = min( g_CompileCommands.Count(), textureEnd ); int i; for( i = textureStart; i < textureEnd; i++ ) { int len; pBuf->read( &len, sizeof( len ) ); if( len == 0 ) { continue; } CUtlBuffer fileData; fileData.EnsureCapacity( len ); pBuf->read( fileData.Base(), len ); Warning( "%s\n", g_CompileCommands[i] ); FILE *fp; fp = fopen( g_CompileCommands[i], "wb" ); if ( !fp ) Error( "Can't open %s for writing.\n", g_CompileCommands[i] ); fwrite( fileData.Base(), 1, len, fp ); fclose( fp ); } } // same as "system", but doesn't pop up a window void MySystem( char *pCommand ) { FILE *batFp = fopen( "temp.bat", "w" ); fprintf( batFp, "%s\n", pCommand ); fclose( batFp ); STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); // Start the child process. if( !CreateProcess( NULL, // No module name (use command line). "temp.bat", // Command line. NULL, // Process handle not inheritable. NULL, // Thread handle not inheritable. FALSE, // Set handle inheritance to FALSE. IDLE_PRIORITY_CLASS | CREATE_NO_WINDOW, // No creation flags. NULL, // Use parent's environment block. g_WorkerTempPath, // Use parent's starting directory. &si, // Pointer to STARTUPINFO structure. &pi ) // Pointer to PROCESS_INFORMATION structure. ) { Error( "CreateProcess failed." ); Assert( 0 ); } // Wait until child process exits. WaitForSingleObject( pi.hProcess, INFINITE ); // Close process and thread handles. CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); } void VTFNameToTGAName( const char *pSrcName, char *pDstName ) { pDstName[0] = '\0'; const char *pMaterials = Q_stristr( pSrcName, "materials" ); Assert( pMaterials ); if( pMaterials ) { Q_strncpy( pDstName, pSrcName, pMaterials - pSrcName + 1 ); } Q_strcat( pDstName, "materialsrc", MAX_PATH ); Q_strcat( pDstName, pMaterials + strlen( "materials" ), MAX_PATH ); Q_StripExtension( pDstName, pDstName, strlen( pDstName ) ); Q_strcat( pDstName, ".tga", MAX_PATH ); } // You must append data to pBuf with the work unit results. void Worker_ProcessWorkUnitFn( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf ) { DebugOut( "Worker_ProcessWorkUnitFn textures/workunit=%d\n", g_nTexturesPerWorkUnit ); Worker_GetSourceFiles( iWorkUnit ); int textureStart = iWorkUnit * g_nTexturesPerWorkUnit; int textureEnd = textureStart + g_nTexturesPerWorkUnit; textureEnd = min( g_CompileCommands.Count(), textureEnd ); int i; for( i = textureStart; i < textureEnd; i++ ) { DebugOut( "texture to compile: \"%s\"\n", g_CompileCommands[i] ); char cmdline[1024]; char tganame[1024]; VTFNameToTGAName( g_CompileCommands[i], tganame ); sprintf( cmdline, "vtex -allowdebug -vproject \"%s%s\" -mkdir -nopause \"%s%s\"", g_WorkerTempPath, g_pGameDir + 3, g_WorkerTempPath, tganame + 3 ); // hack hack DebugOut( cmdline ); DebugOut( "\n" ); // MySystem( cmdline ); system( cmdline ); char localVTFName[1024]; sprintf( localVTFName, "%s%s", g_WorkerTempPath, g_CompileCommands[i] + 3 ); DebugOut( "local: \"%s\"\n", localVTFName ); FILE *fp = fopen( localVTFName, "rb" ); if( fp ) { // Send the compiled shader to the master fseek( fp, 0, SEEK_END ); int len = ftell( fp ); fseek( fp, 0, SEEK_SET ); CUtlBuffer buf; buf.EnsureCapacity( len ); int nBytesRead = fread( buf.Base(), 1, len, fp ); fclose( fp ); buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead ); pBuf->write( &len, sizeof( len ) ); pBuf->write( buf.Base(), len ); } else { // static CUtlStringMap m_FileAlreadyFailed; // // if( !m_FileAlreadyFailed.Defined( g_CompileCommands[i] ) ) // { // m_FileAlreadyFailed[g_CompileCommands[i]] = true; // CUtlVector fileNameBuf; // fileNameBuf.AddToTail( ( char )TEXTUREHADERROR_PACKETID ); // int len = strlen( g_CompileCommands[i] ); // fileNameBuf.AddMultipleToTail( len + 1, g_CompileCommands[i] ); // VMPI_SendData( fileNameBuf.Base(), fileNameBuf.Count(), VMPI_MASTER_ID ); // } // Write zero to signify that we didn't do anything here. int len = 0; pBuf->write( &len, sizeof( len ) ); } VMPI_HandleSocketErrors(); } } void MakeDirHier( const char *pPath ) { char temp[1024]; Q_strncpy( temp, pPath, 1024 ); int i; for( i = 0; i < strlen( temp ); i++ ) { if( temp[i] == '/' || temp[i] == '\\' ) { temp[i] = '\0'; // DebugOut( "mkdir( %s )\n", temp ); mkdir( temp ); temp[i] = '\\'; } } // DebugOut( "mkdir( %s )\n", temp ); mkdir( temp ); } void Worker_ReadFilesToCopy( void ) { // Create virtual files for all of the stuff that we need to compile the shader // make sure and prefix the file name so that it doesn't find it locally. char filename[1024]; sprintf( filename, "%s\\filestocopy.txt", g_pGameDir ); DebugOut( "using \"%s\" as filestocopy\n", filename ); char buf[1024]; FileHandle_t fp = g_pFileSystem->Open( filename, "r" ); if( fp == FILESYSTEM_INVALID_HANDLE ) { fprintf( stderr, "Can't open uniquefilestocopy.txt!\n" ); exit( -1 ); } while( CmdLib_FGets( buf, sizeof( buf ), fp ) ) { // get rid of the newline if there is one. int len = strlen( buf ); if( buf[len-1] == 0xd || buf[len-1] == 0xa ) { buf[len-1] = '\0'; } // DebugOut( "buf: %s\n", buf ); char *pStar = Q_stristr( buf, "*" ); Assert( pStar ); if( !pStar ) { continue; } *pStar = '\0'; char *pVtfName = buf; char *pSrcName = pStar + 1; SourceTargetPair_t &pair = g_SourceTargetPairs[g_SourceTargetPairs.AddToTail()]; pair.pSrcName = strdup( pSrcName ); pair.pTargetName = strdup( pVtfName ); } g_pFileSystem->Close( fp ); } void Worker_GetSourceFiles( int iWorkUnit ) { DebugOut( "Worker_GetSourceFiles( %d )\n", iWorkUnit ); int textureStart = iWorkUnit * g_nTexturesPerWorkUnit; int textureEnd = textureStart + g_nTexturesPerWorkUnit; textureEnd = min( g_CompileCommands.Count(), textureEnd ); int i; for( i = textureStart; i < textureEnd; i++ ) { Worker_GetLocalCopyOfTextureSource( g_CompileCommands[i] ); } } void Worker_GetFileFromMaster( const char *pFileName ) { DebugOut( "Worker_GetFileFromMaster: \"%s\"\n", pFileName ); FileHandle_t fp2 = g_pFileSystem->Open( pFileName, "rb" ); bool bZeroLength = false; if( fp2 == FILESYSTEM_INVALID_HANDLE ) { bZeroLength = true; Warning( "zero length file: \"%s\"\n", pFileName ); // make a zero length file hack hack hack // continue; } CUtlVector fileBuf; int fileLen = 0; if( !bZeroLength ) { // printf( "getting local copy of file: \"%s\"\n", pFileName ); fileLen = g_pFileSystem->Size( fp2 ); fileBuf.SetCount( fileLen ); g_pFileSystem->Read( fileBuf.Base(), fileLen, fp2 ); g_pFileSystem->Close( fp2 ); } // create the dir that the file needs to go into. char path[1024]; char filename[1024]; sprintf( path, "%s%s", g_WorkerTempPath, pFileName + 3 ); // dear lord . .skip the u:\ BUG BUG BUG // printf( "creating \"%s\"\n", path ); Q_StripFilename( path ); MakeDirHier( path ); sprintf( filename, "%s%s", g_WorkerTempPath, pFileName + 3 ); // dear lord . .skip the u:\ BUG BUG BUG // printf( "creating \"%s\"\n", pFileName ); FILE *fp3 = fopen( filename, "wb" ); if( !fp3 ) { Error( "Couldn't open \"%s\"\n", filename ); } if( !bZeroLength ) { fwrite( fileBuf.Base(), 1, fileLen, fp3 ); } fclose( fp3 ); } void Worker_GetLocalCopyOfTextureSource( const char *pVtfName ) { int i; for( i = 0; i < g_SourceTargetPairs.Count(); i++ ) { // DebugOut( "comparing: \"%s\" \"%s\"\n", pVtfName, g_SourceTargetPairs[i].pTargetName ); if( Q_stricmp( pVtfName, g_SourceTargetPairs[i].pTargetName ) == 0 ) { // DebugOut( "MATCH!\n" ); Worker_GetFileFromMaster( g_SourceTargetPairs[i].pSrcName ); } } } void Worker_GetLocalCopyOfBinary( const char *pFilename ) { CUtlBuffer fileBuf; char tmpFilename[MAX_PATH]; sprintf( tmpFilename, "%s\\%s", g_ExeDir, pFilename ); printf( "trying to open: %s\n", tmpFilename ); FILE *fp = fopen( tmpFilename, "rb" ); if( !fp ) { Assert( 0 ); fprintf( stderr, "Can't open %s!\n", pFilename ); exit( -1 ); } fseek( fp, 0, SEEK_END ); int fileLen = ftell( fp ); fseek( fp, 0, SEEK_SET ); fileBuf.EnsureCapacity( fileLen ); int nBytesRead = fread( fileBuf.Base(), 1, fileLen, fp ); fclose( fp ); fileBuf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead ); char newFilename[MAX_PATH]; sprintf( newFilename, "%s%s", g_WorkerTempPath, pFilename ); DebugOut( "this is fucked \"%s\"\n", newFilename ); FILE *fp2 = fopen( newFilename, "wb" ); if( !fp2 ) { Assert( 0 ); fprintf( stderr, "Can't open %s!\n", newFilename ); exit( -1 ); } fwrite( fileBuf.Base(), 1, fileLen, fp2 ); fclose( fp2 ); } void Worker_GetLocalCopyOfBinaries( void ) { Worker_GetLocalCopyOfBinary( "vtex.exe" ); Worker_GetLocalCopyOfBinary( "vtex_dll.dll" ); Worker_GetLocalCopyOfBinary( "vstdlib.dll" ); Worker_GetLocalCopyOfBinary( "tier0.dll" ); } void Shared_ParseListOfCompileCommands( void ) { char buf[1024]; char fileListFileName[1024]; sprintf( fileListFileName, "%s\\texturelist.txt", g_pGameDir ); FileHandle_t fp = g_pFileSystem->Open( fileListFileName, "r" ); if( fp == FILESYSTEM_INVALID_HANDLE ) { DebugOut( "Can't open %s!\n", fileListFileName ); fprintf( stderr, "Can't open %s!\n", fileListFileName ); exit( -1 ); } while( CmdLib_FGets( buf, 1023, fp ) ) { char *pNewString = new char[ strlen( buf ) + 1 ]; strcpy( pNewString, buf ); pNewString[strlen( pNewString ) - 2] = '\0'; // This is some hacky shit right here. int newID = g_CompileCommands.AddToTail(); g_CompileCommands[newID] = pNewString; } g_pFileSystem->Close( fp ); // printf( "%d compiles\n", g_CompileCommands.Count() ); DebugOut( "%d compiles\n", g_CompileCommands.Count() ); } void SetupPaths( int argc, char **argv ) { GetTempPath( sizeof( g_WorkerTempPath ), g_WorkerTempPath ); strcat( g_WorkerTempPath, "texturecompiletemp\\" ); char tmp[MAX_PATH]; sprintf( tmp, "rd /s /q \"%s\"", g_WorkerTempPath ); system( tmp ); _mkdir( g_WorkerTempPath ); // printf( "g_WorkerTempPath: \"%s\"\n", g_WorkerTempPath ); CommandLine()->CreateCmdLine( argc, argv ); g_pGameDir = CommandLine()->ParmValue( "-gamedir", "" ); strcpy( g_ExeDir, argv[0] ); Q_StripFilename( g_ExeDir ); Q_FixSlashes( g_ExeDir ); // printf( "exedir: \"%s\"\n", g_ExeDir ); g_pTextureOutputDir = CommandLine()->ParmValue( "-textureoutputdir", "" ); // printf( "shaderoutputdir: \"%s\"\n", g_pShaderOutputDir ); g_bVerbose = CommandLine()->FindParm("-verbose") != 0; } void SetupDebugFile( void ) { #ifdef DEBUGFP const char *pComputerName = getenv( "COMPUTERNAME" ); char filename[MAX_PATH]; sprintf( filename, "\\\\fileserver\\user\\gary\\debug\\%s.txt", pComputerName ); g_WorkerDebugFp = fopen( filename, "w" ); Assert( g_WorkerDebugFp ); DebugOut( "opened debug file\n" ); #endif } void WriteTexture( const char *pTextureName ) { #if 0 CUtlVector &byteCodeArray = g_ByteCode[pShaderName]; const ShaderInfo_t &shaderInfo = g_ShaderToShaderInfo[pShaderName]; // printf( "%s : %d combos centroid mask: 0x%x numDynamicCombos: %d flags: 0x%x\n", // pShaderName, shaderInfo.m_nTotalShaderCombos, // shaderInfo.m_CentroidMask, shaderInfo.m_nDynamicCombos, shaderInfo.m_Flags ); CUtlBuffer header; CUtlBuffer body; header.PutInt( SHADER_VCS_VERSION_NUMBER ); // version header.PutInt( shaderInfo.m_nTotalShaderCombos ); header.PutInt( shaderInfo.m_nDynamicCombos ); header.PutUnsignedInt( shaderInfo.m_Flags ); header.PutUnsignedInt( shaderInfo.m_CentroidMask ); int headerSize = sizeof( int ) * 5; int directorySize = sizeof( int ) * 2 * shaderInfo.m_nTotalShaderCombos; int bodyOffset = headerSize + directorySize; int nCombo; for( nCombo = 0; nCombo < shaderInfo.m_nTotalShaderCombos; nCombo++ ) { CUtlBuffer &byteCode = byteCodeArray[nCombo]; if( byteCode.TellPut() == 0 ) { // This is a skipped combo. header.PutInt( -1 ); header.PutInt( 0 ); } else { header.PutInt( body.TellPut() + bodyOffset ); header.PutInt( byteCode.TellPut() ); body.Put( byteCode.Base(), byteCode.TellPut() ); } } char filename[MAX_PATH]; char filename2[MAX_PATH]; // strcpy( filename2, g_pShaderOutputDir ); strcpy( filename2, g_pShaderPath ); strcat( filename2, "\\shaders\\fxc" ); struct _stat buf; if( _stat( filename2, &buf ) == -1 ) { printf( "mkdir %s\n", filename2 ); // doh. . need to make the directory that the vcs file is going to go into. _mkdir( filename2 ); } strcat( filename2, "\\" ); strcpy( filename, pShaderName ); char *dot = strstr( filename, "." ); if( dot ) { *dot = '\0'; } strcat( filename, ".vcs" ); strcat( filename2, filename ); if( _stat( filename2, &buf ) != -1 ) { // The file exists, let's see if it's writable. if( !( buf.st_mode & _S_IWRITE ) ) { // It isn't writable. . we'd better change it's permissions (or check it out possibly). printf( "Warning: making %s writable!\n", filename2 ); _chmod( filename2, _S_IREAD | _S_IWRITE ); } } FILE *fp = fopen( filename2, "wb" ); if( !fp ) { printf( "Can't open %s\n", filename2 ); return; } printf( "writing %s\n", filename ); fwrite( header.Base(), 1, headerSize + directorySize, fp ); fwrite( body.Base(), 1, body.TellPut(), fp ); fclose( fp ); #endif } void TouchFile( const char *path ) { Warning( "TouchFile: %s\n", path ); char dir[MAX_PATH]; Q_strcpy( dir, path ); Q_StripFilename( dir ); MakeDirHier( dir ); FILE *fp = fopen( path, "wb" ); fclose( fp ); } int TextureCompile_Main( int argc, char* argv[] ) { InstallSpewFunction(); g_bSuppressPrintfOutput = false; g_flStartTime = Plat_FloatTime(); SetupDebugFile(); numthreads = 1; // holy shit batman! SetupPaths( argc, argv ); // Master, start accepting connections. // Worker, make a connection. DebugOut( "Before VMPI_Init\n" ); g_bSuppressPrintfOutput = true; VMPIRunMode mode = VMPI_RUN_NETWORKED; if ( !VMPI_Init( argc, argv, "dependency_info_texturecompile.txt", MyDisconnectHandler, mode ) ) { g_bSuppressPrintfOutput = false; DebugOut( "MPI_Init failed.\n" ); Error( "MPI_Init failed." ); } g_bSuppressPrintfOutput = false; DebugOut( "After VMPI_Init\n" ); int maxFileSystemMemoryUsageBytes = 50000000; CmdLib_InitFileSystem( ".", maxFileSystemMemoryUsageBytes ); DebugOut( "After VMPI_FileSystem_Init\n" ); Shared_ParseListOfCompileCommands(); DebugOut( "After Shared_ParseListOfCompileCommands\n" ); // 4-ish work units per machine g_nTexturesPerWorkUnit = TEXTURES_PER_WORKUNIT; int nWorkUnits = g_CompileCommands.Count() / g_nTexturesPerWorkUnit + 1; // printf( "nWorkUnits: %d\n", nWorkUnits ); // printf( "g_nShadersPerWorkUnit: %d\n", g_nShadersPerWorkUnit ); DebugOut( "Before conditional\n" ); if ( g_bMPIMaster ) { // Send all of the workers the complete list of work to do. DebugOut( "Before STARTWORK_PACKETID\n" ); char packetID = STARTWORK_PACKETID; VMPI_SendData( &packetID, sizeof( packetID ), VMPI_PERSISTENT ); // nWorkUnits is how many work units. . .1000 is good. // The work unit number impies which combo to do. DebugOut( "Before DistributeWork\n" ); DistributeWork( nWorkUnits, WORKUNIT_PACKETID, NULL, Master_ReceiveWorkUnitFn ); } else { // wait until we get a packet from the master to start doing stuff. MessageBuffer buf; DebugOut( "Before VMPI_DispatchUntil\n" ); while ( !g_bGotStartWorkPacket ) { VMPI_DispatchNextMessage(); } DebugOut( "after VMPI_DispatchUntil\n" ); Worker_ReadFilesToCopy(); // Worker_GetSourceFiles(); // DebugOut( "Before Worker_GetLocalCopyOfShaders\n" ); // Worker_GetLocalCopyOfTextureSource(); // DebugOut( "Before Worker_GetLocalCopyOfBinaries\n" ); DebugOut( "Before _chdir\n" ); _chdir( g_WorkerTempPath ); // DIE DIE KILL KILL AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! char path[MAX_PATH]; sprintf( path, "%s%s\\bin\\server.dll", g_WorkerTempPath, g_pGameDir + 3 ); // hack hack TouchFile( path ); sprintf( path, "%s%s\\bin\\client.dll", g_WorkerTempPath, g_pGameDir + 3 );// hack hack TouchFile( path ); Worker_GetLocalCopyOfBinaries(); // nWorkUnits is how many work units. . .1000 is good. // The work unit number impies which combo to do. DebugOut( "Before DistributeWork\n" ); DistributeWork( nWorkUnits, WORKUNIT_PACKETID, Worker_ProcessWorkUnitFn, NULL ); } DebugOut( "Before VMPI_Finalize\n" ); g_bSuppressPrintfOutput = true; VMPI_FileSystem_Term(); VMPI_Finalize(); g_bSuppressPrintfOutput = false; if( g_bMPIMaster ) { /* printf( "\n" ); int nStrings = g_ByteCode.GetNumStrings(); int i; for( i = 0; i < nStrings; i++ ) { if( g_Master_TextureHadError.Defined( g_ByteCode.String( i ) ) ) { printf( "FAILED: \"%s\"\n", g_ByteCode.String( i ) ); } else { // printf( "\"%s\" succeeded!\n", g_ByteCode.String( i ) ); WriteTexture( g_ByteCode.String( i ) ); } } double end = Plat_FloatTime(); char str[512]; GetHourMinuteSecondsString( (int)( end - g_flStartTime ), str, sizeof( str ) ); Msg( "%s elapsed\n", str ); */ } return 0; } class CTextureCompileDLL : public ILaunchableDLL { int main( int argc, char **argv ); }; int CTextureCompileDLL::main( int argc, char **argv ) { return TextureCompile_Main( argc, argv ); } EXPOSE_SINGLE_INTERFACE( CTextureCompileDLL, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION );