// Copyright Epic Games, Inc. All Rights Reserved. #include "NNERuntimeIREEShaderShared.h" #ifdef WITH_NNE_RUNTIME_IREE_SHADER #include "DataDrivenShaderPlatformInfo.h" #include "Interfaces/ITargetPlatform.h" #include "NNERuntimeIREEShader.h" #include "NNERuntimeIREEShaderCompilationManager.h" #include "NNERuntimeIREEShaderLog.h" #include "NNERuntimeIREEShaderMetadataAllocations.h" #include "RendererInterface.h" #include "RHIShaderFormatDefinitions.inl" #include "ShaderCompiler.h" #include "ShaderParameterMetadataBuilder.h" #include "Stats/StatsMisc.h" #include "TextureResource.h" #include "UObject/CoreObjectVersion.h" #include "UObject/UObjectHash.h" #include "UObject/UObjectIterator.h" IMPLEMENT_TYPE_LAYOUT(FNNERuntimeIREECompilationOutput); IMPLEMENT_TYPE_LAYOUT(FNNERuntimeIREEShaderMapId); IMPLEMENT_TYPE_LAYOUT(FNNERuntimeIREEShaderMapContent); FNNERuntimeIREEResource::FNNERuntimeIREEResource() : bLoadedCookedShaderMapId(false) { } FNNERuntimeIREEResource::~FNNERuntimeIREEResource() { } bool FNNERuntimeIREEResource::ShouldCache(EShaderPlatform InPlatform, const FShaderType* InShaderType) const { check(InShaderType->GetNNERuntimeIREEShaderType()) return true; } bool FNNERuntimeIREEResource::SerializeShaderMap(FArchive& Ar) { bool bSuccess = false; if (Ar.IsSaving()) { #if WITH_EDITOR FinishCompilation(); bool bValid = bSuccess = GameThreadShaderMap != nullptr && GameThreadShaderMap->CompiledSuccessfully(); Ar << bValid; if (bValid) { GameThreadShaderMap->AssociateWithAsset(AssetPath); GameThreadShaderMap->Serialize(Ar); } #endif } else { bool bValid = false; Ar << bValid; if (bValid) { TRefCountPtr LoadedShaderMap = new FNNERuntimeIREEShaderMap(); bSuccess = LoadedShaderMap->Serialize(Ar); // Toss the loaded shader data if this is a server only instance or if it's for a different RHI than the current one. // todo[CF] Don't cook it in the first place if (bSuccess && FApp::CanEverRender()) { GameThreadShaderMap = RenderingThreadShaderMap = LoadedShaderMap; GameThreadShaderMap->GetResource()->SetOwnerName(GetOwnerFName()); } } } return bSuccess; } void FNNERuntimeIREEResource::SetRenderingThreadShaderMap(FNNERuntimeIREEShaderMap* InShaderMap) { check(IsInRenderingThread()); RenderingThreadShaderMap = InShaderMap; } void FNNERuntimeIREEResource::RemoveOutstandingCompileId(const int32 InOldOutstandingCompileShaderMapId) { if (0 <= OutstandingCompileShaderMapIds.Remove(InOldOutstandingCompileShaderMapId)) { UE_LOG(LogNNERuntimeIREEShader, Verbose, TEXT("RemoveOutstandingCompileId %p %d"), this, InOldOutstandingCompileShaderMapId); } } void FNNERuntimeIREEResource::NotifyCompilationFinished(FString const& ResultMessage) { UE_LOG(LogNNERuntimeIREEShader, Log, TEXT("%s"), *ResultMessage); FNNERuntimeIREEShaderCompileMessage Message; Message.Type = FNNERuntimeIREEShaderCompileMessage::EMessageType::Info; Message.Text = ResultMessage; CompilationResults.Messages.Add(Message); // OnCompilationCompleteDelegate.ExecuteIfBound(this); } #if WITH_EDITOR void FNNERuntimeIREEResource::FinishCompilation() { TArray ShaderMapIdsToFinish; GetShaderMapIDsWithUnfinishedCompilation(ShaderMapIdsToFinish); if (ShaderMapIdsToFinish.Num() > 0) { for (int32 i = 0; i < ShaderMapIdsToFinish.Num(); i++) { UE_LOG(LogNNERuntimeIREEShader, Verbose, TEXT("FinishCompilation()[%d] %s id %d!"), i, *GetFriendlyName(), ShaderMapIdsToFinish[i]); } // Block until the shader maps that we will save have finished being compiled GNNERuntimeIREEShaderCompilationManager.FinishCompilation(*GetFriendlyName(), ShaderMapIdsToFinish); // Shouldn't have anything left to do... TArray ShaderMapIdsToFinish2; GetShaderMapIDsWithUnfinishedCompilation(ShaderMapIdsToFinish2); ensure(ShaderMapIdsToFinish2.Num() == 0); } } #endif // WITH_EDITOR void FNNERuntimeIREEResource::GetDependentShaderTypes(EShaderPlatform InPlatform, TArray& OutShaderTypes) const { for (TLinkedList::TIterator ShaderTypeIt(FShaderType::GetTypeList()); ShaderTypeIt; ShaderTypeIt.Next()) { FNNERuntimeIREEShaderType* ShaderType = ShaderTypeIt->GetNNERuntimeIREEShaderType(); if (ShaderType && ShaderType->ShouldCache(InPlatform, this) && ShouldCache(InPlatform, ShaderType) ) { OutShaderTypes.Add(ShaderType); } } OutShaderTypes.Sort(FCompareShaderTypes()); } void FNNERuntimeIREEResource::GetShaderMapId(EShaderPlatform InPlatform, const ITargetPlatform* TargetPlatform, FNNERuntimeIREEShaderMapId& OutId) const { if (bLoadedCookedShaderMapId) { OutId = CookedShaderMapId; } else { TArray ShaderTypes; GetDependentShaderTypes(InPlatform, ShaderTypes); OutId.FeatureLevel = GetFeatureLevel(); OutId.ShaderCodeHash = ShaderCodeHash; #if WITH_EDITOR OutId.SetShaderDependencies(ShaderTypes, InPlatform); if (TargetPlatform) { OutId.LayoutParams.InitializeForPlatform(TargetPlatform->IniPlatformName(), TargetPlatform->HasEditorOnlyData()); } else { OutId.LayoutParams.InitializeForCurrent(); } #else if (TargetPlatform != nullptr) { UE_LOG(LogNNERuntimeIREEShader, Error, TEXT("FNNERuntimeIREEResource::GetShaderMapId: TargetPlatform is not null, but a cooked executable cannot target platforms other than its own.")); } OutId.LayoutParams.InitializeForCurrent(); #endif } } bool FNNERuntimeIREEResource::CacheShaders(EShaderPlatform InPlatform, const ITargetPlatform* TargetPlatform, bool bApplyCompletedShaderMapForRendering, bool bSynchronous) { FNNERuntimeIREEShaderMapId ShaderMapId; GetShaderMapId(InPlatform, TargetPlatform, ShaderMapId); return CacheShaders(ShaderMapId, InPlatform, bApplyCompletedShaderMapForRendering, bSynchronous); } bool FNNERuntimeIREEResource::CacheShaders(const FNNERuntimeIREEShaderMapId& InShaderMapId, EShaderPlatform InPlatform, bool bApplyCompletedShaderMapForRendering, bool bSynchronous) { // Find the kernel's cached shader map. GameThreadShaderMap = FNNERuntimeIREEShaderMap::FindId(InShaderMapId, InPlatform); if (GameThreadShaderMap && GameThreadShaderMap->IsComplete(this, false)) { return true; } bool bSucceeded = false; #if WITH_EDITOR // If there's no cached shader map for this kernel compile a new one. // This is just kicking off the compile, GameThreadShaderMap will not be complete yet bSucceeded = BeginCompileShaderMap(InShaderMapId, InPlatform, GameThreadShaderMap, bApplyCompletedShaderMapForRendering, bSynchronous); #endif // WITH_EDITOR if (!bSucceeded) { GameThreadShaderMap = nullptr; } if (bApplyCompletedShaderMapForRendering) { FNNERuntimeIREEResource* Kernel = this; FNNERuntimeIREEShaderMap* LoadedShaderMap = GameThreadShaderMap; ENQUEUE_RENDER_COMMAND(FSetShaderMapOnComputeKernel)( [Kernel, LoadedShaderMap](FRHICommandListImmediate& RHICmdList) { Kernel->SetRenderingThreadShaderMap(LoadedShaderMap); }); } return bSucceeded; } void FNNERuntimeIREEResource::SetupResource( ERHIFeatureLevel::Type InFeatureLevel, FString const& InFriendlyName, FString const& InShaderEntryPoint, FString const& InShaderHashKey, FString const& InShaderSource, TUniquePtr InShaderParameterMetadataAllocations, FShaderParametersMetadata* InShaderParameterMetadata, FName const& InAssetPath, TConstArrayView InBufferBindings ) { FeatureLevel = InFeatureLevel; FriendlyName = InFriendlyName; ShaderEntryPoint = InShaderEntryPoint; ShaderCodeHash = GetTypeHash(InShaderHashKey); ShaderSource = InShaderSource; ShaderParameterMetadataAllocations = MoveTemp(InShaderParameterMetadataAllocations); ShaderParameterMetadata = InShaderParameterMetadata; CompilationResults.Messages.Reset(); AssetPath = InAssetPath; BufferBindings = InBufferBindings; } TShaderRef FNNERuntimeIREEResource::GetShader(int32 PermutationId) const { check(!GIsThreadedRendering || !IsInGameThread()); if (!GIsEditor || RenderingThreadShaderMap) { return RenderingThreadShaderMap->GetShader(PermutationId); } return TShaderRef(); } bool FNNERuntimeIREEResource::IsSame(const FNNERuntimeIREEShaderMapId& InIdentifier) const { return InIdentifier.ShaderCodeHash == ShaderCodeHash && InIdentifier.FeatureLevel == FeatureLevel; } uint32 FNNERuntimeIREEResource::GetBindingIndex(uint32 BufferIdx) const { check(BufferIdx < (uint32)BufferBindings.Num()); return BufferBindings[BufferIdx]; } #if WITH_EDITOR void FNNERuntimeIREEResource::GetShaderMapIDsWithUnfinishedCompilation(TArray& OutShaderMapIds) { // Build an array of the shader map Id's are not finished compiling. if (GameThreadShaderMap && GameThreadShaderMap.IsValid() && !GameThreadShaderMap->IsCompilationFinalized()) { OutShaderMapIds.Add(GameThreadShaderMap->GetCompilingId()); } else if (OutstandingCompileShaderMapIds.Num() != 0) { OutShaderMapIds.Append(OutstandingCompileShaderMapIds); } } /** * Compiles this kernel for Platform, storing the result in OutShaderMap * * @param InShaderMapId - the set of static parameters to compile * @param InPlatform - the platform to compile for * @param OutShaderMap - the shader map to compile * @return - true if compile succeeded or was not necessary (shader map for ShaderMapId was found and was complete) */ bool FNNERuntimeIREEResource::BeginCompileShaderMap(const FNNERuntimeIREEShaderMapId& InShaderMapId, EShaderPlatform InPlatform, TRefCountPtr& OutShaderMap, bool bApplyCompletedShaderMapForRendering, bool bSynchronous) { bool bSuccess = false; STAT(double NNERuntimeIREEShaderCompileTime = 0); SCOPE_SECONDS_COUNTER(NNERuntimeIREEShaderCompileTime); TRefCountPtr NewShaderMap = new FNNERuntimeIREEShaderMap(); // Create a shader compiler environment for the material that will be shared by all jobs from this material TRefCountPtr Environment = new FSharedShaderCompilerEnvironment(); // Compile the shaders for the kernel. FNNERuntimeIREECompilationOutput CompilationOutput; NewShaderMap->Compile(this, InShaderMapId, Environment, CompilationOutput, InPlatform, bSynchronous, bApplyCompletedShaderMapForRendering); if (bSynchronous) { // If this is a synchronous compile, assign the compile result to the output OutShaderMap = NewShaderMap->CompiledSuccessfully() ? NewShaderMap : nullptr; } else { UE_LOG(LogNNERuntimeIREEShader, Verbose, TEXT("BeginCompileShaderMap AddUnique %p %d"), this, NewShaderMap->GetCompilingId()); unimplemented(); // OutstandingCompileShaderMapIds.AddUnique(NewShaderMap->GetCompilingId()); // Async compile, use nullptr to detect it if used OutShaderMap = nullptr; } return true; } void FNNERuntimeIREEShaderMapId::SetShaderDependencies(const TArray& InShaderTypes, EShaderPlatform InShaderPlatform) { if (!FPlatformProperties::RequiresCookedData()) { for (FShaderType* ShaderType : InShaderTypes) { if (ShaderType != nullptr) { FShaderTypeDependency Dependency; Dependency.ShaderTypeName = ShaderType->GetHashedName(); Dependency.SourceHash = ShaderType->GetSourceHash(InShaderPlatform); ShaderTypeDependencies.Add(Dependency); } } } } #endif // WITH_EDITOR bool FNNERuntimeIREEShaderMapId::ContainsShaderType(const FShaderType* ShaderType) const { for (int32 TypeIndex = 0; TypeIndex < ShaderTypeDependencies.Num(); TypeIndex++) { if (ShaderTypeDependencies[TypeIndex].ShaderTypeName == ShaderType->GetHashedName()) { return true; } } return false; } #endif // WITH_NNE_RUNTIME_IREE_SHADER