Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/Lumen/LumenHardwareRayTracingMaterials.cpp
2025-05-18 13:04:45 +08:00

363 lines
18 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "RHIDefinitions.h"
#if RHI_RAYTRACING
#include "RenderCore.h"
#include "GlobalShader.h"
#include "ShaderParameterStruct.h"
#include "RayTracing/RayTracingScene.h"
#include "RayTracing/RayTracingMaterialHitShaders.h"
#include "RayTracing/RayTracing.h"
#include "RenderGraphUtils.h"
#include "DeferredShadingRenderer.h"
#include "PipelineStateCache.h"
#include "ScenePrivate.h"
#include "ShaderCompilerCore.h"
#include "Lumen/LumenHardwareRayTracingCommon.h"
#include "Nanite/NaniteRayTracing.h"
#include "Lumen/LumenReflections.h"
#include "Lumen/RayTracedTranslucency.h"
static TAutoConsoleVariable<float> CVarLumenHardwareRayTracingSkipBackFaceHitDistance(
TEXT("r.Lumen.HardwareRayTracing.SkipBackFaceHitDistance"),
5.0f,
TEXT("Distance to trace with backface culling enabled, useful when the Ray Tracing geometry doesn't match the GBuffer (Nanite Proxy geometry)."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarLumenHardwareRayTracingSkipTwoSidedHitDistance(
TEXT("r.Lumen.HardwareRayTracing.SkipTwoSidedHitDistance"),
1.0f,
TEXT("When the SkipBackFaceHitDistance is enabled, the first two-sided material hit within this distance will be skipped. This is useful for avoiding self-intersections with the Nanite fallback mesh on foliage, as SkipBackFaceHitDistance doesn't work on two sided materials."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
namespace LumenHardwareRayTracing
{
// 0 - hit group with EAvoidSelfIntersectionsMode::Disabled
// 1 - hit group with EAvoidSelfIntersectionsMode::AHS
constexpr uint32 NumHitGroups = 2;
};
IMPLEMENT_RT_PAYLOAD_TYPE(ERayTracingPayloadType::LumenMinimal, 16);
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FLumenHardwareRayTracingUniformBufferParameters, "LumenHardwareRayTracingUniformBuffer");
class FLumenHardwareRayTracingMaterialHitGroup : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FLumenHardwareRayTracingMaterialHitGroup)
SHADER_USE_ROOT_PARAMETER_STRUCT(FLumenHardwareRayTracingMaterialHitGroup, FGlobalShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FLumenHardwareRayTracingUniformBufferParameters, LumenHardwareRayTracingUniformBuffer)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_STRUCT_REF(FNaniteRayTracingUniformParameters, NaniteRayTracing)
SHADER_PARAMETER_STRUCT_REF(FSceneUniformParameters, Scene)
END_SHADER_PARAMETER_STRUCT()
class FAvoidSelfIntersectionsMode : SHADER_PERMUTATION_ENUM_CLASS("AVOID_SELF_INTERSECTIONS_MODE", LumenHardwareRayTracing::EAvoidSelfIntersectionsMode);
class FNaniteRayTracing : SHADER_PERMUTATION_BOOL("NANITE_RAY_TRACING");
using FPermutationDomain = TShaderPermutationDomain<FAvoidSelfIntersectionsMode, FNaniteRayTracing>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform)
&& (DoesPlatformSupportLumenGI(Parameters.Platform) || MegaLights::ShouldCompileShaders(Parameters.Platform));
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("VF_SUPPORTS_PRIMITIVE_SCENE_DATA"), 1);
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::LumenMinimal;
}
static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters)
{
return RayTracing::GetShaderBindingLayout(Parameters.Platform);
}
};
IMPLEMENT_GLOBAL_SHADER(FLumenHardwareRayTracingMaterialHitGroup, "/Engine/Private/Lumen/LumenHardwareRayTracingMaterials.usf", "closesthit=LumenHardwareRayTracingMaterialCHS anyhit=LumenHardwareRayTracingMaterialAHS", SF_RayHitGroup);
class FLumenHardwareRayTracingMaterialMS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FLumenHardwareRayTracingMaterialMS)
SHADER_USE_ROOT_PARAMETER_STRUCT(FLumenHardwareRayTracingMaterialMS, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform)
&& (DoesPlatformSupportLumenGI(Parameters.Platform) || MegaLights::ShouldCompileShaders(Parameters.Platform));
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::LumenMinimal;
}
static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters)
{
return RayTracing::GetShaderBindingLayout(Parameters.Platform);
}
using FParameters = FEmptyShaderParameters;
};
IMPLEMENT_GLOBAL_SHADER(FLumenHardwareRayTracingMaterialMS, "/Engine/Private/Lumen/LumenHardwareRayTracingMaterials.usf", "LumenHardwareRayTracingMaterialMS", SF_RayMiss);
void FDeferredShadingSceneRenderer::SetupLumenHardwareRayTracingUniformBuffer(FViewInfo& View)
{
FLumenHardwareRayTracingUniformBufferParameters LumenHardwareRayTracingUniformBufferParameters;
LumenHardwareRayTracingUniformBufferParameters.SkipBackFaceHitDistance = CVarLumenHardwareRayTracingSkipBackFaceHitDistance.GetValueOnRenderThread();
LumenHardwareRayTracingUniformBufferParameters.SkipTwoSidedHitDistance = CVarLumenHardwareRayTracingSkipTwoSidedHitDistance.GetValueOnRenderThread();
LumenHardwareRayTracingUniformBufferParameters.SkipTranslucent = LumenReflections::UseTranslucentRayTracing(View) || RayTracedTranslucency::IsEnabled(View) ? 0.0f : 1.0f;
LumenHardwareRayTracingUniformBufferParameters.DiffuseColorBoost = 1.0f / FMath::Max(View.FinalPostProcessSettings.LumenDiffuseColorBoost, 1.0f);
View.LumenHardwareRayTracingUniformBuffer = TUniformBufferRef<FLumenHardwareRayTracingUniformBufferParameters>::CreateUniformBufferImmediate(LumenHardwareRayTracingUniformBufferParameters, UniformBuffer_SingleFrame);
}
uint32 CalculateLumenHardwareRayTracingUserData(const FRayTracingShaderBindingData& RTShaderBinding, const FRayTracingMeshCommand& MeshCommand)
{
const bool bDynamicGeometry = RTShaderBinding.RayTracingGeometry->GetInitializer().bAllowUpdate;
return (MeshCommand.MaterialShaderIndex & LUMEN_MATERIAL_SHADER_INDEX_MASK)
| (((bDynamicGeometry != 0) & 0x01) << 27)
| (((MeshCommand.bAlphaMasked != 0) & 0x01) << 28)
| (((MeshCommand.bCastRayTracedShadows != 0) & 0x01) << 29)
| (((MeshCommand.bTwoSided != 0) & 0x01) << 30)
| (((MeshCommand.bIsTranslucent != 0) & 0x01) << 31);
}
// TODO: This should be moved into FRayTracingScene and used as a base for other effects. There is not need for it to be Lumen specific.
void FDeferredShadingSceneRenderer::SetupLumenHardwareRayTracingHitGroupBuffer(FRDGBuilder& GraphBuilder, FViewInfo& View)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FDeferredShadingSceneRenderer::BuildLumenHardwareRayTracingHitGroupData);
const uint32 NumTotalSegments = FMath::Max(Scene->RayTracingSBT.GetNumGeometrySegments(), 1u);
FRDGUploadData<Lumen::FHitGroupRootConstants> HitGroupData(GraphBuilder, NumTotalSegments);
// if buffer is persistent then dirty bindings could be used to perform partial update
const uint32 NumTotalShaderBindings = View.VisibleRayTracingShaderBindings.Num();
if(NumTotalShaderBindings > 0)
{
const uint32 TargetBindingsPerTask = 512;
// Distribute work evenly to the available task graph workers based on NumTotalShaderBindings.
const uint32 NumThreads = FMath::Min(FTaskGraphInterface::Get().GetNumWorkerThreads(), CVarRHICmdWidth.GetValueOnRenderThread());
const uint32 NumTasks = FMath::Min(NumThreads, FMath::DivideAndRoundUp(NumTotalShaderBindings, TargetBindingsPerTask));
const uint32 NumBindingsPerTask = FMath::DivideAndRoundUp(NumTotalShaderBindings, NumTasks);
for (uint32 TaskIndex = 0; TaskIndex < NumTasks; ++TaskIndex)
{
const uint32 FirstTaskBindingIndex = TaskIndex * NumBindingsPerTask;
const FRayTracingShaderBindingData* RTShaderBindings = View.VisibleRayTracingShaderBindings.GetData() + FirstTaskBindingIndex;
const uint32 NumBindings = FMath::Min(NumBindingsPerTask, NumTotalShaderBindings - FirstTaskBindingIndex);
GraphBuilder.AddSetupTask([RTShaderBindings, NumBindings, HitGroupData]()
{
TRACE_CPUPROFILER_EVENT_SCOPE(BuildLumenHardwareRayTracingHitGroupDataTask);
for (uint32 BindingIndex = 0; BindingIndex < NumBindings; ++BindingIndex)
{
const FRayTracingShaderBindingData& RTShaderBinding = RTShaderBindings[BindingIndex];
const FRayTracingMeshCommand& MeshCommand = *RTShaderBinding.RayTracingMeshCommand;
// Only store hit group data for single shader slot for lightwight SBT
// NOTE: InstanceContributionToHitGroupIndex stored in instance data is also divided by RAY_TRACING_NUM_SHADER_SLOTS in the shader
const uint32 HitGroupIndex = RTShaderBinding.SBTRecordIndex / RAY_TRACING_NUM_SHADER_SLOTS;
HitGroupData[HitGroupIndex].UserData = CalculateLumenHardwareRayTracingUserData(RTShaderBinding, MeshCommand);
}
});
}
}
View.LumenHardwareRayTracingHitDataBuffer = CreateStructuredBuffer(GraphBuilder, TEXT("LumenHardwareRayTracingHitDataBuffer"), HitGroupData);
}
void FDeferredShadingSceneRenderer::CreateLumenHardwareRayTracingMaterialPipeline(
FRDGBuilder& GraphBuilder,
const TArrayView<FRHIRayTracingShader*>& RayGenShaderTable,
uint32& OutMaxLocalBindingDataSize)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FDeferredShadingSceneRenderer::CreateLumenHardwareRayTracingMaterialPipeline);
SCOPE_CYCLE_COUNTER(STAT_CreateLumenRayTracingPipeline);
FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(ShaderPlatform);
FRHICommandList& RHICmdList = GraphBuilder.RHICmdList;
FRayTracingPipelineStateInitializer Initializer;
const FShaderBindingLayout* ShaderBindingLayout = RayTracing::GetShaderBindingLayout(ShaderPlatform);
if (ShaderBindingLayout)
{
Initializer.ShaderBindingLayout = &ShaderBindingLayout->RHILayout;
}
Initializer.SetRayGenShaderTable(RayGenShaderTable);
Initializer.MaxPayloadSizeInBytes = GetRayTracingPayloadTypeMaxSize(ERayTracingPayloadType::LumenMinimal);
// Get the ray tracing materials
FLumenHardwareRayTracingMaterialHitGroup::FPermutationDomain PermutationVector;
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FAvoidSelfIntersectionsMode>(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::Disabled);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FNaniteRayTracing>(false);
auto HitGroupShader = ShaderMap->GetShader<FLumenHardwareRayTracingMaterialHitGroup>(PermutationVector);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FAvoidSelfIntersectionsMode>(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::AHS);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FNaniteRayTracing>(false);
auto HitGroupShaderWithAvoidSelfIntersections = ShaderMap->GetShader<FLumenHardwareRayTracingMaterialHitGroup>(PermutationVector);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FAvoidSelfIntersectionsMode>(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::Disabled);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FNaniteRayTracing>(true);
auto HitGroupShaderNaniteRT = ShaderMap->GetShader<FLumenHardwareRayTracingMaterialHitGroup>(PermutationVector);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FAvoidSelfIntersectionsMode>(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::AHS);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FNaniteRayTracing>(true);
auto HitGroupShaderNaniteRTWithAvoidSelfIntersections = ShaderMap->GetShader<FLumenHardwareRayTracingMaterialHitGroup>(PermutationVector);
FRHIRayTracingShader* HitShaderTable[] = {
HitGroupShader.GetRayTracingShader(),
HitGroupShaderWithAvoidSelfIntersections.GetRayTracingShader(),
HitGroupShaderNaniteRT.GetRayTracingShader(),
HitGroupShaderNaniteRTWithAvoidSelfIntersections.GetRayTracingShader()
};
Initializer.SetHitGroupTable(HitShaderTable);
auto MissShader = ShaderMap->GetShader<FLumenHardwareRayTracingMaterialMS>();
FRHIRayTracingShader* MissShaderTable[] = { MissShader.GetRayTracingShader() };
Initializer.SetMissShaderTable(MissShaderTable);
OutMaxLocalBindingDataSize = FMath::Max(Initializer.GetMaxLocalBindingDataSize(), OutMaxLocalBindingDataSize);
FRayTracingPipelineState* PipelineState = PipelineStateCache::GetAndOrCreateRayTracingPipelineState(RHICmdList, Initializer);
// Send RTPSO to all views since they all share the same one
EnumerateLinkedViews([PipelineState](FViewInfo& View)
{
if (View.bHasAnyRayTracingPass)
{
View.LumenRayTracingData.PipelineState = PipelineState;
}
return true;
});
}
void FDeferredShadingSceneRenderer::SetupLumenHardwareRayTracingHitGroupBindings(FRDGBuilder& GraphBuilder, FViewInfo& View)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FDeferredShadingSceneRenderer::SetupLumenHardwareRayTracingHitGroupBindings);
FRHIUniformBuffer* LumenHardwareRayTracingUniformBuffer = View.LumenHardwareRayTracingUniformBuffer;
struct FBinding
{
int32 ShaderIndexInPipeline;
uint32 NumUniformBuffers;
FRHIUniformBuffer** UniformBufferArray;
};
auto SetupBinding = [&](FLumenHardwareRayTracingMaterialHitGroup::FPermutationDomain PermutationVector)
{
auto Shader = View.ShaderMap->GetShader<FLumenHardwareRayTracingMaterialHitGroup>(PermutationVector);
auto HitGroupShader = Shader.GetRayTracingShader();
FBinding Binding;
Binding.ShaderIndexInPipeline = FindRayTracingHitGroupIndex(View.LumenRayTracingData.PipelineState, HitGroupShader, true);
Binding.NumUniformBuffers = Shader->ParameterMapInfo.UniformBuffers.Num();
Binding.UniformBufferArray = (FRHIUniformBuffer**)View.LumenRayTracingData.MaterialBindingsMemory.Alloc(sizeof(FRHIUniformBuffer*) * Binding.NumUniformBuffers, alignof(FRHIUniformBuffer*));
const auto& LumenHardwareRayTracingUniformBufferParameter = Shader->GetUniformBufferParameter<FLumenHardwareRayTracingUniformBufferParameters>();
const auto& ViewUniformBufferParameter = Shader->GetUniformBufferParameter<FViewUniformShaderParameters>();
const auto& SceneUniformBufferParameter = Shader->GetUniformBufferParameter<FSceneUniformParameters>();
const auto& NaniteUniformBufferParameter = Shader->GetUniformBufferParameter<FNaniteRayTracingUniformParameters>();
if (LumenHardwareRayTracingUniformBufferParameter.IsBound())
{
check(LumenHardwareRayTracingUniformBufferParameter.GetBaseIndex() < Binding.NumUniformBuffers);
Binding.UniformBufferArray[LumenHardwareRayTracingUniformBufferParameter.GetBaseIndex()] = LumenHardwareRayTracingUniformBuffer;
}
if (ViewUniformBufferParameter.IsBound())
{
check(ViewUniformBufferParameter.GetBaseIndex() < Binding.NumUniformBuffers);
Binding.UniformBufferArray[ViewUniformBufferParameter.GetBaseIndex()] = View.ViewUniformBuffer.GetReference();
}
if (SceneUniformBufferParameter.IsBound())
{
check(SceneUniformBufferParameter.GetBaseIndex() < Binding.NumUniformBuffers);
Binding.UniformBufferArray[SceneUniformBufferParameter.GetBaseIndex()] = GetSceneUniforms().GetBufferRHI(GraphBuilder);
}
if (NaniteUniformBufferParameter.IsBound())
{
check(NaniteUniformBufferParameter.GetBaseIndex() < Binding.NumUniformBuffers);
Binding.UniformBufferArray[NaniteUniformBufferParameter.GetBaseIndex()] = Nanite::GRayTracingManager.GetUniformBufferRHI(GraphBuilder);
}
return Binding;
};
FBinding* ShaderBindings = (FBinding*)View.LumenRayTracingData.MaterialBindingsMemory.Alloc(sizeof(FBinding) * LumenHardwareRayTracing::NumHitGroups, alignof(FBinding));
FBinding* ShaderBindingsNaniteRT = (FBinding*)View.LumenRayTracingData.MaterialBindingsMemory.Alloc(sizeof(FBinding) * LumenHardwareRayTracing::NumHitGroups, alignof(FBinding));
{
FLumenHardwareRayTracingMaterialHitGroup::FPermutationDomain PermutationVector;
{
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FAvoidSelfIntersectionsMode>(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::Disabled);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FNaniteRayTracing>(false);
ShaderBindings[0] = SetupBinding(PermutationVector);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FAvoidSelfIntersectionsMode>(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::AHS);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FNaniteRayTracing>(false);
ShaderBindings[1] = SetupBinding(PermutationVector);
}
{
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FAvoidSelfIntersectionsMode>(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::Disabled);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FNaniteRayTracing>(true);
ShaderBindingsNaniteRT[0] = SetupBinding(PermutationVector);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FAvoidSelfIntersectionsMode>(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::AHS);
PermutationVector.Set<FLumenHardwareRayTracingMaterialHitGroup::FNaniteRayTracing>(true);
ShaderBindingsNaniteRT[1] = SetupBinding(PermutationVector);
}
}
AddRayTracingLocalShaderBindingWriterTasks(GraphBuilder, View.DirtyPersistentRayTracingShaderBindings, View.LumenRayTracingData.MaterialBindings,
[ShaderBindings, ShaderBindingsNaniteRT](const FRayTracingShaderBindingData& RTShaderBindingData, FRayTracingLocalShaderBindingWriter* BindingWriter)
{
const FRayTracingMeshCommand& MeshCommand = *RTShaderBindingData.RayTracingMeshCommand;
for (uint32 SlotIndex = 0; SlotIndex < LumenHardwareRayTracing::NumHitGroups; ++SlotIndex)
{
FRayTracingLocalShaderBindings& Binding = BindingWriter->AddWithExternalParameters();
Binding.RecordIndex = RTShaderBindingData.SBTRecordIndex + SlotIndex;
Binding.Geometry = RTShaderBindingData.RayTracingGeometry;
Binding.SegmentIndex = MeshCommand.GeometrySegmentIndex;
Binding.BindingType = RTShaderBindingData.BindingType;
Binding.UserData = CalculateLumenHardwareRayTracingUserData(RTShaderBindingData, MeshCommand);
const FBinding& LumenBinding = MeshCommand.IsUsingNaniteRayTracing() ? ShaderBindingsNaniteRT[SlotIndex] : ShaderBindings[SlotIndex];
Binding.ShaderIndexInPipeline = LumenBinding.ShaderIndexInPipeline;
Binding.UniformBuffers = LumenBinding.UniformBufferArray;
Binding.NumUniformBuffers = LumenBinding.NumUniformBuffers;
}
});
}
#endif // RHI_RAYTRACING