// 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 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 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; 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::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 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& 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(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::Disabled); PermutationVector.Set(false); auto HitGroupShader = ShaderMap->GetShader(PermutationVector); PermutationVector.Set(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::AHS); PermutationVector.Set(false); auto HitGroupShaderWithAvoidSelfIntersections = ShaderMap->GetShader(PermutationVector); PermutationVector.Set(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::Disabled); PermutationVector.Set(true); auto HitGroupShaderNaniteRT = ShaderMap->GetShader(PermutationVector); PermutationVector.Set(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::AHS); PermutationVector.Set(true); auto HitGroupShaderNaniteRTWithAvoidSelfIntersections = ShaderMap->GetShader(PermutationVector); FRHIRayTracingShader* HitShaderTable[] = { HitGroupShader.GetRayTracingShader(), HitGroupShaderWithAvoidSelfIntersections.GetRayTracingShader(), HitGroupShaderNaniteRT.GetRayTracingShader(), HitGroupShaderNaniteRTWithAvoidSelfIntersections.GetRayTracingShader() }; Initializer.SetHitGroupTable(HitShaderTable); auto MissShader = ShaderMap->GetShader(); 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(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(); const auto& ViewUniformBufferParameter = Shader->GetUniformBufferParameter(); const auto& SceneUniformBufferParameter = Shader->GetUniformBufferParameter(); const auto& NaniteUniformBufferParameter = Shader->GetUniformBufferParameter(); 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(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::Disabled); PermutationVector.Set(false); ShaderBindings[0] = SetupBinding(PermutationVector); PermutationVector.Set(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::AHS); PermutationVector.Set(false); ShaderBindings[1] = SetupBinding(PermutationVector); } { PermutationVector.Set(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::Disabled); PermutationVector.Set(true); ShaderBindingsNaniteRT[0] = SetupBinding(PermutationVector); PermutationVector.Set(LumenHardwareRayTracing::EAvoidSelfIntersectionsMode::AHS); PermutationVector.Set(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