fastbuild进行ue4 shader连编
2019-11-26 10:19 kk20161206 阅读(2469) 评论(2) 编辑 收藏 举报参考https://github.com/fastbuild/fastbuild/issues/539中
wsj0000 上传的代码,
根据
wout276所说,将
FShaderCompileUtilities::DoWriteTasks in ShaderCompiler.cpp里的:
for (TPair<FString, FString>& Pair : ShaderSourceDirectoryMappings) { Pair.Value = FPaths::ConvertRelativePathToFull(Pair.Value); }
注释掉。
https://blog.kangkang.org/index.php/archives/409这篇文章说了编译机器上要安装一些东西,笔者因为是在别人基础上改的,不知道是否要安装,可能也是要安装的。这篇文章中其他的东西好像也没啥卵用,不按照它那个搞也没啥问题。
我用的是ue4.22版本,下面是代码文件:
shaderCompile.h文件,增加了FShaderCompileThreadRunnableBase类和文件相关函数,FShaderCompilingManager里增加FShaderCompileFASTBuildThreadRunnable,其他不变:
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. /*============================================================================= ShaderCompiler.h: Platform independent shader compilation definitions. =============================================================================*/ #pragma once #include "CoreMinimal.h" #include "Templates/RefCounting.h" #include "Templates/ScopedPointer.h" #include "HAL/PlatformProcess.h" #include "ShaderCore.h" #include "Shader.h" #include "HAL/RunnableThread.h" #include "HAL/Runnable.h" #include "Templates/Atomic.h" #include "Templates/UniquePtr.h" class FShaderCompileJob; class FShaderPipelineCompileJob; class FVertexFactoryType; DECLARE_LOG_CATEGORY_EXTERN(LogShaderCompilers, Log, All); class FShaderCompileJob; class FShaderPipelineCompileJob; #define DEBUG_INFINITESHADERCOMPILE 0 /** Stores all of the common information used to compile a shader or pipeline. */ class FShaderCommonCompileJob : public FRefCountedObject { public: /** Id of the shader map this shader belongs to. */ uint32 Id; /** true if the results of the shader compile have been processed. */ bool bFinalized; /** Output of the shader compile */ bool bSucceeded; bool bOptimizeForLowLatency; FShaderCommonCompileJob(uint32 InId) : Id(InId), bFinalized(false), bSucceeded(false), bOptimizeForLowLatency(false) { } virtual ~FShaderCommonCompileJob() {} virtual FShaderCompileJob* GetSingleShaderJob() { return nullptr; } virtual const FShaderCompileJob* GetSingleShaderJob() const { return nullptr; } virtual FShaderPipelineCompileJob* GetShaderPipelineJob() { return nullptr; } virtual const FShaderPipelineCompileJob* GetShaderPipelineJob() const { return nullptr; } }; /** Stores all of the input and output information used to compile a single shader. */ class FShaderCompileJob : public FShaderCommonCompileJob { public: /** Vertex factory type that this shader belongs to, may be NULL */ FVertexFactoryType* VFType; /** Shader type that this shader belongs to, must be valid */ FShaderType* ShaderType; /** Unique permutation identifier of the global shader type. */ int32 PermutationId; /** Input for the shader compile */ FShaderCompilerInput Input; FShaderCompilerOutput Output; // List of pipelines that are sharing this job. TMap<const FVertexFactoryType*, TArray<const FShaderPipelineType*>> SharingPipelines; FShaderCompileJob(uint32 InId, FVertexFactoryType* InVFType, FShaderType* InShaderType, int32 InPermutationId) : FShaderCommonCompileJob(InId), VFType(InVFType), ShaderType(InShaderType), PermutationId(InPermutationId) { } virtual FShaderCompileJob* GetSingleShaderJob() override { return this; } virtual const FShaderCompileJob* GetSingleShaderJob() const override { return this; } }; class FShaderPipelineCompileJob : public FShaderCommonCompileJob { public: TArray<FShaderCommonCompileJob*> StageJobs; bool bFailedRemovingUnused; /** Shader pipeline that this shader belongs to, may (currently) be NULL */ const FShaderPipelineType* ShaderPipeline; FShaderPipelineCompileJob(uint32 InId, const FShaderPipelineType* InShaderPipeline, int32 NumStages) : FShaderCommonCompileJob(InId), bFailedRemovingUnused(false), ShaderPipeline(InShaderPipeline) { check(InShaderPipeline && InShaderPipeline->GetName()); check(NumStages > 0); StageJobs.Empty(NumStages); } ~FShaderPipelineCompileJob() { for (int32 Index = 0; Index < StageJobs.Num(); ++Index) { delete StageJobs[Index]; } StageJobs.Reset(); } virtual FShaderPipelineCompileJob* GetShaderPipelineJob() override { return this; } virtual const FShaderPipelineCompileJob* GetShaderPipelineJob() const override { return this; } }; class FGlobalShaderTypeCompiler { public: /** * Enqueues compilation of a shader of this type. */ ENGINE_API static class FShaderCompileJob* BeginCompileShader(FGlobalShaderType* ShaderType, int32 PermutationId, EShaderPlatform Platform, const FShaderPipelineType* ShaderPipeline, TArray<FShaderCommonCompileJob*>& NewJobs); /** * Enqueues compilation of a shader pipeline of this type. */ ENGINE_API static void BeginCompileShaderPipeline(EShaderPlatform Platform, const FShaderPipelineType* ShaderPipeline, const TArray<FGlobalShaderType*>& ShaderStages, TArray<FShaderCommonCompileJob*>& NewJobs); /** Either returns an equivalent existing shader of this type, or constructs a new instance. */ static FShader* FinishCompileShader(FGlobalShaderType* ShaderType, const FShaderCompileJob& CompileJob, const FShaderPipelineType* ShaderPipelineType); }; class FShaderCompileThreadRunnableBase : public FRunnable { friend class FShaderCompilingManager; protected: /** The manager for this thread */ class FShaderCompilingManager* Manager; /** The runnable thread */ FRunnableThread* Thread; /** If the thread has been terminated by an unhandled exception, this contains the error message. */ FString ErrorMessage; /** true if the thread has been terminated by an unhandled exception. */ bool bTerminatedByError; TAtomic<bool> bForceFinish; public: FShaderCompileThreadRunnableBase(class FShaderCompilingManager* InManager); virtual ~FShaderCompileThreadRunnableBase() {} void StartThread(); // FRunnable interface. virtual void Stop() { bForceFinish = true; } virtual uint32 Run(); inline void WaitForCompletion() const { if( Thread ) { Thread->WaitForCompletion(); } } /** Checks the thread's health, and passes on any errors that have occured. Called by the main thread. */ void CheckHealth() const; /** Main work loop. */ virtual int32 CompilingLoop() = 0; }; /** * Shader compiling thread * This runs in the background while UE4 is running, launches shader compile worker processes when necessary, and feeds them inputs and reads back the outputs. */ class FShaderCompileThreadRunnable : public FShaderCompileThreadRunnableBase { friend class FShaderCompilingManager; private: /** Information about the active workers that this thread is tracking. */ TArray<struct FShaderCompileWorkerInfo*> WorkerInfos; /** Tracks the last time that this thread checked if the workers were still active. */ double LastCheckForWorkersTime; public: /** Initialization constructor. */ FShaderCompileThreadRunnable(class FShaderCompilingManager* InManager); virtual ~FShaderCompileThreadRunnable(); private: /** * Grabs tasks from Manager->CompileQueue in a thread safe way and puts them into QueuedJobs of available workers. * Also writes completed jobs to Manager->ShaderMapJobs. */ int32 PullTasksFromQueue(); /** Used when compiling through workers, writes out the worker inputs for any new tasks in WorkerInfos.QueuedJobs. */ void WriteNewTasks(); /** Used when compiling through workers, launches worker processes if needed. */ bool LaunchWorkersIfNeeded(); /** Used when compiling through workers, attempts to open the worker output file if the worker is done and read the results. */ void ReadAvailableResults(); /** Used when compiling directly through the console tools dll. */ void CompileDirectlyThroughDll(); /** Main work loop. */ virtual int32 CompilingLoop() override; }; namespace FShaderCompileUtilities { bool DoWriteTasks(const TArray<FShaderCommonCompileJob*>& QueuedJobs, FArchive& TransferFile); void DoReadTaskResults(const TArray<FShaderCommonCompileJob*>& QueuedJobs, FArchive& OutputFile); FArchive* CreateFileHelper(const FString& Filename); void MoveFileHelper(const FString& To, const FString& From); void DeleteFileHelper(const FString& Filename); } #if PLATFORM_WINDOWS // FASTBuild shader compilation is only supported on Windows. class FShaderCompileFASTBuildThreadRunnable : public FShaderCompileThreadRunnableBase { private: /** The handle referring to the FASTBuild console process, if a build is in progress. */ FProcHandle BuildProcessHandle; /** Process ID of the FASTBuild console, if a build is in progress. */ uint32 BuildProcessID; /** * A map of directory paths to shader jobs contained within that directory. * One entry per FASTBuild task. */ class FShaderBatch { TArray<FShaderCommonCompileJob*> Jobs; bool bTransferFileWritten; public: bool bSuccessfullyCompleted; const FString& DirectoryBase; const FString& InputFileName; const FString& SuccessFileName; const FString& OutputFileName; int32 BatchIndex; int32 DirectoryIndex; FString WorkingDirectory; FString OutputFileNameAndPath; FString SuccessFileNameAndPath; FString InputFileNameAndPath; FShaderBatch(const FString& InDirectoryBase, const FString& InInputFileName, const FString& InSuccessFileName, const FString& InOutputFileName, int32 InDirectoryIndex, int32 InBatchIndex) : bTransferFileWritten(false) , bSuccessfullyCompleted(false) , DirectoryBase(InDirectoryBase) , InputFileName(InInputFileName) , SuccessFileName(InSuccessFileName) , OutputFileName(InOutputFileName) { SetIndices(InDirectoryIndex, InBatchIndex); } void SetIndices(int32 InDirectoryIndex, int32 InBatchIndex); void CleanUpFiles(bool keepInputFile); inline int32 NumJobs() { return Jobs.Num(); } inline const TArray<FShaderCommonCompileJob*>& GetJobs() const { return Jobs; } void AddJob(FShaderCommonCompileJob* Job); void WriteTransferFile(); }; TArray<FShaderBatch*> ShaderBatchesInFlight; int32 ShaderBatchesInFlightCompleted; TArray<FShaderBatch*> ShaderBatchesFull; TSparseArray<FShaderBatch*> ShaderBatchesIncomplete; /** The full path to the two working directories for FASTBuild shader builds. */ const FString FASTBuildWorkingDirectory; uint32 FASTBuildDirectoryIndex; uint64 LastAddTime; uint64 StartTime; int32 BatchIndexToCreate; int32 BatchIndexToFill; FDateTime ScriptFileCreationTime; void PostCompletedJobsForBatch(FShaderBatch* Batch); void GatherResultsFromFASTBuild(); public: /** Initialization constructor. */ FShaderCompileFASTBuildThreadRunnable(class FShaderCompilingManager* InManager); virtual ~FShaderCompileFASTBuildThreadRunnable(); /** Main work loop. */ virtual int32 CompilingLoop() override; static bool IsSupported(); }; #endif #if PLATFORM_WINDOWS // XGE shader compilation is only supported on Windows. class FShaderCompileXGEThreadRunnable_XmlInterface : public FShaderCompileThreadRunnableBase { private: /** The handle referring to the XGE console process, if a build is in progress. */ FProcHandle BuildProcessHandle; /** Process ID of the XGE console, if a build is in progress. */ uint32 BuildProcessID; /** * A map of directory paths to shader jobs contained within that directory. * One entry per XGE task. */ class FShaderBatch { TArray<FShaderCommonCompileJob*> Jobs; bool bTransferFileWritten; public: const FString& DirectoryBase; const FString& InputFileName; const FString& SuccessFileName; const FString& OutputFileName; int32 BatchIndex; int32 DirectoryIndex; FString WorkingDirectory; FString OutputFileNameAndPath; FString SuccessFileNameAndPath; FString InputFileNameAndPath; FShaderBatch(const FString& InDirectoryBase, const FString& InInputFileName, const FString& InSuccessFileName, const FString& InOutputFileName, int32 InDirectoryIndex, int32 InBatchIndex) : bTransferFileWritten(false) , DirectoryBase(InDirectoryBase) , InputFileName(InInputFileName) , SuccessFileName(InSuccessFileName) , OutputFileName(InOutputFileName) { SetIndices(InDirectoryIndex, InBatchIndex); } void SetIndices(int32 InDirectoryIndex, int32 InBatchIndex); void CleanUpFiles(bool keepInputFile); inline int32 NumJobs() { return Jobs.Num(); } inline const TArray<FShaderCommonCompileJob*>& GetJobs() const { return Jobs; } void AddJob(FShaderCommonCompileJob* Job); void WriteTransferFile(); }; TArray<FShaderBatch*> ShaderBatchesInFlight; TArray<FShaderBatch*> ShaderBatchesFull; TSparseArray<FShaderBatch*> ShaderBatchesIncomplete; /** The full path to the two working directories for XGE shader builds. */ const FString XGEWorkingDirectory; uint32 XGEDirectoryIndex; uint64 LastAddTime; uint64 StartTime; int32 BatchIndexToCreate; int32 BatchIndexToFill; FDateTime ScriptFileCreationTime; void PostCompletedJobsForBatch(FShaderBatch* Batch); void GatherResultsFromXGE(); public: /** Initialization constructor. */ FShaderCompileXGEThreadRunnable_XmlInterface(class FShaderCompilingManager* InManager); virtual ~FShaderCompileXGEThreadRunnable_XmlInterface(); /** Main work loop. */ virtual int32 CompilingLoop() override; static bool IsSupported(); }; class FShaderCompileXGEThreadRunnable_InterceptionInterface : public FShaderCompileThreadRunnableBase { uint32 NumDispatchedJobs; TSparseArray<class FXGEShaderCompilerTask*> DispatchedTasks; public: /** Initialization constructor. */ FShaderCompileXGEThreadRunnable_InterceptionInterface(class FShaderCompilingManager* InManager); virtual ~FShaderCompileXGEThreadRunnable_InterceptionInterface(); /** Main work loop. */ virtual int32 CompilingLoop() override; static bool IsSupported(); private: void DispatchShaderCompileJobsBatch(TArray<FShaderCommonCompileJob*>& JobsToSerialize); }; #endif // PLATFORM_WINDOWS /** Results for a single compiled shader map. */ struct FShaderMapCompileResults { FShaderMapCompileResults() : NumJobsQueued(0), bAllJobsSucceeded(true), bApplyCompletedShaderMapForRendering(true), bRecreateComponentRenderStateOnCompletion(false) {} int32 NumJobsQueued; bool bAllJobsSucceeded; bool bApplyCompletedShaderMapForRendering; bool bRecreateComponentRenderStateOnCompletion; TArray<FShaderCommonCompileJob*> FinishedJobs; }; /** Results for a single compiled and finalized shader map. */ struct FShaderMapFinalizeResults : public FShaderMapCompileResults { /** Tracks finalization progress on this shader map. */ int32 FinalizeJobIndex; // List of pipelines with shared shaders; nullptr for non mesh pipelines TMap<const FVertexFactoryType*, TArray<const FShaderPipelineType*> > SharedPipelines; FShaderMapFinalizeResults(const FShaderMapCompileResults& InCompileResults) : FShaderMapCompileResults(InCompileResults), FinalizeJobIndex(0) {} }; /** * Manager of asynchronous and parallel shader compilation. * This class contains an interface to enqueue and retreive asynchronous shader jobs, and manages a FShaderCompileThreadRunnable. */ class FShaderCompilingManager { friend class FShaderCompileThreadRunnableBase; friend class FShaderCompileThreadRunnable; #if PLATFORM_WINDOWS friend class FShaderCompileXGEThreadRunnable_XmlInterface; friend class FShaderCompileXGEThreadRunnable_InterceptionInterface; friend class FShaderCompileFASTBuildThreadRunnable; #endif // PLATFORM_WINDOWS private: ////////////////////////////////////////////////////// // Thread shared properties: These variables can only be read from or written to when a lock on CompileQueueSection is obtained, since they are used by both threads. /** Tracks whether we are compiling while the game is running. If true, we need to throttle down shader compiling CPU usage to avoid starving the runtime threads. */ bool bCompilingDuringGame; /** Queue of tasks that haven't been assigned to a worker yet. */ TArray<FShaderCommonCompileJob*> CompileQueue; /** Map from shader map Id to the compile results for that map, used to gather compiled results. */ TMap<int32, FShaderMapCompileResults> ShaderMapJobs; /** Number of jobs currently being compiled. This includes CompileQueue and any jobs that have been assigned to workers but aren't complete yet. */ int32 NumOutstandingJobs; /** Number of jobs currently being compiled. This includes CompileQueue and any jobs that have been assigned to workers but aren't complete yet. */ int32 NumExternalJobs; /** Critical section used to gain access to the variables above that are shared by both the main thread and the FShaderCompileThreadRunnable. */ FCriticalSection CompileQueueSection; ////////////////////////////////////////////////////// // Main thread state - These are only accessed on the main thread and used to track progress /** Map from shader map id to results being finalized. Used to track shader finalizations over multiple frames. */ TMap<int32, FShaderMapFinalizeResults> PendingFinalizeShaderMaps; /** The threads spawned for shader compiling. */ TUniquePtr<FShaderCompileThreadRunnableBase> Thread; ////////////////////////////////////////////////////// // Configuration properties - these are set only on initialization and can be read from either thread /** Number of busy threads to use for shader compiling while loading. */ uint32 NumShaderCompilingThreads; /** Number of busy threads to use for shader compiling while in game. */ uint32 NumShaderCompilingThreadsDuringGame; /** Largest number of jobs that can be put in the same batch. */ int32 MaxShaderJobBatchSize; /** Process Id of UE4. */ uint32 ProcessId; /** Whether to allow compiling shaders through the worker application, which allows multiple cores to be used. */ bool bAllowCompilingThroughWorkers; /** Whether to allow shaders to compile in the background or to block after each material. */ bool bAllowAsynchronousShaderCompiling; /** Whether to ask to retry a failed shader compile error. */ bool bPromptToRetryFailedShaderCompiles; /** Whether to log out shader job completion times on the worker thread. Useful for tracking down which global shader is taking a long time. */ bool bLogJobCompletionTimes; /** Target execution time for ProcessAsyncResults. Larger values speed up async shader map processing but cause more hitchiness while async compiling is happening. */ float ProcessGameThreadTargetTime; /** Base directory where temporary files are written out during multi core shader compiling. */ FString ShaderBaseWorkingDirectory; /** Absolute version of ShaderBaseWorkingDirectory. */ FString AbsoluteShaderBaseWorkingDirectory; /** Absolute path to the directory to dump shader debug info to. */ FString AbsoluteShaderDebugInfoDirectory; /** Name of the shader worker application. */ FString ShaderCompileWorkerName; /** Whether the SCW has crashed and we should fall back to calling the compiler dll's directly. */ bool bFallBackToDirectCompiles; /** * Tracks the total time that shader compile workers have been busy since startup. * Useful for profiling the shader compile worker thread time. */ double WorkersBusyTime; /** * Tracks which opt-in shader platforms have their warnings suppressed. */ uint64 SuppressedShaderPlatforms; /** Launches the worker, returns the launched process handle. */ FProcHandle LaunchWorker(const FString& WorkingDirectory, uint32 ProcessId, uint32 ThreadId, const FString& WorkerInputFile, const FString& WorkerOutputFile); /** Blocks on completion of the given shader maps. */ void BlockOnShaderMapCompletion(const TArray<int32>& ShaderMapIdsToFinishCompiling, TMap<int32, FShaderMapFinalizeResults>& CompiledShaderMaps); /** Blocks on completion of all shader maps. */ void BlockOnAllShaderMapCompletion(TMap<int32, FShaderMapFinalizeResults>& CompiledShaderMaps); /** Finalizes the given shader map results and optionally assigns the affected shader maps to materials, while attempting to stay within an execution time budget. */ void ProcessCompiledShaderMaps(TMap<int32, FShaderMapFinalizeResults>& CompiledShaderMaps, float TimeBudget); /** Finalizes the given Niagara shader map results and assigns the affected shader maps to Niagara scripts, while attempting to stay within an execution time budget. */ void ProcessCompiledNiagaraShaderMaps(TMap<int32, FShaderMapFinalizeResults>& CompiledShaderMaps, float TimeBudget); /** Propagate the completed compile to primitives that might be using the materials compiled. */ void PropagateMaterialChangesToPrimitives(const TMap<FMaterial*, class FMaterialShaderMap*>& MaterialsToUpdate); /** Recompiles shader jobs with errors if requested, and returns true if a retry was needed. */ bool HandlePotentialRetryOnError(TMap<int32, FShaderMapFinalizeResults>& CompletedShaderMaps); public: ENGINE_API FShaderCompilingManager(); /** * Returns whether to display a notification that shader compiling is happening in the background. * Note: This is dependent on NumOutstandingJobs which is updated from another thread, so the results are non-deterministic. */ bool ShouldDisplayCompilingNotification() const { // Heuristic based on the number of jobs outstanding return NumOutstandingJobs > 80 || CompileQueue.Num() > 80 || NumExternalJobs > 10; } bool AllowAsynchronousShaderCompiling() const { return bAllowAsynchronousShaderCompiling; } /** * Returns whether async compiling is happening. * Note: This is dependent on NumOutstandingJobs which is updated from another thread, so the results are non-deterministic. */ bool IsCompiling() const { return NumOutstandingJobs > 0 || PendingFinalizeShaderMaps.Num() > 0 || CompileQueue.Num() > 0 || NumExternalJobs > 0; } /** * return true if we have shader jobs in any state * shader jobs are removed when they are applied to the gamethreadshadermap * accessable from gamethread */ bool HasShaderJobs() const { return ShaderMapJobs.Num() > 0 || PendingFinalizeShaderMaps.Num() > 0; } /** * Returns the number of outstanding compile jobs. * Note: This is dependent on NumOutstandingJobs which is updated from another thread, so the results are non-deterministic. */ int32 GetNumRemainingJobs() const { return NumOutstandingJobs + NumExternalJobs; } void SetExternalJobs(int32 NumJobs) { NumExternalJobs = NumJobs; } ENGINE_API bool GetDumpShaderDebugInfo() const; const FString& GetAbsoluteShaderDebugInfoDirectory() const { return AbsoluteShaderDebugInfoDirectory; } bool AreWarningsSuppressed(const EShaderPlatform Platform) const { return (SuppressedShaderPlatforms & (static_cast<uint64>(1) << Platform)) != 0; } void SuppressWarnings(const EShaderPlatform Platform) { SuppressedShaderPlatforms |= static_cast<uint64>(1) << Platform; } /** * Adds shader jobs to be asynchronously compiled. * FinishCompilation or ProcessAsyncResults must be used to get the results. */ ENGINE_API void AddJobs(TArray<FShaderCommonCompileJob*>& NewJobs, bool bApplyCompletedShaderMapForRendering, bool bOptimizeForLowLatency, bool bRecreateComponentRenderStateOnCompletion); /** * Removes all outstanding compile jobs for the passed shader maps. */ ENGINE_API void CancelCompilation(const TCHAR* MaterialName, const TArray<int32>& ShaderMapIdsToCancel); /** * Blocks until completion of the requested shader maps. * This will not assign the shader map to any materials, the caller is responsible for that. */ ENGINE_API void FinishCompilation(const TCHAR* MaterialName, const TArray<int32>& ShaderMapIdsToFinishCompiling); /** * Blocks until completion of all async shader compiling, and assigns shader maps to relevant materials. * This should be called before exit if the DDC needs to be made up to date. */ ENGINE_API void FinishAllCompilation(); /** * Shutdown the shader compiler manager, this will shutdown immediately and not process any more shader compile requests. */ ENGINE_API void Shutdown(); /** * Processes completed asynchronous shader maps, and assigns them to relevant materials. * @param bLimitExecutionTime - When enabled, ProcessAsyncResults will be bandwidth throttled by ProcessGameThreadTargetTime, to limit hitching. * ProcessAsyncResults will then have to be called often to finish all shader maps (eg from Tick). Otherwise, all compiled shader maps will be processed. * @param bBlockOnGlobalShaderCompletion - When enabled, ProcessAsyncResults will block until global shader maps are complete. * This must be done before using global shaders for rendering. */ ENGINE_API void ProcessAsyncResults(bool bLimitExecutionTime, bool bBlockOnGlobalShaderCompletion); /** * Returns true if the given shader compile worker is still running. */ static bool IsShaderCompilerWorkerRunning(FProcHandle & WorkerHandle); }; /** The global shader compiling thread manager. */ extern ENGINE_API FShaderCompilingManager* GShaderCompilingManager; /** The shader precompilers for each platform. These are only set during the console shader compilation while cooking or in the PrecompileShaders commandlet. */ extern class FConsoleShaderPrecompiler* GConsoleShaderPrecompilers[SP_NumPlatforms]; /** Enqueues a shader compile job with GShaderCompilingManager. */ extern ENGINE_API void GlobalBeginCompileShader( const FString& DebugGroupName, class FVertexFactoryType* VFType, class FShaderType* ShaderType, const class FShaderPipelineType* ShaderPipelineType, const TCHAR* SourceFilename, const TCHAR* FunctionName, FShaderTarget Target, FShaderCompileJob* NewJob, TArray<FShaderCommonCompileJob*>& NewJobs, bool bAllowDevelopmentShaderCompile = true ); /** Implementation of the 'recompileshaders' console command. Recompiles shaders at runtime based on various criteria. */ extern bool RecompileShaders(const TCHAR* Cmd, FOutputDevice& Ar); /** Returns whether all global shader types containing the substring are complete and ready for rendering. if type name is null, check everything */ extern ENGINE_API bool IsGlobalShaderMapComplete(const TCHAR* TypeNameSubstring = nullptr); /** * Makes sure all global shaders are loaded and/or compiled for the passed in platform. * Note: if compilation is needed, this only kicks off the compile. * * @param Platform Platform to verify global shaders for */ extern ENGINE_API void VerifyGlobalShaders(EShaderPlatform Platform, bool bLoadedFromCacheFile); /** * Forces a recompile of the global shaders. */ extern ENGINE_API void RecompileGlobalShaders(); /** * Recompiles global shaders and material shaders * rebuilds global shaders and also * clears the cooked platform data for all materials if there is a global shader change detected * can be slow */ extern ENGINE_API bool RecompileChangedShadersForPlatform(const FString& PlatformName); /** * Begins recompiling the specified global shader types, and flushes their bound shader states. * FinishRecompileGlobalShaders must be called after this and before using the global shaders for anything. */ extern ENGINE_API void BeginRecompileGlobalShaders(const TArray<FShaderType*>& OutdatedShaderTypes, const TArray<const FShaderPipelineType*>& OutdatedShaderPipelineTypes, EShaderPlatform ShaderPlatform); /** Finishes recompiling global shaders. Must be called after BeginRecompileGlobalShaders. */ extern ENGINE_API void FinishRecompileGlobalShaders(); /** Called by the shader compiler to process completed global shader jobs. */ extern ENGINE_API void ProcessCompiledGlobalShaders(const TArray<FShaderCommonCompileJob*>& CompilationResults); /** * Saves the global shader map as a file for the target platform. * @return the name of the file written */ extern ENGINE_API FString SaveGlobalShaderFile(EShaderPlatform Platform, FString SavePath, class ITargetPlatform* TargetPlatform = nullptr); /** * Recompiles global shaders * * @param PlatformName Name of the Platform the shaders are compiled for * @param OutputDirectory The directory the compiled data will be stored to * @param MaterialsToLoad List of Materials that need to be loaded and compiled * @param SerializedShaderResources Serialized shader resources * @param MeshMaterialMaps Mesh material maps * @param ModifiedFiles Returns the list of modified files if not NULL * @param bCompileChangedShaders Whether to compile all changed shaders or the specific material that is passed **/ extern ENGINE_API void RecompileShadersForRemote( const FString& PlatformName, EShaderPlatform ShaderPlatform, const FString& OutputDirectory, const TArray<FString>& MaterialsToLoad, const TArray<uint8>& SerializedShaderResources, TArray<uint8>* MeshMaterialMaps, TArray<FString>* ModifiedFiles, bool bCompileChangedShaders = true); extern ENGINE_API void CompileGlobalShaderMap(bool bRefreshShaderMap=false); extern ENGINE_API void CompileGlobalShaderMap(EShaderPlatform Platform, bool bRefreshShaderMap = false); extern ENGINE_API void CompileGlobalShaderMap(ERHIFeatureLevel::Type InFeatureLevel, bool bRefreshShaderMap=false); extern ENGINE_API FString GetGlobalShaderMapDDCKey(); extern ENGINE_API FString GetMaterialShaderMapDDCKey();
shaderCompile.cpp文件,主要按照xge的搞,另外,转换为绝对路径的代码删掉:
shaderCompilerFASTBuild.cpp:
1 // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 3 #include "ShaderCompiler.h" 4 #include "GenericPlatform/GenericPlatformFile.h" 5 #include "HAL/PlatformFilemanager.h" 6 #include "HAL/FileManager.h" 7 #include "Misc/ScopeLock.h" 8 #include "CoreGlobals.h" 9 #include "Misc/ConfigCacheIni.h" 10 11 #if PLATFORM_WINDOWS 12 13 // -------------------------------------------------------------------------- 14 // Legacy XGE Xml interface 15 // -------------------------------------------------------------------------- 16 namespace FASTBuildConsoleVariables 17 { 18 /** The total number of batches to fill with shaders before creating another group of batches. */ 19 int32 BatchGroupSize = 128; 20 FAutoConsoleVariableRef CVarFASTBuildShaderCompileBatchGroupSize( 21 TEXT("r.FASTBuildShaderCompile.BatchGroupSize"), 22 BatchGroupSize, 23 TEXT("Specifies the number of batches to fill with shaders.\n") 24 TEXT("Shaders are spread across this number of batches until all the batches are full.\n") 25 TEXT("This allows the FASTBuild compile to go wider when compiling a small number of shaders.\n") 26 TEXT("Default = 128\n"), 27 ECVF_Default); 28 29 int32 Enabled = 1; 30 FAutoConsoleVariableRef CVarFASTBuildShaderCompile( 31 TEXT("r.FASTBuildShaderCompile"), 32 Enabled, 33 TEXT("Enables or disables the use of FASTBuild to build shaders.\n") 34 TEXT("0: Local builds only. \n") 35 TEXT("1: Distribute builds using FASTBuild."), 36 ECVF_Default); 37 38 /** The maximum number of shaders to group into a single FASTBuild task. */ 39 int32 BatchSize = 16; 40 FAutoConsoleVariableRef CVarFASTBuildShaderCompileBatchSize( 41 TEXT("r.FASTBuildShaderCompile.BatchSize"), 42 BatchSize, 43 TEXT("Specifies the number of shaders to batch together into a single FASTBuild task.\n") 44 TEXT("Default = 16\n"), 45 ECVF_Default); 46 47 /** 48 * The number of seconds to wait after a job is submitted before kicking off the XGE process. 49 * This allows time for the engine to enqueue more shaders, so we get better batching. 50 */ 51 float JobTimeout = 0.5f; 52 FAutoConsoleVariableRef CVarFASTBuildShaderCompileJobTimeout( 53 TEXT("r.FastBuiShaderCompile.Xml.JobTimeout"), 54 JobTimeout, 55 TEXT("The number of seconds to wait for additional shader jobs to be submitted before starting a build.\n") 56 TEXT("Default = 0.5\n"), 57 ECVF_Default); 58 } 59 60 static FString GetFASTBuild_ExecutablePath() 61 { 62 return FPaths::ConvertRelativePathToFull(FPaths::EngineDir()) / TEXT("Binaries\\ThirdParty\\FastBuild\\FBuild.exe"); 63 } 64 65 static FString FASTBuild_CachePath(TEXT("..\\Saved\\FASTBuildCache")); 66 static const FString FASTBuild_OrbisSDKToolchainRoot(TEXT("Engine\\Binaries\\ThirdParty\\PS4\\OrbisSDK")); 67 static const FString FASTBuild_Toolchain[] 68 { 69 TEXT("Engine\\Binaries\\ThirdParty\\Windows\\DirectX\\x64\\d3dcompiler_47.dll"), 70 /*TEXT("Engine\\Binaries\\Win64\\dxcompiler.dll"), 71 TEXT("Engine\\Binaries\\ThirdParty\\Windows\\DirectX\\x64\\WinPixEventRuntime.dll"), 72 TEXT("Engine\\Binaries\\Win64\\dxil.dll"),*/ 73 }; 74 75 76 static const FString FASTBuild_ScriptFileName(TEXT("fastbuildscript.bff")); 77 static const FString FASTBuild_InputFileName(TEXT("Worker.in")); 78 static const FString FASTBuild_OutputFileName(TEXT("Worker.out")); 79 static const FString FASTBuild_SuccessFileName(TEXT("Success")); 80 81 bool FShaderCompileFASTBuildThreadRunnable::IsSupported() 82 { 83 // Check to see if the FASTBuild exe exists. 84 if (FASTBuildConsoleVariables::Enabled == 1) 85 { 86 IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); 87 if (!PlatformFile.FileExists(*GetFASTBuild_ExecutablePath())) 88 { 89 UE_LOG(LogShaderCompilers, Warning, TEXT("Cannot use FASTBuild Shader Compiler as FASTBuild is not found. Target Path:%s"), *GetFASTBuild_ExecutablePath()); 90 FASTBuildConsoleVariables::Enabled = 0; 91 } 92 else 93 { 94 UE_LOG(LogShaderCompilers, Warning, TEXT("FASTBuild Shader Compiler found. Target Path:%s"), *GetFASTBuild_ExecutablePath()); 95 } 96 } 97 return FASTBuildConsoleVariables::Enabled == 1; 98 99 /*if (FASTBuildShaderCompilerVariables::Enabled == 1) 100 { 101 102 FString BaseRoot = FPaths::RootDir(); 103 const auto DirectoryMappings = AllShaderSourceDirectoryMappings(); 104 for (const auto& MappingEntry : DirectoryMappings) 105 { 106 if (!GetCommonBaseDir(MappingEntry.Value, BaseRoot, BaseRoot)) 107 { 108 UE_LOG(LogShaderCompilers, Warning, TEXT("Con not Use FASTBuild to compile shader,\ 109 Because the shader files are not in the same Hardware Disk Drive!")); 110 } 111 } 112 } 113 114 return FASTBuildShaderCompilerVariables::Enabled == 1;*/ 115 } 116 117 118 /** Initialization constructor. */ 119 FShaderCompileFASTBuildThreadRunnable::FShaderCompileFASTBuildThreadRunnable(class FShaderCompilingManager* InManager) 120 : FShaderCompileThreadRunnableBase(InManager) 121 , BuildProcessID(INDEX_NONE) 122 , ShaderBatchesInFlightCompleted(0) 123 , FASTBuildWorkingDirectory(InManager->AbsoluteShaderBaseWorkingDirectory / TEXT("FASTBuild")) 124 , FASTBuildDirectoryIndex(0) 125 , LastAddTime(0) 126 , StartTime(0) 127 , BatchIndexToCreate(0) 128 , BatchIndexToFill(0) 129 { 130 //FString Path = FPaths::EngineDir() / TEXT("Binaries/Win64/ShaderCompileWorker-Win64-Debug.exe"); 131 // InManager->ShaderCompileWorkerName = FPaths::ConvertRelativePathToFull(Path); 132 //FbWorkingDirectory = InManager->AbsoluteShaderBaseWorkingDirectory / TEXT("FASTBuild_Shader"); 133 } 134 135 FShaderCompileFASTBuildThreadRunnable::~FShaderCompileFASTBuildThreadRunnable() 136 { 137 if (BuildProcessHandle.IsValid()) 138 { 139 // We still have a build in progress. 140 // Kill it... 141 FPlatformProcess::TerminateProc(BuildProcessHandle); 142 FPlatformProcess::CloseProc(BuildProcessHandle); 143 } 144 145 // Clean up any intermediate files/directories we've got left over. 146 IFileManager::Get().DeleteDirectory(*FASTBuildWorkingDirectory, false, true); 147 148 // Delete all the shader batch instances we have. 149 for (FShaderBatch* Batch : ShaderBatchesIncomplete) 150 delete Batch; 151 152 for (FShaderBatch* Batch : ShaderBatchesInFlight) 153 delete Batch; 154 155 for (FShaderBatch* Batch : ShaderBatchesFull) 156 delete Batch; 157 158 ShaderBatchesIncomplete.Empty(); 159 ShaderBatchesInFlight.Empty(); 160 ShaderBatchesFull.Empty(); 161 } 162 163 164 void FShaderCompileFASTBuildThreadRunnable::PostCompletedJobsForBatch(FShaderBatch* Batch) 165 { 166 // Enter the critical section so we can access the input and output queues 167 FScopeLock Lock(&Manager->CompileQueueSection); 168 for (FShaderCommonCompileJob* Job : Batch->GetJobs()) 169 { 170 FShaderMapCompileResults& ShaderMapResults = Manager->ShaderMapJobs.FindChecked(Job->Id); 171 ShaderMapResults.FinishedJobs.Add(Job); 172 ShaderMapResults.bAllJobsSucceeded = ShaderMapResults.bAllJobsSucceeded && Job->bSucceeded; 173 } 174 175 // Using atomics to update NumOutstandingJobs since it is read outside of the critical section 176 FPlatformAtomics::InterlockedAdd(&Manager->NumOutstandingJobs, -Batch->NumJobs()); 177 } 178 179 void FShaderCompileFASTBuildThreadRunnable::FShaderBatch::AddJob(FShaderCommonCompileJob* Job) 180 { 181 // We can only add jobs to a batch which hasn't been written out yet. 182 if (bTransferFileWritten) 183 { 184 UE_LOG(LogShaderCompilers, Fatal, TEXT("Attempt to add shader compile jobs to a FASTBuild shader batch which has already been written to disk.")); 185 } 186 else 187 { 188 Jobs.Add(Job); 189 } 190 } 191 192 void FShaderCompileFASTBuildThreadRunnable::FShaderBatch::WriteTransferFile() 193 { 194 // Write out the file that the worker app is waiting for, which has all the information needed to compile the shader. 195 FArchive* TransferFile = FShaderCompileUtilities::CreateFileHelper(InputFileNameAndPath); 196 FShaderCompileUtilities::DoWriteTasks(Jobs, *TransferFile); 197 delete TransferFile; 198 199 bTransferFileWritten = true; 200 } 201 202 void FShaderCompileFASTBuildThreadRunnable::FShaderBatch::SetIndices(int32 InDirectoryIndex, int32 InBatchIndex) 203 { 204 DirectoryIndex = InDirectoryIndex; 205 BatchIndex = InBatchIndex; 206 207 WorkingDirectory = FString::Printf(TEXT("%s/%d/%d"), *DirectoryBase, DirectoryIndex, BatchIndex); 208 209 InputFileNameAndPath = WorkingDirectory / InputFileName; 210 OutputFileNameAndPath = WorkingDirectory / OutputFileName; 211 SuccessFileNameAndPath = WorkingDirectory / SuccessFileName; 212 } 213 214 void FShaderCompileFASTBuildThreadRunnable::FShaderBatch::CleanUpFiles(bool keepInputFile) 215 { 216 if (!keepInputFile) 217 { 218 FShaderCompileUtilities::DeleteFileHelper(InputFileNameAndPath); 219 } 220 221 FShaderCompileUtilities::DeleteFileHelper(OutputFileNameAndPath); 222 FShaderCompileUtilities::DeleteFileHelper(SuccessFileNameAndPath); 223 } 224 225 static void FASTBuildWriteScriptFileHeader(FArchive* ScriptFile, const FString& WorkerName) 226 { 227 static const TCHAR HeaderTemplate[] = 228 TEXT("Settings\r\n") 229 TEXT("{\r\n") 230 TEXT("\t.CachePath = '%s'\r\n") 231 TEXT("}\r\n") 232 TEXT("\r\n") 233 TEXT("Compiler('ShaderCompiler')\r\n") 234 TEXT("{\r\n") 235 TEXT("\t.CompilerFamily = 'custom'\r\n") 236 TEXT("\t.Executable = '%s'\r\n") 237 TEXT("\t.ExecutableRootPath = '%s'\r\n") 238 TEXT("\t.SimpleDistributionMode = true\r\n") 239 TEXT("\t.CustomEnvironmentVariables = { 'SCE_ORBIS_SDK_DIR=%%1%s' }\r\n") 240 TEXT("\t.ExtraFiles = \r\n") 241 TEXT("\t{\r\n"); 242 243 244 FString HeaderString = FString::Printf(HeaderTemplate, *FASTBuild_CachePath, *WorkerName, *IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*FPaths::RootDir()), *FASTBuild_OrbisSDKToolchainRoot); 245 ScriptFile->Serialize((void*)StringCast<ANSICHAR>(*HeaderString, HeaderString.Len()).Get(), sizeof(ANSICHAR) * HeaderString.Len()); 246 247 for (const FString& ExtraFilePartialPath : FASTBuild_Toolchain) 248 { 249 FString ExtraFile = TEXT("\t\t'") + IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*(FPaths::RootDir() / ExtraFilePartialPath)) + TEXT("',\r\n"); 250 ScriptFile->Serialize((void*)StringCast<ANSICHAR>(*ExtraFile, ExtraFile.Len()).Get(), sizeof(ANSICHAR) * ExtraFile.Len()); 251 } 252 253 class FDependencyEnumerator:public IPlatformFile::FDirectoryVisitor 254 { 255 public: 256 FDependencyEnumerator(FArchive* InScriptFile, const TCHAR* InPrefix, const TCHAR* InExtension 257 //, const TCHAR* InExcludeExtensions = NULL 258 ) 259 :ScriptFile(InScriptFile) 260 , Prefix(InPrefix) 261 , Extension(InExtension) 262 { 263 } 264 265 virtual bool Visit(const TCHAR* FilenameChar, bool bIsDirectory) override 266 { 267 268 if (!bIsDirectory) 269 { 270 FString Filename = FString(FilenameChar); 271 272 if ((!Prefix || Filename.Contains(Prefix)) && (!Extension || Filename.EndsWith(Extension))) 273 { 274 FString ExtraFile = TEXT("\t\t'") + IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*Filename) + TEXT("',\r\n"); 275 ScriptFile->Serialize((void*)StringCast<ANSICHAR>(*ExtraFile, ExtraFile.Len()).Get(), sizeof(ANSICHAR) * ExtraFile.Len()); 276 } 277 } 278 return true; 279 } 280 281 FArchive* const ScriptFile; 282 const TCHAR* Prefix; 283 const TCHAR* Extension; 284 }; 285 286 FDependencyEnumerator DllDeps = FDependencyEnumerator(ScriptFile, TEXT("ShaderCompileWorker-"), TEXT(".dll")); 287 IFileManager::Get().IterateDirectoryRecursively(*FPlatformProcess::GetModulesDirectory(), DllDeps); 288 289 FDependencyEnumerator ManifestDeps = FDependencyEnumerator(ScriptFile, TEXT("ShaderCompileWorker"), TEXT(".modules")); 290 IFileManager::Get().IterateDirectoryRecursively(*FPlatformProcess::GetModulesDirectory(), ManifestDeps); 291 292 FDependencyEnumerator ShaderUshDeps = FDependencyEnumerator(ScriptFile, nullptr, TEXT(".ush")); 293 IFileManager::Get().IterateDirectoryRecursively(FPlatformProcess::ShaderDir(), ShaderUshDeps); 294 295 FDependencyEnumerator ShaderUsfDeps = FDependencyEnumerator(ScriptFile, nullptr, TEXT(".usf")); 296 IFileManager::Get().IterateDirectoryRecursively(FPlatformProcess::ShaderDir(), ShaderUsfDeps); 297 298 /*FDependencyEnumerator ShaderHDeps = FDependencyEnumerator(ScriptFile, nullptr, TEXT(".h")); 299 IFileManager::Get().IterateDirectoryRecursively(FPlatformProcess::ShaderDir(), ShaderHDeps); 300 301 FDependencyEnumerator ShaderHlslDeps = FDependencyEnumerator(ScriptFile, nullptr, TEXT(".hlsl")); 302 IFileManager::Get().IterateDirectoryRecursively(FPlatformProcess::ShaderDir(), ShaderHlslDeps); 303 304 FDependencyEnumerator ShaderGlslDeps = FDependencyEnumerator(ScriptFile, nullptr, TEXT(".glsl")); 305 IFileManager::Get().IterateDirectoryRecursively(FPlatformProcess::ShaderDir(), ShaderGlslDeps);*/ 306 307 FDependencyEnumerator PluginShaderUshDeps = FDependencyEnumerator(ScriptFile, nullptr, TEXT(".ush")); 308 IFileManager::Get().IterateDirectoryRecursively(*FPaths::Combine(*(FPaths::EngineDir()), TEXT("Plugins")), PluginShaderUshDeps); 309 310 FDependencyEnumerator PluginShaderUsfDeps = FDependencyEnumerator(ScriptFile, nullptr, TEXT(".usf")); 311 IFileManager::Get().IterateDirectoryRecursively(*FPaths::Combine(*(FPaths::EngineDir()), TEXT("Plugins")), PluginShaderUsfDeps); 312 313 /*FDependencyEnumerator PluginShaderHDeps = FDependencyEnumerator(ScriptFile, nullptr, TEXT(".h")); 314 IFileManager::Get().IterateDirectoryRecursively(*FPaths::Combine(*(FPaths::EngineDir()), TEXT("Plugins")), PluginShaderHDeps); 315 316 FDependencyEnumerator PluginShaderHlslDeps = FDependencyEnumerator(ScriptFile, nullptr, TEXT(".hlsl")); 317 IFileManager::Get().IterateDirectoryRecursively(*FPaths::Combine(*(FPaths::EngineDir()), TEXT("Plugins")), PluginShaderHlslDeps); 318 319 FDependencyEnumerator PluginShaderGlslDeps = FDependencyEnumerator(ScriptFile, nullptr, TEXT(".glsl")); 320 IFileManager::Get().IterateDirectoryRecursively(*FPaths::Combine(*(FPaths::EngineDir()), TEXT("Plugins")), PluginShaderGlslDeps);*/ 321 322 const FString ExtraFilesFooter = 323 TEXT("\t}\r\n") 324 TEXT("}\r\n"); 325 ScriptFile->Serialize((void*)StringCast<ANSICHAR>(*ExtraFilesFooter, ExtraFilesFooter.Len()).Get(), sizeof(ANSICHAR) * ExtraFilesFooter.Len()); 326 327 } 328 329 static void WriteFASTBuildScriptFileFooter(FArchive* ScriptFile) 330 { 331 } 332 333 334 void FShaderCompileFASTBuildThreadRunnable::GatherResultsFromFASTBuild() 335 { 336 IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); 337 IFileManager& FileManager = IFileManager::Get(); 338 339 // Reverse iterate so we can remove batches that have completed as we go. 340 for (int32 Index = ShaderBatchesInFlight.Num() - 1; Index >= 0; Index--) 341 { 342 FShaderBatch* Batch = ShaderBatchesInFlight[Index]; 343 344 // If this batch is completed already, skip checks. 345 if (Batch->bSuccessfullyCompleted) 346 { 347 continue; 348 } 349 350 // Instead of checking for another file, we just check to see if the file exists, and if it does, we check if it has a write lock on it. FASTBuild never does partial writes, so this should tell us if FASTBuild is complete. 351 // Perform the same checks on the worker output file to verify it came from this build. 352 if (PlatformFile.FileExists(*Batch->OutputFileNameAndPath)) 353 { 354 if (FileManager.FileSize(*Batch->OutputFileNameAndPath) > 0) 355 { 356 IFileHandle* Handle = PlatformFile.OpenWrite(*Batch->OutputFileNameAndPath, true); 357 if (Handle) 358 { 359 delete Handle; 360 361 if (PlatformFile.GetTimeStamp(*Batch->OutputFileNameAndPath) >= ScriptFileCreationTime) 362 { 363 FArchive* OutputFilePtr = FileManager.CreateFileReader(*Batch->OutputFileNameAndPath, FILEREAD_Silent); 364 if (OutputFilePtr) 365 { 366 FArchive& OutputFile = *OutputFilePtr; 367 FShaderCompileUtilities::DoReadTaskResults(Batch->GetJobs(), OutputFile); 368 369 // Close the output file. 370 delete OutputFilePtr; 371 372 // Cleanup the worker files 373 // Do NOT clean up files until the whole batch is done, so we can clean them all up once the fastbuild process exits. Otherwise there is a race condition between FastBuild checking the output files, and us deleting them here. 374 //Batch->CleanUpFiles(false); // (false = don't keep the input file) 375 Batch->bSuccessfullyCompleted = true; 376 PostCompletedJobsForBatch(Batch); 377 //ShaderBatchesInFlight.RemoveAt(Index); 378 ShaderBatchesInFlightCompleted++; 379 //delete Batch; 380 } 381 } 382 } 383 } 384 } 385 } 386 } 387 388 389 int32 FShaderCompileFASTBuildThreadRunnable::CompilingLoop() 390 { 391 392 bool bWorkRemaining = false; 393 394 // We can only run one XGE build at a time. 395 // Check if a build is currently in progress. 396 if (BuildProcessHandle.IsValid()) 397 { 398 // Read back results from the current batches in progress. 399 GatherResultsFromFASTBuild(); 400 401 bool bDoExitCheck = false; 402 if (FPlatformProcess::IsProcRunning(BuildProcessHandle)) 403 { 404 if (ShaderBatchesInFlight.Num() == ShaderBatchesInFlightCompleted) 405 { 406 // We've processed all batches. 407 // Wait for the XGE console process to exit 408 FPlatformProcess::WaitForProc(BuildProcessHandle); 409 bDoExitCheck = true; 410 } 411 } 412 else 413 { 414 bDoExitCheck = true; 415 } 416 417 if (bDoExitCheck) 418 { 419 if (ShaderBatchesInFlight.Num() > ShaderBatchesInFlightCompleted) 420 { 421 // The build process has stopped. 422 // Do one final pass over the output files to gather any remaining results. 423 GatherResultsFromFASTBuild(); 424 } 425 426 // The build process is no longer running. 427 // We need to check the return code for possible failure 428 int32 ReturnCode = 0; 429 FPlatformProcess::GetProcReturnCode(BuildProcessHandle, &ReturnCode); 430 431 switch (ReturnCode) 432 { 433 case 0: 434 // No error 435 break; 436 437 case 1: 438 // One or more of the shader compile worker processes crashed. 439 UE_LOG(LogShaderCompilers, Fatal, TEXT("An error occurred during an XGE shader compilation job. One or more of the shader compile worker processes exited unexpectedly (Code 1).")); 440 break; 441 442 case 2: 443 // Fatal IncrediBuild error 444 UE_LOG(LogShaderCompilers, Fatal, TEXT("An error occurred during an FASTBuild shader compilation job. XGConsole.exe returned a fatal Incredibuild error (Code 2).")); 445 break; 446 447 case 3: 448 // User canceled the build 449 UE_LOG(LogShaderCompilers, Display, TEXT("The user terminated an XGE shader compilation job. Incomplete shader jobs will be redispatched in another FASTBuild build.")); 450 break; 451 452 default: 453 UE_LOG(LogShaderCompilers, Display, TEXT("An unknown error occurred during an XGE shader compilation job (Code %d). Incomplete shader jobs will be redispatched in another FASTBuild build."), ReturnCode); 454 break; 455 } 456 457 // Reclaim jobs from the workers which did not succeed (if any). 458 for (int i = 0; i < ShaderBatchesInFlight.Num(); ++i) 459 { 460 FShaderBatch* Batch = ShaderBatchesInFlight[i]; 461 462 if (Batch->bSuccessfullyCompleted) 463 { 464 // If we completed successfully, clean up. 465 //PostCompletedJobsForBatch(Batch); 466 Batch->CleanUpFiles(false); 467 468 // This will be a dangling pointer until we clear the array at the end of this for loop 469 delete Batch; 470 } 471 else 472 { 473 474 // Delete any output/success files, but keep the input file so we don't have to write it out again. 475 Batch->CleanUpFiles(true); 476 477 // We can't add any jobs to a shader batch which has already been written out to disk, 478 // so put the batch back into the full batches list, even if the batch isn't full. 479 ShaderBatchesFull.Add(Batch); 480 481 // Reset the batch/directory indices and move the input file to the correct place. 482 FString OldInputFilename = Batch->InputFileNameAndPath; 483 Batch->SetIndices(FASTBuildDirectoryIndex, BatchIndexToCreate++); 484 FShaderCompileUtilities::MoveFileHelper(Batch->InputFileNameAndPath, OldInputFilename); 485 } 486 } 487 ShaderBatchesInFlightCompleted = 0; 488 ShaderBatchesInFlight.Empty(); 489 FPlatformProcess::CloseProc(BuildProcessHandle); 490 } 491 492 bWorkRemaining |= ShaderBatchesInFlight.Num() > ShaderBatchesInFlightCompleted; 493 } 494 // No build process running. Check if we can kick one off now. 495 else 496 { 497 // Determine if enough time has passed to allow a build to kick off. 498 // Since shader jobs are added to the shader compile manager asynchronously by the engine, 499 // we want to give the engine enough time to queue up a large number of shaders. 500 // Otherwise we will only be kicking off a small number of shader jobs at once. 501 bool BuildDelayElapsed = (((FPlatformTime::Cycles() - LastAddTime) * FPlatformTime::GetSecondsPerCycle()) >= FASTBuildConsoleVariables::JobTimeout); 502 bool HasJobsToRun = (ShaderBatchesIncomplete.Num() > 0 || ShaderBatchesFull.Num() > 0); 503 504 if (BuildDelayElapsed && HasJobsToRun && ShaderBatchesInFlight.Num() == ShaderBatchesInFlightCompleted) 505 { 506 // Move all the pending shader batches into the in-flight list. 507 ShaderBatchesInFlight.Reserve(ShaderBatchesIncomplete.Num() + ShaderBatchesFull.Num()); 508 509 for (FShaderBatch* Batch : ShaderBatchesIncomplete) 510 { 511 // Check we've actually got jobs for this batch. 512 check(Batch->NumJobs() > 0); 513 514 // Make sure we've written out the worker files for any incomplete batches. 515 Batch->WriteTransferFile(); 516 ShaderBatchesInFlight.Add(Batch); 517 } 518 519 for (FShaderBatch* Batch : ShaderBatchesFull) 520 { 521 // Check we've actually got jobs for this batch. 522 check(Batch->NumJobs() > 0); 523 524 ShaderBatchesInFlight.Add(Batch); 525 } 526 527 ShaderBatchesFull.Empty(); 528 ShaderBatchesIncomplete.Empty(FASTBuildConsoleVariables::BatchGroupSize); 529 530 FString ScriptFilename = FASTBuildWorkingDirectory / FString::FromInt(FASTBuildDirectoryIndex) / FASTBuild_ScriptFileName; 531 UE_LOG(LogShaderCompilers, Log, TEXT("Script Path:%s"), *ScriptFilename); 532 // Create the XGE script file. 533 FArchive* ScriptFile = FShaderCompileUtilities::CreateFileHelper(ScriptFilename); 534 FASTBuildWriteScriptFileHeader(ScriptFile, Manager->ShaderCompileWorkerName); 535 536 // Write the XML task line for each shader batch 537 for (FShaderBatch* Batch : ShaderBatchesInFlight) 538 { 539 FString WorkerAbsoluteDirectory = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*Batch->WorkingDirectory); 540 FPaths::NormalizeDirectoryName(WorkerAbsoluteDirectory); 541 542 // Proper path should be "WorkingDir" 0 0 "Worker.in" "Worker.out" 543 544 FString ExecFunction = FString::Printf( 545 TEXT("ObjectList('ShaderBatch-%d')\r\n") 546 TEXT("{\r\n") 547 TEXT("\t.Compiler = 'ShaderCompiler'\r\n") 548 TEXT("\t.CompilerOptions = '\"\" %d %d \"%%1\" \"%%2\"' \r\n") 549 TEXT("\t.CompilerOutputExtension = '.out' \r\n") 550 TEXT("\t.CompilerInputFiles = {'%s'}\r\n") 551 TEXT("\t.CompilerOutputPath = '%s'\r\n") 552 TEXT("}\r\n\r\n"), 553 Batch->BatchIndex, 554 Manager->ProcessId, 555 Batch->BatchIndex, 556 *Batch->InputFileNameAndPath, 557 *WorkerAbsoluteDirectory 558 559 ); 560 561 ScriptFile->Serialize((void*)StringCast<ANSICHAR>(*ExecFunction, ExecFunction.Len()).Get(), sizeof(ANSICHAR) * ExecFunction.Len()); 562 }//end for 563 564 FString AliasBuildTargetOpen = FString( 565 TEXT("Alias('all')\r\n") 566 TEXT("{\r\n") 567 TEXT("\t.Targets = { \r\n") 568 ); 569 570 ScriptFile->Serialize((void*)StringCast<ANSICHAR>(*AliasBuildTargetOpen, AliasBuildTargetOpen.Len()).Get(), 571 sizeof(ANSICHAR) * AliasBuildTargetOpen.Len()); 572 573 574 for (FShaderBatch* Batch : ShaderBatchesInFlight) 575 { 576 FString TargetExport = 577 FString::Printf(TEXT("'ShaderBatch-%d', "), Batch->BatchIndex); 578 579 ScriptFile->Serialize((void*)StringCast<ANSICHAR>(*TargetExport, TargetExport.Len()).Get(), 580 sizeof(ANSICHAR) * TargetExport.Len()); 581 582 } 583 584 FString AliasBuildTargetClose = FString(TEXT(" }\r\n}\r\n")); 585 ScriptFile->Serialize((void*)StringCast<ANSICHAR>(*AliasBuildTargetClose, AliasBuildTargetClose.Len()).Get(), 586 sizeof(ANSICHAR) * AliasBuildTargetClose.Len()); 587 588 // End the XML script file and close it. 589 WriteFASTBuildScriptFileFooter(ScriptFile);//FASTBuildWriteScriptFileFooter ;todo 590 delete ScriptFile; 591 ScriptFile = nullptr; 592 593 // Grab the timestamp from the script file. 594 // We use this to ignore any left over files from previous builds by only accepting files created after the script file. 595 ScriptFileCreationTime = IFileManager::Get().GetTimeStamp(*ScriptFilename); 596 597 StartTime = FPlatformTime::Cycles(); 598 599 // Use stop on errors so we can respond to shader compile worker crashes immediately. 600 // Regular shader compilation errors are not returned as worker errors. 601 FString FBConsoleArgs = TEXT("-config \"") + ScriptFilename + TEXT("\" -dist -monitor -cache"); 602 603 // Kick off the FASTBuild process... 604 BuildProcessHandle = FPlatformProcess::CreateProc(*GetFASTBuild_ExecutablePath(), *FBConsoleArgs, false, false, true, &BuildProcessID, 0, nullptr, nullptr); 605 if (!BuildProcessHandle.IsValid()) 606 { 607 UE_LOG(LogShaderCompilers, Fatal, TEXT("Failed to launch %s during shader compilation."), *GetFASTBuild_ExecutablePath()); 608 } 609 610 // If the engine crashes, we don't get a chance to kill the build process. 611 // Start up the build monitor process to monitor for engine crashes. 612 uint32 BuildMonitorProcessID; 613 FProcHandle BuildMonitorHandle = FPlatformProcess::CreateProc(*Manager->ShaderCompileWorkerName, 614 *FString::Printf(TEXT("-xgemonitor %d %d"), Manager->ProcessId, BuildProcessID), 615 true, false, false, &BuildMonitorProcessID, 0, nullptr, nullptr); 616 617 FPlatformProcess::CloseProc(BuildMonitorHandle); 618 619 // Reset batch counters and switch directories 620 BatchIndexToFill = 0; 621 BatchIndexToCreate = 0; 622 FASTBuildDirectoryIndex = 1 - FASTBuildDirectoryIndex; 623 624 bWorkRemaining = true; 625 } 626 } 627 628 // Try to prepare more shader jobs (even if a build is in flight). 629 TArray<FShaderCommonCompileJob*> JobQueue; 630 { 631 // Enter the critical section so we can access the input and output queues 632 FScopeLock Lock(&Manager->CompileQueueSection); 633 634 // Grab as many jobs from the job queue as we can. 635 int32 NumNewJobs = Manager->CompileQueue.Num(); 636 if (NumNewJobs > 0) 637 { 638 int32 DestJobIndex = JobQueue.AddUninitialized(NumNewJobs); 639 for (int32 SrcJobIndex = 0; SrcJobIndex < NumNewJobs; SrcJobIndex++, DestJobIndex++) 640 { 641 JobQueue[DestJobIndex] = Manager->CompileQueue[SrcJobIndex]; 642 } 643 644 Manager->CompileQueue.RemoveAt(0, NumNewJobs); 645 } 646 } 647 648 if (JobQueue.Num() > 0) 649 { 650 // We have new jobs in the queue. 651 // Group the jobs into batches and create the worker input files. 652 for (int32 JobIndex = 0; JobIndex < JobQueue.Num(); JobIndex++) 653 { 654 if (BatchIndexToFill >= ShaderBatchesIncomplete.GetMaxIndex() || !ShaderBatchesIncomplete.IsAllocated(BatchIndexToFill)) 655 { 656 // There are no more incomplete shader batches available. 657 // Create another one... 658 ShaderBatchesIncomplete.Insert(BatchIndexToFill, new FShaderBatch( 659 FASTBuildWorkingDirectory, 660 FASTBuild_InputFileName, 661 FASTBuild_SuccessFileName, 662 FASTBuild_OutputFileName, 663 FASTBuildDirectoryIndex, 664 BatchIndexToCreate)); 665 666 BatchIndexToCreate++; 667 } 668 669 // Add a single job to this batch 670 FShaderBatch* CurrentBatch = ShaderBatchesIncomplete[BatchIndexToFill]; 671 CurrentBatch->AddJob(JobQueue[JobIndex]); 672 673 // If the batch is now full... 674 if (CurrentBatch->NumJobs() == FASTBuildConsoleVariables::BatchSize) 675 { 676 CurrentBatch->WriteTransferFile(); 677 678 // Move the batch to the full list. 679 ShaderBatchesFull.Add(CurrentBatch); 680 ShaderBatchesIncomplete.RemoveAt(BatchIndexToFill); 681 } 682 683 BatchIndexToFill++; 684 BatchIndexToFill %= FASTBuildConsoleVariables::BatchGroupSize; 685 } 686 687 // Keep track of the last time we added jobs. 688 LastAddTime = FPlatformTime::Cycles(); 689 690 bWorkRemaining = true; 691 } 692 693 if (Manager->bAllowAsynchronousShaderCompiling) 694 { 695 // Yield for a short while to stop this thread continuously polling the disk. 696 FPlatformProcess::Sleep(0.01f); 697 } 698 699 return bWorkRemaining ? 1 : 0; 700 } 701 702 FArchive* FShaderCompileUtilities::CreateFileHelper(const FString& Filename) 703 { 704 // TODO: This logic came from FShaderCompileThreadRunnable::WriteNewTasks(). 705 // We can't avoid code duplication unless we refactored the local worker too. 706 707 FArchive* File = nullptr; 708 int32 RetryCount = 0; 709 // Retry over the next two seconds if we can't write out the file. 710 // Anti-virus and indexing applications can interfere and cause this to fail. 711 while (File == nullptr && RetryCount < 200) 712 { 713 if (RetryCount > 0) 714 { 715 FPlatformProcess::Sleep(0.01f); 716 } 717 File = IFileManager::Get().CreateFileWriter(*Filename, FILEWRITE_EvenIfReadOnly); 718 RetryCount++; 719 } 720 if (File == nullptr) 721 { 722 File = IFileManager::Get().CreateFileWriter(*Filename, FILEWRITE_EvenIfReadOnly | FILEWRITE_NoFail); 723 } 724 checkf(File, TEXT("Failed to create file %s!"), *Filename); 725 return File; 726 } 727 728 void FShaderCompileUtilities::MoveFileHelper(const FString& To, const FString& From) 729 { 730 IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); 731 732 if (PlatformFile.FileExists(*From)) 733 { 734 FString DirectoryName; 735 int32 LastSlashIndex; 736 if (To.FindLastChar('/', LastSlashIndex)) 737 { 738 DirectoryName = To.Left(LastSlashIndex); 739 } 740 else 741 { 742 DirectoryName = To; 743 } 744 745 // TODO: This logic came from FShaderCompileThreadRunnable::WriteNewTasks(). 746 // We can't avoid code duplication unless we refactored the local worker too. 747 748 bool Success = false; 749 int32 RetryCount = 0; 750 // Retry over the next two seconds if we can't move the file. 751 // Anti-virus and indexing applications can interfere and cause this to fail. 752 while (!Success && RetryCount < 200) 753 { 754 if (RetryCount > 0) 755 { 756 FPlatformProcess::Sleep(0.01f); 757 } 758 759 // MoveFile does not create the directory tree, so try to do that now... 760 Success = PlatformFile.CreateDirectoryTree(*DirectoryName); 761 if (Success) 762 { 763 Success = PlatformFile.MoveFile(*To, *From); 764 } 765 RetryCount++; 766 } 767 checkf(Success, TEXT("Failed to move file %s to %s!"), *From, *To); 768 } 769 } 770 771 void FShaderCompileUtilities::DeleteFileHelper(const FString& Filename) 772 { 773 // TODO: This logic came from FShaderCompileThreadRunnable::WriteNewTasks(). 774 // We can't avoid code duplication unless we refactored the local worker too. 775 776 if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*Filename)) 777 { 778 bool bDeletedOutput = IFileManager::Get().Delete(*Filename, true, true); 779 780 // Retry over the next two seconds if we couldn't delete it 781 int32 RetryCount = 0; 782 while (!bDeletedOutput && RetryCount < 200) 783 { 784 FPlatformProcess::Sleep(0.01f); 785 bDeletedOutput = IFileManager::Get().Delete(*Filename, true, true); 786 RetryCount++; 787 } 788 checkf(bDeletedOutput, TEXT("Failed to delete %s!"), *Filename); 789 } 790 } 791 792 #endif // PLATFORM_WINDOWS