Files
UnrealEngineUWP/Engine/Source/Runtime/ShaderCore/Private/Shader.cpp
2014-07-29 17:32:52 -04:00

1154 lines
32 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
Shader.cpp: Shader implementation.
=============================================================================*/
#include "ShaderCore.h"
#include "Shader.h"
#include "VertexFactory.h"
#include "DiagnosticTable.h"
#include "DerivedDataCacheInterface.h"
#include "ModuleManager.h"
#include "TargetPlatform.h"
#include "RHICommandList.h"
DEFINE_LOG_CATEGORY(LogShaders);
void FShaderParameterMap::VerifyBindingsAreComplete(const TCHAR* ShaderTypeName, EShaderFrequency Frequency, FVertexFactoryType* InVertexFactoryType) const
{
#if WITH_EDITORONLY_DATA
// Only people working on shaders (and therefore have LogShaders unsuppressed) will want to see these errors
if (UE_LOG_ACTIVE(LogShaders, Warning))
{
const TCHAR* VertexFactoryName = InVertexFactoryType ? InVertexFactoryType->GetName() : TEXT("?");
bool bBindingsComplete = true;
FString UnBoundParameters = TEXT("");
for (TMap<FString,FParameterAllocation>::TConstIterator ParameterIt(ParameterMap);ParameterIt;++ParameterIt)
{
const FString& ParamName = ParameterIt.Key();
const FParameterAllocation& ParamValue = ParameterIt.Value();
if(!ParamValue.bBound)
{
// Only valid parameters should be in the shader map
checkSlow(ParamValue.Size > 0);
bBindingsComplete = bBindingsComplete && ParamValue.bBound;
UnBoundParameters += FString(TEXT(" Parameter ")) + ParamName + TEXT(" not bound!\n");
}
}
if (!bBindingsComplete)
{
FString ErrorMessage = FString(TEXT("Found unbound parameters being used in shadertype ")) + ShaderTypeName + TEXT(" (VertexFactory: ") + VertexFactoryName + TEXT(")\n") + UnBoundParameters;
// An unbound parameter means the engine is not going to set its value (because it was never bound)
// but it will be used in rendering, which will most likely cause artifacts
// We use a non-Slate message box to avoid problem where we haven't compiled the shaders for Slate.
FPlatformMisc::MessageBoxExt(EAppMsgType::Ok, *ErrorMessage, TEXT("Error"));
}
}
#endif // WITH_EDITORONLY_DATA
}
void FShaderParameterMap::UpdateHash(FSHA1& HashState) const
{
for(TMap<FString,FParameterAllocation>::TConstIterator ParameterIt(ParameterMap);ParameterIt;++ParameterIt)
{
const FString& ParamName = ParameterIt.Key();
const FParameterAllocation& ParamValue = ParameterIt.Value();
HashState.Update((const uint8*)*ParamName, ParamName.Len() * sizeof(TCHAR));
HashState.Update((const uint8*)&ParamValue.BufferIndex, sizeof(ParamValue.BufferIndex));
HashState.Update((const uint8*)&ParamValue.BaseIndex, sizeof(ParamValue.BaseIndex));
HashState.Update((const uint8*)&ParamValue.Size, sizeof(ParamValue.Size));
}
}
bool FShaderType::bInitializedSerializationHistory = false;
FShaderType::FShaderType(
const TCHAR* InName,
const TCHAR* InSourceFilename,
const TCHAR* InFunctionName,
uint32 InFrequency,
ConstructSerializedType InConstructSerializedRef
):
Name(InName),
SourceFilename(InSourceFilename),
FunctionName(InFunctionName),
Frequency(InFrequency),
ConstructSerializedRef(InConstructSerializedRef),
GlobalListLink(this)
{
for (int32 Platform = 0; Platform < SP_NumPlatforms; Platform++)
{
bCachedUniformBufferStructDeclarations[Platform] = false;
}
// This will trigger if an IMPLEMENT_SHADER_TYPE was in a module not loaded before InitializeShaderTypes
// Shader types need to be implemented in modules that are loaded before that
check(!bInitializedSerializationHistory);
//make sure the name is shorter than the maximum serializable length
check(FCString::Strlen(InName) < NAME_SIZE);
// register this shader type
GlobalListLink.Link(GetTypeList());
GetNameToTypeMap().Add(FName(InName), this);
// Assign the shader type the next unassigned hash index.
static uint32 NextHashIndex = 0;
HashIndex = NextHashIndex++;
}
FShaderType::~FShaderType()
{
GlobalListLink.Unlink();
GetNameToTypeMap().Remove(FName(Name));
}
TLinkedList<FShaderType*>*& FShaderType::GetTypeList()
{
static TLinkedList<FShaderType*>* GShaderTypeList = NULL;
return GShaderTypeList;
}
FShaderType* FShaderType::GetShaderTypeByName(const TCHAR* Name)
{
for(TLinkedList<FShaderType*>::TIterator It(GetTypeList()); It; It.Next())
{
FShaderType* Type = *It;
if (FPlatformString::Strcmp(Name, Type->GetName()) == 0)
{
return Type;
}
}
return NULL;
}
TArray<FShaderType*> FShaderType::GetShaderTypesByFilename(const TCHAR* Filename)
{
TArray<FShaderType*> OutShaders;
for(TLinkedList<FShaderType*>::TIterator It(GetTypeList()); It; It.Next())
{
FShaderType* Type = *It;
if (FPlatformString::Strcmp(Filename, Type->GetShaderFilename()) == 0)
{
OutShaders.Add(Type);
}
}
return OutShaders;
}
TMap<FName, FShaderType*>& FShaderType::GetNameToTypeMap()
{
static TMap<FName, FShaderType*>* GShaderNameToTypeMap = NULL;
if(!GShaderNameToTypeMap)
{
GShaderNameToTypeMap = new TMap<FName, FShaderType*>();
}
return *GShaderNameToTypeMap;
}
void FShaderType::GetOutdatedTypes(TArray<FShaderType*>& OutdatedShaderTypes, TArray<const FVertexFactoryType*>& OutdatedFactoryTypes)
{
for(TLinkedList<FShaderType*>::TIterator It(GetTypeList()); It; It.Next())
{
FShaderType* Type = *It;
for(TMap<FShaderId,FShader*>::TConstIterator ShaderIt(Type->ShaderIdMap);ShaderIt;++ShaderIt)
{
FShader* Shader = ShaderIt.Value();
const FVertexFactoryParameterRef* VFParameterRef = Shader->GetVertexFactoryParameterRef();
const FSHAHash& SavedHash = Shader->GetHash();
const FSHAHash& CurrentHash = Type->GetSourceHash();
const bool bOutdatedShader = SavedHash != CurrentHash;
const bool bOutdatedVertexFactory =
VFParameterRef && VFParameterRef->GetVertexFactoryType() && VFParameterRef->GetVertexFactoryType()->GetSourceHash() != VFParameterRef->GetHash();
if (bOutdatedShader)
{
OutdatedShaderTypes.AddUnique(Shader->Type);
}
if (bOutdatedVertexFactory)
{
OutdatedFactoryTypes.AddUnique(VFParameterRef->GetVertexFactoryType());
}
}
}
for (int32 TypeIndex = 0; TypeIndex < OutdatedShaderTypes.Num(); TypeIndex++)
{
UE_LOG(LogShaders, Warning, TEXT(" Recompiling %s"), OutdatedShaderTypes[TypeIndex]->GetName());
}
for (int32 TypeIndex = 0; TypeIndex < OutdatedFactoryTypes.Num(); TypeIndex++)
{
UE_LOG(LogShaders, Warning, TEXT(" Recompiling %s"), OutdatedFactoryTypes[TypeIndex]->GetName());
}
}
FArchive& operator<<(FArchive& Ar,FShaderType*& Ref)
{
if(Ar.IsSaving())
{
FName FactoryName = Ref ? FName(Ref->Name) : NAME_None;
Ar << FactoryName;
}
else if(Ar.IsLoading())
{
FName FactoryName = NAME_None;
Ar << FactoryName;
Ref = NULL;
if(FactoryName != NAME_None)
{
// look for the shader type in the global name to type map
FShaderType** ShaderType = FShaderType::GetNameToTypeMap().Find(FactoryName);
if (ShaderType)
{
// if we found it, use it
Ref = *ShaderType;
}
}
}
return Ar;
}
FShader* FShaderType::FindShaderById(const FShaderId& Id) const
{
return ShaderIdMap.FindRef(Id);
}
FShader* FShaderType::ConstructForDeserialization() const
{
return (*ConstructSerializedRef)();
}
const FSHAHash& FShaderType::GetSourceHash() const
{
return GetShaderFileHash(GetShaderFilename());
}
void FShaderType::Initialize(const TMap<FString, TArray<const TCHAR*> >& ShaderFileToUniformBufferVariables)
{
if (!FPlatformProperties::RequiresCookedData())
{
for(TLinkedList<FShaderType*>::TIterator It(FShaderType::GetTypeList()); It; It.Next())
{
FShaderType* Type = *It;
GenerateReferencedUniformBuffers(Type->SourceFilename, Type->Name, ShaderFileToUniformBufferVariables, Type->ReferencedUniformBufferStructsCache);
// Cache serialization history for each shader type
// This history is used to detect when shader serialization changes without a corresponding .usf change
{
// Construct a temporary shader, which is initialized to safe values for serialization
FShader* TempShader = Type->ConstructForDeserialization();
check(TempShader != NULL);
TempShader->Type = Type;
// Serialize the temp shader to memory and record the number and sizes of serializations
TArray<uint8> TempData;
FMemoryWriter Ar(TempData, true);
FShaderSaveArchive SaveArchive(Ar, Type->SerializationHistory);
TempShader->SerializeBase(SaveArchive, false);
// Destroy the temporary shader
delete TempShader;
}
}
}
bInitializedSerializationHistory = true;
}
void FShaderType::Uninitialize()
{
for(TLinkedList<FShaderType*>::TIterator It(FShaderType::GetTypeList()); It; It.Next())
{
FShaderType* Type = *It;
Type->SerializationHistory = FSerializationHistory();
}
bInitializedSerializationHistory = false;
}
TMap<FShaderResourceId, FShaderResource*> FShaderResource::ShaderResourceIdMap;
FShaderResource::FShaderResource()
: NumInstructions(0)
, NumTextureSamplers(0)
, NumRefs(0)
{
INC_DWORD_STAT_BY(STAT_Shaders_NumShaderResourcesLoaded, 1);
}
FShaderResource::FShaderResource(const FShaderCompilerOutput& Output)
: NumInstructions(Output.NumInstructions)
, NumTextureSamplers(Output.NumTextureSamplers)
, NumRefs(0)
{
Target = Output.Target;
Code = Output.Code;
check(Code.Num() > 0);
OutputHash = Output.OutputHash;
checkSlow(OutputHash != FSHAHash());
ShaderResourceIdMap.Add(GetId(), this);
INC_DWORD_STAT_BY_FName(*GetMemoryStatType((EShaderFrequency)Target.Frequency), Code.Num());
INC_DWORD_STAT_BY(STAT_Shaders_ShaderResourceMemory, GetSizeBytes());
INC_DWORD_STAT_BY(STAT_Shaders_NumShaderResourcesLoaded, 1);
}
FShaderResource::~FShaderResource()
{
DEC_DWORD_STAT_BY_FName(*GetMemoryStatType((EShaderFrequency)Target.Frequency), Code.Num());
DEC_DWORD_STAT_BY(STAT_Shaders_ShaderResourceMemory, GetSizeBytes());
DEC_DWORD_STAT_BY(STAT_Shaders_NumShaderResourcesLoaded, 1);
}
void FShaderResource::Register()
{
ShaderResourceIdMap.Add(GetId(), this);
}
void FShaderResource::Serialize(FArchive& Ar)
{
Ar << Target;
Ar << Code;
Ar << OutputHash;
Ar << NumInstructions;
Ar << NumTextureSamplers;
if (Ar.IsLoading())
{
INC_DWORD_STAT_BY_FName(*GetMemoryStatType((EShaderFrequency)Target.Frequency), (int64)Code.Num());
INC_DWORD_STAT_BY(STAT_Shaders_ShaderResourceMemory, GetSizeBytes());
}
}
void FShaderResource::AddRef()
{
++NumRefs;
}
void FShaderResource::Release()
{
check(NumRefs != 0);
if(--NumRefs == 0)
{
ShaderResourceIdMap.Remove(GetId());
// Send a release message to the rendering thread when the shader loses its last reference.
BeginReleaseResource(this);
BeginCleanup(this);
}
}
FShaderResource* FShaderResource::FindShaderResourceById(const FShaderResourceId& Id)
{
return ShaderResourceIdMap.FindRef(Id);
}
FShaderResource* FShaderResource::FindOrCreateShaderResource(const FShaderCompilerOutput& Output)
{
const FShaderResourceId ResourceId(Output);
FShaderResource* Resource = FindShaderResourceById(ResourceId);
if (!Resource)
{
Resource = new FShaderResource(Output);
}
return Resource;
}
void FShaderResource::GetAllShaderResourceId(TArray<FShaderResourceId>& Ids)
{
ShaderResourceIdMap.GetKeys(Ids);
}
void FShaderResource::FinishCleanup()
{
delete this;
}
void FShaderResource::InitRHI()
{
checkf(Code.Num() > 0, TEXT("FShaderResource::InitRHI was called with empty bytecode, which can happen if the resource is initialized multiple times on platforms with no editor data."));
// we can't have this called on the wrong platform's shaders
if (Target.Platform != GRHIShaderPlatform)
{
if (FPlatformProperties::RequiresCookedData())
{
UE_LOG(LogShaders, Fatal, TEXT("FShaderResource::InitRHI got platform %s but expected %s"),
*LegacyShaderPlatformToShaderFormat((EShaderPlatform)Target.Platform).ToString(), *LegacyShaderPlatformToShaderFormat(GRHIShaderPlatform).ToString() );
}
return;
}
INC_DWORD_STAT_BY(STAT_Shaders_NumShadersUsedForRendering, 1);
SCOPE_CYCLE_COUNTER(STAT_Shaders_RTShaderLoadTime);
if(Target.Frequency == SF_Vertex)
{
VertexShader = RHICreateVertexShader(Code);
}
else if(Target.Frequency == SF_Pixel)
{
PixelShader = RHICreatePixelShader(Code);
}
else if(Target.Frequency == SF_Hull)
{
HullShader = RHICreateHullShader(Code);
}
else if(Target.Frequency == SF_Domain)
{
DomainShader = RHICreateDomainShader(Code);
}
else if(Target.Frequency == SF_Geometry)
{
GeometryShader = RHICreateGeometryShader(Code);
}
else if(Target.Frequency == SF_Compute)
{
ComputeShader = RHICreateComputeShader(Code);
}
if (!FPlatformProperties::HasEditorOnlyData())
{
DEC_DWORD_STAT_BY_FName(*GetMemoryStatType((EShaderFrequency)Target.Frequency), Code.Num());
DEC_DWORD_STAT_BY(STAT_Shaders_ShaderResourceMemory, Code.GetAllocatedSize());
Code.Empty();
}
}
void FShaderResource::ReleaseRHI()
{
DEC_DWORD_STAT_BY(STAT_Shaders_NumShadersUsedForRendering, 1);
VertexShader.SafeRelease();
PixelShader.SafeRelease();
HullShader.SafeRelease();
DomainShader.SafeRelease();
GeometryShader.SafeRelease();
ComputeShader.SafeRelease();
}
void FShaderResource::InitializeVertexShaderRHI()
{
if (!IsInitialized())
{
STAT(double ShaderInitializationTime = 0);
{
SCOPE_CYCLE_COUNTER(STAT_Shaders_FrameRTShaderInitForRenderingTime);
SCOPE_SECONDS_COUNTER(ShaderInitializationTime);
InitResourceFromPossiblyParallelRendering();
}
INC_FLOAT_STAT_BY(STAT_Shaders_TotalRTShaderInitForRenderingTime,(float)ShaderInitializationTime);
}
checkSlow(IsInitialized());
}
void FShaderResource::InitializePixelShaderRHI()
{
if (!IsInitialized())
{
STAT(double ShaderInitializationTime = 0);
{
SCOPE_CYCLE_COUNTER(STAT_Shaders_FrameRTShaderInitForRenderingTime);
SCOPE_SECONDS_COUNTER(ShaderInitializationTime);
InitResourceFromPossiblyParallelRendering();
}
INC_FLOAT_STAT_BY(STAT_Shaders_TotalRTShaderInitForRenderingTime,(float)ShaderInitializationTime);
}
checkSlow(IsInitialized());
}
const FHullShaderRHIRef& FShaderResource::GetHullShader()
{
checkSlow(Target.Frequency == SF_Hull);
if (!IsInitialized())
{
STAT(double ShaderInitializationTime = 0);
{
SCOPE_CYCLE_COUNTER(STAT_Shaders_FrameRTShaderInitForRenderingTime);
SCOPE_SECONDS_COUNTER(ShaderInitializationTime);
InitResourceFromPossiblyParallelRendering();
}
INC_FLOAT_STAT_BY(STAT_Shaders_TotalRTShaderInitForRenderingTime,(float)ShaderInitializationTime);
}
checkSlow(IsInitialized());
return HullShader;
}
const FDomainShaderRHIRef& FShaderResource::GetDomainShader()
{
checkSlow(Target.Frequency == SF_Domain);
if (!IsInitialized())
{
STAT(double ShaderInitializationTime = 0);
{
SCOPE_CYCLE_COUNTER(STAT_Shaders_FrameRTShaderInitForRenderingTime);
SCOPE_SECONDS_COUNTER(ShaderInitializationTime);
InitResourceFromPossiblyParallelRendering();
}
INC_FLOAT_STAT_BY(STAT_Shaders_TotalRTShaderInitForRenderingTime,(float)ShaderInitializationTime);
}
checkSlow(IsInitialized());
return DomainShader;
}
const FGeometryShaderRHIRef& FShaderResource::GetGeometryShader()
{
checkSlow(Target.Frequency == SF_Geometry);
if (!IsInitialized())
{
STAT(double ShaderInitializationTime = 0);
{
SCOPE_CYCLE_COUNTER(STAT_Shaders_FrameRTShaderInitForRenderingTime);
SCOPE_SECONDS_COUNTER(ShaderInitializationTime);
InitResourceFromPossiblyParallelRendering();
}
INC_FLOAT_STAT_BY(STAT_Shaders_TotalRTShaderInitForRenderingTime,(float)ShaderInitializationTime);
}
checkSlow(IsInitialized());
return GeometryShader;
}
const FComputeShaderRHIRef& FShaderResource::GetComputeShader()
{
checkSlow(Target.Frequency == SF_Compute);
if (!IsInitialized())
{
STAT(double ShaderInitializationTime = 0);
{
SCOPE_CYCLE_COUNTER(STAT_Shaders_FrameRTShaderInitForRenderingTime);
SCOPE_SECONDS_COUNTER(ShaderInitializationTime);
InitResourceFromPossiblyParallelRendering();
}
INC_FLOAT_STAT_BY(STAT_Shaders_TotalRTShaderInitForRenderingTime,(float)ShaderInitializationTime);
}
checkSlow(IsInitialized());
return ComputeShader;
}
FShaderId::FShaderId(const FSHAHash& InMaterialShaderMapHash, FVertexFactoryType* InVertexFactoryType, FShaderType* InShaderType, FShaderTarget InTarget)
: MaterialShaderMapHash(InMaterialShaderMapHash)
, ShaderType(InShaderType)
, SourceHash(InShaderType->GetSourceHash())
, SerializationHistory(InShaderType->GetSerializationHistory())
, Target(InTarget)
{
if (InVertexFactoryType)
{
VFSerializationHistory = InVertexFactoryType->GetSerializationHistory((EShaderFrequency)InTarget.Frequency);
VertexFactoryType = InVertexFactoryType;
VFSourceHash = InVertexFactoryType->GetSourceHash();
}
else
{
VFSerializationHistory = NULL;
VertexFactoryType = NULL;
}
}
FSelfContainedShaderId::FSelfContainedShaderId() :
Target(FShaderTarget(SF_NumFrequencies, SP_NumPlatforms))
{}
FSelfContainedShaderId::FSelfContainedShaderId(const FShaderId& InShaderId)
{
MaterialShaderMapHash = InShaderId.MaterialShaderMapHash;
VertexFactoryTypeName = InShaderId.VertexFactoryType ? InShaderId.VertexFactoryType->GetName() : TEXT("");
VFSourceHash = InShaderId.VFSourceHash;
VFSerializationHistory = InShaderId.VFSerializationHistory ? *InShaderId.VFSerializationHistory : FSerializationHistory();
ShaderTypeName = InShaderId.ShaderType->GetName();
SourceHash = InShaderId.SourceHash;
SerializationHistory = InShaderId.SerializationHistory;
Target = InShaderId.Target;
}
bool FSelfContainedShaderId::IsValid()
{
FShaderType** TypePtr = FShaderType::GetNameToTypeMap().Find(FName(*ShaderTypeName));
if (TypePtr && SourceHash == (*TypePtr)->GetSourceHash() && SerializationHistory == (*TypePtr)->GetSerializationHistory())
{
FVertexFactoryType* VFTypePtr = FVertexFactoryType::GetVFByName(VertexFactoryTypeName);
if (VertexFactoryTypeName == TEXT("")
|| (VFTypePtr && VFSourceHash == VFTypePtr->GetSourceHash() && VFSerializationHistory == *VFTypePtr->GetSerializationHistory((EShaderFrequency)Target.Frequency)))
{
return true;
}
}
return false;
}
FArchive& operator<<(FArchive& Ar,class FSelfContainedShaderId& Ref)
{
Ar << Ref.MaterialShaderMapHash
<< Ref.VertexFactoryTypeName
<< Ref.VFSourceHash
<< Ref.VFSerializationHistory
<< Ref.ShaderTypeName
<< Ref.SourceHash
<< Ref.SerializationHistory
<< Ref.Target;
return Ar;
}
/**
* Used to construct a shader for deserialization.
* This still needs to initialize members to safe values since FShaderType::GenerateSerializationHistory uses this constructor.
*/
FShader::FShader() :
VFType(NULL),
Type(NULL),
NumRefs(0),
SetParametersId(0),
Canary(ShaderMagic_Uninitialized)
{
// set to undefined (currently shared with SF_Vertex)
Target.Frequency = 0;
Target.Platform = GRHIShaderPlatform;
}
/**
* Construct a shader from shader compiler output.
*/
FShader::FShader(const CompiledShaderInitializerType& Initializer):
MaterialShaderMapHash(Initializer.MaterialShaderMapHash),
VFType(Initializer.VertexFactoryType),
Type(Initializer.Type),
Target(Initializer.Target),
NumRefs(0),
SetParametersId(0),
Canary(ShaderMagic_Initialized)
{
OutputHash = Initializer.OutputHash;
checkSlow(OutputHash != FSHAHash());
if (Type)
{
// Store off the source hash that this shader was compiled with
// This will be used as part of the shader key in order to identify when shader files have been changed and a recompile is needed
SourceHash = Type->GetSourceHash();
}
if (VFType)
{
// Store off the VF source hash that this shader was compiled with
VFSourceHash = VFType->GetSourceHash();
}
// Bind uniform buffer parameters automatically
for (TLinkedList<FUniformBufferStruct*>::TIterator StructIt(FUniformBufferStruct::GetStructList()); StructIt; StructIt.Next())
{
if (Initializer.ParameterMap.ContainsParameterAllocation(StructIt->GetShaderVariableName()))
{
UniformBufferParameterStructs.Add(*StructIt);
UniformBufferParameters.Add(StructIt->ConstructTypedParameter());
FShaderUniformBufferParameter* Parameter = UniformBufferParameters.Last();
Parameter->Bind(Initializer.ParameterMap, StructIt->GetShaderVariableName(), SPF_Mandatory);
}
}
SetResource(Initializer.Resource);
// Register the shader now that it is valid, so that it can be reused
Register();
}
FShader::~FShader()
{
check(Canary == ShaderMagic_Uninitialized || Canary == ShaderMagic_Initialized);
check(NumRefs == 0);
Canary = 0;
for (int32 StructIndex = 0; StructIndex < UniformBufferParameters.Num(); StructIndex++)
{
delete UniformBufferParameters[StructIndex];
}
}
const FSHAHash& FShader::GetHash() const
{
return SourceHash;
}
bool FShader::SerializeBase(FArchive& Ar, bool bShadersInline)
{
Serialize(Ar);
Ar << OutputHash;
Ar << MaterialShaderMapHash;
Ar << VFType;
Ar << VFSourceHash;
Ar << Type;
Ar << SourceHash;
Ar << Target;
if (Ar.IsLoading())
{
int32 NumUniformParameters;
Ar << NumUniformParameters;
for (int32 ParameterIndex = 0; ParameterIndex < NumUniformParameters; ParameterIndex++)
{
FString StructName;
Ar << StructName;
FUniformBufferStruct* Struct = FindUniformBufferStructByName(*StructName);
FShaderUniformBufferParameter* Parameter = Struct ? Struct->ConstructTypedParameter() : new FShaderUniformBufferParameter();
Ar << *Parameter;
UniformBufferParameterStructs.Add(Struct);
UniformBufferParameters.Add(Parameter);
}
// The shader has been serialized in, so this shader is now initialized.
Canary = ShaderMagic_Initialized;
}
else
{
int32 NumUniformParameters = UniformBufferParameters.Num();
Ar << NumUniformParameters;
for (int32 StructIndex = 0; StructIndex < UniformBufferParameters.Num(); StructIndex++)
{
FString StructName(UniformBufferParameterStructs[StructIndex]->GetStructTypeName());
Ar << StructName;
Ar << *UniformBufferParameters[StructIndex];
}
}
if (bShadersInline)
{
// Save the shader resource if we are inlining shaders
if (Ar.IsSaving())
{
Resource->Serialize(Ar);
}
if (Ar.IsLoading())
{
// Load the inlined shader resource
FShaderResource* ShaderResource = new FShaderResource();
ShaderResource->Serialize(Ar);
FShaderResource* ExistingResource = FShaderResource::FindShaderResourceById(ShaderResource->GetId());
// Reuse an existing shader resource if a matching one already exists in memory
if (ExistingResource)
{
delete ShaderResource;
ShaderResource = ExistingResource;
}
else
{
// Register the newly loaded shader resource so it can be reused by other shaders
ShaderResource->Register();
}
SetResource(ShaderResource);
}
}
else
{
// if saving, there's nothing to, the required data is already saved above to look it up at load time
if (Ar.IsLoading())
{
// generate a resource id
FShaderResourceId ResourceId;
ResourceId.Target = Target;
ResourceId.OutputHash = OutputHash;
// use it to look up in the registered resource map
FShaderResource* ExistingResource = FShaderResource::FindShaderResourceById(ResourceId);
SetResource(ExistingResource);
}
}
return false;
}
void FShader::AddRef()
{
++NumRefs;
if (NumRefs == 1)
{
INC_DWORD_STAT_BY(STAT_Shaders_ShaderMemory, GetSizeBytes());
INC_DWORD_STAT_BY(STAT_Shaders_NumShadersLoaded,1);
}
}
void FShader::Release()
{
check(NumRefs != 0);
if(--NumRefs == 0)
{
DEC_DWORD_STAT_BY(STAT_Shaders_ShaderMemory, GetSizeBytes());
DEC_DWORD_STAT_BY(STAT_Shaders_NumShadersLoaded,1);
// Deregister the shader now to eliminate references to it by the type's ShaderIdMap
Deregister();
BeginCleanup(this);
}
}
void FShader::Register()
{
FShaderId ShaderId = GetId();
check(ShaderId.MaterialShaderMapHash != FSHAHash());
check(ShaderId.SourceHash != FSHAHash());
check(Resource);
Type->GetShaderIdMap().Add(ShaderId, this);
}
void FShader::Deregister()
{
Type->GetShaderIdMap().Remove(GetId());
}
FShaderId FShader::GetId() const
{
FShaderId ShaderId(Type->GetSerializationHistory());
ShaderId.MaterialShaderMapHash = MaterialShaderMapHash;
ShaderId.VertexFactoryType = VFType;
ShaderId.VFSourceHash = VFSourceHash;
ShaderId.VFSerializationHistory = VFType ? VFType->GetSerializationHistory((EShaderFrequency)GetTarget().Frequency) : NULL;
ShaderId.ShaderType = Type;
ShaderId.SourceHash = SourceHash;
ShaderId.Target = Target;
return ShaderId;
}
void FShader::SetResource(FShaderResource* InResource)
{
check(InResource && InResource->Target == Target);
Resource = InResource;
}
void FShader::FinishCleanup()
{
delete this;
}
void FShader::VerifyBoundUniformBufferParameters()
{
// Support being called on a NULL pointer
// TODO: doesn't work with uniform buffer parameters on helper structs like FDeferredPixelShaderParameters
if (0&&this)
{
for (int32 StructIndex = 0; StructIndex < UniformBufferParameters.Num(); StructIndex++)
{
const FShaderUniformBufferParameter& UniformParameter = *UniformBufferParameters[StructIndex];
if (UniformParameter.SetParametersId != SetParametersId)
{
// Log an error when a shader was used for rendering but did not have all of its uniform buffers set
// This can have false positives, for example when sharing state between draw calls with the same shader, the SetParametersId logic will break down
// Also if the uniform buffer is compiled into the shader but not actually used due to control flow, failing to set that parameter will cause this error
UE_LOG(LogShaders, Error, TEXT("Automatically bound uniform buffer parameter %s %s was not set before used for rendering in shader %s!"),
UniformBufferParameterStructs[StructIndex]->GetStructTypeName(),
UniformBufferParameterStructs[StructIndex]->GetShaderVariableName(),
GetType()->GetName());
}
}
SetParametersId++;
}
}
void DumpShaderStats( EShaderPlatform Platform, EShaderFrequency Frequency )
{
#if ALLOW_DEBUG_FILES
FDiagnosticTableViewer ShaderTypeViewer(*FDiagnosticTableViewer::GetUniqueTemporaryFilePath(TEXT("ShaderStats")));
// Iterate over all shader types and log stats.
int32 TotalShaderCount = 0;
int32 TotalTypeCount = 0;
int32 TotalInstructionCount = 0;
int32 TotalSize = 0;
float TotalSizePerType = 0;
// Write a row of headings for the table's columns.
ShaderTypeViewer.AddColumn(TEXT("Type"));
ShaderTypeViewer.AddColumn(TEXT("Instances"));
ShaderTypeViewer.AddColumn(TEXT("Average instructions"));
ShaderTypeViewer.AddColumn(TEXT("Size"));
ShaderTypeViewer.AddColumn(TEXT("AvgSizePerInstance"));
ShaderTypeViewer.CycleRow();
for( TLinkedList<FShaderType*>::TIterator It(FShaderType::GetTypeList()); It; It.Next() )
{
const FShaderType* Type = *It;
if(Type->GetNumShaders())
{
// Calculate the average instruction count and total size of instances of this shader type.
float AverageNumInstructions = 0.0f;
int32 NumInitializedInstructions = 0;
int32 Size = 0;
int32 NumShaders = 0;
for(TMap<FShaderId,FShader*>::TConstIterator ShaderIt(Type->ShaderIdMap);ShaderIt;++ShaderIt)
{
const FShader* Shader = ShaderIt.Value();
// Skip shaders that don't match frequency.
if( Shader->GetTarget().Frequency != Frequency && Frequency != SF_NumFrequencies )
{
continue;
}
// Skip shaders that don't match platform.
if( Shader->GetTarget().Platform != Platform && Platform != SP_NumPlatforms )
{
continue;
}
NumInitializedInstructions += Shader->GetNumInstructions();
Size += Shader->GetCode().Num();
NumShaders++;
}
AverageNumInstructions = (float)NumInitializedInstructions / (float)Type->GetNumShaders();
// Only add rows if there is a matching shader.
if( NumShaders )
{
// Write a row for the shader type.
ShaderTypeViewer.AddColumn(Type->GetName());
ShaderTypeViewer.AddColumn(TEXT("%u"),NumShaders);
ShaderTypeViewer.AddColumn(TEXT("%.1f"),AverageNumInstructions);
ShaderTypeViewer.AddColumn(TEXT("%u"),Size);
ShaderTypeViewer.AddColumn(TEXT("%.1f"),Size / (float)NumShaders);
ShaderTypeViewer.CycleRow();
TotalShaderCount += NumShaders;
TotalInstructionCount += NumInitializedInstructions;
TotalTypeCount++;
TotalSize += Size;
TotalSizePerType += Size / (float)NumShaders;
}
}
}
// Write a total row.
ShaderTypeViewer.AddColumn(TEXT("Total"));
ShaderTypeViewer.AddColumn(TEXT("%u"),TotalShaderCount);
ShaderTypeViewer.AddColumn(TEXT("%u"),TotalInstructionCount);
ShaderTypeViewer.AddColumn(TEXT("%u"),TotalSize);
ShaderTypeViewer.AddColumn(TEXT("0"));
ShaderTypeViewer.CycleRow();
// Write an average row.
ShaderTypeViewer.AddColumn(TEXT("Average"));
ShaderTypeViewer.AddColumn(TEXT("%.1f"),TotalShaderCount / (float)TotalTypeCount);
ShaderTypeViewer.AddColumn(TEXT("%.1f"),(float)TotalInstructionCount / TotalShaderCount);
ShaderTypeViewer.AddColumn(TEXT("%.1f"),TotalSize / (float)TotalShaderCount);
ShaderTypeViewer.AddColumn(TEXT("%.1f"),TotalSizePerType / TotalTypeCount);
ShaderTypeViewer.CycleRow();
#endif
}
FShaderType* FindShaderTypeByName(const TCHAR* ShaderTypeName)
{
for(TLinkedList<FShaderType*>::TIterator ShaderTypeIt(FShaderType::GetTypeList());ShaderTypeIt;ShaderTypeIt.Next())
{
if(!FCString::Stricmp(ShaderTypeIt->GetName(),ShaderTypeName))
{
return *ShaderTypeIt;
}
}
return NULL;
}
void DispatchComputeShader(
FRHICommandList& RHICmdList,
FShader* Shader,
uint32 ThreadGroupCountX,
uint32 ThreadGroupCountY,
uint32 ThreadGroupCountZ)
{
Shader->VerifyBoundUniformBufferParameters();
RHICmdList.DispatchComputeShader(ThreadGroupCountX, ThreadGroupCountY, ThreadGroupCountZ);
}
void DispatchIndirectComputeShader(
FRHICommandList& RHICmdList,
FShader* Shader,
FVertexBufferRHIParamRef ArgumentBuffer,
uint32 ArgumentOffset)
{
Shader->VerifyBoundUniformBufferParameters();
RHICmdList.DispatchIndirectComputeShader(ArgumentBuffer, ArgumentOffset);
}
const TArray<FName>& GetTargetShaderFormats()
{
static bool bInit = false;
static TArray<FName> Results;
#if WITH_ENGINE
if (!bInit)
{
bInit = true;
ITargetPlatformManagerModule* TPM = GetTargetPlatformManager();
if (!TPM || TPM->RestrictFormatsToRuntimeOnly())
{
// for now a runtime format and a cook format are very different, we don't put any formats here
}
else
{
const TArray<ITargetPlatform*>& Platforms = TPM->GetActiveTargetPlatforms();
for (int32 Index = 0; Index < Platforms.Num(); Index++)
{
Platforms[Index]->GetAllTargetedShaderFormats(Results);
}
}
}
#endif // WITH_ENGINE
return Results;
}
FName GetRuntimeShaderFormat()
{
static FName RuntimeFormat = LegacyShaderPlatformToShaderFormat(GRHIShaderPlatform);
return RuntimeFormat;
}
void ShaderMapAppendKeyString(EShaderPlatform Platform, FString& KeyString)
{
// Globals that should cause all shaders to recompile when changed must be appended to the key here
// Key should be kept as short as possible while being somewhat human readable for debugging
{
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("Compat.UseDXT5NormalMaps"));
KeyString += (CVar && CVar->GetValueOnGameThread() != 0) ? TEXT("_DXTN") : TEXT("_BC5N");
}
{
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.UseDiffuseSpecularMaterialInputs"));
KeyString += (CVar && CVar->GetValueOnGameThread() != 0) ? TEXT("_DSIN") : TEXT("_BMSIN");
}
{
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.CompileShadersForDevelopment"));
KeyString += (CVar && CVar->GetValueOnGameThread() != 0) ? TEXT("_DEV") : TEXT("_NoDEV");
}
{
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.AllowStaticLighting"));
const bool bValue = CVar ? CVar->GetValueOnGameThread() != 0 : true;
KeyString += bValue ? TEXT("_SL") : TEXT("_NoSL");
}
{
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.GBuffer"));
if(CVar ? CVar->GetValueOnGameThread() == 0 : false)
{
KeyString += TEXT("_NoGB");
}
}
{
static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.DBuffer"));
KeyString += (CVar && CVar->GetInt() != 0) ? TEXT("_DBuf") : TEXT("_NoDBuf");
}
if( Platform == SP_PS4 )
{
{
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.PS4MixedModeShaderDebugInfo"));
if( CVar && CVar->GetValueOnGameThread() != 0 )
{
KeyString += TEXT("_MMDBG");
}
}
{
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.PS4DumpShaderSDB"));
if( CVar && CVar->GetValueOnGameThread() != 0 )
{
KeyString += TEXT("_SDB");
}
}
}
}