Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/MeshPassProcessor.cpp
mickael gilabert 6ca9971c40 Replaced global frame minimal graphic pipeline state TSet to a TSet per DynamicPassMeshDrawListContext. This remove RW lock contention when global TSet was accessed by multiple threads
[at]krzysztof.narkowicz
[FYI] marcus.wassmer, ben.woodhouse
#jira UE-73448
#rnx

#ROBOMERGE-VERSION: 348-6547088
#ROBOMERGE-OWNER: ryan.vance
#ROBOMERGE-AUTHOR: mickael.gilabert
#ROBOMERGE-SOURCE: CL 6536065 via CL 6537948 via CL 6538157
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)

[CL 6577065 by mickael gilabert in Dev-VR branch]
2019-05-20 13:37:50 -04:00

1207 lines
45 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
MeshPassProcessor.cpp:
=============================================================================*/
#include "MeshPassProcessor.h"
#include "SceneUtils.h"
#include "SceneRendering.h"
#include "Logging/LogMacros.h"
#include "SceneCore.h"
#include "ScenePrivate.h"
#include "SceneInterface.h"
#include "MeshPassProcessor.inl"
#include "PipelineStateCache.h"
TSet<FRefCountedGraphicsMinimalPipelineStateInitializer, RefCountedGraphicsMinimalPipelineStateInitializerKeyFuncs> FGraphicsMinimalPipelineStateId::PersistentIdTable;
int32 FGraphicsMinimalPipelineStateId::LocalPipelineIdTableSize = 0;
int32 FGraphicsMinimalPipelineStateId::CurrentLocalPipelineIdTableSize = 0;
const FMeshDrawCommandSortKey FMeshDrawCommandSortKey::Default = { {0} };
int32 GEmitMeshDrawEvent = 0;
static FAutoConsoleVariableRef CVarEmitMeshDrawEvent(
TEXT("r.EmitMeshDrawEvents"),
GEmitMeshDrawEvent,
TEXT("Emits a GPU event around each drawing policy draw call. /n")
TEXT("Useful for seeing stats about each draw call, however it greatly distorts total time and time per draw call."),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarSafeStateLookup(
TEXT("r.SafeStateLookup"),
1,
TEXT("Forces new-style safe state lookup for easy runtime perf comparison\n"),
ECVF_Scalability | ECVF_RenderThreadSafe);
enum { MAX_SRVs_PER_SHADER_STAGE = 128 };
enum { MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE = 14 };
enum { MAX_SAMPLERS_PER_SHADER_STAGE = 32 };
class FShaderBindingState
{
public:
int32 MaxSRVUsed = -1;
FShaderResourceViewRHIParamRef SRVs[MAX_SRVs_PER_SHADER_STAGE] = {};
int32 MaxUniformBufferUsed = -1;
FUniformBufferRHIParamRef UniformBuffers[MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE] = {};
int32 MaxTextureUsed = -1;
FTextureRHIParamRef Textures[MAX_SRVs_PER_SHADER_STAGE] = {};
int32 MaxSamplerUsed = -1;
FSamplerStateRHIParamRef Samplers[MAX_SAMPLERS_PER_SHADER_STAGE] = {};
};
class FReadOnlyMeshDrawSingleShaderBindings : public FMeshDrawShaderBindingsLayout
{
public:
FReadOnlyMeshDrawSingleShaderBindings(const FMeshDrawShaderBindingsLayout& InLayout, const uint8* InData) :
FMeshDrawShaderBindingsLayout(InLayout)
{
Data = InData;
}
inline const FUniformBufferRHIParamRef* GetUniformBufferStart() const
{
return (const FUniformBufferRHIParamRef*)(Data + GetUniformBufferOffset());
}
inline const FSamplerStateRHIParamRef* GetSamplerStart() const
{
const uint8* SamplerDataStart = Data + GetSamplerOffset();
return (const FSamplerStateRHIParamRef*)SamplerDataStart;
}
inline const FRHIResource** GetSRVStart() const
{
const uint8* SRVDataStart = Data + GetSRVOffset();
return (const FRHIResource**)SRVDataStart;
}
inline const uint8* GetSRVTypeStart() const
{
const uint8* SRVTypeDataStart = Data + GetSRVTypeOffset();
return SRVTypeDataStart;
}
inline const uint8* GetLooseDataStart() const
{
const uint8* LooseDataStart = Data + GetLooseDataOffset();
return LooseDataStart;
}
private:
const uint8* Data;
};
template<class RHIShaderType>
void FMeshDrawShaderBindings::SetShaderBindings(
FRHICommandList& RHICmdList,
RHIShaderType Shader,
const FReadOnlyMeshDrawSingleShaderBindings& RESTRICT SingleShaderBindings,
FShaderBindingState& RESTRICT ShaderBindingState)
{
const FUniformBufferRHIParamRef* RESTRICT UniformBufferBindings = SingleShaderBindings.GetUniformBufferStart();
const FShaderParameterInfo* RESTRICT UniformBufferParameters = SingleShaderBindings.ParameterMapInfo.UniformBuffers.GetData();
const int32 NumUniformBuffers = SingleShaderBindings.ParameterMapInfo.UniformBuffers.Num();
for (int32 UniformBufferIndex = 0; UniformBufferIndex < NumUniformBuffers; UniformBufferIndex++)
{
FShaderParameterInfo Parameter = UniformBufferParameters[UniformBufferIndex];
checkSlow(Parameter.BaseIndex < ARRAY_COUNT(ShaderBindingState.UniformBuffers));
FUniformBufferRHIParamRef UniformBuffer = UniformBufferBindings[UniformBufferIndex];
if (UniformBuffer != ShaderBindingState.UniformBuffers[Parameter.BaseIndex])
{
RHICmdList.SetShaderUniformBuffer(Shader, Parameter.BaseIndex, UniformBuffer);
ShaderBindingState.UniformBuffers[Parameter.BaseIndex] = UniformBuffer;
ShaderBindingState.MaxUniformBufferUsed = FMath::Max((int32)Parameter.BaseIndex, ShaderBindingState.MaxUniformBufferUsed);
}
}
const FSamplerStateRHIParamRef* RESTRICT SamplerBindings = SingleShaderBindings.GetSamplerStart();
const FShaderParameterInfo* RESTRICT TextureSamplerParameters = SingleShaderBindings.ParameterMapInfo.TextureSamplers.GetData();
const int32 NumTextureSamplers = SingleShaderBindings.ParameterMapInfo.TextureSamplers.Num();
for (int32 SamplerIndex = 0; SamplerIndex < NumTextureSamplers; SamplerIndex++)
{
FShaderParameterInfo Parameter = TextureSamplerParameters[SamplerIndex];
checkSlow(Parameter.BaseIndex < ARRAY_COUNT(ShaderBindingState.Samplers));
FSamplerStateRHIParamRef Sampler = SamplerBindings[SamplerIndex];
if (Sampler != ShaderBindingState.Samplers[Parameter.BaseIndex])
{
RHICmdList.SetShaderSampler(Shader, Parameter.BaseIndex, Sampler);
ShaderBindingState.Samplers[Parameter.BaseIndex] = Sampler;
ShaderBindingState.MaxSamplerUsed = FMath::Max((int32)Parameter.BaseIndex, ShaderBindingState.MaxSamplerUsed);
}
}
const uint8* RESTRICT SRVType = SingleShaderBindings.GetSRVTypeStart();
const FRHIResource** RESTRICT SRVBindings = SingleShaderBindings.GetSRVStart();
const FShaderParameterInfo* RESTRICT SRVParameters = SingleShaderBindings.ParameterMapInfo.SRVs.GetData();
const uint32 NumSRVs = SingleShaderBindings.ParameterMapInfo.SRVs.Num();
for (uint32 SRVIndex = 0; SRVIndex < NumSRVs; SRVIndex++)
{
FShaderParameterInfo Parameter = SRVParameters[SRVIndex];
checkSlow(Parameter.BaseIndex < ARRAY_COUNT(ShaderBindingState.SRVs));
uint32 TypeByteIndex = SRVIndex / 8;
uint32 TypeBitIndex = SRVIndex - TypeByteIndex;
if (SRVType[TypeByteIndex] & (1 << TypeBitIndex))
{
FShaderResourceViewRHIParamRef SRV = (FShaderResourceViewRHIParamRef)SRVBindings[SRVIndex];
if (SRV != ShaderBindingState.SRVs[Parameter.BaseIndex])
{
RHICmdList.SetShaderResourceViewParameter(Shader, Parameter.BaseIndex, SRV);
ShaderBindingState.SRVs[Parameter.BaseIndex] = SRV;
ShaderBindingState.MaxSRVUsed = FMath::Max((int32)Parameter.BaseIndex, ShaderBindingState.MaxSRVUsed);
}
}
else
{
FTextureRHIParamRef Texture = (FTextureRHIParamRef)SRVBindings[SRVIndex];
if (Texture != ShaderBindingState.Textures[Parameter.BaseIndex])
{
RHICmdList.SetShaderTexture(Shader, Parameter.BaseIndex, Texture);
ShaderBindingState.Textures[Parameter.BaseIndex] = Texture;
ShaderBindingState.MaxTextureUsed = FMath::Max((int32)Parameter.BaseIndex, ShaderBindingState.MaxTextureUsed);
}
}
}
const uint8* LooseDataStart = SingleShaderBindings.GetLooseDataStart();
for (const FShaderLooseParameterBufferInfo& LooseParameterBuffer : SingleShaderBindings.ParameterMapInfo.LooseParameterBuffers)
{
for (FShaderParameterInfo Parameter : LooseParameterBuffer.Parameters)
{
RHICmdList.SetShaderParameter(
Shader,
LooseParameterBuffer.BufferIndex,
Parameter.BaseIndex,
Parameter.Size,
LooseDataStart
);
LooseDataStart += Parameter.Size;
}
}
}
template<class RHIShaderType>
void FMeshDrawShaderBindings::SetShaderBindings(
FRHICommandList& RHICmdList,
RHIShaderType Shader,
const FReadOnlyMeshDrawSingleShaderBindings& RESTRICT SingleShaderBindings)
{
const FUniformBufferRHIParamRef* RESTRICT UniformBufferBindings = SingleShaderBindings.GetUniformBufferStart();
const FShaderParameterInfo* RESTRICT UniformBufferParameters = SingleShaderBindings.ParameterMapInfo.UniformBuffers.GetData();
const int32 NumUniformBuffers = SingleShaderBindings.ParameterMapInfo.UniformBuffers.Num();
for (int32 UniformBufferIndex = 0; UniformBufferIndex < NumUniformBuffers; UniformBufferIndex++)
{
FShaderParameterInfo Parameter = UniformBufferParameters[UniformBufferIndex];
FUniformBufferRHIParamRef UniformBuffer = UniformBufferBindings[UniformBufferIndex];
RHICmdList.SetShaderUniformBuffer(Shader, Parameter.BaseIndex, UniformBuffer);
}
const FSamplerStateRHIParamRef* RESTRICT SamplerBindings = SingleShaderBindings.GetSamplerStart();
const FShaderParameterInfo* RESTRICT TextureSamplerParameters = SingleShaderBindings.ParameterMapInfo.TextureSamplers.GetData();
const int32 NumTextureSamplers = SingleShaderBindings.ParameterMapInfo.TextureSamplers.Num();
for (int32 SamplerIndex = 0; SamplerIndex < NumTextureSamplers; SamplerIndex++)
{
FShaderParameterInfo Parameter = TextureSamplerParameters[SamplerIndex];
FSamplerStateRHIParamRef Sampler = SamplerBindings[SamplerIndex];
RHICmdList.SetShaderSampler(Shader, Parameter.BaseIndex, Sampler);
}
const uint8* RESTRICT SRVType = SingleShaderBindings.GetSRVTypeStart();
const FRHIResource** RESTRICT SRVBindings = SingleShaderBindings.GetSRVStart();
const FShaderParameterInfo* RESTRICT SRVParameters = SingleShaderBindings.ParameterMapInfo.SRVs.GetData();
const uint32 NumSRVs = SingleShaderBindings.ParameterMapInfo.SRVs.Num();
for (uint32 SRVIndex = 0; SRVIndex < NumSRVs; SRVIndex++)
{
FShaderParameterInfo Parameter = SRVParameters[SRVIndex];
uint32 TypeByteIndex = SRVIndex / 8;
uint32 TypeBitIndex = SRVIndex - TypeByteIndex;
if (SRVType[TypeByteIndex] & (1 << TypeBitIndex))
{
FShaderResourceViewRHIParamRef SRV = (FShaderResourceViewRHIParamRef)SRVBindings[SRVIndex];
RHICmdList.SetShaderResourceViewParameter(Shader, Parameter.BaseIndex, SRV);
}
else
{
FTextureRHIParamRef Texture = (FTextureRHIParamRef)SRVBindings[SRVIndex];
RHICmdList.SetShaderTexture(Shader, Parameter.BaseIndex, Texture);
}
}
const uint8* LooseDataStart = SingleShaderBindings.GetLooseDataStart();
for (const FShaderLooseParameterBufferInfo& LooseParameterBuffer : SingleShaderBindings.ParameterMapInfo.LooseParameterBuffers)
{
for (FShaderParameterInfo Parameter : LooseParameterBuffer.Parameters)
{
RHICmdList.SetShaderParameter(
Shader,
LooseParameterBuffer.BufferIndex,
Parameter.BaseIndex,
Parameter.Size,
LooseDataStart
);
LooseDataStart += Parameter.Size;
}
}
}
#if RHI_RAYTRACING
void FMeshDrawShaderBindings::SetRayTracingShaderBindingsForHitGroup(
FRHICommandList& RHICmdList,
FRayTracingSceneRHIParamRef Scene,
uint32 InstanceIndex,
uint32 SegmentIndex,
FRayTracingPipelineStateRHIParamRef PipelineState,
uint32 HitGroupIndex,
uint32 ShaderSlot) const
{
check(ShaderLayouts.Num() == 1);
FReadOnlyMeshDrawSingleShaderBindings SingleShaderBindings(ShaderLayouts[0], GetData());
FUniformBufferRHIParamRef LocalUniformBuffers[MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE] = {};
const FUniformBufferRHIParamRef* RESTRICT UniformBufferBindings = SingleShaderBindings.GetUniformBufferStart();
const FShaderParameterInfo* RESTRICT UniformBufferParameters = SingleShaderBindings.ParameterMapInfo.UniformBuffers.GetData();
const int32 NumUniformBuffers = SingleShaderBindings.ParameterMapInfo.UniformBuffers.Num();
int32 MaxUniformBufferUsed = -1;
for (int32 UniformBufferIndex = 0; UniformBufferIndex < NumUniformBuffers; UniformBufferIndex++)
{
FShaderParameterInfo Parameter = UniformBufferParameters[UniformBufferIndex];
checkSlow(Parameter.BaseIndex < ARRAY_COUNT(LocalUniformBuffers));
FUniformBufferRHIParamRef UniformBuffer = UniformBufferBindings[UniformBufferIndex];
if (Parameter.BaseIndex < ARRAY_COUNT(LocalUniformBuffers))
{
LocalUniformBuffers[Parameter.BaseIndex] = UniformBuffer;
MaxUniformBufferUsed = FMath::Max((int32)Parameter.BaseIndex, MaxUniformBufferUsed);
}
}
checkf(SingleShaderBindings.ParameterMapInfo.TextureSamplers.Num() == 0, TEXT("Texture sampler parameters are not supported for ray tracing. UniformBuffers must be used for all resource binding."));
checkf(SingleShaderBindings.ParameterMapInfo.SRVs.Num() == 0, TEXT("SRV parameters are not supported for ray tracing. UniformBuffers must be used for all resource binding."));
checkf(SingleShaderBindings.ParameterMapInfo.LooseParameterBuffers.Num() == 0, TEXT("Loose parameter buffers are not supported for ray tracing. UniformBuffers must be used for all resource binding."));
check(SegmentIndex < 0xFF);
uint32 NumUniformBuffersToSet = MaxUniformBufferUsed + 1;
const uint32 UserData = 0; // UserData could be used to store material ID or any other kind of per-material constant. This can be retrieved in hit shaders via GetHitGroupUserData().
RHICmdList.SetRayTracingHitGroup(Scene, InstanceIndex, SegmentIndex, ShaderSlot, PipelineState, HitGroupIndex, NumUniformBuffersToSet, LocalUniformBuffers, UserData);
}
#endif // RHI_RAYTRACING
FGraphicsMinimalPipelineStateId FGraphicsMinimalPipelineStateId::GetPersistentId(const FGraphicsMinimalPipelineStateInitializer& InPipelineState)
{
checkSlow(IsInRenderingThread());
FSetElementId TableId = PersistentIdTable.FindId(InPipelineState);
if (TableId.IsValidId())
{
++PersistentIdTable[TableId].RefNum;
}
else
{
TableId = PersistentIdTable.Add(FRefCountedGraphicsMinimalPipelineStateInitializer(InPipelineState, 1));
}
checkf(TableId.AsInteger() < (MAX_uint32 >> 2), TEXT("Persistent FGraphicsMinimalPipelineStateId table overflow!"));
FGraphicsMinimalPipelineStateId Ret;
Ret.bValid = 1;
Ret.bComesFromLocalPipelineStateSet = 0;
Ret.SetElementIndex = TableId.AsInteger();
return Ret;
}
void FGraphicsMinimalPipelineStateId::RemovePersistentId(FGraphicsMinimalPipelineStateId Id)
{
check(!Id.bComesFromLocalPipelineStateSet && Id.bValid);
const FSetElementId SetElementId = FSetElementId::FromInteger(Id.SetElementIndex);
FRefCountedGraphicsMinimalPipelineStateInitializer& RefCountedStateInitializer = PersistentIdTable[SetElementId];
check(RefCountedStateInitializer.RefNum > 0);
--RefCountedStateInitializer.RefNum;
if (RefCountedStateInitializer.RefNum <= 0)
{
PersistentIdTable.Remove(SetElementId);
}
}
FGraphicsMinimalPipelineStateId FGraphicsMinimalPipelineStateId::GetPipelineStateId(const FGraphicsMinimalPipelineStateInitializer& InPipelineState, FGraphicsMinimalPipelineStateSet& InOutPassSet)
{
FGraphicsMinimalPipelineStateId Ret;
Ret.bValid = 1;
Ret.bComesFromLocalPipelineStateSet = 0;
FSetElementId TableId = PersistentIdTable.FindId(InPipelineState);
if (!TableId.IsValidId())
{
Ret.bComesFromLocalPipelineStateSet = 1;
TableId = InOutPassSet.FindId(InPipelineState);
if (!TableId.IsValidId())
{
TableId = InOutPassSet.Add(InPipelineState);
}
}
checkf(TableId.AsInteger() < (MAX_uint32 >> 2), TEXT("One frame FGraphicsMinimalPipelineStateId table overflow!"));
Ret.SetElementIndex = TableId.AsInteger();
return Ret;
}
void FGraphicsMinimalPipelineStateId::ResetLocalPipelineIdTableSize()
{
LocalPipelineIdTableSize = CurrentLocalPipelineIdTableSize;
CurrentLocalPipelineIdTableSize = 0;
}
void FGraphicsMinimalPipelineStateId::AddSizeToLocalPipelineIdTableSize(SIZE_T Size)
{
CurrentLocalPipelineIdTableSize += int32(Size);
}
class FMeshDrawCommandStateCache
{
public:
uint32 PipelineId;
uint32 StencilRef;
FShaderBindingState ShaderBindings[SF_NumStandardFrequencies];
FVertexInputStream VertexStreams[MaxVertexElementCount];
FMeshDrawCommandStateCache()
{
// Must init to impossible values to avoid filtering the first draw's state
PipelineId = -1;
StencilRef = -1;
}
inline void SetPipelineState(int32 NewPipelineId)
{
PipelineId = NewPipelineId;
StencilRef = -1;
// Vertex streams must be reset if PSO changes.
for (int32 VertexStreamIndex = 0; VertexStreamIndex < ARRAY_COUNT(VertexStreams); ++VertexStreamIndex)
{
VertexStreams[VertexStreamIndex].VertexBuffer = nullptr;
}
// Shader bindings must be reset if PSO changes
for (int32 FrequencyIndex = 0; FrequencyIndex < ARRAY_COUNT(ShaderBindings); FrequencyIndex++)
{
FShaderBindingState& RESTRICT ShaderBinding = ShaderBindings[FrequencyIndex];
for (int32 SlotIndex = 0; SlotIndex <= ShaderBinding.MaxSRVUsed; SlotIndex++)
{
ShaderBinding.SRVs[SlotIndex] = nullptr;
}
ShaderBinding.MaxSRVUsed = -1;
for (int32 SlotIndex = 0; SlotIndex <= ShaderBinding.MaxUniformBufferUsed; SlotIndex++)
{
ShaderBinding.UniformBuffers[SlotIndex] = nullptr;
}
ShaderBinding.MaxUniformBufferUsed = -1;
for (int32 SlotIndex = 0; SlotIndex <= ShaderBinding.MaxTextureUsed; SlotIndex++)
{
ShaderBinding.Textures[SlotIndex] = nullptr;
}
ShaderBinding.MaxTextureUsed = -1;
for (int32 SlotIndex = 0; SlotIndex <= ShaderBinding.MaxSamplerUsed; SlotIndex++)
{
ShaderBinding.Samplers[SlotIndex] = nullptr;
}
ShaderBinding.MaxSamplerUsed = -1;
}
}
};
FMeshDrawShaderBindings::~FMeshDrawShaderBindings()
{
#if VALIDATE_UNIFORM_BUFFER_LIFETIME
uint8* ShaderBindingDataPtr = GetData();
for (int32 ShaderBindingsIndex = 0; ShaderBindingsIndex < ShaderLayouts.Num(); ShaderBindingsIndex++)
{
FMeshDrawSingleShaderBindings SingleShaderBindings(ShaderLayouts[ShaderBindingsIndex], ShaderBindingDataPtr);
const FUniformBufferRHIParamRef* RESTRICT UniformBufferBindings = SingleShaderBindings.GetUniformBufferStart();
const int32 NumUniformBuffers = SingleShaderBindings.ParameterMapInfo.UniformBuffers.Num();
for (int32 UniformBufferIndex = 0; UniformBufferIndex < NumUniformBuffers; UniformBufferIndex++)
{
FUniformBufferRHIParamRef UniformBuffer = UniformBufferBindings[UniformBufferIndex];
if (UniformBuffer)
{
UniformBuffer->NumMeshCommandReferencesForDebugging--;
check(UniformBuffer->NumMeshCommandReferencesForDebugging >= 0);
}
}
ShaderBindingDataPtr += ShaderLayouts[ShaderBindingsIndex].GetDataSizeBytes();
}
#endif
if (Size > ARRAY_COUNT(InlineStorage))
{
delete[] HeapData;
}
}
void FMeshDrawShaderBindings::Initialize(FMeshProcessorShaders Shaders)
{
const int32 NumShaderFrequencies = (Shaders.VertexShader ? 1 : 0) + (Shaders.HullShader ? 1 : 0) + (Shaders.DomainShader ? 1 : 0) + (Shaders.PixelShader ? 1 : 0) + (Shaders.GeometryShader ? 1 : 0) + (Shaders.ComputeShader ? 1 : 0)
#if RHI_RAYTRACING
+ (Shaders.RayHitGroupShader ? 1 : 0)
#endif
;
ShaderLayouts.Empty(NumShaderFrequencies);
int32 ShaderBindingDataSize = 0;
if (Shaders.VertexShader)
{
ShaderLayouts.Add(FMeshDrawShaderBindingsLayout(Shaders.VertexShader));
ShaderBindingDataSize += ShaderLayouts.Last().GetDataSizeBytes();
}
if (Shaders.HullShader)
{
ShaderLayouts.Add(FMeshDrawShaderBindingsLayout(Shaders.HullShader));
ShaderBindingDataSize += ShaderLayouts.Last().GetDataSizeBytes();
}
if (Shaders.DomainShader)
{
ShaderLayouts.Add(FMeshDrawShaderBindingsLayout(Shaders.DomainShader));
ShaderBindingDataSize += ShaderLayouts.Last().GetDataSizeBytes();
}
if (Shaders.PixelShader)
{
ShaderLayouts.Add(FMeshDrawShaderBindingsLayout(Shaders.PixelShader));
ShaderBindingDataSize += ShaderLayouts.Last().GetDataSizeBytes();
}
if (Shaders.GeometryShader)
{
ShaderLayouts.Add(FMeshDrawShaderBindingsLayout(Shaders.GeometryShader));
ShaderBindingDataSize += ShaderLayouts.Last().GetDataSizeBytes();
}
if (Shaders.ComputeShader)
{
ShaderLayouts.Add(FMeshDrawShaderBindingsLayout(Shaders.ComputeShader));
ShaderBindingDataSize += ShaderLayouts.Last().GetDataSizeBytes();
}
#if RHI_RAYTRACING
if (Shaders.RayHitGroupShader)
{
ShaderLayouts.Add(FMeshDrawShaderBindingsLayout(Shaders.RayHitGroupShader));
ShaderBindingDataSize += ShaderLayouts.Last().GetDataSizeBytes();
}
#endif
checkSlow(ShaderLayouts.Num() == NumShaderFrequencies);
if (ShaderBindingDataSize > 0)
{
AllocateZeroed(ShaderBindingDataSize);
}
}
void FMeshDrawShaderBindings::Finalize(const FMeshProcessorShaders* ShadersForDebugging)
{
#if VALIDATE_MESH_COMMAND_BINDINGS
if (!ShadersForDebugging)
{
return;
}
const uint8* ShaderBindingDataPtr = GetData();
for (int32 ShaderBindingsIndex = 0; ShaderBindingsIndex < ShaderLayouts.Num(); ShaderBindingsIndex++)
{
const FMeshDrawShaderBindingsLayout& ShaderLayout = ShaderLayouts[ShaderBindingsIndex];
FMeshMaterialShader* Shader = ShadersForDebugging->GetShader(ShaderLayout.Frequency);
check(Shader);
FReadOnlyMeshDrawSingleShaderBindings SingleShaderBindings(ShaderLayout, ShaderBindingDataPtr);
const FUniformBufferRHIParamRef* UniformBufferBindings = SingleShaderBindings.GetUniformBufferStart();
for (int32 BindingIndex = 0; BindingIndex < ShaderLayout.ParameterMapInfo.UniformBuffers.Num(); BindingIndex++)
{
FShaderParameterInfo ParameterInfo = ShaderLayout.ParameterMapInfo.UniformBuffers[BindingIndex];
FUniformBufferRHIParamRef UniformBufferValue = UniformBufferBindings[BindingIndex];
if (!UniformBufferValue)
{
// Search the automatically bound uniform buffers for more context if available
const FShaderParametersMetadata* AutomaticallyBoundUniformBufferStruct = Shader->FindAutomaticallyBoundUniformBufferStruct(ParameterInfo.BaseIndex);
if (AutomaticallyBoundUniformBufferStruct)
{
ensureMsgf(UniformBufferValue, TEXT("Shader %s with vertex factory %s never set automatically bound uniform buffer at BaseIndex %i. Expected buffer of type %s. This can cause GPU hangs, depending on how the shader uses it."),
Shader->GetType()->GetName(),
Shader->GetVertexFactoryType()->GetName(),
ParameterInfo.BaseIndex,
AutomaticallyBoundUniformBufferStruct->GetStructTypeName());
}
else
{
ensureMsgf(UniformBufferValue, TEXT("Shader %s with vertex factory %s never set uniform buffer at BaseIndex %i. This can cause GPU hangs, depending on how the shader uses it."),
Shader->GetVertexFactoryType()->GetName(),
Shader->GetType()->GetName(),
ParameterInfo.BaseIndex);
}
}
}
const FSamplerStateRHIParamRef* SamplerBindings = SingleShaderBindings.GetSamplerStart();
for (int32 BindingIndex = 0; BindingIndex < ShaderLayout.ParameterMapInfo.TextureSamplers.Num(); BindingIndex++)
{
FShaderParameterInfo ParameterInfo = ShaderLayout.ParameterMapInfo.TextureSamplers[BindingIndex];
FSamplerStateRHIParamRef SamplerValue = SamplerBindings[BindingIndex];
ensureMsgf(SamplerValue, TEXT("Shader %s with vertex factory %s never set sampler at BaseIndex %u. This can cause GPU hangs, depending on how the shader uses it."),
Shader->GetType()->GetName(),
Shader->GetVertexFactoryType()->GetName(),
ParameterInfo.BaseIndex);
}
const uint8* RESTRICT SRVType = SingleShaderBindings.GetSRVTypeStart();
const FRHIResource** RESTRICT SRVBindings = SingleShaderBindings.GetSRVStart();
const FShaderParameterInfo* RESTRICT SRVParameters = SingleShaderBindings.ParameterMapInfo.SRVs.GetData();
const uint32 NumSRVs = SingleShaderBindings.ParameterMapInfo.SRVs.Num();
for (uint32 SRVIndex = 0; SRVIndex < NumSRVs; SRVIndex++)
{
FShaderParameterInfo Parameter = SRVParameters[SRVIndex];
uint32 TypeByteIndex = SRVIndex / 8;
uint32 TypeBitIndex = SRVIndex - TypeByteIndex;
if (SRVType[TypeByteIndex] & (1 << TypeBitIndex))
{
FShaderResourceViewRHIParamRef SRV = (FShaderResourceViewRHIParamRef)SRVBindings[SRVIndex];
ensureMsgf(SRV, TEXT("Shader %s with vertex factory %s never set SRV at BaseIndex %u. This can cause GPU hangs, depending on how the shader uses it."),
Shader->GetType()->GetName(),
Shader->GetVertexFactoryType()->GetName(),
Parameter.BaseIndex);
}
else
{
FTextureRHIParamRef Texture = (FTextureRHIParamRef)SRVBindings[SRVIndex];
ensureMsgf(Texture, TEXT("Shader %s with vertex factory %s never set texture at BaseIndex %u. This can cause GPU hangs, depending on how the shader uses it."),
Shader->GetType()->GetName(),
Shader->GetVertexFactoryType()->GetName(),
Parameter.BaseIndex);
}
}
ShaderBindingDataPtr += ShaderLayout.GetDataSizeBytes();
}
#endif
}
void FMeshDrawShaderBindings::CopyFrom(const FMeshDrawShaderBindings& Other)
{
ShaderLayouts = Other.ShaderLayouts;
Allocate(Other.Size);
FPlatformMemory::Memcpy(GetData(), Other.GetData(), Size);
#if VALIDATE_UNIFORM_BUFFER_LIFETIME
uint8* ShaderBindingDataPtr = GetData();
for (int32 ShaderBindingsIndex = 0; ShaderBindingsIndex < ShaderLayouts.Num(); ShaderBindingsIndex++)
{
FMeshDrawSingleShaderBindings SingleShaderBindings(ShaderLayouts[ShaderBindingsIndex], ShaderBindingDataPtr);
const FUniformBufferRHIParamRef* RESTRICT UniformBufferBindings = SingleShaderBindings.GetUniformBufferStart();
const int32 NumUniformBuffers = SingleShaderBindings.ParameterMapInfo.UniformBuffers.Num();
for (int32 UniformBufferIndex = 0; UniformBufferIndex < NumUniformBuffers; UniformBufferIndex++)
{
FUniformBufferRHIParamRef UniformBuffer = UniformBufferBindings[UniformBufferIndex];
if (UniformBuffer)
{
UniformBuffer->NumMeshCommandReferencesForDebugging++;
}
}
ShaderBindingDataPtr += ShaderLayouts[ShaderBindingsIndex].GetDataSizeBytes();
}
#endif
}
void FMeshDrawCommand::SetShaders(FVertexDeclarationRHIParamRef VertexDeclaration, const FMeshProcessorShaders& Shaders, FGraphicsMinimalPipelineStateInitializer& PipelineState)
{
PipelineState.BoundShaderState = FBoundShaderStateInput(
VertexDeclaration
, GETSAFERHISHADER_VERTEX(Shaders.VertexShader)
#if PLATFORM_SUPPORTS_TESSELLATION_SHADERS
, GETSAFERHISHADER_HULL(Shaders.HullShader)
, GETSAFERHISHADER_DOMAIN(Shaders.DomainShader)
#endif
, GETSAFERHISHADER_PIXEL(Shaders.PixelShader)
#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
, GETSAFERHISHADER_GEOMETRY(Shaders.GeometryShader)
#endif
);
ShaderBindings.Initialize(Shaders);
}
#if RHI_RAYTRACING
void FRayTracingMeshCommand::SetShaders(const FMeshProcessorShaders& Shaders)
{
check(Shaders.RayHitGroupShader)
MaterialShaderIndex = Shaders.RayHitGroupShader->GetRayTracingMaterialLibraryIndex();
ShaderBindings.Initialize(Shaders);
}
#endif // RHI_RAYTRACING
void FMeshDrawCommand::SetDrawParametersAndFinalize(
const FMeshBatch& MeshBatch,
int32 BatchElementIndex,
FGraphicsMinimalPipelineStateId PipelineId,
const FMeshProcessorShaders* ShadersForDebugging)
{
const FMeshBatchElement& BatchElement = MeshBatch.Elements[BatchElementIndex];
check(!BatchElement.IndexBuffer || (BatchElement.IndexBuffer && BatchElement.IndexBuffer->IsInitialized() && BatchElement.IndexBuffer->IndexBufferRHI));
checkSlow(!BatchElement.bIsInstanceRuns);
IndexBuffer = BatchElement.IndexBuffer ? BatchElement.IndexBuffer->IndexBufferRHI : nullptr;
FirstIndex = BatchElement.FirstIndex;
NumPrimitives = BatchElement.NumPrimitives;
NumInstances = BatchElement.NumInstances;
if (NumPrimitives > 0)
{
VertexParams.BaseVertexIndex = BatchElement.BaseVertexIndex;
VertexParams.NumVertices = BatchElement.MaxVertexIndex - BatchElement.MinVertexIndex + 1;
checkf(!BatchElement.IndirectArgsBuffer, TEXT("FMeshBatchElement::NumPrimitives must be set to 0 when a IndirectArgsBuffer is used"));
}
else
{
checkf(BatchElement.IndirectArgsBuffer, TEXT("It is only valid to set BatchElement.NumPrimitives == 0 when a IndirectArgsBuffer is used"));
IndirectArgsBuffer = BatchElement.IndirectArgsBuffer;
}
Finalize(PipelineId, ShadersForDebugging);
}
void FMeshDrawShaderBindings::SetOnCommandList(FRHICommandList& RHICmdList, FBoundShaderStateInput Shaders, FShaderBindingState* StateCacheShaderBindings) const
{
const uint8* ShaderBindingDataPtr = GetData();
for (int32 ShaderBindingsIndex = 0; ShaderBindingsIndex < ShaderLayouts.Num(); ShaderBindingsIndex++)
{
FReadOnlyMeshDrawSingleShaderBindings SingleShaderBindings(ShaderLayouts[ShaderBindingsIndex], ShaderBindingDataPtr);
const EShaderFrequency Frequency = SingleShaderBindings.Frequency;
FShaderBindingState& ShaderBindingState = StateCacheShaderBindings[Frequency];
if (Frequency == SF_Vertex)
{
SetShaderBindings(RHICmdList, Shaders.VertexShaderRHI, SingleShaderBindings, ShaderBindingState);
}
else if (Frequency == SF_Pixel)
{
SetShaderBindings(RHICmdList, Shaders.PixelShaderRHI, SingleShaderBindings, ShaderBindingState);
}
else if (Frequency == SF_Hull)
{
SetShaderBindings(RHICmdList, Shaders.HullShaderRHI, SingleShaderBindings, ShaderBindingState);
}
else if (Frequency == SF_Domain)
{
SetShaderBindings(RHICmdList, Shaders.DomainShaderRHI, SingleShaderBindings, ShaderBindingState);
}
else if (Frequency == SF_Geometry)
{
SetShaderBindings(RHICmdList, Shaders.GeometryShaderRHI, SingleShaderBindings, ShaderBindingState);
}
else
{
checkf(0, TEXT("Unknown shader frequency"));
}
ShaderBindingDataPtr += ShaderLayouts[ShaderBindingsIndex].GetDataSizeBytes();
}
}
void FMeshDrawShaderBindings::SetOnCommandListForCompute(FRHICommandList& RHICmdList, FComputeShaderRHIParamRef Shader) const
{
check(ShaderLayouts.Num() == 1);
FReadOnlyMeshDrawSingleShaderBindings SingleShaderBindings(ShaderLayouts[0], GetData());
check(SingleShaderBindings.Frequency == SF_Compute);
SetShaderBindings(RHICmdList, Shader, SingleShaderBindings);
}
bool FMeshDrawShaderBindings::MatchesForDynamicInstancing(const FMeshDrawShaderBindings& Rhs) const
{
if (!(ShaderLayouts == Rhs.ShaderLayouts
&& Size == Rhs.Size))
{
return false;
}
const uint8* ShaderBindingDataPtr = GetData();
const uint8* OtherShaderBindingDataPtr = Rhs.GetData();
for (int32 ShaderBindingsIndex = 0; ShaderBindingsIndex < ShaderLayouts.Num(); ShaderBindingsIndex++)
{
FReadOnlyMeshDrawSingleShaderBindings SingleShaderBindings(ShaderLayouts[ShaderBindingsIndex], ShaderBindingDataPtr);
FReadOnlyMeshDrawSingleShaderBindings OtherSingleShaderBindings(Rhs.ShaderLayouts[ShaderBindingsIndex], OtherShaderBindingDataPtr);
if (SingleShaderBindings.ParameterMapInfo.SRVs.Num() > 0 || SingleShaderBindings.ParameterMapInfo.LooseParameterBuffers.Num() > 0 || SingleShaderBindings.ParameterMapInfo.TextureSamplers.Num() > 0)
{
// Not implemented. Note: this must match with GetDynamicInstancingHash.
return false;
}
const FUniformBufferRHIParamRef* UniformBufferBindings = SingleShaderBindings.GetUniformBufferStart();
const FUniformBufferRHIParamRef* OtherUniformBufferBindings = OtherSingleShaderBindings.GetUniformBufferStart();
for (int32 UniformBufferIndex = 0; UniformBufferIndex < SingleShaderBindings.ParameterMapInfo.UniformBuffers.Num(); UniformBufferIndex++)
{
FUniformBufferRHIParamRef UniformBuffer = UniformBufferBindings[UniformBufferIndex];
FUniformBufferRHIParamRef OtherUniformBuffer = OtherUniformBufferBindings[UniformBufferIndex];
if (UniformBuffer != OtherUniformBuffer)
{
return false;
}
}
ShaderBindingDataPtr += ShaderLayouts[ShaderBindingsIndex].GetDataSizeBytes();
OtherShaderBindingDataPtr += Rhs.ShaderLayouts[ShaderBindingsIndex].GetDataSizeBytes();
}
return true;
}
uint32 FMeshDrawShaderBindings::GetDynamicInstancingHash() const
{
uint32 Hash = FCrc::TypeCrc32(Size, 0);
for (const FMeshDrawShaderBindingsLayout& MeshDrawShaderBindingsLayout: ShaderLayouts)
{
Hash = HashCombine(MeshDrawShaderBindingsLayout.GetHash(), Hash);
}
const uint8* ShaderBindingDataPtr = GetData();
for (int32 ShaderBindingsIndex = 0; ShaderBindingsIndex < ShaderLayouts.Num(); ShaderBindingsIndex++)
{
FReadOnlyMeshDrawSingleShaderBindings SingleShaderBindings(ShaderLayouts[ShaderBindingsIndex], ShaderBindingDataPtr);
if (SingleShaderBindings.ParameterMapInfo.SRVs.Num() > 0 || SingleShaderBindings.ParameterMapInfo.LooseParameterBuffers.Num() > 0 || SingleShaderBindings.ParameterMapInfo.TextureSamplers.Num() > 0)
{
// Since this is not implemented, we must return a unique hash to minimize hash collisions.
// Note: this must match with MatchesForDynamicInstancing.
Hash = PointerHash(this, Hash);
return Hash;
}
const FUniformBufferRHIParamRef* UniformBufferBindings = SingleShaderBindings.GetUniformBufferStart();
for (int32 UniformBufferIndex = 0; UniformBufferIndex < SingleShaderBindings.ParameterMapInfo.UniformBuffers.Num(); UniformBufferIndex++)
{
FUniformBufferRHIParamRef UniformBuffer = UniformBufferBindings[UniformBufferIndex];
Hash = PointerHash(UniformBuffer, Hash);
}
ShaderBindingDataPtr += ShaderLayouts[ShaderBindingsIndex].GetDataSizeBytes();
}
return Hash;
}
void FMeshDrawCommand::SubmitDraw(
const FMeshDrawCommand& RESTRICT MeshDrawCommand,
const FGraphicsMinimalPipelineStateSet& GraphicsMinimalPipelineStateSet,
FVertexBufferRHIParamRef ScenePrimitiveIdsBuffer,
int32 PrimitiveIdOffset,
uint32 InstanceFactor,
FRHICommandList& RHICmdList,
FMeshDrawCommandStateCache& RESTRICT StateCache)
{
checkSlow(MeshDrawCommand.CachedPipelineId.IsValid());
#if WANTS_DRAW_MESH_EVENTS
TDrawEvent<FRHICommandList> MeshEvent;
if (GShowMaterialDrawEvents)
{
const FMaterial* Material = MeshDrawCommand.DebugData.Material;
FName ResourceName = MeshDrawCommand.DebugData.ResourceName;
FString DrawEventName = FString::Printf(
TEXT("%s %s"),
// Note: this is the parent's material name, not the material instance
*Material->GetFriendlyName(),
ResourceName.IsValid() ? *ResourceName.ToString() : TEXT(""));
const uint32 Instances = MeshDrawCommand.NumInstances * InstanceFactor;
if (Instances > 1)
{
BEGIN_DRAW_EVENTF(
RHICmdList,
MaterialEvent,
MeshEvent,
TEXT("%s %u instances"),
*DrawEventName,
Instances);
}
else
{
BEGIN_DRAW_EVENTF(RHICmdList, MaterialEvent, MeshEvent, *DrawEventName);
}
}
#endif
{
const FGraphicsMinimalPipelineStateInitializer& MeshPipelineState = MeshDrawCommand.CachedPipelineId.GetPipelineState(GraphicsMinimalPipelineStateSet);
if (MeshDrawCommand.CachedPipelineId.GetId() != StateCache.PipelineId)
{
FGraphicsPipelineStateInitializer GraphicsPSOInit = MeshPipelineState;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
StateCache.SetPipelineState(MeshDrawCommand.CachedPipelineId.GetId());
}
if (MeshDrawCommand.StencilRef != StateCache.StencilRef)
{
RHICmdList.SetStencilRef(MeshDrawCommand.StencilRef);
StateCache.StencilRef = MeshDrawCommand.StencilRef;
}
for (int32 VertexBindingIndex = 0; VertexBindingIndex < MeshDrawCommand.VertexStreams.Num(); VertexBindingIndex++)
{
const FVertexInputStream& Stream = MeshDrawCommand.VertexStreams[VertexBindingIndex];
if (MeshDrawCommand.PrimitiveIdStreamIndex != -1 && Stream.StreamIndex == MeshDrawCommand.PrimitiveIdStreamIndex)
{
RHICmdList.SetStreamSource(Stream.StreamIndex, ScenePrimitiveIdsBuffer, PrimitiveIdOffset);
StateCache.VertexStreams[Stream.StreamIndex] = Stream;
}
else if (StateCache.VertexStreams[Stream.StreamIndex] != Stream)
{
RHICmdList.SetStreamSource(Stream.StreamIndex, Stream.VertexBuffer, Stream.Offset);
StateCache.VertexStreams[Stream.StreamIndex] = Stream;
}
}
MeshDrawCommand.ShaderBindings.SetOnCommandList(RHICmdList, MeshPipelineState.BoundShaderState, StateCache.ShaderBindings);
}
if (MeshDrawCommand.IndexBuffer)
{
if (MeshDrawCommand.NumPrimitives > 0)
{
RHICmdList.DrawIndexedPrimitive(
MeshDrawCommand.IndexBuffer,
MeshDrawCommand.VertexParams.BaseVertexIndex,
0,
MeshDrawCommand.VertexParams.NumVertices,
MeshDrawCommand.FirstIndex,
MeshDrawCommand.NumPrimitives,
MeshDrawCommand.NumInstances * InstanceFactor
);
}
else
{
RHICmdList.DrawIndexedPrimitiveIndirect(
MeshDrawCommand.IndexBuffer,
MeshDrawCommand.IndirectArgsBuffer,
0
);
}
}
else
{
RHICmdList.DrawPrimitive(
MeshDrawCommand.VertexParams.BaseVertexIndex + MeshDrawCommand.FirstIndex,
MeshDrawCommand.NumPrimitives,
MeshDrawCommand.NumInstances * InstanceFactor
);
}
}
#if MESH_DRAW_COMMAND_DEBUG_DATA
void FMeshDrawCommand::SetDebugData(const FPrimitiveSceneProxy* PrimitiveSceneProxy, const FMaterial* Material, const FMaterialRenderProxy* MaterialRenderProxy, const FMeshProcessorShaders& UntypedShaders)
{
DebugData.PrimitiveSceneProxyIfNotUsingStateBuckets = PrimitiveSceneProxy;
DebugData.Material = Material;
DebugData.MaterialRenderProxy = MaterialRenderProxy;
DebugData.VertexShader = UntypedShaders.VertexShader;
DebugData.PixelShader = UntypedShaders.PixelShader;
DebugData.ResourceName = PrimitiveSceneProxy ? PrimitiveSceneProxy->GetResourceName() : FName();
}
#endif
void SubmitMeshDrawCommands(
const FMeshCommandOneFrameArray& VisibleMeshDrawCommands,
const FGraphicsMinimalPipelineStateSet& GraphicsMinimalPipelineStateSet,
FVertexBufferRHIParamRef PrimitiveIdsBuffer,
int32 BasePrimitiveIdsOffset,
bool bDynamicInstancing,
uint32 InstanceFactor,
FRHICommandList& RHICmdList)
{
SubmitMeshDrawCommandsRange(VisibleMeshDrawCommands, GraphicsMinimalPipelineStateSet, PrimitiveIdsBuffer, BasePrimitiveIdsOffset, bDynamicInstancing, 0, VisibleMeshDrawCommands.Num(), InstanceFactor, RHICmdList);
}
void SubmitMeshDrawCommandsRange(
const FMeshCommandOneFrameArray& VisibleMeshDrawCommands,
const FGraphicsMinimalPipelineStateSet& GraphicsMinimalPipelineStateSet,
FVertexBufferRHIParamRef PrimitiveIdsBuffer,
int32 BasePrimitiveIdsOffset,
bool bDynamicInstancing,
int32 StartIndex,
int32 NumMeshDrawCommands,
uint32 InstanceFactor,
FRHICommandList& RHICmdList)
{
FMeshDrawCommandStateCache StateCache;
INC_DWORD_STAT_BY(STAT_MeshDrawCalls, NumMeshDrawCommands);
for (int32 DrawCommandIndex = StartIndex; DrawCommandIndex < StartIndex + NumMeshDrawCommands; DrawCommandIndex++)
{
SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, MeshEvent, GEmitMeshDrawEvent != 0, TEXT("Mesh Draw"));
const FVisibleMeshDrawCommand& VisibleMeshDrawCommand = VisibleMeshDrawCommands[DrawCommandIndex];
const int32 PrimitiveIdBufferOffset = BasePrimitiveIdsOffset + (bDynamicInstancing ? VisibleMeshDrawCommand.PrimitiveIdBufferOffset : DrawCommandIndex) * sizeof(int32);
checkSlow(!bDynamicInstancing || VisibleMeshDrawCommand.PrimitiveIdBufferOffset >= 0);
FMeshDrawCommand::SubmitDraw(*VisibleMeshDrawCommand.MeshDrawCommand, GraphicsMinimalPipelineStateSet, PrimitiveIdsBuffer, PrimitiveIdBufferOffset, InstanceFactor, RHICmdList, StateCache);
}
}
void DrawDynamicMeshPassPrivate(
const FSceneView& View,
FRHICommandList& RHICmdList,
FMeshCommandOneFrameArray& VisibleMeshDrawCommands,
FDynamicMeshDrawCommandStorage& DynamicMeshDrawCommandStorage,
FGraphicsMinimalPipelineStateSet& GraphicsMinimalPipelineStateSet,
uint32 InstanceFactor)
{
if (VisibleMeshDrawCommands.Num() > 0)
{
const bool bDynamicInstancing = IsDynamicInstancingEnabled(View.GetFeatureLevel());
FVertexBufferRHIParamRef PrimitiveIdVertexBuffer = nullptr;
SortAndMergeDynamicPassMeshDrawCommands(View.GetFeatureLevel(), VisibleMeshDrawCommands, DynamicMeshDrawCommandStorage, PrimitiveIdVertexBuffer, InstanceFactor);
SubmitMeshDrawCommandsRange(VisibleMeshDrawCommands, GraphicsMinimalPipelineStateSet, PrimitiveIdVertexBuffer, 0, bDynamicInstancing, 0, VisibleMeshDrawCommands.Num(), InstanceFactor, RHICmdList);
}
}
FMeshDrawCommandSortKey CalculateMeshStaticSortKey(const FMeshMaterialShader* VertexShader, const FMeshMaterialShader* PixelShader)
{
FMeshDrawCommandSortKey SortKey;
SortKey.Generic.VertexShaderHash = PointerHash(VertexShader);
SortKey.Generic.PixelShaderHash = PointerHash(PixelShader);
return SortKey;
}
FMeshPassProcessor::FMeshPassProcessor(const FScene* InScene, ERHIFeatureLevel::Type InFeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
: Scene(InScene)
, FeatureLevel(InFeatureLevel)
, ViewIfDynamicMeshCommand(InViewIfDynamicMeshCommand)
, DrawListContext(InDrawListContext)
{
}
enum class EDrawingPolicyOverrideFlags
{
None = 0,
TwoSided = 1 << 0,
DitheredLODTransition = 1 << 1,
Wireframe = 1 << 2,
ReverseCullMode = 1 << 3,
};
ENUM_CLASS_FLAGS(EDrawingPolicyOverrideFlags);
struct FMeshDrawingPolicyOverrideSettings
{
EDrawingPolicyOverrideFlags MeshOverrideFlags = EDrawingPolicyOverrideFlags::None;
EPrimitiveType MeshPrimitiveType = PT_TriangleList;
};
FORCEINLINE_DEBUGGABLE FMeshDrawingPolicyOverrideSettings ComputeMeshOverrideSettings(const FMeshBatch& Mesh)
{
FMeshDrawingPolicyOverrideSettings OverrideSettings;
OverrideSettings.MeshPrimitiveType = (EPrimitiveType)Mesh.Type;
OverrideSettings.MeshOverrideFlags |= Mesh.bDisableBackfaceCulling ? EDrawingPolicyOverrideFlags::TwoSided : EDrawingPolicyOverrideFlags::None;
OverrideSettings.MeshOverrideFlags |= Mesh.bDitheredLODTransition ? EDrawingPolicyOverrideFlags::DitheredLODTransition : EDrawingPolicyOverrideFlags::None;
OverrideSettings.MeshOverrideFlags |= Mesh.bWireframe ? EDrawingPolicyOverrideFlags::Wireframe : EDrawingPolicyOverrideFlags::None;
OverrideSettings.MeshOverrideFlags |= Mesh.ReverseCulling ? EDrawingPolicyOverrideFlags::ReverseCullMode : EDrawingPolicyOverrideFlags::None;
return OverrideSettings;
}
ERasterizerFillMode FMeshPassProcessor::ComputeMeshFillMode(const FMeshBatch& Mesh, const FMaterial& InMaterialResource) const
{
const FMeshDrawingPolicyOverrideSettings InOverrideSettings = ComputeMeshOverrideSettings(Mesh);
const bool bMaterialResourceIsTwoSided = InMaterialResource.IsTwoSided();
const bool bIsWireframeMaterial = InMaterialResource.IsWireframe() || !!(InOverrideSettings.MeshOverrideFlags & EDrawingPolicyOverrideFlags::Wireframe);
return bIsWireframeMaterial ? FM_Wireframe : FM_Solid;
}
ERasterizerCullMode FMeshPassProcessor::ComputeMeshCullMode(const FMeshBatch& Mesh, const FMaterial& InMaterialResource) const
{
const FMeshDrawingPolicyOverrideSettings InOverrideSettings = ComputeMeshOverrideSettings(Mesh);
const bool bMaterialResourceIsTwoSided = InMaterialResource.IsTwoSided();
const bool bInTwoSidedOverride = !!(InOverrideSettings.MeshOverrideFlags & EDrawingPolicyOverrideFlags::TwoSided);
const bool bInReverseCullModeOverride = !!(InOverrideSettings.MeshOverrideFlags & EDrawingPolicyOverrideFlags::ReverseCullMode);
const bool bIsTwoSided = (bMaterialResourceIsTwoSided || bInTwoSidedOverride);
const bool bMeshRenderTwoSided = bIsTwoSided || bInTwoSidedOverride;
return bMeshRenderTwoSided ? CM_None : (bInReverseCullModeOverride ? CM_CCW : CM_CW);
}
int32 FMeshPassProcessor::GetDrawCommandPrimitiveId(const FPrimitiveSceneInfo* RESTRICT PrimitiveSceneInfo, const FMeshBatchElement& BatchElement) const
{
int32 DrawPrimitiveId;
if (UseGPUScene(GMaxRHIShaderPlatform, FeatureLevel))
{
if (BatchElement.PrimitiveIdMode == PrimID_FromPrimitiveSceneInfo)
{
ensureMsgf(BatchElement.PrimitiveUniformBufferResource == nullptr, TEXT("PrimitiveUniformBufferResource should not be setup when PrimitiveIdMode == PrimID_FromPrimitiveSceneInfo"));
DrawPrimitiveId = PrimitiveSceneInfo->GetIndex();
}
else if (BatchElement.PrimitiveIdMode == PrimID_DynamicPrimitiveShaderData)
{
DrawPrimitiveId = Scene->Primitives.Num() + BatchElement.DynamicPrimitiveShaderDataIndex;
}
else
{
check(BatchElement.PrimitiveIdMode == PrimID_ForceZero);
DrawPrimitiveId = 0;
}
}
else
{
DrawPrimitiveId = PrimitiveSceneInfo ? PrimitiveSceneInfo->GetIndex() : INT32_MAX;
}
return DrawPrimitiveId;
}
FCachedPassMeshDrawListContext::FCachedPassMeshDrawListContext(FCachedMeshDrawCommandInfo& InCommandInfo, FCachedPassMeshDrawList& InDrawList, FScene& InScene) :
CommandInfo(InCommandInfo),
DrawList(InDrawList),
Scene(InScene)
{
bUseStateBuckets = UseGPUScene(GMaxRHIShaderPlatform, GMaxRHIFeatureLevel);
}
FMeshDrawCommand& FCachedPassMeshDrawListContext::AddCommand(const FMeshDrawCommand& Initializer)
{
if (bUseStateBuckets)
{
MeshDrawCommandForStateBucketing = Initializer;
return MeshDrawCommandForStateBucketing;
}
else
{
// Only one FMeshDrawCommand supported per FStaticMesh in a pass
check(CommandInfo.CommandIndex == -1);
// Allocate at lowest free index so that 'r.DoLazyStaticMeshUpdate' can shrink the TSparseArray more effectively
CommandInfo.CommandIndex = DrawList.MeshDrawCommands.AddAtLowestFreeIndex(Initializer, DrawList.LowestFreeIndexSearchStart);
return DrawList.MeshDrawCommands[CommandInfo.CommandIndex];
}
}
void FCachedPassMeshDrawListContext::FinalizeCommand(
const FMeshBatch& MeshBatch,
int32 BatchElementIndex,
int32 DrawPrimitiveId,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode,
FMeshDrawCommandSortKey SortKey,
const FGraphicsMinimalPipelineStateInitializer& PipelineState,
const FMeshProcessorShaders* ShadersForDebugging,
FMeshDrawCommand& MeshDrawCommand)
{
// disabling this by default as it incurs a high cost in perf captures due to sheer volume. Recommendation is to re-enable locally if you need to profile this particular code.
// QUICK_SCOPE_CYCLE_COUNTER(STAT_FinalizeCachedMeshDrawCommand);
FGraphicsMinimalPipelineStateId PipelineId;
PipelineId = FGraphicsMinimalPipelineStateId::GetPersistentId(PipelineState);
MeshDrawCommand.SetDrawParametersAndFinalize(MeshBatch, BatchElementIndex, PipelineId, ShadersForDebugging);
if (bUseStateBuckets)
{
FSetElementId SetId = Scene.CachedMeshDrawCommandStateBuckets.FindId(MeshDrawCommand);
if (SetId.IsValidId())
{
Scene.CachedMeshDrawCommandStateBuckets[SetId].Num++;
}
else
{
#if MESH_DRAW_COMMAND_DEBUG_DATA
MeshDrawCommand.ClearDebugPrimitiveSceneProxy(); //When using State Buckets multiple PrimitiveSceneProxies use the same MeshDrawCommand, so The PrimitiveSceneProxy pointer can't be stored.
#endif
SetId = Scene.CachedMeshDrawCommandStateBuckets.Add(FMeshDrawCommandStateBucket(1, MeshDrawCommand));
}
check(CommandInfo.StateBucketId == -1);
CommandInfo.StateBucketId = SetId.AsInteger();
check(CommandInfo.CommandIndex == -1);
}
else
{
check(CommandInfo.CommandIndex != -1);
}
CommandInfo.SortKey = SortKey;
CommandInfo.MeshFillMode = MeshFillMode;
CommandInfo.MeshCullMode = MeshCullMode;
}
PassProcessorCreateFunction FPassProcessorManager::JumpTable[(int32)EShadingPath::Num][EMeshPass::Num] = {};
EMeshPassFlags FPassProcessorManager::Flags[(int32)EShadingPath::Num][EMeshPass::Num] = {};