2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
// ShaderCompileWorker.cpp : Defines the entry point for the console application.
//
# include "Core.h"
# include "RequiredProgramMainCPPInclude.h"
# include "ShaderCore.h"
# include "ExceptionHandling.h"
# include "IShaderFormat.h"
# include "IShaderFormatModule.h"
# define DEBUG_USING_CONSOLE 0
2015-09-25 17:34:00 -04:00
// this is for the protocol, not the data, bump if FShaderCompilerInput or ProcessInputFromArchive changes (also search for the second one with the same name, todo: put into one header file)
const int32 ShaderCompileWorkerInputVersion = 4 ;
// this is for the protocol, not the data, bump if FShaderCompilerOutput or WriteToOutputArchive changes (also search for the second one with the same name, todo: put into one header file)
const int32 ShaderCompileWorkerOutputVersion = 2 ;
2014-03-14 14:13:41 -04:00
double LastCompileTime = 0.0 ;
2014-12-16 20:00:07 -05:00
static bool GShaderCompileUseXGE = false ;
2015-07-13 18:02:21 -04:00
static bool GFailedDueToShaderFormatVersion = false ;
2014-12-16 20:00:07 -05:00
static void WriteXGESuccessFile ( const TCHAR * WorkingDirectory )
{
// To signal compilation completion, create a zero length file in the working directory.
delete IFileManager : : Get ( ) . CreateFileWriter ( * FString : : Printf ( TEXT ( " %s/Success " ) , WorkingDirectory ) , FILEWRITE_EvenIfReadOnly ) ;
}
2015-07-13 18:02:21 -04:00
static const TArray < const IShaderFormat * > & GetShaderFormats ( )
2014-03-14 14:13:41 -04:00
{
static bool bInitialized = false ;
static TArray < const IShaderFormat * > Results ;
if ( ! bInitialized )
{
bInitialized = true ;
Results . Empty ( Results . Num ( ) ) ;
TArray < FName > Modules ;
FModuleManager : : Get ( ) . FindModules ( SHADERFORMAT_MODULE_WILDCARD , Modules ) ;
if ( ! Modules . Num ( ) )
{
UE_LOG ( LogShaders , Error , TEXT ( " No target shader formats found! " ) ) ;
}
for ( int32 Index = 0 ; Index < Modules . Num ( ) ; Index + + )
{
IShaderFormat * Format = FModuleManager : : LoadModuleChecked < IShaderFormatModule > ( Modules [ Index ] ) . GetShaderFormat ( ) ;
2014-08-01 02:40:54 -04:00
if ( Format ! = nullptr )
2014-03-14 14:13:41 -04:00
{
Results . Add ( Format ) ;
}
}
}
return Results ;
}
2015-07-13 18:02:21 -04:00
static const IShaderFormat * FindShaderFormat ( FName Name )
2014-03-14 14:13:41 -04:00
{
const TArray < const IShaderFormat * > & ShaderFormats = GetShaderFormats ( ) ;
for ( int32 Index = 0 ; Index < ShaderFormats . Num ( ) ; Index + + )
{
TArray < FName > Formats ;
ShaderFormats [ Index ] - > GetSupportedFormats ( Formats ) ;
for ( int32 FormatIndex = 0 ; FormatIndex < Formats . Num ( ) ; FormatIndex + + )
{
if ( Formats [ FormatIndex ] = = Name )
{
return ShaderFormats [ Index ] ;
}
}
}
2014-08-01 02:40:54 -04:00
return nullptr ;
2014-03-14 14:13:41 -04:00
}
/** Processes a compilation job. */
static void ProcessCompilationJob ( const FShaderCompilerInput & Input , FShaderCompilerOutput & Output , const FString & WorkingDirectory )
{
const IShaderFormat * Compiler = FindShaderFormat ( Input . ShaderFormat ) ;
if ( ! Compiler )
{
UE_LOG ( LogShaders , Fatal , TEXT ( " Can't compile shaders for format %s " ) , * Input . ShaderFormat . ToString ( ) ) ;
}
// Compile the shader directly through the platform dll (directly from the shader dir as the working directory)
Compiler - > CompileShader ( Input . ShaderFormat , Input , Output , WorkingDirectory ) ;
}
2015-07-13 18:02:21 -04:00
2014-03-14 14:13:41 -04:00
class FWorkLoop
{
public :
enum ECommunicationMode
{
ThroughFile ,
} ;
2015-07-13 18:02:21 -04:00
FWorkLoop ( const TCHAR * ParentProcessIdText , const TCHAR * InWorkingDirectory , const TCHAR * InInputFilename , const TCHAR * InOutputFilename , ECommunicationMode InCommunicationMode , TMap < FString , uint16 > & InFormatVersionMap )
2014-03-14 14:13:41 -04:00
: ParentProcessId ( FCString : : Atoi ( ParentProcessIdText ) )
, WorkingDirectory ( InWorkingDirectory )
, InputFilename ( InInputFilename )
, OutputFilename ( InOutputFilename )
, CommunicationMode ( InCommunicationMode )
, InputFilePath ( InCommunicationMode = = ThroughFile ? ( FString ( InWorkingDirectory ) + InInputFilename ) : InInputFilename )
, OutputFilePath ( InCommunicationMode = = ThroughFile ? ( FString ( InWorkingDirectory ) + InOutputFilename ) : InOutputFilename )
2015-07-13 18:02:21 -04:00
, FormatVersionMap ( InFormatVersionMap )
2014-03-14 14:13:41 -04:00
{
}
void Loop ( )
{
UE_LOG ( LogShaders , Log , TEXT ( " Entering job loop " ) ) ;
while ( true )
{
TArray < FJobResult > JobResults ;
// Read & Process Input
{
FArchive * InputFilePtr = OpenInputFile ( ) ;
if ( ! InputFilePtr )
{
break ;
}
UE_LOG ( LogShaders , Log , TEXT ( " Processing shader " ) ) ;
LastCompileTime = FPlatformTime : : Seconds ( ) ;
ProcessInputFromArchive ( InputFilePtr , JobResults ) ;
// Close the input file.
delete InputFilePtr ;
}
// Prepare for output
FArchive * OutputFilePtr = CreateOutputArchive ( ) ;
check ( OutputFilePtr ) ;
WriteToOutputArchive ( OutputFilePtr , JobResults ) ;
// Close the output file.
delete OutputFilePtr ;
// Change the output file name to requested one
IFileManager : : Get ( ) . Move ( * OutputFilePath , * TempFilePath ) ;
2014-12-16 20:00:07 -05:00
if ( GShaderCompileUseXGE )
{
// To signal compilation completion, create a zero length file in the working directory.
WriteXGESuccessFile ( * WorkingDirectory ) ;
// We only do one pass per process when using XGE.
break ;
}
2014-03-14 14:13:41 -04:00
}
UE_LOG ( LogShaders , Log , TEXT ( " Exiting job loop " ) ) ;
}
private :
struct FJobResult
{
FShaderCompilerOutput CompilerOutput ;
} ;
const int32 ParentProcessId ;
const FString WorkingDirectory ;
const FString InputFilename ;
const FString OutputFilename ;
const ECommunicationMode CommunicationMode ;
const FString InputFilePath ;
const FString OutputFilePath ;
2015-07-13 18:02:21 -04:00
TMap < FString , uint16 > FormatVersionMap ;
2014-03-14 14:13:41 -04:00
FString TempFilePath ;
/** Opens an input file, trying multiple times if necessary. */
FArchive * OpenInputFile ( )
{
2014-08-01 02:40:54 -04:00
FArchive * InputFile = nullptr ;
2014-03-14 14:13:41 -04:00
bool bFirstOpenTry = true ;
while ( ! InputFile & & ! GIsRequestingExit )
{
// Try to open the input file that we are going to process
if ( CommunicationMode = = ThroughFile )
{
InputFile = IFileManager : : Get ( ) . CreateFileReader ( * InputFilePath , FILEREAD_Silent ) ;
}
if ( ! InputFile & & ! bFirstOpenTry )
{
CheckExitConditions ( ) ;
// Give up CPU time while we are waiting
FPlatformProcess : : Sleep ( 0.01f ) ;
}
bFirstOpenTry = false ;
}
return InputFile ;
}
2015-07-13 18:02:21 -04:00
void VerifyFormatVersions ( TMap < FString , uint16 > & ReceivedFormatVersionMap )
{
for ( auto Pair : ReceivedFormatVersionMap )
{
auto * Found = FormatVersionMap . Find ( Pair . Key ) ;
if ( Found )
{
GFailedDueToShaderFormatVersion = true ;
2015-08-19 17:26:48 -04:00
if ( Pair . Value ! = * Found )
{
FCString : : Snprintf ( GErrorExceptionDescription , sizeof ( GErrorExceptionDescription ) , TEXT ( " Mismatched shader version for format %s; did you forget to build ShaderCompilerWorker? " ) , * Pair . Key , * Found , Pair . Value ) ;
2015-07-13 18:02:21 -04:00
2015-08-19 17:26:48 -04:00
checkf ( Pair . Value = = * Found , TEXT ( " Exiting due to mismatched shader version for format %s, version %d from ShaderCompilerWorker, received %d! Did you forget to build ShaderCompilerWorker? " ) , * Pair . Key , * Found , Pair . Value ) ;
}
2015-07-13 18:02:21 -04:00
}
}
}
2014-03-14 14:13:41 -04:00
void ProcessInputFromArchive ( FArchive * InputFilePtr , TArray < FJobResult > & OutJobResults )
{
int32 NumBatches = 0 ;
FArchive & InputFile = * InputFilePtr ;
int32 InputVersion ;
InputFile < < InputVersion ;
2015-07-13 18:02:21 -04:00
checkf ( ShaderCompileWorkerInputVersion = = InputVersion , TEXT ( " Exiting due to ShaderCompilerWorker expecting input version %d, got %d instead! Did you forget to build ShaderCompilerWorker? " ) , ShaderCompileWorkerInputVersion , InputVersion ) ;
TMap < FString , uint16 > ReceivedFormatVersionMap ;
InputFile < < ReceivedFormatVersionMap ;
VerifyFormatVersions ( ReceivedFormatVersionMap ) ;
2014-03-14 14:13:41 -04:00
InputFile < < NumBatches ;
// Flush cache, to make sure we load the latest version of the input file.
// (Otherwise quick changes to a shader file can result in the wrong output.)
FlushShaderFileCache ( ) ;
for ( int32 BatchIndex = 0 ; BatchIndex < NumBatches ; BatchIndex + + )
{
// Deserialize the job's inputs.
FShaderCompilerInput CompilerInput ;
InputFile < < CompilerInput ;
if ( IsValidRef ( CompilerInput . SharedEnvironment ) )
{
// Merge the shared environment into the per-shader environment before calling into the compile function
CompilerInput . Environment . Merge ( * CompilerInput . SharedEnvironment ) ;
}
// Process the job.
FShaderCompilerOutput CompilerOutput ;
ProcessCompilationJob ( CompilerInput , CompilerOutput , WorkingDirectory ) ;
// Serialize the job's output.
FJobResult & JobResult = * new ( OutJobResults ) FJobResult ;
JobResult . CompilerOutput = CompilerOutput ;
}
}
FArchive * CreateOutputArchive ( )
{
2014-08-01 02:40:54 -04:00
FArchive * OutputFilePtr = nullptr ;
2014-03-14 14:13:41 -04:00
if ( CommunicationMode = = ThroughFile )
{
2014-04-23 18:06:09 -04:00
const double StartTime = FPlatformTime : : Seconds ( ) ;
bool bResult = false ;
2014-12-16 20:00:07 -05:00
// It seems XGE does not support deleting files.
// Don't delete the input file if we are running under Incredibuild.
// Instead, we signal completion by creating a zero byte "Success" file after the output file has been fully written.
if ( ! GShaderCompileUseXGE )
2014-03-14 14:13:41 -04:00
{
2014-12-16 20:00:07 -05:00
do
{
// Remove the input file so that it won't get processed more than once
bResult = IFileManager : : Get ( ) . Delete ( * InputFilePath ) ;
}
while ( ! bResult & & ( FPlatformTime : : Seconds ( ) - StartTime < 2 ) ) ;
2014-04-23 18:06:09 -04:00
2014-12-16 20:00:07 -05:00
if ( ! bResult )
{
UE_LOG ( LogShaders , Fatal , TEXT ( " Couldn't delete input file %s, is it readonly? " ) , * InputFilePath ) ;
}
2014-03-14 14:13:41 -04:00
}
// To make sure that the process waiting for results won't read unfinished output file,
// we use a temp file name during compilation.
do
{
FGuid Guid ;
FPlatformMisc : : CreateGuid ( Guid ) ;
TempFilePath = WorkingDirectory + Guid . ToString ( ) ;
} while ( IFileManager : : Get ( ) . FileSize ( * TempFilePath ) ! = INDEX_NONE ) ;
2014-04-23 18:06:09 -04:00
const double StartTime2 = FPlatformTime : : Seconds ( ) ;
do
{
// Create the output file.
2015-05-05 16:54:07 -04:00
OutputFilePtr = IFileManager : : Get ( ) . CreateFileWriter ( * TempFilePath , FILEWRITE_EvenIfReadOnly ) ;
2014-04-23 18:06:09 -04:00
}
while ( ! OutputFilePtr & & ( FPlatformTime : : Seconds ( ) - StartTime2 < 2 ) ) ;
if ( ! OutputFilePtr )
{
2015-05-05 16:54:07 -04:00
UE_LOG ( LogShaders , Fatal , TEXT ( " Couldn't save output file %s " ) , * TempFilePath ) ;
2014-04-23 18:06:09 -04:00
}
2014-03-14 14:13:41 -04:00
}
return OutputFilePtr ;
}
void WriteToOutputArchive ( FArchive * OutputFilePtr , TArray < FJobResult > & JobResults )
{
FArchive & OutputFile = * OutputFilePtr ;
int32 OutputVersion = ShaderCompileWorkerOutputVersion ;
OutputFile < < OutputVersion ;
int32 ErrorCode = 0 ;
OutputFile < < ErrorCode ;
int32 ErrorStringLength = 0 ;
OutputFile < < ErrorStringLength ;
OutputFile < < ErrorStringLength ;
int32 NumBatches = JobResults . Num ( ) ;
OutputFile < < NumBatches ;
for ( int32 ResultIndex = 0 ; ResultIndex < JobResults . Num ( ) ; ResultIndex + + )
{
FJobResult & JobResult = JobResults [ ResultIndex ] ;
OutputFile < < JobResult . CompilerOutput ;
}
}
/** Called in the idle loop, checks for conditions under which the helper should exit */
void CheckExitConditions ( )
{
2015-07-13 16:53:23 -04:00
if ( ! InputFilename . Contains ( TEXT ( " Only " ) ) )
2014-03-14 14:13:41 -04:00
{
2015-07-13 16:53:23 -04:00
UE_LOG ( LogShaders , Log , TEXT ( " InputFilename did not contain 'Only', exiting after one job. " ) ) ;
2014-03-14 14:13:41 -04:00
FPlatformMisc : : RequestExit ( false ) ;
}
2014-07-25 00:07:42 -04:00
# if PLATFORM_MAC || PLATFORM_LINUX
2014-03-14 14:13:41 -04:00
if ( ! FPlatformMisc : : IsDebuggerPresent ( ) & & ParentProcessId > 0 )
{
// If the parent process is no longer running, exit
if ( ! FPlatformProcess : : IsApplicationRunning ( ParentProcessId ) )
{
2015-04-22 15:04:48 -04:00
FString FilePath = FString ( WorkingDirectory ) + InputFilename ;
checkf ( IFileManager : : Get ( ) . FileSize ( * FilePath ) = = INDEX_NONE , TEXT ( " Exiting due to the parent process no longer running and the input file is present! " ) ) ;
2014-03-14 14:13:41 -04:00
UE_LOG ( LogShaders , Log , TEXT ( " Parent process no longer running, exiting " ) ) ;
FPlatformMisc : : RequestExit ( false ) ;
}
}
const double CurrentTime = FPlatformTime : : Seconds ( ) ;
// If we have been idle for 20 seconds then exit
if ( CurrentTime - LastCompileTime > 20.0 )
{
UE_LOG ( LogShaders , Log , TEXT ( " No jobs found for 20 seconds, exiting " ) ) ;
FPlatformMisc : : RequestExit ( false ) ;
}
# else
// Don't do these if the debugger is present
//@todo - don't do these if Unreal is being debugged either
if ( ! IsDebuggerPresent ( ) )
{
if ( ParentProcessId > 0 )
{
2015-04-22 15:04:48 -04:00
FString FilePath = FString ( WorkingDirectory ) + InputFilename ;
2014-03-14 14:13:41 -04:00
bool bParentStillRunning = true ;
HANDLE ParentProcessHandle = OpenProcess ( SYNCHRONIZE , false , ParentProcessId ) ;
// If we couldn't open the process then it is no longer running, exit
2014-08-01 02:40:54 -04:00
if ( ParentProcessHandle = = nullptr )
2014-03-14 14:13:41 -04:00
{
2015-07-13 16:53:23 -04:00
checkf ( IFileManager : : Get ( ) . FileSize ( * FilePath ) = = INDEX_NONE , TEXT ( " Exiting due to OpenProcess(ParentProcessId) failing and the input file is present! " ) ) ;
2014-03-14 14:13:41 -04:00
UE_LOG ( LogShaders , Log , TEXT ( " Couldn't OpenProcess, Parent process no longer running, exiting " ) ) ;
FPlatformMisc : : RequestExit ( false ) ;
}
else
{
// If we did open the process, that doesn't mean it is still running
// The process object stays alive as long as there are handles to it
// We need to check if the process has signaled, which indicates that it has exited
uint32 WaitResult = WaitForSingleObject ( ParentProcessHandle , 0 ) ;
if ( WaitResult ! = WAIT_TIMEOUT )
{
2015-07-13 16:53:23 -04:00
checkf ( IFileManager : : Get ( ) . FileSize ( * FilePath ) = = INDEX_NONE , TEXT ( " Exiting due to WaitForSingleObject(ParentProcessHandle) signaling and the input file is present! " ) ) ;
2014-03-14 14:13:41 -04:00
UE_LOG ( LogShaders , Log , TEXT ( " WaitForSingleObject signaled, Parent process no longer running, exiting " ) ) ;
FPlatformMisc : : RequestExit ( false ) ;
}
CloseHandle ( ParentProcessHandle ) ;
}
}
const double CurrentTime = FPlatformTime : : Seconds ( ) ;
// If we have been idle for 20 seconds then exit
if ( CurrentTime - LastCompileTime > 20.0 )
{
UE_LOG ( LogShaders , Log , TEXT ( " No jobs found for 20 seconds, exiting " ) ) ;
FPlatformMisc : : RequestExit ( false ) ;
}
}
# endif
}
} ;
/**
* Main entrypoint , guarded by a try . . . except .
* This expects 4 parameters :
* The image path and name
* The working directory path , which has to be unique to the instigating process and thread .
* The parent process Id
* The thread Id corresponding to this worker
*/
2014-04-23 20:10:59 -04:00
int32 GuardedMain ( int32 argc , TCHAR * argv [ ] )
2014-03-14 14:13:41 -04:00
{
GEngineLoop . PreInit ( argc , argv , TEXT ( " -NOPACKAGECACHE -Multiprocess " ) ) ;
# if DEBUG_USING_CONSOLE
GLogConsole - > Show ( true ) ;
# endif
# if PLATFORM_WINDOWS
//@todo - would be nice to change application name or description to have the ThreadId in it for debugging purposes
2014-04-23 20:10:59 -04:00
SetConsoleTitle ( argv [ 3 ] ) ;
2014-03-14 14:13:41 -04:00
# endif
// We just enumerate the shader formats here for debugging.
const TArray < const class IShaderFormat * > & ShaderFormats = GetShaderFormats ( ) ;
check ( ShaderFormats . Num ( ) ) ;
2015-07-13 18:02:21 -04:00
TMap < FString , uint16 > FormatVersionMap ;
2014-03-14 14:13:41 -04:00
for ( int32 Index = 0 ; Index < ShaderFormats . Num ( ) ; Index + + )
{
TArray < FName > OutFormats ;
ShaderFormats [ Index ] - > GetSupportedFormats ( OutFormats ) ;
check ( OutFormats . Num ( ) ) ;
for ( int32 InnerIndex = 0 ; InnerIndex < OutFormats . Num ( ) ; InnerIndex + + )
{
UE_LOG ( LogShaders , Display , TEXT ( " Available Shader Format %s " ) , * OutFormats [ InnerIndex ] . ToString ( ) ) ;
2015-07-13 18:02:21 -04:00
uint16 Version = ShaderFormats [ Index ] - > GetVersion ( OutFormats [ InnerIndex ] ) ;
FormatVersionMap . Add ( OutFormats [ InnerIndex ] . ToString ( ) , Version ) ;
2014-03-14 14:13:41 -04:00
}
}
LastCompileTime = FPlatformTime : : Seconds ( ) ;
2015-07-13 16:53:23 -04:00
FWorkLoop : : ECommunicationMode Mode = FWorkLoop : : ThroughFile ;
2015-07-13 18:02:21 -04:00
FWorkLoop WorkLoop ( argv [ 2 ] , argv [ 1 ] , argv [ 4 ] , argv [ 5 ] , Mode , FormatVersionMap ) ;
2014-03-14 14:13:41 -04:00
WorkLoop . Loop ( ) ;
return 0 ;
}
2014-04-23 20:10:59 -04:00
int32 GuardedMainWrapper ( int32 ArgC , TCHAR * ArgV [ ] , const TCHAR * CrashOutputFile )
2014-03-14 14:13:41 -04:00
{
2014-12-16 20:00:07 -05:00
// We need to know whether we are using XGE now, in case an exception
// is thrown before we parse the command line inside GuardedMain.
2015-04-22 10:29:49 -04:00
GShaderCompileUseXGE = ( ArgC > 6 ) & & FCString : : Strcmp ( ArgV [ 6 ] , TEXT ( " -xge " ) ) = = 0 ;
2014-12-16 20:00:07 -05:00
2014-03-14 14:13:41 -04:00
int32 ReturnCode = 0 ;
2014-07-25 00:07:42 -04:00
# if PLATFORM_WINDOWS
2014-03-14 14:13:41 -04:00
if ( FPlatformMisc : : IsDebuggerPresent ( ) )
# endif
{
ReturnCode = GuardedMain ( ArgC , ArgV ) ;
}
2014-07-25 00:07:42 -04:00
# if PLATFORM_WINDOWS
2014-03-14 14:13:41 -04:00
else
{
2015-07-13 18:02:21 -04:00
// Don't want 32 dialogs popping up when SCW fails
GUseCrashReportClient = false ;
2014-03-14 14:13:41 -04:00
__try
{
GIsGuarded = 1 ;
ReturnCode = GuardedMain ( ArgC , ArgV ) ;
GIsGuarded = 0 ;
}
__except ( ReportCrash ( GetExceptionInformation ( ) ) )
{
FArchive & OutputFile = * IFileManager : : Get ( ) . CreateFileWriter ( CrashOutputFile , FILEWRITE_NoFail ) ;
int32 OutputVersion = ShaderCompileWorkerOutputVersion ;
OutputFile < < OutputVersion ;
2015-07-13 18:02:21 -04:00
int32 ErrorCode = GFailedDueToShaderFormatVersion ? 2 : 1 ;
2014-03-14 14:13:41 -04:00
OutputFile < < ErrorCode ;
// Note: Can't use FStrings here as SEH can't be used with destructors
int32 CallstackLength = FCString : : Strlen ( GErrorHist ) ;
OutputFile < < CallstackLength ;
int32 ExceptionInfoLength = FCString : : Strlen ( GErrorExceptionDescription ) ;
OutputFile < < ExceptionInfoLength ;
OutputFile . Serialize ( GErrorHist , CallstackLength * sizeof ( TCHAR ) ) ;
OutputFile . Serialize ( GErrorExceptionDescription , ExceptionInfoLength * sizeof ( TCHAR ) ) ;
int32 NumBatches = 0 ;
OutputFile < < NumBatches ;
// Close the output file.
delete & OutputFile ;
2014-12-16 20:00:07 -05:00
if ( GShaderCompileUseXGE )
{
ReturnCode = 1 ;
WriteXGESuccessFile ( ArgV [ 1 ] ) ;
}
2014-03-14 14:13:41 -04:00
}
}
# endif
FEngineLoop : : AppPreExit ( ) ;
FEngineLoop : : AppExit ( ) ;
return ReturnCode ;
}
IMPLEMENT_APPLICATION ( ShaderCompileWorker , " ShaderCompileWorker " )
/**
* Application entry point
*
* @ param ArgC Command - line argument count
* @ param ArgV Argument strings
*/
2014-04-23 20:10:59 -04:00
INT32_MAIN_INT32_ARGC_TCHAR_ARGV ( )
2014-03-14 14:13:41 -04:00
{
2014-12-17 01:20:20 -05:00
// FPlatformProcess::OpenProcess only implemented for windows atm
# if PLATFORM_WINDOWS
2014-12-16 20:00:07 -05:00
if ( ArgC = = 4 & & FCString : : Strcmp ( ArgV [ 1 ] , TEXT ( " -xgemonitor " ) ) = = 0 )
{
// Open handles to the two processes
FProcHandle EngineProc = FPlatformProcess : : OpenProcess ( FCString : : Atoi ( ArgV [ 2 ] ) ) ;
FProcHandle BuildProc = FPlatformProcess : : OpenProcess ( FCString : : Atoi ( ArgV [ 3 ] ) ) ;
if ( EngineProc . IsValid ( ) & & BuildProc . IsValid ( ) )
{
// Whilst the build is still in progress
while ( FPlatformProcess : : IsProcRunning ( BuildProc ) )
{
// Check that the engine is still alive.
if ( ! FPlatformProcess : : IsProcRunning ( EngineProc ) )
{
// The engine has shutdown before the build was stopped.
// Kill off the build process
FPlatformProcess : : TerminateProc ( BuildProc ) ;
break ;
}
FPlatformProcess : : Sleep ( 0.01f ) ;
}
}
return 0 ;
}
2014-12-17 01:20:20 -05:00
# endif
2014-03-14 14:13:41 -04:00
if ( ArgC < 6 )
{
2014-07-25 00:07:42 -04:00
printf ( " ShaderCompileWorker is called by UE4, it requires specific command like arguments. \n " ) ;
return - 1 ;
2014-03-14 14:13:41 -04:00
}
// Game exe can pass any number of parameters through with appGetSubprocessCommandline
// so just make sure we have at least the minimum number of parameters.
check ( ArgC > = 6 ) ;
TCHAR OutputFilePath [ PLATFORM_MAX_FILEPATH_LENGTH ] ;
2014-04-23 20:10:59 -04:00
FCString : : Strncpy ( OutputFilePath , ArgV [ 1 ] , PLATFORM_MAX_FILEPATH_LENGTH ) ;
FCString : : Strncat ( OutputFilePath , ArgV [ 5 ] , PLATFORM_MAX_FILEPATH_LENGTH ) ;
2014-03-14 14:13:41 -04:00
const int32 ReturnCode = GuardedMainWrapper ( ArgC , ArgV , OutputFilePath ) ;
return ReturnCode ;
}