代码改变世界

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