// Copyright Epic Games, Inc. All Rights Reserved. #include "RayTracingDecals.h" #include "RHIDefinitions.h" #if RHI_RAYTRACING #include "RendererPrivate.h" #include "RayTracingMaterialHitShaders.h" #include "DecalRenderingShared.h" #include "RayTracingTypes.h" #include "RayTracingDefinitions.h" #include "SceneProxies/DeferredDecalProxy.h" #include TAutoConsoleVariable CVarDecalGridResolution( TEXT("r.RayTracing.DecalGrid.Resolution"), 256, TEXT("Controls the resolution of the 2D decal grid used to cull irrelevant decal from calculations (default = 256)\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarDecalGridMaxCount( TEXT("r.RayTracing.DecalGrid.MaxCount"), 128, TEXT("Controls the maximum number of decals per cell in the 2D decal grid. The minimum of this value and the number of decal in the scene is used. (default = 128)\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarDecalGridAxis( TEXT("r.RayTracing.DecalGrid.Axis"), -1, TEXT("Choose the coordinate axis along which to project the decal grid (default = -1, automatic)\n"), ECVF_RenderThreadSafe ); enum class EDecalsWriteFlags : uint32 { None = 0, BaseColor = (1 << 0), Normal = (1 << 1), RoughnessSpecularMetallic = (1 << 2), Emissive = (1 << 3), AmbientOcclusion = (1 << 4), }; static uint32 CalculateDecalWriteFlags(FDecalBlendDesc DecalBlendDesc) { uint32 Flags = (uint32)EDecalsWriteFlags::None; #define SET_FLAG(BOOL_VAL, WRITE_FLAG) Flags |= uint32(DecalBlendDesc.BOOL_VAL ? EDecalsWriteFlags::WRITE_FLAG : EDecalsWriteFlags::None) SET_FLAG(bWriteBaseColor, BaseColor); SET_FLAG(bWriteNormal, Normal); SET_FLAG(bWriteRoughnessSpecularMetallic, RoughnessSpecularMetallic); SET_FLAG(bWriteEmissive, Emissive); SET_FLAG(bWriteAmbientOcclusion, AmbientOcclusion); #undef SET_FLAG return Flags; } IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FRayTracingDecals, "RayTracingDecals"); BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FDecalParametersRayTracing, ) SHADER_PARAMETER(FMatrix44f, WorldToDecal) SHADER_PARAMETER(FMatrix44f, DecalToWorld) SHADER_PARAMETER(FVector3f, DecalPositionHigh) SHADER_PARAMETER(FVector3f, DecalToWorldInvScale) SHADER_PARAMETER(FVector3f, DecalOrientation) SHADER_PARAMETER(FVector2f, DecalParams) SHADER_PARAMETER(FVector4f, DecalColorParam) SHADER_PARAMETER(uint32, DecalWriteFlags) END_GLOBAL_SHADER_PARAMETER_STRUCT() IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FDecalParametersRayTracing, "RayTracingDecalParameters"); static TUniformBufferRef CreateDecalParametersBuffer( const FViewInfo& View, EShaderPlatform Platform, const FVisibleDecal& VisibleDecal, EUniformBufferUsage Usage) { FDecalParametersRayTracing Parameters; const FMatrix DecalToWorldMatrix = VisibleDecal.ComponentTrans.ToMatrixWithScale(); const FMatrix WorldToDecalMatrix = VisibleDecal.ComponentTrans.ToInverseMatrixWithScale(); const FDFVector3 AbsoluteOrigin(DecalToWorldMatrix.GetOrigin()); const FVector3f PositionHigh = AbsoluteOrigin.High; const FMatrix44f RelativeDecalToWorldMatrix = FDFMatrix::MakeToRelativeWorldMatrix(PositionHigh, DecalToWorldMatrix).M; const FVector3f OrientationVector = (FVector3f)VisibleDecal.ComponentTrans.GetUnitAxis(EAxis::X); Parameters.DecalPositionHigh = PositionHigh; Parameters.WorldToDecal = FMatrix44f(FTranslationMatrix(-View.ViewMatrices.GetPreViewTranslation()) * WorldToDecalMatrix); Parameters.DecalToWorld = RelativeDecalToWorldMatrix; Parameters.DecalToWorldInvScale = static_cast(DecalToWorldMatrix.GetScaleVector().Reciprocal()); Parameters.DecalOrientation = OrientationVector; Parameters.DecalColorParam = VisibleDecal.DecalColor; float LifetimeAlpha = 1.0f; // Certain engine captures (e.g. environment reflection) don't have a tick. Default to fully opaque. if (View.Family->Time.GetWorldTimeSeconds()) { LifetimeAlpha = FMath::Clamp(FMath::Min(View.Family->Time.GetWorldTimeSeconds() * -VisibleDecal.InvFadeDuration + VisibleDecal.FadeStartDelayNormalized, View.Family->Time.GetWorldTimeSeconds() * VisibleDecal.InvFadeInDuration + VisibleDecal.FadeInStartDelayNormalized), 0.0f, 1.0f); } Parameters.DecalParams = FVector2f(VisibleDecal.FadeAlpha, LifetimeAlpha); Parameters.DecalColorParam = VisibleDecal.DecalColor; Parameters.DecalWriteFlags = CalculateDecalWriteFlags(VisibleDecal.BlendDesc); return CreateUniformBufferImmediate(Parameters, Usage); } class FRayTracingBuildDecalGridCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FRayTracingBuildDecalGridCS) SHADER_USE_PARAMETER_STRUCT(FRayTracingBuildDecalGridCS, FGlobalShader) static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompileRayTracingShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.CompilerFlags.Add(CFLAG_WarningsAsErrors); OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads); OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_X"), FComputeShaderUtils::kGolden2DGroupSize); OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_Y"), FComputeShaderUtils::kGolden2DGroupSize); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, SceneDecals) SHADER_PARAMETER(uint32, SceneDecalCount) SHADER_PARAMETER(FVector3f, SceneDecalsTranslatedBoundMin) SHADER_PARAMETER(FVector3f, SceneDecalsTranslatedBoundMax) SHADER_PARAMETER(unsigned, DecalGridResolution) SHADER_PARAMETER(unsigned, DecalGridMaxCount) SHADER_PARAMETER(int, DecalGridAxis) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2DArray, RWDecalGrid) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWDecalGridData) END_SHADER_PARAMETER_STRUCT() }; IMPLEMENT_SHADER_TYPE(, FRayTracingBuildDecalGridCS, TEXT("/Engine/Private/RayTracing/RayTracingBuildDecalGrid.usf"), TEXT("BuildDecalGridCS"), SF_Compute); class FRayTracingDecalMaterialShader : public FMaterialShader { DECLARE_SHADER_TYPE(FRayTracingDecalMaterialShader, Material); public: FRayTracingDecalMaterialShader() = default; FRayTracingDecalMaterialShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FMaterialShader(Initializer) { DecalParameter.Bind(Initializer.ParameterMap, TEXT("RaytracingDecalParameters")); } static bool ShouldCompilePermutation(const FMaterialShaderPermutationParameters& Parameters) { if (!ShouldCompileRayTracingCallableShadersForProject(Parameters.Platform)) { // is raytracing enabled at all? return false; } if (!FDataDrivenShaderPlatformInfo::GetSupportsPathTracing(Parameters.Platform)) { // this shader is currently only used by the path tracer return false; } if (Parameters.MaterialParameters.MaterialDomain != MD_DeferredDecal) { // only compile callable shader permutation of deferred decal material return false; } return true; } static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); // DECAL_PRIMITIVE informs material templates which functions to expose when rendering decals. OutEnvironment.SetDefine(TEXT("DECAL_PRIMITIVE"), 1); } static bool ValidateCompiledResult(EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray& OutError) { if (ParameterMap.ContainsParameterAllocation(FSceneTextureUniformParameters::FTypeInfo::GetStructMetadata()->GetShaderVariableName())) { OutError.Add(TEXT("Ray tracing callable shaders cannot read from the SceneTexturesStruct.")); return false; } for (const auto& It : ParameterMap.GetParameterMap()) { const FParameterAllocation& ParamAllocation = It.Value; if (ParamAllocation.Type != EShaderParameterType::UniformBuffer) { OutError.Add(FString::Printf(TEXT("Invalid ray tracing callable shader parameter '%s'. Only uniform buffers and loose data parameters are supported."), *(It.Key))); return false; } } return true; } static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId) { return ERayTracingPayloadType::Decals; } static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters) { return RayTracing::GetShaderBindingLayout(Parameters.Platform); } void GetShaderBindings( const FScene* Scene, ERHIFeatureLevel::Type FeatureLevel, const FMaterialRenderProxy& MaterialRenderProxy, const FMaterial& Material, const FViewInfo& View, const TUniformBufferRef& DecalParameters, FMeshDrawSingleShaderBindings& ShaderBindings) const { FMaterialShader::GetShaderBindings(Scene, FeatureLevel, MaterialRenderProxy, Material, ShaderBindings); ShaderBindings.Add(GetUniformBufferParameter(), View.ViewUniformBuffer); // Similar to DecalRendering::SetShader(...) uses GIdentityPrimitiveUniformBuffer // We could potentially bind the actual primitive uniform buffer ShaderBindings.Add(GetUniformBufferParameter(), GIdentityPrimitiveUniformBuffer); ShaderBindings.Add(DecalParameter, DecalParameters); } private: LAYOUT_FIELD(FShaderUniformBufferParameter, DecalParameter); }; IMPLEMENT_RT_PAYLOAD_TYPE(ERayTracingPayloadType::Decals, 48); IMPLEMENT_SHADER_TYPE(, FRayTracingDecalMaterialShader, TEXT("/Engine/Private/RayTracing/RayTracingDecalMaterialShader.usf"), TEXT("RayTracingDecalMaterialShader"), SF_RayCallable); static bool DecalNeedsAnyHitShader(EBlendMode BlendMode) { return BlendMode != BLEND_Opaque; } template class TRayTracingDecalMaterial : public FMeshMaterialShader { DECLARE_SHADER_TYPE(TRayTracingDecalMaterial, MeshMaterial); public: TRayTracingDecalMaterial() = default; TRayTracingDecalMaterial(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer) : FMeshMaterialShader(Initializer) {} static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters) { if (!ShouldCompileRayTracingShadersForProject(Parameters.Platform)) { // is raytracing enabled at all? return false; } if (!FDataDrivenShaderPlatformInfo::GetSupportsPathTracing(Parameters.Platform)) { // this shader is currently only used by the path tracer return false; } if (!Parameters.VertexFactoryType->SupportsRayTracing()) { // does the VF support ray tracing at all? return false; } if (Parameters.MaterialParameters.MaterialDomain != MD_DeferredDecal) { return false; } if (DecalNeedsAnyHitShader(Parameters.MaterialParameters.BlendMode) != bUseAnyHitShader) { // the anyhit permutation is only required if the material is masked or has a non-opaque blend mode return false; } return true; } static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FMeshMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); check(Parameters.MaterialParameters.MaterialDomain == MD_DeferredDecal); FDecalBlendDesc DecalBlendDesc = DecalRendering::ComputeDecalBlendDesc(Parameters.Platform, Parameters.MaterialParameters); OutEnvironment.SetDefine(TEXT("DECAL_PAYLOAD_FLAGS"), CalculateDecalWriteFlags(DecalBlendDesc)); OutEnvironment.SetDefine(TEXT("USE_MATERIAL_ANY_HIT_SHADER"), bUseAnyHitShader ? 1 : 0); } static bool ValidateCompiledResult(EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray& OutError) { if (ParameterMap.ContainsParameterAllocation(FSceneTextureUniformParameters::FTypeInfo::GetStructMetadata()->GetShaderVariableName())) { OutError.Add(TEXT("Ray tracing closest hit shaders cannot read from the SceneTexturesStruct.")); return false; } for (const auto& It : ParameterMap.GetParameterMap()) { const FParameterAllocation& ParamAllocation = It.Value; if (ParamAllocation.Type != EShaderParameterType::UniformBuffer && ParamAllocation.Type != EShaderParameterType::LooseData) { OutError.Add(FString::Printf(TEXT("Invalid ray tracing shader parameter '%s'. Only uniform buffers and loose data parameters are supported."), *(It.Key))); return false; } } return true; } static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId) { return ERayTracingPayloadType::Decals; } static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters) { return RayTracing::GetShaderBindingLayout(Parameters.Platform); } }; using FRayTracingDecalMaterialCHS = TRayTracingDecalMaterial; using FRayTracingDecalMaterialCHS_AHS = TRayTracingDecalMaterial; IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, FRayTracingDecalMaterialCHS, TEXT("/Engine/Private/RayTracing/RayTracingDecalMaterialShader.usf"), TEXT("closesthit=RayTracingDecalMaterialCHS"), SF_RayHitGroup); IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, FRayTracingDecalMaterialCHS_AHS, TEXT("/Engine/Private/RayTracing/RayTracingDecalMaterialShader.usf"), TEXT("closesthit=RayTracingDecalMaterialCHS anyhit=RayTracingDecalMaterialAHS"), SF_RayHitGroup); FShaderType* GetRayTracingDecalMaterialShaderType(EBlendMode BlendMode) { if (DecalNeedsAnyHitShader(BlendMode)) { return &FRayTracingDecalMaterialCHS_AHS::GetStaticType(); } else { return &FRayTracingDecalMaterialCHS::GetStaticType(); } } class FDefaultOpaqueMeshDecalHitGroup : public FGlobalShader { DECLARE_GLOBAL_SHADER(FDefaultOpaqueMeshDecalHitGroup) SHADER_USE_ROOT_PARAMETER_STRUCT(FDefaultOpaqueMeshDecalHitGroup, FGlobalShader) static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompileRayTracingShadersForProject(Parameters.Platform); } static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId) { return ERayTracingPayloadType::Decals; } static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters) { return RayTracing::GetShaderBindingLayout(Parameters.Platform); } using FParameters = FEmptyShaderParameters; }; IMPLEMENT_SHADER_TYPE(, FDefaultOpaqueMeshDecalHitGroup, TEXT("/Engine/Private/RayTracing/RayTracingDefaultDecalHitShader.usf"), TEXT("closesthit=RayTracingDefaultOpaqueDecalMaterialCHS"), SF_RayHitGroup); class FDefaultHiddenMeshDecalHitGroup : public FGlobalShader { DECLARE_GLOBAL_SHADER(FDefaultHiddenMeshDecalHitGroup) SHADER_USE_ROOT_PARAMETER_STRUCT(FDefaultHiddenMeshDecalHitGroup, FGlobalShader) static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompileRayTracingShadersForProject(Parameters.Platform); } static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId) { return ERayTracingPayloadType::Decals; } static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters) { return RayTracing::GetShaderBindingLayout(Parameters.Platform); } using FParameters = FEmptyShaderParameters; }; IMPLEMENT_SHADER_TYPE(, FDefaultHiddenMeshDecalHitGroup, TEXT("/Engine/Private/RayTracing/RayTracingDefaultDecalHitShader.usf"), TEXT("closesthit=RayTracingDefaultHiddenDecalMaterialCHS anyhit=RayTracingDefaultHiddenDecalMaterialAHS"), SF_RayHitGroup); FRHIRayTracingShader* GetDefaultOpaqueMeshDecalHitShader(const FGlobalShaderMap* ShaderMap) { return ShaderMap->GetShader().GetRayTracingShader(); } FRHIRayTracingShader* GetDefaultHiddenMeshDecalHitShader(const FGlobalShaderMap* ShaderMap) { return ShaderMap->GetShader().GetRayTracingShader(); } void BuildDecalGrid(FRDGBuilder& GraphBuilder, uint32 NumDecals, FRDGBufferSRVRef DecalsSRV, FRayTracingDecals& OutParameters) { // if we have some decals -- build a decal grid if (NumDecals > 0) { const uint32 Resolution = FMath::Clamp(CVarDecalGridResolution.GetValueOnRenderThread(), 1, 2048); const uint32 MaxCount = FMath::Clamp(CVarDecalGridMaxCount.GetValueOnRenderThread(), 1, FMath::Min((int32)NumDecals, RAY_TRACING_DECAL_COUNT_MAXIMUM)); OutParameters.GridResolution = Resolution; OutParameters.GridMaxCount = MaxCount; OutParameters.GridAxis = CVarDecalGridAxis.GetValueOnRenderThread(); FRDGTextureDesc DecalGridDesc = FRDGTextureDesc::Create2DArray( FIntPoint(Resolution, Resolution), PF_R32_UINT, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV, 3); FRDGTexture* DecalGridTexture = GraphBuilder.CreateTexture(DecalGridDesc, TEXT("PathTracer.DecalGrid"), ERDGTextureFlags::None); EPixelFormat DecalGridDataFormat = PF_R32_UINT; size_t DecalGridDataNumBytes = sizeof(uint32); if (NumDecals <= (MAX_uint8 + 1)) { DecalGridDataFormat = PF_R8_UINT; DecalGridDataNumBytes = sizeof(uint8); } else if (NumDecals <= (MAX_uint16 + 1)) { DecalGridDataFormat = PF_R16_UINT; DecalGridDataNumBytes = sizeof(uint16); } FRDGBufferDesc DecalGridDataDesc = FRDGBufferDesc::CreateBufferDesc(DecalGridDataNumBytes, 3 * MaxCount * Resolution * Resolution); FRDGBuffer* DecalGridData = GraphBuilder.CreateBuffer(DecalGridDataDesc, TEXT("PathTracer.DecalGridData")); auto* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWDecalGrid = GraphBuilder.CreateUAV(DecalGridTexture); PassParameters->RWDecalGridData = GraphBuilder.CreateUAV(DecalGridData, DecalGridDataFormat); PassParameters->SceneDecals = DecalsSRV; PassParameters->SceneDecalCount = OutParameters.Count; PassParameters->SceneDecalsTranslatedBoundMin = OutParameters.TranslatedBoundMin; PassParameters->SceneDecalsTranslatedBoundMax = OutParameters.TranslatedBoundMax; PassParameters->DecalGridResolution = OutParameters.GridResolution; PassParameters->DecalGridMaxCount = OutParameters.GridMaxCount; PassParameters->DecalGridAxis = OutParameters.GridAxis; TShaderMapRef ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel)); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("Decal Grid Create (%u decals)", NumDecals), ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(FIntVector(Resolution, Resolution, 3), FIntVector(FComputeShaderUtils::kGolden2DGroupSize, FComputeShaderUtils::kGolden2DGroupSize, 1))); // hookup to the actual rendering pass OutParameters.Grid = DecalGridTexture; OutParameters.GridData = GraphBuilder.CreateSRV(DecalGridData, DecalGridDataFormat); } else { // light grid is not needed - just hookup dummy data FRDGBufferDesc DecalGridDataDesc = FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1); FRDGBuffer* DecalGridData = GraphBuilder.CreateBuffer(DecalGridDataDesc, TEXT("PathTracer.DecalGridData")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(DecalGridData, PF_R32_UINT), 0); OutParameters.GridResolution = 0; OutParameters.GridMaxCount = 0; OutParameters.GridAxis = 0; OutParameters.Grid = GSystemTextures.GetBlackDummy(GraphBuilder); OutParameters.GridData = GraphBuilder.CreateSRV(DecalGridData, PF_R32_UINT); } } FRelevantDecalList GetSortedDecals(FVisibleDecalList& VisibleDecals, TConstArrayView Decals, FScene& Scene, const FViewInfo& View) { const float FadeMultiplier = DecalRendering::GetDecalFadeScreenSizeMultiplier(); const bool bIsPerspectiveProjection = View.IsPerspectiveProjection(); FRelevantDecalList SortedDecals; // NOTE: Reserve is critical here to avoid reallocation of the underlying array, since the sorted decals array holds a pointer! SortedDecals.Reserve(Decals.Num()); VisibleDecals.Reserve(Decals.Num()); for (auto DecalProxy : Decals) { if (!DecalProxy->DecalMaterial || !DecalProxy->DecalMaterial->IsValidLowLevelFast()) { continue; } bool bIsShown = true; if (!DecalProxy->IsShown(&View)) { bIsShown = false; } if (bIsShown) { float FadeAlpha = 1.0f; if (bIsPerspectiveProjection && DecalProxy->FadeScreenSize != 0.0f) { const FMatrix ComponentToWorldMatrix = DecalProxy->ComponentTrans.ToMatrixWithScale(); FadeAlpha = DecalRendering::CalculateDecalFadeAlpha(DecalProxy->FadeScreenSize, ComponentToWorldMatrix, View, FadeMultiplier); } const bool bShouldRender = FadeAlpha > 0.0f; if (bShouldRender) { VisibleDecals.Emplace(*DecalProxy, 0.0f, FadeAlpha, Scene.GetShaderPlatform(), Scene.GetFeatureLevel()); SortedDecals.Emplace(&VisibleDecals.Last()); } } } DecalRendering::SortDecalList(SortedDecals); return SortedDecals; } TRDGUniformBufferRef CreateNullRayTracingDecalsUniformBuffer(FRDGBuilder& GraphBuilder) { auto* OutParameters = GraphBuilder.AllocParameters(); OutParameters->Count = 0; OutParameters->TranslatedBoundMin = FVector3f::ZeroVector; OutParameters->TranslatedBoundMax = FVector3f::ZeroVector; BuildDecalGrid(GraphBuilder, 0, nullptr, *OutParameters); return GraphBuilder.CreateUniformBuffer(OutParameters); } TRDGUniformBufferRef CreateRayTracingDecalData(FRDGBuilder& GraphBuilder, FScene& Scene, const FViewInfo& View, uint32 BaseCallableSlotIndex) { const EShaderPlatform ShaderPlatform = View.Family->GetShaderPlatform(); FVisibleDecalList VisibleDecals; FRelevantDecalList SortedDecals = GetSortedDecals(VisibleDecals, Scene.Decals, Scene, View); // Allocate from the graph builder so that we don't need to copy the data again when queuing the upload auto& RayTracingDecals = GraphBuilder.AllocArray(); RayTracingDecals.Reserve(SortedDecals.Num()); const float Inf = std::numeric_limits::infinity(); FVector3f TranslatedBoundMin(+Inf, +Inf, +Inf); FVector3f TranslatedBoundMax(-Inf, -Inf, -Inf); for (int32 Index = 0; Index < SortedDecals.Num(); ++Index) { const FVisibleDecal& VisibleDecal = *SortedDecals[Index]; const FMaterialRenderProxy* MaterialProxy = VisibleDecal.MaterialProxy; const FMaterial* MaterialResource = &MaterialProxy->GetMaterialWithFallback(View.GetFeatureLevel(), MaterialProxy); const FMaterialShaderMap* MaterialShaderMap = MaterialResource->GetRenderingThreadShaderMap(); auto CallableShader = MaterialShaderMap->GetShader(); if (!CallableShader.IsValid()) { continue; } TUniformBufferRef DecalParameters = CreateDecalParametersBuffer(View, ShaderPlatform, VisibleDecal, EUniformBufferUsage::UniformBuffer_SingleFrame); Scene.RayTracingSBT.TransientUniformBuffers.Add(DecalParameters); // Hold uniform buffer ref in RayTracingSBT since FMeshDrawSingleShaderBindings doesn't FRayTracingShaderCommand& Command = Scene.RayTracingSBT.CallableCommands.AddDefaulted_GetRef(); Command.SetShader(CallableShader); Command.SlotInScene = BaseCallableSlotIndex + Index; FMeshDrawSingleShaderBindings SingleShaderBindings = Command.ShaderBindings.GetSingleShaderBindings(SF_RayCallable); CallableShader->GetShaderBindings(&Scene, Scene.GetFeatureLevel(), *MaterialProxy, *MaterialResource, View, DecalParameters, SingleShaderBindings); const FBox BoxBounds = VisibleDecal.BoxBounds; FRayTracingDecal& DestDecal = RayTracingDecals.AddDefaulted_GetRef(); DestDecal.TranslatedBoundMin = FVector3f(BoxBounds.Min + View.ViewMatrices.GetPreViewTranslation()); DestDecal.TranslatedBoundMax = FVector3f(BoxBounds.Max + View.ViewMatrices.GetPreViewTranslation()); DestDecal.CallableSlotIndex = BaseCallableSlotIndex + Index; { const FMatrix WorldToDecalMatrix = VisibleDecal.ComponentTrans.ToInverseMatrixWithScale(); DestDecal.TranslatedWorldToDecal = FMatrix44f(FTranslationMatrix(-View.ViewMatrices.GetPreViewTranslation()) * WorldToDecalMatrix); } TranslatedBoundMin = FVector3f::Min(TranslatedBoundMin, DestDecal.TranslatedBoundMin); TranslatedBoundMax = FVector3f::Max(TranslatedBoundMax, DestDecal.TranslatedBoundMax); } // Upload the buffer of decals to the GPU const uint32 NumCopyDecals = FMath::Max(1u, (uint32)RayTracingDecals.Num()); // need at least one since zero-sized buffers are not allowed const size_t DataSize = sizeof(FRayTracingDecal) * RayTracingDecals.Num(); FRDGBufferSRVRef DecalsSRV = GraphBuilder.CreateSRV(FRDGBufferSRVDesc(CreateStructuredBuffer(GraphBuilder, TEXT("PathTracer.DecalsBuffer"), sizeof(FRayTracingDecal), NumCopyDecals, RayTracingDecals.GetData(), DataSize, ERDGInitialDataFlags::NoCopy))); auto* OutParameters = GraphBuilder.AllocParameters(); OutParameters->Count = RayTracingDecals.Num(); OutParameters->TranslatedBoundMin = TranslatedBoundMin; OutParameters->TranslatedBoundMax = TranslatedBoundMax; BuildDecalGrid(GraphBuilder, RayTracingDecals.Num(), DecalsSRV, *OutParameters); return GraphBuilder.CreateUniformBuffer(OutParameters); } #endif // RHI_RAYTRACING