You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
1154 lines
32 KiB
C++
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");
|
|
}
|
|
}
|
|
}
|
|
}
|