Files
UnrealEngineUWP/Engine/Source/Runtime/ShaderCore/Private/ShaderCache.cpp

1414 lines
44 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ShaderCache.cpp: Bound shader state cache implementation.
=============================================================================*/
#include "ShaderCore.h"
#include "ShaderCache.h"
#include "Shader.h"
#include "RHI.h"
#include "RenderingThread.h"
const FGuid FShaderCacheCustomVersion::Key(0xB954F018, 0xC9624DD6, 0xA74E79B1, 0x8EA113C2);
const FGuid FShaderCacheCustomVersion::GameKey(0x03D4EB48, 0xB50B4CC3, 0xA598DE41, 0x5C6CC993);
FCustomVersionRegistration GRegisterShaderCacheVersion(FShaderCacheCustomVersion::Key, FShaderCacheCustomVersion::Latest, TEXT("ShaderCacheVersion"));
static TCHAR const* GShaderCacheFileName = TEXT("ShaderCache.ushadercache");
// Only the cooked Mac build defaults to using the shader cache for now, Editor is too likely to invalidate shader keys leading to ever growing cache
int32 FShaderCache::bUseShaderCaching = (PLATFORM_MAC && !WITH_EDITOR) ? 1 : 0;
FAutoConsoleVariableRef FShaderCache::CVarUseShaderCaching(
TEXT("r.UseShaderCaching"),
bUseShaderCaching,
TEXT("If true, log all shaders & bound-shader-states, so that they may be instantiated in the RHI on deserialisation rather than waiting for first use."),
ECVF_ReadOnly|ECVF_RenderThreadSafe
);
// Predrawing takes an existing shader cache with draw log & renders each shader + draw-state combination before use to avoid in-driver recompilation
// This requires plenty of setup & is done in batches at frame-end.
int32 FShaderCache::bUseShaderPredraw = (PLATFORM_MAC && !WITH_EDITOR) ? 1 : 0;
FAutoConsoleVariableRef FShaderCache::CVarUseShaderPredraw(
TEXT("r.UseShaderPredraw"),
bUseShaderPredraw,
TEXT("Use an existing draw-log to predraw shaders in batches before being used to reduce hitches due to in-driver recompilation."),
ECVF_ReadOnly|ECVF_RenderThreadSafe
);
// The actual draw loggging is even more expensive as it has to cache all the RHI draw state & is disabled by default.
int32 FShaderCache::bUseShaderDrawLog = 0;
FAutoConsoleVariableRef FShaderCache::CVarUseShaderDrawLog(
TEXT("r.UseShaderDrawLog"),
bUseShaderDrawLog,
TEXT("If true, log all the draw states used for each shader pipeline, so that they may be pre-drawn in batches (see: r.UseShaderPredraw). This can be expensive & should be used only when generating the shader cache."),
ECVF_ReadOnly|ECVF_RenderThreadSafe
);
// As predrawing can take significant time then batch the draws up into chunks defined by frame time
int32 FShaderCache::PredrawBatchTime = -1;
FAutoConsoleVariableRef FShaderCache::CVarPredrawBatchTime(
TEXT("r.PredrawBatchTime"),
PredrawBatchTime,
TEXT("Time in ms to spend predrawing shaders each frame, or -1 to perform all predraws immediately."),
ECVF_RenderThreadSafe
);
FShaderCache* FShaderCache::Cache = nullptr;
int32 FShaderCache::GameVersion = 0;
static bool ShaderPlatformCanPrebindBoundShaderState(EShaderPlatform Platform)
{
switch (Platform)
{
case SP_PCD3D_SM5:
case SP_PS4:
case SP_XBOXONE:
case SP_PCD3D_SM4:
case SP_PCD3D_ES2:
case SP_METAL:
case SP_OPENGL_SM4_MAC:
case SP_METAL_MRT:
{
return true;
}
case SP_OPENGL_SM4:
case SP_OPENGL_PCES2:
case SP_OPENGL_SM5:
case SP_OPENGL_ES2:
case SP_OPENGL_ES2_WEBGL:
case SP_OPENGL_ES2_IOS:
case SP_OPENGL_ES31_EXT:
default:
{
return false;
}
}
}
void FShaderCache::SetGameVersion(int32 InGameVersion)
{
check(!Cache);
GameVersion = InGameVersion;
}
void FShaderCache::InitShaderCache()
{
check(!Cache);
if(bUseShaderCaching)
{
Cache = new FShaderCache;
}
}
void FShaderCache::ShutdownShaderCache()
{
if (Cache)
{
delete Cache;
Cache = nullptr;
}
}
FArchive& operator<<( FArchive& Ar, FShaderCache& Info )
{
Ar.UsingCustomVersion(FShaderCacheCustomVersion::Key);
Ar.SetCustomVersion(FShaderCacheCustomVersion::GameKey, FShaderCache::GameVersion, TEXT("ShaderCacheGameVersion"));
FCustomVersionContainer Vers = Ar.GetCustomVersions();
Vers.Serialize(Ar);
if ( Ar.IsLoading() )
{
Ar.SetCustomVersions(Vers);
}
if ( !Ar.IsError() && Ar.CustomVer(FShaderCacheCustomVersion::Key) == FShaderCacheCustomVersion::Latest && Ar.CustomVer(FShaderCacheCustomVersion::GameKey) == FShaderCache::GameVersion )
{
Ar << Info.Caches;
}
return Ar;
}
FShaderCache::FShaderCache()
: StreamingKey(0)
, bCurrentDepthStencilTarget(false)
, CurrentNumRenderTargets(0)
, CurrentShaderState(nullptr)
, bIsPreDraw(false)
{
Viewport[0] = Viewport[1] = Viewport[2] = Viewport[3] = 0;
DepthRange[0] = DepthRange[1] = 0.0f;
FString UserBinaryShaderFile = FPaths::GameSavedDir() / GShaderCacheFileName;
FString GameBinaryShaderFile = FPaths::GameContentDir() / GShaderCacheFileName;
// Try to load user cache, making sure that if we fail version test we still try game-content version.
bool bLoadedUserCache = false;
if ( IFileManager::Get().FileSize(*UserBinaryShaderFile) > 0 )
{
FArchive* BinaryShaderAr = IFileManager::Get().CreateFileReader(*UserBinaryShaderFile);
if ( BinaryShaderAr != nullptr )
{
*BinaryShaderAr << *this;
if ( !BinaryShaderAr->IsError() && BinaryShaderAr->CustomVer(FShaderCacheCustomVersion::Key) == FShaderCacheCustomVersion::Latest && BinaryShaderAr->CustomVer(FShaderCacheCustomVersion::GameKey) == FShaderCache::GameVersion )
{
bLoadedUserCache = true;
}
delete BinaryShaderAr;
}
}
// Fallback to game-content version.
if ( !bLoadedUserCache && IFileManager::Get().FileSize(*GameBinaryShaderFile) > 0 )
{
FArchive* BinaryShaderAr = IFileManager::Get().CreateFileReader(*GameBinaryShaderFile);
if ( BinaryShaderAr != nullptr )
{
*BinaryShaderAr << *this;
delete BinaryShaderAr;
}
}
}
FShaderCache::~FShaderCache()
{
FString BinaryShaderFile = FPaths::GameSavedDir() / GShaderCacheFileName;
FArchive* BinaryShaderAr = IFileManager::Get().CreateFileWriter(*BinaryShaderFile);
if( BinaryShaderAr != NULL )
{
*BinaryShaderAr << *this;
delete BinaryShaderAr;
}
}
FVertexShaderRHIRef FShaderCache::GetVertexShader(EShaderPlatform Platform, FSHAHash Hash, TArray<uint8> const& Code)
{
FShaderCacheKey Key;
Key.Platform = Platform;
Key.Frequency = SF_Vertex;
Key.SHAHash = Hash;
Key.bActive = true;
FVertexShaderRHIRef Shader = CachedVertexShaders.FindRef(Key);
if(!IsValidRef(Shader))
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(Platform);
PlatformCache.Shaders.Add(Key);
Shader = RHICreateVertexShader(Code);
check(IsValidRef(Shader));
Shader->SetHash(Hash);
CachedVertexShaders.Add(Key, Shader);
PrebindShader(Key);
}
return Shader;
}
FPixelShaderRHIRef FShaderCache::GetPixelShader(EShaderPlatform Platform, FSHAHash Hash, TArray<uint8> const& Code)
{
FShaderCacheKey Key;
Key.Platform = Platform;
Key.Frequency = SF_Pixel;
Key.SHAHash = Hash;
Key.bActive = true;
FPixelShaderRHIRef Shader = CachedPixelShaders.FindRef(Key);
if(!IsValidRef(Shader))
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(Platform);
PlatformCache.Shaders.Add(Key);
Shader = RHICreatePixelShader(Code);
check(IsValidRef(Shader));
Shader->SetHash(Hash);
CachedPixelShaders.Add(Key, Shader);
PrebindShader(Key);
}
return Shader;
}
FGeometryShaderRHIRef FShaderCache::GetGeometryShader(EShaderPlatform Platform, FSHAHash Hash, TArray<uint8> const& Code)
{
FShaderCacheKey Key;
Key.Platform = Platform;
Key.Frequency = SF_Geometry;
Key.SHAHash = Hash;
Key.bActive = true;
FGeometryShaderRHIRef Shader = CachedGeometryShaders.FindRef(Key);
if(!IsValidRef(Shader))
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(Platform);
PlatformCache.Shaders.Add(Key);
Shader = RHICreateGeometryShader(Code);
check(IsValidRef(Shader));
Shader->SetHash(Hash);
CachedGeometryShaders.Add(Key, Shader);
PrebindShader(Key);
}
return Shader;
}
FHullShaderRHIRef FShaderCache::GetHullShader(EShaderPlatform Platform, FSHAHash Hash, TArray<uint8> const& Code)
{
FShaderCacheKey Key;
Key.Platform = Platform;
Key.Frequency = SF_Hull;
Key.SHAHash = Hash;
Key.bActive = true;
FHullShaderRHIRef Shader = CachedHullShaders.FindRef(Key);
if(!IsValidRef(Shader))
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(Platform);
PlatformCache.Shaders.Add(Key);
Shader = RHICreateHullShader(Code);
check(IsValidRef(Shader));
Shader->SetHash(Hash);
CachedHullShaders.Add(Key, Shader);
PrebindShader(Key);
}
return Shader;
}
FDomainShaderRHIRef FShaderCache::GetDomainShader(EShaderPlatform Platform, FSHAHash Hash, TArray<uint8> const& Code)
{
FShaderCacheKey Key;
Key.Platform = Platform;
Key.Frequency = SF_Domain;
Key.SHAHash = Hash;
Key.bActive = true;
FDomainShaderRHIRef Shader = CachedDomainShaders.FindRef(Key);
if(!IsValidRef(Shader))
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(Platform);
PlatformCache.Shaders.Add(Key);
Shader = RHICreateDomainShader(Code);
check(IsValidRef(Shader));
Shader->SetHash(Hash);
CachedDomainShaders.Add(Key, Shader);
PrebindShader(Key);
}
return Shader;
}
FComputeShaderRHIRef FShaderCache::GetComputeShader(EShaderPlatform Platform, TArray<uint8> const& Code)
{
FShaderCacheKey Key;
Key.Platform = Platform;
Key.Frequency = SF_Compute;
// @todo WARNING: RHI is responsible for hashing Compute shaders due to the way OpenGLDrv implements compute!
FSHA1::HashBuffer(Code.GetData(), Code.Num(), Key.SHAHash.Hash);
Key.bActive = true;
FComputeShaderRHIRef Shader = CachedComputeShaders.FindRef(Key);
if(!IsValidRef(Shader))
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(Platform);
PlatformCache.Shaders.Add(Key);
Shader = RHICreateComputeShader(Code);
check(IsValidRef(Shader));
CachedComputeShaders.Add(Key, Shader);
PrebindShader(Key);
}
return Shader;
}
void FShaderCache::InternalLogStreamingKey(uint32 StreamKey, bool const bActive)
{
// Defer to the render thread to avoid race conditions
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
FShaderCacheInternalLogLevel,
FShaderCache*,Cache,Cache,
uint32,StreamKey,StreamKey,
bool,bActive,bActive,
{
if(bActive)
{
Cache->ActiveStreamingKeys.Add(StreamKey);
}
else
{
Cache->ActiveStreamingKeys.Remove(StreamKey);
}
uint32 NewStreamingKey = 0;
for(uint32 Key : Cache->ActiveStreamingKeys)
{
NewStreamingKey ^= Key;
}
Cache->StreamingKey = NewStreamingKey;
if(!Cache->ShadersToDraw.Contains(NewStreamingKey))
{
FShaderPlatformCache& PlatformCache = Cache->Caches.FindOrAdd(GMaxRHIShaderPlatform);
Cache->ShadersToDraw.Add(NewStreamingKey, PlatformCache.StreamingDrawStates.FindRef(NewStreamingKey));
}
});
}
void FShaderCache::InternalLogShader(EShaderPlatform Platform, EShaderFrequency Frequency, FSHAHash Hash, TArray<uint8> const& Code)
{
bool bUsable = FShaderResource::ArePlatformsCompatible(GMaxRHIShaderPlatform, Platform);
switch (Frequency)
{
case SF_Geometry:
bUsable &= RHISupportsGeometryShaders(GMaxRHIShaderPlatform);
break;
case SF_Hull:
case SF_Domain:
bUsable &= RHISupportsTessellation(GMaxRHIShaderPlatform);
break;
case SF_Compute:
bUsable &= RHISupportsComputeShaders(GMaxRHIShaderPlatform);
break;
default:
break;
}
if ( bUsable )
{
FShaderCacheKey Key;
Key.SHAHash = Hash;
Key.Platform = Platform;
Key.Frequency = Frequency;
Key.bActive = true;
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(LogShader, FShaderCacheKey,Key,Key, TArray<uint8>,Code,Code, {
FShaderCache::GetShaderCache()->SubmitShader(Key, Code);
});
}
}
void FShaderCache::InternalLogVertexDeclaration(const FVertexDeclarationElementList& VertexElements, FVertexDeclarationRHIParamRef VertexDeclaration)
{
VertexDeclarations.Add(VertexDeclaration, VertexElements);
}
void FShaderCache::InternalLogBoundShaderState(EShaderPlatform Platform, FVertexDeclarationRHIParamRef VertexDeclaration,
FVertexShaderRHIParamRef VertexShader,
FPixelShaderRHIParamRef PixelShader,
FHullShaderRHIParamRef HullShader,
FDomainShaderRHIParamRef DomainShader,
FGeometryShaderRHIParamRef GeometryShader,
FBoundShaderStateRHIParamRef BoundState)
{
FShaderCacheBoundState Info;
if(VertexDeclaration)
{
Info.VertexDeclaration = VertexDeclarations.FindChecked(VertexDeclaration);
}
if(VertexShader)
{
Info.VertexShader.Platform = Platform;
Info.VertexShader.Frequency = SF_Vertex;
Info.VertexShader.SHAHash = VertexShader->GetHash();
Info.VertexShader.bActive = true;
}
if(PixelShader)
{
Info.PixelShader.Platform = Platform;
Info.PixelShader.Frequency = SF_Pixel;
Info.PixelShader.SHAHash = PixelShader->GetHash();
Info.PixelShader.bActive = true;
}
if(GeometryShader)
{
Info.GeometryShader.Platform = Platform;
Info.GeometryShader.Frequency = SF_Geometry;
Info.GeometryShader.SHAHash = GeometryShader->GetHash();
Info.GeometryShader.bActive = true;
}
if(HullShader)
{
Info.HullShader.Platform = Platform;
Info.HullShader.Frequency = SF_Hull;
Info.HullShader.SHAHash = HullShader->GetHash();
Info.HullShader.bActive = true;
}
if(DomainShader)
{
Info.DomainShader.Platform = Platform;
Info.DomainShader.Frequency = SF_Domain;
Info.DomainShader.SHAHash = DomainShader->GetHash();
Info.DomainShader.bActive = true;
}
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(Platform);
PlatformCache.BoundShaderStates.Add(Info);
if(VertexShader)
{
TSet<FShaderCacheBoundState>& Set = PlatformCache.ShaderStateMembership.FindOrAdd(Info.VertexShader);
if(!Set.Find(Info))
{
Set.Add(Info);
}
}
if(PixelShader)
{
TSet<FShaderCacheBoundState>& Set = PlatformCache.ShaderStateMembership.FindOrAdd(Info.PixelShader);
if(!Set.Find(Info))
{
Set.Add(Info);
}
}
if(GeometryShader)
{
TSet<FShaderCacheBoundState>& Set = PlatformCache.ShaderStateMembership.FindOrAdd(Info.GeometryShader);
if(!Set.Find(Info))
{
Set.Add(Info);
}
}
if(HullShader)
{
TSet<FShaderCacheBoundState>& Set = PlatformCache.ShaderStateMembership.FindOrAdd(Info.HullShader);
if(!Set.Find(Info))
{
Set.Add(Info);
}
}
if(DomainShader)
{
TSet<FShaderCacheBoundState>& Set = PlatformCache.ShaderStateMembership.FindOrAdd(Info.DomainShader);
if(!Set.Find(Info))
{
Set.Add(Info);
}
}
if ( bUseShaderPredraw || bUseShaderDrawLog )
{
ShaderStates.Add(BoundState, Info);
}
}
void FShaderCache::InternalLogBlendState(FBlendStateInitializerRHI const& Init, FBlendStateRHIParamRef State)
{
if ( bUseShaderPredraw || bUseShaderDrawLog )
{
BlendStates.Add(State, Init);
}
}
void FShaderCache::InternalLogRasterizerState(FRasterizerStateInitializerRHI const& Init, FRasterizerStateRHIParamRef State)
{
if ( bUseShaderPredraw || bUseShaderDrawLog )
{
RasterizerStates.Add(State, Init);
}
}
void FShaderCache::InternalLogDepthStencilState(FDepthStencilStateInitializerRHI const& Init, FDepthStencilStateRHIParamRef State)
{
if ( bUseShaderPredraw || bUseShaderDrawLog )
{
DepthStencilStates.Add(State, Init);
}
}
void FShaderCache::InternalLogSamplerState(FSamplerStateInitializerRHI const& Init, FSamplerStateRHIParamRef State)
{
if ( bUseShaderDrawLog )
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(GMaxRHIShaderPlatform);
FSetElementId ID = PlatformCache.SamplerStates.Add(Init);
SamplerStates.Add(State, ID.AsInteger());
}
}
void FShaderCache::InternalLogTexture(FShaderTextureKey const& Init, FTextureRHIParamRef State)
{
if ( bUseShaderPredraw || bUseShaderDrawLog )
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(GMaxRHIShaderPlatform);
FShaderResourceKey Key;
Key.Tex = Init;
Key.Format = Init.Format;
FSetElementId ID = PlatformCache.Resources.Add(Key);
Textures.Add(State, ID.AsInteger());
CachedTextures.Add(Init, State);
}
}
void FShaderCache::InternalLogSRV(FShaderResourceViewRHIParamRef SRV, FTextureRHIParamRef Texture, uint8 StartMip, uint8 NumMips, uint8 Format)
{
if ( bUseShaderPredraw || bUseShaderDrawLog )
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(GMaxRHIShaderPlatform);
FSetElementId ID = FSetElementId::FromInteger(Textures.FindChecked(Texture));
FShaderResourceKey TexKey = PlatformCache.Resources[ID];
FShaderResourceKey Key;
Key.Tex = TexKey.Tex;
Key.BaseMip = StartMip;
Key.MipLevels = NumMips;
Key.Format = Format;
Key.bSRV = true;
SRVs.Add(SRV, Key);
CachedSRVs.Add(Key, FShaderResourceViewBinding(SRV, nullptr, Texture));
PlatformCache.Resources.Add(Key);
}
}
void FShaderCache::InternalLogSRV(FShaderResourceViewRHIParamRef SRV, FVertexBufferRHIParamRef Vb, uint32 Stride, uint8 Format)
{
if ( bUseShaderPredraw || bUseShaderDrawLog )
{
FShaderResourceKey Key;
Key.Tex.Type = SCTT_Buffer;
Key.Tex.X = Vb->GetSize();
Key.Tex.Y = Vb->GetUsage();
Key.Tex.Z = Stride;
Key.Tex.Format = Format;
Key.bSRV = true;
SRVs.Add(SRV, Key);
CachedSRVs.Add(Key, FShaderResourceViewBinding(SRV, Vb, nullptr));
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(GMaxRHIShaderPlatform);
PlatformCache.Resources.Add(Key);
}
}
void FShaderCache::InternalRemoveSRV(FShaderResourceViewRHIParamRef SRV)
{
if ( bUseShaderPredraw || bUseShaderDrawLog )
{
auto Key = SRVs.FindRef(SRV);
CachedSRVs.Remove(Key);
SRVs.Remove(SRV);
}
}
void FShaderCache::InternalRemoveTexture(FTextureRHIParamRef Texture)
{
if ( bUseShaderPredraw || bUseShaderDrawLog )
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(GMaxRHIShaderPlatform);
FSetElementId ID = FSetElementId::FromInteger(Textures.FindChecked(Texture));
FShaderResourceKey TexKey = PlatformCache.Resources[ID];
CachedTextures.Remove(TexKey.Tex);
Textures.Remove(Texture);
}
}
void FShaderCache::InternalSetBlendState(FBlendStateRHIParamRef State)
{
if ( (bUseShaderPredraw || bUseShaderDrawLog) && !bIsPreDraw )
{
CurrentDrawKey.BlendState = BlendStates.FindChecked(State);
CurrentDrawKey.Hash = 0;
}
}
void FShaderCache::InternalSetRasterizerState(FRasterizerStateRHIParamRef State)
{
if ( (bUseShaderPredraw || bUseShaderDrawLog) && !bIsPreDraw )
{
CurrentDrawKey.RasterizerState = RasterizerStates.FindChecked(State);
CurrentDrawKey.Hash = 0;
}
}
void FShaderCache::InternalSetDepthStencilState(FDepthStencilStateRHIParamRef State)
{
if ( (bUseShaderPredraw || bUseShaderDrawLog) && !bIsPreDraw )
{
CurrentDrawKey.DepthStencilState = DepthStencilStates.FindChecked(State);
CurrentDrawKey.Hash = 0;
}
}
void FShaderCache::InternalSetRenderTargets( uint32 NumSimultaneousRenderTargets, const FRHIRenderTargetView* NewRenderTargetsRHI, const FRHIDepthRenderTargetView* NewDepthStencilTargetRHI )
{
if ( bUseShaderDrawLog && !bIsPreDraw )
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(GMaxRHIShaderPlatform);
CurrentNumRenderTargets = NumSimultaneousRenderTargets;
bCurrentDepthStencilTarget = (NewDepthStencilTargetRHI != nullptr);
FMemory::Memzero(CurrentRenderTargets, sizeof(FRHIRenderTargetView) * MaxSimultaneousRenderTargets);
FMemory::Memcpy(CurrentRenderTargets, NewRenderTargetsRHI, sizeof(FRHIRenderTargetView) * NumSimultaneousRenderTargets);
if( NewDepthStencilTargetRHI )
{
CurrentDepthStencilTarget = *NewDepthStencilTargetRHI;
}
FMemory::Memset(CurrentDrawKey.RenderTargets, 255, sizeof(uint32) * MaxSimultaneousRenderTargets);
for( int32 RenderTargetIndex = NumSimultaneousRenderTargets - 1; RenderTargetIndex >= 0; --RenderTargetIndex )
{
FRHIRenderTargetView const& Target = NewRenderTargetsRHI[RenderTargetIndex];
if ( Target.Texture )
{
FShaderRenderTargetKey Key;
FSetElementId ID = FSetElementId::FromInteger(Textures.FindChecked(Target.Texture));
FShaderResourceKey TexKey = PlatformCache.Resources[ID];
Key.Texture = TexKey.Tex;
check(Key.Texture.MipLevels == Target.Texture->GetNumMips());
Key.MipLevel = Key.Texture.MipLevels > Target.MipIndex ? Target.MipIndex : 0;
Key.ArrayIndex = Target.ArraySliceIndex;
CurrentDrawKey.RenderTargets[RenderTargetIndex] = PlatformCache.RenderTargets.Add(Key).AsInteger();
}
else
{
CurrentDrawKey.RenderTargets[RenderTargetIndex] = FShaderDrawKey::NullState;
}
}
if ( NewDepthStencilTargetRHI && NewDepthStencilTargetRHI->Texture )
{
FShaderRenderTargetKey Key;
FSetElementId ID = FSetElementId::FromInteger(Textures.FindChecked(NewDepthStencilTargetRHI->Texture));
FShaderResourceKey TexKey = PlatformCache.Resources[ID];
Key.Texture = TexKey.Tex;
CurrentDrawKey.DepthStencilTarget = PlatformCache.RenderTargets.Add(Key).AsInteger();
}
else
{
CurrentDrawKey.DepthStencilTarget = FShaderDrawKey::NullState;
}
CurrentDrawKey.Hash = 0;
}
}
void FShaderCache::InternalSetSamplerState(EShaderFrequency Frequency, uint32 Index, FSamplerStateRHIParamRef State)
{
if ( bUseShaderDrawLog && !bIsPreDraw )
{
check(Index < GetFeatureLevelMaxTextureSamplers(GMaxRHIFeatureLevel));
if ( State )
{
CurrentDrawKey.SamplerStates[Frequency][Index] = SamplerStates.FindChecked(State);
}
else
{
CurrentDrawKey.SamplerStates[Frequency][Index] = FShaderDrawKey::NullState;
}
CurrentDrawKey.Hash = 0;
}
}
void FShaderCache::InternalSetTexture(EShaderFrequency Frequency, uint32 Index, FTextureRHIParamRef State)
{
if ( bUseShaderDrawLog && !bIsPreDraw )
{
check(Index < GetFeatureLevelMaxTextureSamplers(GMaxRHIFeatureLevel));
if ( State )
{
FShaderResourceKey Key;
FTextureRHIParamRef Tex = State;
if ( State->GetTextureReference() )
{
Tex = State->GetTextureReference()->GetReferencedTexture();
}
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(GMaxRHIShaderPlatform);
FSetElementId ID = FSetElementId::FromInteger(Textures.FindChecked(Tex));
CurrentDrawKey.Resources[Frequency][Index] = ID.AsInteger();
}
else
{
CurrentDrawKey.Resources[Frequency][Index] = FShaderDrawKey::NullState;
}
CurrentDrawKey.Hash = 0;
}
}
void FShaderCache::InternalSetSRV(EShaderFrequency Frequency, uint32 Index, FShaderResourceViewRHIParamRef SRV)
{
if ( bUseShaderDrawLog && !bIsPreDraw )
{
check(Index < GetFeatureLevelMaxTextureSamplers(GMaxRHIFeatureLevel));
if ( SRV )
{
FShaderResourceKey Key = SRVs.FindChecked(SRV);
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(GMaxRHIShaderPlatform);
CurrentDrawKey.Resources[Frequency][Index] = PlatformCache.Resources.Add(Key).AsInteger();
}
else
{
CurrentDrawKey.Resources[Frequency][Index] = FShaderDrawKey::NullState;
}
CurrentDrawKey.Hash = 0;
}
}
void FShaderCache::InternalSetBoundShaderState(FBoundShaderStateRHIParamRef State)
{
if ( (bUseShaderPredraw || bUseShaderDrawLog) && !bIsPreDraw )
{
FMemory::Memset(CurrentDrawKey.SamplerStates, 255, sizeof(CurrentDrawKey.SamplerStates));
FMemory::Memset(CurrentDrawKey.Resources, 255, sizeof(CurrentDrawKey.Resources));
CurrentShaderState = State;
if ( State )
{
BoundShaderState = ShaderStates.FindChecked(State);
}
CurrentDrawKey.Hash = 0;
}
}
void FShaderCache::InternalSetViewport(uint32 MinX, uint32 MinY, float MinZ, uint32 MaxX, uint32 MaxY, float MaxZ)
{
if ( (bUseShaderPredraw || bUseShaderDrawLog) && !bIsPreDraw )
{
Viewport[0] = MinX;
Viewport[1] = MinY;
Viewport[2] = MaxX;
Viewport[3] = MaxY;
DepthRange[0] = MinZ;
DepthRange[1] = MaxZ;
}
}
void FShaderCache::InternalLogDraw(uint8 IndexType)
{
if ( bUseShaderDrawLog && !bIsPreDraw )
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(GMaxRHIShaderPlatform);
CurrentDrawKey.IndexType = IndexType;
FSetElementId Id = PlatformCache.DrawStates.FindId(CurrentDrawKey);
if ( !Id.IsValidId() )
{
Id = PlatformCache.DrawStates.Add(CurrentDrawKey);
}
TSet<int32>& ShaderDrawSet = PlatformCache.StreamingDrawStates.FindOrAdd(StreamingKey).ShaderDrawStates.FindOrAdd(BoundShaderState);
if( !ShaderDrawSet.Contains(Id.AsInteger()) )
{
ShaderDrawSet.Add(Id.AsInteger());
}
// No need to predraw this shader draw key - we've already done it
for(auto StreamingMap : ShadersToDraw)
{
StreamingMap.Value.ShaderDrawStates.FindRef(BoundShaderState).Remove(Id.AsInteger());
}
}
}
void FShaderCache::InternalPreDrawShaders(FRHICommandList& RHICmdList)
{
if ( bUseShaderPredraw && ShadersToDraw.FindRef(StreamingKey).ShaderDrawStates.Num() > 0 )
{
bIsPreDraw = true;
if ( !IsValidRef(IndexBufferUInt16) )
{
FRHIResourceCreateInfo Info;
uint32 Stride = sizeof(uint16);
uint32 Size = sizeof(uint16) * 3;
IndexBufferUInt16 = RHICreateIndexBuffer(Stride, Size, BUF_Static, Info);
void* Data = RHILockIndexBuffer(IndexBufferUInt16, 0, Size, RLM_WriteOnly);
if ( Data )
{
FMemory::Memzero(Data, Size);
}
RHIUnlockIndexBuffer(IndexBufferUInt16);
}
if ( !IsValidRef(IndexBufferUInt32) )
{
FRHIResourceCreateInfo Info;
uint32 Stride = sizeof(uint32);
uint32 Size = sizeof(uint32) * 3;
IndexBufferUInt32 = RHICreateIndexBuffer(Stride, Size, BUF_Static, Info);
void* Data = RHILockIndexBuffer(IndexBufferUInt32, 0, Size, RLM_WriteOnly);
if ( Data )
{
FMemory::Memzero(Data, Size);
}
RHIUnlockIndexBuffer(IndexBufferUInt32);
}
RHICmdList.SetViewport(0, 0, FLT_MIN, 3, 3, FLT_MAX);
int64 TimeForPredrawing = 0;
TMap<FShaderCacheBoundState, TSet<int32>>& ShaderDrawStates = ShadersToDraw.FindOrAdd(StreamingKey).ShaderDrawStates;
for ( auto It = ShaderDrawStates.CreateIterator(); (PredrawBatchTime == -1 || TimeForPredrawing < PredrawBatchTime) && It; ++It )
{
uint32 Start = FPlatformTime::Cycles();
auto Shader = *It;
TSet<int32>& ShaderDrawSet = Shader.Value;
PreDrawShader(RHICmdList, Shader.Key, ShaderDrawSet);
It.RemoveCurrent();
uint32 End = FPlatformTime::Cycles();
TimeForPredrawing += FPlatformTime::ToMilliseconds(End - Start);
}
// This is a bit dirty/naughty but it forces the draw commands to be flushed through on OS X
// which means we can delete the resources without crashing MTGL.
RHIFlushResources();
RHICmdList.SetBoundShaderState(CurrentShaderState);
FBlendStateRHIRef BlendState = RHICreateBlendState(CurrentDrawKey.BlendState);
FDepthStencilStateRHIRef DepthStencil = RHICreateDepthStencilState(CurrentDrawKey.DepthStencilState);
FRasterizerStateRHIRef Rasterizer = RHICreateRasterizerState(CurrentDrawKey.RasterizerState);
RHICmdList.SetBlendState(BlendState);
RHICmdList.SetDepthStencilState(DepthStencil);
RHICmdList.SetRasterizerState(Rasterizer);
RHICmdList.SetViewport(Viewport[0], Viewport[1], DepthRange[0], Viewport[2], Viewport[3], DepthRange[1]);
if ( ShadersToDraw.FindOrAdd(StreamingKey).ShaderDrawStates.Num() == 0 )
{
PredrawRTs.Empty();
PredrawBindings.Empty();
PredrawVBs.Empty();
}
bIsPreDraw = false;
}
}
void FShaderCache::PrebindShader(FShaderCacheKey const& Key)
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(Key.Platform);
bool const bCanPreBind = (ShaderPlatformCanPrebindBoundShaderState(Key.Platform) || CurrentNumRenderTargets > 0);
if (bCanPreBind || bUseShaderPredraw)
{
TSet<FShaderCacheBoundState>& BoundStates = PlatformCache.ShaderStateMembership.FindOrAdd(Key);
for (FShaderCacheBoundState State : BoundStates)
{
FVertexShaderRHIRef VertexShader = State.VertexShader.bActive ? CachedVertexShaders.FindRef(State.VertexShader) : nullptr;
FPixelShaderRHIRef PixelShader = State.PixelShader.bActive ? CachedPixelShaders.FindRef(State.PixelShader) : nullptr;
FGeometryShaderRHIRef GeometryShader = State.GeometryShader.bActive ? CachedGeometryShaders.FindRef(State.GeometryShader) : nullptr;
FHullShaderRHIRef HullShader = State.HullShader.bActive ? CachedHullShaders.FindRef(State.HullShader) : nullptr;
FDomainShaderRHIRef DomainShader = State.DomainShader.bActive ? CachedDomainShaders.FindRef(State.DomainShader) : nullptr;
bool bOK = true;
bOK &= (State.VertexShader.bActive == IsValidRef(VertexShader));
bOK &= (State.PixelShader.bActive == IsValidRef(PixelShader));
bOK &= (State.GeometryShader.bActive == IsValidRef(GeometryShader));
bOK &= (State.HullShader.bActive == IsValidRef(HullShader));
bOK &= (State.DomainShader.bActive == IsValidRef(DomainShader));
if (bOK)
{
FVertexDeclarationRHIRef VertexDeclaration = RHICreateVertexDeclaration(State.VertexDeclaration);
bOK &= IsValidRef(VertexDeclaration);
if (bOK)
{
if (bCanPreBind)
{
FBoundShaderStateRHIRef BoundState = RHICreateBoundShaderState(VertexDeclaration,
VertexShader,
HullShader,
DomainShader,
PixelShader,
GeometryShader);
if (IsValidRef(BoundState))
{
BoundShaderStates.Add(State, BoundState);
if (bUseShaderPredraw)
{
TSet<int32>& StreamCache = PlatformCache.StreamingDrawStates.FindOrAdd(StreamingKey).ShaderDrawStates.FindOrAdd(State);
if(!ShadersToDraw.FindOrAdd(StreamingKey).ShaderDrawStates.Contains(State))
{
ShadersToDraw.FindOrAdd(StreamingKey).ShaderDrawStates.Add(State, StreamCache);
}
}
}
}
else if (bUseShaderPredraw)
{
TSet<int32>& StreamCache = PlatformCache.StreamingDrawStates.FindOrAdd(StreamingKey).ShaderDrawStates.FindOrAdd(State);
if(!ShadersToDraw.FindOrAdd(StreamingKey).ShaderDrawStates.Contains(State))
{
ShadersToDraw.FindOrAdd(StreamingKey).ShaderDrawStates.Add(State, StreamCache);
}
}
}
}
}
}
}
void FShaderCache::SubmitShader(FShaderCacheKey const& Key, TArray<uint8> const& Code)
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(Key.Platform);
switch (Key.Frequency)
{
case SF_Vertex:
if(!CachedVertexShaders.Find(Key))
{
FVertexShaderRHIRef Shader = RHICreateVertexShader(Code);
if(IsValidRef(Shader))
{
Shader->SetHash(Key.SHAHash);
CachedVertexShaders.Add(Key, Shader);
PrebindShader(Key);
PlatformCache.Shaders.Add(Key);
}
}
break;
case SF_Pixel:
if(!CachedPixelShaders.Find(Key))
{
FPixelShaderRHIRef Shader = RHICreatePixelShader(Code);
if(IsValidRef(Shader))
{
Shader->SetHash(Key.SHAHash);
CachedPixelShaders.Add(Key, Shader);
PrebindShader(Key);
PlatformCache.Shaders.Add(Key);
}
}
break;
case SF_Geometry:
if(!CachedGeometryShaders.Find(Key))
{
FGeometryShaderRHIRef Shader = RHICreateGeometryShader(Code);
if(IsValidRef(Shader))
{
Shader->SetHash(Key.SHAHash);
CachedGeometryShaders.Add(Key, Shader);
PrebindShader(Key);
PlatformCache.Shaders.Add(Key);
}
}
break;
case SF_Hull:
if(!CachedHullShaders.Find(Key))
{
FHullShaderRHIRef Shader = RHICreateHullShader(Code);
if(IsValidRef(Shader))
{
Shader->SetHash(Key.SHAHash);
CachedHullShaders.Add(Key, Shader);
PrebindShader(Key);
PlatformCache.Shaders.Add(Key);
}
}
break;
case SF_Domain:
if(!CachedDomainShaders.Find(Key))
{
FDomainShaderRHIRef Shader = RHICreateDomainShader(Code);
if(IsValidRef(Shader))
{
Shader->SetHash(Key.SHAHash);
CachedDomainShaders.Add(Key, Shader);
PrebindShader(Key);
PlatformCache.Shaders.Add(Key);
}
}
break;
case SF_Compute:
{
bool const bCanPreBind = (ShaderPlatformCanPrebindBoundShaderState(Key.Platform) || CurrentNumRenderTargets > 0);
if (!CachedComputeShaders.Find(Key) && bCanPreBind)
{
FComputeShaderRHIRef Shader = RHICreateComputeShader(Code);
if (IsValidRef(Shader))
{
// @todo WARNING: The RHI is responsible for hashing Compute shaders, unlike other stages because of how OpenGLDrv implements compute.
FShaderCacheKey ComputeKey = Key;
ComputeKey.SHAHash = Shader->GetHash();
CachedComputeShaders.Add(ComputeKey, Shader);
PrebindShader(ComputeKey);
PlatformCache.Shaders.Add(ComputeKey);
}
}
break;
}
default:
check(false);
break;
}
}
FTextureRHIRef FShaderCache::CreateTexture(FShaderTextureKey const& TextureKey, bool const bCached)
{
FTextureRHIRef Tex = bCached ? CachedTextures.FindRef(TextureKey) : nullptr;
if ( !IsValidRef(Tex) )
{
FRHIResourceCreateInfo Info;
switch(TextureKey.Type)
{
case SCTT_Texture2D:
{
Tex = RHICreateTexture2D(TextureKey.X, TextureKey.Y, TextureKey.Format, TextureKey.MipLevels, TextureKey.Samples, TextureKey.Flags, Info);
break;
}
case SCTT_Texture2DArray:
{
Tex = RHICreateTexture2DArray(TextureKey.X, TextureKey.Y, TextureKey.Z, TextureKey.Format, TextureKey.MipLevels, TextureKey.Flags, Info);
break;
}
case SCTT_Texture3D:
{
Tex = RHICreateTexture3D(TextureKey.X, TextureKey.Y, TextureKey.Z, TextureKey.Format, TextureKey.MipLevels, TextureKey.Flags, Info);
break;
}
case SCTT_TextureCube:
{
Tex = RHICreateTextureCube(TextureKey.X, TextureKey.Format, TextureKey.MipLevels, TextureKey.Flags, Info);
break;
}
case SCTT_TextureCubeArray:
{
Tex = RHICreateTextureCubeArray(TextureKey.X, TextureKey.Z, TextureKey.Format, TextureKey.MipLevels, TextureKey.Flags, Info);
break;
}
case SCTT_Buffer:
case SCTT_Texture1D:
case SCTT_Texture1DArray:
default:
{
check(false);
break;
}
}
}
return Tex;
}
FShaderCache::FShaderTextureBinding FShaderCache::CreateSRV(FShaderResourceKey const& ResourceKey)
{
FShaderTextureBinding Binding = CachedSRVs.FindRef(ResourceKey);
if ( !IsValidRef(Binding.SRV) )
{
FShaderTextureKey const& TextureKey = ResourceKey.Tex;
switch(TextureKey.Type)
{
case SCTT_Buffer:
{
FRHIResourceCreateInfo Info;
Binding.VertexBuffer = RHICreateVertexBuffer(TextureKey.X, TextureKey.Y, Info);
Binding.SRV = RHICreateShaderResourceView(Binding.VertexBuffer, TextureKey.Z, TextureKey.Format);
break;
}
case SCTT_Texture2D:
{
Binding.Texture = CreateTexture(TextureKey, true);
Binding.SRV = RHICreateShaderResourceView(Binding.Texture->GetTexture2D(), ResourceKey.BaseMip, ResourceKey.MipLevels, ResourceKey.Format);
break;
}
default:
{
check(false);
break;
}
}
}
return Binding;
}
FTextureRHIRef FShaderCache::CreateRenderTarget(FShaderRenderTargetKey const& TargetKey)
{
FTextureRHIRef Texture;
if( TargetKey.Texture.Format != PF_Unknown )
{
Texture = PredrawRTs.FindRef(TargetKey);
if ( !IsValidRef(Texture) )
{
Texture = CreateTexture(TargetKey.Texture, false);
PredrawRTs.Add(TargetKey, Texture);
}
}
return Texture;
}
template <typename TShaderRHIRef>
void FShaderCache::SetShaderSamplerTextures( FRHICommandList& RHICmdList, FShaderDrawKey const& DrawKey, EShaderFrequency Frequency, TShaderRHIRef Shader, bool bClear )
{
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(GMaxRHIShaderPlatform);
for ( uint32 i = 0; i < GetFeatureLevelMaxTextureSamplers(GMaxRHIFeatureLevel); i++ )
{
if ( DrawKey.SamplerStates[Frequency][i] != FShaderDrawKey::NullState )
{
FSamplerStateInitializerRHI SamplerInit = PlatformCache.SamplerStates[FSetElementId::FromInteger(DrawKey.SamplerStates[Frequency][i])];
FSamplerStateRHIRef State = RHICreateSamplerState(SamplerInit);
RHICmdList.SetShaderSampler(Shader, i, State);
FShaderTextureBinding Bind;
if ( DrawKey.Resources[Frequency][i] != FShaderDrawKey::NullState )
{
FShaderResourceKey Resource = PlatformCache.Resources[FSetElementId::FromInteger(DrawKey.Resources[Frequency][i])];
if( Resource.bSRV == false )
{
if ( !bClear && Resource.Tex.Type != SCTT_Invalid )
{
Bind.Texture = CreateTexture(Resource.Tex, true);
RHICmdList.SetShaderTexture(Shader, i, Bind.Texture.GetReference());
}
else
{
RHICmdList.SetShaderTexture(Shader, i, nullptr);
}
}
else
{
if ( !bClear )
{
Bind = CreateSRV(Resource);
RHICmdList.SetShaderResourceViewParameter(Shader, i, Bind.SRV.GetReference());
}
else
{
RHICmdList.SetShaderResourceViewParameter(Shader, i, nullptr);
}
}
}
else
{
RHICmdList.SetShaderTexture(Shader, i, nullptr);
}
if ( IsValidRef(Bind.Texture) || IsValidRef(Bind.SRV) )
{
PredrawBindings.Add(Bind);
}
}
}
}
void FShaderCache::PreDrawShader(FRHICommandList& RHICmdList, FShaderCacheBoundState const& Shader, TSet<int32> const& DrawStates)
{
FBoundShaderStateRHIRef ShaderBoundState = BoundShaderStates.FindRef( Shader );
{
uint32 VertexBufferSize = 0;
for( auto VertexDec : Shader.VertexDeclaration )
{
VertexBufferSize = VertexBufferSize > (uint32)(VertexDec.Stride + VertexDec.Offset) ? VertexBufferSize : (uint32)(VertexDec.Stride + VertexDec.Offset);
}
FRHIResourceCreateInfo Info;
if ( VertexBufferSize > 0 && (( !IsValidRef(PredrawVB) || !IsValidRef(PredrawZVB) ) || PredrawVB->GetSize() < VertexBufferSize || PredrawZVB->GetSize() < VertexBufferSize) )
{
// Retain previous VBs for outstanding draws
PredrawVBs.Add(PredrawVB);
PredrawVBs.Add(PredrawZVB);
PredrawVB = RHICreateVertexBuffer(VertexBufferSize, BUF_Static, Info);
{
void* Data = RHILockVertexBuffer(PredrawVB, 0, VertexBufferSize, RLM_WriteOnly);
if ( Data )
{
FMemory::Memzero(Data, VertexBufferSize);
}
RHIUnlockVertexBuffer(PredrawVB);
}
PredrawZVB = RHICreateVertexBuffer(VertexBufferSize, BUF_Static|BUF_ZeroStride, Info);
{
void* Data = RHILockVertexBuffer(PredrawZVB, 0, VertexBufferSize, RLM_WriteOnly);
if ( Data )
{
FMemory::Memzero(Data, VertexBufferSize);
}
RHIUnlockVertexBuffer(PredrawZVB);
}
}
for( auto VertexDec : Shader.VertexDeclaration )
{
if ( VertexDec.Stride > 0 )
{
RHICmdList.SetStreamSource(VertexDec.StreamIndex, PredrawVB, VertexDec.Stride, VertexDec.Offset);
}
else
{
RHICmdList.SetStreamSource(VertexDec.StreamIndex, PredrawZVB, VertexDec.Stride, VertexDec.Offset);
}
}
FShaderPlatformCache& PlatformCache = Caches.FindOrAdd(GMaxRHIShaderPlatform);
for ( auto DrawKeyIdx : DrawStates )
{
FShaderDrawKey DrawKey = PlatformCache.DrawStates[FSetElementId::FromInteger(DrawKeyIdx)];
FBlendStateRHIRef BlendState = RHICreateBlendState(DrawKey.BlendState);
FDepthStencilStateRHIRef DepthStencil = RHICreateDepthStencilState(DrawKey.DepthStencilState);
FRasterizerStateRHIRef Rasterizer = RHICreateRasterizerState(DrawKey.RasterizerState);
RHICmdList.SetBlendState(BlendState);
RHICmdList.SetDepthStencilState(DepthStencil);
RHICmdList.SetRasterizerState(Rasterizer);
uint32 NewNumRenderTargets = 0;
FRHIRenderTargetView RenderTargets[MaxSimultaneousRenderTargets];
for ( uint32 i = 0; i < MaxSimultaneousRenderTargets; i++ )
{
if( DrawKey.RenderTargets[i] != FShaderDrawKey::NullState )
{
FShaderTextureBinding Bind;
FShaderRenderTargetKey RTKey = PlatformCache.RenderTargets[FSetElementId::FromInteger(DrawKey.RenderTargets[i])];
Bind.Texture = CreateRenderTarget(RTKey);
RenderTargets[i].MipIndex = Bind.Texture->GetNumMips() > RTKey.MipLevel ? RTKey.MipLevel : 0;
RenderTargets[i].ArraySliceIndex = RTKey.ArrayIndex;
PredrawBindings.Add(Bind);
RenderTargets[i].Texture = Bind.Texture;
NewNumRenderTargets++;
}
else
{
break;
}
}
bool bDepthStencilTarget = (DrawKey.DepthStencilTarget != FShaderDrawKey::NullState);
FRHIDepthRenderTargetView DepthStencilTarget;
if ( bDepthStencilTarget )
{
FShaderTextureBinding Bind;
FShaderRenderTargetKey RTKey = PlatformCache.RenderTargets[FSetElementId::FromInteger(DrawKey.DepthStencilTarget)];
Bind.Texture = CreateRenderTarget(RTKey);
PredrawBindings.Add(Bind);
DepthStencilTarget.Texture = Bind.Texture;
}
RHICmdList.SetRenderTargets(NewNumRenderTargets, RenderTargets, bDepthStencilTarget ? &DepthStencilTarget : nullptr, 0, nullptr);
if ( !IsValidRef(ShaderBoundState) )
{
FVertexShaderRHIRef VertexShader = Shader.VertexShader.bActive ? CachedVertexShaders.FindRef(Shader.VertexShader) : nullptr;
FPixelShaderRHIRef PixelShader = Shader.PixelShader.bActive ? CachedPixelShaders.FindRef(Shader.PixelShader) : nullptr;
FGeometryShaderRHIRef GeometryShader = Shader.GeometryShader.bActive ? CachedGeometryShaders.FindRef(Shader.GeometryShader) : nullptr;
FHullShaderRHIRef HullShader = Shader.HullShader.bActive ? CachedHullShaders.FindRef(Shader.HullShader) : nullptr;
FDomainShaderRHIRef DomainShader = Shader.DomainShader.bActive ? CachedDomainShaders.FindRef(Shader.DomainShader) : nullptr;
bool bOK = true;
bOK &= (Shader.VertexShader.bActive == IsValidRef(VertexShader));
bOK &= (Shader.PixelShader.bActive == IsValidRef(PixelShader));
bOK &= (Shader.GeometryShader.bActive == IsValidRef(GeometryShader));
bOK &= (Shader.HullShader.bActive == IsValidRef(HullShader));
bOK &= (Shader.DomainShader.bActive == IsValidRef(DomainShader));
if ( bOK )
{
FVertexDeclarationRHIRef VertexDeclaration = RHICreateVertexDeclaration(Shader.VertexDeclaration);
bOK &= IsValidRef(VertexDeclaration);
if ( bOK )
{
ShaderBoundState = RHICreateBoundShaderState( VertexDeclaration,
VertexShader,
HullShader,
DomainShader,
PixelShader,
GeometryShader );
}
}
}
if ( IsValidRef( ShaderBoundState ) )
{
RHICmdList.SetBoundShaderState(ShaderBoundState);
}
else
{
break;
}
if ( Shader.VertexShader.bActive )
{
SetShaderSamplerTextures(RHICmdList, DrawKey, SF_Vertex, CachedVertexShaders.FindRef(Shader.VertexShader).GetReference());
}
if ( Shader.PixelShader.bActive )
{
SetShaderSamplerTextures(RHICmdList, DrawKey, SF_Pixel, CachedPixelShaders.FindRef(Shader.PixelShader).GetReference());
}
if ( Shader.GeometryShader.bActive )
{
SetShaderSamplerTextures(RHICmdList, DrawKey, SF_Geometry, CachedGeometryShaders.FindRef(Shader.GeometryShader).GetReference());
}
if ( Shader.HullShader.bActive )
{
SetShaderSamplerTextures(RHICmdList, DrawKey, SF_Hull, CachedHullShaders.FindRef(Shader.HullShader).GetReference());
}
if ( Shader.DomainShader.bActive )
{
SetShaderSamplerTextures(RHICmdList, DrawKey, SF_Domain, CachedDomainShaders.FindRef(Shader.DomainShader).GetReference());
}
switch ( DrawKey.IndexType )
{
case 0:
{
RHICmdList.DrawPrimitive(PT_TriangleList, 0, 1, 1);
break;
}
case 2:
{
RHICmdList.DrawIndexedPrimitive(IndexBufferUInt16, PT_TriangleList, 0, 0, 3, 0, 1, 1);
break;
}
case 4:
{
RHICmdList.DrawIndexedPrimitive(IndexBufferUInt32, PT_TriangleList, 0, 0, 3, 0, 1, 1);
break;
}
default:
{
break;
}
}
}
if( IsValidRef( ShaderBoundState ) && DrawStates.Num() )
{
if ( Shader.VertexShader.bActive )
{
SetShaderSamplerTextures(RHICmdList, CurrentDrawKey, SF_Vertex, CachedVertexShaders.FindRef(Shader.VertexShader).GetReference(), true);
}
if ( Shader.PixelShader.bActive )
{
SetShaderSamplerTextures(RHICmdList, CurrentDrawKey, SF_Pixel, CachedPixelShaders.FindRef(Shader.PixelShader).GetReference(), true);
}
if ( Shader.GeometryShader.bActive )
{
SetShaderSamplerTextures(RHICmdList, CurrentDrawKey, SF_Geometry, CachedGeometryShaders.FindRef(Shader.GeometryShader).GetReference(), true);
}
if ( Shader.HullShader.bActive )
{
SetShaderSamplerTextures(RHICmdList, CurrentDrawKey, SF_Hull, CachedHullShaders.FindRef(Shader.HullShader).GetReference(), true);
}
if ( Shader.DomainShader.bActive )
{
SetShaderSamplerTextures(RHICmdList, CurrentDrawKey, SF_Domain, CachedDomainShaders.FindRef(Shader.DomainShader).GetReference(), true);
}
}
for( auto VertexDec : Shader.VertexDeclaration )
{
RHICmdList.SetStreamSource(VertexDec.StreamIndex, nullptr, 0, 0);
}
}
}
static FORCEINLINE uint32 CalculateSizeOfSamplerStateInitializer()
{
static uint32 SizeOfSamplerStateInitializer = 0;
if (SizeOfSamplerStateInitializer == 0)
{
TArray<uint8> Data;
FMemoryWriter Writer(Data);
FSamplerStateInitializerRHI State;
Writer << State;
SizeOfSamplerStateInitializer = Data.Num();
}
return SizeOfSamplerStateInitializer;
}
bool FShaderCache::FSamplerStateInitializerRHIKeyFuncs::Matches(KeyInitType A,KeyInitType B)
{
return FMemory::Memcmp(&A, &B, CalculateSizeOfSamplerStateInitializer()) == 0;
}
uint32 FShaderCache::FSamplerStateInitializerRHIKeyFuncs::GetKeyHash(KeyInitType Key)
{
return FCrc::MemCrc_DEPRECATED(&Key, CalculateSizeOfSamplerStateInitializer());
}