// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. /*============================================================================= GlobalShader.cpp: Global shader implementation. =============================================================================*/ #include "GlobalShader.h" #include "Misc/MessageDialog.h" #include "HAL/FileManager.h" #include "Serialization/NameAsStringProxyArchive.h" #include "Misc/Paths.h" #include "Serialization/MemoryWriter.h" #include "Serialization/MemoryReader.h" #include "Misc/ScopedSlowTask.h" #include "Misc/App.h" #include "StaticBoundShaderState.h" /** The global shader map. */ TShaderMap* GGlobalShaderMap[SP_NumPlatforms] = {}; IMPLEMENT_SHADER_TYPE(,FNULLPS,TEXT("/Engine/Private/NullPixelShader.usf"),TEXT("Main"),SF_Pixel); /** Used to identify the global shader map in compile queues. */ const int32 GlobalShaderMapId = 0; FGlobalShaderMapId::FGlobalShaderMapId(EShaderPlatform Platform) { TArray ShaderTypes; TArray ShaderPipelineTypes; for (TLinkedList::TIterator ShaderTypeIt(FShaderType::GetTypeList()); ShaderTypeIt; ShaderTypeIt.Next()) { FGlobalShaderType* GlobalShaderType = ShaderTypeIt->GetGlobalShaderType(); if (!GlobalShaderType) { continue; } bool bList = false; for (int32 PermutationId = 0; PermutationId < GlobalShaderType->GetPermutationCount(); PermutationId++) { if (GlobalShaderType->ShouldCompilePermutation(Platform, PermutationId)) { bList = true; break; } } if (bList) { ShaderTypes.Add(GlobalShaderType); } } for (TLinkedList::TIterator ShaderPipelineIt(FShaderPipelineType::GetTypeList()); ShaderPipelineIt; ShaderPipelineIt.Next()) { const FShaderPipelineType* Pipeline = *ShaderPipelineIt; if (Pipeline->IsGlobalTypePipeline()) { int32 NumStagesNeeded = 0; auto& StageTypes = Pipeline->GetStages(); for (const FShaderType* Shader : StageTypes) { const FGlobalShaderType* GlobalShaderType = Shader->GetGlobalShaderType(); if (GlobalShaderType->ShouldCompilePermutation(Platform, /* PermutationId = */ 0)) { ++NumStagesNeeded; } else { break; } } if (NumStagesNeeded == StageTypes.Num()) { ShaderPipelineTypes.Add(Pipeline); } } } // Individual shader dependencies ShaderTypes.Sort(FCompareShaderTypes()); for (int32 TypeIndex = 0; TypeIndex < ShaderTypes.Num(); TypeIndex++) { FShaderType* ShaderType = ShaderTypes[TypeIndex]; FShaderTypeDependency Dependency(ShaderType, Platform); const TCHAR* ShaderFilename = Dependency.ShaderType->GetShaderFilename(); TArray& Dependencies = ShaderFilenameToDependenciesMap.FindOrAdd(ShaderFilename); Dependencies.Add(Dependency); } // Shader pipeline dependencies ShaderPipelineTypes.Sort(FCompareShaderPipelineNameTypes()); for (int32 TypeIndex = 0; TypeIndex < ShaderPipelineTypes.Num(); TypeIndex++) { const FShaderPipelineType* Pipeline = ShaderPipelineTypes[TypeIndex]; FShaderPipelineTypeDependency Dependency(Pipeline, Platform); ShaderPipelineTypeDependencies.Add(Dependency); } } void FGlobalShaderMapId::AppendKeyString(FString& KeyString, const TArray& Dependencies) const { #if WITH_EDITOR TMap ReferencedUniformBuffers; for (int32 ShaderIndex = 0; ShaderIndex < Dependencies.Num(); ShaderIndex++) { const FShaderTypeDependency& ShaderTypeDependency = Dependencies[ShaderIndex]; KeyString += TEXT("_"); KeyString += ShaderTypeDependency.ShaderType->GetName(); KeyString += FString::Printf(TEXT("%i"), ShaderTypeDependency.PermutationId); // Add the type's source hash so that we can invalidate cached shaders when .usf changes are made KeyString += ShaderTypeDependency.SourceHash.ToString(); // Add the serialization history to the key string so that we can detect changes to global shader serialization without a corresponding .usf change ShaderTypeDependency.ShaderType->GetSerializationHistory().AppendKeyString(KeyString); const TMap& ReferencedUniformBufferStructsCache = ShaderTypeDependency.ShaderType->GetReferencedUniformBufferStructsCache(); // Gather referenced uniform buffers for (TMap::TConstIterator It(ReferencedUniformBufferStructsCache); It; ++It) { ReferencedUniformBuffers.Add(It.Key(), It.Value()); } } for (int32 Index = 0; Index < ShaderPipelineTypeDependencies.Num(); ++Index) { const FShaderPipelineTypeDependency& Dependency = ShaderPipelineTypeDependencies[Index]; KeyString += TEXT("_"); KeyString += Dependency.ShaderPipelineType->GetName(); // Add the type's source hash so that we can invalidate cached shaders when .usf changes are made KeyString += Dependency.StagesSourceHash.ToString(); for (const FShaderType* ShaderType : Dependency.ShaderPipelineType->GetStages()) { const TMap& ReferencedUniformBufferStructsCache = ShaderType->GetReferencedUniformBufferStructsCache(); // Gather referenced uniform buffers for (TMap::TConstIterator It(ReferencedUniformBufferStructsCache); It; ++It) { ReferencedUniformBuffers.Add(It.Key(), It.Value()); } } } { TArray TempData; FSerializationHistory SerializationHistory; FMemoryWriter Ar(TempData, true); FShaderSaveArchive SaveArchive(Ar, SerializationHistory); // Save uniform buffer member info so we can detect when layout has changed SerializeUniformBufferInfo(SaveArchive, ReferencedUniformBuffers); SerializationHistory.AppendKeyString(KeyString); } #endif // WITH_EDITOR } FGlobalShader::FGlobalShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FShader(Initializer) {} void BackupGlobalShaderMap(FGlobalShaderBackupData& OutGlobalShaderBackup) { for (int32 i = (int32)ERHIFeatureLevel::ES2; i < (int32)ERHIFeatureLevel::Num; ++i) { EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform((ERHIFeatureLevel::Type)i); if (ShaderPlatform < EShaderPlatform::SP_NumPlatforms && GGlobalShaderMap[ShaderPlatform] != nullptr) { TUniquePtr> ShaderData = MakeUnique>(); FMemoryWriter Ar(*ShaderData); GGlobalShaderMap[ShaderPlatform]->SerializeInline(Ar, true, true, false, nullptr); GGlobalShaderMap[ShaderPlatform]->RegisterSerializedShaders(false); GGlobalShaderMap[ShaderPlatform]->Empty(); OutGlobalShaderBackup.FeatureLevelShaderData[i] = MoveTemp(ShaderData); } } // Remove cached references to global shaders for (TLinkedList::TIterator It(FGlobalBoundShaderStateResource::GetGlobalBoundShaderStateList()); It; It.Next()) { BeginUpdateResourceRHI(*It); } } void RestoreGlobalShaderMap(const FGlobalShaderBackupData& GlobalShaderBackup) { for (int32 i = (int32)ERHIFeatureLevel::ES2; i < (int32)ERHIFeatureLevel::Num; ++i) { EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform((ERHIFeatureLevel::Type)i); if (GlobalShaderBackup.FeatureLevelShaderData[i] != nullptr && ShaderPlatform < EShaderPlatform::SP_NumPlatforms && GGlobalShaderMap[ShaderPlatform] != nullptr) { FMemoryReader Ar(*GlobalShaderBackup.FeatureLevelShaderData[i]); GGlobalShaderMap[ShaderPlatform]->SerializeInline(Ar, true, true, false, nullptr); GGlobalShaderMap[ShaderPlatform]->RegisterSerializedShaders(false); } } } TShaderMap* GetGlobalShaderMap(EShaderPlatform Platform) { // If the global shader map hasn't been created yet check(GGlobalShaderMap[Platform]); return GGlobalShaderMap[Platform]; }